// The verdicts of a signature of alg 2 and of a seal of seal_type 2 against // Go, on the VM: // // - every case of testdata/vectors/security_cms.json, frozen by the Go // reference at the draft v0.12, read in its context as TestCMSVectors of // Go reads it, with its verdicts, the result of each signer, the authority // and t of a valid seal, and its lines, byte for byte; // - every case of test/vectors/securitycms_vectors.json, which // tool/security_go_vectors.go makes and evaluates with package capsule of // the reference: signers of every result, required and foreign, the // validity of a certificate at the time of its seal, t plus the accuracy // against the round time at the nanosecond, a seal of each kind beside a // signature of each kind, and mutations; // - and the part of both that securitycms_vectors.g.dart holds for the // tests compiled to JavaScript, which must be that of the files. @TestOn('vm') library; import 'dart:convert'; import 'dart:io'; import 'package:datekeys/datekeys.dart'; import 'package:test/test.dart'; import 'open_vectors_support.dart'; import 'security_support.dart'; import 'securitycms_support.dart'; import 'vectors/securitycms_vectors.g.dart'; Json readJson(String path) => jsonDecode(File(path).readAsStringSync()) as Json; void main() { group('security_cms.json', () { final f = readJson('testdata/vectors/security_cms.json'); final cases = (f['cases']! as List).cast(); test('has the 135 cases of its README, which reach every verdict but X ' 'and F3', () { expect(f['spec'], specVersion); expect(f['description'], contains('v0.12')); expect(cases, hasLength(135)); expect({for (final c in cases) c['name']}, hasLength(135)); final reached = { for (final c in cases) ...[c['signature'], c['seal']], }; expect(reached, { 'F0', 'F1', 'F2', 'F4', 'F5', 'F6', // 'S0', 'S1', 'S2', 'S3', 'S4', 'S5', }); // Every result of a signer, and a foreign one. final results = { for (final c in cases) for (final s in (c['signers'] as List? ?? const []).cast()) s['result'], }; expect(results, {for (final r in SignerResult.values) r.code}); expect(cases.where((c) => c['foreign_signers'] != null), isNotEmpty); }); for (final c in cases) { test(str(c, 'name'), () { expect(cmsVectorDifferences(c), isEmpty); }); } }); group('securitycms_vectors.json', () { final f = readJson('test/vectors/securitycms_vectors.json'); final cases = (f['cases']! as List).cast(); final chunks = chunksOf(f); final bases = basesOf(f, chunks); final contexts = contextsOf(f); final texts = (f['texts']! as List).cast(); test('is of this spec, from its generator, with every verdict and every ' 'result of a signer, required and foreign', () { expect(f['spec'], specVersion); expect(f['generator'], 'tool/security_go_vectors.go'); expect(cases, hasLength(greaterThan(700))); final verdicts = { for (final c in cases) ...[c['signature'], c['seal']], }; expect(verdicts, containsAll(['F0', 'F1', 'F2', 'F4', 'F5'])); expect(verdicts, containsAll(['F6', 'S0', 'S1', 'S2', 'S3'])); expect(verdicts, containsAll(['S4', 'S5'])); Set resultsOf(String list) => { for (final c in cases) if (c['detail'] case final Json d) for (final s in (d[list]! as List).cast()) s['result'], }; final all = {for (final r in SignerResult.values) r.code}; expect(resultsOf('signers'), all); expect(resultsOf('foreign'), all.difference({'absent'})); // A seal before the round time and one that is not, for a signer // and for key 3, and Go's zero time, which gives no earliest seal. final befores = { for (final c in cases) if (c['detail'] case final Json d) for (final s in (d['signers']! as List).cast()) s['before'], }; expect(befores, {true, false}); expect( cases.where( (c) => c['seal'] == 'S4' && c['sealed_at'] == null && c['detail'] != null, ), isNotEmpty, ); expect(contexts.where((c) => c.roundTime == null), isNotEmpty); }); test('every case, with the verdicts, the lines, the detail and the ' 'earliest seal of Go', () { for (final c in cases) { final area = cmsAreaOf(c, chunks, bases); expect( evaluateDifferences(c, area, contexts, texts), isEmpty, reason: c['name'] as String? ?? canonical(c), ); } }); test('securitycms_vectors.g.dart holds a part of it', () { final part = jsonDecode(securityCmsVectorsJson) as Json; for (final k in ['spec', 'generator', 'contexts', 'texts']) { expect(canonical(part[k]), canonical(f[k]), reason: k); } expect( [for (final b in basesOf(part, const [])) toHex(b)], [for (final b in bases) toHex(b)], ); // Each case of the part is one of the file, in the same order, with // the same area. String key(Json c) => canonical({ for (final e in c.entries) if (e.key != 'pieces' && e.key != 'hex') e.key: e.value, }); final some = (part['cases']! as List).cast(); expect(some, hasLength(greaterThan(40))); var at = 0; for (final c in some) { while (at < cases.length && key(cases[at]) != key(c)) { at++; } expect(at, lessThan(cases.length), reason: key(c)); expect( cmsAreaOf(c, const [], bases), cmsAreaOf(cases[at], chunks, bases), reason: key(c), ); } }); }); test('the fixtures of securitycms_vectors.g.dart are those of testdata/, ' 'with what their records say', () { final fixtures = ((jsonDecode(securityCmsFixturesJson) as Json)['fixtures']! as List) .cast(); expect( [for (final x in fixtures) x['name']], ['format3_signed_cms', 'format3_sealed'], ); for (final x in fixtures) { final name = str(x, 'name'); final r = readJson('testdata/fixtures/$name.json'); expect( x['dkc'], toHex(File('testdata/fixtures/${r['file']}').readAsBytesSync()), reason: name, ); for (final k in ['release', 'unlock_at', 'verdicts']) { expect(canonical(x[k]), canonical(r[k]), reason: '$name: $k'); } final sig = r['signature'] as Json?; expect( canonical(x['signer_results']), canonical(sig?['signer_results']), reason: name, ); final seal = r['seal'] as Json?; expect( canonical(x['seal']), canonical( seal == null ? null : {'holder': seal['holder'], 'time': seal['time']}, ), reason: name, ); } }); }