Insecure encryption algorithm - AES
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.