datekeys-ts 0.5.0 implements spec v0.16 (tag spec-v0.16 of datekeys-go):
a seal without accuracy proves nothing before the opening date, and
drand's JSON is read strictly; it also draws the random words of a key of
words, from a computer or from dice, and says what the official SDK says
when it seals, besides everything 0.4.0 does.
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 the README of testdata; the README and the CHANGELOG
name the approved version.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3fd0e93 restores the escapes of release.json that 4f78854 had lost: the
cases "round escaped as round" and "round twice, once escaped as
round", and the surrogate pair of "a surrogate pair in a value". The
annex changes with the SHA-256 of the draft. The comment of the strict
reader had lost the same escape.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The draft v0.16 of datekeys-go at 4f78854 (branch v0.16): SPEC_VERSION
0.16, and testdata, wordlists and annex synced from that commit. The
annex is §79 of the draft, with the CC BY-ND 4.0 license of the
specification in its title and the key of words in 79.7.
A seal without accuracy proves nothing before the opening date (§29.7,
§29.11, as 7e3b810): 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 (0.4.0.2023.1.1), or no accuracy. cms.ts reads
hasAccuracy and the policy of the token (tokenIsBTSP); securitycms.ts
gives SealReason, Detail.sealReason and SignerLine.reason; S5 has no
fixed text any more, and verdictLines writes it and the line of a signer
of F6 with the reason, the texts of Go byte for byte (sealReasonText).
encryptFiles returns the verdicts of the area it wrote in
Encrypted.security, as Result.Security of Go, so that a writer warns of
a seal without accuracy (§62.1 rule 19).
drand's JSON is read strictly (§47.1, as b570338): parseDrandJSON, as
ParseDrandJSON of Go, reads RFC 8259 JSON in valid UTF-8 whose value is
an object, with no name repeated in any object, names compared exactly
once their escapes are decoded, a lone escaped surrogate malformed, the
round a number without sign, fraction or exponent from 1 to 2^53 - 1,
and signature and randomness strings, with the error texts of Go.
ParsedRelease is now a Release: no round above 2^53 - 1 is read. The
page reads the answers of the relays with it (drand.ts), as the client
of Go does, and the pasted release with strictJSON and jsonRound
(release-input.ts), so that it never reads another round than step 10.
Tests: security_cms.json with seal_reason (143 cases), the 38 JSON
inputs of release.json, the new cases of signature2_test.go and
drandjson_test.go (with the escapes written as escapes), and the new
fixtures: format3_time_and_key_words opens with the identity that the
words of its words_text give with normalizeWords and wordKey, in the
library and in the page, and format3_full_chunk, whose PAYLOAD_AGE ends
in a full STREAM chunk, opens. check-build.mjs counts words_text among
the secrets of the fixtures.
Reference files made again with Go at 4f78854: mutation-texts.json (its
spec field only), ibe-vectors.json (the two new fixtures, the rest
unchanged) and signing-vectors.json, in an export of 4f78854 with the
same frozen samples read again: the capsules are the same, and the
tokens of the sealer, without accuracy, now give S5.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As aefc8f6 of datekeys-go. sync-testdata.mjs also vendors annex/ of Go,
the recovery annex (§79 under a title with the version and the SHA-256 of
the specification), and testdata, wordlists and annex are at aefc8f6.
/create recommends the key to a time_only capsule more than a year ahead
(§7.6), and once the capsule is made it says what opening it later will
take: the capsule, one of its keys if it has them, and the signature of
drand for its round, which an archive or a cache service must keep if
drand no longer serves it (§62.1, rule 26); and it offers the annex for
download as <capsule>.recuperacion.txt (rule 27, annex.ts). check-build.mjs
wants the annex shipped byte for byte.
profile.ts gains ProfileStatus, PROFILE_STATUS and profileStatusOf, as
StatusOf of Go: Quicknet is active. planCapsule writes no capsule with a
profile that is not, and /inspect warns when the profile of a capsule is
compromised (buildReport takes profileStatus).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For whoever does not trust the random numbers of a computer, as dice.go of
datekeys-go at 92e7154, with its texts: five dice for each word give a
number from 11111 to 66666, the position of the word in a list of 7776.
wordlist.ts gains DICE_LIST_SIZE, diceNumber, diceWord, diceWords and
diceList, the list numbered as the EFF publishes its own, and wordkey.ts
goFields, which splits at white space as strings.Fields of Go.
/create offers "Con dados" between the random words and the person's own:
it turns the numbers into words as they are typed, tells a number that is
not five dice or that gives a word again, and offers the list numbered for
dice, to print it, with its SHA-256. planCapsule takes dice and diceList,
and readDice reads the numbers one by one. The list loads once for random
words and dice, and an effect that finds it loaded writes no state, so it
does not run again without end.
testdata and wordlists at 92e7154, whose README of the lists records the
SHA-256 of each list numbered for dice.
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. WORD_LIST_SHA256 pins it, and
the alphabet of en is a to z and the hyphen of its four compound words.
/create still offers the Spanish list, and check-build.mjs wants the site
to ship that one alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the policy with a key, a check box opens the capsule with words too,
random by default: the page fetches the Spanish list of datekeys-go from
its own site (create-words.ts, the hashed file that Vite ships), takes it
only with its pinned SHA-256, and draws 7 words that never leave the
browser. It shows them numbered, since they are typed in that order, with
their strength computed from the list loaded and a button to draw others.
"Las elijo yo" lets the person type their own, with the warning that they
are weaker. Either way they are written again; case and accents do not
matter. planCapsule takes wordsKind (none, random or own) and asks for the
words that are missing.
licenses.txt carries the README of wordlists/, with the source, the method
and the license of the list (CC BY-SA 4.0), and check-build.mjs wants it,
and the list shipped byte for byte.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wordlist.ts does what wordkey.Generate, CheckList and Bits of Go do at
27a75ee, with their texts: generateWords draws different words, 7 by
default, with crypto.getRandomValues; wordBits is their strength;
checkWordList refuses a list of fewer than 2048 words, with two words that
are one once normalized or with a character outside the alphabet of its
language, which the code gives and not the list. readWordList takes a list
only with the SHA-256 pinned for its language, in UTF-8 and accepted by
checkWordList: no list is trusted, not even those of DateKeys.
wordkey.ts gains wordRules, which loads the Unicode tables once for a
caller that reads many words; normalizeWords and checkWords use it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
datekeys-ts 0.4.0 implements spec v0.15 (tag spec-v0.15 of datekeys-go):
the release object, release archives and a release in hand that the clock
does not stop, besides everything 0.3.0 does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The versions table and the current version, releaseobject.ts and the
changes of release.ts and open.ts, the page with the release from a file
and a clock behind, the tests of release.json, releases/ and the source of
the mutation corpus, and the testdata at 3c3e737; a CHANGELOG section
"0.4.0 — sin publicar".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
- 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 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>
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>
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>
/inspect showed the plaintext only of the official fixtures; a capsule
of one's own was only offered for download. Now the start of the
plaintext is shown whenever the capsule opens and it is text:
opener.ts reads its first 128 KiB (PREVIEW_BYTES), from memory or from
the temporary file, and plaintextPreview in opening.ts shows up to
100 000 characters of printable UTF-8, cut on a whole character. A text
written on Windows shows too: CR LF as a line feed, no BOM. The
download keeps the exact bytes.
The pages now explain age keys: /create, in a folding block, what an
age1… recipient is and how to get one with age-keygen; /inspect, next
to the identities, which line of the age-keygen file to paste.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/create seals a person's file into a .dkc of format 2 and, when asked, a
portable .dkk, in the browser and without network, with the decisions the
author confirmed in step 6: time_only by default, the zone of the device
with a selector, the warnings of §53 and §50 beyond 365 days, a notice of
preliminary protocol, and files named capsula-<opening time, UTC>.
- lengths.ts: sealedControlLength moves out of writer.ts, and
capsuleLength gives the size of the .dkc before writing it; the
property loop checks it on every capsule (500 seeds pass).
- datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon.
- src/lib/inspector: localtime.ts (local times of a zone as UTC instants,
a skipped time refused, a repeated one taken at its later instant),
create-input.ts (the form, checked in its order) and creator.ts (the
writing, loaded on demand), all at 100 %.
- The .dkc goes to an OPFS temporary file, committed only when complete
and checked, or to memory up to 64 MiB; the writing can be cancelled.
The .dkk stays in memory only; losing it when it is the only
credential asks for confirmation.
- /inspect also cleans the temporary files of /create, and check-build
checks the code loaded on demand of both pages.
Checked in the browser on the production build: a capsule made for four
minutes later opened afterwards in /inspect with the pasted release and
with datekeys decrypt of Go over the network, to the same content. An
adversarial review found one major and eight minor issues, all fixed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/capsule-ts-samples.mjs writes thirteen capsules of format 2 with
encrypt (both policies and padding rules, 0 to 16 credentials, chunk
borders, extensions in the three objects, an instant with nanoseconds),
four mixes of two capsules, the inputs of an encoder differential drawn
from a seed, a corpus of recipient strings and the invalid options that
Go also rejects. scripts/capsule-go-verdicts.go gives the verdicts of the
reference on them:
- capsule.Inspect, and capsule.Open with each credential alone and all
together: every sample opens to its content, with format 2 and the L,
padding rule and P requested;
- SEALED_CONTROL opened layer by layer with the agewrap identities, 16
stanzas in INNER_ACCESS_AGE, and PUBLIC_HEADER, CONTROL_CBOR and the
.dkk encoded again to the same bytes;
- the code and step of each mix;
- EncodeHeader, EncodeControl (format 2) and MarshalBody equal on all 500
encoder inputs;
- the texts of age.ParseX25519Recipient, agewrap.CheckX25519Recipient and
capsule.Encrypt, equal to those of this library.
The capsules are random, so the output is frozen in
src/lib/dkc/testing/capsule-vectors.json, and interop.test.ts checks the
verdicts of Go and that open reaches the same ones on the frozen bytes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
encrypt(src, opts) writes a .dkc of format 2 and, when asked, a portable
.dkk (spec §61, §62, §62.1), in the order and with the texts and codes
of capsule.Encrypt:
- L known in advance: the size of a Uint8Array or a Blob, or the length
declared with a ReadableStream; a source of another length fails with
Go's texts;
- reforzado padding by default, or bloque256;
- 1 to 16 credentials, canonical and not of low order, a dummy in each
slot left, whose scalar is wiped once its public key is derived, and
a uniform order of the 16;
- SEALED_CONTROL_LEN from the formula of §62.1, checked against the
real seal;
- the self-checks of rule 11, plus OUTER_TIME_AGE under the reader's
rules and the header of PAYLOAD_AGE opened by I_PAYLOAD before
anything is written.
The content is streamed in pieces of 64 KiB, then the zeros of the
padding, into memory (up to MAX_MEMORY_DKC) or an output that is closed
only once the capsule is complete and checked and aborted on any
failure. The core in writer.ts takes its random values from the caller:
encrypt.ts passes crypto.getRandomValues, and only testing/encrypt.ts
fixes them.
Tests: the deterministic sections of the seven format 2 fixtures of Go
byte for byte; round trips with open for both policies, 1 to 16
credentials and every padding boundary; the invalid options; streaming
and failures of the source and the output; the internal errors with
age-encryption replaced by a spy; a property loop (50 seeds per run,
500 by hand).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- recipient.ts: age1… recipients as age 1.3.2 reads and writes them, the
rules of spec §37 with the texts of agewrap.CheckX25519Recipient (the
five low-order u checked by list, the twist accepted as in Go), and a
recipient list read line by line. No noble.
- random.ts: an index without bias and the Fisher-Yates permutation of
the 16 slots, with Go's uniformity test.
- agefile.ts: the whole-age-file helpers of the opening, shared with the
writer's self-checks.
- x25519.ts: newX25519Identity and x25519PublicKey (RFC 7748 vectors);
digest.ts: sha256Hasher; datekey.ts: compareInstants, used by open.ts,
and isInstant.
- tempfile.ts: an area for the opening and one for creating capsules.
- Guards: age-encryption and the writer core have import allowlists, and
index.ts re-exports neither the opening nor the writer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
datekeys-ts 0.1.0 inspects and opens DateKeys capsules of both formats
of spec v0.9 (tag spec-v0.9 of datekeys-go), in memory or streaming,
with the /inspect page. The writer of phase 3 comes with 0.2.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the
reader to the DateKeys Protocol Specification v0.9. Both capsule formats
are read; a format 1 capsule keeps the verdict v0.8.2 gave it.
- framing: the VERSION of the prelude is the capsule format, 1 or 2
(Prelude.format, FORMAT_1, FORMAT_2, isFormat).
- control: decodeControl and encodeControl take the format; schema
version 2 adds payload_length (8 bytes, at most L_MAX) and padding
(1 or 2).
- padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up
to L_MAX with BigInt bit lengths and ceil roundings, and the length of
PAYLOAD_AGE.
- open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P
at step 16, and at step 17 a plaintext of exactly P bytes whose
padding is zero; only the first L bytes are delivered, never the
padding. Step 17 is recorded when it passes, and step 18 gives the
bytes of content, as the reference does. Opened reports the format, L
and, in format 2, the rule and P.
- inspect: the JSON view carries format, as datekeys inspect -json.
- The page shows the format, warns about format 1, and gives the
padding rule and P once a format 2 capsule opens.
Tests: the twelve fixtures, the 125 mutation cases through open from
memory and from a Blob, the 4380 differential cases, padding.json, the
format 2 CBOR vectors, and padding.test.ts against a BigInt statement of
§29.1. The error texts of the 125 corpus cases were compared with
capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2
fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>