Changes: Add an isolated pinned mechanism wrapper using Stanza's public constructor and factory APIs, preserve the SCRAM-SHA-512 wire name and fail-closed final guard, and document the proposed ADR, browser bundle evidence, independent Scramp vectors, notices and limits. Leave parent selection, dependency experiments, specs and shared contracts unchanged. Validation: Fresh-cache npm ci and Vite browser build pass; 87 focused tests pass, including RFC HMAC vectors, original defect reproduction, independent full SHA-512 exchanges and rejected invalid finals/order/reset paths. Hashed Scramp oracle fixture comparison passes. Dependency check verifies 49 locked entries, 25 installed pins/licenses and 340 unmodified Stanza files. make check passes 72 tooling tests. Native browsers, WSS/live server interoperability and production readiness are untested; independent security review remains pending. Task: S00-019 Agent: auto-crypto-c68801170fa4 Role: crypto Action: claim Status: review Task title: Validate browser-compatible SCRAM-SHA-512 through a public mechanism API
7.9 KiB
ADR 0002 SHA-512 remediation: public mechanism composition
- Status: proposed; independent security review required.
- Task: S00-019, remediation of S00-003.
- Date: 2026-10-15.
- Owner: auto-crypto-c68801170fa4 (crypto).
- Reviewer: pending, security role.
- Requirements: ACC-004, NET-003, NET-004.
Input: original SHA-512 blocker, accepted scope/stack/SASL/parser decisions, exact pinned Stanza browser source/public declarations and primary vectors. Output is one isolated runnable experiment in experiments/0002-xmpp-library/sha512-validation. No production library selection, parent WSS work, shared contract, stack change or original spec change.
Defect and options
The built Stanza 12.22.1 browser backend reproduces the RFC 4231 case 1 failure for createHmac('sha-512'). Source chooses 128-byte blocks only for sha512; SCRAM's normal wire name derives sha-512. The browser digest implementation already accepts both spellings; HMAC does not normalize the hyphen. Direct public SCRAM('SCRAM-SHA-512') produces the wrong full client proof as well.
- Compose the existing exported SCRAM through its public constructor with the working algorithm alias, expose the correct independent wire name, and register the wrapper through public Factory APIs. Proposed for this exact version, with complete browser-backend vectors guarding the alias behavior.
- Replace crypto or the complete mechanism with another library. Not needed for this outcome: introduces new runtime crypto/dependency/API assumptions. Scramp is used only as an independent test oracle, not a browser candidate.
- Patch private algorithm/vendor HMAC, mutate vendor prototypes, rewrite imports to a replacement crypto backend, rename the wire mechanism or downgrade. Rejected; no such patch/workaround is used.
Proposed integration
Sha512Mechanism implements the shipped public Mechanism interface and delegates all crypto to a new public SCRAM('SCRAM-SHA512'). Constructor typing accepts a string; exact shipped source derives sha512, supported by createHash/Hmac. The wrapper exposes SCRAM-SHA-512 on the wire and never accesses/changes a Stanza private field. The delegate retains its internal alias name throughout. This uses documented public constructor/method signatures and version-inspected alias behavior, not an upstream promise of a named SCRAM-SHA512 wire mechanism. Future upgrades must revalidate this composition; an incompatible API is a new blocker, not permission for a private patch.
Public Factory.disable/register replaces SHA-512 before negotiation; fresh Client.use installs the isolated S00-017 guard copy. Guard is narrowed to SHA-512 and its 88-character verifier, while preserving strict validation, ordering, mutual authentication, cancellation and no-success-on-error behavior. Delegate processSuccess/finalize must confirm both flags before auth event/flags/restart. Library-derived keys remain attempt-local, getCacheableCredentials returns null; no storage/cache update path is added. Crypto primitives, SCRAM composition, SASLprep and verifier remain entirely existing library code.
Only non-PLUS SHA-512 is permitted by this fixture profile. PLUS-only/plaintext/ unsupported offers fail; compatible mixed offers select SHA-512. tlsUnique input fails. GS2 n,, and c=biws reflect no channel binding. Full NET-003 fallback policy and real WSS ACC-004 stay with S00-003; this experiment makes no complete product requirement or library-selection claim.
Pins, public API and sources
Inspected primary sources on 2026-10-15:
- RFC 4231 sections 4.2–4.8 supplies seven fixed HMAC-SHA-512 cases, including 131-byte keys and truncation.
- RFC 6234 test driver supplies SHA-512 abc digest. No primitive code is copied from the RFC.
- Stanza source supplies public API context; exact shipped 12.22.1 lib/sasl/index.d.ts/.js, Client.d.ts/.js, plugins/features.d.ts/.js and platform/browser/crypto files are authoritative version-specific evidence. Factory accepts a constructor; wrapper calls only declared public methods. Stanza license is MIT with bundled browser crypto ancestry notices preserved.
- events upstream documents browser EventEmitter API, ES5 support, MIT license and limited maintainer resources. events 3.3.0 supplies the explicit polyfill required by Stanza's browser graph.
- Vite build supports library output/browser targets; accepted Vite 8.3.4 is reused, not a new stack selection.
- Scramp 1.4.6 API documents SHA-512 and public client/server methods. Test oracle pins Scramp 1.4.6 (MIT-0) and asn1crypto 1.5.1 (MIT), exact wheel SHA256 hashes, Python >=3.8. Version selected for available Python 3.9 host compatibility; not a latest-version/audit claim.
Direct npm pins: stanza 12.22.1, events 3.3.0 (MIT), Vite 8.3.4 (MIT, build only). 49 exact locked entries, 25 installed on this macOS ARM64 host. All lock entries have HTTPS registry URL, SHA512 integrity and license inventory; installed notices include Stanza NOTICE browser code lineage and build third-party texts. Native rolldown binding omits a license file; owning rolldown MIT/third-party licenses preserved. Optional platform entries are inventory-only. Build graph includes MIT/ISC/BSD/Apache/MPL packages; no modified covered vendor source. Neither pins nor these checks certify advisory/provenance/security clearance.
Runtime remains accepted Node 24.21.0/npm 11.19.0. Normal HTTPS, strict engines, disabled install scripts; no global configuration change. Browser bundle uses package browser mapping and explicit events alias, with no Node crypto/backend or external builtin. Build graph assertions and executed artifact supply backend evidence; Node crypto appears only as the separate primitive oracle in tests.
Actual validation and limits
Fresh-cache npm ci, browser library build and 87 tests passed. All seven fixed RFC HMAC cases match sha512/SHA512; SHA digest aliases match abc, varied HMAC keys 0..256 bytes match independent Node crypto. Stock hyphenated HMAC/full SCRAM failures reproduce. Three full fixed SHA-512 exchanges match independent Scramp client/server output (ordinary/131-byte/Unicode passwords). Scramp regeneration verifies client proof and server-final independently before comparing fixtures.
Actual browser-built Client, XML registry, feature dispatcher and mechanism run positive and missing/forged/premature/malformed/duplicate/racing-final checks. Fixture send/transport effects are captured explicitly; crypto is never stubbed. Fail never authenticates/restarts; reset/error/replay cannot revive it. Server silence after proof remains unauthenticated until external reset. All 340 Stanza files byte-match original pinned candidate. Detailed commands, checks and hashes are in README and executor evidence; independent review remains mandatory.
Bundle execution host is Node, using browser backend and host native web APIs. Actual Chrome/Firefox/Safari runtime, WSS/TLS, live independent server/client, parent parser integration, workers/performance and production readiness are not claimed. Targets Chrome/Edge 111, Firefox 114, Safari 16.4 are accepted build floors, not a certified product matrix; native crypto RNG/fetch/WebSocket needed. Synchronous SCRAM derivation can block UI; no constant-time verifier guarantee (Stanza compares strings), timeout or exhaustive SASLprep claim. Only success additional-data final, extension-free syntax, 4096 decoded bytes/100000 iteration fixture limits; separate final challenge conservatively rejects. Accepted S00-017/S00-018 source is untouched. Independent security reviewer must assess public alias composition and all limits, repeat commands and record acceptance before the coordinator resumes the parent in another fresh process.