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.
 
 
Go to file
dev c57ed4869c
Spec v0.8.2: corrections from the formal review
2 weeks ago
.gitea/workflows Host on Gitea, not GitHub 2 weeks ago
.githooks Run the local gate from a versioned pre-push hook 2 weeks ago
accesskey Spec v0.8.2 refinements: error precedence, trust model, strict order 2 weeks ago
agewrap Spec v0.8.2: corrections from the formal review 2 weeks ago
capsule Spec v0.8.2: corrections from the formal review 2 weeks ago
cmd/datekeys Spec v0.8.2 refinements: error precedence, trust model, strict order 2 weeks ago
codec Hand-written CBOR codec and shared test vectors (plan steps 2b and 3) 2 weeks ago
datekey Reject invalid UTF-8 in the dk1_ JSON at step 2 (spec §19) 2 weeks ago
docs Spec v0.8.2: corrections from the formal review 2 weeks ago
extension Spec v0.8.2: corrections from the formal review 2 weeks ago
internal Spec v0.8.2: corrections from the formal review 2 weeks ago
profile Spec v0.8.2 amendment: canonical point encoding; no library error text 2 weeks ago
provider Spec v0.8.2: corrections from the formal review 2 weeks ago
scripts Hand-written CBOR codec and shared test vectors (plan steps 2b and 3) 2 weeks ago
spec Spec v0.8.2: corrections from the formal review 2 weeks ago
testdata Spec v0.8.2: corrections from the formal review 2 weeks ago
.gitattributes Initial implementation of the DateKeys Protocol v0.8.1 2 weeks ago
.gitignore Initial implementation of the DateKeys Protocol v0.8.1 2 weeks ago
.golangci.yml Host on Gitea, not GitHub 2 weeks ago
.goreleaser.yaml Host on Gitea, not GitHub 2 weeks ago
CHANGELOG.md Spec v0.8.2: corrections from the formal review 2 weeks ago
CONTRIBUTING.md Hand-written CBOR codec and shared test vectors (plan steps 2b and 3) 2 weeks ago
LICENSE Initial implementation of the DateKeys Protocol v0.8.1 2 weeks ago
README.es.md Spec v0.8.2 amendment: canonical point encoding; no library error text 2 weeks ago
README.md Spec v0.8.2 amendment: canonical point encoding; no library error text 2 weeks ago
SECURITY.md Hand-written CBOR codec and shared test vectors (plan steps 2b and 3) 2 weeks ago
TRADEMARKS.md Implement v0.8.2 extension data, limits and writer self-checks 2 weeks ago
errors.go Implement v0.8.2 extension data, limits and writer self-checks 2 weeks ago
errors_test.go Implement v0.8.2 extension data, limits and writer self-checks 2 weeks ago
go.mod Spec v0.8.2: corrections from the formal review 2 weeks ago
go.sum Hand-written CBOR codec and shared test vectors (plan steps 2b and 3) 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_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.

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 and dk1_ vectors (spec §65, §66), CBOR profile and schema vectors, the exported mutation corpus and a differential corpus of the pre-unlock checks; formats in testdata/README.md.
  • testdata/fixtures: official .dkc/.dkk fixtures over published rounds, with the BLS signature embedded and every intermediate value (spec §67, §68); they decrypt offline. Each .dkc has its frozen datekeys inspect -json output.
  • internal/testkit.Mutations: the 33 mutations of spec §64 and 32 more, each with its exact error and step, and a check that pre-unlock failures never cause a release request; exported to testdata/vectors/mutations.json.
  • 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.

Powered by TurnKey Linux.