Forked from DavidBuchanan314/31_round_sha256_poc.py
Created
February 7, 2025 12:24
-
-
Save ChypherC0d3/e64a0335989287d684be2d8f2f22ce10 to your computer and use it in GitHub Desktop.
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
""" | |
31-round sha256 collision. | |
Not my research, just a PoC script I put together with numbers plugged in from the slide at | |
https://twitter.com/jedisct1/status/1772647350554464448 from FSE2024 | |
SHA256 impl follows FIPS 180-4 | |
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | |
""" | |
# Section 2.2.2 | |
def rotr(x, n): | |
return ((x >> n) | (x << (32 - n))) & 0xFFFFFFFF | |
# Section 2.2.2 | |
def shr(x, n): | |
return x >> n | |
# Section 4.1.2 (4.2) | |
def Ch(x, y, z): | |
return (x & y) ^ (~x & z) | |
# Section 4.1.2 (4.3) | |
def Maj(x, y, z): | |
return (x & y) ^ (x & z) ^ (y & z) | |
# Section 4.1.2 (4.4) | |
def S0(x): | |
return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22) | |
# Section 4.1.2 (4.5) | |
def S1(x): | |
return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25) | |
# Section 4.1.2 (4.6) | |
def s0(x): | |
return rotr(x, 7) ^ rotr(x, 18) ^ shr(x, 3) | |
# Section 4.1.2 (4.7) | |
def s1(x): | |
return rotr(x, 17) ^ rotr(x, 19) ^ shr(x, 10) | |
""" | |
Section 4.2.2 - SHA-256 Constants | |
""" | |
K = [ | |
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, | |
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, | |
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, | |
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, | |
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, | |
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, | |
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, | |
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, | |
] | |
# Section 5.2.1 | |
def word_iterator(m): | |
for i in range(0, len(m), 32//8): | |
yield int.from_bytes(m[i:i+32//8], "big") | |
# Section 5.3.3 | |
initial_H = [ | |
0x6a09e667, | |
0xbb67ae85, | |
0x3c6ef372, | |
0xa54ff53a, | |
0x510e527f, | |
0x9b05688c, | |
0x1f83d9ab, | |
0x5be0cd19, | |
] | |
# Section 6.2 | |
def sha256_31(blocks): | |
# 6.2.1, 1) Initialize H | |
H = initial_H.copy() | |
NUM_ROUNDS = 31 # XXX: normally 64! | |
# 6.2.2 - SHA-256 Hash Computation | |
for block in blocks: | |
# 1. Prepare the message schedule | |
W = list(word_iterator(block)) | |
for t in range(16, NUM_ROUNDS): | |
W.append((s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16]) & 0xFFFFFFFF) | |
# 2. Initialize the eight working variables | |
a, b, c, d, e, f, g, h = H | |
# 3. | |
for t in range(NUM_ROUNDS): | |
T1 = h + S1(e) + Ch(e, f, g) + K[t] + W[t] | |
T2 = S0(a) + Maj(a, b, c) | |
h = g | |
g = f | |
f = e | |
e = (d + T1) & 0xFFFFFFFF | |
d = c | |
c = b | |
b = a | |
a = (T1 + T2) & 0xFFFFFFFF | |
# 4. Calculate the next Hash value | |
for i, x in enumerate((a, b, c, d, e, f, g, h)): | |
H[i] = (H[i] + x) & 0xFFFFFFFF | |
# convert the result to bytes | |
M = b"" | |
for word in H: | |
M += word.to_bytes(32//8, "big") | |
return M | |
if __name__ == "__main__": | |
m0 = bytes.fromhex(""" | |
c32aef52 512294ba 9db5ed8c 8c8c88ed b2de2765 63a2d14e ec7619cc 93b21182 | |
e5050f50 f0839b60 7b1ee176 aaa06d68 c462343c 67898962 9558f495 04281f2c | |
""") | |
m1a = bytes.fromhex(""" | |
5d0f5ae6 05e98311 8fa3c73a 9af8c49d a2bf31f7 de547b67 5baecee3 da0d8c94 | |
e4c19564 f682d45c f7c57698 f871f9b5 f14469b7 fc28eb0c 2d76db75 043fe071 | |
""") | |
m1b = bytes.fromhex(""" | |
5d0f5ae6 05e98311 8fa3c73a 9af8c49d a2bf31f7 de548b61 5b8e46f2 8a1dd69a | |
bcc08464 f6825458 f7c57698 f871f9b5 f14469b7 fc28eb0c 2d76db75 043fe071 | |
""") | |
assert(m1a != m1b) # they're different | |
h1 = sha256_31([m0, m1a]) | |
print(h1.hex()) | |
h2 = sha256_31([m0, m1b]) | |
print(h2.hex()) | |
assert(h1 == h2) # they're the same!!! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment