You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

275 lines
26 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Plan: la librería Dart (`datekeys-dart`)
*v2, 2 de octubre de 2026. El autor hará la app en Flutter, así que la librería del protocolo hace falta en Dart. La v1 de este plan la escribió una sesión hecha por descuido con Sonnet 5.5. La revisión de esa sesión ([spec_v0.11/revision_sesion_1_2_octubre.md](spec_v0.11/revision_sesion_1_2_octubre.md), R3) encontró que le faltaban piezas, y esta versión las añade. Las tres decisiones del autor están en «Decidido».*
## Qué es
Un paquete Dart **puro**: sin Flutter y sin plataforma.
- Implementa el mismo protocolo que `datekeys-go` y `datekeys-ts`, para que la app Flutter lo use como dependencia de ruta o de git.
- Funciona en la VM de Dart, en Flutter (móvil y escritorio) y en la web si no usa `dart:io`. La app Flutter no tendrá versión web (decisión del autor, 7-10-2026): la web se hace con TypeScript y Svelte. Que la librería dé lo mismo compilada a JavaScript ya no es un requisito; el gate sigue pasando las pruebas en Node mientras no estorbe, y el código nuevo no tiene por qué cuidarlo.
- Es la tercera implementación del spec. Como la de TypeScript, **no genera datos de prueba propios**: se contrasta con el `testdata/` de `datekeys-go`, sincronizado a un commit fijo.
Estado de la máquina: el 5-10, Flutter está instalado en `C:\dev\flutter`, y su Dart 3.13.0 es el `dart` del PATH. La librería no necesita Flutter; la app sí.
**Hecho:** todas las etapas, 0 a 7, el 5 y el 6-10, en la rama `v0.11` de `datekeys-dart`, hasta `d77ef9e`. Los commits de cada parte están al final de este plan, y el estado de cada etapa en el README del repo.
## Decidido (2 de octubre)
1. **Dependencias:** solo `package:crypto`, el paquete oficial de dart-lang, para SHA-1, SHA-2 y HMAC. Todo lo demás es código propio:
- HKDF y PBKDF2;
- ChaCha20-Poly1305, X25519 y Ed25519 estricto;
- scrypt;
- BLS12-381 con el IBE de tlock;
- ECDSA, RSA, DER, CMS y CBOR.
2. **Orden:** primero lectura y verificación (etapas 1 a 5) y después el escritor (etapas 6 y 7), como en TypeScript.
3. **Nombre del paquete:** `datekeys`, sin publicar en pub.dev.
## Alcance, por etapas
Cada etapa se cierra con sus pruebas contra los vectores y fixtures de Go, como en TypeScript. El orden sigue la dificultad y lo que desbloquea.
| Etapa | Contenido | Referencia en TypeScript |
|---|---|---|
| 0 | Esqueleto del paquete: `dart analyze` y `dart test`, `testdata/` sincronizado con un `SOURCE.json`, licencia Apache-2.0 | `scripts/sync-testdata.mjs` |
| 1 | Bytes, errores normativos, CBOR determinista (§58) y DER estricto, también el de los tiempos | `bytes.ts`, `errors.ts`, `cbor.ts`, `der.ts` |
| 2 | Primitivas: SHA-1/2 y HMAC (de `package:crypto`); HKDF, PBKDF2, scrypt, ChaCha20-Poly1305, X25519 y Ed25519 estricto; `age` (stanza X25519, stanza scrypt y STREAM) | `age.ts`, `agefile.ts`, `x25519.ts`, `ed25519strict.ts` |
| 3 | BLS12-381 y tlock: emparejamiento, hash a curva, IBE-CCA de drand y verificación de la firma de la ronda | `bls12381.ts`, `ibe.ts`, `tlock.ts`, `release.ts` |
| 4 | Formatos: DateKey, perfil, cabecera, control, head, cuerpo, rutas (tablas Unicode 18.0.0), `.dkk`, llave de palabras, nota, inspección (pasos 1 a 8) y apertura (pasos 9 a 18) | `datekey.ts`, `profile.ts`, `header.ts`, `control.ts`, `head.ts`, `pathrule.ts`, `wordkey.ts`, `note.ts`, `open.ts`, `open3.ts` |
| 5 | Firma y sello: los compromisos, `alg` 1 (Ed25519), `alg` 2 (CMS con el perfil de certificado del §29.10, ECDSA y RSA en `BigInt`) y RFC 3161, y los veredictos con sus textos | `author.ts`, `cms.ts`, `securitycms.ts`, `security.ts` |
| 6 | Escritor: formato 3 con el área de 32 KiB, `time_only` y `time_and_key`, llave de palabras y nota pública | `writer.ts`, `encrypt.ts`, `wordkey.ts`, `note.ts` |
| 7 | Localizador y sobre (`datekeys.capsule`) y claves de autor `dkauthor1…`, con su fichero cifrado con scrypt | `locator` y `authorkey` de Go |
Queda fuera de la librería:
- la descarga de releases de drand, porque el cliente HTTP lo pone la app;
- la descarga del resto de un localizador, también de la app, con las reglas de direcciones del §44.1 y la comprobación de la IP resuelta en cada conexión;
- el almacenamiento y la interfaz.
La librería recibe una fuente de releases, como `OpenOptions.Source` en Go.
## Lo que la v1 no tenía en cuenta
- **PBKDF2-HMAC-SHA256 con 600 000 iteraciones** (§38.1) para abrir con una llave de palabras, que es lectura, no escritura. Con el API general de `package:crypto`, cada iteración reserva memoria y recalcula el estado de las dos claves del HMAC: en un móvil tarda bastantes segundos. Hace falta un HMAC-SHA256 propio que precalcule los estados interno y externo una sola vez y reutilice los búferes; y la app lo llama en un `Isolate`. El vector de §38.1 y los de la página dan la referencia.
- **scrypt** con logN = 16 (§29.12), para leer y escribir los ficheros de clave de autor: el stanza scrypt de `age`, con su límite de factor de trabajo, como `SetMaxWorkFactor(16)` de Go.
- **Las tablas de Unicode 18.0.0** (NFD, minúsculas simples y best-fit) para las reglas de rutas y de texto (§29.5.1) y para la llave de palabras. No se escriben a mano: el generador de Go, `internal/pathrule/gen`, ya escribe el módulo de TypeScript con `-ts`, y hará falta una salida `-dart`. El digest de las tablas recalculado en Dart debe coincidir con `TablesDigest`, como en TypeScript.
- **Los vectores de firma y sello.** En la v0.11 cubrían mucho menos de lo que dice el spec: una implementación con fallos los pasaba todos. La revisión del 2 de octubre los está completando en la rama `v0.12` de `datekeys-go`, con el perfil de certificado del borrador v0.12. La etapa 5 debe sincronizarse con esa versión, no con `spec-v0.11`. Las etapas 0 a 4 no dependen de eso.
## Rendimiento
Hay dos cuellos de botella:
- **BLS12-381:** abrir una cápsula verifica la firma de la ronda y hace un emparejamiento. Con `BigInt` en la VM de Dart serán cientos de milisegundos o más, y bastante más compilado a JavaScript.
- **PBKDF2 con 600 000 iteraciones,** solo al abrir o crear con una llave de palabras.
La app debe llamar a los dos en un `Isolate` para no congelar la interfaz. La librería no lo hace por sí misma. Las cifras reales se miden en la etapa 3, en la VM y en un móvil de gama media.
## Estructura del repo
`G:\bussines\datekeys\datekeys-dart`, repo independiente:
```
datekeys-dart/
pubspec.yaml # name: datekeys; sin Flutter; entorno de Dart 3.x; solo package:crypto
lib/datekeys.dart # la API pública, como index.ts
lib/src/… # un fichero por módulo de la tabla
test/… # dart test, contra testdata/
testdata/ # sincronizado desde datekeys-go con su SOURCE.json
tool/sync_testdata.dart # equivalente de sync-testdata.mjs
```
La app Flutter lo usa con `datekeys: { path: ../datekeys-dart }`, o con una dependencia de git cuando haya remoto.
**Ramas.** La librería sigue la versión del spec que implementa: empieza en `v0.11` y pasa a `v0.12` cuando `datekeys-go` cierre esa versión.
## Cómo se hacen las etapas
Las etapas 1 y 2 se encargaron a un agente Opus, que trabaja en el propio `datekeys-dart`. La sesión revisa después su trabajo y pasa el gate. Las dos salieron bien así. El encargo de la etapa 2, que se relanzó el 5-10 y cerró el mismo día, fue este:
- **Alcance:**
- HMAC-SHA256 propio, con una compresión SHA-256 propia y los estados interior y exterior calculados una vez, para el PBKDF2 de 600 000 iteraciones;
- HKDF-SHA256, scrypt con Salsa20/8, ChaCha20-Poly1305 con comparación del tag en tiempo constante, X25519 con clamping y secreto todo a ceros rechazado, y Ed25519 estricto (port de `internal/ed25519strict`);
- lectura de `age` v1 portada de `filippo.io/age` v1.3.2: la cabecera y sus límites, el MAC, los stanzas X25519 y scrypt (con factor de trabajo acotado, como `authorkey` de Go), la clave del payload y el STREAM de `internal/stream`, con los mismos casos de fin de fichero que Go, y las reglas de `agewrap` y del §28.1;
- las identidades `AGE-SECRET-KEY-1…` en Bech32;
- errores con la finura suficiente para reproducir en la etapa 4 los textos de Go (ver `agefile.ts` y `mutation-texts.json` en `datekeys-ts`).
- **Exactitud:**
- los enteros siguen la regla de la etapa 1: exactos en la VM y en la web;
- en Poly1305, los productos y las sumas de los limbs quedan por debajo de 2^53;
- la aritmética de 32 bits es correcta también compilada a JavaScript.
- **Vectores:**
- los de las RFC 7748, 8439, 5869, 7914 y 8032 y los de PBKDF2, producidos con las librerías de Go de la caché de módulos, nunca escritos de memoria. El generador va en `tool/` con `//go:build ignore`, y su salida en `test/vectors/`;
- ficheros `age` de `filippo.io/age`, con recipients X25519 y scrypt, cortes y manipulaciones;
- `ed25519_strict.json`;
- el PAYLOAD_AGE de cada fixture, con su `payload_identity`, y el INNER_ACCESS_AGE de los `time_and_key`.
- **Pruebas:**
- las que leen ficheros, con `@TestOn('vm')`;
- pruebas de las primitivas que también corran en Node, con parámetros pequeños;
- al final, fallos inyectados de uno en uno para comprobar que las pruebas los detectan.
- **Informe:** los tiempos en la VM y en Node de X25519, Ed25519, ChaCha20-Poly1305 por MiB, PBKDF2 a 600 000 iteraciones y scrypt con logN 16; y los commits.
- **Reglas:**
- sin dependencias nuevas;
- código, comentarios y commits en inglés, y README y CHANGELOG en español;
- `tool/check.sh` antes de cada commit;
- decir en el código y en el README que `BigInt` no es de tiempo constante;
- no tocar `testdata/`, `datekeys-go` ni `datekeys-ts`.
**Revisión de la etapa 2 (5-10).** La sesión comparó cada módulo con su original de Go, línea a línea: `internal/format`, `decryptHdr`, las identities X25519 y scrypt y `internal/stream` de `age` v1.3.2; `agewrap` y `internal/ed25519strict` de `datekeys-go`; el `scrypt` de `x/crypto`; el Base64 de Go; y el Bech32 de `age`. Comprobó también, contra TweetNaCl, las cotas de los enteros en la web. No encontró ningún fallo. `tool/check.sh` pasó: 296 pruebas en la VM y 83 en Node. Quedan tres notas menores:
- el mensaje de `4850b6b` dice que todos los valores caben en 32 bits, y no es así antes de las máscaras;
- el texto de «mixed case» del Bech32 usa las mayúsculas y minúsculas de Unicode de Dart, como el TypeScript, y puede diferir del de Go con entradas no ASCII que los tres rechazan;
- `age.json` y `age_fixtures.json` cambian en cada ejecución del generador, porque `age` usa `crypto/rand`: son vectores congelados.
**La etapa 3, BLS12-381 y tlock,** siguió el mismo esquema. El 5-10 el autor pidió adelantarla, así que corrió en paralelo con la 2. Su agente trabajó en un worktree aparte, en la rama `stage3`, que salía de `c8bf3c0`. Solo creó ficheros nuevos, y dejó los cambios a los ficheros comunes para un último commit.
La sesión revisó la etapa 3 contra Go y contra el TypeScript:
- `verifyRelease`, contra `provider.Verify` de Go;
- `unwrapTlockStanza`, contra `NewTimeIdentity` y su `Unwrap`;
- el IBE, contra `DecryptCCAonG2`, `EncryptCCAonG2`, `h3` (con el desplazamiento del primer byte y el límite de iteraciones), H2 y H4 de kyber;
- la decodificación de puntos, contra `FromCompressed` de kilic: flags, signo y subgrupo;
- `fetchRelease`, contra `sourceFailure` de `capsule`.
No encontró ningún fallo. La aritmética de la curva la cubren los vectores de Go: GT, hash a G1, firmas y el stanza de cada fixture.
Después la integró: `rebase` sobre `v0.11`, con conflictos solo en el README, el CHANGELOG y `lib/datekeys.dart`, y avance de `v0.11` sin fusión. El gate pasó, con 367 pruebas en la VM, más una aplazada, y 113 en Node. La sesión borró luego el worktree y la rama.
Notas:
- **Diferencias con Go, a propósito, las mismas que en el TypeScript:**
- solo se verifica el scheme de Quicknet;
- una firma en el infinito nunca vale, mientras que Go la acepta si la clave también es el infinito, una clave que ningún perfil pinneado tiene.
- **SHA-256:** BLS12-381 y tlock usan el de `package:crypto`, aunque la etapa 2 ya trae uno propio. Las dos opciones están permitidas, y se puede unificar más adelante.
- **Pendiente:**
- medir en un móvil de gama media;
- `Random.secure` no funciona en `dart test -p node`, así que las dos pruebas que sacan un sigma aleatorio corren solo en la VM.
- **Tiempos en la VM:**
- un emparejamiento, 11 ms;
- la verificación de la firma de una ronda, 16 ms;
- el descifrado del stanza, 20 ms;
- los pasos 10 y 11 juntos, 35 ms.
En Node, unas veinte veces más.
**La etapa 4, en marcha desde el 5-10.** El autor dijo «sí, sigue con la etapa 4».
Primero, la sesión añadió al generador de las tablas de Unicode una salida para Dart:
- en `datekeys-go`, `internal/pathrule/gen -dart`, commit `c531e93` en la rama `v0.12`, subido el 5-10 con permiso del autor;
- en `datekeys-dart`, `lib/src/pathrule_tables.dart`, commit `1e7753d`, con el mismo `TablesDigest`;
- la salida de TypeScript y `tables.go` no cambian.
La etapa se reparte en tres partes, con el esquema de la 3: cada agente crea solo ficheros nuevos y deja los cambios a los ficheros comunes para un último commit.
- **4a, en paralelo con la 4b:** las reglas de rutas y de textos sobre las tablas (`pathrule.dart`) y la llave de palabras (`wordkey.dart`). Trabaja en el worktree `G:\bussines\datekeys\datekeys-dart-stage4a`, rama `stage4a`, desde `1e7753d`.
- **4b, en paralelo con la 4a:** las tramas del `.dkc` y la `.dkk`, el esquema, las extensiones, el relleno, el digest, la trama de `BODY`, el Provider Profile, la DateKey, `PUBLIC_HEADER`, `CONTROL_CBOR` y la `.dkk`. Trabaja en la copia principal, rama `v0.11`.
- **4c, después de integrar la 4a y la 4b:** el head, la nota pública, la inspección (pasos 1 a 8) y la apertura (pasos 9 a 18) de los formatos 1 a 3, con el corpus de mutaciones y sus textos de Go. La evaluación de la firma y del sello queda para la etapa 5, detrás de un punto de enganche.
**La 4a y la 4b, hechas, revisadas e integradas el 5-10.** La 4b son los commits `627f6fc` a `b492782`, y la 4a, puesta encima, `03c4333` a `d8c790d`. El gate pasa con 802 pruebas en la VM, sin ninguna aplazada, y 208 en Node. El worktree y la rama `stage4a` están borrados.
Revisión:
- **4a:** `pathrule.dart` y `wordkey.dart`, comparados línea a línea con `internal/pathrule` y `wordkey` de Go. Coinciden también en los casos raros de bytes UTF-8 no válidos, que Go cuenta como U+FFFD de tres bytes. Las pruebas cubren cada punto de código de los 17 planos.
- **4b:** el relleno, las tramas y la resolución de rondas y tiempos, comparados con Go, porque son la parte aritmética, la delicada en la web. El resto lo cubre un diferencial contra Go de unos 6 400 casos, con el resultado, el código y el texto, y cada par de fallos de capas distintas del §69.1.
- **Ninguna de las dos tiene fallos.** Lo único distinto entre Go en `c531e93` y el tag `spec-v0.11` es la regla de los codificadores del §72, que el tag no tiene.
**La 4c, hecha y revisada el 5-10:** `e18045e` a `8e0e993`. Con ella la etapa 4 queda completa. El gate pasa con 1 320 pruebas en la VM y 291 en Node.
Qué hace:
- el head, la nota pública, la inspección de los pasos 1 a 8 y la apertura de los pasos 9 a 18 de los formatos 1 a 3;
- abre cápsulas grandes en streaming, desde una fuente de acceso aleatorio, y la memoria no crece con el tamaño;
- deja preparado el enganche de los veredictos para la etapa 5.
Resultados:
- las 210 mutaciones del corpus dan el código, el paso y el texto de Go;
- 5 110 inspecciones mutadas no dan ninguna diferencia con Go;
- los 24 fixtures abren con cada credencial;
- las pruebas detectaron los 15 fallos inyectados.
La revisión de la sesión comparó con Go `open3.go` y el paso 17 de los formatos 1 y 2: el orden de los subpasos, el SHA-256 de cada fichero, el relleno, la salida que se cierra solo cuando pasa el paso 17 y que se aborta ante cualquier fallo, y el enganche de `Accept`. No encontró fallos.
Notas:
- **Un fallo de dart2js en 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 recibe ahí. Está documentado en el README. Reportarlo al equipo de Dart sería publicar fuera: solo con permiso del autor.
- `Opened.payloadLength` solo se rellena cuando la apertura sale bien.
- Las identidades se pasan como claves X25519 en bruto, como en TypeScript.
**La etapa 5, en marcha desde el 5-10**, cuando el autor dijo «sigue con la etapa 5». Se reparte como la 4:
- **5a, en paralelo con la 5b:** el lector CMS con el perfil de certificado de la v0.12, ECDSA y RSA propios en `BigInt` y los sellos RFC 3161, port de `internal/cms`. Trabaja en el worktree `G:\bussines\datekeys\datekeys-dart-stage5a`, rama `stage5a`, desde `8e0e993`.
- **5b, en paralelo con la 5a,** en la copia principal:
1. sincroniza `testdata` con `datekeys-go` en `c531e93`, la cabeza de `v0.12`, cuyo `testdata` es idéntico al de `601e6d2`, el de TypeScript. `specVersion` sigue en 0.11, y la sincronización trae 26 fixtures y 218 mutaciones. Después regenera los vectores;
2. hace los compromisos, `SECURITY_CBOR`, la firma `alg` 1 y los veredictos con sus textos y líneas, conectados a la apertura.
- **5c, después de integrar las dos:** `securitycms`, es decir, `alg` 2 y `seal_type` 2 en los veredictos, con los 135 casos de `security_cms.json`.
**La 5b, hecha y revisada el 5-10:** `95f9c5a` a `a7a7423`, en `v0.11`. El gate pasa con 1 378 pruebas en la VM y 305 en Node, y `testdata` tiene 133 ficheros, en `c531e93`.
Qué hizo:
- **Sincronización:** al pasar `testdata` a la v0.12 no apareció ninguna diferencia con Go, así que la librería no necesitó arreglos. Go regeneró los vectores que leen `testdata`.
- **Firma y sello:**
- `author.dart`, los compromisos;
- `security.dart`, `SECURITY_CBOR` y su evaluación, con la interfaz `CmsEvaluator` para la 5c;
- los textos y las líneas de los veredictos en `verdicts.dart`.
El evaluador real es el de por defecto al abrir. `alg` 2 y `seal_type` 2 quedan «no evaluados» hasta la 5c, sin inventar un veredicto.
- **Pruebas:** 23 de los 24 casos de `security.json` coinciden con Go; el que falta es de la 5c. Un diferencial de 1 730 evaluaciones de Go coincide también, y las pruebas detectaron los 12 fallos inyectados.
La revisión de la sesión comparó con Go los compromisos, byte a byte, y `EvaluateSecurityIn`, `evaluateSignature` y `setSeal`. Coinciden también en que un pánico o un fallo de una parte solo afecta a su propio veredicto. No encontró fallos.
**La 5a, hecha, revisada e integrada encima de la 5b el 5-10:** `68eb96e` a `6ba6893`. El gate pasa con 1 422 pruebas en la VM y 322 en Node. El worktree y la rama `stage5a` están borrados.
Qué hizo: `cms.dart`, el port de `internal/cms` con el perfil de certificado de la v0.12, sobre `nist_curves.dart`, `ecdsa.dart` (P-256, P-384 y P-521, porque Go la acepta) y `rsa.dart` (PKCS#1 v1.5 y PSS, de 2048 a 4096 bits).
- **Vectores:** salen de Go como una prueba de Go en una exportación, porque Go 1.26 no deja fijar el azar de las firmas fuera de un binario de pruebas.
- **Pruebas:** detectaron los 8 fallos inyectados en la VM.
La revisión de la sesión comparó con la librería criptográfica de Go:
- ECDSA, con r y s de 1 a n − 1, s alta aceptada, el hash recortado a los bits del orden y la lectura `cryptobyte` de la firma;
- RSA, con la codificación PKCS#1 v1.5 completa, PSS con la sal igual al hash y la comprobación de longitudes;
- los límites de las claves RSA de `internal/cms`.
El resto del lector lo cubren los vectores de Go.
Según el agente, `cms.ParseCert` de Go no comprueba que el certificado sea DER, y el de TypeScript sí. La sesión lo comprobó: el `parse` de `internal/cms` pasa `der.Check` a toda la firma y a todo el token antes de leer sus certificados, así que la diferencia no se puede dar dentro de una cápsula.
**La 5c, hecha y revisada el 5-10:** `6ad251b` a `a14a3b8`. Con ella la etapa 5 queda completa. El gate pasa con 1 572 pruebas en la VM y 332 en Node.
Qué hizo:
- **`securitycms.dart`:** `cmsReader`, el port de `evaluateCMS`, `signerLine` y `evaluateSeal` de `signature2.go`. Es el evaluador de CMS por defecto, así que abrir una cápsula evalúa su área de seguridad entera como Go.
- **Exportaciones:** `encodeSigners` y `maxSigners`.
Resultados:
- coinciden con Go los 135 casos de `security_cms.json`, los 24 de `security.json`, las 161 partes que dejó la 5b y 760 casos nuevos de Go;
- `format3_signed_cms` y `format3_sealed` abren con los veredictos de sus registros;
- las pruebas detectaron los 12 fallos inyectados, en la VM y en Node.
El encargo decía que la validez del certificado se comprueba en la hora de la ronda. Era un error del encargo: Go y el §29.10, paso 6, la comprueban en t, la hora del sello. El agente siguió a Go.
La revisión de la sesión comparó `securitycms.dart` con `signature2.go`: el orden de las comprobaciones, los resultados de cada firmante, F2, F5 y F6, S1 a S5, y la hora cero de Go como «sin hora de ronda». No encontró fallos.
**Las etapas 6 y 7, desde el 6-10.** El autor dijo «continúa» a aceptar, por ahora, que el cifrado tlock no sea de tiempo constante; queda documentado. Se reparten en dos fases.
**Fase 1, hecha, revisada, integrada y subida el 6-10:**
- **6a, el escritor de `age`:** `6837b9c` a `fb86216`.
- `random.dart`: `RandomSource`, con `secureRandom` por defecto y `SeededRandomSource` para las pruebas, y `randomIndex` y `permute` como los de Go.
- `age_writer.dart`: `AgeEncryptor` en streaming, `ageEncrypt` y las longitudes de un fichero antes de escribirlo.
- `recipient.dart`: X25519, con `age1…` y `checkX25519Recipient`; scrypt; y tlock, con la etiqueta única de Go.
- Comprobación: el generador hace correr el `age.Encrypt` real de Go con un `crypto/rand.Reader` fijado, y Dart escribe los mismos bytes. Go abre todo lo que escribe Dart.
- Revisión de la sesión: comparó con Go el `EncryptWriter` (el último trozo de tamaño completo, el vacío solo para un texto vacío, nada que vaya tras `Close`), `X25519Recipient.Wrap`, `ParseX25519Recipient` y `crypto/rand.Int`. No encontró fallos.
- **7a, el localizador y el sobre, salvo el sellado:** `09eaf21` a `b23a0ee`, integrada encima de la 6a.
- `locator.dart`: `CapsuleInfo`, `checkAddressUri` con el §44.1 de la v0.12, `checkResolvedIp` para la app, `Locator`, `openLocator`, `splitEnvelope` y `hideRest`.
- `ipaddr.dart`: `netip` de Go, en código propio.
- `StandardExtensions` comprueba ya por defecto `datekeys.capsule`, como `locator.Standard()`.
- Revisión de la sesión: comparó con Go los bloques no públicos de IPv4 e IPv6 y `publicIP`, y coinciden. El resto lo cubren unos 6 000 casos de Go.
- Lo que el port encontró en Go está en el handoff, en «Decisiones pendientes del autor».
El gate pasa con 1 713 pruebas en la VM y 432 en Node.
**Fase 2, hecha, revisada e integrada el 6-10.** El autor dijo «sí» a lanzarla. Dos agentes Opus en paralelo, con el esquema de siempre: la 6b en la copia principal, rama `v0.11`, y la 7b en el worktree `datekeys-dart-stage7b`, rama `stage7b`, desde `b23a0ee`. La 7b se integró encima de la 6b con `rebase`; el worktree y la rama están borrados. El gate pasa con 2 059 pruebas en la VM y 665 en Node, y `v0.11` está en `d77ef9e`.
- **6b, el escritor de cápsulas del formato 3:** `4c9bc72` a `6239e30`.
- `encrypt3.dart`: `encryptFiles`, port de `EncryptFiles`, `sealer` y el área de seguridad, con `EncryptOptions`, `FileSource`, `EncryptResult`, `CapsuleWriteException` y los enganches `AuthorSigner`, `CmsSigner` y `Sealer`, que pueden devolver un `Future`. El reloj es la opción `now`, obligatoria. El sink se cierra solo con la cápsula completa y comprobada, y se aborta ante cualquier fallo.
- Vectores de Go, como una prueba de Go en una exportación: 87 recetas. Las 21 cápsulas salen iguales byte a byte, con su `.dkk`, su resultado y los mismos valores al azar en el mismo orden; los 66 errores dan el mismo texto, el mismo código y los mismos bytes escritos antes.
- En la otra dirección, Go abre con cada credencial las 12 muestras de Dart, sola y todas juntas, y vuelve a codificar cada capa a los mismos bytes.
- Las pruebas detectaron los 20 fallos inyectados.
- Una diferencia a propósito: Go sella un control de prueba para medir `SEALED_CONTROL`; Dart calcula la longitud y saca y descarta los mismos valores al azar, para que los siguientes coincidan. El formato 2 (`Encrypt`) no se porta.
- Tiempos: una cápsula pequeña `time_only`, 48 ms en la VM y 0,71 s en Node; un fichero de 64 MiB, 4,7 s en la VM, por el SHA-256.
- **7b, las claves de autor y el sellado del localizador:** `3358f5c` a `d77ef9e`.
- `authorkey.dart`, port de `authorkey`: `AuthorKey`, las cadenas `dkauthor1…` y `DKAUTHOR-SECRET-KEY-1…` y el fichero cifrado con scrypt de logN 16. `go_unicode.dart`, generado con Go, da la caja y los espacios de Go, para que los textos de error coincidan con cualquier entrada.
- `ed25519_sign.dart`: la firma de TweetNaCl con el SHA-512 de `package:crypto`, exacta en la VM y en JavaScript, sin `BigInt` con secretos.
- `locator_seal.dart`: `sealLocator` y `newEnvelope`, `Seal` y `NewEnvelope` de Go.
- Con los mismos valores al azar, Dart escribe los bytes de Go en todos los casos, y Go abre las 15 muestras de Dart. Las pruebas detectaron los 20 fallos inyectados.
- Diferencias: tras `clear`, la clave lanza un `StateError`, donde Go firma con una clave a ceros; y se copia un fallo de Go, el texto de `PublicString` con los números al revés.
- Tiempos en la VM: una firma, 4,5 ms; el fichero de clave, 0,57 s al escribirlo y al leerlo.
- **Al integrar:** la interfaz de la firma de `alg` 1, que la 6b llamaba `AuthorKey`, pasa a `AuthorSigner`, y la `AuthorKey` de la 7b la implementa. Las pruebas del escritor firman las cápsulas de `alg` 1 con la clave de la semilla de Go, y escriben los mismos bytes que Go.
La revisión de la sesión comparó con Go `EncryptFiles`, `newSealer`, `newHead`, `security`, `write`, `readSource`, `accessRecipients` y `fillSlots`, y `authorkey`, `Seal` y `NewEnvelope`. No encontró fallos. Una nota menor: si `AuthorSigner.sign` lanza, el error no lleva el prefijo `capsule:`; en Go esa firma no puede fallar.
**Con esto están hechas todas las etapas del plan.**

Powered by TurnKey Linux.