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/testdata/README.md

1153 lines
65 KiB

# DateKeys test data
Official vectors, fixtures and corpora of the DateKeys Protocol Specification
v0.15, generated by the reference implementation.
Another implementation consumes them as they are: this file documents every
format, so that no Go code has to be read. The rules that decide each verdict
are in the specification; this file points to them, and states only what
belongs to the files themselves.
```
go run ./internal/testkit/genfixtures -out testdata
```
regenerates everything except the `.dkc` and `.dkk` fixtures, which are
generated once and frozen (spec §67), and the vectors `security_cms.json` and
`locator.json`, frozen too because they hold randomness: delete one of them
to make it again. The records of each fixture (`<name>.json`,
`<name>.dkk.json`, `<name>.inspect.json`) are recomputed from its frozen bytes,
so the fixtures of v0.8.2, v0.9 and v0.10 carry `"spec": "0.11"` and the fields
added since. The local gate (`scripts/check.sh`) and CI run it and fail if any
committed file changes: every file below is exactly what the implementation
computes today.
The `spec` field of every file is `"0.15"`, the version this module declares.
v0.15 adds the release object and a release in the caller's hand (§47.1,
§63 step 9.c): `vectors/release.json`, the files of `releases/`, and the
field `source` of `mutations.json`.
What v0.12 changes from v0.11, the texts of the verdicts of a certificate and
of a seal, the profile of a certificate and the rules of the addresses and of
the padding of a locator, is in the files: the verdicts and the lines of
`security.json`, `security_cms.json` and `mutations.json`, and the cases of
`locator.json`, are those of v0.12.
Conventions for every file:
- JSON in UTF-8, with LF line endings. Binary values are lowercase hex strings.
- `error`, `result` and similar fields hold the normative codes of spec §69, such
as `ERR_NON_CANONICAL_CBOR`.
- `step` is a step of the reading flow of spec §63, 1 to 18. Steps 1 to 8 are the
pre-unlock checks (`datekeys inspect`, `capsule.Inspect`): no network, no
secret.
- The `spec` field names the version of the specification.
- `format` is a capsule format, the `VERSION` of the PRELUDE (spec §22): 1, 2
or 3. A writer produces format 3 only; formats 1 and 2, those of v0.8.2 and
v0.9, are still read (spec §70).
| File | Content | Spec |
|---|---|---|
| `vectors/profile_quicknet.json` | Quicknet Provider Profile: its canonical CBOR and `profile_hash` | §11, §12 |
| `vectors/quicknet_rounds.json` | date → round resolution | §15, §16, §65 |
| `vectors/dk1.json` | canonical `dk1_` strings, and rejected encodings with their code | §18, §19, §66 |
| `vectors/cbor.json` | the CBOR profile, and one block of vectors per schema, CONTROL_CBOR in the three formats | §58, CDDL |
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
| `vectors/tlock_ibe.json` | H2 of the tlock IBE: the serialization of an element of GT | §63 step 11 |
| `vectors/release.json` | the release object and drand's JSON, each with the result of step 10; the lookups of a local release archive | §47.1, §50, §63 step 10 (v0.15) |
| `releases/<round>.cbor` | the release object of each published round of the tests: 1000, 1001, 1004 and 2000 | §47.1 (v0.15) |
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>
24 hours ago
| `releases/archive_1000_1004.bin` | a local release archive, the informative format of §50, of rounds 1000 to 1004, two of them missing | §50 (v0.15) |
| `vectors/tlock_steps.json` | steps 10 and 11 for Quicknet value by value: the message of a round, its hash to G1, and the decryption of a tlock stanza with H2, H4, H3 and the file key | §63 steps 10 and 11 (v0.14) |
| `vectors/padding.json` | the padding of formats 2 and 3: P for each content length L, and the length of PAYLOAD_AGE | §29.1 |
| `vectors/paths.json` | the paths of a format 3 head: the rules of one entry, and those of the paths of a head | §29.5 |
| `vectors/path_fold.json` | the key of R7 of segments, and their NFD | §29.5, §29.5.1 |
| `vectors/head_schema.json` | heads of format 3 and the result of decoding them | §29.4 to §29.6, §69.1 |
| `vectors/security.json` | security areas of format 3 in the context of a capsule, their verdicts and the lines that show them | §29.3, §29.7, §29.9 |
| `vectors/security_cms.json` | security areas with a signature of `alg` 2 or a seal of `seal_type` 2, each with its context, verdicts, results and lines | §29.7, §29.10, §29.11 |
| `vectors/ed25519_strict.json` | Ed25519 signatures and the result of the strict profile of the author signature | §29.9 |
| `vectors/note.json` | the data of the public note and the result of its rules | §24.1, §29.6 |
| `vectors/resolved_ip.json` | the IP address a name of a locator resolves to, NAT64 included, and whether a reader may connect | §44.1 (v0.13) |
| `vectors/wordkey.json` | the key of words: the words of a text, what a writer refuses, and the identity the words derive | §38.1, §64 |
| `vectors/locator.json` | the extension `datekeys.capsule` of a `.dkk`, its envelope and its locator, and what a reader rejects and uses of them | §44.1, §64 |
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>
24 hours ago
| `vectors/mutations.json` | the mutation corpus: the 178 mutations of §64 and further cases, each with the kind of its release source | §63, §64 |
| `vectors/inspect_differential.json` | 5110 mutations of fourteen fixtures with the verdict of steps 1 to 8 | §63 |
| `fixtures/<name>.dkc`, `<name>.json` | official capsules and every intermediate value | §67 |
| `fixtures/<name>.dkk`, `<name>.dkk.json` | official access keys | §68 |
| `fixtures/<name>.plaintext` | the content of each capsule: what the reader delivers in formats 1 and 2, without the padding of format 2, and in format 3 BODY, whose files its record lays out | §67 |
| `fixtures/<name>.inspect.json` | the exact output of `datekeys inspect -json` for each `.dkc` | §63 |
There are twenty-six official capsules. Five are in format 1, the fixtures of
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
v0.8.2, kept for compatibility: `time_only`, `time_only_extensions`,
`time_and_key_portable`, `time_and_key_recipients` and `empty_payload`. Seven
are in format 2, the fixtures of v0.9, kept for compatibility too:
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
| Fixture | Policy | Credentials | L | Padding code | P |
|---|---|---|---|---|---|
| `format2_time_only` | `time_only` | — | 78000 | 2, reforzado | 79872 |
| `format2_time_only_bloque256` | `time_only` | — | 78000, the same content | 1, bloque256 | 78080 |
| `format2_empty_payload` | `time_only` | — | 0 | 2 | 256 |
| `format2_time_only_extensions` | `time_only` | — | 34 | 2 | 256 |
| `format2_time_and_key_portable` | `time_and_key` | a portable `.dkk`; 15 dummies | 46 | 2 | 256 |
| `format2_time_and_key_recipients` | `time_and_key` | a portable `.dkk` and three X25519 identities; 12 dummies | 41 | 2 | 256 |
| `format2_time_and_key_sixteen` | `time_and_key` | sixteen X25519 identities; no dummy | 41 | 2 | 256 |
The first two have a P above 64 KiB, so their PAYLOAD_AGE has two STREAM
chunks. `format2_time_only_extensions` carries a noncritical PUBLIC_HEADER
extension and a noncritical CONTROL_CBOR extension. The release that opens each
capsule, a published Quicknet signature, is in its `<name>.json`, so they all
decrypt offline.
Fourteen are in format 3. Their plaintext file is BODY, L bytes: the frame, the
security area, the head and the files (spec §29.2).
| Fixture | Policy | Files | Comment | L | Padding code | P | Area | Verdicts |
|---|---|---|---|---|---|---|---|---|
| `format3_single` | `time_only` | 1, `nota.txt`, with mtime | — | 659 | 2 | 768 | 512 | F0, S0 |
| `format3_tree` | `time_only` | 5 in three folders, one of 80000 bytes, one without mtime | two lines, and a declared author | 84078 | 2 | 86016 | 512 | F0, S0 |
| `format3_comment_only` | `time_only` | — | two lines, the second with a TAB, and a declared author | 636 | 2 | 768 | 512 | F0, S0 |
| `format3_bloque256` | `time_only` | 1 of 20000 bytes | — | 20644 | 1 | 20736 | 512 | F0, S0 |
| `format3_time_and_key_portable` | `time_and_key`, a portable `.dkk` and 15 dummies | 1 | — | 688 | 2 | 768 | 512 | F0, S0 |
| `format3_area_1024` | `time_only` | 1 | — | 1171 | 2 | 1280 | 1024 | F0, S0 |
| `format3_security_v2` | `time_only` | 1 | — | 659 | 2 | 768 | 512 | X |
| `format3_signature_unsupported` | `time_only` | 1 | — | 659 | 2 | 768 | 512 | F1, S0 |
| `format3_seal_unsupported` | `time_only` | 1 | — | 659 | 2 | 768 | 512 | F1, S1 |
| `format3_unsigned` | `time_only` | 1, `nota.txt`, with mtime | — | 32915 | 2 | 34816 | 32768 | F0, S0 |
| `format3_note` | `time_only`, with a public note | 1, `nota.txt`, with mtime | — | 32915 | 2 | 34816 | 32768 | F0, S0 |
| `format3_signed` | `time_only` | 1, `nota.txt`, with mtime | — | 32915 | 2 | 34816 | 32768 | F4, S0 |
| `format3_signed_cms` | `time_only` | 1, `nota.txt`, with mtime | — | 32915 | 2 | 34816 | 32768 | F6, S0 |
| `format3_sealed` | `time_only` | 1, `nota.txt`, with mtime | — | 32915 | 2 | 34816 | 32768 | F4, S4 |
The first five were written by a writer of v0.10, with the area of 512 bytes.
The next four only a generator of test vectors may write (spec §62.1 rule 13):
an area larger than 512 bytes, a security map of version 2, which a reader of
this version cannot read, an author signature of `alg` 4294967295, reserved
for tests, with a random key of 32 bytes and a random signature of 64, and
that with a seal of `seal_type` 4294967295, reserved for tests too, and a
random token of 32 bytes. None of them has a verdict that stops the opening.
The last five were written by a writer of v0.11, with the area of 32768 bytes:
- `format3_unsigned` holds the content of `format3_signed` without a
signature, and its P is the same (spec §64: the same content with a
signature and without one, in the common area).
- `format3_note` carries the public note «Cartas del viaje a Lisboa»,
`datekeys.note` in the noncritical array of PUBLIC_HEADER (spec §24.1),
which its record gives in `header_extensions`.
- The last three are those that `capsule.EncryptFiles` writes with a signer
and a sealer.
Those three are signed and sealed with test keys (spec §29.8 to §29.11), and
their record has a `signature` object, and `seal` in the third:
- `format3_signed`: `alg` 1. The seed of the test key, which is not a secret,
is in `secret_seed`: Ed25519 is deterministic, so signing `author_message`
with it gives `signature` again. Opening it gives F4 and the key `author_key`,
`dkauthor1…`, which `verdicts` repeats; with that key among the saved ones,
F3.
- `format3_signed_cms`: `alg` 2, two certificates, an ECDSA P-256 one and an
RSA 2048 one, each with a seal CAdES-T from a test authority, dated before
the round time. The record has `signers` (SIGNERS in hexadecimal),
`certificates` (the DER of each), `signature` (the DER of the CMS signature)
and `signer_results`, one for each required signer in the order of SIGNERS:
holder, issuer that the certificate says, result, seal time, and whether the
seal, with its accuracy, precedes the round time. The private keys are not
kept: ECDSA and RSA-PSS are not deterministic, and a reader only verifies.
- `format3_sealed`: `alg` 1 as the first, and a seal of `seal_type` 2 over
`SEAL_SUBJECT`. The record has `seal`: `seal_subject`, the `token` in
hexadecimal, the `holder` of the authority as §29.7 shows it, and the time.
In the three, the record gives the commitments `control_commit`, `head_digest`
and `signers_digest`, the text `author_message` and its `author_code`, and the
exact content of key 2 of `SECURITY_CBOR`. An implementation checks them from
the control, the head and the security area of the fixture, and the verdicts
from `verdicts`. The certificates and the tokens are random, so these fixtures
are frozen once written like the others. The BODY of `format3_tree` is over two STREAM
chunks, with its head in the first.
## Edited files
`mutations.json` and `inspect_differential.json` give each mutated `.dkc` as
edits of a base file, not as its full bytes:
```json
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
{ "base": "time_only.dkc", "edits": [[4, 1, "04"]] }
```
- `base` is a file of `testdata/fixtures`. In `mutations.json` it may be absent:
the base is then the empty file, and the single edit holds the whole capsule.
- An edit is `[at, delete, insert]`: the `delete` bytes at offset `at` of the
base are replaced by the bytes of the hex string `insert`.
- The edits of one file refer to offsets of the unmodified base, are sorted by
`at` and do not overlap. The result is therefore built in one pass: copy the
base up to `at`, append `insert`, skip `delete` bytes of the base, go on with
the next edit, and copy the rest of the base.
- `[0, 78799, ""]` on `time_only.dkc` is the empty file; `"edits": []` is the
base unchanged.
## `vectors/cbor.json`
```json
{
"spec": "0.11",
"walk": { "max_depth": 3, "max_len": 64 },
"accept": [ { "name": "uint 2^53 eight bytes", "hex": "1b0020000000000000", "value": "9007199254740992" } ],
"reject": [ { "name": "tag", "hex": "c101", "error": "ERR_NON_CANONICAL_CBOR" } ],
"schemas": [ { "block": "extension", "schema": "public_header", "name": "65 extensions", "hex": "a600…", "result": "ERR_NON_CANONICAL_CBOR" } ]
}
```
### Generic vectors: `accept` and `reject`
Each `hex` is checked as exactly one data item of the CBOR profile of spec §58:
major types 0, 2, 3, 4 and 5 only; integers and lengths in their shortest form;
definite lengths; map keys that are unsigned integers in strictly ascending
order; valid UTF-8 text; nothing after the item. `accept` holds the inputs that
pass, `reject` those that fail, all with `ERR_NON_CANONICAL_CBOR`.
The `walk` limits apply as well, as in the reference `codec.Walk`: containers
nest at most `max_depth` deep (a scalar has depth 0, `81818100` has depth 3),
and every byte string and text string has at most `max_len` bytes, every array
at most `max_len` items and every map at most `max_len` entries. The vectors
named "above max_len" or "above max_depth" fail on these limits only.
`value` is present for an accepted unsigned integer: a JSON number up to
2^53 − 1, and a decimal string above, so that no reader loses precision.
Among them: shortest-form boundaries, `a200010101` (the two-key map
`{0: 1, 1: 1}`), a text with a leading BOM, keys out of order or repeated,
non-integer keys, indefinite lengths, tags, floats, simple values, negative
integers, truncation, lengths beyond the input, trailing bytes, overlong UTF-8
and surrogates.
### Schema vectors: `schemas`
Each vector is one encoded object:
- `schema` names the object and the decoder to run on `hex`:
- `provider_profile`: a Provider Profile (§11), decoded and then validated
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
as a profile to pin, by rules 1 to 3 of spec §12.1 in their order: the
CDDL with the `period` limit of the reference, the field rules
(`ERR_UNKNOWN_PROFILE`) and the chain-hash self-check
(`ERR_PROFILE_MISMATCH`), whose formula §12.1 gives. No `profile_hash` is
expected (rule 4). A vector that changes a hashed key recomputes
`chain_hash`, unless its name says that the chain hash no longer matches.
- `public_header`: PUBLIC_HEADER (§24). No profile registry is consulted and
no extension is known: a header naming an unpinned profile is valid here
(`ERR_UNKNOWN_PROFILE` comes from the registry at step 4), and critical
extensions are not checked here.
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
- `control_cbor`: CONTROL_CBOR (§31), decoded for the capsule format given
by `format`, 1 when the field is absent: the schema version must equal it
(another is `ERR_UNSUPPORTED_VERSION`, layer 2), and keys 6,
`payload_length`, and 7, `padding`, are required in formats 2 and 3 and
not defined in format 1. The names of the vectors of formats 2 and 3
start with "format 2: " and "format 3: ".
- `dkk_body`: BODY_CBOR of a `.dkk` (§41), without the 12-byte DKK1
prelude.
- `block` names the schema the vector exercises: the same as `schema`, or
`verification_metadata` (the `.dkk` body's key 6 varies) or `extension` (the
PUBLIC_HEADER's key 6, `noncritical_extensions`, varies).
- `result` is `ok` or the error code.
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
When bytes break several rules, the code is the one of the first failing
layer of spec §69.1: the type tag and the schema version, read first from keys
0 and 1 (layer 2), then the CDDL, the implementation limits included, and the
re-encoding (layer 3), and only then the fields with codes of their own in
ascending key order (layer 4). The objects of these vectors have no frame, so
layer 1 does not apply, and their critical extensions are not checked (see
above).
Each block has a minimal valid object, an unknown key, a missing required key, a
wrong type and values out of size or range. The `extension` block has, besides:
data `40` (empty) and `5801xx` (length not in its shortest form), data of every
other type (text, `null`, integer, map, array, tag, indefinite length), 64 and
65 extensions, an `extension_id` starting with a BOM, the pair U+FF61 and
U+10000 in UTF-8 byte order (valid) and in UTF-16 order (invalid), and
`extension_version` 2^32 − 1 (valid), 2^32 and 2^53 (invalid).
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
The `control_cbor` block has, besides, a key 6 in format 1, where it is not
defined, and in format 2: the minimal control of 103 bytes (L = 0),
`payload_length` 78000, 2^32 + 1 and L_MAX = 2^53 − 2^46 (valid), L_MAX + 1,
2^53 and 2^64 − 1 (invalid), a `payload_length` of 7 or 9 bytes, as an
unsigned integer or with a length not in its shortest form, `padding` 1
(valid), 0, 3, 257 and as a byte string, a missing key 6 or 7, keys 6 and 7
out of order, an unknown key 8, and schema versions 1 and 3
(`ERR_UNSUPPORTED_VERSION`); and in format 3: the minimal control, both
extension arrays, `payload_length` 84078 with `padding` 1 (valid),
`payload_length` L_MAX + 1, a missing key 6 or 7, schema version 3 without
keys 6 and 7, and schema versions 1, 2 and 4 (`ERR_UNSUPPORTED_VERSION`).
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
#### Implementation limits
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
The vectors apply the implementation limits of the reference that spec §74
lists: the maximum `extension_id` length, the Provider Profile `period` of one
day, the maximum name lengths and the `public_key` length. The vectors named
"the implementation limit" sit at a limit and are valid; those named "above
the implementation limit" go one past it and are otherwise valid, so that an
implementation without the limit accepts them. The name alphabets, the
`genesis_time` range and the drand rules of the `provider_profile`
validation are not limits: they are rules of spec §12.1. Neither is the
minimum of one byte of `extension_id` (spec §31).
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
## `vectors/tlock_ibe.json`
H2, the hash of an element of GT in the IBE-CCA with which tlock wraps
FK_TIME: spec §63 step 11, and the paragraph "Serialización de GT en H2" after
the flow.
```json
{
"spec": "0.11",
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
"description": "…",
"vectors": [
{ "name": "H2(e(G1, G2)), the generators of G1 and G2", "g1": "97f1…", "g2": "93e0…", "gt": "0f41…", "h2": "cb87319f24560b5231579a09ad79f12e" }
]
}
```
- `g1`, `g2`: a point of G1 and one of G2 in the canonical compressed
encoding of spec §12.2, 48 and 96 bytes; the vector uses the generators.
- `gt`: e(g1, g2), the pairing of G1 × G2 of step 11, in 576 bytes. Over the
tower Fp2 = Fp[u]/(u² + 1), Fp6 = Fp2[v]/(v³ − (u + 1)) and
Fp12 = Fp6[w]/(w² − v), every element is written with its coefficients from
the highest degree to the lowest: c1 then c0 for Fp12, c2, c1 and c0 for
Fp6, c1 then c0 for Fp2, and each element of Fp in 48 bytes big-endian. It
is the order of kilic/bls12-381, which drand/kyber and tlock use.
- `h2`: SHA-256 of the ASCII bytes `IBE-H2` followed by `gt`, truncated to
its first 16 bytes, the length of V.
A reader checks that its pairing of `g1` and `g2` serializes to `gt` and that
its H2 of those bytes is `h2`; the vector fixes the pairing and the
serialization at once. The same 576 bytes with the twelve coordinates of Fp in
reverse order, c0 first at every level as `Fp12.toBytes` of `@noble/curves`
writes them, give `0118eea9d5971745f71e3c94926f1717` and another FK_TIME.
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>
24 hours ago
## `vectors/release.json` and `releases/`
The release object of spec v0.15, §47.1: the release of a round as data,
the answer of the Release API, an entry of a Release Cache and a release the
caller gives from a file or from an archive. It is deterministic CBOR with
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>
24 hours ago
the profile of §58, a map of five keys, all required:
```text
0 → "datekeys-release"
1 → 1
2 → chain_hash (32 bytes)
3 → round (1 to 2^53 − 1)
4 → signature (1 to 96 bytes; 48 in Quicknet)
```
The object of a round above 255 measures 111 bytes. `releases/<round>.cbor`
is the object of each published round the fixtures use, 1000, 1001, 1004 and
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>
24 hours ago
2000, with the Quicknet chain hash: the release that opens each fixture, as a
file. The protocol gives the object no file extension; `.cbor` is the generic
one of CBOR (RFC 8949).
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>
24 hours ago
`release.json` has three lists:
- `objects`: an `encoding` in hexadecimal, the `round` of the DateKey it is
checked against, and the `result`, `ok` or a code, with the `text` of the
error of the reference. When the encoding decodes, `release` is what it
says: `round`, `signature` and `chain_hash`. The checks are those of step
10 for a release in the caller's hand (spec v0.15, §63): the size of the
object, from 1 to 1024 bytes, before anything else; its type and version;
its encoding and schema (all three `ERR_NON_CANONICAL_CBOR`, but a version
other than 1, `ERR_UNSUPPORTED_VERSION`); then, against the pinned profile
and the DateKey, the chain hash (`ERR_PROFILE_MISMATCH`), the round
(`ERR_ROUND_MISMATCH`) and the signature (`ERR_RELEASE_INVALID`), in that
order. A case with several faults gets the code of the first.
- `json`: drand's JSON, which a reader accepts too as the input of the
caller, never as the release object: an `input` whose first byte other than
a JSON space is `{`. It has `round` and `signature` in hexadecimal, of either
case, and may have `randomness`, which must then be SHA-256 of the
signature; it names no chain, so its `release` has no `chain_hash`. Any
failure to read it is `ERR_RELEASE_INVALID`, and so is one of more than
8192 bytes; then the round and the signature, as for an object.
- `archive`: the lookups of the local archive `releases/archive_1000_1004.bin`,
an informative format (spec v0.15, §50). It is the `header`, the
deterministic CBOR map `{0: "datekeys-release-archive", 1: 1, 2: chain_hash,
3: first round, 4: number of rounds}`, followed by the 48-byte signature of
each round, one after another; a round the archive lacks is 48 zero bytes.
This one holds rounds 1000 to 1004, and 1002 and 1003 are zeros. Each lookup
gives a `round` and its `result`: `ok` with the `encoding` of the release
object the archive supplies, `releases/<round>.cbor` for that round, or
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>
24 hours ago
`ERR_RELEASE_UNAVAILABLE` for a round the archive lacks or does not cover.
The texts are those of the reference, for an implementation that wants to
match them; the codes are normative.
## `vectors/tlock_steps.json`
Steps 10 and 11 of spec §63 for Quicknet, every intermediate value written
out, over the published releases of rounds 1000, 1001, 1004 and 2000 (spec
v0.14: the paragraphs "Mensaje de ronda y hash a G1" and "H3 y H4" after the
flow).
```json
{
"spec": "0.15",
"description": "…",
"profile": "datekeys:quicknet:v1",
"scheme": "bls-unchained-g1-rfc9380",
"chain_hash": "52db…",
"public_key": "83cf…",
"dst": "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_",
"tags": { "h2": "4942452d4832", "h3": "4942452d4833", "h4": "4942452d4834" },
"vectors": [
{
"name": "round 1000, stanza 0",
"round": 1000,
"signature": "b446…",
"message": "f652…",
"hash_to_g1": "8f5a…",
"body": "a73e…", "u": "a73e…", "v": "f628…", "w": "b799…",
"pairing": "13dc…",
"h2": "…", "sigma": "…", "h4": "…", "file_key": "…",
"h3_base": "…",
"h3_tries": [ { "i": 1, "digest": "…", "shifted": "…", "accepted": true } ],
"r": "20fd…"
}
]
}
```
Every byte string is hex. The top-level fields are those of the pinned
profile (spec §12), the DST of the hash to G1 as text and the tags of H2, H3
and H4 as bytes: the ASCII of `IBE-H2`, `IBE-H3` and `IBE-H4`.
Step 10, the release:
- `signature`: the published signature of `round`, the release, compressed in
G1 (spec §12.2).
- `message`: M = SHA-256 of the round as 8 bytes big-endian, the message an
unchained drand scheme signs.
- `hash_to_g1`: H(M), hash_to_curve of RFC 9380 with the suite
`BLS12381G1_XMD:SHA-256_SSWU_RO_` and the DST `dst`, compressed. The
signature verifies: e(H(M), `public_key`) = e(`signature`, G2), with G2 the
generator of G2.
Step 11, one tlock stanza of the round:
- `body`: the stanza body U || V || W, 128 bytes, and `u`, `v` and `w` its
three parts: U compressed in G2, V and W of 16 bytes.
- `pairing`: e(`signature`, U), 576 bytes in the order of `tlock_ibe.json`.
- `h2`: SHA-256 of `IBE-H2` and `pairing`, truncated to 16 bytes.
- `sigma`: V XOR `h2`.
- `h4`: SHA-256 of `IBE-H4` and `sigma`, truncated to 16 bytes.
- `file_key`: W XOR `h4`, FK_TIME, the file key of OUTER_TIME_AGE.
- `h3_base`: SHA-256 of `IBE-H3`, `sigma` and `file_key`.
- `h3_tries`: the tries of H3, in order. Try `i` hashes the counter `i` as 2
bytes little-endian followed by `h3_base` (`digest`), then shifts the first
byte of the digest one bit to the right (`shifted`); the try is accepted
when `shifted`, read as a big-endian integer, is below the order r of the
groups (spec §12.2). Only the last try is accepted. The shift moves every
bit of the first byte; clearing only its top bit gives another r.
- `r`: the accepted `shifted`, the scalar of the check r·G2 = U.
The last vector is the first stanza of round 1000, in the order of the
generator, whose H3 needs at least three tries: it accepts its fourth, where
clearing the top bit would accept the second. A reader checks every value
in this order and the check r·G2 = U.
The generator chooses `sigma` and `file_key` per stanza, derives r, U, V and W
with its own H2, H3 and H4, and checks the result against the libraries the
reference uses: `message` against `DigestBeacon` of the drand scheme,
`hash_to_g1` against the pairing equation with the published signature, and
the body against `tlock.TimeUnlock`, `DecryptCCAonG2` of drand/kyber and the
tlock identity of `agewrap`, which give back `file_key` only if their H2, H3
and H4 are the ones written here.
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
## `vectors/padding.json`
The padding of the payload of a capsule of format 2 or 3, spec §29.1, where L
is the length of BODY in format 3: for each content length L, the length P of the plaintext of PAYLOAD_AGE with each code, and the
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
length of PAYLOAD_AGE.
```json
{
"spec": "0.11",
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
"l_max": 8936830510563328,
"vectors": [
{ "l": 78000, "bloque256": 78080, "reforzado": 79872, "payload_age_bloque256": 78296, "payload_age_reforzado": 80088, "e": 16, "s": 5, "last_bits": 11 }
],
"rejected": [8936830510563329, 9007199254740992]
}
```
- `l`: L, the length of the content.
- `bloque256`: P with code 1, 256 · ⌈L / 256⌉, at least 256. `reforzado`: P
with code 2, the larger of that and Padmé.
- `payload_age_bloque256`, `payload_age_reforzado`: the length of PAYLOAD_AGE
for each P, 184 + P + 16 · max(1, ⌈P / 65536⌉): an age header of one X25519
stanza (168 bytes), the 16-byte nonce, and the P bytes in STREAM chunks of
64 KiB with a 16-byte tag each.
- `e`, `s`, `last_bits`: the intermediate values of Padmé, E = bitlen(L) − 1,
S = bitlen(E) and E − S; only for L > 256, and informative.
- `l_max`: L_MAX = 2^53 − 2^46, the largest L.
- `rejected`: lengths above L_MAX, which a writer rejects and which, as key 6
of a control, are `ERR_NON_CANONICAL_CBOR`.
Every number is below 2^53, so a JSON number holds it exactly. The lengths
include both sides of the boundaries where 32-bit arithmetic breaks
(2^31 − 2^25, 2^32 − 2^26, 2^32) and 2^49 − 1, the first L for which a
floating-point logarithm gives an E one too large (P does not change).
## `vectors/paths.json` and `vectors/path_fold.json`
The rules of the paths of a format 3 head, spec §29.5, with the tables of
§29.5.1. Both files name the tables they were computed with:
`"unicode_version": "18.0.0"` and `tables_digest`, the SHA-256 of the
canonical text of the generated tables, which a second implementation that
generates them from the same files can recompute.
```json
{
"paths": [ { "name": "TAB", "path": "a\tb", "result": "R4: segment 1: control U+0009" } ],
"trees": [ { "name": "A.txt and a.txt", "paths": ["A.txt", "a.txt"], "result": "ERR_HEAD_INVALID", "detail": "R7: path 2 collides with path 1 in segment 1" } ]
}
```
- `paths`: one path and the rules of one entry, R2, R3, R4, R4b, R5, R6, R6b,
R6c and R10, in that order; `result` is `ok` or the violation of the first
rule that fails, as every implementation must word it: the rule, the
segment when it is about one, and the character as `U+` and at least four
upper-case hexadecimal digits, never the text itself.
- `trees`: the paths of a head, each a file of 0 bytes, in the order given,
and the result of decoding that head (see `head_schema.json`): R1 and R8
in layer 3 (`ERR_NON_CANONICAL_CBOR`), then each entry and R7 and R9 over
the tree in layer 4 (`ERR_HEAD_INVALID`, with its `detail`).
Among them: U+00A0, accepted, and U+3000, refused by R6c, at both ends of a
segment; the best-fit projections, the full-width forms of '/', '\', ':'
and '.', and CON.txt in full-width forms; 8.3 aliases, `~1` alone included;
unassigned code points and noncharacters; U+206A to U+206F, the tags and other
ignorables outside the whitelist; a dot followed by ZWJ and a segment of ZWJ
alone; 127 and 85 times U+0390; U+F03A; `.datekeys-x` at the first level and
at another; 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, accepted, and the flag of
Scotland, refused; and `["b/..", "a"]`, an error of R8 before R3.
`path_fold.json` gives, for each `segment`, its `nfd` and its `key` of R7,
NFD(fold(NFD(s'))) with s' the segment without ZWNJ, ZWJ, VS15 and VS16:
among them the entries F of CaseFolding, the dotless i, the Kelvin and
Angstrom signs, Cherokee, Hangul, the canonical order of two marks, and the
whitelist dropped before NFD.
## `vectors/head_schema.json`
Heads of format 3, HEAD_CBOR, and the result of decoding them with no
extension known (spec §29.4 to §29.6): `ok`, or the code of the first
failing layer of §69.1. A head is decoded alone: the frame of BODY that
bounds it and the files it lays out are not checked here (spec §63 steps
17.2 and 17.5).
```json
{ "name": "a comment with U+202E", "hex": "a4006d…", "result": "ERR_HEAD_INVALID", "detail": "comment: text: bidirectional control U+202E" }
```
- Layer 2: the type tag and version 1; another type tag is
`ERR_NON_CANONICAL_CBOR`, another version `ERR_UNSUPPORTED_VERSION`.
- Layer 3: the CDDL with its sizes and ranges, R1 and R8, and the equality
with the re-encoding: `ERR_NON_CANONICAL_CBOR`.
- Layer 4, in key order: the comment and the declared author (§29.6), then
each file with R2 to R6c, R4b and R10 and its layout, then R7 and R9 over
the tree, all `ERR_HEAD_INVALID`, and then the critical extensions,
`ERR_EXTENSION_CRITICAL_UNKNOWN` with no extension known.
- `detail`, for `ERR_HEAD_INVALID` only: the violation as every
implementation must word it, `comment: text: …`, `declared author:
text: …`, `file N: …` for an entry, counted from 1, or the violation of the
tree.
Besides the cases of each layer, it has a comment with tags that spell a
text, one with VS16 after a letter and one with a run of variation
selectors, one with VS17, one with CR LF, and the precedence of a comment and
a path that both break, and of a path that breaks and an unknown critical
extension.
## `vectors/security.json`
Security areas of format 3, SECURITY_CBOR exactly as its SECURITY_LEN bytes,
with their verdicts and the lines that show them (spec §29.3, §29.7, §29.9),
which never stop the opening. Every area is evaluated in the `context` of the
file, what a verdict needs besides the area: `control_commit` and
`head_digest` in hexadecimal, and `round_time` in RFC 3339.
```json
{
"context": { "control_commit": "0101…", "head_digest": "0202…", "round_time": "2030-01-01T00:00:00Z" },
"vectors": [ { "name": "a signature of alg 4294967295, reserved for tests", "hex": "a300…", "signature": "F1", "seal": "S0", "lines": ["No se ha comprobado ninguna firma: trátala como no firmada."] } ]
}
```
X stands for both, when the outer map fails its layer 2 or 3: key 2 that is
not a byte string, or an empty one, an unknown key 4, a byte more after the
map, version 2, another type tag, keys out of order, an array. Otherwise the
signature and the seal are evaluated apart, and the first row of the table of
§29.7 that holds decides:
- an empty area, as a writer without a signer writes it: F0 and S0;
- a signature of `alg` 1 that verifies over the context: F4, and one that
does not: F2;
- `alg` 0 or 4294967295, an empty key, or content that is not CBOR or has a
byte more: F1, with the seal intact;
- a seal of `seal_type` 1, 3 or 4294967295: S1; one of `seal_type` 0, one that
breaks its schema, even with an unknown `seal_type`, which is read only from
a seal that meets it, and one of `seal_type` 2 whose token is not DER: S2.
`lines` are the verdicts as §29.7 words them, which an implementation writes
byte for byte. The valid signatures of `alg` 2 and seals of `seal_type` 2 are
in `security_cms.json`.
## `vectors/security_cms.json`
Security areas with an author signature of `alg` 2, a CMS signature with
certificates, or a time seal of `seal_type` 2, an RFC 3161 token: 135 cases,
each with the context of its capsule, the verdicts, the result of each signer
and the lines of v0.12, §29.7, §29.10 and §29.11. They complete
`security.json`, whose areas have no valid signature or seal of these kinds.
The file is frozen: the certificates and the tokens are made once, with test
keys, so a second implementation reads them and must reach the same verdicts
and write the same lines. Delete the file to make it again.
```json
{ "name": "alg 2: a required signer is absent: F5", "security_cbor": "a3…",
"context": { "control_commit": "…", "head_digest": "…", "round_time": "2030-01-01T00:00:00Z" },
"signature": "F5", "seal": "S0",
"signers": [ { "holder": "Ana López", "issuer": "Ana López", "result": "valid", "seal_time": "2026-09-30T12:00:00Z", "before_round_time": true }, … ],
"lines": ["…"] }
```
- `context`: what a verdict needs besides `SECURITY_CBOR`, as in
`security.json`.
- `signers`: the results of the required signers, in the order of SIGNERS;
`foreign_signers`: those of the signers that are not required, which never
count. Each has the `holder` and the `issuer` as §29.7 shows them, its
`result` (`valid`, `invalid`, `absent`, `without seal`, `invalid seal`,
`out of validity` or `not verifiable`), the `seal_time` of its CAdES-T when
it has one, and `before_round_time`, whether that time plus its accuracy
precedes the round time.
- `seal_holder` and `seal_time`: the authority and the time of a valid seal
of key 3.
- `lines`: the verdicts as the official SDK shows them (§29.7), byte for byte:
the names between « and », the line of each signer with its authority, the
warning that DateKeys does not check who issued the seals, and the times in
RFC 3339 with the fraction of the token.
A time is in RFC 3339, with the fraction of the token when it has one. The
cases follow each row of §29.7 and each item of the lists of §64 for v0.11
and v0.12: F6 with two signers, after the round time, with a signer who is not
required and with 16 signers; F5 for an absent signer, a withdrawn CAdES-T, no
seal, a certificate out of validity and keys outside the table; F2 in the
context of another head and for a message-digest of another message; F1 for
SIGNERS that break its rule beside a valid CMS, BER, two SignerInfo of one
certificate, the version against the `sid`, two content-type attributes, the
ESSCertIDv2 and the certificate of the signer; every hash and curve of the
table, RSASSA-PSS with and without `trailerField`, an attribute with an arc of
2^31 and a certificate twice; the names of the holder and of the issuer in
each string type and against each rule, `givenName` and `surname` before a
`commonName` with its NIF included; and, over an `alg` 1 signature, the seals
S1 to S5 at the edges of the token: its accuracy, its `genTime`, `ordering`,
a field after the last, the imprint, `crls` and the authority.
## `vectors/locator.json`
The extension `datekeys.capsule` of a `.dkk` and what it points to (spec
§44.1): a `.dkc` of patterned bytes in an envelope of age whose header
(`envelope_header`) goes in the locator and whose `rest`, without a mark, is
hidden in a `host` file at `host_offset`; the locator sealed with tlock for
round 1000 (`locator_sealed`), with its plaintext of 4096 bytes
(`locator_plaintext`) and its fields; and the data of the extension
(`extension_data`), with the note `note` and the DateKey `datekey`. A reader
opens the locator with the release of round 1000, which `mutations.json` and
the records of the fixtures of that round give, finds the rest in the host,
checks `rest_size`, `rest_digest` and `capsule_digest`, and gets the `.dkc`
back. The file is frozen: the envelope and the locators hold randomness.
On the same envelope, the cases of §64, each checked against the reference
when the file is made:
- `padding_cases`: the length of the plaintext of a locator for `base`, the
length of its CBOR without key 6: the least multiple of 4096 that holds it,
or the next one when key 6 cannot complete it, because 1, 2, 26 or 259
bytes are missing (§44.1). Among them the bases 3837, 4070, 4094 and 4095,
which give 8192, those of the next multiple, which give 12288, and their
neighbours.
- `uri_cases`: an address and whether the rules of §44.1 accept it (`ok`):
the scheme, the authority without a percent sign, userinfo or a backslash,
the port, each character outside RFC 3986 and a percent sign without two
hexadecimal digits, the "." and ".." segments, written or with `%2e`, in the
path but not in the query or the fragment, the first and the last address
of each IPv4 block of §44.1 with the public addresses next to them, the
IPv6 blocks and the addresses that hold an IPv4 one, the names that only a
machine or a local network resolves, a last segment that is numeric or
starts with `0x`, and base32 that is not a CID v1. Then what the text of
v0.12 fixes besides: segments of 63 and 64 characters, or with a hyphen at
an end, the scheme, `0X` and the local names in upper case, an IPv4 and a
port with leading zeros, and a CID with the bits left over not zero, with
padding, in upper case, with a varint that is not minimal, with an empty
digest, or of 128 and 136 characters.
- `mixed`: a second locator of the envelope, sealed for round 1000 too. Of its
`addresses`, a reader rejects the first two, an `http` one and one of NAT64
that leads to 127.0.0.1, and uses the third (`usable`), which finds the rest
in `host`: the locator reads all the same. A writer never writes it.
- `rest_cases`: a `resource` as a reader downloads it, the `offset` that an
address gives, and whether the rest read there opens the envelope (`opens`):
the rest alone, the host with bytes after the rest, of which only
`rest_size` bytes from the offset are read, a byte of the rest changed, an
offset that is not its own, and a rest cut short.
- `extension_cases`: data of `datekeys.capsule` and whether a reader can use
it (`ok`): without a locator, and unusable, with only
`ERR_EXTENSION_DATA_INVALID`, for a locator sealed for round 1001 beside a
DateKey of round 1000, a locator that is not an age file or is empty, no
DateKey or one that is not canonical, a note that breaks its rules and a
key 3.
- `plaintext_cases`: plaintexts of a locator of the envelope, each with the
defect that its name says or none, and whether a reader reads them (`ok`).
The bases 4094, 4070 and 3837 completed to 4096 with an empty key 6 or with
its length not in its shortest form, against the 8192 bytes of the base
4094 (§76 of v0.12, change 7); eight addresses and nine, none, an empty one
and one of 1025 bytes, which make the whole locator unreadable, an offset of
0 written, an offset and a `resto_size` of 2^53; two blocks where one
suffices, padding that is not zeros, a byte less and a byte more. An
address that breaks the rules of §44.1 does not make a locator unreadable:
that is `mixed`.
## `vectors/note.json`
The data of the public note, `datekeys.note` version 1 in the noncritical
array of PUBLIC_HEADER (spec §24.1): text in UTF-8, from 1 to 1024 bytes, that
meets the rules of the declared author of §29.6. A writer writes a note only
when its `result` is `ok`; a reader shows it only then, and otherwise treats
the note as unusable, never the capsule (§54), and says so.
```json
{ "name": "a bidi override", "data": "61e280ae62", "result": "ERR_EXTENSION_DATA_INVALID", "detail": "a public note that breaks the rules of text: text: bidirectional control U+202E" }
```
`detail` is the text of the rule that a note breaks, without the code, as the
reference words it. Among the cases: letters that are not ASCII and an emoji,
an emoji with VS16, 1024 bytes, accepted; no byte, 1025 bytes, a tab, a line
feed, a space at either end, U+202E, U+200B, a byte order mark, a
noncharacter, a byte that is not UTF-8 and the UTF-8 of a lone surrogate,
refused.
## `vectors/resolved_ip.json`
The IP address that the name of an https address of a locator resolves to,
which a reader checks on every connection (spec §44.1 of v0.13):
`ip`, `nat64`, the NAT64 prefix of the network that the reader knows, or ""
for none, and `result`, `ok`, or `error` with the text of the reference in
`error`.
```json
{ "name": "the well-known prefix with 192.168.1.10", "ip": "64:ff9b::c0a8:10a", "nat64": "", "result": "error", "error": "locator: an https address whose name resolves to 64:ff9b::c0a8:10a, an address of NAT64 that holds 192.168.1.10, an IP address that is not public" }
```
A public address is accepted. An address of NAT64 (RFC 6052) of
`64:ff9b::/96`, or of the prefix of the network, counts by the IPv4 address
it holds, at the positions of RFC 6052: the cases put a public one and one of
several blocks that are not public in each, and the prefix of the network in
each length of RFC 6052. A prefix of another length, with bits after its
length, outside `64:ff9b::/16` and the public IPv6 addresses, or of IPv4, is
refused. Among the addresses that are not public: IPv4-mapped, 6to4, Teredo,
link-local, unique local, loopback, and the local-use prefix of RFC 8215
without the prefix of the network.
## `vectors/wordkey.json`
The key of words of spec §38.1, the cases that §64 of v0.11 asks for, in
three lists:
- `normalize`: a `text` and its `words`, after NFD with the tables of Unicode
18.0.0, without U+0300 to U+036F, with the simple lower case of each code
point and split by the spaces of the list of §38.1. Among the cases: the
vector of §38.1; «Ábaco ÁRBOL», «abaco arbol» and «ábaco árbol», which give
the same words; accents written as combining marks; punctuation, which
counts; a final sigma, a capital sharp s, a dotless i, the angstrom sign,
Hangul, fullwidth letters and U+A7CB, whose lower case is that of Unicode
18.0.0; each of the 25 spaces of the list, one by one; and U+200B, U+180E
and U+FEFF, which are not spaces.
- `check`: a `text`, its `words` and what a writer does with them: `result`
`ok`, or `error` with the text of the reference in `error`. At least six
different words of three characters or more; a control, a
Default_Ignorable code point (U+200B, the soft hyphen, a variation
selector, U+FEFF) or a code point unassigned in Unicode 18.0.0 is refused,
and a private use code point or an emoji is not.
- `keys`: the identity of `words` for the capsule `capsule_id` of `round` of
the chain `chain_hash`, PBKDF2-HMAC-SHA256 of 600000 rounds: `key`, the raw
X25519 identity, and `recipient`, its `age1…` recipient. The vector of
§38.1; the next round, another `capsule_id` and another chain hash, each a
different key; round 2^53 − 1; and words that are not ASCII.
```json
{ "name": "a zero width space, which the writer refuses", "text": "perro luna casa verde tren mar\u200b", "words": ["perro", "luna", "casa", "verde", "tren", "mar\u200b"], "result": "error", "error": "wordkey: the words hold the invisible character U+200B" }
```
The texts are valid UTF-8: a reader of JSON in another language cannot hold
anything else in a string.
## `vectors/ed25519_strict.json`
Ed25519 signatures, in hexadecimal, and whether the strict profile of the
author signature accepts them (spec §29.9): the equation of RFC 8032
without the cofactor, A and R canonical, S below ℓ and A not of small order.
They follow the cases of «Taming the many EdDSAs»: S + ℓ, the top bits of S, a
non-canonical R, the eight points of small order as A, non-canonical
encodings of A, a key of mixed order with and without the cofactor, and an R
of small order with a key of prime order, which this profile accepts.
```json
{ "name": "A of small order, the point 0 of the torsion, R the identity and S = 0", "message": "…", "public_key": "0100…", "signature": "0100…", "valid": false, "stdlib": true }
```
`stdlib` is what `crypto/ed25519` of Go answers, for the record: where it is
true and `valid` is false, an implementation needs the checks of the profile
before the equation, as `internal/ed25519strict` does.
## `vectors/mutations.json`
The mutation corpus of spec §64, as frozen data. Each case is a `.dkc`, what
the reader is given to open it, and the exact error and step at which the full
reading flow (`capsule.Open`, §63) must fail.
```json
{
"name": "version changed",
"spec": true,
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
"dkc": { "base": "time_only.dkc", "edits": [[4, 1, "04"]] },
"release": { "round": 1000, "signature": "b446…" },
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>
24 hours ago
"source": "supplied",
"now": "2023-08-23T15:59:24Z",
"registry": "default",
"network": false,
"frozen": false,
"error": "ERR_UNSUPPORTED_VERSION",
"step": 2
}
```
- `name`: unique, stable.
- `spec`: true for the 178 mutations listed in spec §64, false for the further
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
cases of the reference. The cases come in this order: the 33 mutations of
the first two lists of §64 on the format 1 fixtures (cases 1 to 33), 32
further cases (34 to 65), the same 33 mutations on the format 2 fixtures,
named "format 2: …" (66 to 98), the 23 of the list of format 2 (99 to 121),
5 further cases (122 to 126), the same 33 on the format 3 fixtures, named
"format 3: …" (127 to 159), the 48 of the list of format 3 (160 to 207), the
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>
24 hours ago
8 of the list of v0.11 that a capsule can hold (208 to 215), 3 further
cases (216 to 218), and the 4 cases of the source of the release of v0.15
(219 to 222). A line of the lists of §64 with several values, such as
"AREA_LEN 0, 511, 513 o 66048", is one case for each.
- `dkc`: the capsule, as edits of a fixture (see above). The reader gets it as a
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
seekable file, so that the `capsule_digest` of an offered `.dkk` is checked
before any release request (spec §63 step 9.a).
- `dkk`: the hex of a complete `.dkk` file (prelude and body) offered as the
access credential; absent when none is offered.
- `identities`: age X25519 identities (`AGE-SECRET-KEY-1…`) offered as access
credentials; absent when none.
- `release`: what the release source answers to every request, whatever round
is asked for. The reader must verify it (§51): a case may serve a release of
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
another round, a round with the signature of another, or a signature that is
not the canonical encoding of a point (§12.2). `null` means that no release
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>
24 hours ago
is available (`ERR_RELEASE_UNAVAILABLE`). `chain_hash`, present in two
cases only, is the chain the release object names when it is not the
Quicknet chain.
- `source` (v0.15): what kind of source answers, spec v0.15 §63 step 9:
- `supplied`: the release is in the caller's hand, as a release object
read from a file would be.
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>
24 hours ago
The reader is given the release object of `release`, with the Quicknet
chain hash unless `chain_hash` says another, and decodes and verifies it
at step 10: one that breaks a rule of step 10 gets the code of step 10.
The clock is not compared with the round time (step 9.c): with a `now`
before it, the capsule opens all the same. Every case but two is
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>
24 hours ago
`supplied`.
- `network`: a network source, such as a drand relay. It is never asked
before the round time (step 9.c), and it verifies its answer with the
rules of step 10 and discards it when it fails, so the reader gets no
release: `ERR_RELEASE_UNAVAILABLE` at step 9.
- `now`: the reader's clock, RFC 3339. A network source is not asked before
the round time of the DateKey; a release in hand is not compared with it.
- `registry`: `default` pins exactly the Quicknet profile of
`profile_quicknet.json`; `empty` pins none.
- `extensions`: the extensions the application implements. Each entry is known
at `(id, version)`, and its data is valid only when it equals the bytes of
Spec v0.8.2: second-round corrections from the formal review The second round of the formal review confirmed the nine corrections of c57ed48 and asked for these, recorded in §76 as corrections 4 to 6 and an editorial note: - §72: an encoder MUST NOT write a registered extension in an object or array it is not registered for; §54: a reader MUST NOT interpret the data of a noncritical one it ignores for that reason. capsule.Encrypt and accesskey.Encode take no Registry, so the application applies the rule; their documentation and extension.Placement say so. - §17 and §51 give the step-10 codes only for a directly supplied release, as step 10 does; a network source discards a failing one at step 9. - Step 9 reports ERR_RELEASE_UNAVAILABLE and no other code, whatever the failure of the source. provider/drand.Client keeps each relay's failure as text only (errors.Join made a relay's ERR_ROUND_MISMATCH match with errors.Is), and capsule.Open keeps only the text of a source error that carries another code (a caller's source failing with ERR_RELEASE_INVALID gave that code at step 9). A context that ended stays detectable: Fetch now has a single failure path, so the canceled and deadline cases are deterministic. - TestExtensionPlacement covers the noncritical array of a .dkk: with the object-blind extension.CheckNoncritical at step 9.a it fails. - Editorial: §28.1 "analizan solo la cabecera age", one arrow at step 9, two §76 introductions; §73 lines for release sources and placement. - testdata/README.md says the corpus registers its extensions in both arrays of every object; traceability, CHANGELOG and both READMEs (integrity holds against whoever lacks the file keys, §27, §55.1) follow. Spec dated 28 September 2026; new SHA-256 in spec/README.md. No fixture or vector changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
`valid_data`. Each entry is registered in both arrays of every object
(§72), as a Go `extension.Registry` that does not implement
`extension.Placement`: an extension outside its registration counts as
unknown there (§54), so the cases "known critical PUBLIC_HEADER extension
with invalid data", "known critical CONTROL_CBOR extension with invalid
data" and "known critical .dkk extension with invalid data", at steps 4, 14
and 9, depend on it. Absent: the application knows no extension, the state
of the base protocol V1.
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>
24 hours ago
- `network`: whether the failure may come after a release request, to a
network source or to the caller's release in hand. When false, the reader
must fail without requesting any release (§27, §63): every failure of steps
1 to 8 and of step 9 before the request (9.a to 9.c).
- `frozen`: the capsule was built once with age randomness; its bytes are kept
and never regenerated. These cases have no `base`.
- `error`, `step`: the expected code and the step of §63 that fails. For a
capsule that opens, `error` is `ok`, `step` is 0 and `verdicts` holds the
verdicts of its security area, `signature` and `seal`, with the `lines`
that show them (spec §29.7): the four cases of security of the list of
format 3, and seven of the list of v0.11.
Every case reproduces offline: the recorded release stands in for the network.
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>
24 hours ago
What v0.15 changed in this file: every case gained `source`, `supplied` but
for two; the further case "round not reached yet", a valid release of round
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>
24 hours ago
1000 and a clock one nanosecond before its round time, was
`ERR_RELEASE_UNAVAILABLE` at step 9 and now opens (`ok`, step 0, with
`network` true), because the release is in the caller's hand; and four cases
were added at the end: the same capsule and clock with a network source,
still `ERR_RELEASE_UNAVAILABLE` at step 9 without any request; a release of
another round from a network source, discarded, `ERR_RELEASE_UNAVAILABLE` at
step 9, where the same release in hand is `ERR_ROUND_MISMATCH` at step 10;
and two release objects of another chain, `ERR_PROFILE_MISMATCH` at step 10,
one of them of another round too and with a clock behind. No other case
changed its result.
A reader that implements only steps 1 to 8 can replay every case whose `step` is
at most 8: 57 cases, 39 of them from §64. Steps 1 to 8 are summarised in "The
checks of steps 1 to 8" below.
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
### Credentials and the release: step 9
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
What happens between step 8 and the release request is spec §63 step 9: for
`time_and_key` only, an offered `.dkk` is checked as an object and then bound
to the capsule (9.a), at least one credential must be offered (9.b), and for
either policy a `now` before the round time of the DateKey fails without a
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>
24 hours ago
request to a network source (9.c), while a release in hand is not compared
with it; only then is the release requested. For `time_only` the
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
credentials play no part: the §64 case "access_policy=time_only with
time_and_key structure" offers a `.dkk` whose `capsule_digest` is that of the
unmutated capsule, and fails at step 12, not at step 9. The codes after the
request, for the release (step 10) and for the identities that open each age
file (steps 11, 13 and 17), are those of spec §63 as well. In this corpus:
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
- identities are not examined before the release (spec §63 step 13);
- a `release` of `null` is `ERR_RELEASE_UNAVAILABLE` at step 9;
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>
24 hours ago
- a `release` of a `supplied` case is in the caller's hand, so an invalid one
fails at step 10, and step 9.c does not apply to it; a `network` source
discards it at step 9, and is not asked before the round time;
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
- every `.dkk` offered decodes: the corpus checks step 9.a, not the decoding
of a `.dkk`, whose errors spec §63 also places at step 9.a.
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
### Point encodings: steps 10 and 11
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
Ten cases of §64, in each format, test the canonical point encoding of spec
§12.2 and the tlock stanza body `U || V || W` of spec §63 step 11:
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
- five edit the tlock stanza body of `time_only.dkc`: U with c0 + p, U the
point at infinity (the byte 0xc0, then zeros), U with the infinity flag
over its own coordinate bits, and bodies of 127 and 129 bytes. Each recomputes the header
MAC of OUTER_TIME_AGE with FK_TIME, so the age header stays authentic and
only the rules of step 11 reject the capsule (`ERR_INTEGRITY`); a reader
whose decoder reduces coordinates modulo p opens the first one;
- three serve the release of `time_only.dkc` with its signature edited: the
point at infinity, the infinity flag over its own coordinate bits, and the
negated signature (the sort flag flipped: a canonical point that does not
verify); a fourth serves the published signature of round 1004 re-encoded
as x + p, over a frozen capsule for round 1004, because no fixture round
has a signature whose x + p fits in 381 bits. All four fail at step 10
(`ERR_RELEASE_INVALID`);
- the last one serves the negated signature with U with c0 + p: step 10
comes first.
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
In format 2 the same ten edit `format2_time_only.dkc` and its release, and the
x + p case has a frozen format 2 capsule for round 1004.
### Format 2: its list of §64
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
The 23 cases of the list of format 2 of §64 test what format 2 adds:
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
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
- the format against the rest of the capsule, with another `VERSION`, a
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
public byte (§22, §76): a format 1 `time_and_key` capsule of one stanza
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
relabeled format 2 fails at step 12, and the other relabelings, format 2 as
1 or 3 and format 1 as 2, at step 14, where the schema version of the
control is not the format;
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
- the 16 stanzas of INNER_ACCESS_AGE: 15, 17, two for one recipient, and an
identity that opens none of them, at steps 12 and 13;
- keys 6 and 7 of CONTROL_CBOR, at step 14;
- the plaintext of PAYLOAD_AGE, at step 17: a padding byte that is not zero,
a plaintext of P − 1 or P + 256 bytes or without its padding, and a control
that declares another code or another L.
Each derives from a fixture without randomness. What it changes is sealed again
with the file keys that the release and the credentials of the fixture open,
FK_TIME and FK_ACCESS, or that its I_PAYLOAD unwraps, FK_PAYLOAD, and with the
nonces of the fixture; a stanza it adds takes its ephemeral scalar from a fixed
seed. So each case is a `base` with edits, and regenerates byte for byte. A
case that changes a `time_and_key` capsule offers the identity of its `.dkk`,
because the `.dkk` itself would fail at step 9.a: its `capsule_digest` no
longer matches. Five further cases, 122 to 126, offer the `.dkk` and show
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
that.
### Format 3: its list of §64
The 48 cases of the list of format 3 of §64 test what format 3 adds, all at
step 17 but the first:
- `VERSION` 2 on a format 3 capsule, at step 14 (`VERSION` 4 is
"format 3: version changed", as in format 2);
- the frame of BODY and the area (§29.2): each value of `AREA_LEN`,
`SECURITY_LEN` and `HEAD_LEN` that §64 lists, a frame that does not fit in
L, L < 12, a byte of the area that is not zero, all `ERR_INTEGRITY`;
- the head (§29.4 to §29.6): its version and type tag, a byte more within
`HEAD_LEN`, R8 and R1 in layer 3, and in layer 4 the paths, the comment,
the declared author, the layout and the tree, 65536 implicit folders
included;
- the files: the end of the last file that is not C, and a byte of a file
changed, `ERR_INTEGRITY`;
- the precedence of the end of step 17: a path `..` with a padding byte that
is not zero is `ERR_HEAD_INVALID`, but with the next STREAM chunk corrupt,
or with PAYLOAD_AGE cut right after the chunk that holds the head, it is
`ERR_INTEGRITY`;
- four cases of security that open, without a code, with the verdicts X,
F1, F2 and S1: a security map of version 2, a signature of `alg`
4294967295, a signature of `alg` 1 that does not verify and a seal of
`seal_type` 4294967295.
They derive from `format3_single`, and the two that need a head followed by
another chunk from `format3_tree`. What a case changes in BODY is sealed again
with FK_PAYLOAD and the nonce of its fixture, followed by the zeros of the
padding up to the P of its L, and when L changes the control is sealed again
with the new L, as anyone can seal the control of a `time_only` capsule
(spec §36.1). The case of 65536 implicit folders carries a head of 235 KB,
2115 paths, all but the last of 32 segments; it is most of the size of the
file. The three
further cases relabel format 3 as 1, and a `time_and_key` capsule as 2 with
its identity and with its `.dkk`.
### Signature, seal and note: the list of v0.11
Eight cases of the list of v0.11 of §64 change a capsule; the rest of that
list is in `ed25519_strict.json`, `security_cms.json`, `note.json` and
`locator.json`, and in the fixtures `format3_unsigned` and `format3_signed`,
which have the same P. Seven open with their verdicts:
- on `format3_signed`, the signature of `alg` 1 altered (F2), removed (F0),
made again with another key (F4, with the key of that signature), with a
key of 31 bytes or a signature of 65 bytes (F1), and the area widened from
32 KiB to 64 KiB after signing, which leaves the signature valid and
`AUTHOR_MESSAGE` unchanged (F4);
- the signature of `format3_signed` transplanted to `format3_unsigned` (F2).
The eighth changes the public note in the PUBLIC_HEADER of `format3_note`:
`ERR_HEADER_BINDING` at step 15.
## The checks of steps 1 to 8
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
`mutations.json` and `inspect_differential.json` follow the rules of the
specification for steps 1 to 8, with the `default` registry (Quicknet pinned),
no extension known unless a case lists some, and no secret. The first failure
ends the flow, and within one object the first failing layer decides the code
(spec §69.1):
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
| Step | Rules | Spec |
|---|---|---|
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
| 1, 2 | magic, truncated prelude, `VERSION` 1, 2 or 3 (the format), FLAGS and RESERVED, `PUBLIC_HEADER_LEN` in 1 to 1048576 and `SEALED_CONTROL_LEN` in 1 to 67108864 | §22, §23 |
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
| 3 | the PUBLIC_HEADER bytes are present | §23 |
| 4 | PUBLIC_HEADER: layers 2 to 4, the `public_header` decoder of the schema vectors, then the pinned profile of the DateKey and the critical extensions; with no extension known, every `critical_extensions` array fails | §63, §69.1 |
| 5 | the SEALED_CONTROL bytes are present, then its age header, parsed within `SEALED_CONTROL_LEN` bytes: malformed is `ERR_INTEGRITY`, one tlock stanza is required | §28.1 |
| 6 | the age header of PAYLOAD_AGE, from its offset to the end of the file: malformed is `ERR_INTEGRITY`, one X25519 stanza is required | §22, §28.1 |
| 7 | the round time of the DateKey is at most 9999-12-31T23:59:59Z (Quicknet: round 83903165811 at most); a `dk1_` round above it passes step 4 and fails here | §15 |
| 8 | the tlock stanza has exactly two arguments, the canonical decimal round and the lowercase hex chain hash, compared as strings | §63 step 8 |
The reference parses age headers with `filippo.io/age` v1.3.2, whose parser
limits (1024 stanzas, 128 arguments after the type, 2 MiB) spec §74 lists as
implementation limits. No case of these corpora depends on them.
## `vectors/inspect_differential.json`
A differential corpus of the pre-unlock checks: 5110 deterministic mutations of
fourteen official `.dkc` fixtures, 365 of each, with the verdict of steps 1
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
to 8 of §63 as the reference computes it (`capsule.Inspect` with the `default`
registry, no extension known, no network, no secret), by the rules that "The
checks of steps 1 to 8" above points to. The first 1825, those of the five
format 1 fixtures, are the corpus of v0.8.2 unchanged; the seven format 2
fixtures follow, and then two of format 3, `format3_single` and
`format3_time_and_key_portable`, one for each policy: steps 1 to 8 see nothing
of format 3 that format 2 does not have, but `VERSION`. The `format` field of the file describes the layout below in
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
words; it is not a capsule format.
```json
{
"seed": 20260925,
"bases": [ { "file": "time_only.dkc", "sha256": "99e9…" } ],
"mutations": [
{"base":0,"kind":"flip","edits":[[121,1,"b3"]],"result":"ERR_NON_CANONICAL_CBOR","step":4},
{"base":0,"kind":"flip","edits":[[725,1,"4d"]],"result":"ok"}
]
}
```
- `bases`: the fixtures, with the SHA-256 of their exact bytes. `base` in a
mutation is an index into this list.
- `edits`: see "Edited files".
- `result`: `ok` when steps 1 to 8 pass, or the error code; `step` is the step
that failed, absent when `ok`.
- `kind` names the generator and is informative: `flip` (one bit), `byte` (one
byte replaced), `truncate`, `insert` and `delete` (one to four bytes),
`length` (PUBLIC_HEADER_LEN and SEALED_CONTROL_LEN), `header` (PUBLIC_HEADER
re-encoded with one CBOR-aware change: a key removed, added or retyped, the
type tag, version, `capsule_id`, DateKey or `access_policy` changed, an
extension array added, a head not in its shortest form, keys out of order or
repeated, the whole item tagged, wrapped, made indefinite, truncated or
followed by bytes), `datekey` (the DateKey string alone), `age` (the age
header of SEALED_CONTROL or PAYLOAD_AGE edited: intro line, stanza type,
arguments, stanzas added or removed, body lines, MAC line, line endings).
Most `header`, `datekey` and SEALED_CONTROL `age` mutations also rewrite the
prelude lengths to match; some keep the old ones on purpose.
- `seed` seeds the generator of the reference and is informative too: every
mutation is stored explicitly.
## `fixtures/<name>.inspect.json`
For each official `.dkc`, the exact bytes that `datekeys inspect -json -in
<name>.dkc` prints when run in `testdata/fixtures`: JSON indented with two
spaces, fields in this order, and a final newline. The pre-unlock checks use
the `default` registry.
| Field | Content |
|---|---|
| `file` | the `-in` argument, `<name>.dkc` |
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
| `format` | the capsule format, once step 2 has passed |
| `capsule_id` | hex, once step 4 has decoded the header |
| `datekey`, `profile`, `round` | the canonical `dk1_` string, its profile and round |
| `unlock_at` | the round time of the DateKey, RFC 3339 in UTC, once step 7 passes |
| `access_policy` | `time_only` or `time_and_key` |
| `valid` | true when steps 1 to 8 pass |
| `error` | the code of the failure, absent when valid |
| `checks` | one entry per step run: `step`, `name`, `ok`, `detail` (free text) and, for a failed step, `error` |
`detail` is informative text of the reference; a second implementation compares
at least `step`, `name`, `ok` and `error`, and every other field.
## Existing vectors and fixtures
- `vectors/profile_quicknet.json`: the Quicknet profile fields,
`canonical_cbor` (hex) and `profile_hash`.
- `vectors/quicknet_rounds.json`: `vectors` of `requested` instants (RFC 3339
with nanoseconds) and the resolved `round` and `effective` time, or `error`.
- `vectors/dk1.json`: valid `vectors` with `network`, `round`,
`canonical_json`, `base64url` and `dk1`; invalid ones with `input` and the
`error` code (`accepted` would mean the input decodes).
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
- `fixtures/<name>.json`: for each `.dkc`, its `format`, its SHA-256, the
release that opens it, the hex of the prelude, PUBLIC_HEADER and
CONTROL_CBOR, the DateKey, `capsule_id`, `header_binding`,
`payload_identity` (I_PAYLOAD, a test secret), the visible stanzas of each
age file, the identities or `.dkk` that open it, `payload_length` (L), the
exact extension data, and the result of every step of §63. In format 2 it
adds `padding` (the code) and `padded_length` (P) and, for `time_and_key`,
`access_key_stanza` and `identity_stanzas`: the index, from 0, in
`inner_stanzas` of the stanza that the `.dkk` and each identity of
`identities`, in order, open. The other stanzas are dummies; the official
vectors are the only place where that is recorded (spec §39). In format 3,
as in format 2, with `payload_length` the length of BODY, it adds
`area_len`, `security_cbor`, `head_cbor`, `salt`, `comment`,
`declared_author`, `head_extensions`, `content_offset`, the offset of the
files in BODY, 12 + `AREA_LEN` + `HEAD_LEN`, `files`, each with its `path`,
`size`, `start`, `end`, `sha256` and `mtime` when it has one, its bytes
being those of BODY from `content_offset + start` to `content_offset +
end`, and `verdicts`, with `signature`, `seal`, the `lines` that show
them and, with F4, the `author_key`.
- `fixtures/<name>.dkk.json`: for each `.dkk`, its SHA-256, `credential_id`,
`capsule_id`, `access_type`, `access_material` (a test secret),
`capsule_digest`, extensions and the capsule it opens.

Powered by TurnKey Linux.