MD4(SHA-1,SM3)算法的实现

 

一、实验目的

深度理解MD4(SHA-1,SM3)算法的工作原理,理解单向散列函数的应用,体会区块链挖矿的难度系数、加深对单向散列函数性质的理解。

二、实验器材

pycharm+python3.11

三、实验内容

1.实验要求:自己配置python环境,编写MD4(SHA-1, SM3)算法实现程序,运行MD4(SHA-1,SM3)程序,演示MD4(SHA-1,SM3)算法的计算过程。

(1)编写程序实现任意消息的填充分组。

 1 flag = 1 # 补1标志位,补一次1后置0
 2 while len(M) % 512 != 448:
 3 if flag:
 4 M += '1'
 5 flag = 0
 6 else:
 7 M += '0'
 8 M += "{:064b}".format(l) # 末尾64位写入长度,空余补位补0
 9 M = hex(int(M, 2))[2:] # 这种转换会用到很多次,2进制转16进制,M现在是一个16进制字符串,如'1342a2c12...'
10 Mn = [] # 存储每个32位的字,因为M中一个字符4位(16进制),所以取M中的8个为一组,按要求将M分割成16个32位的字,故这里8*4=32,32*16=512
11 for i in range(16):
12 Mn.append(M[8*i: 8*i+8])

(2)编写程序实现消息扩展将每个分组中的16个32比特字扩展为80个32比特字。

 1 W = ['' for _ in range(80)] # 存储80份扩展子明文
 2 for i in range(80):
 3 if 16 <= i <= 79:
 4 # 16-79要进行异或运算,这里先转换成十进制(W中存的是16进制字符串,str无法运算)
 5 temp = int(W[i-3], 16) ^ int(W[i-8],
 6 16) ^ int(W[i-14], 16) ^ int(W[i-16], 16)
 7 
 8 W[i] = hex(roll_left(temp, 1))[2:].zfill(8) # 循环左移1位
 9 else:
10 W[i] = Mn[i]

(3)分别编写程序实现循环移位、32比特与(或、非)运算、模232加运算,建议将这些程序作为独立的程序,在整个运算中调用这些程序完成单向散列函数的计算。

def roll_left(num, k):
"""循环左移函数

Parameters
----------
num : int
输入一个数字,2进制、10进制等均可
k : int
左移位数

Returns
-------
int
返回一个int结果
"""
num_bin = bin(num)[2:].zfill(
32)
out = num_bin[k % len(num_bin):]+num_bin[:k % len(num_bin)] # 注意预防溢出
return int(out, 2) # 二进制左移完成后转化成10进制输出

def ft(b, c, d, t):
"""ft为逻辑函数

Parameters
----------
b : int
B值
c : int
C值
d : int
D值
t : int
轮次

Returns
-------
int
运算结果
"""
if t >= 0 and t <= 19:
return ((b & c) | (~b & d))
elif t >= 20 and t <= 39:
return (b ^ c ^ d)
elif t >= 40 and t <= 59:
return ((b & c) | (b & d) | (d & c))
elif t >= 60 and t <= 79:
return (b ^ c ^ d)

for t in range(80):
tmp = B
B = A
A = ((((E + ft(tmp, C, D, t)) % (2**32)+roll_left(A, 5)) %
(2**32)+int(W[t], 16)) % (2**32)+K[t//20]) % (2**32) # 预防溢出进行取模运算
E = D
D = C
C = roll_left(tmp, 30)

(4)用第二步的80个32比特字迭代修改初始的单向散列函数值,并输出最后的单向散列函数值。

SHA-1杂凑算法实现:

  1 A, B, C, D, E = 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 # 常量
  2 K = [0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6] # 常量
  3 
  4 str = input("输入明文:\n").encode('utf-8') # 这里对输入明文进行编码,str为bytes型
  5 l = len(str)*8 # 每个字符8位
  6 # python中二进制是字符串,不保留高位的0,这里使用zfill补高位0,如十进制6->110->0110,M这里是用了一个很长的字符串如:'11001010100011...'来表示原始数据
  7 M = bin(int(str.hex(), 16))[2:].zfill(l)
  8 
  9 # [可选项] 下面的函数仅仅显示输入明文的ascii,末尾为长度,该段显示的是补位后的
 10 for i in range(64):
 11 if i < len(str):
 12 print(str[i], end=' ')
 13 elif i < len(str)+1:
 14 print('128', end=' ')
 15 elif i < 63:
 16 print('0', end=' ')
 17 else:
 18 print(l)
 19 
 20 flag = 1 # 补1标志位,补一次1后置0
 21 while len(M) % 512 != 448:
 22 if flag:
 23 M += '1'
 24 flag = 0
 25 else:
 26 M += '0'
 27 M += "{:064b}".format(l) # 末尾64位写入长度,空余补位补0
 28 M = hex(int(M, 2))[2:] # 这种转换会用到很多次,2进制转16进制,M现在是一个16进制字符串,如'1342a2c12...'
 29 Mn = [] # 存储每个32位的字,因为M中一个字符4位(16进制),所以取M中的8个为一组,按要求将M分割成16个32位的字,故这里8*4=32,32*16=512
 30 for i in range(16):
 31 Mn.append(M[8*i: 8*i+8])
 32 
 33 
 34 def roll_left(num, k):
 35 """循环左移函数
 36 
 37 Parameters
 38 ----------
 39 num : int
 40 输入一个数字,2进制、10进制等均可
 41 k : int
 42 左移位数
 43 
 44 Returns
 45 -------
 46 int
 47 返回一个int结果
 48 """
 49 num_bin = bin(num)[2:].zfill(
 50 32)
 51 out = num_bin[k % len(num_bin):]+num_bin[:k % len(num_bin)] # 注意预防溢出
 52 return int(out, 2) # 二进制左移完成后转化成10进制输出
 53 
 54 
 55 W = ['' for _ in range(80)] # 存储80份扩展子明文
 56 for i in range(80):
 57 if 16 <= i <= 79:
 58 # 16-79要进行异或运算,这里先转换成十进制(W中存的是16进制字符串,str无法运算)
 59 temp = int(W[i-3], 16) ^ int(W[i-8],
 60 16) ^ int(W[i-14], 16) ^ int(W[i-16], 16)
 61 
 62 W[i] = hex(roll_left(temp, 1))[2:].zfill(8) # 循环左移1位
 63 else:
 64 W[i] = Mn[i]
 65 
 66 
 67 def ft(b, c, d, t):
 68 """ft为逻辑函数
 69 
 70 Parameters
 71 ----------
 72 b : int
 73 B值
 74 c : int
 75 C值
 76 d : int
 77 D值
 78 t : int
 79 轮次
 80 
 81 Returns
 82 -------
 83 int
 84 运算结果
 85 """
 86 if t >= 0 and t <= 19:
 87 return ((b & c) | (~b & d))
 88 elif t >= 20 and t <= 39:
 89 return (b ^ c ^ d)
 90 elif t >= 40 and t <= 59:
 91 return ((b & c) | (b & d) | (d & c))
 92 elif t >= 60 and t <= 79:
 93 return (b ^ c ^ d)
 94 
 95 
 96 Ap, Bp, Cp, Dp, Ep = A, B, C, D, E # 暂存初始值
 97 for t in range(80):
 98 tmp = B
 99 B = A
100 A = ((((E + ft(tmp, C, D, t)) % (2**32)+roll_left(A, 5)) %
101 (2**32)+int(W[t], 16)) % (2**32)+K[t//20]) % (2**32) # 预防溢出进行取模运算
102 E = D
103 D = C
104 C = roll_left(tmp, 30)
105 
106 #print(f" round{t+1} : {hex(A)} {hex(B)} {hex(C)} {hex(D)} {hex(E)}\n")
107 A, B, C, D, E = (Ap+A) % (2**32), (Bp+B) % (2**32), (Cp +
108 C) % (2**32), (Dp+D) % (2**32), (Ep+E) % (2**32)
109 # 相加运算,因为python不像c/c++可以使用unsigned char_32直接限制位数,因此要对位数进行限制
110 print("明文对应的杂凑码:\n", hex(A), hex(B), hex(C), hex(D), hex(E))

加密结果:

SM-3杂凑算法实现:

  1 def rotation_left(x, num):
  2 # 循环左移
  3 num %= 32
  4 left = (x << num) % (2 ** 32)
  5 right = (x >> (32 - num)) % (2 ** 32)
  6 result = left ^ right
  7 return result
  8 
  9 
 10 def Int2Bin(x, k):
 11 x = str(bin(x)[2:])
 12 result = "0" * (k - len(x)) + x
 13 return result
 14 
 15 class SM3:
 16 
 17 def __init__(self):
 18 # 常量初始化
 19 self.IV = [0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600, 0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E]
 20 self.T = [0x79cc4519, 0x7a879d8a]
 21 self.maxu32 = 2 ** 32
 22 self.w1 = [0] * 68
 23 self.w2 = [0] * 64
 24 
 25 def ff(self, x, y, z, j):
 26 # 布尔函数FF
 27 result = 0
 28 if j < 16:
 29 result = x ^ y ^ z
 30 elif j >= 16:
 31 result = (x & y) | (x & z) | (y & z)
 32 return result
 33 
 34 def gg(self, x, y, z, j):
 35 # 布尔函数GG
 36 result = 0
 37 if j < 16:
 38 result = x ^ y ^ z
 39 elif j >= 16:
 40 result = (x & y) | (~x & z)
 41 return result
 42 
 43 def p(self, x, mode):
 44 result = 0
 45 # 置换函数P
 46 # 输入参数X的长度为32bit(=1个字)
 47 # 输入参数mode共两种取值:0和1
 48 if mode == 0:
 49 result = x ^ rotation_left(x, 9) ^ rotation_left(x, 17)
 50 elif mode == 1:
 51 result = x ^ rotation_left(x, 15) ^ rotation_left(x, 23)
 52 return result
 53 
 54 def sm3_fill(self, msg):
 55 # 填充消息,使其长度为512bit的整数倍
 56 # 输入参数msg为bytearray类型
 57 # 中间参数msg_new_bin为二进制string类型
 58 # 输出参数msg_new_bytes为bytearray类型
 59 length = len(msg) # msg的长度(单位:byte)
 60 l = length * 8 # msg的长度(单位:bit)
 61 
 62 num = length // 64
 63 remain_byte = length % 64
 64 msg_remain_bin = ""
 65 msg_new_bytes = bytearray((num + 1) * 64) ##填充后的消息长度,单位:byte
 66 
 67 # 将原数据存储至msg_new_bytes中
 68 for i in range(length):
 69 msg_new_bytes[i] = msg[i]
 70 
 71 # remain部分以二进制字符串形式存储
 72 remain_bit = remain_byte * 8 # 单位:bit
 73 for i in range(remain_byte):
 74 msg_remain_bin += "{:08b}".format(msg[num * 64 + i])
 75 
 76 k = (448 - l - 1) % 512
 77 while k < 0:
 78 # k为满足 l + k + 1 = 448 % 512 的最小非负整数
 79 k += 512
 80 
 81 msg_remain_bin += "1" + "0" * k + Int2Bin(l, 64)
 82 
 83 for i in range(0, 64 - remain_byte):
 84 str = msg_remain_bin[i * 8 + remain_bit: (i + 1) * 8 + remain_bit]
 85 temp = length + i
 86 msg_new_bytes[temp] = int(str, 2) # 将2进制字符串按byte为组转换为整数
 87 return msg_new_bytes
 88 
 89 def sm3_msg_extend(self, msg):
 90 # 扩展函数: 将512bit的数据msg扩展为132个字(w1共68个字,w2共64个字)
 91 # 输入参数msg为bytearray类型,长度为512bit=64byte
 92 for i in range(0, 16):
 93 self.w1[i] = int.from_bytes(msg[i * 4:(i + 1) * 4], byteorder="big")
 94 
 95 for i in range(16, 68):
 96 self.w1[i] = self.p(self.w1[i - 16] ^ self.w1[i - 9] ^ rotation_left(self.w1[i - 3], 15),
 97 1) ^ rotation_left(self.w1[i - 13], 7) ^ self.w1[i - 6]
 98 
 99 for i in range(64):
100 self.w2[i] = self.w1[i] ^ self.w1[i + 4]
101 
102 # 测试扩展数据w1和w2
103 # print("w1:")
104 # for i in range(0, len(self.w1), 8):
105 # print(hex(self.w1[i]))
106 # print("w2:")
107 # for i in range(0, len(self.w2), 8):
108 # print(hex(self.w2[i]))
109 
110 def sm3_compress(self, msg):
111 # 压缩函数
112 # 输入参数v为初始化参数,类型为bytes/bytearray,大小为256bit
113 # 输入参数msg为512bit的待压缩数据
114 
115 self.sm3_msg_extend(msg)
116 ss1 = 0
117 
118 A = self.IV[0]
119 B = self.IV[1]
120 C = self.IV[2]
121 D = self.IV[3]
122 E = self.IV[4]
123 F = self.IV[5]
124 G = self.IV[6]
125 H = self.IV[7]
126 
127 for j in range(64):
128 if j < 16:
129 ss1 = rotation_left((rotation_left(A, 12) + E + rotation_left(self.T[0], j)) % self.maxu32, 7)
130 elif j >= 16:
131 ss1 = rotation_left((rotation_left(A, 12) + E + rotation_left(self.T[1], j)) % self.maxu32, 7)
132 ss2 = ss1 ^ rotation_left(A, 12)
133 tt1 = (self.ff(A, B, C, j) + D + ss2 + self.w2[j]) % self.maxu32
134 tt2 = (self.gg(E, F, G, j) + H + ss1 + self.w1[j]) % self.maxu32
135 D = C
136 C = rotation_left(B, 9)
137 B = A
138 A = tt1
139 H = G
140 G = rotation_left(F, 19)
141 F = E
142 E = self.p(tt2, 0)
143 
144 # 测试IV的压缩中间值
145 # print("j= %d:" % j, hex(A)[2:], hex(B)[2:], hex(C)[2:], hex(D)[2:], hex(E)[2:], hex(F)[2:], hex(G)[2:], hex(H)[2:])
146 
147 self.IV[0] ^= A
148 self.IV[1] ^= B
149 self.IV[2] ^= C
150 self.IV[3] ^= D
151 self.IV[4] ^= E
152 self.IV[5] ^= F
153 self.IV[6] ^= G
154 self.IV[7] ^= H
155 
156 def sm3_update(self, msg):
157 # 迭代函数
158 # 输入参数msg为bytearray类型
159 # msg_new为bytearray类型
160 msg_new = self.sm3_fill(msg) # msg_new经过填充后一定是512的整数倍
161 n = len(msg_new) // 64 # n是整数,n>=1
162 
163 for i in range(0, n):
164 self.sm3_compress(msg_new[i * 64:(i + 1) * 64])
165 
166 def sm3_final(self):
167 digest_str = ""
168 for i in range(len(self.IV)):
169 digest_str += hex(self.IV[i])[2:]
170 
171 return digest_str.upper()
172 
173 def hashFile(self, filename):
174 with open(filename, 'rb') as fp:
175 contents = fp.read()
176 self.sm3_update(bytearray(contents))
177 return self.sm3_final()
178 
179 
180 if __name__ == "__main__":
181 msg1 = bytearray(b"abc")
182 print("msg1:", msg1.hex(), len(msg1))
183 
184 test1 = SM3()
185 test1.sm3_update(msg1)
186 digest1 = test1.sm3_final()
187 print("digest1:", digest1)
188 
189 msg2 = bytearray(b'abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd')
190 msg2 = bytes(msg2)
191 print("msg2:", msg2.hex(), len(msg2))
192 
193 test2 = SM3()
194 test2.sm3_update(msg2)
195 digest2 = test2.sm3_final()
196 print("digest2:", digest2)
197 
198 # 求大小为48M的文件的摘要,大约需要7分钟
199 # test3 = SM3()
200 # file_digest = test3.hashFile("test.exe")
201 # print('file_digest', file_digest)

加密结果:

posted @ 2024-01-19 18:09  棒打鲜橙不加冰  阅读(47)  评论(0编辑  收藏  举报