Spec v0.8.2: opaque extension data

Extension data becomes a non-empty opaque bstr bounded by the frame of its
container; the base protocol never decodes or validates it. Arrays hold at
most 64 extensions, extension_version is bounded to 2^32-1 and the profile's
period and genesis_time to 2^53-1. The multiplicity exception is removed,
ERR_EXTENSION_DATA_INVALID is added, the §57 limits become MUST with an
explicit error mapping, §58 names the protocol's CBOR profile and §72 sets
the registration rules. §76 records the six reproducible cases behind the
change. The implementation follows in the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.8.2
dev 2 weeks ago
parent 5719f6a4b3
commit ace734462f

@ -1,6 +1,6 @@
# DateKeys Protocol Specification # DateKeys Protocol Specification
## DateKey, DateKeyCap (`.dkc`) y DateKeys Access Key (`.dkk`) ## DateKey, DateKeyCap (`.dkc`) y DateKeys Access Key (`.dkk`)
### Borrador normativo v0.8.1 ### Borrador normativo v0.8.2
**Estado:** Draft / pre-estándar **Estado:** Draft / pre-estándar
**Fecha:** 25 septiembre 2026 **Fecha:** 25 septiembre 2026
@ -251,7 +251,7 @@ Cualquier cambio criptográficamente relevante exige otro perfil.
## 11. Codificación canónica del Provider Profile ## 11. Codificación canónica del Provider Profile
V1 usa **Deterministic CBOR** conforme a RFC 8949. V1 usa **Deterministic CBOR** conforme a RFC 8949, con el perfil CBOR del protocolo (§58).
Quicknet se serializa como mapa CBOR con claves enteras: Quicknet se serializa como mapa CBOR con claves enteras:
@ -269,6 +269,11 @@ Quicknet se serializa como mapa CBOR con claves enteras:
10 → <genesis seed bytes> 10 → <genesis seed bytes>
``` ```
Rangos de los enteros del perfil:
- `period` (clave 7) MUST estar entre 1 y 2⁵³ − 1 (9007199254740991);
- `genesis_time` (clave 8) MUST estar entre 0 y 2⁵³ − 1.
El hash: El hash:
```text ```text
@ -574,6 +579,10 @@ Schema:
6 → noncritical_extensions 6 → noncritical_extensions
``` ```
Las claves 5 y 6 son opcionales y se omiten cuando no hay extensiones (§58.1). Cada una, cuando existe, contiene entre 1 y 64 extensiones (§54).
La `data` de las extensiones de `PUBLIC_HEADER` es pública y el protocolo base no la vincula al control hasta que se verifica `header_binding` (§63, paso 15). Esa verificación aporta coherencia interna, no autoría (§36.1, §72).
No se almacena `profile_id` por separado. No se almacena `profile_id` por separado.
El perfil se obtiene de la DateKey, evitando dos fuentes de verdad. El perfil se obtiene de la DateKey, evitando dos fuentes de verdad.
@ -760,6 +769,8 @@ Schema base:
5 → noncritical_extensions 5 → noncritical_extensions
``` ```
Las claves 4 y 5 son opcionales y se omiten cuando no hay extensiones (§58.1).
No contiene el payload grande. No contiene el payload grande.
DateKeys V1 utiliza **un único mecanismo de extensión**. No existe un campo core separado para aplicaciones o semánticas superiores. Cualquier semántica adicional se registra como una extensión identificada por namespace. DateKeys V1 utiliza **un único mecanismo de extensión**. No existe un campo core separado para aplicaciones o semánticas superiores. Cualquier semántica adicional se registra como una extensión identificada por namespace.
@ -769,21 +780,25 @@ Una extensión usa conceptualmente:
```text ```text
0 → extension_id 0 → extension_id
1 → extension_version 1 → extension_version
2 → data 2 → data (opcional)
``` ```
Reglas: Reglas:
- `extension_id` MUST ser UTF-8 válido; - `extension_id` MUST ser UTF-8 válido;
- `extension_version` MUST ser un entero sin signo entre 0 y 2³² − 1;
- `(extension_id, extension_version)` identifica el schema; - `(extension_id, extension_version)` identifica el schema;
- `data`, cuando existe, MUST ser una cadena de bytes (`bstr`) no vacía, opaca para el protocolo base (§54);
- una extensión sin datos MUST omitir la clave 2;
- cada array de extensiones MUST contener entre 1 y 64 extensiones;
- un mismo `extension_id` MUST NOT aparecer simultáneamente en `critical_extensions` y `noncritical_extensions` dentro del mismo objeto; - un mismo `extension_id` MUST NOT aparecer simultáneamente en `critical_extensions` y `noncritical_extensions` dentro del mismo objeto;
- salvo que el schema registrado permita multiplicidad, un mismo `extension_id` MUST NOT aparecer más de una vez dentro del mismo objeto, aunque cambie `extension_version`; - un mismo `extension_id` MUST NOT aparecer más de una vez dentro del mismo objeto, aunque cambie `extension_version`; si un schema necesita varios valores, los lleva dentro de su `data`;
- el orden del array NO tiene semántica; - el orden del array NO tiene semántica;
- el encoder canónico MUST ordenar por bytes UTF-8 de `extension_id` y después por versión; - el encoder canónico MUST ordenar por bytes UTF-8 de `extension_id`;
- una extensión crítica desconocida MUST provocar rechazo; - una extensión crítica desconocida MUST provocar rechazo;
- una extensión no crítica desconocida MAY ignorarse. - una extensión no crítica desconocida MAY ignorarse.
El protocolo base no interpreta `data`. El protocolo base no decodifica ni valida el contenido de `data` (§54).
--- ---
@ -1024,6 +1039,8 @@ Schema:
8 → noncritical_extensions 8 → noncritical_extensions
``` ```
Las claves 6, 7 y 8 son opcionales y se omiten cuando están ausentes (§43, §58.1). Las claves 7 y 8, cuando existen, contienen entre 1 y 64 extensiones (§54).
Para V1 portable X25519: Para V1 portable X25519:
```text ```text
@ -1091,6 +1108,8 @@ salvo que una futura especificación DateKeys registre una extensión crítica c
De este modo las implementaciones no inventan campos core incompatibles. De este modo las implementaciones no inventan campos core incompatibles.
La `data` de esas extensiones viaja en claro junto a la credencial y `header_binding` no la cubre: el protocolo base no la autentica (§72).
--- ---
## 45. Release API ## 45. Release API
@ -1244,7 +1263,7 @@ Cada extensión declara:
```text ```text
0 → extension_id 0 → extension_id
1 → extension_version 1 → extension_version
2 → data 2 → data (opcional)
``` ```
El par: El par:
@ -1253,7 +1272,7 @@ El par:
(extension_id, extension_version) (extension_id, extension_version)
``` ```
identifica el schema de la extensión. identifica el schema de la extensión. `extension_version` es un entero sin signo entre 0 y 2³² − 1 (4294967295).
La posición del array determina si la extensión es: La posición del array determina si la extensión es:
@ -1269,10 +1288,26 @@ noncritical
Reglas: Reglas:
- una extensión crítica desconocida → MUST reject; - una extensión crítica desconocida → MUST reject (`ERR_EXTENSION_CRITICAL_UNKNOWN`);
- una extensión no crítica desconocida → MAY ignore; - una extensión no crítica desconocida → MAY ignore;
- un mismo `extension_id` MUST NOT aparecer simultáneamente en `critical_extensions` y `noncritical_extensions` dentro del mismo objeto; - un mismo `extension_id` MUST NOT aparecer simultáneamente en `critical_extensions` y `noncritical_extensions` dentro del mismo objeto;
- salvo que un schema registrado permita expresamente multiplicidad, un mismo `extension_id` MUST NOT aparecer más de una vez en el mismo objeto, aunque cambie `extension_version`. - un mismo `extension_id` MUST NOT aparecer más de una vez en el mismo objeto, aunque cambie `extension_version`; la multiplicidad, si un schema la necesita, va dentro de su `data`;
- cada array de extensiones contiene entre 1 y 64 extensiones: un encoder MUST NOT producir más de 64 y un decoder MUST rechazar un array de más de 64.
`data` (clave 2):
- MUST ser una cadena de bytes CBOR (`bstr`, tipo mayor 2) de al menos 1 byte;
- una extensión sin datos MUST omitir la clave 2; `h''` (`0x40`) es inválido;
- cualquier otro tipo CBOR en la clave 2 es inválido;
- su longitud está acotada por la trama del objeto que la contiene (§57);
- su contenido es opaco: el protocolo base nunca lo decodifica ni lo valida, y la validez base de `PUBLIC_HEADER`, `CONTROL_CBOR` y `.dkk` nunca depende de él.
Una `data` vacía o de tipo distinto de `bstr` y un array de más de 64 extensiones son violaciones del CDDL: MUST rechazarse con `ERR_NON_CANONICAL_CBOR` (§57).
Solo una implementación que conoce `(extension_id, extension_version)` interpreta su `data`, conforme a su schema registrado (§72):
- una extensión crítica conocida cuya `data` no cumple su schema registrado → MUST reject (`ERR_EXTENSION_DATA_INVALID`);
- una extensión no crítica conocida cuya `data` no cumple su schema registrado no invalida el objeto: la implementación MUST tratarla como inutilizable, sin usar su `data`, y MUST notificarlo al llamador.
La información específica de una aplicación que no pertenezca al núcleo DateKeys —incluidos datos de transporte o descubrimiento si una aplicación los necesita— MUST ir en `noncritical_extensions` y no en campos core del protocolo. La información específica de una aplicación que no pertenezca al núcleo DateKeys —incluidos datos de transporte o descubrimiento si una aplicación los necesita— MUST ir en `noncritical_extensions` y no en campos core del protocolo.
@ -1312,7 +1347,7 @@ No debe presentar plaintext parcial como válido si falla cualquier autenticaci
## 57. Límites del parser ## 57. Límites del parser
V1 recomienda: Encoders y decoders MUST aplicar estos límites de trama:
```text ```text
PUBLIC_HEADER <= 1 MiB PUBLIC_HEADER <= 1 MiB
@ -1320,20 +1355,54 @@ SEALED_CONTROL <= 64 MiB
DKK BODY <= 16 MiB DKK BODY <= 16 MiB
``` ```
- un encoder MUST NOT producir un objeto que los supere;
- un decoder MUST rechazar un objeto que los supere;
- cada trama acota todo lo que contiene, incluida la `data` de las extensiones (§54); `CONTROL_CBOR` queda acotado por `SEALED_CONTROL`.
`PAYLOAD_AGE` se procesa en streaming. `PAYLOAD_AGE` se procesa en streaming.
Los tamaños se validan antes de reservar memoria. Los tamaños se validan antes de reservar memoria.
Correspondencia de errores:
- una longitud de trama (`PUBLIC_HEADER_LEN`, `SEALED_CONTROL_LEN`, `BODY_LEN`) fuera de estos límites, 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 tamaño y de rango y el máximo de 64 extensiones por array → `ERR_NON_CANONICAL_CBOR`, salvo los casos con código propio: versión de schema (clave 1) no soportada → `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` (§13);
- 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.
--- ---
## 58. Canonical CBOR ## 58. Canonical CBOR
Todas las estructuras CBOR del protocolo MUST: Todas las estructuras CBOR del protocolo MUST:
- usar Deterministic CBOR; - usar Deterministic CBOR con el perfil CBOR del protocolo definido en esta sección;
- rechazar siempre codificaciones no canónicas; - rechazar siempre codificaciones no canónicas;
- usar enteros de clave según los schemas normativos. - usar enteros de clave según los schemas normativos.
Perfil CBOR del protocolo: Deterministic CBOR (RFC 8949 §4.2.1) restringido a:
- tipos mayores 0 (entero sin signo), 2 (cadena de bytes), 3 (cadena de texto), 4 (array) y 5 (mapa);
- claves de mapa que son enteros sin signo;
- longitudes definidas;
- enteros y longitudes en su forma más corta;
- claves de mapa en orden ascendente estricto, sin duplicados;
- mapas cerrados: una clave no prevista por el schema MUST rechazarse;
- cadenas de texto en UTF-8 válido.
Con claves enteras sin signo en su forma más corta, el orden por bytes de RFC 8949 §4.2.1 coincide con el orden numérico.
Se rechazan expresamente, con `ERR_NON_CANONICAL_CBOR`:
- enteros negativos (tipo mayor 1);
- tags (tipo mayor 6);
- flotantes y valores simples, incluidos `false`, `true`, `null` y `undefined` (tipo mayor 7);
- longitudes indefinidas;
- claves de mapa que no son enteros sin signo.
Los schemas del protocolo acotan todo entero sin signo a 2⁵³ − 1 (9007199254740991) como máximo (§11, §54), de modo que cada uno se representa exactamente como double IEEE 754.
En la `data` de una extensión, el perfil se aplica a la cabecera de la cadena de bytes (tipo mayor 2 y longitud en su forma más corta), nunca a su contenido (§54). Las extensiones registradas cuya `data` sea CBOR siguen además §72.
--- ---
## 58.1 Regla global para campos opcionales ## 58.1 Regla global para campos opcionales
@ -1349,16 +1418,18 @@ No se debe representar ausencia mediante:
[] []
null null
"" ""
h''
``` ```
salvo que el schema de ese campo defina expresamente uno de esos valores como dato real. salvo que el schema de ese campo defina expresamente uno de esos valores como dato real. `null` no puede serlo nunca: el perfil de §58 excluye los valores simples.
Por tanto: Por tanto:
- metadata opcional ausente → clave omitida; - metadata opcional ausente → clave omitida;
- arrays opcionales sin elementos → clave omitida; - arrays opcionales sin elementos → clave omitida;
- mapas opcionales sin entradas → clave omitida; - mapas opcionales sin entradas → clave omitida;
- listas de extensiones vacías → clave omitida. - listas de extensiones vacías → clave omitida;
- extensión sin `data` → clave 2 omitida; `h''` es inválido (§54).
Esto reduce representaciones equivalentes y simplifica vectores canónicos. Esto reduce representaciones equivalentes y simplifica vectores canónicos.
@ -1491,6 +1562,9 @@ y MUST ser independientes.
3. Leer PUBLIC_HEADER exacto. 3. Leer PUBLIC_HEADER exacto.
4. Validar CBOR canónico, DateKey canónica y Provider Profile pinneado. 4. Validar CBOR canónico, DateKey canónica y Provider Profile pinneado.
Validar las extensiones críticas de PUBLIC_HEADER (§54):
desconocida → ERR_EXTENSION_CRITICAL_UNKNOWN;
conocida con data inválida → ERR_EXTENSION_DATA_INVALID.
5. SHOULD: inspeccionar OUTER_TIME_AGE antes de usar red o secretos: 5. SHOULD: inspeccionar OUTER_TIME_AGE antes de usar red o secretos:
exactamente un stanza; exactamente un stanza;
@ -1532,8 +1606,12 @@ y MUST ser independientes.
después, abrir INNER_ACCESS_AGE con la identity adecuada. después, abrir INNER_ACCESS_AGE con la identity adecuada.
14. Parsear CONTROL_CBOR canónico. 14. Parsear CONTROL_CBOR canónico.
Validar sus extensiones críticas como en el paso 4.
15. Verificar header_binding. 15. Verificar 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).
16. Recuperar I_PAYLOAD. 16. Recuperar I_PAYLOAD.
@ -1584,6 +1662,9 @@ stanza adicional en PAYLOAD_AGE
stanza de tipo distinto de X25519 en INNER_ACCESS_AGE stanza de tipo distinto de X25519 en INNER_ACCESS_AGE
ronda del stanza tlock distinta de DateKey.round ronda del stanza tlock distinta de DateKey.round
chain hash del stanza tlock distinto del Provider Profile pinneado chain hash del stanza tlock distinto del Provider Profile pinneado
data de extensión de tipo distinto de bstr
data de extensión vacía (h'')
65 extensiones en un mismo array
``` ```
--- ---
@ -1664,9 +1745,11 @@ Cada vector incluirá:
- `access_type` esperado; - `access_type` esperado;
- 32 bytes crudos esperados de X25519 identity; - 32 bytes crudos esperados de X25519 identity;
- `verification_metadata` esperado cuando exista; - `verification_metadata` esperado cuando exista;
- extensiones esperadas; - extensiones esperadas, con los bytes exactos de su `data`;
- resultado esperado al utilizar la identity contra el `INNER_ACCESS_AGE` asociado. - resultado esperado al utilizar la identity contra el `INNER_ACCESS_AGE` asociado.
Al menos un vector oficial `.dkk` MUST incluir una extensión con `data`.
No se exige reproducir los bytes de una `.dkk` partiendo de una generación aleatoria nueva. No se exige reproducir los bytes de una `.dkk` partiendo de una generación aleatoria nueva.
--- ---
@ -1691,6 +1774,7 @@ ERR_POLICY_STRUCTURE_MISMATCH
ERR_HEADER_BINDING ERR_HEADER_BINDING
ERR_INTEGRITY ERR_INTEGRITY
ERR_EXTENSION_CRITICAL_UNKNOWN ERR_EXTENSION_CRITICAL_UNKNOWN
ERR_EXTENSION_DATA_INVALID
``` ```
--- ---
@ -1727,9 +1811,26 @@ DateKeys MAY publicar un registro de `extension_id`.
Registrar una extensión no cambia el núcleo del protocolo. Registrar una extensión no cambia el núcleo del protocolo.
Cada extensión registrada, identificada por `(extension_id, extension_version)`, MUST declarar:
- la codificación de su `data`, o que no lleva `data`;
- su forma canónica;
- su longitud máxima, que no puede superar la trama del objeto que la contiene (§57);
- sus vectores de prueba.
Si la codificación de `data` es CBOR:
- MUST usar el perfil de §58 de forma recursiva, con enteros sin signo de como mucho 2⁵³ − 1, arrays y mapas no vacíos y sin `null`;
- los lectores que implementan la extensión MUST rechazar una codificación interna no canónica o fuera de ese perfil como `data` inválida (§54);
- su encoder MUST decodificar su propia salida, con las reglas de esos lectores, antes de sellar o escribir el objeto.
Una extensión de firma que cubra la `data` de otras extensiones MUST firmar los bytes exactos del contenido de esos `bstr`, nunca una reserialización de su contenido decodificado.
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 (§36.1): solo una extensión de firma puede aportar autenticidad del creador (§27). La de `.dkk` viaja en claro y `header_binding` no la cubre, así que el protocolo base no la autentica en ningún paso.
--- ---
## 73. Decisiones canónicas v0.8.1 ## 73. Decisiones canónicas v0.8.2
```text ```text
DateKeys DateKeys
@ -1786,6 +1887,18 @@ portable .dkk
extensions extensions
= único mecanismo genérico de extensibilidad en PUBLIC_HEADER, CONTROL_CBOR y .dkk = único mecanismo genérico de extensibilidad en PUBLIC_HEADER, CONTROL_CBOR y .dkk
extension arrays
= 1 a 64 extensiones; extension_id único en cada objeto
extension data
= bstr no vacío y opaco; el protocolo base nunca lo decodifica
CBOR profile
= RFC 8949 §4.2.1 con tipos mayores 0, 2, 3, 4 y 5 y claves enteras sin signo
parser limits
= MUST para encoders y decoders
recovery recovery
= puede obtener release directamente del provider = puede obtener release directamente del provider
@ -1802,12 +1915,11 @@ Antes de v1.0 quedan por cerrar:
- schema CBOR final byte-a-byte de `PUBLIC_HEADER`; - schema CBOR final byte-a-byte de `PUBLIC_HEADER`;
- schema CBOR final byte-a-byte de `CONTROL_CBOR`; - schema CBOR final byte-a-byte de `CONTROL_CBOR`;
- schema CBOR final byte-a-byte de `.dkk`; - schema CBOR final byte-a-byte de `.dkk`;
- formato exacto de extensiones;
- límites definitivos de campos; - límites definitivos de campos;
- vectores definitivos de Provider Profile; - vectores definitivos de Provider Profile;
- suite exacta de tests interoperables. - suite exacta de tests interoperables.
El framing base, la ausencia de `PAYLOAD_LEN`, el uso de age files estándar y la identity X25519 cruda de `.dkk` dejan de considerarse provisionales en este borrador. 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.
--- ---
@ -1845,6 +1957,31 @@ Nuevas ideas, preferencias editoriales o posibilidades futuras que no estén res
Esta política no impide correcciones editoriales que no alteren la semántica normativa. Esta política no impide correcciones editoriales que no alteren la semántica normativa.
### Cambio normativo v0.8.2: extensiones
La v0.8.2 cierra el formato de las extensiones (§74) en un único cambio normativo:
- `data` (clave 2) pasa de `any` a `bstr` no vacío y opaco, sin más límite de longitud que la trama de su contenedor; el protocolo base nunca decodifica ni valida su contenido (§31, §54, §57, §58.1);
- cada array admite como máximo 64 extensiones (§31, §54);
- `extension_version` queda acotado a 2³² − 1, y `period` y `genesis_time` a 2⁵³ − 1 (§11, §54, §58);
- se elimina la excepción de multiplicidad: un `extension_id` aparece una sola vez por objeto (§31, §54);
- nuevo error `ERR_EXTENSION_DATA_INVALID` para una extensión crítica conocida con `data` inválida (§54, §63, §69);
- los límites de §57 pasan de recomendación a MUST para encoders y decoders, con su correspondencia de errores;
- §58 nombra el perfil CBOR del protocolo;
- §72 fija las reglas de registro de extensiones;
- nuevas mutaciones (§64) y un vector `.dkk` con extensión (§68).
Casos reproducibles que lo justifican, obtenidos con la implementación de referencia, sus fixtures y `datekeys.cddl` de la v0.8.1:
1. El CDDL se contradecía: sus líneas 12 a 14 establecen que `null` y los valores vacíos nunca representan ausencia, y su línea 82 declaraba `? 2 => any`.
2. `extension.New(id, v, nil)` emitía `f6` (`null`) como `data`.
3. La misma cápsula de 810 bytes, con `data` de cabecera `{NaN:0, NaN:1}` (`a2f97e0000f97e0001`), se aceptaba de forma no determinista: `Inspect` la aceptó 437 veces de 500 y `Open` 82 de 100.
4. El fixture oficial `time_only_extensions` usaba claves de texto y `true` en `data`.
5. `Encrypt` sellaba una `data` de `CONTROL_CBOR` con 14 arrays anidados que `Open` rechazaba en el paso 14, ya desbloqueada (`exceeded max nested level 16`): la cápsula quedaba irrecuperable.
6. Sin máximo de extensiones, una `PUBLIC_HEADER` de 880 KB con 40 000 + 40 000 extensiones hacía que `DecodeHeader` tardara 8,3 s, por una comprobación de disjunción cuadrática.
Las versiones de framing y de schema (clave 1) no cambian. Un objeto v0.8.1 deja de ser válido si la `data` de una extensión no es un `bstr` no vacío, si un array supera 64 extensiones, si repite un `extension_id` al amparo de la antigua excepción de multiplicidad o si un `extension_version` supera 2³² − 1; también un Provider Profile con `period` o `genesis_time` por encima de 2⁵³ − 1. El fixture `time_only_extensions` se regenera.
--- ---
## 77. Referencias ## 77. Referencias

@ -1,8 +1,10 @@
# Specification # Specification
- `DateKeys_Protocol_Specification_v0.8.1.md`: frozen copy of the normative - `DateKeys_Protocol_Specification_v0.8.2.md`: frozen copy of the normative
draft v0.8.1 (25 September 2026) implemented by this module. SHA-256: draft v0.8.2 (25 September 2026) implemented by this module. SHA-256:
`8beee534efecf19dcdee765f7d198b3ce4da12c799ada525e64878dc263f6fad`. `8103a1893617c46a6c42ffb2a310772c17b6c0f077507ec0d88cb7842cbfb8ac`.
v0.8.2 replaces v0.8.1 with one normative change to extensions, recorded
with its justification in the specification's §76.
- `datekeys.cddl`: the CBOR schemas of the specification as implemented, with - `datekeys.cddl`: the CBOR schemas of the specification as implemented, with
the encoding rules CDDL cannot express. the encoding rules CDDL cannot express.

@ -1,22 +1,31 @@
; DateKeys Protocol Specification v0.8.1 - CBOR schemas (RFC 8610 CDDL). ; DateKeys Protocol Specification v0.8.2 - CBOR schemas (RFC 8610 CDDL).
; ;
; Normative companion of spec/DateKeys_Protocol_Specification_v0.8.1.md, as ; Normative companion of spec/DateKeys_Protocol_Specification_v0.8.2.md, as
; implemented by the reference implementation g.activething.com/go/DateKeys. ; implemented by the reference implementation g.activething.com/go/DateKeys.
; ;
; Encoding rules that CDDL cannot express (spec section 58, 58.1): ; Encoding rules that CDDL cannot express (spec section 58, 58.1):
; - Every structure is Deterministic CBOR (RFC 8949 section 4.2.1): map keys ; - Every structure uses the CBOR profile of the protocol (spec section 58):
; sorted by their encoded bytes, shortest-form integers and lengths, ; Deterministic CBOR (RFC 8949 section 4.2.1) restricted to major types 0,
; definite lengths only, no tags. ; 2, 3, 4 and 5, with unsigned integer map keys in strictly ascending
; order, shortest-form integers and lengths, definite lengths only and
; valid UTF-8 text. Negative integers, tags, floats, simple values
; (including null) and indefinite lengths are rejected.
; - A decoder re-encodes what it decoded and rejects any byte difference ; - A decoder re-encodes what it decoded and rejects any byte difference
; (ERR_NON_CANONICAL_CBOR). ; (ERR_NON_CANONICAL_CBOR).
; - A semantically absent optional field is omitted. Empty arrays, empty ; - A semantically absent optional field is omitted. Empty arrays, empty
; maps, null and "" never stand for absence; the .size and non-empty ; maps, null, "" and h'' never stand for absence; the .size and non-empty
; constraints below make those forms invalid. ; constraints below make those forms invalid.
; - Maps are closed: keys not listed here are rejected. New semantics go in ; - Maps are closed: keys not listed here are rejected. New semantics go in
; extensions (spec section 54). ; extensions (spec section 54).
; - Within one object an extension_id appears at most once and never in both ; - Within one object an extension_id appears at most once and never in both
; extension arrays; arrays are sorted by the UTF-8 bytes of extension_id and ; extension arrays; arrays are sorted by the UTF-8 bytes of extension_id.
; then by extension_version. ; - A violation of a normative rule of this schema, including .size,
; integer ranges and the 64-extension maximum, is ERR_NON_CANONICAL_CBOR
; unless spec section 57 names a more specific error (schema version,
; compact_datekey, access_type and access_material, Provider Profile
; names and public key). The implementation limits marked below are not
; normative (spec section 74); an implementation that applies them uses
; the same mapping.
; Spec section 11 and 12. ; Spec section 11 and 12.
provider-profile = { provider-profile = {
@ -26,9 +35,9 @@ provider-profile = {
3 => name, ; provider, "drand" 3 => name, ; provider, "drand"
4 => name, ; provider network identifier, "quicknet" 4 => name, ; provider network identifier, "quicknet"
5 => bstr .size 32, ; chain_hash 5 => bstr .size 32, ; chain_hash
6 => bstr, ; group public key 6 => public-key, ; group public key
7 => uint .ge 1, ; period in seconds 7 => 1..max-safe-uint, ; period in seconds
8 => uint, ; genesis_time, Unix seconds 8 => 0..max-safe-uint, ; genesis_time, Unix seconds
9 => name, ; scheme, "bls-unchained-g1-rfc9380" 9 => name, ; scheme, "bls-unchained-g1-rfc9380"
10 => bstr .size 32, ; genesis_seed 10 => bstr .size 32, ; genesis_seed
} }
@ -75,22 +84,34 @@ verification-metadata = {
} }
; Spec section 31 and 54. ; Spec section 31 and 54.
extensions = [+ extension] extensions = [1*64 extension]
extension = { extension = {
0 => extension-id, 0 => extension-id,
1 => uint, ; extension_version 1 => extension-version,
? 2 => any, ; data, not interpreted by the base protocol ? 2 => extension-data,
} }
extension-version = uint .le 4294967295
; Opaque bytes: the base protocol never decodes or validates the content, and
; an extension without data omits key 2. The effective bound is the frame of
; the containing object (spec section 57); 67108864 bytes (64 MiB) is the
; largest frame, SEALED_CONTROL. Each registered extension declares its own
; maximum (spec section 72).
extension-data = bstr .size (1..67108864)
capsule-id = bstr .size 16 capsule-id = bstr .size 16
access-policy = &(time_only: 0, time_and_key: 1) access-policy = &(time_only: 0, time_and_key: 1)
access-type = "x25519" access-type = "x25519"
access-material = bstr .size 32 ; for access-type "x25519" access-material = bstr .size 32 ; for access-type "x25519"
; 2^53-1: every unsigned integer of the protocol is exact as an IEEE 754
; double (spec section 58).
max-safe-uint = 9007199254740991
; Implementation limits of the reference implementation (spec section 74 ; Implementation limits of the reference implementation (spec section 74
; leaves definitive field limits open). ; leaves definitive field limits open).
profile-id = tstr .regexp "[a-z0-9][a-z0-9:._-]{0,127}" profile-id = tstr .regexp "[a-z0-9][a-z0-9:._-]{0,127}"
name = tstr .regexp "[a-z0-9][a-z0-9._-]{0,63}" name = tstr .regexp "[a-z0-9][a-z0-9._-]{0,63}"
public-key = bstr .size (1..1024)
extension-id = tstr .size (1..256) extension-id = tstr .size (1..256)
; Spec section 18 and 19: "dk1_" + unpadded Base64URL of the canonical JSON ; Spec section 18 and 19: "dk1_" + unpadded Base64URL of the canonical JSON

Loading…
Cancel
Save

Powered by TurnKey Linux.