Skip to content

Instantly share code, notes, and snippets.

@AmosAidoo
Created July 15, 2021 11:39
Show Gist options
  • Save AmosAidoo/df92e402c3d1eca0fff6324b93e536e1 to your computer and use it in GitHub Desktop.
Save AmosAidoo/df92e402c3d1eca0fff6324b93e536e1 to your computer and use it in GitHub Desktop.
Implementation of MD5 from RFC 1321 in python
"""
The MD5 is an algorithm that takes as input a message
of arbitrary length and produces as output a 128-bit "fingerprint"
or "message digest" of the input. Source: RFC 1321
"""
# Constants
T = [0] * 64
T[ 0: 3+1] = [ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee ]
T[ 4: 7+1] = [ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501 ]
T[ 8:11+1] = [ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be ]
T[12:15+1] = [ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 ]
T[16:19+1] = [ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa ]
T[20:23+1] = [ 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8 ]
T[24:27+1] = [ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed ]
T[28:31+1] = [ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a ]
T[32:35+1] = [ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c ]
T[36:39+1] = [ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70 ]
T[40:43+1] = [ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05 ]
T[44:47+1] = [ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 ]
T[48:51+1] = [ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039 ]
T[52:55+1] = [ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 ]
T[56:59+1] = [ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 ]
T[60:63+1] = [ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 ]
# Number of shifts per round
s = [0] * 64
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 ]
def left_rotate(x, amount):
x &= 0xFFFFFFFF
return ((x<<amount) | (x>>(32-amount))) & 0xffffffff
# Functions for each round
def F(X, Y, Z):
return ((X) & (Y)) | (((~X)) & (Z))
def G(X, Y, Z):
return ((X) & (Z)) | ((Y) & ((~Z)))
def H(X, Y, Z):
return ((X) ^ (Y) ^ (Z))
def I(X, Y, Z):
return ((Y) ^ ((X) | ((~Z))))
def rounds(a, b, c, d, k, s, i, X, func):
t = (a + func(b, c, d) + int.from_bytes(X[k], "little") + T[i-1]) & 0xffffffff
a = (b + left_rotate(t, s)) & 0xffffffff
return a
import struct
def MD5(message: bytes) -> bytes:
"""
Implementing the MD5 algorithm from
rfc1321
"""
# Step 1. Append Padding Bits
message_bytes = bytearray(message)
message_length = (len(message_bytes) * 8) & 0xffffffffffffffff #The length of the message in bits
# Append a single byte
# Starting with bit 1
message_bytes.append(0x80)
while len(message_bytes)%64 != 56:
message_bytes.append(0)
# Step 2. Append Length
message_bytes += message_length.to_bytes(8, byteorder='little')
# Step 3. Initialize the MD5 buffer
a0 = 0x67452301
b0 = 0xefcdab89
c0 = 0x98badcfe
d0 = 0x10325476
# Step 4. Process message in 16-word blocks
# Process each 16-word block
for i in range(0, len(message_bytes), 64):
X = [None] * 16
for j in range(16):
X[j] = message_bytes[i+j*4 : i+j*4+4]
A = a0
B = b0
C = c0
D = d0
# Round 1
A = rounds(A, B, C, D, 0, 7, 1, X, F)
D = rounds(D, A, B, C, 1, 12, 2, X, F)
C = rounds(C, D, A, B, 2, 17, 3, X, F)
B = rounds(B, C, D, A, 3, 22, 4, X, F)
A = rounds(A, B, C, D, 4, 7, 5, X, F)
D = rounds(D, A, B, C, 5, 12, 6, X, F)
C = rounds(C, D, A, B, 6, 17, 7, X, F)
B = rounds(B, C, D, A, 7, 22, 8, X, F)
A = rounds(A, B, C, D, 8, 7, 9, X, F)
D = rounds(D, A, B, C, 9, 12, 10, X, F)
C = rounds(C, D, A, B, 10, 17, 11, X, F)
B = rounds(B, C, D, A, 11, 22, 12, X, F)
A = rounds(A, B, C, D, 12, 7, 13, X, F)
D = rounds(D, A, B, C, 13, 12, 14, X, F)
C = rounds(C, D, A, B, 14, 17, 15, X, F)
B = rounds(B, C, D, A, 15, 22, 16, X, F)
# Round 2
A = rounds(A, B, C, D, 1, 5, 17, X, G)
D = rounds(D, A, B, C, 6, 9, 18, X, G)
C = rounds(C, D, A, B, 11, 14, 19, X, G)
B = rounds(B, C, D, A, 0, 20, 20, X, G)
A = rounds(A, B, C, D, 5, 5, 21, X, G)
D = rounds(D, A, B, C, 10, 9, 22, X, G)
C = rounds(C, D, A, B, 15, 14, 23, X, G)
B = rounds(B, C, D, A, 4, 20, 24, X, G)
A = rounds(A, B, C, D, 9, 5, 25, X, G)
D = rounds(D, A, B, C, 14, 9, 26, X, G)
C = rounds(C, D, A, B, 3, 14, 27, X, G)
B = rounds(B, C, D, A, 8, 20, 28, X, G)
A = rounds(A, B, C, D, 13, 5, 29, X, G)
D = rounds(D, A, B, C, 2, 9, 30, X, G)
C = rounds(C, D, A, B, 7, 14, 31, X, G)
B = rounds(B, C, D, A, 12, 20, 32, X, G)
# Round 3
A = rounds(A, B, C, D, 5, 4, 33, X, H)
D = rounds(D, A, B, C, 8, 11, 34, X, H)
C = rounds(C, D, A, B, 11, 16, 35, X, H)
B = rounds(B, C, D, A, 14, 23, 36, X, H)
A = rounds(A, B, C, D, 1, 4, 37, X, H)
D = rounds(D, A, B, C, 4, 11, 38, X, H)
C = rounds(C, D, A, B, 7, 16, 39, X, H)
B = rounds(B, C, D, A, 10, 23, 40, X, H)
A = rounds(A, B, C, D, 13, 4, 41, X, H)
D = rounds(D, A, B, C, 0, 11, 42, X, H)
C = rounds(C, D, A, B, 3, 16, 43, X, H)
B = rounds(B, C, D, A, 6, 23, 44, X, H)
A = rounds(A, B, C, D, 9, 4, 45, X, H)
D = rounds(D, A, B, C, 12, 11, 46, X, H)
C = rounds(C, D, A, B, 15, 16, 47, X, H)
B = rounds(B, C, D, A, 2, 23, 48, X, H)
# Round 4
A = rounds(A, B, C, D, 0, 6, 49, X, I)
D = rounds(D, A, B, C, 7, 10, 50, X, I)
C = rounds(C, D, A, B, 14, 15, 51, X, I)
B = rounds(B, C, D, A, 5, 21, 52, X, I)
A = rounds(A, B, C, D, 12, 6, 53, X, I)
D = rounds(D, A, B, C, 3, 10, 54, X, I)
C = rounds(C, D, A, B, 10, 15, 55, X, I)
B = rounds(B, C, D, A, 1, 21, 56, X, I)
A = rounds(A, B, C, D, 8, 6, 57, X, I)
D = rounds(D, A, B, C, 15, 10, 58, X, I)
C = rounds(C, D, A, B, 6, 15, 59, X, I)
B = rounds(B, C, D, A, 13, 21, 60, X, I)
A = rounds(A, B, C, D, 4, 6, 61, X, I)
D = rounds(D, A, B, C, 11, 10, 62, X, I)
C = rounds(C, D, A, B, 2, 15, 63, X, I)
B = rounds(B, C, D, A, 9, 21, 64, X, I)
# Update the four register
a0 = (a0 + A)&0xffffffff
b0 = (b0 + B)&0xffffffff
c0 = (c0 + C)&0xffffffff
d0 = (d0 + D)&0xffffffff
# Step 5. Output
digest = ""
for value in [a0, b0, c0, d0]:
digest += value.to_bytes(4, "little").hex()
return digest
# Tests
print(MD5(b""))
print(MD5(b"a"))
print(MD5(b"abc"))
print(MD5(b"message digest"))
print(MD5(b"abcdefghijklmnopqrstuvwxyz"))
print(MD5(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"))
print(MD5(b"12345678901234567890123456789012345678901234567890123456789012345678901234567890"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment