|
|
2 weeks ago | |
|---|---|---|
| .gitea/workflows | 2 weeks ago | |
| .githooks | 2 weeks ago | |
| accesskey | 2 weeks ago | |
| agewrap | 2 weeks ago | |
| capsule | 2 weeks ago | |
| cmd/datekeys | 2 weeks ago | |
| codec | 2 weeks ago | |
| datekey | 2 weeks ago | |
| docs | 2 weeks ago | |
| extension | 2 weeks ago | |
| internal/testkit | 2 weeks ago | |
| profile | 2 weeks ago | |
| provider | 2 weeks ago | |
| scripts | 2 weeks ago | |
| spec | 2 weeks ago | |
| testdata | 2 weeks ago | |
| .gitattributes | 2 weeks ago | |
| .gitignore | 2 weeks ago | |
| .golangci.yml | 2 weeks ago | |
| .goreleaser.yaml | 2 weeks ago | |
| CHANGELOG.md | 2 weeks ago | |
| CONTRIBUTING.md | 2 weeks ago | |
| LICENSE | 2 weeks ago | |
| README.es.md | 2 weeks ago | |
| README.md | 2 weeks ago | |
| SECURITY.md | 2 weeks ago | |
| TRADEMARKS.md | 2 weeks ago | |
| errors.go | 2 weeks ago | |
| errors_test.go | 2 weeks ago | |
| go.mod | 2 weeks ago | |
| go.sum | 2 weeks ago | |
README.md
datekeys-go
Reference implementation in Go of the DateKeys Protocol Specification
v0.8.2 (spec/).
Versión en español.
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 |
Provider Profile, pinned Quicknet profile, profile_hash |
§10–§13 | profile |
| Release sources, local BLS verification, drand relays | §45–§52 | provider, provider/drand |
DateKeyCap .dkc: time_only and time_and_key |
§20–§39, §61–§63 | capsule |
DateKeys Access Key .dkk |
§40–§44 | accesskey |
| Extensions | §54 | extension |
| Deterministic CBOR | §58 | codec |
| Normative errors | §69 | errors.go |
| CLI | — | cmd/datekeys |
No cryptography is implemented here. Encryption is age
(filippo.io/age); the timelock is 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
.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
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.
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
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_onlygives internal coherence, not proof of who created a capsule, before or after it matures;time_and_keyadds 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.
Conformance and tests
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 anddk1_vectors (spec §65, §66).testdata/fixtures: official.dkc/.dkkfixtures over published rounds, with the BLS signature embedded and every intermediate value (spec §67, §68); they decrypt offline.capsule/mutation_test.go: the 23 mutations of spec §64 and 30 more, each with its exact error and step, and a check that pre-unlock failures never cause a release request.docs/traceability.md: spec section → code → test.spec/datekeys.cddl: CBOR schemas.
License
Code: Apache-2.0 (LICENSE). Specification: CC-BY-4.0
(spec/README.md). codec/bech32 is copied from age under its
own license. "DateKeys" is a reserved name: see TRADEMARKS.md.