Tools · Cryptography and passwords in practice · 11 / 20
AES-GCM authenticated encryption in Python: keys, nonces, and associated data
Encrypt and decrypt with AES-256-GCM using the cryptography library, understand why a nonce must never repeat with the same key, and use associated data to bind context.
Authenticated encryption
AES-GCM provides confidentiality and integrity together: decryption fails if the ciphertext, nonce, tag, or associated data were changed. That is what you want for almost every application use of symmetric encryption. ChaCha20-Poly1305 is an equally good alternative, faster on devices without AES hardware.
The nonce rule
GCM uses a 96-bit nonce that must be unique for every encryption with the same key. Reusing a nonce with the same key reveals the XOR of the two plaintexts and lets an attacker forge messages. Generate a random 12-byte nonce per message with os.urandom and store it with the ciphertext; it does not need to be secret. For very high message counts under one key, use counters or rotate keys, or use AES-GCM-SIV, which tolerates nonce reuse better.
Lab: encrypt, tamper, and bind context
Install cryptography, generate a key, encrypt a record with associated data naming the user ID, and decrypt it. Then try three tamper tests: change a ciphertext byte, change the associated data to another user ID, and change the nonce. Each must raise InvalidTag.
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.exceptions import InvalidTag
key = AESGCM.generate_key(bit_length=256)
aead = AESGCM(key)
nonce = os.urandom(12)
aad = b'user_id=1001'
ct = aead.encrypt(nonce, b'card ending 4242', aad)
print(aead.decrypt(nonce, ct, aad))
for label, n, c, a in [
('ciphertext', nonce, bytes([ct[0] ^ 1]) + ct[1:], aad),
('aad', nonce, ct, b'user_id=1002'),
('nonce', os.urandom(12), ct, aad),
]:
try:
aead.decrypt(n, c, a)
except InvalidTag:
print('tamper detected:', label)
record = nonce + ct # store nonce alongside ciphertextKey handling
The key is the secret. Generate it with a CSPRNG, store it in a KMS or secret manager, and use envelope encryption for data at rest: a data key encrypts the data, and a KMS key encrypts the data key. Derive keys from passwords only through a password KDF such as Argon2id or scrypt, never by hashing the password once.
Diagram
What AES-GCM checks on decrypt
Change anything and decryption fails.
Original
nonce N, ciphertext C, AAD user_id=1001
Plaintext returned
Tampered AAD
nonce N, ciphertext C, AAD user_id=1002
InvalidTag
Reused nonce
Same key and nonce for two messages
Both decrypt, but security is broken
Check yourself
1. What is the most important rule for GCM nonces?
2. Decrypting with different associated data than was used to encrypt results in what?
3. Where should the nonce be stored?