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.
dateKeys-dart/test/locator_test.dart

235 lines
7.9 KiB

// The locator and the envelope against Go (spec §44.1), from the part of
// locator_vectors.json that locator_vectors.g.dart holds, so that it runs on
// the VM and compiled to JavaScript: the padding of every base, plaintexts
// valid and broken, Marshal at every limit, sealed locators opened with
// their release and with others, and the envelope, its rest and its
// split. locator_vm_test.dart runs every case of the files.
library;
import 'dart:typed_data';
import 'package:datekeys/src/bytes.dart';
import 'package:datekeys/src/locator.dart';
import 'package:test/test.dart';
import 'locator_support.dart';
import 'vectors/locator_vectors.g.dart';
/// The cases of the locator, shared with locator_vm_test.dart, which runs
/// them on the whole file.
void locatorCases(Json v) {
final envelope = v['envelope']! as Json;
final plainBases = [
for (final h in v['plaintext_bases']! as List<Object?>)
fromHex(h! as String),
];
final sealedBases = [
for (final s in (v['sealed_bases']! as List<Object?>).cast<Json>())
fromHex(s['sealed']! as String),
];
Locator base({
List<LocatorAddress>? addresses,
List<int>? key,
List<int>? header,
List<int>? restDigest,
int? restSize,
List<int>? capsuleDigest,
}) => Locator(
addresses:
addresses ?? [LocatorAddress('https://ejemplo.org/foto.jpg', 3000)],
envelopeKey: key ?? fromHex(envelope['key']! as String),
envelopeHeader: header ?? fromHex(envelope['header']! as String),
restDigest: restDigest ?? fromHex(envelope['rest_digest']! as String),
restSize: restSize ?? envelope['rest_size']! as int,
capsuleDigest:
capsuleDigest ?? fromHex(envelope['capsule_digest']! as String),
);
test('locatorPlaintextLength gives the least multiple that key 6 fills, '
'on every base from -4100 to 16484', () {
var n = 0;
for (final r in rows(v, 'padding')) {
for (var b = r[0]! as int; b <= (r[1]! as int); b++) {
expect(locatorPlaintextLength(b), r[2], reason: '$b');
n++;
}
}
expect(n, 4100 + 1 + 16484);
});
test('unmarshalLocator reads plaintexts as Unmarshal', () {
for (final c in rows(v, 'plaintexts')) {
final b = applyLocatorEdits(plainBases[c[0]! as int], c[1]);
Locator? l;
expect(
errorText(() => l = unmarshalLocator(b)),
textOf(v, c[2]),
reason: '${c[1]}',
);
if (c[3] != null) {
expect(summaryOf(l!), expandSummary(c[3]), reason: '${c[1]}');
// As FuzzUnmarshal of Go: an address that a reader uses passes the
// rules, and has a host to show.
for (final a in l!.usable) {
checkAddressUri(a.uri);
expect(a.host, isNotEmpty);
}
}
}
});
test('marshal writes the plaintext of Marshal, or its error', () {
for (final c in rows(v, 'marshal')) {
final l = base(
addresses: [
for (final a in (c[0]! as List<Object?>).cast<List<Object?>>())
LocatorAddress(uriOf(a[0]), a[1]! as int),
],
header: c[1] == null ? null : fromHex(c[1]! as String),
restSize: c[2]! as int,
);
Uint8List? b;
expect(errorText(() => b = l.marshal()), textOf(v, c[3]), reason: '$c');
if (b != null) {
expect([b!.length, sha256Hex(b!)], [c[4], c[5]], reason: '$c');
// It reads back as it was written.
expect(summaryOf(unmarshalLocator(b!)), summaryOf(l));
}
}
});
test('openLocator opens a sealed locator with the release of its round, '
'as Open', () {
for (final c in rows(v, 'open')) {
final round = c[1]! as int;
final b = applyLocatorEdits(sealedBases[c[0]! as int], c[4]);
Locator? l;
expect(
errorText(
() => l = openLocator(
profileOf(c[3]! as String),
round,
openRelease(v, round, c[2]),
b,
),
),
textOf(v, c[5]),
reason: '$c',
);
if (c[6] != null) {
expect(summaryOf(l!), expandSummary(c[6]), reason: '$c');
}
}
});
test('openEnvelope checks the rest and the .dkc as OpenEnvelope', () {
final rest = fromHex(envelope['rest']! as String);
final header = fromHex(envelope['header']! as String);
for (final c in rows(v, 'envelopes')) {
final l = base(
key: c[1] == '' ? null : fromHex(c[1]! as String),
header: applyLocatorEdits(header, c[2]),
restDigest: c[3] == '' ? null : fromHex(c[3]! as String),
restSize: c[4]! as int,
capsuleDigest: c[5] == '' ? null : fromHex(c[5]! as String),
);
Uint8List? dkc;
final r = applyLocatorEdits(rest, c[6]);
expect(
errorText(() => dkc = l.openEnvelope(r)),
textOf(v, c[7]),
reason: '${c[0]}',
);
if (dkc != null) {
expect(sha256Hex(dkc!), c[8]);
expect(toHex(dkc!), envelope['dkc']);
}
}
});
test('restIn reads rest_size bytes from the offset, as RestIn', () {
final resources = [fromHex(v['rest_in_resource']! as String), Uint8List(0)];
for (final c in rows(v, 'rest_in')) {
final l = base(restSize: c[2]! as int);
Uint8List? r;
expect(
errorText(() => r = l.restIn(resources[c[0]! as int], c[1]! as int)),
textOf(v, c[3]),
reason: '$c',
);
if (r != null) expect(sha256Hex(r!), c[4]);
}
});
test('hideRest appends the rest, as Hide', () {
for (final c in rows(v, 'hide')) {
final (:file, :offset) = hideRest(
fromHex(c[0]! as String),
fromHex(c[1]! as String),
);
expect([toHex(file), offset], [c[2], c[3]]);
}
});
test('splitEnvelope splits an age file where NewEnvelope does', () {
final key = fromHex(envelope['key']! as String);
final dkc = fromHex(envelope['dkc']! as String);
for (final c in rows(v, 'split')) {
final file = fromHex(c[0]! as String);
({Locator locator, Uint8List rest})? s;
expect(
errorText(() => s = splitEnvelope(file, key, dkc)),
textOf(v, c[1]),
reason: '$c',
);
if (s != null) {
final end = c[2]! as int;
expect(toHex(s!.locator.envelopeHeader), toHex(file.sublist(0, end)));
expect(toHex(s!.rest), toHex(file.sublist(end)));
expect(s!.locator.addresses, isEmpty);
}
}
// The envelope of NewEnvelope: its locator is the one of the vectors.
final rows0 = rows(v, 'split').first;
final s = splitEnvelope(fromHex(rows0[0]! as String), key, dkc);
expect(toHex(s.locator.envelopeHeader), envelope['header']);
expect(toHex(s.rest), envelope['rest']);
expect(toHex(s.locator.restDigest), envelope['rest_digest']);
expect(s.locator.restSize, envelope['rest_size']);
expect(toHex(s.locator.capsuleDigest), envelope['capsule_digest']);
expect(s.locator.openEnvelope(s.rest), dkc);
});
}
void main() {
final v = decodeJson(locatorVectorsJson);
group('the part of locator_vectors.json', () => locatorCases(v));
test('a Locator has the types of Go: keys and digests of 32 bytes and no '
'negative size or offset', () {
final k = Uint8List(32);
Locator make({int key = 32, int size = 0}) => Locator(
addresses: const [],
envelopeKey: Uint8List(key),
envelopeHeader: [1],
restDigest: k,
restSize: size,
capsuleDigest: k,
);
make();
expect(() => make(key: 31), throwsArgumentError);
expect(() => make(size: -1), throwsArgumentError);
expect(() => LocatorAddress('https://a.org/', -1), throwsArgumentError);
expect(() => make().restIn([1, 2], -1), throwsArgumentError);
// An envelope without addresses yet does not marshal.
expect(
errorText(() => make().marshal()),
'locator: 0 addresses, not 1 to 8',
);
final l = make().withAddresses([LocatorAddress('https://a.org/', 5)]);
expect(l.addresses.single.offset, 5);
l.wipe();
expect(l.envelopeKey, Uint8List(32));
});
}

Powered by TurnKey Linux.