def encryption_oracle(b: bytes) -> bytes: """ params: b: bytes to encrypt returns: `b` encrypted using AES-128 with a random key using either ECB mode or CBC mode randomly (one in two chance) also prepends 5-10 random bytes and appends 5-10 random bytes to `b` before encryption if CBC mode is used random bytes are used for the IV """ prefix_size = random.randint(5, 10) prefix = rand_bytes_gen(prefix_size) suffix_size = random.randint(5, 10) suffix = rand_bytes_gen(suffix_size) plain = PKCS7Padding.apply(prefix+b+suffix, blksize) key = rand_bytes_gen(blksize) cipher = AES.new(key, AES.MODE_ECB) if use_ecb: # print("ECB") # for manual verification ecb = ECBMode(blksize, cipher.encrypt, cipher.decrypt) return ecb.encrypt(plain) iv = rand_bytes_gen(blksize) cbc = CBCMode( blksize=blksize, encrypt_blk=cipher.encrypt, decrypt_blk=cipher.decrypt, iv=iv ) # print("CBC") return cbc.encrypt(plain)
def test_decrypt_bytes_not_padded_raises(self): b = b"\x00\x01\x00\x01\x23" iv = b"\x03\x03" cbc = CBCMode( blksize=2, encrypt_blk=self.mock_fun, decrypt_blk=self.mock_fun, iv=iv, ) with self.assertRaises(ValueError): cbc.decrypt(b)
def test_encrypt_bytes_of_proper_length(self): b = b"\x00\x01\x00\x01" iv = b"\x03\x03" expected_bytes = b"\x03\x03\x03\x03" cbc = CBCMode( blksize=2, encrypt_blk=self.mock_fun, decrypt_blk=self.mock_fun, iv=iv, ) actual_bytes = cbc.encrypt(b) self.assertEqual(expected_bytes, actual_bytes)
def test_break_cbc_single_blk_nominal_case(self): plain = b"YELLOW SUBMARINE" iv = b"abcdefghijklmnop" cipher = AES.new(CONSISTENT_KEY, AES.MODE_ECB) cbc = CBCMode( blksize=16, encrypt_blk=cipher.encrypt, decrypt_blk=cipher.decrypt, iv=iv ) encrypted = cbc.encrypt(plain) decrypted = break_cbc_single_blk(iv, encrypted, valid_padding) self.assertEqual(plain, decrypted)
def is_admin(encrypted: bytes, blksize: int = 16) -> bool: """ params: encrypted: message encrypted using `encryption_oracle()` blksize: blocksize used by `encryption_oracle()` returns: True if decrypted message contains ";admin=true;", False otherwise """ cipher = AES.new(CONSISTENT_KEY, AES.MODE_ECB) cbc = CBCMode(blksize=blksize, encrypt_blk=cipher.encrypt, decrypt_blk=cipher.decrypt, iv=CONSISTENT_IV) padded = cbc.decrypt(encrypted) decrypted = PKCS7Padding.unapply(padded) return b";admin=true;" in decrypted
def aes_cbc(plaintext: bytes) -> bytes: """ params: b: bytes to encrypt returns: `b` encrypted using AES-128-CBC uses key as IV """ padded = PKCS7Padding.apply(plaintext, blksize) cipher = AES.new(key, AES.MODE_ECB) cbc = CBCMode( blksize=blksize, encrypt_blk=cipher.encrypt, decrypt_blk=cipher.decrypt, iv=key, ) return cbc.encrypt(padded)
def test_encrypt_decrypt_integration_case(self): key = b"YELLOW SUBMARINE" iv = b"THIS IS 16 BYTES" cipher = AES.new(key, AES.MODE_ECB) cbc = CBCMode( blksize=16, encrypt_blk=cipher.encrypt, decrypt_blk=cipher.decrypt, iv=iv, ) plaintext = (b"Lorem ipsum dolo" b"r sit amet, cons" b"ectetur adipisci" b"ng elitAAAAAAAAA") encrypted = cbc.encrypt(plaintext) decrypted = cbc.decrypt(encrypted) self.assertEqual(plaintext, decrypted)
def test_init_iv_smaller_than_blksize_returns_empty_bytes(self): iv = b"\x03" with self.assertRaises(ValueError): CBCMode( blksize=2, encrypt_blk=self.mock_fun, decrypt_blk=self.mock_fun, iv=iv, )
def test_init_iv_bigger_than_blksize_raises(self): iv = b"\x03\x03\x23" with self.assertRaises(ValueError): CBCMode( blksize=2, encrypt_blk=self.mock_fun, decrypt_blk=self.mock_fun, iv=iv, )
def validate_ascii(encrypted: bytes) -> bytes: """ params: encrypted: message encrypted using `aes_cbc()` returns: decrypted bytes if they contain extended ascii characters, None otherwise """ cipher = AES.new(key, AES.MODE_ECB) cbc = CBCMode(blksize=blksize, encrypt_blk=cipher.encrypt, decrypt_blk=cipher.decrypt, iv=key) padded = cbc.decrypt(encrypted) decrypted = PKCS7Padding.unapply(padded) for c in decrypted: if c >= 128: return decrypted return None
def encryption_oracle(b: bytes) -> bytes: """ params: b: bytes to encrypt returns: `b` encrypted using AES-128-CBC prepends `prefix` and appends `suffix` before encrypting """ cleaned_data = b.replace(b';', b"';'").replace(b'=', b"'='") plain = PKCS7Padding.apply(prefix + cleaned_data + suffix, blksize) cipher = AES.new(CONSISTENT_KEY, AES.MODE_ECB) cbc = CBCMode( blksize=blksize, encrypt_blk=cipher.encrypt, decrypt_blk=cipher.decrypt, iv=CONSISTENT_IV, ) return cbc.encrypt(plain)
def encryption_oracle() -> Tuple[bytes, bytes]: """ params: none returns: ciphertext: one of ten plaintexts encrypted using AES-128-CBC with `iv` iv: iv used to encrypt the ciphertext """ plain_strs = ( b"MDAwMDAwTm93IHRoYXQgdGhlIHBhcnR5IGlzIGp1bXBpbmc=", b"MDAwMDAxV2l0aCB0aGUgYmFzcyBraWNrZWQgaW4gYW5kIHRoZSBWZWdhJ3MgYXJlIH" + b"B1bXBpbic=", b"MDAwMDAyUXVpY2sgdG8gdGhlIHBvaW50LCB0byB0aGUgcG9pbnQsIG5vIGZha2luZw" + b"==", b"MDAwMDAzQ29va2luZyBNQydzIGxpa2UgYSBwb3VuZCBvZiBiYWNvbg==", b"MDAwMDA0QnVybmluZyAnZW0sIGlmIHlvdSBhaW4ndCBxdWljayBhbmQgbmltYmxl", b"MDAwMDA1SSBnbyBjcmF6eSB3aGVuIEkgaGVhciBhIGN5bWJhbA==", b"MDAwMDA2QW5kIGEgaGlnaCBoYXQgd2l0aCBhIHNvdXBlZCB1cCB0ZW1wbw==", b"MDAwMDA3SSdtIG9uIGEgcm9sbCwgaXQncyB0aW1lIHRvIGdvIHNvbG8=", b"MDAwMDA4b2xsaW4nIGluIG15IGZpdmUgcG9pbnQgb2g=", b"MDAwMDA5aXRoIG15IHJhZy10b3AgZG93biBzbyBteSBoYWlyIGNhbiBibG93", ) blksize = len(CONSISTENT_KEY) plain = base64.b64decode(plain_strs[random.randint(0, len(plain_strs) - 1)]) padded = PKCS7Padding.apply(plain, blksize) cipher = AES.new(CONSISTENT_KEY, AES.MODE_ECB) iv = rand_bytes_gen(blksize) cbc = CBCMode(blksize=blksize, encrypt_blk=cipher.encrypt, decrypt_blk=cipher.decrypt, iv=iv) encrypted = cbc.encrypt(padded) return encrypted, iv
def valid_padding(encrypted: bytes, iv: bytes) -> bool: """ params: encrypted: encrypted message iv: IV used to encrypt `encrypted` returns: True if decryption of `encrypted` has valid padding False otherwise """ blksize = len(CONSISTENT_KEY) cipher = AES.new(CONSISTENT_KEY, AES.MODE_ECB) cbc = CBCMode(blksize=blksize, encrypt_blk=cipher.encrypt, decrypt_blk=cipher.decrypt, iv=iv) decrypted = cbc.decrypt(encrypted) try: PKCS7Padding.unapply(decrypted) return True except InvalidPaddingException: return False
def main(): """ decrypt contents of a file encrypted using AES-128 in CBC mode """ key = b"YELLOW SUBMARINE" iv = bytes(16) cipher = AES.new(key, AES.MODE_ECB) cbc = CBCMode( blksize=16, encrypt_blk=cipher.encrypt, decrypt_blk=cipher.decrypt, iv=iv ) input_filename = os.path.join(pathlib.Path(__file__).parent, "input") with open(input_filename, 'r') as input_file: base64_str = input_file.read() base64_bytes = base64_str.replace('\n', '').encode("utf-8") encrypted = base64.decodebytes(base64_bytes) message = cbc.decrypt(encrypted) print(message.decode("utf-8", errors="ignore"))