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.
102 lines
3.4 KiB
102 lines
3.4 KiB
// The age writer against Go, in the other direction: the files that this
|
|
// library writes, opened by Go. test/vectors/age_interop.json holds the
|
|
// recipes of test/age_interop_support.dart, the files that this library
|
|
// wrote from them with SeededRandomSource (their length and SHA-256, and
|
|
// their bytes when small) and what filippo.io/age and agewrap made of each
|
|
// with each opener (tool/age_interop_go_verdicts.go). Here each file is
|
|
// written again from its recipe and must be the one that Go read; Go
|
|
// opened it to the plaintext of the recipe, or refused it where the recipe
|
|
// expects a refusal; and this library makes the same of it as Go: the same
|
|
// plaintext or the same error text, and the same stanza rules. The vectors
|
|
// come from a Dart constant, so that this also runs compiled to
|
|
// JavaScript, without the expensive samples.
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'package:datekeys/datekeys.dart';
|
|
import 'package:datekeys/src/age.dart';
|
|
import 'package:datekeys/src/agewrap.dart';
|
|
import 'package:datekeys/src/sha256.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import 'age_interop_support.dart';
|
|
import 'age_writer_support.dart';
|
|
import 'vectors/age_interop.g.dart';
|
|
|
|
final Json vectors = jsonDecode(ageInteropJson) as Json;
|
|
|
|
const recipeKeys = [
|
|
'name',
|
|
'seed',
|
|
'recipients',
|
|
'inner',
|
|
'length',
|
|
'pieces',
|
|
'node',
|
|
'openers',
|
|
];
|
|
|
|
Json recipe(Json s) => {
|
|
for (final k in recipeKeys)
|
|
if (s.containsKey(k)) k: s[k],
|
|
};
|
|
|
|
String rule(void Function() f) {
|
|
try {
|
|
f();
|
|
return 'ok';
|
|
} on DateKeysException catch (e) {
|
|
return e.message;
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
final samples = listOf(vectors['samples']);
|
|
|
|
test('the recipes are those that the tool writes', () {
|
|
expect(jsonDecode(jsonEncode(interopSamples())), samples.map(recipe));
|
|
});
|
|
|
|
for (final s in samples.where((s) => !isWeb || s['node'] != false)) {
|
|
test(s['name'], () {
|
|
final file = writeSample(s);
|
|
expect(file.length, s['file_length']);
|
|
expect(toHex(sha256(file)), s['file_sha256']);
|
|
if (s['file'] != null) expect(toHex(file), s['file']);
|
|
final header = parseAgeHeader(file);
|
|
expect(header.length, s['header_length']);
|
|
final stanzas = header.stanzas;
|
|
expect([for (final st in stanzas) st.type], s['stanzas']);
|
|
|
|
// Go opened it to the plaintext of the recipe with some opener, and
|
|
// this library makes of it what Go made with each.
|
|
final plain = toHex(sha256(finalPlaintext(s)));
|
|
final openers = listOf(s['openers']);
|
|
final verdicts = listOf(s['verdicts']);
|
|
expect(verdicts, hasLength(openers.length));
|
|
expect(verdicts.where((v) => v['error'] == null), isNotEmpty);
|
|
for (var i = 0; i < openers.length; i++) {
|
|
final v = verdicts[i];
|
|
if (v['error'] == null) expect(v['plaintext_sha256'], plain);
|
|
expect(openWith(file, openers[i]), v, reason: 'opener $i');
|
|
}
|
|
|
|
// The stanza rules of agewrap, as Go applied them.
|
|
final q = quicknet();
|
|
expect({
|
|
'payload': rule(() => checkPayloadStanzas(stanzas)),
|
|
'access_0': rule(() => checkAccessStanzas(stanzas, 0)),
|
|
'access_16': rule(() => checkAccessStanzas(stanzas, accessSlots)),
|
|
'time_1000': rule(
|
|
() => checkTimeStanzas(
|
|
stanzas,
|
|
round: 1000,
|
|
chainHashHex: q.chainHashHex,
|
|
profileId: q.id,
|
|
),
|
|
),
|
|
}, s['rules']);
|
|
});
|
|
}
|
|
}
|