温馨提示:本文翻译自stackoverflow.com,查看原文请点击:其他 - Implementation of MD5 in Python
bit md5 python python-3.x

其他 - 在Python中实现MD5

发布于 2020-05-17 03:47:29

我在Wikipedia上关注有关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表示二进制,还是我错了?

任何帮助表示赞赏。

查看更多

提问者
cpy24
被浏览
11
ShadowRanger 2020-02-28 11:12

第一个明显的错误:

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

伪代码正在使用包含边界符号。range在上限处是独占的,因此您K假设长度为63个元素,而长度应该为64个元素。

您的最终循环也存在以下问题:

for i in range(63):

可能应该检查您的其他范围,以确保您没有在其他地方犯过类似的错误。

这也是完全错误的:

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

因为它不会按应有的方式填充输入字节,所以您的所有信息都是混乱的。有很多方法可以做到这一点(您可以使用'08b'假设所有字符的序数值都小于256 的格式字符串来工作,尽管这是一种效率很低的解决方案)。

也错了:

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

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

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

对您来说不幸的是,在Python中,ints是无限的精度;当计算产生的结果超过32位时,它们不会丢失溢出位,而MD5计算假定它们确实会溢出。因此,您将需要通过按位和-a进行大量掩蔽来模拟它& 0xffffffff

简而言之,用伪代码在纯Python中实现MD5是一个棘手的难题,因为伪代码依赖于诸如C的低级语言的限制而且您显然还没有足够小心地实现甚至可以自然转换为Python的位。