crypto/crypto/ SHA256InPython
sha.py
#!/usr/bin/env python3
import struct
from consts import I,K
from shifts import right_rotate, right_shift
wordmask = 0xffffffff # for taking remainder modulo 2**32-1
def word_array_to_hex(words):
t = ""
for word in words:
t += f"{word:08x}"
return t
class sha256:
block_size_bytes = 512 // 8
def __init__(self):
self.H = [x for x in I]
self.data = bytearray()
self.length_in_bits = 0
def update(self,data):
# convert data from utf8 if needed
if isinstance(data,str):
data = data.encode("utf8")
# append data to self.data
# if len(self.data) >= 512, process blocks and consume until
# len(self.data) < 512
self.length_in_bits += len(data) * 8
self.data += data
while len(self.data) > sha256.block_size_bytes:
self._process_block()
def finalize(self,data):
self.update(data) # append data and process any blocks as needed
# pad with 0x80 0x00 .... 0x00 L where L is 8 bytes and is the length of the data in bits
# at least 9 bytes are added. so we need to take len(self.data) + 9 and round to next block_size_bytes
# so len(self.data) + 9 + x = 0 (mod 512//8)
# we know that len(self.data) < 512
# so x = (1024//8 - 9 - len(self.data)) % 512//8
# self.data += bytearray
num_zero_bytes = (2*sha256.block_size_bytes - 9 - len(self.data)) % sha256.block_size_bytes
len_be = struct.pack(">Q",self.length_in_bits)
pad = b'\x80' + (b'\x00' * num_zero_bytes) + len_be
self.data += pad
try:
assert(len(self.data)%64 == 0)
except AssertionError:
print("Assert error 1")
print(num_zero_bytes,len_be,len(self.data),len(self.data)%64)
raise
while len(self.data) > 0:
self._process_block() # we sometimes need todo this twice
return self.H
def _process_block(self):
block = self.data[:sha256.block_size_bytes] # remove one block from start of data
self.data = self.data[sha256.block_size_bytes:]
w = list(struct.unpack(">"+("L"*16),block)) + [0]*48
for i in range(16,64):
s0 = right_rotate(w[i-15], 7) ^ right_rotate(w[i-15], 18) ^ right_shift(w[i-15],3)
s1 = right_rotate(w[i-2], 17) ^ right_rotate(w[i-2], 19) ^ right_shift(w[i-2],10)
w[i] = (w[i-16] + s0 + w[i-7] + s1) & wordmask
a, b, c, d, e, f, g, h = self.H
for i in range(64):
s1 = right_rotate(e,6) ^ right_rotate(e,11) ^ right_rotate(e,25)
ch = (e & f) ^ ((~e) & g)
temp1 = (h + s1 + ch + K[i] + w[i]) & wordmask
s0 = right_rotate(a,2) ^ right_rotate(a,13) ^ right_rotate(a,22)
maj = (a & b) ^ (a & c) ^ (b & c)
temp2 = s0 + maj
h = g
g = f
f = e
e = d + temp1
d = c
c = b
b = a
a = temp1 + temp2
dH = [a,b,c,d,e,f,g,h]
for i in range(8):
self.H[i] = (self.H[i] + dH[i]) & wordmask
consts.py
We generate the constants from the definitions rather than using literals
#!/usr/bin/env python3
import math
# generate constants for sha256
def isprime(x):
for i in range(2,int(1+x**0.5)):
if x % i == 0:
return False
return True
def tofrac(x):
f = x - math.floor(x)
g = f * 2**32
h = int(g)
return h
def gen_consts():
a = list(filter(isprime,range(2,312)))[:64]
b = a[:8]
I = list(map(lambda t: tofrac(t**(1/2)),b))
K = list(map(lambda t: tofrac(t**(1/3)),a))
return (I,K)
I,K = gen_consts()
if __name__ == "__main__":
print("I")
for i,x in enumerate(I):
print(f"{i+1:2d} {x:08x}")
print("K")
for i,x in enumerate(K):
print(f"{i+1:2d} {x:08x}")
sha256sum.py
A simple implementation of sha256sum
#!/usr/bin/env python3
from sha import sha256
def word_array_to_hex(words):
t = ""
for word in words:
t += f"{word:08x}"
return t
def main():
import sys
args = sys.argv[1:]
for arg in args:
with open(arg,"rb") as f:
sha = sha256()
data = f.read()
h = sha.finalize(data)
hx = word_array_to_hex(h)
print(f"{hx} {arg}")
if __name__ == "__main__":
main()