4ec192347e
Per-key AEAD usage limits per RFC 9001 §6.6 / §B.1:
* Confidentiality limit (encrypt count per send key):
AES-128-GCM = 2^23, ChaCha20-Poly1305 = 2^62. Reaching this means
AEAD security is no longer assured; the endpoint MUST initiate a
key update or close.
* Integrity limit (forged-packet count per receive key):
AES-128-GCM = 2^52, ChaCha20-Poly1305 = 2^36. Reaching this means
an attacker has been grinding for AEAD key recovery; MUST close
with AEAD_LIMIT_REACHED.
Pre-fix neither limit was tracked. Long-running AES-128-GCM sessions
(~2hrs at 1000pkt/s) could roll past the confidentiality limit; an
attacker spamming forged packets had no failure ceiling.
Implementation:
* `Aead.confidentialityLimit` / `Aead.integrityLimit` properties
surface the spec values per cipher; AES-128-GCM and
ChaCha20-Poly1305 (commonMain singletons + jvmAndroid JCA classes)
return the §B.1 numbers.
* `QuicConnection.aeadEncryptCount` / `aeadDecryptFailureCount` —
per-key counters, reset on every 1-RTT key rotation
(`commitKeyUpdate` and `initiateKeyUpdate`).
* Writer increments encrypt count after each application-level
build. At half the limit it soft-triggers `initiateKeyUpdate`
(latched via `aeadKeyUpdateRequested` so the in-flight rotation
isn't re-issued); at the limit it closes with AEAD_LIMIT_REACHED
if rotation hasn't completed.
* Parser increments decrypt-failure count on every 1-RTT AEAD
auth-tag failure; closes when the count hits the integrity limit.
Initial / Handshake levels are out of scope — their keys' lifetime is
too short to approach the limit.
Test: `AeadInvocationLimitTest` (6 cases) covers spec-value
verification, counter increment on application send, counter reset on
rotation, confidentiality-limit close, integrity-limit close (via a
real ciphertext-tampered datagram from the in-process pipe).
https://claude.ai/code/session_01XGmmaVuy2wsSZQx2AqN2ik