Modern authenticated encryption can protect both the confidentiality and integrity of data, but some algorithms depend on a small operational rule that is easy to overlook: do not reuse a nonce with the same key.

A nonce is a value supplied to a cryptographic operation for a particular invocation. The word comes from “number used once,” but a nonce is not necessarily secret and is not necessarily a simple counter. What matters is the requirement of the algorithm using it. For widely used authenticated-encryption schemes such as AES-GCM and ChaCha20-Poly1305, nonce reuse under the same key can invalidate important security guarantees.

That makes nonce generation an engineering problem, not merely a cryptographic parameter. Restarts, multiple application instances, restored backups, counter overflow, and poorly coordinated workers can all create reuse even when each individual process appears correct.

This article builds a practical mental model for nonce uniqueness, shows where reuse comes from, and explains how to choose a design that remains correct across the lifetime of an encryption key.

Think in key-and-nonce pairs

The simplest useful rule is:

For each encryption key K:
    never use the same nonce N for two different encryption invocations

The important unit is the pair (key, nonce). A nonce may be used again after changing to an independent key if the algorithm permits that. Reusing a nonce while keeping the same key is the dangerous case.

This distinction matters because developers sometimes try to make every nonce globally unique forever. That is usually unnecessary. The real boundary is the key under which the nonce is interpreted.

Suppose an application encrypts records using one key:

record A: key K1, nonce 000001
record B: key K1, nonce 000002
record C: key K1, nonce 000003

This simplified counter design gives each invocation a distinct nonce. If the application later rotates to K2, the nonce sequence may be able to start again under that new key, depending on the chosen construction:

record D: key K2, nonce 000001

The repeated number is not itself the problem because the key-and-nonce pair is different.

Why reuse is more serious than a duplicate identifier

A nonce is not just metadata attached to ciphertext. Authenticated-encryption algorithms incorporate it into their cryptographic computation. Their security analysis therefore assumes that applications obey the nonce requirements defined for the algorithm.

For AES-GCM, NIST requires IV uniqueness for invocations under a given key. GCM uses the term initialization vector (IV); in this context, the IV serves as a nonce. NIST also recommends 96-bit IVs for GCM because that size supports a simple and efficient processing path.

For the IETF construction of ChaCha20-Poly1305, the 96-bit nonce must not be repeated for the same key. Repetition can cause the same ChaCha20 keystream and one-time Poly1305 key material to be derived again. That can expose relationships between plaintexts and undermine authentication.

The exact failure differs between constructions, so it is better not to memorize one attack and generalize it to every cipher. The reusable defensive lesson is simpler: when an authenticated-encryption scheme requires nonce uniqueness, reuse violates an assumption on which its confidentiality or integrity guarantees depend.

Changing the ciphertext format, hiding the nonce, or encrypting the nonce does not repair that violated assumption.

A nonce usually does not need to be secret

Developers sometimes treat a nonce like a password because both appear next to a cryptographic key. They serve different purposes.

A key must remain secret from unauthorized parties. A nonce commonly travels alongside the ciphertext so that the decrypting side can supply the same value to the decryption operation:

stored object:
    key_id: 42
    nonce: <nonce bytes>
    ciphertext: <encrypted bytes>
    tag: <authentication tag>

This is a conceptual format, not a universal serialization standard. The cryptographic library may combine the ciphertext and authentication tag, and protocols may encode fields differently.

Publishing a nonce is normally compatible with the security model of AES-GCM and ChaCha20-Poly1305. Reusing it with the same key is the problem.

This distinction leads to a useful design principle: spend engineering effort on guaranteeing the required nonce property rather than trying to conceal the nonce.

Randomness and uniqueness are different properties

A random value can repeat. A unique value does not have to be unpredictable.

That difference is easy to miss because cryptographic APIs often expose a byte array named nonce or iv, and generating random bytes feels like the natural answer. Whether random generation is appropriate depends on the algorithm, nonce size, invocation limits, and the guarantees of the random-number generator.

A counter illustrates the distinction clearly. If one process owns a counter and increments it without rollback, the sequence

1, 2, 3, 4, ...

is predictable but unique within that counter’s domain. Predictability alone is not a defect when the encryption construction requires uniqueness rather than unpredictability.

Random nonces instead provide a probability of avoiding collisions. For some constructions and operational limits that probability can be acceptable; for others, specifications impose stricter generation rules. Do not replace an algorithm’s nonce-generation requirements with a generic “use randomness” rule.

The safest application-level decision is usually to use a well-maintained cryptographic library or protocol that defines nonce handling, rather than inventing a new construction around a low-level primitive.

The hard part is preserving uniqueness across system boundaries

A counter looks easy until more than one process can encrypt with the same key.

Imagine two application instances start with the same key and each keeps an in-memory counter:

instance A -> K1, nonce 1
instance B -> K1, nonce 1

Both instances behaved correctly according to their local state, but the system reused (K1, 1).

The same problem appears after a restart if a counter resets to zero, after restoring an old database snapshot, or when a cloned virtual machine resumes with copied nonce state. A design that is unique only during one process lifetime is not sufficient when the key survives longer than that process.

This is why the trust boundary for nonce generation must match the scope of the key. Ask:

Which components can encrypt with this key?
Which component owns the nonce space?
Can its state move backward or be duplicated?

If several writers share one key, they need a coordinated way to avoid collisions. One approach is a durable centralized counter. Another is to partition the nonce space so that each writer has a distinct prefix and maintains its own counter within that prefix. The exact layout must fit the nonce construction permitted by the chosen algorithm or protocol.

Partitioning is useful only if writer identifiers themselves cannot collide. Giving every worker the same default prefix simply moves the reuse problem to another field.

Restarts and restores deserve explicit design

Persistent counters reduce restart risk only if updates are durable enough for the failure model.

Consider this sequence:

1. read counter value 50
2. encrypt using nonce 50
3. return ciphertext to another system
4. persist counter value 51

A crash between steps 3 and 4 can expose ciphertext using nonce 50 while durable state still says that 50 is available. After restart, the application may reuse it.

Changing the order can create different trade-offs. Reserving nonce values durably before encryption can avoid reuse at the cost of leaving gaps after crashes. Gaps are normally harmless for a uniqueness requirement; reuse is not.

Backup restoration creates a related problem. If a backup contains both an encryption key and an old nonce counter, restoring both can rewind the pair to a previous state. A system that may restore cryptographic state should therefore have an explicit recovery strategy. Depending on the design, that might mean rotating to a new encryption key after rollback, allocating nonce ranges from state that is not rolled back with the workload, or using a higher-level encryption service that manages this state.

The correct choice depends on the system, but “the counter is persisted somewhere” is not enough. Its rollback behavior matters.

Key rotation is part of nonce lifecycle management

Nonce exhaustion and uncertainty should be treated as key-lifecycle events.

A finite nonce field has a finite space. Counter-based designs must stop before wrapping around. Randomized constructions can have invocation limits based on collision probability or on requirements defined by the encryption specification. Some standards also impose limits beyond simple nonce counting.

Do not let integer overflow silently turn a large counter back into an earlier value. Track the number of encryptions under each key and establish a rotation point before any construction-specific limit is reached.

Key rotation is also a useful recovery boundary when nonce state can no longer be trusted. If an operator cannot establish whether a restored or corrupted counter has already produced a value under the current key, continuing from a guessed value creates unnecessary risk. Moving future encryption to a new independent key creates a new key-and-nonce namespace.

Rotation does not repair ciphertext that was already produced with a repeated nonce. It limits future exposure.

Keep nonce state with the ciphertext

Decryption needs the same nonce that encryption used. Store or transmit it explicitly unless the protocol defines another reliable derivation method.

Do not attempt to reconstruct a nonce from mutable properties such as a record’s current row number, display name, or storage position. If that property changes, decryption can fail. If it is reused, encryption may repeat a nonce under the same key.

A practical encrypted record often needs at least enough information to identify:

which key version was used
which nonce was used
which ciphertext and authentication tag belong together

Applications may also authenticate unencrypted context as additional authenticated data (AAD). AAD is useful for binding metadata to ciphertext, but it does not replace nonce uniqueness.

Keeping a key identifier also helps rotation. Old records can remain decryptable with an older key while new records use a new key, without guessing which key belongs to each ciphertext.

Test failure conditions, not just successful encryption

A round-trip test proves only that one encryption can be decrypted. It does not prove that the nonce lifecycle is correct.

Test the operational events that can break uniqueness. For a counter-based design, useful tests include restarting an encrypting process, running several writers concurrently, approaching the counter limit, restoring persisted state, and rotating keys. The expected result should be either a provably unused nonce or a deliberate refusal to encrypt.

Instrumentation can help as well. Track encryption counts by key identifier, key age, rotation status, and failures in nonce allocation. Avoid logging secret keys or plaintext. Logging every nonce is usually unnecessary, but systems with coordinated allocation may benefit from monitoring allocated ranges and detecting overlapping ownership.

For high-value systems, make nonce allocation an explicit component with documented invariants rather than a few lines of incidental code near the encryption call.

Know what nonce uniqueness does not solve

Correct nonce handling protects one important assumption of the encryption construction. It does not make the rest of the system secure.

An attacker who obtains the encryption key may still decrypt protected data. A compromised application with legitimate access to the key may encrypt attacker-controlled content or decrypt records. Weak key storage, incorrect authorization, unauthenticated metadata, poor randomness where randomness is required, and unsafe error handling remain separate concerns.

Nonce uniqueness also does not choose an appropriate encryption algorithm for you. Use authenticated encryption through a mature cryptographic library, follow that library’s API contract, and follow the selected algorithm’s specification. If a higher-level API manages nonces internally, do not bypass that mechanism merely to gain more control.

There are also specialized nonce-misuse-resistant authenticated-encryption designs that reduce the consequences of accidental nonce reuse. They can be valuable where uniqueness is difficult to guarantee, but they have their own requirements and do not justify ignoring API contracts. Choosing such a construction is a cryptographic design decision, not a substitute for understanding state management.

A practical decision process

When an application needs authenticated encryption, start by asking whether a high-level library, envelope-encryption service, or established protocol can manage the cryptographic details. Prefer that when it fits the threat model and operational requirements.

If the application must manage nonces itself, identify the exact authenticated-encryption construction and read its nonce requirements. Then define the lifetime and scope of each encryption key. Only after those boundaries are clear should you choose a nonce strategy.

For a single durable writer, a specification-compatible counter can be straightforward. For several writers, coordinate allocation or partition the permitted nonce space so writers cannot overlap. For workloads that can be cloned or rolled back, include those events in the design rather than treating them as rare exceptions. Rotate the key before nonce-space limits are reached, and rotate when nonce state becomes untrustworthy.

Finally, test the invariants under concurrency, crashes, restores, and rotation. The important question is not “Does encryption work?” It is “Can any two encryption invocations under this key receive the same nonce?”

Conclusion

Nonce uniqueness is a small cryptographic requirement with a large operational footprint. For authenticated-encryption schemes that require unique nonces, the useful mental model is the key-and-nonce pair: each encryption under one key needs a nonce that has not been used with that key before.

The cryptographic call is usually the easy part. The defensive work is making that invariant survive multiple writers, restarts, backup restores, counter limits, and key rotation. Define who owns the nonce space, preserve or partition that state deliberately, refuse to encrypt when uniqueness cannot be established, and treat uncertain nonce state as a reason to move future encryption to a new key.

When those decisions are explicit, nonce management becomes a property you can reason about and test instead of an assumption hidden inside an encryption helper.