diff --git a/spec/DateKeys_Protocol_Specification_v0.8.1.md b/spec/DateKeys_Protocol_Specification_v0.8.2.md similarity index 78% rename from spec/DateKeys_Protocol_Specification_v0.8.1.md rename to spec/DateKeys_Protocol_Specification_v0.8.2.md index 83e483d..c8f201d 100644 --- a/spec/DateKeys_Protocol_Specification_v0.8.1.md +++ b/spec/DateKeys_Protocol_Specification_v0.8.2.md @@ -1,6 +1,6 @@ # DateKeys Protocol Specification ## DateKey, DateKeyCap (`.dkc`) y DateKeys Access Key (`.dkk`) -### Borrador normativo v0.8.1 +### Borrador normativo v0.8.2 **Estado:** Draft / pre-estándar **Fecha:** 25 septiembre 2026 @@ -251,7 +251,7 @@ Cualquier cambio criptográficamente relevante exige otro perfil. ## 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: @@ -269,6 +269,11 @@ Quicknet se serializa como mapa CBOR con claves enteras: 10 → ``` +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: ```text @@ -574,6 +579,10 @@ Schema: 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. El perfil se obtiene de la DateKey, evitando dos fuentes de verdad. @@ -760,6 +769,8 @@ Schema base: 5 → noncritical_extensions ``` +Las claves 4 y 5 son opcionales y se omiten cuando no hay extensiones (§58.1). + 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. @@ -769,21 +780,25 @@ Una extensión usa conceptualmente: ```text 0 → extension_id 1 → extension_version -2 → data +2 → data (opcional) ``` Reglas: - `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; +- `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; -- 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 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 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 ``` +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: ```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. +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 @@ -1244,7 +1263,7 @@ Cada extensión declara: ```text 0 → extension_id 1 → extension_version -2 → data +2 → data (opcional) ``` El par: @@ -1253,7 +1272,7 @@ El par: (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: @@ -1269,10 +1288,26 @@ noncritical 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; - 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. @@ -1312,7 +1347,7 @@ No debe presentar plaintext parcial como válido si falla cualquier autenticaci ## 57. Límites del parser -V1 recomienda: +Encoders y decoders MUST aplicar estos límites de trama: ```text PUBLIC_HEADER <= 1 MiB @@ -1320,20 +1355,54 @@ SEALED_CONTROL <= 64 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. 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 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; - 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 @@ -1349,16 +1418,18 @@ No se debe representar ausencia mediante: [] 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: - metadata opcional ausente → clave omitida; - arrays opcionales sin elementos → 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. @@ -1491,6 +1562,9 @@ y MUST ser independientes. 3. Leer PUBLIC_HEADER exacto. 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: exactamente un stanza; @@ -1532,8 +1606,12 @@ y MUST ser independientes. después, abrir INNER_ACCESS_AGE con la identity adecuada. 14. Parsear CONTROL_CBOR canónico. + Validar sus extensiones críticas como en el paso 4. 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. @@ -1584,6 +1662,9 @@ stanza adicional en PAYLOAD_AGE stanza de tipo distinto de X25519 en INNER_ACCESS_AGE ronda del stanza tlock distinta de DateKey.round 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; - 32 bytes crudos esperados de X25519 identity; - `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. +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. --- @@ -1691,6 +1774,7 @@ ERR_POLICY_STRUCTURE_MISMATCH ERR_HEADER_BINDING ERR_INTEGRITY 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. +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 DateKeys @@ -1786,6 +1887,18 @@ portable .dkk extensions = ú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 = 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 `CONTROL_CBOR`; - schema CBOR final byte-a-byte de `.dkk`; -- formato exacto de extensiones; - límites definitivos de campos; - vectores definitivos de Provider Profile; - 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. +### 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 diff --git a/spec/README.md b/spec/README.md index bc8b2c5..117cd92 100644 --- a/spec/README.md +++ b/spec/README.md @@ -1,8 +1,10 @@ # Specification -- `DateKeys_Protocol_Specification_v0.8.1.md`: frozen copy of the normative - draft v0.8.1 (25 September 2026) implemented by this module. SHA-256: - `8beee534efecf19dcdee765f7d198b3ce4da12c799ada525e64878dc263f6fad`. +- `DateKeys_Protocol_Specification_v0.8.2.md`: frozen copy of the normative + draft v0.8.2 (25 September 2026) implemented by this module. SHA-256: + `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 the encoding rules CDDL cannot express. diff --git a/spec/datekeys.cddl b/spec/datekeys.cddl index 119f4f4..ef5b4a9 100644 --- a/spec/datekeys.cddl +++ b/spec/datekeys.cddl @@ -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. ; ; Encoding rules that CDDL cannot express (spec section 58, 58.1): -; - Every structure is Deterministic CBOR (RFC 8949 section 4.2.1): map keys -; sorted by their encoded bytes, shortest-form integers and lengths, -; definite lengths only, no tags. +; - Every structure uses the CBOR profile of the protocol (spec section 58): +; Deterministic CBOR (RFC 8949 section 4.2.1) restricted to major types 0, +; 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 ; (ERR_NON_CANONICAL_CBOR). ; - 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. ; - Maps are closed: keys not listed here are rejected. New semantics go in ; extensions (spec section 54). ; - 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 -; then by extension_version. +; extension arrays; arrays are sorted by the UTF-8 bytes of extension_id. +; - 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. provider-profile = { @@ -26,9 +35,9 @@ provider-profile = { 3 => name, ; provider, "drand" 4 => name, ; provider network identifier, "quicknet" 5 => bstr .size 32, ; chain_hash - 6 => bstr, ; group public key - 7 => uint .ge 1, ; period in seconds - 8 => uint, ; genesis_time, Unix seconds + 6 => public-key, ; group public key + 7 => 1..max-safe-uint, ; period in seconds + 8 => 0..max-safe-uint, ; genesis_time, Unix seconds 9 => name, ; scheme, "bls-unchained-g1-rfc9380" 10 => bstr .size 32, ; genesis_seed } @@ -75,22 +84,34 @@ verification-metadata = { } ; Spec section 31 and 54. -extensions = [+ extension] +extensions = [1*64 extension] extension = { 0 => extension-id, - 1 => uint, ; extension_version - ? 2 => any, ; data, not interpreted by the base protocol + 1 => extension-version, + ? 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 access-policy = &(time_only: 0, time_and_key: 1) 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 ; leaves definitive field limits open). profile-id = tstr .regexp "[a-z0-9][a-z0-9:._-]{0,127}" name = tstr .regexp "[a-z0-9][a-z0-9._-]{0,63}" +public-key = bstr .size (1..1024) extension-id = tstr .size (1..256) ; Spec section 18 and 19: "dk1_" + unpadded Base64URL of the canonical JSON