Safeguard
Application Security•

ASVS 5.0 V11.2.2: Design for Crypto Agility

ASVS V11.2.2 requires that algorithms, key sizes and keys can be swapped without a rewrite. Versioned envelopes, config driven choices and a re encryption path.

Safeguard Security Team
3 min read

ASVS 5.0 V11.2.2 (Level 2) requires crypto agility: you must be able to reconfigure, upgrade or swap random number, authenticated encryption, MAC and hashing algorithms, key lengths, rounds, ciphers and modes at any time. You must also be able to replace keys and passwords and re encrypt data, which prepares the ground for post quantum cryptography.

Why it matters

Every algorithm is eventually retired. Teams that hard coded one choice into ten services learn this during an incident, when the fix is a multi week migration. Agility turns that into a configuration change and a background job.

How to implement it

Three design moves cover most of the requirement.

First, version every ciphertext. Store a small header that names the scheme and key id, so old and new data can coexist.

import os, json, base64
from cryptography.hazmat.primitives.ciphers.aead import AESGCM, ChaCha20Poly1305

SCHEMES = {
    1: lambda k: AESGCM(k),
    2: lambda k: ChaCha20Poly1305(k),
}
CURRENT = 1

def seal(keyring, data: bytes) -> bytes:
    kid, key = keyring.current()
    nonce = os.urandom(12)
    ct = SCHEMES[CURRENT](key).encrypt(nonce, data, None)
    head = json.dumps({"v": CURRENT, "kid": kid}).encode()
    return len(head).to_bytes(2, "big") + head + nonce + ct

def open_(keyring, blob: bytes):
    n = int.from_bytes(blob[:2], "big")
    head = json.loads(blob[2:2 + n])
    key = keyring.get(head["kid"])
    nonce, ct = blob[2 + n:14 + n], blob[14 + n:]
    return SCHEMES[head["v"]](key).decrypt(nonce, ct, None)

Second, move choices to configuration and a registry, not scattered literals. One module decides the algorithm; every caller asks it.

Third, build the re encryption path before you need it. A job reads each record, opens it with its recorded scheme and key, and seals it with the current one. Make it resumable and rate limited, and log counts without logging plaintext.

Keys and passwords need the same treatment. Support multiple active keys with ids so rotation does not break reads, and store password hash parameters with each hash so you can upgrade on next login.

How to test it

Run a drill in a test environment. Change the current scheme number or key, deploy, and confirm that new writes use it while old data still reads. Run the re encryption job and verify every record now carries the new header. Time the whole exercise; if it takes more than a day of engineering effort, agility is not real yet.

Also confirm that retiring a key is possible: remove the old key from the ring after migration and check that nothing fails.

Common mistakes

  • No version marker, so data cannot be told apart after a change
  • Algorithm names hard coded in many files
  • A key id missing from stored data, so rotation orphans old records
  • Declaring agility in a design doc but never rehearsing the migration
  • Re encrypting by loading the whole table into memory

Quick check

What is the cheapest first step toward agility?

Add a version and key id header to everything you encrypt.

Why rehearse a migration?

It proves the swap works while you are calm, not during an incident.

Sources

  • OWASP ASVS
  • NIST Post Quantum Cryptography project

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.