#声明:本文创作内容含代码均为个人创作所得,允许学习、传阅,不得用于商业用途#
#本文包含AES从算法到硬件实现的全部#
#实践会让你更加透彻理解算法原理,才能让知识灌入大脑#
一 背景:
AES(Advanced Encryption Standard)是对称加密算法中的一种,应用于数据块加密,是美国国家标准与技术提供的一种数据加密算法,目前已成为国际上标准的数据加密算法;AES算法有三种子类型,分贝是AES-128,AES-192,AES-256,区别是加密的密钥长度分别是128bit,192bit,256bit,密钥越长,算法的安全性也越高,但每次加密的数据块的大小均是128bit,AES和国密SM4一样,均是多轮迭代算法,密钥长度越长,迭代次数越多;
密钥长度 | 数据块大小 | 迭代次数 | |
AES-128 | 128 bit | 128 bit | 10 |
AES-192 | 192 bit | 128 bit | 12 |
AES-256 | 256 bit | 128 bit | 14 |
二 算法说明:
整个算法迭代过程可以用下面公式来表示:
其中Xi是第i轮迭代的输入,Ki是第i轮迭代扩展后的密钥;F函数特定的逻辑运算;初始值X0为输入的128bit明文,n为迭代的总轮数。
F函数可以用这个公式来表达:F(a,b) = f( g ( h( t(x) ) ), b)
即,每一轮迭代过程分为四个步骤,t,h,g,f。
步骤一 t:字节替换层:
将输入的128bit,也就是16个字节,每个字节根据查找表替换出对应的字节;比如8bit数字 01001101,对应的16进制为4D,4作为查找表的行,D作为查找表的列,则替换的字节为e3。查找表如下:
再举个例子,输入数据为5a610dc3,则替换后的数据为:be4770eb
步骤二 h:行移位
把经过字节替换后的128bit依次分为16个字节,{B0,B1,B2... B15};为了更好理解行移位,我们将这16个字节放入到4x4的矩阵中,如下图,
B0 | B4 | B8 | B12 |
B1 | B5 | B9 | B13 |
B2 | B6 | B10 | B14 |
B3 | B7 | B11 | B15 |
按照以下变换规则:第一行位置不变,第二行循环右移3个格子,第三行循环右移2个格子,第四行循环右移1个格子,得到变换后的矩阵,如下图所示
B0 | B4 | B7 | B12 |
B5 | B9 | B13 | B1 |
B10 | B14 | B2 | B6 |
B15 | B3 | B7 | B11 |
用公式表示为{B0 B5 B10 B15 B4 B9 B14 B3 B7 B13 B2 B7 B12 B1 B6 B11} = h(B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15)
步骤三 g:列混淆
列混淆是一个线性变换,将矩阵的列给打乱,具体形式是一个矩阵乘法:
其中P系数如下:
02 | 03 | 01 | 01 |
01 | 02 | 03 | 01 |
01 | 01 | 02 | 03 |
03 | 01 | 01 | 02 |
B矩阵是步骤二变换后的矩阵,C矩阵排列方式如下表示:
C0 | C4 | C8 | C12 |
C1 | C5 | C9 | C13 |
C2 | C6 | C10 | C14 |
C3 | C7 | C11 | C15 |
举个例子:C0= 02*B0 + 03*B5 + 01*B10 + 01*B15
这里的加法和乘法是基于伽罗华域(2,8),至于伽罗华域运算,这是另外一个话题,大家可以先参考下其他的资料;这里我们直接给出运算方法:
01 对应的是0000 0001,即多项式f(x) = 1
02对应的是0000 0010, 即多项式f(x) = x
03 对应的是0000 0100,即多项式f(x) = x^2 = x * x = 02 * 02
我们假设被乘数字节为b,对应的二进制表示为{b7 b6 b5 b4 b3 b2 b1 b0}
01 * b = b
02 * b = {b6 b5 b4 b3 b2 b1 0} ,若b7=0
02 * b = {b6 b5 b4 b3 b2 b1 0} ^ {0 0 0 1 1 0 1 1} ,若b7=1
03 * b = 02 * 02* b = 02*(02 * b);需要经过两次迭代
步骤四 f:密钥加法层
密钥加法层就是将第三步的输出结果和当前轮的密钥直接异或
即 f(a,b) = a ^ b 其中a b均为128bit
下图表示的一轮迭代过程:
AES整体的架构如下图:
注意:第一轮迭代前,先进行密钥加法运算,最后一轮迭代少了列混淆运算
密钥迭代方法:
密钥函数迭代相对来说简单点, 公式如下:
公式中n指的是迭代的轮数,[]是密钥字索引,g()是一个逻辑运算,配合下图可能更容易理解
那图片中的g()函数表现形式是啥呢?
g函数的输入是32bit 用V来表示,也就是4个字节,我们用 V0,V1,V2,V3来表示,
g函数主要分为3个操作,第一步移位,V<<<8,V向左循环移位8位;第二位查找表替换,也就是将第一步得到的4个字节从查找表中得到对应的4个字节;第三步将第二步中得到第一个字节和数字n(8bit)异或,n当前迭代的轮数的gf(2,8)的八位二级制数;这里的密钥查找表和密文迭代的查找表一样
g函数架构如下图所示:
以上就是AES-128算法的所有过程;AES-196和AES-256迭代过程与128稍有不过,主要是密钥迭代和轮数
AES-196:由于其密钥长度为196,密钥迭代的公式多了一个K[4],
AES-256:由于其密钥长度为256,密钥迭代的公式多了K[4],K[5],K[6],K[7]
上诉公式中 h() 为32bit经过查找表替换
为了统一三种不同格式,我们在密钥迭代中,先迭代出所有密钥,然后将其拼起来。在每一轮明文迭代中依次取四个字(一共128bit),这样就可以保证不需要针对不同类型,开发出不同算法;
算法讲解就到这里,下面我们用python来实现其功能
三 python实现
--------今天就先到这里,快晚上12点了,打一局王者睡觉,明天还得上班-----------
我来更新代码了
该代码支持AES-128,AES-196, AES-256 三种模式
大家在代码中输入明文和密文的位宽要符合规定,否者会跑不通
大家在手动敲查找表的时候,一定要注意,我就因为这个浪费了点时间;由于代码中名字有点随意,大家结合算法原理来看:
class AesEncryption:
TABLE = {'00': '63', '01': '7c', '02': '77', '03': '7b', '04': 'f2', '05': '6b', '06': '6f', '07': 'c5',
'08': '30', '09': '01', '0a': '67', '0b': '2b', '0c': 'fe', '0d': 'd7', '0e': 'ab', '0f': '76',
'10': 'ca', '11': '82', '12': 'c9', '13': '7d', '14': 'fa', '15': '59', '16': '47', '17': 'f0',
'18': 'ad', '19': 'd4', '1a': 'a2', '1b': 'af', '1c': '9c', '1d': 'a4', '1e': '72', '1f': 'c0',
'20': 'b7', '21': 'fd', '22': '93', '23': '26', '24': '36', '25': '3f', '26': 'f7', '27': 'cc',
'28': '34', '29': 'a5', '2a': 'e5', '2b': 'f1', '2c': '71', '2d': 'd8', '2e': '31', '2f': '15',
'30': '04', '31': 'c7', '32': '23', '33': 'c3', '34': '18', '35': '96', '36': '05', '37': '9a',
'38': '07', '39': '12', '3a': '80', '3b': 'e2', '3c': 'eb', '3d': '27', '3e': 'b2', '3f': '75',
'40': '09', '41': '83', '42': '2c', '43': '1a', '44': '1b', '45': '6e', '46': '5a', '47': 'a0',
'48': '52', '49': '3b', '4a': 'd6', '4b': '83', '4c': '29', '4d': 'e3', '4e': '2f', '4f': '84',
'50': '53', '51': 'd1', '52': '00', '53': 'ed', '54': '20', '55': 'fc', '56': 'b1', '57': '5b',
'58': '6a', '59': 'cb', '5a': 'be', '5b': '39', '5c': '4a', '5d': '4c', '5e': '58', '5f': 'cf',
'60': 'd0', '61': 'ef', '62': 'aa', '63': 'fb', '64': '43', '65': '4d', '66': '33', '67': '85',
'68': '45', '69': 'f9', '6a': '02', '6b': '7f', '6c': '50', '6d': '3c', '6e': '9f', '6f': 'a8',
'70': '51', '71': 'a3', '72': '40', '73': '8f', '74': '92', '75': '9d', '76': '38', '77': 'f5',
'78': 'bc', '79': 'b6', '7a': 'da', '7b': '21', '7c': '10', '7d': 'ff', '7e': 'f3', '7f': 'd2',
'80': 'cd', '81': '0c', '82': '13', '83': 'ec', '84': '5f', '85': '97', '86': '44', '87': '17',
'88': 'c4', '89': 'a7', '8a': '7e', '8b': '3d', '8c': '64', '8d': '5d', '8e': '19', '8f': '73',
'90': '60', '91': '81', '92': '4f', '93': 'dc', '94': '22', '95': '2a', '96': '90', '97': '88',
'98': '46', '99': 'ee', '9a': 'b8', '9b': '14', '9c': 'de', '9d': '5e', '9e': '0b', '9f': 'db',
'a0': 'e0', 'a1': '32', 'a2': '3a', 'a3': '0a', 'a4': '49', 'a5': '06', 'a6': '24', 'a7': '5c',
'a8': 'c2', 'a9': 'd3', 'aa': 'ac', 'ab': '62', 'ac': '91', 'ad': '95', 'ae': 'e4', 'af': '79',
'b0': 'e7', 'b1': 'c8', 'b2': '37', 'b3': '6d', 'b4': '8d', 'b5': 'd5', 'b6': '4e', 'b7': 'a9',
'b8': '6c', 'b9': '56', 'ba': 'f4', 'bb': 'ea', 'bc': '65', 'bd': '7a', 'be': 'ae', 'bf': '08',
'c0': 'ba', 'c1': '78', 'c2': '25', 'c3': '2e', 'c4': '1c', 'c5': 'a6', 'c6': 'b4', 'c7': 'c6',
'c8': 'e8', 'c9': 'dd', 'ca': '74', 'cb': '1f', 'cc': '4b', 'cd': 'bd', 'ce': '8b', 'cf': '8a',
'd0': '70', 'd1': '3e', 'd2': 'b5', 'd3': '66', 'd4': '48', 'd5': '03', 'd6': 'f6', 'd7': '0e',
'd8': '61', 'd9': '35', 'da': '57', 'db': 'b9', 'dc': '86', 'dd': 'c1', 'de': '1d', 'df': '9e',
'e0': 'e1', 'e1': 'f8', 'e2': '98', 'e3': '11', 'e4': '69', 'e5': 'd9', 'e6': '8e', 'e7': '94',
'e8': '9b', 'e9': '1e', 'ea': '87', 'eb': 'e9', 'ec': 'ce', 'ed': '55', 'ee': '28', 'ef': 'df',
'f0': '8c', 'f1': 'a1', 'f2': '89', 'f3': '0d', 'f4': 'bf', 'f5': 'e6', 'f6': '42', 'f7': '68',
'f8': '41', 'f9': '99', 'fa': '2d', 'fb': '0f', 'fc': 'b0', 'fd': '54', 'fe': 'bb', 'ff': '16'}
GF_8 = {1: '01', 2: '02', 3: '04', 4: '08', 5: '10', 6: '20', 7: '40', 8: '80', 9: '1b', 10: '36'}
def gf_01(self,x):
return x
def gf_02(self,x):
num = int(x,16) & 0xFF
flag = num & 0x80
y = 0
if (flag) :
y = ((num << 1) & 0xFF) ^ 0x1b
else:
y = (num << 1) & 0xFF
return '{:02x}'.format(y)
def gf_03(self,x):
y0 = int(self.gf_01(x),16) & 0xFF
y1 = int(self.gf_02(x),16) & 0xFF
return '{:02x}'.format(y0 ^ y1)
def sub_byte(self, x):
y = ''
for i in range(4):
y = y + self.TABLE[x[2*i:2*i+2]]
return y
def shift_row(self, x):
y = [''] * 4
y[0] = x[0:2] + x[10:12] + x[20:22] + x[30:32]
y[1] = x[8:10] + x[18:20] + x[28:30] + x[6:8]
y[2] = x[16:18] + x[26:28] + x[4:6] + x[14:16]
y[3] = x[24:26] + x[2:4] + x[12:14] + x[22:24]
return y
def mix_columns(self, x):
y0 = int(self.gf_02(x[0:2]), 16) ^ int(self.gf_03(x[2:4]), 16) ^ int(self.gf_01(x[4:6]), 16) ^ int(self.gf_01(x[6:8]), 16) & 0xFF
y0_str = '{:02x}'.format(y0)
y1 = int(self.gf_01(x[0:2]), 16) ^ int(self.gf_02(x[2:4]), 16) ^ int(self.gf_03(x[4:6]), 16) ^ int(self.gf_01(x[6:8]), 16) & 0xFF
y1_str = '{:02x}'.format(y1)
y2 = int(self.gf_01(x[0:2]), 16) ^ int(self.gf_01(x[2:4]), 16) ^ int(self.gf_02(x[4:6]), 16) ^ int(self.gf_03(x[6:8]), 16) & 0xFF
y2_str = '{:02x}'.format(y2)
y3 = int(self.gf_03(x[0:2]), 16) ^ int(self.gf_01(x[2:4]), 16) ^ int(self.gf_01(x[4:6]), 16) ^ int(self.gf_02(x[6:8]), 16) & 0xFF
y3_str = '{:02x}'.format(y3)
y4 = int(self.gf_02(x[8:10]), 16) ^ int(self.gf_03(x[10:12]), 16) ^ int(self.gf_01(x[12:14]), 16) ^ int(self.gf_01(x[14:16]), 16) & 0xFF
y4_str = '{:02x}'.format(y4)
y5 = int(self.gf_01(x[8:10]), 16) ^ int(self.gf_02(x[10:12]), 16) ^ int(self.gf_03(x[12:14]), 16) ^ int(self.gf_01(x[14:16]), 16) & 0xFF
y5_str = '{:02x}'.format(y5)
y6 = int(self.gf_01(x[8:10]), 16) ^ int(self.gf_01(x[10:12]), 16) ^ int(self.gf_02(x[12:14]), 16) ^ int(self.gf_03(x[14:16]), 16) & 0xFF
y6_str = '{:02x}'.format(y6)
y7 = int(self.gf_03(x[8:10]), 16) ^ int(self.gf_01(x[10:12]), 16) ^ int(self.gf_01(x[12:14]), 16) ^ int(self.gf_02(x[14:16]), 16) & 0xFF
y7_str = '{:02x}'.format(y7)
y8 = int(self.gf_02(x[16:18]), 16) ^ int(self.gf_03(x[18:20]), 16) ^ int(self.gf_01(x[20:22]), 16) ^ int(self.gf_01(x[22:24]), 16) & 0xFF
y8_str = '{:02x}'.format(y8)
y9 = int(self.gf_01(x[16:18]), 16) ^ int(self.gf_02(x[18:20]), 16) ^ int(self.gf_03(x[20:22]), 16) ^ int(self.gf_01(x[22:24]), 16) & 0xFF
y9_str = '{:02x}'.format(y9)
y10 = int(self.gf_01(x[16:18]), 16) ^ int(self.gf_01(x[18:20]), 16) ^ int(self.gf_02(x[20:22]), 16) ^ int(self.gf_03(x[22:24]), 16) & 0xFF
y10_str = '{:02x}'.format(y10)
y11 = int(self.gf_03(x[16:18]), 16) ^ int(self.gf_01(x[18:20]), 16) ^ int(self.gf_01(x[20:22]), 16) ^ int(self.gf_02(x[22:24]), 16) & 0xFF
y11_str = '{:02x}'.format(y11)
y12 = int(self.gf_02(x[24:26]), 16) ^ int(self.gf_03(x[26:28]), 16) ^ int(self.gf_01(x[28:30]), 16) ^ int(self.gf_01(x[30:32]), 16) & 0xFF
y12_str = '{:02x}'.format(y12)
y13 = int(self.gf_01(x[24:26]), 16) ^ int(self.gf_02(x[26:28]), 16) ^ int(self.gf_03(x[28:30]), 16) ^ int(self.gf_01(x[30:32]), 16) & 0xFF
y13_str = '{:02x}'.format(y13)
y14 = int(self.gf_01(x[24:26]), 16) ^ int(self.gf_01(x[26:28]), 16) ^ int(self.gf_02(x[28:30]), 16) ^ int(self.gf_03(x[30:32]), 16) & 0xFF
y14_str = '{:02x}'.format(y14)
y15 = int(self.gf_03(x[24:26]), 16) ^ int(self.gf_01(x[26:28]), 16) ^ int(self.gf_01(x[28:30]), 16) ^ int(self.gf_02(x[30:32]), 16) & 0xFF
y15_str = '{:02x}'.format(y15)
y = ['']*4
y[0] = y0_str + y1_str + y2_str + y3_str
y[1] = y4_str + y5_str + y6_str + y7_str
y[2] = y8_str + y9_str + y10_str + y11_str
y[3] = y12_str + y13_str + y14_str + y15_str
return y
def add_round_key(self, x, key):
x0 = int(x[0], 16) ^ int(key[0], 16) & 0xFFFFFFFF
x0_str = '{:08x}'.format(x0)
x1 = int(x[1], 16) ^ int(key[1], 16) & 0xFFFFFFFF
x1_str = '{:08x}'.format(x1)
x2 = int(x[2], 16) ^ int(key[2], 16) & 0xFFFFFFFF
x2_str = '{:08x}'.format(x2)
x3 = int(x[3], 16) ^ int(key[3], 16) & 0xFFFFFFFF
x3_str = '{:08x}'.format(x3)
return [x0_str , x1_str , x2_str , x3_str]
def key_g_function(self, x, i):
if i<= 10:
rn = self.GF_8[i]
else:
rn = '00'
tmp = int(self.TABLE[x[2:4]], 16) ^ int(rn, 16) & 0xFF
return '{:02x}'.format(tmp) + self.TABLE[x[4:6]] + self.TABLE[x[6:8]] + self.TABLE[x[0:2]]
def key_h_function(self, x):
return self.TABLE[x[0:2]] + self.TABLE[x[2:4]] + self.TABLE[x[4:6]] + self.TABLE[x[6:8]]
def key_round(self, x, mode):
if mode == 0:
round_num = 10
key_len = 4 # 4 word ---32bit
elif mode == 1:
round_num = 12
key_len = 6
else:
round_num = 14
key_len = 8
y = [''] * key_len
key = [''] * key_len * (round_num+1)
for i in range(key_len):
key[i] = x[8*i:8*(i+1)]
for i in range(1, round_num+1):
for j in range(key_len):
if j == 0:
key[i* key_len + j] = '{:08x}'.format(int(key[(i-1)* key_len], 16) ^ int(self.key_g_function(key[i*key_len+j-1],i), 16) & 0xFFFFFFFF)
elif ((j == 4) & (mode == 2)):
key[i * key_len + j] = '{:08x}'.format(int(self.key_h_function(key[i * key_len + j - 1]), 16) ^ int(key[(i - 1) * key_len + j], 16) & 0xFFFFFFFF)
else:
key[i* key_len + j] = '{:08x}'.format(int(key[i* key_len + j -1], 16) ^ int(key[(i-1)* key_len+j], 16) & 0xFFFFFFFF)
#print(i*key_len+j, key[i* key_len + j])
for i in range(len(key)):
print(i, key[i])
return key
def encryption_iteration(self, x, key, mode):
if mode == 0:
round_num = 10
key_len = 4 # 4 word ---32bit
elif mode == 1:
round_num = 12
key_len = 6
else:
round_num = 14
key_len = 8
y = [''] * 4
for i in range(4):
y[i] = x[i*8:i*8+8]
key_list = self.key_round(key, mode)
y = self.add_round_key(y, key_list[0:key_len]) # pre add key
for i in range(round_num-1):
print(i,y)
for j in range(4):
y[j] = self.sub_byte(y[j])
y = self.shift_row(y[0]+y[1]+y[2]+y[3])
y = self.mix_columns(y[0]+y[1]+y[2]+y[3])
y = self.add_round_key(y, key_list[4*(i+1):4*(i+1)+4])
for j in range(4):
y[j] = self.sub_byte(y[j])
y = self.shift_row(y[0] + y[1] + y[2] + y[3])
y = self.add_round_key(y, key_list[4 * round_num:4 * round_num + 4])
return y
if __name__ == '__main__':
get_class = AesEncryption()
output = get_class.encryption_iteration('3243f6a8885a308d313198a2e0370734', '2b7e151628aed2a6abf7158809cf4f3c',0)
# output = get_class.encryption_iteration('3243f6a8885a308d313198a2e0370734', '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b', 1)
# output = get_class.encryption_iteration('3243f6a8885a308d313198a2e0370734',
'603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4', 2)
print(output)
四 Verilog硬件实现
Verilog考虑的比较多,还需要验证,后续等休假我来统一补充