// Tests for the vault lock layer's crypto (web/static/vaultcrypto.js), run // in Node (Node 22 has Web Crypto). node test/vaultcrypto.test.mjs // // The WebAuthn ceremonies need an authenticator, so — like home's crypto tests — // PRF outputs here are synthetic 32-byte secrets: kekFromPRF only ever sees the // bytes, so the pipeline from PRF output to unsealed value is exercised end to // end, with only the authenticator itself stubbed out. import assert from "node:assert/strict"; import { prfEvalInput, kekFromPRF, importKEK, generateVaultKeypair, wrapVaultPriv, unwrapVaultPriv, sealValue, unsealValue, sealVaultKeyTo, openVaultHandoff, } from "../web/static/vaultcrypto.js"; let failures = 0; async function test(name, fn) { try { await fn(); console.log("PASS " + name); } catch (e) { console.log("FAIL " + name + " — " + (e && e.message)); failures++; } } const prf = () => crypto.getRandomValues(new Uint8Array(32)); await test("prfEvalInput is the fixed versioned constant", () => { assert.equal(new TextDecoder().decode(prfEvalInput()), "eleven-vault-kek-v1"); }); await test("kekFromPRF is deterministic per PRF output", async () => { const out = prf(); const kek1 = await kekFromPRF(out); const kek2 = await kekFromPRF(out); const kp = await generateVaultKeypair(); // Same PRF bytes → interchangeable KEKs (wrap with one, unwrap with the other). const wrapped = await wrapVaultPriv(kp.privateKey, kek1); assert.equal(await unwrapVaultPriv(wrapped, kek2), kp.privateKey); }); await test("seal to the vault public key → unseal with the private key (incl. unicode + '=' values)", async () => { const kp = await generateVaultKeypair(); for (const v of ["hunter2", "pässwörd 🌍", "BASE64==", "KEY=VALUE\nOTHER=x", " leading and trailing "]) { const sealed = await sealValue(kp.publicKey, v); assert.equal(await unsealValue(sealed, kp.privateKey), v); } }); await test("unseal with the wrong private key throws (never a wrong value)", async () => { const kp = await generateVaultKeypair(); const other = await generateVaultKeypair(); const sealed = await sealValue(kp.publicKey, "secret"); await assert.rejects(() => unsealValue(sealed, other.privateKey)); }); await test("wrapVaultPriv under one KEK never opens under another", async () => { const kp = await generateVaultKeypair(); const wrapped = await wrapVaultPriv(kp.privateKey, await kekFromPRF(prf())); await assert.rejects(async () => unwrapVaultPriv(wrapped, await kekFromPRF(prf()))); }); await test("importKEK: raw 32 bytes (the CLI keychain shape) drive the same wrap format", async () => { const kp = await generateVaultKeypair(); const raw = crypto.getRandomValues(new Uint8Array(32)); const wrapped = await wrapVaultPriv(kp.privateKey, await importKEK(raw)); assert.equal(await unwrapVaultPriv(wrapped, await importKEK(raw)), kp.privateKey); }); await test("the membership handoff: lock hands the key to a member's roster key → they self-enrol → both unseal", async () => { // Paul locks: vault keypair, his wrap under his (synthetic) PRF KEK. const paulKek = await kekFromPRF(prf()); const vault = await generateVaultKeypair(); const paulWrap = await wrapVaultPriv(vault.privateKey, paulKek); const sealed = await sealValue(vault.publicKey, "LCHAT_ADMIN_TOKEN=abc123"); // The handoff: Paul seals the vault PRIVATE key to James's ROSTER public key // (this is the `member` wrap's `wrapped`, ridden by vault.lock's handoffs / a // vault.give). James holds a normal user keypair — his identity, not a vault key. const james = await generateVaultKeypair(); const handoff = await sealVaultKeyTo(james.publicKey, vault.privateKey); // James self-enrols: open the handoff with HIS session private key (no // biometric — it's sealed to his identity), then wrap the vault key under his // OWN passkey KEK. From here on he needs his passkey. const vaultPrivForJames = await openVaultHandoff(handoff, james.privateKey); const jamesKek = await kekFromPRF(prf()); const jamesWrap = await wrapVaultPriv(vaultPrivForJames, jamesKek); // Both members now unseal the same value through their own biometric-gated wrap. assert.equal(await unsealValue(sealed, await unwrapVaultPriv(paulWrap, paulKek)), "LCHAT_ADMIN_TOKEN=abc123"); assert.equal(await unsealValue(sealed, await unwrapVaultPriv(jamesWrap, jamesKek)), "LCHAT_ADMIN_TOKEN=abc123"); // …and a third party (not the handoff's recipient) can't open it. const stranger = await generateVaultKeypair(); await assert.rejects(() => openVaultHandoff(handoff, stranger.privateKey)); }); console.log(failures === 0 ? "\nALL PASSED" : `\n${failures} FAILED`); process.exit(failures === 0 ? 0 : 1);