Skip to content

Instantly share code, notes, and snippets.

@agrif
Created October 19, 2024 00:58
Show Gist options
  • Save agrif/863464d7623b6a0cbbb0054c144ef966 to your computer and use it in GitHub Desktop.
Save agrif/863464d7623b6a0cbbb0054c144ef966 to your computer and use it in GitHub Desktop.
import hashlib
import os
import struct
class Sha1:
def __init__(self):
self.state = [
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0,
]
self.chunk = [0] * 16
def load_chunk(self, chunk):
self.chunk = list(struct.unpack('>16I', chunk))
@classmethod
def leftrotate(cls, v, bits):
a = (v << bits) & 0xffff_ffff
b = (v >> (32 - bits)) & 0xffff_ffff
return a | b
def get_word(self, i):
return self.chunk[i % 16]
def message_schedule(self):
for i in range(80):
if i < 16:
yield self.chunk[i]
else:
v = self.get_word(i - 3) ^ self.get_word(i - 8) ^ self.get_word(i - 14) ^ self.get_word(i - 16)
v = self.leftrotate(v, 1)
self.chunk[i % 16] = v
yield v
@classmethod
def chunks(cls, data):
chunksize = 64
i = 0
endmarked = False
end = False
while not end:
chunk = data[i:i+64]
if not endmarked and len(chunk) < chunksize:
chunk += b'\x80'
endmarked = True
while len(chunk) < chunksize and len(chunk) != chunksize - 8:
chunk += b'\x00'
if len(chunk) == chunksize - 8:
# length in bits
chunk += struct.pack('>Q', len(data) * 8)
end = True
i += len(chunk)
assert len(chunk) == chunksize
yield chunk
def process(self):
state = self.state
for i, m in enumerate(self.message_schedule()):
a, b, c, d, e = state
if i < 20:
f = (b & c) | (~b & d)
k = 0x5A827999
elif i < 40:
f = b ^ c ^ d
k = 0x6ED9EBA1
elif i < 60:
f = (b & c) | (b & d) | (c & d)
k = 0x8F1BBCDC
else:
f = b ^ c ^ d
k = 0xCA62C1D6
v = self.leftrotate(a, 5) + f + e + k + m
v &= 0xffff_ffff
state = [v] + state[:-1]
state[2] = self.leftrotate(state[2], 30)
self.state = [(i + j) & 0xffff_ffff for i, j in zip(self.state, state)]
def digest(self):
return struct.pack('>5I', *self.state)
def hexdigest(self):
s = ''
for w in self.state:
s += '{:08x}'.format(w)
return s
if __name__ == '__main__':
for num in range(128):
message = os.urandom(num)
h = Sha1()
for chunk in h.chunks(message):
h.load_chunk(chunk)
h.process()
ours = h.hexdigest()
real = hashlib.sha1(message).hexdigest()
if ours != real:
print('fail, data', repr(message))
print(ours, '!=', real)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment