You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
92 lines
3.5 KiB
92 lines
3.5 KiB
// The writer of capsules against Go, in the other direction: the capsules
|
|
// that this library writes, opened by Go. test/vectors/capsule_interop.json
|
|
// holds the recipes of capsule_interop_support.dart with what Go made of
|
|
// each capsule (tool/capsule_interop_go_verdicts.go). Go must open it with
|
|
// each credential, alone and together, to the files of the recipe, in the
|
|
// byte order of their paths, with their SHA-256 and their mtime as the
|
|
// writer stores it, refuse it without a credential, find 16 stanzas in
|
|
// INNER_ACCESS_AGE, encode each layer and the .dkk again to the bytes
|
|
// written, and, for a seeded sample, write the same bytes itself. A seeded
|
|
// sample is written again here and must be the one that Go read.
|
|
|
|
import 'package:datekeys/datekeys.dart';
|
|
import 'package:datekeys/src/body.dart' show areaLen;
|
|
import 'package:datekeys/src/bytes.dart' show utf8Bytes;
|
|
import 'package:test/test.dart';
|
|
|
|
import 'capsule_interop_support.dart';
|
|
import 'capsule_writer_support.dart';
|
|
|
|
/// The files that the recipe [r] puts in its capsule, as Go must open
|
|
/// them.
|
|
List<Json> expectedFiles(Json r) {
|
|
final files = [...listOf(r['files'] ?? const [])]
|
|
..sort(
|
|
(a, b) => compareBytes(
|
|
utf8Bytes(a['path']! as String),
|
|
utf8Bytes(b['path']! as String),
|
|
),
|
|
);
|
|
return [
|
|
for (final f in files)
|
|
{
|
|
'path': f['path'],
|
|
'size': contentOf(f).length,
|
|
'sha256': toHex(sha256Of(contentOf(f))),
|
|
if (f['mtime'] case [final int s, _] when s >= 0 && s <= maxMTime)
|
|
'mtime': s,
|
|
},
|
|
];
|
|
}
|
|
|
|
/// The tests of the samples of [doc], with the recipes that wrote them.
|
|
void interopCases(Json doc, {required bool rewrite}) {
|
|
final recipes = {for (final s in interopSamples()) s['name']: s};
|
|
for (final g in listOf(doc['samples'])) {
|
|
final r = recipes[g['name']]!;
|
|
test(g['name'], () async {
|
|
expect(g['reencoded'], isTrue);
|
|
final inspect = g['inspect']! as Json;
|
|
expect(inspect['format'], 3);
|
|
expect(inspect['policy'], r['policy'] ?? 'time_only');
|
|
expect(inspect['note'], r['note'] ?? '');
|
|
final tak = r['policy'] == 'time_and_key';
|
|
expect(g['inner_stanzas'], tak ? 16 : null);
|
|
expect(g['dkk_capsule_digest'], r['portable'] == true ? true : null);
|
|
expect(g['go_same'], r['random'] == 'seeded' ? true : null);
|
|
final comment = (r['comment'] as String? ?? '').replaceAll('\r\n', '\n');
|
|
final opens = listOf(g['opens']);
|
|
final names = [for (final o in opens) o['with']];
|
|
expect(names, [
|
|
if (!tak) 'time',
|
|
if (tak) ...[
|
|
for (final l in (r['identities'] as List? ?? const [])) 'identity $l',
|
|
if (r['words'] != null) 'words',
|
|
if (r['portable'] == true) 'portable',
|
|
'all',
|
|
'none',
|
|
],
|
|
]);
|
|
for (final o in opens) {
|
|
if (o['with'] == 'none') {
|
|
expect(o['result'], 'ERR_ACCESS_REQUIRED');
|
|
continue;
|
|
}
|
|
expect(o['result'], 'ok', reason: '${o['with']}');
|
|
expect(o['files'], expectedFiles(r));
|
|
expect(o['comment'], comment);
|
|
expect(o['author'], r['author'] ?? '');
|
|
expect(o['verdicts'], ['F0', 'S0']);
|
|
expect(o['area_len'], r['test_area_len'] ?? areaLen);
|
|
}
|
|
if (rewrite && r['random'] == 'seeded') {
|
|
final w = await writeSample(r);
|
|
expect(w.dkc.length, g['length_dkc']);
|
|
expect(toHex(sha256Of(w.dkc)), g['sha256']);
|
|
final dkk = w.dkk;
|
|
expect(dkk == null ? null : toHex(sha256Of(dkk)), g['dkk_sha256']);
|
|
}
|
|
});
|
|
}
|
|
}
|