5.1 TRUST BUT VERIFY
EXERCISE 5.1: TRUST BUT VERIFY
Finish out the code of the simple encryption plus hash system and add a decryption operation. The decryption operation should, upon finalization, recompute the hash of the ciphertext and compare it to the hash that was sent over. If the hashes don’t match, it should raise an exception. Be careful! The MAC is not encrypted and should not be decrypted! If you don’t think carefully about this, you might decrypt data that doesn’t exist!
# ex5_1.py
# FAKE MAC WITH SYMMETRIC ENCRYPTION
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
import hashlib
class MessageWasTamperedWithError(Exception):
'''
Raised when the ciphertext has been tampered with
by Eve.
'''
def __init__(self, expected_mac: str, actual_mac: str):
self.expected_mac = expected_mac
self.actual_mac = actual_mac
def __str__(self):
return f"\nExpected: {self.expected_mac}.\nBut got: {self.actual_mac}"
class Encryptor:
def __init__(self, key, nonce):
= Cipher(
aesContext
algorithms.AES(key),
modes.CTR(nonce),=default_backend()
backend
)self.encryptor = aesContext.encryptor()
self.hasher = hashlib.sha256()
def update_encryptor(self, plaintext):
= self.encryptor.update(plaintext)
ciphertext self.hasher.update(ciphertext)
return ciphertext
def finalize_encryptor(self):
return self.encryptor.finalize() + self.hasher.digest()
class Decryptor:
def __init__(self, key: bytes, nonce: bytes, digest: bytes):
= Cipher(
aesContext
algorithms.AES(key),
modes.CTR(nonce),=default_backend()
backend
)self.decryptor = aesContext.decryptor()
self.hasher = hashlib.sha256()
self.digest = digest
def update_decryptor(self, ciphertext: bytes):
'''Make sure that ciphertext doesn't include the MAC'''
= self.decryptor.update(ciphertext)
plaintext self.hasher.update(ciphertext)
return plaintext
def finalize_decryptor(self):
if self.hasher.digest() != self.digest:
raise MessageWasTamperedWithError(
=self.hasher.digest(),
expected_mac=self.digest
actual_mac
)return self.decryptor.finalize()
@staticmethod
def get_mac(ciphertext: bytes):
return ciphertext[-32:]
if __name__ == '__main__':
= os.urandom(32)
key = os.urandom(16)
nonce
= Encryptor(key, nonce)
encryptionManager = b"Hi Bob, this is Alice !"
plaintext = encryptionManager.update_encryptor(plaintext)
ciphertextWithMac += encryptionManager.finalize_encryptor()
ciphertextWithMac
= Decryptor(
decryptionManager =key, nonce=nonce, digest=Decryptor.get_mac(ciphertextWithMac))
key= decryptionManager.update_decryptor(
_plaintext =ciphertextWithMac[:-32])
ciphertext+= decryptionManager.finalize_decryptor()
_plaintext
assert (plaintext == _plaintext)
print("[+] Successful!!")
When you run the above code multiple times, you get the following: