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 BINARY crypto path — encryptBytes /
2// decryptBytes, the raw `iv||ct||tag` bytes attachments upload and download as
3// (no base64, no JSON). The native client opens the same blobs (LChatCore's
4// GroupKey.decryptBytes, via BlobImageLoader); drift here means photos decrypt
5// on one platform and not the other. Pinned two ways:
6//
7// 1. a fresh encryptBytes → decryptBytes round-trips in JS, and
8// 2. the committed fixture (LChatCore's Tests/…/Fixtures/attachment-bytes-vector.json)
9// still decrypts to the expected bytes — the SAME fixture the Swift test
10// (`swift test` in ios/LChatCore, AttachmentBytesVectorTests) consumes.
11//
12// If (2) fails, regenerate with --emit and update LChatCore in the same change.
13import { readFileSync, writeFileSync } from "node:fs";
14import { fileURLToPath } from "node:url";
15import { dirname, join } from "node:path";
16import { generateKey, importKey, encryptBytes, decryptBytes, bytesToB64url, b64urlToBytes } from "../web/static/crypto.js";
17
18const here = dirname(fileURLToPath(import.meta.url));
19const fixturePath = join(
20 here, "..", "ios", "LChatCore", "Tests", "LChatCoreTests", "Fixtures", "attachment-bytes-vector.json"
21);
22
23// Every byte value once — catches any encoding/sign slip in either language.
24function knownBytes() {
25 const raw = new Uint8Array(256);
26 for (let i = 0; i < 256; i++) raw[i] = i;
27 return raw;
28}
29
30function assert(cond, msg) {
31 if (!cond) {
32 console.error("FAIL:", msg);
33 process.exit(1);
34 }
35}
36
37// `node test/blobbytes.test.mjs --emit` regenerates the committed fixture (the
38// encrypt uses a fresh IV, so the fixture must be frozen, not recomputed).
39async function emit() {
40 const { key, b64 } = await generateKey();
41 const blob = await encryptBytes(key, knownBytes());
42 const fixture = {
43 _comment:
44 "Attachment-bytes test vector (binary iv||ct||tag path). Generated by " +
45 "test/blobbytes.test.mjs from web/static/crypto.js. LChatCore's " +
46 "AttachmentBytesVectorTests opens blobB64url's raw bytes with keyB64 via " +
47 "GroupKey.decryptBytes and must get plaintextB64url's bytes back — the " +
48 "gate for native attachment/avatar decryption. " +
49 "Regenerate with: node test/blobbytes.test.mjs --emit",
50 keyB64: b64,
51 blobB64url: bytesToB64url(blob),
52 plaintextB64url: bytesToB64url(knownBytes()),
53 };
54 writeFileSync(fixturePath, JSON.stringify(fixture, null, 2) + "\n");
55 console.log("wrote", fixturePath);
56}
57
58async function test() {
59 // (1) crypto.js self-consistency on the binary path.
60 const { key } = await generateKey();
61 const round = await decryptBytes(key, await encryptBytes(key, knownBytes()));
62 assert(bytesToB64url(round) === bytesToB64url(knownBytes()), "fresh encryptBytes/decryptBytes round-trips");
63
64 // (2) the committed fixture (shared with the Swift test) still decrypts.
65 const fx = JSON.parse(readFileSync(fixturePath, "utf8"));
66 const got = await decryptBytes(await importKey(fx.keyB64), b64urlToBytes(fx.blobB64url));
67 assert(bytesToB64url(got) === fx.plaintextB64url, "committed fixture decrypts to expected bytes");
68
69 console.log("blobbytes.test.mjs OK");
70}
71
72if (process.argv.includes("--emit")) await emit();
73else await test();
74