v0.16
main
v0.15
v0.14
v0.13
v0.12
v0.11
v0.10
v0.9
v0.8.2
spec-v0.16
spec-v0.15
spec-v0.14
spec-v0.13
spec-v0.12
spec-v0.11
spec-v0.10
spec-v0.9
spec-v0.8.2
${ noResults }
18 Commits (cc35d2c78c07363ae1f75d965b2d99d0d7677b19)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
6fa2b54bd3 |
Format 3, step 8: fuzzing of format 3 and the SHA-256 of spec v0.10
- Three fuzz targets, in scripts/fuzz.sh too: FuzzDecodeHead (a head that is accepted re-encodes to its input, and a rejection carries one normative code), FuzzEvaluateSecurity (verdicts of this version, X for both or for neither) and FuzzCheckPath (the rules of one entry and the decoder of the head agree on every path). About a million runs each, clean; scripts/check.sh 60s is clean. - Spec v0.10, section 67: the fixtures of format 3 exist, so "Serán ... (por implementar)" reads "Son ...", as for those of format 2. No rule changes. - spec/README.md: v0.10 approved by its author on 30 September 2026 and implemented on this branch, with the SHA-256 of its text; the tag spec-v0.10 waits for the author. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
18eb3c3f48 |
Format 3, step 6d: the mutations of format 3
The mutation corpus of spec 64 gains format 3: 209 cases, 169 of the spec. - The 33 mutations of the first two lists on format3_single and format3_time_and_key_portable, named "format 3: ...", with a sibling written by EncryptFiles and built capsules that hold a BODY. - The list of format 3, 47 cases: one for each value of a line with several (AREA_LEN 0, 511, 513 and 66048; SECURITY_LEN 0 and 513; HEAD_LEN 0 and 2^24 + 1), and the three that open without a code, with the verdicts X, F1 and S1. VERSION 4 is "format 3: version changed", as in format 2. Each seals BODY again with FK_PAYLOAD and the nonce of its fixture, and the control with the new L when L changes; the two that need a head followed by another STREAM chunk derive from format3_tree, whose comment takes the bytes the path loses so that only the head and its chunk change. - Further cases: format 3 relabeled 1, and time_and_key relabeled 2 with the identity and with the .dkk. - A mutation may expect the capsule to open with its verdicts; the exported case records them, with the result ok at step 0. - Splice gives an edit for each run of changed bytes, runs closer than 16 bytes merged, and one more for what one side has beyond the other: a head sealed again changes its bytes and the tag of its chunk, 64 KiB apart. Earlier cases are written with more edits and give the same capsules. The corpus is 706 KB, 476 KB of them the capsule of 65536 implicit folders, whose head is 235 KB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
db862d5524 |
Format 3, step 6c: the vectors of paths, keys, heads and security
The vector files of spec 67 for format 3, generated with the result each case is written for, so that the generator fails when the implementation or the tables change: - paths.json: 83 paths with the result of the rules of one entry, the violation worded as every implementation must word it, and 16 trees, the paths of a head and the result of decoding it: U+00A0, accepted, and U+3000, R6c, at both ends of a segment; best-fit, full-width forms, U+00B4 of cp1253; 8.3 aliases with ~1; Cn; U+206A to U+206F, tags and other ignorables; a dot and ZWJ, and ZWJ alone; 127 and 85 times U+0390; U+F03A; .datekeys-x at two levels; U+FF5E and U+1F600 in both orders; ab with and without ZWNJ; U+00BF, U+00A7 and U+2665, accepted; VS16 after U+2764 and after a; ZWJ at the start, at the end and twice; the rainbow flag and the flag of Scotland; b/.. and a. - path_fold.json: 22 segments with their NFD and their key of R7, among them the entries F of CaseFolding, the dotless i, the Kelvin and Angstrom signs, Cherokee, Hangul and the whitelist dropped before NFD. - head_schema.json: 63 heads through layers 2, 3 and 4, in key order, with the violation of each ERR_HEAD_INVALID; they add comments with tags and with loose variation selectors. - security.json: 21 areas with their verdicts, X, F0, F1, S0, S1 and S2, among them key 2 that is not a byte string, a key 4, a byte more, alg 0, an empty key with the seal intact, and a seal that breaks its schema with an unknown seal_type. - cbor.json gains the control of schema version 3. A test replays every committed vector through the implementation, and another checks that the files are current. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
e070f891ea |
Format 3, step 6b: the nine fixtures of format 3
The fixtures of spec 67 for format 3, each with its record, its BODY, its inspect output and, for time_and_key, its .dkk: - format3_single, format3_tree (five files in three folders, one over two STREAM chunks, one without mtime, a comment and a declared author), format3_comment_only (no files; a TAB in the comment), format3_bloque256 and format3_time_and_key_portable, written with EncryptFiles; - format3_area_1024, format3_security_v2 (verdict X), format3_signature_unsupported (an author-signature of alg 1 with a random key of 32 bytes and a random signature of 64: F1) and format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1), which only a generator of test vectors writes (62.1 rule 13), built with testkit.Build. The record of a format 3 fixture adds the area, SECURITY_CBOR, HEAD_CBOR, the salt, the comment, the declared author, the head extensions, the offset of CONTENT in BODY, each file with its layout, SHA-256 and mtime, and the verdicts with their lines; its plaintext file is BODY. The generator writes, then recovers every value by opening layer by layer for the three formats alike, and refreshes the records of format 3 through a Sink. Tests: the conformance test checks BODY, the head, security and every file, and opens through a MemorySink; the .dkk tests take the .dkk of formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures into folders; the control fuzz target decodes with the three schema versions. The differential corpus gains two bases, format3_single and format3_time_and_key_portable, one per policy: 5110 cases, the earlier ones unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
3c3933627d |
Format 3, step 6a: specification 0.10 and ERR_HEAD_INVALID
- SpecVersion is 0.10: the version command, the catalogue test and the spec field of every test data file name spec v0.10. The regenerated test data change in that field only. - All lists ERR_HEAD_INVALID, last, as section 69 of the spec does. - The tests of the path rules and of format 3 held literal invisible and combining characters (ZWJ, VS16, U+202E, soft hyphen, the Kelvin sign and others), which an editor could normalize or hide; they are Go escapes now, with the same values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
8955c0f650 |
Format 3, step 4: the writer
EncryptFiles writes format 3 (spec 29.2 to 29.6, 61, 62, 62.1): the files of a list of Sources, each read twice, with the comment and the declared author. - Before anything is written: the paths and the texts are checked with the rules of the reader, in the words of a writer, naming the rule and the character, and the two paths of an R7 collision (rule 15); the comment has its CR LF and lone CR turned into LF (29.6); L is measured with a head whose salt and SHA-256 are zero, as long as the final one, and the first reading hashes each file, which must have exactly its Size. - The files go in the byte order of their paths (R8), whatever the order of the Sources; the mtime is kept only from 1970 to 9999, never clipped (rule 16); at least one file or a comment (rule 14). - The head, with a fresh salt, the control and the security area are decoded with the rules of the reader before sealing (rule 17), and the frame is checked against L. The area is 512 bytes with the empty security, whatever the options (rule 13). - The second reading writes each file into PAYLOAD_AGE and fails if its size or SHA-256 changed (rule 18). - Encrypt and EncryptFiles share the sealing; Encrypt writes format 2 only with the new TestVectors option (rule 1), and takes no head. The test data generators set it, and so does the CLI until step 5 moves it to EncryptFiles. - Result.Head is the head written. DecodeHead keeps the check of the critical extensions apart, so that the self-check decodes the head as the one of the control does. - The examples and the live test write with EncryptFiles. - The reader tests had a literal U+202E, now escaped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
7249ef4cd1 |
Format 3, step 3: the reader
Open reads format 3 (spec 29.2 to 29.7, 63 steps 17 and 18): the PRELUDE accepts VERSION 3, and the files go to a Sink. - Sink: Begin with the validated head, Create for each file in the order of the head, and Commit only after every check of step 17; after any failure that follows a successful Begin, Abort, once. A format 3 capsule without a Sink fails right after step 2 with ErrSinkRequired, a caller error with no code, no failed step and no request; a capsule of format 1 or 2 without dst fails there too. - Step 17 in its substeps: the frame and the area, security and its verdicts, which never fail, the head, the files filling CONTENT, the SHA-256 of each file and the padding. A failure of age or a plaintext whose length is not P prevails; otherwise the first substep that fails decides, and a code other than ERR_INTEGRITY is reported only after reading PAYLOAD_AGE to its end. - The reads of BODY grow with the bytes received, never with AREA_LEN, HEAD_LEN or a declared size (spec 57); a test measures it. - A failure of the Sink is the caller's own error with ERR_INTEGRITY, as one of dst is in formats 1 and 2. - Opened gains Head, Verdicts, AreaLen and UnusableHeadExtensions. - Test data: "version changed" sets VERSION 4, and the format 2 list gains "format 2 time_only relabeled format 3", which fails at step 14, as section 64 of spec v0.10 lists: 126 cases, 89 of the spec. The randomly built capsules keep their recorded bytes. - testkit: Build writes format 3 and can edit the padded plaintext; Head3, Body3, DiscardSink and MemorySink build and open BODY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
6fba1ddc01 |
Format 3, step 1: Unicode 18.0.0 tables and the path and text rules
internal/pathrule checks the paths and the texts of a format 3 head (spec 29.5, 29.6) with its own tables, never with the Unicode functions of the platform, whose version changes with each runtime. - gen reads the 19 pinned data files (UnicodeData, DerivedCoreProperties, CaseFolding and emoji-variation-sequences of Unicode 18.0.0, and the 15 WindowsBestFit tables), checks their SHA-256 and writes tables.go: assigned code points, Default_Ignorable_Code_Point, full canonical decompositions and combining classes, C and F folding, the bases of the emoji variation sequences, and the non-ASCII code points each code page maps to ASCII. The data files stay out of git, in .cache. - NFD, Fold and the key of R7; CheckPath with R2 to R6c and R10, CheckTree with R7 and then R9, and CheckComment and CheckAuthor with the invisible-character rule. Errors name the rule and never echo the creator's text, so that another implementation can match them. - The canonical text of the tables has a SHA-256, TablesDigest, which the tests recompute and a TypeScript implementation will share. - Checked against golang.org/x/text (Unicode 15.0.0) outside this module: NFD matches on every code point both know, and folding only differs on the 86 Cherokee letters that CaseFolding.txt folds to upper case, as these tables do. - The spec pins the SHA-256 of the 19 files in 29.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
f24a280c28 |
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved by its author on 29 September 2026. Encrypt writes capsule format 2 only; Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the verdict v0.8.2 gave it. Format 2 (spec §22, §29.1, §31, §39): - VERSION in the PRELUDE is the capsule format, capsule.Format; any other value is ERR_UNSUPPORTED_VERSION at step 2. - CONTROL_CBOR has the schema version of its format. Version 2 adds key 6, payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without extensions, whatever L. - The payload is the content padded with zeros to P = rule(L). Step 17 checks the length and the zeros, and Open writes only the first L bytes. - INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials, and a dummy in each slot left, in a uniformly random order. Writer rules (spec §62.1): EncryptOptions.Length is required and the source must deliver exactly that many bytes; recipients that are not canonical or of low order are rejected (agewrap.CheckX25519Recipient); self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE. The CLI measures its input, takes -padding and reports the format. Test data: seven format 2 fixtures, padding vectors checked against math/big, format 2 CBOR vectors, and the mutation corpus in both formats with the 22 cases of the third list of spec §64, built without randomness by sealing the fixtures again with their known keys and nonces. The format 1 fixtures are kept byte for byte and never regenerated; the differential corpus keeps its 1825 cases and adds a block per format 2 fixture. The spec copy loses its "to be implemented" markers, and the READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
1 week ago |
|
|
5b342d32dc |
Version constants: the specification and the module
- datekeys.SpecVersion ("0.8.2") names the specification the module
implements. A test ties it to the spec file, its title and
spec/README.md, and TestCatalogueMatchesSpec and the vector files use
it (testkit.SpecVersion now aliases it), so the vectors regenerate
unchanged.
- datekeys.Version() is the version of the module as the go command
recorded it. That is a tag, or for a binary built in a checkout the
pseudo-version of its commit (for example
v0.0.0-20260928105528-9ac9cd952f04), or (devel) when it is unknown, as
in tests or under a replace directive to a directory. It works as the
main module and as a dependency, whatever the module path, which it
reads from the root package.
- `datekeys version` (also -version and --version) prints both and the
Go toolchain.
- README.md and README.es.md explain the three versions (format,
specification, module) and what the code on main covers.
traceability §70 and CHANGELOG follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
1 week ago |
|
|
c57ed4869c |
Spec v0.8.2: corrections from the formal review
A formal review of the whole v0.8.2 text found it approvable after
these corrections, recorded in §76 ("Correcciones de la revisión
formal"):
- §27 no longer calls header_binding the authenticity of PUBLIC_HEADER:
it binds the header to the opened control, never authorship or date
(§55.1); the age MAC only protects against whoever lacks the file key.
- §63 steps 9 and 10: a network source (relay, Release API, cache) MUST
verify every response and gives ERR_RELEASE_UNAVAILABLE at step 9 when
none verifies; the step-10 codes are for a directly supplied release.
The reference already behaved so; TestReleaseFromANetworkSource pins
both paths.
- §54 and §72: registrations declare the objects and arrays where an
extension may appear, and a known extension out of place counts as
unknown there. The reference gains the optional extension.Placement
interface, used at steps 4, 9.a and 14.
- §63 step 11 fixes the GT serialization hashed by H2 (kilic/kyber order)
with the frozen vector H2(e(G1, G2))[:16] = cb87319f..., shared as
testdata/vectors/tlock_ibe.json; H2-H4 are cited to drand/kyber.
- Step 5 makes the SEALED_CONTROL read mandatory, step 15 names
ERR_HEADER_BINDING, §21 makes capsule_id 16 CSPRNG bytes a MUST, §76
is made accurate (four dk1.json vectors, the §36 time_only rule, two
cases rewritten against the texts that really existed), and editorial
fixes in §5, §36, §55.1, §69.1 and §77. §73 lists the three new
decisions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2 weeks ago |
|
|
f6f2e9f55f |
Spec v0.8.2 amendment: canonical point encoding; no library error text
Amendment of the unreleased v0.8.2, recorded in §76 with its case: the second implementation's phase-2 research found that tlock-js over @noble/curves 1.9.7 accepts U re-encoded as c0 + p and a signature x + p and returns the same file key, while the reference rejects both (noble 1.9.7 differed from kilic on 5,615 of 41,686 encodings), and the spec did not say which encodings are valid. - §12.2 defines the canonical encoding of a BLS12-381 point (drand's compressed ZCash form) and requires decoders to reject every other byte string; §12.1 applies it to public_key. - §63 step 10 applies it to the release signature (ERR_RELEASE_INVALID) and step 11 defines the tlock stanza body U || V || W (96 + 16 + 16 bytes for Quicknet) with a canonical, non-infinity U (ERR_INTEGRITY). - §64 gains ten mutations, exported to mutations.json (65 cases). The signature x + p case uses published Quicknet round 1004, the first after 1000 whose x allows x + p < 2^381. The reference already gave every stated code and step. Errors no longer copy text from tlock, kyber, age, drand or kyber-bls12381. kyber's IBE error carried the candidate plaintext and r, and with one bit of W flipped the message disclosed the real tlock file key with that bit flipped. Every such place now uses a fixed reason with its normative sentinel; TestTlockFailureDiagnosticsCarryNoSecrets fails with the old wrapping. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
2 weeks ago |
|
|
692cf87db1 |
Reject invalid UTF-8 in the dk1_ JSON at step 2 (spec §19)
encoding/json replaces invalid UTF-8 with U+FFFD inside strings, so a member that a repeated name overwrites passed steps 2 and 3 and ended as ERR_DATEKEY_NON_CANONICAL at step 6, while §19 makes invalid UTF-8 fail step 2 with ERR_DATEKEY_INVALID. parseJSON now checks utf8.Valid first. Found by the differential of the TypeScript implementation; pinned by TestReadingRules (which fails without the fix) and a new dk1.json vector. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
2 weeks ago |
|
|
382006649f |
Spec v0.8.2 refinements: error precedence, trust model, strict order
Approved refinements, each recorded with its reproducible case in the §76 v0.8.2 subsection: - §69.1: layered error model with normative precedence (frame, type tag and version, CBOR profile and CDDL, then fields with their own code in ascending key order; across steps the §63 order decides), with a scope paragraph for the optional steps 5, 6 and 8. - §55.1: normative trust table per section (who can write it, from which step it is bound, what it never proves); §72: security-relevant claims go in CONTROL_CBOR or under a signature, .dkk data is advisory. - §31/§54: extension arrays in strictly ascending unsigned byte order of extension_id (one rule for order and uniqueness). - Gaps a second implementation needed: §28.1 malformed age headers, §15/§19 latest unlock time and dk1_ reading rules, §22/§23/§57 length lower bounds, §63 step 8 tlock argument comparison and step 9 order, §12.1 profile validation with the drand chain-hash formula, §74 table of implementation limits. Reference alignment: .dkk errors only at step 9.a (new OpenOptions.AccessKeyFile, used by the CLI), CR/LF in dk1_ is ERR_DATEKEY_INVALID, BODY_LEN 0 is ERR_INTEGRITY, nil identities are not credentials, and AccessIdentity tries every identity on every stanza so its verdict does not depend on their order. dk1.json gains three vectors; every other testdata file is byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
2 weeks ago |
|
|
9cbcab10b2 |
Hand-written CBOR codec and shared test vectors (plan steps 2b and 3)
Step 2b: package codec is rewritten without reflection or struct tags. A strict Decoder accepts only the spec §58 profile, Unmarshal decodes, re-encodes and compares, Peek reads the type tag and version, and Walk is a bounded iterative helper for vectors and fuzzing. Every schema has its own hand-written encoder and decoder that checks all CDDL rules before the fields with their own error codes. github.com/fxamacker/cbor/v2 and github.com/x448/float16 are gone; nothing replaces them. Valid objects encode and decode exactly as before (1.34 million differential verdicts); the invalid-input differences are documented in CHANGELOG and traceability decision 12. A review found and fixed an access_policy check that truncated to uint8. Step 3: testdata gains vectors/cbor.json (generic and per-schema CBOR vectors), vectors/mutations.json (the 55-case mutation corpus, replayable offline), vectors/inspect_differential.json (1,825 fixed-seed mutations with the Go verdict) and one inspect -json golden per fixture, all regenerated byte-identically by genfixtures and documented in testdata/README.md for second implementations. Gate green with 90 s of fuzzing per target on all 15 targets; codec at 100 % coverage; pre-existing testdata byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
2 weeks ago |
|
|
afb44a396e |
Implement v0.8.2 extension data, limits and writer self-checks
- extension: data is an opaque []byte; New takes []byte and rejects empty data; key 2 must be a non-empty shortest-form bstr; at most 64 extensions per array; extension_version <= 2^32-1; CheckDisjoint is a linear merge; optional DataValidator with ERR_EXTENSION_DATA_INVALID for known critical extensions and Unusable reports for known noncritical ones. - capsule, accesskey: frame limits on encode and decode with ERR_INTEGRITY; Encrypt and MarshalBody decode their own output before sealing or returning it; unusable extensions are reported. - profile: period and genesis_time bounded to 2^53-1, genesis decoded as unsigned. - codec: Valid removed; empty values never encode as null. - Regression tests for the nested-data seal/open asymmetry, the nondeterministic verdict on NaN-keyed data and the quadratic disjointness check; three new §64 mutations and five more. - Fixtures: time_only_extensions regenerated with opaque data, new time_and_key_portable_extension.dkk; genfixtures gains -only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
2 weeks ago |
|
|
13dcca119c |
Host on Gitea, not GitHub
The module is imported as g.activething.com/go/DateKeys, the path the project's Gitea advertises. The .github directory is gone: workflows now live in .gitea/workflows, use the gitea.com action mirrors and install every tool from its Go module; releases go to this Gitea with goreleaser and a key-based cosign signature; Dependabot is replaced by a nightly report of available updates. scripts/check.sh runs the same checks on any machine and is the gate while the server has no runner. SECURITY.md, README and CONTRIBUTING no longer refer to GitHub. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
2 weeks ago |
|
|
0bd38f18cf |
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan (milestones M0 to M5): datekey, profile, provider, codec, agewrap, extension, capsule, accesskey, the datekeys CLI, official vectors and fixtures, the mutation corpus, fuzz targets, interop and live tests, CI workflows, traceability and policy documents. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
2 weeks ago |