Skip to content

ADR-004: Crypto agility and post-quantum cryptography

ADR-004: Crypto agility and post-quantum cryptography

Section titled “ADR-004: Crypto agility and post-quantum cryptography”
  • Status: Proposed
  • Date: 2026-05-30
  • Deciders: Keepin’ Tracks engineering
  • Supersedes: Crypto details in ADR-003 (personal authentication)
  • Superseded by:

Personal authentication (ADR-003) initially used fixed SHA-256 token hashing and opaque session cookies. We need:

  1. Crypto agility — swap hash, encryption, and signing algorithms without breaking stored data
  2. Post-quantum readiness — protect long-lived PII and session integrity against future quantum attacks
  3. Plane isolation — shared primitives only; each API plane supplies its own SESSION_SECRET

Transport-layer PQC is already provided by Cloudflare (TLS 1.3 with X25519MLKEM768 on customer-facing hostnames). This ADR covers application-layer cryptography.

Implement packages/crypto (@keepintracks/crypto):

  • No imports from apps/*
  • No plane-specific cookie names or table names
  • Callers pass rootSecret and context strings (e.g. personal:email:v1)

All stored digests and ciphertexts include an algorithm id:

FormatExample
Token hashv1.sha256.<hex> (legacy bare hex = sha256-v1)
Field encryptionaes256gcm-v1:<payload> or hybrid-mlkem768-aesgcm-v1:<payload>
Session cookiev1.hmac.<payload>.<sig>
PhaseCapabilityAlgorithms
AAgility foundationSHA-256-v1 hashes, HKDF-SHA256, AES-256-GCM field encryption
BPQC at-rest PIIML-KEM-768 envelope encryption for users.email when PQC_KEM_ENABLED=true
CSigned sessionsHMAC-SHA256 (session-v1.hmac); hybrid HMAC + ML-DSA-65 (session-v2.hybrid) when PQC_SIGN_ENABLED=true
  • Prefer Web Crypto (crypto.subtle) for classical algorithms
  • ML-KEM via mlkem-wasm behind a KemProvider interface until native Web Crypto ships ML-KEM
  • Lazy-load WASM; fall back to aes256gcm-v1 if PQC disabled or WASM unavailable
SecretPlanePurpose
SESSION_SECRETpersonal-api onlyHKDF root, HMAC session signing, classical field encryption
PQC_KEM_ENABLEDvar (not secret)Gate ML-KEM email encryption
PQC_SIGN_ENABLEDvar (not secret)Gate ML-DSA hybrid session cookies

Production/staging fail closed if SESSION_SECRET is missing or shorter than 32 bytes.

ML-KEM keypair for at-rest encryption is lazily generated and cached in KV (crypto:mlkem:v1) per isolation plane.

No application TLS code. Document reliance on Cloudflare PQC for browser ↔ edge.

DataProtectionRetention
users.email (plaintext column)Deprecated during migrationUntil backfill completes
users.email_encryptedAES-GCM or hybrid ML-KEMUntil account deletion
Magic-link token_hashVersioned SHA-256Until used or expired
Session cookieHMAC-signed reference to D1 sessionSession TTL
  • Algorithms can rotate via version prefixes without schema migrations per algorithm
  • Business plane can reuse packages/crypto with separate secrets and HKDF contexts
  • Aligns with NIST hybrid transition guidance
  • WASM adds bundle size (monitor with wrangler deploy --dry-run)
  • Dual-read email path adds complexity during migration
  • ML-KEM keypair in KV requires backup/rotation procedures
  • ML-DSA hybrid session signatures (Phase C2) — Implemented (session-v2.hybrid, PQC_SIGN_ENABLED)
  • Email backfill job for existing plaintext rowsPOST /dev/backfill-email-encryption, bun run backfill:emails
  • Account export / deletionGET /me/export, DELETE /me
  • Session cleanup cron — Daily 0 4 * * * via scheduled handler
  • WebAuthn passkey COSE algorithm agility
  • Business-plane adoption with business:* HKDF contexts
  • Remove plaintext users.email column after backfill verification