Skip to content

Nonce Guard

AES-GCM Β· AES-GCM-SIV Β· nonce misuse (AEAD)

Reuse a (key, nonce) pair and watch AES-GCM leak the plaintext XOR, recover its GHASH key via Joux's forbidden attack, and forge tags β€” while AES-GCM-SIV degrades gracefully.

A β€” What is a Nonce?

A1 β€” The one rule

A nonce (number used once) in AEAD schemes is a per-encryption value that must be unique for every encryption under the same key. It does not need to be secret. AES-GCM uses a 96-bit nonce (12 bytes). The security proof assumes the nonce is never reused β€” and the rest of this page is what happens when that one rule is broken.

NIST SP 800-38D calls it an Initialization Vector (IV); RFC 5116 calls it a nonce β€” same concept.

96-BIT NONCE (12 BYTES)

No theory yet β€” go break it first. Head to Section B, flip the nonce toggle, and watch AES-GCM leak. Then Section C explains the algebra behind what you just saw.

B β€” See It Break (Live Attack Demo)

Encrypt two messages under AES-GCM and AES-GCM-SIV with the same key. Leave the nonce toggle on to reuse the nonce, then run the attack. Level 1 uses your messages and reveals only their XOR, not either plaintext. Level 2 also runs on your messages: it factors the key equation their two (ciphertext, tag) pairs produce, recovers the GHASH key H, and forges a tag real AES-GCM accepts. Curiosity first: see the break here, then jump to Section C for why it happened.

First plaintext message for encryption
Second plaintext message for encryption

πŸ”“ AES-GCM Vulnerable

Click "Encrypt Both" to begin

πŸ”’ AES-GCM-SIV Resistant

Click "Encrypt Both" to begin

C β€” Why It Breaks

You just watched AES-GCM leak. Here is the algebra behind it. Two levels: the first cracks confidentiality (recovering the plaintexts), the second cracks integrity (forging tags). Each turns on one idea β€” a shared secret term that cancels when you XOR two things together. The animations below show that cancellation happen.

C1 β€” Level 1: keystream reuse (confidentiality)

AES-GCM encrypts by XORing plaintext with a keystream β€” a pseudorandom pad derived from (key, nonce, counter). The keystream depends only on the key and nonce, not on the message. So if you reuse a (key, nonce) pair, both messages get XORed with the same pad:

The same keystream (highlighted) sits in both ciphertexts.

Because x βŠ• x = 0, the identical keystream blocks annihilate each other. What's left is C₁ βŠ• Cβ‚‚ = P₁ βŠ• Pβ‚‚ β€” the XOR of the two plaintexts, with no key involved at all. In practice that often recovers both messages (via crib-dragging / known structure). This is exactly the Recovered P₁ βŠ• Pβ‚‚ you saw in Section B.

C2 β€” Level 2: authentication-key recovery (integrity)

GHASH β€” a keyed checksum built from multiply-add in a special number system. GF(2¹²⁸) β€” arithmetic on 128-bit blocks where "add" is just XOR. The mask β€” a per-nonce pad AES adds on top of the checksum so the tag looks random.

AES-GCM's tag is that GHASH checksum, keyed by H = E_K(0¹²⁸) (fixed by the key alone), with the mask XORed on top. For a 96-bit nonce:

tag = GHASHH(AAD, C) βŠ• EK(nonce β€– 0Β³ΒΉ1) └─ the mask β”€β”˜

The mask E_K(nonce β€– 0Β³ΒΉ1) depends only on (key, nonce) β€” so, just like the keystream in Level 1, two tags under the same nonce carry the identical mask. XOR the two tags and it cancels:

The same mask (highlighted) sits in both tags.

With the mask gone, what remains is one equation in the single unknown H over GF(2¹²⁸). The picture above shows the simplest shape of it β€” two equal-length single blocks, where it collapses to tag₁ βŠ• tagβ‚‚ = (C₁ βŠ• Cβ‚‚)Β·HΒ² and H = √((tag₁ βŠ• tagβ‚‚)Β·(C₁ βŠ• Cβ‚‚)⁻¹) falls out directly. In general your messages span several blocks and the equation is the full polynomial:

tag₁ βŠ• tagβ‚‚ = Ξ”C₁·Hn+1 βŠ• Ξ”Cβ‚‚Β·Hn βŠ• … βŠ• Ξ”CnΒ·HΒ² βŠ• Ξ”LΒ·H └─ degree n+1 in the unknown H β”€β”˜

β€” one term per 16-byte ciphertext block, plus the length block. That is a real polynomial, and recovering H means factoring it over GF(2¹²⁸). Section B does exactly that on your two messages: gcd with x2¹²⁸ βˆ’ x isolates the roots that live in the field, then characteristic-2 Cantor–Zassenhaus trace splitting separates them. Usually one root comes back; sometimes a handful, and one forged tag per candidate settles which is the real key. Recover H and you also recover the mask, and then you can forge a valid tag for any ciphertext under that nonce β€” this is Joux's "forbidden attack." Not theoretical: a 2016 survey (BΓΆck, Zauner, Devlin et al.) found production HTTPS servers reusing AES-GCM nonces, exposed to exactly this.

Two honest details about the demo. First, building a forged ciphertext β€” rather than just a valid tag over random bytes β€” assumes the attacker knows Message 1, the same known-plaintext assumption Level 1 already leans on, because the keystream is recovered as C₁ βŠ• P₁. Second, the root search runs in your browser tab and its cost grows with the square of the polynomial degree, so this lab caps it at 512 bytes per message; past that Level 2 declines to run and says so instead of pretending. That cap is a budget of this page, not a limit of the attack. Type the same text into both boxes to meet the other, genuine limit: identical ciphertexts make the equation 0 = 0 and nothing at all is learned about H.

D β€” SIV: Safe by Design

AES-GCM-SIV closes both holes with one idea: make the IV depend on the plaintext, and AES-encrypt the checksum before anyone sees it. Below: the construction, why the tag-cancellation trick dies, and an interactive panel where you can watch plaintext β†’ tag β†’ IV β†’ ciphertext move together.

D0 β€” The synthetic-IV construction (RFC 8452)

AES-GCM-SIV uses a synthetic IV approach:

  1. Derive a per-nonce authentication key and encryption key from the master key and nonce
  2. Hash the plaintext (and AAD) with POLYVAL (a keyed checksum, GHASH's twin in a little-endian-friendly field) β€” then XOR in the nonce and AES-encrypt the result to produce the tag
  3. Use that tag (top bit set) as the synthetic IV for AES-CTR encryption

The IV is derived from the plaintext itself. If the same (key, nonce) pair encrypts two different plaintexts, the two IVs differ, so the two keystreams differ β€” the Level 1 cancellation can never happen. Nonce reuse degrades to leaking only whether two plaintexts were identical.

D1 β€” Key Derivation

AES-GCM-SIV derives per-nonce keys from the master key. For each counter it AES-encrypts the block counter β€– nonce (counter is a 32-bit little-endian value; nonce is 96 bits) and keeps the low 8 bytes of the output, concatenating them:

For a 128-bit master key the encryption key uses only counters 2–3 (two blocks).

D2 β€” POLYVAL Authentication (why the cancel trick dies)

POLYVAL β€” a keyed checksum, GHASH's twin, defined in RFC 8452 in a little-endian-friendly version of the same GF(2¹²⁸) field (add = XOR).

Its key H is derived per-nonce from (key, nonce). But the decisive difference from AES-GCM is the final step: the POLYVAL result is AES-encrypted before it is output as the tag.

S = POLYVAL(H, AAD, PT); XOR nonce into bytes 0–11; clear MSB of byte 15
tag = AESKenc(S)

Recall the Level 2 break in Section C: subtracting two tags cancelled the mask and left a clean polynomial in H. Here the attacker only ever sees AESKenc(S) β€” never POLYVAL's raw output S. XORing two tags now XORs two AES outputs, which linearizes into nothing: the AES pseudorandom permutation shreds the polynomial structure. So even reusing a nonce (which does reuse H) never leaks the authentication key.

D3 β€” Tag-as-IV, Live: plaintext β†’ tag β†’ IV β†’ ciphertext

SIV's whole safety property is the IV depends on the plaintext. The 128-bit tag (the AES-encrypted POLYVAL result) is the initial AES-CTR counter block β€” copied verbatim, with the most significant bit of its last byte set to 1 (RFC 8452 Β§4). That bit is the same one cleared on the tag-generation input, so the tag value can never collide with a CTR counter. Watch all four values move together as you type β€” the tag changes, so the IV changes, so the very first ciphertext block changes.

Type any message to see how the AES-GCM-SIV tag, derived AES-CTR IV, and first ciphertext block all change together

Compare against a second message B:

E β€” When to Use Which

E1 β€” Comparison Table

Comparison of AES-GCM and AES-GCM-SIV properties
PropertyAES-GCMAES-GCM-SIV
StandardNIST SP 800-38DRFC 8452
Nonce size96 bits96 bits
Nonce reuse consequenceKey recovery + full plaintext XORIdentical plaintext detection only
Performance~1 pass~2 passes (extra POLYVAL)
FIPS approvedYesNo β€” an IRTF CFRG spec (RFC 8452), not a NIST mode
Hardware accelerationAES-NI + PCLMULQDQAES-NI + PCLMULQDQ
Online encryptionYesNo β€” must buffer full plaintext
Deployed inTLS 1.3, QUIC, IPsecGoogle internal, QUIC experiments
Recommendationβœ… Nonce uniqueness guaranteedβœ… Nonce uniqueness uncertain

E2 β€” Decision Guidance

Scenario 1 β€” Single server, sequential encryption

AES-GCM with a counter nonce is safe. Nonce uniqueness is easy to guarantee. No need for SIV overhead.

Scenario 2 β€” Distributed system, multiple encryptors

Coordinating nonces across servers is error-prone. AES-GCM-SIV degrades gracefully on accidental reuse. Use SIV or use random 96-bit nonces with AES-GCM (collision risk is low at < 2Β³Β² messages per key).

Scenario 3 β€” Key wrapping and key storage

AES-GCM-SIV is designed for this β€” short messages, repeated access to the same key, nonce coordination difficult. Google's Tink library uses it for key wrapping.

E3 β€” Honest Limitations of AES-GCM-SIV

  • Not FIPS-approved β€” cannot use in FIPS 140-2/3 contexts
  • No online encryption β€” must have full plaintext before starting (not suitable for streaming)
  • Two passes over the data β€” the tag must be computed before encryption can begin, so throughput can approach half that of single-pass AES-GCM on long messages, though hardware-optimized (AES-NI + PCLMULQDQ) implementations narrow the gap
  • Not yet widely deployed β€” operational experience is thin compared to AES-GCM