AES加密学习笔记(二)

 接下来讲点编程实现上应该注意的具体问题。

先回顾一下每轮运算的操作步骤:

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分组数*/


 

你可能感兴趣的:(c,加密,解密,Integer,BI,byte)