{"id":50716938,"url":"https://github.com/systemslibrarian/crypto-lab-opaque-gate","last_synced_at":"2026-06-09T19:01:51.952Z","repository":{"id":363002735,"uuid":"1214271117","full_name":"systemslibrarian/crypto-lab-opaque-gate","owner":"systemslibrarian","description":"Browser-based OPAQUE aPAKE demo (RFC 9807, July 2025) — OPRF blind/evaluate/unblind, AES-256-GCM credential envelope, 3DH mutual authentication, server breach simulation. The password never touches the server. No backends. No simulated math.","archived":false,"fork":false,"pushed_at":"2026-06-06T23:40:00.000Z","size":11230,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T01:06:47.172Z","etag":null,"topics":["3dh","authenticated-key-exchange","crypto-lab","cryptography","forward-secrecy","opaque","oprf","pake","password-authentication","rfc9807"],"latest_commit_sha":null,"homepage":"https://systemslibrarian.github.io/crypto-lab-opaque-gate/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/systemslibrarian.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-18T10:47:29.000Z","updated_at":"2026-06-06T23:40:04.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/systemslibrarian/crypto-lab-opaque-gate","commit_stats":null,"previous_names":["systemslibrarian/crypto-lab-opaque-gate"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/systemslibrarian/crypto-lab-opaque-gate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systemslibrarian%2Fcrypto-lab-opaque-gate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systemslibrarian%2Fcrypto-lab-opaque-gate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systemslibrarian%2Fcrypto-lab-opaque-gate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systemslibrarian%2Fcrypto-lab-opaque-gate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/systemslibrarian","download_url":"https://codeload.github.com/systemslibrarian/crypto-lab-opaque-gate/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systemslibrarian%2Fcrypto-lab-opaque-gate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34121022,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3dh","authenticated-key-exchange","crypto-lab","cryptography","forward-secrecy","opaque","oprf","pake","password-authentication","rfc9807"],"created_at":"2026-06-09T19:01:49.509Z","updated_at":"2026-06-09T19:01:51.944Z","avatar_url":"https://github.com/systemslibrarian.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OPAQUE aPAKE Demo — RFC 9807\n\n\"Whether therefore ye eat, or drink, or whatsoever ye do, do all to the glory of God.\"  \n— 1 Corinthians 10:31\n\n## What It Is\n\nA browser-based educational demo of **OPAQUE** (RFC 9807, July 2025) — an Augmented Password-Authenticated Key Exchange (aPAKE) where **the server never sees the password**, not during registration, not during login, not ever.\n\nThis is the most practically relevant password authentication scheme for the post-hash-compromise era:\n\n- **Registration**: Client generates credentials, encrypts them with a key derived from the password via an Oblivious PRF. Server stores only the encrypted envelope, the OPRF evaluation key, and the client's public key. Zero password bytes.\n\n- **Login**: Client blinds the password with a random factor. Server evaluates it with a secret key (only known to the server). Client unblinds the result to recover the key that opens the credential envelope. Server never sees the unblinded value, never sees the password.\n\n- **Mutual Authentication \u0026 Forward Secrecy**: Three Diffie-Hellman operations provide mutual proof that both client and server know the recovered credentials, with ephemeral keys ensuring forward secrecy (if session keys are leaked, future sessions are safe).\n\n**Key property**: If the server database is breached:\n- Attacker has encrypted credential envelope + OPRF key\n- Offline dictionary attack _is_ possible (but requires 1+ evaluation per guess, same computational cost as bcrypt cost-10)\n- No plaintext password exposed; no rainbow tables work (OPRF key varies per user)\n- Pre-computation attacks are **impossible**\n\n## When to Use It\n\n- Replacing bcrypt+TLS password authentication in applications where database breaches are a concern\n- Patron portal authentication in library systems (ILS, self-checkout, staff login)\n- Understanding why current password hashing (even modern schemes) still exposes credentials to offline attacks\n- Any system where you want to eliminate \"password compromise in a breach\" from the threat model\n\n## Stack\n\n- **Vite** + TypeScript (strict mode)\n- **[@noble/curves](https://github.com/paulmillr/noble-curves)** for the\n  P-256 ECC and the RFC 9497 OPRF (audited, ~25 KB gzipped contribution)\n- **[@noble/hashes](https://github.com/paulmillr/noble-hashes)** for HKDF,\n  HMAC, and scrypt\n- **WebCrypto** for randomness (`crypto.getRandomValues`)\n- **Vanilla CSS** with light/dark theme toggle\n- Deployable to **GitHub Pages** (no backend)\n- Mobile-first, responsive layout\n- WCAG 2.1 AA compliant\n\n## Live Demo\n\nhttps://systemslibrarian.github.io/crypto-lab-opaque-gate/\n\n## How OPAQUE Works\n\n### Component 1: OPRF (Oblivious Pseudorandom Function)\n\nThe server has a secret key **k**. The client has a password **pwd**. An OPRF computes F(k, pwd) such that:\n\n1. **Client blinds**: Generate random factor _r_, send blinded(pwd) to server\n2. **Server evaluates**: Compute evaluated = F(k, blinded), send back\n3. **Client unblinds**: Compute rwd = F(k, pwd) without ever learning k or sending pwd\n\nThe server sees only _blinded_ values (look like random noise). The client computes the final key _rwd_.\n\n**Implementation**: real RFC 9497 OPRF on NIST P-256 with hash-to-curve\n(SSWU map), via `@noble/curves`. The blind, evaluate, and unblind steps\nuse scalar multiplication on the curve — not HKDF stand-ins. The final\n`oprf_output` is `Hash(input || N || \"Finalize\")` where `N = r⁻¹·evaluated`.\n\n### Component 2: Credential Envelope (RFC 9807 §4, internal mode)\n\nThe client's long-term private key is _not_ encrypted — it's *derived*\nfrom the OPRF output. The envelope is just a MAC tag that proves the\nserver hasn't tampered with the credentials.\n\n```\noprf_output      = OPRF.Finalize(password, blind, evaluated)\nrandomized_pwd   = HKDF-Extract(\"\", oprf_output || stretch(oprf_output))\nenvelope_nonce   = random(32)\n\nseed             = HKDF-Expand(randomized_pwd, envelope_nonce || \"PrivateKey\", 32)\n(client_sk, pk)  = DeriveAuthKeyPair(seed)\nauth_key         = HKDF-Expand(randomized_pwd, envelope_nonce || \"AuthKey\", 32)\nexport_key       = HKDF-Expand(randomized_pwd, envelope_nonce || \"ExportKey\", 32)\nmasking_key      = HKDF-Expand(randomized_pwd, \"MaskingKey\", 32)\n\nenvelope         = envelope_nonce || HMAC(auth_key, envelope_nonce || cleartext_creds)\n```\n\nSame password → same `randomized_pwd` → same `client_sk` and same\n`envelope` MAC. Wrong password (or tampered envelope) → MAC mismatch on\nrecovery. The `stretch` step (scrypt N=2^15 by default) is what makes\neach guess in an offline attack expensive.\n\nServer stores: `{credential_identifier, client_public_key, masking_key, envelope, oprf_key}`.\n**No password material on the server.**\n\n### Component 3: 3DH AKE (RFC 9807 §6, internal mode)\n\nAfter OPRF unblinding and envelope recovery, both sides compute three\nDiffie-Hellman shared secrets:\n\n```\ndh1 = DH(client_eph_sk, server_eph_pk)     [ephemeral × ephemeral — fresh-fresh]\ndh2 = DH(client_eph_sk, server_static_pk)  [ephemeral × static    — fresh-server]\ndh3 = DH(client_static_sk, server_eph_pk)  [static × ephemeral    — client-fresh]\n```\n\nThese feed an HKDF-based key schedule that derives separate keys for\neach direction:\n\n```\nprk              = HKDF-Extract(\"\", dh1 || dh2 || dh3)\nhandshake_secret = Derive-Secret(prk, \"HandshakeSecret\", H(preamble))\nsession_key      = Derive-Secret(prk, \"SessionKey\",      H(preamble))\nkm2              = Expand-Label(handshake_secret, \"ServerMAC\", \"\", 32)\nkm3              = Expand-Label(handshake_secret, \"ClientMAC\", \"\", 32)\n\nserver_mac       = HMAC(km2, H(preamble))\nclient_mac       = HMAC(km3, H(preamble || server_mac))\n```\n\n- **dh1** provides \"fresh × fresh\" forward secrecy.\n- **dh2 + dh3** mix in long-term keys, giving mutual authentication.\n- The preamble commits to context, identities, KE1, the credential\n  response, and the server's keyshare — any byte modified by an attacker\n  changes `H(preamble)` and breaks both MACs.\n\n## Real-World Usage\n\n**RFC 9807** was published by the IRTF Crypto Forum Research Group in July 2025. Authors:\n\n- **Hugo Krawczyk** (AWS, also designed HMAC, HKDF, Noise, IKE)\n- **Kevin Lewi** (Meta, WhatsApp E2E Encrypted Backups)\n- **Christopher Wood** (Cloudflare)\n- **Stanislaw Jarecki** (UC Irvine)\n\n**Deployments**:\n\n- **WhatsApp** (2021+): End-to-End Encrypted Backups for 300M+ users use an OPAQUE-based construction.\n- **Cloudflare Zero Trust**: Exploring OPAQUE for passwordless authentication.\n- **Apple Private Cloud Compute**: Uses related OPRF constructions for privacy-preserving authentication.\n- **1Password**: Research into OPAQUE for vault unlock.\n\n## Library Patron Context\n\nCurrent library systems send patron passwords to ILS servers (or store hashes):\n\n```\nPatron enters password → TLS → ILS server hashes and compares → database\n```\n\nRisk: ILS database breach → patron passwords (or hashes) leaked → credential stuffing, reuse attacks\n\nOPAQUE deployment would mean:\n\n```\nPatron enters password (never leaves device) → \n  Blind with OPRF → \n  Server evaluates (returns encrypted credentials) → \n  Patron decrypts locally → \n  3DH session established\n\nBreach: Encrypted envelope + OPRF key (useless without password)\nAuth failure: Attacker cannot forge login without correct password\n```\n\n**Practical path**: SirsiDynix Symphony, Innovative Interfaces Polaris, EBSCO Discovery Service could integrate OPAQUE. Requires vendor adoption; no standards barrier (RFC 9807 is published).\n\n## Exhibit Tour\n\nThe demo includes five interactive exhibits:\n\n1. **Why Current Password Auth Is Broken**: Compare plaintext, hashed, and OPAQUE to show the breach risk at each level.\n\n2. **The OPRF**: Interactive blind/evaluate/unblind flow showing what server sees vs. never sees.\n\n3. **Registration and Login Protocol**: Full message flow (KE1 → KE2 → KE3) with session key agreement and mutual authentication.\n\n4. **Server Breach Simulation**: Analyze attack scenarios when the database is compromised. Show why offline dictionary attacks _are_ possible with OPRF key, but pre-computation is impossible.\n\n5. **Real-World Deployments**: WhatsApp, Cloudflare, Apple, 1Password. Library patron privacy impact.\n\n## What Can Go Wrong — Limitations\n\n1. **Validated, but not audited.** Every named intermediate and output\n   matches the CFRG `vectors.json` for P256-SHA256 byte-for-byte\n   (`src/test-vectors.ts` — 17 checks across one Real and one Fake\n   vector), and the spec-derived protocol properties pass\n   (`src/verify.ts` — 10 checks). That's strong evidence each derivation\n   matches RFC 9807, but it isn't a substitute for the testing, fuzzing,\n   constant-time review, and side-channel analysis a production\n   deployment needs. Use a vetted PAKE library in real systems.\n\n2. **Offline attack surface with OPRF key compromise.** If an attacker\n   gets the server's OPRF key for a user (via database breach), they can\n   try offline password guesses. Each guess costs one OPRF evaluation\n   plus one `stretch()` (scrypt N=2^15 here ≈ 50 ms). OPAQUE's advantages:\n   - Pre-computation is impossible (the OPRF key varies per user)\n   - No plaintext password exposure\n   - Mutual authentication + forward secrecy\n\n3. **Registration requires TLS.** OPAQUE doesn't bootstrap trust from\n   nothing. First registration assumes the client can trust the server\n   (via TLS). Subsequent logins are password-only. By design (RFC 9807\n   §10); real deployments may layer additional factors on top.\n\n4. **No backend in this demo.** All crypto runs client-side; both\n   \"sides\" are simulated in the same browser. Real deployments need a\n   server to:\n   - Store registration records\n   - Perform OPRF evaluations (server's OPRF key never leaves)\n   - Enforce rate limits to slow online attacks\n   - Possibly use an HSM to protect OPRF keys\n\n5. **Only the P256-SHA256 suite is validated.** The CFRG publishes\n   vectors for ristretto255-SHA512 as well; supporting them requires\n   generalizing this codebase across OPRF groups. The framework in\n   `src/test-vectors.ts` is ready for them.\n\n## No Math.random()\n\nAll randomness uses `crypto.getRandomValues()`. No `Math.random()` anywhere in the codebase.\n\n```bash\ngrep -r \"Math.random\" src/\n# Output: (empty)\n```\n\n## Build \u0026 Run\n\n```bash\nnpm install\nnpm run build        # TypeScript strict, zero errors\nnpm run dev          # Local development server\n```\n\nThe built output is in `dist/` — ready to deploy to GitHub Pages.\n\n## Code Architecture\n\n```\nsrc/\n  oprf.ts          — RFC 9497 OPRF wrapper (@noble/curves) + demo wrappers\n  kdf.ts           — HKDF, Expand-Label, Derive-Secret, DeriveKeyPair,\n                     stretch (scrypt + identity), dh()\n  envelope.ts      — RFC 9807 §4 Store / Recover, register()\n  ake.ts           — RFC 9807 §6 KE1 / KE2 / KE3, masked credential\n                     response, full key schedule\n  verify.ts        — protocol-property tests (round-trip, tampering, FS)\n  test-vectors.ts  — RFC 9807 §C P256-SHA256 vector validation\n                     (Real + Fake-login)\n  main.ts          — UI: five interactive exhibits\n  style.css        — Dark/light theme, responsive, accessible\n```\n\n## Security Considerations\n\n- **Password strength**: OPAQUE assumes strong passwords. Weak passwords can be cracked offline (same as bcrypt).\n- **OPRF key protection**: Server OPRF key (k) must be protected. If leaked, offline attacks become practical. Use HSM or key management service in production.\n- **TLS**: All communication should still be over TLS (OPAQUE is not a replacement for transport security).\n- **Rate limiting**: Server should limit login attempts to slow offline attacks.\n- **Session key reuse**: Session keys are ephemeral and discarded after use. They don't protect across sessions.\n\n## WCAG 2.1 AA Compliance\n\n- Keyboard navigation (all buttons, inputs, tabs focusable)\n- `aria-label` on all inputs and code blocks\n- `role=\"alert\"` on errors\n- High contrast light/dark themes\n- `prefers-color-scheme` NOT used (explicit toggle instead)\n- Mobile-first responsive design (320px+)\n- Focus outlines on all interactive elements\n\n## Repository Description (GitHub)\n\nBrowser-based OPAQUE aPAKE demo (RFC 9807, July 2025). Real RFC 9497\nOPRF on P-256 via `@noble/curves`. RFC 9807-compliant envelope (HMAC\ntag + derived static key, no AES), full 3DH key schedule with separate\nMAC keys per direction. Validated byte-for-byte against the CFRG\nP256-SHA256 test vectors (Real and Fake-login). Password never touches\nthe server.\n\n## Topics\n\n`cryptography` `pake` `opaque` `password-authentication` `oprf` `authenticated-key-exchange` `3dh` `forward-secrecy` `browser-demo` `educational` `typescript` `vite` `rfc9807` `library-auth` `patron-privacy`\n\n---\n\n\u003e \"Whether therefore ye eat, or drink, or whatsoever ye do, do all to the glory of God.\"  \n\u003e — 1 Corinthians 10:31","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsystemslibrarian%2Fcrypto-lab-opaque-gate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsystemslibrarian%2Fcrypto-lab-opaque-gate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsystemslibrarian%2Fcrypto-lab-opaque-gate/lists"}