// The recipes of the interoperability of stage 7b, Dart to Go: sealed // locators with their envelopes, author key files and Ed25519 signatures, // which this library writes with SeededRandomSource. tool/ // seal_interop_dart_samples.dart writes their files, tool/ // seal_interop_go_verdicts.go opens them with Go and writes test/vectors/ // seal_interop.json, and seal_interop_test.dart writes them again and // compares them with what Go read. It reads no file. import 'dart:typed_data'; import 'package:datekeys/src/authorkey.dart'; import 'package:datekeys/src/bytes.dart'; import 'package:datekeys/src/locator.dart'; import 'package:datekeys/src/locator_seal.dart'; import 'package:datekeys/src/profile.dart'; import 'package:datekeys/src/sha256.dart'; import 'age_support.dart' show pattern; import 'random_support.dart'; export 'random_support.dart'; /// The recipes, in their order. List interopRecipes() => [ for (final (round, n, addresses) in [ (1000, 0, 1), (1001, 100, 2), (1004, 70000, 8), (2000, 5000, 3), ]) { 'kind': 'locator', 'name': 'a locator of round $round, a .dkc of $n bytes', 'seed': 'interop locator $round', 'round': round, 'dkc_length': n, 'addresses': [ for (var i = 0; i < addresses; i++) {'uri': 'https://example.com/$round/$i', 'offset': i * 1000}, ], }, for (final (i, pass) in [(0, ''), (1, 'una contraseƱa larga'), (2, 'p')]) { 'kind': 'key', 'name': pass.isEmpty ? 'a plain key file' : 'a key file for "$pass"', 'seed': 'interop key $i', 'passphrase': pass, }, for (final n in [0, 1, 32, 64, 99, 100, 1000, 70000]) { 'kind': 'signature', 'name': 'a signature of $n bytes', 'seed': 'interop signature $n', 'message_length': n, }, ]; /// The files of a recipe, by the suffix of their name, and what Go must /// find in them. Map interopFiles(Json r) { final random = seeded(r['seed']! as String); switch (r['kind']) { case 'locator': final dkc = pattern(r['dkc_length']! as int); final e = newEnvelope(dkc, random: random); final loc = e.locator.withAddresses([ for (final a in listOf(r['addresses'])) LocatorAddress(a['uri']! as String, a['offset']! as int), ]); final sealed = sealLocator( quicknet(), r['round']! as int, loc, random: random, ); return { 'sealed': sealed, 'rest': e.rest, 'plaintext': loc.marshal(), 'dkc': dkc, }; case 'key': final key = AuthorKey.generate(random); final pass = r['passphrase']! as String; return { 'file': pass.isEmpty ? marshalAuthorKey(key) : encryptAuthorKey(key, pass, random: random), 'secret': utf8Bytes(key.secret), 'public': utf8Bytes(key.publicString), }; default: final key = AuthorKey.generate(random); final msg = pattern(r['message_length']! as int); return { 'signature': key.sign(msg), 'public_key': key.publicKey, 'seed': utf8Bytes(key.secret), }; } } /// The SHA-256 of each file, as the vectors write them. Json interopDigests(Map files) => { for (final e in files.entries) e.key: toHex(sha256(e.value)), };