The author approved the draft v0.16 on 7 October 2026 with the
recommendation of each of its decisions: the review of v0.15 by Astra, a
seal without accuracy, the key of words in the recovery annex, a full last
chunk of age, the evidence of a signature with certificates and drand's
JSON read strictly. Only the date of the header changes in the text;
spec/README.md records its SHA-256, and annex/recovery.md is written again
with it. The READMEs, SECURITY.md, the traceability, the CDDL header, the
README of testdata and the CHANGELOG name v0.16 and its tag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editor had written the escapes of the sources as their characters:
the cases "round escaped as round" and "round twice, once escaped as
round" of release.json had a plain round, and the surrogate pair of
"a surrogate pair in a value" a plain emoji, so the shared vectors tested
no escaped name. TestStrictJSON had lost its escaped é, its pair and the
escape after a lone high surrogate. They are escapes again, and the texts
of words_test.go too, so that no mark or invisible character hides in the
source. release.json gained 26 cases of drand's JSON, not 25 as b570338
says; the draft and the CHANGELOG now say 26.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SpecVersion is 0.16, and so is the spec field of every file of testdata,
the frozen security_cms.json and locator.json included. annex/recovery.md
is §79 of the draft v0.16, with the key of words in 79.7. The draft lists
the two new fixtures in 67, says in 79 what recovery_check.sh opens, and
names in 76 the tests that now exist. The CHANGELOG has its section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author chose CC-BY-ND-4.0 over CC-BY-4.0: the specification may be
copied and shared unchanged, with credit; a modified version or a
translation needs the written permission of its author. The recovery
annex, which travels alone next to every capsule, says so in its title.
The code stays Apache-2.0 and the word lists keep their own licenses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The SHOULDs of the official SDK that the CLI did not follow yet. encrypt
writes next to the .dkc the recovery annex, FILE.dkc.recuperacion.txt
(spec §62.1, rule 27): datekeys.RecoveryAnnex, annex/recovery.md, which is
§79 of the specification under a title with its version and SHA-256, the
same for every capsule; TestRecoveryAnnex checks it against the text of
SpecVersion. -no-recovery leaves it out. encrypt also says what opening the
capsule years later will take (rule 26): the .dkc, a credential of a
time_and_key capsule, and the release of its round, which an archive of
releases or a cache service must keep if drand no longer serves it; and
beyond one year it recommends time_and_key to a time_only capsule (§7.6).
profile.Status and StatusOf give the state of a pinned profile in the
registry of §71, which DateKeys does not publish yet: Quicknet is active.
encrypt writes no capsule with a profile that is not active, and decrypt
and inspect warn when the profile of a capsule is compromised.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For whoever does not trust the random numbers of a computer, as the author
decided: five dice for each word, read in a fixed order, give a number
from 11111 to 66666, the position of the word in a list of 7776 words, the
first die the most significant. wordkey.DiceNumber and DiceWord number the
words and give the word of a number; DiceWords takes several numbers, at
least 6 and never the same word twice, which is rolled again; DiceList is
the list numbered for dice, a line for each word with its dice and a tab,
as the EFF publishes its own: for en it is the file of the EFF, byte for
byte.
encrypt -dice TEXT and -dice-file FILE take the words from the dice of the
list -dic and show them: the words open the capsule, the numbers give them
only with that list. datekeys wordlist [-dic LIST] writes the numbered
list, to print it, and its SHA-256, which wordkey/lists/README.md records.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wordkey/lists/en.txt is the large wordlist for passphrases of the EFF,
7776 words, CC BY 4.0 under its copyright policy, without the dice number
of each line and in its order, so that the position of a word still gives
its dice, 11111 for the first. encrypt -new-words takes it by default, as
the author decided; -dic es takes the Spanish one. The alphabet of en is a
to z and the ASCII hyphen of its four compound words, such as t-shirt,
kept so that no word loses its dice. lists/README.md records its source,
the SHA-256 of the download, the change and the license.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wordkey.CheckList takes the language of the list and refuses a word with a
character that is not a letter of its alphabet, which only the code gives:
for es, a to z, the five vowels with an acute accent, u with diaeresis and
n with tilde, in lower case and NFC. A Cyrillic letter that looks like a
Latin one, a capital, a digit or the carriage return of a file with CRLF
lines would be written down and typed again with another character, and
the capsule would not open. The Spanish list passes as it is; its SHA-256
does not change.
wordkey.Bits is the strength of the words that Generate draws, log2 of the
number of draws in order, and encrypt -new-words prints it: 90 bits for 7
words of 7776.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wordkey.Generate draws words uniformly with crypto/rand from a built-in
list, and encrypt -new-words FILE writes them to a new file (-dic, default
es; -word-count, default 7). wordkey.List and CheckList refuse a list of
fewer than 2048 words or with two words that are one once normalized
(spec 38.1). The Spanish list, 7776 words, is a draft not yet reviewed,
licensed CC BY-SA 4.0 as an adaptation of FrequencyWords; its source,
method and SHA-256 are in wordkey/lists/README.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author approved the draft v0.15 on 7 October 2026, after removing the
.dkr file from it: the long-term recovery of capsules rests on the release
object, archives and cache services of all rounds, a release in hand that
the clock does not stop, and the recovery annex. Only the date of the
header changes here. SpecVersion is 0.15, the records of the fixtures and
the vectors say so, and the frozen security_cms.json and locator.json
change only their spec field. spec/README.md records the SHA-256.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author's decision of 7 October 2026. A release saved next to a capsule
cannot exist when the capsule is made, and once the date comes the capsule
can be opened: such a file only opens it again and does not cover the real
case, someone opening it decades later when drand is gone and nobody saved
anything. Long-term recovery rests instead on archives and cache services
that keep the releases of all rounds; a reader asks for its round and
verifies the signature against the pinned key.
Spec: the .dkr extension (section 20, back to v0.14), sections 1, 4, 8, 45,
47.1, 49, 50 (rewritten), 53, 62.1 (rule 28 removed, rule 26 reworded),
63, 70, 73, 74 (the datekeys.release .dkk extension dropped too), 76
(the v0.15 block, with the discarded design) and the annex 79. The
release object, the chain hash at step 10, step 9.c option B and the
archive format stay.
Code: decrypt -save-release and the command datekeys release are gone,
with writeRelease and their tests; decrypt -release FILE stays. The
release objects of testdata/releases are now <round>.cbor, and
TestVectorFilesAreCurrent fails on a file the generator no longer writes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The draft writes down the long-term recovery of capsules with the eight
decisions the author took on 6 October 2026. Section 47.1, new, defines
the release object, the content of a .dkr file: deterministic CBOR
without a frame, the chain identified by its chain_hash, from 1 to 1024
bytes, with drand's JSON still accepted as input. Step 10 checks its
layers and its chain hash before the round and the signature, with no new
code. Step 9.c applies only before a network request: a release in hand,
from a .dkr, drand's JSON or a local archive, is not compared with the
clock. Section 45 keeps the Release API with the object as its answer and
its HTTP form informative; section 50 makes the .dkr next to the .dkc the
main path and describes the release archive as an informative format;
sections 53 and 62.1 say what the writer and the SDK keep; section 79, an
informative annex, opens a capsule without DateKeys software. Section 74
drops the two items now covered, and section 76 records six changes with
their cases. datekeys.cddl adds the rule release. SpecVersion stays 0.14.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author approved the draft v0.14 on 6 October 2026 with the
recommendation of each of its ten decisions; decision 8, one scheme of
drand, is the previous commit. Only the date of the header changes here.
SpecVersion is 0.14, the records of the fixtures and the vectors say so,
and the frozen security_cms.json and locator.json change only their spec
field. spec/README.md records the SHA-256 of the text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author approved the draft with its recommendations, decision 8 among
them: section 12.1 admits only the scheme whose verification and tlock
decryption the text now writes byte for byte. profile.Validate refuses the
other two unchained schemes of drand with ERR_UNKNOWN_PROFILE. No pinned
profile and no vector changes; section 76 records it as change 7.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The draft writes down what the completeness review of 6 October 2026 found
missing, and changes no format and no verdict: what the protocol does not
guarantee, the provider and the states of a profile, signatures and seals
against a quantum adversary, the web client, the entropy of a key of words,
and errata of section 76. Steps 10 and 11 of section 63 now give the root
of trust byte for byte, as the three implementations apply it: the message
a Quicknet round signs, its hash to G1 with its DST, and H2, H3 and H4 of
the tlock IBE. testdata/vectors/tlock_steps.json gives every intermediate
value for four published rounds, checked against drand, kyber and tlock.
SpecVersion stays 0.13 until the author approves the draft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec section 44.1 makes unusable a locator whose round or chain is not that
of its DateKey, and its plaintext is 4096 bytes or a multiple. ParseInfo
checked only the round: it now checks that the chain hash is lower-case
hexadecimal and, for a profile this module pins, the chain of the DateKey;
and that the body after the age header holds a plaintext of 4096 bytes or a
multiple, so that a header without a body is refused. Info.Extension, which
reads what it writes, refuses a DateKey of a profile that is not pinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author approved the draft v0.13 on 6 October 2026, as it stood: only
the date of its header changes. SpecVersion is 0.13, the records of the
fixtures and the vectors say so, and the frozen security_cms.json and
locator.json change only their spec field. spec/README.md records the
SHA-256 of the text, and the READMEs, SECURITY.md, the traceability table,
testdata/README.md and the changelog name v0.13.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec v0.12, section 44.1, already asked for the CID in its canonical form
and an IPv6 literal: a CID with a character more, of zero bits, decoded to
the same bytes and passed, and https://[[2000::]/ passed because checkHost
trimmed every bracket. isCIDv1 refuses 5 or more bits left over, and
checkHost takes one pair of brackets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On an IPv6-only network with DNS64, a name that has only IPv4 addresses
resolves to an address of NAT64, which spec v0.12, section 44.1, always
rejected: a reader on such a network, as many mobile ones, could not
download the rest of an envelope. The draft v0.13 counts an address of
64:ff9b::/96, or of the NAT64 prefix of the network, by the IPv4 address it
holds (RFC 6052). An address of NAT64 written in a locator is still
rejected. Section 76 records the change with its case.
locator.CheckResolvedIP implements it for the readers that download, and
testdata/vectors/resolved_ip.json gives 42 cases. SpecVersion stays 0.12
until the author approves the draft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author approved the draft v0.12 on 6 October 2026, as it stood: only
the date of its header changes, and no normative text is added. SpecVersion
is 0.12, the records of the fixtures and the vectors say so, and the frozen
security_cms.json and locator.json change only their spec field.
spec/README.md records the SHA-256 of the text, and the READMEs, SECURITY.md,
the traceability table, testdata/README.md and the changelog name v0.12.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec section 64 of v0.11 asks for the vectors of the key of words, which
were only in the tests of package wordkey. The file has the words of 45
texts, among them each space of section 38.1 and three that are not; what a
writer does with 20 texts, with the text of the error of wordkey.Check; and
6 identities with their recipients, the vector of section 38.1 first. It is
regenerated by genfixtures and checked by TestVectorFilesAreCurrent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
internal/pathrule/gen -dart renders the Unicode and best-fit tables as
const lists of a Dart library, as -ts does for datekeys-ts: the same data
and the same TablesDigest, with the formatter turned off for the file.
tables.go and the TypeScript module come out unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Section 29.7 said givenName and surname "si tiene uno de cada", and the
organizationName of the issuer "si no tiene" commonName, and left open an
attribute without text. The reference treats it as absent: the holder is
givenName and surname only when both have text that is not empty, and the
issuer falls back to its organizationName when its commonName is absent,
repeated or not text (internal/cms, cert_test.go). The datekeys-ts port
follows it, and the text now says so: "con texto" in 29.7 and in change 3
of section 76. The author decided it on 5 October; no code changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The text of 44.1 now says what the reference already refused and a
second implementation that followed the text would have accepted:
segments of 1 to 63 characters that neither start nor end with a hyphen,
the scheme in lower case, 0x and the local names in either case, an IPv4
without leading zeros, and a CID of at most 128 characters in canonical
base32, with minimal varints and a digest of at least one byte. A port is
written without leading zeros: the reference accepted 0443 and 00443 and
refused 000443, the same number, and now refuses the three. Change 6 of
section 76 records it, and section 64 lists the cases.
locator.json is made again with 30 more addresses, 247 in all, and
TestAddresses checks the same rules.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- testdata/README.md: the files of v0.11 and of the draft, the spec
label 0.11 until the approval, 26 fixtures with format3_unsigned and
format3_note, security.json in its context with lines, security_cms.json
without the cases of no context, note.json, the new parts of
locator.json, and the corpus of 218 cases, 178 of the spec, with the
eight of the list of v0.11. The release of round 1000 is in the records
and in mutations.json, not in quicknet_rounds.json.
- docs/traceability.md: the cases of section 64 that security_cms.json
and locator.json now hold are no longer pending.
- CHANGELOG: security_cms.json and locator.json under the test data of
the draft, instead of pending.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The session that closed v0.11 left the documentation at v0.10 (review of
2 October, G14).
- README.md and README.es.md say the same again: the reference implements
v0.11, tagged, and the branch v0.12 the draft; SpecVersion 0.11; the area
of 32 KiB in the picture of BODY; the table of modules with authorkey,
internal/cms, internal/der, locator, wordkey and the public note; and what
the CLI does now: encrypt -sign shows the key and the code of
AUTHOR_MESSAGE before it signs, decrypt -expect-author writes nothing
unless the key of an F4 matches, the lines of the verdicts break behind a
mark, and decrypt and inspect say when a public note is not shown. The
security properties no longer say that no signature is checked.
- SECURITY.md: the scope is v0.11 and the draft v0.12; the limits of a
signature, a seal and a key of words; the standard library among the
cryptographic dependencies.
- docs/traceability.md at the draft v0.12: rows for 24.1, 29.8 to 29.12,
38.1 and 44.1, and rows 29.2, 29.3, 29.7, 62.1, 64, 67, 70, 72 and 76 up
to date, with the code and the tests of each. The cases of spec 64 that
security_cms.json and locator.json still lack are marked pending.
- CHANGELOG.md: an entry for the draft v0.12: the review and its fixes, the
draft, the CMS reader with its own profile, the addresses of the locator,
the CLI, the new test data, and what is pending.
- capsule/format3.go: the comments of EncodeSecurityWith,
EncodeAuthorSignature and EncodeSeal no longer say that this version
defines no alg and no seal_type.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author approved the text and the six open decisions on 1 October 2026.
The spec says now what the review left open: the form of the CMS signature
and of the TSTInfo field by field, the ESSCertIDv2 with SHA-256 written, the
padding of the locator at its boundaries, base32 CIDs, the addresses read
without decoding, the issuer shown by the rules of the holder, and the area
decided after the signatures. SpecVersion is 0.11, the records of fixtures
and vectors say so, and decrypt shows an mtime later than a valid seal as an
inconsistency, which 29.7 asks as a SHOULD.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
locator has the data of the extension of a .dkk, the locator sealed with
tlock for the round of the DateKey, and the envelope: the .dkc in age, split
into a header that the locator carries and a rest that can hide inside
another file. The plaintext of the locator measures the least multiple of
4096 bytes that holds it. Nothing is downloaded: a reader gives the rest to
OpenEnvelope, which checks its size and digests.
README and CHANGELOG describe what the draft adds.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- README and README.es: specification 0.10, format 3 written and formats
1 to 3 read; BODY in the picture of a capsule; the CLI with folders,
-comment, -author, -no-mtime and decrypt into a new folder, with the
presentation of 29.7; EncryptFiles, Source and Sink in the library; the
metadata that format 3 hides, safe extraction, and the declared author
and comment that prove nothing; the fixtures, vectors and mutations of
format 3; 18 normative errors.
- CHANGELOG: the section of specification v0.10.
- docs/traceability.md: rows 29.2 to 29.7 and 29.5.1, and the rows that
format 3 changes (22, 23, 28, 29, 29.1, 31, 33, 39, 56, 57, 61 to 64,
67 to 70, 75 and 76); ERR_HEAD_INVALID in the error mapping; two
implementation decisions, the Sink and the width of the terminal.
- testdata/README.md: the nine fixtures of format 3 and their records,
the four new vector files, the mutation corpus of 209 cases with its
list of format 3 and the verdicts of the cases that open, and the two
bases of format 3 of the differential corpus.
- SECURITY.md: specification v0.10, and the head as untrusted input.
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>
- datekeys.SpecVersion ("0.8.2") names the specification the module
implements. A test ties it to the spec file, its title and
spec/README.md, and TestCatalogueMatchesSpec and the vector files use
it (testkit.SpecVersion now aliases it), so the vectors regenerate
unchanged.
- datekeys.Version() is the version of the module as the go command
recorded it. That is a tag, or for a binary built in a checkout the
pseudo-version of its commit (for example
v0.0.0-20260928105528-9ac9cd952f04), or (devel) when it is unknown, as
in tests or under a replace directive to a directory. It works as the
main module and as a dependency, whatever the module path, which it
reads from the root package.
- `datekeys version` (also -version and --version) prints both and the
Go toolchain.
- README.md and README.es.md explain the three versions (format,
specification, module) and what the code on main covers.
traceability §70 and CHANGELOG follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
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>
encoding/json replaces invalid UTF-8 with U+FFFD inside strings, so a
member that a repeated name overwrites passed steps 2 and 3 and ended as
ERR_DATEKEY_NON_CANONICAL at step 6, while §19 makes invalid UTF-8 fail
step 2 with ERR_DATEKEY_INVALID. parseJSON now checks utf8.Valid first.
Found by the differential of the TypeScript implementation; pinned by
TestReadingRules (which fails without the fix) and a new dk1.json vector.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
Step 2b: package codec is rewritten without reflection or struct tags. A
strict Decoder accepts only the spec §58 profile, Unmarshal decodes,
re-encodes and compares, Peek reads the type tag and version, and Walk is a
bounded iterative helper for vectors and fuzzing. Every schema has its own
hand-written encoder and decoder that checks all CDDL rules before the
fields with their own error codes. github.com/fxamacker/cbor/v2 and
github.com/x448/float16 are gone; nothing replaces them. Valid objects
encode and decode exactly as before (1.34 million differential verdicts);
the invalid-input differences are documented in CHANGELOG and
traceability decision 12. A review found and fixed an access_policy check
that truncated to uint8.
Step 3: testdata gains vectors/cbor.json (generic and per-schema CBOR
vectors), vectors/mutations.json (the 55-case mutation corpus, replayable
offline), vectors/inspect_differential.json (1,825 fixed-seed mutations
with the Go verdict) and one inspect -json golden per fixture, all
regenerated byte-identically by genfixtures and documented in
testdata/README.md for second implementations.
Gate green with 90 s of fuzzing per target on all 15 targets; codec at
100 % coverage; pre-existing testdata byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 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>
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>