Search code examples
pythonpython-3.xhashsha256sha

Python SHA256 hash computation


I'm writing a SHA256 implementation in Python, padding, parsing and message schedule seem to work fine, my problem lies in the hash computation. Currently I'm just trying to calculate the working variable 'a'. This is the value I get (In hex)

5d6aebe0

Expected Output, according to this:

5D6AEBCD

Here is my code:

Set the working variables to the constants specified in FIPS-180

a = int('6a09e667', 16)
b = int('bb67ae85', 16)
c = int('3c6ef372', 16)
d = int('a54ff53a', 16)
e = int('510e527f', 16)
f = int('9b05688c', 16)
g = int('1f83d9ab', 16)
h = int('5be0cd19', 16)

Set the two important variables that depend on value t:

W = int('61626380', 16)
K = int('428a2f98', 16)

From the pseudo code on wikipedia:

S1 = hash.ROTR(e, 6) ^ hash.ROTR(e, 11) ^ hash.ROTR(e, 25)
ch = (e & f) ^ ((~e) & g)#((e1) & g)
temp1 = (h + S1 + ch + K + W) % math.pow(2, 32)
S0 = hash.ROTR(a, 2) ^ hash.ROTR(a, 13) ^ hash.ROTR(a, 22)
maj = (a & b) ^ (a & c) ^ (b & c)
temp2 = (S0 + maj) % math.pow(2, 32)
a = int((temp1 + temp2) % math.pow(2, 32))

ROTR function:

@staticmethod
def ROTR(x, n, w=32):
    return (x >> n) | (x << w - n)

Or, split into functions, like specified in FIPS-180 (Prodcuces the same output)

T1 = int((h + hash.SIGMA1(e) + hash.Ch(e, f, g) + hash.K[t] + W) % math.pow(2, 32))
T2 = int((hash.SIGMA0(a) + hash.Maj(a, b, c)) % math.pow(2, 32))
a = int((T1 + T2) % math.pow(2, 32))

Hash class:

@staticmethod
def ROTR(x, n, w=32):
    return (x >> n) | (x << w - n)
def SIGMA0(x):
    return hash.ROTR(x, 2) ^ hash.ROTR(x, 13) ^ hash.ROTR(x, 22)
def SIGMA1(x):
    return hash.ROTR(x, 6) ^ hash.ROTR(x, 11) ^ hash.ROTR(x, 25)
def Ch(x, y, z):
    return (x & y) ^ (~x & z)
def Maj(x, y, z):
    return (x & y) ^ (x & z) ^ (y & z)

I'm using Python 3 btw. Thanks in advance.


Solution

  • You need to add a lot more masking here to cut down overflowing bits. For example, your ROTR:

    def ROTR(x, n, w=32):
        return (x >> n) | (x << w - n)
    

    leaves all the high bits of x intact above the w boundary; you'd want to construct a mask from w and mask off the high bits, e.g.:

    def ROTR(x, n, w=32):
        return ((x >> n) | (x << w - n)) & ((1 << w) - 1)
    

    Similar masks are needed anytime you might have overflowed the assumed "register width". They can also replace the error-prone use of % math.pow(2, 32) you've got going on, changing:

    int((temp1 + temp2) % math.pow(2, 32))
    

    to:

    (temp1 + temp2) & ((1 << 32) - 1)
    

    or equivalently:

    (temp1 + temp2) % 2 ** 32
    

    This also needs to happen for bitwise negation where the overflow isn't as obvious: Python's ints are infinite precision, and bitwise negation of a non-negative value makes a negative value, effectively adding infinite 1 bits on the left (in the pseudo-two's complement behavior the language specifies). So ~x must become ~x & ((1 << 32) - 1) or the like to force it back to a positive value containing only the low 32 bits.

    This has to be done globally (so temp1 and temp2 are actually int, not float values when you compute with them). In general, math.pow is completely useless; you either want to use the ** operator (which doesn't coerce to float and executes more efficiently) or the built-in pow function (which is only needed for its three argument for doing efficient modular exponentiation).