19 KiB
Respuesta de Fable y Astra a la consulta area_firmas_para_revision.md, copiada tal como la pasó el autor el 1 de octubre de 2026. Las comprobaciones contra la spec y el código, y lo que decide el autor, están en el plan de la firma.
DateKeys formato 3: firma y sello. Propuesta consolidada
1 de octubre de 2026. Propuesta común de Fable y Astra para el autor de DateKeys, tras la consulta «El tamaño del área de firmas y sellos».
Decisión en breve
Proponemos un área fija e igual para todas las cápsulas, con 32 KiB como valor provisional hasta medir, y un compromiso firmado nuevo que no depende del tamaño reservado para las firmas. Es la postura común de Fable y Astra tras la revisión cruzada de la consulta del 1 de octubre.
- Área: una sola constante para todo escritor de la versión, firme o no. No crece sola.
- Lo firmado: un único hash sobre datos con significado. Quedan fuera L,
AREA_LENySEALED_CONTROL_LEN. - Firmas: una
SignedDataCMS con uno o varios firmantes en el hueco actual. Sin listas en la primera versión. - Sellos: un solo tipo por cápsula. CAdES-T obligatorio si hay certificado; el hueco de sello, opcional, si no lo hay.
- Fuera de la cápsula: OpenTimestamps y los resellados, sobre
capsule_digest.
El autor ha cerrado estas decisiones:
- A1 se mantiene. El área es fija para todos y la ampliación expresa es la única excepción. Si algún día A1 deja de importar, el mismo compromiso firmado admite un área variable.
- 32 KiB como valor provisional. Se implementa como constante del escritor, y la medición la confirma o la mueve.
- Sello de tiempo obligatorio con certificado, con su coste incluido en esa opción. En las demás cápsulas es opcional.
- Sin firma del servicio DateKeys en esta entrega.
Ninguno de los dos revisores ha visto la spec ni el código: todo se apoya en lo que afirma el documento de consulta.
Riesgos de un área fija y de un área variable
El área fija arriesga comodidad y bytes; la variable arriesga discreción. Ninguna pone en riesgo el contenido, la fecha ni la validez de la firma.
| Riesgo | Área fija | Área variable |
|---|---|---|
| Que la firma no quepa | Sí. Hay que ampliar, quitar algo o rechazar | No, hasta el techo de 64 KiB |
| Que el tamaño delate si hay firma | No, mientras todo quepa en la reserva | Sí: permite inferir o descartar si hay firma y de qué tipo. Con otros datos puede ayudar a acotar al firmante |
| Peso de las cápsulas | Todas crecen, firmadas o no | Solo crecen las firmadas |
| Acierto del tamaño elegido | Es una apuesta hasta medir, y puede quedarse corta con el tiempo | No hay tamaño que elegir |
| Techo de 64 KiB de los lectores publicados | Se mantiene | Se mantiene |
- Quién ve la fuga del área variable: quien conoce el contenido, quien compara cápsulas del mismo autor y quien aportó la plantilla. Quien solo tiene el fichero no separa el área del contenido.
- La fuga no se apaga con el tamaño. Quien elige el contenido puede ajustarlo al borde de un paso de relleno, y entonces la firma se nota también en cápsulas grandes.
- El empaquetado deja de estar firmado en los dos casos. Con el compromiso nuevo, quien ya puede abrir la cápsula puede rehacerla con otra área y la firma sigue valiendo. No afecta al contenido, a la fecha ni al autor.
- Lo que ninguna de las dos evita: que alguien retire una firma después de abrir y que falten pruebas de validación en 2050. Lo tratan el cambio 5 y la sección de evidencias.
Cambios sobre el diseño
Son ocho cambios sobre un diseño que aún no está publicado. Los cambios 1 y 2 reabren el apartado 10, el 5 añade una clave al esquema del área y el resto son reglas del escritor y del verificador.
1. Compromiso firmado sin tamaños
La firma cubre solo datos con significado y se recalcula desde la cápsula, sin almacenarse.
payload_commit = SHA-256("datekeys:dkc3:payload:v1" || 0x00 || I_PAYLOAD)
header_commit = SHA-256("datekeys:dkc3:header:v1" || 0x00 || PRELUDE* || PUBLIC_HEADER)
CONTROL_SIG = CONTROL_CBOR sin L, con I_PAYLOAD -> payload_commit
y header_binding -> header_commit
control_commit = SHA-256("datekeys:dkc3:control:v1" || 0x00 || CONTROL_SIG)
head_digest = SHA-256("datekeys:dkc3:head:v1" || 0x00 || HEAD_CBOR)
signers_digest = SHA-256("datekeys:dkc3:signers:v1" || 0x00 || REQUIRED_SIGNERS)
AUTHOR_MESSAGE = "datekeys:dkc3:author-signature:v1" || 0x00
|| SHA-256(control_commit || head_digest || signers_digest) ; 66 B
PRELUDE* = PRELUDE con SEALED_CONTROL_LEN a cero
- Qué cubre: fecha, política,
capsule_id, compromiso deI_PAYLOAD, regla de relleno, extensiones de control, head y lista de firmantes exigidos. - Qué deja fuera: L,
AREA_LEN,SECURITY_LENySEALED_CONTROL_LEN. Los tamaños de los ficheros siguen en el head, yPUBLIC_HEADER_LENsigue enPRELUDE*. header_bindingno cambia y se sigue comprobando al abrir.header_commites un valor distinto, con nombre propio, que solo sirve para la firma.- Por qué: el tamaño del área deja de afectar a la firma y desaparecen los umbrales de CBOR (65 536 y 2³²).
- Codificación: la spec debe fijar los bytes exactos de
CONTROL_SIGyREQUIRED_SIGNERS. Eso incluye la variante determinista de CBOR (RFC 8949), tipos y claves, la lista vacía, el orden, ausente frente a vacío y las extensiones. - Condición: un lector estricto. Debe exigir
SECURITY_LEN≤AREA_LENantes de interpretar o reservar memoria, L = 12 +AREA_LEN+HEAD_LEN+ C, ceros en la parte no utilizada del área y en el relleno, P = regla(L), CBOR sin claves duplicadas y ningún byte sobrante.
Los nombres y los dominios del esquema son orientativos.
2. AUTHOR_MESSAGE con un único hash
El mensaje que sale hacia la aplicación de firma ya no expone head_digest.
- El problema: hoy
head_digestviaja en claro en los 98 bytes. Quien obtenga ese fichero y sospeche de un documento puede confirmar antes de la fecha que la cápsula lo contiene. - El arreglo: firmar un solo hash, como ya hace
SEAL_SUBJECT.control_commitlo protege mientrasI_PAYLOADsiga secreta. - Gravedad: de menor a mayor según el contenido. Es mayor si lo sensible es saber cuál de dos documentos va dentro.
- Flujo web: el mensaje y el
.p7squedan en disco, y el.p7slleva el certificado del firmante. Pedir que se borren ayuda, pero no alcanza a copias sincronizadas ni a respaldos.
3. Área fija, que no crece sola
Todo escritor de la versión reserva la misma área, haya firma o no. El valor es de 32 KiB, provisional hasta medir.
- Antes de pedir el PIN, el escritor estima si firmas, sellos y OCSP caben.
- Si no caben, se detiene y pregunta: quitar firmantes o ampliar. Todavía no ha firmado nadie.
- Ampliar es una elección expresa del autor y va directa a 64 KiB. Así la fuga se reduce a dos clases de reserva y no da el tamaño de las firmas.
- Si la sorpresa llega después del PIN, ampliar conserva las firmas. Quitar un firmante cambia la lista y obliga a firmar de nuevo.
- No se descarta nada en silencio, y por encima de 64 KiB se rechaza.
| Operación después de firmar | ¿Conserva las firmas? |
|---|---|
| Ampliar el área | Sí |
| Añadir OCSP o sellos en su sitio | Sí |
| Cambiar la lista de firmantes exigidos | No: hay que volver a firmar |
| Retirar una firma exigida sin cambiar la lista | Las demás siguen válidas, pero el conjunto queda incompleto |
El tamaño es política del escritor. Puede cambiar en versiones posteriores, pero solo para cápsulas nuevas.
Regla para confirmar los 32 KiB al medir:
- Un firmante completo (firma, certificado, sello y OCSP) debe caber con un margen de al menos un 25 %. El margen cubre que certificados y sellos cambien de tamaño con los años.
- Si dos firmantes completos no caben, se mantienen los 32 KiB y ese caso usa la ampliación expresa. No se sube a 64 KiB para todos.
- Solo se baja a 16 KiB si dos firmantes completos miden menos de 12 KiB.
4. Firma con certificado: una SignedData CMS
alg 2 es una firma CAdES separada sobre AUTHOR_MESSAGE, con uno o varios firmantes (cofirma).
- Perfil estricto: firma separada, DER,
signing-certificate-v2obligatorio y un máximo de firmantes. - Algoritmos:
alg2 identifica el contenedor. Hace falta una política propia para los algoritmos de hash y de firma que lleva dentro. - Cadena: el escritor la poda antes de sellar y deja solo el certificado de cada firmante.
- Sin lista de firmas. Si un día hace falta mezclar tipos, se añade una clave nueva; no se cambia el tipo de la clave 2.
5. Firmantes exigidos
Lo firmado enumera quién debe firmar, y el verificador exige todas esas firmas.
- Quién va en la lista: todos los firmantes exigidos, también el autor. CMS no ordena a los firmantes, así que no existe un «primer firmante».
- Por qué todos: si la lista solo exigiera al organismo, alguien podría retirar la firma de la autora y el conjunto seguiría pareciendo completo.
- Identificación: el SHA-256 del certificado completo en DER. Sin duplicados y en un orden determinista.
- Ubicación: en el área, no en el control.
SEALED_CONTROL_LENva en claro, y una extensión que solo exista con firmantes los delataría. Es el único dato del área que entra en lo firmado, y necesita una clave nueva en su esquema. - Lista vacía: solo en cápsulas sin firma o con Ed25519, que ya vincula su clave por su cuenta.
- Antes de la primera firma: la lista debe estar cerrada, y eso exige conocer los certificados. Se cumple de tres maneras: seleccionando el certificado en AutoFirma sin firmar (función selectCertificate, si hay integración web), importando el certificado público o con una firma de prueba.
- Regla del verificador: deben estar todas las firmas de la lista. Un firmante que no figure en ella se muestra aparte y no cuenta.
- Límite: si se retiran todas las firmas, nada interno lo detecta. Hace falta una expectativa del receptor.
La firma de prueba es la vía práctica mientras el flujo web consista en descargar, firmar fuera y subir el .p7s. Tiene cuatro condiciones:
- Mensaje propio: identificado como prueba de DateKeys, sin contenido del usuario, sin sello y distinto del que autoriza una cápsula.
- El tamaño es una estimación. El CMS definitivo crece con el sello y las evidencias. No sustituye la comprobación final ni la política de desbordamiento.
- La firma real se comprueba. Su certificado debe coincidir con la huella comprometida. Una renovación u otro certificado obligan a reconstruir el compromiso.
- El certificado es un dato identificativo. Se procesa en el dispositivo y no va a registros ni a analítica.
6. Un solo tipo de sello por cápsula
| Cápsula | Sello | Qué cubre |
|---|---|---|
| Con certificado | CAdES-T dentro del CMS, uno por firmante | El valor de la firma, según la regla estándar |
| Sin firma o con Ed25519 (sello opcional) | El hueco de sello (seal_type 1 o 2) |
SEAL_SUBJECT |
- CAdES-T es obligatorio en el perfil con certificado, uno por firmante. Es un atributo no firmado y se puede retirar, así que el verificador debe detectar su ausencia.
- Por qué: la cápsula se abre más tarde, y hay que poder acreditar que la firma existía mientras el certificado era válido. La caducidad no rompe la firma; sin sello falta esa prueba.
- Sin otro PIN: si la TSA falla, se reintenta durante la sesión sin volver a firmar.
- No se da por terminada una cápsula con certificado y sin sello.
- Petición mínima: a la TSA solo va la huella del valor de firma, no el documento ni el certificado. La conexión y la cuenta sí dejan metadatos.
- Qué TSA: el perfil exige un sello RFC 3161. Si debe ser cualificado es decisión de producto, y el veredicto debe indicarlo.
- No son intercambiables. Un token sobre
SEAL_SUBJECTno es un CAdES-T, y cada uno tiene su verificación. SIG_PARTsolo se define para dos casos: una constante cuando no hay firma y, con Ed25519, un hash con dominio propio del algoritmo, la clave pública y la firma.seal_type3 (OpenTimestamps) queda reservado y sin implementar dentro.
7. Veredictos
Abrir y validar son cosas distintas: una firma nunca impide abrir, pero «se abre» no significa «está verificada».
- Cada firma tiene su veredicto: válida según la política, incorrecta o no verificable (algoritmo desconocido o faltan evidencias).
- La cápsula tiene un resultado de conjunto: sin firma, cumple el perfil o no lo cumple. No lo cumple si falta un firmante o un sello exigidos, aunque las firmas presentes sean correctas.
- «Firmado antes de la fecha» solo se dice de quien tenga un sello anterior a la ronda. Quien puede abrir la cápsula puede rehacer el área.
- Firma externa sobre
capsule_digest: acredita ese fichero cifrado, no que el firmante conociera el contenido. La interfaz no debe llamarla igual que una firma interna.
8. Nueva redacción de A1
Quien tiene el
.dkcantes de la fecha no puede saber si lleva firma o sello, mientras la cápsula esté dentro de la reserva común. Una cápsula que el autor amplía expresamente queda fuera de esta garantía.
La fuga aceptada del área de 512 bytes desaparece en las cápsulas de la versión nueva.
La garantía se limita al .dkc: no cubre los archivos externos ni el tráfico hacia los servicios. Depende además de confirmar que ninguna otra longitud visible cambia al activar firmas o sellos.
Evidencias: dentro, fuera y cuándo
Las evidencias se capturan al firmar, se guarden donde se guarden: «fuera» no significa «después de la fecha».
| Evidencia | Dónde | Por qué ahí |
|---|---|---|
| Firma y certificado de cada firmante | Dentro | Identifican al firmante |
| Sello de la firma (CAdES-T) | Dentro | Lo entienden los validadores estándar |
| OCSP de cada firmante | Dentro | Lleva el número de serie del certificado |
| Cadenas, listas de las autoridades y certificados de la TSA | Fuera, capturados ese día | Son iguales para cualquier usuario de esa autoridad |
OpenTimestamps del .dkc |
Fuera | Se completa horas después y se verifica con el cliente estándar |
Sello cualificado del .dkc, si se quiere |
Fuera | Acredita ese fichero exacto sin revelar lo de dentro |
| Resellados de archivo | Fuera | Se añaden durante décadas, con la cápsula cerrada |
- OCSP: debe ser aplicable al momento de la firma. No vale cualquier respuesta cercana.
- La tabla no es exhaustiva. Hay que conservar lo necesario para validar las cadenas del firmante, de la TSA y de quien firma las respuestas OCSP, según el perfil.
- El material común no debe delatar la firma. Si la app solo lo guarda en cápsulas firmadas, la fuga de A1 reaparece ahí. Propuesta sin contrastar con la spec: guardarlo siempre, haya firma o no.
- Los sellos de fuera conservan lo que hay dentro, pero no suplen una evidencia que nunca se guardó.
- OpenTimestamps no caduca con ningún certificado. Hay que completar la prueba y guardarla. Revela que ese fichero se selló y cuándo, no su contenido.
- Resellado (RFC 4998): el sello se renueva antes de que caduque el anterior. Antes de que el hash deje de ser seguro, se renueva con otro que cubra el
.dkcoriginal y las evidencias anteriores. - Nada se descarta en silencio. Si una evidencia no cabe, se conserva en otro sitio o se declara la garantía que se pierde.
A décadas, el eslabón débil es el cierre y no la firma: que drand siga publicando y que BLS12-381 y X25519 aguanten, porque no resisten a un ordenador cuántico.
Fuera de la primera entrega
Seis cosas se dejan para después, por alcance y no porque sean malas ideas.
- Borradores cifrados en disco, para que un organismo firme días después dentro de la cápsula. Dejan el contenido e
I_PAYLOADen reposo y piden decidir la custodia de claves. Mientras tanto, un organismo solo firma dentro si lo hace en la misma sesión. - Listas generales de firmas y de sellos.
- Roles y umbrales de firmantes. La lista de firmantes exigidos basta por ahora.
- OpenTimestamps dentro de la cápsula.
- Un anexo cifrado con tlock para la misma ronda, para organismos que firmen después y deban seguir ocultos hasta la fecha.
- Firma del servicio DateKeys. Aporta poco mientras no certifique algo propio, como una organización verificada.
seal_type1 sigue reservado para ese día.
Pendiente de medir y de comprobar
Los 32 KiB son provisionales hasta medir, y varias afirmaciones dependen de la spec y del código, que no hemos visto.
Medir
- Firma CAdES con FNMT persona física, representante, DNIe y sello de entidad, con y sin cadena.
- Token de dos autoridades de sellado cualificadas.
- Respuesta OCSP de cada autoridad.
- Las combinaciones completas previstas: dos firmantes, sus sellos, las evidencias y la estructura CBOR final.
- Con esas medidas, confirmar o mover los 32 KiB según la regla del cambio 3.
Comprobar en la spec y el código
- Que los lectores Go y TypeScript cumplen la condición de lector estricto del cambio 1.
- Fijar la codificación exacta de
CONTROL_SIGyREQUIRED_SIGNERS, con vectores de prueba comunes a Go y TypeScript. - Asignar la clave de
REQUIRED_SIGNERSen el área y comprobar qué hacen los lectores v0.10 con un área no vacía y con claves que no conocen. - Si el control sellado lleva relleno propio. De eso depende dónde va la lista de firmantes exigidos.
- Que ninguna otra longitud visible del
.dkccambia al activar firmas o sellos. - Qué versiones de AutoFirma permiten seleccionar el certificado sin firmar, si se integra en la web.
- Escribir el argumento de que un
.dkcsolo admite un contenido: la MAC de cabecera deagey el resultado único de tlock (RFC 4998, apartado 6).
Comprobar en lo que se promete
- «Firma cualificada» solo con dispositivo cualificado, como el DNIe. Un
.p12no lo es. - «Sin confiar en ningún servidor» necesita precisión: depende de que no se comprometa un umbral de drand.
- El coste de cada sello depende del contrato con la TSA. No dar por hecha una tarifa por operación.
Casos de prueba antes de publicar
Diez casos cubren los cambios; Astra propuso los cuatro primeros y los tres últimos.
| Caso | Resultado esperado |
|---|---|
| L cruza 65 536 o 2³² al cambiar el área | AUTHOR_MESSAGE no cambia |
| Se retira la firma de un organismo exigido y se rehace la cápsula | El conjunto no cumple el perfil |
| Se añaden sello y OCSP durante la preparación | Las firmas ya hechas siguen siendo válidas |
| Un lector v0.10 abre una cápsula firmada | Abre, y no la presenta como verificada |
| El área se amplía a 64 KiB después de firmar | La firma sigue valiendo sin un segundo PIN |
| El mismo contenido con firma y sin firma, dentro de la reserva | La misma P |
Alguien tiene AUTHOR_MESSAGE y un documento candidato |
No puede confirmar que la cápsula lo contiene |
| Se retira la firma del autor y se mantienen las demás | El conjunto no cumple el perfil |
| Se retira una firma y se cambia la lista para ocultarlo | Las firmas restantes dejan de validar |
| Se retira un sello CAdES-T exigido | La firma sigue siendo correcta, pero el perfil no se cumple |