writeups/2023/dcq/sha2extend.py

39 lines
1.4 KiB
Python

import binascii, ctypes, math, struct
class sha2_ctx(ctypes.Structure):
_fields_ = [("state", ctypes.c_uint32 * 8),
("count", ctypes.c_uint64),
("buf", ctypes.c_byte * 64)]
sodium = ctypes.CDLL("libsodium.so")
sodium.crypto_hash_sha256_init.argtypes = [ctypes.POINTER(sha2_ctx)]
sodium.crypto_hash_sha256_init.restype = ctypes.c_int
sodium.crypto_hash_sha256_update.argtypes = [ctypes.POINTER(sha2_ctx), ctypes.c_char_p,
ctypes.c_size_t]
sodium.crypto_hash_sha256_update.restype = ctypes.c_int
sodium.crypto_hash_sha256_final.argtypes = [ctypes.POINTER(sha2_ctx), ctypes.c_char_p]
sodium.crypto_hash_sha256_final.restype = ctypes.c_int
def hash_extend(orig_data_len, orig_hash, extend_data):
if isinstance(orig_hash, str):
orig_hash = binascii.unhexlify(orig_hash)
total_len = math.ceil((orig_data_len + 1 + 8) / 64) * 64
padding_len = total_len - orig_data_len - 1 - 8
padding = b"\x80" + (b"\x00" * padding_len) + struct.pack(">Q", orig_data_len*8)
ctx = sha2_ctx()
sodium.crypto_hash_sha256_init(ctx)
for i in range(8):
ctx.state[i] = struct.unpack(">I", orig_hash[i*4:(i+1)*4])[0]
ctx.count = total_len * 8
out_hash = ctypes.create_string_buffer(32)
sodium.crypto_hash_sha256_update(ctx, extend_data, len(extend_data))
sodium.crypto_hash_sha256_final(ctx, out_hash)
return padding + extend_data, bytes(out_hash)