def break_cbc_using_padding(oracle, cipher, iv): cracked = bytearray() actual_iv = iv for target_start in range(0, len(cipher), 16): # this is the intermediate block that comes out of the decryptor intermediate = bytearray() first_block = bytearray(random_key(16)) for target_padding_byte in range(1, 17): # adjust all solved bytes to match new target padding for solved in range(0, len(intermediate)): reverse_index = -(solved+1) first_block[reverse_index] = intermediate[reverse_index] ^ target_padding_byte for b in range(0, 256): first_block[-target_padding_byte] = b valid = oracle(cipher[target_start : target_start+16], first_block) if(valid): # valid padding, b ^ found_padding = real_byte intermediate.insert(0, target_padding_byte ^ b) break elif(b == 255): raise Exception("couldn't find valid padding byte") cracked.extend(xortools.xor_bytes(bytes(intermediate), actual_iv)) actual_iv = cipher[target_start : target_start + 16] return strip_valid_padding(bytes(cracked))
def do_ctr(input, key, nonce): aes = AES.new(key) input_chunks = chunks(input, 16) encrypted = b'' count = bytearray(bytes(8)) for input_chunk in input_chunks: keystream = nonce + bytes(count) encrypted_keystream = aes.encrypt(keystream) encrypted += xortools.xor_bytes(encrypted_keystream[0 : len(input_chunk)], input_chunk) count[0] += 1 return encrypted
def decrypt_cbc(input, key, iv, strip=True): blocksize = 16 last = iv output = bytearray() blocks = chunks(input, blocksize) for block in blocks: decrypted = decrypt_ecb(bytes(block), key) combined = xortools.xor_bytes(last, decrypted) output.extend(combined) last = block return strip_valid_padding(output) if strip else output
def encrypt_cbc(input, key, iv): blocksize = 16 last = iv output = bytearray() input = padded(input, blocksize) blocks = chunks(input, blocksize) for block in blocks: combined = xortools.xor_bytes(last, block) encrypted = encrypt_ecb(combined, key) output.extend(encrypted) last = encrypted return output
def test_challenge_2(self): """ Challenge 2: XOR two strings """ input_one = '1c0111001f010100061a024b53535009181c' input_two = '686974207468652062756c6c277320657965' expected_out = '746865206b696420646f6e277420706c6179' bytes_one = conv.hex_to_bytes(input_one) bytes_two = conv.hex_to_bytes(input_two) expected_bytes_out = conv.hex_to_bytes(expected_out) self.assertEqual(xortools.xor_bytes(bytes_one, bytes_two), expected_bytes_out)
def test_challenge_19(self): """ Challenge 19: Break fixed-nonce CTR """ key = aestools.random_key(16) nonce = bytes(8) input_file = open('files/19.txt', 'r') lines = [conv.base_64_to_bytes(line.rstrip()) for line in input_file] input_file.close() encrypted_lines = [aestools.do_ctr(line, key, nonce) for line in lines] index = 0 probable_bytes = bytearray() while(True): rotated = "".join([chr(line[index]) if index < len(line) else '' for line in encrypted_lines]) b, all, score = xortools.solve_xor_block(bytes(rotated, 'utf-8')) probable_bytes.append(b) index += 1 if len(rotated) == 0: break for line in encrypted_lines: close = xortools.xor_bytes(line, bytes(probable_bytes[0 : len(line)])) readable = " ".join([chr(b) if b in range(32, 127) else 'X' for b in close])