Created
July 15, 2021 11:39
-
-
Save AmosAidoo/df92e402c3d1eca0fff6324b93e536e1 to your computer and use it in GitHub Desktop.
Implementation of MD5 from RFC 1321 in python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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