Skip to content

Instantly share code, notes, and snippets.

@pornin
Created June 17, 2024 17:03
Show Gist options
  • Save pornin/46a1701d2c2e377cf4ffe9ecb6c94b32 to your computer and use it in GitHub Desktop.
Save pornin/46a1701d2c2e377cf4ffe9ecb6c94b32 to your computer and use it in GitHub Desktop.
Perfunctory implementation of the SHA-256 block processing function, for testing purposes only
class SHA256:
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
]
IV_standard = bytes.fromhex('6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19')
@classmethod
def ROTR(cls, n, x):
return (x >> n) | ((x << (32 - n)) & 0xFFFFFFFF)
@classmethod
def ROTL(cls, n, x):
return cls.ROTR(32 - n, x)
@classmethod
def Ch(cls, x, y, z):
return (x & y) ^ (~x & z)
@classmethod
def Maj(cls, x, y, z):
return (x & y) ^ (x & z) ^ (y & z)
@classmethod
def BigSigma_0(cls, x):
return cls.ROTR(2, x) ^ cls.ROTR(13, x) ^ cls.ROTR(22, x)
@classmethod
def BigSigma_1(cls, x):
return cls.ROTR(6, x) ^ cls.ROTR(11, x) ^ cls.ROTR(25, x)
@classmethod
def LittleSigma_0(cls, x):
return cls.ROTR(7, x) ^ cls.ROTR(18, x) ^ (x >> 3)
@classmethod
def LittleSigma_1(cls, x):
return cls.ROTR(17, x) ^ cls.ROTR(19, x) ^ (x >> 10)
@classmethod
def ProcessBlock(cls, initial_value, message_block):
initial_value = bytes(initial_value)
message_block = bytes(message_block)
assert len(initial_value) == 32
assert len(message_block) == 64
H = []
for i in range(0, 8):
H.append(int.from_bytes(initial_value[4*i : 4*i+4], byteorder='big'))
W = []
for i in range(0, 16):
W.append(int.from_bytes(message_block[4*i : 4*i+4], byteorder='big'))
for i in range(16, 64):
W.append((cls.LittleSigma_1(W[i - 2]) + W[i - 7] + cls.LittleSigma_0(W[i - 15]) + W[i - 16]) & 0xFFFFFFFF)
a = H[0]
b = H[1]
c = H[2]
d = H[3]
e = H[4]
f = H[5]
g = H[6]
h = H[7]
for i in range(0, 64):
T1 = (h + cls.BigSigma_1(e) + cls.Ch(e, f, g) + cls.K[i] + W[i]) & 0xFFFFFFFF
T2 = (cls.BigSigma_0(a) + cls.Maj(a, b, c)) & 0xFFFFFFFF
h = g
g = f
f = e
e = (d + T1) & 0xFFFFFFFF
d = c
c = b
b = a
a = (T1 + T2) & 0xFFFFFFFF
H[0] = (H[0] + a) & 0xFFFFFFFF
H[1] = (H[1] + b) & 0xFFFFFFFF
H[2] = (H[2] + c) & 0xFFFFFFFF
H[3] = (H[3] + d) & 0xFFFFFFFF
H[4] = (H[4] + e) & 0xFFFFFFFF
H[5] = (H[5] + f) & 0xFFFFFFFF
H[6] = (H[6] + g) & 0xFFFFFFFF
H[7] = (H[7] + h) & 0xFFFFFFFF
res = b''
for i in range(0, 8):
res += H[i].to_bytes(4, byteorder='big')
return res
# =========================================================================
# Example: using the standard IV value (from FIPS 180-4, section 5.3.3)
# and the given message block (random 55-byte string, with padding applied),
# the output should be:
# d283164a342a43160f18c4f384429d2ab25cdbb686853b7f620fe60a0476593a
IV = bytes.fromhex('6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19')
Mb = bytes.fromhex('f579a8ca3762fb77c02342f872114eb54752842a280f63f1b644d30e4e0710b347171d9031f56caf2f33872c9690365b080e475527b1ac8000000000000001B8')
print(SHA256.ProcessBlock(IV, Mb).hex())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment