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 3e4755e351
Record the version constants in the traceability table
1 week 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: second-round corrections from the formal review 1 week ago
agewrap Spec v0.8.2: corrections from the formal review 2 weeks ago
capsule Spec v0.8.2: second-round corrections from the formal review 1 week ago
cmd/datekeys Version constants: the specification and the module 1 week 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 Record the version constants in the traceability table 1 week ago
extension Spec v0.8.2: second-round corrections from the formal review 1 week ago
internal Version constants: the specification and the module 1 week ago
profile Spec v0.8.2 amendment: canonical point encoding; no library error text 2 weeks ago
provider Spec v0.8.2: second-round corrections from the formal review 1 week ago
scripts Hand-written CBOR codec and shared test vectors (plan steps 2b and 3) 2 weeks ago
spec Spec v0.8.2: second-round corrections from the formal review 1 week ago
testdata Spec v0.8.2: second-round corrections from the formal review 1 week 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 Version constants: the specification and the module 1 week 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 Version constants: the specification and the module 1 week ago
README.md Version constants: the specification and the module 1 week 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 Version constants: the specification and the module 1 week 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
version.go Version constants: the specification and the module 1 week ago
version_test.go Version constants: the specification and the module 1 week 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).

Versions

Three version numbers, each with its own meaning:

Version Where Changes when
Format Inside the objects: the framing version of DKC1 and DKK1 and the schema version at key 1, all 1 today The format changes. A reader rejects a version it does not know (spec §70)
Specification datekeys.SpecVersion, today 0.8.2, and the tag spec-v0.8.2 The normative text changes. Spec §76 records each change with its case
Module The tags of this Go module, vX.Y.Z, and datekeys.Version() The API or the behaviour changes. Semantic versioning, with no stability promise before v1.0.0

datekeys version prints the module version, the specification and the Go toolchain. A binary built in a checkout shows the pseudo-version of its commit, for example v0.0.0-20260928105528-9ac9cd952f04.

Each release states what it covers, here and in the CHANGELOG. The code on main, not yet released, covers:

  • specification 0.8.2, with format versions 1;
  • the pinned Quicknet profile, and any profile on the three drand schemes that tlock supports;
  • encryption, inspection and opening, and the CLI;
  • every shared vector and fixture of testdata/.

The first tag, v0.1.0, comes once go get works from a clean machine.

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
datekeys version

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 authenticated against whoever does not know the file keys: a third party's change makes opening fail (fixtures and the mutation corpus). Once the round is published anyone can compute the time file key, and anyone can seal a new control for a public header; who can write each part, and from which step it is bound, is the trust model of spec §27 and §55.1.
  • 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.