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.

18 KiB

Recuperación a largo plazo: diseño para decidir

Propuesta para cerrar los dos puntos que §74 de la v0.14 deja como trabajo futuro: «el formato de un objeto de release y de su fuente de archivo» y «el uso del reloj local en el paso 9.c de §63». Recoge el punto 6 de REVISION_completitud_protocolo.md y el 2.6 de REVISION_completitud_v0.13.md. No cambia nada todavía: la v0.14 está cerrada, así que todo lo de aquí iría a una v0.15.

Nota del 7-10. La v0.15 aprobada no sigue este diseño en un punto: el fichero .dkr se descartó. El autor no lo había aprobado conscientemente, y guardarlo junto a la cápsula no tiene sentido: al crearla no hay release que guardar, y cuando llega la ronda la cápsula ya se abre. El objeto release se queda, sin extensión propia, y sale de archivos y servicios de caché que guardan todas las rondas (§50). Lo que aquí se dice del .dkr es historia; lo vigente está en la spec v0.15 y en spec_v0.15/decisiones.md.

1. El problema

Una cápsula de Quicknet se abre con el release de su ronda: la firma BLS de 48 bytes que drand publica en round_time (§15, §63 paso 10). Sin ese release no hay apertura, ni en time_only ni en time_and_key (§7.6). El .dkc no lo contiene y el protocolo no lo guarda (§6, §50).

Lo que no falla

  • Un release es público y se comprueba sin conexión contra el perfil pinneado: e(H(M), clave_pública) == e(firma, G2) (§51, §63 paso 10). No caduca.
  • La firma BLS es única: para una ronda y una clave solo hay una firma válida. Cualquier copia que verifique es el release, venga de donde venga. Un archivo de releases no necesita firma ni confianza.
  • La raíz de confianza está en el texto (§12) y en cada implementación, byte a byte. No depende de que drand siga publicándola.
  • age v1, CBOR determinista (RFC 8949) y BLS12-381 (RFC 9380) son formatos públicos y documentados fuera del proyecto.

Lo que falla, por horizonte

A 5 años

  • Los endpoints cambian. Go, la CLI y la página /inspect usan api.drand.sh, api2 y api3 (provider/drand/client.go, App/src/lib/inspector/drand.ts), y la CSP de la página los fija. Si cambian de nombre, ningún cliente lo encuentra solo.
  • El reloj local está mal: una pila del CMOS gastada, una máquina virtual o un cambio a propósito. Hoy el paso 9.c da ERR_RELEASE_UNAVAILABLE aunque la persona tenga el release correcto en la mano. Lo fija el caso round not reached yet de mutations.json: un release válido y un reloj 1 ns antes de round_time.

A 20 años

  • La red puede haberse parado. Fastnet es el precedente: drand la retiró, y tle aún avisa de ello. Si se para antes de la ronda, la cápsula se pierde sin remedio (§7.6). Si se para después, abrirla depende de que alguien conserve el release.
  • Los relays pueden dejar de servir rondas antiguas. Hoy las sirven: los vectores usan rondas 1000 a 2000, de agosto de 2023. Nada lo garantiza (§5, §50), y no he encontrado ninguna promesa de drand sobre el histórico.
  • El software puede no existir: los módulos están en g.activething.com, que no es del autor, y no hay release publicada del módulo de Go.

A 50 años

  • Lo anterior, y además los soportes y los lenguajes de hoy.
  • BLS12-381 no es post-cuántico (§7.7). Eso rompe la confidencialidad, no la recuperación: quien rompa la clave puede calcular el release de cualquier ronda, que es justo el que abre la cápsula.

2. Opciones por pieza

a. Objeto release canónico

Hoy no existe. §47 deja release_material sin definir. Las implementaciones aceptan la respuesta JSON de drand, {"round": …, "signature": "…"}, o la firma sola en hexadecimal (App/src/lib/inspector/release-input.ts, y cmd/datekeys/main_test.go en Go). La CLI de Go no tiene ninguna opción para darle un release: solo relays.

Codificación

Opción A favor En contra
JSON de drand Ya lo aceptan TS y los tests de Go No es canónico y no dice de qué cadena es
CBOR determinista (§58), como el Provider Profile (§11) Una sola codificación, con el codec que ya tienen las tres implementaciones El anexo debe describirlo
CBOR con trama DKR1, como la .dkk (§40) Magic bytes (§20) Otra trama que validar; el tipo ya va en la clave 0

Recomendación: CBOR determinista sin trama, como el Provider Profile:

0 → "datekeys-release"
1 → 1
2 → chain_hash (32 bytes)
3 → round (entero, 1 a 2⁵³ − 1)
4 → signature (cadena de bytes; 48 en Quicknet)

Mide unos 113 bytes. Los lectores SHOULD seguir aceptando el JSON de drand como entrada del llamador, sin que sea el formato canónico.

Qué identifica la cadena

  • chain_hash: es el identificador de drand, el mismo del stanza tlock (§63 paso 8) y de los archivos ajenos a DateKeys. Recomendado.
  • profile_hash: ata al perfil de DateKeys, pero nadie fuera del proyecto lo conoce.
  • profile_id: un nombre, no un hash. §24 ya evita guardarlo dos veces.

Verificación. No hace falta confiar en el objeto. Se comprueba contra el perfil pinneado, en el paso 10 y en este orden:

  1. capas del objeto (§69.1): ERR_NON_CANONICAL_CBOR o ERR_UNSUPPORTED_VERSION;
  2. chain_hash distinto del pinneado: ERR_PROFILE_MISMATCH;
  3. ronda: ERR_ROUND_MISMATCH;
  4. firma: ERR_RELEASE_INVALID.

No hace falta ningún código nuevo. El 2 es nuevo en el paso 10, y eso exige una línea en §69.1. Como con la .dkk (§63), un lector MAY decodificar el objeto al recibirlo, pero informa sus errores en el paso 10.

Extensión y nombre: .dkr. El nombre del fichero no es del protocolo (§6). El SDK SHOULD usar el de la cápsula, como carta.dkc y carta.dkr. En un archivo, el nombre es la ronda.

Dónde puede vivir

Lugar Valoración
Fichero .dkr junto al .dkc Recomendado. No cambia ningún formato y sirve para las cápsulas que ya existen
Dentro del .dkc No: el payload llega hasta EOF (§22) y cambiaría capsule_digest (§43). Sería un formato nuevo
En la .dkk, extensión no crítica datekeys.release Posible tras la fecha, reescribiendo la .dkk; solo en time_and_key. Útil a quien solo tiene la llave con localizador (§44.1). Opcional, más tarde
En el plaintext del localizador Imposible: se sella antes de que exista el release
En la nota pública (§24.1) No: es anterior a la fecha y es texto del creador

b. El paso 9.c y el reloj local

Hoy: si el instante actual es anterior a round_time, da ERR_RELEASE_UNAVAILABLE sin pedir el release (§63 paso 9.c). Las tres implementaciones lo aplican a toda fuente, también a un release suministrado: capsule/open.go, open.ts (la página /inspect pasa systemClock con el release pegado) y open.dart.

Por qué existe: no hace peticiones de red inútiles ni observables antes de la fecha, y falla pronto. Las dos razones son de red. Para un release que ya está en la mano no vale ninguna: su firma prueba que la ronda existe, y un dato local que no se puede verificar acaba pesando más que uno que sí, lo contrario de §3.

Opciones

  • A. Dejarlo como está. El reloj veta releases válidos.
  • B. 9.c solo antes de una petición de red. Un release suministrado, leído de un fichero o de un archivo local, no se compara con el reloj. Si su round_time es posterior al reloj, el lector MAY avisar de que el reloj va atrasado. Para una fuente de red, 9.c sigue igual, pero la persona MAY pedir la petición expresamente («mi reloj puede estar mal»).
  • C. Quitar 9.c. Siempre se pide, y el relay contesta que aún no existe. Hace peticiones observables antes de la fecha.
  • D. 9.c solo como aviso. Como C para la red.

Recomendación: B. Una firma válida de una ronda futura solo es posible si drand está comprometido (§7.6), y entonces la confidencialidad ya está perdida: el reloj no la protege. La única pérdida es una garantía falsa.

c. Archivo de releases de Quicknet

Tamaño: una ronda cada 3 s son 10 512 000 rondas al año. A 48 bytes, unos 505 MB al año y 1,4 MB al día. Desde el génesis (agosto de 2023) hasta finales de 2026, unos 1,7 GB; a 50 años, unos 25 GB. Las firmas no se comprimen.

Quién lo guarda

Quién A favor En contra
La persona, solo su ronda (.dkr) 113 bytes, sin terceros ni fuga de privacidad Depende de que lo guarde
El proyecto, la cadena entera Una fuente para todos; no revela qué fechas interesan Coste continuo. Ni el Gitea ni GitHub: hace falta otro sitio
Réplicas públicas (IPFS, otras) Cualquiera replica y verifica, firma a firma Nadie garantiza que sigan; IPFS solo conserva lo que alguien fija
El proyecto, solo las rondas de cápsulas conocidas Pequeño Revela fechas de cápsulas. No

Formato del paquete (informativo): una cabecera CBOR {0: "datekeys-release-archive", 1: 1, 2: chain_hash, 3: primera_ronda, 4: número} seguida de las firmas de 48 bytes, una tras otra. La firma de la ronda r está en cabecera + (r − primera_ronda)·48. Una ronda que falta se escribe a cero y no verifica. Recomiendo un paquete por mes, de unos 42 MB, publicado con su SHA-256. Un fichero por ronda serían millones de ficheros, así que solo tiene sentido como .dkr individual.

Como fuente (§49): una fuente de archivo lee 48 bytes. En local, sin red: cuenta como release en la mano, y 9.c no se aplica (opción B). En remoto, con un rango de HTTP: es una fuente de red, verifica y descarta como un relay, y el servidor ve la ronda.

Recomendación: el .dkr de la persona es la vía principal. El archivo del proyecto es una red de seguridad, sin promesa de servicio en el texto normativo. §50 lo nombra como fuente posible. Dónde se aloja es decisión del autor (decisión 5).

d. Anexo de recuperación

Hace falta una página que permita abrir una cápsula sin software de DateKeys:

  1. Los parámetros de Quicknet de §12: clave pública, chain_hash, génesis y periodo. En 50 años pueden no estar en ningún otro sitio.
  2. La trama: PRELUDE de 16 bytes, PUBLIC_HEADER en CBOR (clave 3: la DateKey, y con ella la ronda), SEALED_CONTROL y PAYLOAD_AGE hasta EOF (§22).
  3. El release: verificarlo con cualquier librería BLS12-381 que tenga pairing y hash a G1 de RFC 9380, con el DST de §63.
  4. El stanza tlock: U ‖ V ‖ W, y las fórmulas de sigma, FK_TIME y r del paso 11, con H2, H3 y H4. tle v1.2.0 no admite un release dado, sino que lo pide a la red, según su ayuda: el anexo no puede depender de él.
  5. Con FK_TIME, descifrar SEALED_CONTROL como un fichero age con su file key. La herramienta age no acepta una file key, así que el anexo resume el formato age: HKDF, el MAC de la cabecera y STREAM con ChaCha20-Poly1305.
  6. En time_and_key, el resultado es otro fichero age. Se abre con age -d y la identity de la .dkk (su access_material, clave 5, §41) en forma AGE-SECRET-KEY-1.
  7. En CONTROL_CBOR, la clave 3 es I_PAYLOAD. En forma AGE-SECRET-KEY-1, age -d abre PAYLOAD_AGE.
  8. En formato 3, la trama de BODY (§29.2): saltar el área, leer el head y cortar los ficheros. En formato 2, los L primeros bytes.

Dónde vive

  • En el spec, como anexo informativo. Es la fuente que se versiona y se traduce. Recomendado.
  • Como texto fijo que el SDK guarda junto al .dkc, igual para toda cápsula, sin datos de la cápsula. No revela nada que no revele ya el magic DKC1. Recomendado como SHOULD del SDK.
  • No en la nota pública: 1024 bytes, texto del creador sin autenticar (§24.1), y no cabe.
  • No dentro del .dkc: sería un formato nuevo.

La prueba de que el anexo basta: un script de scripts/check.sh que abre un fixture time_only y uno time_and_key con age y una librería BLS ajena al proyecto, sin código de DateKeys. Hoy tle solo se prueba con time_only (punto 6 de la revisión).

e. Al sellar y después

En el momento de sellar no existe el release, así que el escritor solo puede preparar:

  • Avisar (amplía §50 y §53): en 20 años harán falta el .dkc, el release de la ronda y, en time_and_key, la .dkk. El release solo existe después de la fecha.
  • Guardar el anexo junto al .dkc (d).
  • Recordatorio: la app MAY programar un aviso local para round_time. No recomiendo un fichero de calendario: sería otro formato que mantener.
  • Después de la fecha: el SDK SHOULD, la primera vez que tenga red tras round_time, obtener el release, verificarlo y guardar el .dkr junto al .dkc, aunque no abra la cápsula. Pedirlo revela la ronda al relay, como al abrir. Por eso, en un cliente sin estado como /inspect, solo cuando la persona lo pida. La página SHOULD ofrecer «Guardar el release» tras verificarlo, y aceptar un .dkr como entrada.
  • time_only: guardar el .dkr junto al .dkc no cambia la confidencialidad: tras la fecha, el release es público.
  • time_and_key: el .dkr también SHOULD ir con la .dkk cuando lleva localizador, porque lo abre el mismo release (§44.1). §7.6 ya recomienda time_and_key para horizontes largos. No cambia nada más.

3. Qué cambiaría

Formatos

  • Objeto nuevo: el release .dkr. Es un formato nuevo, pero no toca el .dkc ni la .dkk: toda cápsula existente, de los formatos 1, 2 y 3, se beneficia sin cambios.
  • Paquete de archivo: formato informativo, fuera del núcleo.
  • Opcional: la extensión datekeys.release de la .dkk (§72), solo si se decide.

Texto normativo (v0.15)

  • §20: la extensión .dkr.
  • Una sección nueva junto a §47, «Objeto release»: schema, reglas y códigos. §47: release_material es ese objeto. §45: la respuesta de la Release API es ese objeto.
  • §49: el release desde un fichero o un archivo como fuente.
  • §50: el archivo, el .dkr junto a la cápsula y el enlace con el sobre de §44.1 (nota 2 de la revisión v0.13).
  • §63 paso 9.c (opción B) y paso 10 (capas del objeto y chain_hash). §69.1: la línea del paso 10.
  • §62.1: un SHOULD del SDK para guardar el release tras la fecha, y el anexo.
  • §53: el aviso incluye el release.
  • §74: quitar los dos puntos de trabajo futuro.
  • §76: el cambio, con su caso reproducible, round not reached yet. Nuevo SHA-256 en spec/README.md.
  • Anexo informativo «Recuperación sin software DateKeys».
  • datekeys.cddl: la regla release.

Implementaciones

  • Go: en provider, codificar y decodificar el objeto, y una fuente de fichero y otra de archivo. En capsule.Open, distinguir el release en la mano de una fuente de red, por ejemplo con un campo OpenOptions.Release exclusivo con Source, y no aplicar 9.c al primero. En la CLI, decrypt -release FILE (.dkr o JSON de drand) y una orden que obtiene, verifica y guarda el .dkr. Sin dependencias nuevas: el codec CBOR es propio y la BLS es la de drand/kyber.
  • TypeScript (App/): en release.ts, el objeto; suppliedRelease marcado como release en la mano; en open.ts, 9.c como en Go. En /inspect: soltar un .dkr, «Guardar el release» y el aviso de reloj atrasado. Sin dependencias nuevas.
  • Dart: lo mismo en release.dart y open.dart, sin dependencias nuevas.

Testdata

  • vectors/release.json: codificaciones válidas y no canónicas, tipo y versión, chain_hash ajeno, ronda ajena, firma inválida y punto en el infinito, cada caso con su código.
  • mutations.json: el caso round not reached yet pasa a abrir, y otro igual con una fuente de red sigue dando ERR_RELEASE_UNAVAILABLE. Hace falta un campo nuevo que diga el tipo de fuente.
  • Un .dkr por ronda usada en los fixtures (1000, 1001, 1004 y 2000).
  • Un paquete de archivo pequeño, con una ronda a cero.
  • Sincronizar con scripts/sync-testdata.mjs y tool/sync_testdata.dart. La guarda de TS exige que algún test use cada fichero nuevo.

4. Decisiones para el autor

  1. Cómo identifica el objeto la cadena. chain_hash, profile_hash o profile_id. Recomiendo chain_hash.
  2. Codificación. CBOR determinista sin trama, CBOR con trama DKR1 o solo el JSON de drand. Recomiendo CBOR sin trama, aceptando además el JSON de drand como entrada.
  3. Paso 9.c. A, B, C o D. Recomiendo B.
  4. Dónde vive el release. Solo el .dkr junto al .dkc, o también la extensión datekeys.release en la .dkk. Recomiendo el .dkr ahora y la extensión más adelante, si hace falta.
  5. Archivo de Quicknet. Ninguno, solo el .dkr; o paquetes mensuales del proyecto en IPFS y otras réplicas. Dónde se alojan: no en el Gitea ni en GitHub. Recomiendo el .dkr como vía principal, el paquete como formato informativo y el alojamiento cuando haya sitio propio.
  6. Anexo. Informativo o normativo; y si el SDK guarda o no su texto junto al .dkc. Recomiendo un anexo informativo, que el SDK guarde como SHOULD.
  7. Release API (§45 a §47). Mantenerla con el objeto como respuesta, o pasarla a un anexo informativo, como sugirió la revisión, porque no hay servicio. Recomiendo mantener §45 con el objeto y pasar la semántica HTTP a informativa.
  8. Versión. Todo esto va a la v0.15, junto con el texto de §7.6 y §71 si se quiere un solo cambio. Recomiendo una v0.15 solo con esto, porque cambia vectores.

Lo que se puede hacer sin decidir nada

  • En la CLI de Go, aceptar un release dado como JSON de drand (-release FILE). La v0.14 ya contempla el release suministrado (§63 paso 10). Igual en Dart, si la app lo necesita.
  • En /inspect, ofrecer copiar o guardar el JSON de drand tras verificarlo.
  • Escribir el borrador del anexo y probarlo a mano con age y una librería BLS ajena, sobre los fixtures de time_only y time_and_key.
  • Comprobar qué rondas antiguas sirven hoy los tres relays, desde la ronda 1, y anotarlo.
  • Redactar el párrafo que une §50 y §44.1.

Powered by TurnKey Linux.