v0.10
main
v0.5.0
v0.4.0
v0.3.0
v0.2.0
v0.1.0
${ noResults }
17 Commits (4377a87f7ff230ffbe7ebcface2a57e1d1352edf)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
4377a87f7f |
The writers as Go: test vectors only through testing/, and the note
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> |
6 days ago |
|
|
2ec7102f1b |
/inspect asks drand for the release when the person asks
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> |
1 week ago |
|
|
651178a740 |
Format 3, step 6: /inspect opens format 3
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> |
1 week ago |
|
|
ee82f4382f |
Format 3, step 5: the ZIP sink of the page, which archive/zip reads
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> |
1 week ago |
|
|
2efc8bcda8 |
Format 3, step 4: Go opens what encryptFiles writes
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> |
1 week ago |
|
|
c879b104d2 |
Format 3, step 4: format 2 only for test vectors, and /create writes format 3
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> |
1 week ago |
|
|
b176ad2f17 |
Format 3, step 3: read capsule format 3 of spec v0.10
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> |
1 week ago |
|
|
3a9d2b11fe |
Phase 3, steps 6 and 7: the create page
/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> |
1 week ago |
|
|
da0b39ff8c |
Phase 3, step 5: interoperability with Go at the level of the capsule
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> |
1 week ago |
|
|
48d6704b4b |
Phase 2, step 8: the open action of /inspect
After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
66970cf82b |
Phase 2, step 7: tlock encryption, checked against Go both ways
- ibe.ts gains encryptOnG2RFC9380, EncryptCCAonG2 of kyber with the suite of tlock for Quicknet. Qid is H(id) on G1 with the RFC 9380 DST, sigma comes from crypto.getRandomValues, U = r·G2, V = sigma XOR H2(e(Qid, key)^r) and W = msg XOR H4(sigma). The key passes the canonical gate, and sigma and the masks are wiped. encryptOnG2WithSigma takes a given sigma, for the vectors only; index.ts exports neither. - tlock.ts adds timeRecipient, the age-encryption Recipient of OUTER_TIME_AGE, as Go's agewrap.TimeRecipient. It writes the stanza "tlock <round> <chain hash>" with the checks and texts of NewTimeRecipient: the scheme and the pinned key, then the round range. age-encryption has no labels, so the writer of phase 3 adds it alone. Vectors, in src/lib/dkc/testing/tlock-vectors.json from scripts/tlock-go-vectors.go: - Fixed-sigma encryptions of 1, 16 and 32 bytes for rounds 1000 and 1001. Go restates EncryptCCAonG2, since kyber draws sigma itself, and checks the restatement with ibe.DecryptCCAonG2 and tlock.TimeUnlock. encryptOnG2WithSigma reproduces them byte for byte. - The samples of scripts/tlock-ts-samples.mjs, which Node runs on the TypeScript sources: IBE bodies and age files that this library made for rounds 1000 and 1001. Go opened every one: the bodies with tlock.TimeUnlock and the age files with age.Decrypt and agewrap.NewTimeIdentity, the identity of step 11. It got the same file keys and plaintexts, and the samples are frozen with those verdicts. tlock.test.ts replays both blocks, the random round trip, the rejections with their texts, and an age file sealed with timeRecipient and opened with the step-11 identity of open.ts. Coverage of ibe.ts and tlock.ts is 100 %, now a threshold for tlock.ts too. Step 6 of the plan is recorded as done: the canonicality amendment is in spec-v0.8.2. npm run verify is green: 2,567 tests. The site does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
35be27cf92 |
Phase 2, step 3: the tlock IBE on noble 2, checked against Go
src/lib/dkc/ibe.ts is DecryptCCAonG2 of drand/kyber encrypt/ibe, the decryption of tlock.TimeUnlock for Quicknet, on @noble/curves 2.4.0 (plan of phase 2, section 4). It adds nothing that kyber would reject: - the signature and U pass the canonical-encoding gate of bls12381.ts, which rejects the point at infinity too; - H2 hashes GT in the order of kilic, never with noble's Fp12.toBytes; - H3 and H4 follow kyber, including the rejection sampling of r, and r = 0 never proves; - roundIdentity is drand's DigestBeacon; - the stanza body is exactly U || V || W, 128 bytes, as in tlock. Errors are IbeError with a fixed reason (length, encoding, identity, proof) and message: none carries sigma, the message, r or input bytes. Anything noble throws past the gate is a proof failure. sigma and the hashes derived from it are wiped on every path. The file keeps the MIT notice of tlock-js, whose structure it follows. index.ts does not re-export it yet; the opening of step 5 will use it. scripts/ibe-go-vectors.go writes src/lib/dkc/testing/ibe-vectors.json with kyber, tlock and age: - the GT of e(G1, G2) and of its square, with H2; - H3, including inputs accepted at the second and third iteration, and H4; - round identities; - for the tlock stanza of every official fixture, the pairing, sigma, r and the file key. tlock.TimeUnlock unwraps that file key, and age opens OUTER_TIME_AGE with it; - messages of 0, 1, 16 and 32 bytes encrypted by EncryptCCAonG2; - kyber's verdict on eleven edited copies of the time_only stanza. It restates the unexported H2, H3 and H4 and checks them on every fixture against tlock and U = r·G2. ibe.test.ts replays every vector and opens OUTER_TIME_AGE of each fixture through age-encryption with a custom Identity: the header MAC and STREAM verify, and a time_only fixture yields its control_cbor. It also gates all 157 BLS edge encodings as the Go reference decodes them and checks the fixed error texts. ibe.failure.test.ts mocks a noble failure. Coverage of ibe.ts is 100 % and is now a threshold. The site does not change. npm run verify is green: 2,397 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
74e1215ee1 |
Phase 2, step 2: runtime dependencies and their guards
- age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and @noble/hashes 2.4.0 become exact runtime dependencies (plan section 3, decision 5). The lockfile gains six packages: age-encryption, @noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports them yet, so the site does not change. - src/lib/dependencies.test.ts guards them. package.json declares exactly these three, pinned. The lockfile has no tlock-js, drand-client or noble 1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under @noble/post-quantum. No file of src/ imports tlock-js or drand-client. Only ibe.ts, release.ts and the tests name @noble/, always subpaths of @noble/curves or @noble/hashes that resolve to the root 2.4.0 copy. Every check also runs on bad inputs. It replaces the "only tests import @noble/curves" test of bls12381.contrast.test.ts. - vite.config.ts records the modules of each client chunk in .svelte-kit/output/client-modules.json. check-build.mjs fails if the bundle holds tlock-js, drand-client or @babel/*, or a nested copy other than noble under @noble/post-quantum. It also reports the JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip. - Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256 28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM recipients statically, so post-quantum and its nested noble copy are about 99 KB of the Decrypter's 212 KB of rendered code. - npm audit --omit=dev: no vulnerabilities. The full audit finds two low ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3, which is the latest version and affects only SvelteKit's server. npm run verify is green: 2,384 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
c13377af2b |
Contrast bls12381.ts with the Go reference and an audited library
Our checkCompressedPoint stays: it matches the Go reference on every edge case, is 1.3 KB gzip and adds no runtime dependency. Its assurance now comes from a contrast test run on every test pass: - 41 frozen edge-case encodings with the Go reference verdict (drand crypto KeyGroup over kyber-bls12381 and kilic/bls12-381, as profile.Validate uses it), reproducible with scripts/bls12381-go-verdicts.go; - the audited @noble/curves 2.4.0 on the same edge cases and on a fixed-seed corpus of valid points, negations, bit flips, random x and G1 points on the curve outside the subgroup. Breaking the G1 or the G2 subgroup check makes the test fail. @noble/curves 2.4.0 is a development dependency only; a test fails if anything that is not a test imports it, and the build contains none of it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
2 weeks ago |
|
|
5f8c8c8031 |
Inspector page: static SvelteKit site with /inspect (plan step 5)
A prerendered static site (adapter-static) with a landing page and /inspect, which runs spec §63 steps 1 to 8 on a .dkc chosen with the file picker, dropped anywhere on the page, or taken from the official fixtures bundled at build time. It shows every step, the decoded header, the unlock date in UTC and local time, and each extension's id, version, criticality, length and hex, with a text view and an informative CBOR diagnostic view, all escaped and labelled as unauthenticated before step 15. Copiar JSON copies the exact "datekeys inspect -json" view. No network: a hash-mode Content-Security-Policy with connect-src 'self' is the first element of every page, and scripts/check-build.mjs verifies it, the fixtures and the absence of external URLs after every build. Large files are read only up to what steps 1 to 8 need. Reviewed for design and accessibility (WCAG AA contrast, keyboard, focus, live status, 360 px), security and correctness; 262 tests pass, svelte-check has no warnings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
2 weeks ago |
|
|
46630133d3 |
TypeScript implementation of DateKeys v0.8.2, steps 1 to 8
Canonical CBOR codec of the spec §58 profile, hand-written schema codecs (profile, PUBLIC_HEADER, CONTROL_CBOR, .dkk, extensions), DKC1/DKK1 framing, a strict age header parser, dk1_ parsing and nanosecond date to round resolution, and inspect (spec §63 steps 1 to 8) with the same checks and view as "datekeys inspect -json". No runtime dependencies. 216 tests, cbor.ts at 100 % coverage, typecheck of the library without Node types. A differential comparison with the Go reference at afb44a3 found no verdict, code or step disagreement in about 336,000 inputs. The harness for the future Go vector files runs them when they appear. vite 8.3.0 is declared explicitly: it is a required peer of vitest 5.0.1 that legacy-peer-deps does not install. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
2 weeks ago |
|
|
ba8418cdbf |
Start the DateKeys TypeScript project
Skeleton for the TypeScript implementation of DateKeys v0.8.x: the plans in docs/, testdata/ vendored from g.activething.com/go/DateKeys at 5719f6a with a zero-dependency sync and check script, and the TypeScript and vitest tooling already used by the prototype, which now lives in ../AppOld. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
2 weeks ago |