carlos / contra Public

Clone
git clone https://amadan.net/carlos/contra

Plain git — no account needed to clone.

Download

Download this file

1// The Emergency Kit, rendered and read back.
2//
3// The matrix being right (test/qr.test.mjs) does not mean the document is
4// right. Between a correct matrix and a scannable page sit the coordinate
5// flip, the module size, the quiet zone, and whether the squares land on the
6// same grid — and every one of those can be wrong in a document whose matrix
7// is perfect. So this renders the actual PDF and scans it.
8//
9// Needs python3 with pymupdf, zxing-cpp, pypdf, pillow and numpy. A HARD
10// gate: a skip here is indistinguishable from a pass, on the one artifact
11// that has to work years from now with nothing else left.
12
13import { test } from "node:test";
14import assert from "node:assert/strict";
15import { fileURLToPath } from "node:url";
16import { dirname, join } from "node:path";
17import { run as runOracle } from "./oracle.mjs";
18
19const here = dirname(fileURLToPath(import.meta.url));
20const mod = (name) => join(here, "..", "internal", "contra", "static", "crypto", name);
21
22const { accountKeyCard, recoverySheet, handle } = await import(mod("kit.js"));
23const { formatCode, TAG_ACCOUNT_KEY, TAG_RECOVERY } = await import(mod("code.js"));
24const { fromHex } = await import(mod("bytes.js"));
25
26// Generated fixtures, from the golden vector file. Nothing here is real.
27const ACCOUNT_ID = "b22c6a5eae1be9d6a773f474fd0532b0";
28const ACCOUNT_KEY = fromHex("f60fb281e8c72f6725add4cbd40e7b08");
29const RECOVERY = fromHex("70da5ffc983510bba4672030946b9eed");
30const WHEN = new Date(Date.UTC(2026, 8, 2));
31
32function ask(cases) {
33 return JSON.parse(runOracle("pdforacle.py", cases));
34}
35
36const b64 = (bytes) => Buffer.from(bytes).toString("base64");
37
38test("both documents render, scan, and read back", async () => {
39 const cardCode = await formatCode(TAG_ACCOUNT_KEY, ACCOUNT_KEY);
40 const sheetCode = await formatCode(TAG_RECOVERY, RECOVERY);
41
42 const verdicts = ask([
43 {
44 name: "Account Key card",
45 pdf_base64: b64(accountKeyCard(ACCOUNT_ID, cardCode, WHEN)),
46 expect_code: cardCode,
47 expect_text: [
48 "Store somewhere safe.",
49 "Opens nothing without your passphrase.",
50 `Account ${handle(ACCOUNT_ID)}`,
51 "Do not photograph this.",
52 ],
53 },
54 {
55 name: "Recovery sheet",
56 pdf_base64: b64(recoverySheet(ACCOUNT_ID, sheetCode, WHEN)),
57 expect_code: sheetCode,
58 expect_text: [
59 "Store securely.",
60 "This sheet alone can open your vault.",
61 "Nobody can reissue it.",
62 `Account ${handle(ACCOUNT_ID)}`,
63 "Printed 2026-09-02",
64 ],
65 },
66 ]);
67
68 for (const v of verdicts) {
69 assert.ok(v.qr_scans,
70 `${v.name}: the QR in the rendered page did not scan (found ${JSON.stringify(v.scanned)})`);
71 assert.ok(v.one_qr, `${v.name}: expected exactly one QR, found ${v.scanned.length}`);
72 assert.ok(v.code_in_text, `${v.name}: the code is not in the extractable text`);
73 assert.deepEqual(v.missing_text, [], `${v.name}: missing copy`);
74 assert.equal(v.pages, 1, `${v.name}: should be a single page`);
75 }
76});
77
78// The two documents exist to be two files. If either ever carried the other's
79// secret, the whole reason for splitting them would be gone — so it is
80// checked rather than assumed.
81test("neither document carries the other's secret", async () => {
82 const cardCode = await formatCode(TAG_ACCOUNT_KEY, ACCOUNT_KEY);
83 const sheetCode = await formatCode(TAG_RECOVERY, RECOVERY);
84
85 const card = Buffer.from(accountKeyCard(ACCOUNT_ID, cardCode, WHEN)).toString("latin1");
86 const sheet = Buffer.from(recoverySheet(ACCOUNT_ID, sheetCode, WHEN)).toString("latin1");
87
88 assert.ok(card.includes(cardCode), "the card does not contain its own code");
89 assert.ok(sheet.includes(sheetCode), "the sheet does not contain its own code");
90 assert.ok(!card.includes(sheetCode), "the Account Key card carries the recovery code");
91 assert.ok(!sheet.includes(cardCode), "the Recovery sheet carries the Account Key");
92});
93
94// The base-14 fonts have no glyph for a curly quote in this encoding, so the
95// writer refuses one rather than drawing a wrong character into the document
96// somebody is reading at the worst moment of their year.
97test("the writer refuses characters it cannot draw", async () => {
98 const { Page } = await import(mod("pdf.js"));
99 assert.throws(() => new Page().text(10, 10, "don’t"), /cannot draw/);
100 assert.throws(() => new Page().wrap(10, 10, 200, "an em — dash"), /cannot draw/);
101 // And the copy that ships is already clean, which is the point of the check.
102 assert.doesNotThrow(() => new Page().text(10, 10, "Keep it where you'd keep a passport"));
103});
104