// The QR encoder, against two implementations that are not ours. // // This is the artifact that has to be right years after it was printed, on a // phone we have never seen, when the person holding it has already lost // everything else. So it is checked twice on every run: // // 1. ZXing decodes each symbol back to its payload — "will it scan?" // 2. Python's `qrcode` produces the same matrix, bit-for-bit, at the same // version, EC level and mask — "is it the RIGHT symbol?" // // The second question is the one decoding cannot answer. Error correction // exists to make a damaged symbol readable, which means a symbol with a real // mistake in it can still scan — and would, right up until the day somebody's // print was slightly faded too. Reed–Solomon would hide our bug behind its // own redundancy. // // Needs python3 with zxing-cpp, qrcode, pillow and numpy: // // pip install --user zxing-cpp qrcode pillow numpy // // A HARD gate. If the oracle is missing this fails rather than skipping: a // skip on this test is indistinguishable from a pass, on the one artifact // where being wrong is unrecoverable. import { test } from "node:test"; import assert from "node:assert/strict"; import { fileURLToPath } from "node:url"; import { dirname, join } from "node:path"; import { run as runOracle } from "./oracle.mjs"; const here = dirname(fileURLToPath(import.meta.url)); const { encode, smallestVersion } = await import( join(here, "..", "internal", "contra", "static", "crypto", "qr.js")); // Real printed forms, the extremes of the alphabet, and the boundaries where // a version rolls over — which is where a capacity table is wrong if it is // wrong anywhere. const PAYLOADS = [ "C1-E3D5Z-Z4R6M-8BQ93-740R9-8TWYX-MHGE0", "A1-YR7V5-0F8RW-QPE9D-DTK5X-83KV1-0GJC8", "A1-00000-00000-00000-00000-00000-00000", "C1-ZZZZZ-ZZZZZ-ZZZZZ-ZZZZZ-ZZZZZ-ZZZZZ", "0", "HELLO WORLD", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:", ]; function ask(cases) { return JSON.parse(runOracle("qroracle.py", cases)); } test("every symbol scans, and is the symbol another encoder would have made", () => { const cases = []; for (const text of PAYLOADS) { for (const ec of ["L", "M", "Q", "H"]) { for (let mask = 0; mask < 8; mask++) { const r = encode(text, { ec, mask }); cases.push({ text, ec, mask, version: r.version, modules: r.modules }); } } } const verdicts = ask(cases); assert.equal(verdicts.length, cases.length); const bad = verdicts.filter((v) => !v.ok); const describe = (v) => `${v.ec} mask=${v.mask} v${v.version} ${JSON.stringify(v.text).slice(0, 30)}: ` + `scans=${v.scans} identical=${v.identical}` + (v.differing_modules ? ` differing=${v.differing_modules}` : "") + (v.reference_error ? ` refErr=${v.reference_error}` : ""); assert.deepEqual(bad.map(describe), [], `${bad.length} of ${cases.length} symbols wrong`); }); // The mask the encoder picks on its own must also be right — the sweep above // forces every mask, which would not catch a broken penalty score. test("the automatically chosen mask still scans", () => { const cases = PAYLOADS.map((text) => { const r = encode(text, { ec: "Q" }); return { text, ec: "Q", mask: r.mask, version: r.version, modules: r.modules }; }); for (const v of ask(cases)) { assert.ok(v.scans, `${JSON.stringify(v.text)} did not scan at its chosen mask`); assert.ok(v.identical, `${JSON.stringify(v.text)} differs from the reference`); } }); // A CONTRA code at the EC level the kit prints at. Pinned so a change to // either is a deliberate one. test("a printed code is a version 3 symbol at EC-Q", () => { const code = "C1-E3D5Z-Z4R6M-8BQ93-740R9-8TWYX-MHGE0"; assert.equal(code.length, 38); assert.equal(smallestVersion(code, "Q"), 3); const r = encode(code, { ec: "Q" }); assert.equal(r.version, 3); assert.equal(r.modules.length, 29, "a version 3 symbol is 4*3+17 = 29 modules across"); }); // The encoder refuses rather than guessing, on both edges. test("it refuses what it cannot encode", () => { assert.throws(() => encode("lower case"), /not alphanumeric/); assert.throws(() => encode("héllo"), /not alphanumeric/); assert.throws(() => encode("Z".repeat(200), { ec: "H" }), /version above 6/); assert.throws(() => encode("SHORT", { ec: "X" }), /unknown EC level/); });