# datekeys-go Reference implementation in Go of the **DateKeys Protocol Specification v0.8.1** ([`spec/`](spec/DateKeys_Protocol_Specification_v0.8.1.md)). [Versión en español](README.es.md). DateKeys encrypts data so that it can only be opened after a chosen instant. The time condition comes from the drand **Quicknet** randomness beacon: data is sealed with timelock encryption to a future round, and the round's BLS signature, published by drand when the round arrives, is the key. Everything that can be verified locally is verified locally; relays, caches and APIs are untrusted transports. > **Status: v0.x, pre-standard.** The specification is a draft and the API may > change before v1.0.0. The code has not had an external cryptographic review > yet (spec §75). Do not rely on it for high-value secrets. ## What it implements | Object | Spec | Package | |---|---|---| | DateKey: date → round, canonical `dk1_…` string | §14–§19 | [`datekey`](datekey) | | Provider Profile, pinned Quicknet profile, `profile_hash` | §10–§13 | [`profile`](profile) | | Release sources, local BLS verification, drand relays | §45–§52 | [`provider`](provider), [`provider/drand`](provider/drand) | | DateKeyCap `.dkc`: `time_only` and `time_and_key` | §20–§39, §61–§63 | [`capsule`](capsule) | | DateKeys Access Key `.dkk` | §40–§44 | [`accesskey`](accesskey) | | Extensions | §54 | [`extension`](extension) | | Deterministic CBOR | §58 | [`codec`](codec) | | Normative errors | §69 | [`errors.go`](errors.go) | | CLI | — | [`cmd/datekeys`](cmd/datekeys) | No cryptography is implemented here. Encryption is [age](https://age-encryption.org) (`filippo.io/age`); the timelock is [tlock](https://github.com/drand/tlock) (its exported core only); BLS verification is drand's. This module adds framing, CBOR, bindings, verification rules and the flow, and it enforces the stanza rules of the protocol inside the age identities, so that a file is never accepted just because age could unwrap a key. Not implemented on purpose: the Release API server and queue, storage and delivery, concrete extensions, and the TypeScript client (plan §2). ## A capsule, in one picture ```text .dkc = PRELUDE (16 B) || PUBLIC_HEADER (CBOR) || SEALED_CONTROL (age) || PAYLOAD_AGE (age, to EOF) time_only: SEALED_CONTROL = age(tlock round R → CONTROL_CBOR) time_and_key: SEALED_CONTROL = age(tlock round R → age(X25519 recipients → CONTROL_CBOR)) CONTROL_CBOR = { header_binding = SHA-256(PRELUDE || PUBLIC_HEADER), I_PAYLOAD, extensions } PAYLOAD_AGE = age(X25519 R_PAYLOAD → your data), streamed ``` ## CLI ```bash go install github.com/datekeys/datekeys-go/cmd/datekeys@latest ``` ```bash datekeys datekey resolve -at 2030-01-01T00:00:00Z datekeys encrypt -at 2030-01-01T00:00:00Z -in letter.txt -out letter.dkc datekeys encrypt -at 2030-01-01T00:00:00Z -policy time_and_key -dkk letter.dkk -in letter.txt -out letter.dkc datekeys inspect -in letter.dkc datekeys decrypt -in letter.dkc -out letter.txt -dkk letter.dkk datekeys profile hash ``` `encrypt` never touches the network. `inspect` runs only the pre-unlock checks (spec §63 steps 1–8): it never requests a release and never uses a secret. `decrypt` fetches the release from public drand relays, verifies it locally and publishes the plaintext only after age authenticated all of it. Outputs are never overwritten. ## Library ```go reg, err := profile.Default() // pinned Quicknet profile, checked against its profile_hash // Encrypt: no network, the round is resolved locally. res, err := capsule.Encrypt(dst, src, capsule.EncryptOptions{ Profile: profile.Quicknet(), UnlockAt: time.Date(2030, 1, 1, 0, 0, 0, 0, time.UTC), Policy: capsule.TimeAndKey, NewPortableKey: true, // res.PortableKey is the .dkk; encode it with accesskey.Encode Now: time.Now, }) // Inspect: steps 1–8, no network, no secrets. in, err := capsule.Inspect(f, capsule.InspectOptions{Registry: reg}) // Open: steps 1–18; the release is verified locally. opened, err := capsule.Open(ctx, tmp, f, capsule.OpenOptions{ Registry: reg, Source: drand.New(), AccessKey: key, // or Identities: []age.Identity{...} Now: time.Now, }) if errors.Is(err, datekeys.ErrReleaseUnavailable) { /* not yet */ } ``` `Open` streams the plaintext; on error, discard what was written (spec §56). Every protocol failure wraps one of the 17 normative errors of spec §69, so `errors.Is` and `datekeys.Code(err)` identify it. ## Security properties and limits - **Time confidentiality** holds under drand's threshold assumption. The Quicknet timelock is **not post-quantum**: ciphertexts kept for years are exposed to harvest-now, decrypt-later (spec §7.7, §53). - **No trust in servers**: the profile is pinned in the binary, the round is computed locally, releases are BLS-verified locally, and a valid signature of another round is rejected (spec §13, §17, §51). - **Integrity**: framing, header, control and payload are all authenticated; any change makes opening fail (fixtures and the mutation corpus). - **No authorship**: `time_only` gives internal coherence, not proof of who created a capsule, before or after it matures; `time_and_key` adds an access barrier, not a signature (spec §36.1). - **Recovery years later** needs the historical release: from a drand relay that still serves it or from any cache, re-verified locally (spec §50). See [SECURITY.md](SECURITY.md). ## Conformance and tests ```bash go test ./... # unit, golden vectors, fixtures, mutation corpus go test -race -cover ./... go test -fuzz=FuzzInspect ./capsule # one fuzz target at a time go test -tags interop ./capsule # official age and tle CLIs open our files go test -tags integration ./capsule ./provider/drand # live Quicknet ``` - `testdata/vectors`: profile hash, date→round and `dk1_` vectors (spec §65, §66). - `testdata/fixtures`: official `.dkc`/`.dkk` fixtures over published rounds, with the BLS signature embedded and every intermediate value (spec §67, §68); they decrypt offline. - `capsule/mutation_test.go`: the 20 mutations of spec §64 and 25 more, each with its exact error and step, and a check that pre-unlock failures never cause a release request. - [`docs/traceability.md`](docs/traceability.md): spec section → code → test. - [`spec/datekeys.cddl`](spec/datekeys.cddl): CBOR schemas. ## License Code: Apache-2.0 ([LICENSE](LICENSE)). Specification: CC-BY-4.0 ([spec/README.md](spec/README.md)). `codec/bech32` is copied from age under its own license. "DateKeys" is a reserved name: see [TRADEMARKS.md](TRADEMARKS.md).