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

153 lines
6.9 KiB

# 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 g.activething.com/go/DateKeys/cmd/datekeys@latest
```
The module is served by the project's own Gitea, whose certificate Go does not
trust by default. Set `GOPRIVATE=g.activething.com` so that the Go proxy and
checksum database are bypassed for it, and either install the server's
certificate or, on a trusted network, set `GOINSECURE=g.activething.com`.
```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.