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>
tool/cms_bench.dart times, on the VM and compiled to JavaScript, one
verification of ECDSA on each curve and of RSA of each size and scheme,
the reading of a certificate, and a signature and a token read and
checked, on the cases of cms_vectors.g.dart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cms.dart ports internal/cms of datekeys-go at c531e93, with its order of
checks and its texts: parseCert reads a certificate field by field with
the profile of spec v0.12 §29.10, with the holder from givenName and
surname before the commonName and the issuer from organizationName when
there is no commonName with text; parseSignature reads a detached
SignedData, its certificates, OCSP responses, signers, signed and
unsigned attributes, and SignerInfo.check gives valid, invalid or not
verifiable with the closed table of algorithms, a key of another scheme
invalid; parseToken reads an RFC 3161 token and its TSTInfo field by
field, form errors before algorithm errors, and Token.check verifies it
over a subject. Object identifiers are compared by their bytes, and a
SET OF may repeat an element.
The tests run every case of the vectors on the VM, the fixtures
format3_signed_cms and format3_sealed included, and the part that
cms_vectors.g.dart holds compiled to JavaScript.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nist_curves.dart reads an uncompressed point as Go's
ecdsa.ParseUncompressedPublicKey does, and computes u1 G + u2 Q in
Jacobian coordinates with Shamir's trick, the infinity included.
ecdsa.dart is VerifyASN1: the encoding as cryptobyte reads it, r and s
in [1, n - 1] and never reduced, s above n/2 accepted, the hash cut to
the bits of the order. rsa.dart is VerifyPKCS1v15, which rebuilds the
encoding and compares it whole, and VerifyPSS with a salt of the length
of the hash, with the bytes before emLen that must be zero; and the
hashes of the table, SHA-256, SHA-384 and SHA-512 of package:crypto.
All on BigInt, exact on the VM and compiled to JavaScript, and not
constant-time: a verification sees public values only. The curve
constants are those that the vectors record from Go.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/cms_go_vectors_test.go records what internal/cms of datekeys-go at
c531e93 (the draft v0.12) and the ECDSA and RSA of Go give: the curves,
points and signatures of ecdsa.VerifyASN1; RSA keys of 2048 to 4096 bits
and signatures of rsa.VerifyPKCS1v15 and VerifyPSS, with encodings built
by hand, each with one defect of its padding; ParseCert, ParseSignature,
SignerInfo.Check, ParseToken and Token.Check on the cases of the tests of
internal/cms and others (identifiers whose arcs wrap around in 32 or 64
bits, SET OF with an element repeated, the limits of the accuracy and of
the imprint, the ends of the validity); signatures, tokens and
certificates edited node by node and bit by bit; and every signature and
token of security_cms.json and of the two CMS fixtures, signer by signer.
It runs as a test in an export of datekeys-go, so that it can import
internal/cms and make keys and signatures deterministic with
testing/cryptotest: every run writes the same bytes. Each file stays
under 560 KB; repeated certificates are written once per file.
cms_vectors.g.dart holds a part of each file for the tests compiled to
JavaScript, and a test on the VM checks that it is that part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/datekeys.dart exports author.dart and security.dart, as package
capsule of Go exports the commitments and the evaluation, and the tests
that imported them from lib/src import them from the library.
The README and the changelog give the testdata of the branch v0.12 of
datekeys-go, the modules of part 5b with their notes, the boundary with
the reader of CMS of part 5c, the vectors of the security area, and the
tests and the faults injected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OpenOptions.evaluator is now evaluateSecurityInput, so that opening a
capsule of format 3 gives the verdicts of Go at step 17: the signature of
alg 1 and every verdict of the form, with the author keys of the options,
and the signature of alg 2 and the seal of seal_type 2 not evaluated
until a reader of CMS is given. notEvaluated stays for a caller that
shows no verdict.
tool/open_go_vectors.go records the verdicts of each capsule of format 3
that opens, with their lines, the key and the label of alg 1 and the
earliest valid seal, and opens the fixtures signed with alg 1 also with
their author key saved, F3, and with another, F4. Every case of
open_cases.json and every case of the mutation corpus that opens gives
those verdicts and lines: format3_signed, format3_unsigned,
format3_signature_unsupported, format3_seal_unsupported,
format3_security_v2 and format3_note all of them, and format3_signed_cms
and format3_sealed the part that needs no reader of CMS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
author.dart ports what an author signs and a seal seals from
signature.go of datekeys-go: payload_commit, control_commit over
CONTROL_SIG in each format, head_digest, signers_digest, AUTHOR_MESSAGE
with its prefix and its 99 bytes, its code taken byte by byte as Go
takes it, SIG_PART and SEAL_SUBJECT.
security.dart ports SECURITY_CBOR and EvaluateSecurityIn: the outer
map, author-signature and seal with the schema and the limits of Go,
their encoders, and an evaluation that never throws. X for an outer map
that fails its layer 2 or 3, version 2 among them; F0 to F4 for the
signature, with verifyStrict for alg 1 and the key matched against the
saved ones by its dkauthor1 string; S0 to S2 for the seal; and a failure
inside one part fails that part only, as Go recovers a panic. Without a
context it reads as a reader of v0.10. securityContext is
newSecurityContext with the head digest, control_commit at zero when
CONTROL_SIG cannot be encoded, and holderText the rule of a name of a
certificate.
The signature of alg 2 and the seal of seal_type 2 belong to the reader
of CMS of stage 5c, behind the interface CmsEvaluator: without one their
verdict is null, not evaluated, never guessed.
verdicts.dart gains the texts of Go at the draft v0.12, Verdicts.lines
and sealedAt, Detail, SignerLine and SignerResult, and verdicts that may
be evaluated in part.
The tests check every case of security_vectors.json, security.json in its
context, the commitments, the signature and the seal of each fixture of
format 3, and the boundary with a reader of CMS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/security_go_vectors.go writes test/vectors/security_vectors.json
with package capsule of datekeys-go at c531e93, the draft v0.12, run in
its module without changing it, and a part of it for Node.js in
security_vectors.g.dart:
- the commitments: PayloadCommit, ControlCommit of the control of every
fixture and of controls built with extensions, in each format, with
the text of the error where CONTROL_SIG cannot be encoded, HeadDigest,
SignersDigest, AuthorMessage, AuthorCode, also of messages that
AuthorMessage never writes, SigPart and SealSubject;
- the encoders of SECURITY_CBOR, author-signature and seal;
- 1730 evaluations of SECURITY_CBOR with EvaluateSecurityIn in 23
contexts, and EvaluateSecurity without one: the outer map,
author-signature and seal broken in every way of their schemas and
limits; signatures of alg 1 valid and invalid, saved or not, with the
cases of Taming the many EdDSAs made over AUTHOR_MESSAGE by searching
the context; and mutations of nine bases from a fixed seed. Each case
gives the verdicts, the key and the label of alg 1, the lines, alg and
seal_type as read, and the parts that only the reader of CMS evaluates;
- Lines and SealedAt of verdicts built with every pair of verdicts and
the details of signers and seals;
- holderText, reached with go:linkname, on names at the limit of 64 code
points and drawn from the seed.
The files are ASCII, and every run writes the same bytes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each of the 16 notes of testdata/vectors/note.json, the cases of the
list of v0.11 of spec section 64, gives the result and the detail of the
reference through checkNoteData, and a reader shows it as publicNote only
when it is ok; otherwise unusableNote says so and StandardExtensions
reports it unusable with ERR_EXTENSION_DATA_INVALID, never the capsule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
open_cases_test.dart compares the opening of each fixture with each of its
credentials with the record of the fixture itself, not only with what Go
wrote: the content of formats 1 and 2 is its .plaintext, whose SHA-256 the
record gives, and in format 3 each file is the range of BODY that the
record names, with its comment, its declared author and the size of its
area. The changelog counts 1320 tests on the VM.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README describes the modules of part 4c and their notes: the input in
memory or from a ByteSource and what is read of it before the release;
the output and the sinks, required for their format, closed or committed
only at step 18 and aborted after any failure; the result, which never
throws for an invalid capsule; the credentials, the key of words among
them; the evaluator of stage 5 and accept; StandardExtensions with the
rules of the note; the differences of form with Go; and a bug of dart2js
of Dart 3.13 that an application for the web should know. It adds the
times of the opening, the generators of the vectors of stage 4c and their
files. The changelog has the part, its tests and the 15 faults injected,
all of them found on the VM and 13 on Node.js. The comment of
lib/datekeys.dart says what the library reads now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/open_bench.dart times, on the VM or compiled to JavaScript, the
opening in memory of a fixture of each kind, time_only, time_and_key with
its .dkk in formats 1, 2 and 3, and format 3 with its file, each with the
verification of its release, and the throughput of a capsule opened from a
source in streaming, which test/large_capsule.dart makes from the
fixtures: 64 MiB on the VM, 16 MiB on Node.js. Medians of three runs on
the VM: 48 to 73 ms for a fixture, 1.50 s for 64 MiB in format 1 and
2.64 s for 64 MiB in one file of format 3, whose SHA-256 adds 17 ms per
MiB; on Node.js about 0.8 s for a fixture, 0.73 s for 16 MiB in format 1
and 0.92 s in format 3.
It never passes a sink as `c ? null : sink`: dart2js of Dart 3.13 loses
the writes to an object that reaches a field that way, and its counter of
bytes read 0 afterwards.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/src/open.dart and open3.dart port Open and openBody of package capsule
of datekeys-go at c531e93, with the checks, codes, steps and texts of the
reference and the detail of each check: the .dkk of step 9.a, decoded or
still encoded, with its material, its critical extensions, its capsule_id
and its capsule_digest; at least one credential; the clock and the release,
with the rule of step 9 for the failures of its source (sourceFailure);
its verification at step 10; OUTER_TIME_AGE with the tlock stanza;
INNER_ACCESS_AGE with the X25519 identities, those of the .dkk and the key
of words, and the rules of the slots; CONTROL_CBOR, header_binding,
I_PAYLOAD and P; PAYLOAD_AGE streamed, with the padding of format 2
checked and never delivered, and in format 3 the frame of BODY, the area,
the head, each file to the sink with its SHA-256 and the padding, with the
precedence of spec §63; and the commit. A failure of age keeps the code of
the identity that reports it, or is ERR_INTEGRITY with the reason of its
phase, as classify of Go.
openCapsule opens a capsule in memory and openCapsuleSource one that a
ByteSource reads: the prefix of the inspection and the nonce of
PAYLOAD_AGE before the release, then pieces of 1 MiB. lib/src/sink.dart
has ByteSink, for the content of formats 1 and 2 and for each file, and
FileSink, for the files of format 3, as the dst and the Sink of Go, with
MemoryByteSink and MemoryFileSink. Nothing is presented as valid before
step 17 ends: the output is closed only then and aborted after any
failure, and the sink aborted after any failure that follows its begin
(spec §56).
The signature and the seal are stage 5: lib/src/verdicts.dart has the
verdicts and the SecurityEvaluator, given what newSecurityContext and
EvaluateSecurityIn of Go take, which never fails the opening; the default
evaluates nothing. OpenOptions.accept is Accept of Go. And
AgePayloadDecryptor.wipe clears the key of a STREAM left unread.
The tests run open_cases.json and the mutation corpus with the texts and
the checks of Go, in memory and from a source read in pieces; capsules of
several MiB made from the fixtures, for the streaming; a capsule with a
stanza for a key of words; and the caller, the sinks and the evaluator.
open_test.dart runs on Node.js too, with open_vectors.g.dart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/src/inspect.dart ports Inspect of package capsule of datekeys-go at
c531e93: the steps 1 to 8 of spec §63, without network and without
secrets, with the check of each step as Go records it, its name and its
detail, and Inspection with the fields of the steps that passed. The
opening runs them with a hook right after step 2, as the afterPrelude of
Go. inspectedLength, from prefix.ts of datekeys-ts, names the first bytes
that give the inspection of a whole file, so that a large capsule is read
up to one byte after the largest age header of PAYLOAD_AGE, and
maxAccessKeyRead the most bytes of a .dkk that its decoder needs. And
inspectView and inspectJson, the exact output of datekeys inspect -json
of internal/inspectview, with the public note.
lib/src/source.dart has ByteSource, the bytes of a capsule read by ranges,
which the application adapts from a file or a Blob, and BytesSource, over
bytes in memory; inspectCapsuleSource reads only the prefix.
The tests compare, byte for byte, the 24 fixtures/*.inspect.json and the
views of open_inspect.json, and each of the 5110 mutations of
inspect_differential.json with its code, its step and the text of Go,
also from a source read in pieces; and the prefix at the limits of age.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
tool/mutation_go_texts.go ports scripts/mutation-go-texts.go of
datekeys-ts over the synced testdata of this repository, the tag
spec-v0.11: it replays every case of the mutation corpus as the testkit of
the reference does and writes the text of capsule.Open and its checks,
with the detail of each step, in mutation_texts.json. Where Go and the
corpus disagree on a code or a step it would say so in the case; Go at
c531e93 and at the tag spec-v0.11 agree with all 210 cases, and give the
same file, byte for byte.
tool/open_go_vectors.go runs in an export of datekeys-go, since it uses
internal/testkit, internal/cbortest and internal/inspectview, and writes:
- open_cases.json: capsule.Open on every fixture with each of its
credentials, and 117 openings of edited fixtures or with other options
at each step that the corpus does not reach: the frame, the fields, the
extensions and the bindings of a .dkk at step 9.a, the clock and the
failures of the release source, the age headers of steps 11 and 17, a
malformed X25519 stanza in INNER_ACCESS_AGE, CONTROL_CBOR and the BODY
of format 3 sealed again, the sinks and the output that fail, the
refusal of Accept and the unusable extensions of each object, with the
text, the step, the checks, the release requests, the state of the
sink and the content or the files;
- open_heads.json: capsule.DecodeHead and EncodeHead of heads of a fixed
seed, valid and broken in each layer of spec §69.1;
- open_notes.json: extension.CheckNote, Note, Header.UnusableNote and
extension.Standard with a note;
- open_inspect.json: the text of capsule.Inspect for each of the 5110
mutations of inspect_differential.json, where Go and the file also
agree, and the exact output of datekeys inspect -json with public notes;
- open_vectors.g.dart: seven small fixtures, their records and a part of
each file, for the tests that run compiled to JavaScript.
The edited capsules are sealed again with the file keys and the nonces of
the fixtures, as the testkit does, so the output is the same on every run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/datekeys.dart exports the key of words, as Go exports its package
wordkey: normalizeWords, checkWords, wordKey, WordKeyException, minWords,
minLetters and wordKeyRounds. wordIdentity stays internal, since it
returns an identity of age.dart, and so do the rules of paths and texts,
as internal/pathrule does in Go: the head and the public note of stage 4c
will use them.
The README says what stage 4a ports and how: the bytes of Go, the
platform's Unicode left aside, the errors, the round of 53 bits, the one
difference with datekeys-ts and what is exported; the integer rule of the
tables; the timings; and how the vectors are written, the generator of
the paths in an export of datekeys-go. The changelog has the entry of the
stage.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/pathrule_bench.dart times, on the VM or compiled to JavaScript,
checkPath of a path of ASCII and of one that R6c projects with its 15
tables, checkComment of a comment of 16 351 bytes, checkTree of 1000
paths, the largest head (65 535 paths checked and their tree),
normalizeWords with checkWords, and wordKey with its 600 000 iterations
of PBKDF2. Medians of three runs on the VM: 9.5 and 28 microseconds for
the paths, 0.68 ms for the comment, 2.0 s for the largest head and
1.14 s for the key; on Node.js 11.8 and 38 microseconds, 0.63 ms, 2.3 s
and 0.73 s.
The header of tool/pathrule_go_vectors.go says now that its named cases
hold both sides of each limit of R2, R3 and R6b.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Faults injected one at a time found four that the vectors let through:
R2 at 32 segments, R3 counting code points instead of UTF-16 code units
in an astral NFD, U+036F kept by normalizeWords, and DEL let through by
checkWords. The generators now write both sides of each limit: 32 and 33
segments, 255 and 256 bytes in letters of two and four bytes, 255 and 256
UTF-16 code units of NFD and 252 and 258 from astral decompositions,
bases of 8 and 9 runes, extensions of 3 and 4 also astral, the first and
the last mark of U+0300 to U+036F and their neighbours, and U+001F,
U+007E, U+007F, U+0080 and U+00A0 in a word. All four faults are caught
now.
wordKeyPassword is the password P of spec §38.1, as wordKeySalt is S, and
wordKey uses both: the tests compiled to JavaScript check P against the
one of Go, so that a wrong separator of the words is caught there too,
not only by the keys of 600 000 iterations on the VM.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/src/wordkey.dart ports package wordkey of datekeys-go at c531e93:
normalizeWords is wordkey.Normalize (the NFD of pathrule.dart, without
U+0300 to U+036F, the simple lowercase of Unicode 18.0.0, split at the
white space of §38.1), checkWords is wordkey.Check with its texts,
wordKey is wordkey.Key (PBKDF2-HMAC-SHA256 of sha256.dart, 600 000
iterations, with the salt of §38.1) and wordIdentity is wordkey.Identity,
an X25519Identity of age.dart. As in pathrule.dart, the functions whose
name ends in Utf8 take the bytes of a Go string, and a String is taken as
utf8Bytes writes it.
tool/wordkey_go_vectors.go runs in the module context of datekeys-go and
writes test/vectors/wordkey_vectors.json and its Dart copy: 400 texts and
their words, 513 lists of words and the result of Check, and four keys
with their salt, the PBKDF2 of 1000 iterations for Node.js and the
recipient, the vector of §38.1 first. The keys of 600 000 iterations run
on the VM only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>