def decode_repeating_byte_xor(ciphertext: bytes): c = ciphertext edit_distances = defaultdict(list) for keysize in range(2, 41): k, e = keysize, [] e.append(normalised_hamming_distance(c[:k], c[k:k * 2])) e.append(normalised_hamming_distance(c[k * 2:k * 3], c[k * 3:k * 4])) e.append(normalised_hamming_distance(c[k * 3:k * 4], c[k * 4:k * 5])) e.append(normalised_hamming_distance(c[k * 4:k * 5], c[k * 5:k * 6])) edit_distances[sum(e) / 4].append(keysize) likely_keysizes = sorted(edit_distances.items())[:5] def reducing_func(x: List[int], y: Tuple[Any, List[int]]) -> List[Any]: return x + y[1] possible_keysizes = reduce(reducing_func, likely_keysizes, []) keys = [] # type: List[str] for keysize in possible_keysizes: blocks = split_into_groups(ciphertext, keysize) if len(blocks[-1]) != keysize: del blocks[-1] transposed_blocks = zip(*blocks) key = [] for block in transposed_blocks: char, _ = decode_1_byte_xor(block) key.append(char) keys.append(''.join(key)) keys = [x for x in keys if x] plaintexts = {} # Dict[str, str] for key in keys: plaintexts[ repeating_key_xor( ciphertext, bytes(key, 'ascii')).decode('ascii')] = key plaintext = find_english_text(plaintexts.keys()) return plaintexts[plaintext], plaintext
def aes_cbc_encode(plaintext: bytes, password: bytes, iv: bytes) -> bytes: blocks = split_into_groups(plaintext, 16) res = [] prev_block = iv for block in blocks: prev_block = aes_ecb_encode(xor(prev_block, block), password) res.append(prev_block) return b''.join(res)
def detect_aes_ecb_encrypted_texts(ciphertexts: List[bytes]): max_repeats = defaultdict(list) for text in ciphertexts: counts = Counter(split_into_groups(text, 16)) most_no_of_repeats = counts.most_common(1)[0][1] max_repeats[most_no_of_repeats].append(text) most_repeats = max(max_repeats.keys()) return most_repeats, max_repeats[most_repeats]
def aes_cbc_decode(ciphertext: bytes, password: bytes, iv: bytes) -> bytes: blocks = split_into_groups(ciphertext, 16) res = [] # Sort out the first block on its own, as it requires iv res.append(xor(aes_ecb_decode(blocks[0], password), iv)) # Sort out all other blocks for i, block in enumerate(blocks[1:], 1): res.append(xor(aes_ecb_decode(block, password), blocks[i - 1])) return b''.join(res)