Symmetric Encryption#

ToDo:

  • Add illustration for Symmetric Encryption - Similar to this.

  • Add illustration for authenticated vs non-autenticated encryption.

  • Explain difference between EtM, E&M and MtE - See this.

  • Add relevant resources at the end.


Encryption is the process of transforming a message, usually called cyphered message, in such a way that only the intended parties can reverse it. The process of reversing encryption is called decryption. This implies that, as opposed to hashes, encryption is reversible.

There are two main types of encryption, symmetric and asymmetric, this chapter will cover symmetric encryption.

In symmetric encryption, the message to be sent is encrypted using a single secret password, also called key. Anyone with that secret key and decrypt the message and see the original content.

This type of encryption is fast, reliable and extremely secure, however it has a major disadvantages, the secret key must be shared between the intended parties and the whole encryption procedure will be as secure as the security used for that key exchange. That is why usually other mechanisms such as asymmetric encryption are used only for the key-exchange.

Encryption != Encoding#

Some times this terms are confused, encryption as mentioned is reversible but only by the intended parties whereas encoding is simply a way to transform a message in a non-secure way, mainly as means of compression or to transform a message into a more suitable format.

One of the most common encodings used is Base64, which can be used to send binary files (PDFs, images, etc.) as plain text in HTTP Requests. Working with Base64 is a topic outside of this guide, however some introductory material is provided in one of the appendixes.

Authenticated vs Non-Authenticated Symmetric Encryption#

A futher categorization of symmetric encryption algorithms is between those with authentication features and those without. Both provide a way to securely transmit a meesage, however, authenticated Encryption also allows, among other things, to verify whether the ciphered message has been modified.

Modifying the ciphered message in a non-authenticated scheme will produce a nonsensical output whereas in authenticated encryption it will throw an error.

The Cryptography Library#

Python does not include any symmetric encryption features in its standard library, therefore, to use it one should install a third party tool. It is of great importance that the library to be used has some characteristics to ensure real security:

  • It should be built only on the standard library, this will ensure full compatibility and avoid dependency-related problems.

  • It should be open source, that way the code could be review and everyone knows exactly how it works.

  • It should be time-proved, new libraries tend to have bugs or poor implementations, libraries with some time online are usually prefered.

  • It should be used by trustworthy users, if it is an industry standard it means many companies and individuals trust it.

The Python Cryptographic Authority, or pyca for short, is a non-official group of users who developed cryptographic libraries for this very purpose. It supports symmetric (authenticated and non-authenticated) and asymmetric encryption.

The most common and used is homonomous to the group name: cryptography

It can be install via pip with:

pip install cryptography
import secrets
import cryptography

Symmetric Encryption without Authentication#

One of the most popular algorithms used is the Advanced Encryption Standard (AES). It works in combination with a Mode of Operation, this modes change the way in which the cyphered message is generated.

Depending on the mode, the result could be authenticated or non-authenticated. The available (in the cryptography module) modes for non-authenticated encryption are CBC, CFB, OFB, CTR. For more information see this resource.

Usually the name of the algorithm is called AES-MODE in this chapter for non-authenticated encryption AES-CTR will be used.

AES is one of the symmetric encryption algorithms available in the PyCA cryptography library. The particular objects used here are part of the hazmat package, hazmat stands for “Hazardous Materials” and quoting from their site:

This is a “Hazardous Materials” module. You should ONLY use it if you’re 100% absolutely sure that you know what you’re doing because this module is full of land mines, dragons, and dinosaurs with laser guns.

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

Encrypting#

message = b"Hello World!"

key = secrets.token_bytes(32)
initialization_vector = secrets.token_bytes(16)

algorithm = algorithms.AES(key)
mode = modes.CTR(initialization_vector)

cipher = Cipher(algorithm, mode)
encryptor = cipher.encryptor()

message_encrypted = encryptor.update(message) + encryptor.finalize()

print(f"Secret Key: {key.hex()}")
print(f"Public Initialization Vector: {initialization_vector.hex()}")
print(f"Encrypted Message: {message_encrypted.hex()}")
Secret Key: d84d41cdf66296169b5fd2c7ce76df60882526e42adb097c5c2acc03279f74aa
Public Initialization Vector: f915c337f2f9155213fcf0dca1ae964a
Encrypted Message: 7568ffcde99e9e39be8a2244

Decrypting#

Wrong Key#

guess_password = secrets.token_bytes(32)

algorithm = algorithms.AES(guess_password)
mode = modes.CTR(initialization_vector)

cipher = Cipher(algorithm, mode)

decryptor = cipher.decryptor()
message_decrypted = decryptor.update(message_encrypted) + decryptor.finalize()

print(f"Decrypted Message: {message_decrypted}")
Decrypted Message: b'dJ\xdd\xbc\x03@\xa1\x01\xf62\xd3\x94'

Right Key#

algorithm = algorithms.AES(key)
mode = modes.CTR(initialization_vector)

cipher = Cipher(algorithm, mode)

decryptor = cipher.decryptor()
message_decrypted = decryptor.update(message_encrypted) + decryptor.finalize()
print(f"Decrypted Message: {message_decrypted}")
Decrypted Message: b'Hello World!'

Symmetric Encryption with Authentication#

The same AES algorithm can be combined with modes that results in a authenticated encryption. Such modes are GCM, OCB3 and CCM.

Even though they provide more features that the non-authenticated modes, they are sensitive to nonce re-use, having known vulnerabilities if the same nonce is used twice. There is a new generation of modes called Synthetic Initialization Vector (SIV) which mitigates this risks, producing modes like AES-GCM-SIV and so on, this new generation has no time-proved implementation neither in Python nor in other programming language at the time of this writting.

from cryptography.hazmat.primitives.ciphers.aead import AESGCM

Encrypting#

message = b"Hello World!"

key = secrets.token_bytes(32) # 16, 24, or 32
nonce = secrets.token_bytes(24)

ciphered_message = AESGCM(key).encrypt(nonce, message, None)

encrypted_message = f"{nonce.hex()}:{ciphered_message.hex()}"

print(f"Secret Key: {key.hex()}")
print(f"Public Nonce: {nonce.hex()}")
print(f"Encrypted Message: {encrypted_message}")
Secret Key: 6550164006d556e10308967538f097b425b662b07414a464cfef03fb7c7e0327
Public Nonce: 5efdeef65e862baf5de8bf8ace72599e4e06624bc3c3e983
Encrypted Message: 5efdeef65e862baf5de8bf8ace72599e4e06624bc3c3e983:464332a2c34fca88fdc5ce4424aa55a38f5e9ed1668243806eb132ec

Decrypting#

def verify(password, nonce, message):
    try:
        decrypted_message = AESGCM(password).decrypt(nonce, message, None)
        return f"Decrypted Message: {message_decrypted}"
    except cryptography.exceptions.InvalidTag:
        return "Verification Failed - Either the message has been altered or the nonce or key are incorrect"

Wrong Key#

guess_password = bytes.fromhex("1329f363a87306c33952a7cbfc0ebf8105126764d1c72c511031a5b028110cf9")

nonce, ciphered_message = encrypted_message.split(":")
nonce_bytes = bytes.fromhex(nonce)
ciphered_message_bytes = bytes.fromhex(ciphered_message)

print(verify(guess_password, nonce_bytes, ciphered_message_bytes))
Verification Failed - Either the message has been altered or the nonce or key are incorrect

Right Key#

nonce, ciphered_message = encrypted_message.split(":")
nonce_bytes = bytes.fromhex(nonce)
ciphered_message_bytes = bytes.fromhex(ciphered_message)

print(verify(key, nonce_bytes, ciphered_message_bytes))
Decrypted Message: b'Hello World!'

The Fernet Method#

The Fernet method is an opinionated way of using AES and HMAC to provide authenticated encryption. It is meant for easy use but it requires that the recipient also has a library compatible with it to effectively use it.

It is not nearly as commonly used as the other methods described in this chapter but it is much more practical for simple use cases.

At the time of this writing there are implementations for:

This method also allows the usage of passwords (human-readable) instead of random bytes as keys, see this example for more information.

The Fernet algorithm is not part of the hazmat package and then it is meant to be use as a developer friendly approach to symmetric encryption.

from cryptography.fernet import Fernet

Encrypting#

message = b"Hello World!"

key = Fernet.generate_key()

cipher = Fernet(key)

message_encrypted = cipher.encrypt(message)

print(f"Secret Key: {key.hex()}")
print(f"Encrypted Message: {message_encrypted.hex()}")
Secret Key: 71565a4d76667432454c48785973307263344e75616c487a7169317258435770692d6575783731526b5a453d
Encrypted Message: 674141414141426a6a4d576c5f4b754c325f37624d73475953524e6e5965676b496f6449714d71396e55666136616e6d42534f6245734e51796672597043386a386e6b46436e7434696a566656353957636e394e7172746f4a63414c63316d7942673d3d

Decrypting#

Wrong Key#

guess_password = bytes.fromhex("785754746c6136366d4f467431395543336c46444c71692d3249792d5a365374386665566f64726f4639303d")

cipher = Fernet(guess_password)

try:
    message_decrypted = cipher.decrypt(message_encrypted)
    print(f"Encrypted Message: {message_decrypted}")
except cryptography.fernet.InvalidToken:
    print("Token Invalid")
Token Invalid

Right Key#

cipher = Fernet(key)

try:
    message_decrypted = cipher.decrypt(message_encrypted)
    print(f"Encrypted Message: {message_decrypted}")
except cryptography.fernet.InvalidToken:
    print("Token Invalid")
Encrypted Message: b'Hello World!'

Conclusion#

Symmetric encryption provides a way to hide the original information in a reversible way. When using authenticated algorithms, if the encrypted message is modified, when decrypted, the output will be have no sense. Authenticated algorithms can detect changes and throw errors. There are many implementations of symmetric algorithms, many are low-level like AES but are more cross-platform whereas other such as Fernet have a more user-friendly approach but are only avaiable in certain programming languages.