Created
October 19, 2024 00:58
-
-
Save agrif/863464d7623b6a0cbbb0054c144ef966 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
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