{"id":333,"date":"2026-09-05T03:18:46","date_gmt":"2026-09-05T03:18:46","guid":{"rendered":"https:\/\/neodata.com.uy\/labs\/a-nonce-is-not-a-footnote\/"},"modified":"2026-09-07T15:41:47","modified_gmt":"2026-09-07T15:41:47","slug":"a-nonce-is-not-a-footnote","status":"publish","type":"post","link":"https:\/\/neodata.com.uy\/labs\/a-nonce-is-not-a-footnote\/","title":{"rendered":"A nonce is not a footnote."},"content":{"rendered":"<p><!--\nWORDPRESS CONTENT: editorial notes removed\n--><\/p>\n<p class=\"nlabs-kicker\">AUTHENTICATED ENCRYPTION<\/p>\n<h2 id=\"why-encryption-can-fail-when-counters-restarts-and-writers-are-treated-as-plumbing\">Why encryption can fail when counters, restarts and writers are treated as plumbing.<\/h2>\n<p>A nonce is usually visible, often stored beside the ciphertext and sometimes reconstructed from a message sequence number. It does not need to be secret.<\/p>\n<p>That makes it easy to treat as incidental metadata.<\/p>\n<p>For many authenticated-encryption constructions, the nonce is part of the security contract. Repeating it with the same key can disclose relationships between plaintexts, weaken authentication or make forgery practical. The primitive may be implemented perfectly while the surrounding system violates the assumption on which its security depends.<\/p>\n<p>The difficult question is not how to create twelve bytes. It is how to guarantee the required property across processes, devices, crashes, backups, rollbacks and years of operation.<\/p>\n<h2 id=\"authenticated-encryption-has-four-inputs\">Authenticated encryption has four inputs<\/h2>\n<p>The AEAD interface described in RFC 5116 makes the contract explicit. Encryption receives:<\/p>\n<ol type=\"1\">\n<li>a secret key <code>K<\/code>;<\/li>\n<li>a nonce <code>N<\/code>;<\/li>\n<li>plaintext <code>P<\/code>;<\/li>\n<li>associated data <code>A<\/code>.<\/li>\n<\/ol>\n<p>It returns a ciphertext containing an authentication tag. Decryption receives the corresponding values and must return either the complete plaintext or failure.<\/p>\n<p>This interface protects confidentiality and integrity together. Associated data lets a protocol authenticate fields that must remain visible, such as version, routing information, record type, sequence number or key identifier.<\/p>\n<p>The interface does not provide replay protection, access control or a naming convention for messages. Those remain protocol responsibilities.<\/p>\n<p>Security therefore depends on more than choosing AES-GCM or ChaCha20-Poly1305. It depends on constructing <code>N<\/code> and <code>A<\/code> so that the cryptographic result belongs to the intended record and cannot be confused with another one.<\/p>\n<h2 id=\"uniqueness-is-a-system-property\">Uniqueness is a system property<\/h2>\n<p>For nonce-based AEAD, the usual requirement is uniqueness for every encryption performed under one key. The same nonce may be used under a different independent key, but the pair <code>(K, N)<\/code> must not repeat.<\/p>\n<p>An encryption function cannot generally enforce this rule by itself. It sees only the current call. It does not know whether another process, device or earlier software version has already used the same value.<\/p>\n<p>The source of uniqueness may be:<\/p>\n<ul>\n<li>a monotonically increasing counter;<\/li>\n<li>a device identifier combined with a local counter;<\/li>\n<li>a protocol sequence number;<\/li>\n<li>a sufficiently large random value with a bounded collision probability;<\/li>\n<li>a synthetic value produced by a misuse-resistant construction.<\/li>\n<\/ul>\n<p>Each choice imports assumptions. A counter needs durable state. A device prefix needs collision-free assignment. A sequence number needs a key lifecycle aligned with its wrap limit. A random nonce needs a trustworthy generator and a quantitative bound on total use.<\/p>\n<p>The format should be designed from the population of encryptors and the lifetime of the key, not copied from an example.<\/p>\n<h2 id=\"gcm-reuse-fails-in-two-directions\">GCM reuse fails in two directions<\/h2>\n<p>AES-GCM combines counter-mode encryption with polynomial authentication. Reusing a nonce under one key repeats the counter-mode keystream.<\/p>\n<p>If two ciphertexts use the same keystream, XORing them cancels that stream and exposes the XOR of the plaintexts. Known or predictable content in one record can reveal content in the other.<\/p>\n<p>The damage is not limited to confidentiality. Reuse also exposes algebraic relationships in GCM&#8217;s authentication function. With enough information, an attacker may recover what is needed to construct valid tags for modified data.<\/p>\n<p>This is why nonce reuse is not a minor reduction in margin. For GCM it can invalidate both properties the mode was selected to provide.<\/p>\n<p>Tag truncation introduces another dimension. Shorter tags increase the probability that a forgery attempt succeeds, and repeated verification attempts accumulate. The security analysis must include tag length, message volume, failed verification policy and total use of each key.<\/p>\n<h2 id=\"random-does-not-mean-unique\">Random does not mean unique<\/h2>\n<p>A random 96-bit nonce offers a large space, but random sampling permits collisions. The probability grows approximately with the square of the number of encryptions under one key.<\/p>\n<p>For modest volumes and a well-functioning generator, the risk can be negligible. For very high volumes, many independent writers or long-lived keys, a deterministic construction can provide a clearer bound.<\/p>\n<p>Random generation also has operational failure modes that the ideal probability does not capture:<\/p>\n<ul>\n<li>virtual machines cloned with identical generator state;<\/li>\n<li>embedded devices starting before sufficient entropy is available;<\/li>\n<li>test configurations reaching production;<\/li>\n<li>faulty libraries returning repeated output;<\/li>\n<li>snapshots restoring a previous random-generator state;<\/li>\n<li>several services using the same key without coordinated accounting.<\/li>\n<\/ul>\n<p>A statistical argument assumes independent samples. Infrastructure can create identical state instead.<\/p>\n<h2 id=\"counters-have-to-survive-reality\">Counters have to survive reality<\/h2>\n<p>A counter provides uniqueness only if it never moves backwards while the key remains active.<\/p>\n<p>That condition becomes difficult under:<\/p>\n<ul>\n<li>unexpected power loss;<\/li>\n<li>database rollback;<\/li>\n<li>restoration from backup;<\/li>\n<li>active-active replication;<\/li>\n<li>device replacement;<\/li>\n<li>factory reset;<\/li>\n<li>firmware downgrade;<\/li>\n<li>counter exhaustion.<\/li>\n<\/ul>\n<p>Persisting the counter before every encryption can impose unacceptable latency and wear. Reserving ranges reduces write frequency: a device durably advances its high-water mark, then consumes values from the reserved interval. After a crash, unused values are skipped rather than reused.<\/p>\n<p>Multiple encryptors can receive disjoint prefixes or disjoint ranges. The allocation mechanism then becomes part of key management. Adding a new writer without assigning a unique namespace is a cryptographic change, even if the encryption code is untouched.<\/p>\n<p>A key should never outlive the mechanism that guarantees uniqueness for it.<\/p>\n<h2 id=\"rollback-connects-encryption-to-platform-state\">Rollback connects encryption to platform state<\/h2>\n<p>Encrypted local storage creates a particularly subtle case. A device may persist both ciphertext and nonce state, then later restore an earlier filesystem image.<\/p>\n<p>The restored data may be internally consistent while the external world has already observed later ciphertexts under the same key. Continuing from the restored counter repeats values.<\/p>\n<p>Preventing this may require a monotonic hardware counter, a server-issued epoch, key rotation on restore or a construction designed to tolerate accidental nonce repetition. Backup and recovery procedures therefore belong in the encryption design.<\/p>\n<p>This is an example of a broader principle: cryptographic state cannot be reasoned about solely from the current disk image. Its history matters.<\/p>\n<h2 id=\"associated-data-defines-the-record\">Associated data defines the record<\/h2>\n<p>Associated data is authenticated but not encrypted. It binds visible context to the ciphertext.<\/p>\n<p>For a stored object, useful associated data might include:<\/p>\n<ul>\n<li>format and schema version;<\/li>\n<li>record type;<\/li>\n<li>tenant or partition identifier;<\/li>\n<li>key identifier and algorithm identifier;<\/li>\n<li>object identifier;<\/li>\n<li>purpose or domain-separation string.<\/li>\n<\/ul>\n<p>For a protocol record, it may include direction, session identifier, sequence number and message type.<\/p>\n<p>If context is omitted, a valid ciphertext can sometimes be moved to another location where the plaintext bytes are interpreted differently. If context is encoded ambiguously, two implementations can authenticate different conceptual records using the same byte sequence.<\/p>\n<p>Associated data should therefore use a canonical encoding and an explicit domain. It should contain the facts that must remain inseparable from the protected plaintext, not every available field.<\/p>\n<h2 id=\"authentication-must-complete-before-release\">Authentication must complete before release<\/h2>\n<p>Authenticated decryption has one safe external result: complete plaintext or failure.<\/p>\n<p>Streaming implementations may internally produce candidate plaintext before the final tag is checked. Exposing those bytes to the application creates a decryption oracle and lets unauthenticated data influence parsers, files or downstream decisions.<\/p>\n<p>An API should make premature release difficult. When large messages cannot be buffered in memory, the implementation can write to quarantined storage and commit the result only after authentication succeeds.<\/p>\n<p>Error handling should also avoid revealing why verification failed. The system may need detailed internal telemetry, but an attacker should not learn whether a key identifier, padding convention, tag byte or associated-data field was nearly correct.<\/p>\n<h2 id=\"aead-does-not-detect-replay\">AEAD does not detect replay<\/h2>\n<p>A previously valid tuple of nonce, associated data and ciphertext remains cryptographically valid when presented again.<\/p>\n<p>AEAD detects alteration. It does not know whether the application has already processed the record.<\/p>\n<p>Replay protection can use sequence windows, unique transaction identifiers, state-machine rules or an application ledger. The relevant identifier should be authenticated, usually as associated data or inside the plaintext.<\/p>\n<p>This distinction matters in intermittent and offline systems. A verifier may be able to establish that a token is authentic while being unable to know whether another disconnected verifier has already accepted it. Cryptographic validity and business acceptance are separate decisions.<\/p>\n<h2 id=\"misuse-resistance-changes-the-failure-mode\">Misuse resistance changes the failure mode<\/h2>\n<p>Some constructions are designed to degrade more safely when a nonce repeats.<\/p>\n<p>AES-GCM-SIV derives a synthetic initial value from the key, nonce, associated data and plaintext before performing counter-mode encryption. Accidental nonce repetition can reveal whether the complete inputs repeat, but it avoids the catastrophic failure pattern of ordinary GCM.<\/p>\n<p>This does not make nonces irrelevant. Repetition can still leak equality, security bounds still apply and the construction may not be available in required hardware or standards profiles.<\/p>\n<p>ChaCha20-Poly1305 also requires unique nonces under a key. Its advantages concern software performance and implementation on platforms without fast constant-time AES, not tolerance of arbitrary nonce reuse.<\/p>\n<p>Ascon-AEAD128, standardised by NIST for lightweight environments, brings a different permutation-based design and constrained-device profile. It also has a nonce contract and usage limits that the system must respect.<\/p>\n<p>Algorithm selection should compare expected platforms and expected failures. If rollback or multi-writer coordination cannot be ruled out, misuse resistance may be more valuable than peak throughput.<\/p>\n<h2 id=\"key-rotation-bounds-the-problem\">Key rotation bounds the problem<\/h2>\n<p>Every nonce strategy has a finite domain and every AEAD has quantitative limits on data and forgery attempts under one key.<\/p>\n<p>Key rotation bounds exposure, but it must create an independent nonce namespace. Resetting a counter is safe only when a new independent key is certainly active. If a deployment can roll back the key while retaining the reset counter, the pair may still repeat.<\/p>\n<p>An encryption envelope should carry enough information to select the correct key and construction during decryption. Historical keys may remain available for reading while being disabled for new encryption.<\/p>\n<p>Useful telemetry includes:<\/p>\n<ul>\n<li>number of encryptions per key;<\/li>\n<li>highest allocated counter or range;<\/li>\n<li>number and identity of writers;<\/li>\n<li>authentication failures;<\/li>\n<li>approaching message or data limits;<\/li>\n<li>restore, clone and rollback events;<\/li>\n<li>remaining ciphertext by key generation.<\/li>\n<\/ul>\n<p>Rotation without this inventory can strand data or silently recreate the conditions it was intended to remove.<\/p>\n<h2 id=\"test-the-assumption-that-should-never-fail\">Test the assumption that should never fail<\/h2>\n<p>Positive test vectors confirm that a primitive produces the expected bytes. System tests should attack nonce and context management directly.<\/p>\n<p>A useful test plan includes:<\/p>\n<ol type=\"1\">\n<li>two writers starting simultaneously;<\/li>\n<li>process crash before and after counter persistence;<\/li>\n<li>restoration of an old snapshot;<\/li>\n<li>device cloning;<\/li>\n<li>counter exhaustion and wrap;<\/li>\n<li>key rotation during active writes;<\/li>\n<li>altered associated data;<\/li>\n<li>replayed valid ciphertext;<\/li>\n<li>truncated ciphertext and tag;<\/li>\n<li>authentication failure after partial internal decryption.<\/li>\n<\/ol>\n<p>The expected result should identify whether encryption is refused, a new key is required, the record is rejected or recovery enters a controlled state.<\/p>\n<p>An assumption described as impossible is often the most valuable one to simulate.<\/p>\n<h2 id=\"the-state-beside-the-cipher\">The state beside the cipher<\/h2>\n<p>Authenticated encryption gives applications a disciplined way to protect confidentiality, integrity and visible context together.<\/p>\n<p>Its compact interface can obscure how much system behaviour supports it.<\/p>\n<p>Nonce uniqueness may depend on durable counters, writer allocation, entropy, backup policy and key rotation. Associated data depends on canonical encoding and protocol meaning. Safe decryption depends on withholding plaintext until authentication completes. Replay depends on state outside the primitive.<\/p>\n<p>The nonce is not a footnote attached to the ciphertext. It is evidence that the system kept the promise required by the cipher.<\/p>\n<hr \/>\n<h2 id=\"references\">References<\/h2>\n<ol type=\"1\">\n<li><a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc5116.html\">RFC 5116: An Interface and Algorithms for Authenticated Encryption<\/a><\/li>\n<li><a href=\"https:\/\/csrc.nist.gov\/pubs\/sp\/800\/38\/d\/final\">NIST SP 800-38D: Galois\/Counter Mode and GMAC<\/a><\/li>\n<li><a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc8439.html\">RFC 8439: ChaCha20 and Poly1305 for IETF Protocols<\/a><\/li>\n<li><a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc8452.html\">RFC 8452: AES-GCM-SIV, Nonce Misuse-Resistant Authenticated Encryption<\/a><\/li>\n<li><a href=\"https:\/\/csrc.nist.gov\/pubs\/sp\/800\/232\/final\">NIST SP 800-232: Ascon-Based Lightweight Cryptography Standards for Constrained Devices<\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>A secure cipher can fail when nonce state does not survive writers, restarts and rollbacks. Uniqueness is a property of the operating system around the primitive.<\/p>\n","protected":false},"author":2,"featured_media":332,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10],"tags":[],"nlabs_article_type":[36],"class_list":["post-333","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-research","nlabs_article_type-primer"],"_links":{"self":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts\/333","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/comments?post=333"}],"version-history":[{"count":1,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts\/333\/revisions"}],"predecessor-version":[{"id":224744,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts\/333\/revisions\/224744"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/media\/332"}],"wp:attachment":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/media?parent=333"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/categories?post=333"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/tags?post=333"},{"taxonomy":"nlabs_article_type","embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/nlabs_article_type?post=333"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}