|
|
# datekeys-dart
|
|
|
|
|
|
Librería Dart del protocolo DateKeys: DateKey, DateKeyCap (`.dkc`) y DateKeys Access Key (`.dkk`). Es Dart puro, sin Flutter ni plataforma, para que la app Flutter la use como dependencia de ruta (`datekeys: { path: ../datekeys-dart }`).
|
|
|
|
|
|
Es la tercera implementación de la especificación, después de la de referencia en Go (`datekeys-go`) y la de TypeScript (`datekeys-ts`). Como la de TypeScript, no genera datos de prueba propios: se contrasta con el `testdata/` de `datekeys-go`, y con los vectores de `test/vectors/`, que calcula Go con los programas de `tool/` (ver «Datos de prueba»).
|
|
|
|
|
|
## Estado
|
|
|
|
|
|
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 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.
|
|
|
|
|
|
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:
|
|
|
|
|
|
| Módulo | Contenido | En Go |
|
|
|
|---|---|---|
|
|
|
| `lib/src/errors.dart` | Los 19 códigos del §69 en su orden (`ErrorCode`), `DateKeysException` con el mensaje `contexto: CÓDIGO`, `wrap`, `withContext` y `errorCode` | `errors.go` |
|
|
|
| `lib/src/bytes.dart` | Hexadecimal, comparación y concatenación; UTF-8 estricto que conserva un U+FEFF inicial; `utf8.DecodeRune` y el `%q` de Go, con la tabla de `strconv.IsPrint` de Go 1.26 | `unicode/utf8`, `strconv` |
|
|
|
| `lib/src/cbor.dart` | `CborEncoder`, `CborDecoder`, `unmarshalCbor`, `peekSchema`, `checkSchema` y `walkCbor` | `codec/codec.go` |
|
|
|
| `lib/src/der.dart` | `check`, `split`, `content`, `setOfSorted` y `parseTime`, con UTCTime y GeneralizedTime exactos al nanosegundo. Es interno, como en Go | `internal/der/der.go`, ya del borrador v0.12 |
|
|
|
|
|
|
La etapa 2 trae las primitivas, todas de código propio salvo SHA-512, y la lectura de los ficheros `age` de `filippo.io/age` v1.3.2. Son internas, como en Go y en `datekeys-ts`: `lib/datekeys.dart` no las exporta.
|
|
|
|
|
|
| Módulo | Contenido | En Go |
|
|
|
|---|---|---|
|
|
|
| `lib/src/sha256.dart` | SHA-256 con su compresión; HMAC-SHA256 con los estados interior y exterior de la clave calculados una vez; HKDF-SHA256 (RFC 5869); PBKDF2-HMAC-SHA256 (RFC 8018), cuyas iteraciones son dos compresiones sobre palabras, sin bytes entre medias | `crypto/sha256`, `crypto/hmac`, `x/crypto/hkdf`, `x/crypto/pbkdf2` |
|
|
|
| `lib/src/scrypt.dart` | scrypt (RFC 7914) con Salsa20/8, con las comprobaciones y los textos de `scrypt.Key` | `x/crypto/scrypt` |
|
|
|
| `lib/src/chacha20poly1305.dart` | ChaCha20, Poly1305 y ChaCha20-Poly1305 (RFC 8439); el tag se compara en tiempo constante | `x/crypto/chacha20poly1305` |
|
|
|
| `lib/src/curve25519.dart` | X25519 (RFC 7748) con clamping y el secreto todo a ceros rechazado; la verificación estricta de Ed25519 del §29.9, con `canonical`, `smallOrder` y `onCurve` | `x/crypto/curve25519`, `internal/ed25519strict` |
|
|
|
| `lib/src/base64.dart` | El Base64 de Go, con su modo estricto y el offset de sus errores | `encoding/base64` |
|
|
|
| `lib/src/bech32.dart` | El Bech32 de `age`, con sus textos de error | `codec/bech32` |
|
|
|
| `lib/src/age.dart` | La cabecera y sus límites (2 MiB, 1024 stanzas, un tipo y 128 argumentos por stanza), el MAC de la cabecera, los stanzas X25519 y scrypt, la clave del payload y el STREAM, con los mismos casos de fin de fichero que Go. Cada fallo es una `AgeException` con el texto de Go y su fase: la cabecera (`age.Decrypt`) o el payload (la lectura del STREAM) | `filippo.io/age`, `internal/format`, `internal/stream` |
|
|
|
| `lib/src/agewrap.dart` | Las reglas de stanzas de `OUTER_TIME_AGE`, `PAYLOAD_AGE` e `INNER_ACCESS_AGE`, la lectura de los stanzas sin secretos y las identities del payload y del acceso, con los textos y los códigos de Go | `agewrap` |
|
|
|
|
|
|
Notas de la etapa 2:
|
|
|
- **La identity de scrypt** rechaza por defecto un factor de trabajo por encima de 16, el de los ficheros de clave de autor (§29.12), como `SetMaxWorkFactor(16)` en el `authorkey` de Go. El de `age` es 22: 4 GiB de memoria, que un fichero hostil podría pedir a un móvil. `ScryptIdentity(maxWorkFactor: …)` lo cambia.
|
|
|
- **El STREAM** se descifra según llega el texto cifrado (`AgePayloadDecryptor`), como el lector de Go: entrega el texto de cada chunk en cuanto se autentica, y un fallo que revela un byte posterior llega en la llamada siguiente.
|
|
|
- **La identity de `OUTER_TIME_AGE`** se compone en la etapa 4, con el perfil: `checkTimeStanzas` de esta etapa, que recibe la ronda, el chain hash y el id del perfil, y `checkTlockProfile` y `unwrapTlockStanza` de la etapa 3.
|
|
|
|
|
|
La etapa 3 porta BLS12-381 de `kilic/bls12-381` v0.1.0, sobre el que calcula `drand/kyber-bls12381` v0.3.4 para drand y tlock; el IBE de `encrypt/ibe` de `drand/kyber` v1.3.2, el de `tlock` v1.2.0 con Quicknet; y `provider.Verify` y el stanza tlock de `agewrap` de `datekeys-go`, con las mismas comprobaciones en el mismo orden y los mismos textos de error. Como `datekeys-ts`, verifica solo el scheme de Quicknet, `bls-unchained-g1-rfc9380`. Todo es código propio: de `package:crypto` usa solo SHA-256.
|
|
|
|
|
|
| Módulo | Contenido | En Go |
|
|
|
|---|---|---|
|
|
|
| `lib/src/bls12381_fp.dart` | El cuerpo Fp sobre `BigInt` (`Fp`) y las sumas de productos sin reducir (`FpWide`): la única capa que toca la representación de un elemento | `fp.go` de kilic |
|
|
|
| `lib/src/bls12381_tower.dart` | Fp2, Fp6 y Fp12 con las fórmulas de kilic, cada coeficiente reducido una vez; el Frobenius y el cuadrado ciclotómico; GT serializado c1 antes que c0 en cada nivel | `fp2.go`, `fp6.go`, `fp12.go` de kilic |
|
|
|
| `lib/src/bls12381_curve.dart` | G1 y G2 en coordenadas jacobianas, su codificación comprimida y los veredictos de `FromCompressed` (§12.2): `checkCompressedPoint` | `g1.go`, `g2.go` de kilic |
|
|
|
| `lib/src/bls12381_pairing.dart` | El emparejamiento ate óptimo: el bucle de Miller con las rectas de kilic y su exponenciación final | `pairing.go` de kilic |
|
|
|
| `lib/src/bls12381_hash.dart` | `expand_message_xmd`, `hash_to_field`, el mapa SWU simplificado, la isogenia de grado 11 y el cofactor: el hash a G1 del RFC 9380 con el DST de Quicknet | `hash_to_field.go`, `swu.go`, `isogeny.go` de kilic |
|
|
|
| `lib/src/ibe.dart` | El IBE-CCA de tlock sobre G2 (§63 paso 11): H2, H3 con su rechazo de candidatos, H4, la identidad de la ronda, el descifrado y el cifrado, este con sigma inyectable | `encrypt/ibe` de kyber |
|
|
|
| `lib/src/release.dart` | `verifyRelease` (§17, §51, §63 paso 10), `Release`, `ReleaseSource`, `suppliedRelease` y `fetchRelease`, la regla del paso 9 | `provider` |
|
|
|
| `lib/src/tlock.dart` | El stanza tlock de `OUTER_TIME_AGE` (§32, §35, §63 paso 11): abrirlo, como `NewTimeIdentity` y su `Unwrap`, y escribirlo, como `NewTimeRecipient` | `agewrap` |
|
|
|
|
|
|
Notas de la etapa 3:
|
|
|
- **La capa del cuerpo.** `Fp` es un extension type sobre `BigInt`, sin envoltorio en ejecución, y `FpWide` una suma de productos sin reducir. La torre, las curvas, el emparejamiento y el hash solo usan sus operaciones: una implementación con limbs fijos, más rápida, cambiaría ese fichero y nada más.
|
|
|
- **Los veredictos de un punto** son los de `FromCompressed` de kilic: el flag de compresión a 1; el punto en el infinito solo como `0xc0` y ceros; coordenadas menores que p, sin reducir; un punto de la curva; y un punto del subgrupo de orden r. En G1 el subgrupo se comprueba con [r]·P = O; en G2, con ψ(P) = [x]·P (Scott, eprint 2021/1130, como noble), que las pruebas comparan con [r]·P = O en puntos fuera del subgrupo, también con torsión de cada orden pequeño del cofactor.
|
|
|
- **GT** es el valor del emparejamiento de kilic, con su exponenciación final, f^(3·(p⁴ − p² + 1)/r), y se serializa en su orden (§63, «Serialización de GT en H2»).
|
|
|
- **El hash a G1** sigue a kilic, que suma las dos salidas del mapa en E′ antes de la isogenia. kilic se aparta del RFC 9380 solo donde ningún hash llega: dos salidas iguales u opuestas, que suma con la fórmula de E. Este código las suma con la ley de grupo de E′, como el RFC.
|
|
|
- **Las diferencias con Go, a propósito,** son las de `datekeys-ts`: otro scheme que el de Quicknet da `ERR_UNKNOWN_PROFILE` con un texto propio, donde Go verificaría los otros schemes de drand; y el punto en el infinito nunca es una firma válida (§63 paso 10), mientras que Go la acepta si la clave pública también es el punto en el infinito, una clave que ningún perfil pinneado tiene (§12.1). Las pruebas fijan las dos.
|
|
|
- **El stanza tlock.** `unwrapTlockStanza` recibe los argumentos y el cuerpo del único stanza y hace lo que `NewTimeIdentity` y su `Unwrap` en Go: el perfil, los argumentos, otra vez el release y el cuerpo. El número y el tipo de los stanzas los comprueban las reglas de `agewrap` de la etapa 2, que necesitan la cabecera entera. Se guarda el último release verificado: el paso 11 vuelve a verificar el del paso 10, como en Go, sin otro emparejamiento.
|
|
|
- **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 4a de la etapa 4 porta `internal/pathrule` y `wordkey` de `datekeys-go` en `c531e93`, la cabeza de la rama `v0.12`, que sigue la v0.11 con las correcciones de la revisión del 2 de octubre; los dos paquetes no cambian desde el tag `spec-v0.11`. Tienen las mismas comprobaciones, en el mismo orden y con los mismos textos de error:
|
|
|
|
|
|
| Módulo | Contenido | En Go |
|
|
|
|---|---|---|
|
|
|
| `lib/src/pathrule_tables.dart` | Las tablas de Unicode 18.0.0 y de WindowsBestFit del §29.5.1, que escribe `internal/pathrule/gen -dart` de `datekeys-go`; no se editan | `internal/pathrule/tables.go` |
|
|
|
| `lib/src/pathrule.dart` | NFD con el orden canónico y la descomposición de Hangul, el pliegue de CaseFolding con U+0131 → U+0069, la minúscula simple, `Default_Ignorable` con la lista blanca de R4 y las proyecciones best-fit; las reglas de una ruta (R2 a R6c y R10), las del árbol (R7 con su clave, y R9) y las de los textos del §29.6, el comentario y el autor declarado; y `canonicalTables`, cuyo SHA-256 es `tablesDigest` | `internal/pathrule` |
|
|
|
| `lib/src/wordkey.dart` | La llave de palabras del §38.1: `normalizeWords`, `checkWords`, `wordKey`, con el PBKDF2-HMAC-SHA256 de `sha256.dart` y 600 000 iteraciones sobre `wordKeyPassword` y `wordKeySalt`, la P y la S del §38.1, y `wordIdentity` | `wordkey` |
|
|
|
|
|
|
Notas de la parte 4a:
|
|
|
- **Bytes, como Go.** Go lee un string como bytes, y estas funciones también:
|
|
|
- las acabadas en `Utf8` toman los bytes de un string de Go. Un byte que no es UTF-8 válido es la runa U+FFFD, como en el `for range` de Go. Los límites cuentan bytes, salvo el de R3 sobre el NFD, en unidades UTF-16, como dice el spec, y el de R6b, en puntos de código;
|
|
|
- las demás toman un `String` como lo escribe `utf8Bytes`, que pone un surrogate suelto como los tres bytes de su punto de código, y dan lo que da Go con esos bytes.
|
|
|
|
|
|
Un lector solo ve UTF-8 válido (§58), y un escritor rechaza antes un `String` mal formado, como el de Go. Aun así, cualquier entrada da el resultado de Go, rarezas incluidas. R4b busca el final de un segmento sumando el tamaño de cada runa, así que Go cuenta cada byte no válido como los tres bytes de U+FFFD: acepta los bytes `ff 61 e2 80 8d`, con el ZWJ al final, y dice «U+200D at the end» de un ZWJ que no lo está. Los vectores lo fijan.
|
|
|
- **Ninguna función de Unicode de la plataforma** (§29.5.1): ni `toLowerCase` ni `RegExp`. La expresión regular de R6b se comprueba a mano: la base acaba en `~` y de 1 a 6 dígitos ASCII.
|
|
|
- **Los errores.** `PathRuleException` es `pathrule.Error`: la regla, el detalle y, en R7, las dos rutas que nombra, la posterior primero. No lleva código normativo: la cabecera lo dará como `ERR_HEAD_INVALID` y la nota pública como `ERR_EXTENSION_DATA_INVALID`, en la etapa 4c. Como en Go, cada regla devuelve su violación y solo las funciones públicas lanzan. `WordKeyException` lleva el texto de `wordkey.Check`.
|
|
|
- **La ronda** de la llave de palabras es un `int` de 0 a 2^53-1, como toda ronda de un perfil pinneado; en Go es un `uint64`.
|
|
|
- **Una diferencia con `datekeys-ts`:** con un `String` mal formado, que ningún lector ni escritor pasa a estas reglas, `datekeys-ts` toma un surrogate suelto como un punto de código; aquí son los tres bytes que daría Go.
|
|
|
- **Lo que se exporta.** `lib/datekeys.dart` exporta la llave de palabras, como el paquete público `wordkey` de Go: `normalizeWords`, `checkWords`, `wordKey`, `WordKeyException` y sus constantes. La CLI de Go y `datekeys-ts` derivan la llave fuera de la apertura y la pasan como una identity más. `wordIdentity` es interna, porque devuelve una identity de `age.dart`; las reglas de rutas y textos también lo son, como `internal/pathrule`, y las usarán la cabecera y la nota de la etapa 4c.
|
|
|
|
|
|
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 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`;
|
|
|
- `CborDecoder.uint` devuelve un `int`, porque todos los esquemas acotan sus enteros en 2^53-1, y `uint64` devuelve un `BigInt`;
|
|
|
- SHA-256, ChaCha20 y Salsa20 trabajan con palabras de 32 bits: suman de dos a cinco y enmascaran la suma, y en cada rotación enmascaran la mitad desplazada a la izquierda, así que la VM y la web dan los mismos 32 bits;
|
|
|
- Poly1305 guarda el acumulador y r en diez limbs de 13 bits: cada producto queda por debajo de 2^31 y cada suma por debajo de 2^35, y sus acarreos se toman con `~/`, no con un desplazamiento, que en la web truncaría a 32 bits;
|
|
|
- el cuerpo de curve25519 es el de TweetNaCl en su versión de JavaScript: dieciséis limbs de 16 bits en un `Float64List`, con sumas de productos por debajo de 2^44;
|
|
|
- en BLS12-381 los elementos del cuerpo y los escalares son `BigInt`, y una ronda es un `int` hasta 2^53-1, cuyos 8 bytes se escriben como dos mitades de 32 bits;
|
|
|
- en las tablas de Unicode, la clase de combinación de un punto de código va en una entrada que vale el punto por 256 más la clase, por debajo de 2^29, así que sus desplazamientos quedan en 32 bits.
|
|
|
|
|
|
**`BigInt` no es de tiempo constante.** En las primitivas se usa solo con datos públicos: la reducción de escalares módulo ℓ y las comprobaciones de `onCurve` en Ed25519, que solo verifica, y el factor de trabajo de un stanza scrypt. Los secretos de las primitivas (el escalar de X25519, las claves de HMAC, ChaCha20 y Poly1305) van en la aritmética de limbs, sin ramas ni índices que dependan de ellos; ni la VM ni un motor de JavaScript prometen tiempo constante, aun así.
|
|
|
|
|
|
BLS12-381, en cambio, hace con `BigInt` toda su aritmética. Verificar un release y descifrar un stanza tlock solo manejan datos públicos: la firma de la ronda lo es desde que drand la publica, y el stanza va en la cápsula. Cifrar no: sigma y r son secretos, y el tiempo del código depende de ellos. El escritor (etapa 6) cifrará el stanza tlock donde ese tiempo no se pueda observar, o con una capa del cuerpo de limbs fijos sin ramas que dependan de los datos.
|
|
|
|
|
|
Las etapas siguientes traen el resto del protocolo en este orden:
|
|
|
|
|
|
| Etapa | Contenido |
|
|
|
|---|---|
|
|
|
| 5 | Firma y sello, con los textos de los veredictos, en el `SecurityEvaluator` de la apertura |
|
|
|
| 6 | Escritor |
|
|
|
| 7 | Localizador y claves de autor |
|
|
|
|
|
|
## Rendimiento
|
|
|
|
|
|
Las cifras del 5 de octubre de 2026 son del PC de desarrollo (Windows 11, Dart 3.13, Node.js 24.9). No hay cifras de un móvil de gama media: están por medir.
|
|
|
|
|
|
### Primitivas
|
|
|
|
|
|
`dart run tool/bench.dart` mide las primitivas en la VM. Compilado a JavaScript:
|
|
|
|
|
|
```bash
|
|
|
dart compile js -O2 -o bench.js tool/bench.dart
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
node bench.js
|
|
|
```
|
|
|
|
|
|
| Operación | VM | Node.js |
|
|
|
|---|---|---|
|
|
|
| X25519, un acuerdo de claves | 1,2 ms | 0,9 ms |
|
|
|
| Ed25519 estricto, una verificación | 4,5 ms | 9,4 ms |
|
|
|
| ChaCha20-Poly1305, 1 MiB, cifrar o descifrar | 21 ms | 16 ms |
|
|
|
| PBKDF2-HMAC-SHA256, 600 000 iteraciones (§38.1) | 1,1 s | 0,9 s |
|
|
|
| scrypt con logN 16 y r 8 (§29.12) | 0,55 s | 0,9 s |
|
|
|
|
|
|
En la VM, el HMAC de `package:crypto`, que calcula otra vez los estados de la clave en cada llamada, tarda unos 3,5 s en las mismas 600 000 iteraciones.
|
|
|
|
|
|
La app llama a PBKDF2 y a scrypt en un `Isolate` (plan, «Rendimiento»).
|
|
|
|
|
|
### BLS12-381 y tlock
|
|
|
|
|
|
`dart run tool/bls12381_bench.dart` mide BLS12-381 y tlock en la VM. Compilado a JavaScript:
|
|
|
|
|
|
```bash
|
|
|
dart compile js -O2 -o bench.js tool/bls12381_bench.dart
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
node -e "globalThis.self = globalThis; require('./bench.js')"
|
|
|
```
|
|
|
|
|
|
Son medianas de 15 ejecuciones en la VM y de 7 en Node.js:
|
|
|
|
|
|
| Operación | VM | Node.js |
|
|
|
|---|---|---|
|
|
|
| Decodificar un punto de G1, la firma | 3 ms | 60 ms |
|
|
|
| Decodificar un punto de G2, U | 1,3 ms | 40 ms |
|
|
|
| Un emparejamiento | 11 ms | 230 ms |
|
|
|
| Verificar la firma de una ronda de Quicknet | 16 ms | 380 ms |
|
|
|
| La misma verificación, decodificando además la clave pinneada | 18 ms | 420 ms |
|
|
|
| Descifrar un stanza tlock (IBE) | 20 ms | 435 ms |
|
|
|
| Cifrar un stanza tlock (IBE) | 20 ms | 460 ms |
|
|
|
| Pasos 10 y 11: verificar el release y abrir el stanza | 35 ms | 760 ms |
|
|
|
|
|
|
La verificación hace el hash a G1, decodifica la firma y comprueba e(H(m), clave) · e(−firma, G2) = 1 con un solo bucle de Miller para los dos pares y una exponenciación final. El descifrado decodifica U, hace un emparejamiento y comprueba U = r·G2. En los pasos 10 y 11, el paso 11 verifica otra vez el release, como en Go, sin repetir el emparejamiento del paso 10.
|
|
|
|
|
|
Una estimación para un móvil: si uno de gama media, con Dart compilado AOT para ARM64, va de dos a cuatro veces más lento que esta VM, la verificación y el descifrado tardarán entre 30 y 80 ms cada uno, y los pasos 10 y 11 juntos, entre 70 y 150 ms. La app los llama en un `Isolate` (plan, «Rendimiento»). En la web, el `BigInt` de dart2js es unas veinte veces más lento que el de la VM; una capa del cuerpo de limbs fijos sería la forma de acelerarlo.
|
|
|
|
|
|
### Rutas, textos y llave de palabras
|
|
|
|
|
|
`dart run tool/pathrule_bench.dart` mide las reglas de rutas y textos y la llave de palabras en la VM. Compilado a JavaScript:
|
|
|
|
|
|
```bash
|
|
|
dart compile js -O2 -o pathrule_bench.js tool/pathrule_bench.dart
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
node pathrule_bench.js
|
|
|
```
|
|
|
|
|
|
Son medianas de tres ejecuciones, el 5 de octubre de 2026:
|
|
|
|
|
|
| Operación | VM | Node.js |
|
|
|
|---|---|---|
|
|
|
| `checkPath` de una ruta ASCII de 38 caracteres | 9,5 µs | 12 µs |
|
|
|
| `checkPath` de una ruta de 28 caracteres con acentos y U+2665, que R6c proyecta con sus 15 tablas | 28 µs | 38 µs |
|
|
|
| `checkComment` de un comentario de 16 351 bytes, casi el máximo | 0,7 ms | 0,6 ms |
|
|
|
| `checkTree` de 1000 rutas | 5,3 ms | 6,1 ms |
|
|
|
| La cabecera más grande: `checkPath` de 65 535 rutas como la segunda, y `checkTree` | 2,0 s | 2,3 s |
|
|
|
| `normalizeWords` y `checkWords` de seis palabras | 6,6 µs | 9,2 µs |
|
|
|
| `wordKey`: PBKDF2-HMAC-SHA256 con 600 000 iteraciones (§38.1) | 1,1 s | 0,7 s |
|
|
|
|
|
|
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 |
|
|
|
|---|---|---|
|
|
|
| Librería | `version` de `lib/src/version.dart` y de `pubspec.yaml`, que una prueba mantiene iguales | `0.1.0-dev` |
|
|
|
| Especificación | `specVersion` de `lib/src/version.dart`: la que nombra el campo `spec` de cada fichero de `testdata/` | `0.11` |
|
|
|
|
|
|
La rama sigue la versión de la especificación: `v0.11`, hasta que `datekeys-go` cierre la v0.12. La etapa 5 se sincronizará ya con la v0.12.
|
|
|
|
|
|
## Dependencias
|
|
|
|
|
|
- **En ejecución:** solo `package:crypto`, para SHA-1, SHA-2 y HMAC. Todo lo demás es código propio: HKDF, PBKDF2, scrypt, ChaCha20-Poly1305, X25519, Ed25519, BLS12-381, ECDSA, RSA, DER, CMS y CBOR. Hoy la librería usa de `package:crypto` SHA-512, en Ed25519, y SHA-256, en BLS12-381 y tlock. Las primitivas tienen además su propio SHA-256 con HMAC-SHA256, porque PBKDF2 los necesita con los estados de la clave calculados una vez.
|
|
|
- **En desarrollo:** solo `package:test`, aprobado el 5 de octubre de 2026. No hay paquete de lints: las reglas del análisis están en `analysis_options.yaml`.
|
|
|
- **`pubspec.lock`** va en el repositorio, para que las pruebas usen siempre las mismas versiones.
|
|
|
|
|
|
Nada nuevo sin aprobación escrita del autor.
|
|
|
|
|
|
## Comprobar
|
|
|
|
|
|
```bash
|
|
|
dart pub get
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
tool/check.sh
|
|
|
```
|
|
|
|
|
|
`tool/check.sh` es el gate local, como `scripts/check.sh` en `datekeys-go` y `npm run verify` en `datekeys-ts`. Comprueba:
|
|
|
- el formato;
|
|
|
- `dart analyze --fatal-infos`;
|
|
|
- `dart test`;
|
|
|
- `dart test -p node`: las pruebas que no leen ficheros corren también compiladas a JavaScript, en Node.js, para comprobar que los enteros son exactos en la web. Las que leen ficheros llevan `@TestOn('vm')`. Por eso el gate necesita Node.js, como `datekeys-ts`; lo decidió el autor el 5 de octubre de 2026;
|
|
|
- la copia de `testdata/` frente al repositorio de Go, que debe estar al lado, en `../datekeys-go`.
|
|
|
|
|
|
## Datos de prueba
|
|
|
|
|
|
`testdata/` es una copia de `datekeys-go/testdata` en un commit fijo. `testdata/SOURCE.json` registra el commit y el SHA-256 de cada fichero, igual que en `datekeys-ts`. La copia actual es la del tag `spec-v0.11` (`ae33434`).
|
|
|
|
|
|
```bash
|
|
|
dart run tool/sync_testdata.dart sync --commit spec-v0.11
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
dart run tool/sync_testdata.dart check --against ../datekeys-go
|
|
|
```
|
|
|
|
|
|
- `sync` lee los ficheros con git en ese commit, así que nunca entran cambios sin commit del repositorio de 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, 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
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
cd ../datekeys-go && go run ../datekeys-dart/tool/gen_age_vectors.go -out ../datekeys-dart/test/vectors
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
cd ../datekeys-go && go run ../datekeys-dart/tool/bls12381_go_vectors.go ../datekeys-ts/src/lib/dkc/testing/bls12381-vectors.json > ../datekeys-dart/test/vectors/bls12381_vectors.json
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
cd ../datekeys-go && go run ../datekeys-dart/tool/ibe_go_vectors.go ../datekeys-dart/testdata/fixtures ../datekeys-ts/src/lib/dkc/testing/ibe-vectors.json > ../datekeys-dart/test/vectors/ibe_vectors.json
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
cd ../datekeys-go && go run ../datekeys-dart/tool/tlock_go_vectors.go ../datekeys-ts/src/lib/dkc/testing/tlock-vectors.json > ../datekeys-dart/test/vectors/tlock_vectors.json
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
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
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
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
|
|
|
commit=$(git -C ../datekeys-go rev-parse v0.12)
|
|
|
out=$PWD/test/vectors
|
|
|
tmp=$(mktemp -d)
|
|
|
git -C ../datekeys-go archive "$commit" | tar -x -C "$tmp"
|
|
|
cp tool/pathrule_go_vectors.go "$tmp"
|
|
|
(cd "$tmp" && go run ./pathrule_go_vectors.go -source "$commit" -out "$out")
|
|
|
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` |
|
|
|
| `primitives.g.dart` | El mismo JSON como constante de Dart, para las pruebas compiladas a JavaScript, que no leen ficheros |
|
|
|
| `age.json` | Ficheros con stanzas X25519 y scrypt, sus cortes y manipulaciones, un corpus de cabeceras contra la gramática del §28.1 y el límite de 2 MiB, y las reglas e identities de `agewrap`, con el texto del error de Go en cada caso |
|
|
|
| `age_fixtures.json` | El `PAYLOAD_AGE` de cada fixture con su `payload_identity`, y el `INNER_ACCESS_AGE` de los `time_and_key`, que Go saca de `OUTER_TIME_AGE` con el release del fixture |
|
|
|
| `bls12381_vectors.json` | Las 157 codificaciones límite congeladas de `datekeys-ts` con el veredicto de Go calculado otra vez, y, con una semilla fija, decodificaciones (con la clase del fallo de kilic: formato, curva o subgrupo), sumas, múltiplos, emparejamientos, hashes a G1 (también los mensajes del apéndice J.9.1 del RFC 9380), el mapa de un elemento, los excepcionales incluidos, y firmas BLS sobre G1 |
|
|
|
| `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 |
|
|
|
| `pathrule_vectors.json` | Las reglas de `internal/pathrule`: los casos de las pruebas de Go y de `datekeys-ts` y los dos lados de los límites de R2, R3 y R6b; 1300 cadenas y 350 árboles de una semilla fija (marcas combinantes, Hangul, ignorables, emoji, sosias best-fit de ASCII, nombres de dispositivo, alias 8.3, textos y bytes que no son UTF-8 válido) con el resultado de `CheckPath`, `CheckComment` y `CheckAuthor`, su NFD y su clave; los casos de R9; y, por plano, el SHA-256 de una línea por punto de código de cada función |
|
|
|
| `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.
|
|
|
- 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
|
|
|
|
|
|
Apache-2.0 (`LICENSE`), como `datekeys-go` y `datekeys-ts`. La especificación tiene su propia licencia, CC-BY-4.0.
|