先回顾一下每轮运算的操作步骤:
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<<i round_P.append(f_mod(c,0x11b)) def s_line(line): '''对一行中的每个元素进行s变换''' l=line[:] for i in range(len(line)): l[i]=s_box[line[i]] return l def is_line(line): '''对一行中的每个元素进行反S变换''' l=line[:] for i in range(len(line)): l[i]=s_box.index(line[i]) return l def xor_line(line1,line2): '''对每一行中的每一个元素进行异或''' line=[] for i in range(len(line1)): line.append( line1[i]^line2[i]) return line Keys=[] Nk=0 Nr=0 def setKey( inKey): '''生成每轮的密钥,密钥按4字节进行分组,128位、192位、256位分别对应 Nk=4、6、8 每一轮加密需要使用一组4个双字的密钥,加上初始一次异或,各Nk下一共需要 44、52、60组密钥, 除前面的Nk个密钥为用户输入的外,其它密钥根据一定的规则生成,这个规则为: Nk为输入密钥组长度、Nb为每轮需要使用的密钥组数(在AES算法中固定为4),Nr为需要进行的轮数,即为10,12,14 Keys为存放生成密钥的二维数组 则前面的Nk个复制输入的密钥,以下标为0开始,从下标为Nk个,执行以下的操作 : i=Nk while i<Nb*(Nr+1): temp=Keys[i-1] if i % Nk==0: temp= s_line ( roll(temp) ) xor round_P[i/Nk-1] #即当i是Nk的倍数时,先将前一组循环左移一字节, 再对每字节进行S变换,再对首字节异或当前轮常数 elif Nk>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 i<Nk: temp=[0]*4 for j in range(4): temp[j]=inKey[4*i+j] Keys.append(temp) i+=1 while i<4*(Nr+1): temp=Keys[i-1][:] if i%Nk==0: temp=s_line(roll(temp,1)) temp[0]=temp[0]^round_P[i/Nk-1] elif Nk>6 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 round<Nr: state=s_state(state) printState('r'+`round`+'sbox',state) state=rol_state(state) printState('r'+`round`+'rline',state) state=f_mix(mix_box,state) printState('r'+`round`+'mixCol',state) state=xorkey(state,groupKey(round)) printState('r'+`round`+'Key',state) round+=1 state=s_state(state) printState('r'+`round`+'sbox',state) state=rol_state(state) printState('r'+`round`+'rline',state) state=xorkey(state,groupKey(round)) printState('r'+`round`+'Key',state) out=outState(state) printState('Out',state) return out def is_line(line): '''对一行中的每个元素进行s变换''' l=line[:] for i in range(len(line)): l[i]=s_box.index(line[i]) return l def is_state(data): '''对一个数据的state块进行反s变换''' for i in range(4): data[i]=is_line(data[i]) return data def irol_state(data): '''对一个数据state块各行进反行变换''' data[1]=roll(data[1],3) data[2]=roll(data[2],2) data[3]=roll(data[3],1) return data def Decrypt( incode): '''解密过程,是加密过程的反过程''' if len(Keys)==0: print '还没有设置密码!' return [] if len(incode)!=16: print '错误的长度,每次只能处理16个字节' return [] #分组 state=[[0]*4,[0]*4,[0]*4,[0]*4] for i in range(4): for j in range(4): state[j][i]=incode[i*4+j] state=xorkey(state,groupKey(Nr)) round=Nr-1 while round>0: 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 <stdlib.h> # 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<N;j++)//循环加密N次,每次128bit { rij.encrypt((unsigned char *)lpsz,e_bit); lpsz+=16; e_bit+=16; } lpsz=lpsz-16*N;//明文指针回到明文头 e_bit=e_bit-16*N;//密文指针回到密文头 miwen.SetWindowText((LPCTSTR)e_bit);//显示密文 // TODO: 在此添加控件通知处理程序代码 } void Caesproject2Dlg::OnBnClickedButton2()//解密按钮按下 { CString c,s1;//c用来接收密文,s1用来接收密钥//length密文长度,N分组数都为成员变量 rijndael rij;//加密类的对象 miwen.GetWindowText(c);//从文本框取得密文 N=length/16;//取得组数做为循环次数 LPTSTR lpsz = new TCHAR[c.GetLength()+1]; _tcscpy(lpsz,c);//将密文拷贝到lpsz指向的内存 miyao.GetWindowText(s1);//从文本框获取密钥 LPTSTR k_bit = new TCHAR[256]; _tcscpy(k_bit,s1);//将密钥拷贝到k_bit指向的内存 rij.set_key((unsigned char *)k_bit,256);//设置密钥 unsigned char *m_bit;//用于指向解密后的明文 m_bit=new unsigned char [length]; for(int j=0;j<N;j++) //循环解密N次 { rij.decrypt((unsigned char *)lpsz,m_bit); m_bit+=16; lpsz+=16; } m_bit=m_bit-16*N;//解密后的明文指针回到明文头 lpsz=lpsz-16*N;//密文指针回到密文头 jmmingwen.SetWindowText((LPCTSTR)m_bit);//显示明文 // TODO: 在此添加控件通知处理程序代码 } /*说明:Caesproject2Dlg类为对话框类由MFC向导自动生成,需要在Caesproject2Dlg类添加两个成员变量(添加在头文件的类定义中): int length,N;//length密文长度,N分组数*/