scripts/recovery opens a time_and_key capsule with the words of a key of
words (-words FILE), as the annex says in 79.7: the normalization without
tables for the text of the DateKeys lists (printable ASCII, the ASCII
spaces, á é í ó ú ü ñ and their capitals, and the marks U+0300 to U+036F),
and for any other text the full one, NFD, without the marks, simple lower
case and the spaces of 38.1, from UnicodeData.txt of Unicode 18.0.0
(-unicodedata FILE), checked by its SHA-256; then PBKDF2-HMAC-SHA256 of the
standard library. Its tests check both normalizations against every case
of wordkey.json and the two vectors of the annex.
Two fixtures of v0.16: format3_time_and_key_words, opened with the text of
the second vector of the annex, recorded in words_text with the identity it
gives; and format3_full_chunk, whose BODY and P are 65536 bytes, so that
PAYLOAD_AGE ends in a full STREAM chunk (79.5). recovery_check.sh opens
both. wordkey.json gains the text of the annex, also with its marks apart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A valid seal is S4 only when its token carries accuracy and t plus the
accuracy is before round_time; otherwise S5, whose text gives the reason,
the first that holds: sealed after or too close, no accuracy under the BTSP
policy of ETSI EN 319 421 (0.4.0.2023.1.1), or no accuracy (spec v0.16,
29.7, 29.11). The line of a signer of F6 whose seal does not prove it says
so with the same reason. cms.Token gains HasAccuracy, Policy and BTSP;
Verdicts gain SealReason and SignerLine.Reason; EncryptFiles returns the
verdicts of the area it wrote in Result.Security, so that a writer warns of
a seal without accuracy (rule 19).
security_cms.json is made again: 143 cases, the seals about something else
with an accuracy of a second, and the new cases of 64 with seal_reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The review found that the cases that spec 64 lists for the signature of
alg 1, the area and the public note were tested in Go and not exported to
testdata, where a second implementation reads them (D3). mutations.json
adds eight, from format3_signed, format3_unsigned and a new format3_note:
- the signature altered (F2), removed (F0), made again with another key
(F4 of that key) and transplanted to another capsule (F2);
- a key of 31 bytes and a signature of 65 (F1);
- the area widened to 64 KiB after signing (F4, the same AUTHOR_MESSAGE);
- the public note changed in PUBLIC_HEADER (ERR_HEADER_BINDING, step 15).
The frozen cases of the corpus do not change. The records of the fixtures
give the extensions of the header as it is written, the note included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The last commit added the fixture and left the lists of the tests and the
count of frozen inspect outputs behind: TestInspectJSONGoldens failed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixtures format3_signed_cms (alg 2, two certificates, each sealed, F6) and
format3_sealed (alg 1 and a seal of seal_type 2, F4 and S4), with the
certificates, SIGNERS, the commitments, SEAL_SUBJECT, the token and the
result of each signer in their records. vectors/security_cms.json has 22
frozen areas with their context and verdicts (F1, F2, F5, F6, S1 to S5), and
vectors/locator.json the extension datekeys.capsule with its envelope hidden
in a host, its locator sealed with tlock, the padding at the boundaries and
the rules of the addresses. The README of testdata describes all of it.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
format3_signed is written by EncryptFiles with a test key. Its record gives
the seed of the key, control_commit, head_digest, signers_digest,
AUTHOR_MESSAGE with its code, the signature and the content of key 2, and
verdicts carries the dkauthor1 key. The conformance test recomputes all of
it from the control, the head and the security area, and signs again from
the seed. A second test changes the context, a bit of the signature or of
the key, and the key itself, and removes the signature.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The fixtures of spec 67 for format 3, each with its record, its BODY,
its inspect output and, for time_and_key, its .dkk:
- format3_single, format3_tree (five files in three folders, one over
two STREAM chunks, one without mtime, a comment and a declared
author), format3_comment_only (no files; a TAB in the comment),
format3_bloque256 and format3_time_and_key_portable, written with
EncryptFiles;
- format3_area_1024, format3_security_v2 (verdict X),
format3_signature_unsupported (an author-signature of alg 1 with a
random key of 32 bytes and a random signature of 64: F1) and
format3_seal_unsupported (that and a seal of seal_type 1: F1 and S1),
which only a generator of test vectors writes (62.1 rule 13), built
with testkit.Build.
The record of a format 3 fixture adds the area, SECURITY_CBOR,
HEAD_CBOR, the salt, the comment, the declared author, the head
extensions, the offset of CONTENT in BODY, each file with its layout,
SHA-256 and mtime, and the verdicts with their lines; its plaintext
file is BODY. The generator writes, then recovers every value by
opening layer by layer for the three formats alike, and refreshes the
records of format 3 through a Sink.
Tests: the conformance test checks BODY, the head, security and every
file, and opens through a MemorySink; the .dkk tests take the .dkk of
formats 2 and 3 too (spec 68); the CLI decrypts five of the fixtures
into folders; the control fuzz target decodes with the three schema
versions. The differential corpus gains two bases, format3_single and
format3_time_and_key_portable, one per policy: 5110 cases, the earlier
ones unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
- extension: data is an opaque []byte; New takes []byte and rejects empty
data; key 2 must be a non-empty shortest-form bstr; at most 64 extensions
per array; extension_version <= 2^32-1; CheckDisjoint is a linear merge;
optional DataValidator with ERR_EXTENSION_DATA_INVALID for known critical
extensions and Unusable reports for known noncritical ones.
- capsule, accesskey: frame limits on encode and decode with ERR_INTEGRITY;
Encrypt and MarshalBody decode their own output before sealing or
returning it; unusable extensions are reported.
- profile: period and genesis_time bounded to 2^53-1, genesis decoded as
unsigned.
- codec: Valid removed; empty values never encode as null.
- Regression tests for the nested-data seal/open asymmetry, the
nondeterministic verdict on NaN-keyed data and the quadratic disjointness
check; three new §64 mutations and five more.
- Fixtures: time_only_extensions regenerated with opaque data, new
time_and_key_portable_extension.dkk; genfixtures gains -only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The module is imported as g.activething.com/go/DateKeys, the path the
project's Gitea advertises. The .github directory is gone: workflows
now live in .gitea/workflows, use the gitea.com action mirrors and
install every tool from its Go module; releases go to this Gitea with
goreleaser and a key-based cosign signature; Dependabot is replaced by
a nightly report of available updates. scripts/check.sh runs the same
checks on any machine and is the gate while the server has no runner.
SECURITY.md, README and CONTRIBUTING no longer refer to GitHub.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>