Stage 4c: the public note and the head of format 3
lib/src/note.dart ports CheckNote, NewNote and Note of package extension
of datekeys-go at c531e93: checkNote and checkNoteData check the length,
the UTF-8 and the rules of text of the declared author, in that order and
with the texts of Go, on the bytes of the note as Go reads its string;
newNote, publicNote and unusableNote, and Header.publicNote and
Header.unusableNote. StandardExtensions checks the data of a note as the
Standard of Go does, and its parameter validateNote, which could replace
those rules, is gone.
lib/src/head.dart ports DecodeHead, EncodeHead and CheckHeadEnd of
format3.go: the limit of HEAD_LEN, the type tag and the version, the CDDL
with R1 and R8 on the UTF-8 bytes of the paths, never on the UTF-16 code
units of a String, and then the comment, the declared author and the files
with the rules of pathrule and their layout, by subtraction, and R7 and R9,
as ERR_HEAD_INVALID with the text of Go, and the critical extensions of the
head. decodeWrittenHead is the decoder without the critical extensions, for
the self-check of the writer of stage 6.
The tests read open_heads.json, open_notes.json, head_schema.json with the
detail of each ERR_HEAD_INVALID, the trees of paths.json as heads of files
of 0 bytes, and the head of each fixture of format 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
// The head of format 3 against Go: test/vectors/open_heads.json, which
|
|
|
|
|
// tool/open_go_vectors.go writes with capsule.DecodeHead and EncodeHead of
|
|
|
|
|
// the reference; testdata/vectors/head_schema.json, with the detail of each
|
|
|
|
|
// ERR_HEAD_INVALID; and the trees of testdata/vectors/paths.json, each a
|
|
|
|
|
// head of files of 0 bytes, through the decoder of the head, from layer 3
|
|
|
|
|
// to layer 4.
|
|
|
|
|
@TestOn('vm')
|
|
|
|
|
library;
|
|
|
|
|
|
|
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'dart:io';
|
|
|
|
|
import 'dart:typed_data';
|
|
|
|
|
|
|
|
|
|
import 'package:datekeys/datekeys.dart';
|
|
|
|
|
import 'package:datekeys/src/sha256.dart' show sha256;
|
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
|
|
|
|
|
|
import 'open_vectors_support.dart';
|
|
|
|
|
|
|
|
|
|
Json readJson(String path) => jsonDecode(File(path).readAsStringSync()) as Json;
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
final heads = readJson('test/vectors/open_heads.json');
|
|
|
|
|
|
|
|
|
|
test('open_heads.json: every head decodes as in Go', () {
|
|
|
|
|
expect(heads['spec'], specVersion);
|
|
|
|
|
final cases = (heads['decode']! as List).cast<Json>();
|
|
|
|
|
expect(cases, hasLength(greaterThan(300)));
|
|
|
|
|
checkDecodeCases(cases);
|
|
|
|
|
// Every kind of result, and both orders of U+FF5E and U+1F600.
|
|
|
|
|
expect(
|
|
|
|
|
{for (final c in cases) c['result']},
|
|
|
|
|
{
|
|
|
|
|
'ok',
|
|
|
|
|
'ERR_NON_CANONICAL_CBOR',
|
|
|
|
|
'ERR_UNSUPPORTED_VERSION',
|
|
|
|
|
'ERR_HEAD_INVALID',
|
|
|
|
|
'ERR_EXTENSION_CRITICAL_UNKNOWN',
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('open_heads.json: every head encodes as in Go', () {
|
|
|
|
|
final cases = (heads['encode']! as List).cast<Json>();
|
|
|
|
|
expect(cases, hasLength(greaterThan(20)));
|
|
|
|
|
checkEncodeCases(cases);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('head_schema.json: the result and the detail of each head', () {
|
|
|
|
|
final f = readJson('testdata/vectors/head_schema.json');
|
|
|
|
|
expect(f['spec'], specVersion);
|
|
|
|
|
final cases = (f['heads']! as List).cast<Json>();
|
|
|
|
|
expect(cases, hasLength(63));
|
|
|
|
|
for (final c in cases) {
|
|
|
|
|
final b = fromHex(c['hex']! as String);
|
|
|
|
|
var result = 'ok';
|
|
|
|
|
var detail = '';
|
|
|
|
|
try {
|
|
|
|
|
decodeHead(b);
|
|
|
|
|
} on DateKeysException catch (e) {
|
|
|
|
|
result = e.code.code;
|
|
|
|
|
if (e.code == ErrorCode.headInvalid) {
|
|
|
|
|
detail = headDetail(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
expect(result, c['result'], reason: c['name'] as String?);
|
|
|
|
|
expect(detail, c['detail'] ?? '', reason: c['name'] as String?);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('paths.json: the trees, as heads of files of 0 bytes', () {
|
|
|
|
|
final f = readJson('testdata/vectors/paths.json');
|
|
|
|
|
final trees = (f['trees']! as List).cast<Json>();
|
|
|
|
|
expect(trees, hasLength(greaterThan(10)));
|
|
|
|
|
final empty = sha256(Uint8List(0));
|
|
|
|
|
for (final c in trees) {
|
|
|
|
|
// TreeHead of the reference: a zero salt and a file of 0 bytes for
|
|
|
|
|
// each path, in the order given.
|
|
|
|
|
final b = encodeHead(
|
|
|
|
|
Head(
|
|
|
|
|
salt: Uint8List(saltSize),
|
|
|
|
|
files: [
|
|
|
|
|
for (final p in (c['paths']! as List).cast<String>())
|
|
|
|
|
HeadFile(path: p, size: 0, start: 0, end: 0, sha256: empty),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
var result = 'ok';
|
|
|
|
|
var detail = '';
|
|
|
|
|
try {
|
|
|
|
|
decodeHead(b);
|
|
|
|
|
} on DateKeysException catch (e) {
|
|
|
|
|
result = e.code.code;
|
|
|
|
|
if (e.code == ErrorCode.headInvalid) detail = headDetail(e);
|
|
|
|
|
}
|
|
|
|
|
expect(result, c['result'], reason: c['name'] as String?);
|
|
|
|
|
expect(detail, c['detail'] ?? '', reason: c['name'] as String?);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('the files of every format 3 fixture, from its record', () {
|
|
|
|
|
final names = Directory('testdata/fixtures')
|
|
|
|
|
.listSync()
|
|
|
|
|
.map((f) => f.uri.pathSegments.last)
|
|
|
|
|
.where((n) => n.startsWith('format3_') && n.endsWith('.json'))
|
|
|
|
|
.where((n) => !n.endsWith('.inspect.json') && !n.contains('.dkk'))
|
|
|
|
|
.toList();
|
Test data of the branch v0.12 of datekeys-go at c531e93
testdata/ is synced with datekeys-go at c531e93, the head of the branch
v0.12, whose testdata is that of 601e6d2, the copy of datekeys-ts. Every
file keeps "spec": "0.11": the draft v0.12 is not approved yet. From the
tag spec-v0.11 it brings the fixtures format3_unsigned and format3_note,
format3_seal_unsupported with seal_type 4294967295, the records of
format3_sealed and format3_signed_cms, the mutation corpus of 218 cases,
note.json, security.json with a context and lines, security_cms.json of
135 cases and locator.json.
The vectors that the generators of tool/ make from the testdata are
written again by Go at c531e93: mutation_texts.json, open_cases.json,
formats_*.json with formats_vectors.g.dart, ibe_vectors.json and
age_fixtures.json; release_vectors.json, primitives.json and the views
of open_vectors.g.dart come out the same. age.json does not read the
testdata, and stays frozen: age draws its keys from crypto/rand. The
tests count 26 fixtures and 218 cases, and the inspection of
format3_note gives the note of its record. No difference with Go.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
expect(names, hasLength(14));
|
Stage 4c: the public note and the head of format 3
lib/src/note.dart ports CheckNote, NewNote and Note of package extension
of datekeys-go at c531e93: checkNote and checkNoteData check the length,
the UTF-8 and the rules of text of the declared author, in that order and
with the texts of Go, on the bytes of the note as Go reads its string;
newNote, publicNote and unusableNote, and Header.publicNote and
Header.unusableNote. StandardExtensions checks the data of a note as the
Standard of Go does, and its parameter validateNote, which could replace
those rules, is gone.
lib/src/head.dart ports DecodeHead, EncodeHead and CheckHeadEnd of
format3.go: the limit of HEAD_LEN, the type tag and the version, the CDDL
with R1 and R8 on the UTF-8 bytes of the paths, never on the UTF-16 code
units of a String, and then the comment, the declared author and the files
with the rules of pathrule and their layout, by subtraction, and R7 and R9,
as ERR_HEAD_INVALID with the text of Go, and the critical extensions of the
head. decodeWrittenHead is the decoder without the critical extensions, for
the self-check of the writer of stage 6.
The tests read open_heads.json, open_notes.json, head_schema.json with the
detail of each ERR_HEAD_INVALID, the trees of paths.json as heads of files
of 0 bytes, and the head of each fixture of format 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
for (final n in names) {
|
|
|
|
|
final r = readJson('testdata/fixtures/$n');
|
|
|
|
|
final hb = fromHex(r['head_cbor']! as String);
|
|
|
|
|
final h = decodeHead(hb);
|
|
|
|
|
expect(toHex(h.salt), r['salt'], reason: n);
|
|
|
|
|
expect(
|
|
|
|
|
[h.comment, h.author],
|
|
|
|
|
[r['comment'] ?? '', r['declared_author'] ?? ''],
|
|
|
|
|
reason: n,
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
[
|
|
|
|
|
for (final f in h.files)
|
|
|
|
|
{
|
|
|
|
|
'path': f.path,
|
|
|
|
|
'size': f.size,
|
|
|
|
|
'start': f.start,
|
|
|
|
|
'end': f.end,
|
|
|
|
|
'sha256': toHex(f.sha256),
|
|
|
|
|
if (f.mtime != null) 'mtime': f.mtime,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
r['files'] ?? <Object?>[],
|
|
|
|
|
reason: n,
|
|
|
|
|
);
|
|
|
|
|
expect(encodeHead(h), hb, reason: n);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|