ASVS 5.0 V11.3.4 (Level 3) requires that nonces, initialization vectors and other single use numbers are never used for more than one key and data element pair, and that they are generated by a method suited to the algorithm in use.
Why it matters
Reuse is catastrophic for several common modes. With AES GCM, encrypting two messages under the same key and nonce reveals the XOR of the plaintexts and lets an attacker forge authentication tags. With CTR style modes the keystream repeats and the same XOR problem appears. With CBC a predictable IV enables chosen plaintext attacks.
The bug is silent. Every test passes, and the flaw sits in your data until someone looks.
How to implement it
First learn what your mode needs. The rules differ:
| Mode | Requirement |
|---|---|
| AES GCM | Unique per key. 96 bit random is acceptable for limited volume, a counter is better at scale |
| ChaCha20 Poly1305 | Unique per key |
| AES CBC | Unpredictable random IV per message |
| AES GCM SIV | Misuse resistant, tolerates repeats better |
Random 96 bit nonces under one key are safe only up to a limit on the number of messages; rotating keys keeps you under it. For heavy workloads use a counter per key, or derive per message keys.
Simple random pattern for moderate volume:
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err // never continue with a zero nonce
}
sealed := gcm.Seal(nonce, nonce, plaintext, nil)
Counter pattern, where state must be durable and unique per key:
# one counter per key id, incremented atomically in the datastore
n = store.incr(f"nonce:{key_id}")
nonce = n.to_bytes(12, "big")
Never derive a nonce from a timestamp, a user id or a record id alone. Never copy an example's constant IV into production code. Store the nonce with the ciphertext; it need not be secret.
After restoring from a backup or cloning a virtual machine, a counter can rewind. Plan for that: include a random per instance prefix, or rotate to a fresh key after any restore.
How to test it
Review every encrypt call site and answer three questions: where does the nonce come from, is it unique per key, and what happens after restart or restore? Search for constants such as new byte[12] with no random fill, iv = b"0000", and fixed strings.
Then run a statistical check on stored data: extract the nonce prefix of every ciphertext under one key and assert no duplicates. For high volume systems also compute the message count per key and compare it to the algorithm limit.
Common mistakes
- A hard coded IV copied from a tutorial
- A zero nonce from an uninitialised buffer when the random call fails
- Counters that reset on restart
- Same nonce and key reused across two services
- Using CBC with a sequential IV
Quick check
What happens if AES GCM reuses a nonce under one key?
Confidentiality leaks and attackers can forge tags, so integrity is lost too.
Must a nonce be secret?
No. It must be unique, and it is stored with the ciphertext.
Sources
- OWASP ASVS
- NIST SP 800-38D, GCM recommendation