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 }
14 Commits (116af242c5759b7977e0eeed2029500d6d1430f0)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
116af242c5 |
Spec v0.15 draft: remove the .dkr file
The author's decision of 7 October 2026. A release saved next to a capsule cannot exist when the capsule is made, and once the date comes the capsule can be opened: such a file only opens it again and does not cover the real case, someone opening it decades later when drand is gone and nobody saved anything. Long-term recovery rests instead on archives and cache services that keep the releases of all rounds; a reader asks for its round and verifies the signature against the pinned key. Spec: the .dkr extension (section 20, back to v0.14), sections 1, 4, 8, 45, 47.1, 49, 50 (rewritten), 53, 62.1 (rule 28 removed, rule 26 reworded), 63, 70, 73, 74 (the datekeys.release .dkk extension dropped too), 76 (the v0.15 block, with the discarded design) and the annex 79. The release object, the chain hash at step 10, step 9.c option B and the archive format stay. Code: decrypt -save-release and the command datekeys release are gone, with writeRelease and their tests; decrypt -release FILE stays. The release objects of testdata/releases are now <round>.cbor, and TestVectorFilesAreCurrent fails on a file the generator no longer writes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
23 hours ago |
|
|
2eeca40d63 |
Release object, release in hand and step 9.c option B (spec v0.15 draft)
The release of a round becomes a file, .dkr: a release object in
deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round,
4: signature}, which provider.EncodeRelease writes and DecodeRelease reads
with its layers (size, type and version, schema). provider.ParseRelease
also reads drand's JSON as the input of the caller. Verify checks the chain
hash a release names before its round and its signature, with
ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the
informative format of the draft.
capsule.OpenOptions.Release takes a release in hand, a provider.Supplier,
exclusive with Source: Open does not compare it with the clock (step 9.c,
option B) and reports a clock behind it in Opened.ClockBehind; a network
source is still never asked before the round time. The CLI gains
decrypt -release FILE (.dkr, drand's JSON or a local archive),
decrypt -save-release FILE.dkr and the command release, which fetches,
verifies and saves the .dkr without opening the capsule.
Test data: vectors/release.json, releases/<round>.dkr for rounds 1000,
1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In
mutations.json every case says its source, "supplied" or "network"; the
case "round not reached yet", a release in hand, now opens, and four cases
are added: the same with a network source, a release of another round from
a network source, and two release objects of another chain. SpecVersion
stays 0.14 until the author approves the draft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
1 day ago |
|
|
fee531b768 |
Review fixes: author keys, the writer, the CLI, extensions and the locator
Fixes of the review of the session of 1 and 2 October that the text of spec v0.11 already asks for: - authorkey: String and GoString hide the secret key, which only Secret returns; ParsePublic refuses a key that is not a point of the curve (ed25519strict.OnCurve, checked against the square root of testkit). - capsule: a typed nil in AuthorKey, CMSSigner or Sealer is an error, never a capsule without the signature or the seal that was asked for. A panic while evaluating the signature or the seal fails only that part, F1 or S2, not both. OpenOptions.Accept sees the verdicts before step 18 and can refuse to publish the files. - extension.CheckWrite, the rule of encoders of spec 72: the writers of capsules and .dkk files refuse datekeys.note and datekeys.capsule outside the arrays where they are registered, or with invalid data. - CLI: encrypt -sign shows the author key and the code of AUTHOR_MESSAGE before it signs (rule 20); decrypt -expect-author compares the key of an F4 and writes nothing unless it matches; decrypt notifies a public note that it does not show; the lines of the verdicts break at the last space that fits, each row after the first behind a mark, so that the terminal never breaks them; L is the payload, not the content. - locator: a reader rejects an address that breaks 44.1 and keeps the others; addresses refuse the special-purpose blocks of IANA, IPv6 outside 2000::/3, localhost and local names, characters outside RFC 3986, dot segments, and a CID that does not decode to version 1 and a multihash; ParseInfo checks that the locator is an age file with one tlock stanza for the round of its DateKey; Info.Extension reads what it writes; its errors carry no normative code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
6 days ago |
|
|
c3175a150a |
Signature plan, steps 3 and 4: EncryptFiles signs and Open checks, alg 1
AreaLen is 32 KiB, and LargeArea asks for 64 KiB. EncryptOptions.AuthorKey signs inside sealer.write, through a prepare hook that gets the final control: SECURITY_CBOR and the frame are built and evaluated with the rules of the reader before anything is written. OpenOptions.AuthorKeys feeds EvaluateSecurityIn from openBody with control_commit, head_digest and the round time: F4, F3 with a saved key, F2 when it does not verify. The fixtures of v0.10 keep the area of 512 (AreaUnit). The two "unsupported" fixtures use alg 4294967295, since a random alg 1 is now F2. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> |
6 days 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 |
|
|
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 |
|
|
9ac9cd952f |
Spec v0.8.2: second-round corrections from the formal review
The second round of the formal review confirmed the nine corrections of
|
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 |
|
|
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 |