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/README.md

75 KiB

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 5 del plan (docs/PLAN_dart.md del espacio de trabajo) y la parte 6a de la etapa 6:

  • 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 etapa 5, en tres partes:
    • la 5b, los compromisos de lo que firma un autor y sella un sello, SECURITY_CBOR, la firma de alg 1 y los veredictos con sus textos y sus líneas;
    • la 5a, el lector de las firmas CMS y de los sellos RFC 3161 con el perfil de certificado del borrador v0.12, ECDSA y RSA. Es interno;
    • la 5c, la firma de alg 2 y el sello de seal_type 2 en los veredictos, que une el lector de la 5a a la evaluación de la 5b;
  • de la etapa 6, la parte 6a: la escritura de age, con la fuente inyectable de lo aleatorio, los recipients X25519, scrypt y tlock y las longitudes de un fichero antes de escribirlo. Es interna: la usarán el escritor de la cápsula, la parte 6b, y las claves de autor, la etapa 7b.

La librería ya abre cápsulas reales, de los tres formatos, con todas sus credenciales, y evalúa toda su área de seguridad como Go: la firma de alg 1, la de alg 2 con certificados, el sello de seal_type 2 y todos los veredictos de su forma. Y ya escribe los ficheros age que forman una cápsula, byte a byte como Go con los mismos valores aleatorios.

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, y la 5b también, en paralelo con la 5a, el lector de CMS, que se hizo en la rama stage5a y se integró encima de la 5b el mismo día. La 5c, que necesitaba las dos, se hizo después, en la rama v0.11. La 6a se hizo en la rama v0.11, en paralelo con la 7a, la lectura del localizador, en la rama stage7a.

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) = ·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. Desde la parte 5b es evaluateSecurityInput; notEvaluated da Verdicts.notEvaluated, para quien no muestra veredictos; 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.

La parte 5b de la etapa 5 porta el área de seguridad del formato 3 de datekeys-go en c531e93, el borrador v0.12, salvo lo que necesita el lector de CMS de las partes 5a y 5c. El API sigue al de Go:

Módulo Contenido En Go
lib/src/author.dart Lo que firma un autor y sella un sello (§29.8, §29.11): payloadCommit, controlCommit sobre CONTROL_SIG en cada formato, headDigest, signersDigest, authorMessage, sus 99 bytes y su prefijo, authorCode, sigPart y sealSubject signature.go
lib/src/security.dart SECURITY_CBOR (§29.3) con sus esquemas y sus límites, sus codificadores y sus lectores (decodeSecurity, decodeAuthorSignature, decodeSeal); evaluateSecurity, con la firma de alg 1; SecurityContext, securityContext y evaluateSecurityInput, el evaluador de la apertura; holderText, el nombre de un certificado del §29.7; y CmsEvaluator, la frontera con el lector de CMS format3.go, signature.go, newSecurityContext de open3.go, holderText de signature2.go
lib/src/verdicts.dart Los textos de los veredictos (Verdict.text), sus líneas (Verdicts.lines), el sello más temprano (Verdicts.sealedAt), Detail, SignerLine y SignerResult Verdict.Text, Verdicts.Lines, SealedAt, Detail y SignerLine de format3.go

Notas de la parte 5b:

  • El área nunca decide la apertura (§29.3). evaluateSecurity no lanza: X para un mapa exterior que falla su capa 2 o 3, la versión 2 incluida; y para la firma y el sello por separado, la primera fila de la tabla del §29.7 que se cumple. Un fallo dentro de una parte, sea cual sea, es de esa parte, F1 para la firma y S2 para el sello, como Go recupera un panic. Sin contexto lee como un lector de la v0.10: toda firma es F1 y un sello de seal_type 2, S1.

  • alg 1 se verifica con verifyStrict, y la clave se busca entre las guardadas por su cadena dkauthor1…, exacta, como en Go: una guardada en mayúsculas no coincide. Las claves de autor y su fichero son de la etapa 7.

  • La frontera con la parte 5c es CmsEvaluator:

    • evaluateSignature(signers, value, hasSeal, context) da null, que es F1, o F2, F5 o F6 con el Detail de los firmantes, como evaluateCMS de Go;
    • evaluateSeal(token, signature, context) da de S1 a S5, y con S4 y S5 la autoridad y t, como evaluateSeal de Go.

    evaluateSecurity lo recibe en cms. Sin él, la firma de alg 2 y el sello de seal_type 2 en un contexto quedan en null, sin evaluar, y la otra parte se evalúa igual: Verdicts.evaluated es falso y las líneas son las de la otra parte. Lo que lance el lector, o un veredicto fuera de su rango, es F1 o S2. La 5c lo implementa en securitycms.dart, cmsReader, que es desde entonces el cms por defecto; holderText estaba ya aquí para ella.

  • Las líneas son las de Go, byte a byte: los nombres de un certificado entre « y », la autoridad de cada sello en las de F6 con su aviso, los resultados en español y t en RFC 3339 con la fracción del sello. Partir cada línea en filas con ↳ (§29.7) es de la CLI de Go (cmd/datekeys/present.go), no del paquete capsule: no se porta, y lo hará la app en una salida de texto.

  • El contexto de la apertura es newSecurityContext con el head_digest: un control cuyo CONTROL_SIG no se puede codificar deja control_commit a cero, como en Go, y la firma no verifica.

  • authorCode toma los bytes del mensaje como Go toma los de un string: un byte que no es UTF-8 se lee como U+FFFD.

  • Lo que se exporta. lib/datekeys.dart exporta author.dart y security.dart enteros, como el paquete capsule de Go; datekeys-ts los deja internos.

La parte 5a de la etapa 5 porta internal/cms de datekeys-go en c531e93, la cabeza de la rama v0.12, con el perfil de certificado del borrador v0.12 (§29.7, §29.10 y §29.11), y la ECDSA y la RSA de Go que usa, con las mismas lecturas, las mismas comprobaciones en el mismo orden, los mismos resultados y los mismos textos de error:

Módulo Contenido En Go
lib/src/nist_curves.dart P-256, P-384 y P-521 sobre BigInt: la lectura de un punto sin comprimir y u1·G + u2·Q en coordenadas jacobianas, con el truco de Shamir ecdsa.ParseUncompressedPublicKey, crypto/internal/fips140/nistec
lib/src/ecdsa.dart verifyEcdsaAsn1: la firma leída como la lee cryptobyte, r y s entre 1 y n − 1 sin reducirlos, una s por encima de n/2 aceptada y el hash cortado a los bits del orden ecdsa.VerifyASN1
lib/src/rsa.dart verifyPkcs1v15, que construye la codificación y la compara entera, y verifyPss, con MGF1 y una sal de la longitud del hash; Sha2, los hashes de la tabla, con el DigestInfo de cada uno rsa.VerifyPKCS1v15, rsa.VerifyPSS
lib/src/cms.dart parseCert con el perfil del §29.10, campo a campo, y Cert con holder, issuerName y validAt; parseSignature, SignedData y SignerInfo con check, que da un CmsResult; parseToken y Token con check; addDuration, t más la precisión; y los errores: CmsFormException, CmsAlgorithmException y CertificateException internal/cms

Notas de la parte 5a:

  • Lo que trae. El lector de la firma de alg 2 y del sello de seal_type 2. Los compromisos, SECURITY_CBOR, la firma de alg 1 y los veredictos con sus textos son de la parte 5b, y la unión de este lector con los veredictos, de la 5c: evaluateCMS y evaluateSeal, con la línea de cada firmante.
  • Los nombres siguen a los de Go, para importar el módulo con prefijo (import 'cms.dart' as cms;): ErrForm y ErrAlgorithm son CmsFormException y CmsAlgorithmException, las dos de la clase sellada CmsException, y el Result de Go es CmsResult, porque CheckResult ya es de la inspección. Cada error lleva el texto de err.Error() de Go.
  • El perfil del certificado. Los identificadores de objeto se comparan por los bytes de su DER, así que un arco de 2^64 + 3 no es el 3 de nadie; un SET OF puede repetir un elemento; el titular sale de givenName y surname si los dos tienen texto no vacío, antes que del commonName; el emisor, del commonName o, si no tiene uno con texto, del organizationName. Como Go, y no como datekeys-ts, parseCert no comprueba antes que los bytes sean DER: dentro de una firma siempre lo son.
  • Las curvas son las tres de la tabla del §29.10, P-521 incluida, porque Go la acepta. Las claves RSA miden de 2048 a 4096 bits, con un módulo impar y un exponente impar de 3 a 2³¹ − 1; una clave de otro esquema que el algoritmo da invalid, como en Go.
  • Los tiempos son Instant, al nanosegundo. La precisión de un token es una Duration: como mucho 2³¹ − 1 segundos, 999 milisegundos y 999 microsegundos, cuyos microsegundos quedan por debajo de 2^53, exactos en la web. addDuration da t más la precisión, lo que el §29.7 compara con round_time.
  • Los vectores salen de la rama v0.12 de Go en c531e93, en una exportación suya. La 5a se hizo con testdata/ todavía en spec-v0.11, así que toma del security_cms.json y de los fixtures del borrador lo que necesita a través de su generador, de la exportación.
  • Interno, como internal/cms: lib/datekeys.dart no lo exporta.

La parte 5c de la etapa 5 porta signature2.go de datekeys-go en c531e93, el borrador v0.12: une el lector de CMS de la 5a a la evaluación de la 5b, con el mismo orden de comprobaciones, los mismos veredictos, el mismo resultado de cada firmante y el mismo detalle, que las líneas de la 5b escriben como Go:

Módulo Contenido En Go
lib/src/securitycms.dart cmsReader, el CmsEvaluator por defecto: la firma de alg 2 (SIGNERS, la SignedData, la línea de cada firmante exigido y de cada ajeno, y F2, F5 o F6 con su detalle) y el sello de seal_type 2 (de S1 a S5, con la autoridad y t); encodeSigners y maxSigners evaluateCMS, signerLine, evaluateSeal, EncodeSigners y MaxSigners de signature2.go

Notas de la parte 5c:

  • El orden de las comprobaciones es el de Go y el del §29.10: SIGNERS, con su perfil y como mucho 16 entradas, antes que la SignedData, y un fallo de cualquiera de los dos es F1. Después, cada firmante exigido, en el orden de SIGNERS, y cada ajeno, en el orden de la codificación, con su resultado: no verificable, inválido, sin sello, con el sello inválido, fuera de validez o válido. El sello de un firmante es su token, leído con el perfil del §29.11 sobre el valor de su firma: S2, S1 o S3 es un sello inválido, y su messageImprint puede usar cualquier hash de la tabla. F2 va antes que F5, y una clave 3 junto a una firma de alg 2 es F5.
  • La validez de un certificado se comprueba en t, el instante de su sello, con los dos extremos incluidos y al nanosegundo (§29.10, paso 6), como ValidAt(tok.GenTime) de Go: ni en round_time ni en el reloj de la apertura. La de la autoridad, en genTime, la comprueba el lector de la 5a.
  • Antes de la fecha de apertura quiere decir t más la precisión estrictamente antes de round_time. Sin round_time ningún sello es anterior, y un round_time en el instante cero de Go, 0001-01-01T00:00:00Z, cuenta como ninguno, como IsZero en Go. Verdicts.sealedAt tampoco cuenta un sello en ese instante, como SealedAt; su línea lo muestra igual.
  • Lo que lanza el lector de CMS con una firma o un token que incumple su perfil, una CmsException, es un veredicto: F1, un sello inválido, S2 o S1. Cualquier otra cosa llega a evaluateSecurity, que la hace un fallo de su parte, F1 o S2, como Go recupera un panic.
  • El evaluador por defecto. cmsReader es el cms por defecto de evaluateSecurity, y con él el de evaluateSecurityInput y de la apertura: nada que Go evalúe queda sin evaluar. Con cms: null la firma de alg 2 y el sello de seal_type 2 quedan en null, para quien no quiera pagar su coste, por ejemplo en la web (ver «Rendimiento»).
  • Lo que se exporta es lo del paquete capsule de Go: encodeSigners y maxSigners, y cmsReader. cms.dart sigue interno.

La parte 6a de la etapa 6 porta la escritura de age de filippo.io/age v1.3.2 y los recipients de agewrap de datekeys-go en c531e93, con las mismas comprobaciones en el mismo orden, los mismos textos de error y los mismos valores aleatorios, sacados en el mismo orden: con los mismos valores, escribe los mismos bytes que Go. Es lo que necesitan el escritor de la cápsula, la parte 6b, y las claves de autor, la etapa 7b:

Módulo Contenido En Go
lib/src/random.dart RandomSource, la fuente inyectable de todo lo aleatorio de un escritor; secureRandom, la de por defecto, el CSPRNG de la plataforma (Random.secure); SeededRandomSource, determinista, solo para pruebas y vectores; randomIndex, un entero uniforme como lo saca crypto/rand.Int, y permute, como el permute de capsule.Encrypt crypto/rand; permute de capsule/encrypt.go
lib/src/age_writer.dart AgeEncryptor y ageEncrypt, age.Encrypt con la cabecera, su MAC y el nonce; AgePayloadEncryptor, el STREAM según llega el texto, en chunks de 64 KiB; AgeRecipient, un recipient con sus etiquetas; marshalAgeHeader; y las longitudes, ageStreamLength, ageStanzaLength, ageHeaderLength y ageFileLength age.go, primitives.go, internal/format e internal/stream de age
lib/src/recipient.dart X25519Recipient, con sus cadenas age1…; ScryptRecipient; TimeRecipient, el de tlock; checkX25519Recipient, las reglas del §37; generateX25519Identity; rawX25519Identity y rawX25519Recipient; y la longitud de cada stanza, x25519StanzaLength, scryptStanzaLength y tlockStanzaLength x25519.go y scrypt.go de age; TimeRecipient, CheckX25519Recipient, RawX25519Identity y RawX25519Recipient de agewrap

Notas de la parte 6a:

  • Lo aleatorio sale todo de una RandomSource, secureRandom por defecto: la file key, el secreto efímero de cada stanza X25519, la sal y la etiqueta de un stanza scrypt, sigma y la etiqueta del stanza tlock, y el nonce del payload. AgeEncryptor los saca en el orden de age.Encrypt: la file key, lo de cada recipient, en su orden, y el nonce. Por eso, con SeededRandomSource, el keystream de ChaCha20 bajo el SHA-256 de una semilla, escribe los mismos bytes que Go cuando su crypto/rand lee el mismo keystream. También sigma de tlock sale de la fuente: encryptOnG2 de ibe.dart y wrapTlockStanza de tlock.dart la reciben, con secureRandom por defecto, y ya no tienen un Random.secure propio.
  • El STREAM se cifra según llega el texto (AgePayloadEncryptor), como el EncryptWriter de Go: un chunk lleno solo se cifra cuando llega un byte más, porque hasta entonces puede ser el último. Así el último chunk es completo cuando el texto es un múltiplo de 64 KiB distinto de cero, y vacío solo cuando el texto lo es. El fichero son la cabecera, el nonce y los chunks que devuelven add y close, en ese orden, y la memoria no crece con su tamaño.
  • Las etiquetas son las de Go: X25519 no tiene; scrypt tiene una al azar, para no mezclarse con ningún otro recipient, otro scrypt incluido; y tlock, datekeys-tlock- y 16 bytes al azar, para que OUTER_TIME_AGE lleve solo su stanza. age.Encrypt rechaza un recipient con otras etiquetas que el primero, con su texto, que las cita.
  • Los errores de age.Encrypt son AgeException, con el texto de Go: ningún recipient, etiquetas que no se pueden mezclar, un recipient que no envuelve la file key (failed to wrap key for recipient #i: …, y una clave X25519 de orden bajo da el texto de crypto/ecdh) y stanzas que no se pueden escribir. Una DateKeysException de un recipient conserva su código, con el prefijo de Go. checkX25519Recipient lanza un ArgumentError con el texto de agewrap, porque en Go no tiene código normativo: es un error del llamador. Usar el STREAM tras close es un StateError, con el texto de Go.
  • Las reglas del §37. X25519Recipient acepta cualquier clave de 32 bytes, como age, y una de orden bajo falla al envolver la file key. Las que un escritor no debe usar, una no canónica o una de orden bajo, las rechaza checkX25519Recipient, como agewrap.CheckX25519Recipient.
  • Las longitudes de un fichero salen de la de su texto y de la forma de sus stanzas, sin cifrar nada (ageFileLength): un stanza X25519 mide 98 bytes; uno scrypt, 78 más las cifras del factor de trabajo; y el de tlock, 249 más las cifras de la ronda (§62.1). La parte 6b puede sacar de ahí SEALED_CONTROL_LEN antes de sellar el control, sin el sellado de prueba de Go, que en Node.js costaría medio segundo más.
  • El tiempo constante. El cifrado del stanza tlock hace su aritmética con BigInt, que no es de tiempo constante, sobre sigma y r, que son secretos; el autor lo aceptó el 6 de octubre de 2026. X25519, ChaCha20-Poly1305 y scrypt son los de la etapa 2.
  • Random.secure en Node.js no está disponible dentro de dart test -p node, así que las pruebas compiladas a JavaScript usan SeededRandomSource. Fuera de las pruebas, con globalThis.self = globalThis, sí lo está.
  • Lo que se exporta: nada. La escritura de age es interna, como su lectura y agewrap; el escritor de la parte 6b exportará lo que necesite la app.

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;
  • en ECDSA y RSA los enteros grandes son BigInt; las longitudes de DER, los INTEGER pequeños y el exponente de RSA son un int de cuatro bytes como mucho, por debajo de 2^32, que se lee sin desplazamientos;
  • al escribir age, el contador de 11 bytes del nonce del STREAM se incrementa byte a byte, las longitudes son sumas y cocientes por debajo de 2^53, y randomIndex lee sus cuatro bytes como mucho multiplicando por 256, sin un desplazamiento que la web truncaría.

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í.

ECDSA y RSA hacen con BigInt toda su aritmética, y solo verifican: la clave de un certificado, la firma y el mensaje son públicos. La librería no firma con ninguno de los dos.

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 autor aceptó el 6 de octubre de 2026 que el escritor cifre así el stanza tlock (parte 6a); una capa del cuerpo de limbs fijos, sin ramas que dependan de los datos, lo evitaría.

Las etapas siguientes traen el resto del protocolo en este orden:

Etapa Contenido
6 Escritor: la parte 6b, la cápsula de formato 3, time_only y time_and_key, con la llave de palabras y la nota pública
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:

dart compile js -O2 -o bench.js tool/bench.dart
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:

dart compile js -O2 -o bench.js tool/bls12381_bench.dart
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:

dart compile js -O2 -o pathrule_bench.js tool/pathrule_bench.dart
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:

dart compile js -O2 -o open_bench.js tool/open_bench.dart
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
Abrir format3_signed_cms, una firma de alg 2 de dos firmantes, cada uno con su sello 55 ms, de ellos el área de seguridad 9,4 ms 0,95 s, de ellos el área 0,13 s
Abrir format3_sealed, una firma de alg 1 y un sello de seal_type 2 55 ms, de ellos el área 8,5 ms 0,89 s, de ellos el área 0,05 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. El área de seguridad de las dos cápsulas con certificados la evalúa el evaluador por defecto, con el lector de CMS, y su tiempo se mide dentro de la apertura; sus dos filas son del 6 de octubre. En format3_signed_cms son cuatro verificaciones, la ECDSA de P-256 de Ana, la RSA-2048 de Luis y las ECDSA de P-256 de sus dos sellos; en format3_sealed, la de Ed25519 de la firma de alg 1 y la de P-256 del sello. En Node.js manda la ECDSA sobre el BigInt compilado a JavaScript (ver «ECDSA, RSA y CMS»). 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.

El escritor de age

dart run tool/age_writer_bench.dart mide el escritor de age en la VM. Compilado a JavaScript:

dart compile js -O2 -o age_writer_bench.js tool/age_writer_bench.dart
node -e "globalThis.self = globalThis; require('./age_writer_bench.js')"

Lo aleatorio sale del CSPRNG de la plataforma, como en producción. Son medianas de entre tres y quince ejecuciones, el 6 de octubre de 2026:

Operación VM Node.js
La cabecera de PAYLOAD_AGE, un stanza X25519 4 ms 4 ms
INNER_ACCESS_AGE de 16 stanzas X25519 sobre 103 bytes 45 ms 35 ms
OUTER_TIME_AGE, el stanza tlock de la ronda 1000, 2128 bytes 40 ms 0,55 s
Un fichero de clave de autor, scrypt con logN 16 0,6 s 0,85 s
64 MiB en streaming, en trozos de 1 MiB 1,38 s, 46 MiB/s 16 MiB: 0,33 s, 49 MiB/s

Un stanza X25519 son dos X25519, la clave efímera y el secreto compartido, de algo más de 1 ms cada uno. El de tlock es el cifrado del IBE, con su emparejamiento, sobre BigInt. En un fichero grande manda ChaCha20-Poly1305, como al abrir; la memoria no crece con el tamaño, porque cada chunk sale en cuanto se cifra.

ECDSA, RSA y CMS

dart run tool/cms_bench.dart mide ECDSA, RSA y el lector de CMS en la VM. Compilado a JavaScript:

dart compile js -O2 -o cms_bench.js tool/cms_bench.dart
node cms_bench.js

Son medianas de tres ejecuciones, el 5 de octubre de 2026:

Operación VM Node.js
ECDSA P-256 con SHA-256, una verificación 2,4 ms 32 ms
ECDSA P-384 con SHA-384, una verificación 5,6 ms 96 ms
ECDSA P-521 con SHA-512, una verificación 12 ms 235 ms
RSA de 2048 bits, PKCS #1 v1.5 o PSS, una verificación 0,10 ms 5,1 ms
RSA de 3072 bits, una verificación 0,17 ms 9,1 ms
RSA de 4096 bits, una verificación 0,29 ms 17 ms
parseCert de un certificado de 337 bytes (P-256) o de 739 (RSA-2048) 0,01 a 0,02 ms 0,02 ms
parseSignature y check de una cofirma de ECDSA y RSA 3,6 ms 36 ms
parseToken y check de un token de ECDSA P-256 2,6 ms 30 ms

Manda ECDSA: con el exponente 65 537, una verificación de RSA son 17 multiplicaciones modulares, y una de ECDSA en P-256, unos 256 dobles de punto y 190 sumas. Una firma de dos firmantes con sus sellos son cuatro verificaciones: unos 10 ms en la VM y 0,13 s en Node.js con P-256.

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. Desde la etapa 5, testdata/ es ya el de la rama 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, SHA-256, en BLS12-381 y tlock, y SHA-1, SHA-256, SHA-384 y SHA-512, en CMS. 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

dart pub get
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 de la rama v0.12 de datekeys-go en c531e93, cuyo testdata/ es el de 601e6d2, el que tiene datekeys-ts: 133 ficheros, con los veredictos y las líneas del borrador v0.12. Cada fichero dice aún "spec": "0.11", como el SpecVersion de Go, hasta que el autor apruebe el borrador.

dart run tool/sync_testdata.dart sync --commit c531e93
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 la lectura y la escritura de age, de BLS12-381, de tlock, de las reglas de rutas y textos, de la llave de palabras, de los formatos, de la apertura, del área de seguridad, de la firma con certificados y el sello, y del lector de CMS. 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, de la apertura, del lector de CMS y del escritor de age, en una exportación suya, y el último, tool/cms_go_vectors_test.go, como una prueba de Go. 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:

cd ../datekeys-go && go run ../datekeys-dart/tool/gen_primitive_vectors.go -out ../datekeys-dart/test/vectors
cd ../datekeys-go && go run ../datekeys-dart/tool/gen_age_vectors.go -out ../datekeys-dart/test/vectors
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
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
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
cd ../datekeys-go && go run ../datekeys-dart/tool/release_go_vectors.go ../datekeys-dart/testdata > ../datekeys-dart/test/vectors/release_vectors.json
cd ../datekeys-go && go run ../datekeys-dart/tool/formats_go_vectors.go -testdata ../datekeys-dart/testdata -out ../datekeys-dart/test/vectors
cd ../datekeys-go && go run ../datekeys-dart/tool/wordkey_go_vectors.go -out ../datekeys-dart/test/vectors
cd ../datekeys-go && go run ../datekeys-dart/tool/mutation_go_texts.go ../datekeys-dart/testdata > ../datekeys-dart/test/vectors/mutation_texts.json
cd ../datekeys-go && go run ../datekeys-dart/tool/security_go_vectors.go -testdata ../datekeys-dart/testdata -out ../datekeys-dart/test/vectors

holderText no se exporta en el paquete capsule: tool/security_go_vectors.go llega a él con go:linkname, que Go permite con un paquete de fuera de la biblioteca estándar. El mismo programa escribe los vectores de la parte 5c, securitycms_vectors.json, con firmas CMS y tokens RFC 3161 que hace él mismo como los hace internal/cms/cmstest para las pruebas de la referencia: ese paquete no se puede importar desde fuera del árbol de datekeys-go, así que el generador reescribe la parte que necesita. Las claves salen de etiquetas, ECDSA firma con el nonce del RFC 6979 y RSA con PKCS #1 v1.5, así que la salida no depende de crypto/rand.

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:

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. El del lector de CMS, que importa internal/cms y internal/cms/cmstest, corre además como una prueba de Go, para que testing/cryptotest.SetGlobalRandom haga deterministas las claves y las firmas:

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"
mkdir "$tmp/cmsvectors"
cp tool/cms_go_vectors_test.go "$tmp/cmsvectors/"
(cd "$tmp/cmsvectors" && go test -run TestWriteVectors -count=1 -args -source "$commit" -out "$out")
rm -rf "$tmp"

La exportación para la apertura:

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"

Los del escritor de age también corren en una exportación, porque tool/age_writer_go_vectors.go comprueba sus ficheros con internal/testkit. Mientras escribe cada caso, sustituye crypto/rand.Reader por el keystream de SeededRandomSource, así que age.Encrypt saca de él la file key, los secretos efímeros, las sales, sigma, las etiquetas y el nonce, y la salida es la misma en cada ejecución:

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/age_writer_go_vectors.go "$tmp"
(cd "$tmp" && go run ./age_writer_go_vectors.go -source "$commit" -out "$out")
rm -rf "$tmp"

En la otra dirección, este repositorio escribe los ficheros y Go los abre: tool/age_interop_dart_samples.dart escribe los de las recetas de test/age_interop_support.dart, y tool/age_interop_go_verdicts.go los abre con age y las identities de agewrap y escribe sus veredictos:

commit=$(git -C ../datekeys-go rev-parse v0.12)
root=$PWD
tmp=$(mktemp -d)
dart run tool/age_interop_dart_samples.dart "$tmp/samples"
git -C ../datekeys-go archive "$commit" | tar -x -C "$tmp"
cp tool/age_interop_go_verdicts.go "$tmp"
(cd "$tmp" && go run ./age_interop_go_verdicts.go -source "$commit" -samples "$tmp/samples" -out "$root/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 de los 218 casos del corpus del borrador v0.12 lo hacen con Go en c531e93
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, y los del formato 3 que se abren, sus veredictos con sus líneas, también con la clave de autor guardada
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
security_vectors.json El área de seguridad del paquete capsule: los compromisos, también ControlCommit del control de cada fixture en cada formato; los codificadores; 1730 evaluaciones con EvaluateSecurityIn en 23 contextos y sin contexto, del mapa exterior, de author-signature y de seal rotos de todas las formas de sus esquemas y en sus límites, de firmas de alg 1 válidas e inválidas, guardadas o no, con los casos de «Taming the many EdDSAs» hechos sobre AUTHOR_MESSAGE, y de mutaciones de una semilla fija, con los veredictos, las líneas, alg y seal_type leídos y las partes que solo evalúa el lector de CMS; las líneas y el sello más temprano de veredictos con cada detalle; y holderText
security_vectors.g.dart Todo security_vectors.json salvo siete de cada ocho evaluaciones, como constante de Dart
securitycms_vectors.json La firma de alg 2 y el sello de seal_type 2 como los evalúa EvaluateSecurityIn: 760 áreas que hace el generador, con los veredictos, las líneas, el detalle de cada firmante y del sello y el sello más temprano. Un firmante exigido de cada resultado junto a ajenos de cada resultado, sin round_time, al abrir en el instante de los sellos, en el contexto de otro head y con una clave 3; tres firmantes exigidos sacados de una semilla; SIGNERS de 16 y 17 entradas; la validez de un certificado en el instante de su sello, al nanosegundo, de un UTCTime a un GeneralizedTime y con diez cifras de fracción, y la de la autoridad; t más la precisión frente a round_time a un nanosegundo de cada lado, con cada forma de la precisión, en el instante cero de Go y en el último segundo de 9999; un sello de cada veredicto junto a una firma de cada veredicto; y mutaciones de la SignedData, de SIGNERS y de los tokens. Los certificados, los tokens y los SignerInfo que se repiten van una vez, como trozos
securitycms_vectors.g.dart Una parte de securitycms_vectors.json, con cada par de veredictos de cada grupo, y los fixtures format3_signed_cms y format3_sealed con lo que dicen sus registros, como constantes de Dart
cms_ecdsa.json Las curvas de crypto/elliptic; puntos que lee o no ecdsa.ParseUncompressedPublicKey; y firmas de ecdsa.VerifyASN1: válidas, con s por encima de n/2, r o s de n o más, cero, negativas o no mínimas, en otra codificación, sobre otro hash, y con claves de d = 1 y d = n − 1, con u1·G + u2·Q en el infinito
cms_rsa.json Claves de 2048, 2049, 3072, 4095 y 4096 bits con los exponentes 3, 65 537 y 2³¹ − 1, y firmas de rsa.VerifyPKCS1v15 y rsa.VerifyPSS como las comprueba internal/cms: válidas, con un byte más o menos, de n o más, y codificaciones hechas a mano y firmadas con la clave privada, cada una con un defecto de su relleno
cms_certs.json cms.ParseCert, sus campos, Holder, IssuerName y ValidAt alrededor de los dos extremos, en los certificados de las pruebas de internal/cms, en identificadores cuyo último arco da la vuelta en 32 o 64 bits, y en certificados editados nodo a nodo y bit a bit
cms_signatures.json, cms_algorithms.json cms.ParseSignature y SignerInfo.Check en las firmas de las pruebas de internal/cms (la forma, los algoritmos y las claves), con identificadores que dan la vuelta y SET OF con un elemento repetido
cms_tokens.json cms.ParseToken y Token.Check en los tokens de esas pruebas, el TSTInfo campo a campo, la precisión y el messageImprint en sus límites, y la autoridad en los extremos de su validez, al nanosegundo
cms_mutations.json Firmas y tokens editados nodo a nodo y bit a bit, cada edición como el nodo, la operación y el SHA-256 del resultado
cms_corpus.json Cada firma CMS y cada token de security_cms.json y de los fixtures format3_signed_cms y format3_sealed del borrador v0.12, leídos como los leen los veredictos de capsule, firmante a firmante
cms_vectors.g.dart Una parte de cada fichero cms_*.json, como constantes de Dart
age_writer.json Los ficheros que escribe age.Encrypt con el keystream de una semilla como crypto/rand, con cada valor que saca, su tamaño y su orden: un recipient X25519, sobre textos de 0 a 3 MiB, en los bordes de los chunks de 64 KiB; dos, tres y dieciséis, y uno con el bit 255 a 1, que age acepta; scrypt con factores de trabajo de 1 a 16 y contraseñas en UTF-8; y el stanza tlock de rondas de 1 a 11 cifras. Uno pequeño va entero, y uno grande, como su cabecera, su longitud y su SHA-256. Además: el keystream de varias semillas, crypto/rand.Int y el permute de capsule.Encrypt sobre él; los errores de age.Encrypt, con lo que saca antes de cada uno; los de NewScryptRecipient, SetWorkFactor y NewTimeRecipient; el STREAM tras Close; ParseX25519Recipient, CheckX25519Recipient y GenerateX25519Identity; y testkit.StreamLen y capsule.PayloadAgeLength
age_writer.g.dart El mismo JSON como constante de Dart
age_interop.json Los ficheros que escribe este repositorio con SeededRandomSource, con las recetas de test/age_interop_support.dart: X25519 con textos de 0 a 3 MiB, uno escrito en trozos, tres y dieciséis recipients, tlock en la ronda 1000, tlock sobre dieciséis X25519 como un SEALED_CONTROL, y scrypt con los factores de trabajo 10 y 16. Cada uno con su longitud, su SHA-256, el fichero si es pequeño, la longitud de su cabecera, sus stanzas, las reglas de agewrap sobre ellos y el veredicto de Go con cada identity: el texto que abre, o el error
age_interop.g.dart El mismo JSON como constante 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, los de los formatos, security_vectors.json, securitycms_vectors.json, los del lector de CMS y los del escritor de age salen iguales en cada ejecución, los del lector de CMS con Go 1.26.8. 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. age.json no lee testdata/ y es el congelado de la etapa 2; age_fixtures.json se escribió otra vez con el testdata/ de la v0.12, y fuera de los dos fixtures nuevos y del de seal_type 4294967295 sale igual.
  • 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.
  • Los ficheros del lector de CMS escriben una vez por fichero los certificados que se repiten: el DER de un caso es entonces una lista de trozos, el hexadecimal de unos bytes o el índice de un certificado, con el SHA-256 del resultado. securitycms_vectors.json hace lo mismo con los certificados, los tokens y los SignerInfo, cada trozo hecho a su vez de los anteriores, y una mutación se guarda como su base, el objetivo de la edición (la SignedData, SIGNERS o el token) y la edición.
  • 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, security_vectors.g.dart, securitycms_vectors.g.dart, cms_vectors.g.dart, age_writer.g.dart, age_interop.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, el área de seguridad, la firma con certificados y el sello, el lector de CMS, el escritor de age, BLS12-381 y el IBE, y con testdata/ los fixtures que llevan.

Licencia

Apache-2.0 (LICENSE), como datekeys-go y datekeys-ts. La especificación tiene su propia licencia, CC-BY-4.0.

Powered by TurnKey Linux.