Skip to content

VOL 08 / CH 01 / LESSON 01

1.1 Symmetric Cryptography Engineering: AEAD, Nonce, and Key Lifecycle ​

To assess a cryptographic guarantee, first identify the data an attacker can access and the components they can control. Before choosing an algorithm, determine what needs protection, where keys reside, and how much data a key compromise would expose.

Clarify Security Objectives ​

ObjectiveWhat it preventsCommon primitives
ConfidentialityUnauthorized access to plaintextEncryption
IntegrityData being silently modifiedMAC, AEAD, digital signatures
AuthenticationImpersonation of messages or endpointsMAC, signatures, authentication protocols
Evidence for non-repudiationGive third parties verifiable evidence about a signature’s originDigital signatures, identity binding, and audit records

Signature evidence still depends on key custody, identity binding, and event timing; successful verification does not automatically identify the actual operator.

Encryption alone does not guarantee integrity. If an attacker can modify ciphertext and the application processes the decrypted output before authentication, vulnerabilities such as padding oracle attacks, format probing, or manipulation of business fields may arise. Modern applications should prioritize using standardized AEAD schemes over manually combining "encryption + hash."

AEAD Simultaneously Protects Ciphertext and Associated Context ​

Authenticated Encryption with Associated Data (AEAD) accepts:

text
ciphertext, tag = Seal(key, nonce, plaintext, aad)
plaintext       = Open(key, nonce, ciphertext, aad, tag)

plaintext is encrypted; aad is not encrypted but is included in the authentication process. For example, message version, tenant ID, record primary key, and protocol type can be placed in the AAD, so ciphertext fails authentication under a different expected context. The decryptor must derive expected AAD from a trusted request or resource lookup. Accepting a tenant field moved along with the ciphertext does not check the intended tenant. AEAD also accepts an unchanged replay; sequence numbers, expiry, or deduplication state remain protocol responsibilities.

Common choices include AES-GCM and ChaCha20-Poly1305. Do not select based on which name sounds more secure; instead, prefer platform-level APIs, protocol-defined cipher suites, and well-maintained libraries. The security of the algorithm also depends on the nonce, key, error handling, and side-channel protections in the implementation.

Nonce Does Not Need to Be Secret, But Must Satisfy Algorithm Constraints ​

The core requirement for a nonce is that it must not repeat under the same key. For example, in GCM mode, reusing a nonce with the same key can leak relationships between plaintexts and compromise message authentication, consequences far more severe than merely having a slightly weaker randomness.

text
key K + nonce N1 → message A
key K + nonce N1 → message B   ← Dangerous: reusing the same key/nonce pair

Nonce generation strategies can include sufficiently large random values or a monotonic counter per key. The choice depends on the protocol. Counters must survive system crashes and prevent duplicate usage across multiple nodes; random schemes require collision budget calculations. Time stamps, short random numbers, or business IDs should never be used as substitutes without clear knowledge of the cryptographic algorithm's requirements.

Decryption APIs must first validate the authentication tag before returning the plaintext to the caller. On authentication failure, a uniform error message should be returned, never expose specific errors such as "format error," "padding error," or "tag error" as detectable branches.

Run a Tampering and Context Test ​

This example requires Python’s cryptography package and uses its AESGCM API. The returned bytes already contain a 16-byte tag; no additional hash is needed. Each run generates a fresh key, encrypts one message, and checks rejection of altered ciphertext or AAD. Production key storage and cross-process nonce allocation need separate implementation.

python
import os
from cryptography.exceptions import InvalidTag
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

key = AESGCM.generate_key(bit_length=256)
aead = AESGCM(key)
nonce = os.urandom(12)
aad = b'{"tenant":"acme","record":42,"version":1}'
plaintext = b"invoice total: 125"
ciphertext = aead.encrypt(nonce, plaintext, aad)
assert aead.decrypt(nonce, ciphertext, aad) == plaintext
assert len(ciphertext) == len(plaintext) + 16

altered = ciphertext[:-1] + bytes([ciphertext[-1] ^ 1])
for candidate, context in (
    (altered, aad),
    (ciphertext, b'{"tenant":"other","record":42,"version":1}'),
):
    try:
        aead.decrypt(nonce, candidate, context)
    except InvalidTag:
        pass
    else:
        raise AssertionError("unauthenticated data was accepted")

Separate Key Access from Data Access ​

Storing encryption keys in source code, images, the same database field, or standard environment configurations is like taping a key to a safe, exposing the mechanism that unlocks it. Production systems typically use envelope encryption:

text
KEK in KMS/HSM
       │ wrapped
       ▼
Random DEK per batch or per object ──AEAD──> Business data

The Data Encryption Key (DEK) handles frequent encryption of data; the Key Encryption Key (KEK) remains securely stored in a KMS or HSM and is only used to wrap the DEK. This limits master-key exposure and allows DEKs to be rewrapped without re-encrypting large objects. KMS logs record operations requested from KMS. Every local decryption with an application-cached DEK does not automatically produce a KMS event; application auditing remains necessary.

Changing a KEK does not automatically rewrap existing DEKs; migration depends on the KMS API. If a KEK or DEK has been compromised, rewrapping cannot reclaim keys or old ciphertext already obtained by an attacker. Incident response may require new DEKs and data re-encryption.

Encrypted data must be self-describing but not reveal sensitive information. It should store: cipher suite, format version, key ID, nonce, ciphertext, and tag. Avoid storing just a raw byte array, years later, no one will know which key or parameters were used to decrypt it.

Rotation Does Not Mean Immediate Deletion of Old Keys ​

A key's lifecycle includes at least the following stages: generation, distribution, activation, usage, rotation, revocation, archiving, and destruction. Rotation typically follows these practices:

  1. New writes use the new key version;
  2. Reads select the old key based on the key ID recorded in the record;
  3. Background re-encryption occurs gradually, or key migration happens only during read operations;
  4. The old key is disabled only after confirming recovery requirements for all backups and delayed replicas;
  5. Finally, the key is permanently destroyed in accordance with retention policies.

If an old key is deleted before its backup expires, the backup remains but becomes unrecoverable. If an old key is never retired, rotation fails to reduce its long-term exposure.

Cryptographically Secure Pseudo-Random Number Generation Is Mandatory ​

Keys, salts, session IDs, recovery codes, and nonces using a random strategy should use an operating-system or cryptographic-library CSPRNG. A protocol-assigned unique counter nonce need not be random; uniqueness and unpredictability are different requirements. Standard pseudo-random number generators, timestamps, process IDs, and homegrown "obfuscation algorithms" are unsuitable for producing secrets.

Do not implement AES, ChaCha, GCM, or random number generators from scratch. Even if the mathematical formulas are correct, vulnerabilities can arise from issues such as constant-time execution, error handling, memory clearing, nonce management, and hardware-accelerated implementation paths.

Design Review ​

  • Does the threat model require confidentiality, integrity, authentication, or all three?
  • Are higher-level AEAD APIs used instead of ECB/CBC modes or custom combinations?
  • How is the uniqueness and monitoring of nonces under the same key ensured?
  • Is the AAD bound to tenant, version, and object identity to prevent ciphertext smuggling?
  • Is the key isolated from the ciphertext and protected by the principle of least privilege and auditability?
  • Are key rotation, backup recovery, and old key destruction exercised together in practice?
  • Is authentication failure handled by terminating processing before any plaintext is accessed?

Continue with Hashes, MACs, KDFs, and Password Storage.

References ​

Built with VitePress | Software Systems Atlas