Spec v0.15 draft: the release object, step 9.c and the recovery annex

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>
v0.15
dev 2 days ago
parent 18769be758
commit b7402bdad4

@ -3,6 +3,50 @@
All notable changes to this module are documented here. The project follows All notable changes to this module are documented here. The project follows
semantic versioning; `v0.x` versions make no API stability promise. semantic versioning; `v0.x` versions make no API stability promise.
## Unreleased — specification v0.15 draft
The draft v0.15 of the DateKeys Protocol Specification, on the branch
`v0.15` and not approved yet: the long-term recovery of capsules, with the
eight decisions its author took on 6 October 2026. It changes no format of
`.dkc` or `.dkk`, and one verdict: a valid release in the caller's hand
opens a capsule even when the clock is before the round time. `SpecVersion`
stays 0.14 until the author approves the draft.
- **Specification.** `spec/DateKeys_Protocol_Specification_v0.15.md`, whose
§76 lists six changes with their cases: the release object (§47.1, with
§20, §45, §47 and §57), its chain hash at step 10 (§63, §69.1), step 9.c
only before a network request (§49, §63, §70), the long-term recovery
(§50, §53, §62.1 rules 26 to 28, and the informative annex §79), the
Release API (§45) and §74. `datekeys.cddl` adds the rule `release`.
- **Release object.** `provider.EncodeRelease`, `DecodeRelease` and
`ParseRelease`, which also reads drand's JSON; `provider.Verify` checks the
chain hash a release names, `ERR_PROFILE_MISMATCH`, before its round and its
signature. `provider.Archive` reads a local release archive, the
informative format of §50.
- **A release in hand.** `capsule.OpenOptions.Release`, a
`provider.Supplier` exclusive with `Source`, is not compared with the clock
(step 9.c, option B); `Opened.ClockBehind` says when the clock was before
the round time, and `Opened.Release` carries the chain hash, ready for
`provider.EncodeRelease`. A network source is still never asked before the
round time.
- **CLI.** `datekeys decrypt -release FILE` takes a `.dkr`, drand's JSON or a
local archive, with no network request; `-save-release FILE.dkr` keeps the
release that opened the capsule; `datekeys release -in FILE.dkc -out
FILE.dkr` fetches, verifies and saves it without opening the capsule.
- **Test data.** `vectors/release.json`, new, from
`internal/testkit.ReleaseVectors`; `releases/<round>.dkr` for rounds 1000,
1001, 1004 and 2000, and `releases/archive_1000_1004.bin`. In
`mutations.json` every case has the field `source`, `supplied` or
`network`; "round not reached yet", a release in hand with a clock 1 ns
before the round time, was `ERR_RELEASE_UNAVAILABLE` at step 9 and now
opens; four cases are new: the same with a network source, a release of
another round from a network source, and two release objects of another
chain. No other case changes its result.
- **Recovery check.** `scripts/recovery`, a program that opens a capsule by
following the annex, with no code of this module, tlock or drand, and
`scripts/recovery_check.sh`, which `scripts/check.sh` runs on a
`time_only` and a `time_and_key` fixture of format 3.
## Unreleased — specification v0.14 ## Unreleased — specification v0.14
Implements the DateKeys Protocol Specification v0.14, which its author Implements the DateKeys Protocol Specification v0.14, which its author

@ -3,6 +3,11 @@
Implementación de referencia en Go de la **DateKeys Protocol Specification Implementación de referencia en Go de la **DateKeys Protocol Specification
v0.14** ([`spec/`](spec/DateKeys_Protocol_Specification_v0.14.md)), etiquetada v0.14** ([`spec/`](spec/DateKeys_Protocol_Specification_v0.14.md)), etiquetada
`spec-v0.14`, que no cambia ningún formato de la v0.11, la v0.12 ni la v0.13. `spec-v0.14`, que no cambia ningún formato de la v0.11, la v0.12 ni la v0.13.
Esta rama, `v0.15`, lleva además el borrador v0.15
([`spec/`](spec/DateKeys_Protocol_Specification_v0.15.md)), aún sin aprobar,
sobre la recuperación de cápsulas a largo plazo: el release de una ronda
guardado en un fichero `.dkr`, un release en la mano que el reloj no detiene,
y un anexo para abrir una cápsula sin software de DateKeys.
[English version](README.md). [English version](README.md).
DateKeys cifra datos de forma que solo puedan abrirse a partir de un instante DateKeys cifra datos de forma que solo puedan abrirse a partir de un instante
@ -128,6 +133,9 @@ datekeys encrypt -at 2030-01-01T00:00:00Z -in carta.txt -note "Cartas de Lisboa"
datekeys decrypt -in carta.dkc -out carta -expect-author dkauthor1... datekeys decrypt -in carta.dkc -out carta -expect-author dkauthor1...
datekeys inspect -in regalo.dkc datekeys inspect -in regalo.dkc
datekeys decrypt -in regalo.dkc -out regalo -dkk regalo.dkk datekeys decrypt -in regalo.dkc -out regalo -dkk regalo.dkk
datekeys decrypt -in regalo.dkc -out regalo -dkk regalo.dkk -save-release regalo.dkr
datekeys decrypt -in regalo.dkc -out regalo -dkk regalo.dkk -release regalo.dkr
datekeys release -in regalo.dkc -out regalo.dkr
datekeys profile hash datekeys profile hash
datekeys version datekeys version
``` ```
@ -143,7 +151,12 @@ reforzado, o con bloque256 si se pide (spec §29.1).
`inspect` ejecuta solo las comprobaciones previas al desbloqueo (spec §63, `inspect` ejecuta solo las comprobaciones previas al desbloqueo (spec §63,
pasos 1 a 8): nunca pide un release ni usa secretos. pasos 1 a 8): nunca pide un release ni usa secretos.
`decrypt` obtiene el release de relays públicos de drand y lo verifica `decrypt` obtiene el release de relays públicos de drand y lo verifica
localmente. Los ficheros de una cápsula de formato 3 van a la carpeta nueva localmente, nunca antes de la hora de la ronda; con `-release` toma en su
lugar el release en la mano, un `.dkr`, el JSON de drand o un archivo local
de releases, sin ninguna petición y diga lo que diga el reloj (borrador v0.15,
§47.1, §63 paso 9.c), y `-save-release` guarda como `.dkr` el release que
abrió la cápsula. `release` obtiene, verifica y guarda ese `.dkr` sin abrir
la cápsula. Los ficheros de una cápsula de formato 3 van a la carpeta nueva
`-out`, preparados dentro de ella y movidos a su sitio solo cuando todas las `-out`, preparados dentro de ella y movidos a su sitio solo cuando todas las
comprobaciones han pasado (spec §56); una cápsula con solo un comentario no comprobaciones han pasado (spec §56); una cápsula con solo un comentario no
crea la carpeta. Muestra los veredictos al principio y al final, y el autor crea la carpeta. Muestra los veredictos al principio y al final, y el autor
@ -263,6 +276,9 @@ normativos del §69, así que `errors.Is` y `datekeys.Code(err)` lo identifican.
acceso, no una firma. acceso, no una firma.
- **Recuperar años después** exige el release histórico: de un relay drand que - **Recuperar años después** exige el release histórico: de un relay drand que
aún lo sirva o de cualquier caché, verificado de nuevo localmente (spec §50). aún lo sirva o de cualquier caché, verificado de nuevo localmente (spec §50).
El borrador v0.15 lo guarda como `.dkr` junto a la cápsula, y su §79 dice
cómo abrir una cápsula sin software de DateKeys, lo que comprueba
`scripts/recovery_check.sh`.
Ver [SECURITY.md](SECURITY.md). Ver [SECURITY.md](SECURITY.md).

@ -2,7 +2,12 @@
Reference implementation in Go of the **DateKeys Protocol Specification Reference implementation in Go of the **DateKeys Protocol Specification
v0.14** ([`spec/`](spec/DateKeys_Protocol_Specification_v0.14.md)), tagged v0.14** ([`spec/`](spec/DateKeys_Protocol_Specification_v0.14.md)), tagged
`spec-v0.14`, which changes no format of v0.11, v0.12 or v0.13. `spec-v0.14`, which changes no format of v0.11, v0.12 or v0.13. This branch,
`v0.15`, also carries the draft v0.15
([`spec/`](spec/DateKeys_Protocol_Specification_v0.15.md)), not approved yet,
on the long-term recovery of capsules: the release of a round saved as a
`.dkr` file, a release in hand that the clock does not stop, and an annex to
open a capsule without DateKeys software.
[Versión en español](README.es.md). [Versión en español](README.es.md).
DateKeys encrypts data so that it can only be opened after a chosen instant. DateKeys encrypts data so that it can only be opened after a chosen instant.
@ -128,6 +133,9 @@ datekeys encrypt -at 2030-01-01T00:00:00Z -in letter.txt -note "Letters from Lis
datekeys decrypt -in letter.dkc -out letter -expect-author dkauthor1... datekeys decrypt -in letter.dkc -out letter -expect-author dkauthor1...
datekeys inspect -in gift.dkc datekeys inspect -in gift.dkc
datekeys decrypt -in gift.dkc -out gift -dkk gift.dkk datekeys decrypt -in gift.dkc -out gift -dkk gift.dkk
datekeys decrypt -in gift.dkc -out gift -dkk gift.dkk -save-release gift.dkr
datekeys decrypt -in gift.dkc -out gift -dkk gift.dkk -release gift.dkr
datekeys release -in gift.dkc -out gift.dkr
datekeys profile hash datekeys profile hash
datekeys version datekeys version
``` ```
@ -143,7 +151,12 @@ asked (spec §29.1).
`inspect` runs only the pre-unlock checks (spec §63 steps 1–8): it never `inspect` runs only the pre-unlock checks (spec §63 steps 1–8): it never
requests a release and never uses a secret. requests a release and never uses a secret.
`decrypt` fetches the release from public drand relays and verifies it `decrypt` fetches the release from public drand relays and verifies it
locally. The files of a format 3 capsule go to the new folder `-out`, staged locally, never before the round time; with `-release` it takes the release
in hand instead, a `.dkr`, drand's JSON or a local release archive, without
any request and whatever the clock says (draft v0.15, §47.1, §63 step 9.c),
and `-save-release` keeps the release that opened the capsule as a `.dkr`.
`release` fetches, verifies and saves that `.dkr` without opening the
capsule. The files of a format 3 capsule go to the new folder `-out`, staged
inside it and moved into place only after every check passed (spec §56); a inside it and moved into place only after every check passed (spec §56); a
capsule with only a comment creates no folder. It shows the verdicts first capsule with only a comment creates no folder. It shows the verdicts first
and last, and the declared author, the comment and the paths as text of the and last, and the declared author, the comment and the paths as text of the
@ -260,6 +273,9 @@ one of the 19 normative errors of spec §69, so `errors.Is` and
access barrier, not a signature. access barrier, not a signature.
- **Recovery years later** needs the historical release: from a drand relay - **Recovery years later** needs the historical release: from a drand relay
that still serves it or from any cache, re-verified locally (spec §50). that still serves it or from any cache, re-verified locally (spec §50).
The draft v0.15 keeps it as a `.dkr` next to the capsule, and its §79
says how to open a capsule without DateKeys software, which
`scripts/recovery_check.sh` checks.
See [SECURITY.md](SECURITY.md). See [SECURITY.md](SECURITY.md).

@ -1,4 +1,4 @@
# Traceability: DateKeys Protocol Specification v0.14 ↔ datekeys-go # Traceability: DateKeys Protocol Specification v0.15 draft ↔ datekeys-go
This table maps every normative section of the specification to the code that This table maps every normative section of the specification to the code that
implements it and to the tests that exercise it. It is updated in the same implements it and to the tests that exercise it. It is updated in the same
@ -7,8 +7,10 @@ reviewer together with the specification, the fixtures and the mutation corpus
(plan §10). (plan §10).
Paths are relative to the repository root. `§` numbers refer to Paths are relative to the repository root. `§` numbers refer to
`spec/DateKeys_Protocol_Specification_v0.14.md`, tagged `spec-v0.14`; v0.13, `spec/DateKeys_Protocol_Specification_v0.15.md`, the draft of the branch
v0.12 and v0.11 number their sections the same, but for §7.10, new in v0.14. A case of §64 that is not in the repository yet is marked `v0.15`, not approved yet; v0.14, tagged `spec-v0.14`, v0.13, v0.12 and v0.11
number their sections the same, but for §7.10, new in v0.14, and §47.1 and
§79, new in the draft v0.15. A case of §64 that is not in the repository yet is marked
*pending*. *pending*.
## Section map ## Section map
@ -31,7 +33,7 @@ v0.12 and v0.11 number their sections the same, but for §7.10, new in v0.14. A
| 17 | Past-round attack; at step 10 the release round is compared before the signature, `ERR_ROUND_MISMATCH` for a release supplied directly, a network source discarding one of another round at step 9 (`ERR_RELEASE_UNAVAILABLE`) | `provider.Verify` (round equality first), `capsule.Encrypt` (round time ≥ requested), `agewrap.CheckTimeStanzas`, `provider/drand.Client` | `provider.TestVerifyRejects` (*another round and a short signature*); `capsule.TestReleaseFromANetworkSource`; mutations *DateKey A + release of round B*, *tlock stanza round differs from DateKey.round* | | 17 | Past-round attack; at step 10 the release round is compared before the signature, `ERR_ROUND_MISMATCH` for a release supplied directly, a network source discarding one of another round at step 9 (`ERR_RELEASE_UNAVAILABLE`) | `provider.Verify` (round equality first), `capsule.Encrypt` (round time ≥ requested), `agewrap.CheckTimeStanzas`, `provider/drand.Client` | `provider.TestVerifyRejects` (*another round and a short signature*); `capsule.TestReleaseFromANetworkSource`; mutations *DateKey A + release of round B*, *tlock stanza round differs from DateKey.round* |
| 18 | `dk1_` representation; the canonical JSON has no escapes, the `profile_id` alphabet needs none | `DateKey.CanonicalJSON`, `DateKey.Compact` | `datekey.TestGoldenDK1Vectors`, `TestNormativeRoundVector` | | 18 | `dk1_` representation; the canonical JSON has no escapes, the `profile_id` alphabet needs none | `DateKey.CanonicalJSON`, `DateKey.Compact` | `datekey.TestGoldenDK1Vectors`, `TestNormativeRoundVector` |
| 19 | `dk1_` canonicality; steps 1 to 3 `ERR_DATEKEY_INVALID`, step 6 `ERR_DATEKEY_NON_CANONICAL`; step 1 accepts either Base64 alphabet, padding and non-zero trailing bits but no other character (CR and LF included); step 2 one RFC 8259 JSON object, no byte order mark; JSON numbers by their exact decimal value; round in 1..2^53−1 without the profile | `datekey.Parse` (`decodeBase64`, which rejects CR and LF before the Go decoders, `parseJSON`, which rejects invalid UTF-8 before `encoding/json` can replace it, `jsonUint`) | `datekey.TestGoldenDK1Vectors`, `TestReadingRules`, `TestNumberSpellings`, `FuzzParse`; mutation *non-canonical dk1_ JSON*; `testdata/vectors/dk1.json` (*byte order mark*, *line feed inside the Base64*, *carriage return and line feed after the Base64*, *version 1.0000000000000001: its exact value, not a double*, *invalid UTF-8 in a member a repeated name overwrites*) | | 19 | `dk1_` canonicality; steps 1 to 3 `ERR_DATEKEY_INVALID`, step 6 `ERR_DATEKEY_NON_CANONICAL`; step 1 accepts either Base64 alphabet, padding and non-zero trailing bits but no other character (CR and LF included); step 2 one RFC 8259 JSON object, no byte order mark; JSON numbers by their exact decimal value; round in 1..2^53−1 without the profile | `datekey.Parse` (`decodeBase64`, which rejects CR and LF before the Go decoders, `parseJSON`, which rejects invalid UTF-8 before `encoding/json` can replace it, `jsonUint`) | `datekey.TestGoldenDK1Vectors`, `TestReadingRules`, `TestNumberSpellings`, `FuzzParse`; mutation *non-canonical dk1_ JSON*; `testdata/vectors/dk1.json` (*byte order mark*, *line feed inside the Base64*, *carriage return and line feed after the Base64*, *version 1.0000000000000001: its exact value, not a double*, *invalid UTF-8 in a member a repeated name overwrites*) |
| 20 | File extensions and magic | magic checks in `capsule.ParsePrelude`, `accesskey.Decode` | mutation *a .dkk offered as a .dkc*; `accesskey.TestDecodeRejects` *a .dkc* | | 20 | File extensions and magic | magic checks in `capsule.ParsePrelude`, `accesskey.Decode`; v0.15: `.dkr`, a release object identified by its type tag (`provider.DecodeRelease`, `provider.IsArchive` to tell an archive apart in `cmd/datekeys`) | mutation *a .dkk offered as a .dkc*; `accesskey.TestDecodeRejects` *a .dkc*; `provider.TestReleaseVectors` |
| 21 | `capsule_id`: exactly 16 bytes from a CSPRNG (MUST) | `capsule.Encrypt` (16 bytes from `crypto/rand`), `capsule.DecodeHeader` | `capsule.TestPortableKeysAreNeverReused` | | 21 | `capsule_id`: exactly 16 bytes from a CSPRNG (MUST) | `capsule.Encrypt` (16 bytes from `crypto/rand`), `capsule.DecodeHeader` | `capsule.TestPortableKeysAreNeverReused` |
| 22 | `.dkc` framing; `VERSION` is the capsule format, 1, 2 or 3, which fixes the schema version of CONTROL_CBOR, the stanzas of INNER_ACCESS_AGE and the padding of the payload; `PUBLIC_HEADER_LEN` in 1..1 MiB, `SEALED_CONTROL_LEN` in 1..64 MiB; PAYLOAD_AGE to EOF, at least an age header | `capsule.Format` (`Format1`, `Format2`, `Format3`), `capsule.Prelude` (`Format`), `capsule.ParsePrelude` | `capsule.TestFormatDispatch`, `TestFormatRelabel`, `TestFrameLengthLowerBounds`, `FuzzParsePrelude`; mutations *version changed* (`VERSION` 4, in the three formats), *flags != 0*, *reserved != 0*, *magic*, length limits, and the relabelings of the lists of formats 2 and 3 of §64 | | 22 | `.dkc` framing; `VERSION` is the capsule format, 1, 2 or 3, which fixes the schema version of CONTROL_CBOR, the stanzas of INNER_ACCESS_AGE and the padding of the payload; `PUBLIC_HEADER_LEN` in 1..1 MiB, `SEALED_CONTROL_LEN` in 1..64 MiB; PAYLOAD_AGE to EOF, at least an age header | `capsule.Format` (`Format1`, `Format2`, `Format3`), `capsule.Prelude` (`Format`), `capsule.ParsePrelude` | `capsule.TestFormatDispatch`, `TestFormatRelabel`, `TestFrameLengthLowerBounds`, `FuzzParsePrelude`; mutations *version changed* (`VERSION` 4, in the three formats), *flags != 0*, *reserved != 0*, *magic*, length limits, and the relabelings of the lists of formats 2 and 3 of §64 |
| 23 | PRELUDE; order of the checks of steps 1 and 2, the version being the format, 1, 2 or 3; section bytes present at steps 3 and 5 | `Prelude.Bytes`, `capsule.ParsePrelude`, `capsule.Inspect` | `capsule.TestConformanceFixtures`, `TestFrameLengthLowerBounds`, `TestPrecedenceAcrossSteps`, `TestFormatDispatch`; `testdata/vectors/inspect_differential.json` | | 23 | PRELUDE; order of the checks of steps 1 and 2, the version being the format, 1, 2 or 3; section bytes present at steps 3 and 5 | `Prelude.Bytes`, `capsule.ParsePrelude`, `capsule.Inspect` | `capsule.TestConformanceFixtures`, `TestFrameLengthLowerBounds`, `TestPrecedenceAcrossSteps`, `TestFormatDispatch`; `testdata/vectors/inspect_differential.json` |
@ -75,15 +77,16 @@ v0.12 and v0.11 number their sections the same, but for §7.10, new in v0.14. A
| 43 | `verification_metadata` | `accesskey.Verification`, `decodeVerification` (the closed map `{0: capsule_digest}`); `capsule.Open` (`checkCapsuleDigest`, seekable readers) | `accesskey.TestDecodeRejects` *empty verification map*, `TestDecodeBodyStructure`; mutation *capsule_digest of the .dkk does not match* | | 43 | `verification_metadata` | `accesskey.Verification`, `decodeVerification` (the closed map `{0: capsule_digest}`); `capsule.Open` (`checkCapsuleDigest`, seekable readers) | `accesskey.TestDecodeRejects` *empty verification map*, `TestDecodeBodyStructure`; mutation *capsule_digest of the .dkk does not match* |
| 44 | Application extensions in `.dkk` | `AccessKey.Critical/Noncritical`; `capsule.Open` (`checkAccessKey`, `Opened.UnusableAccessKeyExtensions`) | `accesskey.TestEncodeRejectsAbsenceAsEmptyMap`, `TestDecodeBodyExtensionRules`, `TestFixtureWithExtension`; `capsule.TestAccessKeyFixtureWithExtension`; mutation *known critical .dkk extension with invalid data* | | 44 | Application extensions in `.dkk` | `AccessKey.Critical/Noncritical`; `capsule.Open` (`checkAccessKey`, `Opened.UnusableAccessKeyExtensions`) | `accesskey.TestEncodeRejectsAbsenceAsEmptyMap`, `TestDecodeBodyExtensionRules`, `TestFixtureWithExtension`; `capsule.TestAccessKeyFixtureWithExtension`; mutation *known critical .dkk extension with invalid data* |
| 44.1 | The extension `datekeys.capsule` of a `.dkk`, noncritical: the note, the DateKey and an optional locator, an age file with one tlock stanza for the round of that DateKey, unusable for another round or chain; its plaintext, 1 to 8 addresses, `I_SOBRE`, the header of the envelope, the digest and the size of the rest, `capsule_digest` and a zero padding of at least one byte, of exactly 4096 bytes or the least multiple that holds it; the envelope, the `.dkc` in age split into the header and a rest without a mark, alone or inside a host at an offset; the addresses, ASCII of RFC 3986, read without decoding, the scheme in lower case: `https` with a host of labels of 1 to 63 letters, digits and hyphens that neither start nor end with a hyphen, or a public IP outside the special-purpose blocks of IANA, an IPv4 without leading zeros, a port of 1 to 65535 without leading zeros, no local name in either case, no dot segment, or `ipfs` with a CID v1 of at most 128 characters in canonical base32; a reader rejects each address that breaks them and uses the others; the rest and the `.dkc` checked by their digests; a writer never writes a rejected address and decodes what it writes | `locator` (`Info`, `Info.Extension`, `ParseInfo`, `Info.OpenLocator`, `Standard`; `Locator`, `Locator.Marshal`, `Unmarshal`, `Locator.Usable`, `PlaintextLength`, `Block`, `MaxAddresses`, `MaxURILen`, `MaxHeaderLen`; `Seal`, `Open`; `NewEnvelope`, `Locator.OpenEnvelope`, `Hide`, `Locator.RestIn`; `Address`, `Address.Host`, `CheckURI`; `CheckResolvedIP`, the address a name resolves to, NAT64 included, of v0.13), which downloads nothing; `extension.CapsuleID`, `extension.Standard` (`ValidateCapsule`); `accesskey.AccessKey.MarshalBody` (`extension.CheckWrite`); `spec/datekeys.cddl` (`capsule-locator`, `capsule-address`) | `locator.TestEnvelope`, `TestLocatorPlaintext`, `TestAddresses` (the blocks of IANA, NAT64, mapped and 6to4 addresses, local names, characters outside RFC 3986, dot segments, CIDs that do not decode), `TestSealedLocator` (another round or release: unusable), `TestInfo`, `TestInfoSealedForm` (the round, the chain and the body of the sealed locator), `TestUsableAddresses`, `TestLeastMultiple`, `TestPaddingBoundaries`, `TestLocatorVectors` (`testdata/vectors/locator.json`); `TestResolvedIPVectors` (`testdata/vectors/resolved_ip.json`, `internal/testkit.ResolvedIPVectors`), `TestResolvedIPWellKnownPrefix`; `capsule.TestRegisteredExtensionsWhereRegistered` (never in a capsule); the cases of §64 of v0.11 and v0.12 for `datekeys.capsule` in `locator.json`: the addresses, a locator with a rejected address and a usable one, the resources of the rest, the data of the extension and the plaintexts of the locator | | 44.1 | The extension `datekeys.capsule` of a `.dkk`, noncritical: the note, the DateKey and an optional locator, an age file with one tlock stanza for the round of that DateKey, unusable for another round or chain; its plaintext, 1 to 8 addresses, `I_SOBRE`, the header of the envelope, the digest and the size of the rest, `capsule_digest` and a zero padding of at least one byte, of exactly 4096 bytes or the least multiple that holds it; the envelope, the `.dkc` in age split into the header and a rest without a mark, alone or inside a host at an offset; the addresses, ASCII of RFC 3986, read without decoding, the scheme in lower case: `https` with a host of labels of 1 to 63 letters, digits and hyphens that neither start nor end with a hyphen, or a public IP outside the special-purpose blocks of IANA, an IPv4 without leading zeros, a port of 1 to 65535 without leading zeros, no local name in either case, no dot segment, or `ipfs` with a CID v1 of at most 128 characters in canonical base32; a reader rejects each address that breaks them and uses the others; the rest and the `.dkc` checked by their digests; a writer never writes a rejected address and decodes what it writes | `locator` (`Info`, `Info.Extension`, `ParseInfo`, `Info.OpenLocator`, `Standard`; `Locator`, `Locator.Marshal`, `Unmarshal`, `Locator.Usable`, `PlaintextLength`, `Block`, `MaxAddresses`, `MaxURILen`, `MaxHeaderLen`; `Seal`, `Open`; `NewEnvelope`, `Locator.OpenEnvelope`, `Hide`, `Locator.RestIn`; `Address`, `Address.Host`, `CheckURI`; `CheckResolvedIP`, the address a name resolves to, NAT64 included, of v0.13), which downloads nothing; `extension.CapsuleID`, `extension.Standard` (`ValidateCapsule`); `accesskey.AccessKey.MarshalBody` (`extension.CheckWrite`); `spec/datekeys.cddl` (`capsule-locator`, `capsule-address`) | `locator.TestEnvelope`, `TestLocatorPlaintext`, `TestAddresses` (the blocks of IANA, NAT64, mapped and 6to4 addresses, local names, characters outside RFC 3986, dot segments, CIDs that do not decode), `TestSealedLocator` (another round or release: unusable), `TestInfo`, `TestInfoSealedForm` (the round, the chain and the body of the sealed locator), `TestUsableAddresses`, `TestLeastMultiple`, `TestPaddingBoundaries`, `TestLocatorVectors` (`testdata/vectors/locator.json`); `TestResolvedIPVectors` (`testdata/vectors/resolved_ip.json`, `internal/testkit.ResolvedIPVectors`), `TestResolvedIPWellKnownPrefix`; `capsule.TestRegisteredExtensionsWhereRegistered` (never in a capsule); the cases of §64 of v0.11 and v0.12 for `datekeys.capsule` in `locator.json`: the addresses, a locator with a rejected address and a usable one, the resources of the rest, the data of the extension and the plaintexts of the locator |
| 45 | Release API | `provider.ReleaseSource` interface only (server out of scope, plan §2) | — | | 45 | Release API: its answer is the release object of §47.1 (v0.15), and its user a network source; the HTTP form is informative | the object: `provider.EncodeRelease`, `provider.DecodeRelease`; the API itself is out of scope (server, plan §2) | `provider.TestReleaseVectors` |
| 46 | Release Queue | out of scope (server) | — | | 46 | Release Queue | out of scope (server) | — |
| 47 | Release Cache | every release is verified again: `capsule.Open` step 10 and `agewrap.TimeIdentity` | mutations *release of another round* | | 47 | Release Cache | every release is verified again: `capsule.Open` step 10 and `agewrap.TimeIdentity`; `release_material` is the release object (v0.15): `provider.EncodeRelease` | mutations *release of another round* |
| 47.1 | Release object (v0.15): deterministic CBOR without a frame, `{0: "datekeys-release", 1: 1, 2: chain_hash, 3: round, 4: signature}`; size from 1 to 1024 bytes, then type and version, then schema (`ERR_NON_CANONICAL_CBOR`, a version other than 1 `ERR_UNSUPPORTED_VERSION`); the chain hash, the round and the signature at step 10; drand's JSON accepted as the input of the caller, `ERR_RELEASE_INVALID` when unreadable; a writer saves the object | `provider/release.go`: `EncodeRelease`, `DecodeRelease` (`releaseWire` with `codec.CheckSchema` and `codec.Unmarshal`), `ParseRelease`, `NewReleaseObject`, `MaxReleaseObjectSize`, `MaxReleaseJSONSize`; `cmd/datekeys` `releaseInHand`, `writeRelease` | `provider.TestReleaseVectors`, `TestEncodeRelease`, `FuzzDecodeRelease`; `testdata/vectors/release.json`, `testdata/releases/<round>.dkr`; `internal/testkit.TestVectorFilesAreCurrent` |
| 48 | Multi-relay; a network source verifies every response with the rules of §63 step 10 and discards the invalid ones: none valid is `ERR_RELEASE_UNAVAILABLE` at step 9, and so is any other failure of a source, with no other code | `provider/drand.Client` (race, first *verified* release wins; the failure of each relay kept as text only, a context that ended detectable with `errors.Is`), the `provider.ReleaseSource` contract, `capsule.Open` (step 9 keeps only the text of a source error with another code or none) | `drand.TestRaceWaitsForAValidSignature`, `TestRejectMalformedRelayResponses`, `TestFetchErrorHasOneCode`, `TestUnavailabilityAndCancellation`; `capsule.TestReleaseFromANetworkSource`, `TestReleaseSourceErrorsAtStep9` | | 48 | Multi-relay; a network source verifies every response with the rules of §63 step 10 and discards the invalid ones: none valid is `ERR_RELEASE_UNAVAILABLE` at step 9, and so is any other failure of a source, with no other code | `provider/drand.Client` (race, first *verified* release wins; the failure of each relay kept as text only, a context that ended detectable with `errors.Is`), the `provider.ReleaseSource` contract, `capsule.Open` (step 9 keeps only the text of a source error with another code or none) | `drand.TestRaceWaitsForAValidSignature`, `TestRejectMalformedRelayResponses`, `TestFetchErrorHasOneCode`, `TestUnavailabilityAndCancellation`; `capsule.TestReleaseFromANetworkSource`, `TestReleaseSourceErrorsAtStep9` |
| 49 | Direct recovery from the provider | `provider/drand` | `drand.TestLiveRelays`, `capsule.TestLiveLifecycle` (`-tags integration`) | | 49 | Direct recovery from the provider | `provider/drand`; v0.15: a release in hand, `provider.Supplier` (`provider.Encoded`, `provider.Archive`) in `capsule.OpenOptions.Release`; `datekeys decrypt -release` | `drand.TestLiveRelays`, `capsule.TestLiveLifecycle` (`-tags integration`); `capsule.TestReleaseInHand`, `TestReleaseInHandErrors`; `cmd/datekeys.TestDecryptWithReleaseInHand` |
| 50 | Historical release dependency | documented in `README.md` | — | | 50 | Historical release dependency; v0.15: the `.dkr` next to the `.dkc` as the main path, the `.dkr` next to a `.dkk` with a locator, the release archive as an informative format | documented in `README.md`; `provider.Archive`, `provider.EncodeArchiveHeader`; `datekeys decrypt -save-release`, `datekeys release` | `provider.TestArchive`; `cmd/datekeys.TestReleaseCommand`, `TestDecryptWithReleaseInHand` (*a local archive*); the `archive` block of `testdata/vectors/release.json`, `testdata/releases/archive_1000_1004.bin` |
| 51 | Quicknet release verification; order and codes of §63 step 10: the round (`ERR_ROUND_MISMATCH`), then the signature, the canonical encoding of a point of G1 other than the identity (§12.2) that verifies as the BLS signature of the round (`ERR_RELEASE_INVALID`); those codes for a release supplied directly, a network source discarding an invalid one at step 9 (`ERR_RELEASE_UNAVAILABLE`) | `provider.Verify` | `provider.TestVerifyPublishedReleases`, `TestVerifyRejects` (x + p, the point at infinity alone, with a payload or with the sort flag, no compression flag, the negated signature), `TestVerifyUsesThePinnedKeyOnly`; `capsule.TestReleaseFromANetworkSource`; mutations *DateKey A + release of round B*, *release of another round*, *release signature …* | | 51 | Quicknet release verification; order and codes of §63 step 10: the round (`ERR_ROUND_MISMATCH`), then the signature, the canonical encoding of a point of G1 other than the identity (§12.2) that verifies as the BLS signature of the round (`ERR_RELEASE_INVALID`); those codes for a release supplied directly, a network source discarding an invalid one at step 9 (`ERR_RELEASE_UNAVAILABLE`) | `provider.Verify`; v0.15: first the chain hash a release object names, `ERR_PROFILE_MISMATCH` (`provider.Verify`) | `provider.TestVerifyPublishedReleases`, `TestVerifyRejects` (x + p, the point at infinity alone, with a payload or with the sort flag, no compression flag, the negated signature), `TestVerifyUsesThePinnedKeyOnly`; `capsule.TestReleaseFromANetworkSource`; mutations *DateKey A + release of round B*, *release of another round*, *release signature …*; the `objects` of `testdata/vectors/release.json`; mutations *release object of another chain* |
| 52 | DNS / MITM | `provider/drand` (no redirects, bounded responses, BLS) | `drand.TestRedirectsAreNotFollowed`, `TestRejectMalformedRelayResponses`, `TestRandomnessMustMatchWhenPresent` | | 52 | DNS / MITM | `provider/drand` (no redirects, bounded responses, BLS) | `drand.TestRedirectsAreNotFollowed`, `TestRejectMalformedRelayResponses`, `TestRandomnessMustMatchWhenPresent` |
| 53 | Harvest now, decrypt later | `cmd/datekeys` warning beyond one year | `cmd/datekeys.TestLongHorizonWarning` | | 53 | Harvest now, decrypt later | `cmd/datekeys` warning beyond one year; v0.15: the release among what a long horizon needs (text) | `cmd/datekeys.TestLongHorizonWarning` |
| 54 | Extensions: data absent or a non-empty opaque byte string, never decoded; 1 to 64 per array; `extension_id` of at least 1 byte; `extension_version` ≤ 2^32−1; elements in strictly ascending unsigned bytewise order of the UTF-8 bytes of `extension_id` (a proper prefix first, never UTF-16 code units or a collation), so one `extension_id` per array, and none in both arrays; a known extension in an object or array it is not registered for is unknown there, its data never interpreted | `extension.New`, `Canonical`, `EncodeArray` (refuses, through `codec.Encoder.Fail`, an array that `DecodeArray` rejects), `DecodeArray` (64 entries checked on the array head, explicit key 2 check), `CheckDisjoint` (linear merge), `CheckCritical`, `CheckNoncritical`, `Unusable`; `Object`, `Array`, the optional `Placement` of a `Registry`, `KnownIn`, and `CheckCriticalIn` and `CheckNoncriticalIn`, which `capsule` runs at steps 4, 9.a and 14 | `extension.TestNew`, `TestData`, `TestCanonicalSorts`, `TestOrderIsUnsignedBytewise`, `TestCanonicalRejects`, `TestEncodeArrayRejects`, `TestDecodeArrayRejects`, `TestCheckDisjoint`, `TestCheckDisjointIsLinear`, `TestCheckCritical`, `TestCheckNoncritical`, `TestPlacement`, `FuzzDecodeArray`; `capsule.TestKnownCriticalExtensions`, `TestUnusableNoncriticalExtensions`, `TestExtensionPlacement`; mutations *unknown critical … extension*, *known critical … extension with invalid data*, *extension_version above 2^32-1*, *null extension data* | | 54 | Extensions: data absent or a non-empty opaque byte string, never decoded; 1 to 64 per array; `extension_id` of at least 1 byte; `extension_version` ≤ 2^32−1; elements in strictly ascending unsigned bytewise order of the UTF-8 bytes of `extension_id` (a proper prefix first, never UTF-16 code units or a collation), so one `extension_id` per array, and none in both arrays; a known extension in an object or array it is not registered for is unknown there, its data never interpreted | `extension.New`, `Canonical`, `EncodeArray` (refuses, through `codec.Encoder.Fail`, an array that `DecodeArray` rejects), `DecodeArray` (64 entries checked on the array head, explicit key 2 check), `CheckDisjoint` (linear merge), `CheckCritical`, `CheckNoncritical`, `Unusable`; `Object`, `Array`, the optional `Placement` of a `Registry`, `KnownIn`, and `CheckCriticalIn` and `CheckNoncriticalIn`, which `capsule` runs at steps 4, 9.a and 14 | `extension.TestNew`, `TestData`, `TestCanonicalSorts`, `TestOrderIsUnsignedBytewise`, `TestCanonicalRejects`, `TestEncodeArrayRejects`, `TestDecodeArrayRejects`, `TestCheckDisjoint`, `TestCheckDisjointIsLinear`, `TestCheckCritical`, `TestCheckNoncritical`, `TestPlacement`, `FuzzDecodeArray`; `capsule.TestKnownCriticalExtensions`, `TestUnusableNoncriticalExtensions`, `TestExtensionPlacement`; mutations *unknown critical … extension*, *known critical … extension with invalid data*, *extension_version above 2^32-1*, *null extension data* |
| 55 | Auxiliary integrity | `capsule_digest` treated as UX only | — | | 55 | Auxiliary integrity | `capsule_digest` treated as UX only | — |
| 55.1 | Trust model: who writes each section, from which step and by what it is bound, what it never proves | no code of its own: `header_binding` (step 15), the age header MACs (steps 11, 13 and 17), `capsule_id` and `capsule_digest` (step 9.a) | `capsule.TestTrustModel` (a capsule forged from the public bytes of `time_only.dkc` or `format2_time_only.dkc` opens; a control that declares L + 1 over a zero padding byte opens to another content; edited PUBLIC_HEADER data passes steps 1 to 8 and fails step 15; other `.dkk` extension data opens the capsule) | | 55.1 | Trust model: who writes each section, from which step and by what it is bound, what it never proves | no code of its own: `header_binding` (step 15), the age header MACs (steps 11, 13 and 17), `capsule_id` and `capsule_digest` (step 9.a) | `capsule.TestTrustModel` (a capsule forged from the public bytes of `time_only.dkc` or `format2_time_only.dkc` opens; a control that declares L + 1 over a zero padding byte opens to another content; edited PUBLIC_HEADER data passes steps 1 to 8 and fails step 15; other `.dkk` extension data opens the capsule) |
@ -96,21 +99,22 @@ v0.12 and v0.11 number their sections the same, but for §7.10, new in v0.14. A
| 60 | Conceptual Go interfaces | `provider.ReleaseSource`, `provider.Verify`, `datekey.Resolve`, `datekey.RoundTime` | — | | 60 | Conceptual Go interfaces | `provider.ReleaseSource`, `provider.Verify`, `datekey.Resolve`, `datekey.RoundTime` | — |
| 61 | `time_only` encryption flow, format 3: the files measured, hashed, sealed and read again | `capsule.EncryptFiles` | `capsule.TestEncryptFilesRoundTrip`, `TestEncryptFilesLengths`, `TestEncryptFilesChangedFile`, `TestSealedControlLength`; the live test (`-tags integration`) | | 61 | `time_only` encryption flow, format 3: the files measured, hashed, sealed and read again | `capsule.EncryptFiles` | `capsule.TestEncryptFilesRoundTrip`, `TestEncryptFilesLengths`, `TestEncryptFilesChangedFile`, `TestSealedControlLength`; the live test (`-tags integration`) |
| 62 | `time_and_key` encryption flow, format 3: 16 recipients, and the SEALED_CONTROL_LEN of an INNER_ACCESS_AGE of 16 stanzas | `capsule.EncryptFiles` | `capsule.TestEncryptFilesTimeAndKey`, `TestEncryptRoundTripBothPolicies`, `TestPortableKeysAreNeverReused`, `TestSealedControlLength` | | 62 | `time_and_key` encryption flow, format 3: 16 recipients, and the SEALED_CONTROL_LEN of an INNER_ACCESS_AGE of 16 stanzas | `capsule.EncryptFiles` | `capsule.TestEncryptFilesTimeAndKey`, `TestEncryptRoundTripBothPolicies`, `TestPortableKeysAreNeverReused`, `TestSealedControlLength` |
| 62.1 | Writer rules: format 3 only, formats 1 and 2 being written only by a generator of test vectors; an instant after the clock of the writer; from 1 to 16 credentials, none twice, canonical and not of low order; dummies and a random order; `capsule_id`, `I_PAYLOAD`, `I_ACCESS`, `credential_id`, dummies and order from a CSPRNG, `I_PAYLOAD` and dummies never reused or derived; L known before sealing, at most L_MAX, code 1 or 2; SEALED_CONTROL_LEN exact, measured by a provisional seal and checked; limits; on error, the output is discarded; in format 3, the area of 32768 bytes, signed or not, and 65536 only when the creator widens it once the signatures are made, without signing again for it, a capsule refused when they do not fit, `SECURITY_CBOR` always and empty without a signature or a seal, another area or `SECURITY_CBOR` only from a generator of test vectors (rule 13), a head with a fresh salt, a file or a comment, the order of R8, the layout and the SHA-256 of what is written, at most 16 MiB, paths and texts refused with the rule and the character, the mtime taken at load and omitted out of range, the three CBOR objects decoded with the rules of the reader (MUST), and files that must not change between the two readings; with a signature or a seal, each verified with the rules of the reader before writing, never one that gives F1, F2, F5, S1, S2 or S3 (rule 19), `AUTHOR_MESSAGE` given as text and its code shown before each signature, `SIGNERS` closed before the first (rule 20), a CAdES-T for each signer of `alg` 2 (rule 21), the seal of `seal_type` 2 over `SEAL_SUBJECT` after the signature (rule 22), and no secret on disk while waiting for them (rule 25); the public note only when asked for, with the rules of the declared author and a warning (rule 23); the rules of §38.1 for a key of words, and for a `.dkk` with a locator the rest stored before the `.dkk` is written (rule 24). SHOULD: code 2 by default, self-checks, wiping | `capsule.EncryptFiles` (`newHead`, `readSource`, `selfCheckHead`) and `capsule.Encrypt`, through their sealer (`EncryptOptions.Padding`, `accessRecipients`, `fillSlots`, `copyExactly`, `selfCheckHeader`, `selfCheckControl`, `selfCheckInner`, `selfCheckPayload`); `agewrap.CheckX25519Recipient`; `EncryptOptions.TestVectors` for `Encrypt` of format 2, and `internal/testkit.Build`, generators of test vectors (§70); in format 3, `capsule.AreaLen`, `LargeAreaLen` and `EncryptOptions.LargeArea`, the area decided by `EncryptFiles`, in the `prepare` that it gives `sealer.write`, once `sealer.security` has signed and sealed and checked both with `EvaluateSecurityIn` (rules 13, 19, 21 and 22), `EncryptOptions.AuthorKey`, `CMSSigner` and `Sealer`, a typed nil in one of them an error (`newSealer`, `isNil`), nothing written to `dst` before they return and the control and `I_PAYLOAD` kept in memory (rule 25); `EncryptOptions.TestAreaLen`, with `TestVectors`, the area of 512 bytes of the fixtures of v0.10 (rule 13); `EncryptOptions.PublicNote` and `extension.CheckWrite` (rule 23, §72); `EncryptOptions.Words` and `wordkey.Check`, and `locator.NewEnvelope`, `Locator.Marshal` and `Info.Extension` (rule 24); `cmd/datekeys`: `announced` (rule 20) and the warning of `-note` (rule 23) | `capsule.TestEncryptFilesRejects`, `TestEncryptFilesChangedFile`, `TestEncryptIsForTestVectors`, `TestEncryptFilesHeadCritical`, `TestEncryptRejectsInvalidOptions`, `TestCredentialBounds`, `TestEncryptSourceLength`, `TestEncryptSelfCheck`, `TestSealedControlLength`, `TestPayloadIdentityReuse`, `TestStanzaOrderIsUniform`, `TestDummyRecipients`, `TestPortableKeysAreNeverReused`; `cmd/datekeys.TestEncryptRefusesPaths`; rules 13 and 19 to 25: `capsule.TestEncryptFilesSigned`, `TestEncryptFilesSignatureChecked` (nothing written), `TestEncryptFilesCMSAndSeal` (a signature without a required signer, or without seals, is not written), `TestAreaChosenAfterSigning` (the area widened once signed, signing once; without a signature, 32 KiB with `LargeArea`), `TestWriterOptionsChecked`, `TestPublicNoteRules`, `TestRegisteredExtensionsWhereRegistered`, `TestEncryptFilesWords`; `wordkey.TestCheck`; `locator.TestUsableAddresses`, `TestInfo`; `cmd/datekeys.TestAuthorSignRoundTrip` (the key and the code before the signature), `TestPublicNoteCLI`, `TestKeyOfWords`; rule 25 has no test of its own | | 62.1 | Writer rules: format 3 only, formats 1 and 2 being written only by a generator of test vectors; an instant after the clock of the writer; from 1 to 16 credentials, none twice, canonical and not of low order; dummies and a random order; `capsule_id`, `I_PAYLOAD`, `I_ACCESS`, `credential_id`, dummies and order from a CSPRNG, `I_PAYLOAD` and dummies never reused or derived; L known before sealing, at most L_MAX, code 1 or 2; SEALED_CONTROL_LEN exact, measured by a provisional seal and checked; limits; on error, the output is discarded; in format 3, the area of 32768 bytes, signed or not, and 65536 only when the creator widens it once the signatures are made, without signing again for it, a capsule refused when they do not fit, `SECURITY_CBOR` always and empty without a signature or a seal, another area or `SECURITY_CBOR` only from a generator of test vectors (rule 13), a head with a fresh salt, a file or a comment, the order of R8, the layout and the SHA-256 of what is written, at most 16 MiB, paths and texts refused with the rule and the character, the mtime taken at load and omitted out of range, the three CBOR objects decoded with the rules of the reader (MUST), and files that must not change between the two readings; with a signature or a seal, each verified with the rules of the reader before writing, never one that gives F1, F2, F5, S1, S2 or S3 (rule 19), `AUTHOR_MESSAGE` given as text and its code shown before each signature, `SIGNERS` closed before the first (rule 20), a CAdES-T for each signer of `alg` 2 (rule 21), the seal of `seal_type` 2 over `SEAL_SUBJECT` after the signature (rule 22), and no secret on disk while waiting for them (rule 25); the public note only when asked for, with the rules of the declared author and a warning (rule 23); the rules of §38.1 for a key of words, and for a `.dkk` with a locator the rest stored before the `.dkk` is written (rule 24). SHOULD: code 2 by default, self-checks, wiping | `capsule.EncryptFiles` (`newHead`, `readSource`, `selfCheckHead`) and `capsule.Encrypt`, through their sealer (`EncryptOptions.Padding`, `accessRecipients`, `fillSlots`, `copyExactly`, `selfCheckHeader`, `selfCheckControl`, `selfCheckInner`, `selfCheckPayload`); `agewrap.CheckX25519Recipient`; `EncryptOptions.TestVectors` for `Encrypt` of format 2, and `internal/testkit.Build`, generators of test vectors (§70); in format 3, `capsule.AreaLen`, `LargeAreaLen` and `EncryptOptions.LargeArea`, the area decided by `EncryptFiles`, in the `prepare` that it gives `sealer.write`, once `sealer.security` has signed and sealed and checked both with `EvaluateSecurityIn` (rules 13, 19, 21 and 22), `EncryptOptions.AuthorKey`, `CMSSigner` and `Sealer`, a typed nil in one of them an error (`newSealer`, `isNil`), nothing written to `dst` before they return and the control and `I_PAYLOAD` kept in memory (rule 25); `EncryptOptions.TestAreaLen`, with `TestVectors`, the area of 512 bytes of the fixtures of v0.10 (rule 13); `EncryptOptions.PublicNote` and `extension.CheckWrite` (rule 23, §72); `EncryptOptions.Words` and `wordkey.Check`, and `locator.NewEnvelope`, `Locator.Marshal` and `Info.Extension` (rule 24); `cmd/datekeys`: `announced` (rule 20) and the warning of `-note` (rule 23); v0.15, rules 26 to 28 (SHOULD of the SDK): `datekeys release` and `decrypt -save-release` save the `.dkr`; the warning and the annex next to the `.dkc` are not implemented by the CLI | `capsule.TestEncryptFilesRejects`, `TestEncryptFilesChangedFile`, `TestEncryptIsForTestVectors`, `TestEncryptFilesHeadCritical`, `TestEncryptRejectsInvalidOptions`, `TestCredentialBounds`, `TestEncryptSourceLength`, `TestEncryptSelfCheck`, `TestSealedControlLength`, `TestPayloadIdentityReuse`, `TestStanzaOrderIsUniform`, `TestDummyRecipients`, `TestPortableKeysAreNeverReused`; `cmd/datekeys.TestEncryptRefusesPaths`; rules 13 and 19 to 25: `capsule.TestEncryptFilesSigned`, `TestEncryptFilesSignatureChecked` (nothing written), `TestEncryptFilesCMSAndSeal` (a signature without a required signer, or without seals, is not written), `TestAreaChosenAfterSigning` (the area widened once signed, signing once; without a signature, 32 KiB with `LargeArea`), `TestWriterOptionsChecked`, `TestPublicNoteRules`, `TestRegisteredExtensionsWhereRegistered`, `TestEncryptFilesWords`; `wordkey.TestCheck`; `locator.TestUsableAddresses`, `TestInfo`; `cmd/datekeys.TestAuthorSignRoundTrip` (the key and the code before the signature), `TestPublicNoteCLI`, `TestKeyOfWords`; rule 25 has no test of its own; `cmd/datekeys.TestReleaseCommand` |
| 63 | Decryption flow; step 2 accepts the formats 1, 2 and 3, and the steps after it apply the rules of the format: in formats 2 and 3, 16 stanzas at step 12, a control of schema version 2 at step 14, L, the code and P at step 16, a plaintext of P bytes with a zero padding at step 17 (`ERR_INTEGRITY` whenever it is found), the first L bytes at step 18; in format 3, step 17 in its substeps, a failure of age or a plaintext whose length is not P prevailing and a code other than `ERR_INTEGRITY` reported only after reading to EOF, and a caller without a `Sink` stopped right after step 2; steps 4 and 14 validate critical extensions (unknown, then invalid data); step 5 reads SEALED_CONTROL, a MUST (`ERR_INTEGRITY`), and SHOULD inspect its age header; step 8 argument rules; step 9 order: the `.dkk` as an object (decoded there when still encoded), its `capsule_id` and `capsule_digest`, credentials (nil identities are none) before the clock, round time, request, and nothing of the credentials under `time_only`; a network source verifies each response with the rules of step 10 and discards the invalid ones (none valid: `ERR_RELEASE_UNAVAILABLE`, step 9), and any failure of a source is `ERR_RELEASE_UNAVAILABLE` alone, whatever code its error carries; step 10: round, then signature, a canonical point other than the identity (§12.2), the codes of a release supplied directly; step 11: the tlock stanza body `U \|\| V \|\| W` of \|U\| + 32 bytes (128 in Quicknet), U canonical and not the identity, the IBE check r·G == U, every failure `ERR_INTEGRITY`, H2, H3 and H4 those of drand/kyber `encrypt/ibe`, H2 over the element of GT serialized in the order of kilic/bls12-381 (c1 before c0 at every level of the tower), with the frozen vector H2(e(G1, G2)) = `cb87319f24560b5231579a09ad79f12e`; the codes of the identities at steps 11, 13 (malformed X25519 stanza `ERR_INTEGRITY`, an identity that unwraps two stanzas `ERR_POLICY_STRUCTURE_MISMATCH` whatever the order, none `ERR_ACCESS_INVALID`) and 17; step 15 `ERR_HEADER_BINDING` | `capsule.Inspect` (steps 1–8), `capsule.Open` (steps 9–18; `openBody`, `drain`, `ErrSinkRequired`; `OpenOptions.AccessKeyFile`, `checkAccessKey`, `checkCapsuleDigest`), the `provider.ReleaseSource` contract, `provider/drand.Client` and `capsule.sourceFailure` (step 9), `tlock.TimeUnlock` with the kyber-bls12381 pairing (step 11), MUST rules inside `agewrap` identities (`AccessIdentity` tries every identity on every stanza; `TimeIdentity` checks the length of the tlock stanza body and U before `tlock.TimeUnlock`); no error copies the text of an error of age, tlock, kyber or drand (`agewrap`, `capsule.classify`), since kyber's IBE error carries the candidate plaintext and r; `cmd/datekeys` hands the `.dkk` over encoded; `datekeys inspect -json` rendered by `internal/inspectview` | `capsule.TestConformanceFixtures` (stage by stage), `TestOpen3`, `TestOpen3Substeps`, `TestFormatDispatch`, `TestFormatRelabel`, `TestPaddingChecksAtStep17`, `TestTlockFailureDiagnosticsCarryNoSecrets`, `TestPlaintextWriterFailureKeepsItsText`, `TestAccessKeyCheckOrder`, `TestAccessKeyFileAtStep9`, `TestPrecedenceAcrossSteps`, `TestControlCriticalBeforeHeaderBinding`, `TestReleaseFromANetworkSource`, `TestReleaseSourceErrorsAtStep9`, `agewrap.TestAccessIdentityStrictness`, `TestMalformedX25519Stanzas`, `TestTlockH2Vector` (`testdata/vectors/tlock_ibe.json`, generated by `internal/testkit.IBEVectors`, and step 11 recomputed with H2 and H4 against the file key tlock unwraps), `cmd/datekeys.TestDecryptAccessKeyOrder`, `TestMutationCorpus`, `TestInspectDifferentialCorpus` (`testdata/vectors/inspect_differential.json`: 5110 deterministic mutations of 14 fixtures, two of them of format 3, the 1825 of the format 1 ones first with the verdict of steps 1–8, generated by `internal/testkit.InspectDifferential`); `cmd/datekeys.TestInspectJSONGoldens` (`testdata/fixtures/*.inspect.json`) | | 63 | Decryption flow; step 2 accepts the formats 1, 2 and 3, and the steps after it apply the rules of the format: in formats 2 and 3, 16 stanzas at step 12, a control of schema version 2 at step 14, L, the code and P at step 16, a plaintext of P bytes with a zero padding at step 17 (`ERR_INTEGRITY` whenever it is found), the first L bytes at step 18; in format 3, step 17 in its substeps, a failure of age or a plaintext whose length is not P prevailing and a code other than `ERR_INTEGRITY` reported only after reading to EOF, and a caller without a `Sink` stopped right after step 2; steps 4 and 14 validate critical extensions (unknown, then invalid data); step 5 reads SEALED_CONTROL, a MUST (`ERR_INTEGRITY`), and SHOULD inspect its age header; step 8 argument rules; step 9 order: the `.dkk` as an object (decoded there when still encoded), its `capsule_id` and `capsule_digest`, credentials (nil identities are none) before the clock, round time, request, and nothing of the credentials under `time_only`; a network source verifies each response with the rules of step 10 and discards the invalid ones (none valid: `ERR_RELEASE_UNAVAILABLE`, step 9), and any failure of a source is `ERR_RELEASE_UNAVAILABLE` alone, whatever code its error carries; step 10: round, then signature, a canonical point other than the identity (§12.2), the codes of a release supplied directly; step 11: the tlock stanza body `U \|\| V \|\| W` of \|U\| + 32 bytes (128 in Quicknet), U canonical and not the identity, the IBE check r·G == U, every failure `ERR_INTEGRITY`, H2, H3 and H4 those of drand/kyber `encrypt/ibe`, H2 over the element of GT serialized in the order of kilic/bls12-381 (c1 before c0 at every level of the tower), with the frozen vector H2(e(G1, G2)) = `cb87319f24560b5231579a09ad79f12e`; the codes of the identities at steps 11, 13 (malformed X25519 stanza `ERR_INTEGRITY`, an identity that unwraps two stanzas `ERR_POLICY_STRUCTURE_MISMATCH` whatever the order, none `ERR_ACCESS_INVALID`) and 17; step 15 `ERR_HEADER_BINDING` | `capsule.Inspect` (steps 1–8), `capsule.Open` (steps 9–18; `openBody`, `drain`, `ErrSinkRequired`; `OpenOptions.AccessKeyFile`, `checkAccessKey`, `checkCapsuleDigest`), the `provider.ReleaseSource` contract, `provider/drand.Client` and `capsule.sourceFailure` (step 9), `tlock.TimeUnlock` with the kyber-bls12381 pairing (step 11), MUST rules inside `agewrap` identities (`AccessIdentity` tries every identity on every stanza; `TimeIdentity` checks the length of the tlock stanza body and U before `tlock.TimeUnlock`); no error copies the text of an error of age, tlock, kyber or drand (`agewrap`, `capsule.classify`), since kyber's IBE error carries the candidate plaintext and r; `cmd/datekeys` hands the `.dkk` over encoded; `datekeys inspect -json` rendered by `internal/inspectview`; v0.15: step 9.c only for `OpenOptions.Source`, a release in hand (`OpenOptions.Release`) not compared with the clock and `Opened.ClockBehind`; step 10 starts with `provider.ParseRelease`, then `provider.Verify` with the chain hash | `capsule.TestConformanceFixtures` (stage by stage), `TestOpen3`, `TestOpen3Substeps`, `TestFormatDispatch`, `TestFormatRelabel`, `TestPaddingChecksAtStep17`, `TestTlockFailureDiagnosticsCarryNoSecrets`, `TestPlaintextWriterFailureKeepsItsText`, `TestAccessKeyCheckOrder`, `TestAccessKeyFileAtStep9`, `TestPrecedenceAcrossSteps`, `TestControlCriticalBeforeHeaderBinding`, `TestReleaseFromANetworkSource`, `TestReleaseSourceErrorsAtStep9`, `agewrap.TestAccessIdentityStrictness`, `TestMalformedX25519Stanzas`, `TestTlockH2Vector` (`testdata/vectors/tlock_ibe.json`, generated by `internal/testkit.IBEVectors`, and step 11 recomputed with H2 and H4 against the file key tlock unwraps), `cmd/datekeys.TestDecryptAccessKeyOrder`, `TestMutationCorpus`, `TestInspectDifferentialCorpus` (`testdata/vectors/inspect_differential.json`: 5110 deterministic mutations of 14 fixtures, two of them of format 3, the 1825 of the format 1 ones first with the verdict of steps 1–8, generated by `internal/testkit.InspectDifferential`); `cmd/datekeys.TestInspectJSONGoldens` (`testdata/fixtures/*.inspect.json`); `capsule.TestReleaseInHand`, `TestReleaseInHandErrors`; mutations *round not reached yet* (opens) and *round not reached yet, from a network source* |
| 64 | Mandatory mutation tests: the first two lists in the three formats, the list of format 2 in format 2, and that of format 3, four of whose cases open with their verdicts, F2 for a signature of `alg` 1 that does not verify among them; the lists of v0.11 and v0.12: the signature of `alg` 1 and of `alg` 2, the area widened after signing, the seal, `alg` and `seal_type` 4294967295, the same P with a signature and without, the public note, the key of words and `datekeys.capsule` | `internal/testkit.Mutations` (the corpus: `specMutations` for each format, `furtherMutations`, `format2Mutations`, `format3Mutations` with `LoadedFixture.WithBody`, among them those of the list of v0.11 from `format3_signed`, `format3_unsigned` and `format3_note`, `signed1`), `internal/testkit.MutationCorpus` (its export); the cases of formats 2 and 3 derived without randomness, by sealing the fixtures again with their known file keys and nonces (`internal/testkit/reseal.go`, `mutations3.go`), exported as edits of their fixture (`internal/testkit.Splice`); the cases of `alg` 2, `seal_type` 2 and `datekeys.capsule`, vectors of `security_cms.json` and `locator.json`, frozen once written (`internal/testkit/genfixtures`, `frozenVectors`) | `capsule.TestMutationCorpus`: the 178 listed mutations, 33 in each format, the 23 of the list of format 2, the 48 of that of format 3 and 8 of that of v0.11 (`internal/testkit.SpecMutationsPerFormat`, `Format2SpecMutations`, `Format3SpecMutations`, `V011SpecMutations`), plus 40 more, built afresh; `capsule.TestExportedMutationCorpus`: `testdata/vectors/mutations.json`, the same 218 cases as frozen data (capsule, `.dkk`, identities, recorded release, clock, registry, known extensions), replayed with the recorded error and step, or the recorded verdicts; `capsule.TestPointMutationsChangeOnlyTheEncoding`: the ten point mutations keep a valid header MAC, and a decoder that reduces coordinates modulo p opens the c0 + p and x + p cases; `internal/testkit.TestResealReproducesFixtures`, `TestFixedX25519Stanza`; the cases of the lists of v0.11 and v0.12 outside the corpus: `ed25519strict.TestVectors` (`ed25519_strict.json`), `capsule.TestCMSVectors` (`security_cms.json`), `locator.TestLocatorVectors` (`locator.json`), `wordkey.TestKeyVector`, `TestNormalize` and `TestCheck`, `testdata/vectors/note.json`, and the same P of the fixtures `format3_unsigned` and `format3_signed` (`capsule.TestConformanceFixtures`); those of `alg` 2, `seal_type` 2 and `datekeys.capsule` of the lists of v0.11 and v0.12, in `security_cms.json` and `locator.json` | | 64 | Mandatory mutation tests: the first two lists in the three formats, the list of format 2 in format 2, and that of format 3, four of whose cases open with their verdicts, F2 for a signature of `alg` 1 that does not verify among them; the lists of v0.11 and v0.12: the signature of `alg` 1 and of `alg` 2, the area widened after signing, the seal, `alg` and `seal_type` 4294967295, the same P with a signature and without, the public note, the key of words and `datekeys.capsule` | `internal/testkit.Mutations` (the corpus: `specMutations` for each format, `furtherMutations`, `format2Mutations`, `format3Mutations` with `LoadedFixture.WithBody`, among them those of the list of v0.11 from `format3_signed`, `format3_unsigned` and `format3_note`, `signed1`), `internal/testkit.MutationCorpus` (its export); the cases of formats 2 and 3 derived without randomness, by sealing the fixtures again with their known file keys and nonces (`internal/testkit/reseal.go`, `mutations3.go`), exported as edits of their fixture (`internal/testkit.Splice`); the cases of `alg` 2, `seal_type` 2 and `datekeys.capsule`, vectors of `security_cms.json` and `locator.json`, frozen once written (`internal/testkit/genfixtures`, `frozenVectors`) | `capsule.TestMutationCorpus`: the 178 listed mutations, 33 in each format, the 23 of the list of format 2, the 48 of that of format 3 and 8 of that of v0.11 (`internal/testkit.SpecMutationsPerFormat`, `Format2SpecMutations`, `Format3SpecMutations`, `V011SpecMutations`), plus 44 more, built afresh; `capsule.TestExportedMutationCorpus`: `testdata/vectors/mutations.json`, the same 222 cases as frozen data (capsule, `.dkk`, identities, recorded release and its source, clock, registry, known extensions), replayed with the recorded error and step, or the recorded verdicts; `capsule.TestPointMutationsChangeOnlyTheEncoding`: the ten point mutations keep a valid header MAC, and a decoder that reduces coordinates modulo p opens the c0 + p and x + p cases; `internal/testkit.TestResealReproducesFixtures`, `TestFixedX25519Stanza`; the cases of the lists of v0.11 and v0.12 outside the corpus: `ed25519strict.TestVectors` (`ed25519_strict.json`), `capsule.TestCMSVectors` (`security_cms.json`), `locator.TestLocatorVectors` (`locator.json`), `wordkey.TestKeyVector`, `TestNormalize` and `TestCheck`, `testdata/vectors/note.json`, and the same P of the fixtures `format3_unsigned` and `format3_signed` (`capsule.TestConformanceFixtures`); those of `alg` 2, `seal_type` 2 and `datekeys.capsule` of the lists of v0.11 and v0.12, in `security_cms.json` and `locator.json` |
| 65 | Quicknet vectors | `internal/testkit.RoundVectors` | `datekey.TestGoldenRoundVectors` | | 65 | Quicknet vectors | `internal/testkit.RoundVectors` | `datekey.TestGoldenRoundVectors` |
| 66 | `dk1_` vectors | `internal/testkit.DK1Vectors` | `datekey.TestGoldenDK1Vectors` | | 66 | `dk1_` vectors | `internal/testkit.DK1Vectors` | `datekey.TestGoldenDK1Vectors` |
| 67 | `.dkc` vectors: the format 1 fixtures of v0.8.2, kept for compatibility, and format 2 fixtures for both policies, both codes, L = 0, one, several and 16 credentials, and extensions; the format 3 fixtures: one file, a tree, a comment alone, both codes, `time_and_key` with a portable key, an area of 1024 bytes, security of version 2, a signature of `alg` 4294967295 and that with a seal of `seal_type` 4294967295, the area of 32 KiB without a signature, a signature of `alg` 1, that with a seal of `seal_type` 2 and a file whose mtime is later than the seal, and a signature of `alg` 2 of two signers, ECDSA P-256 and RSA 2048, each with its CAdES-T; the records give the format, L, the code, P and the stanza each credential opens, and in format 3 the head, security, each file and the verdicts with their lines, and for a signature or a seal the commitments, `AUTHOR_MESSAGE` and its code, the key or `SIGNERS` and the certificates, the result of each signer, `SEAL_SUBJECT` and the token; the padding vectors, and those of paths, keys of R7, heads, security, `security_cms.json`, `ed25519_strict.json`, `note.json` and `locator.json` | `testdata/fixtures/*.dkc` + `*.json`, `internal/testkit/genfixtures`, which never regenerates a format 1 fixture, gives the five fixtures that `EncryptFiles` wrote in v0.10 their area of 512 bytes (`EncryptOptions.TestAreaLen`) and recomputes the derived fields of every record; `format3_note`, with a public note, for the mutations of §64; the frozen `datekeys inspect -json` output of each, `*.inspect.json`; `testdata/vectors/padding.json`; `security_cms.json` and `locator.json`, frozen once written (`frozenVectors`); formats in `testdata/README.md` | `capsule.TestConformanceFixtures` (`checkBody3`, `checkSignature3`), `TestPaddingAcrossChunks` (a capsule generated at run time), `TestCMSVectors`; `locator.TestLocatorVectors`; `ed25519strict.TestVectors`; `internal/testkit.TestFormat3VectorFiles`, `TestVectorFilesAreCurrent`; `cmd/datekeys.TestInspectJSONGoldens`, `TestDecryptFixtures`, `TestDecryptFormat3Fixtures`, `TestMTimeAfterSeal` | | 67 | `.dkc` vectors: the format 1 fixtures of v0.8.2, kept for compatibility, and format 2 fixtures for both policies, both codes, L = 0, one, several and 16 credentials, and extensions; the format 3 fixtures: one file, a tree, a comment alone, both codes, `time_and_key` with a portable key, an area of 1024 bytes, security of version 2, a signature of `alg` 4294967295 and that with a seal of `seal_type` 4294967295, the area of 32 KiB without a signature, a signature of `alg` 1, that with a seal of `seal_type` 2 and a file whose mtime is later than the seal, and a signature of `alg` 2 of two signers, ECDSA P-256 and RSA 2048, each with its CAdES-T; the records give the format, L, the code, P and the stanza each credential opens, and in format 3 the head, security, each file and the verdicts with their lines, and for a signature or a seal the commitments, `AUTHOR_MESSAGE` and its code, the key or `SIGNERS` and the certificates, the result of each signer, `SEAL_SUBJECT` and the token; the padding vectors, and those of paths, keys of R7, heads, security, `security_cms.json`, `ed25519_strict.json`, `note.json` and `locator.json` | `testdata/fixtures/*.dkc` + `*.json`, `internal/testkit/genfixtures`, which never regenerates a format 1 fixture, gives the five fixtures that `EncryptFiles` wrote in v0.10 their area of 512 bytes (`EncryptOptions.TestAreaLen`) and recomputes the derived fields of every record; `format3_note`, with a public note, for the mutations of §64; the frozen `datekeys inspect -json` output of each, `*.inspect.json`; `testdata/vectors/padding.json`; `security_cms.json` and `locator.json`, frozen once written (`frozenVectors`); formats in `testdata/README.md` | `capsule.TestConformanceFixtures` (`checkBody3`, `checkSignature3`), `TestPaddingAcrossChunks` (a capsule generated at run time), `TestCMSVectors`; `locator.TestLocatorVectors`; `ed25519strict.TestVectors`; `internal/testkit.TestFormat3VectorFiles`, `TestVectorFilesAreCurrent`; `cmd/datekeys.TestInspectJSONGoldens`, `TestDecryptFixtures`, `TestDecryptFormat3Fixtures`, `TestMTimeAfterSeal` |
| 68 | `.dkk` vectors, with the exact extension data; one carries an extension with data, two accompany a format 2 capsule and one a format 3 capsule | `testdata/fixtures/*.dkk` + `*.dkk.json`; `time_and_key_portable_extension.dkk` derived by `genfixtures`; `format2_time_and_key_portable.dkk`, `format2_time_and_key_recipients.dkk`, `format3_time_and_key_portable.dkk` | `accesskey.TestFixtures`, `TestFixtureWithExtension`; `capsule.TestAccessKeyFixtureWithExtension` | | 68 | `.dkk` vectors, with the exact extension data; one carries an extension with data, two accompany a format 2 capsule and one a format 3 capsule | `testdata/fixtures/*.dkk` + `*.dkk.json`; `time_and_key_portable_extension.dkk` derived by `genfixtures`; `format2_time_and_key_portable.dkk`, `format2_time_and_key_recipients.dkk`, `format3_time_and_key_portable.dkk` | `accesskey.TestFixtures`, `TestFixtureWithExtension`; `capsule.TestAccessKeyFixtureWithExtension` |
| 69 | Normative errors, including `ERR_EXTENSION_DATA_INVALID` and `ERR_HEAD_INVALID`; every error of the module wraps exactly one | `errors.go`; `provider/drand.Client` and step 9 of `capsule.Open` keep another code of a failure as text only | `datekeys.TestCatalogueMatchesSpec`, `TestCode`; `drand.TestFetchErrorHasOneCode`; `capsule.TestReleaseSourceErrorsAtStep9` | | 69 | Normative errors, including `ERR_EXTENSION_DATA_INVALID` and `ERR_HEAD_INVALID`; every error of the module wraps exactly one | `errors.go`; `provider/drand.Client` and step 9 of `capsule.Open` keep another code of a failure as text only | `datekeys.TestCatalogueMatchesSpec`, `TestCode`; `drand.TestFetchErrorHasOneCode`; `capsule.TestReleaseSourceErrorsAtStep9` |
| 69.1 | Error precedence: the first failing layer of each object (frame, a truncated prelude before the version; type tag and schema version; CBOR profile and CDDL, except the rules with codes of their own; fields with codes of their own in ascending key order, an extension unknown in its object or array before invalid data), the step order of §63 across objects and steps; only the optional inspection of steps 5, 6 and 8 and the `capsule_digest` check can change the code; the codes of step 10 are those of a release supplied directly, one from a network source being discarded at step 9; in format 2, the 16 stanzas belong to step 12, the version of CONTROL_CBOR against the format to layer 2 of step 14, the rules of keys 6 and 7 to its layer 3, and the length and padding of the plaintext to step 17 | `capsule.ParsePrelude`, `capsule.DecodeHeader`, `capsule.DecodeControl`, `accesskey.Decode`, `accesskey.DecodeBody`, `profile.Decode`, `codec.CheckSchema`, `codec.Unmarshal`, `extension.CheckCriticalIn`, `capsule.checkAccessKey`, `OpenOptions.AccessKeyFile`, `agewrap.AccessIdentity`, `provider/drand.Client` | `capsule.TestPrecedenceWithinPublicHeader`, `TestPrecedenceAcrossSteps` (with the examples of format 2), `TestDecodeHeaderReportsTheCDDLFirst`, `TestAccessKeyCheckOrder`, `TestAccessKeyFileAtStep9`, `TestControlCriticalBeforeHeaderBinding`, `TestReleaseFromANetworkSource`, `TestExtensionPlacement`; `accesskey.TestDecodePrecedence`; `agewrap.TestAccessIdentityStrictness`; `profile.TestDecodePrecedence`, `TestPinPathMatchesDecode`; `cmd/datekeys.TestDecryptAccessKeyOrder`; `extension.TestCheckCritical`, `TestPlacement`; `codec.TestCheckSchemaVersionForms`; `testdata/vectors/cbor.json`, `inspect_differential.json` | | 69.1 | Error precedence: the first failing layer of each object (frame, a truncated prelude before the version; type tag and schema version; CBOR profile and CDDL, except the rules with codes of their own; fields with codes of their own in ascending key order, an extension unknown in its object or array before invalid data), the step order of §63 across objects and steps; only the optional inspection of steps 5, 6 and 8 and the `capsule_digest` check can change the code; the codes of step 10 are those of a release supplied directly, one from a network source being discarded at step 9; in format 2, the 16 stanzas belong to step 12, the version of CONTROL_CBOR against the format to layer 2 of step 14, the rules of keys 6 and 7 to its layer 3, and the length and padding of the plaintext to step 17 | `capsule.ParsePrelude`, `capsule.DecodeHeader`, `capsule.DecodeControl`, `accesskey.Decode`, `accesskey.DecodeBody`, `profile.Decode`, `codec.CheckSchema`, `codec.Unmarshal`, `extension.CheckCriticalIn`, `capsule.checkAccessKey`, `OpenOptions.AccessKeyFile`, `agewrap.AccessIdentity`, `provider/drand.Client` | `capsule.TestPrecedenceWithinPublicHeader`, `TestPrecedenceAcrossSteps` (with the examples of format 2), `TestDecodeHeaderReportsTheCDDLFirst`, `TestAccessKeyCheckOrder`, `TestAccessKeyFileAtStep9`, `TestControlCriticalBeforeHeaderBinding`, `TestReleaseFromANetworkSource`, `TestExtensionPlacement`; `accesskey.TestDecodePrecedence`; `agewrap.TestAccessIdentityStrictness`; `profile.TestDecodePrecedence`, `TestPinPathMatchesDecode`; `cmd/datekeys.TestDecryptAccessKeyOrder`; `extension.TestCheckCritical`, `TestPlacement`; `codec.TestCheckSchemaVersionForms`; `testdata/vectors/cbor.json`, `inspect_differential.json` |
| 70 | Compatibility: a reader accepts the three formats and opens formats 1 and 2 with the semantics of v0.8.2 and v0.9; an implementation that writes capsules writes format 3, and only a generator of test vectors writes formats 1 and 2; a reader should report the format; the format is neither the version of the specification (`datekeys.SpecVersion`) nor that of the module (`datekeys.Version`, `datekeys version`); a reader of v0.10 opens the capsules of v0.11, with an area of 32 or 64 KiB, F1 for `alg` 1 and 2, S1 for `seal_type` 2, and the public note and `datekeys.capsule` ignored, and a reader of v0.11 those of v0.10, with their area of 512 bytes; v0.12 changes no format, only texts of the verdicts and how a certificate and a token are read | magic and version checks; `capsule.Format`; `codec.Peek` and `codec.CheckSchema` read keys 0 and 1 only, before strict decoding, with a type tag of at most `codec.MaxTypeTagLen` bytes; `Inspection.Prelude.Format`, `Opened.Format`; `internal/testkit.Build`; `version.go`, where `SpecVersion` stays 0.11 until v0.12 is approved; `capsule.EvaluateSecurity`, which reads security without a context, as a reader of v0.10; `ParseBodyFrame`, which accepts any area of the frame | mutations; `capsule.TestFormatDispatch`, `TestFormatRelabel`, `TestFormat1Compatibility`; `codec.TestPeek`, `TestCheckSchema`, `TestCheckSchemaVersionForms`, `FuzzPeek`; `capsule.TestDecodeSchemaVersion`; `datekeys.TestSpecVersionNamesTheSpecification`, `TestVersion`; `cmd/datekeys.TestVersion`; `capsule.TestEvaluateSecurityIn` and `TestEvaluateCMS` (no context: F1), `TestEvaluateSeal` (no context: S1), `TestCMSVectors` (the cases without a context); the fixtures of v0.10, with their area of 512 bytes, among those of v0.11 (`TestConformanceFixtures`) | | 70 | Compatibility: a reader accepts the three formats and opens formats 1 and 2 with the semantics of v0.8.2 and v0.9; an implementation that writes capsules writes format 3, and only a generator of test vectors writes formats 1 and 2; a reader should report the format; the format is neither the version of the specification (`datekeys.SpecVersion`) nor that of the module (`datekeys.Version`, `datekeys version`); a reader of v0.10 opens the capsules of v0.11, with an area of 32 or 64 KiB, F1 for `alg` 1 and 2, S1 for `seal_type` 2, and the public note and `datekeys.capsule` ignored, and a reader of v0.11 those of v0.10, with their area of 512 bytes; v0.12 changes no format, only texts of the verdicts and how a certificate and a token are read | magic and version checks; `capsule.Format`; `codec.Peek` and `codec.CheckSchema` read keys 0 and 1 only, before strict decoding, with a type tag of at most `codec.MaxTypeTagLen` bytes; `Inspection.Prelude.Format`, `Opened.Format`; `internal/testkit.Build`; `version.go`, where `SpecVersion` stays 0.11 until v0.12 is approved; `capsule.EvaluateSecurity`, which reads security without a context, as a reader of v0.10; `ParseBodyFrame`, which accepts any area of the frame; v0.15: a valid release in hand opens a capsule with a clock behind its round time | mutations; `capsule.TestFormatDispatch`, `TestFormatRelabel`, `TestFormat1Compatibility`; `codec.TestPeek`, `TestCheckSchema`, `TestCheckSchemaVersionForms`, `FuzzPeek`; `capsule.TestDecodeSchemaVersion`; `datekeys.TestSpecVersionNamesTheSpecification`, `TestVersion`; `cmd/datekeys.TestVersion`; `capsule.TestEvaluateSecurityIn` and `TestEvaluateCMS` (no context: F1), `TestEvaluateSeal` (no context: S1), `TestCMSVectors` (the cases without a context); the fixtures of v0.10, with their area of 512 bytes, among those of v0.11 (`TestConformanceFixtures`); mutation *round not reached yet* |
| 71 | Profile registry | `profile.Decode` + `profile.NewRegistry` with pinned hashes | `profile.TestRegistry` | | 71 | Profile registry | `profile.Decode` + `profile.NewRegistry` with pinned hashes | `profile.TestRegistry` |
| 72 | Extension registry and registration rules, among them the objects and arrays where each extension may appear, and an encoder never writes one elsewhere; the encoder decodes its own output before sealing; security-relevant claims in CONTROL_CBOR or under a signature extension, `.dkk` extension data advisory; the registered extensions, `datekeys.note` in the noncritical array of PUBLIC_HEADER and `datekeys.capsule` in the noncritical array of a `.dkk`, both informative | `extension.Registry`, `extension.Set`, `extension.DataValidator`, `extension.Placement` (optional: a `Registry` without it knows its extensions in every object and array); `extension.Standard`, the registry of the extensions of the specification (`NoteID`, `CapsuleID`), and `locator.Standard`, which validates the data of `datekeys.capsule`; `extension.CheckWrite`, which the writers of capsules and `.dkk` files apply with `extension.Standard` (`capsule.newSealer`, `accesskey.AccessKey.MarshalBody`); self-checks in `capsule.Encrypt`, `capsule.EncryptFiles`, `accesskey.MarshalBody` and `locator.Info.Extension`; these writers take no `Registry`: the application writes each extension of its own only where it is registered | `capsule.TestKnownCriticalExtensions`, `TestUnusableNoncriticalExtensions`, `TestExtensionPlacement`, `TestNestedDataSealsAndOpens`, `TestRegisteredExtensionsWhereRegistered`, `TestPublicNoteRules`, `FuzzEncodeImpliesDecode`; `extension.TestPlacement`, `TestCheckWrite`; `locator.TestInfo` | | 72 | Extension registry and registration rules, among them the objects and arrays where each extension may appear, and an encoder never writes one elsewhere; the encoder decodes its own output before sealing; security-relevant claims in CONTROL_CBOR or under a signature extension, `.dkk` extension data advisory; the registered extensions, `datekeys.note` in the noncritical array of PUBLIC_HEADER and `datekeys.capsule` in the noncritical array of a `.dkk`, both informative | `extension.Registry`, `extension.Set`, `extension.DataValidator`, `extension.Placement` (optional: a `Registry` without it knows its extensions in every object and array); `extension.Standard`, the registry of the extensions of the specification (`NoteID`, `CapsuleID`), and `locator.Standard`, which validates the data of `datekeys.capsule`; `extension.CheckWrite`, which the writers of capsules and `.dkk` files apply with `extension.Standard` (`capsule.newSealer`, `accesskey.AccessKey.MarshalBody`); self-checks in `capsule.Encrypt`, `capsule.EncryptFiles`, `accesskey.MarshalBody` and `locator.Info.Extension`; these writers take no `Registry`: the application writes each extension of its own only where it is registered | `capsule.TestKnownCriticalExtensions`, `TestUnusableNoncriticalExtensions`, `TestExtensionPlacement`, `TestNestedDataSealsAndOpens`, `TestRegisteredExtensionsWhereRegistered`, `TestPublicNoteRules`, `FuzzEncodeImpliesDecode`; `extension.TestPlacement`, `TestCheckWrite`; `locator.TestInfo` |
| 74 | Provisional aspects; the implementation limits of the reference (name lengths, `public_key`, `period`, maximum `extension_id` length, `dk1_` length, age parser limits, `ERR_POLICY_STRUCTURE_MISMATCH` for INNER_ACCESS_AGE) | `profile.ValidID`, `validName`, `maxPublicKeyLen`, `maxPeriod`; `extension.MaxIDLen`; `datekey.MaxEncodedLen`; `filippo.io/age` | `profile.TestValidateRejectsTamperedProfiles`, `TestIntegerRanges`; `extension.TestNew`; the vectors of `cbor.json` named after the implementation limit | | 74 | Provisional aspects; the implementation limits of the reference (name lengths, `public_key`, `period`, maximum `extension_id` length, `dk1_` length, age parser limits, `ERR_POLICY_STRUCTURE_MISMATCH` for INNER_ACCESS_AGE) | `profile.ValidID`, `validName`, `maxPublicKeyLen`, `maxPeriod`; `extension.MaxIDLen`; `datekey.MaxEncodedLen`; `filippo.io/age` | `profile.TestValidateRejectsTamperedProfiles`, `TestIntegerRanges`; `extension.TestNew`; the vectors of `cbor.json` named after the implementation limit |
| 75 | Blocking requirements before v1.0 | items 1–9 and 11 above, with fixtures and mutations in the three formats; item 10 (external review) pending | — | | 75 | Blocking requirements before v1.0 | items 1–9 and 11 above, with fixtures and mutations in the three formats; item 10 (external review) pending | — |
| 76 | Change policy; the normative changes of v0.8.2: the extension change and its reproducible cases; the refinements and theirs; the amendment on point canonicality and its case (a second implementation on `tlock-js` and `@noble/curves` 1.9.7 accepted U with c0 + p and a signature with x + p); the corrections of the formal review (an invalid release from a network source, the objects and arrays of each extension, the serialization of GT in H2) and their cases, and those of its second round (the encoder rule of §72, the codes of step 10 in §17 and §51 for a release supplied directly, one code for any failure of a source at step 9); the normative changes of v0.9, capsule format 2, and their cases; those of v0.10, capsule format 3, and theirs; those of v0.11, the area of 32 KiB, what is signed, the signatures of `alg` 1 and 2, the seal of `seal_type` 2, the key of words, the public note and `datekeys.capsule`, and theirs; and those of the draft v0.12, which change no format: the names of certificates and the seal of each signer of F6 in the verdicts, the holder without its identifier, the profile of the certificate, identifiers, repetitions and edge cases, the addresses and the padding of the locator, errata, and the vectors of v0.11 | `extension`, `codec`, fixture `time_only_extensions` regenerated; refinements: the order of `capsule.checkAccessKey`, `BODY_LEN` 0 in `accesskey.Decode`, CR and LF and invalid UTF-8 in `datekey.Parse`, the `.dkk` decoded at step 9.a (`OpenOptions.AccessKeyFile`, the CLI), nil identities in `capsule.Open`, every identity tried in `agewrap.AccessIdentity`, `profile.NewRegistry` through `Decode`, `Profile.Validate` rule 1 first; four new `dk1.json` vectors; corrections: `extension.Placement` and the object-aware checks, the `provider.ReleaseSource` contract, `testdata/vectors/tlock_ibe.json`; second round: the error of `provider/drand.Client` and of step 9 in `capsule.Open`; v0.9: rows 22, 29, 29.1, 31, 33, 36, 37, 39, 55.2, 56, 57, 61, 62, 62.1, 63 and 70; v0.10: rows 22, 23, 29 to 29.7, 31, 56, 57, 61 to 64 and 67 to 70; v0.11: rows 24.1, 29.2, 29.3, 29.7 to 29.12, 38.1, 44.1, 62.1, 64, 67, 70 and 72; v0.12: rows 29.3, 29.7, 29.10, 29.11, 44.1, 64, 67 and 70, and the sizes of the locator in `spec/datekeys.cddl` | case 2: `extension.TestNew`; case 3: `capsule.TestNaNKeyedDataHasOneVerdict`; case 4: `capsule.TestExtensionFixtureData`; case 5: `capsule.TestNestedDataSealsAndOpens`; case 6: `capsule.TestHugeExtensionArraysAreRejected`, `extension.TestCheckDisjointIsLinear`; refinements: the tests of rows 12.1, 15, 17, 19, 22, 28.1, 35, 36, 40, 51, 55.1, 63 and 69.1, and `extension.TestOrderIsUnsignedBytewise`; amendment: the tests of rows 12.2 and 64; corrections: `capsule.TestReleaseFromANetworkSource`, `TestExtensionPlacement`, `extension.TestPlacement`, `agewrap.TestTlockH2Vector`; second round: `capsule.TestReleaseSourceErrorsAtStep9`, `TestExtensionPlacement` (the noncritical array of a `.dkk`), `drand.TestFetchErrorHasOneCode`, `TestUnavailabilityAndCancellation`, `datekeys.TestCode`; v0.9: the tests that §76 names for each change, in rows 22, 29.1, 31, 37, 39, 55.2, 57, 62.1, 64 and 70; v0.10: those of the rows it changed; v0.11: those of the rows it added and changed; v0.12: changes 1 and 2, `capsule.TestEvaluateCMS`, `TestIssuerTextFiltered`, `cmd/datekeys.TestRows` and the record of `format3_signed_cms`; change 5, `der.TestSetOfSorted`, `TestCheck` and `cms.TestTSTInfoStrict`; changes 6 and 7, `locator.TestAddresses`, `TestUsableAddresses`, `TestLeastMultiple` and `TestPaddingBoundaries`; change 9, `testdata/vectors/security.json` and the fixture `format3_seal_unsupported`; the cases of changes 1 and 3 to 5 in `security_cms.json`, and those of changes 6 and 7 in `locator.json` (`TestLocatorVectors`) | | 76 | Change policy; the normative changes of v0.8.2: the extension change and its reproducible cases; the refinements and theirs; the amendment on point canonicality and its case (a second implementation on `tlock-js` and `@noble/curves` 1.9.7 accepted U with c0 + p and a signature with x + p); the corrections of the formal review (an invalid release from a network source, the objects and arrays of each extension, the serialization of GT in H2) and their cases, and those of its second round (the encoder rule of §72, the codes of step 10 in §17 and §51 for a release supplied directly, one code for any failure of a source at step 9); the normative changes of v0.9, capsule format 2, and their cases; those of v0.10, capsule format 3, and theirs; those of v0.11, the area of 32 KiB, what is signed, the signatures of `alg` 1 and 2, the seal of `seal_type` 2, the key of words, the public note and `datekeys.capsule`, and theirs; and those of the draft v0.12, which change no format: the names of certificates and the seal of each signer of F6 in the verdicts, the holder without its identifier, the profile of the certificate, identifiers, repetitions and edge cases, the addresses and the padding of the locator, errata, and the vectors of v0.11 | `extension`, `codec`, fixture `time_only_extensions` regenerated; refinements: the order of `capsule.checkAccessKey`, `BODY_LEN` 0 in `accesskey.Decode`, CR and LF and invalid UTF-8 in `datekey.Parse`, the `.dkk` decoded at step 9.a (`OpenOptions.AccessKeyFile`, the CLI), nil identities in `capsule.Open`, every identity tried in `agewrap.AccessIdentity`, `profile.NewRegistry` through `Decode`, `Profile.Validate` rule 1 first; four new `dk1.json` vectors; corrections: `extension.Placement` and the object-aware checks, the `provider.ReleaseSource` contract, `testdata/vectors/tlock_ibe.json`; second round: the error of `provider/drand.Client` and of step 9 in `capsule.Open`; v0.9: rows 22, 29, 29.1, 31, 33, 36, 37, 39, 55.2, 56, 57, 61, 62, 62.1, 63 and 70; v0.10: rows 22, 23, 29 to 29.7, 31, 56, 57, 61 to 64 and 67 to 70; v0.11: rows 24.1, 29.2, 29.3, 29.7 to 29.12, 38.1, 44.1, 62.1, 64, 67, 70 and 72; v0.12: rows 29.3, 29.7, 29.10, 29.11, 44.1, 64, 67 and 70, and the sizes of the locator in `spec/datekeys.cddl` | case 2: `extension.TestNew`; case 3: `capsule.TestNaNKeyedDataHasOneVerdict`; case 4: `capsule.TestExtensionFixtureData`; case 5: `capsule.TestNestedDataSealsAndOpens`; case 6: `capsule.TestHugeExtensionArraysAreRejected`, `extension.TestCheckDisjointIsLinear`; refinements: the tests of rows 12.1, 15, 17, 19, 22, 28.1, 35, 36, 40, 51, 55.1, 63 and 69.1, and `extension.TestOrderIsUnsignedBytewise`; amendment: the tests of rows 12.2 and 64; corrections: `capsule.TestReleaseFromANetworkSource`, `TestExtensionPlacement`, `extension.TestPlacement`, `agewrap.TestTlockH2Vector`; second round: `capsule.TestReleaseSourceErrorsAtStep9`, `TestExtensionPlacement` (the noncritical array of a `.dkk`), `drand.TestFetchErrorHasOneCode`, `TestUnavailabilityAndCancellation`, `datekeys.TestCode`; v0.9: the tests that §76 names for each change, in rows 22, 29.1, 31, 37, 39, 55.2, 57, 62.1, 64 and 70; v0.10: those of the rows it changed; v0.11: those of the rows it added and changed; v0.12: changes 1 and 2, `capsule.TestEvaluateCMS`, `TestIssuerTextFiltered`, `cmd/datekeys.TestRows` and the record of `format3_signed_cms`; change 5, `der.TestSetOfSorted`, `TestCheck` and `cms.TestTSTInfoStrict`; changes 6 and 7, `locator.TestAddresses`, `TestUsableAddresses`, `TestLeastMultiple` and `TestPaddingBoundaries`; change 9, `testdata/vectors/security.json` and the fixture `format3_seal_unsupported`; the cases of changes 1 and 3 to 5 in `security_cms.json`, and those of changes 6 and 7 in `locator.json` (`TestLocatorVectors`); the changes of v0.15, each with its case: the release object (`release.json`, `testdata/releases`), its chain hash at step 10 and step 9.c (`mutations.json`, field `source`), the recovery (`scripts/recovery_check.sh`) |
| 79 | Informative annex: recovery without DateKeys software (v0.15) | `scripts/recovery` (no package of this module, tlock or drand: Go, `golang.org/x/crypto`, `filippo.io/age`, `drand/kyber-bls12381`), `scripts/recovery_check.sh`, run by `scripts/check.sh` | `scripts/recovery.TestImports`, `TestRecoverFixtures` and the other tests of the package, over eight fixtures of the three formats; `scripts/recovery_check.sh` on `format3_single` and `format3_time_and_key_portable` |
## Error mapping ## Error mapping

File diff suppressed because it is too large Load Diff

@ -65,7 +65,18 @@
of words, the states of a profile) and the root of trust byte for byte: of words, the states of a profile) and the root of trust byte for byte:
the message a Quicknet round signs, its hash to G1, and H2, H3 and H4 of the message a Quicknet round signs, its hash to G1, and H2, H3 and H4 of
the tlock IBE. Its §76 records each change with its case. the tlock IBE. Its §76 records each change with its case.
- `datekeys.cddl`: the CBOR schemas of v0.14, the same as those of v0.12 and v0.13, the three control - `DateKeys_Protocol_Specification_v0.15.md`: the draft v0.15, work in
progress and not approved; the branch `v0.15` implements it. It covers the
long-term recovery of capsules: the release object, the content of a `.dkr`
file that keeps the release of a round next to its capsule (§47.1), with
its chain hash checked at step 10; step 9.c only before a network request,
so that a release in hand is not compared with the clock; the release
archive as an informative format (§50); what the writer and the SDK keep
(§62.1); and an informative annex (§79) to open a capsule without DateKeys
software. It changes no format of `.dkc` or `.dkk`, and one verdict: a
valid release in hand opens a capsule with a clock behind its round time.
Its §76 records each change with its case.
- `datekeys.cddl`: the CBOR schemas of the draft v0.15, those of v0.12, v0.13 and v0.14 with the rule `release` of the draft added, the three control
versions and the security and head objects of format 3 included, with the versions and the security and head objects of format 3 included, with the
encoding rules CDDL cannot express. Those of v0.9 and v0.8.2 are at the tags encoding rules CDDL cannot express. Those of v0.9 and v0.8.2 are at the tags
`spec-v0.9` and `spec-v0.8.2`. `spec-v0.9` and `spec-v0.8.2`.

@ -1,7 +1,8 @@
; DateKeys Protocol Specification v0.14 - CBOR schemas (RFC 8610 CDDL). ; DateKeys Protocol Specification v0.15 draft - CBOR schemas (RFC 8610 CDDL).
; ;
; Normative companion of spec/DateKeys_Protocol_Specification_v0.14.md, which ; Normative companion of spec/DateKeys_Protocol_Specification_v0.15.md, the
; the reference implementation g.activething.com/go/DateKeys implements. The ; draft v0.15, not approved yet; spec-v0.14 tags the schemas of v0.14. The
; draft adds the release object of a .dkr file (spec section 47.1); the other
; schemas have not changed since v0.12. They include the three control ; schemas have not changed since v0.12. They include the three control
; versions: 1, of capsule format 1 (v0.8.2), 2, of format 2 (v0.9), and 3, of ; versions: 1, of capsule format 1 (v0.8.2), 2, of format 2 (v0.9), and 3, of
; format 3, and the security and head objects of format 3. ; format 3, and the security and head objects of format 3.
@ -86,6 +87,25 @@ provider-profile = {
10 => bstr .size 32, ; genesis_seed 10 => bstr .size 32, ; genesis_seed
} }
; Spec section 47.1 (v0.15). The release of a round, the content of a .dkr
; file, the release_material of a Release Cache (section 47) and the answer
; of the Release API (section 45). It has no frame: an input of 0 bytes or of
; more than 1024 is ERR_NON_CANONICAL_CBOR before it is decoded, its layer 1
; (spec section 69.1); a valid one is at most 165 bytes. A violation of this
; rule is ERR_NON_CANONICAL_CBOR, but a version other than 1,
; ERR_UNSUPPORTED_VERSION. No field has a code of its own: at step 10 of
; section 63, the chain_hash is compared with the pinned profile
; (ERR_PROFILE_MISMATCH), then the round with the DateKey
; (ERR_ROUND_MISMATCH), then the signature is verified (ERR_RELEASE_INVALID),
; which is 48 bytes in Quicknet.
release = {
0 => "datekeys-release",
1 => 1,
2 => bstr .size 32, ; chain_hash
3 => 1..max-safe-uint, ; round
4 => bstr .size (1..96), ; signature, a compressed point of BLS12-381
}
; Spec section 24. Stored as exact bytes after the 16-byte PRELUDE and covered ; Spec section 24. Stored as exact bytes after the 16-byte PRELUDE and covered
; by header_binding = SHA-256(PRELUDE || PUBLIC_HEADER). The same header, ; by header_binding = SHA-256(PRELUDE || PUBLIC_HEADER). The same header,
; schema version 1, in all three capsule formats. ; schema version 1, in all three capsule formats.

Loading…
Cancel
Save

Powered by TurnKey Linux.