Spec v0.11 draft: the adversarial review and the author's four decisions

The author's decisions after the review of 1 October 2026:

- Recognized authorities (29.13): a list pinned in the SDK for the
  certificates of signers and for timestamp authorities, checked at the
  time of the seal, without revocation. Without it a certificate or a
  seal was checked with what it brings itself. New verdicts F7 and S6.
- The key of words is salted with capsule_id too (salt v2), so that the
  capsules of a popular round cannot be attacked together; the writer
  rejects controls and ignorable code points and should reject short or
  repeated words. New vector.
- What is stored outside is an opaque envelope: the .dkc encrypted for a
  random identity whose key goes in the time-locked locator, padded to
  4096 bytes, with https and ipfs addresses only.
- AUTHOR_MESSAGE is ASCII text of 99 bytes with the digest in hex and an
  8-character code to compare before signing.

Fixes from the three reports: SIG_PART over the exact content of key 2
for any alg; one procedure per signer in 29.10, with F1 only for the
shape of what is present; a closed table of OIDs and DER with sorted SET
OF; the token checks its content-type and message-digest; S4 needs
genTime + accuracy before the round; F1 or F2 in 29.9 and the noble text;
alg and seal_type 4294967295 reserved for tests, and the five v0.10
vectors that change verdict listed in 76; the leftovers of v0.10 in
55.1, 27, 63, 72 and 73; the note as one line of the declared author
rules; extension data never gives ERR_NON_CANONICAL_CBOR; what a
certificate signature reveals; blind signing in 7.9; no secrets at rest
while waiting for a signature; the figures of 76.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.11
dev 6 days ago
parent e224119f8f
commit 5a0b95b47b

@ -29,7 +29,7 @@ Define:
- formatos 1, 2 y 3 de `.dkc` y su compatibilidad;
- huecos fijos de `INNER_ACCESS_AGE` y relleno del payload;
- contenido del formato 3: varios ficheros con sus rutas y metadatos, y el área `security`;
- la firma de autor del formato 3, con una clave propia Ed25519 o con certificados X.509 (CMS), y el sello de tiempo RFC 3161;
- la firma de autor del formato 3, con una clave propia Ed25519 o con certificados X.509 (CMS), el sello de tiempo RFC 3161 y las autoridades que los reconocen;
- la llave de palabras, una credencial que se deriva de palabras que elige quien crea la cápsula;
- la nota pública de una cápsula y la extensión de cápsula de la `.dkk`;
- verificación;
@ -175,7 +175,8 @@ Puede:
- intercambiar payloads;
- cambiar una política declarada;
- intentar downgrade;
- fabricar una cápsula de formato 3 con rutas que escapen de la carpeta del lector o que colisionen al extraerlas, nombres que se confundan, un head que agote su memoria o un comentario que imite un veredicto (§29.4 a §29.7).
- fabricar una cápsula de formato 3 con rutas que escapen de la carpeta del lector o que colisionen al extraerlas, nombres que se confundan, un head que agote su memoria o un comentario que imite un veredicto (§29.4 a §29.7);
- poner en la nota pública un texto que imite un veredicto, una autoría o un aviso, o cambiarla después de escribir la cápsula (§24.1).
### 7.4 Poseedor de `.dkk`
@ -212,7 +213,10 @@ En formato 3, con firma o sello, además:
- un escritor puede crear dos cápsulas distintas con el mismo `capsule_id`, firmadas las dos (equivocación). Contra ella, el creador puede publicar `capsule_digest` antes de la fecha (§43);
- quien puede abrir una cápsula puede rehacerla tras la fecha con otra área, sin una firma o con otra. Solo un sello anterior a la ronda prueba que una firma existía antes de la fecha (§29.11);
- una autoridad de sellado deshonesta o comprometida puede fechar mal sus sellos. El protocolo no la audita, y el veredicto la nombra (§29.11);
- la aplicación de firma externa recibe `AUTHOR_MESSAGE`, que no revela el contenido (§29.8).
- la aplicación de firma externa recibe `AUTHOR_MESSAGE`, que no revela el contenido (§29.8). Un firmante firma lo que esa aplicación le muestra: una página comprometida o una aplicación de firma falsa pueden obtener su firma para una cápsula que no ha visto, y un organismo que cofirma solo con `AUTHOR_MESSAGE` nunca ve el contenido. El código de `AUTHOR_MESSAGE` permite comparar lo que se firma con lo que muestra DateKeys (§62.1, regla 20);
- un firmante puede firmar coaccionado, y la firma no lo distingue;
- una clave de autor robada no se puede revocar: un lector que la guardó seguirá mostrando su etiqueta (§29.12);
- un certificado o un sello solo prueban algo si su cadena llega a una autoridad reconocida (§29.13): cualquiera puede fabricar un certificado a nombre de otra persona, o un sello con la fecha que quiera.
---
@ -748,17 +752,21 @@ El perfil se obtiene de la DateKey, evitando dos fuentes de verdad.
## 24.1 Nota pública
La nota pública es un texto corto que quien crea la cápsula deja a la vista, para que se sepa qué es antes de la fecha, como «Para Lucía, en su 18 cumpleaños». Es la extensión `datekeys.note`, versión 1, registrada para el array no crítico de `PUBLIC_HEADER` (§72):
La nota pública es un texto corto que quien crea la cápsula deja a la vista, para que se sepa qué es antes de la fecha, como «Cartas del viaje a Lisboa». Es la extensión `datekeys.note`, versión 1, registrada para el array no crítico de `PUBLIC_HEADER` (§72):
- su `data` es el texto en UTF-8, de 1 a 1024 bytes, sin CBOR alrededor, y MUST cumplir las reglas de texto de §29.6;
- su `data` es el texto en UTF-8, de 1 a 1024 bytes, sin CBOR alrededor, y MUST cumplir las reglas de texto del autor declarado de §29.6: una sola línea, sin tabuladores ni espacios en los extremos;
- un lector que no la conoce la ignora y abre la cápsula igual (§54);
- un lector que la conoce y encuentra una `data` que incumple esas reglas la trata como inutilizable, no la muestra y lo notifica (§54).
Es pública: la lee cualquiera que tenga el `.dkc`, también antes de la fecha (§55.2). El escritor SHOULD avisarlo al pedirla.
Es pública: la lee cualquiera que tenga el `.dkc`, también antes de la fecha, y no se puede borrar de las copias que circulen (§55.2). Junto con la fecha puede identificar a alguien: «Para Lucía, en su 18 cumpleaños» da la fecha de nacimiento de una menor. El escritor SHOULD avisarlo al pedirla.
No está comprobada antes de la fecha: cifrar hacia una ronda no exige ningún secreto, así que cualquiera puede fabricar una cápsula con la nota que quiera (§36.1). El paso 15 la ata al control, porque `header_binding` cubre los bytes exactos de `PUBLIC_HEADER` (§26), y una firma la cubre a través de `control_commit` (§29.8). Una nota que alguien cambia después de escribir la cápsula hace fallar el paso 15 con `ERR_HEADER_BINDING`.
No está comprobada antes de la fecha: cifrar hacia una ronda no exige ningún secreto, así que cualquiera puede fabricar una cápsula con la nota que quiera (§36.1). El paso 15 la ata al control, porque `header_binding` cubre los bytes exactos de `PUBLIC_HEADER` (§26), y una firma la cubre a través de `control_commit` (§29.8). Una nota que alguien cambia después de escribir la cápsula hace fallar el paso 15 con `ERR_HEADER_BINDING`, y nadie lo nota antes de la fecha.
Un lector que la muestra MUST presentarla como texto del creador que nadie ha comprobado, con la presentación de §29.7 para ese texto. El SDK oficial MUST titularla «Nota pública del creador (sin comprobar)».
Un lector que la muestra:
- MUST presentarla como texto del creador que nadie ha comprobado, con la presentación de §29.7 para ese texto. El SDK oficial MUST titularla «Nota pública del creador (sin comprobar)»;
- antes de la fecha, MUST mostrar junto a ella «Nadie puede comprobar antes de la fecha quién creó la cápsula ni si va firmada.»;
- MUST NOT convertirla en un enlace ni ofrecer editarla.
---
@ -829,7 +837,7 @@ Estas comprobaciones de stanzas previas al descifrado son estructurales. Su aute
La inspección pre-unlock es una optimización de validación y privacidad y, por tanto, es un SHOULD. La aplicación de las reglas de cardinalidad de las secciones 29, 32, 33 y 39 es un MUST y DEBE realizarse, como mínimo, en el momento de abrir cada fichero `age`: la identity que desenvuelve la file key MUST rechazar el fichero si el conjunto completo de stanzas recibido viola la regla correspondiente.
`header_binding` vincula `PUBLIC_HEADER` al control abierto (§63, paso 15): aporta coherencia interna, no autoría ni fecha (§55.1). Solo una extensión de firma puede aportar autenticidad del creador (§36.1, §72).
`header_binding` vincula `PUBLIC_HEADER` al control abierto (§63, paso 15): aporta coherencia interna, no autoría ni fecha (§55.1). Solo una firma de `security` (§29.8) o una extensión de firma pueden aportar autenticidad (§36.1, §72).
---
@ -1060,7 +1068,7 @@ y los `AREA_LEN − SECURITY_LEN` bytes que siguen a `SECURITY_CBOR` MUST valer
El área:
- su tamaño lo fija la versión del spec con la que se escribe, no lo que contiene: un escritor de esta versión MUST escribir `AREA_LEN` = 32768 (32 KiB), lleve o no firma o sello, salvo que quien crea la cápsula amplíe el área expresamente, y entonces MUST escribir 65536 (§62.1, regla 13). MUST escribir `SECURITY_CBOR` siempre, aunque vaya vacío. Los escritores de la v0.10 escriben 512;
- su tamaño lo fija la versión del spec con la que se escribe, no lo que contiene: un escritor de esta versión MUST escribir `AREA_LEN` = 32768 (32 KiB), lleve o no firma o sello, salvo que quien crea la cápsula la amplíe expresamente porque sus firmas no caben, y entonces MUST escribir 65536 (§62.1, regla 13). MUST escribir `SECURITY_CBOR` siempre, aunque vaya vacío. Los escritores de la v0.10 escriben 512;
- un lector MUST aceptar cualquier `AREA_LEN` que cumpla la trama, sin compararlo con la versión del spec: así lee el área, quizá mayor, de un escritor de una versión posterior (§70);
- por eso P no depende de lo que lleve `security`, salvo en un área ampliada (§55.2).
@ -1110,7 +1118,7 @@ Esta versión define:
- `alg` 2, una firma CMS con uno o varios certificados X.509 (§29.10). Su clave 1 lleva la lista de firmantes exigidos en lugar de una clave pública;
- `seal_type` 2, un sello de tiempo RFC 3161 (§29.11).
Quedan reservados `seal_type` 1 (servicio de sellado de DateKeys) y 3 (OpenTimestamps). En esta versión:
Quedan reservados `seal_type` 1 (servicio de sellado de DateKeys) y 3 (OpenTimestamps). `alg` y `seal_type` 4294967295 quedan reservados para pruebas: ninguna versión los definirá, así que un lector siempre los da por no soportados. En esta versión:
- un escritor MUST escribir `security` vacío, `{0: "datekeys-security", 1: 1}`, de 22 bytes, en una cápsula sin firma ni sello (§62.1, regla 13);
- con `alg` 2, el sello va dentro de la firma (§29.10), y la clave 3 MUST NOT existir;
@ -1286,19 +1294,21 @@ La firma y el sello se evalúan por separado:
| `alg` conocido cuyo contenido incumple su perfil (§29.9, §29.10) | F1 | no cambia |
| Una firma presente que no verifica | F2 | no cambia |
| `alg` 1, firma válida | F3 o F4 | no cambia |
| `alg` 2, falta un firmante exigido o su sello, un sello no verifica, o hay clave 3 | F5 | no cambia |
| `alg` 2, todos los firmantes exigidos válidos, cada uno con su sello | F6 | no cambia |
| `alg` 2, un firmante exigido ausente, no verificable, sin sello, con el sello inválido o con el certificado fuera de validez, o hay clave 3 (§29.10) | F5 | no cambia |
| `alg` 2, un firmante exigido con el certificado o el sello de una autoridad no reconocida (§29.13) | F7 | no cambia |
| `alg` 2, todos los firmantes exigidos válidos y reconocidos, cada uno con su sello | F6 | no cambia |
| Sin clave 3 | no cambia | S0 |
| El contenido de la clave 3 no decodifica o incumple el schema de `seal` | no cambia | S2 |
| `seal_type` que el lector no implementa | no cambia | S1 |
| `seal_type` 2 cuyo token incumple su perfil, según el orden de §29.11 | no cambia | S1 o S2 |
| `seal_type` 2 cuyo token incumple su forma o usa un algoritmo fuera de la tabla, en el orden de §29.11 | no cambia | S2 o S1 |
| Sello que no verifica | no cambia | S3 |
| Sello válido, con t < round_time | no cambia | S4 |
| Sello válido, con t ≥ round_time | no cambia | S5 |
| Sello válido de una autoridad no reconocida (§29.13) | no cambia | S6 |
| Sello válido y reconocido, con t + precisión < round_time | no cambia | S4 |
| Sello válido y reconocido, en otro caso | no cambia | S5 |
Para la firma y para el sello por separado, decide la primera fila de la tabla que se cumple, de arriba abajo: `alg` y `seal_type` solo se leen de un contenido que decodifica y cumple su schema. Así, un `seal` con una clave desconocida, o con `seal_type` 0, es S2 y no S1.
X es una sola línea, en lugar de la de la firma y la del sello. Un lector de la v0.10 solo llega a X, F0, F1, S0, S1 y S2; esta versión añade F2 a F6 y S3 a S5 sin cambiar los anteriores.
X es una sola línea, en lugar de la de la firma y la del sello. Un lector de la v0.10 solo llega a X, F0, F1, S0, S1 y S2; esta versión añade F2 a F7 y S3 a S6 sin cambiar los anteriores.
| Veredicto | Significado | Texto |
|---|---|---|
@ -1312,16 +1322,18 @@ X es una sola línea, en lugar de la de la firma y la del sello. Un lector de la
| F3 | `alg` 1, válida con una clave guardada | «Firmado con la clave que guardaste como ‹etiqueta›.» |
| F4 | `alg` 1, válida con otra clave | «Firmado con la clave ‹dkauthor1… completa›. No prueba quién la tiene.» |
| F5 | `alg` 2, incompleta | «Faltan firmas o sellos que la propia cápsula exige: trátala como no firmada.» |
| F6 | `alg` 2, completa | «Firmado con certificado por ‹titulares›. DateKeys ha comprobado las firmas y sus sellos de tiempo, no la validez legal de los certificados.» |
| F6 | `alg` 2, completa y reconocida | «Firmado con certificado por ‹titulares›, de autoridades reconocidas. DateKeys no comprueba si se revocaron: para la validez legal, usa un validador como VALIDe.» |
| F7 | `alg` 2, de autoridades no reconocidas | «Firmas correctas, pero de certificados o sellos de autoridades que DateKeys no reconoce: no prueban quién firmó ni cuándo.» |
| S3 | sello inválido | «El sello no corresponde a este contenido.» |
| S4 | válido, t < round_time | «Según ‹TSA›, existía el ‹t›, antes de que la cápsula pudiera abrirse.» |
| S5 | válido, t ≥ round_time | «Sellado después de la fecha de apertura: no prueba nada anterior.» |
| S4 | válido y reconocido, t + precisión < round_time | «Según ‹TSA›, existía el ‹t›, antes de que la cápsula pudiera abrirse.» |
| S5 | válido, sin margen antes de round_time | «Sellado después de la fecha de apertura: no prueba nada anterior.» |
| S6 | autoridad no reconocida | «Sello de una autoridad que DateKeys no reconoce: no prueba nada.» |
El SDK oficial MUST usar los textos de esta tabla. Otra implementación MUST usar esos textos o una traducción que no afirme más que ellos.
En los textos, ‹etiqueta› es la que la persona dio a una clave guardada (§29.12); ‹titulares›, el nombre del titular de cada certificado de los firmantes exigidos, tomado de su `subject`; ‹TSA›, el de la autoridad de sellado; y ‹t›, el instante del sello en UTC. Con F6, el lector MUST mostrar además una línea por firmante, con su titular, el emisor de su certificado y el instante de su sello, y decir si ese instante es anterior a la fecha de apertura. Un `SignerInfo` cuyo certificado no está entre los exigidos se muestra aparte, con su resultado, y no cuenta (§29.10).
En los textos, ‹etiqueta› es la que la persona dio a una clave guardada (§29.12); ‹titulares›, el nombre del titular de cada certificado de los firmantes exigidos, tomado de su `subject` (`commonName`, o `givenName` y `surname`) sin su `serialNumber`, si cumple las reglas del autor declarado de §29.6, y si no, el SHA-256 del certificado en hexadecimal; ‹TSA›, el nombre que la lista de §29.13 da a la autoridad de sellado, nunca el del token; y ‹t›, el instante del sello en UTC. Con F6, el lector MUST mostrar además una línea por firmante, con su titular, la autoridad de su certificado según esa lista y el instante de su sello, y decir si ese instante, más su precisión, es anterior a la fecha de apertura. Un `SignerInfo` cuyo certificado no está entre los exigidos se muestra aparte, con su resultado, y no cuenta (§29.10).
Un lector MUST NOT decir que una cápsula se firmó antes de la fecha salvo por un sello válido con t < round_time: quien puede abrirla puede rehacer el área (§7.9). Con un sello válido, una mtime posterior a t SHOULD mostrarse como incoherencia.
Un lector MUST NOT decir que una cápsula se firmó antes de la fecha salvo por un sello válido y reconocido con t + precisión < round_time: quien puede abrirla puede rehacer el área (§7.9). Con un sello válido, una mtime posterior a t SHOULD mostrarse como incoherencia.
Un lector que muestra el contenido de una cápsula de formato 3:
@ -1351,19 +1363,21 @@ CONTROL_SIG = CONTROL_CBOR con los 32 bytes de payload_identity (clave 3)
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 || u32(alg) || SIGNERS)
AUTHOR_MESSAGE = "datekeys:dkc3:author-signature:v1" || 0x00
|| SHA-256(control_commit || head_digest || signers_digest) ; 66 bytes
D = SHA-256(control_commit || head_digest || signers_digest)
AUTHOR_MESSAGE = "datekeys:dkc3:author-signature:v1" || 0x0A || hex(D) || 0x0A ; 99 bytes
código = los 8 primeros caracteres de hex(D), en dos grupos de 4: «xxxx-xxxx»
```
- `CONTROL_SIG` tiene la longitud y la estructura de `CONTROL_CBOR`: solo cambian esos 40 bytes. Cubre `header_binding`, y con él PRELUDE y `PUBLIC_HEADER` (§26), la regla de relleno y las extensiones de control, y oculta `I_PAYLOAD`.
- L no entra. Ocupa siempre 8 bytes (§31), así que el tamaño del área no cambia `SEALED_CONTROL_LEN` ni `header_binding`. Los tamaños de los ficheros sí entran, por el head.
- `head_digest` fija `CONTENT` byte a byte, porque el head lleva el tamaño y el SHA-256 de cada fichero (§29.4). La sal del head lo hace un compromiso que oculta: quien ve `AUTHOR_MESSAGE` sin el head, como la aplicación de firma externa, no puede confirmar una conjetura sobre el contenido.
- `u32(alg)` son 4 bytes big-endian. `SIGNERS` son los bytes exactos del contenido de la clave 1 del mapa `author-signature` con `alg` 2 (§29.10), y ninguno con `alg` 1.
- Cada prefijo de dominio es una cadena ASCII seguida del byte 0x00.
- Cada prefijo de dominio es una cadena ASCII. En las fórmulas lo sigue un byte 0x00, y en `AUTHOR_MESSAGE`, un salto de línea (0x0A). `hex` escribe en minúsculas.
- `AUTHOR_MESSAGE` es texto ASCII: quien firma puede abrirlo, y antes de firmar compara su código con el que muestra DateKeys (§62.1, regla 20).
Todos estos valores se recalculan desde la cápsula abierta; ninguno se almacena.
**Qué prueba una firma válida:** que sus firmantes firmaron este head para este control y esta lista de firmantes. No prueba cuándo firmaron, salvo con un sello (§29.11), ni la autoría del contenido, ni el autor declarado, ni que no exista otra cápsula con el mismo `capsule_id` (§7.9).
**Qué prueba una firma válida:** que sus firmantes firmaron este head para este control y esta lista de firmantes. Cada uno firma lo que le muestra su aplicación: si conocía el contenido depende de ella, y un cofirmante que solo recibe `AUTHOR_MESSAGE` no lo conoce (§7.9). No prueba cuándo firmaron, salvo con un sello (§29.11), ni la autoría del contenido, ni el autor declarado, ni que no exista otra cápsula con el mismo `capsule_id` (§7.9).
**Trasplantes:** una firma no vale en otra cápsula, porque `control_commit` cubre `capsule_id`, la DateKey y `payload_commit`, ni en otro contexto, porque cada valor firmado lleva su prefijo de dominio.
@ -1380,14 +1394,14 @@ author-signature:
2 → firma (64 bytes): R (32 bytes) || S (32 bytes)
```
Una clave o una firma de otra longitud incumple el perfil (F1). La firma es válida si y solo si:
Una clave o una firma de otra longitud incumple el perfil (F1). La firma es válida si y solo si se cumplen las cuatro condiciones siguientes; si falla alguna, no verifica (F2):
1. A es la codificación canónica de un punto: su y es menor que p, y si y vale 1 o p − 1, el bit de signo es 0;
2. A decodifica a un punto que no es de orden pequeño;
3. `sig[63] & 0xE0 = 0`, y S, como entero little-endian, es menor que ℓ;
4. [S]B − [k]A codifica exactamente R, con k = SHA-512(R ‖ A ‖ AUTHOR_MESSAGE) mod ℓ.
Es la verificación sin cofactor de RFC 8032 con A y R canónicas. Una implementación MUST NOT aceptar más. Go 1.26 `ed25519.Verify` acepta A = 01 00…00 con R igual a la identidad y S = 0, y noble 2.4 con `zip215` usa la ecuación con cofactor: las dos necesitan comprobaciones propias. Los casos de «Taming the many EdDSAs» los cubren (§64).
Es la verificación sin cofactor de RFC 8032 con A y R canónicas. Una implementación MUST NOT aceptar más. Go 1.26 `ed25519.Verify` acepta A = 01 00…00 con R igual a la identidad y S = 0, y `verify` de noble 2.4 usa siempre la ecuación con cofactor: las dos necesitan comprobaciones propias. `ed25519_strict.json` da el resultado esperado de cada caso de «Taming the many EdDSAs» (§64).
Con `alg` 1, la lista de firmantes exigidos va vacía: la propia clave identifica al firmante. Una firma válida prueba que alguien con la clave secreta de A firmó, no quién la tiene; quien abre la reconoce si la recibió antes por otro canal (§29.12).
@ -1404,34 +1418,46 @@ author-signature:
2 → la firma: un ContentInfo de tipo SignedData, en DER (cadena de bytes)
```
**Firmantes exigidos.** `SIGNERS` es un array CBOR con el perfil de §58, de 1 a 16 cadenas de 32 bytes: el SHA-256 del certificado de cada firmante en DER, en orden estrictamente ascendente de bytes y sin repeticiones. Están todos, también el autor, porque CMS no ordena a los firmantes y una lista parcial dejaría retirar sin rastro a los que no estuvieran.
**Firmantes exigidos.** `SIGNERS` es un array CBOR con el perfil de §58, de 1 a 16 cadenas de 32 bytes: el SHA-256 del certificado de cada firmante, sobre sus bytes tal cual, en orden estrictamente ascendente de bytes y sin repeticiones. Están todos, también el autor, porque CMS no ordena a los firmantes y una lista parcial dejaría retirar sin rastro a los que no estuvieran.
- La lista se cierra antes de la primera firma, así que exige conocer antes los certificados (§62.1, regla 20).
- Va en el área y no en el control: `SEALED_CONTROL_LEN` es visible (§55.2), y una extensión de control que solo existiera con firmantes los delataría. Entra en lo firmado por `signers_digest` (§29.8).
- Un `SignerInfo` de un certificado que no está en `SIGNERS` es un firmante ajeno: se verifica igual, se muestra aparte con su resultado y nunca cuenta para el veredicto.
**Perfil.** La firma MUST cumplir, y un lector MUST comprobar:
**Forma.** La firma MUST cumplir, y un lector MUST comprobar, en este orden; un fallo es F1:
1. DER en todo el `ContentInfo`, sin bytes de más, con `contentType` id-signedData (1.2.840.113549.1.7.2).
1. DER de X.690 en todo el `ContentInfo`, con los SET OF ordenados y sin bytes de más, y `contentType` id-signedData (1.2.840.113549.1.7.2). Los certificados, las respuestas OCSP y los tokens que lleva dentro se analizan con sus propias reglas (RFC 5280, RFC 6960 y §29.11).
2. Firma separada: `encapContentInfo` con `eContentType` id-data (1.2.840.113549.1.7.1) y sin `eContent`.
3. En `certificates`, exactamente un certificado por firmante, el suyo, y ninguno más: el escritor poda la cadena (§62.1, regla 21). En `crls`, solo respuestas OCSP (`OtherRevocationInfoFormat` con id-ri-ocsp-response, RFC 5940).
4. Un `SignerInfo` por cada hash de `SIGNERS`, cuyo `sid` identifica el certificado con ese hash, por `issuerAndSerialNumber` o por `subjectKeyIdentifier`.
5. En cada `SignerInfo`, `signedAttrs` con al menos `content-type` (id-data), `message-digest` de `AUTHOR_MESSAGE` y `signing-certificate-v2` (RFC 5035) con el hash de su certificado. Ningún otro atributo firmado decide nada.
6. Algoritmos de la lista: `digestAlgorithm` SHA-256, SHA-384 o SHA-512; y como `signatureAlgorithm`, RSASSA-PKCS1-v1_5 o RSASSA-PSS (MGF1 con el mismo hash y una sal de su longitud) con claves RSA de 2048 bits o más, o ECDSA con P-256 o P-384, cada uno con un hash de la lista.
7. En cada `SignerInfo`, el atributo no firmado `signature-time-stamp` (id-aa-signatureTimeStampToken, 1.2.840.113549.1.9.16.2.14) con un token RFC 3161 sobre el valor de su firma (CAdES-T), con el perfil de §29.11.
3. Cada `SignerInfo` identifica con su `sid`, por `issuerAndSerialNumber` o por `subjectKeyIdentifier`, exactamente un certificado de `certificates`, y ningún certificado tiene dos `SignerInfo`. Los certificados que ningún `SignerInfo` identifica solo sirven como intermedios (§29.13). En `crls` solo van respuestas OCSP (`OtherRevocationInfoFormat` con id-ri-ocsp-response, RFC 5940).
4. Cada `SignerInfo` lleva `signedAttrs` con exactamente un `content-type` (id-data), un `message-digest` y un `signing-certificate-v2` (RFC 5035) cuyo primer `ESSCertIDv2` da el hash de su certificado; y como mucho un atributo no firmado `signature-time-stamp` (id-aa-signatureTimeStampToken, 1.2.840.113549.1.9.16.2.14), con un solo valor. Los demás atributos, firmados o no, no deciden nada.
**Algoritmos.** Una tabla cerrada; el hash de la firma es el de `digestAlgorithm`:
| Algoritmo | OID |
|---|---|
| SHA-256, SHA-384 y SHA-512 | 2.16.840.1.101.3.4.2.1, .2 y .3, con los parámetros ausentes o NULL |
| RSASSA-PKCS1-v1_5 | rsaEncryption (1.2.840.113549.1.1.1), o sha256, sha384 y sha512WithRSAEncryption (1.2.840.113549.1.1.11, .12 y .13) |
| RSASSA-PSS | 1.2.840.113549.1.1.10, con el hash de `digestAlgorithm`, MGF1 con ese hash, una sal de su longitud y `trailerField` 1 |
| ECDSA | ecdsa-with-SHA256, SHA384 y SHA512 (1.2.840.10045.4.3.2, .3 y .4), con P-256 (1.2.840.10045.3.1.7) o P-384 (1.3.132.0.34) |
Un fallo de 1 a 5 incumple el perfil (F1), salvo un `message-digest` que no es el de `AUTHOR_MESSAGE`, que es una firma que no verifica (F2). Un algoritmo fuera de la lista de 6 deja al firmante no verificable, y la falta del sello de 7 lo deja sin sello (F5).
Las claves RSA miden de 2048 a 4096 bits, con un exponente impar de 3 a 2³¹ − 1.
**Verificación.** Para cada `SignerInfo`, un lector comprueba con el certificado del firmante el `message-digest`, la firma de `signedAttrs` y el sello, y que el instante del sello cae dentro de la validez del certificado. El resultado de cada firmante es válido, inválido o no verificable, este último con un algoritmo fuera de la lista o una evidencia que falta:
**Verificación.** Para cada hash de `SIGNERS`, el resultado es el primero que se cumple:
- F2 si una firma presente no verifica;
- F5 si falta un firmante de `SIGNERS` o su sello, si un sello no verifica, si un resultado es no verificable o si la cápsula lleva clave 3;
- F6 si todos los de `SIGNERS` son válidos, cada uno con su sello válido.
1. ningún `SignerInfo` es de ese certificado → ausente;
2. un algoritmo fuera de la tabla → no verificable;
3. el `message-digest` no es el hash de `AUTHOR_MESSAGE`, o la firma de `signedAttrs` no verifica con la clave del certificado → inválido;
4. sin `signature-time-stamp` → sin sello;
5. el token, con el perfil de §29.11, sobre los bytes del campo `signature` de ese `SignerInfo`: S2, S1 o S3 → sello inválido; S6 → sello no reconocido;
6. el certificado no es válido en t, el instante del sello → fuera de validez;
7. su cadena no es reconocida en t (§29.13) → no reconocido;
8. en otro caso → válido, con t.
Un `SignerInfo` cuyo certificado no está en `SIGNERS` se muestra aparte, con su resultado, y no cuenta.
El veredicto es el primero que se cumple: F2 si algún firmante exigido es inválido; F5 si alguno está ausente, no verificable, sin sello, con el sello inválido o fuera de validez, o si la cápsula lleva clave 3; F7 si alguno tiene el sello o el certificado no reconocidos; y F6 si todos son válidos.
**Lo que DateKeys no comprueba:** la cadena hasta una raíz de confianza, la revocación, ni si el certificado o la firma son cualificados. Los valora un validador externo, como VALIDe, al que el SDK oficial SHOULD poder exportar `AUTHOR_MESSAGE` y la firma. El texto de F6 lo dice (§29.7).
**Lo que DateKeys no comprueba:** la revocación, ni si el certificado o la firma son cualificados. Los valora un validador externo, como VALIDe, al que el SDK oficial SHOULD poder exportar `AUTHOR_MESSAGE` y la firma, avisando de que así entrega a un tercero el certificado de otra persona (§55.2). El texto de F6 lo dice (§29.7).
**Lo que prueba:** que cada titular de los certificados exigidos firmó este head para este control, y con su sello, que esa firma existía en un instante. Si alguien retira todas las firmas tras abrir la cápsula, nada interno lo detecta: hace falta que quien la recibe espere una firma.
**Lo que prueba F6:** que el titular de cada certificado exigido, emitido por una autoridad reconocida, firmó este `AUTHOR_MESSAGE`, y que esa firma existía en t según una autoridad de sellado reconocida. Cada firmante firma lo que le muestra su aplicación: si conocía el contenido depende de ella (§7.9). Si alguien retira todas las firmas tras abrir la cápsula, nada interno lo detecta: hace falta que quien la recibe espere una firma.
---
@ -1445,9 +1471,9 @@ Un sello de tiempo prueba que unos bytes existían en un instante t, según una
| Sin firma o con `alg` 1 | opcional, en la clave 3, con `seal_type` 2 | `SEAL_SUBJECT` |
```text
SIG_PART = 0x00 ; sin firma
SIG_PART = 0x00 ; sin clave 2
| 0x01 || SHA-256("datekeys:dkc3:sig-part:v1" || 0x00
|| u32(1) || A || firma) ; alg 1
|| el contenido de la clave 2) ; con clave 2
SEAL_SUBJECT = SHA-256("datekeys:dkc3:seal-subject:v1" || 0x00
|| control_commit || head_digest || SIG_PART)
@ -1456,22 +1482,20 @@ seal:
1 → token: un ContentInfo de tipo SignedData con un TSTInfo, en DER (cadena de bytes)
```
Con `seal_type` 2, el `messageImprint` del token es SHA-256(`SEAL_SUBJECT`), con el algoritmo SHA-256: la TSA nunca ve `SEAL_SUBJECT`. El sello se pide después de la firma, y la cubre por `SIG_PART`.
`SIG_PART` usa el contenido exacto de la cadena de bytes de la clave 2, sin su cabecera CBOR, sea cual sea su `alg` y su veredicto: un sello se verifica igual junto a una firma ilegible, no soportada o inválida, y prueba que esos bytes existían en t. Con `seal_type` 2, el `messageImprint` del token es SHA-256(`SEAL_SUBJECT`), con el algoritmo SHA-256: la TSA nunca ve `SEAL_SUBJECT`. El sello se pide después de la firma.
**Perfil del token**, en los dos sitios. Un lector MUST comprobar, en este orden:
**Perfil del token**, en los dos sitios. Un lector comprueba, en este orden, y el resultado es el del primer fallo:
1. DER, un `SignedData` con `eContentType` id-ct-TSTInfo (1.2.840.113549.1.9.16.1.4), su `eContent` y un solo `SignerInfo`;
2. el certificado de la TSA en `certificates`, identificado como en §29.10, con el uso extendido id-kp-timeStamping;
3. que sus algoritmos son de la lista de §29.10;
4. la firma de la TSA sobre el token;
5. que `messageImprint` es el hash de lo sellado, con el algoritmo que declara;
6. que `genTime` cae dentro de la validez del certificado de la TSA.
1. **Forma (S2):** DER de X.690; un `SignedData` con `eContentType` id-ct-TSTInfo (1.2.840.113549.1.9.16.1.4), su `eContent` y un solo `SignerInfo`; en él, `signedAttrs` con `content-type` id-ct-TSTInfo, `message-digest` y un `signing-certificate` (ESSCertID, cuyo SHA-1 solo identifica) o `signing-certificate-v2` que identifica el certificado de la TSA en `certificates`; y un `TSTInfo` de versión 1.
2. **Algoritmos (S1):** los de la tabla de §29.10, incluido el de `messageImprint`, que con `seal_type` 2 es SHA-256.
3. **Verificación (S3):** el `message-digest` es el hash del `eContent`, la firma de la TSA verifica, `messageImprint` es el hash de lo sellado, y el certificado de la TSA es válido en `genTime`.
4. **Autoridad (S6):** la cadena del certificado de la TSA es reconocida en `genTime` (§29.13).
t es `genTime`. Con `seal_type` 2, un fallo de 1 o 2 es S2, uno de 3 es S1, y uno de 4 a 6, S3. Dentro de `alg` 2, cualquiera de ellos da F5.
t es `genTime`, y su precisión, la de `accuracy`, o 0 si no viene. Un sello que pasa los cuatro da S4 si t + precisión < round_time, y S5 en otro caso. Dentro de `alg` 2, el resultado se traduce como dice §29.10.
**Lo que prueba:** según esa TSA, lo sellado existía en t; si t < round_time, antes de que la cápsula pudiera abrirse. Depende de la TSA: DateKeys no comprueba su cadena, su revocación ni si es cualificada, y el veredicto la nombra. Un sello no cambia el veredicto de la firma.
**Lo que prueba S4:** según una autoridad de sellado reconocida, lo sellado existía en t, antes de que la cápsula pudiera abrirse. Depende de esa autoridad: DateKeys no comprueba su revocación ni si es cualificada. Un sello no cambia el veredicto de la firma.
**Fuera de la cápsula,** sobre `capsule_digest` (§43), pueden ir otros sellos: OpenTimestamps, un sello cualificado del `.dkc` o resellados de archivo (RFC 4998). Esta versión no los define. Prueban que existía ese fichero cifrado, no que un firmante conociera su contenido, y un lector MUST NOT presentarlos como una firma interna.
**Fuera de la cápsula** pueden ir otros sellos: OpenTimestamps, un sello cualificado del `.dkc` o resellados de archivo (RFC 4998). Esta versión no los define. Sellan ese fichero cifrado, no que un firmante conociera su contenido, y un lector MUST NOT presentarlos como una firma interna.
---
@ -1479,18 +1503,38 @@ t es `genTime`. Con `seal_type` 2, un fallo de 1 o 2 es S2, uno de 3 es S1, y un
Una clave de autor es una semilla Ed25519 de 32 bytes:
- la pública, A, se escribe en bech32 (BIP 173) con el prefijo `dkauthor`: `dkauthor1…`, 67 caracteres en minúsculas;
- la pública, A, se escribe en bech32 (BIP 173) con el prefijo `dkauthor`, en minúsculas: `dkauthor1…`, 67 caracteres;
- la secreta, la semilla, en bech32 con el prefijo `DKAUTHOR-SECRET-KEY-`, en mayúsculas: 79 caracteres.
Una implementación MUST rechazar una clave con mayúsculas y minúsculas mezcladas, con otra longitud o con otro prefijo. Un fichero de clave secreta lleva una línea con ella y, por defecto, va cifrado con `age` y una contraseña, con scrypt de logN = 16.
Una implementación MUST rechazar una clave en otra caja, con otra longitud, con otro prefijo o con los bits de relleno distintos de cero. Un fichero de clave secreta lleva una línea con ella y, por defecto, va cifrado con `age` y una contraseña, con scrypt de logN = 16.
**Cómo conoce la clave quien abre:** por un canal externo, porque una firma `alg` 1 no dice quién tiene la clave. Un lector MAY guardar una clave con una etiqueta y dar después F3, pero MUST NOT ofrecer guardarla desde una cápsula salvo que:
**Cómo conoce la clave quien abre:** por un canal externo, porque una firma `alg` 1 no dice quién tiene la clave. Un lector MAY guardar una clave con una etiqueta y dar después F3, y MUST permitir borrarla. MUST NOT ofrecer guardarla desde una cápsula salvo que:
- la firma esté cubierta por un sello válido con t < round_time (S4), lo que excluye a quien volvió a firmar tras la fecha;
- la firma esté cubierta por un sello válido de una autoridad reconocida con t + precisión < round_time (S4, §29.13), lo que excluye a quien volvió a firmar tras la fecha;
- la etiqueta no se rellene con el autor declarado;
- la persona confirme la clave completa por otro canal.
Ninguna decisión se basa en una huella truncada de la clave.
Una clave robada no se puede revocar: un lector que la guardó seguirá dando F3 a las firmas de quien la robó (§7.9). Ninguna decisión se basa en una huella truncada de la clave.
---
## 29.13 Autoridades reconocidas
Un certificado o un sello solo prueban algo si su cadena llega a una autoridad que el lector conoce de antemano: quien firma o sella no puede traer su propia raíz, como no puede traerla un release de drand (§13). El SDK oficial fija una **lista de autoridades reconocidas**, con dos tipos de ancla:
- para los firmantes, los certificados raíz de las autoridades admitidas, como la FNMT y la del DNIe, con sus intermedios y un nombre para mostrar;
- para los sellos, los de las autoridades de sellado de tiempo admitidas, con sus intermedios y un nombre para mostrar.
La lista va con el SDK, versionada e identificada por su SHA-256, y su contenido inicial se fija al medir firmas y sellos reales (§74). Un lector MUST NOT ampliarla con certificados que lleguen en una cápsula, y MUST tomar de ella, y no del certificado, el nombre de la autoridad que muestra.
Una cadena es reconocida en un instante t si:
1. va del certificado a un ancla del tipo que toca, con intermedios del propio objeto o de la lista;
2. cada firma de la cadena verifica, con la tabla de algoritmos de §29.10;
3. cada certificado es válido en t, y cada intermedio tiene `basicConstraints` con cA y `keyUsage` con keyCertSign;
4. el certificado de un firmante tiene `keyUsage` con digitalSignature o nonRepudiation, y el de una autoridad de sellado, id-kp-timeStamping como único uso extendido, crítico (RFC 3161).
No se comprueba la revocación ni la cualificación: las valora un validador externo, como VALIDe (§29.10).
---
@ -1818,28 +1862,38 @@ Una llave de palabras es una credencial X25519 de `time_and_key` que se deriva d
1. su NFD, con las tablas de Unicode 18.0.0 de §29.5.1;
2. sin los puntos de código U+0300 a U+036F;
3. cada punto de código en minúscula, uno a uno, con su correspondencia simple de Unicode 18.0.0 (campo 13 de `UnicodeData.txt`);
3. cada punto de código en minúscula, uno a uno, con su correspondencia simple de Unicode 18.0.0 (campo 13 de `UnicodeData.txt`), de una tabla que las implementaciones generan como las de §29.5.1;
4. partido por los espacios U+0009 a U+000D, U+0020, U+0085, U+00A0, U+1680, U+2000 a U+200A, U+2028, U+2029, U+202F, U+205F y U+3000, sin palabras vacías.
Así, «Ábaco ÁRBOL», «abaco arbol» y «ábaco árbol» son las mismas palabras.
Así, «Ábaco ÁRBOL», «abaco arbol» y «ábaco árbol» son las mismas palabras. La puntuación cuenta: «perro,» no es «perro».
**Derivación.**
```text
P = las palabras en UTF-8, separadas por un U+0020
S = "DateKeys llave de palabras v1|" || hex(chain_hash) || "|" || decimal(ronda)
S = "DateKeys llave de palabras v2|" || hex(chain_hash) || "|" || decimal(ronda)
|| "|" || hex(capsule_id)
id = PBKDF2-HMAC-SHA256(P, S, 600000 iteraciones, 32 bytes) ; RFC 8018
```
`hex(chain_hash)` es el chain hash del perfil de la DateKey en hexadecimal en minúsculas, y `decimal(ronda)`, la ronda sin ceros a la izquierda. `id` es una identity X25519 cruda, como el `access_material` de una `.dkk` (§41), y su clave pública es el recipient.
`hex` escribe en minúsculas el chain hash del perfil de la DateKey y los 16 bytes de `capsule_id` (§21), y `decimal(ronda)` es la ronda sin ceros a la izquierda. `id` es una identity X25519 cruda, como el `access_material` de una `.dkk` (§41), y su clave pública es el recipient. Con `capsule_id` en la sal, las mismas palabras dan una llave distinta en cada cápsula: cada intento de un atacante prueba una sola cápsula, aunque muchas compartan ronda.
**Reglas:**
**Reglas del escritor.** MUST:
- un escritor MUST exigir al menos 6 palabras tras la normalización, y SHOULD recomendar más o unas al azar;
- un lector MAY pedir las palabras en lugar de una `.dkk` o de una identity, y derivar `id` con la DateKey de la cápsula, que dan los pasos 1 a 8 de §63;
- la sal obliga a un ataque propio por cadena y por ronda, pero tras la fecha quien tenga el `.dkc` puede probar palabras sin conexión: unas palabras elegidas por una persona son más débiles que unas al azar.
- exigir al menos 6 palabras tras la normalización;
- rechazar un texto con controles distintos de los espacios de arriba, o con puntos de código Default_Ignorable o sin asignar (Cn) en Unicode 18.0.0: uno pegado sin querer dejaría la cápsula sin abrir.
Vector: con «perro luna casa verde tren mar», el chain hash de Quicknet y la ronda 1000, `id` = `be74aecd9ea734bfece963597a2269188a4ea8a1247bc381dffbd6a38a2c6376`.
Y SHOULD:
- recomendar más palabras, o unas al azar, y rechazar las repetidas o de menos de 3 caracteres;
- mostrar las palabras normalizadas y pedir que se escriban de nuevo;
- avisar de que no se reutilice una contraseña: tras la fecha, la cápsula sirve para probarla.
**Lector.** MAY pedir las palabras en lugar de una `.dkk` o de una identity, y derivar `id` con la DateKey y el `capsule_id` de la cápsula, que dan los pasos 1 a 8 de §63.
Tras la fecha, quien tenga el `.dkc` puede probar palabras sin conexión: unas palabras elegidas por una persona son más débiles que unas al azar.
Vector: con «perro luna casa verde tren mar», el chain hash de Quicknet, la ronda 1000 y `capsule_id` = `000102030405060708090a0b0c0d0e0f`, `id` = `fceec4d8ca8de86c85a1f26ed49f82a2b38431bd0ce36db995ae7dfd49b96e41`.
---
@ -1978,7 +2032,7 @@ Una identity X25519 solo podrá abrir el `INNER_ACCESS_AGE` para el que fue util
Si no existe metadata de verificación, la clave `verification_metadata` MUST omitirse. Un mapa vacío no es una representación canónica válida de ausencia en V1.
Un escritor que añade un localizador a la extensión `datekeys.capsule` MUST omitir `verification_metadata`: `capsule_digest` va entonces dentro del localizador, cifrado para la fecha (§44.1).
Con un localizador (§44.1), `capsule_digest` en claro no delata dónde está la cápsula: lo que se guarda fuera es un sobre cifrado, con otro hash.
---
@ -2010,27 +2064,39 @@ La extensión `datekeys.capsule`, versión 1, registrada para el array no críti
- La nota es la copia de la nota pública de la cápsula (§24.1), con sus mismas reglas.
- `compact_datekey` es la DateKey de la cápsula en su forma canónica (§19): dice cuándo se abre.
- El localizador es un fichero `age` con un único stanza tlock para la ronda de esa DateKey (§28.1, §35). Su plaintext es otro mapa CBOR:
- El localizador es un fichero `age` con un único stanza tlock para la ronda y la cadena de esa DateKey (§28.1, §35). Su plaintext es otro mapa CBOR con el perfil de §58:
```text
0 → direcciones (array de 1 a 16 textos: URI de 1 a 2048 bytes cada uno)
1 → capsule_digest (32 bytes, §43)
0 → direcciones (array de 1 a 8 textos, de 1 a 1024 bytes cada uno)
1 → I_SOBRE (32 bytes: la identity X25519 cruda del sobre)
2 → sobre_digest (32 bytes: el SHA-256 del sobre)
3 → sobre_size (entero: la longitud del sobre en bytes)
4 → capsule_digest (32 bytes, §43)
5 → relleno (cadena de bytes a cero, opcional)
```
Nadie lo lee antes de la fecha, tampoco quien tiene la llave. En la fecha, el mismo release que abre la cápsula lo descifra.
El plaintext mide exactamente 4096 bytes, o el menor múltiplo de 4096 en el que quepa: la clave 5 completa lo que falte, así que su longitud no delata cuántas direcciones hay ni de qué tipo.
**El sobre.** Lo que se guarda fuera no es el `.dkc`, sino un sobre: un fichero `age` con un único stanza X25519 para la pública de `I_SOBRE`, una identity nueva de un CSPRNG, cuyo plaintext son los bytes exactos del `.dkc`. Quien encuentra el sobre ve un fichero `age` y su tamaño, nada de la cápsula: ni `capsule_id`, ni la fecha, ni la nota. Nadie, tampoco quien tiene la llave, lee el localizador ni el sobre antes de la fecha. En la fecha, el mismo release que abre la cápsula descifra el localizador.
**Direcciones.** Cada una es un URI ASCII de RFC 3986, con el esquema `https` o `ipfs` (un CID v1), sin userinfo. Un lector:
- MUST NOT descargar sin que la persona lo pida, y MUST mostrar antes el host o el CID: la descarga revela a quien controla la dirección cuándo y desde dónde se usa la llave, y en IPFS la ven la pasarela y los pares;
- MUST NOT seguir una redirección a otro esquema ni a una dirección de loopback, privada o de enlace local, y MUST cortar la descarga al pasar de `sobre_size` bytes;
- MUST comprobar que el SHA-256 del sobre es `sobre_digest`, descifrarlo con `I_SOBRE` y comprobar que el SHA-256 del `.dkc` es `capsule_digest` antes de usarlo, y SHOULD guardar una copia;
- MUST tratar como inutilizable un localizador cuya ronda o cuya cadena no son las de `compact_datekey`.
`sobre_digest` y `capsule_digest` protegen frente a quien guarda el sobre, no frente a quien escribió la `.dkk`.
Como toda extensión de la `.dkk`, es informativa: nada la ata a la cápsula (§44). Un lector:
- MUST emparejar la `.dkk` con su cápsula por `capsule_id` y, cuando lo tiene, por `capsule_digest`, nunca por la nota ni por la DateKey de la extensión;
- MUST mostrar la nota de la cápsula si la cápsula y la llave traen notas distintas, y SHOULD avisar de la diferencia;
- MUST NOT descargar de una dirección sin que la persona lo pida: la descarga revela a quien controla la dirección cuándo y desde dónde se usa la llave;
- MUST comprobar que el SHA-256 de lo descargado es `capsule_digest` antes de usarlo, y SHOULD guardar una copia.
- MUST mostrar la nota de la cápsula si sus bytes difieren de los de la nota de la llave, y SHOULD avisar de la diferencia.
Un escritor:
- MUST conocer las direcciones antes de escribir la `.dkk`, así que guarda antes la cápsula, también en IPFS (§62.1, regla 24);
- MUST omitir `verification_metadata` cuando escribe un localizador (§43): en IPFS, la dirección de un fichero pequeño con CIDv1 y bloques raw es su SHA-256, y `capsule_digest` en claro la delataría;
- SHOULD avisar de que con un localizador la `.dkk` basta para encontrar la cápsula y abrirla en la fecha, de que nadie puede bajarla antes, y de que una dirección que deja de existir pierde la cápsula para quien no tenga otra copia.
- MUST guardar fuera solo el sobre, nunca el `.dkc`, y conocer las direcciones antes de escribir la `.dkk`: primero crea el sobre y lo guarda, también en IPFS (§62.1, regla 24);
- SHOULD avisar de que con un localizador la `.dkk` basta para encontrar la cápsula y abrirla en la fecha; de que quien solo tiene la llave no sabe dónde está la cápsula hasta la fecha, aunque quien guarda el sobre lo tiene; y de que una dirección que deja de existir pierde la cápsula para quien no tenga otra copia.
---
@ -2263,17 +2329,17 @@ No sustituyen:
## 55.1 Modelo de confianza por sección
La tabla resume, para cada sección de un `.dkc` y para una `.dkk`, quién puede escribirla, desde qué paso de §63 queda vinculada al resto de la cápsula y mediante qué, y qué no prueba nunca. Ninguna fila prueba autoría: solo una extensión de firma puede aportarla (§36.1, §72). La tabla es normativa: una implementación MUST NOT presentar una sección como prueba de algo que su columna «Nunca prueba» excluye, salvo que lo cubra una extensión de firma.
La tabla resume, para cada sección de un `.dkc` y para una `.dkk`, quién puede escribirla, desde qué paso de §63 queda vinculada al resto de la cápsula y mediante qué, y qué no prueba nunca. Ninguna fila prueba autoría: solo la aportan una firma de `security` (§29.8 a §29.10) o una extensión de firma (§36.1, §72). La tabla es normativa: una implementación MUST NOT presentar una sección como prueba de algo que su columna «Nunca prueba» excluye, salvo que lo cubran una firma o un sello válidos de `security`, con el alcance de §29.8 a §29.13, o una extensión de firma.
| Sección | Quién puede escribirla | Vinculada desde el paso, mediante | Nunca prueba |
|---|---|---|---|
| `PRELUDE` y `PUBLIC_HEADER` | Cualquiera que tenga el `.dkc`: viajan en claro y ninguna clave los protege. | Paso 15: `header_binding`, dentro de `CONTROL_CBOR`, cubre sus bytes exactos (§26). Los pasos 1 a 8 solo comprueban su estructura. | Autoría ni fecha de creación: `header_binding` se calcula con bytes públicos (§36.1). Antes del paso 15, ni siquiera su coherencia con el control. |
| `CONTROL_CBOR` | Cualquiera puede sellar un control hacia la DateKey, porque basta la clave pública del perfil (§36.1). En `time_and_key`, uno que abra una credencial dada solo quien conozca la clave pública de su recipient. | Paso 11: el MAC de la cabecera `age` y STREAM de `OUTER_TIME_AGE`, y en `time_and_key` los de `INNER_ACCESS_AGE` en el paso 13, autentican sus bytes frente a quien no conoce la file key. Paso 15: `header_binding` lo vincula a `PUBLIC_HEADER`. | Que lo escribiera el creador de `PUBLIC_HEADER`: un tercero puede sellar otro control con un `header_binding` correcto. Tras la apertura, quien conoce `FK_TIME` o `FK_ACCESS` puede reescribirlo. |
| `PAYLOAD_AGE` | Quien conoce `R_PAYLOAD`: antes de la apertura, solo quien selló el control; después, cualquiera que haya abierto `CONTROL_CBOR` y obtenido `I_PAYLOAD`. | Paso 17: `I_PAYLOAD` desenvuelve `FK_PAYLOAD`, y el MAC de la cabecera y STREAM autentican cada byte (§30.1). En los formatos 2 y 3, L y el código de `CONTROL_CBOR` fijan además su longitud y su relleno (§29.1). | Autoría; tampoco que siga siendo el payload original después de que alguien haya abierto la cápsula, porque puede cifrar otro para `R_PAYLOAD`. Tampoco que L sea la longitud original: quien puede reescribir el control puede declarar otra L de la misma P (§30.1). |
| Contenido de formato 3: `security`, head y ficheros (§29.2) | Quien escribe `PAYLOAD_AGE`, porque es su plaintext. | Paso 17: los autentican el MAC de la cabecera y STREAM de `PAYLOAD_AGE`, y el head fija el tamaño y el SHA-256 de cada fichero (§29.4). | Autoría ni fecha: el autor declarado, el comentario, las rutas y las mtimes son texto del creador. En esta versión, `security` tampoco prueba nada (§29.3, §29.7). |
| Contenido de formato 3: `security`, head y ficheros (§29.2) | Quien escribe `PAYLOAD_AGE`, porque es su plaintext. | Paso 17: los autentican el MAC de la cabecera y STREAM de `PAYLOAD_AGE`, y el head fija el tamaño y el SHA-256 de cada fichero (§29.4). | Autoría ni fecha: el autor declarado, el comentario, las rutas y las mtimes son texto del creador. `security` prueba solo lo que dicen sus veredictos (§29.7 a §29.13). |
| Cuerpo de la `.dkk` | Cualquiera que tenga la `.dkk`: no lleva MAC ni firma. | Paso 9.a: su `capsule_id` debe ser el de `PUBLIC_HEADER`, un valor público, y su `capsule_digest`, cuando existe y se comprueba, la ata a los bytes exactos de un `.dkc` (§43). Paso 13: `access_material` abre `INNER_ACCESS_AGE` solo si es la identity de uno de sus recipients. | Que la emitiera el creador de la cápsula. La `data` de sus extensiones no queda vinculada a nada. |
En consecuencia, una afirmación de la que dependa una decisión de seguridad del lector —autorización, identidad, integridad o fecha— no puede apoyarse en `PUBLIC_HEADER` ni en una `.dkk` sin una extensión de firma, y la `data` de las extensiones de una `.dkk` solo informa a quien la posee (§72).
En consecuencia, una afirmación de la que dependa una decisión de seguridad del lector —autorización, identidad, integridad o fecha— no puede apoyarse en `PUBLIC_HEADER` ni en una `.dkk` sin una firma que los cubra, y la `data` de las extensiones de una `.dkk` solo informa a quien la posee (§72).
---
@ -2326,6 +2392,9 @@ Ese supuesto no resiste a un adversario cuántico futuro (§7.7): quien conserve
- Las firmas, sus certificados y los sellos van en el área, cifrados: los titulares y la autoridad de sellado solo se ven tras la fecha.
- La aplicación de firma recibe `AUTHOR_MESSAGE`, que no revela el contenido (§29.8). En la web, ese mensaje y la firma quedan como ficheros en el dispositivo, y la firma lleva el certificado del firmante.
- La autoridad de sellado recibe el hash de lo que sella y, como cualquier servicio, la dirección de quien pregunta y el instante.
- El certificado de un firmante lleva su nombre y su NIF, y el de un representante, la entidad: tras la fecha los ve quien pueda abrir la cápsula, y en `time_only`, cualquiera. Un lector muestra el nombre sin el `serialNumber` (§29.7).
- Pedir una respuesta OCSP, a menudo por HTTP sin cifrar, revela a la red el número de serie del certificado y a la autoridad el momento de la firma. El escritor SHOULD pedir el OCSP y el sello desde el dispositivo; si usa un intermediario, MUST declararlo, porque vería la dirección, el instante y el hash de cada cápsula firmada.
- Exportar una firma a un validador externo le entrega el certificado de otra persona.
- Esto cubre el `.dkc`, no los sellos ni las firmas que van fuera (§29.11), ni el tráfico hacia esos servicios.
### Lo que los formatos 2 y 3 impiden comprobar
@ -2336,7 +2405,7 @@ Ese supuesto no resiste a un adversario cuántico futuro (§7.7): quien conserve
### Fuera del `.dkc`
- una `.dkk` lleva en claro la credencial, `access_material`, y además `capsule_id`, `credential_id`, el `capsule_digest` opcional, que la ata a los bytes exactos de un `.dkc`, y la `data` de sus extensiones (§41, §43, §44);
- la extensión `datekeys.capsule` de una `.dkk` lleva en claro la nota y la fecha, y cifrado para la fecha el localizador con las direcciones de la cápsula (§44.1);
- la extensión `datekeys.capsule` de una `.dkk` lleva en claro la nota y la fecha, y cifrado para la fecha el localizador. Lo que se guarda fuera es un sobre cifrado: quien lo aloja ve su tamaño y quién lo sube o lo baja, nada de la cápsula (§44.1);
- pedir el release revela a la Release API y a los relays el perfil, la ronda y la dirección de quien pregunta, pero no `capsule_id` (§45, §48);
- el nombre del fichero `.dkc`, sus fechas en el sistema de ficheros y el canal de entrega no pertenecen al protocolo (§6). En formato 3, los nombres y las mtimes de los ficheros que guarda la cápsula sí van dentro, cifrados en el head (§29.4).
@ -2412,6 +2481,7 @@ Correspondencia de errores:
- una longitud de trama (`PUBLIC_HEADER_LEN`, `SEALED_CONTROL_LEN` y el `BODY_LEN` de la `.dkk`) fuera de estos límites, incluida una longitud 0, o un objeto que supera el límite de su trama → `ERR_INTEGRITY`;
- cualquier violación de una regla normativa de `datekeys.cddl`, incluidas las restricciones de tipo, de tamaño y de rango, el orden de los `extension_id`, el máximo de 64 extensiones por array, y las de las claves 6 y 7 de `CONTROL_CBOR` (§31) → `ERR_NON_CANONICAL_CBOR`, salvo los casos con código propio: versión de schema (clave 1) que el objeto no admite —para `CONTROL_CBOR`, la que no corresponde al formato de su cápsula (§22, §31)— → `ERR_UNSUPPORTED_VERSION` (§70); `compact_datekey` → `ERR_DATEKEY_INVALID` o `ERR_DATEKEY_NON_CANONICAL` (§19); `access_type` no soportado o `access_material` de longitud incorrecta → `ERR_ACCESS_INVALID`; en el Provider Profile, un `profile_id`, `provider`, `network` o `scheme` que no cumple su regla, o una clave pública (clave 6) fuera de la suya → `ERR_UNKNOWN_PROFILE` (§12.1, §13);
- en formato 3, una violación de la trama del contenido (§29.2) → `ERR_INTEGRITY`; una de las reglas del head con código propio (§29.4 a §29.6, capa 4 de §69.1) → `ERR_HEAD_INVALID`; y las de `security`, ningún código (§29.3);
- una violación de las reglas de la `data` de una extensión registrada (`note-data`, `capsule-data` y `capsule-locator` de `datekeys.cddl`), ningún código: la comprueba solo quien conoce la extensión, que la trata como dice §54;
- un código propio se aplica a un campo que ya tiene el tipo CBOR de su regla: un `compact_datekey`, `access_type` o nombre del perfil que no es una cadena de texto, o un `access_material` o una clave pública que no es una cadena de bytes, es `ERR_NON_CANONICAL_CBOR`;
- las reglas que `datekeys.cddl` marca como límites de la implementación de referencia no son normativas mientras §74 deje abiertos los límites definitivos de campos; quien las aplica usa esta misma correspondencia (§74 las enumera).
@ -2592,7 +2662,7 @@ FK_PAYLOAD
FK_TIME
```
Con firma o sello, entre los pasos 12 y 13: calcular `AUTHOR_MESSAGE` con `CONTROL_SIG` (§29.8), que no depende de L; obtener las firmas y los sellos y verificarlos (§62.1, regla 19); y construir `SECURITY_CBOR` con ellos. Si no caben en el área, ampliarla solo con el permiso expreso de quien crea la cápsula (regla 13) y escribir en `CONTROL_CBOR` la L del área ampliada: la firma no cambia.
Con firma o sello, entre los pasos 12 y 13: calcular `AUTHOR_MESSAGE` con `CONTROL_SIG` (§29.8), que no depende de L; obtener las firmas y los sellos y verificarlos (§62.1, regla 19); y construir `SECURITY_CBOR` con ellos. Si no caben en el área, ampliarla solo con el permiso expreso de quien crea la cápsula (regla 13) y escribir en `CONTROL_CBOR` la L del área ampliada: la firma no cambia. Después, decodificar de nuevo `CONTROL_CBOR` y `SECURITY_CBOR` definitivos con las reglas del lector (regla 17). Mientras espera las firmas, el escritor guarda `I_PAYLOAD` y el contenido solo en memoria (regla 25).
---
@ -2693,15 +2763,19 @@ En formato 3, además, MUST:
Con firma o sello, además, MUST:
19. **Verificación.** Verificar cada firma y cada sello con las reglas del lector (§29.9 a §29.11) antes de escribir la cápsula, y no escribir una que daría F1, F2 o F5, ni S1, S2 o S3.
20. **Firmantes exigidos.** Con `alg` 2, cerrar `SIGNERS` antes de la primera firma con el certificado de cada firmante: seleccionado sin firmar, importado o tomado de una firma de prueba. Una firma de prueba MUST ser sobre un mensaje propio de DateKeys, sin nada de quien crea la cápsula y distinto de cualquier `AUTHOR_MESSAGE`; su tamaño solo es una estimación. El certificado se procesa en el dispositivo y no va a registros. Si un firmante firma con otro certificado, rehacer la lista y volver a firmar.
21. **Firma con certificado.** Con `alg` 2, podar la cadena a un certificado por firmante y añadir a cada `SignerInfo` su sello CAdES-T, y SHOULD añadir la respuesta OCSP de cada firmante del momento de la firma. Si la autoridad de sellado falla, reintentar sin volver a firmar, y no dar por terminada una cápsula sin sellos. Antes de pedir la primera firma, SHOULD estimar si firmas, sellos y respuestas OCSP caben en el área y, si no, preguntar si ampliarla o quitar firmantes.
19. **Verificación.** Verificar cada firma y cada sello con las reglas del lector (§29.9 a §29.11) antes de escribir la cápsula, y no escribir una que daría F1, F2, F5 o F7, ni S1, S2, S3 o S6.
20. **Firmantes exigidos y lo que se firma.** Entregar `AUTHOR_MESSAGE` como texto y mostrar su código (§29.8) antes de pedir cada firma. Con `alg` 2, cerrar `SIGNERS` antes de la primera firma con el certificado de cada firmante: seleccionado sin firmar, importado o tomado de una firma de prueba. Una firma de prueba es sobre `TEST_MESSAGE = "datekeys:dkc3:test-signature:v1" || 0x0A || hex(32 bytes de un CSPRNG) || 0x0A`, nunca sobre un `AUTHOR_MESSAGE`, y su tamaño solo es una estimación. El certificado se procesa en el dispositivo y no va a registros. Si un firmante firma con otro certificado, rehacer la lista y volver a firmar.
21. **Firma con certificado.** Con `alg` 2, añadir a cada `SignerInfo` su sello CAdES-T de una autoridad reconocida (§29.13), y SHOULD podar la cadena a un certificado por firmante y añadir la respuesta OCSP de cada firmante del momento de la firma. Una aplicación de firma puede entregar una firma implícita y con la cadena entera: el escritor MAY quitar su `eContent` y podar la cadena, lo que no invalida la firma. Si la autoridad de sellado falla, reintentar sin volver a firmar, y no dar por terminada una cápsula sin sellos. Antes de pedir la primera firma, SHOULD estimar si firmas, sellos y respuestas OCSP caben en el área y, si no, preguntar si ampliarla o quitar firmantes.
22. **Sello sin certificado.** Sin firma o con `alg` 1, el sello es opcional: `seal_type` 2 sobre `SEAL_SUBJECT`, pedido después de la firma (§29.11).
Con nota pública, llave de palabras o extensión de cápsula, MUST:
23. **Nota pública.** Escribir la extensión `datekeys.note` solo si la persona la pide, con un texto que cumpla §29.6, y SHOULD avisar de que es pública (§24.1).
24. **Llaves.** Exigir al menos 6 palabras a una llave de palabras (§38.1). Escribir una `.dkk` con localizador después de guardar la cápsula, con sus direcciones (§44.1).
23. **Nota pública.** Escribir la extensión `datekeys.note` solo si la persona la pide, con un texto que cumpla las reglas del autor declarado de §29.6, y SHOULD avisar de que es pública y de que, con la fecha, puede identificar a alguien (§24.1).
24. **Llaves.** Cumplir las reglas del escritor de §38.1 para una llave de palabras. Para una `.dkk` con localizador, crear el sobre, guardarlo fuera y escribir después la `.dkk` con sus direcciones; nunca guardar fuera el `.dkc` (§44.1).
Con firma o sello, además, MUST:
25. **Secretos en reposo.** Mientras espera una firma o un sello, no guardar en disco `I_PAYLOAD`, `CONTROL_CBOR` ni el contenido: si la espera se interrumpe, se empieza de nuevo.
Nota informativa: longitudes en Quicknet. Con `age` estándar, un stanza X25519 mide 98 bytes, y el stanza tlock, 249 + d, con d el número de dígitos decimales de la ronda. Con c(n) = max(1, ⌈n / 65536⌉) y k stanzas X25519 (16 en los formatos 2 y 3):
@ -2875,7 +2949,8 @@ Sin extensiones de control, C = 103: en la ronda 1000, `SEALED_CONTROL_LEN` vale
→ ERR_HEADER_BINDING.
Desde aquí la data de las extensiones de PUBLIC_HEADER queda
vinculada al control abierto: coherencia interna (§36.1), no
autoría, salvo que la cubra una extensión de firma (§72).
autoría, salvo que la cubra una firma de security (§29.8) o
una extensión de firma (§72).
16. Recuperar I_PAYLOAD. En los formatos 2 y 3, recuperar también L y el código
de relleno (claves 6 y 7) y calcular P = regla(L) (§29.1). Este
@ -3106,13 +3181,15 @@ Las mutaciones del formato 3 cambian el plaintext de `PAYLOAD_AGE`: las construy
Firma, sello, nota y llave de palabras (v0.11), además:
- `alg` 1: la firma alterada, quitada, rehecha con otra clave o trasplantada a otra cápsula; los casos de «Taming the many EdDSAs»: A de orden pequeño, A o R no canónicas, S ≥ ℓ y la ecuación con cofactor;
- `alg` 2: un firmante exigido retirado (F5); el autor retirado y los demás intactos (F5); una firma retirada y `SIGNERS` cambiado para ocultarlo (F2); el CAdES-T de un firmante retirado (F5); un `SignerInfo` de un certificado que no está en `SIGNERS` (aparte, sin contar); `message-digest` de otro mensaje (F2); BER en lugar de DER (F1); un algoritmo fuera de la lista (F5); la clave 3 con `alg` 2 (F5);
- `alg` 1: la firma alterada, quitada, rehecha con otra clave o trasplantada a otra cápsula; una clave o una firma de otra longitud (F1); los casos de «Taming the many EdDSAs» (A de orden pequeño, A o R no canónicas, S ≥ ℓ, la ecuación con cofactor), con el veredicto de cada uno en `ed25519_strict.json`;
- `alg` 2: un firmante exigido retirado (F5); el autor retirado y los demás intactos (F5); una firma retirada y `SIGNERS` cambiado para ocultarlo (F2); el CAdES-T de un firmante retirado (F5); un certificado fuera de validez en t (F5); un certificado o un sello de una autoridad no reconocida (F7); un firmante ajeno (aparte, sin contar); `message-digest` de otro mensaje (F2); BER, o los `signerInfos` desordenados (F1); dos `SignerInfo` del mismo certificado (F1); un algoritmo fuera de la tabla (F5); la clave 3 con `alg` 2 (F5);
- el área ampliada de 32 KiB a 64 KiB después de firmar: la firma sigue valiendo y `AUTHOR_MESSAGE` no cambia;
- `seal_type` 2: `messageImprint` de otro `SEAL_SUBJECT` (S3), t ≥ round_time (S5) y un token en BER (S2);
- `seal_type` 2: `messageImprint` de otro `SEAL_SUBJECT` (S3); un token sin el `message-digest` de su `eContent` (S2); una autoridad no reconocida (S6); t + precisión ≥ round_time (S5); un token en BER (S2); un sello junto a una clave 2 de un `alg` desconocido, que se verifica igual;
- `alg` y `seal_type` 4294967295: F1 y S1 en cualquier versión;
- el mismo contenido con firma y sin ella, en el área común: la misma P;
- la nota pública cambiada en `PUBLIC_HEADER`: `ERR_HEADER_BINDING` en el paso 15; con un carácter que §29.6 prohíbe, inutilizable y sin mostrarse;
- la llave de palabras: el vector de §38.1, y textos con mayúsculas, acentos y espacios que dan las mismas palabras.
- la nota pública cambiada en `PUBLIC_HEADER`: `ERR_HEADER_BINDING` en el paso 15; con un salto de línea o un carácter que §29.6 prohíbe: inutilizable y sin mostrarse;
- la llave de palabras: el vector de §38.1; textos con mayúsculas, acentos y espacios que dan las mismas palabras; y un texto con U+200B, que el escritor rechaza;
- la extensión `datekeys.capsule`: un localizador para otra ronda (inutilizable); una dirección `file:` o `http:` (no se descarga); un sobre cuyo SHA-256 no es `sobre_digest` (no se usa).
---
## 65. Test vectors Quicknet
@ -3202,8 +3279,8 @@ Los de formato 3 cubren, como mínimo:
- `time_and_key` con clave portable;
- un área de 1024 bytes, que el lector acepta;
- `security` de versión 2, con el veredicto X;
- una firma de `alg` 1 con una clave de 32 bytes y una firma de 64, aleatorias, con el veredicto F1;
- esa firma y un sello de `seal_type` 1 con un token aleatorio, con F1 y S1.
- una firma de `alg` 4294967295, reservado para pruebas (§29.3), con una clave de 32 bytes y una firma de 64, aleatorias, con el veredicto F1;
- esa firma y un sello de `seal_type` 4294967295 con un token aleatorio, con F1 y S1.
Son `format3_single.dkc`, `format3_tree.dkc`, `format3_comment_only.dkc`, `format3_bloque256.dkc`, `format3_time_and_key_portable.dkc` con su `.dkk`, `format3_area_1024.dkc`, `format3_security_v2.dkc`, `format3_signature_unsupported.dkc` y `format3_seal_unsupported.dkc`. Los vectores de rutas y del head van en `testdata/vectors/paths.json`, `head_schema.json` y `path_fold.json`, y los de `security`, en `security.json`.
@ -3401,7 +3478,7 @@ Ubicación según el modelo de confianza (§55.1):
- una extensión que lleva afirmaciones relevantes para la seguridad —aquellas de las que depende una decisión de seguridad de quien abre la cápsula: autorización, identidad, integridad o fecha— MUST registrarse en `CONTROL_CBOR` o estar firmada por una extensión de firma; ni siquiera en `CONTROL_CBOR` prueba autoría (§36.1);
- sin una extensión de firma que la cubra, la `data` de las extensiones de una `.dkk` es solo informativa para quien la posee: una implementación MUST NOT basar en ella una decisión de seguridad sobre la cápsula.
Nota: la `data` de `PUBLIC_HEADER` es pública. El protocolo base no la vincula al control hasta que se verifica `header_binding` (§63, paso 15), y esa verificación aporta coherencia interna, no autoría: solo una extensión de firma puede aportar autenticidad del creador (§36.1, §55.1). La de `.dkk` viaja en claro y `header_binding` no la cubre, así que el protocolo base no la autentica en ningún paso.
Nota: la `data` de `PUBLIC_HEADER` es pública. El protocolo base no la vincula al control hasta que se verifica `header_binding` (§63, paso 15), y esa verificación aporta coherencia interna, no autoría: solo una firma de `security` (§29.8) o una extensión de firma pueden aportar autenticidad (§36.1, §55.1). La de `.dkk` viaja en claro y `header_binding` no la cubre, así que el protocolo base no la autentica en ningún paso.
### Extensiones registradas
@ -3499,7 +3576,8 @@ error precedence
subpaso
trust model
= §55.1; autoría solo mediante una extensión de firma
= §55.1; autoría solo mediante una firma de security o una
extensión de firma, con autoridades reconocidas (§29.13)
BLS12-381 points
= una sola codificación válida, la comprimida canónica de drand
@ -3566,22 +3644,23 @@ security
= mapa de versión 1 con la firma y el sello codificados aparte; nunca
decide la apertura; alg 1 (Ed25519 estricto) y 2 (CMS con certificados
y un CAdES-T por firmante), seal_type 2 (RFC 3161); veredictos X, F0
a F6 y S0 a S5
a F7 y S0 a S6
author signature
= AUTHOR_MESSAGE de 66 bytes sobre control_commit, sin I_PAYLOAD ni L,
head_digest y signers_digest; nunca cubre el área; los firmantes
exigidos de alg 2 en su clave 1
= AUTHOR_MESSAGE, un texto de 99 bytes con el hash de control_commit,
sin I_PAYLOAD ni L, de head_digest y de signers_digest, y un código de
8 caracteres; nunca cubre el área; los firmantes exigidos de alg 2 en
su clave 1; certificados y sellos, solo de autoridades reconocidas
word key
= recipient X25519 derivado de 6 o más palabras normalizadas, con
PBKDF2-HMAC-SHA256 de 600 000 iteraciones y la cadena y la ronda
como sal
PBKDF2-HMAC-SHA256 de 600 000 iteraciones y la cadena, la ronda y
capsule_id como sal
public note
= datekeys.note en PUBLIC_HEADER, pública y atada en el paso 15; copia
informativa en datekeys.capsule de la .dkk, con la fecha y un
localizador cifrado para la fecha
localizador cifrado para la fecha, que apunta a un sobre opaco
recovery
= puede obtener release directamente del provider
@ -3606,8 +3685,7 @@ Antes de v1.0 quedan por cerrar:
En la v0.11 son además provisionales:
- los 32 KiB del área, hasta medir firmas CAdES de la FNMT y del DNIe, con y sin cadena, sellos de dos autoridades cualificadas y respuestas OCSP. Se confirman si un firmante completo cabe con un 25 % de margen; si dos firmantes completos no caben, usan la ampliación expresa; solo se bajan a 16 KiB si dos firmantes completos miden menos de 12 KiB;
- la lista de algoritmos de §29.10;
- la correspondencia de minúsculas de la llave de palabras (§38.1), que las implementaciones toman hoy de sus propias tablas de Unicode.
- la tabla de algoritmos de §29.10 y la lista de autoridades reconocidas de §29.13, hasta probarlas con firmas y sellos reales;
El framing base, la ausencia de `PAYLOAD_LEN`, el uso de age files estándar, la identity X25519 cruda de `.dkk` y el formato de extensiones (§54) dejan de considerarse provisionales en este borrador. Tampoco lo son los formatos 1, 2 y 3 (§22), los 16 huecos de `INNER_ACCESS_AGE` (§39), las reglas de relleno 1 y 2 (§29.1), ni la trama del contenido, el head y las reglas de rutas y de texto del formato 3 (§29.2 a §29.6). El schema byte a byte de `CONTROL_CBOR`, claves 6 y 7 incluidas, sigue abierto como el de los demás objetos; su versión final conservará una codificación de L de longitud fija (§31, §55.2).
@ -3932,17 +4010,17 @@ La v0.11 define la firma de autor y el sello de tiempo del área `security`, que
1. **Área de 32 KiB** (§29.2, §55.2, §62.1 regla 13).
- Cambio: un escritor escribe `AREA_LEN` = 32768, firme o no, y 65536 solo si se amplía expresamente.
- Motivo: una firma con certificado, su sello y su respuesta OCSP no caben en 512 bytes, y un área que creciera con ellos revelaría en P si la cápsula va firmada, y de qué tipo, a quien conoce su contenido o compara cápsulas del mismo autor.
- Caso: una carta de 2 KB da P = 2 816 sin firma y P = 8 704 con una firma de unos 6 KiB en un área ajustada, la misma P que una carta de 8 KB sin firma: quien sabe qué carta va dentro deduce la firma. Con el área de 32 KiB, P = 34 816 en los dos casos.
- Caso: con un head de 100 bytes, una carta de 2 000 bytes da P = 2 816 sin firma, y P = 8 704 con una firma que ocupe un área ajustada de 6 144 bytes, la misma P que una carta de 8 000 bytes sin firma: quien sabe qué carta va dentro deduce la firma. Con el área de 32 KiB, P = 36 864 en los dos casos.
- Pruebas previstas: la misma P con firma y sin ella (§64).
2. **Qué se firma** (§29.8).
- Cambio: `AUTHOR_MESSAGE` de 66 bytes sobre `control_commit` de `CONTROL_SIG`, sin `I_PAYLOAD` ni L, `head_digest` y `signers_digest`.
- Motivo: con L dentro, el área tendría que fijarse antes de firmar, y una firma que no cupiera obligaría a firmar de nuevo; sin L, el área se amplía después sin un segundo PIN. L ocupa siempre 8 bytes (§31), así que `header_binding` no depende del área.
- Cambio: `AUTHOR_MESSAGE`, un texto de 99 bytes con el hash de `control_commit` de `CONTROL_SIG`, sin `I_PAYLOAD` ni L, de `head_digest` y de `signers_digest`, y un código de 8 caracteres para compararlo antes de firmar.
- Motivo: con L dentro, el área tendría que fijarse antes de firmar, y una firma que no cupiera obligaría a firmar de nuevo; sin L, el área se amplía después sin un segundo PIN. L ocupa siempre 8 bytes (§31), así que `header_binding` no depende del área. En texto, quien firma puede leer lo que firma.
- Caso: el diseño de la entrega 2 firmaba `control_commit` con L. Una firma CAdES no tiene tamaño conocido hasta hacerla, así que el escritor no podría conocer L antes de pedir el PIN.
- Pruebas previstas: el área ampliada después de firmar, con la misma `AUTHOR_MESSAGE` (§64).
3. **Firma con clave propia, `alg` 1** (§29.9, §29.12).
- Cambio: Ed25519 estricto y claves `dkauthor1…`.
- Motivo: la firma de autor que reservó la v0.10.
- Caso: Go 1.26 `ed25519.Verify` acepta A = 01 00…00 con R igual a la identidad y S = 0, y noble 2.4 con `zip215` usa la ecuación con cofactor: sin un perfil estricto, las dos implementaciones darían veredictos distintos.
- Caso: Go 1.26 `ed25519.Verify` acepta A = 01 00…00 con R igual a la identidad y S = 0, y `verify` de noble 2.4 usa siempre la ecuación con cofactor: sin un perfil estricto, las dos implementaciones darían veredictos distintos.
- Pruebas previstas: `ed25519_strict.json` (§64).
4. **Firma con certificado, `alg` 2** (§29.10, §62.1 reglas 19 a 21).
- Cambio: CMS separada con uno o varios firmantes, la lista de firmantes exigidos en la clave 1 y un CAdES-T por firmante.
@ -3955,17 +4033,22 @@ La v0.11 define la firma de autor y el sello de tiempo del área `security`, que
- Caso: sin sello, quien abre una cápsula puede rehacer su área tras la fecha, así que una firma no prueba que existiera antes (§7.9).
- Pruebas previstas: las de `seal_type` 2 de §64.
6. **Llave de palabras** (§38.1).
- Cambio: una credencial X25519 derivada de 6 o más palabras.
- Cambio: una credencial X25519 derivada de 6 o más palabras, con la cadena, la ronda y `capsule_id` como sal.
- Motivo: pocas personas saben guardar una clave, y una `.dkk` se pierde.
- Caso: la página `/create` de `datekeys-ts` y la CLI de Go la implementan con el vector de §38.1, y la CLI abre una cápsula hecha en la página.
- Caso: la página `/create` de `datekeys-ts` y la CLI de Go la implementan, y la CLI abre una cápsula hecha en la página. Su primera versión no llevaba `capsule_id` en la sal: las mismas palabras daban la misma llave en todas las cápsulas de una ronda, y un diccionario las atacaba a la vez (revisión del borrador).
- Pruebas previstas: el vector y la normalización (§64).
7. **Nota pública y extensión de cápsula** (§24.1, §43, §44.1, §72).
- Cambio: `datekeys.note` en `PUBLIC_HEADER`, y `datekeys.capsule` en la `.dkk`, con la nota, la fecha y un localizador cifrado para la fecha.
- Cambio: `datekeys.note` en `PUBLIC_HEADER`, y `datekeys.capsule` en la `.dkk`, con la nota, la fecha y un localizador cifrado para la fecha que apunta a un sobre opaco.
- Motivo: una aplicación que lista cápsulas y llaves solo puede distinguirlas hoy por `capsule_id`, y una llave suelta no dice de qué cápsula es ni dónde está.
- Caso: una `.dkk` de la v0.10 lleva `capsule_id` y `capsule_digest`, nada legible para una persona.
- Pruebas previstas: la nota cambiada y la nota inválida (§64).
Ningún objeto de la v0.10 cambia de veredicto ni de código. Los vectores de la firma, del sello, de la nota y de la extensión de cápsula se añadirán a `testdata` con el paso 6 del plan de la firma.
- Caso: una `.dkk` de la v0.10 lleva `capsule_id` y `capsule_digest`, nada legible para una persona. Y en IPFS un `.dkc` es público: un rastreador indexaría `capsule_id`, la nota y la fecha, y quien tiene la `.dkk` encontraría la cápsula antes de la fecha; por eso se guarda un sobre.
- Pruebas previstas: la nota cambiada y la nota inválida, y los casos de `datekeys.capsule` (§64).
8. **Autoridades reconocidas** (§29.13).
- Cambio: una lista de autoridades fijada en el SDK, para los certificados de los firmantes y para los sellos.
- Motivo: sin ella, un certificado o un sello se comprueban con lo que traen ellos mismos.
- Caso: un certificado fabricado a nombre de «GARCÍA LÓPEZ MARÍA», emitido por «AC FNMT Usuarios», y un sello de una «TSA FNMT» también fabricada, darían F6 y «antes de la fecha» (revisión del borrador).
- Pruebas previstas: un certificado y un sello de autoridades no reconocidas, con F7 y S6 (§64).
Con un lector de la v0.11 cambian de veredicto cinco vectores de la v0.10 que usaban `alg` 1 y `seal_type` 2 como no soportados: en `security.json`, «a signature of alg 1» y «a signature and a seal» pasan de F1 a F2, y «a seal of seal_type 2», de S1 a S2; y los fixtures `format3_signature_unsupported` y `format3_seal_unsupported`, de F1 a F2. Los casos no soportados se rehacen con `alg` y `seal_type` 4294967295 (§29.3), y los actuales quedan como casos de F2 y S2. Ningún otro objeto cambia de veredicto ni de código. Los vectores de la firma, del sello, de la nota y de la extensión de cápsula, y los de `control_commit`, `signers_digest`, `AUTHOR_MESSAGE` y `SEAL_SUBJECT` sobre `format3_single`, se añadirán a `testdata` con el paso 6 del plan de la firma.
---
## 77. Referencias

@ -196,9 +196,13 @@ seal-rfc3161 = {
1 => bstr, ; TimeStampToken, DER
}
; The data rules below, of registered extensions, are checked only by an
; implementation that knows the extension, and a violation makes the
; extension unusable (spec sections 54 and 57): never ERR_NON_CANONICAL_CBOR.
; Spec section 24.1. data of extension datekeys.note, version 1, in the
; noncritical array of PUBLIC_HEADER: UTF-8 text with the rules of section
; 29.6, not CBOR.
; noncritical array of PUBLIC_HEADER: UTF-8 text with the rules of the
; declared author of section 29.6, one line, not CBOR.
note-data = bstr .size (1..1024)
; Spec section 44.1. data of extension datekeys.capsule, version 1, in the
@ -207,11 +211,17 @@ capsule-data = {
? 0 => tstr .size (1..1024), ; note, a copy of the public note
1 => tstr, ; compact_datekey, canonical dk1_
? 2 => bstr, ; locator: an age file with one tlock stanza
; for the round and chain of key 1
}
; Plaintext of the locator, readable at the unlock date.
; Plaintext of the locator, readable at the unlock date: exactly 4096
; bytes, or the least multiple of 4096 that holds it, with key 5.
capsule-locator = {
0 => [1*16 tstr .size (1..2048)], ; addresses (URI)
1 => bstr .size 32, ; capsule_digest
0 => [1*8 tstr .size (1..1024)], ; addresses: https or ipfs URI
1 => bstr .size 32, ; I_SOBRE, identity of the envelope
2 => bstr .size 32, ; SHA-256 of the envelope
3 => uint, ; size of the envelope in bytes
4 => bstr .size 32, ; capsule_digest
? 5 => bstr, ; zero padding
}
; Spec section 29.4. HEAD_CBOR of format 3, at most 16 MiB. Always version 1

Loading…
Cancel
Save

Powered by TurnKey Linux.