datekeys-ts 0.3.0 implements spec v0.14 (tag spec-v0.14 of datekeys-go):
one drand scheme and the root of trust byte for byte, with the vectors of
tlock_steps.json, besides everything 0.2.0 does. It is the version frozen
for the external review.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- SPEC_VERSION 0.14; testdata synced from datekeys-go at 39b2033
(spec-v0.14), which adds vectors/tlock_steps.json;
testing/mutation-texts.json regenerated with Go: only its spec field
changes.
- Decision 8: validateProfile admits only bls-unchained-g1-rfc9380, with
its public key in G2, in the order and with the texts of Go's
validateDrand at c041fa3; any other drand scheme fails with
ERR_UNKNOWN_PROFILE before the key and the chain hash.
- vectors.test.ts walks tlock_steps.json value by value with the code of
ibe.ts, release.ts and bls12381.ts, with its negative checks, and the
testdata guard requires it. ibe.ts exports h3Base, h3Try and hashToG1,
which h3, the encryption and release.ts now use.
- The comment of h3 said the top bit is cleared: the first byte is
shifted one bit to the right, as kyber does.
- README and CHANGELOG.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
datekeys-ts 0.2.0 implements spec v0.13 (tag spec-v0.13 of datekeys-go): it
reads capsule formats 1 to 3 and writes format 3, with the author signature
of alg 1 and alg 2, the seal, the key of words, the public note and the
locator of datekeys.capsule, and the /inspect and /create pages.
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>
/inspect shows the public note under the verdict, as text of the creator
that nobody checked, with the warning of Go's showNote. The pages no longer
show the message of an unexpected exception: unexpectedProblem says in
Spanish what to do, and logs the exception. vite.config.ts pre-bundles the
dependencies the pages load on demand, so that the dev server does not
reload the page on the first opening and break the import in flight.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
encryptFiles gains authorKey (alg 1, an AuthorSigner such as AuthorKey),
cmsSigner (alg 2, a CMS signature with certificates), sealer (seal_type 2,
an RFC 3161 token) and largeArea, as EncryptOptions of Go at spec-v0.12:
the same checks in the same order with the same texts, the signature and
the seal made with the final control and head and before anything is
written, and the security area evaluated by the reader of this library in
the context of the capsule before it is written, as Go's security does.
The hooks may be asynchronous. The area grows to 64 KiB only when what was
signed does not fit and largeArea allows it, and the larger capsule counts
in the limit of memory. security.ts encodes the area with its signature and
seal, and securitycms.ts encodes SIGNERS.
scripts/signing-go-vectors_test.go, run as a test in an export of
datekeys-go at spec-v0.12, writes testing/signing-vectors.json: with the
draws of crypto/rand of Go and the signatures and tokens of its hooks,
encryptFiles writes the eight signed and sealed capsules of Go byte for
byte, asks the hooks over the same messages, and fails with the text of Go
in the other 15 recipes; and Go opens the five capsules that
scripts/signing-ts-samples.mjs writes with this library, its own random
values and certificates, with the same verdicts and lines.
check-build.mjs fails when a page loads the author keys with the page, or
when /inspect can load them at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
authorkey.ts ports the package authorkey of datekeys-go at spec-v0.12:
generate with an injectable random source, fromSeed, publicKey, sign,
clear, secret, a toString that hides the secret, publicString, parsePublic
(canonical, on the curve, not of small order), parseSecret, marshal, and
the key file encrypted with age and scrypt of work factor 16, read with a
maximum of 16, 64 KiB and the lines of bufio.Scanner, with the error texts
of Go and of Go's age byte for byte. Key strings are read as Go strings,
with the case and space tables of Go's package unicode (gounicode.ts,
generated by scripts/go-unicode-tables.go).
ed25519sign.ts is crypto_sign of TweetNaCl, as the Dart port, with the
SHA-512 of @noble/hashes: exact arithmetic in Float64Array, secrets never
in BigInt. No new package or module: age-encryption writes the scrypt
stanza, and the STREAM of a key file uses the ChaCha20-Poly1305 and HKDF
already imported.
scripts/authorkey-go-vectors.go, the generator of the Dart port with this
library's output, writes testing/authorkey-vectors.json in an export of
datekeys-go at spec-v0.12: 234 signatures, scalars, keys, Generate and
Encrypt with Go's draws (reproduced byte for byte), 1288 key strings,
3240 runes at the edges of the tables and 130 key files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ageio.ts reads and writes age files as filippo.io/age v1.3.2, with its
texts and its order of random draws, which Go's locator copies; it uses
only the noble modules that x25519.ts already uses. envelope.ts is Open,
Info.OpenLocator, OpenEnvelope, Seal and NewEnvelope of package locator
at spec-v0.12, with an injectable random source read in the order of Go.
- locator-seal.json (scripts/locator-seal-go-vectors.go): with the same
seed, seal and newEnvelope write the bytes of Go;
- locator-interop.json (scripts/locator-ts-samples.mjs and
locator-go-verdicts.go): Go opens what this library writes, up to a
.dkc of 16 MiB and one byte;
- vectors.test.ts runs all of testdata/vectors/locator.json with the
texts of Go, instead of its spec field only.
Shared files: dependencies.test.ts lets ageio.ts import noble and keeps
the four locator modules out of index.ts; check-build.mjs fails when a
page loads the locator with its first load; vitest.config.ts holds them
at 100 % coverage; ibe.ts updates the comment of encryptOnG2WithSigma;
README and CHANGELOG describe the port.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port of package locator of datekeys-go at spec-v0.12 (spec §43 to §44.1),
with the checks, the order, the codes and the texts of Go: the data of the
extension (parseInfo, infoExtension), the registry of locator.Standard,
the addresses with every rule of §44.1 (checkURI, addressHost,
usableAddresses), the IP addresses as netip reads them, compared byte by
byte with the IANA blocks, checkResolvedIp as datekeys-dart has it, the
plaintext with its padding, and the rest in its host.
scripts/locator-go-vectors.go, the generator of datekeys-dart stage 7a
with the seeds of this repository, writes testing/locator-uris.json and
locator-vectors.json from an export of datekeys-go at spec-v0.12.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README: the module rows of der.ts, cms.ts, securitycms.ts, security.ts,
extension.ts (checkWrite), note.ts (checkNoteData, unusableNote) and
inspect.ts (the public note of the view); testdata at 601e6d2 while
SPEC_VERSION stays 0.11; the 218 cases of mutations.json; security.json,
security_cms.json, note.json and locator.json among the vectors that the
tests read. CHANGELOG: the two entries of 5 October.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
testdata is synced with the head of the branch v0.12 of datekeys-go
(601e6d2); SPEC_VERSION stays 0.11 until the author approves the draft.
- CMS as Go reads it at the draft v0.12 (review of 2 October, T2, T6 to T8
and the TypeScript side of E7 to E9):
- the certificate field by field with the profile of section 29.10, its
errors in CertificateError with the texts of Go; one that breaks it
decides nothing unless a SignerInfo names it, and two copies are one;
- the text of a name only from UTF8String, PrintableString, IA5String,
TeletexString in ASCII and BMPString without surrogates, never from an
attribute that appears twice; the holder by givenName and surname
before the commonName, the issuer by its commonName or its
organizationName;
- object identifiers by the bytes of their DER; a SET OF may repeat an
element; RSA with NULL parameters and an odd modulus; a key of another
scheme than its algorithm is invalid; a messageImprint of another
length is S3; the crls of a token decide nothing;
- DER: UTCTime and GeneralizedTime in their X.690 forms with a date that
exists, the restricted string types as primitive, the accuracy as
minimal INTEGERs. The test of cms.test.ts that compared a function
with itself has an expected value of its own.
- The verdicts in the texts of the draft: names between « and », shown
with at most 64 code points and no two spaces in a row, or their SHA-256;
in F6 the authority of each seal and the warning that nobody checks who
issued it; foreign signers in Spanish; times with their fraction.
- security.json in its context with lines, and the 135 cases of
security_cms.json, compared field by field, lines included.
- The 218 cases of mutations.json, with the texts of capsule.Open
regenerated by scripts/mutation-go-texts.go, and ibe-vectors.json with
the fixtures format3_note, format3_unsigned and the new
format3_seal_unsupported; its frozen values do not change.
- note.json, run with checkNoteData, publicNote and unusableNote.
- inspect reads the public note on demand, only for a header with one, so
that the Unicode tables never load with /inspect; the view of inspect
-json gives public_note and public_note_unusable, as Go.
A Go/TypeScript differential of 63,623 security areas, made from the
vectors, edited element by element and signed afresh with varied
certificates, tokens and authorities, gave no difference in verdicts,
results or lines; the code before this change differed in 13,296 of the
first 42,986.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As extension.CheckWrite of the Go reference on the branch v0.12:
- extension.ts holds NOTE_ID and CAPSULE_ID, and checkWrite: datekeys.note
goes only in the noncritical array of PUBLIC_HEADER and datekeys.capsule
only in that of a .dkk, version 1, with data that is checked; any other
extension is the application's own.
- The writer of capsules applies it in newSealer, after the public note
and before the profile, to the arrays of PUBLIC_HEADER, CONTROL_CBOR and
the head, with the rules of the note; the writer of a .dkk applies it
after checkDisjoint. The texts are those of Go, taken from a probe.
- note.ts: checkNoteData checks the bytes of a note in the order of Go's
CheckNote, length, then UTF-8, then the rules of text; unusableNote is
Go's Header.UnusableNote. lengths.ts no longer keeps its own NOTE_ID.
Green on the test data of spec-v0.11: it changes no output of a valid
writer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes T14 of the review of the session of 1 and 2 October:
- README: the table of modules gains author.ts, ed25519strict.ts, der.ts,
cms.ts, securitycms.ts and note.ts, and names Go at spec-v0.11; the row
of the format 3 gives the verdicts of v0.11, X, F0 to F6 and S0 to S5,
where it said X to S2, and says that evaluateSecurity never throws; the
rows of the writers, lengths.ts, index.ts and testing/ say where the test
vectors come from now, the area of 32 KiB and the public note.
- README: the table of runtime dependencies says what noble does for the
signatures and the seals, and the guards list the importers of noble of
v0.11 and the new guards of testing/; the counts of the corpus of
mutations (210 cases, four that open) and of capsule-vectors.json are
those of today.
- security.test.ts no longer says that the library does not reach the
verdicts of v0.11, nor that its texts are those of spec-v0.10.
- CHANGELOG: an entry for the fixes of the review, and what waits for
v0.12: the reader of certificates (T2, T6, T7, T8), the text of an
issuer without a commonName and the test of cms.test.ts that compares it
with itself.
npm run verify passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes T10 of the review of the session of 1 and 2 October: no test checked
that Go opens what encryptFiles writes today, since capsule-vectors.json
came from c3c124a, with an area of 512 bytes and no note.
- capsule-ts-samples.mjs writes format 2 with encryptVectors of
testing/encrypt.ts, as a generator of test vectors, and format 3 with
encryptFiles, as any caller writes it: the six samples of before, now
with the area of 32 KiB, and two more with a public note, one out of
ASCII, and one of 1024 bytes in time_and_key beside another noncritical
extension of the header.
- capsule-go-verdicts.go records the size of the area that capsule.Open
gives, the note that Header.PublicNote reads, the text of capsule.Encrypt
for a public note in format 2, and the text of capsule.EncryptFiles for
nine public notes that break their rules.
- interop.test.ts requires Go to open each of the 21 samples with each
credential, to find the area of 32 KiB in format 3 and to read the note
written, and this library to read the same note; and the texts of the 22
options and of the 9 notes to be those of Go.
Go at spec-v0.11 (ae33434) opens the 21 samples with each credential and
with all of them, encodes their objects again byte for byte, and gives the
texts of this library for the 22 options and the 9 notes; the four mixes,
the 500 inputs of the encoder differential and the 22 recipients give what
they gave. npm run verify passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes T9, T11 and T12 of the review of the session of 1 and 2 October:
- T9: EncryptOptions no longer has testVectors nor areaLen, with which any
caller could write format 2, or an area of 512 bytes, which rule 13
forbids and which tells that the capsule has no signature (§55.2). What
only a generator of test vectors asks, as Go's TestVectors, is the
TestVectors argument of the core of writer.ts, which only the helpers of
testing/encrypt.ts pass: encryptVectors and encryptWith write format 2,
and encryptFilesWith another area, with which the tests still reproduce
byte for byte the fixtures of 512 bytes. encrypt keeps the shape of
capsule.Encrypt: without a generator it fails with the text of Go,
whatever the caller adds. dependencies.test.ts refuses an import of
testing/ from anything but the tests and testing/ itself, check-build.mjs
refuses a test or a module of testing/ in the bundle of the pages, and
note.ts joins the modules that index.ts must not re-export.
- T12: encrypt as a generator refuses a public note and an area with the
text of Go, "capsule: format 2 has no security area or public note: ...",
after the head and the length, as capsule.Encrypt. The errors of the note
carry "capsule: ", and newSealer checks the note, then the profile and the
clock, in the order of Go. The tests compare the texts byte for byte, also
for two faults at once.
- T11: capsuleLength takes the public note and predicts exactly the size of
the .dkc with it: eight notes of 1 to 1024 bytes, across the boundaries of
the heads of CBOR, with both policies and with other extensions.
The 40 texts that capsule.EncryptFiles and extension.CheckNote give at
spec-v0.11 on the same notes and options, taken with an oracle, are those
of this library; HEAD gave another one in 23 of them. npm run verify
passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes T4 and the rest of T5 of the review of the session of 1 and 2
October:
- note.ts: checkNote refuses a string with a lone surrogate, which UTF-8
cannot hold, after its length and with the text that extension.CheckNote
gives for invalid UTF-8. newNote wrote U+FFFD in its place, and the note
is public and permanent (spec §62.1 rules 15 and 23).
- note.ts: publicNote reads the data with decodeUtf8, which keeps a leading
U+FEFF: such a note is unusable, as in Go, where it showed without it.
- author.ts: authorCode takes the eight bytes of the code as Go's
AuthorCode takes them, a leading U+FEFF kept.
- inspector/drand.ts: an answer of a relay that starts with a BOM is
refused, as json.Unmarshal refuses it in the client of the reference.
- The texts of the head and the paths of format 3 already kept it, since
cbor.ts decodes them with decodeUtf8: head.test.ts now pins it with the
texts of Go. The other TextDecoder of src/lib, in age.ts, reads tokens of
ASCII that are checked byte by byte before.
The texts are those of extension.CheckNote, extension.Note,
capsule.DecodeHead and capsule.AuthorCode at spec-v0.11, taken with an
oracle on the same bytes; the drand client of the reference refuses the
answer with json.Unmarshal. npm run verify passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes T1, T3, T13 and the CMS part of T5 of the review of the session of
1 and 2 October (docs, spec_v0.11/revision_sesion_1_2_octubre.md):
- securitycms.ts: an issuer that breaks the rules of the declared author
shows the SHA-256 of the DER of its Name, Go's sha256.Sum256(RawIssuer),
and no longer that of the certificate (cms.certIssuerHash).
- cms.ts: an ECDSA key counts only with its point uncompressed, 0x04 and
the two coordinates, the only form that Go's x509.ParsePKIXPublicKey
reads: a compressed one makes a signer not verifiable (F5) and a seal S1.
- cms.ts: a UTF8String and the times of a certificate and of a token keep a
leading U+FEFF, as Go reads the bytes: such a name shows the hash, and
such a time breaks the profile (F1, S2).
- cms.ts: oidOf and intOf take time linear in the length of the element.
An arc of up to seven digits accumulates in a number, a longer one and
every INTEGER are read whole from hexadecimal, never by a shift per byte,
which took some 700 ms for 60 KB; attributes of one type are appended,
not copied.
- security.ts: evaluateSecurity never throws. A fault while it evaluates
the signature gives F1, one while it evaluates the seal S2, each apart,
and one while it decodes the area X, as the Go reference will from v0.12.
- Tests: securitycms.test.ts and security.failure.test.ts are new.
cms.test.ts now refuses a second content-type and two signature-time-
stamps for the rule of the count, with every SET OF in DER order, and
der.test.ts tests the depth at its boundary. cmsbuild.ts makes names,
validities and compressed points of its own.
capsule.EvaluateSecurityIn of the Go reference at spec-v0.11 gives the same
verdicts, signer lines and Spanish lines on 25 areas made with cmsbuild.ts:
issuers with ESC, U+202E, empty, of 300 bytes or with a leading U+FEFF;
names and times with a leading U+FEFF; and compressed keys on P-256, P-384
and P-521, as signers and as authorities of a seal. HEAD gave other ones in
19 of them. npm run verify passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
encryptFiles writes the security area of 32 KiB, as rule 13 of 62.1 asks of
a writer of v0.11, and accepts publicNote, the extension datekeys.note of
PUBLIC_HEADER, with the rules of text of 24.1 (note.ts). Another area is for
a generator of test vectors, with testVectors and areaLen, so that the tests
still reproduce byte for byte the fixtures that a writer of v0.10 wrote with
512 bytes. lengths.ts and the page plan L with the new area. A note changed
after writing fails at step 15. npm run verify passes.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
der.ts checks DER byte by byte. cms.ts reads the CMS signature and the RFC
3161 token of spec v0.11 29.10 and 29.11 with the closed table of
algorithms: RSA PKCS 1 and PSS with BigInt, ECDSA with the arithmetic of
@noble/curves, no new package. securitycms.ts gives F1, F2, F5 and F6 with
the signers named, and S1 to S5 with the authority of a valid seal.
evaluateSecurity returns them with their detail, and verdictLines writes the
lines of F6 and S4. The 22 cases of security_cms.json and the fixtures
format3_signed_cms and format3_sealed give the verdicts, the signers and the
seal of the Go reference. testing/cmsbuild.ts builds signatures and tokens
with WebCrypto for the hostile cases ported from the Go tests, and the
pending mechanism of the first sync is gone. npm run verify passes.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
ed25519strict.ts checks the four conditions of spec v0.11 29.9 on top of the
arithmetic of @noble/curves and gives the answer of Go on the 18 vectors of
ed25519_strict.json. author.ts computes payload_commit, control_commit,
head_digest, signers_digest, AUTHOR_MESSAGE and its code, as the record of
format3_signed says. evaluateSecurity takes the context of the capsule and
gives F2, F3 or F4; open passes it, and OpenOptions.authorKeys are the keys
the person saved. No new package: both modules use @noble/curves and
@noble/hashes, which were already in the bundle.
Alg 2 and the time seal are still read as v0.10 reads them, and the tests say
so with testing/pending.ts. npm run verify passes.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
SPEC_VERSION is 0.11. testdata brings format3_signed, format3_signed_cms and
format3_sealed, and the vectors ed25519_strict.json, security_cms.json and
locator.json; the mutation corpus has 210 cases. ibe-vectors.json adds the
three fixtures and remakes the two that Go regenerated, and
mutation-texts.json is made again with that reference.
This library still reads the security area as a reader of v0.10, so a
signature or a seal that the reference checks gives F1 or S1 here. The
Verdict type and the texts know F2 to F6 and S3 to S5, and the tests state
the gap with testing/pending.ts instead of hiding it; porting the
verification makes that file the identity. npm run verify and
testdata:check pass.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
As the spec v0.11 draft decides after its review (38.1), and as the Go
reference does from 2213b8c:
- dkc/wordkey.ts replaces inspector/wordkey.ts. The salt carries the
capsule_id too, so the same words give another key in each capsule;
the words are lowered with the new LOWERCASE table of pathrule.ts,
loaded on demand; checkWords refuses controls, invisible and
unassigned code points and counts only different words of three
letters or more, with the errors of Go. New vector of 38.1.
- encryptFiles takes the words and derives their key once it has drawn
capsule_id; the creator passes them, and the opener salts them with
the capsule_id of the capsule.
- /create asks for the words twice and shows how they are kept; the form
checks them with the tables of the platform, and the writer again with
those of Unicode 18.0.0.
- The tables, regenerated with the lower case, and testdata synced from
2213b8c; the frozen texts of Go for the invalid options, updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
normalizeWords lowers each code point on its own, as Go's simple
mapping does (no final sigma), and splits at the white space of Go's
unicode.IsSpace, so that the page and wordkey.Normalize of datekeys-go
give the same words; both test the same PBKDF2 vector.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author wanted a key that people can keep without files. A capsule
"solo con una llave" can now take words the person chooses, at least
six, on /create, and /inspect opens it with them. wordkey.ts derives
the X25519 identity with PBKDF2-SHA256 of Web Crypto, 600 000 rounds,
salted with the chain and the round of the capsule, after making case,
accents and extra spaces not matter; its public key is one more
recipient, so the format does not change. The writer wipes the private
half at once; the opener adds it to the identities it tries.
Checked in Chromium: a capsule created with "Perro luna casa verde
trén mar" and no .dkk opened with "perro LUNA casa verde tren mar",
with the release fetched from drand by the page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author found copying the release of the round tedious. A button,
"Pedir la firma a drand", fetches it from the three public relays of
the CLI of the reference, as its client does: raced, 6 s, at most
8 KiB an answer, no redirects, and the randomness checked against the
signature; step 10 still verifies the signature with the pinned key,
so a relay cannot make the page accept a false one. It is the only
connection the page makes to another site, and only on that click: the
CSP allows those three origins in connect-src, check-build.mjs
requires exactly them, and the footer says so. Pasting by hand still
works. Checked in Chromium: api2.drand.sh gave the release of round
32668196 and the capsule opened.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The drop zone stretched to the height of the list of test capsules
beside it, with its words lost in the middle of the screen, and that
list, developer material in English, filled the page. Now the page
asks for the capsule in one column, with a drop zone of its own size,
and the examples are folded under "Probar con una cápsula de ejemplo".
The menu says "Abrir", and the fields of a capsule that needs a key
speak of the key and of the secret key of age, as /create does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author found the pages too technical and did not want a dark
background. With the frontend design guidance:
- The site is always light, whatever the system asks.
- A capsule is a letter posted to the future: white paper, blue ink,
an old-style serif for the voice of the page (Palatino, Iowan) and a
DIN-like sans for the form (Bahnschrift), all system fonts, as the
CSP allows no other origin. Blue is what you act on.
- /create asks three questions, with short sentences: what it keeps,
when it opens, who can open it. The date it opens is set large on an
airmail envelope with the button that creates the capsule; quick
dates of 1, 5 and 10 years; sizes in KB and MB; everything technical
folded in "Detalles técnicos". The messages of the rules no longer
name R4 or §29.6.
Fixes of the review of the pages:
- /inspect labels the start of a single file as content of the
creator, unchecked, so that it cannot pass for a verdict; keeps ZWNJ
and ZWJ in the comment and the author, which R4b allows; and says
the right thing without files and without a comment.
- /create pins at most 500 more rows with problems and counts the
rest; shows what happened with a drop, not only to screen readers;
a comment or an author over its limit is its own problem, not that
of the files; a writing cannot start after the page is destroyed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reader:
- PAYLOAD_AGE reaches age in pieces of one STREAM chunk, read only when
asked (agefile.chunked): a whole payload was decrypted ahead of step
17, with more memory, and a later STREAM failure hid the failure of
the reader that Go reports. The text still differs from Go when the
capsule is cut right after a full chunk: age-encryption holds that
chunk until it sees one more byte.
- The unusable head extensions are found before the commit, so the
sink is never aborted after it; a registry whose validateData throws
rejects the data; getWriter of a created file is inside the failure
of the sink; the sink gets copies, which it may keep or transfer.
Writer:
- Each piece of a file is copied as it is read: a source that reuses
its buffer could make the head record a SHA-256 that is not that of
the bytes encrypted, and the capsule would not open.
- A FileSource written as a class works: open is called on its object,
and each property is read once. Extensions of the wrong types are a
TypeError. In memory, the limit is checked before reading anything.
A chunk that is not a Uint8Array, and a lone surrogate in a path, are
named.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README: format 3 in the table of contents and in the current
version, and the coverage thresholds as vitest.config.ts has them,
prefix.ts and the modules of the page included.
- /create: each path is a textarea of one row that grows with its
content where the browser can size it, so that a long path shows
its end, the file name, on a phone of 375 px too. Enter adds no line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tests of create-files.ts, format.ts, opening.ts and create-input.ts
held literal code points that a reader cannot tell apart, or cannot
see: U+212A KELVIN SIGN and U+017F LONG S, fullwidth and dotted I,
ZWJ, a combining acute, U+202E and U+FEFF, and equalFold compared with
two of them in its code. They are now \uXXXX escapes, with the same
values; no source in src or scripts holds a format character.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page seals files and folders, chosen or dropped, with a comment
and a declared author, into a capsule of format 3.
- create-files.ts, with the first load: the files as the person chose
them, a dropped folder walked with webkitGetAsEntry and named first
in each path as webkitRelativePath does, the files of a system left
out of folders as collect.go leaves them out (strings.EqualFold for
.DS_Store, Thumbs.db and desktop.ini, ._* and __MACOSX), and the
list of editable paths.
- create-check.ts, on demand with the tables: every problem of every
path, and of the comment and the author, in Spanish, from the
violations of pathrule.ts. A property test holds that the page sees
no problem exactly when checkPath and checkTree accept the paths.
- lengths.ts: measureFiles, headLengthOf and bodyLengthOf, so that
the exact size is planned again without sorting the files again.
- creator.ts: several files, the comment and the author, the progress
of both readings of encryptFiles, the cancellation in the first
one, and the room checked before reading anything.
Checked in Chromium: Go's datekeys decrypt and /inspect open a capsule
that the page wrote, with its six files, their mtimes, the author and
the comment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page opens capsules of format 3. The files go to the temporary
file through a ZipSink, a lone file of one segment as it is and a ZIP
otherwise, or to memory; the page shows the verdicts first, then the
declared author and the comment as unchecked text of the creator, and
then each path as text in a bdi, with its size, its mtime and the
warnings of the CLI of the reference, compared by their key of R7.
- files.ts: pathWarnings, fileFacts, and the names and order of the
downloads: the file itself when it is the only one, with the ZIP of
its folder second (decision 8), or the ZIP and each file.
- opener.ts: OpenedFiles, and noRoom when the ZIP does not fit.
- zipsink.ts: NoRoom, thrown by begin before writing anything.
- check-build.mjs: the tables of pathrule-tables.ts never come with
the first load of a page, and do come with the code on demand of
/inspect and /create.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zipsink.ts is the sink of the page for a format 3 capsule (design of
format 3, section 5). Its files go to the temporary file of OPFS: the
file itself when the capsule holds one file of one segment, and
otherwise a ZIP of stored entries laid out from the head before any
byte arrives. Each local header is written at its offset, the bytes of
the file follow as open delivers them, the CRC-32 of the entry is
patched in its header with a positioned write, and commit writes the
central directory and closes the file; abort discards it, also when
the opening failed before begin. Each file is a contiguous range of the
file written (ranges), and zipOf makes the same ZIP in memory as a Blob
of its parts.
tempfile.ts: the writable of a temporary file takes TempChunk, bytes at
the position of the file or at a given one, as
FileSystemWritableFileStream does; cancellable is generic.
Interoperability: scripts/zip-ts-samples.mjs writes the samples of
testing/zip.ts with ZipSink, and scripts/zip-go-read.go reads them with
archive/zip of the Go standard library: names out of ASCII with bit 11,
stored entries, their CRC-32 and sizes, the times of the extra fields
in 1970, at 2^31 - 1 and after it, in 9999 and the time of the round for
a file without one, and 65535 entries, ZIP64 by their number. The
reading is frozen in testing/zip-vectors.json, and zipsink.test.ts
writes each sample again, requires its SHA-256 and computes the entries
Go must have read. zipsink.ts is covered at 100 %.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
capsule-vectors.json is written again with six capsules of format 3
from encryptFiles, next to the thirteen of format 2:
- one file with its mtime; a tree of seven files, one of them over two
STREAM chunks, with paths out of ASCII and the pair U+FFFD and
U+10000, which UTF-8 and UTF-16 order the other way round, a comment
and a declared author; a comment and no file; bloque256; time_and_key
with three recipients and a portable key; and head extensions.
- capsule-go-verdicts.go opens them with capsule.Open into a Sink, with
each credential alone and with all of them, and records the files it
receives with their SHA-256, the head encoded again with
capsule.EncodeHead and the verdicts of the security area. It decodes
the control of each capsule in the format of its prelude, and runs
capsule.Encrypt on the invalid options as a generator of test vectors.
- interop.test.ts requires Go to find the files, the head and the
verdicts written, and open, into a MemorySink, to reach the same
verdicts on the same bytes.
The format 2 samples, the mixes, the encoder differential (500 equal),
the recipients and the 21 option errors are regenerated too, with the
same verdicts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec 62.1 rule 1: a writer writes format 3, and only a generator of test
vectors may write format 2. As capsule.Encrypt at spec-v0.10, encrypt now
fails without EncryptOptions.testVectors, and with a comment, a declared
author or head extensions, which format 2 has no place for, with the
texts of the reference, before anything else is checked. The tests of
the writer, encryptWith, the interoperability cases and the sample
script ask for it.
The create page writes format 3 with encryptFiles: the chosen file goes
under its name, which is its path in the capsule, with its modification
time, both sealed in the head. The plan carries the file as encryptFiles
takes it, and its size is exact again with bodyLength. A name that
breaks a rule of the paths makes the writing fail with the text of the
rule; several files, folders, editable paths, the comment and the
author come with step 7.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
encryptFiles(files, opts) writes a .dkc of format 3 as
capsule.EncryptFiles at spec-v0.10, on the sealer of the previous
commit:
- newHead: the comment, with CR LF and a lone CR turned into LF, the
declared author and every path checked with the rules of the reader,
in the words of a writer (spec 62.1 rule 15); the files in the byte
order of their paths, not the UTF-16 order of JavaScript strings; and
the mtime in seconds from 1970 to 9999, none outside.
- L measured with a head whose salt and SHA-256 are zero, at most 16 MiB
and L_MAX; the first reading hashes each file; the head with a fresh
salt, the empty security area and the frame are checked with the rules
of the reader before anything is written.
- BODY is the content of seal: the frame, the area, the head and the
files read a second time, which fail if a size or a SHA-256 changed
(rule 18), with the texts of readSource.
FileSource describes a file (path, size, mtime in milliseconds, open),
and fileSource makes the one of a File or a Blob. The draws gain the
salt of the head. lengths.ts gains bodyLength and headLength, which
measure the head from the sizes of its CBOR items without the Unicode
tables, and mtimeSeconds and headComment, which the writer shares.
Tests: the five fixtures that EncryptFiles wrote are reproduced byte for
byte, PRELUDE, PUBLIC_HEADER, CONTROL_CBOR, HEAD_CBOR and BODY, and
their .dkk; capsuleLength with bodyLength gives the size written, and
headLength agrees with encodeHead on 300 random heads around every
boundary of the CBOR heads; the invalid inputs give the texts that
capsule.EncryptFiles gives to the same inputs, taken from the reference
with a scratch program. writer.ts, encrypt.ts and lengths.ts stay at
100 %.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
writer.ts follows the sealer of capsule/encrypt.go at spec-v0.10, so that
the writer of format 3 can share everything but its content:
- newSealer: the copies of the options, the profile, the clock, the
padding rule with a first L, the DateKey and the credentials, in the
order and with the texts they had;
- seal: steps 7 to 19 for a format and a content of L bytes given in
pieces, which plaintextStream follows with the zeros of the padding;
- sourceContent: the content of format 2, with the texts of the source
that ends early or delivers more than L.
No behaviour changes: the 175 tests of the writer pass unchanged, and
writer.ts stays covered at 100 %.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Syncs testdata with datekeys-go at the tag spec-v0.10 (cc35d2c) and
moves the reader to the DateKeys Protocol Specification v0.10. The
three capsule formats are read.
- framing: FORMAT_3, and isPadded for formats 2 and 3. control: schema
version 3, with the keys 6 and 7 of version 2. SPEC_VERSION is 0.10.
- open: OpenOptions.sink receives the files of a format 3 capsule
(sink.ts: Sink with begin, create, commit and abort, as capsule.Sink,
and MemorySink). Without one, open rejects with a TypeError right
after step 2, before any request, as ErrSinkRequired. Opened gains
head, verdicts, areaLen and unusableHeadExtensions.
- open3.ts: step 17 of format 3 in its substeps 17.2 to 17.8, as
openBody of the reference: a failure of age or a plaintext whose
length is not P prevails, the first failing substep decides, and the
codes other than ERR_INTEGRITY are reported only after reading
PAYLOAD_AGE to its end. Reads grow with the bytes received, never
with the lengths BODY declares. A failure of the sink is ERR_INTEGRITY
with its text, and the sink is aborted once after begin.
- The page: opener.ts opens the fixtures of format 3 into a
MemorySink; the open panel says that it does not deliver their files
yet, and the glosses of the steps name format 3. check-build.mjs
refuses to ship the heads, salts, comments and paths of the format 3
fixtures.
Tests: the 21 fixtures, format 3 laid out byte by byte from its record
and opened into a sink with its files and verdicts; the 209 cases of
the corpus from memory and from a Blob, with the code, the step and,
new, the exact text of capsule.Open, frozen by
scripts/mutation-go-texts.go in testing/mutation-texts.json, which
replays the corpus as internal/testkit does (its extension validator
texts included); the 5110 differential cases over 14 bases; paths,
path_fold, head_schema and security vectors; the control of schema
version 3 in cbor.json; and step 17 on crafted plaintexts sealed again
to I_PAYLOAD, whose texts capsule.Open gives on the same plaintexts.
ibe-vectors.json gains the nine format 3 fixtures from
scripts/ibe-go-vectors.go; the twelve before are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
body.ts, security.ts and head.ts port format3.go of the Go reference at
spec-v0.10:
- body.ts: the frame of BODY (spec 29.2), AREA_LEN, SECURITY_LEN and
HEAD_LEN with their limits, and the zeros of the security area, all
ERR_INTEGRITY.
- security.ts: the empty SECURITY_CBOR that writers write, and the
verdicts X, F0, F1, S0, S1 and S2 of spec 29.7 with the Spanish lines
of the official SDK. The evaluation never fails and carries no code.
- head.ts: HEAD_CBOR in the layers of spec 69.1: R1 and R8 in the CDDL,
R8 by UTF-8 bytes and not by the UTF-16 order of JavaScript strings;
then the comment, the declared author, the paths with pathrule.ts, the
layout of the files, R7 and R9, all ERR_HEAD_INVALID; and the critical
extensions of the new extension object "head". decodeWrittenHead
leaves the extensions to the caller, for the self-check of the writer.
ERR_HEAD_INVALID becomes the 19th normative code, with its gloss.
Tests: the cases of format3_test.go and a few more, with the error
texts and the verdicts of the reference byte for byte, taken from it at
spec-v0.10 with a scratch program. Coverage 100 %.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page delivers the files of a format 3 capsule in a ZIP of its own
(design of format 3, section 5): stored entries with UTF-8 names, no
data descriptors and no entries for folders. The head gives every size
before any byte arrives, so the layout is known in advance and each file
is a contiguous range of the ZIP, offered as a download of its own. The
CRC-32 of an entry is patched in its local header after its bytes.
Times go in DOS, in UTC and clipped to 1980-2107, in the NTFS extra
field, which governs, and in the extended timestamp when they fit in 32
signed bits. ZIP64 applies by the size or offset of an entry and by the
number of entries or the size of the central directory.
Both modules live in src/lib/inspector: the reference has no ZIP, only
the page. Coverage 100 %.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pathrule-tables.ts holds the fixed Unicode 18.0.0 and WindowsBestFit
tables of spec §29.5.1, written by internal/pathrule/gen -ts of
datekeys-go (13910b3) from the same pinned files as the Go tables.
pathrule.ts ports internal/pathrule at spec-v0.10: NFD, the case
folding and the key of R7, the rules R1 to R10 with R4b, R6b, R6c and
R9, and the text rules of the comment and the declared author (§29.6).
It never uses normalize, toLowerCase, localeCompare, Intl or the
Unicode property classes of regular expressions, whose version of
Unicode changes with the engine. The limits count UTF-8 bytes, and every
error text is the one of Go, byte for byte.
Tests: the cases of the Go tests with their exact messages, and the
SHA-256 of the canonical text of the tables, recomputed in TypeScript,
against TABLES_DIGEST. Coverage 100 %.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a capsule opens, /inspect now shows its content right under the
verdict, before steps 9 to 18. A capsule does not keep the name of the
file it seals (spec §6, §55.2), and the capsula-<date>.dkc of /create
has no extension of its own, so the download was nameless for the
system: contentExtension now gives it .txt for a text, or the
extension of a common type of file by its first bytes (.pdf, .png,
.jpg, .zip…).
vite preview served the pages without Cache-Control, and a tab
reloaded after a build could keep the old page, whose chunks are gone;
a small plugin, before SvelteKit's, has the pages revalidated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>