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.

181 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# DateKeys: el tamaño del área de firmas y sellos
*1 de octubre de 2026. Consulta para Fable y Astra. El autor de DateKeys no está convencido de lo que le propone Claude (Opus 5.5) y pide una opinión independiente. El documento se lee solo; las fuentes, por si queréis comprobar algo, están al final.*
## Qué se pide
Vuestra opinión razonada sobre las preguntas del apartado 8. No hace falta dar la razón a ninguna de las dos posturas del apartado 7: si veis un diseño mejor, proponedlo. Si encontráis un fallo de seguridad, dad su gravedad (bloqueante, mayor o menor), un escenario concreto y un arreglo.
## 1. Contexto
DateKeys cifra hoy ficheros que nadie puede abrir antes de una fecha, sin confiar en ningún servidor: la clave depende de una firma que la red drand publica en esa fecha (tlock, un IBE sobre BLS12-381).
**Una cápsula (`.dkc`)**, en este orden:
1. **PRELUDE**, 16 bytes en claro: la marca, `VERSION`, `PUBLIC_HEADER_LEN` y `SEALED_CONTROL_LEN`.
2. **PUBLIC_HEADER**, CBOR en claro: `capsule_id`, la fecha (perfil de drand y ronda) y la política de acceso.
3. **SEALED_CONTROL**, un fichero `age` cifrado con tlock para la ronda y, con la política `time_and_key`, además para las llaves de las personas. Dentro va `CONTROL_CBOR`, con:
- `header_binding` = SHA-256(PRELUDE ‖ PUBLIC_HEADER);
- `I_PAYLOAD`, la identidad X25519 que abre el contenido;
- L, la longitud del contenido, y el código de la regla de relleno;
- las extensiones de control.
4. **PAYLOAD_AGE**, un fichero `age` para la pública de `I_PAYLOAD`. Su texto en claro es el contenido, L bytes, más ceros hasta P = regla(L).
**El contenido del formato 3:**
```text
BODY = AREA_LEN (uint32) || SECURITY_LEN (uint32) || HEAD_LEN (uint32)
|| SECURITY_CBOR || 0x00^(AREA_LEN − SECURITY_LEN) ← el área: firma y sello
|| HEAD_CBOR ← nombres, tamaños y SHA-256 de los ficheros
|| CONTENT ← los ficheros
L = |BODY| P = regla(L) AREA_LEN = 512·k, con 1 ≤ k ≤ 128
```
**El relleno** `reforzado` es max(múltiplo de 256, Padmé). Su paso crece con la cápsula: 256 bytes en las pequeñas, 2 KiB hacia los 50 KB, 16 KiB hacia 1 MB y 256 KiB hacia 10 MB.
**El área.** `SECURITY_CBOR` tiene un hueco para una firma (clave 2: `alg`, clave pública y firma) y otro para un sello (clave 3: `seal_type` y token), y entre los dos no pueden pasar del área. Están reservados `alg` 1 (Ed25519) y `seal_type` 1 (servicio propio), 2 (RFC 3161) y 3 (OpenTimestamps). Un `alg` desconocido da el veredicto F1 y la cápsula se abre igual: una firma nunca impide abrir.
**Estado.**
- El formato 3 sin firma (spec v0.10) está publicado y etiquetado, e implementado en Go y TypeScript. Sus escritores ponen siempre `AREA_LEN` = 512 y el área vacía. Sus lectores aceptan cualquier `AREA_LEN` de 512 a 65 536 y rechazan uno mayor, así que 64 KiB es el techo de toda versión posterior que quiera seguir abriéndose en ellos.
- La firma y el sello (entregas 2 y 3) están diseñados pero no publicados: lo de los apartados 2 a 6 aún puede cambiar.
**Qué ve quien tiene el `.dkc` antes de la fecha:** PRELUDE, PUBLIC_HEADER y los dos ficheros `age`, cifrados. De sus tamaños sale P exacta. No ve L, `AREA_LEN`, `HEAD_LEN` ni nada del contenido. El modelo de amenazas del diseño pide que este observador (A1) no sepa «si hay firma o sello», con una fuga aceptada: un área de 512 bytes no admite un sello y, con P pequeña, delata que no lo hay.
## 2. Qué se firma y qué se sella (diseño, no publicado)
```text
payload_commit = SHA-256("datekeys:dkc3:payload:v1" || 0x00 || I_PAYLOAD)
CONTROL_PUB = CONTROL_CBOR con I_PAYLOAD sustituida por payload_commit
control_commit = SHA-256("datekeys:dkc3:control:v1" || 0x00 || CONTROL_PUB)
head_digest = SHA-256("datekeys:dkc3:head:v1" || 0x00 || HEAD_CBOR)
AUTHOR_MESSAGE = "datekeys:dkc3:author-signature:v1" || 0x00 || control_commit || head_digest ; 98 B
SEAL_SUBJECT = SHA-256("datekeys:dkc3:seal-subject:v1" || 0x00 || control_commit || head_digest || SIG_PART)
```
- `control_commit` cubre la cabecera pública (por `header_binding`), L, la regla de relleno, las extensiones de control y, por el compromiso, `I_PAYLOAD` sin revelarla.
- `head_digest` fija el head y, con el tamaño y el SHA-256 de cada fichero, el contenido byte a byte.
- `SIG_PART` es el hash de la firma: el sello se pide después de firmar y cubre la firma.
- El escritor prepara la cápsula, calcula `AUTHOR_MESSAGE`, obtiene la firma y el sello, los verifica y escribe. Los secretos solo viven en memoria entre esos pasos.
**La consecuencia que está en el centro de la discusión.** `head_digest` ya fija `HEAD_LEN` y C, la suma de los tamaños de los ficheros. Como L = 12 + `AREA_LEN` + `HEAD_LEN` + C, lo único que L añade a lo firmado es `AREA_LEN` y, con la regla, P. La firma no cubre el contenido del área, ni a sí misma ni al sello, pero sí su tamaño: el del hueco donde va.
## 3. Lo nuevo: firmas con certificado y sellos de terceros
El 1 de octubre el autor fijó estos requisitos:
- **Firma con certificado reconocido** (FNMT, DNIe), para que se sepa quién firma: `alg` 2, una firma CAdES separada sobre `AUTHOR_MESSAGE`.
- En la web, la persona firma fuera con su aplicación (AutoFirma) y sube la firma.
- En escritorio (macOS, Windows y Linux), con el almacén del sistema o la tarjeta.
- En el móvil, con el certificado importado o el DNIe por NFC.
- En la línea de órdenes, con un `.p12` o PKCS#11.
- **Sello de una autoridad cualificada** (RFC 3161). Se estudia también OpenTimestamps.
- **Clave propia** (Ed25519), solo cuando los destinatarios identifiquen al autor por ella.
- **Quizá un organismo** que firme además de la persona.
Tamaños estimados, sin medir todavía:
| Qué va en el área | Tamaño |
|---|---|
| Firma Ed25519, con su mapa | unos 130 bytes: cabe en 512 |
| CAdES con el certificado del firmante | 2 a 3 KiB |
| CAdES con la cadena completa | 5 a 7 KiB |
| Sello RFC 3161, dentro de la firma (CAdES-T) o aparte | 3 a 6 KiB más |
| Datos de revocación: cada respuesta OCSP | 1,5 a 3 KiB más |
| Una CRL | de decenas de KiB a varios MiB: no cabe |
| Prueba de OpenTimestamps | menos de 1 KiB |
Para medirlo de verdad basta firmar con AutoFirma un fichero de 98 bytes.
## 4. El problema
El tamaño de una firma CAdES no se sabe hasta que existe: depende del certificado, de su cadena, de los atributos y de si lleva sello. Pero, por el apartado 2, la firma cubre `AREA_LEN`. Así que el hueco tiene que tener su tamaño antes de firmar, y la firma y el sello tienen que caber en él. Si no caben, hay que preparar de nuevo con un hueco mayor y volver a firmar. Con Ed25519 no pasa, porque la firma mide siempre 64 bytes.
## 5. La privacidad del tamaño
Quien tiene el `.dkc` ve P y nada más. No puede partir P en área, head y ficheros. Lo que sí puede es restar lo que ya sabe. Este es el caso de un área que creciera con la firma, con un head de unos 100 bytes:
| Cápsula | `AREA_LEN` | P |
|---|---|---|
| Carta de 2 KB sin firma | 512 | 2 816 |
| Carta de 2 KB con firma de certificado | 6 144 | 8 704 |
| Carta de 8 KB sin firma | 512 | 8 704 |
Las dos últimas miden igual: con solo el fichero no se distinguen. La fuga aparece en tres casos:
1. **Quien conoce el contenido**, porque lo redactó, lo vio antes de cerrar la cápsula o es una plantilla. Resta y le queda el área. Con contenido pequeño distingue incluso el tipo: clave propia (cabe en 512), certificado (unos 6 KiB) o certificado y organismo (el doble).
2. **Quien compara cápsulas** del mismo autor con contenidos parecidos: las firmadas salen unos KiB mayores.
3. **Un suelo, para cualquiera.** Una cápsula con P = 2 816 no lleva dentro 6 KiB de firma. Solo dice «no va firmada con certificado», nunca «sí lo va».
La fuga se apaga al crecer la cápsula, porque crece el paso del relleno. Para quien conoce el contenido, una firma de 6 KiB cambia P en estos casos, si L cae al azar dentro del paso:
| Cápsula | Paso del relleno | Se ve la firma |
|---|---|---|
| 50 KB | 2 KiB | siempre |
| 1 MB | 16 KiB | en un 37 % de los casos |
| 10 MB | 256 KiB | en un 2 % |
| 100 MB | 2 MiB | en un 0,3 % |
## 6. Opciones
**O1. Área fija para todo escritor de la versión**, firme o no: 64 KiB, o 16 o 32.
- A favor: se mantiene A1, nadie sabe si hay firma o sello. Una sola firma. No hay que decidir nada antes de preparar.
- En contra: toda cápsula crece. Una carta de 2 KB pasa de P = 2 816 a 69 632 con 64 KiB, o a 19 456 con 16 KiB. Lo que no quepa no entra.
**O2. Dos tamaños, elegidos antes de firmar** según lo que vaya a llevar la cápsula: 512 sin nada o con Ed25519, y 64 KiB con certificado o sello.
- A favor: una sola firma. Las cápsulas sin firma siguen como hoy. Los lectores v0.10 lo aceptan.
- En contra: delata «certificado o sello» en los tres casos del apartado 5. Cambia la fila A1 y la decisión del hallazgo 5 del diseño, según la cual el tamaño no dependía de las opciones de cada cápsula.
**O3. Área justa, con `AREA_LEN` fuera de lo firmado.** Se firma y se sella un compromiso del control sin L, y después `AREA_LEN` = 512·⌈`SECURITY_LEN`/512⌉.
- A favor: una sola firma y el área mínima. Es lo que pide la objeción del autor.
- En contra: la firma deja de fijar el tamaño del área y P. La fuga es más fina: el tamaño de la firma, a 512 bytes. Cambia el apartado 10 del diseño, ya revisado.
- Una pega más: no basta con quitar L. L va en `CONTROL_CBOR` con la codificación más corta: 3 bytes por debajo de 65 536 y 5 por encima. Si al crecer el área L cruza ese umbral, `CONTROL_CBOR` mide 2 bytes más y cambia `SEALED_CONTROL_LEN`. Ese valor va en PRELUDE, y por `header_binding` entra en lo firmado. O3 necesita, por tanto, una regla más: comprometer el ancho de L antes de firmar y, si hace falta, alargar el área con ceros hasta que L llegue al umbral.
> **Corrección del 1 de octubre, tras la revisión:** esta pega no existe. `payload_length` ocupa siempre 8 bytes en `CONTROL_CBOR`, precisamente para que `SEALED_CONTROL_LEN` no dependa de L, así que basta con quitar L de lo firmado.
**O4. Área justa, firmando dos veces:** una para medir y otra con el área ya fijada, con margen.
- A favor: no cambia lo que se firma.
- En contra: dos firmas, con dos PIN o dos lecturas del DNIe, y con sello, dos sellos. Hace falta margen, porque una firma ECDSA o un sello pueden variar unos bytes de una vez a otra.
**O5. Fuera de la cápsula:** firmas de organismos y sellos sobre `capsule_digest`, el SHA-256 del `.dkc`, en un fichero aparte. Se combina con cualquiera de las anteriores.
- A favor: sin límite de tamaño. Se puede añadir después de escribir, por ejemplo un OpenTimestamps completo o nuevas marcas de archivo.
- En contra: se ve antes de la fecha, también quién firma. Es un fichero más que guardar junto a la cápsula.
## 7. Las dos posturas
**Claude (Opus 5.5)** recomienda O2, más O5 para los organismos que necesiten más de 64 KiB o que deban verse antes de la fecha:
- La protección de O1 es modesta: solo frente a quien ya conoce el contenido o compara cápsulas, y casi solo en cápsulas pequeñas.
- O3 cambia un apartado revisado y añade la regla del ancho de L para ahorrar unas decenas de KiB por cápsula firmada.
- O4 obliga a firmar dos veces.
Su primera respuesta al autor exageró la fuga: dijo que cualquiera con el fichero podría deducir si la cápsula va firmada. El apartado 5 es la versión corregida.
**El autor** no está convencido. Sus preguntas, tal cual:
- «¿Y por qué dejar fijo ese tamaño? Imagina que fuera también un organismo quien lo firmara. ¿Qué conlleva no establecer un tamaño de firmas?»
- «¿Cómo diferenciaría la parte de la cabecera del contenido?»
- «Y sinceramente, ¿qué cambia que conozca el tamaño del archivo?»
- «¿Cómo la firma va a firmar unas firmas que no existen, o se va a autofirmar?»
## 8. Preguntas
1. ¿Es correcto el apartado 2? La firma cubre el tamaño del área, a través de L, pero no su contenido, y por eso el hueco tiene que fijarse antes de firmar.
2. ¿Aporta seguridad que la firma y el sello cubran L y la regla de relleno? ¿Qué ataque concreto evita? ¿Se puede sacar `AREA_LEN` de lo firmado (O3) sin perder nada? ¿Es correcta la pega del ancho de L?
3. ¿Es real la fuga de un área variable (apartado 5)? ¿Merece la pena gastar 64 KiB por cápsula para ocultarla, o hay que rebajar la promesa de A1?
4. ¿Qué opción recomendáis, y con qué tamaño?
5. **Varias firmas**, de la persona y de un organismo:
- ¿Una sola `SignedData` CMS con varios firmantes (cofirma) en el único hueco de firma, o una lista?
- ¿Dentro, antes de escribir? Entonces el organismo tiene que firmar mientras la cápsula espera, con los secretos en memoria. ¿O fuera, sobre `capsule_digest`?
6. **Varios sellos**, RFC 3161 y OpenTimestamps, con un solo hueco de sello:
- ¿El RFC 3161 dentro de la firma (CAdES-T) y el hueco para OpenTimestamps, o un `seal_type` que sea una lista?
- Una prueba de OpenTimestamps se completa horas después, cuando se confirma el bloque, y dentro de una cápsula ya cifrada no se puede completar. ¿Se guarda incompleta, se espera antes de escribir o va fuera?
7. **Validez a décadas.** Una firma con certificado que se compruebe en 2050 necesita los datos de revocación del momento de la firma. Para durar, necesita además nuevas marcas de tiempo de archivo antes de que caduquen los certificados o se debiliten los algoritmos. Dentro de una cápsula cerrada no se puede añadir nada. ¿Qué tiene que ir dentro y qué fuera?
8. ¿Hay un diseño más simple que no estamos viendo?
## Fuentes
En el espacio de trabajo de DateKeys:
- `docs/spec_v0.10/formato3_diseno.md`: el diseño del formato 3. Los apartados 0.4 (amenazas), 2 (el área y el hallazgo 5), 10 a 15 (firma) y 16 a 22 (sello), y el anexo A (la revisión anterior de Fable).
- `datekeys-go/spec/DateKeys_Protocol_Specification_v0.10.md`: §29.1 (relleno), §29.2 (trama y área) y §29.3 (`security`).
- `docs/PLAN_firma_go.md`: el plan de la firma, con los cambios del 1 de octubre.
- `App/src/lib/dkc/padding.ts`: la regla de relleno con la que se calcularon las cifras.

Powered by TurnKey Linux.