def test_aes_cbc_encrypt(): key = "YELLOW SUBMARINE".encode("utf-8") plaintext = "test".encode("utf-8") iv = bytes([0]) * BLOCK_SIZE ciphertext = aes_cbc_encrypt(key, plaintext, iv, BLOCK_SIZE) computed_plaintext = aes_cbc_decrypt(key, ciphertext, iv, BLOCK_SIZE) assert plaintext == computed_plaintext
def decrypt(self, ciphertext, iv, blocksize=16) -> bool: plaintext = block.aes_cbc_decrypt(ciphertext, self.key, iv, remove_padding=False) padding_char = plaintext[-1] if padding_char > blocksize or padding_char < 1: return False for i in range(padding_char): if plaintext[-i - 1] != padding_char: return False return True
def test_cbc_recover_key_with_iv_eq_key(): # Set 4, challenge 27: Recover the key from CBC with IV=Key key = gen_random_block() plaintext = "comment1=cooking%20MCs;userdata=1234567890123456" # Verify each byte of the plaintext for ASCII compliance # (ie, look for high-ASCII values). Noncompliant messages # should raise an exception or return an error that includes # the decrypted plaintext. def verify_plaintext(plaintext: bytes): # Check for high-ASCII (bytes 127 or higher) for b in plaintext: assert b in range(32, 126) verify_plaintext(plaintext.encode("utf8")) ciphertext = aes_cbc_encrypt(key, plaintext.encode("utf8"), key) block_1 = ciphertext[:BLOCK_SIZE] block_4 = ciphertext[BLOCK_SIZE * 3:] all_zero = b"\x00" * BLOCK_SIZE # Modify the message (you are now the attacker): # C_1, C_2, C_3 -> C_1, 0, C_1 new_ciphertext = block_1 + all_zero + block_1 + block_4 # This will raise BadPaddingValidation on block_4 decrypted_plaintext = aes_cbc_decrypt(key, new_ciphertext, key, remove_padding=False) try: verify_plaintext(decrypted_plaintext) except AssertionError: # And this will happen as the attacker tampered with the ciphertext! pass # As the attacker, recovering the plaintext from the error, extract the key # P'_1 XOR P'_3 p_1_prime = decrypted_plaintext[:BLOCK_SIZE] p_3_prime = decrypted_plaintext[BLOCK_SIZE * 2:BLOCK_SIZE * 3] reconstructed_key = b"" for x, y in zip(p_1_prime, p_3_prime): byte = bytes([x ^ y]) reconstructed_key += byte # Check we reconstructed the key successfully for x, y in zip(key, reconstructed_key): assert x == y
def test_cbc_bitflip_attack(): # Set 2, challenge 16 CBC Bitflipping # Note: I did this assuming the prepended text was known by the attacker. block_size = BLOCK_SIZE key = gen_random_block() iv = gen_random_block() prepend = "comment1=cooking%20MCs;userdata=" append = ";comment2=%20like%20a%20pound%20of%20bacon" # Make sure ; and = are quoted # Selecting user controlled value exactly twice the target text plaintext = "123456789012123456789012".replace(";", "").replace("=", "") full_plaintext = prepend + plaintext + append ciphertext = cbc_encrypt_prepend_and_append( key, iv, plaintext.encode("utf-8"), append.encode("utf-8"), prepend.encode("utf-8"), ) modified_ciphertext = b"" target_text = ";admin=true;" edit_start_position = 0 edit_stop_position = len(target_text) # Now tweak the bytes in the first ciphertext (comment field) such that the change # is introduced in the second plaintext. for ind, by in enumerate(ciphertext): if ind in range(edit_start_position, edit_stop_position): new_value = bytes([ int.from_bytes( target_text[edit_start_position + ind].encode("utf-8"), "big") ^ ciphertext[ind] ^ int.from_bytes( full_plaintext[ind + block_size].encode("utf-8"), "big") ]) modified_ciphertext = modified_ciphertext + new_value else: modified_ciphertext = modified_ciphertext + bytes([by]) decrypted_plaintext = aes_cbc_decrypt(key, modified_ciphertext, iv) assert target_text.encode("utf-8") in decrypted_plaintext
def test_aes_cbc_decrypt(): # Set 2, challenge 10: Implement CBC mode path_to_data = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data/10.txt") with open(path_to_data, "r") as f: base64_ciphertext = f.read() ciphertext = base64_to_bytes(base64_ciphertext) key = "YELLOW SUBMARINE".encode("utf-8") iv = bytes([0]) * BLOCK_SIZE plaintext = aes_cbc_decrypt(key, ciphertext, iv, remove_padding=False) assert "Vanilla's on the mike, man I'm not lazy." in plaintext.decode( "utf-8") assert "I'm back and I'm ringin' the bell" in plaintext.decode("utf-8")
def bitflipping_oracle_decrypt(ciphertext, aes_key, iv: bytes) -> bool: plaintext = block.aes_cbc_decrypt(ciphertext, aes_key, iv) print(plaintext) return b';admin=true;' in plaintext
from cryptopals import block import base64 BLOCK_SIZE = 16 if __name__ == '__main__': with open('./10.txt') as f: ciphertext = base64.b64decode(f.read()) key = b'YELLOW SUBMARINE' iv = b'\x00' * BLOCK_SIZE plaintext = block.aes_cbc_decrypt(ciphertext, key, iv, remove_padding=True) print(plaintext.decode()) # We'll silently test to make sure encryption works correctly too. candidate_ciphertext = block.aes_cbc_encrypt(plaintext, key, iv) if candidate_ciphertext != ciphertext: print('Encryption is not working correctly.')