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.
dateKeys-dart/CHANGELOG.md

16 KiB

Changelog

Cambios notables de la librería Dart. El proyecto usa versionado semántico; mientras sea 0.x, no hay promesa de estabilidad.

Especificación 0.11, en la rama v0.11 — sin versión

Etapa 4b: los formatos de la cápsula y de la llave de acceso (05-10-2026)

  • Las tramas (lib/src/framing.dart): el PRELUDE de un .dkc y la trama de una .dkk, con las comprobaciones de los §23 y §40 en su orden y los textos de Go; splitCapsule, los pasos 1 a 3 de la inspección, con su FramingException; y headerBinding. El formato de una cápsula es el enum CapsuleFormat.
  • El relleno (lib/src/padding.dart): paddedLength y payloadAgeLength con los códigos de PaddingRule, exactos hasta L_MAX también en la web, sin desplazamientos ni máscaras de más de 31 bits; y PaddingCheck, la comprobación del plaintext de PAYLOAD_AGE frente a L y P del paso 17, por trozos.
  • BODY del formato 3 (lib/src/body.dart): su trama y el área, sin el head. Y el capsule_digest incremental (lib/src/digest.dart). Son internos, como en datekeys-ts.
  • Las extensiones (lib/src/extension.dart): las reglas de un array, al decodificarlo y antes de escribirlo; los registros, con la comprobación de la data y los lugares del §72; las extensiones críticas y no críticas de cada objeto; y la regla de los codificadores del §72, checkWrite, con StandardExtensions, a la que se dan las comprobaciones de la nota y del localizador.
  • El Provider Profile (lib/src/profile.dart): su CBOR, profile_hash, las reglas 1 a 3 del §12.1 en su orden, el chain hash de drand, maxRound y el registro con Quicknet pinneado. Profile implementa el PinnedProfile de la etapa 3.
  • La DateKey (lib/src/datekey.dart): dk1_ con la aceptación y los textos de Go, también los de su JSON; la resolución de un instante a su ronda y la hora de una ronda; e Instant, al nanosegundo, con el RFC 3339 de time.Parse y de Format de Go.
  • PUBLIC_HEADER, CONTROL_CBOR de las versiones 1 a 3 y la .dkk (lib/src/header.dart, control.dart y accesskey.dart), en las capas del §69.1. I_PAYLOAD y access_material se copian una vez y se borran en todos los caminos.
  • Los errores que Go devuelve sin código normativo, como un código de relleno que no existe o una extensión fuera de su sitio al escribir, son ArgumentError con el texto de Go.
  • lib/datekeys.dart exporta los formatos, como index.ts de datekeys-ts.
  • Vectores de Go (tool/formats_go_vectors.go, en el contexto del módulo de datekeys-go en c531e93, sin cambiar nada en él): unos 6 400 casos en test/vectors/formats_*.json, con el resultado, el código y el texto de Go: tramas, cabeceras, controles, .dkk, perfiles, extensiones, dk1_, RFC 3339, rondas, relleno hasta L_MAX, la comprobación del relleno de capsule.Open, los codificadores, los límites del §57, BODY, y cada fallo de una lista, solo y con cada otro, para la precedencia del §69.1. formats_vectors.g.dart lleva uno de cada ocho para Node.js. Con el tag spec-v0.11 el generador da la misma salida, salvo la regla de los codificadores del §72, que ese tag no tiene.
  • Pruebas. Los 24 fixtures y las 6 .dkk; dk1.json, quicknet_rounds.json, profile_quicknet.json, padding.json y los 172 esquemas de cbor.json que la etapa 1 dejó aplazados; y el diferencial. 404 pruebas nuevas en la VM y 77 en Node.js: 771 y 190 en total, sin ninguna aplazada.
  • Fallos inyectados, uno a uno y revertidos: 24. Las pruebas detectan 21, en la VM, en Node.js o en las dos; el de Padmé con desplazamientos de más de 31 bits, solo en Node.js, como debe ser. Dos no se detectaban al principio, y por ellos el generador escribe ahora cada par de fallos de capas distintas y cada bit de FLAGS y RESERVED: una DateKey comprobada antes que la regla entre los arrays de extensiones, y el bit alto de FLAGS ignorado. Los otros tres no cambian ningún resultado: quitar la comprobación de la recodificación de PUBLIC_HEADER o de CONTROL_CBOR, porque sus decodificadores, como los de Go, ya rechazan toda forma no canónica, y leer una longitud con un desplazamiento de 24 bits, porque los operadores de bits compilados a JavaScript dan 32 bits sin signo.

Etapa 3: BLS12-381 y tlock (05-10-2026)

  • BLS12-381, de código propio, como lo calcula kilic/bls12-381 v0.1.0 para drand/kyber-bls12381 v0.3.4:
    • la capa del cuerpo, Fp, un extension type sobre BigInt, con FpWide para las sumas de productos sin reducir; la única que toca la representación, para que unos limbs fijos la puedan sustituir sola;
    • Fp2, Fp6 y Fp12 con las fórmulas de kilic, reduciendo cada coeficiente una vez, el Frobenius con sus coeficientes y el cuadrado ciclotómico;
    • G1 y G2, su codificación comprimida y los veredictos de FromCompressed (§12.2), con checkCompressedPoint, que lib/datekeys.dart exporta como datekeys-ts. El subgrupo de G2 se comprueba con ψ(P) = ·P;
    • el emparejamiento ate óptimo con las rectas y la exponenciación final de kilic: GT es su valor, serializado c1 antes que c0 en cada nivel;
    • el hash a G1 del RFC 9380 con el DST de Quicknet y de tlock, sumando las salidas del mapa en E′ como kilic.
  • El IBE de tlock (lib/src/ibe.dart), DecryptCCAonG2 y EncryptCCAonG2 de drand/kyber v1.3.2 para Quicknet, como ibe.ts: H2 sobre GT en el orden de kilic, H3 con su rechazo de candidatos, H4, la identidad de la ronda y las puertas de la firma y de U, con razones y textos fijos que no llevan ningún valor del cálculo. El cifrado admite un sigma dado, para reproducir byte a byte los vectores de Go; es interno hasta el escritor, la etapa 6.
  • Releases (lib/src/release.dart): verifyRelease es provider.Verify, en su orden y con sus textos, solo para el scheme de Quicknet, como release.ts; suppliedRelease; y fetchRelease, la regla del paso 9: lo que lance una fuente es ERR_RELEASE_UNAVAILABLE. Los exporta lib/datekeys.dart, con PinnedProfile, lo que lee del perfil, que llega con la etapa 4.
  • El stanza tlock (lib/src/tlock.dart): unwrapTlockStanza hace lo que NewTimeIdentity y su Unwrap con los argumentos y el cuerpo del stanza, y wrapTlockStanza lo que NewTimeRecipient, con los códigos y los textos de agewrap. Es interno: lo usará la apertura de la etapa 4.
  • Diferencias con Go, a propósito, las de datekeys-ts: otro scheme que el de Quicknet da ERR_UNKNOWN_PROFILE con un texto propio, y el punto en el infinito nunca es una firma válida, 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.
  • BigInt no es de tiempo constante. Verificar y descifrar solo manejan datos públicos; al cifrar, sigma y r son secretos. El README lo explica.
  • Vectores de Go en test/vectors/, que escriben cuatro programas de tool/ con las librerías de la caché de módulos, en el contexto del módulo de datekeys-go y sin cambiar nada en él:
    • bls12381_vectors.json: las 157 codificaciones límite de datekeys-ts con el veredicto de Go, y decodificaciones, sumas, múltiplos, emparejamientos, hashes a G1, el mapa de un elemento y firmas BLS con una semilla fija;
    • ibe_vectors.json, port del generador de datekeys-ts sobre los fixtures de testdata/: GT y H2, H3, H4, identidades de rondas, el stanza de cada fixture con su file key, los ciphertexts de kyber y los veredictos de DecryptCCAonG2;
    • tlock_vectors.json, port también: el cifrado de kyber con sigma fijo y los ciphertexts que escribió datekeys-ts y abre Go;
    • release_vectors.json: los veredictos, códigos y textos de provider.Verify, NewTimeIdentity con Unwrap y NewTimeRecipient.
  • Pruebas. 71 nuevas en la VM y 30 en Node.js. La etapa se hizo en paralelo con la 2, en otra rama, desde la etapa 1; con las dos integradas hay 367 pruebas en la VM, más una aplazada, y 113 en Node.js. Las que leen los vectores llevan @TestOn('vm'); en Node.js corren las propiedades de la aritmética y unos pocos vectores de Go copiados en Dart, que una prueba en la VM compara con los JSON. Un emparejamiento tarda allí un cuarto de segundo, así que los bucles sacan menos casos.
  • Fallos inyectados, uno a uno y revertidos: 25, en la torre, las curvas, el emparejamiento, el hash, el IBE, los releases y el stanza. Las pruebas los detectan todos, en la VM y en Node.js.
  • tool/bls12381_bench.dart mide BLS12-381 y tlock en la VM y compilado a JavaScript: en la VM, un emparejamiento tarda unos 11 ms, la verificación de la firma de una ronda 16 ms y el descifrado de un stanza 20 ms. Las cifras, en el README.

Etapa 2: primitivas y age (05-10-2026)

  • Primitivas, de código propio. Son internas: lib/datekeys.dart no las exporta.
    • SHA-256 con su compresión, y HMAC-SHA256 con los estados interior y exterior de la clave calculados una vez. Sobre ellos, HKDF-SHA256 (RFC 5869) y PBKDF2-HMAC-SHA256 (RFC 8018), cuyas iteraciones son dos compresiones sobre palabras: 600 000 iteraciones tardan 1,1 s en la VM, frente a unos 3,5 s con el HMAC de package:crypto.
    • scrypt (RFC 7914) con Salsa20/8, con las comprobaciones y los textos de scrypt.Key de Go.
    • ChaCha20, Poly1305 en diez limbs de 13 bits, y ChaCha20-Poly1305 (RFC 8439), con el tag comparado en tiempo constante.
    • X25519 (RFC 7748) sobre el cuerpo de TweetNaCl en doubles, con clamping y el secreto todo a ceros rechazado con el texto de crypto/ecdh.
    • Ed25519 estricto (§29.9), port de internal/ed25519strict: verifyStrict, canonical, smallOrder y onCurve. La aritmética de los puntos es la de TweetNaCl; los escalares módulo ℓ y onCurve usan BigInt, que no es de tiempo constante, solo con datos públicos.
    • El Base64 de Go, con su modo estricto y el offset de sus errores, y el Bech32 de age, con sus textos.
    • De package:crypto solo se usa SHA-512, en Ed25519.
  • Lectura de age v1, port de filippo.io/age v1.3.2 (lib/src/age.dart):
    • la cabecera y sus límites de internal/format, con los textos de error de Go;
    • el MAC de la cabecera, sobre la cabecera escrita otra vez como la escribe age;
    • los stanzas X25519 y scrypt. La identity de scrypt rechaza por defecto un factor de trabajo por encima de 16, como authorkey de Go; el de age es 22;
    • la clave del payload y el STREAM de internal/stream, que se descifra según llega el texto cifrado, con los mismos casos de fin de fichero que el lector de Go: sin chunk final, chunk que no se autentica, último chunk vacío y datos tras el final;
    • cada fallo es una AgeException con el texto de Go y su fase, la cabecera o el payload, para que la etapa 4 dé las razones fijas de capsule.classify. La DateKeysException de una identity pasa sin cambios.
  • agewrap (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, PayloadIdentity y AccessIdentity, con los textos y los códigos de Go. checkTimeStanzas recibe la ronda, el chain hash y el id del perfil, que llegará con la etapa 4.
  • Vectores de Go en test/vectors/, que escriben tool/gen_primitive_vectors.go y tool/gen_age_vectors.go con las librerías de la caché de módulos, en el contexto del módulo de datekeys-go y sin cambiar nada en él:
    • primitives.json y su copia en Dart, primitives.g.dart: RFC 5869, 7748, 7914, 8032 (por sign.input de Go) y 8439, PBKDF2 con el vector del §38.1, los puntos de orden pequeño, Base64 y Bech32. Incluye dos mensajes con los que una suma de productos de Poly1305 pasa de 2^32: un acarreo tomado con un desplazamiento fallaría en la web, y una prueba lo detecta en Node;
    • age.json: ficheros 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 de Go en cada caso;
    • age_fixtures.json: el PAYLOAD_AGE de los 24 fixtures con su payload_identity, y el INNER_ACCESS_AGE de los 6 time_and_key, sacado de OUTER_TIME_AGE con el release del fixture.
  • Pruebas. 296 en la VM y una aplazada, 127 más que en la etapa 1; 83 en Node.js, 27 más. Las de las primitivas y las de age sin ficheros corren también compiladas a JavaScript, sin los casos largos (PBKDF2 a 600 000 iteraciones, scrypt con logN 16, X25519 iterada 1000 veces).
  • Fallos inyectados, uno a uno y revertidos: 46 en las primitivas, age y agewrap. Las pruebas detectan todos los que cambian un resultado. Cuatro no lo cambian: la máscara de una mitad de rotación de SHA-256 cuyos bits altos solo llegan a sumas enmascaradas, el redondeo de _car cuando sus entradas nunca son negativas, el de un acarreo del producto del cuerpo, que conserva el valor, y el acarreo del limb 1 de Poly1305 tomado con desplazamiento, cuya suma no pasa de 2^32.
  • tool/bench.dart mide las primitivas en la VM y compilado a JavaScript; las cifras, en el README.

El gate con Node.js (05-10-2026)

  • tool/check.sh corre también dart test -p node: las pruebas que no leen ficheros, compiladas a JavaScript, comprueban en cada commit que los enteros son exactos en la web. Lo decidió el autor.

Etapa 1: errores, bytes, CBOR y DER (05-10-2026)

  • Errores normativos (§69). ErrorCode con los 19 códigos en el orden del spec y DateKeysException, con el mensaje contexto: CÓDIGO de Go. wrap y withContext hacen lo que fmt.Errorf("prefijo: %w"), y errorCode lo que datekeys.Code. Una prueba compara el catálogo con las líneas ERR_ del §69, leído de datekeys-go en el tag spec-v0.11.
  • Bytes. Hexadecimal, comparación y concatenación.
    • UTF-8 estricto: rechaza lo que rechaza utf8.Valid de Go y conserva un U+FEFF inicial. El Utf8Decoder de dart:convert lo descarta, en la VM y en la web, así que no se usa.
    • Un String con un surrogate suelto se escribe en UTF-8 generalizado (WTF-8), que no es UTF-8 válido.
    • utf8.DecodeRune y el %q de Go (strconv.Quote). La tabla de strconv.IsPrint de Go 1.26 se copia de datekeys-ts, y una prueba fija el SHA-256 del conjunto de runas.
  • CBOR (§58, §58.1). Port de codec/codec.go: CborEncoder, CborDecoder, unmarshalCbor, peekSchema, checkSchema y walkCbor, con los mismos textos de error.
    • Los enteros son exactos en la VM y en la web: un int hasta 2^53-1 y un BigInt por encima, también en las claves de los mapas y en los textos de error.
    • El Encoder de Go rechaza el UTF-8 inválido; aquí el único texto inválido es un surrogate suelto, y el error lo cita como Go citaría sus bytes en UTF-8 generalizado.
    • CborEncoder.uint exige 0..2^53-1, con un texto propio, como en datekeys-ts; uint64 escribe hasta 2^64-1.
  • DER estricto. Port de internal/der/der.go en 601e6d2, ya del borrador v0.12: check, split, content, setOfSorted y parseTime. parseTime devuelve un DerTime con los campos y los segundos Unix, exacto al nanosegundo. Es interno: lib/datekeys.dart no lo exporta.
  • Pruebas. Port de errors_test.go, codec_test.go, internal_test.go, vectors_test.go y der_test.go, de cbor.test.ts y de bytes.test.ts, con los textos de error que imprime Go.
    • Los objetivos de fuzzing de Go son propiedades con semilla, contra un codificador y un decodificador de referencia escritos aparte, como internal/cbortest.
    • testdata/vectors/cbor.json: los 36 accept y los 67 reject, con los límites de walk y los valores. Los 172 vectores de schemas se leen y se aplazan: necesitan los decodificadores de esquema de la etapa 4.
    • 169 pruebas en la VM y una aplazada. Las 56 que no leen ficheros pasan también en Node.js (dart test -p node); las que leen ficheros llevan @TestOn('vm').

Etapa 0: el paquete (05-10-2026)

  • Paquete datekeys en Dart puro, sin Flutter, para Dart 3.13 y sin publicar (publish_to: none).
  • testdata/ sincronizado con el tag spec-v0.11 de datekeys-go (ae33434): 124 ficheros. Su testdata/SOURCE.json es el mismo, byte a byte, que escribe datekeys-ts para ese commit.
  • tool/sync_testdata.dart, el equivalente de scripts/sync-testdata.mjs. test/testdata_test.dart comprueba la copia y que cada fichero nombre la versión de la especificación.
  • tool/check.sh, el gate local.
  • Dependencias: package:crypto en ejecución y package:test en desarrollo.
  • Licencia Apache-2.0.

Powered by TurnKey Linux.