Foundations of Blockchain
上QQ阅读APP看书,第一时间看更新

An example implementation of AES

Let's implement the AES cipher technique using a Python cryptographic library called PyCryptodome. We will be using the PyCryptodome library throughout this chapter to implement other ciphers and hashing algorithms.

PyCryptodome is a self-contained Python package of low-level cryptographic primitives. PyCryptodome is a forked project of the PyCrypto library and is an active project with extended primitive support. So, it is an almost drop-in replacement for the old PyCrypto library.

We will use the AES module from Crypto.Cipher package and we will also import a module from Crypto.Random package to generate a random key for AES, as follows:

from Crypto.Cipher import AES 
from Crypto.Random import get_random_bytes 

The encrypting end will create the ciphertext by using a randomly selected symmetric key. Once we have imported the required modules, a 16-byte key is generated using the Crypto.Random package. This is written to a file, which needs to be kept secret:

with open("aes.key", "wb") as file_out: 
    key = get_random_bytes(16) 
    file_out.write(key) 

The AES cipher object is created by passing the key. Cipher mode EAX is used in the code. This object is used to encrypt the data. Nonce, tag, and ciphertext are stored and transmitted to the decryption end:

data = "plaintext for AES" 
cipher = AES.new(key, AES.MODE_EAX) 
cipher_text, tag = cipher.encrypt_and_digest(data.encode()) 
with open("encrypted.bin", "wb") as file_out: 
    [file_out.write(x) for x in (cipher.nonce, tag, cipher_text)] 
print("Data is encrypted and stored in a file") 

The decryption part of AES uses the same 16-byte symmetric key generated during encryption. Ideally, this key has to be transferred over a secure channel to the recipient. The received encrypted binary file is read to get the nonce, tag, and the ciphertext itself. The AES cipher object is created using the same key and nonce value. Finally, decryption is performed using the decrypt_and_verify method by providing cipher_text and tag. The tag is provided to perform verification; it checks for any modifications in the ciphertext:

with open("aes.key", "rb") as file_in: 
    key = file_in.read(16) 
with open("encrypted.bin", "rb") as file_in: 
    nonce, tag, cipher_text = [file_in.read(x) for x in (16, 16, -1)] 
 
cipher = AES.new(key, AES.MODE_EAX, nonce) 
data = cipher.decrypt_and_verify(cipher_text, tag) 
print("Decrypted data is : \"{}\"".format(data.decode())) 

A successful execution of both the encryption and decryption operations will produce the following output:

Data is encrypted and stored in a file
Decrypted data is : "plaintext for AES"  

When the encryption and decryption parts of the AES program are run, we get the original data back after decryption. Any modification to the ciphertext would result in a MAC check error, and Python would throw ValueError: MAC check failed.

A detailed Jupyter Notebook and scripts included in this chapter can be found in the GitHub repository for this book.