10 KiB
DateKeys, formato de cápsula 3: propuesta para revisión de seguridad
30 de septiembre de 2026. Propuesta de ampliación del protocolo DateKeys v0.9, todavía sin implementar.
Para quien revisa
Qué es. Una ampliación del protocolo DateKeys: el formato de cápsula 3. Hasta la v0.9, una cápsula guarda un solo fichero, sin nombre ni fecha. El formato 3 guarda varios ficheros y carpetas, con sus nombres, tamaños, hashes y fechas de modificación. Añade además una firma de autor opcional y un sello de tiempo opcional, emitido por un servicio de DateKeys.
Estado. Es un diseño. El spec v0.9 existe, y sus dos implementaciones, la de referencia en Go y la de TypeScript para el navegador, están hechas y probadas byte a byte entre sí. Nada del formato 3 está programado todavía.
Qué se pide. Encontrar vulnerabilidades en el diseño, en particular:
- Criptografía: los enunciados que se firman y se sellan, los compromisos, el perfil Ed25519 estricto y los trasplantes entre cápsulas o formatos.
- Privacidad: qué se filtra antes de la fecha de apertura, qué ve el servicio de sellado y qué queda vinculado después.
- El lector ante una cápsula maliciosa: el análisis del CBOR, las reglas de rutas, la extracción a un ZIP o a una carpeta, y el consumo de memoria y de CPU.
- El servicio de sellado: la retrodatación, el robo de la clave y el registro auditable.
- Ambigüedades que dos implementaciones independientes podrían resolver de forma distinta.
Cómo informar. Para cada hallazgo:
- la gravedad: bloqueante, mayor o menor;
- un escenario concreto: qué entrada produce qué resultado;
- la sección afectada;
- una propuesta de arreglo.
Ya revisado. Una revisión adversarial interna encontró 28 problemas, todos corregidos; la lista va al final del documento. No hace falta repetirlos, pero sí comprobar que los arreglos son correctos.
Las referencias a ficheros y líneas (framing.go:56, tempfile.ts:198…) apuntan al código actual de las implementaciones. No hace falta abrirlas: el documento se entiende sin ellas.
El protocolo base (v0.9) en una página
Objetivo. Cifrar hoy un fichero que nadie pueda abrir antes de una fecha, sin confiar en ningún servidor. La clave depende de una firma BLS que la red drand publica en esa fecha. drand Quicknet publica una ronda cada 3 segundos, y el cifrado usa tlock, un IBE sobre BLS12-381. Nadie tiene esa firma antes de la ronda, tampoco quien cifra.
Estructura de una cápsula (.dkc), en este orden:
- PRELUDE, 16 bytes en claro: la marca
DKC1,VERSION(el formato de la cápsula),PUBLIC_HEADER_LENySEALED_CONTROL_LEN. - PUBLIC_HEADER, CBOR en claro:
capsule_id(16 bytes aleatorios), la DateKey (el perfil del proveedor y la ronda), la política de acceso y extensiones públicas. - SEALED_CONTROL = OUTER_TIME_AGE, un fichero
agecon un único stanzatlockpara la ronda. Dentro va una de dos cosas:- con la política
time_only, directamenteCONTROL_CBOR; - con
time_and_key,INNER_ACCESS_AGE: un ficheroagecon exactamente 16 stanzas X25519, que contieneCONTROL_CBOR. Esos 16 stanzas son las credenciales (destinatariosage1…y, opcionalmente, una clave portable.dkk) más señuelos, en orden aleatorio.
- con la política
- PAYLOAD_AGE, un fichero
agecon un stanza X25519 paraR_PAYLOAD. Su texto en claro es el contenido, L bytes, seguido de ceros hasta P = regla(L). Las reglas sonbloque256yreforzado, que es max(bloque256, Padmé).
CONTROL_CBOR contiene:
header_binding, el SHA-256 de PRELUDE ‖ PUBLIC_HEADER;payload_identity, que esI_PAYLOAD, la identidad X25519 cuya pública esR_PAYLOAD;- las extensiones de control;
payload_length(L);- el código de la regla de relleno.
Apertura (§63, pasos 1 a 18):
- Pasos 1 a 8: estructura, sin red y sin secretos.
- Paso 9: la firma de la ronda (el release), que se da a mano o se descarga, y en
time_and_key, la credencial. - Paso 10: la firma BLS se verifica contra la clave pública del perfil, que va fijada en el software.
- Paso 11: se abre
OUTER_TIME_AGE. - Paso 12: estructura de la política.
- Paso 13: la credencial abre exactamente uno de los 16 stanzas de
INNER_ACCESS_AGE. - Paso 14: se decodifica
CONTROL_CBOR. - Paso 15: se comprueba
header_binding. - Paso 16:
I_PAYLOADy P. - Paso 17: se abre
PAYLOAD_AGEy se comprueba el relleno. - Paso 18: se entrega.
Nada se presenta antes de autenticar el contenido entero (§56), y solo se entregan L bytes, nunca el relleno.
Qué se ve antes de la fecha (formato 2): la DateKey (la fecha), la política, capsule_id y el tamaño relleno. No se ve la longitud exacta ni el número de credenciales.
La .dkk, la clave portable, contiene:
- la identidad X25519 de acceso;
capsule_id;- el SHA-256 del
.dkcexacto (capsule_digest).
No lleva MAC ni firma.
Lo que v0.9 no garantiza (§5, §36.1, §55.1):
- autoría: cualquiera puede cifrar hacia una fecha futura con la clave pública del perfil;
- fecha probatoria de creación;
- integridad frente a quien ya abrió la cápsula: puede cifrar otro
PAYLOAD_AGEparaR_PAYLOADy volver a sellar el control; - resistencia poscuántica.
Implementaciones. La de Go es la de referencia, con la CLI datekeys. La de TypeScript funciona en el navegador sin red: la página /create cifra y /inspect abre. Las dos son iguales byte a byte, con fixtures, vectores y mutaciones compartidas. Evitan dependencias nuevas: tienen su propio códec CBOR determinista. En criptografía usan noble (curves, hashes, ciphers) y age-encryption en TypeScript, y la biblioteca estándar, filippo.io/age y drand/tlock en Go.
Decisiones del autor para el formato 3
Estas decisiones son el punto de partida del diseño, no algo a revisar. Un hallazgo puede, eso sí, mostrar que alguna es insegura.
-
Formato 3. El texto en claro de
PAYLOAD_AGE, cifrado y bajo el relleno, pasa a ser tres bloques seguidos:security | head | content. Nada va en PUBLIC_HEADER, enCONTROL_CBORni en el nombre del fichero. Los formatos 1 y 2 siguen abriéndose, y un lector v0.9 rechaza el formato 3 en el paso 2. -
headlleva:- su propia versión;
- una sal aleatoria;
- un comentario opcional por cápsula;
- un autor declarado opcional, que es texto y nunca prueba nada;
- una entrada por fichero, con la ruta relativa (con carpetas),
size,start,end, el SHA-256 y la fecha de modificación.start,endysizedeben cuadrar entre sí.
No lleva tipo de contenido, hash global, permisos ni fecha de creación.
-
La fecha de modificación se toma sola al cargar cada fichero. Se incluye por defecto, con una casilla para quitarla, y es informativa.
-
securitylleva una firma de autor Ed25519 y un sello de tiempo, los dos opcionales, sobreheady el contexto de la cápsula. Si van los dos, primero se firma y el sello cubre también la firma. Ninguno de los dos hace falta para abrir. -
El servicio de sellado es de DateKeys, en el mismo origen que la página. Firma con una clave Ed25519 fijada en el protocolo. Lleva un registro de solo añadir, anclado periódicamente en OpenTimestamps. Sellar es opcional y pide consentimiento.
-
Varios ficheros: la página entrega un ZIP sin compresión que construye con código propio, y la CLI escribe el árbol en un directorio nuevo, nunca fuera de él.
Modelo de amenazas
| Adversario | Qué tiene | Qué no debe conseguir |
|---|---|---|
| A1, observador antes de la fecha | El .dkc; quizá, el tráfico hacia el servicio de sellado |
Nombres, número de ficheros, tamaños, hashes, fechas, si hay firma o sello, autor o comentario |
| A2, quien abre | Todo el contenido, después de la fecha | Hacer pasar una cápsula manipulada por la original, con firma o sello que lo respalden |
| A3, escritor malicioso | La libertad de fabricar cualquier cápsula | Que el lector escriba fuera de su carpeta o sobrescriba ficheros, agotarlo, o engañar con veredictos falsos, nombres que se confunden o un ZIP malicioso |
| A4, servicio de sellado deshonesto o comprometido | Su clave y su registro | Retrodatar sin que se note, o vincular las peticiones con quien crea la cápsula antes de la fecha |
| A5, red | El camino entre la página y el servicio | Alterar o suplantar el sello sin que se detecte |
| A6, tercero que verifica | El paquete de verificación, sin I_PAYLOAD |
Obtener I_PAYLOAD, o un contenido que no está en el paquete |
Preguntas concretas
- ¿Atan
control_commityhead_digestla firma y el sello a esta cápsula y solo a ella? ¿Se pueden trasplantar entre cápsulas, entre formatos o a un paquete de verificación? - ¿Es correcto el perfil «Ed25519 estricto» (§4 del diseño)? ¿Da el mismo resultado en Go 1.26 y en noble 2.4?
- ¿Filtran algo antes de la fecha la trama
SECURITY_LEN ‖ HEAD_LEN, el área de 512 bytes o la cota de ficheros que se deduce de P? - ¿Cierran R1 a R9 el escape de carpeta, las colisiones de nombres y los trucos de Windows (best-fit, alias 8.3, nombres reservados) al extraer a un ZIP y a una carpeta, en Windows, macOS y Linux?
- ¿Garantizan la precedencia del paso 17 y la entrega atómica que nada se presenta antes de autenticarlo todo, y que Go y TypeScript dan el mismo código de error?
- ¿Puede un escritor malicioso agotar memoria o CPU? Por ejemplo, con un head de 16 MiB, 65 535 ficheros, rutas largas o el plegado Unicode.
- ¿Resiste el servicio de sellado la retrodatación como afirma el diseño, con ventanas de clave de un año, registro Merkle, anclaje horario en OpenTimestamps y
max_anchor_delayde 26 h? ¿Qué pasa si roban la clave? - ¿Qué aprende el servicio, qué vincula después de la apertura, y basta con no guardar registros de acceso?
- ¿Pueden inducir a error los textos de los veredictos? ¿Pueden suplantarlos el comentario o el autor declarado?
- ¿Son seguras las claves de autor, con su formato
dkauthor1…, el scrypt de logN 16 y la confianza en el primer uso condicionada al sello? - ¿Hay alguna ambigüedad de codificación que dos implementaciones resolverían de forma distinta?