Last active
April 17, 2024 16:17
-
-
Save DavidBuchanan314/aa9ab4265fe402ab86399b5f9da82888 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
Thanks for the nicely written script, much appreciated.
Just a heads up to anyone working with collision attacks --- this SHA-256 collision involves 2 message blocks (1024-bit message) atypical of a single block collision (512-bit message) used in most of the previous works.