eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1// 🤖 Generates internal/e2ee/testdata/vectors.json from web/static/crypto.js —
2// the ground truth for the Go port's wire-format compatibility. Every blob in
3// the file was produced by the exact code the browser runs; the Go tests must
4// decrypt/unwrap/open all of them and reproduce the deterministic derivations.
5//
6// Not part of `npm test` (vectors are committed). Regenerate after any
7// deliberate crypto.js change:
8//
9// node test/gen-e2ee-vectors.mjs # then re-run: go test ./internal/e2ee
10//
11// Randomness is fine here — the vectors carry their own keys.
12
13import { writeFileSync } from "node:fs";
14import {
15 bytesToB64url, generateKey, encrypt, encryptBytes,
16 randomSecret, deriveInvite, wrapGroupKey,
17 generateUserKeypair, wrapThreadKey, sealForPub,
18} from "../web/static/crypto.js";
19
20const OUT = new URL("../internal/e2ee/testdata/vectors.json", import.meta.url);
21
22const aesPayloads = [
23 {},
24 { text: "hello world", from: "Paul" },
25 { text: "unicode: ünïcødé 🎉 — em-dash, \"quotes\", \\backslash\\, \nnewline" },
26 { nested: { deep: [1, 2.5, true, null, "x"] }, n: 1747 },
27];
28
29const sealPayloads = [
30 { token: "cap-abc123", exp: 1751500000 },
31 { note: "🤖 sealed unicode ✓" },
32];
33
34const vectors = { aesJSON: [], aesBytes: [], invites: [], threadWraps: [], seals: [] };
35
36for (const obj of aesPayloads) {
37 const { key, b64 } = await generateKey();
38 vectors.aesJSON.push({ key: b64, plaintext: obj, blob: await encrypt(key, obj) });
39}
40
41for (const len of [0, 1, 13, 4096]) {
42 const { key, b64 } = await generateKey();
43 const pt = crypto.getRandomValues(new Uint8Array(len));
44 vectors.aesBytes.push({
45 key: b64,
46 plaintextB64: bytesToB64url(pt),
47 blobB64: bytesToB64url(await encryptBytes(key, pt)),
48 });
49}
50
51for (let i = 0; i < 3; i++) {
52 const t = randomSecret();
53 const inv = await deriveInvite(t);
54 const groupKey = (await generateKey()).b64;
55 vectors.invites.push({
56 t,
57 id: inv.id,
58 claimSecret: inv.claimSecret,
59 claimHash: inv.claimHash,
60 groupKey,
61 wrappedGroupKey: await wrapGroupKey(groupKey, inv.wrapKey),
62 });
63}
64
65for (let i = 0; i < 3; i++) {
66 const kp = await generateUserKeypair();
67 const threadKey = (await generateKey()).b64;
68 vectors.threadWraps.push({
69 publicKey: kp.publicKey,
70 privateKey: kp.privateKey,
71 threadKey,
72 wrapped: await wrapThreadKey(threadKey, kp.publicKey),
73 });
74}
75
76for (const obj of sealPayloads) {
77 const kp = await generateUserKeypair();
78 vectors.seals.push({
79 publicKey: kp.publicKey,
80 privateKey: kp.privateKey,
81 plaintext: obj,
82 sealed: await sealForPub(kp.publicKey, obj),
83 });
84}
85
86writeFileSync(OUT, JSON.stringify(vectors, null, 2) + "\n");
87console.log(`wrote ${OUT.pathname}`);
88