The author decided on 7 October 2026 that the web is made with TypeScript
and Svelte. That the library gives the same on JavaScript is no longer a
requirement; the gate still runs the tests on Node while it does not get
in the way. The possible dart2js fault is not reported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author approved specification v0.16 on 7 October 2026, tagged
spec-v0.16 at b6ff17a. Only the annex changes, with the SHA-256 of the
approved text, and so does its generated constant, and the README of
testdata; the README and the CHANGELOG name the approved version.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README describes the draft v0.16 as ported (the reason of a seal that
does not prove the opening date, EncryptResult.security, the strict reading
of drand's JSON, the two fixtures), the sync at 3fd0e93, the regenerated
vectors, and the license of the specification, CC-BY-ND-4.0 since 70d907b
of datekeys-go, which the annex carries too. The CHANGELOG has its section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
specVersion is 0.16, and testdata, wordlists and annex are synced with
datekeys-go at 3fd0e93 (branch v0.16, the draft v0.16): 150 files, with
the two fixtures of v0.16, security_cms.json made again (143 cases, with
seal_reason), 26 cases of drand's JSON in release.json, the annex vector in
wordkey.json, and the annex of the draft. The code follows 4f78854; 3fd0e93
only restores the escapes of the JSON vectors.
A valid seal is S4 only when its token carries accuracy and t plus the
accuracy is before round_time; otherwise S5, with the first reason that
holds: late, no accuracy under the BTSP policy of ETSI EN 319 421, or no
accuracy (spec v0.16, 29.7, 29.11), as 7e3b810 of Go. cms.Token gains
hasAccuracy, policy and btsp; SealReason, Detail.sealReason and
SignerLine.reason carry the reason, and the lines of S5 and of a signer of
F6 say it with the texts of Go. A CmsEvaluator that gives S5 without a
reason or S4 with one is out of range: S2. EncryptResult.security gives the
verdicts of the area the writer wrote, as Result.Security, so that the app
warns of a seal that proves nothing before the opening date (rule 19).
drand's JSON is read strictly, as b570338 of Go: parseDrandJson, exported,
reads RFC 8259 JSON in valid UTF-8 whose value is an object, with no name
repeated in any object, names compared exactly after their escapes are
decoded, no lone surrogate escape, a round without sign, fraction or
exponent from 1 to 2^53 - 1 (compared as text at 16 digits, exact on the
web), and string signature and randomness. The error texts are those of
Go. A round above 2^53 - 1 is no longer a difference with Go.
Tests: the new fixtures (format3_time_and_key_words opens with the identity
of its words_text, as TestFixtureWords; format3_full_chunk is one full
STREAM chunk), the annex vector of wordkey.json, seal_reason in every case
of security_cms.json, the reasons and their lines, the token fields, and
TestStrictJSON and TestJSONRound of Go, also compiled to JavaScript.
Vectors regenerated by the Go programs of tool/ in an export of datekeys-go
at 4f78854: security and securitycms (reasons, BTSP tokens, an accuracy of
0 seconds and an empty one; the part for Node.js has each reason), the CMS
files (has_accuracy, policy and btsp of each token, oidBTSP, the new
corpus), the capsule writer (sealed with an accuracy of a second, two
recipes without accuracy, Result.Security), and the formats, open,
mutation, IBE and age fixture vectors, for the new fixtures and the spec
field.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What the official SDK says when it seals, from datekeys-go aefc8f6, without
the CLI. lib/src/annex.dart ports annex.go: recoveryAnnex, the text of
datekeys.RecoveryAnnex that the official SDK saves next to each .dkc (spec
§62.1, rule 27), §79 of the specification under a title with its version and
SHA-256; and recoveryAnnexSuffix, ".recuperacion.txt". Dart has no go:embed,
so the text is the constant of lib/src/recovery_annex.g.dart, which
tool/recovery_annex_copy.dart writes from the vendored annex/recovery.md once
it matches its SOURCE.json: a multi-line string that escapes the backslash,
the dollar sign, the quote and, as Unicode escapes, every rune but the line
feed that Go's strconv.IsPrint rejects, with the SHA-256 of its bytes,
recoveryAnnexSha256, internal, for the tests compiled to JavaScript.
lib/src/profile.dart ports status.go: ProfileStatus, active, readOnly and
compromised, with the names of Go's Status.String, and profileStatusOf, the
state of the profile of a profile_hash and whether this release knows it,
from a table where Quicknet is active (spec §71). An unknown profile is
active, Go's zero Status, and not known. lib/datekeys.dart exports the four.
test/annex_test.dart, also on Node.js, checks what TestRecoveryAnnex checks
of the text and the SHA-256 of its bytes; test/annex_vm_test.dart, that the
constant is annex/recovery.md byte for byte and that the file is §79 of the
specification at the commit of annex/SOURCE.json under the title and the
paragraph of TestRecoveryAnnex; formats_objects_test.dart runs the cases of
TestStatus. README and CHANGELOG for them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/sync_testdata.dart also copies every blob under annex/ of the same
commit of Go into annex/, with a SOURCE.json in the format of
testdata/SOURCE.json, after reading every blob of the three trees; check
verifies it with the same rules, also against the repository, and prints a
third line, annex: N files match ... test/testdata_test.dart checks the annex
tree and its line too, and tool/check.sh names it.
Synced at aefc8f6, the branch v0.15 after the tag spec-v0.15, which adds
annex/recovery.md, the text of datekeys.RecoveryAnnex: §79 of the
specification under a title with its version and SHA-256, which the official
SDK saves next to each .dkc (spec §62.1, rule 27). The files of testdata and
wordlists do not change, only the commit of their SOURCE.json.
.gitattributes keeps the exact bytes of annex/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The port of dice.go of datekeys-go at 92e7154, for whoever does not trust
the random numbers of a computer: five dice for each word give a number
from 11111 to 66666, the position of the word in a list of 7776, the first
die the most significant. diceNumber gives the dice of a position,
diceWord the word of five dice, diceWords the words of several numbers
separated by white space, at least 6 and never the same word twice, and
diceList the list numbered for dice, as the EFF publishes its own: for
en, its UTF-8 bytes are the file of the EFF. The same checks in the same
order and Go's texts, in a WordKeyException. The numbers are split as
Go's strings.Fields splits a string, at unicode.IsSpace (goIsSpace), the
white space at which normalizeWords splits, and nothing else changes.
lib/datekeys.dart exports them; diceWordUtf8 and diceWordsUtf8, on the
bytes of a Go string, are internal, for the tests.
testdata and wordlists at datekeys-go 92e7154: only wordlists/README.md
changes, with the SHA-256 of each list numbered for dice, and the commit
of both SOURCE.json. tool/wordlist_go_vectors.go writes the dice too, run
at 92e7154: DiceNumber, DiceWord and DiceWords on the lists of Go and on
lists of indices, DiceList of each list, and DiceWords with every code
point of planes 0, 1 and 14 between two numbers. The rest of the vectors
is the same but for their source and description. The tests port
TestDiceNumber, TestDiceWord, TestDiceWords and TestDiceList, and run the
vectors on the VM and on Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
testdata and wordlists at datekeys-go e671032, with the same testdata:
wordlists/en.txt is the large wordlist of the EFF, 7776 words, CC BY 4.0,
in its order and without the dice numbers. wordListSha256 pins it, and
the alphabet of en is a to z and the hyphen of its four compound words,
as Go has them. The vectors are generated again by Go at e671032: only
their source and the list of lists change. The tests read the English
list too, and check the hyphen and the accent in each alphabet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/datekeys.dart exports readWordList, checkWordList, generateWords,
wordBits, wordListSha256, wordListLanguages, defaultWordCount and
minListSize, as the public package wordkey of Go; checkWordListUtf8,
parseWordList and checkWordRune stay internal. A test checks the exports.
The README has a section on the random words of a key of words, with the
module table, the alphabet, the draws, the bits of Go, what differs from Go
and what is exported; the list of what is done names them. testdata is that
of datekeys-go at 27a75ee, the branch v0.15 after the tag spec-v0.15, whose
testdata is that of the tag, plus wordkey/lists in wordlists/, which the
sync, the check, the gate and the license describe: the Spanish list is CC
BY-SA 4.0, an adaptation of FrequencyWords by Hermit Dave, unlike the code.
The vectors and their generator are listed. The CHANGELOG opens the entry
of the word lists in the section of the specification 0.15.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/sync_testdata.dart also copies every blob under wordkey/lists of the
same commit of Go into wordlists/, with a SOURCE.json in the format of
testdata/SOURCE.json, after reading every blob of both trees; a tree missing
at the commit is an error. check verifies both trees with the same rules,
also against the repository, and prints one line per tree, testdata first;
test/testdata_test.dart checks the wordlists tree and its line too.
Synced at 27a75ee, the branch v0.15 after the tag spec-v0.15: the 142 files
of testdata are those of the tag, byte for byte, so that only the commit of
testdata/SOURCE.json changes; wordlists/ holds README.md and es.txt, the
Spanish list, whose SHA-256 is the one that wordlist.dart pins. The list is
CC BY-SA 4.0, an adaptation of FrequencyWords by Hermit Dave, unlike the
code; its README, copied from Go, records its source, method and license.
.gitattributes keeps the exact bytes of wordlists/.
wordlist_vm_test.dart reads the Spanish list with readWordList, as
TestBuiltInLists of Go, draws from it the words of Go, and runs TestGenerate
and TestGenerateUniform with the CSPRNG of the platform.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A port of List, CheckList, Generate and Bits of package wordkey of
datekeys-go at 27a75ee (generate.go, spec 38.1), with the same checks in the
same order and the same texts, each error a WordKeyException:
- readWordList takes the file of a list that the app downloads or bundles
only with the SHA-256 that wordListSha256 pins, "wordkey: the list "es"
has the SHA-256 ..., not ...", and then reads it as List reads its text;
- checkWordList refuses fewer than 2048 words, a word that is not one word
of 3 letters or more once normalized, a rune that checkWords refuses, a
character outside the alphabet of the language, which only the code
gives (es: a to z, the five vowels with an acute accent, u with diaeresis
and n with tilde, lower case, NFC), and two words that are one once
normalized;
- generateWords draws each index with randomIndex, as crypto/rand.Int, and
draws again an index already drawn, so that the same bytes draw the same
words as Go; what the RandomSource throws goes through;
- wordBits sums Go's math.Log2, ported with its Frexp and its Log, so that
the double is Go's, on the VM and on the web.
wordkey.dart shares its check of each rune, checkWordRune, with the same
behaviour. lib/datekeys.dart does not export the new file yet.
test/vectors/wordlist_vectors.json, from tool/wordlist_go_vectors.go in an
export of datekeys-go at 27a75ee, holds what Go gives: List on 19 texts,
CheckList on 83 lists and on every code point of planes 0, 1 and 14,
Generate in 51 cases, on the Spanish list and on lists of 12 to 2^20 + 1
words, from seeded, counter and finite streams, and Bits bit for bit.
wordlist_test.dart runs them, also compiled to JavaScript, with the cases of
TestCheckList, TestGenerate and TestBits of Go; wordlist_vm_test.dart checks
the Dart copy of the vectors and every code point.
The seed of TestGenerate of Go draws two indices only, 1793 and 2081, so
that Generate runs out of bytes and the test compares two errors; the
vectors record it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README describes the release object, drand's JSON as an input, the
release in hand with OpenOptions.release and Opened.clockBehind, the local
release archive and what differs from Go; the specification is 0.15 and
testdata is that of datekeys-go at 3c3e737, with release.json and
releases/. The CHANGELOG opens the section of the specification 0.15.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
specVersion is 0.15 and testdata is synced with datekeys-go at 3c3e737
(branch v0.15): every file changes its spec field, mutations.json gains the
field source and four cases, and vectors/release.json and releases/ are new
(142 files).
release.dart, as provider of Go at 2eeca40, with its texts: encodeRelease
and decodeRelease of the release object (spec v0.15, 47.1) with the CBOR
profile of the library, its size first, then its type and version, then its
schema; parseRelease, which also reads drand's JSON with the rules of
encoding/json of Go for its three fields; verifyRelease checks the chain hash
a release names, ERR_PROFILE_MISMATCH, before its round and its signature;
ReleaseSupplier, EncodedRelease and supplyRelease, a release in the caller's
hand; and ReleaseArchive, the lookup of a local release archive (50), whose
failures are ERR_RELEASE_UNAVAILABLE at step 9.
open: OpenOptions.release takes a release in hand, exclusive with source. It
is not compared with the clock (step 9.c); Opened.clockBehind reports a clock
behind it, and the step 9 detail is Go's. Step 10 starts with the layers of
the object. A network source keeps its behaviour. Opened.release carries the
chain hash of the pinned profile.
Vectors: mutation_texts.json is regenerated with tool/mutation_go_texts.go,
which now replays each case with its source as testkit does: 149 cases
change only the detail of step 9 ("release supplied by the caller"), the
case "round not reached yet" now opens, and the four new cases are added.
release_vectors.json, the open vectors, and the formats, locator, security
and seal vectors come out the same at 3c3e737 but for their spec field.
tool/release_archive_go_texts.go writes the texts of provider.Archive on
edited archives; tool/release_copy.dart copies release.json, releases/ and
those texts to release.g.dart for the tests compiled to JavaScript.
errors_spec_test reads the spec at the commit of testdata/SOURCE.json rather
than at its tag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author approved the draft v0.14 with the recommendation of each of its
ten decisions; datekeys-go closes it with the tag spec-v0.14 (39b2033).
specVersion is 0.14 and testdata is synced with that commit: every file
changes its spec field, and vectors/tlock_steps.json is new (136 files).
Decision 8: validateProfile admits only bls-unchained-g1-rfc9380, with its
public key in G2, as validateDrand of Go since c041fa3, in its order and
with its text. formats_profile.json and its part of formats_vectors.g.dart
are regenerated with tool/formats_go_vectors.go on 39b2033: only the
thirteen cases of another drand scheme change. The other Go-generated
vectors change only their spec field.
tlock_steps_vm_test.dart walks tlock_steps.json value by value with the
code of the library, and tlock_steps_test.dart walks its copy,
tlock_steps.g.dart from tool/tlock_steps_copy.dart, compiled to JavaScript.
The comment of h3 in ibe.dart now says what the code does: it shifts the
first byte one bit to the right, as kyber does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The vectors of Go are regenerated on datekeys-go 69dbb0c: only the cases
of those checks change. The writer of the extension refuses a DateKey of a
profile that is not pinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec v0.12, section 44.1, already asked for both. The vectors of Go are
regenerated on datekeys-go e801e03: only the ten addresses of those two
forms, and the locators that carry them, change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebased on stage 6b. The hook of the signature of alg 1, which 6b named
AuthorKey, is now AuthorSigner, and AuthorKey of authorkey.dart implements
it. The tests of the writer sign the capsules of alg 1 with the AuthorKey
of Go's seed instead of replaying the recorded signature, and write the
bytes of Go.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two injected faults went unseen: utf8.DecodeLastRune accepting a rune
that does not end at the end of the line, and the position of the
separator checked one byte short for a string that is not ASCII. The
generator now writes lines whose ends are a space next to a stray
continuation byte or a space cut short, which TrimSpace keeps, and
strings with a byte that is not ASCII and a separator 6, 7 or 8 bytes
before the end. Go and Dart agree on all of them, and the tests now see
both faults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/authorkey_bench.dart times on the VM and compiled to JavaScript the
generation of an author key, its Ed25519 signature of 99 bytes and of
1 MiB, the strict verification for comparison, its file encrypted with
scrypt of logN 16 and read again, sealLocator for round 1000 and
newEnvelope of a .dkc of 1 MiB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
locator_seal.dart ports the writing side of package locator of
datekeys-go: sealLocator, Seal of Go, marshals the locator, makes the
tlock recipient of the round and encrypts, in that order and with the
texts of Go, and wipes the plaintext; newEnvelope, NewEnvelope of Go,
draws I_SOBRE, encrypts the .dkc for it with ageEncrypt of stage 6a and
splits the file with splitEnvelope of stage 7a.
tool/locator_seal_go_vectors.go writes test/vectors/locator_seal.json:
Seal of locators of one to three blocks for rounds from 1 to the last of
Quicknet, its refusals, NewEnvelope of .dkc of 0 bytes to 1 MiB and a
whole flow, while crypto/rand reads the keystream of SeededRandomSource.
With the same seed, Dart draws the same values and writes the same bytes
in every case, and the sealed locators open with the release of their
round.
In the other direction, tool/seal_interop_dart_samples.dart writes sealed
locators with their envelopes, author key files and signatures from the
recipes of test/seal_interop_support.dart, and
tool/seal_interop_go_verdicts.go opens them with locator.Open,
OpenEnvelope, authorkey.Read and crypto/ed25519: Go reads all fifteen.
test/vectors/seal_interop.json keeps the verdicts and the digest of each
file, which the tests write again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ed25519_sign.dart signs as crypto_sign of TweetNaCl in its JavaScript
port, with the SHA-512 of package:crypto: the field of curve25519.dart,
whose arithmetic is private there, copied with the product as a loop, and
modL over 64 limbs of 8 bits in a Float64List, with floor divisions in
place of the shifts of TweetNaCl, exact on the VM and on the web. The
secret scalar and the nonce never meet a BigInt or a branch; neither
platform promises constant time, and the values are wiped as a best
effort.
authorkey.dart ports package authorkey of datekeys-go: AuthorKey with
generate, fromSeed, publicKey, sign, clear and secret, and a toString
that hides it; authorPublicString, parseAuthorPublic and
parseAuthorSecret, also on the bytes of a Go string; marshalAuthorKey;
encryptAuthorKey, scrypt with logN 16 through ScryptRecipient and
ageEncrypt of stage 6a; and readAuthorKey, through the age reader with a
maximum work factor of 16, whose lines are those of bufio.Scanner and
strings.TrimSpace. Every error has the text of Go, with the sets of
go_unicode.dart for the case of a string and the spaces of a line. A
cleared key refuses every use, where Go would give the values of a key of
zeros.
tool/authorkey_go_vectors.go writes test/vectors/authorkey.json: the
signatures of crypto/ed25519 over lines of sign.input (RFC 8032 tests
1, 2, 3 and 1024), TEST SHA(abc), seeded seeds and messages up to 1 MiB
and other public keys; the scalars of math/big; and Generate, Encrypt,
ParsePublic, ParseSecret and Read of authorkey with each text, while
crypto/rand reads the keystream of SeededRandomSource. Dart writes the
same bytes and gives the same texts in every case; authorkey.g.dart, a
part of it, runs also in Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Go's authorkey reads its strings and the lines of a key file with
strings.ToLower, strings.ToUpper and strings.TrimSpace, whose results
depend on the package unicode of Go 1.26.8, Unicode 15.0.0: neither the
case mapping of the platform nor the tables of the path rules give the
same. lib/src/go_unicode.dart holds the three sets as runs of code points,
written by tool/go_unicode_tables.go, which scans every code point,
checks the runs against the functions of Go and checks that strings.Map
changes a string exactly when one of its runes changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/datekeys.dart exports the writer of capsules, and what its options
take: X25519Recipient and checkX25519Recipient, RandomSource and
secureRandom. The tests that imported them from their modules no longer
need to. tool/encrypt3_bench.dart times the writer on the VM and in
Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing reaches the sink before the signature; the sink receives the
prelude first, each buffer its own; a sink that fails stops the writing
with its error and is aborted, one that fails to close is not; a hook or a
source that throws a DateKeysException keeps its code, anything else is a
CapsuleWriteException with its cause; a string that is not well-formed
UTF-16 is not valid UTF-8 for Go; a source is read in streaming; the
result and its .dkk; and, on the VM, two capsules of the CSPRNG differ and
open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/capsule_interop_dart_samples.dart writes the capsules of twelve
recipes, six with SeededRandomSource and six with the CSPRNG of the
platform, among them three MiB in three files, two hundred files, and a
capsule of fourteen recipients, a key of words and a portable key.
tool/capsule_interop_go_verdicts.go inspects and opens each with
capsule.Open of Go, with each credential alone, all together and none,
encodes PUBLIC_HEADER, CONTROL_CBOR and the .dkk again, and writes each
seeded one with capsule.EncryptFiles.
Go opens every capsule to the files of its recipe, refuses each without a
credential, finds the layers and the .dkk encoded as it encodes them, and
writes the seeded ones byte for byte. The tests check those verdicts and
write the seeded samples again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/capsule_writer_go_vectors_test.go runs capsule.EncryptFiles of Go on
87 recipes while crypto/rand reads the keystream of SeededRandomSource, as
a test in an export of datekeys-go so that the CMS signatures and tokens of
its hooks come out the same on every run. It records the size of each
draw, what each hook was given and returned, the capsule, the .dkk and the
openings of Go with each credential, and the text, the code and the bytes
written of each error.
The tests write each recipe again: the same draws, the same requests to
the hooks, and the same bytes or the same error, in 21 capsules and 66
errors; and this library opens each capsule as Go did. The cases marked
node also run compiled to JavaScript.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
encryptFiles ports capsule.EncryptFiles of datekeys-go at c531e93: the
checks of the options, the head and the paths, the two readings of each
source in streaming, the security area of 32 KiB, 64 KiB with LargeArea or
a test area, the signature of alg 1 and 2 and the seal through the hooks
AuthorKey, CmsSigner and Sealer, checked by the reader before anything is
written, time_only and time_and_key with the 16 slots, their dummies and
their permutation, the key of words, the portable .dkk, the padding and
every self-check of spec 62.1, with the texts of Go.
The length of SEALED_CONTROL comes from the form of its stanzas instead of
a measured seal; the values that Go draws for that seal are drawn and
dropped, so that with the same random source the writer draws what Go
draws and writes the same bytes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
StandardExtensions checks by default the data of datekeys.capsule with
checkCapsuleData, as locator.Standard of Go: when a .dkk is opened with
it, an extension whose data does not read is unusable, with the text of
Go. With validateCapsule: null it checks only that there is data, as
Go's extension.Standard without ValidateCapsule: the writer of a .dkk
keeps that one, as Go's writer does, and so do the tests of the formats
that compare with extension.Standard. Inspection and opening give Go's
results on every fixture and vector, and the tests of the locator use the
default registry.
lib/datekeys.dart exports locator.dart. The README has the section of
part 7a, its vectors and how the generator makes them; the changelog has
its entry, with its tests and its injected faults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/src/locator.dart gains the data of the extension, as Info, ParseInfo
and Info.Extension of Go, with the same checks in the same order and the
same texts:
- parseCapsuleInfo: the map of spec 44.1 with the note under the rules of
spec 24.1, the canonical DateKey, and a sealed locator that is an age
file with one tlock stanza for its round; every failure is
ERR_EXTENSION_DATA_INVALID and nothing else, so that it makes the
extension unusable and never the .dkk (spec 54).
- CapsuleInfo.toExtension, which reads back what it writes (spec 72), and
CapsuleInfo.openLocator, OpenLocator of Go, with the default registry of
pinned profiles when none is given, as the opening of a capsule takes it.
- checkCapsuleData, the ValidateCapsule of Go's locator.Standard, for
StandardExtensions.
The tests run the extension cases of locator.json with their code, the
writing and the reading of the data, OpenLocator, the registry of
locator.Standard through checkCritical, checkNoncritical and checkWrite,
and the opening of a fixture whose .dkk carries datekeys.capsule, whose
unusable extensions and checks are those of Go.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/src/locator.dart gains the rest of package locator of datekeys-go but
Seal and the data of the extension, with the same checks in the same
order and the same texts:
- Locator: unmarshalLocator, the map of spec 44.1 with the length that
Marshal gives and nothing else, and marshal, its form, its addresses and
key 6 up to the least multiple of 4096 that it fills, as padFor and
PlaintextLength; usable, the addresses that a reader uses.
- openLocator, Open of Go: the profile, then age with the tlock identity
of agewrap, and at most 1 MiB of plaintext, as io.LimitReader: the
chunks after it are neither decrypted nor checked.
- The envelope: restIn, openEnvelope with the size and the SHA-256 of the
rest and of the .dkc, hideRest, and splitEnvelope, the part of
NewEnvelope after its age encryption, which needs the writer of age.
A Locator keeps the types of Go: keys and digests of 32 bytes, and no
negative size or offset. Its errors carry no code, as in Go.
The tests run the cases of locator.json, the padding of every base, 482
plaintexts, Marshal at its limits, 118 openings, the files past 1 MiB, the
envelope, the rests and the split; a part also on Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/src/locator.dart starts with the addresses of spec 44.1, a port of
CheckURI and Address.Host of package locator of datekeys-go: the same
checks in the same order and the same texts, on the UTF-8 of the address,
with Go's %q of the first byte that RFC 3986 does not allow. And
checkResolvedIp, the check of the IP that a name resolves to, which a
reader runs on every connection: the classification of publicIP on the 4
or 16 bytes of an address, an IPv4-mapped one not public. Go has no such
function, since its reader does not download.
lib/src/ipaddr.dart, internal, parses IPv4 and IPv6 with the exact
acceptance of netip.ParseAddr, writes them as its String, and classifies
them as publicIP, with the blocks of spec 44.1. It works byte by byte, so
that the 128 bits of IPv6 stay exact on the web, and never uses dart:io.
The tests run the 247 addresses of locator.json and the 2 800 of the
vectors, the 1 700 strings of netip and the 868 byte strings of publicIP,
on the VM and on Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/locator_go_vectors.go runs package locator of datekeys-go at c531e93,
the draft v0.12, in its module, without changing it, and writes what Go
gives, with the texts of its errors:
- locator_uris.json: CheckURI and Address.Host on the 247 addresses of
testdata/vectors/locator.json and on 2 800 more, built at every edge of
spec 44.1 and drawn from a seed: IPv4 and IPv6 in every notation that
netip.ParseAddr accepts or rejects, zones, mapped, NAT64 and 6to4
addresses, the first and last address of every IANA block and their
neighbours, long and punycode labels, local names, ports, percent
signs, dot segments and CIDs; netip.ParseAddr and String on 1 700
strings; and publicIP, reached with go:linkname, on 868 byte strings.
- locator_vectors.json: the texts of the other cases of locator.json;
PlaintextLength from -4100 to 16484; Unmarshal on 482 plaintexts; Marshal
at every limit and boundary of the padding; Open on 118 sealed locators
of four rounds, edited or with other releases and profiles; Open past
1 MiB of plaintext, read through io.LimitReader; the envelope, its rest,
Hide and the split of NewEnvelope; Info.Extension, Info.OpenLocator and
ParseInfo; locator.Standard as a registry; and capsule.Open of a fixture
whose .dkk carries datekeys.capsule.
crypto/rand.Reader is a ChaCha8 of a fixed seed, so every run writes the
same bytes. The Dart constants hold the whole of the first file and a
part of the second, for the tests compiled to JavaScript.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README says that part 6a of stage 6 is done, and has its section:
random.dart, age_writer.dart and recipient.dart, the order of the
random values, the STREAM, the labels, the errors, the rules of spec
section 37, the lengths, the tlock encryption that is not constant time,
which the author accepted, and what is exported, nothing. It describes
the new vectors and how the two generators make them, in an export of
datekeys-go, and gives the times of the writer. The changelog has the
entry of the part, with its tests and its injected faults, and the
library comment names it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/age_writer_bench.dart times the header of each age file of a
capsule, the file of an author key and a large PAYLOAD_AGE in streaming,
with the CSPRNG of the platform. On the VM, 64 MiB take 1.38 s, 46
MiB/s; compiled to JavaScript, 16 MiB take 0.33 s, 49 MiB/s. The header
of PAYLOAD_AGE costs 4 ms, INNER_ACCESS_AGE with 16 stanzas about 45 ms
on the VM and 35 ms in Node.js, OUTER_TIME_AGE 40 ms and 0.55 s, and an
author key file with scrypt of logN 16, 0.6 s and 0.85 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/age_interop_dart_samples.dart writes the files of the recipes of
test/age_interop_support.dart with SeededRandomSource, and
tool/age_interop_go_verdicts.go, in an export of datekeys-go, opens them
with age and the identities of agewrap and writes age_interop.json:
X25519 from 0 bytes to 3 MiB, one written in pieces, three and sixteen
recipients, tlock opened with the release of round 1000 of the fixtures,
tlock over sixteen X25519 as a SEALED_CONTROL, and scrypt with work
factors 10 and 16. Each sample keeps its recipe, the length and the
SHA-256 of its file, the file when small, its stanzas, the stanza rules
of agewrap on them and the verdict of Go with each opener.
The tests write each file again and must get the one that Go read; Go
opened it to the plaintext of the recipe, or refused it with an
identity that must not open it; and this library makes the same of it
as Go, with the same texts and the same stanza rules.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
age_writer.dart ports age.Encrypt of filippo.io/age v1.3.2, with its
checks and texts. AgeEncryptor and ageEncrypt draw the file key, wrap it
for each recipient in its order, with its labels, compute the header MAC
and draw the nonce: no recipients, labels that cannot be mixed, a
recipient that fails and stanzas that cannot be marshalled give Go's
errors. AgePayloadEncryptor encrypts the STREAM as its plaintext
arrives, as Go's EncryptWriter: a full chunk waits for the next byte,
so the last one is full-length for a non-zero multiple of 64 KiB and
empty only for an empty plaintext. The lengths of a file follow from its
plaintext and the form of its stanzas, before anything is encrypted.
recipient.dart has X25519Recipient, with its age1 strings and the texts
of ParseX25519Recipient; ScryptRecipient, with its random label;
TimeRecipient, with the label datekeys-tlock- of agewrap;
checkX25519Recipient, with the texts of agewrap.CheckX25519Recipient;
generateX25519Identity and the raw keys of agewrap.
The tests write every file of age_writer.json again with the same seed,
whole and in pieces, and get the same draws and bytes; open each with
the readers of this library; and check the errors, the recipients, the
STREAM and the lengths, on the VM and, without the expensive cases, on
Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
random.dart: RandomSource, the injectable source of every random value
of a writer; secureRandom, the default, Random.secure of the platform;
SeededRandomSource, deterministic, for tests and vectors only; and, for
the 16 slots of INNER_ACCESS_AGE, randomIndex, an integer drawn as
crypto/rand.Int draws it, and permute, as the permute of
capsule.Encrypt.
encryptOnG2 and wrapTlockStanza take the source of sigma, secureRandom
by default, so that a tlock stanza can be written again byte for byte;
ibe.dart no longer holds a Random.secure of its own.
The tests check the keystream, randomIndex and permute against the
vectors of Go, randomIndex at its bounds, the uniformity of permute, and
the CSPRNG on the VM: in dart test -p node there is no Random.secure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/age_writer_go_vectors.go runs in an export of datekeys-go at
c531e93 and makes crypto/rand read the keystream of the seeded source of
the writer: ChaCha20 under SHA-256(seed), with a zero nonce. age.Encrypt,
with the real X25519, scrypt and tlock recipients, then draws known
values, and age_writer.json records every draw, its size and its order,
with the files: one X25519 recipient over plaintexts of 0 bytes to 3 MiB
across the chunk boundaries; two, three and sixteen, and one with bit 255
set, which age accepts; scrypt with work factors 1 to 16; and the tlock
stanza of rounds of 1 to 11 digits.
The generator checks each file against testkit.SealAge, with the first
draw as the file key and the last as the nonce, checks each X25519
stanza against its ephemeral secret, and opens the file with Go. It also
records the errors of age.Encrypt with the draws before them, those of
the constructors, ParseX25519Recipient, CheckX25519Recipient,
GenerateX25519Identity, crypto/rand.Int and the permute of capsule over
the keystream, and the lengths of testkit and capsule. The output is the
same on every run. age_writer.g.dart holds the same JSON for the tests
compiled to JavaScript.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README says that stage 5 is done and the library evaluates the whole
security area as Go, and has the section of part 5c: securitycms.dart,
the order of its checks, the validity of a certificate at the time of
its seal, the round time and Go's zero time, what the reader throws, the
default evaluator and what is exported. It describes the new vectors and
how the generator makes them, and gives the times of opening the two
fixtures with certificates and seals. The changelog has the entry of the
part, with its tests and its injected faults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Injected faults showed two rules that only the tests on the VM checked:
SIGNERS of more than 16 entries, whose only case was in security_cms.json,
and the order of the foreign signers. securitycms_vectors.json gains
SIGNERS of 16 and of 17 entries with Ana among them, beside her signature
for those SIGNERS or for SIGNERS with her alone, and a required signer
beside two foreign ones, without seals so that the area stays small; the
part for Node.js holds them. 760 cases.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/open_bench.dart also opens format3_signed_cms, a signature of alg 2
by two signers each with a seal, and format3_sealed, a signature of alg 1
and a seal of seal_type 2, from securitycms_vectors.g.dart, and times
inside each opening the evaluation of its security area by the default
evaluator. On the VM an opening takes about 51 ms, of which the area
about 9 ms; compiled to JavaScript, about 1 s, of which the area 145 ms
and 49 ms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
securitycms.dart ports evaluateCMS, signerLine and evaluateSeal of
signature2.go of Go at the draft v0.12, on the reader of CMS of stage 5a,
with the same order of checks: SIGNERS with its profile and at most 16
entries, then the SignedData; each required signer in the order of
SIGNERS and each foreign one in the order of the encoding, valid,
invalid, absent, not verifiable, without seal, with an invalid seal or
out of validity at the time of its seal; F2, F5 and F6 with their
detail; and the seal over SEAL_SUBJECT, S1 to S5 with the authority and
t. A round time at Go's zero time is no round time, as IsZero, and
Verdicts.sealedAt skips a seal at that time, as SealedAt.
cmsReader is the default CmsEvaluator of evaluateSecurity, and so of
evaluateSecurityInput and the opening: nothing that Go evaluates is left
not evaluated; a caller that passes cms: null still gets the parts
without CMS alone. encodeSigners and maxSigners are exported, as
EncodeSigners and MaxSigners of Go.
The tests compare every part with Go: the 135 cases of security_cms.json
with the result of each signer, the 24 of security.json, the 56
signatures of alg 2 and 105 seals of seal_type 2 of security_vectors.json,
the 755 cases of securitycms_vectors.json with their detail and earliest
seal, the fixtures format3_signed_cms and format3_sealed, and their
openings in open_cases.json. On Node.js, a part of the vectors and the
two fixtures opened in full. 1572 tests on the VM and 332 on Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/security_go_vectors.go also writes securitycms_vectors.json:
EvaluateSecurityIn of package capsule at the draft v0.12 on 755 areas
whose CMS signature or RFC 3161 seal it makes, as internal/cms/cmstest
makes them, with the verdicts, the lines, the detail of every signer and
of a valid seal, and SealedAt. Required and foreign signers of every
result; three required signers drawn from a seed; the validity of a
certificate at the time of its seal, at the nanosecond; t plus the
accuracy against the round time on both sides of it, Go's zero time and
the last second of 9999; a seal of each verdict beside a signature of
each verdict; and mutations of a SignedData, of SIGNERS and of tokens.
cmstest cannot be imported from outside the tree of datekeys-go, so the
part of it these cases need is restated. The keys come from labels, ECDSA
signs with the nonce of RFC 6979 and RSA with PKCS #1 v1.5: every run
writes the same bytes, and security_vectors.json and its part are the
same as before. Certificates, tokens and SignerInfo are written once, as
chunks. securitycms_vectors.g.dart holds a part of the cases, each verdict
pair of each group among them, and the fixtures format3_signed_cms and
format3_sealed, for the tests compiled to JavaScript.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The modules of the CMS reader, ECDSA and RSA, their notes, their
timings, the generator of their vectors and its files; BigInt in ECDSA
and RSA, which only verify with public values; and the entry of stage
5a in the changelog, with its tests and its injected faults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>