From 4c5cde70c6fcaebe33998c4ff158e1a611053061 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 5 Oct 2026 21:50:47 +0200 Subject: [PATCH] Stage 4c in the README, the changelog and the library The README describes the modules of part 4c and their notes: the input in memory or from a ByteSource and what is read of it before the release; the output and the sinks, required for their format, closed or committed only at step 18 and aborted after any failure; the result, which never throws for an invalid capsule; the credentials, the key of words among them; the evaluator of stage 5 and accept; StandardExtensions with the rules of the note; the differences of form with Go; and a bug of dart2js of Dart 3.13 that an application for the web should know. It adds the times of the opening, the generators of the vectors of stage 4c and their files. The changelog has the part, its tests and the 15 faults injected, all of them found on the VM and 13 on Node.js. The comment of lib/datekeys.dart says what the library reads now. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 20 +++++++++++ README.md | 92 ++++++++++++++++++++++++++++++++++++++++++----- lib/datekeys.dart | 31 +++++++++------- 3 files changed, 121 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ae4c71..2f043a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ 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 4c: el head, la nota pública, la inspección y la apertura (05-10-2026) + +- **La nota pública del §24.1** (`lib/src/note.dart`), port de `CheckNote`, `NewNote` y `Note` de `extension` de `datekeys-go` en `c531e93`: `checkNote` y `checkNoteData`, con la longitud, el UTF-8 y las reglas de texto del autor declarado en ese orden y con los textos de Go; `newNote`, `publicNote` y `unusableNote`, y `Header.publicNote` y `Header.unusableNote`. `StandardExtensions` comprueba ya la nota como el `Standard` de Go, y su parámetro `validateNote` desaparece. +- **El head del formato 3** (`lib/src/head.dart`): `decodeHead` en las capas del §69.1, con R1 y R8 en la tercera, sobre los bytes UTF-8 de las rutas, nunca sobre sus unidades UTF-16; las reglas de rutas, del comentario y del autor de `pathrule` en la cuarta, como `ERR_HEAD_INVALID` con el texto de Go; la maquetación de los ficheros por restas; las extensiones críticas del head; `checkHeadEnd`; y `encodeHead`. +- **La inspección, pasos 1 a 8** (`lib/src/inspect.dart`): `inspectCapsule` e `inspectCapsuleSource`, `Inspection` con la comprobación de cada paso y su detalle, como el `Inspection` de Go; `inspectedLength`, para leer de un fichero grande solo su principio; `maxAccessKeyRead`; y `inspectView` e `inspectJson`, la salida exacta de `datekeys inspect -json`. +- **La apertura, pasos 9 a 18** (`lib/src/open.dart`, `open3.dart`): `openCapsule`, de una cápsula en memoria, y `openCapsuleSource`, de una `ByteSource` que se lee por tramos (`lib/src/source.dart`); `OpenOptions` y `Opened`, como los de Go. + - La `.dkk` del paso 9.a, decodificada o todavía codificada, con su material, sus extensiones críticas, su `capsule_id` y su `capsule_digest`, calculado sobre la fuente por tramos; las identities X25519 del llamador y la llave de palabras como una identity más. + - El release con la regla del paso 9, nunca antes de su ronda, y su verificación en el paso 10; `OUTER_TIME_AGE` con el stanza tlock; `INNER_ACCESS_AGE` con las reglas de los huecos; `CONTROL_CBOR`, `header_binding`, `I_PAYLOAD` y P. + - `PAYLOAD_AGE` en streaming: el contenido de los formatos 1 y 2 a un `ByteSink` según `age` autentica cada chunk, con el relleno del formato 2 comprobado y nunca entregado; y en el formato 3 la trama de `BODY`, el área, el head y cada fichero a un `FileSink`, con su SHA-256, en el orden y con la precedencia del §63 (`lib/src/sink.dart`, con `MemoryByteSink` y `MemoryFileSink`). + - Cada fallo con el código, el paso y el texto de Go, también los de `age`, por fases, como `classify`, y los de la fuente del release, como `sourceFailure`. Nada se presenta como válido antes de que acabe el paso 17: la salida se cierra al publicar y se aborta tras cualquier fallo, y el sink se aborta tras cualquier fallo posterior a su `begin` (§56). +- **La firma y el sello quedan para la etapa 5,** detrás de un punto de enganche (`lib/src/verdicts.dart`): el área de `security` se lee solo hasta donde lo exigen la trama de `BODY` y el área, y sus veredictos los da un `SecurityEvaluator`, que recibe `SECURITY_CBOR`, los bytes del head, el control y el formato, el `round_time` y las claves de autor, lo que toman `newSecurityContext` y `EvaluateSecurityIn` de Go. El de hoy, `notEvaluated`, no evalúa nada, y uno que falla no impide abrir. `OpenOptions.accept`, el `Accept` de Go, ve los veredictos antes del paso 18 y puede negarse a publicar los ficheros. +- **`AgePayloadDecryptor.wipe`** borra la clave del STREAM de una apertura que acaba antes del final de `PAYLOAD_AGE`. +- **Vectores de Go:** + - `tool/mutation_go_texts.go`, port de `scripts/mutation-go-texts.go` de `datekeys-ts` sobre el `testdata/` de este repositorio, escribe `mutation_texts.json`: el texto de `capsule.Open` y sus comprobaciones, con su detalle, en los 210 casos del corpus. Go en `c531e93` y el corpus de `spec-v0.11` coinciden en el código y el paso de todos, y Go en el tag `spec-v0.11` da el mismo fichero, byte a byte; + - `tool/open_go_vectors.go`, en una exportación de `datekeys-go` porque usa `internal/testkit`, escribe `open_cases.json` (cada fixture con cada credencial, y 117 aperturas de fixtures editados o con otras opciones, en cada paso que el corpus no alcanza, con los sinks y la salida que fallan y el rechazo de `Accept`), `open_heads.json`, `open_notes.json`, `open_inspect.json` (el texto de `capsule.Inspect` en las 5110 mutaciones de `inspect_differential.json`, donde Go en `c531e93` y el fichero también coinciden, y la salida de la CLI con notas públicas) y `open_vectors.g.dart`, con siete fixtures pequeños y una parte de cada fichero para Node.js. Los casos editados se sellan otra vez con las claves y los nonces de los fixtures, así que la salida es la misma en cada ejecución. +- **Pruebas.** Los 24 fixtures se abren con cada credencial que documentan, y sus 24 `.inspect.json` salen byte a byte; los 210 casos del corpus dan el código, el paso, el texto y cada comprobación de Go; los 169 casos de `open_cases.json`, también el estado del sink, el contenido o los ficheros y las extensiones inutilizables; todo, en memoria y desde una fuente que se lee a trozos, con el mismo resultado. Cápsulas de varios MiB, hechas desde los fixtures, prueban el streaming: la salida recibe cada chunk al autenticarse, y un chunk posterior que falla la aborta sin cerrarla. Una cápsula con un stanza para una llave de palabras se abre con ella. 517 pruebas nuevas en la VM y 83 en Node.js: 1319 y 291 en total. +- **Fallos inyectados**, uno a uno y revertidos: 15. Las pruebas los detectan todos en la VM: un paso fuera de orden, el `capsule_digest` sin comprobar, la salida publicada antes del final del paso 17, el SHA-256 de un fichero sin comparar, el relleno sin comprobar en los formatos 2 y 3, R8 sobre unidades UTF-16, una nota de 1025 bytes aceptada, un fallo de `age` clasificado en la otra fase, el error de una fuente con su propio código en el paso 9, una identity que abre dos stanzas aceptada, un head inválido informado sin leer hasta el final, el autor comprobado antes que el comentario, el sink sin abortar y el prefijo de la inspección un byte corto. En Node.js, 13: el orden del comentario y del autor y el prefijo solo los ven las pruebas de la VM. +- **`tool/open_bench.dart`** mide la apertura: en la VM, una cápsula pequeña tarda unos 50 ms y 64 MiB en streaming, 1,5 s en el formato 1 y 2,6 s en un fichero del formato 3. Las cifras, en el README. +- **Un fallo de dart2js** de Dart 3.13, ajeno a la librería: un objeto que llega al campo de otro a través de `c ? null : objeto` puede perder las escrituras que reciba allí. El README lo explica. + ### Etapa 4a: rutas, textos y llave de palabras (05-10-2026) - **Las reglas de rutas y de textos** (`lib/src/pathrule.dart`), port de `internal/pathrule` de `datekeys-go` en `c531e93`, sobre las tablas de Unicode 18.0.0 y WindowsBestFit que genera `datekeys-go` (`lib/src/pathrule_tables.dart`): diff --git a/README.md b/README.md index e182ae3..e1b8faf 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,19 @@ 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) y las partes 4a y 4b de la etapa 4: +Están hechas las etapas 0 a 4 del plan (`docs/PLAN_dart.md` del espacio de trabajo): - 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 parte 4a, las reglas de rutas y de textos con las tablas de Unicode 18.0.0, y la llave de palabras; -- 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. +- la etapa 4, en tres partes: + - la 4a, las reglas de rutas y de textos con las tablas de Unicode 18.0.0, y la llave de palabras; + - la 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; + - la 4c, el head del formato 3, la nota pública, la inspección (pasos 1 a 8) y la apertura (pasos 9 a 18) de los tres formatos. -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. Las partes 4a y 4b también: la 4a, en la rama `stage4a`, se integró encima de la 4b el mismo día. La parte 4c, el head, la nota pública, la inspección y la apertura, viene después. +La librería ya abre cápsulas reales, de los tres formatos, con todas sus credenciales. La firma y el sello, la etapa 5, no: sus veredictos llegan por un punto de enganche que hoy no evalúa nada. + +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. Las partes 4a y 4b también: la 4a, en la rama `stage4a`, se integró encima de la 4b el mismo día. La 4c se hizo después, en la rama `v0.11`. La etapa 1 porta tres ficheros de `datekeys-go` en `601e6d2`, con las mismas lecturas, las mismas comprobaciones en el mismo orden y los mismos textos de error: @@ -109,7 +113,30 @@ Notas de la etapa 4b: - **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. +- **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 usa la parte 4c, con el head, la nota, la inspección y la apertura. + +La parte 4c de la etapa 4 porta el head, la nota pública, la inspección y la apertura de `datekeys-go` en `c531e93`, con las mismas comprobaciones en el mismo orden, los mismos códigos y pasos y los mismos textos de error, también los del detalle de cada paso. El API sigue al de `datekeys-ts` y a `OpenOptions` y `Opened` de Go: + +| Módulo | Contenido | En Go | +|---|---|---| +| `lib/src/note.dart` | La nota pública del §24.1: `checkNote`, `checkNoteData`, `newNote`, `publicNote` y `unusableNote`, y `Header.publicNote` y `Header.unusableNote` | `CheckNote`, `NewNote` y `Note` de `extension`; `Header.PublicNote` y `UnusableNote` | +| `lib/src/head.dart` | El head del formato 3 (§29.4 a §29.6) en sus capas: R1 y R8 en la tercera, las reglas de rutas, del comentario y del autor de `pathrule` en la cuarta como `ERR_HEAD_INVALID`, sus extensiones y el final frente a `CONTENT`; y su codificación | `DecodeHead`, `EncodeHead` y `CheckHeadEnd` de `format3.go` | +| `lib/src/inspect.dart` | Los pasos 1 a 8 (`inspectCapsule`, `inspectCapsuleSource`), `Inspection` con la comprobación de cada paso, `inspectedLength`, `maxAccessKeyRead` y la vista de `datekeys inspect -json` (`inspectView`, `inspectJson`) | `inspect.go`; `internal/inspectview` | +| `lib/src/source.dart` | `ByteSource`, una cápsula que se lee por tramos, y `BytesSource`, la de unos bytes en memoria | `io.ReadSeeker` | +| `lib/src/open.dart` | Los pasos 9 a 18 (`openCapsule`, `openCapsuleSource`), `OpenOptions` y `Opened`: las credenciales y el release (9), su verificación (10), `OUTER_TIME_AGE` (11), la estructura frente a la política (12), `INNER_ACCESS_AGE` (13), `CONTROL_CBOR` (14), `header_binding` (15), `I_PAYLOAD` y P (16), `PAYLOAD_AGE` en streaming (17) y la publicación (18) | `open.go` | +| `lib/src/open3.dart` | El paso 17 del formato 3: la trama de `BODY` y el área, el head, cada fichero al sink con su SHA-256 y el relleno, con la precedencia del §63 | `open3.go` | +| `lib/src/sink.dart` | `ByteSink`, que recibe el contenido de los formatos 1 y 2 y cada fichero, y `FileSink`, que recibe los ficheros del formato 3; `MemoryByteSink` y `MemoryFileSink` | `dst` y `Sink` de `capsule.Open` | +| `lib/src/verdicts.dart` | `Verdict`, `Verdicts` y el punto de enganche de la etapa 5: `SecurityEvaluator`, con su `SecurityInput`, y `notEvaluated` | `Verdicts` y `newSecurityContext` | + +Notas de la parte 4c: +- **La entrada** es una cápsula en memoria (`openCapsule`) o una `ByteSource` (`openCapsuleSource`), que la app adapta de un `RandomAccessFile` o de un `Blob`. De una fuente solo se leen los primeros `inspectedLength` bytes, y 16 más para el nonce de `PAYLOAD_AGE`, antes de pedir el release; el `capsule_digest` de una `.dkk` se calcula leyendo la fuente por tramos de 1 MiB, y `PAYLOAD_AGE` llega por tramos de 16 chunks. Nada se reserva según L, P ni las longitudes de `BODY` (§57). Lo que lance la fuente sale del `openCapsuleSource` como un error del llamador, nunca como un veredicto sobre la cápsula, como en `datekeys-ts`. +- **La salida.** El contenido de los formatos 1 y 2 va a `OpenOptions.output`, un `ByteSink`, según `age` autentica cada chunk, y los ficheros del formato 3 a `OpenOptions.sink`, un `FileSink`, en el orden del head. Los dos son obligatorios para su formato: sin ellos, la apertura lanza un `ArgumentError` justo después del paso 2, antes de pedir nada, como `ErrSinkRequired` y `errWriterRequired` de Go. La salida se cierra solo al publicar y se aborta tras cualquier fallo, también de un paso anterior al 17 y también para una cápsula del formato 3; el sink se aborta tras cualquier fallo posterior a su `begin` (§56). Lo que reciben es suyo: copias, nunca vistas de un búfer que la apertura siga leyendo. +- **El resultado.** `openCapsule` no lanza por una cápsula inválida: el fallo está en `Opened.error` y es la última comprobación, con el texto de Go. `Opened.checks` son las de todos los pasos, con el detalle que Go escribe en cada uno. +- **Las credenciales.** `OpenOptions.identities` son identities X25519 en bruto, de 32 bytes: las del llamador y la llave de palabras, que `wordKey` deriva de la cadena, la ronda y el `capsule_id` que da la inspección, como hace la CLI de Go. La `.dkk` va decodificada (`accessKey`) o todavía codificada (`accessKeyFile`), que se decodifica en el paso 9.a y solo para `time_and_key`. La apertura no borra las credenciales del llamador; sí sus copias, la `.dkk` que decodifica, `I_PAYLOAD` y las claves de cada fichero `age`. +- **La firma y el sello** son de la etapa 5. El área de `security` se lee solo hasta donde lo exigen la trama de `BODY` y el área, y sus veredictos los da `OpenOptions.evaluator`, que recibe lo que toman `newSecurityContext` y `EvaluateSecurityIn` de Go: `SECURITY_CBOR`, los bytes del head, el control decodificado y el formato, el `round_time` y las claves de autor. Se llama, como en Go, al leer el head y antes de decodificarlo. El de hoy, `notEvaluated`, da `Verdicts.notEvaluated`; uno que lance da `Verdicts.failed` y la cápsula se abre igual (§29.3). `OpenOptions.accept`, el `Accept` de Go, ve los veredictos tras todas las comprobaciones del paso 17 y antes del 18: si lanza, no se publica nada, el sink se aborta y `Opened.refusal` guarda lo que lanzó. +- **`StandardExtensions`** comprueba ya la nota con las reglas del §24.1, como el `Standard` de Go. El parámetro `validateNote` de la parte 4b, que podía sustituirlas, ya no existe. +- **Las diferencias con Go** son de forma: Go da `Opened.PayloadLength` también tras un fallo (los bytes escritos), y aquí `payloadLength` es solo el de una cápsula abierta; y una identity que no mide 32 bytes es un `ArgumentError` antes de empezar, donde Go recibe objetos `age.Identity`. +- **Un fallo de dart2js** de Dart 3.13, ajeno a la librería, que conviene saber en la web: si un objeto llega al campo de otro a través de `c ? null : objeto`, el compilador puede perder las escrituras que reciba allí y leer después sus campos con el valor inicial. Le pasó a `tool/open_bench.dart` con un sink que cuenta bytes, y se reproduce sin la librería. Para pasar el sink o la salida a `OpenOptions`, mejor sin ese condicional. 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`; @@ -128,8 +155,7 @@ Las etapas siguientes traen el resto del protocolo en este orden: | Etapa | Contenido | |---|---| -| 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 | +| 5 | Firma y sello, con los textos de los veredictos, en el `SecurityEvaluator` de la apertura | | 6 | Escritor | | 7 | Localizador y claves de autor | @@ -216,6 +242,32 @@ Son medianas de tres ejecuciones, el 5 de octubre de 2026: La llave de palabras tarda un segundo, y una cabecera de 65 535 rutas que R6c proyecta, dos: la app llama a las dos en un `Isolate` (plan, «Rendimiento»). R6c comprueba la proyección de cada tabla aunque sea igual a la de una tabla anterior, como Go; saltarse las repetidas da el mismo resultado y baja la segunda ruta de 28 a 18 µs, y la cabecera más grande a 1,45 s. +### La apertura + +`dart run tool/open_bench.dart` mide la apertura en la VM. Compilado a JavaScript: + +```bash +dart compile js -O2 -o open_bench.js tool/open_bench.dart +``` + +```bash +node -e "globalThis.self = globalThis; require('./open_bench.js')" +``` + +Cada apertura verifica su release: entre dos, la de otro release hace que cada una pague su emparejamiento. Las cápsulas grandes las hace `test/large_capsule.dart` desde los fixtures, sellando otra vez su `PAYLOAD_AGE`, y en el formato 3 su control, como puede quien conoce sus claves; se abren desde una `ByteSource`, en streaming. Son medianas de tres ejecuciones en la VM y de dos en Node.js, el 5 de octubre de 2026: + +| Operación | VM | Node.js | +|---|---|---| +| Abrir `time_only_extensions`, formato 1, `time_only` | 52 ms | 0,84 s | +| Abrir `time_and_key_portable` con su `.dkk`, formato 1 | 48 ms | 0,80 s | +| Abrir `format2_time_and_key_recipients` con su `.dkk`, 16 stanzas | 73 ms | 0,82 s | +| Abrir `format3_single`, un fichero | 52 ms | 0,81 s | +| Abrir `format3_time_and_key_portable` con su `.dkk` | 72 ms | 0,82 s | +| 64 MiB en el formato 1, en streaming | 1,50 s, 43 MiB/s | 16 MiB: 0,73 s, 22 MiB/s | +| 64 MiB en un fichero del formato 3, en streaming | 2,64 s, 24 MiB/s | 16 MiB: 0,92 s, 17 MiB/s | + +Una apertura pequeña es casi toda los pasos 10 y 11, la verificación del release y el IBE. En una grande manda ChaCha20-Poly1305, unos 21 ms por MiB en la VM; en el formato 3 se suma el SHA-256 de cada fichero, otros 17 ms por MiB, el de este código como el de `package:crypto`. La memoria no crece con el tamaño: la fuente se lee por tramos de 1 MiB y el texto llega al sink chunk a chunk. + ## Versiones | Número | Dónde | Hoy | @@ -266,7 +318,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, de tlock, de las reglas de rutas y textos, de la llave de palabras 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, como `provider`, `agewrap`, `internal/pathrule` y `wordkey`; 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; el de las rutas, en una exportación suya. 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, de las reglas de rutas y textos, de la llave de palabras, de los formatos y de la apertura. 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, como `provider`, `agewrap`, `capsule`, `internal/pathrule`, `internal/testkit` y `wordkey`; 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 las rutas y de la apertura, en una exportación suya. 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 @@ -300,6 +352,10 @@ cd ../datekeys-go && go run ../datekeys-dart/tool/formats_go_vectors.go -testdat cd ../datekeys-go && go run ../datekeys-dart/tool/wordkey_go_vectors.go -out ../datekeys-dart/test/vectors ``` +```bash +cd ../datekeys-go && go run ../datekeys-dart/tool/mutation_go_texts.go ../datekeys-dart/testdata > ../datekeys-dart/test/vectors/mutation_texts.json +``` + `internal/pathrule` solo se puede importar desde el árbol de `datekeys-go`, así que el generador de las rutas corre en una exportación suya, hecha con `git archive`, sin tocar el repositorio: ```bash @@ -312,6 +368,17 @@ cp tool/pathrule_go_vectors.go "$tmp" rm -rf "$tmp" ``` +El de la apertura también, porque usa `internal/testkit`, `internal/cbortest` e `internal/inspectview`: + +```bash +commit=$(git -C ../datekeys-go rev-parse v0.12) +tmp=$(mktemp -d) +git -C ../datekeys-go archive "$commit" | tar -x -C "$tmp" +cp tool/open_go_vectors.go "$tmp" +(cd "$tmp" && go run ./open_go_vectors.go -source "$commit" -testdata "$OLDPWD/testdata" -out "$OLDPWD/test/vectors") +rm -rf "$tmp" +``` + | 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` | @@ -328,11 +395,18 @@ rm -rf "$tmp" | `pathrule_vectors.g.dart` | El mismo JSON como constante de Dart | | `wordkey_vectors.json` | La llave de palabras de `wordkey`: 400 textos con sus palabras, 515 listas de palabras con el resultado de `Check`, y cuatro llaves con su contraseña P y su sal S, el PBKDF2 de 1000 iteraciones para Node.js y el recipient; la primera es el vector del §38.1 | | `wordkey_vectors.g.dart` | El mismo JSON como constante de Dart | +| `mutation_texts.json` | De `tool/mutation_go_texts.go`, port de `scripts/mutation-go-texts.go` de `datekeys-ts` sobre el `testdata/` de este repositorio: el texto del error de `capsule.Open` en cada caso del corpus de mutaciones, `ok` si se abre, y sus comprobaciones con el detalle de cada paso. Si Go y el corpus discreparan en el código o el paso de un caso, lo diría con `go_error` y `go_step`: en ninguno lo hacen, ni con Go en `c531e93` ni en el tag `spec-v0.11`, que dan el mismo fichero | +| `open_cases.json` | `capsule.Open` sobre cada fixture con cada una de sus credenciales, y sobre fixtures editados o con otras opciones en cada paso que el corpus no alcanza: la trama, los campos, las extensiones y los vínculos de una `.dkk` en el paso 9.a, el reloj y los fallos de la fuente del release, las cabeceras `age` de los pasos 11 y 17, un stanza X25519 mal formado en `INNER_ACCESS_AGE`, `CONTROL_CBOR` sellado otra vez, `BODY` del formato 3 sellado otra vez, los sinks y la salida que fallan, el rechazo de `Accept` y las extensiones inutilizables de cada objeto. Cada caso tiene el resultado, el texto, el paso, las comprobaciones con su detalle, las peticiones del release, el estado del sink y el contenido o los ficheros | +| `open_heads.json` | `capsule.DecodeHead` de heads de una semilla fija, válidos y rotos en cada capa del §69.1, sin registro y con uno, y `capsule.EncodeHead` | +| `open_notes.json` | `extension.CheckNote` sobre textos y bytes, `extension.Note` y `Header.UnusableNote`, y `extension.Standard` con una nota | +| `open_inspect.json` | El texto del error de `capsule.Inspect` en cada mutación de `inspect_differential.json`, y la salida exacta de `datekeys inspect -json` con notas públicas y extensiones inutilizables | +| `open_vectors.g.dart` | Siete fixtures pequeños, lo que sus registros dicen de ellos y una parte de los cuatro ficheros `open_*.json`, como constantes de Dart | - Los fixtures que leen los generadores son los de `testdata/` de este repositorio, la copia sincronizada. - `primitives.json`, los cuatro ficheros de BLS12-381 y tlock, `pathrule_vectors.json`, `wordkey_vectors.json` 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`, `pathrule_vectors.g.dart`, `wordkey_vectors.g.dart`, `formats_vectors.g.dart`, `test/bls12381_constants.dart` y `test/ibe_constants.dart`. Unas pruebas en la VM comparan con los JSON los de las rutas, la llave de palabras, los formatos, BLS12-381 y el IBE. +- Los casos de `open_cases.json` que editan un fixture lo sellan otra vez como el `testkit` de la referencia, con las file keys y los nonces del fixture, así que salen iguales en cada ejecución, y se guardan como ediciones del fixture. +- Las pruebas que corren en Node.js no leen ficheros. Los valores de Go que usan están en `primitives.g.dart`, `pathrule_vectors.g.dart`, `wordkey_vectors.g.dart`, `formats_vectors.g.dart`, `open_vectors.g.dart`, `test/bls12381_constants.dart` y `test/ibe_constants.dart`. Unas pruebas en la VM comparan con los JSON los de las rutas, la llave de palabras, los formatos, la apertura, BLS12-381 y el IBE. ## Licencia diff --git a/lib/datekeys.dart b/lib/datekeys.dart index ead0348..f895425 100644 --- a/lib/datekeys.dart +++ b/lib/datekeys.dart @@ -2,19 +2,24 @@ /// DateKeys Access Key (`.dkk`), as `datekeys-go` and `datekeys-ts` implement /// them, checked against the same test data. /// -/// Stages 0 to 3 of docs/PLAN_dart.md and parts 4a and 4b of stage 4: the -/// package and its test data, the 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. Then the -/// key of words of spec §38.1, and the formats: 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 rules of paths and texts on the Unicode tables, 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. +/// Stages 0 to 4 of docs/PLAN_dart.md: the package and its test data, the +/// 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. Then the key of words of spec §38.1, +/// and the formats: 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, the padding, the head of format 3 and the +/// public note. And the reading of a capsule: its inspection, steps 1 to 8 +/// of spec §63, and its opening, steps 9 to 18, in memory or from a source +/// read by ranges, to a sink of bytes or of files. The signature and the +/// seal arrive with stage 5, through the evaluator of the opening. +/// +/// DER, the primitives, age, agewrap, the curve arithmetic, the IBE of tlock +/// and its stanza, the rules of paths and texts on the Unicode tables, the +/// frame of BODY, the digest of a capsule and the steps of the opening are +/// internal, as in the Go reference and datekeys-ts. library; export 'src/accesskey.dart';