# Changelog All notable changes to this module are documented here. The project follows semantic versioning; `v0.x` versions make no API stability promise. ## Unreleased — specification v0.8.2 Moves the module to the DateKeys Protocol Specification v0.8.2, whose one normative change closes the extension format (spec §76). Framing and schema versions do not change. ### Breaking changes - `extension.New(id, version, data []byte)` takes the opaque data bytes instead of a value that it encoded as CBOR, and rejects nil or empty data. An extension without data is the literal `extension.Extension{ID, Version}`, which omits key 2. - Extension data (key 2) must be a byte string of at least one byte. Any other CBOR type, `null` or `h''` at key 2 is now `ERR_NON_CANONICAL_CBOR`, so a v0.8.1 object with such data no longer decodes. The base protocol never decodes the content (§54). - `extension.Wire.Data` is `[]byte`, the content of the byte string, instead of `cbor.RawMessage`. - `codec.Valid` is removed: nothing decodes extension data any more. `codec.FuzzValid` is replaced by `codec.FuzzUnmarshal`. - At most 64 extensions per array and `extension_version` at most 2^32−1, on encode and decode (`ERR_NON_CANONICAL_CBOR`). An `extension_id` appears at most once per object, and arrays are ordered by `extension_id` only. - Provider Profile: `genesis_time` is an unsigned integer, and `period` and `genesis_time` are at most 2^53−1; a negative or larger value is `ERR_NON_CANONICAL_CBOR`. - `null` in a byte-string field is `ERR_NON_CANONICAL_CBOR` (for example a `null` `access_material` was `ERR_ACCESS_INVALID`): nil byte strings, arrays and maps now encode as empty ones, never as `null`. - `capsule.EncodeHeader` and `capsule.DecodeHeader` enforce the 1 MiB PUBLIC_HEADER limit and `accesskey.DecodeBody` the 16 MiB BODY limit. Every frame-limit refusal, including those of `accesskey.MarshalBody` and of `capsule.Encrypt` for SEALED_CONTROL, now wraps `ERR_INTEGRITY` (§57). - `profile.Profile.CanonicalCBOR` refuses a `period` or `genesis_time` outside the schema with `ERR_NON_CANONICAL_CBOR`, as `profile.Decode` does. - The official fixture `time_only_extensions` is regenerated: its header data is the raw UTF-8 bytes of "public label" and its control data is `{0: 7, 1: "sealed"}` (`a2000701667365616c6564`). Every other `.dkc` and `.dkk` keeps its bytes; the fixture and vector metadata name spec 0.8.2. ### Added - `ErrExtensionDataInvalid` (`ERR_EXTENSION_DATA_INVALID`, §69). - `extension.DataValidator`, an optional interface of a `Registry` that validates the data of the extensions it knows: a known critical extension with invalid data fails with `ErrExtensionDataInvalid` (§63 steps 4 and 14, and the `.dkk` check); a known noncritical one is reported in `capsule.Inspection.UnusableExtensions`, `capsule.Opened.UnusableControlExtensions` or `capsule.Opened.UnusableAccessKeyExtensions` (`extension.CheckNoncritical`, `extension.Unusable`) and does not fail. - Encoder self-checks: `capsule.Encrypt` decodes its PUBLIC_HEADER and CONTROL_CBOR, and `accesskey.MarshalBody` its body, with the readers' decoders before sealing or writing (§72). - `extension.MaxExtensions`, `MaxVersion`, `MaxDataLen` and `codec.MaxSafeUint`. - The `.dkk` fixture `time_and_key_portable_extension`, which carries a noncritical extension with data (§68). - `genfixtures -only NAME[,NAME...]` regenerates the named fixtures only. - Tests: the three new §64 mutations (data that is not a byte string, `h''` data, 65 extensions), regression tests for the cases of §76, conformance checks on the exact data bytes, and the fuzz target `capsule.FuzzEncodeImpliesDecode` (header, control and `.dkk`). ### Fixed - `extension.CheckDisjoint` is a linear merge of the two sorted arrays; a PUBLIC_HEADER with 40 000 + 40 000 extensions took 8.3 s in the pairwise check (§76, case 6). - Control data made of 14 or 15 nested arrays was sealed by `Encrypt` and rejected by `Open` at step 14, after the unlock (§76, case 5). - Header data `{NaN: 0, NaN: 1}` gave a nondeterministic verdict (§76, case 3). - `extension.New(id, v, nil)` wrote `null` as data (§76, case 2). - `codec.Unmarshal` wipes its re-encoding, which after the new self-checks held a copy of I_PAYLOAD or `access_material`, and `accesskey.DecodeBody` wipes the material on its error paths. ## Unreleased — v0.1.0 First implementation of the DateKeys Protocol Specification v0.8.1. ### Added - `datekey`: local date → round resolution at full precision (§15), canonical `dk1_` encoding and strict parsing (§18, §19). - `profile`: Provider Profile Deterministic CBOR and `profile_hash` (§11), the pinned Quicknet profile with its chain-hash self-check (§12), and pinned registries (§13). - `provider`: release sources and local BLS verification (§51); `provider/drand`: racing public relays, verifying every answer (§48, §49, §52). - `codec`: Deterministic CBOR with a re-encoding canonicality check (§58, §58.1). - `extension`: the generic extension mechanism (§54). - `agewrap`: strict tlock and X25519 age identities that enforce the stanza rules (§27, §29, §32, §33, §35), and a secret-free header probe. - `capsule`: `.dkc` framing, `Encrypt` for `time_only` and `time_and_key` (§61, §62), `Inspect` (§63 steps 1–8) and `Open` (§63 steps 9–18). - `accesskey`: `.dkk` encoding and decoding (§40–§44). - `cmd/datekeys`: `encrypt`, `decrypt`, `inspect`, `datekey resolve`, `profile hash`, with atomic, non-overwriting outputs. - Official vectors (§65, §66), `.dkc`/`.dkk` fixtures (§67, §68), the mutation corpus (§64), fuzz targets for every parser, interoperability tests with the official `age` and `tle` CLIs, and live Quicknet integration tests. - `spec/datekeys.cddl` and `docs/traceability.md`.