Stage 1: the CBOR profile of section 58, as package codec of Go
lib/src/cbor.dart is the port of codec/codec.go: CborEncoder,
CborDecoder, unmarshalCbor, peekSchema, checkSchema and walkCbor, with
the same reads, the same checks in the same order and the same error
texts, such as "codec: offset 0: 23 is not in its shortest form
(initial byte 0x18): ERR_NON_CANONICAL_CBOR".
Integers are exact on the VM and on the web, where an int is a double
and the bit operators work on 32 bits. An argument of eight bytes is
read as two halves of 32 bits, and is an int up to 2^53-1 and a BigInt
above, map keys and the numbers of the error texts included. uint
returns an int, since every schema bounds its integers at 2^53-1, and
uint64 a BigInt. The map that peekSchema reads is bounded at 2^63-1,
Go's math.MaxInt, on the web too.
Two kinds of text are of Dart only. CborEncoder.uint refuses an int
outside 0..2^53-1 and uint64 a BigInt outside 0..2^64-1, with the text
of datekeys-ts, where Go's uint64 cannot hold such a value. And the only
invalid text that a Dart String holds is a lone surrogate: the error
quotes it as Go quotes its bytes in generalized UTF-8.
The tests port codec_test.go, internal_test.go and vectors_test.go,
with the texts that Go prints, and cbor.test.ts. The fuzz targets are
properties over seeded inputs, checked against a reference encoder and
decoder written apart, as internal/cbortest. cbor.json runs its 36
accept and 67 reject vectors with the walk limits and the values; its
172 schema vectors are read and wait for the schema decoders of stage 4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 days ago
|
|
|
// The shared vectors of the CBOR profile of spec §58,
|
|
|
|
|
// testdata/vectors/cbor.json, as vectors_test.go of package codec of
|
|
|
|
|
// datekeys-go: walkCbor accepts exactly the accept list, with the value
|
|
|
|
|
// recorded for unsigned integers, and rejects the reject list with the
|
|
|
|
|
// recorded code.
|
|
|
|
|
@TestOn('vm')
|
|
|
|
|
library;
|
|
|
|
|
|
|
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
|
|
import 'package:datekeys/datekeys.dart';
|
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
final file = jsonDecode(
|
|
|
|
|
File('testdata/vectors/cbor.json').readAsStringSync(),
|
|
|
|
|
) as Map<String, Object?>;
|
|
|
|
|
final walk = file['walk']! as Map<String, Object?>;
|
|
|
|
|
final maxDepth = walk['max_depth']! as int;
|
|
|
|
|
final maxLen = walk['max_len']! as int;
|
|
|
|
|
List<Map<String, Object?>> list(String key) =>
|
|
|
|
|
(file[key]! as List).cast<Map<String, Object?>>();
|
|
|
|
|
final accept = list('accept');
|
|
|
|
|
final reject = list('reject');
|
|
|
|
|
final schemas = list('schemas');
|
|
|
|
|
|
|
|
|
|
test('the vector file is complete', () {
|
|
|
|
|
expect(file['spec'], specVersion);
|
|
|
|
|
expect(accept, isNotEmpty);
|
|
|
|
|
expect(reject, isNotEmpty);
|
|
|
|
|
expect(maxDepth, isPositive);
|
|
|
|
|
expect(maxLen, isPositive);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
group('accept', () {
|
|
|
|
|
for (final v in accept) {
|
|
|
|
|
test(v['name'], () {
|
|
|
|
|
final input = fromHex(v['hex']! as String);
|
|
|
|
|
walkCbor(input, maxDepth, maxLen);
|
|
|
|
|
// An unsigned integer records its value: a JSON number up to 2^53-1
|
|
|
|
|
// and a decimal string above, so that no reader loses precision.
|
|
|
|
|
final isUint = input.isNotEmpty && input[0] >> 5 == 0;
|
|
|
|
|
expect(v.containsKey('value'), isUint);
|
|
|
|
|
if (!isUint) return;
|
|
|
|
|
final n = CborDecoder(input).uint64();
|
|
|
|
|
if (n > BigInt.from(maxSafeUint)) {
|
|
|
|
|
expect(v['value'], '$n');
|
|
|
|
|
} else {
|
|
|
|
|
expect(v['value'], n.toInt());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
group('reject', () {
|
|
|
|
|
for (final v in reject) {
|
|
|
|
|
test(v['name'], () {
|
|
|
|
|
final input = fromHex(v['hex']! as String);
|
|
|
|
|
var code = '';
|
|
|
|
|
try {
|
|
|
|
|
walkCbor(input, maxDepth, maxLen);
|
|
|
|
|
} on DateKeysException catch (e) {
|
|
|
|
|
code = e.code.code;
|
|
|
|
|
}
|
|
|
|
|
expect(code, v['error']);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
Stage 4b in the README, the changelog and the exports
lib/datekeys.dart exports the formats, as index.ts of datekeys-ts: the
frames, PUBLIC_HEADER, CONTROL_CBOR, the .dkk, the extensions and their
registries, the Provider Profile, the DateKey with its rounds and times,
and the padding; the frame of BODY, the digest and the helpers of the
schemas stay internal for stage 4c. The README describes the modules, the
enums and the errors without a code, the Standard registry and the checks
it is given, the one difference of the formats between Go at c531e93 and
the tag spec-v0.11, and the generator of the vectors; the changelog the
stage, its tests and the faults injected. cbor_vectors_test.dart drops the
test it kept skipped until the decoders of the schemas, which
formats_vectors_test.dart now runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 days ago
|
|
|
// The schemas block is decoded with the decoder of each schema, Provider
|
|
|
|
|
// Profile, PUBLIC_HEADER, CONTROL_CBOR and the body of a .dkk, in
|
|
|
|
|
// formats_vectors_test.dart. Here its shape is checked, and the objects it
|
|
|
|
|
// holds as valid are items of the profile whose type tag peekSchema reads.
|
Stage 1: the CBOR profile of section 58, as package codec of Go
lib/src/cbor.dart is the port of codec/codec.go: CborEncoder,
CborDecoder, unmarshalCbor, peekSchema, checkSchema and walkCbor, with
the same reads, the same checks in the same order and the same error
texts, such as "codec: offset 0: 23 is not in its shortest form
(initial byte 0x18): ERR_NON_CANONICAL_CBOR".
Integers are exact on the VM and on the web, where an int is a double
and the bit operators work on 32 bits. An argument of eight bytes is
read as two halves of 32 bits, and is an int up to 2^53-1 and a BigInt
above, map keys and the numbers of the error texts included. uint
returns an int, since every schema bounds its integers at 2^53-1, and
uint64 a BigInt. The map that peekSchema reads is bounded at 2^63-1,
Go's math.MaxInt, on the web too.
Two kinds of text are of Dart only. CborEncoder.uint refuses an int
outside 0..2^53-1 and uint64 a BigInt outside 0..2^64-1, with the text
of datekeys-ts, where Go's uint64 cannot hold such a value. And the only
invalid text that a Dart String holds is a lone surrogate: the error
quotes it as Go quotes its bytes in generalized UTF-8.
The tests port codec_test.go, internal_test.go and vectors_test.go,
with the texts that Go prints, and cbor.test.ts. The fuzz targets are
properties over seeded inputs, checked against a reference encoder and
decoder written apart, as internal/cbortest. cbor.json runs its 36
accept and 67 reject vectors with the walk limits and the values; its
172 schema vectors are read and wait for the schema decoders of stage 4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 days ago
|
|
|
group('schemas', () {
|
|
|
|
|
const typeTags = {
|
|
|
|
|
'provider_profile': 'datekeys-provider-profile',
|
|
|
|
|
'public_header': 'datekeycap',
|
|
|
|
|
'control_cbor': 'datekeys-control',
|
|
|
|
|
'dkk_body': 'datekeys-access-key',
|
|
|
|
|
};
|
|
|
|
|
final codes = {for (final c in ErrorCode.values) c.code};
|
|
|
|
|
|
|
|
|
|
test('the block is well formed', () {
|
|
|
|
|
expect(schemas, isNotEmpty);
|
|
|
|
|
for (final v in schemas) {
|
|
|
|
|
expect(typeTags.keys, contains(v['schema']), reason: '${v['name']}');
|
|
|
|
|
expect(v['block'], isA<String>());
|
|
|
|
|
expect(v['name'], isA<String>());
|
|
|
|
|
expect(fromHex(v['hex']! as String), isNotEmpty);
|
|
|
|
|
expect(
|
|
|
|
|
v['result'] == 'ok' || codes.contains(v['result']),
|
|
|
|
|
isTrue,
|
|
|
|
|
reason: '${v['name']}: ${v['result']}',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('the valid objects are items of the profile with their type tag', () {
|
|
|
|
|
final valid = schemas.where((v) => v['result'] == 'ok').toList();
|
|
|
|
|
expect(valid, isNotEmpty);
|
|
|
|
|
for (final v in valid) {
|
|
|
|
|
final input = fromHex(v['hex']! as String);
|
|
|
|
|
walkCbor(input, 16, input.length);
|
|
|
|
|
expect(
|
|
|
|
|
peekSchema(input).typeTag,
|
|
|
|
|
typeTags[v['schema']],
|
|
|
|
|
reason: '${v['name']}',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|