duly / test/doc.test.mjs · raw
// The line-item fingerprint. Client names and line items are encrypted in the browser;// only SHA-256 of the ciphertext goes on-chain (create_invoice's doc_hash). These tests// show the fingerprint is computed the way the contract and the Worker expect, that the// stored document reveals nothing, and that any change to it is caught when it is opened.import test from 'node:test';import assert from 'node:assert/strict';import { b64decode, b64encode, hex, sha256 } from '../web/lib/near.js';import { fromUrlKey, openDocument, sealDocument, toUrlKey, unwrapContentKey } from '../web/lib/doc.js';const doc = { v: 1, from: { name: 'Demo Studio' }, to: { name: 'Acme Ltd' }, items: [{ d: 'Logo design', q: '1', p: '100' }], taxBps: 2000 };const docKey = new Uint8Array(32).fill(7);test('the fingerprint is SHA-256 of the ciphertext, in the 64-hex form create_invoice takes', async () => {  const sealed = await sealDocument(doc, docKey);  assert.match(sealed.hash, /^[0-9a-f]{64}$/);  assert.equal(sealed.hash, hex(await sha256(b64decode(sealed.ct))));});test('the stored document holds no client name or line item', async () => {  const sealed = await sealDocument(doc, docKey);  const bytes = Buffer.from(b64decode(sealed.ct)).toString('latin1');  for (const secret of ['Acme', 'Demo Studio', 'Logo design']) assert.ok(!bytes.includes(secret), secret);});test('the client opens it with the key from the link, and the fingerprint matches', async () => {  const sealed = await sealDocument(doc, docKey);  const key = fromUrlKey(toUrlKey(sealed.contentKey));  assert.deepEqual(await openDocument(sealed.ct, key, sealed.hash), doc);});test('a document changed after issue is refused, even when it decrypts', async () => {  const original = await sealDocument(doc, docKey);  // The server swaps in a different document sealed with the same content key.  const forged = await sealDocument({ ...doc, to: { name: 'Mallory' } }, docKey);  const swapped = await unwrapContentKey(forged.wrap, docKey);  await assert.rejects(openDocument(forged.ct, swapped, original.hash), /does not match the invoice on-chain/);});test('a single flipped byte is refused', async () => {  const sealed = await sealDocument(doc, docKey);  const bytes = b64decode(sealed.ct);  bytes[bytes.length - 1] ^= 1;  await assert.rejects(openDocument(b64encode(bytes), sealed.contentKey, sealed.hash), /does not match the invoice on-chain/);});test('the merchant reopens it from any device with the key derived from their account', async () => {  const sealed = await sealDocument(doc, docKey);  const key = await unwrapContentKey(sealed.wrap, docKey);  assert.deepEqual(await openDocument(sealed.ct, key, sealed.hash), doc);  await assert.rejects(unwrapContentKey(sealed.wrap, new Uint8Array(32).fill(8)));});test('link keys are strict', () => {  assert.throws(() => fromUrlKey('short'), /Bad link key/);  assert.throws(() => fromUrlKey(`${'A'.repeat(42)}=`), /Bad link key/);});