密码学实验老师让我们做手算DES加密的课堂解压小游戏,我这么懒怎么可能手算…(于是我悄悄用python了)
回到宿舍又把DES算法仔细研究并独立实现了一遍,经调试验证,每个细节的数据都和课本上给出的样例数据对照无误。
完整的每一步的数据在 张焕国的《密码学引论》第三版的66页,感兴趣的可以自行验证。
import numpy as np
""" code by bak """
class DES:
# 初始置换表,长度为64
IP_table = np.array([
58,50,42,34,26,18,10,2,
60,52,44,36,28,20,12,4,
62,54,46,38,30,22,14,6,
64,56,48,40,32,24,16,8,
57,49,41,33,25,17, 9,1,
59,51,43,35,27,19,11,3,
61,53,45,37,29,21,13,5,
63,55,47,39,31,23,15,7,
])
# 初始置换表的逆置换表,长度为64
IP_table_reverse = np.array([
40,8,48,16,56,24,64,32,
39,7,47,15,55,23,63,31,
38,6,46,14,54,22,62,30,
37,5,45,13,53,21,61,29,
36,4,44,12,52,20,60,28,
35,3,43,11,51,19,59,27,
34,2,42,10,50,18,58,26,
33,1,41, 9,49,17,57,25,
])
# f函数中的扩展运算E置换表,长度为48
E_table = np.array([
32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9,10,11,12,13,
12,13,14,15,16,17,
16,17,18,19,20,21,
20,21,22,23,24,25,
24,25,26,27,28,29,
28,29,30,31,32, 1,
])
# f函数中的置换运算P置换表,长度为32
P_table = np.array([
16, 7,20,21,
29,12,28,17,
1,15,23,26,
5,18,31,10,
2, 8,24,14,
32,27, 3, 9,
19,13,30, 6,
22,11, 4,25,
])
# 8个S盒
S_BOXS = np.array([
[
[14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7],
[0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8],
[4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0],
[15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13],
],[
[15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10],
[3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5],
[0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15],
[13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9],
],[
[10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8],
[13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1],
[13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7],
[1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12],
],[
[7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15],
[13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9],
[10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4],
[3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14],
],[
[2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9],
[14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6],
[4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14],
[11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3],
],[
[12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11],
[10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8],
[9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6],
[4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13],
],[
[4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1],
[13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6],
[1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2],
[6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12],
],[
[13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7],
[1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2],
[7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8],
[2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11],
]
])
# 子密钥生成算法中的置换选择1的置换表
substitution_table_1 = np.array([
57,49,41,33,25,17, 9,
1,58,50,42,34,26,18,
10, 2,59,51,43,35,27,
19,11, 3,60,52,44,36,
63,55,47,39,31,23,15,
7,62,54,46,38,30,22,
14, 6,61,53,45,37,29,
21,13, 5,28,20,12, 4,
])
# 子密钥生成算法中的置换选择2的置换表
substitution_table_2 = np.array([
14,17,11,24, 1, 5,
3,28,15, 6,21,10,
23,19,12, 4,26, 8,
16, 7,27,20,13, 2,
41,52,31,37,47,55,
30,40,51,45,33,48,
44,49,39,56,34,53,
46,42,50,36,29,32,
])
def IP(self, bit64):
""" 初始置换 """
return bit64[self.IP_table-1]
def IP_reverse(self, bit64):
""" 初始置换的逆置换 """
return bit64[self.IP_table_reverse-1]
def f(self, R, K):
""" 加密函数f,参数R为右32位待加密数据,参数K为本轮48位子密钥 """
def E(bit32):
""" 扩展运算,用来32位数据扩展成48位数据以便于后续步骤运算 """
return bit32[self.E_table-1]
def xor(bit48_a, bit48_b):
""" 逐位异或 """
return np.array([(int(bit_a != bit_b)) for (bit_a, bit_b) in zip(bit48_a, bit48_b)])
def S(bit48):
""" S盒置换,将48位输入数据经过8个S盒置换得到32位数据输出 """
bit32_After_Sub = np.ndarray(shape=(32,))
for i in range(0,48, 6):
# 48位数据每6位分成一组,共分成了8组
bit6 = bit48[i:i+6]
# 每一组的6位数据的第1、6比特组成行号
row_index = int("".join(map(str, bit6[[0,5]])), base=2)
# 每一组的6位数据的第2、3、4、5比特组成列号
col_index = int("".join(map(str, bit6[[1,2,3,4]])), base=2)
# 在当前组对应的那个S盒中依据行号列号取得对应数字
num_selected = self.S_BOXS[i//6][row_index][col_index]
# 将取得数字(该数字小于16)转化为4比特保存到输出结果向量
bit4 = np.array([(num_selected&(1<<i))>>i for i in range(4)])
bit4 = bit4[::-1]
bit32_After_Sub[(i//6)*4:(i//6)*4+4] = bit4
# 48位输入已经转成32位输出
return bit32_After_Sub
def P(bit32):
""" 置换运算P,将输入32数据打乱重排得到32位数据输出 """
return bit32[self.P_table-1]
# 输入32位数据经过扩展得到48位输出
bit48 = E(R)
# 得到的48位结果与48位子密钥逐位异或
bit48 = xor(bit48, K)
# print(f"key xor {bit48}")
# 得到的48位结果送入S盒中替换得到32位输出
bit32 = S(bit48)
# print(f"after S {bit32}")
# 得到的32位数据经过选择重排得到32位输出
output_bit32 = P(bit32)
# print(f"after P {output}")
return output_bit32.astype(np.int32)
def key_generator(self, init_key_bit64):
""" 子密钥生成算法,依据给定64位初始密钥生成16个48位子密钥 """
# 循环左移位数表
shift_table = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]
# 置换选择1
bit56 = init_key_bit64[self.substitution_table_1-1]
# 取得 C0, D0
C, D = bit56[:28], bit56[28:]
for i in range(16):
# 按照循环左移位数表取得当前应该位移的位数
shift = shift_table[i]
# C和D循环左移对应位数
C = np.concatenate([C[shift:], C[:shift]])
D = np.concatenate([D[shift:], D[:shift]])
# 拼接C、D
bit56 = np.concatenate([C, D])
# 置换选择2
sub_key_bit48 = bit56[self.substitution_table_2-1]
# 输出
yield sub_key_bit48
def encrypt_bit64(self, data_bit64, init_key_bit64):
""" 应用DES算法加密64比特数据,参数init_key_bit64为指定的64位初始密钥 """
def xor(bit32_a, bit32_b):
""" 逐位异或 """
return np.array([int(bit_a != bit_b) for (bit_a, bit_b) in zip(bit32_a, bit32_b)])
# 初始置换
bit64_After_IP = self.IP(data_bit64)
# print(bit64_After_IP)
# 取得L0, R0
L, R = bit64_After_IP[:32], bit64_After_IP[32:]
# print(f"L0 {''.join(map(str, L))}")
# print(f"R0 {''.join(map(str, R))}")
# 取得本*密钥并进行加密迭代
for i, key in enumerate(self.key_generator(init_key_bit64)):
if not i==16-1:
L, R = R, xor(L, self.f(R, key))
else:
# 按照DES规定,最后一轮不需要L、R不互换
R, L = R, xor(L, self.f(R, key))
# print(f"L{i+1} {L}")
# print(f"R{i+1} {R}")
# 拼接L、R
bit64 = np.concatenate([L, R])
# 初始置换的逆置换
output = self.IP_reverse(bit64)
# print(f"IP reverse {output}")
# 加密完成,输出
return output
def decrypt_bit64(self, data_bit64_encrypted, init_key_bit64):
""" 应用DES算法解密64比特数据,参数init_key_bit64为指定的64位初始密钥 """
def xor(bit32_a, bit32_b):
return np.array([int(bit_a != bit_b) for (bit_a, bit_b) in zip(bit32_a, bit32_b)])
bit64_After_IP = self.IP(data_bit64_encrypted)
# print(bit64_After_IP)
L, R = bit64_After_IP[:32], bit64_After_IP[32:]
# print(f"L0 {''.join(map(str, L))}")
# print(f"R0 {''.join(map(str, R))}")
# DES的解密相对于加密来说唯一的区别就是子密钥生成的顺序倒过来了
for i, key in enumerate(list(self.key_generator(init_key_bit64))[::-1]):
if not i==16-1:
L, R = R, xor(L, self.f(R, key))
else:
R, L = R, xor(L, self.f(R, key))
# print(f"L{i+1} {L}")
# print(f"R{i+1} {R}")
bit64 = np.concatenate([L, R])
output = self.IP_reverse(bit64)
# print(f"IP reverse {output}")
return output
if __name__ == "__main__":
data_bit64 = np.array(list(map(int, "00110000 00110001 00110010 00110011 00110100 00110101 00110110 00110111".replace(" ", ""))))
key_bit64 = np.array(list(map(int, "00110001 00110010 00110011 00110100 00110101 00110110 00110111 00111000".replace(" ", ""))))
des = DES()
data_bit64_encrypted = des.encrypt_bit64(data_bit64, key_bit64)
print(data_bit64_encrypted)
data_bit64_decrypted = des.decrypt_bit64(data_bit64_encrypted, key_bit64)
print(data_bit64_decrypted)