Skip to content

Repository files navigation

crypto-lab-poly1305-mac

What It Is

Poly1305 is a one-time message authentication code (MAC) designed by Daniel J. Bernstein. It takes a 32-byte key and a variable-length message and produces a 128-bit authentication tag by evaluating a polynomial over the prime field GF(2^130 − 5). The security model is information-theoretic: as long as each key is used only once, no amount of computation gives an attacker better than an 8·⌈L/16⌉ / 2^106 chance of forging a valid tag on a message of at most L bytes (Bernstein's bound — note that it is weaker than the 2^−128 of a blind guess, and grows with message length). This demo uses the standalone poly1305 export from @noble/ciphers/_poly1305.js to compute and verify tags entirely in the browser.

When to Use It

  • Authenticating a single message under a fresh key — Poly1305's one-time guarantee makes it ideal when a higher-level protocol (such as ChaCha20-Poly1305) derives a unique key per message.
  • High-throughput MAC in AEAD constructions — Poly1305's arithmetic is simple enough for constant-time implementations on commodity hardware, making it a common choice inside TLS 1.3 and WireGuard.
  • Replacing HMAC when polynomial MAC performance matters — On platforms without AES-NI, Poly1305 can be faster than HMAC-SHA-256 while offering a strong forgery bound.
  • When you need a MAC with a provable security bound — Poly1305's forgery probability is at most 8·⌈L/16⌉ / 2^106, a concrete, message-length-dependent bound absent from many hash-based MACs.
  • Do NOT use standalone Poly1305 when you cannot guarantee one-time key usage — reusing a key across two messages lets an attacker recover the secret r value and forge tags for any future message. This is a teaching demo, not a production MAC implementation.

Live Demo

systemslibrarian.github.io/crypto-lab-poly1305-mac

Generate a random 32-byte key, type any message, and compute its Poly1305 authentication tag. Three verification scenarios let you confirm the original tag, tamper the message (a trailing space), or flip a single bit of a single tag byte — all with constant-time comparison; each tag byte is drawn as a colored square whose hue encodes its value (0x00 blue → 0xFF red). The Polynomial Stepper visualises the first four blocks with the full recurrence broken into stages — Acc + Block, then × r, then mod p → Acc After — annotates the first block's 0x01 padding and little-endian reading, and includes a "What is clamping?" popover that highlights exactly which key bits are zeroed. The Forge a Tag by Reusing One Key section makes the one-time rule concrete: it signs two short (single-block) messages under one key, then — playing the attacker who never sees the key — recovers the secret r and s from the two tags by algebra and produces a valid forged tag for a third message you choose, verified live against the same key.

The forgery is real, not staged: the recovery solves (c₁ − c₂)·r ≡ (t₁ − t₂) + Δ·2¹²⁸ (mod p) over the nine possible wrap terms Δ, discards every candidate that is not a valid clamped r (below 2¹²⁴ and matching the clamp mask) or that fails to reproduce the second tag, and forges only when exactly one candidate survives. That last condition matters: candidates for neighbouring Δ differ by 2¹²⁸·(c₁ − c₂)⁻¹ (mod p), so two messages that are very close — one byte apart at the same length, like ab and ac — leave several keys that all explain both captured tags, and the demo says so rather than forging from a guess. A unit test asserts the recovered r matches the RFC 8439 key, that any claimed unique recovery really is the sender's r, that the forged tag verifies under real @noble/ciphers Poly1305 across many random keys, and that the underdetermined message pairs are reported as ambiguous instead of answered.

What Can Go Wrong

  • Key reuse — Poly1305 is a one-time MAC. Authenticating two different messages under the same 32-byte key lets an attacker solve for the secret r and s values algebraically and forge tags for any message.
  • Non-constant-time tag comparison — Comparing tags with an early-exit === loop leaks how many leading bytes matched, giving an attacker a timing oracle to iteratively guess a valid tag.
  • Unclamped r value — The first 16 bytes of the key must have 22 specific bits zeroed (clamping) before use, and an implementation that skips it produces tags no conforming peer will accept. Note the direction of the tradeoff, which is easy to get backwards: clamping is a performance measure — it bounds r so the field multiplication can run in fast fixed-width limbs without carry overflow — and it costs security rather than adding it, shrinking r's space from 2^128 to 2^106. That 2^106 is exactly the denominator in the 8·⌈L/16⌉ / 2^106 forgery bound.
  • Using Poly1305 without an AEAD wrapper — Standalone Poly1305 authenticates but does not encrypt. Sending a cleartext message with only a Poly1305 tag gives integrity without confidentiality, which is rarely the correct security goal.
  • Nonce misuse in ChaCha20-Poly1305 — In the AEAD construction, repeating a nonce under the same encryption key re-derives the same Poly1305 sub-key, collapsing back to the key-reuse failure above.

Real-World Usage

  • TLS 1.3 — The TLS_CHACHA20_POLY1305_SHA256 cipher suite uses Poly1305 as the MAC inside its AEAD, protecting every record on connections that negotiate this suite.
  • WireGuard — All tunnel packets are authenticated with ChaCha20-Poly1305, relying on Poly1305 for per-packet integrity and authenticity.
  • OpenSSH — The chacha20-poly1305@openssh.com cipher uses Poly1305 to authenticate both the packet length and payload, replacing the older HMAC-based transport MACs.
  • NaCl / libsodium crypto_secretbox — The widely deployed secretbox construction pairs XSalsa20 encryption with Poly1305 authentication, used in applications from Signal to Keybase.
  • IETF RFC 8439 — The canonical specification for ChaCha20-Poly1305, referenced by QUIC (RFC 9001), Noise Protocol Framework, and other modern protocols.

How to Run Locally

git clone https://github.com/systemslibrarian/crypto-lab-poly1305-mac
cd crypto-lab-poly1305-mac
npm install
npm run dev

Related Demos

Tests

The test suite verifies correctness against the canonical RFC 8439 §2.5.2 Poly1305 test vector (key, message, clamped r, and tag), checks that tampering with either the message or the tag fails verification, and proves the key-reuse forgery honestly — recovering the real r/s from two single-block tags and confirming the forged tag verifies under genuine @noble/ciphers Poly1305 for the RFC key and 50 random keys. It also pins the two ways the recovery could lie: every surviving candidate must be a genuine clamped r (a candidate of the form r + k·2¹²⁸ shares the real r's low 16 bytes and would otherwise pass a mask-only test), and message pairs that underdetermine the key must be reported as ambiguous rather than answered with a guess. It also runs an automated axe-core accessibility audit over the rendered UI. Color contrast for both themes meets WCAG 2.1 AA (≥ 4.5:1 for text).


Part of the Crypto Lab suite.

"So whether you eat or drink or whatever you do, do it all for the glory of God." — 1 Corinthians 10:31

About

Browser-based Poly1305 MAC demo — polynomial evaluation over GF(2¹³⁰−5), constant-time tag verification, key-reuse attack visualizer, and Polynomial Stepper. Part of crypto-lab.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages