You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys-App/vitest.config.ts

68 lines
3.3 KiB

// The tests of src/lib. Vitest prefers this file to vite.config.ts, the
// SvelteKit configuration of the page, so the library tests run without the
// SvelteKit plugin; the page helpers in src/lib/inspector import the library
// by relative paths and need no $lib alias.
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
include: ['src/**/*.test.ts'],
// BLS12-381 point checks and the 65 536-extension cases take a moment.
testTimeout: 30_000,
coverage: {
provider: 'v8',
include: ['src/lib/dkc/**/*.ts', 'src/lib/inspector/**/*.ts'],
exclude: ['src/lib/**/*.test.ts', 'src/lib/dkc/testing/**', 'src/lib/dkc/index.ts'],
reporter: ['text', 'html', 'json-summary'],
thresholds: {
Phase 2, step 4: local release verification src/lib/dkc/release.ts is provider.Verify of the Go reference (spec §17, §51, §63 step 10), in its order and with its texts: - the round within the profile (ERR_DATEKEY_INVALID); - the round of the release before the signature (ERR_ROUND_MISMATCH); - the length of the signature; - the pinned public key (ERR_UNKNOWN_PROFILE when it does not decode; the point at infinity fails the verification, as with kyber); - the signature (ERR_RELEASE_INVALID): the canonical encoding of a point of G1 other than the point at infinity, gated by bls12381.ts, that verifies on @noble/curves 2.4.0 as the BLS signature of SHA-256(uint64be(round)), hashed with the RFC 9380 DST of G1. Nothing noble throws becomes anything but ERR_RELEASE_INVALID, and no text of noble is copied. Only Quicknet's scheme is verified (plan decision 3): another scheme fails with ERR_UNKNOWN_PROFILE after the round checks. It also defines ReleaseSource, with the contract for network sources and correction 6, and suppliedRelease, the release that the caller hands over (unverified, so step 10 checks it). index.ts does not re-export it yet. release.test.ts: - replays TestVerifyRejects of the reference, with exact texts; - accepts the published releases of rounds 1000, 1001, 2000 and 1004 (the last one recovered from the x + p encoding of the corpus, which shows that encoding is the published signature re-encoded); - shows the DST of G2, the one bls-unchained-on-g1 uses, fails; - gives the code of each of the 7 corpus cases that fail at step 10, with the round that inspect reads from the capsule. ibe.failure.test.ts becomes noble.failure.test.ts and also fails noble's BLS verification. Coverage of release.ts is 100 % and is now a threshold. The site does not change. npm run verify is green: 2,404 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// The codec is covered completely (plan §7), and so are the IBE and
// the release verification (plan of phase 2, section 10, steps 3
// and 4).
'src/lib/dkc/cbor.ts': { 100: true },
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
'src/lib/dkc/ibe.ts': { 100: true },
Phase 2, step 4: local release verification src/lib/dkc/release.ts is provider.Verify of the Go reference (spec §17, §51, §63 step 10), in its order and with its texts: - the round within the profile (ERR_DATEKEY_INVALID); - the round of the release before the signature (ERR_ROUND_MISMATCH); - the length of the signature; - the pinned public key (ERR_UNKNOWN_PROFILE when it does not decode; the point at infinity fails the verification, as with kyber); - the signature (ERR_RELEASE_INVALID): the canonical encoding of a point of G1 other than the point at infinity, gated by bls12381.ts, that verifies on @noble/curves 2.4.0 as the BLS signature of SHA-256(uint64be(round)), hashed with the RFC 9380 DST of G1. Nothing noble throws becomes anything but ERR_RELEASE_INVALID, and no text of noble is copied. Only Quicknet's scheme is verified (plan decision 3): another scheme fails with ERR_UNKNOWN_PROFILE after the round checks. It also defines ReleaseSource, with the contract for network sources and correction 6, and suppliedRelease, the release that the caller hands over (unverified, so step 10 checks it). index.ts does not re-export it yet. release.test.ts: - replays TestVerifyRejects of the reference, with exact texts; - accepts the published releases of rounds 1000, 1001, 2000 and 1004 (the last one recovered from the x + p encoding of the corpus, which shows that encoding is the published signature re-encoded); - shows the DST of G2, the one bls-unchained-on-g1 uses, fails; - gives the code of each of the 7 corpus cases that fail at step 10, with the round that inspect reads from the capsule. ibe.failure.test.ts becomes noble.failure.test.ts and also fails noble's BLS verification. Coverage of release.ts is 100 % and is now a threshold. The site does not change. npm run verify is green: 2,404 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
'src/lib/dkc/release.ts': { 100: true },
Phase 2, step 5a: open capsules, steps 9 to 18, in memory src/lib/dkc/open.ts runs steps 9 to 18 of spec §63 on top of the steps 1 to 8 of inspectWith. It follows capsule.Open of the Go reference, with its checks, codes and texts: - step 9: the access credentials (the .dkk as an object, then its capsule_id and capsule_digest), then the release, never before the round time. Any failure of the source is ERR_RELEASE_UNAVAILABLE alone, keeping its text and its cause (correction 6); - step 10: verifyRelease; - steps 11 to 13: OUTER_TIME_AGE, the structure against access_policy and INNER_ACCESS_AGE; - steps 14 to 18: CONTROL_CBOR, header_binding, I_PAYLOAD, PAYLOAD_AGE and the commit. The three age files open with the Decrypter of age-encryption and identities that apply the rules of Go's agewrap: the tlock identity on ibe.ts, and the access and payload identities on x25519.ts. A failure of age that no identity reports is ERR_INTEGRITY with the fixed reason of its phase, header or STREAM, never the text of age-encryption. The plaintext is decrypted in memory and returned only after step 18. Streaming to OPFS is step 5b. src/lib/dkc/x25519.ts opens one age X25519 stanza at a time, in the order of age's X25519Identity. Step 13 must try every identity on every stanza (spec §36), and age-encryption's Decrypter stops at the first. Its primitives are the ones age-encryption uses: X25519 and HKDF from noble curves and hashes, and ChaCha20-Poly1305 from @noble/ciphers 2.4.0. The author approved declaring that package as a direct dependency on 2026-09-28; it is the copy already installed and bundled. The guards now allow ciphers, and x25519.ts in the noble allowlist. src/lib/dkc/bech32.ts ports age's internal/bech32, with its MIT notice, to read AGE-SECRET-KEY-1 identities. Tests: - vectors.test.ts runs all 65 cases of the mutation corpus through open. Each gives the code and the step of Go, and no case that fails without the network requests a release. This includes the 34 cases of steps 9 to 18 that were skipped, so the suite no longer skips any test. - open.test.ts: - the five official fixtures open to their plaintext, with each credential, with the checks and details of the reference; - the unusable noncritical extensions are reported; - the source failures and the clock; - age failures by phase; - CONTROL_CBOR that does not decode, and a low-order share in INNER_ACCESS_AGE, through an OUTER_TIME_AGE resealed with the FK_TIME of the Go vectors; - the texts of the identities. - x25519.test.ts checks against age-encryption both ways and against the Go-written stanzas of the fixtures, and covers every low-order share. - bech32.test.ts has the vectors of the reference. x25519.ts and bech32.ts are at 100 % coverage, now thresholds. open.ts is at 100 % of lines; the one branch left is the one for an error that is not a DateKeysError. index.ts does not re-export the opening yet. The page imports index.ts, and re-exporting would pull noble into /inspect (58.7 to 84.9 KB gzip) even unused; step 8 will load it on demand. The site does not change. npm run verify is green: 2,490 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// The stanza X25519 and its Bech32 identities (step 5).
'src/lib/dkc/x25519.ts': { 100: true },
'src/lib/dkc/bech32.ts': { 100: true },
Phase 2, step 5b: open from a Blob, streaming to an output open(dkc, opts) now takes a Uint8Array or a Blob, such as a File. - Of a Blob it reads only the prefix that steps 1 to 8 need. inspectedLength and readCapsule move from src/lib/inspector/load.ts to src/lib/dkc/prefix.ts, and the page imports them from the library. - The .dkk capsule_digest is computed over the Blob's stream with the new src/lib/dkc/digest.ts, an incremental SHA-256 on @noble/hashes, since Web Crypto hashes whole buffers only. digest.ts joins the noble allowlist of the guards. - PAYLOAD_AGE is decrypted in streaming. The plaintext goes to memory, as before, or to opts.output, a WritableStream. The output is written as age authenticates each chunk, closed only after step 18, and aborted after any failure at any step, even before step 17 (spec §56). A failure of the output is ERR_INTEGRITY with its text, as Go keeps the error of the writer of the plaintext. Tests: - the fixtures from Blobs, to memory and to an output; - a truncated two-chunk payload whose first chunk reached the output before the abort; - early failures that never write; - write and close failures, and an abort that fails; - a .dkk without capsule_digest; - the whole mutation corpus again as Blobs into an output, aborted in every case; - digest.ts against Web Crypto. Coverage of digest.ts is 100 % and a threshold. Checked in the browser (dev server, real OPFS). time_only.dkc, two STREAM chunks, opened from a Blob into FileSystemFileHandle.createWritable gives 78,000 bytes with the SHA-256 of its sidecar. The same file truncated fails at step 17, and the OPFS file keeps its previous content. The quota check, the temporary file and the download belong to the page, in step 8. npm run verify is green: 2,560 tests. The site does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
'src/lib/dkc/digest.ts': { 100: true },
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
// The tlock recipient (step 7).
'src/lib/dkc/tlock.ts': { 100: true },
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
// The prefix reads, and the opening of the page (step 8).
'src/lib/dkc/prefix.ts': { 100: true },
'src/lib/inspector/fixtures.ts': { 100: true },
'src/lib/inspector/opener.ts': { 100: true },
'src/lib/inspector/opening.ts': { 100: true },
'src/lib/inspector/release-input.ts': { 100: true },
'src/lib/inspector/tempfile.ts': { 100: true },
// The pieces of the writer (plan of phase 3, step 2) and the age
// files shared by the opening and the writer.
'src/lib/dkc/recipient.ts': { 100: true },
'src/lib/dkc/random.ts': { 100: true },
'src/lib/dkc/agefile.ts': { 100: true },
'src/lib/dkc/padding.ts': { 100: true },
Phase 3, steps 3 and 4: the writer of capsule format 2 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>
1 week ago
// The writer (plan of phase 3, steps 3 and 4).
'src/lib/dkc/writer.ts': { 100: true },
'src/lib/dkc/encrypt.ts': { 100: true },
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
// The page of step 7: the lengths known before writing, the local
// times, the form and the writing loaded on demand.
'src/lib/dkc/lengths.ts': { 100: true },
'src/lib/inspector/localtime.ts': { 100: true },
'src/lib/inspector/create-input.ts': { 100: true },
'src/lib/inspector/creator.ts': { 100: true },
// Format 3 (plan of format 3 in datekeys-ts): the rules of the paths
// and texts of the head, the codec of BODY, of the security area and of
// the head, and the ZIP in which the page delivers files.
'src/lib/dkc/pathrule.ts': { 100: true },
'src/lib/dkc/body.ts': { 100: true },
'src/lib/dkc/security.ts': { 100: true },
'src/lib/dkc/head.ts': { 100: true },
'src/lib/inspector/crc32.ts': { 100: true },
'src/lib/inspector/zip.ts': { 100: true },
'src/lib/dkc/**/*.ts': { statements: 95, branches: 90, functions: 95, lines: 95 },
// The page model and helpers of the inspector (plan §8, phase 1).
'src/lib/inspector/**/*.ts': { statements: 95, branches: 90, functions: 95, lines: 95 },
},
},
},
});

Powered by TurnKey Linux.