You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys/README.md

148 lines
6.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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).

Powered by TurnKey Linux.