Last active
October 31, 2024 16:17
-
-
Save prokls/41e82472bd4968720d1482f81235e0ac to your computer and use it in GitHub Desktop.
Pure python3 implementation of SHA256 based on @thomdixon's pysha2
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
#!/usr/bin/python3 | |
__base__ = 'https://github.com/thomdixon/pysha2/blob/master/sha2/sha256.py' | |
__author__ = 'Lukas Prokop' | |
__license__ = 'MIT' | |
import copy | |
import struct | |
import binascii | |
F32 = 0xFFFFFFFF | |
_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] | |
_h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, | |
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19] | |
def _pad(msglen): | |
mdi = msglen & 0x3F | |
length = struct.pack('!Q', msglen << 3) | |
if mdi < 56: | |
padlen = 55 - mdi | |
else: | |
padlen = 119 - mdi | |
return b'\x80' + (b'\x00' * padlen) + length | |
def _rotr(x, y): | |
return ((x >> y) | (x << (32 - y))) & F32 | |
def _maj(x, y, z): | |
return (x & y) ^ (x & z) ^ (y & z) | |
def _ch(x, y, z): | |
return (x & y) ^ ((~x) & z) | |
class SHA256: | |
_output_size = 8 | |
blocksize = 1 | |
block_size = 64 | |
digest_size = 32 | |
def __init__(self, m=None): | |
self._counter = 0 | |
self._cache = b'' | |
self._k = copy.deepcopy(_k) | |
self._h = copy.deepcopy(_h) | |
self.update(m) | |
def _compress(self, c): | |
w = [0] * 64 | |
w[0:16] = struct.unpack('!16L', c) | |
for i in range(16, 64): | |
s0 = _rotr(w[i-15], 7) ^ _rotr(w[i-15], 18) ^ (w[i-15] >> 3) | |
s1 = _rotr(w[i-2], 17) ^ _rotr(w[i-2], 19) ^ (w[i-2] >> 10) | |
w[i] = (w[i-16] + s0 + w[i-7] + s1) & F32 | |
a, b, c, d, e, f, g, h = self._h | |
for i in range(64): | |
s0 = _rotr(a, 2) ^ _rotr(a, 13) ^ _rotr(a, 22) | |
t2 = s0 + _maj(a, b, c) | |
s1 = _rotr(e, 6) ^ _rotr(e, 11) ^ _rotr(e, 25) | |
t1 = h + s1 + _ch(e, f, g) + self._k[i] + w[i] | |
h = g | |
g = f | |
f = e | |
e = (d + t1) & F32 | |
d = c | |
c = b | |
b = a | |
a = (t1 + t2) & F32 | |
for i, (x, y) in enumerate(zip(self._h, [a, b, c, d, e, f, g, h])): | |
self._h[i] = (x + y) & F32 | |
def update(self, m): | |
if not m: | |
return | |
self._cache += m | |
self._counter += len(m) | |
while len(self._cache) >= 64: | |
self._compress(self._cache[:64]) | |
self._cache = self._cache[64:] | |
def digest(self): | |
r = copy.deepcopy(self) | |
r.update(_pad(self._counter)) | |
data = [struct.pack('!L', i) for i in r._h[:self._output_size]] | |
return b''.join(data) | |
def hexdigest(self): | |
return binascii.hexlify(self.digest()).decode('ascii') | |
if __name__ == '__main__': | |
def check(msg, sig): | |
m = SHA256() | |
m.update(msg.encode('ascii')) | |
print(m.hexdigest() == sig) | |
tests = { | |
"": | |
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', | |
"a": | |
'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', | |
"abc": | |
'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', | |
"message digest": | |
'f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650', | |
"abcdefghijklmnopqrstuvwxyz": | |
'71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73', | |
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789": | |
'db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0', | |
("12345678901234567890123456789012345678901234567890123456789" | |
"012345678901234567890"): | |
'f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e', | |
"00baf6626abc2df808da36a518c69f09b0d2ed0a79421ccfde4f559d2e42128b": | |
'b835e56173be2b5b7177d71bf02850dc578ac855ac60f91a108eec253bd5a543' | |
} | |
for inp, out in tests.items(): | |
check(inp, out) |
@thomdixon Thanks for the details and the fix! I applied to last one to a new revision
@CesarCastellonE Thanks, now I was able to reproduce it!
@manuelinfosec Not sure what you mean. The algorithm and its subroutines themselves are well-documented → https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf I am not sure what you are looking for.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The annoying part of this code is the lack of comments.