简化版的DES(S-DES,https://en.wikipedia.org/wiki/Data_Encryption_Standard#Simplified_DES)是用来是用来教学的加密算法,可为理解DES、AES等加密算法打下基础。
本文通过Python实现S-DES加密算法。
在写程序的时候为了和资料上的函数名称一致,便于理解,有些函数、变量的命名不是很符合编程规范,也没有异常处理,主要用来加深理解S-DES算法。
算法说明
简化版DES(S-DES)的过程图示
加密过程
书写公式不方便,上述公式用字符表示如下
秘文 = IPinv(fK(SW(fK(IP(明文)))))
设计
输入
- 等待加密的文字,以十六进制字符串输入
- 10位秘钥,以列表输入
输出
加密的字符,以十六进制字符表示
接口设计
# 加密
ciphertext = sdesEncrypt('59616e67', [0,1,0,1,1,1,1,1,0,1])
# 解密
plaintext = sdesDecrypt(ciphertext, [0,1,0,1,1,1,1,1,0,1])
实现
秘钥(key)变换
S-DES需要一个10位(bit)的秘钥。每位表述为k1,k2,k3,k4,k5,k6,k7,k8,k9,k10。
用到Shift、P10和P8三个函数
P10:对10位秘钥重排
P8:选取其中8位排列
Shift:循环左移1位
P10函数如下
P10(k1,k2,k3,k4,k5,k6,k7,k8,k9,k10) = (k3,k5,k2,k7,k4,k10,k1,k9,k8,k6)
P10函数实现
def P10(key):
k = [None] + key
return [k[3],k[5],k[2],k[7],k[4],k[10],k[1],k[9],k[8],k[6]]
编程语言的列表索引是从0开始,这里表述行位操作时,索引
0
存放无用值None
,仅仅作占位用。从索引1
开始存放数据。比如4位二进制1011存放到在长度为5的列表[None, 1, 0, 1, 1]
中。这样的好处是代码的位索引和文本说明次序一致,后面代码有类似思路。
P8和Shift函数的实现
def P8(key):
k = [None] + key
return [k[6],k[3],k[7],k[4],k[8],k[5],k[10],k[9]]
def Shift(value):
return value[1:] + value[0:1]
S-DES 加解密1个字节
IP和 IPinv 函数
IP和IPinv互相为反函数,即:IPinv(IP(X)) = X,IP(IPinv(X)) = X
def IP(value):
v = [None] + value
return [k[2],k[6],k[3],k[1],k[4],k[8],k[5],k[7]]
def IPinv(value):
v = [None] + value
return [k[4],k[1],k[3],k[5],k[7],k[2],k[8],k[6]]
SW 函数
SW函数交换左边和右边的4位
def SW(value):
return value[4:] + value[:4]
fK函数
fK操作8位二进制,最左边的4位记作L,最右边的4位记作作R,表示如下
fK(L,R) = (L⊕F(R,K), R)
⊕是异或运算
K表示子秘钥K1或者K2
F函数是新引入的函数,可通过下面的两个步骤求出:
- 创建一个2行4列的表格P
对输入的4的位数字(n1,n2,n3,n4)
对输入K(k11,k12,k13,k14,k15,k16,k17,k18)
表格P的值如下
P(0,0) = n4 ⊕ k11 | P(0,1) = n1 ⊕ k12 | P(0,2) = n2 ⊕ k13 | P(0,3) = n3 ⊕ n14 |
---|---|---|---|
P(1,0) = n2 ⊕ k15 | P(1,1) = n3 ⊕ k16 | P(1,2) = n4 ⊕ k17 | P(1,3) = n1 ⊕ k18 |
- 在盒中查找
有两个盒子S0和S1,值如下
S0 = [[1, 0, 3, 2],
[3, 2, 1, 0],
[0, 2, 1, 3],
[3, 1, 3, 2]]
S1 = [[0, 1, 2, 3],
[2, 0, 1, 3],
[3, 0, 1, 0],
[2, 1, 0, 3]]
P(0,0)和P(0,1)组合成二进制的值row0,比如P(0,0) = 1, P(0,1) = 0,则row0 = 二进制10 = 2
P(0,2)和P(0,3)组合成二进制的值col0
P(1,0)和P(1,1)组合成二进制的值row1
P(1,2)和P(1,3)组合成二进制的值col1
S0的第row0行col0列的值S0(row0,col0)表示成两位二进制v1,v2,类似地S1(row1,col1)表示成两位二进制v3,v4
(v2,v4,v3,v1)组合成的二进制的值便是F函数的值
fK函数和F函数实现
def F(value, key):
val = lambda x, y: x * 2 + y
highLow = lambda x: (1 if x & 0b10 > 0 else 0), (x & 0b01)
P = [[0, 0, 0, 0],
[0, 0, 0, 0]]
n = [None] + value
k = [None] * 11 + key # 这里前面空11无用的值None
P[0][0] = n[4] ^ k[11]
P[0][1] = n[1] ^ k[12]
P[0][2] = n[2] ^ k[13]
P[0][3] = n[3] ^ k[14]
P[1][0] = n[2] ^ k[15]
P[1][1] = n[3] ^ k[16]
P[1][2] = n[4] ^ k[17]
P[1][3] = n[1] ^ k[18]
row0 = val(P[0][0], P[0][1])
col0 = val(P[0][2], P[0][3])
row1 = val(P[1][0], P[1][1])
col1 = val(P[1][2], P[1][3])
v1, v2 = highLow(S0[row0][col0])
v3, v4 = highLow(S1[row1][col1])
return [v2, v4, v3, v1]
def fK(value, key):
L = value[:4]
R = value[4:]
return list(map(lambda x:x[0]^x[1], zip(L, F(R))) + R
加密单字节函数 sdesEncryptByte
用上文出现过的公式:秘文 = IPinv(fK(SW(fK(IP(明文)))))
代码实现
def sdesEncryptByte(value, key):
K1 = P8(Shift(P10(key)))
K2 = P8(Shift(Shift(P10(key))))
fK1Value = fK(IP(value), K1)
swValue = SW(fK1Value)
fK2Value = fK(swValue, K2)
return IPinv(fK2Value)
解密单字节函数 sdesDecryptByte
代码实现
def sdesDecryptByte(value, key):
K1 = P8(Shift(P10(key)))
K2 = P8(Shift(Shift(P10(key))))
fK2Value = fK(IPinv(value), K2)
swValue = SW(fK2Value)
fK1Value = fK(swValue, K1)
return IP(fK1Value)
S-DES加密
使用了一个辅助函数makeByteList
,把十六进制文本转化成字节数组,每个字节又是一个数组。
def makeByteList(hextext):
l = list(hextext)
arr = []
for i in range(len(l) // 2):
arr += [ l[2*i] + l[2*i+1] ]
binaryList = []
for value in arr:
text = list(bin(int(value, 16))[2:].zfill(8))
binaryList += [text]
def sdesEncrypt(hextext, key):
encryptedString = ''
for value in makeByteList(hextext)
encryptedByte = sdesEncryptByte(value, key)
encryptedString += encryptedByte
def sdesDecrypt(hextext, key):
encryptedString = ''
for value in makeByteList(hextext)
encryptedByte = sdesDecryptByte(value, key)
encryptedString += encryptedByte
相关文章
密码学基础系列
参考文献
William Stallings. "Appendix G: Simplified DES"