MD5在Python中的实现



我在维基百科上关注一个关于MD5实现的页面。伪代码直接嵌入到链接中。

然而,当我将伪代码翻译成python时,它给出了一个非常奇怪的输出,与演示中显示的输出完全不同。

md5.py

import math

def leftrotate(x, c):
return (x << c) or (x >> (32 - c))

# All variables are unsigned 32 bit and wrap modulo 2^32 when calculating
s = [None] * 64
K = [None] * 64
# s specifies the per-round shift amounts
s[0:16] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22]
s[16:32] = [5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20]
s[32:48] = [4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23]
s[48:64] = [6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]
# Use binary integer part of the sines of integers (Radians) as constants:
K = [math.floor(math.pow(2, 32) * math.fabs(math.sin(i + 1))) for i in range(64)]
# Initialize variables
a0 = 0x67452301
b0 = 0xefcdab89
c0 = 0x98badcfe
d0 = 0x10325476
msg0 = "The quick brown fox jumps over the lazy dog"
# Converting String to binary
msg = ''.join(format(ord(i), 'b') for i in msg0)
# Pre-processing: adding a single bit
#   The input bytes are considered as bits strings,
#   where the first bit is the most significant bit of the byte
message = list(msg)
message.append("1")
# Pre-processing: padding with zeros
for i in range(447 - len(msg)):
message.append("0")
for i in range(64):
message.append("64")
msg = "".join(message)
M = []
for i in range(0, 512, 32):
M.append("".join(message[i:i + 32]))
# Initialize hash value for this chunk
A = a0
B = b0
C = c0
D = d0
for i in range(64):
F = 0
g = 0
if 0 <= i <= 15:
F = D ^ (B and (C ^ D))
g = i
elif 16 <= i <= 31:
F = C ^ (D and (B ^ C))
g = (5 * i + 1) % 16
elif 32 <= i <= 47:
F = B ^ C ^ D
g = (3 * i + 5) % 16
elif 48 <= i <= 63:
F = C ^ (B or (not D))
g = (7 * i) % 16
# Be wary of the below definitions of a, b, c, d
F = F + A + K[i] + int(M[g])
A = D
D = C
C = B
B = B + leftrotate(F, s[i])
a0 += A
b0 += B
c0 += C
d0 += D
print(a0 + b0 + c0 + d0)

输出

(input = msg0 = "The quick brown fox jumps over the lazy dog")
64753135551430969455044517516606091785810184138594829859667366632969211689605539951611300227364936455768076694404884226395355419
03996976374438250472707827439981815374596773986313857734975864212023704601385676211761628136173288119161166663698645808505752643
4

请有人向我详细解释一下这句话:

将块分解为16个32位字M[j],0≤j≤15

我当时很困惑,因为我认为message应该作为字符串输入,但将其转换为二进制是行不通的。我想,在页面上,短语bit的意思是二进制,还是我错了?

感谢您的帮助。

第一个明显错误:

K = [math.floor(math.pow(2, 32) * math.fabs(math.sin(i + 1))) for i in range(63)]

伪代码使用包含边界表示法;range在上界上是互斥的,所以您只创建了一个63个元素长的K,而它应该是64个元素长。

与您的最后一个循环相同的问题:

for i in range(63):

也许应该检查你的其他边界,以确保你没有在其他地方犯类似的错误。

这也是完全错误的:

msg = ''.join(format(ord(i), 'b') for i in msg0) 

因为它没有按照应该的方式填充输入字节,所以你的比特都是混乱的。有很多方法可以做到这一点(假设所有字符的序数都低于256,您可能会使用'08b'格式字符串来实现它,尽管这是一个效率相当低的解决方案(。

同样错误:

for i in range(64):
message.append("64")

您应该将原始长度附加在位mod2**64中,而不是重复64次的数字64(我不知道您是从哪里想到的(。

对于您的最后一个问题,为了解释"将块分解为16个32位单词M[j],0≤j≤15","32位单词"的意思是"由32个二进制位组成的整数"。对于一个简单的例子,将0b100100001111分解为三个四位字将产生0b1001, 0b0000, 0b1111

不幸的是,在Python中,int是无限精确的;当计算产生超过32位的结果时,它们不会丢弃溢出位,MD5计算假设它们会丢弃溢出位。因此,您需要通过& 0xffffffff进行大量掩码模拟。

简而言之,从伪代码在普通Python中实现MD5是一件非常痛苦的事情,因为伪代码依赖于等低级语言的限制。很明显,即使是那些可以自然转换为Python的部分,您在实现时也不够小心。

最新更新