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.
150 lines
5.4 KiB
150 lines
5.4 KiB
// The sealing of the locator and the envelope against Go, also compiled to
|
|
// JavaScript: test/vectors/locator_seal.json, from
|
|
// tool/locator_seal_go_vectors.go, holds what Seal and NewEnvelope of Go
|
|
// write while crypto/rand reads the keystream of a seed. With the same
|
|
// seed, sealLocator and newEnvelope draw the same values and write the same
|
|
// bytes; the sealed locators open with the release of their round, and the
|
|
// envelopes with their locator. Seal refuses what Go refuses, with its
|
|
// text, before drawing anything.
|
|
|
|
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:datekeys/src/bytes.dart';
|
|
import 'package:datekeys/src/errors.dart';
|
|
import 'package:datekeys/src/locator.dart';
|
|
import 'package:datekeys/src/locator_seal.dart';
|
|
import 'package:datekeys/src/profile.dart';
|
|
import 'package:datekeys/src/release.dart' show Release;
|
|
import 'package:datekeys/src/sha256.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import 'age_support.dart' show pattern;
|
|
import 'random_support.dart';
|
|
import 'vectors/locator_seal.g.dart';
|
|
|
|
final Json vectors = jsonDecode(locatorSealJson) as Json;
|
|
|
|
Locator locatorOf(Json j) => Locator(
|
|
addresses: [
|
|
for (final a in listOf(j['addresses']))
|
|
LocatorAddress(a['uri']! as String, a['offset']! as int),
|
|
],
|
|
envelopeKey: fromHex(j['envelope_key']! as String),
|
|
envelopeHeader: fromHex(j['envelope_header']! as String),
|
|
restDigest: fromHex(j['rest_digest']! as String),
|
|
restSize: j['rest_size']! as int,
|
|
capsuleDigest: fromHex(j['capsule_digest']! as String),
|
|
);
|
|
|
|
Json jsonOf(Locator l) => {
|
|
'addresses': [
|
|
for (final a in l.addresses) {'uri': a.uri, 'offset': a.offset},
|
|
],
|
|
'envelope_key': toHex(l.envelopeKey),
|
|
'envelope_header': toHex(l.envelopeHeader),
|
|
'rest_digest': toHex(l.restDigest),
|
|
'rest_size': l.restSize,
|
|
'capsule_digest': toHex(l.capsuleDigest),
|
|
};
|
|
|
|
void expectFile(Uint8List b, Json want) {
|
|
expect(b.length, want['length']);
|
|
expect(toHex(sha256(b)), want['sha256']);
|
|
if (want['hex'] != null) expect(toHex(b), want['hex']);
|
|
}
|
|
|
|
Release releaseOf(int round) => Release(
|
|
round,
|
|
fromHex((vectors['releases']! as Json)['$round']! as String),
|
|
);
|
|
|
|
void main() {
|
|
final q = quicknet();
|
|
|
|
for (final c in listOf(vectors['seal'])) {
|
|
test('Seal writes the bytes of Go: ${c['name']}', () {
|
|
final round = c['round']! as int;
|
|
final loc = locatorOf(c['locator']! as Json);
|
|
expect(loc.marshal().length, c['plaintext_length']);
|
|
final r = RecordingSource(seeded(c['seed']! as String));
|
|
final sealed = sealLocator(q, round, loc, random: r);
|
|
expectFile(sealed, c['sealed']! as Json);
|
|
expect(r.draws, hasLength(c['draws']));
|
|
expect(r.json.first, c['first_draw']);
|
|
if (c['opens'] == true) {
|
|
final back = openLocator(q, round, releaseOf(round), sealed);
|
|
expect(jsonOf(back), c['locator']);
|
|
// Another round does not open it.
|
|
final other = round == 1000 ? 1001 : 1000;
|
|
expect(
|
|
() => openLocator(q, other, releaseOf(other), sealed),
|
|
throwsA(isA<LocatorException>()),
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
test('Seal refuses what Go refuses, before drawing anything', () {
|
|
for (final c in listOf(vectors['seal_errors'])) {
|
|
final r = RecordingSource(seeded('unused'));
|
|
String? got;
|
|
try {
|
|
sealLocator(
|
|
q,
|
|
c['round']! as int,
|
|
locatorOf(c['locator']! as Json),
|
|
random: r,
|
|
);
|
|
} on LocatorException catch (e) {
|
|
got = e.message;
|
|
} on DateKeysException catch (e) {
|
|
got = e.message;
|
|
}
|
|
expect(got, c['error'], reason: c['name'] as String?);
|
|
expect(r.draws, hasLength(c['draws']));
|
|
}
|
|
});
|
|
|
|
for (final c in listOf(vectors['envelope'])) {
|
|
if (isWeb && c['node'] != true) continue;
|
|
test('NewEnvelope writes the envelope of Go: ${c['dkc_length']} bytes', () {
|
|
final dkc = pattern(c['dkc_length']! as int);
|
|
final r = RecordingSource(seeded(c['seed']! as String));
|
|
final e = newEnvelope(dkc, random: r);
|
|
expect(jsonOf(e.locator), c['locator']);
|
|
expectFile(e.rest, c['rest']! as Json);
|
|
expect(r.json.first, (c['draws']! as List).first);
|
|
expect(e.locator.openEnvelope(e.rest), dkc);
|
|
});
|
|
}
|
|
|
|
test('a writer and a reader, as Go: envelope, addresses, seal, open', () {
|
|
final c = vectors['flow']! as Json;
|
|
final dkc = pattern(c['dkc_length']! as int);
|
|
final r = RecordingSource(seeded(c['seed']! as String));
|
|
final e = newEnvelope(dkc, random: r);
|
|
final hidden = hideRest(fromHex(c['host']! as String), e.rest);
|
|
final loc = e.locator.withAddresses([
|
|
LocatorAddress('https://example.com/capsule'),
|
|
LocatorAddress('https://example.org/host.gif', hidden.offset),
|
|
]);
|
|
final round = c['round']! as int;
|
|
final sealed = sealLocator(q, round, loc, random: r);
|
|
expect(r.draws, hasLength(c['draws']));
|
|
expect(jsonOf(loc), c['locator']);
|
|
expectFile(e.rest, c['rest']! as Json);
|
|
expectFile(sealed, c['sealed']! as Json);
|
|
final got = openLocator(q, round, releaseOf(round), sealed);
|
|
final a = got.addresses[1];
|
|
expect(got.openEnvelope(got.restIn(hidden.file, a.offset)), dkc);
|
|
});
|
|
|
|
test('sealing leaves the locator as it was', () {
|
|
final loc = locatorOf((listOf(vectors['seal']).first)['locator']! as Json);
|
|
final before = loc.marshal();
|
|
sealLocator(q, 1000, loc, random: seeded('as it was'));
|
|
expect(loc.marshal(), before);
|
|
});
|
|
}
|