Copyright © 2018 Joyce_BY
All rights reserved.
Contact by [email protected]
1、实现128-bit的AES加解密过程
2、python3.7.0,windows10
ef aes_encrypt(text,key): #ok
# input text is a block
state = create_block(text)
# k0 xor plaintext
state = xor(state,create_block(key[0]))
# 10 rounds
for i in range(1,11):
state = aes_encrypt_round(i,state,create_block(key[i]))
# return ciphertext in orginal order:
return create_block(state)
def aes_decrypt(text,key): #ok
# input text is a block
state = create_block(text)
# k0 xor plaintext
state = xor(state,create_block(key[10]))
# 10 rounds
for i in range(1,11):
state = aes_decrypt_round(i,state,create_block(key[10-i]))
# return plaintext
return create_block(state)
(0)此处明文和密钥长度都为128比特,则根据AES算法知道应该加密10轮;
(1)数据分组
将一开始传入AES算法模块的数据,以BYTE为单位如下图顺序排列为4*4的矩阵
def create_block(message): #ok
# arrange byte stream into a matrix
# message is a list of hex values
create = []
seq = [0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15]
# 16 bytes:
for i in range(16):
create.append(message[seq[i]])
return create
(1)初始轮:第0轮,我们将明文矩阵与初始密钥k0做异或运算,结果传入第一轮加密模块。
第1-10轮:对第i轮按顺序执行字节替换、字节移位、列混淆(除第10轮)、轮密钥相加;
CODE
def aes_encrypt_round(i,state,roundkey): #ok
# byte substitution
state = substitution(state,0)
# byte rotation
state = rotation(state,0)
# if not final round: mix column
if i != 10:
state = mixcolumn(state,0)
# round key xor
state = xor(state,roundkey)
return state
过程相同,顺序不同。
初始轮:第0轮,我们将明文矩阵与初始密钥k0做异或运算,结果传入第一轮加密模块。
第1-10轮:对第i轮按顺序执行字节移位、字节替换、轮密钥相加、列混淆(除第10轮);
CODE
def aes_decrypt_round(i,state, roundkey): #ok
# inv byte rotation
state = rotation(state,1)
# inv byte substitution
state = substitution(state,1)
# round key xor
state = xor(state,roundkey)
# if not first round: inv mix column
if i != 10:
state = mixcolumn(state,1)
return state
A、字节替代 Byte Substitution
非线性的字节替代,独立对每个字节进行运算,包括两个具体过程:
(a)对字节x在有限域GF(2^8)上求乘法逆,得到x-1,0x00的逆为其本身;
(b)在GF(2)上进行affine transform:y = Ax^-1+b,A为矩阵,b为向量,定值。
在实现的时候利用S盒打表实现,状态矩阵中的元素按照下面的方式映射为一个新的字节:把该字节的高4位作为行值,低4位作为列值,取出S盒中对应的行的元素作为输出。
解密通过逆S盒实现。
CODE
def substitution(state,mode): #ok
sbox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]
inv_sbox = [0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,
0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,
0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,
0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,
0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,
0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,
0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,
0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,
0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,
0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,
0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,
0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,
0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,
0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,
0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,
0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d]
ans = []
for i in range(len(state)):
# mode 0 for encryption, 1 for decryption:
if mode == 0:
ans.append(sbox[state[i]])
else:
ans.append(inv_sbox[state[i]])
return ans
B、字节移位 ByteRotation
如下图所示,第0-3行分别左移0-3个字节;
则解密过程将0-3行分别右移0-3个字节。
CODE
def rotation(state,mode): #ok
# shift left 0-3 bytes for each row
seq = [0,1,2,3,5,6,7,4,10,11,8,9,15,12,13,14]
# shift right 0-3 bytes for each row
inv_seq = [0,1,2,3,7,4,5,6,10,11,8,9,13,14,15,12]
ans = []
for i in range(16):
# 0 for enc, 1 for dec:
if mode == 0:
ans.append(state[seq[i]])
else:
ans.append(state[inv_seq[i]])
return ans
C、列混淆 MixColumns
在第1-9轮(第10轮不做!),用(逆)混淆矩阵左乘状态矩阵,加密混合矩阵如下:
def mixcolumn(state,mode): #ok
mix = [ 0x02,0x03,0x01,0x01,
0x01,0x02,0x03,0x01,
0x01,0x01,0x02,0x03,
0x03,0x01,0x01,0x02]
inv_mix = [ 0x0E,0x0B,0x0D,0x09,
0x09,0x0E,0x0B,0x0D,
0x0D,0x09,0x0E,0x0B,
0x0B,0x0D,0x09,0x0E]
# for each col, do matrix multiplication:
if mode == 0:
ans = matmul(mix,state)
else:
ans = matmul(inv_mix,state)
return ans
D、轮密钥相加 AddRoundKey
将第i轮的密钥与C中得到的矩阵做异或,传入下一轮。这里是将两个矩阵对应的比特分别做异或。
CODE
# 矩阵异或
def xor(state,key): #ok
# state xor key
ans = []
for i in range(len(key)):
ans.append(state[i] ^ key[i])
return ans
将一开始进行了转置的数据矩阵转置回来,输出数据流,即得到最终的密文/明文。
密钥总bit长度 = 分组长度 * (圈数+1),此实验计算得1408bits。
因为有10轮迭代,所以我们需要扩展获取另外10个轮密钥。
将初始密钥进行编排(同明文数据矩阵编排方法),每一列作为一个单位,则可得到k0-k3。利用k(i-1)和k(i-4)得到k(i),如果i是4的倍数 ,则需要将k(i-1)先做变换T,再与k(i-4)相加,否则直接将两列异或相加。
T变换包含两个操作:S盒变换和左移一个字节。
Rcon是预先指定的一组值。
流程如图:
CODE
def key_expansion(init_key): #ok
#key expansion
# use each col of initial key to get 4 32-bit keys
w = []
w.append(init_key)
# expand 10 more groups:
for group in range(10):
nextg = []
# every group has 4 cols:
# first col in the group:
trans = key_trans(w[group][12:16],group)
col0 = xor(w[group][0:4],trans)
nextg.extend(col0)
# 2,3,4 col:
col1 = xor(w[group][4:8],nextg[0:4])
nextg.extend(col1)
col2 = xor(w[group][8:12],nextg[4:8])
nextg.extend(col2)
col3 = xor(w[group][12:16],nextg[8:12])
nextg.extend(col3)
w.append(nextg)
return w
# 密钥变换
def key_trans(col,i): #ok
rcon = [ 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1B,0x36]
# key rotation, shift left by 1 byte
temp = col[0]
col[0] = col[1]
col[1] = col[2]
col[2] = col[3]
col[3] = temp
# key substitution:
col = substitution(col,0)
col[0] = col[0] ^ rcon[i]
return col
CODE
代码中,我将扩展的密钥存储在w[]这个list中,每个密钥存在一个list中,即w[]中存的是11个list,每个list就是一个密钥,所以只需要用下标取所需密钥即可。
AES是基于有限域GF(2^8)上的运算,具体关于有限域,这里不多讲,有空我会补上一个关于有限域上的运算及代码实现。
AES算法中需要用到的是有限域上的加法和乘法。
因为是二元域,所以AES的加法实际上是对两个数字进行异或运算,使用位运算符(^)即可。
我们在二进制的基础上进行乘法操作。我们仿照手算过程,例如a*b,对b从低位开始取,每次将a左移,即乘2之后与b该位相乘,得到一个临时结果,最后将所有临时结果相加即得到答案。
可以发现每个临时结果都是迭代多次乘00000010(2)之后的结果,所以其实只要实现有限域上乘2的过程即可。
CODE
def mul(a,b): #ok
# aes multiplication on GF(2^8):
temp = a
mod = 1
ans = 0
for i in range(8):
bi = b & mod
if bi == mod:
ans = ans ^ temp
temp = mul2(temp)
mod = mod * 2
return ans
# num * 2
def mul2(num): #ok
# return 2 * num
# shift left:
h = num & 0x80
if h == 128:
return (num-128) * 2 ^ 0x1B
else:
return num * 2
加密和解密都是从第一个数据块开始操作。
CODE
def aes_enc_cbc(m_list,key,iv): #ok
c = []
temp_iv = iv
while m_list:
text = m_list[:16]
m_list = m_list[16:]
text = xor(temp_iv,text)
temp_iv = aes_encrypt(text,key)
c.extend(temp_iv)
return c
def aes_dec_cbc(c_list,key,iv):
m = []
temp_iv = iv
while c_list:
cipher = c_list[:16]
c_list = c_list[16:]
dec = aes_decrypt(cipher,key)
message = xor(temp_iv,dec)
temp_iv = cipher
m.extend(message)
return m
1)对于数据流采用PKCS5 PADDING模式。 PKCS5 Padding模式将数据流长度补足成128bit的倍数。最后一个数据块缺x个字节,就在其后面补x个x,如果原数据流长度为128bit倍数,则添加16个字节的16。
CODE
def PKCS5Padding(s):
test = 16 - (len(s) % 16)
ans = s
if test != 0:
ans.extend([test]*test)
else:
ans.extend([16]*16)
return ans
2)对于密钥和iv,将不足的字节添加0
CODE
def zeroPadding(s):
test = 16 - (len(s) % 16)
ans = s
if test != 0:
ans.extend([0]*test)
return ans
Base64编码将字符转换为可见字符,将原数据流每6个bit为一组进行转换。实际上是将原本的3个8bit转化为4个6bit,如果不足3的倍数则添加0,最后的0转化为‘=‘
CODE
ef list2base64(li):
l = li
ans = []
lens = 3- len(li) % 3
if lens != 3:
l.extend([0]*lens)
table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
while l:
temp = l[:3]
l = l[3:]
a = (temp[0] << 16) + (temp[1] << 8) + temp[2]
a0 = (a >> 18) & 0x3F
a1 = (a >> 12) & 0x3F
a2 = (a >> 6) & 0x3F
a3 = a & 0x3F
ans.extend([table[a0],table[a1],table[a2],table[a3]])
ans = ans[:len(ans)-lens]
ans.extend(['=']*lens)
return ''.join(ans)
# change base64 to list:
def base642list(ss):
index = 0
for ch in ss:
if ch == '=':
index += 1
ans = []
s = ss
table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
while s:
temp = s[:4]
s = s[4:]
pos = []
for ch in temp:
pos.append(table.find(ch))
a1_1 = (pos[1] >> 4) & 0x03
a1_2 = pos[1] & 0x0F
a2_1 = (pos[2] >> 2) & 0x0F
a2_2 = (pos[2]) & 0x03
b0 = (pos[0] << 2) + a1_1
b1 = (a1_2 << 4) + a2_1
b2 = (a2_2 << 6) + pos[3]
ans.extend([b0,b1,b2])
ans = ans[:len(ans)-index]
return ans
至此,全部加解密工作完成,AES完成!