7.5 KiB
datekeys-go
Implementación de referencia en Go de la DateKeys Protocol Specification
v0.8.2 (spec/).
English version.
DateKeys cifra datos de forma que solo puedan abrirse a partir de un instante elegido. La condición temporal procede del beacon de aleatoriedad Quicknet de drand: los datos se sellan con cifrado timelock hacia una ronda futura, y la firma BLS de esa ronda, que drand publica cuando llega, es la llave. Todo lo que se puede verificar localmente se verifica localmente; relays, cachés y APIs son transportes no confiables.
Estado: v0.x, pre-estándar. La especificación es un borrador y la API puede cambiar antes de v1.0.0. El código aún no ha pasado una revisión criptográfica externa (spec §75). No lo uses para secretos de alto valor.
Qué implementa
| Objeto | Spec | Paquete |
|---|---|---|
DateKey: fecha → ronda, cadena canónica dk1_… |
§14–§19 | datekey |
Provider Profile, perfil Quicknet pinneado, profile_hash |
§10–§13 | profile |
| Fuentes de releases, verificación BLS local, relays drand | §45–§52 | provider, provider/drand |
DateKeyCap .dkc: time_only y time_and_key |
§20–§39, §61–§63 | capsule |
DateKeys Access Key .dkk |
§40–§44 | accesskey |
| Extensiones | §54 | extension |
| CBOR determinista | §58 | codec |
| Errores normativos | §69 | errors.go |
| CLI | — | cmd/datekeys |
Aquí no se implementa criptografía. El cifrado es age
(filippo.io/age); el timelock es tlock
(solo su núcleo exportado); la verificación BLS es la de drand. Este módulo
aporta framing, CBOR, bindings, reglas de verificación y flujo, y aplica las
reglas de stanzas del protocolo dentro de las identities de age, para que un
fichero nunca se acepte solo porque age haya podido desenvolver una clave.
No implementa, a propósito: el servidor y la cola de la Release API, el almacenamiento y la entrega, extensiones concretas ni el cliente TypeScript (plan §2).
Una cápsula, en un dibujo
.dkc = PRELUDE (16 B) || PUBLIC_HEADER (CBOR) || SEALED_CONTROL (age) || PAYLOAD_AGE (age, hasta EOF)
time_only: SEALED_CONTROL = age(tlock ronda R → CONTROL_CBOR)
time_and_key: SEALED_CONTROL = age(tlock ronda R → age(recipients X25519 → CONTROL_CBOR))
CONTROL_CBOR = { header_binding = SHA-256(PRELUDE || PUBLIC_HEADER), I_PAYLOAD, extensiones }
PAYLOAD_AGE = age(X25519 R_PAYLOAD → tus datos), en streaming
CLI
go install g.activething.com/go/DateKeys/cmd/datekeys@latest
El módulo se sirve desde el Gitea del proyecto, cuyo certificado Go no
reconoce por defecto. Define GOPRIVATE=g.activething.com para que el proxy y
la base de datos de sumas de Go no intervengan, e instala el certificado del
servidor o, en una red de confianza, define GOINSECURE=g.activething.com.
datekeys datekey resolve -at 2030-01-01T00:00:00Z
datekeys encrypt -at 2030-01-01T00:00:00Z -in carta.txt -out carta.dkc
datekeys encrypt -at 2030-01-01T00:00:00Z -policy time_and_key -dkk carta.dkk -in carta.txt -out carta.dkc
datekeys inspect -in carta.dkc
datekeys decrypt -in carta.dkc -out carta.txt -dkk carta.dkk
datekeys profile hash
encrypt nunca usa la red. inspect ejecuta solo las comprobaciones previas
al desbloqueo (spec §63, pasos 1 a 8): nunca pide un release ni usa secretos.
decrypt obtiene el release de relays públicos de drand, lo verifica
localmente y publica el plaintext solo cuando age lo ha autenticado entero.
Nunca se sobrescriben ficheros de salida.
Librería
reg, err := profile.Default() // perfil Quicknet pinneado, comprobado contra su profile_hash
// Cifrar: sin red, la ronda se resuelve localmente.
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 es la .dkk; se codifica con accesskey.Encode
Now: time.Now,
})
// Inspeccionar: pasos 1 a 8, sin red ni secretos.
in, err := capsule.Inspect(f, capsule.InspectOptions{Registry: reg})
// Abrir: pasos 1 a 18; el release se verifica localmente.
opened, err := capsule.Open(ctx, tmp, f, capsule.OpenOptions{
Registry: reg,
Source: drand.New(),
AccessKey: key, // o Identities: []age.Identity{...}
Now: time.Now,
})
if errors.Is(err, datekeys.ErrReleaseUnavailable) { /* todavía no */ }
Open escribe el plaintext en streaming; si falla, descarta lo escrito (spec
§56). Todo fallo del protocolo envuelve uno de los 17 errores normativos del
§69, así que errors.Is y datekeys.Code(err) lo identifican.
Propiedades de seguridad y límites
- Confidencialidad temporal bajo el supuesto de umbral de drand. El timelock de Quicknet no es post-cuántico: los ciphertexts guardados durante años quedan expuestos a harvest now, decrypt later (spec §7.7, §53).
- Sin confianza en servidores: el perfil va pinneado en el binario, la ronda se calcula localmente, los releases se verifican con BLS localmente y una firma válida de otra ronda se rechaza (spec §13, §17, §51).
- Integridad: framing, cabecera, control y payload están autenticados; cualquier cambio hace fallar la apertura (fixtures y corpus de mutaciones).
- Sin autoría:
time_onlyda coherencia interna, no prueba de quién creó la cápsula, ni antes ni después de madurar;time_and_keyañade una barrera de acceso, no una firma (spec §36.1). - Recuperar años después exige el release histórico: de un relay drand que aún lo sirva o de cualquier caché, verificado de nuevo localmente (spec §50).
Ver SECURITY.md.
Conformidad y tests
go test ./... # unitarios, vectores golden, fixtures, mutaciones
go test -race -cover ./...
go test -fuzz=FuzzInspect ./capsule # un objetivo de fuzzing cada vez
go test -tags interop ./capsule # las CLI oficiales age y tle abren nuestros ficheros
go test -tags integration ./capsule ./provider/drand # Quicknet en vivo
testdata/vectors: vectores deprofile_hash, fecha→ronda ydk1_(spec §65, §66), vectores del perfil CBOR y de cada schema, el corpus de mutaciones exportado y un corpus diferencial de las comprobaciones previas al desbloqueo; formatos entestdata/README.md.testdata/fixtures: fixtures oficiales.dkc/.dkksobre rondas ya publicadas, con la firma BLS embebida y todos los valores intermedios (spec §67, §68); se descifran sin red. Cada.dkctiene congelada su salida dedatekeys inspect -json.internal/testkit.Mutations: las 33 mutaciones del §64 y 32 más, cada una con su error y su paso exactos, comprobando además que los fallos previos al desbloqueo nunca provocan una petición de release; exportadas atestdata/vectors/mutations.json.docs/traceability.md: sección del spec → código → test.spec/datekeys.cddl: schemas CBOR.
Licencia
Código: Apache-2.0 (LICENSE). Especificación: CC-BY-4.0
(spec/README.md). codec/bech32 se copia de age bajo su
propia licencia. "DateKeys" es un nombre reservado: ver TRADEMARKS.md.