先回顾一下每轮运算的操作步骤:
SubBytes(state) 对数据进行S字节变换
ShiftRows(state) 进行行变换
MixColumns(state) 进行列混合变换
AddRoundKey(state, Keys[ 当前轮密钥组] ) 与当前轮的密钥进行异或
首先是S变换,当然计算机编程实现S变换的基本上都是用查表法,这没有什么好多说的。
接下来是行变换。其实由于计算机里面存储数据的顺序的习惯写法和State矩阵的定义是不一样的,比如数据在内存中按D0,D1,D2……D15的顺序存放,而state的定义是
D0 D4 D8 D12
D1 D5 D9 D13
D2 D6 D10 D14
D3 D7 D11 D15
行变换完后为:
D0 D4 D8 D12
D5 D9 D13 D1
D10 D14 D2 D6
D15 D3 D7 D11
行变换完后在内存中存放顺序变为 {D0,D5,D10,D15}, {D4,D9,D14,D3}, {D8,D13,D2,D7} ,{ D12,D1,D6,D11} ,观察它们的规律,可知每个字的首字节为原来各字的首字节i=0,4,8,12,而每个字其后的字节为 (i+5n)mod 16 , n=1,2,3。行变换暂时先看到这里。
接下来是列变换,列变换是用矩阵
| 02 03 01 01 | D0 D4 D8 D12
| 01 02 03 01 | × D5 D9 D13 D1
| 01 01 02 03 | D10 D14 D2 D6
| 03 01 01 02 | D15 D3 D7 D11
这里我们一列一列的看。要生成第一列的结果,也就是内存中的第一个字,需要的变换为:
[02 01 01 03 ]×D0
+[03 02 01 01]×D5
+[ 01 03 02 01]×D10
+[01 01 03 02]×D15
这里的×和+都是GF[2,8]域里的运算,+实际上就是异或。可以看到要生成一列,也就是内存中的一个32位字,需要先生成4个乘法结果字,再对它们做异或。注意到每个乘法结果字只和Dn有关,进一步注意到每行的系数实际上是可以通过循环移位来得到的,所以可以建立一个 [02 01 01 03 ]×Dn 的表,通过查表来得到每个行的结果向量,第一行可以直接查表得到,其余各行需要进行循环移位操作。
好了,再回头看看S变换和行变换。S变换是一对一的变换,如果我们把每轮开始的状态计为:
D0 D4 D8 D12
D1 D5 D9 D13
D2 D6 D10 D14
D3 D7 D11 D15
经过S变换后变为:
SD0 SD4 SD8 SD12
SD1 SD5 SD9 SD13
SD2 SD6 SD10 SD14
SD3 SD7 SD11 SD15
再经过行变换,然后是列变换,其中列变换的第一列为:
[02 01 01 03 ]×SD0
+[03 02 01 01]×SD5
+[ 01 03 02 01]×SD10
+[01 01 03 02]×SD15
那么经过分析,发现其实可以建立一个 Dn到[02 01 01 03 ]×SDn的查找表,一次把三个步骤完成,设这个表为 SCol_Box,那么生成state的一列,也即结果中的一个32位字的过程为:
SCol_Box [ D[i*4] ] ^
rot_l ( SCol_Box[ D[ 0xf &(i*4+5) ] ,1 ) ^
rot_l ( SCol_Box[ D[ 0xf &(i*4+10) ] ,2 ) ^
rot_l ( SCol_Box[ D[ 0xf &(i*4+15) ] ,3 ) ^
其中rot_l( w , n)表示对一个32位字 w进行循环左移n个字节。(注意在小端模式的计算机中,第一个字节是最低的,[02 01 01 03]变为[ 03 02 01 01],实际上是在循环左移,当然大端模式的计算机就是右移)
因此,将一轮中的三个操作合成了一个操作,而且每次操作只需要查4*4次表和3*4循环移位和异或运算,大大减少了计算量。
当然最后与密钥的异或运算,是不能与其它步骤合并的,不过这个操作本身开销也非常小。
说完了加密过程的优化,再说说解密过程。回顾一下解密的一轮的过程:
invShiftRows(state) 进行反行变换
invSubBytes(state) 对数据进行反S字节变换
AddRoundKey(state, Keys[ 当前轮] ) 与当前轮的密钥进行异或
invMixColumns(state) 进行反列混合变换
注意到反行变换和反S变换的顺序是可以直接互换的,那么就变成
invSubBytes(state) 对数据进行反S字节变换
invShiftRows(state) 进行反行变换
AddRoundKey(state, Keys[ 当前轮] ) 与当前轮的密钥进行异或
invMixColumns(state) 进行反列混合变换
可见与加密过程的差别仅仅是 与密钥的异或和列变换的顺序不同,而正是这个不同使我们不能直接象加密过程那样将三种操作合并。但是如果能把
AddRoundKey(state, Keys[ 当前轮] ) 与当前轮的密钥进行异或
invMixColumns(state) 进行反列混合变换
这两个操作的顺序交换一下,那么就和加密过程一样,可以采用同样的优化手段了。那么怎么交换这两个操作呢?
注意到反列变换可以看作是一个矩阵乘法操作,即
[State_Col]= [ iMix_Mat] × [ State_Key ] , State_Key表示经过AddKey操作后的状态,加[ ]表示以一个矩阵操作来理解。
而 [State_Key] = [State_Row] +[ RoundKey]
即 [State_Col]= [ iMix_Mat] × ( [State_Row] +[ RoundKey] ),而在GF[2,8]运算中是可以写成:
[State_Col]= [ iMix_Mat] × [State_Row] +
[ iMix_Mat] ×[ RoundKey]
也就是说,可以先对 State_Row进行反列变换,再与经过反列变换的RoundKey相加,从而把流程变为了:
invSubBytes(state) 对数据进行反S字节变换
invShiftRows(state) 进行反行变换
invMixColumns(state) 进行反列混合变换
AddRoundKey(state, iMixCol(Keys[ 当前轮] )) 与经过反列变换的当前轮的密钥进行异或
这样解密的轮过程顺序就完全和加密过程一样了,我们只需要事先计算出各轮密钥的反列变换后的值,并建立 Dn到 [0e 09 0d 0b] invS(Dn)的查找表,就可以同样把解密的三个步骤合并为一个步骤。
基本上,AES算法编程中需要注意的问题基本上都说了,当然要继续深究的话还是有很多东西可发掘的,比如这个S_Box是不是最优的呢?这个列变换的正变换矩阵和反变换矩阵能不能一样,从而只需要建立一张表呢?很多专家们在研究这些问题,作为非专业人士,我们就不要太计较了,等着专家们拿出结论来就行了。比如有篇论文就说现在S_Box的生成是先求倒数,再进行一次(F1,63)变换这种方式可以再改进,先{ 6B,5D}一次,再求倒数,再 {6B,5D}会更好,还有资料说形如
[ a1+1 ,a0 , a1 , a0
a0, a1+1,a0 , a1
a1, a0 , a1+1 ,a0
a0, a1 , a0 , a1+1]
的列变换矩阵可以使正反变换矩阵一样,最简单的形式就是a0=1,a1=2。等等等等,大家如果想在AES上改进做自己独有的加密算法的话,完全可以采用不同的S_Box和变换矩阵,甚至每轮都换个S_Box和变换矩阵。
说了这么多,都是用软件来实现,如果用硬件来实现的话,要关心的内容就有些不同了,因为硬件查表很费资源,所以就要用其原始的生成关系来生成逻辑,这就更不是我的专业了。
当然,最快的方式是直接用CPU提供的硬件指令,最新的IntelCPU提供了几条AES专用指令,使APE算法的开发速度和效率提高到令人发指的地步,仅仅需要写一条指令就可以完成一轮过程,具体过程什么都不用管了。
最后附上我学习过程中编的Python程序,还有从网上下载的一个C程序。这个C程序水平很高,给了我很多启发,并且使用也很简单。
AES.py
#coding=gbk
'''学习AES加密算法,仅为学习明了其整个原理和过程,不考虑效率,全部根据最直接的定义来实现
若要提高效率,可对其中使用的大量表格预生成,通过查表来实现
特别注意,AES规范中使用的字节顺序是按照大端模式来处理的,密钥和明文的存放格式还不同,如
密码的输入顺序是k1,k2,k3,k4,k5,k6,k7,k8,k9,k10,k11,k12,k13,k14,k15,k16,在AES中分组方式为:
k1,k2,k3,k4
k5,k6,k7,k8
k9,k10,k11,k12
k13,k14,k15,k16
其中每一行可看作一个32位字,如果按大端模式来看就是k1k2k3k4,按小端模式就是k4k3k2k1,而规范中都是按大端模式来理解
对于数据,假设输入顺序为a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,存放格式则又变为
a1 , a5, a9 , a13
a2 , a6, a10, a14
a3 , a7, a11, a15
a4 , a8, a12, a16
而在做与密钥的异或运算时,又是按照 a1-k1 ,a2-k2,a3-k3,....a16,k16的一一对应的顺序来进行
因此一定要注意顺序的问题,并且注意在小端的处理器上行左右移动是与之相反的。
本程序完全按照字节来操作,并遵照规范中的定义
'''
def f_mod(a,b):
'''AES算法中的取模'''
n=0x8000
c=b
while b&0x8000==0:
b<<=1
while b>=c:
if a&n!=0:
a^=b
n>>=1
b>>=1
return a
def f_mul(a,b,m=0x11b):
'''AES算法中使用的乘法,模因子为0x11b'''
#a&=0xff
#b&=0xff
c=0
while b!=0:
if b&1!=0:
c=c^a
b>>=1
a<<=1
if m!=0:
c=f_mod(c,m)
return c
'''AES中的乘法以及模因子0x11b的选择,可以使任一数的256次方与其本身同模,
也即任一数的255次方模为1,根据这一特性,可定义一个数的倒数1/c为c的254次方,
但是为每一个数求254次方是很费时的。注意到某些数比如3的1~255次幂是遍历整个GF2.8空间的,
每个数都可以作为一个3的n次幂出现,又3**n × 3**(255-n)==1,所以只要找出一个数c是3的n幂,
则它的倒数就是3的255-n次幂,从而只要建立一个3的255次的表就可以通过查表的方式来确定C对于
3的幂次,同时算出1/C对于3的幂次从而查出1/C'''
pow_3=[]
c=1
for i in range(255):
c=f_mul(c,3)
pow_3.append(c)
def f_inv(a):
'''通过查表,求a的倒数'''
if a==0:
return 0
a&=0xff
i=pow_3.index(a)
return pow_3[253-i]
'''s 盒的生成 是对每个数CC,取其倒数c=1/CC,再对c进行如下操作:
c^rol(c,1)^rol(c,2)^rol(c,3)^rol(c,4)^99
写成式子形式为 0xF1*c + 0x63 mod 0x101 , 或写为{F1,63}
其中F1理解为多项式 x7+x6+x5+x4+1,按我的理解, 乘x7即表示循环左移1位,x6表示rol两位
有篇文章认为将计算方法改为先{ 6B,5D}一次,再求倒数,再 {6B,5D}一次会有更好的效果
'''
def ror(c,n):
c&=0xff
if n>7:
n=n%8
d=((c<<(8-n))&0xff)|(c>>(n))
return d
def rol(c,n):
c&=0xff
if n>7:
n=n%8
d=((c<<(n))&0xff)|(c>>(8-n))
return d
def s_trans(c,p1,p2):
d=0
a=c
while p1!=0:
if p1&1:
d^=a
p1>>=1
a=ror(a,1)
d^=p2
return d
s_box=[]
for i in range(256):
c=f_inv(i)
#或者用 d= s_trans(c,0xf1,0x63)
d=c
c=rol(c,1)
d^=c
c=rol(c,1)
d^=c
c=rol(c,1)
d^=c
c=rol(c,1)
d^=c
d^=99
s_box.append(d)
'''s_box=[
99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171,118,
202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156,164, 114, 192,
183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21,
4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117,
9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132,
83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207,
208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168,
81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210,
205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115,
96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219,
224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121,
231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174,
8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139,
138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29,
158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85,
40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84,187, 22]'''
'''列混合运算矩阵系数和反变换矩阵系数
将输入作为一个4*4的字节矩阵,对其进行矩阵式乘法
[Out]=[mix_box]* [In]
而 逆运算是 [imix_box]*[Out]==[In]
根据一篇文献,形如
[ a1+1 ,a0 , a1 , a0
a0, a1+1,a0 , a1
a1, a0 , a1+1 ,a0
a0, a1 , a0 , a1+1]
的矩阵可实现正反变换矩阵相同,其中最简的方式是a0=1,a1=2
'''
mix_box=[ [ 2,3,1,1],[1,2,3,1],[1,1,2,3],[3,1,1,2]]
imix_box=[ [ 14,11,13,9],[9,14,11,13],[13,9,14,11],[11,13,9,14]]
def f_mix( box1,box2):
'''列混合函数,可以理解为一个矩阵乘法'''
r=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
for i in range(4):
for j in range(4):
c=0
for k in range(4):
c^=f_mul(box1[i][k],box2[k][j])
r[i][j]=c
return r
'''行变换,比较简单,第一行不变,第二行循环左移1字节
,第三行循环左移2字节,第四行循环左移3字节'''
def roll(line,n):
t=line[n:]
t+=line[:n]
return t
def f_line(box):
box[1]=roll(box[1],1)
box[2]=roll(box[2],2)
box[3]=roll(box[3],3)
return box
'''扩展密钥的生成,首先需要一个轮常数数组,这个轮常数是2**(n-1)在GF(2,8)的值,n为轮数,从1开始,
密钥以32位字组织者,每轮操作需要一个128位的密钥,
轮常数是在扩展密钥的长度为输入密钥的整倍数(128/192/256)时的那个字的第一个字节要与之异或的一个字节
因为轮操作数量当密钥为128位时是10轮,192位12轮,256位时14轮,
所以128位时需要10个轮常数,192位时需要12/(192/128)=8个,256位时需要14/2=7个'''
round_P=[]
for i in range(10):
c=1<6 and i%Nk==4: #其实就是Nk==8时,对第12、20、28轮还要进行以下操作
temp=s_line(temp)
Keys.append( Keys[i-Nk] xor temp)
i+=1
'''
global Keys,round_P,Nk,Nr
inKey=list(inKey)
Nk=len(inKey)/4
if Nk<=4:
Nk=4
Nr=10
elif Nk<=6:
Nk=6
Nr=12
else:
Nk=8
Nr=14
kLen=len(inKey)
for i in range(kLen):
if type(inKey[i])==str:
inKey[i]=ord(inKey[i])
if kLen>32:
kLen=32
i=0
temp=[0]*(Nk*4-kLen) #以0补足输入密钥长度
inKey+=temp
Keys=[]
while i6 and i%Nk==4:
temp=s_line(temp)
temp=xor_line(Keys[i-Nk],temp)
Keys.append(temp)
i+=1
'''定义几种基本变换操作函数'''
def xorkey(data,key):
'''将data与key异或,注意它们的顺序'''
for i in range(4):
for j in range(4):
data[j][i]^=key[i][j]
return data
def s_state(data):
'''对一个数据的state块进行s变换'''
for i in range(4):
data[i]=s_line(data[i])
return data
def rol_state(data):
'''对一个数据state块各行进行行变换'''
data[1]=roll(data[1],1)
data[2]=roll(data[2],2)
data[3]=roll(data[3],3)
return data
def groupKey(r):
k=[ Keys[r*4],Keys[r*4+1],Keys[r*4+2],Keys[r*4+3]]
return k
def outState(state):
out=[]
for i in range(4):
for j in range(4):
out.append( state[j][i])
return out
def printState(desc,state):
'''打印各步骤的数据供观察和比对'''
return 0 #不用的时候关闭打印
if desc:
print desc,':',
for i in outState(state):
print hex(i),
print ''
def encrypt( incode):
'''加密过程,每次输入16个字节,输出16个字节
加密过程为:分组,与密钥异或,然后循环Nr-1轮以下操作:
S变换,行变换,列变换,与扩展密钥异或
然后进行最后一轮,区别是没有列变换'''
if len(Keys)==0:
print '还没有设置密码!'
return []
ilen=len(incode)
temp=[0]*16
if ilen>=16:
ilen=16
i=0
for i in range(ilen):
if type(incode[i])==str:
temp[i]=ord(incode[i])
else:
temp[i]=incode[i]
#分组
state=[[0]*4,[0]*4,[0]*4,[0]*4]
for i in range(4):
for j in range(4):
state[j][i]=temp[i*4+j]
print state
#第一次xor key
state=xorkey(state,groupKey(0))
print state
printState('r1state',state)
round=1
while round0:
state=irol_state(state)
state=is_state(state)
state=xorkey(state,groupKey(round))
state=f_mix(imix_box,state)
round-=1
#最后一轮
state=irol_state(state)
state=is_state(state)
state=xorkey(state,groupKey(0))
return outState(state)
C代码,把头文件、C文件和示例放在一起
------------------------------------------------------------------------------
AES加密 头文件
------------------------------------------------------------------------------
// rijndael.h: interface for the rijndael class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_RIJNDAEL_H__65AE854C_7E77_4488_AC14_66161F67B341__INCLUDED_)
#define AFX_RIJNDAEL_H__65AE854C_7E77_4488_AC14_66161F67B341__INCLUDED_
typedef unsigned char u1byte; /* an 8 bit unsigned character type */
typedef unsigned short u2byte; /* a 16 bit unsigned integer type */
typedef unsigned long u4byte; /* a 32 bit unsigned integer type */
typedef signed char s1byte; /* an 8 bit signed character type */
typedef signed short s2byte; /* a 16 bit signed integer type */
typedef signed long s4byte; /* a 32 bit signed integer type */
/* 2. Standard interface for AES cryptographic routines */
/* These are all based on 32 bit unsigned values and may require */
/* endian conversion for big-endian architectures */
#define LITTLE_ENDIAN
class AES
{
public:
virtual void set_key(const u1byte key[], const u4byte key_bits) = 0;
virtual void encrypt(const u1byte in_blk[16], u1byte out_blk[16]) = 0;
virtual void decrypt(const u1byte in_blk[16], u1byte out_blk[16]) = 0;
};
/* 3. Basic macros for speeding up generic operations */
/* Circular rotate of 32 bit values */
#ifdef _MSC_VER
# include
# pragma intrinsic(_lrotr,_lrotl)
# define rotr(x,n) _lrotr(x,n)
# define rotl(x,n) _lrotl(x,n)
#else
#define rotr(x,n) (((x) >> ((int)(n))) | ((x) << (32 - (int)(n))))
#define rotl(x,n) (((x) << ((int)(n))) | ((x) >> (32 - (int)(n))))
#endif
/* Invert byte order in a 32 bit variable */
#define bswap(x) (rotl(x, 8) & 0x00ff00ff | rotr(x, 8) & 0xff00ff00)
/* Extract byte from a 32 bit quantity (little endian notation) */
#define byte(x,n) ((u1byte)((x) >> (8 * n)))
/* Input or output a 32 bit word in machine order */
#ifdef LITTLE_ENDIAN
#define u4byte_in(x) (*(u4byte*)(x))
#define u4byte_out(x, v) (*(u4byte*)(x) = (v))
#else
#define u4byte_in(x) bswap(*(u4byte)(x))
#define u4byte_out(x, v) (*(u4byte*)(x) = bswap(v))
#endif
class rijndael
{
public:
rijndael();
virtual ~rijndael();
public:
char *name(void);
void set_key(const u1byte key[], const u4byte key_len);
void encrypt(const u1byte in_blk[16], u1byte out_blk[16]);
void decrypt(const u1byte in_blk[16], u1byte out_blk[16]);
private:
u4byte k_len;
u4byte e_key[64];
u4byte d_key[64];
};
#endif // !defined(AFX_RIJNDAEL_H__65AE854C_7E77_4488_AC14_66161F67B341__INCLUDED_)
--------------------------------------------------------------------------------------
c 文件
--------------------------------------------------------------------------------------
// rijndael.cpp: implementation of the rijndael class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
//#include "MainFrm.h"
#include "rijndael.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
rijndael::rijndael()
{
}
rijndael::~rijndael()
{
}
#define LARGE_TABLES
namespace
{
u1byte pow_tab[256];
u1byte log_tab[256];
u1byte sbx_tab[256];
u1byte isb_tab[256];
u4byte rco_tab[ 10];
u4byte ft_tab[4][256];
u4byte it_tab[4][256];
#ifdef LARGE_TABLES
u4byte fl_tab[4][256];
u4byte il_tab[4][256];
#endif
u4byte tab_gen = 0;
#define ff_mult(a,b) (a && b ? pow_tab[(log_tab[a] + log_tab[b]) % 255] : 0)
#define f_rn(bo, bi, n, k) \
bo[n] = ft_tab[0][byte(bi[n],0)] ^ \
ft_tab[1][byte(bi[(n + 1) & 3],1)] ^ \
ft_tab[2][byte(bi[(n + 2) & 3],2)] ^ \
ft_tab[3][byte(bi[(n + 3) & 3],3)] ^ *(k + n)
#define i_rn(bo, bi, n, k) \
bo[n] = it_tab[0][byte(bi[n],0)] ^ \
it_tab[1][byte(bi[(n + 3) & 3],1)] ^ \
it_tab[2][byte(bi[(n + 2) & 3],2)] ^ \
it_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n)
#ifdef LARGE_TABLES
#define ls_box(x) \
( fl_tab[0][byte(x, 0)] ^ \
fl_tab[1][byte(x, 1)] ^ \
fl_tab[2][byte(x, 2)] ^ \
fl_tab[3][byte(x, 3)] )
#define f_rl(bo, bi, n, k) \
bo[n] = fl_tab[0][byte(bi[n],0)] ^ \
fl_tab[1][byte(bi[(n + 1) & 3],1)] ^ \
fl_tab[2][byte(bi[(n + 2) & 3],2)] ^ \
fl_tab[3][byte(bi[(n + 3) & 3],3)] ^ *(k + n)
#define i_rl(bo, bi, n, k) \
bo[n] = il_tab[0][byte(bi[n],0)] ^ \
il_tab[1][byte(bi[(n + 3) & 3],1)] ^ \
il_tab[2][byte(bi[(n + 2) & 3],2)] ^ \
il_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n)
#else
#define ls_box(x) \
((u4byte)sbx_tab[byte(x, 0)] << 0) ^ \
((u4byte)sbx_tab[byte(x, 1)] << 8) ^ \
((u4byte)sbx_tab[byte(x, 2)] << 16) ^ \
((u4byte)sbx_tab[byte(x, 3)] << 24)
#define f_rl(bo, bi, n, k) \
bo[n] = (u4byte)sbx_tab[byte(bi[n],0)] ^ \
rotl(((u4byte)sbx_tab[byte(bi[(n + 1) & 3],1)]), 8) ^ \
rotl(((u4byte)sbx_tab[byte(bi[(n + 2) & 3],2)]), 16) ^ \
rotl(((u4byte)sbx_tab[byte(bi[(n + 3) & 3],3)]), 24) ^ *(k + n)
#define i_rl(bo, bi, n, k) \
bo[n] = (u4byte)isb_tab[byte(bi[n],0)] ^ \
rotl(((u4byte)isb_tab[byte(bi[(n + 3) & 3],1)]), 8) ^ \
rotl(((u4byte)isb_tab[byte(bi[(n + 2) & 3],2)]), 16) ^ \
rotl(((u4byte)isb_tab[byte(bi[(n + 1) & 3],3)]), 24) ^ *(k + n)
#endif
void gen_tabs(void)
{ u4byte i, t;
u1byte p, q;
// log and power tables for GF(2**8) finite field with
// 0x011b as modular polynomial - the simplest prmitive
// root is 0x03, used here to generate the tables
for(i = 0,p = 1; i < 256; ++i)
{
pow_tab[i] = (u1byte)p; log_tab[p] = (u1byte)i;
p = p ^ (p << 1) ^ (p & 0x80 ? 0x01b : 0);
}
log_tab[1] = 0; p = 1;
for(i = 0; i < 10; ++i)
{
rco_tab[i] = p;
p = (p << 1) ^ (p & 0x80 ? 0x1b : 0);
}
for(i = 0; i < 256; ++i)
{
p = (i ? pow_tab[255 - log_tab[i]] : 0); q = p;
q = (q >> 7) | (q << 1); p ^= q;
q = (q >> 7) | (q << 1); p ^= q;
q = (q >> 7) | (q << 1); p ^= q;
q = (q >> 7) | (q << 1); p ^= q ^ 0x63;
sbx_tab[i] = p; isb_tab[p] = (u1byte)i;
}
for(i = 0; i < 256; ++i)
{
p = sbx_tab[i];
#ifdef LARGE_TABLES
t = p; fl_tab[0][i] = t;
fl_tab[1][i] = rotl(t, 8);
fl_tab[2][i] = rotl(t, 16);
fl_tab[3][i] = rotl(t, 24);
#endif
t = ((u4byte)ff_mult(2, p)) |
((u4byte)p << 8) |
((u4byte)p << 16) |
((u4byte)ff_mult(3, p) << 24);
ft_tab[0][i] = t;
ft_tab[1][i] = rotl(t, 8);
ft_tab[2][i] = rotl(t, 16);
ft_tab[3][i] = rotl(t, 24);
p = isb_tab[i];
#ifdef LARGE_TABLES
t = p; il_tab[0][i] = t;
il_tab[1][i] = rotl(t, 8);
il_tab[2][i] = rotl(t, 16);
il_tab[3][i] = rotl(t, 24);
#endif
t = ((u4byte)ff_mult(14, p)) |
((u4byte)ff_mult( 9, p) << 8) |
((u4byte)ff_mult(13, p) << 16) |
((u4byte)ff_mult(11, p) << 24);
it_tab[0][i] = t;
it_tab[1][i] = rotl(t, 8);
it_tab[2][i] = rotl(t, 16);
it_tab[3][i] = rotl(t, 24);
}
tab_gen = 1;
}
#define star_x(x) (((x) & 0x7f7f7f7f) << 1) ^ ((((x) & 0x80808080) >> 7) * 0x1b)
#define imix_col(y,x) \
u = star_x(x); \
v = star_x(u); \
w = star_x(v); \
t = w ^ (x); \
(y) = u ^ v ^ w; \
(y) ^= rotr(u ^ t, 8) ^ \
rotr(v ^ t, 16) ^ \
rotr(t,24)
} // end of anonymous namespace
char *rijndael::name(void)
{
return "rijndael";
}
// initialise the key schedule from the user supplied key
#define loop4(i) \
{ t = ls_box(rotr(t, 8)) ^ rco_tab[i]; \
t ^= e_key[4 * i]; e_key[4 * i + 4] = t; \
t ^= e_key[4 * i + 1]; e_key[4 * i + 5] = t; \
t ^= e_key[4 * i + 2]; e_key[4 * i + 6] = t; \
t ^= e_key[4 * i + 3]; e_key[4 * i + 7] = t; \
}
#define loop6(i) \
{ t = ls_box(rotr(t, 8)) ^ rco_tab[i]; \
t ^= e_key[6 * i]; e_key[6 * i + 6] = t; \
t ^= e_key[6 * i + 1]; e_key[6 * i + 7] = t; \
t ^= e_key[6 * i + 2]; e_key[6 * i + 8] = t; \
t ^= e_key[6 * i + 3]; e_key[6 * i + 9] = t; \
t ^= e_key[6 * i + 4]; e_key[6 * i + 10] = t; \
t ^= e_key[6 * i + 5]; e_key[6 * i + 11] = t; \
}
#define loop8(i) \
{ t = ls_box(rotr(t, 8)) ^ rco_tab[i]; \
t ^= e_key[8 * i]; e_key[8 * i + 8] = t; \
t ^= e_key[8 * i + 1]; e_key[8 * i + 9] = t; \
t ^= e_key[8 * i + 2]; e_key[8 * i + 10] = t; \
t ^= e_key[8 * i + 3]; e_key[8 * i + 11] = t; \
t = e_key[8 * i + 4] ^ ls_box(t); \
e_key[8 * i + 12] = t; \
t ^= e_key[8 * i + 5]; e_key[8 * i + 13] = t; \
t ^= e_key[8 * i + 6]; e_key[8 * i + 14] = t; \
t ^= e_key[8 * i + 7]; e_key[8 * i + 15] = t; \
}
void rijndael::set_key(const u1byte in_key[], const u4byte key_len)
{ u4byte i, t, u, v, w;
if(!tab_gen)
gen_tabs();
k_len = (key_len + 31) / 32;
e_key[0] = u4byte_in(in_key );
e_key[1] = u4byte_in(in_key + 4);
e_key[2] = u4byte_in(in_key + 8);
e_key[3] = u4byte_in(in_key + 12);
switch(k_len)
{
case 4: t = e_key[3];
for(i = 0; i < 10; ++i)
loop4(i);
break;
case 6: e_key[4] = u4byte_in(in_key + 16); t = e_key[5] = u4byte_in(in_key + 20);
for(i = 0; i < 8; ++i)
loop6(i);
break;
case 8: e_key[4] = u4byte_in(in_key + 16); e_key[5] = u4byte_in(in_key + 20);
e_key[6] = u4byte_in(in_key + 24); t = e_key[7] = u4byte_in(in_key + 28);
for(i = 0; i < 7; ++i)
loop8(i);
break;
}
d_key[0] = e_key[0]; d_key[1] = e_key[1];
d_key[2] = e_key[2]; d_key[3] = e_key[3];
for(i = 4; i < 4 * k_len + 24; ++i)
{
imix_col(d_key[i], e_key[i]);
}
return;
}
// encrypt a block of text
#define f_nround(bo, bi, k) \
f_rn(bo, bi, 0, k); \
f_rn(bo, bi, 1, k); \
f_rn(bo, bi, 2, k); \
f_rn(bo, bi, 3, k); \
k += 4
#define f_lround(bo, bi, k) \
f_rl(bo, bi, 0, k); \
f_rl(bo, bi, 1, k); \
f_rl(bo, bi, 2, k); \
f_rl(bo, bi, 3, k)
void rijndael::encrypt(const u1byte in_blk[16], u1byte out_blk[16])
{ u4byte b0[4], b1[4], *kp;
b0[0] = u4byte_in(in_blk ) ^ e_key[0]; b0[1] = u4byte_in(in_blk + 4) ^ e_key[1];
b0[2] = u4byte_in(in_blk + 8) ^ e_key[2]; b0[3] = u4byte_in(in_blk + 12) ^ e_key[3];
kp = e_key + 4;
if(k_len > 6)
{
f_nround(b1, b0, kp); f_nround(b0, b1, kp);
}
if(k_len > 4)
{
f_nround(b1, b0, kp); f_nround(b0, b1, kp);
}
f_nround(b1, b0, kp); f_nround(b0, b1, kp);
f_nround(b1, b0, kp); f_nround(b0, b1, kp);
f_nround(b1, b0, kp); f_nround(b0, b1, kp);
f_nround(b1, b0, kp); f_nround(b0, b1, kp);
f_nround(b1, b0, kp); f_lround(b0, b1, kp);
u4byte_out(out_blk, b0[0]); u4byte_out(out_blk + 4, b0[1]);
u4byte_out(out_blk + 8, b0[2]); u4byte_out(out_blk + 12, b0[3]);
}
// decrypt a block of text
#define i_nround(bo, bi, k) \
i_rn(bo, bi, 0, k); \
i_rn(bo, bi, 1, k); \
i_rn(bo, bi, 2, k); \
i_rn(bo, bi, 3, k); \
k -= 4
#define i_lround(bo, bi, k) \
i_rl(bo, bi, 0, k); \
i_rl(bo, bi, 1, k); \
i_rl(bo, bi, 2, k); \
i_rl(bo, bi, 3, k)
void rijndael::decrypt(const u1byte in_blk[16], u1byte out_blk[16])
{ u4byte b0[4], b1[4], *kp;
b0[0] = u4byte_in(in_blk ) ^ e_key[4 * k_len + 24];
b0[1] = u4byte_in(in_blk + 4) ^ e_key[4 * k_len + 25];
b0[2] = u4byte_in(in_blk + 8) ^ e_key[4 * k_len + 26];
b0[3] = u4byte_in(in_blk + 12) ^ e_key[4 * k_len + 27];
kp = d_key + 4 * (k_len + 5);
if(k_len > 6)
{
i_nround(b1, b0, kp); i_nround(b0, b1, kp);
}
if(k_len > 4)
{
i_nround(b1, b0, kp); i_nround(b0, b1, kp);
}
i_nround(b1, b0, kp); i_nround(b0, b1, kp);
i_nround(b1, b0, kp); i_nround(b0, b1, kp);
i_nround(b1, b0, kp); i_nround(b0, b1, kp);
i_nround(b1, b0, kp); i_nround(b0, b1, kp);
i_nround(b1, b0, kp); i_lround(b0, b1, kp);
u4byte_out(out_blk, b0[0]); u4byte_out(out_blk + 4, b0[1]);
u4byte_out(out_blk + 8, b0[2]); u4byte_out(out_blk + 12, b0[3]);
}
---------------------------------------------------------------------------
调用方法
---------------------------------------------------------------------------
void Caesproject2Dlg::OnBnClickedButton1()//加密按钮按下
{
int i;
CString s,s1;//s用来接收明文,s1用来接收密钥
rijndael rij;//加密类的对象
mingwen.GetWindowText(s);//从文本框获取明文
length=s.GetLength();//明文长度length是Caesproject2Dlg类的成员变量
N=length/16;//每16字节一组,N是Caesproject2Dlg类的成员变量
if(length%16==0) //作用是当明文长度不是128bit整数倍时调整N值
{
N=N;
}
else
{
length=length+16-length%16;
N=N+1;
}
LPTSTR lpsz = new TCHAR[s.GetLength()+1];
_tcscpy(lpsz,s);//将明文拷贝到lpsz指向的内存
if(s.GetLength()%16!=0) //如果明文长度不是128bit的倍数用空格补齐剩余的位
{
for(i=0;i<(int)(16-s.GetLength()%16);i++)
{
lpsz[length-16+s.GetLength()%16+i]=' ';
}
}
unsigned char *e_bit;//用于指向加密后的密文
e_bit=new unsigned char [length];
miyao.GetWindowText(s1);//从文本框中取得密钥
LPTSTR k_bit = new TCHAR[256];
_tcscpy(k_bit,s1);//将密钥拷贝到k_bit指向的内存
rij.set_key((unsigned char *)k_bit,256);//设置密钥
for(int j=0;j