import { describe, expect, it } from 'vitest'; import { defaultRegistry, type ExtensionRegistry, ExtensionSet, inspectView, inspectWith } from '../dkc/index.ts'; import { frame, replaceText, split } from '../dkc/testing/capsule.ts'; import { arr, b, dkRound, ext, map, t, u, bn } from '../dkc/testing/cborhex.ts'; import { h, listTestdata, readBytes, readJSON } from '../dkc/testing/testdata.ts'; import { cliJSON } from './format.ts'; import { buildReport, type Report } from './report.ts'; const registry = await defaultRegistry(); const timeOnly = readBytes('fixtures/time_only_extensions.dkc'); const parts = split(timeOnly); function report(dkc: Uint8Array, extensions?: ExtensionRegistry, fileName = 'x.dkc'): Report { const inspection = inspectWith(dkc, registry, extensions); return buildReport(extensions === undefined ? { fileName, bytes: dkc, size: dkc.length, inspection } : { fileName, bytes: dkc, size: dkc.length, inspection, extensions }); } // A PUBLIC_HEADER like the one of time_only_extensions, with other extensions. function header(o: { crit?: string; non?: string; dk?: string }): Uint8Array { const entries: [number, string][] = [ [0, t('datekeycap')], [1, u(1)], [2, bn(16, 7)], [3, o.dk ?? t(dkRound(2000))], [4, u(0)], ]; if (o.crit) entries.push([5, o.crit]); if (o.non) entries.push([6, o.non]); return h(map(...entries)); } interface FixtureRecord { capsule_id: string; datekey: string; access_policy: string; unlock_at: string; prelude: string; outer_stanzas: { type: string; args: string[] }[]; payload_stanzas: { type: string; args: string[] }[]; header_extensions?: { critical: boolean; id: string; version: number; data?: string }[]; } describe('buildReport', () => { it('shows every official fixture as its JSON record', () => { for (const f of listTestdata('fixtures', '.dkc')) { const dkc = readBytes(f); const want = readJSON(f.replace(/\.dkc$/, '.json')); const name = f.slice('fixtures/'.length); const r = report(dkc, undefined, name); expect(r.valid, f).toBe(true); expect(r.failure).toBeUndefined(); expect(r.steps.map((s) => [s.step, s.state])).toEqual([1, 2, 3, 4, 5, 6, 7, 8].map((s) => [s, 'ok'])); expect(r.capsule).toMatchObject({ capsuleId: want.capsule_id, dateKey: want.datekey, accessPolicy: want.access_policy, network: 'datekeys:quicknet:v1' }); expect(JSON.parse(r.capsule!.dateKeyJSON)).toEqual({ version: 1, network: 'datekeys:quicknet:v1', round: r.capsule!.round }); expect(r.unlock?.rfc3339).toBe(want.unlock_at); expect(r.unlock?.epochMs).toBe(Date.parse(want.unlock_at)); expect(r.prelude?.hex).toBe(want.prelude); expect(r.prelude).toMatchObject({ magic: 'DKC1', version: 1, flags: '0x00', reserved: '0x0000' }); expect(r.prelude!.payloadOffset + r.prelude!.payloadLength!).toBe(dkc.length); expect(r.outerStanzas).toEqual(want.outer_stanzas); expect(r.payloadStanzas).toEqual(want.payload_stanzas); expect(r.tlock?.every((c) => c.match)).toBe(true); expect(r.profile).toMatchObject({ id: 'datekeys:quicknet:v1', network: 'quicknet', provider: 'drand', period: 3, genesis: '2023-08-23T15:09:27Z' }); expect(r.extensions?.map((e) => ({ critical: e.critical, id: e.id, version: e.version, data: e.hex }))).toEqual( (want.header_extensions ?? []).map((e) => ({ critical: e.critical, id: e.id, version: e.version, data: e.data ?? '' })), ); expect(r.view).toEqual(inspectView(inspectWith(dkc, registry), name)); expect(r.json).toBe(cliJSON(r.view)); expect(r.view.file).toBe(name); expect(r.readLength).toBe(dkc.length); } }); it('shows header extension data as text, never as CBOR when it is not', () => { const r = report(timeOnly); expect(r.extensions).toEqual([ { critical: false, id: 'org.example.label', idEscaped: false, version: 1, known: false, length: 12, hex: '7075626c6963206c6162656c', text: 'public label', }, ]); }); it('decodes CBOR data informatively and escapes identifiers', () => { const hb = header({ non: arr(ext('a\u202eb', 3, b('a2000701667365616c6564')), ext('z', 1)) }); const r = report(frame({ ...parts, header: hb })); expect(r.valid).toBe(true); expect(r.extensions).toEqual([ { critical: false, id: '"a\\u202eb"', idEscaped: true, version: 3, known: false, length: 11, hex: 'a2000701667365616c6564', cbor: { text: '{\n 0: 7,\n 1: "sealed"\n}', truncated: false }, }, { critical: false, id: 'z', idEscaped: false, version: 1, known: false, length: 0, hex: '' }, ]); }); it('reports an unknown critical extension at step 4 and still lists it', () => { const hb = header({ crit: arr(ext('org.example.must-understand', 1, b('01'))) }); const r = report(frame({ ...parts, header: hb })); expect(r.valid).toBe(false); expect(r.failure).toEqual({ step: 4, code: 'ERR_EXTENSION_CRITICAL_UNKNOWN', message: 'capsule: PUBLIC_HEADER: extension org.example.must-understand v1: ERR_EXTENSION_CRITICAL_UNKNOWN', }); expect(r.steps.map((s) => s.state)).toEqual(['ok', 'ok', 'ok', 'failed', 'not-run', 'not-run', 'not-run', 'not-run']); expect(r.steps[3]).toMatchObject({ code: 'ERR_EXTENSION_CRITICAL_UNKNOWN', name: 'header validation' }); expect(r.steps[4]!.detail).toBeUndefined(); expect(r.extensions).toEqual([ { critical: true, id: 'org.example.must-understand', idEscaped: false, version: 1, known: false, length: 1, hex: '01', cbor: { text: '1', truncated: false } }, ]); expect(r.capsule?.capsuleId).toBe('07'.repeat(16)); expect(r.unlock).toBeUndefined(); expect(r.outerStanzas).toBeUndefined(); expect(r.tlock).toBeUndefined(); expect(JSON.parse(r.json)).toMatchObject({ valid: false, error: 'ERR_EXTENSION_CRITICAL_UNKNOWN' }); }); it('marks known extensions and the unusable ones', () => { const reg: ExtensionRegistry = { known: (id) => id === 'a' || id === 'b', validateData: (e) => (e.id === 'b' ? new Error('bad') : undefined), }; const r = report(frame({ ...parts, header: header({ crit: arr(ext('a', 1, b('01'))), non: arr(ext('b', 1, b('02')), ext('c', 1)) }) }), reg); expect(r.valid).toBe(true); expect(r.extensions?.map((e) => [e.id, e.critical, e.known, e.unusable])).toEqual([ ['a', true, true, undefined], ['b', false, true, 'extension b v1: data: bad: ERR_EXTENSION_DATA_INVALID'], ['c', false, false, undefined], ]); const known = report(frame({ ...parts, header: header({ crit: arr(ext('a', 1)) }) }), new ExtensionSet([['a', [1]]])); expect(known.extensions?.[0]?.known).toBe(true); }); it('compares the tlock stanza with the pinned profile', () => { const round = report(replaceText(timeOnly, '-> tlock 2000 ', '-> tlock 2001 ')); expect(round.failure?.code).toBe('ERR_ROUND_MISMATCH'); expect(round.unlock?.rfc3339).toBe('2023-08-23T16:49:24Z'); expect(round.tlock).toEqual([ { label: 'Argumentos', found: '2', expected: '2', match: true }, { label: 'Ronda', found: '2001', expected: '2000', match: false }, { label: 'Cadena (chain hash)', found: '52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971', expected: '52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971', match: true, }, ]); const noChain = report(frame({ ...parts, sealed: replaceText(parts.sealed, ' 52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971', '') })); expect(noChain.tlock?.map((c) => [c.found, c.match])).toEqual([ ['1', false], ['2000', true], [undefined, false], ]); const noTlock = report(frame({ ...parts, sealed: replaceText(parts.sealed, '-> tlock', '-> TLOCK') })); expect(noTlock.failure?.step).toBe(5); expect(noTlock.outerStanzas?.map((s) => s.type)).toEqual(['TLOCK']); expect(noTlock.tlock).toBeUndefined(); }); it('keeps what the framing steps found', () => { const r = report(timeOnly.subarray(0, 100)); expect(r.failure).toMatchObject({ step: 3, code: 'ERR_INTEGRITY' }); expect(r.prelude).toMatchObject({ publicHeaderLen: 159, sealedControlLen: 482, payloadOffset: 657 }); expect(r.prelude?.payloadLength).toBeUndefined(); expect(r.capsule).toBeUndefined(); expect(r.extensions).toBeUndefined(); const empty = report(new Uint8Array(0)); expect(empty.failure).toMatchObject({ step: 1, code: 'ERR_INVALID_MAGIC' }); expect(empty.prelude).toBeUndefined(); expect(empty.steps.filter((s) => s.state === 'not-run')).toHaveLength(7); // An unknown profile: the header decoded, no profile, no comparison. const other = report(frame({ ...parts, header: header({ dk: t(dkRound(2000, 'datekeys:evmnet:v1')) }) })); expect(other.failure?.code).toBe('ERR_UNKNOWN_PROFILE'); expect(other.capsule?.network).toBe('datekeys:evmnet:v1'); expect(other.profile).toBeUndefined(); }); it('records the prefix that was read', () => { const inspection = inspectWith(timeOnly, registry); const r = buildReport({ fileName: 'big.dkc', bytes: timeOnly, size: timeOnly.length + 5_000_000, inspection }); expect(r.readLength).toBe(timeOnly.length); expect(r.size).toBe(timeOnly.length + 5_000_000); expect(r.prelude!.payloadLength).toBe(timeOnly.length + 5_000_000 - 657); }); });