Spec v0.11 draft: the envelope can hide inside another file

As the author asks: the rest of the envelope can be stored inside a host
file, an image, a video or any file, and each address of the locator says
at which byte it starts (44.1).

- The envelope is split: its age header, up to the MAC, goes in the
  time-locked locator, and only the rest, nonce and STREAM, is stored
  outside. Those bytes carry no mark: nobody can tell they are an age
  file, let alone a capsule. The reader joins both and decrypts with
  I_SOBRE.
- An address is a map with the URI and an optional offset; the reader
  asks only for those bytes, with an HTTP range when it can, and checks
  their SHA-256.
- Concealment, not steganography: an analysis of the host can see extra
  bytes, not what they are. Only storage that keeps the file byte for
  byte works; social networks and messaging applications spoil a host.
- The CDDL of the locator follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.11
dev 6 days ago
parent 46ca90b447
commit 48b496fdf2

@ -2002,7 +2002,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. 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.
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. Con un localizador (§44.1), `capsule_digest` en claro no delata dónde está la cápsula: lo que se guarda fuera es el resto de un sobre cifrado, con otro hash.
--- ---
@ -2037,26 +2037,37 @@ La extensión `datekeys.capsule`, versión 1, registrada para el array no críti
- 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: - 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 ```text
0 → direcciones (array de 1 a 8 textos, de 1 a 1024 bytes cada uno) 0 → direcciones (array de 1 a 8 mapas de dirección)
1 → I_SOBRE (32 bytes: la identity X25519 cruda del sobre) 1 → I_SOBRE (32 bytes: la identity X25519 cruda del sobre)
2 → sobre_digest (32 bytes: el SHA-256 del sobre) 2 → cabecera del sobre (cadena de bytes de 1 a 1024)
3 → sobre_size (entero: la longitud del sobre en bytes) 3 → resto_digest (32 bytes: el SHA-256 del resto del sobre)
4 → capsule_digest (32 bytes, §43) 4 → resto_size (entero: la longitud del resto en bytes)
5 → relleno (cadena de bytes a cero, opcional) 5 → capsule_digest (32 bytes, §43)
6 → relleno (cadena de bytes a cero, opcional)
dirección:
0 → URI (texto de 1 a 1024 bytes)
1 → desplazamiento (entero, opcional: 0 si falta)
``` ```
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 plaintext mide exactamente 4096 bytes, o el menor múltiplo de 4096 en el que quepa: la clave 6 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. **El sobre.** Quien crea la cápsula cifra el `.dkc` con `age` para la pública de `I_SOBRE`, una identity nueva de un CSPRNG, y parte ese fichero `age` en dos: la cabecera, hasta el salto de línea de su MAC incluido, que va en el localizador; y el resto, el nonce y los chunks de STREAM, que es lo único que se guarda fuera. El resto no lleva ninguna marca: sus bytes no se distinguen del azar, así que quien lo encuentra no sabe que es un fichero `age` ni una cápsula. Nadie, tampoco quien tiene la llave, lee el localizador antes de la fecha. En la fecha, el mismo release que abre la cápsula lo descifra, y el lector une la cabecera y el resto y descifra el `.dkc` con `I_SOBRE`.
**Direcciones.** Cada una es un URI ASCII de RFC 3986, con el esquema `https` o `ipfs` (un CID v1), sin userinfo. Un lector: **Dentro de otro fichero.** El resto puede guardarse solo o dentro de otro fichero, el huésped: una imagen, un vídeo, un documento o un fichero de cualquier tipo, normalmente añadido al final, donde casi todos los programas lo ignoran. Su dirección dice entonces en qué byte del huésped empieza; su longitud es `resto_size`.
- Es ocultación, no esteganografía: quien analice el huésped puede ver que lleva bytes de más, pero no qué son ni de qué cápsula.
- Solo sirve un almacenamiento que conserva el fichero byte a byte, como un disco en la nube, IPFS o un servidor propio. Una red social o una aplicación de mensajería recomprimen las imágenes y los vídeos, o quitan lo que sobra, y el resto se pierde.
**Direcciones.** Cada URI es 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 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 pedir solo los bytes del resto, con un rango de HTTP si el servidor lo admite; si no, MUST dejar de leer al llegar al desplazamiento más `resto_size`;
- 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 NOT seguir una redirección a otro esquema ni a una dirección de loopback, privada o de enlace local;
- MUST comprobar que el SHA-256 del resto es `resto_digest`, descifrar el sobre 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`. - 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`. `resto_digest` y `capsule_digest` protegen frente a quien guarda el resto o el huésped, 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: Como toda extensión de la `.dkk`, es informativa: nada la ata a la cápsula (§44). Un lector:
@ -2065,8 +2076,9 @@ Como toda extensión de la `.dkk`, es informativa: nada la ata a la cápsula (§
Un escritor: Un escritor:
- 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); - MUST guardar fuera solo el resto del sobre, nunca el `.dkc` ni la cabecera del sobre, y conocer las direcciones antes de escribir la `.dkk`: primero guarda el resto, solo o en su huésped, 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. - con un huésped, SHOULD añadir el resto al final, y MUST comprobar antes de escribir la `.dkk` que el fichero que guarda tiene el resto en ese desplazamiento;
- 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; de que una red social o una aplicación de mensajería estropean un huésped; y de que una dirección que deja de existir pierde la cápsula para quien no tenga otra copia.
--- ---
@ -2375,7 +2387,7 @@ Ese supuesto no resiste a un adversario cuántico futuro (§7.7): quien conserve
### Fuera del `.dkc` ### 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); - 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. 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); - 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 el resto de un sobre cifrado, sin cabecera, solo o dentro de otro fichero: quien lo aloja ve bytes al azar, su tamaño y quién los sube o los 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); - 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). - 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).
@ -2741,7 +2753,7 @@ Con firma o sello, además, MUST:
Con nota pública, llave de palabras o extensión de cápsula, MUST: 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 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). 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). 24. **Llaves.** Cumplir las reglas del escritor de §38.1 para una llave de palabras. Para una `.dkk` con localizador, crear el sobre, guardar fuera su resto, solo o en un huésped, y escribir después la `.dkk` con sus direcciones; nunca guardar fuera el `.dkc` ni la cabecera del sobre (§44.1).
Con firma o sello, además, MUST: Con firma o sello, además, MUST:
@ -3159,7 +3171,7 @@ Firma, sello, nota y llave de palabras (v0.11), además:
- el mismo contenido con firma y sin ella, en el área común: la misma P; - 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 salto de línea o un carácter que §29.6 prohíbe: inutilizable y sin mostrarse; - 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 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). - la extensión `datekeys.capsule`: un localizador para otra ronda (inutilizable); una dirección `file:` o `http:` (no se descarga); un resto cuyo SHA-256 no es `resto_digest`, porque alguien recomprimió el huésped o el desplazamiento no es el suyo (no se usa); un recurso que sigue tras el resto, del que se lee solo hasta el desplazamiento más `resto_size`.
--- ---
## 65. Test vectors Quicknet ## 65. Test vectors Quicknet
@ -3632,7 +3644,8 @@ word key
public note public note
= datekeys.note en PUBLIC_HEADER, pública y atada en el paso 15; copia = 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 informativa en datekeys.capsule de la .dkk, con la fecha y un
localizador cifrado para la fecha, que apunta a un sobre opaco localizador cifrado para la fecha, que apunta al resto de un sobre
opaco, solo o dentro de otro fichero
recovery recovery
= puede obtener release directamente del provider = puede obtener release directamente del provider
@ -4013,7 +4026,7 @@ La v0.11 define la firma de autor y el sello de tiempo del área `security`, que
- 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). - 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). - 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). 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 que apunta a un sobre opaco. - 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 al resto de un sobre opaco, sin cabecera, solo o dentro de otro fichero.
- 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á. - 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. 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. - 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). - Pruebas previstas: la nota cambiada y la nota inválida, y los casos de `datekeys.capsule` (§64).

@ -214,14 +214,21 @@ capsule-data = {
; for the round and chain of key 1 ; for the round and chain of key 1
} }
; Plaintext of the locator, readable at the unlock date: exactly 4096 ; Plaintext of the locator, readable at the unlock date: exactly 4096
; bytes, or the least multiple of 4096 that holds it, with key 5. ; bytes, or the least multiple of 4096 that holds it, with key 6. The
; envelope is an age file of the .dkc for I_SOBRE: its header goes here, and
; only the rest, nonce and STREAM, is stored outside, alone or in a host file.
capsule-locator = { capsule-locator = {
0 => [1*8 tstr .size (1..1024)], ; addresses: https or ipfs URI 0 => [1*8 capsule-address], ; where the rest of the envelope is
1 => bstr .size 32, ; I_SOBRE, identity of the envelope 1 => bstr .size 32, ; I_SOBRE, identity of the envelope
2 => bstr .size 32, ; SHA-256 of the envelope 2 => bstr .size (1..1024), ; age header of the envelope, up to its MAC
3 => uint, ; size of the envelope in bytes 3 => bstr .size 32, ; SHA-256 of the rest
4 => bstr .size 32, ; capsule_digest 4 => uint, ; size of the rest in bytes
? 5 => bstr, ; zero padding 5 => bstr .size 32, ; capsule_digest
? 6 => bstr, ; zero padding
}
capsule-address = {
0 => tstr .size (1..1024), ; https or ipfs URI
? 1 => uint, ; offset of the rest in that resource; 0 if absent
} }
; Spec section 29.4. HEAD_CBOR of format 3, at most 16 MiB. Always version 1 ; Spec section 29.4. HEAD_CBOR of format 3, at most 16 MiB. Always version 1

Loading…
Cancel
Save

Powered by TurnKey Linux.