diff --git a/CHANGELOG.md b/CHANGELOG.md index 2600532..0a82909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ Cambios notables de la librería Dart. El proyecto usa versionado semántico; mi ## Especificación 0.11, en la rama `v0.11` — sin versión +### Etapa 4b: los formatos de la cápsula y de la llave de acceso (05-10-2026) + +- **Las tramas** (`lib/src/framing.dart`): el PRELUDE de un `.dkc` y la trama de una `.dkk`, con las comprobaciones de los §23 y §40 en su orden y los textos de Go; `splitCapsule`, los pasos 1 a 3 de la inspección, con su `FramingException`; y `headerBinding`. El formato de una cápsula es el enum `CapsuleFormat`. +- **El relleno** (`lib/src/padding.dart`): `paddedLength` y `payloadAgeLength` con los códigos de `PaddingRule`, exactos hasta L_MAX también en la web, sin desplazamientos ni máscaras de más de 31 bits; y `PaddingCheck`, la comprobación del plaintext de `PAYLOAD_AGE` frente a L y P del paso 17, por trozos. +- **`BODY` del formato 3** (`lib/src/body.dart`): su trama y el área, sin el head. Y el `capsule_digest` incremental (`lib/src/digest.dart`). Son internos, como en `datekeys-ts`. +- **Las extensiones** (`lib/src/extension.dart`): las reglas de un array, al decodificarlo y antes de escribirlo; los registros, con la comprobación de la `data` y los lugares del §72; las extensiones críticas y no críticas de cada objeto; y la regla de los codificadores del §72, `checkWrite`, con `StandardExtensions`, a la que se dan las comprobaciones de la nota y del localizador. +- **El Provider Profile** (`lib/src/profile.dart`): su CBOR, `profile_hash`, las reglas 1 a 3 del §12.1 en su orden, el chain hash de drand, `maxRound` y el registro con Quicknet pinneado. `Profile` implementa el `PinnedProfile` de la etapa 3. +- **La DateKey** (`lib/src/datekey.dart`): `dk1_` con la aceptación y los textos de Go, también los de su JSON; la resolución de un instante a su ronda y la hora de una ronda; e `Instant`, al nanosegundo, con el RFC 3339 de `time.Parse` y de `Format` de Go. +- **`PUBLIC_HEADER`, `CONTROL_CBOR` de las versiones 1 a 3 y la `.dkk`** (`lib/src/header.dart`, `control.dart` y `accesskey.dart`), en las capas del §69.1. `I_PAYLOAD` y `access_material` se copian una vez y se borran en todos los caminos. +- **Los errores que Go devuelve sin código normativo**, como un código de relleno que no existe o una extensión fuera de su sitio al escribir, son `ArgumentError` con el texto de Go. +- **`lib/datekeys.dart`** exporta los formatos, como `index.ts` de `datekeys-ts`. +- **Vectores de Go** (`tool/formats_go_vectors.go`, en el contexto del módulo de `datekeys-go` en `c531e93`, sin cambiar nada en él): unos 6 400 casos en `test/vectors/formats_*.json`, con el resultado, el código y el texto de Go: tramas, cabeceras, controles, `.dkk`, perfiles, extensiones, `dk1_`, RFC 3339, rondas, relleno hasta L_MAX, la comprobación del relleno de `capsule.Open`, los codificadores, los límites del §57, `BODY`, y cada fallo de una lista, solo y con cada otro, para la precedencia del §69.1. `formats_vectors.g.dart` lleva uno de cada ocho para Node.js. Con el tag `spec-v0.11` el generador da la misma salida, salvo la regla de los codificadores del §72, que ese tag no tiene. +- **Pruebas.** Los 24 fixtures y las 6 `.dkk`; `dk1.json`, `quicknet_rounds.json`, `profile_quicknet.json`, `padding.json` y los 172 esquemas de `cbor.json` que la etapa 1 dejó aplazados; y el diferencial. 404 pruebas nuevas en la VM y 77 en Node.js: 771 y 190 en total, sin ninguna aplazada. +- **Fallos inyectados**, uno a uno y revertidos: 24. Las pruebas detectan 21, en la VM, en Node.js o en las dos; el de Padmé con desplazamientos de más de 31 bits, solo en Node.js, como debe ser. Dos no se detectaban al principio, y por ellos el generador escribe ahora cada par de fallos de capas distintas y cada bit de `FLAGS` y `RESERVED`: una DateKey comprobada antes que la regla entre los arrays de extensiones, y el bit alto de `FLAGS` ignorado. Los otros tres no cambian ningún resultado: quitar la comprobación de la recodificación de `PUBLIC_HEADER` o de `CONTROL_CBOR`, porque sus decodificadores, como los de Go, ya rechazan toda forma no canónica, y leer una longitud con un desplazamiento de 24 bits, porque los operadores de bits compilados a JavaScript dan 32 bits sin signo. + ### Etapa 3: BLS12-381 y tlock (05-10-2026) - **BLS12-381, de código propio,** como lo calcula `kilic/bls12-381` v0.1.0 para `drand/kyber-bls12381` v0.3.4: diff --git a/README.md b/README.md index bfd65d7..06b249f 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,12 @@ Es la tercera implementación de la especificación, después de la de referenci ## Estado -Están hechas las etapas 0 a 3 del plan (`docs/PLAN_dart.md` del espacio de trabajo): +Están hechas las etapas 0 a 3 del plan (`docs/PLAN_dart.md` del espacio de trabajo) y la parte 4b de la etapa 4: - la etapa 0, el paquete, sus herramientas y `testdata/` sincronizado; - la etapa 1, los errores normativos, los bytes, el perfil CBOR del §58 y el DER estricto, también el de los tiempos; - la etapa 2, las primitivas y la lectura de `age`; -- la etapa 3, BLS12-381 y tlock: el emparejamiento, el hash a G1, el IBE de drand y la verificación de la firma de la ronda. +- la etapa 3, BLS12-381 y tlock: el emparejamiento, el hash a G1, el IBE de drand y la verificación de la firma de la ronda; +- la parte 4b, los formatos de la cápsula y de la llave de acceso: las tramas, `PUBLIC_HEADER`, `CONTROL_CBOR` de los tres formatos, la `.dkk`, las extensiones, el Provider Profile, la DateKey con sus rondas y el relleno. Las etapas 2 y 3 se hicieron en paralelo, en ramas aparte desde la etapa 1, y se integraron el 5 de octubre de 2026. @@ -64,6 +65,31 @@ Notas de la etapa 3: - **La fuente de releases.** La librería no trae cliente HTTP: recibe una `ReleaseSource`, como `OpenOptions.Source` en Go. `fetchRelease` aplica la regla del paso 9: lo que lance la fuente es `ERR_RELEASE_UNAVAILABLE`, con su texto y ningún otro código. - **Lo que se exporta.** `lib/datekeys.dart` exporta la verificación de releases y `checkCompressedPoint`, como `datekeys-ts`. El IBE y el stanza tlock son internos: los usará la apertura de la etapa 4. El cifrado no se exporta todavía: es del escritor, la etapa 6. +La parte 4b de la etapa 4 porta los formatos de la cápsula y de la llave de acceso de `datekeys-go` en `c531e93` (la rama `v0.12`: la v0.11 con los arreglos de la revisión del 2 de octubre), con las mismas comprobaciones en el mismo orden, las mismas capas del §69.1 y los mismos textos de error. El API sigue al de `datekeys-ts`: + +| Módulo | Contenido | En Go | +|---|---|---| +| `lib/src/framing.dart` | El PRELUDE y la trama de la `.dkk` (`parsePrelude`, `splitAccessKey`), `splitCapsule` con los pasos 1 a 3 de la inspección y `headerBinding`; `CapsuleFormat` | `capsule/framing.go`, `inspect.go`; `accesskey.Decode` | +| `lib/src/padding.dart` | `PaddingRule`, `paddedLength`, `payloadAgeLength` y `PaddingCheck`, la comprobación del plaintext frente a L y P | `capsule/padding.go`; `checkPadding` de `open.go` | +| `lib/src/body.dart` | La trama de `BODY` y el área del formato 3, sin el head | `ParseBodyFrame`, `CheckArea` de `format3.go` | +| `lib/src/digest.dart` | El SHA-256 incremental del `capsule_digest` y su comparación | `checkCapsuleDigest` de `open.go` | +| `lib/src/extension.dart` | Los arrays de extensiones, los registros con sus lugares, `checkCritical`, `checkNoncritical` y la regla de los codificadores del §72 (`checkWrite`, `StandardExtensions`) | `extension` | +| `lib/src/schema.dart` | Lo común de los decodificadores de objetos: la clave de un fallo, las claves obligatorias, los arrays de extensiones | `required`, `encodeExtensions` de `capsule` | +| `lib/src/profile.dart` | El Provider Profile: su CBOR, `profile_hash`, el §12.1, `maxRound`, Quicknet y el registro | `profile` | +| `lib/src/datekey.dart` | `dk1_`, las rondas y sus horas, `Instant` y el RFC 3339 de Go | `datekey`; `time.Parse`, `Format` | +| `lib/src/header.dart` | `PUBLIC_HEADER` y `AccessPolicy` | `DecodeHeader`, `EncodeHeader` | +| `lib/src/control.dart` | `CONTROL_CBOR` de las versiones 1, 2 y 3 | `DecodeControl`, `EncodeControl` | +| `lib/src/accesskey.dart` | La `.dkk`: su cuerpo, su trama y su escritura con la regla del §72 | `accesskey` | + +Notas de la etapa 4b: +- **Los enums.** El formato, la regla de relleno y la política son enums de Dart (`CapsuleFormat`, `PaddingRule`, `AccessPolicy`), donde Go y `datekeys-ts` usan números. La regla de relleno no se llama `Padding`, que es un widget de Flutter. +- **Los errores sin código.** Lo que Go devuelve sin código normativo, como un código de relleno que no existe al codificar, un L por encima de L_MAX en `paddedLength` o una extensión de la especificación fuera de su sitio al escribir, es un `ArgumentError` con el texto de Go: un error del llamador. +- **Las extensiones de la especificación.** `StandardExtensions` es el `Standard` de Go. Las reglas de texto de la nota necesitan las tablas de las rutas, y la `data` de `datekeys.capsule` es del localizador: se le dan sus comprobaciones (`validateNote`, `validateCapsule`), y sin ellas solo se comprueba que haya `data`, como el `Standard` de Go sin `ValidateCapsule`. +- **Los enteros.** Una ronda, L y P son un `int` hasta 2^53-1, exactos en la web; un entero de ocho bytes se lee como dos mitades de 32 bits, y Padmé se calcula con potencias de dos, sin desplazamientos. +- **Un nombre con un surrogate suelto** se cita en los textos como Go citaría sus bytes en UTF-8 generalizado (`\xed\xa0\x80`), como en la etapa 1; `datekeys-ts` escribe U+FFFD. Go no tiene esos nombres. +- **La rama `v0.12` de Go frente al tag `spec-v0.11`.** Para estos paquetes, la única diferencia es la regla de los codificadores del §72: en `spec-v0.11`, `accesskey.Encode` escribe `datekeys.note` en una `.dkk` y `datekeys.capsule` en un array crítico. El generador da la misma salida en las dos para todo lo demás. +- **Lo que se exporta.** `lib/datekeys.dart` exporta los formatos, como `index.ts` de `datekeys-ts`. La trama de `BODY`, el digest y lo común de los esquemas son internos: los usará la etapa 4c, con el head, la nota, la inspección y la apertura. + Los enteros son exactos en la VM y en la web. El `int` de Dart tiene 64 bits con signo en la VM y en la web es un double, exacto hasta 2^53. Por eso la librería no usa un `int` por encima de 2^53-1, ni desplazamientos u operaciones de bits de más de 31 bits: - un entero de CBOR es un `int` hasta 2^53-1 y un `BigInt` por encima, como el `number | bigint` de `datekeys-ts`; - `CborDecoder.uint` devuelve un `int`, porque todos los esquemas acotan sus enteros en 2^53-1, y `uint64` devuelve un `BigInt`; @@ -80,7 +106,7 @@ Las etapas siguientes traen el resto del protocolo en este orden: | Etapa | Contenido | |---|---| -| 4 | Formatos, inspección (pasos 1 a 8) y apertura (pasos 9 a 18) | +| 4 | El resto de la etapa: las rutas y la llave de palabras, el head y la nota pública, la inspección (pasos 1 a 8) y la apertura (pasos 9 a 18) | | 5 | Firma y sello, con los textos de los veredictos | | 6 | Escritor | | 7 | Localizador y claves de autor | @@ -192,7 +218,7 @@ dart run tool/sync_testdata.dart check --against ../datekeys-go - Los ficheros de `testdata/` no se editan ni se generan aquí. - En cada `dart test`, `test/testdata_test.dart` comprueba la copia, y que cada fichero nombre `specVersion`. -`test/vectors/` tiene los vectores de las primitivas, de `age`, de BLS12-381 y de tlock. Los escribe Go, con las librerías de la caché de módulos que usa `datekeys-go` (`x/crypto`, `filippo.io/age`, `kilic/bls12-381`, `drand/kyber`, `kyber-bls12381` y `tlock`) y sus paquetes `provider` y `agewrap`; ningún valor esperado se escribe a mano. Los generadores van en `tool/`, con `//go:build ignore`, y se ejecutan en el contexto del módulo de `datekeys-go`, sin cambiar nada en él. Los de BLS12-381 y tlock dan la misma salida con el tag `spec-v0.11` y con el borrador v0.12, y leen ficheros congelados de `datekeys-ts`, cuya carpeta en esta máquina se llama todavía `App`: +`test/vectors/` tiene los vectores de las primitivas, de `age`, de BLS12-381, de tlock y de los formatos. Los escribe Go, con las librerías de la caché de módulos que usa `datekeys-go` (`x/crypto`, `filippo.io/age`, `kilic/bls12-381`, `drand/kyber`, `kyber-bls12381` y `tlock`) y sus paquetes `provider` y `agewrap`; ningún valor esperado se escribe a mano. Los generadores van en `tool/`, con `//go:build ignore`, y se ejecutan en el contexto del módulo de `datekeys-go`, sin cambiar nada en él. Los de BLS12-381 y tlock dan la misma salida con el tag `spec-v0.11` y con el borrador v0.12, y leen ficheros congelados de `datekeys-ts`, cuya carpeta en esta máquina se llama todavía `App`: ```bash cd ../datekeys-go && go run ../datekeys-dart/tool/gen_primitive_vectors.go -out ../datekeys-dart/test/vectors @@ -218,6 +244,10 @@ cd ../datekeys-go && go run ../datekeys-dart/tool/tlock_go_vectors.go ../datekey cd ../datekeys-go && go run ../datekeys-dart/tool/release_go_vectors.go ../datekeys-dart/testdata > ../datekeys-dart/test/vectors/release_vectors.json ``` +```bash +cd ../datekeys-go && go run ../datekeys-dart/tool/formats_go_vectors.go -testdata ../datekeys-dart/testdata -out ../datekeys-dart/test/vectors +``` + | Fichero | Contenido | |---|---| | `primitives.json` | SHA-256, HMAC, HKDF (RFC 5869), PBKDF2 (con el vector del §38.1), scrypt (RFC 7914), ChaCha20, Poly1305 y ChaCha20-Poly1305 (RFC 8439), X25519 (RFC 7748, BoringSSL y los puntos de orden pequeño), Ed25519 (las 64 primeras líneas de `sign.input` de Go, cuyas tres primeras son las de la RFC 8032, y sus mutaciones), el Base64 de Go y el Bech32 de `age` | @@ -228,11 +258,13 @@ cd ../datekeys-go && go run ../datekeys-dart/tool/release_go_vectors.go ../datek | `ibe_vectors.json` | GT y H2, H3 con candidatos rechazados, H4, identidades de rondas, el stanza tlock de cada fixture de `testdata/` con sus valores intermedios y su file key, los ciphertexts de kyber y los veredictos de `DecryptCCAonG2` sobre copias editadas del stanza de `time_only` | | `tlock_vectors.json` | El cifrado de kyber con sigma fijo, reproducido byte a byte, y los ciphertexts que escribió `datekeys-ts` y abrió Go | | `release_vectors.json` | Los veredictos, códigos y textos de `provider.Verify`, de `NewTimeIdentity` con su `Unwrap` y de `NewTimeRecipient` | +| `formats_*.json` | El diferencial de los formatos, de `datekeys-go` en `c531e93`: el resultado, el código y el texto de Go en unos 6 400 casos, válidos y rotos en cada capa del §69.1, con una semilla fija y sobre los fixtures editados. Las tramas, `PUBLIC_HEADER`, `CONTROL_CBOR` de los tres formatos, la `.dkk`, los perfiles, las extensiones con sus registros, `dk1_`, el RFC 3339, las rondas, el relleno hasta L_MAX, la comprobación del relleno de `capsule.Open`, los codificadores, los límites del §57, `BODY` y, para la precedencia del §69.1, cada fallo de una lista, solo y con cada otro | +| `formats_vectors.g.dart` | Uno de cada ocho casos de cada sección de `formats_*.json`, como constantes de Dart para las pruebas compiladas a JavaScript | - Los fixtures que leen los generadores son los de `testdata/` de este repositorio, la copia sincronizada. -- `primitives.json` y los cuatro ficheros de BLS12-381 y tlock salen iguales en cada ejecución. Lo aleatorio de tlock, los ciphertexts de kyber con su sigma y los de `datekeys-ts`, se lee de los ficheros congelados de `datekeys-ts` en `289fe71`, y Go los descifra otra vez. `age`, en cambio, saca sus claves y nonces de `crypto/rand`, así que `age.json` y `age_fixtures.json` cambian en cada ejecución; las pruebas leen lo que esté en el repositorio. +- `primitives.json`, los cuatro ficheros de BLS12-381 y tlock y los de los formatos salen iguales en cada ejecución. Lo aleatorio de tlock, los ciphertexts de kyber con su sigma y los de `datekeys-ts`, se lee de los ficheros congelados de `datekeys-ts` en `289fe71`, y Go los descifra otra vez. `age`, en cambio, saca sus claves y nonces de `crypto/rand`, así que `age.json` y `age_fixtures.json` cambian en cada ejecución; las pruebas leen lo que esté en el repositorio. - Un fichero `age` de más de un chunk se guarda como su cabecera, su nonce y su file key: la prueba cifra otra vez el texto documentado y comprueba el SHA-256 del fichero entero antes de leerlo. Las cabeceras de megabytes se escriben como partes que se repiten. -- Las pruebas que corren en Node.js no leen ficheros. Los valores de Go que usan están en `primitives.g.dart`, `test/bls12381_constants.dart` y `test/ibe_constants.dart`. Una prueba en la VM compara los dos últimos con los JSON. +- Las pruebas que corren en Node.js no leen ficheros. Los valores de Go que usan están en `primitives.g.dart`, `formats_vectors.g.dart`, `test/bls12381_constants.dart` y `test/ibe_constants.dart`. Una prueba en la VM compara los tres últimos con los JSON. ## Licencia diff --git a/lib/datekeys.dart b/lib/datekeys.dart index 88b2067..ac18364 100644 --- a/lib/datekeys.dart +++ b/lib/datekeys.dart @@ -6,17 +6,30 @@ /// normative errors of spec §69, byte helpers and the CBOR profile of spec /// §58, the primitives and the reading of age files, then BLS12-381 and /// tlock: the check of a compressed point and the verification of releases, -/// with the sources that deliver them. DER, the primitives, age, agewrap, -/// the curve arithmetic, the IBE of tlock and its stanza are internal, as in -/// the Go reference. The protocol arrives stage by stage. +/// with the sources that deliver them. And the formats of stage 4b: the +/// frames of the .dkc and the .dkk, PUBLIC_HEADER, CONTROL_CBOR of the three +/// formats, the .dkk, the extensions with their registries, the Provider +/// Profile with the pinned Quicknet, the DateKey with its rounds and times, +/// and the padding. DER, the primitives, age, agewrap, the curve arithmetic, +/// the IBE of tlock and its stanza, the frame of BODY and the digest of a +/// capsule are internal, as in the Go reference and datekeys-ts. The +/// protocol arrives stage by stage. library; +export 'src/accesskey.dart'; export 'src/bls12381_curve.dart' show BlsGroup, PointVerdict, checkCompressedPoint; export 'src/bytes.dart' show compareBytes, concatBytes, decodeUtf8, equalBytes, fromHex, toHex; export 'src/cbor.dart'; +export 'src/control.dart'; +export 'src/datekey.dart'; export 'src/errors.dart'; +export 'src/extension.dart'; +export 'src/framing.dart'; +export 'src/header.dart'; +export 'src/padding.dart'; +export 'src/profile.dart'; export 'src/release.dart' show PinnedProfile, diff --git a/test/cbor_vectors_test.dart b/test/cbor_vectors_test.dart index 8bf0077..a081fde 100644 --- a/test/cbor_vectors_test.dart +++ b/test/cbor_vectors_test.dart @@ -68,11 +68,10 @@ void main() { } }); - // The schemas block is decoded with the decoder of each schema: Provider - // Profile, PUBLIC_HEADER, CONTROL_CBOR and the body of a .dkk, which arrive - // with stage 4 of docs/PLAN_dart.md. Until then the block is only read: its - // shape is checked, and the objects it holds as valid are items of the - // profile whose type tag peekSchema reads. + // The schemas block is decoded with the decoder of each schema, Provider + // Profile, PUBLIC_HEADER, CONTROL_CBOR and the body of a .dkk, in + // formats_vectors_test.dart. Here its shape is checked, and the objects it + // holds as valid are items of the profile whose type tag peekSchema reads. group('schemas', () { const typeTags = { 'provider_profile': 'datekeys-provider-profile', @@ -110,11 +109,5 @@ void main() { ); } }); - - test( - 'the vectors decode with the decoders of their schemas', - () {}, - skip: 'the decoders of the schemas arrive with stage 4', - ); }); }