def _generate_messages(k): h = '\x00\x00' state_size = len(h) collisions = [] while k > 0: lookup = {} prefix = '\x00' * AES.block_size * (2**(k - 1)) pre_hash = mdhash(prefix, h) for _ in range(2**(state_size * 8)): m = urandom(state_size) lookup[mdhash(m, h)] = m m = urandom(state_size) hashed = mdhash(m, pre_hash) while hashed not in lookup: m = urandom(state_size) hashed = mdhash(m, pre_hash) collisions.append((prefix + m, lookup[hashed])) k -= 1 h = hashed return collisions, hashed
def p53(): k = 16 M = urandom(AES.block_size * 2 ** k) intermediate_hashes = _block_hash_map(M) collision_pairs, final_state = _generate_messages(k) while final_state not in intermediate_hashes: collision_pairs, final_state = _generate_messages(k) bridge_index = intermediate_hashes[final_state] bridge_offset = bridge_index * AES.block_size print(f'Bridge block is at index {bridge_index}') prefix = _generate_prefix(bridge_index, collision_pairs) assert mdhash(prefix, b'\x00\x00', nopadding=True) == final_state assert len(prefix) == (bridge_index * AES.block_size) preimage = prefix + M[bridge_offset:] hashed = mdhash(M, b'\x00\x00') assert len(preimage) == len(M) assert mdhash(preimage, b'\x00\x00') == hashed return f'Found a preimage for message M with length 2^{k}\n' \ f'M = {hexlify(M).decode()[:32]}...\n' \ f'hash = {hexlify(hashed).decode()}\n' \ f'preimage = {hexlify(preimage).decode()[:32]}...'
def p54(): funnel = _generate_states(8) assert funnel[0][1] is None prediction = mdhash(b'', funnel[0][0]) print(f'Prediction hash = {hexlify(prediction).decode()}') m = b'This message predicts every result for the coming baseball season' m = _generate_suffix(m, funnel) hashed = mdhash(m, b'\x00\x00') assert hashed == prediction return f'Generated message {m} with hash = {hexlify(hashed).decode()}'
def p54(): funnel = _generate_states(8) assert funnel[0][1] is None prediction = mdhash('', funnel[0][0]) print 'Prediction hash = {}'.format(hexlify(prediction)) m = 'This message predicts every result for the coming baseball season' m = _generate_suffix(m, funnel) hashed = mdhash(m, '\x00\x00') assert hashed == prediction return 'Generated message {} with hash = {}'.format( repr(m), hexlify(hashed))
def _find_collision(h1, h2): lookup = {} for _ in xrange(2**8): m = urandom(2) hashed = mdhash(m, h1) lookup[hashed] = m m = urandom(2) hashed = mdhash(m, h2) while hashed not in lookup: m = urandom(2) hashed = mdhash(m, h2) return lookup[hashed], m, hashed
def p53(): k = 16 M = urandom(AES.block_size * 2**k) intermediate_hashes = _block_hash_map(M) collision_pairs, final_state = _generate_messages(k) while final_state not in intermediate_hashes: collision_pairs, final_state = _generate_messages(k) bridge_index = intermediate_hashes[final_state] + 1 bridge_offset = bridge_index * AES.block_size print 'Bridge block is at index {}'.format(bridge_index) prefix = _generate_prefix(bridge_index, collision_pairs) assert len(prefix) == (bridge_index * AES.block_size) preimage = prefix + M[bridge_offset:] assert len(preimage) == len(M) assert mdhash(preimage, '\x00\x00') == mdhash(M, '\x00\x00') return 'Found a preimage for message M with length 2^{}'.format(k)
def _generate_suffix(m, funnel): target_states = { h: i for (i, (h, _)) in enumerate(funnel) if i > len(funnel) / 2 } glue = urandom(2) hashed = mdhash(m + glue, '\x00\x00') while hashed not in target_states: glue = urandom(2) hashed = mdhash(m + glue, '\x00\x00') m = pkcs7(m + glue) i = target_states[hashed] while i != 0: h, a = funnel[i] m += pkcs7(a) i = (i - 1) / 2 return m
def _generate_messages(k: int) -> Tuple[List[Tuple[bytes, bytes]], bytes]: h = b'\x00\x00' state_size = len(h) collisions = [] hashed = b'' while k > 0: lookup = {} prefix = b'\x00' * AES.block_size * (2 ** (k - 1)) pre_hash = mdhash(prefix, h, nopadding=True) for _ in range(2 ** (state_size * 8)): m = urandom(state_size) lookup[mdhash(m, h)] = m m = urandom(state_size) hashed = mdhash(m, pre_hash) while hashed not in lookup: m = urandom(state_size) hashed = mdhash(m, pre_hash) single_block_hash = mdhash(lookup[hashed], h) klen_block_hash = mdhash(prefix + m, h) assert single_block_hash == klen_block_hash assert klen_block_hash == hashed assert hashed == single_block_hash collisions.append((prefix + m, lookup[hashed])) k -= 1 h = hashed return collisions, hashed
def _block_hash_map(M): block_to_index = {} h = '\x00\x00' M = pkcs7(M) for i in range(len(M) / AES.block_size): start = i * AES.block_size end = start + AES.block_size block = M[start:end] hashed = mdhash(block, h, nopadding=True) block_to_index[hashed] = i h = hashed return block_to_index
def _block_hash_map(M: bytes) -> Dict[bytes, int]: state_to_index = {} h = b'\x00\x00' M = pkcs7(M) for i in range(len(M) // AES.block_size): state_to_index[h] = i start = i * AES.block_size end = start + AES.block_size block = M[start:end] hashed = mdhash(block, h, nopadding=True) h = hashed return state_to_index