eleven / messenger Public

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

Plain git — no account needed to clone.

Download

Download this file

1// Cross-implementation gate for the message payload — the encrypted envelope
2// chat.js/shell.js build ({text, attachments, replyTo} stamped with name/uid/ts).
3// The native client decrypts the same blobs (LChatCore's GroupKey +
4// MessagePayload); if crypto.js's format or the payload's field names ever
5// change, the two silently drift and native renders garbage. Pinned two ways:
6//
7// 1. a fresh encrypt → decrypt round-trips in JS (crypto.js self-consistency), and
8// 2. the committed fixture (LChatCore's Tests/…/Fixtures/message-payload-vector.json)
9// still decrypts to the expected payload — the SAME fixture the Swift test
10// (`swift test` in ios/LChatCore, MessagePayloadVectorTests) consumes.
11//
12// If (2) fails, the format changed incompatibly: regenerate with --emit and
13// update LChatCore (MessagePayload / Crypto.swift) to match in the same change.
14import { readFileSync, writeFileSync } from "node:fs";
15import { fileURLToPath } from "node:url";
16import { dirname, join } from "node:path";
17import { generateKey, importKey, encrypt, decrypt } from "../web/static/crypto.js";
18
19const here = dirname(fileURLToPath(import.meta.url));
20const fixturePath = join(
21 here, "..", "ios", "LChatCore", "Tests", "LChatCoreTests", "Fixtures", "message-payload-vector.json"
22);
23
24// The full envelope shape, every optional field populated — so the Swift side
25// proves it decodes replies, attachment dimensions, ts and uid, not just text.
26const payload = {
27 text: "Hello from the vector! 🎉",
28 name: "Paul",
29 uid: "u_vector",
30 ts: 1751830000000,
31 attachments: [
32 { id: "blob123", name: "photo.jpg", type: "image/jpeg", w: 800, h: 600, size: 123456 },
33 { id: "blob456", name: "clip.mp4", type: "video/mp4", w: 1280, h: 720, size: 9876543, poster: "blob457", dur: 12.5 },
34 ],
35 replyTo: { id: 41, name: "Ada", snippet: "the original line" },
36};
37
38function assert(cond, msg) {
39 if (!cond) {
40 console.error("FAIL:", msg);
41 process.exit(1);
42 }
43}
44
45// `node test/messageblob.test.mjs --emit` regenerates the committed fixture
46// (encryption picks a fresh IV, so the fixture must be frozen, not recomputed).
47async function emit() {
48 const { key, b64 } = await generateKey();
49 const blob = await encrypt(key, payload);
50 const fixture = {
51 _comment:
52 "Message-payload test vector. Generated by test/messageblob.test.mjs from " +
53 "web/static/crypto.js. LChatCore's MessagePayloadVectorTests decrypts `blob` " +
54 "with `keyB64` and must decode every `expect` field — a cross-implementation " +
55 "gate so native message decryption never drifts from the web's. " +
56 "Regenerate with: node test/messageblob.test.mjs --emit",
57 keyB64: b64,
58 blob,
59 expect: payload,
60 };
61 writeFileSync(fixturePath, JSON.stringify(fixture, null, 2) + "\n");
62 console.log("wrote", fixturePath);
63}
64
65async function test() {
66 // (1) crypto.js self-consistency on this payload shape.
67 const { key } = await generateKey();
68 const round = await decrypt(key, await encrypt(key, payload));
69 assert(JSON.stringify(round) === JSON.stringify(payload), "fresh encrypt/decrypt round-trips");
70
71 // (2) the committed fixture (shared with the Swift test) still decrypts.
72 const fx = JSON.parse(readFileSync(fixturePath, "utf8"));
73 const got = await decrypt(await importKey(fx.keyB64), fx.blob);
74 assert(JSON.stringify(got) === JSON.stringify(fx.expect), "committed fixture decrypts to expected payload");
75
76 console.log("messageblob.test.mjs OK");
77}
78
79if (process.argv.includes("--emit")) await emit();
80else await test();
81