// Tests of security.ts: the empty area that writers write, the verdicts of // the signature and of the seal on the cases of TestSecurityVerdicts of the // Go reference and a few more, and the Spanish lines of spec §29.7, all as // the reference gives them at spec-v0.10. import { describe, expect, it } from 'vitest'; import { h, hx } from './testing/testdata.ts'; import { arr, b, bn, map, t, u } from './testing/cborhex.ts'; import { encodeSecurity, evaluateSecurity, type Verdict, verdictLines, verdictText } from './security.ts'; const EMPTY = 'a20071646174656b6579732d73656375726974790101'; // An author-signature of alg 1 with a key of 32 zero bytes and a signature // of 64: 105 bytes, as Go's EncodeAuthorSignature(1, ...) writes it. const SIGNATURE = map([0, u(1)], [1, bn(32)], [2, bn(64)]); const SEAL = map([0, u(1)], [1, b('010203')]); // SECURITY_CBOR with keys 2 and 3 when given, as hex. function security(signature?: string, seal?: string): Uint8Array { const pairs: [number, string][] = [ [0, t('datekeys-security')], [1, u(1)], ]; if (signature !== undefined) pairs.push([2, b(signature)]); if (seal !== undefined) pairs.push([3, b(seal)]); return h(map(...pairs)); } describe('encodeSecurity', () => { it('writes the empty area of 22 bytes, which yields F0 and S0', () => { expect(hx(encodeSecurity())).toBe(EMPTY); expect(SIGNATURE.length / 2).toBe(105); expect(security(SIGNATURE).length).toBe(130); }); }); describe('evaluateSecurity', () => { it.each([ ['empty', security(), 'F0', 'S0'], ['a signature of alg 1', security(SIGNATURE), 'F1', 'S0'], ['a seal of seal_type 1', security(undefined, SEAL), 'F0', 'S1'], ['both', security(SIGNATURE, SEAL), 'F1', 'S1'], ['alg 0', security(map([0, u(0)], [1, b('')], [2, b('')])), 'F1', 'S0'], ['a signature that is no map', security('01'), 'F1', 'S0'], // The first row that holds decides: a seal with an unknown key and an // unknown seal_type breaks its schema, S2, before its type is read. ['a seal with an unknown key', security(undefined, 'a300070141000200'), 'F0', 'S2'], ['seal_type 0', security(undefined, map([0, u(0)], [1, b('01')])), 'F0', 'S2'], ['a seal that is not CBOR', security(SIGNATURE, 'ff'), 'F1', 'S2'], ['a seal with seal_type 2^32', security(undefined, map([0, u(2 ** 32)], [1, b('')])), 'F0', 'S2'], ['a seal with a trailing byte', security(undefined, SEAL + '00'), 'F0', 'S2'], ['a seal without its token', security(undefined, map([0, u(1)])), 'F0', 'S2'], ['a seal with key 2 in place of its token', security(undefined, map([0, u(1)], [2, b('')])), 'F0', 'S2'], ['a seal with an empty token', security(undefined, map([0, u(7)], [1, b('')])), 'F0', 'S1'], ['version 2', h(map([0, t('datekeys-security')], [1, u(2)])), 'X', 'X'], ['another type tag', h(map([0, t('datekeys-head')], [1, u(1)])), 'X', 'X'], ['an unknown key 4', h(map([0, t('datekeys-security')], [1, u(1)], [4, b('01')])), 'X', 'X'], ['key 2 not a byte string', h(map([0, t('datekeys-security')], [1, u(1)], [2, u(1)])), 'X', 'X'], ['an empty key 2', h(map([0, t('datekeys-security')], [1, u(1)], [2, b('')])), 'X', 'X'], ['a key 2 of 65537 bytes', h(map([0, t('datekeys-security')], [1, u(1)], [2, bn(65537, 1)])), 'X', 'X'], ['a byte more', h(EMPTY + '00'), 'X', 'X'], ['not CBOR', new TextEncoder().encode('security'), 'X', 'X'], ['no key 1', h(map([0, t('datekeys-security')])), 'X', 'X'], ['an array', h(arr(t('datekeys-security'), u(1))), 'X', 'X'], ])('%s: %s and %s', (_, input, signature, seal) => { expect(evaluateSecurity(input)).toEqual({ signature, seal }); }); }); describe('verdictLines', () => { const F1 = 'No se ha comprobado ninguna firma: trátala como no firmada.'; const S1 = 'Lleva un sello de tiempo que esta versión no sabe comprobar: aquí no prueba nada.'; const S2 = 'El sello de tiempo es ilegible: no prueba nada.'; it('shows X alone, and otherwise the signature and then the seal unless it is S0', () => { const lines = (signature: Verdict, seal: Verdict): string[] => verdictLines({ signature, seal }); expect(lines('X', 'X')).toEqual(['No se han podido comprobar la firma ni el sello: trátala como no firmada y sin fecha probada.']); expect(lines('F0', 'S0')).toEqual(['Sin firma de autor.']); expect(lines('F0', 'S1')).toEqual(['Sin firma de autor.', S1]); expect(lines('F0', 'S2')).toEqual(['Sin firma de autor.', S2]); expect(lines('F1', 'S0')).toEqual([F1]); expect(lines('F1', 'S1')).toEqual([F1, S1]); expect(lines('F1', 'S2')).toEqual([F1, S2]); expect(verdictText('S0')).toBe(''); }); });