Insecure encryption algorithm - AES - Python

Insecure encryption algorithm - AES - Python

Need

Implementation of secure encryption algorithms

Context

  • Usage of Python 3 for writing and executing Python code
  • Usage of pycryptodome for cryptographic operations in Python

Description

Non compliant code

        from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes

def encrypt(plain_text):
    key = get_random_bytes(16)
    cipher = AES.new(key, AES.MODE_CBC)
    cipher_text = cipher.encrypt(pad(plain_text, AES.block_size))
    return cipher.iv + cipher_text

def decrypt(cipher_text):
    key = get_random_bytes(16)
    iv = cipher_text[:AES.block_size]
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    plain_text = unpad(cipher.decrypt(cipher_text[AES.block_size:]), AES.block_size)
    return plain_text
        
        

In the code above, the AES encryption algorithm is used with CBC mode and PKCS5 padding.

The encrypt function generates a random 16 bytes key for the AES encryption. It creates a new AES cipher using this key and the AES.MODE_CBC mode. Then it encrypts the plain text after padding it to the AES block size. The result is the concatenation of the initialization vector (IV) and the cipher text.

The decrypt function also generates a random 16 bytes key. It extracts the IV from the cipher text and creates a new AES cipher with this key, the AES.MODE_CBC mode and the extracted IV. It decrypts the cipher text and unpads the result to get the plain text.

The vulnerability in this code is the usage of the CBC mode and PKCS5 padding which are considered insecure. The CBC mode is vulnerable to padding oracle attacks, and the PKCS5 padding can be exploited to reveal information about the plain text. Furthermore, the key used for decryption should not be a random key but the same key used for encryption. The current implementation will not correctly decrypt the cipher text.

Steps

  • Replace the AES.MODE_CBC mode with AES.MODE_GCM which is a more secure mode for AES encryption.
  • In the decrypt function, the key should not be randomly generated. It should be the same key used for encryption.
  • Store the key securely, it should not be exposed or easily accessible.
  • Ensure that the IV (Initialization Vector) is unique and random for each encryption process.
  • Consider using a key derivation function like PBKDF2 to generate the encryption key from a password.

Compliant code

        from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad

def encrypt(plain_text, password):
    salt = get_random_bytes(AES.block_size)
    key = PBKDF2(password, salt, dkLen=32)
    cipher = AES.new(key, AES.MODE_GCM)
    cipher_text, tag = cipher.encrypt_and_digest(pad(plain_text, AES.block_size))
    return salt + cipher.nonce + cipher_text + tag

def decrypt(cipher_text, password):
    salt = cipher_text[:AES.block_size]
    nonce = cipher_text[AES.block_size:AES.block_size*2]
    key = PBKDF2(password, salt, dkLen=32)
    cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
    plain_text = unpad(cipher.decrypt(cipher_text[AES.block_size*2:-16]), AES.block_size)
    return plain_text
        
        

The above code fixes the vulnerability by using AES.MODE_GCM, which is a more secure mode for AES encryption.

In the encrypt function, a salt is generated and used along with the password to create a key using the PBKDF2 key derivation function. This key is then used to create a new AES cipher in GCM mode. The plaintext is encrypted and the function returns the salt, nonce, ciphertext, and tag.

In the decrypt function, the salt and nonce are extracted from the ciphertext. The same PBKDF2 function is used to generate the key from the password and salt. This key is then used to create a new AES cipher in GCM mode with the extracted nonce. The ciphertext is decrypted and the plaintext is returned.

This code ensures that the key is not randomly generated in the decrypt function, but is instead derived from the password and salt. The salt and nonce are unique and random for each encryption process, ensuring the security of the encryption. The key is also securely stored as it is derived from the password and not directly stored or transmitted.

References