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

53 KiB

Changelog

Cambios notables de la librería TypeScript y de la página. El proyecto usa versionado semántico; mientras sea 0.x, no hay promesa de estabilidad. La sección «Versiones» del README explica qué cubre cada número.

0.5.0 — sin publicar

Las palabras al azar de la llave de palabras (07-10-2026)

El SHOULD de §38.1, ofrecer palabras al azar de una lista pública, como hace datekeys encrypt -new-words desde c49c67c de datekeys-go. No cambia ningún formato ni la derivación.

  • Las listas de datekeys-go. scripts/sync-testdata.mjs copia del mismo commit wordkey/lists en wordlists/, con su SOURCE.json, y check comprueba las dos copias. testdata y wordlists están en 27a75ee de datekeys-go, de la rama v0.15 después del tag spec-v0.15: los ficheros de testdata son los del tag, y wordlists trae la lista española, de 7 776 palabras, un borrador aún sin revisar, con licencia CC BY-SA 4.0.
  • wordlist.ts, como wordkey.Generate, CheckList y Bits de Go en 27a75ee, con sus textos: generateWords sortea palabras distintas, 7 por defecto, con crypto.getRandomValues; wordBits da su fuerza, 90 bits para 7 de 7 776; checkWordList rechaza una lista de menos de 2 048 palabras, con dos que son una al normalizarlas o con un carácter que no es una letra del alfabeto de su idioma, que da el código y no la lista (para es, de la a a la z, á, é, í, ó, ú, ü y ñ); y readWordList solo acepta una lista con el SHA-256 fijado para su idioma, que sea UTF-8 y que checkWordList acepte. wordkey.ts gana wordRules, que carga una vez las tablas de Unicode para quien lee muchas palabras.
  • La página /create ofrece palabras al azar por defecto. Con la política «con llave», una casilla abre la cápsula también con unas palabras: al marcarla, la página descarga del propio sitio la lista española (create-words.ts, el fichero que Vite publica con un nombre con hash), la acepta solo con su SHA-256 fijado, y sortea 7 palabras que no salen del navegador. Las muestra numeradas, con su fuerza calculada con la lista cargada y un botón para sacar otras. «Las elijo yo» deja escribir las propias, con el aviso de que son más débiles. En los dos casos hay que escribirlas otra vez para comprobar que se tienen; dan igual mayúsculas y tildes. planCapsule recibe wordsKind (none, random u own) y pide las palabras que falten.
  • Corregido en /create: el efecto que comprueba las rutas escribía pathCheck y lo volvía a leer, así que Svelte lo relanzaba sin fin en cuanto se cargaban las reglas, con el primer fichero, comentario o autor: effect_update_depth_exceeded en la consola y checkPaths repetido sobre toda la lista. Lo lee ahora de una variable local. Venía de 7dc88ef (1 de octubre) y está en la 0.4.0.
  • La lista inglesa. testdata y wordlists pasan a e671032 de datekeys-go, con el mismo testdata: wordlists/en.txt es la lista grande de la EFF, de 7 776 palabras, CC BY 4.0, en su orden y sin los números de los dados. WORD_LIST_SHA256 fija su SHA-256, y el alfabeto de en es de la a a la z y el guion de sus cuatro palabras compuestas, como t-shirt. /create sigue ofreciendo la española, y check-build.mjs exige que la página publique solo esa.
  • Los dados, para quien no se fía del azar del ordenador, como dice.go de datekeys-go en 92e7154, con sus textos: cinco dados por palabra dan un número del 11111 al 66666, la posición de la palabra en una lista de 7 776. wordlist.ts gana DICE_LIST_SIZE, diceNumber, diceWord, diceWords y diceList, la lista numerada como la publica la EFF (la de en es su fichero, byte a byte), y wordkey.ts, goFields, que separa por espacios como strings.Fields de Go. En /create, «Con dados» convierte los números en palabras a medida que se escriben, avisa de un número que no vale o de una palabra repetida, y ofrece la lista numerada para imprimirla, con su SHA-256; planCapsule recibe dice y diceList, y readDice lee los números uno a uno. testdata y wordlists pasan a 92e7154, cuyo README.md de las listas recoge el SHA-256 de cada lista numerada.
  • Lo que dice el SDK oficial al sellar, como aefc8f6 de datekeys-go (§7.6, §62.1 reglas 26 y 27, §71). sync-testdata.mjs copia también annex/ de Go, el anexo de recuperación (§79 bajo un título con la versión y el SHA-256 de la especificación), y testdata, wordlists y annex pasan a aefc8f6. En /create: con una fecha a más de un año y «solo fecha», la recomendación de la llave; tras crear la cápsula, «Para abrirla más adelante», con lo que hará falta y la descarga de las instrucciones para abrirla sin DateKeys, <cápsula>.recuperacion.txt (annex.ts); y check-build.mjs exige el anexo byte a byte. profile.ts gana ProfileStatus, PROFILE_STATUS y profileStatusOf, como StatusOf de Go: Quicknet está activo; planCapsule no escribe con un perfil que no lo esté, y /inspect avisa si el de una cápsula está comprometido (buildReport recibe profileStatus).
  • licenses.txt lleva el README.md de wordlists/, con el origen, el método y la licencia de la lista (CC BY-SA 4.0), y check-build.mjs exige que esté y que la lista se publique byte a byte.

0.4.0 — 7 de octubre de 2026

La especificación 0.15, el tag spec-v0.15 de datekeys-go: la recuperación a largo plazo, con el objeto release, los archivos de releases y el release en la mano. El autor la cerró el 7 de octubre de 2026, con el tag v0.4.0.

La especificación 0.15, de la rama v0.15 de datekeys-go: el objeto release, el release en la mano y el paso 9.c.

La especificación 0.15: el objeto release y el release en la mano (07-10-2026)

  • SPEC_VERSION pasa a 0.15 y la versión a 0.4.0-dev. testdata se sincroniza con el tag spec-v0.15 de datekeys-go (fe50885), que añade vectors/release.json, los ficheros de releases/ y el campo source de mutations.json.
  • El objeto release (§47.1). releaseobject.ts, sin noble, hace lo de provider/release.go y provider/archive.go de Go, con sus textos byte a byte: encodeRelease y decodeRelease, con las capas del paso 10 (el tamaño de 1 a 1024 bytes antes de decodificar, el tipo y la versión, el schema); parseRelease, que lee también el JSON de drand como lo lee encoding/json de Go, con ERR_RELEASE_INVALID para cualquier fallo; ReleaseSupplier y encodedRelease, el release en la mano; y ReleaseArchive, el archivo de releases local, formato informativo (§50), cuyos fallos son ERR_RELEASE_UNAVAILABLE, también una ronda a ceros.
  • La cadena en el paso 10. verifyRelease compara primero la cadena que nombra el release con la del perfil fijado (ERR_PROFILE_MISMATCH), y después la ronda y la firma.
  • El paso 9.c, opción B. open acepta release, un release en la mano, en lugar de source: no se compara con el reloj, Opened.clockBehind dice si el reloj iba por detrás de la fecha, y el paso 10 lo decodifica con los códigos de ese paso. A una fuente de red no se le pide nada antes de round_time, como antes. El release verificado lleva la cadena del perfil fijado.
  • Las pruebas. vectors.test.ts corre release.json entero, con los textos de Go, y cada fichero de releases/, que la guarda de testdata exige. El corpus de mutaciones lee source y da a cada caso su clase de fuente, como singleSource de Go. scripts/mutation-go-texts.go hace lo mismo, y testing/mutation-texts.json se regenera con Go: cambian el campo spec, «round not reached yet», que ahora abre, y los cuatro casos nuevos, con los textos que explica la v0.15. Los demás ficheros de testing/ generados con Go no cambian: sus scripts usan una fuente de red con releases válidos y sin cadena, con la que Go se comporta como antes.
  • La página /inspect. El release se puede pegar, como antes, o dar en un fichero: el objeto release, la respuesta de drand en JSON o un archivo de releases. Va a open como release en la mano. Si el reloj del dispositivo dice que la fecha no ha llegado, la página ya no se cierra: deja dar el release, sin pedirlo a drand, y si abre la cápsula dice que el reloj parece ir atrasado. Las glosas de los pasos 9 y 10 dicen lo que comprueba la v0.15.

0.3.0 — 6 de octubre de 2026

La especificación 0.14, el tag spec-v0.14 de datekeys-go: un solo scheme de drand y la raíz de confianza byte a byte, con tlock_steps.json. El autor la cerró el 6 de octubre de 2026, con el tag v0.3.0, para congelarla en el paquete de la revisión externa: la 0.2.0 tenía el testdata en spec-v0.13.

La especificación 0.14, aprobada (06-10-2026)

  • El autor aprobó el 6 de octubre de 2026 el borrador v0.14 con la recomendación de cada una de sus diez decisiones: datekeys-go lo cierra con el tag spec-v0.14 (39b2033). SPEC_VERSION pasa a 0.14, testdata se sincroniza con ese tag, y testing/mutation-texts.json se regenera con Go: solo cambia su campo spec. Ningún otro fichero de testing/ generado con Go lleva ese campo ni depende de un perfil de otro scheme; los veredictos de bls12381-vectors.json salen idénticos con Go en 39b2033, y solo se corrige su descripción.
  • Un solo scheme de drand (decisión 8). validateProfile, y con ella decodeProfile, admite solo bls-unchained-g1-rfc9380, con la clave pública en G2, como validateDrand de Go desde c041fa3: un nombre que drand no conoce sigue siendo «is not a drand scheme», y cualquier otro scheme de drand, pedersen-bls-unchained y bls-unchained-on-g1 incluidos, falla con ERR_UNKNOWN_PROFILE y el texto de Go, byte a byte, antes de mirar la clave y el chain_hash. Ningún perfil fijado ni ninguna cápsula válida cambian.
  • tlock_steps.json. Un bloque nuevo de vectors.test.ts lee el fichero con el formato estricto de los demás y recorre, valor a valor y con el código de la librería, los pasos 10 y 11 de las cinco stanzas: M, H(M), la ecuación de pairing, las partes del stanza, e(firma, U), H2, sigma, H4, la file key, cada intento de H3 y r, y r·G2 = U; y las comprobaciones negativas: el DST de G2, la ronda sin SHA-256, el bit más alto puesto a cero, una firma de otra ronda y un V o un W editados. La guarda de testdata lo exige. ibe.ts exporta para ello h3Base, h3Try y hashToG1, que h3, el cifrado y release.ts usan ahora; index.ts no los exporta.
  • El comentario de h3 decía que se pone a cero el bit más alto de cada intento: el código desplaza el primer byte un bit a la derecha, como kyber, y así lo dice ahora.

0.2.0 — 6 de octubre de 2026

La especificación 0.13, el tag spec-v0.13 de datekeys-go: lee los formatos 1 a 3 y escribe el 3, con la firma de autor, el sello, la llave de palabras, la nota pública y el localizador de datekeys.capsule. El autor la cerró el 6 de octubre de 2026, con el tag v0.2.0. Desde la fase 3 hasta la especificación 0.13, en orden inverso:

El localizador sellado, comprobado antes de la fecha (06-10-2026)

  • parseInfo comprueba el localizador sellado como ParseInfo de Go desde 69dbb0c, a petición del autor: la cadena del stanza tlock en hexadecimal en minúsculas y, si la DateKey es de Quicknet, la de Quicknet; y que el cuerpo tras la cabecera age lleve un texto de 4096 bytes o un múltiplo, así que una cabecera sin cuerpo se rechaza. Cada caso es ERR_EXTENSION_DATA_INVALID con el texto de Go. infoExtension rechaza además una DateKey de un perfil que la librería no fija. El límite de 1 MiB al abrir el localizador se queda, por decisión del autor.
  • Los vectores de Go del localizador se regeneran sobre ese commit: cambian solo los casos de esas comprobaciones.

La especificación 0.13, aprobada (06-10-2026)

  • El autor aprobó el 6 de octubre de 2026 el borrador v0.13, NAT64, tal como estaba: datekeys-go lo cierra con el tag spec-v0.13 (913dd60). SPEC_VERSION pasa a 0.13, testdata se sincroniza con ese tag, y testing/mutation-texts.json se regenera con Go: solo cambia el campo spec de cada fichero.

Dos direcciones que el §44.1 ya rechazaba (06-10-2026)

  • Un CID con un carácter de más cuyos bits son cero, que decodifica a los mismos bytes, y https://[[2000::]/ se rechazan, como en datekeys-go desde e801e03: isCidV1 rechaza 5 bits sobrantes o más, y el host de un literal IPv6 lleva un solo par de corchetes. Eran dos de las decisiones pendientes del autor, pero el texto aprobado de la v0.12 ya lo pedía.
  • testing/locator-uris.json y locator-vectors.json se regeneran con scripts/locator-go-vectors.go sobre ese commit: cambian solo las diez direcciones de esas dos formas y los localizadores que las llevan.

La nota pública en /inspect (06-10-2026)

  • /inspect muestra la nota pública justo debajo del veredicto: el texto del creador, en la letra serif de la página y sobre el fondo de aviso, con dos líneas que dicen que nadie la ha comprobado, que la lee cualquiera que tenga el fichero y que antes de la fecha nadie puede comprobar quién creó la cápsula ni si va firmada, como showNote de la CLI de Go. Una nota que no cumple las reglas de texto no se muestra, y la página lo dice.
  • buildReport lleva la nota en Report.note, con su prueba. Comprobado en el navegador con el fixture format3_note.
  • Errores que no se esperaban. Las páginas ya no enseñan el mensaje de una excepción, que podía venir en inglés y con una URL interna, como «Failed to fetch dynamically imported module: http://localhost:5188/src/lib/inspector/opener.ts». unexpectedProblem de format.ts lo dice en español y con lo que hay que hacer: recargar si no llegó una parte de la página, volver a elegir un fichero que el navegador ya no deja leer, liberar espacio, o, en otro caso, recargar y usar la herramienta de línea de órdenes. La excepción va a la consola. Lo usan la apertura, la lectura de una cápsula en /inspect y la creación en /create.
  • El servidor de desarrollo ya no recarga la página al abrir. vite.config.ts hace que Vite prepare desde el arranque las dependencias que las páginas cargan bajo demanda (optimizeDeps.include). Sin eso, la primera apertura le hacía preparar dos módulos de noble y recargar la página, y la carga en curso fallaba con ese mensaje. dependencies.test.ts comprueba que la lista está completa. La versión compilada no tenía el problema.

NAT64, del borrador v0.13 (06-10-2026)

  • testdata se sincroniza con datekeys-go en a83b44d, el borrador v0.13, sin aprobar: añade vectors/resolved_ip.json, y ningún otro fichero cambia. SPEC_VERSION sigue en 0.12.
  • checkResolvedIp cuenta una dirección de NAT64 a la que resuelve un nombre, de 64:ff9b::/96 o del prefijo de la red (el parámetro nat64), por la IPv4 que lleva dentro, con los textos de locator.CheckResolvedIP de Go. ipaddr.ts exporta isIpv4In6. Un bloque nuevo de vectors.test.ts corre los 42 casos.

Las claves de autor y la firma al escribir (06-10-2026)

Las claves de autor de alg 1 y los enganches de firma y de sello del escritor, como authorkey y capsule.EncryptFiles de Go en spec-v0.12, byte a byte. Ningún paquete ni módulo nuevo: el SHA-512, el HKDF, ChaCha20-Poly1305 y el scrypt de age-encryption ya estaban.

  • ed25519sign.ts, la firma Ed25519 en código propio: crypto_sign de TweetNaCl, como el port de Dart, con el SHA-512 de @noble/hashes. Aritmética exacta en Float64Array, y los secretos nunca en BigInt. JavaScript no promete tiempo constante ni borrar la memoria, y el código lo dice.

  • authorkey.ts, el paquete authorkey de Go: AuthorKey (generate con una fuente de azar inyectable, fromSeed, publicKey, sign, clear, secret y un toString que oculta el secreto), authorPublicString, parseAuthorPublic, parseAuthorSecret, marshalAuthorKey, encryptAuthorKey y readAuthorKey, con los textos de error de Go byte a byte, también los de age, también los números al revés del de PublicString. Las cadenas se leen como bytes, con las mayúsculas, las minúsculas y los espacios del paquete unicode de Go, que gounicode.ts trae en tablas generadas con Go (scripts/go-unicode-tables.go). El fichero cifrado lleva scrypt con logN 16, y se lee con un máximo de 16, 64 KiB y las líneas de bufio.Scanner.

  • El escritor. encryptFiles gana authorKey (alg 1), cmsSigner (alg 2) y sealer (seal_type 2), que pueden ser asíncronos, y largeArea. Se comprueban en el orden de Go y con sus textos; la firma y el sello se piden con el control y el head finales y antes de escribir nada, y el área se evalúa con el lector antes de escribirla, como security de Go. Un área que crece a 64 KiB cuenta en el límite de memoria. security.ts escribe el área con firma y sello, y securitycms.ts, SIGNERS.

  • Interoperabilidad con Go, en dos ficheros congelados de testing/:

    • authorkey-vectors.json, de scripts/authorkey-go-vectors.go: 234 firmas, la reducción de escalares, 24 claves, Generate y el fichero cifrado con los valores al azar de Go, que encryptAuthorKey reproduce byte a byte, 1 288 cadenas, 3 240 runas y 130 ficheros que leer, cada uno con el resultado o el texto de Go;
    • signing-vectors.json, de scripts/signing-go-vectors_test.go en una exportación de spec-v0.12: con los valores al azar y las firmas de Go, encryptFiles escribe sus ocho cápsulas firmadas y selladas byte a byte, y falla con su texto en las otras 15. Y Go abre las cinco cápsulas que escribe scripts/signing-ts-samples.mjs con esta librería, sus certificados y su azar, con los mismos veredictos y las mismas líneas.
  • Guardas. dependencies.test.ts deja importar noble a authorkey.ts y ed25519sign.ts, y age-encryption a authorkey.ts, y no deja que index.ts los reexporte. check-build.mjs falla si una página carga las claves de autor con la página, o si /inspect puede cargarlas. authorkey.ts, ed25519sign.ts y gounicode.ts quedan al 100 % de cobertura.

  • Pruebas. npm run verify pasa con 7 904; una más, la de un área que crece y ya no cabe en memoria, lee 1 GiB y corre con DATEKEYS_LARGE=1. Con 27 fallos inyectados uno a uno en la firma, las claves, el escritor y los codificadores, las pruebas detectan 26; el otro es equivalente: el escritor compara la clave del veredicto con la del enganche, que no puede ser otra si la firma da F4. Y check-build.mjs detecta las claves de autor en /inspect.

  • Integración. Se hizo en la rama signing, en paralelo con el localizador, y se puso encima de él y de NAT64 con rebase, con conflictos solo en las guardas, en vitest.config.ts, en el README y aquí. Con las tres partes, npm run verify pasa con 8 017 pruebas y una aplazada.

El localizador de datekeys.capsule (06-10-2026)

El paquete locator de datekeys-go en spec-v0.12 (§43 a §44.1), con los mismos checks en el mismo orden, los mismos códigos y los mismos textos de error, byte a byte, como lo portó datekeys-dart en sus partes 7a y 7b.

  • Lo que no necesita criptografía (locator.ts, ipaddr.ts): los datos de la extensión (parseInfo e infoExtension); standardExtensions, el registro de las extensiones de la especificación, que por defecto comprueba los datos de datekeys.capsule como locator.Standard; las direcciones, con cada regla del §44.1 de la v0.12 (checkURI, addressHost, usableAddresses), los bloques de IANA comparados byte a byte sobre los 16 bytes de una IPv6 y los CID v1 en base32; checkResolvedIp, la IP a la que resuelve un nombre, como la de datekeys-dart, que hoy rechaza 64:ff9b::/96; el texto en claro con su relleno (marshalLocator, unmarshalLocator, plaintextLength); y el resto en su host (restIn, hide).

  • La criptografía (ageio.ts, envelope.ts): abrir un localizador sellado con el release de su ronda (openSealed, openInfoLocator), que lee como mucho 1 MiB como Go; abrir el sobre (openEnvelope); sellar (seal) y crear el sobre (newEnvelope), con una fuente de lo aleatorio inyectable que se lee en el orden de Go. ageio.ts lee y escribe ficheros age como filippo.io/age 1.3.2, con sus textos, porque locator.Open y OpenEnvelope los copian. Sin dependencias nuevas: usa los módulos de noble que ya usa x25519.ts, y HMAC es el HKDF-Extract de @noble/hashes/hkdf.js.

  • Nada entra en /inspect. index.ts no reexporta el localizador: la nota trae las tablas de Unicode, y el sobre, noble. dependencies.test.ts añade ageio.ts a los que pueden importar noble y los cuatro módulos a los que index.ts no reexporta, y check-build.mjs falla si una página carga el localizador con su primera carga.

  • Contra Go, todo con el resultado y el texto de Go:

    • vectors.test.ts corre entero testdata/vectors/locator.json, en vez de mirar solo su campo spec;
    • testing/locator-uris.json y testing/locator-vectors.json, de scripts/locator-go-vectors.go: 6 531 casos de direcciones, IP, textos en claro, localizadores sellados, sobres, datos de la extensión, el registro y la apertura de una cápsula cuya .dkk lleva datekeys.capsule, y las 20 585 bases del relleno de −4 100 a 16 484;
    • testing/locator-seal.json, de scripts/locator-seal-go-vectors.go: con la misma semilla, seal y newEnvelope sacan los mismos valores que Go en el mismo orden y escriben los mismos bytes;
    • testing/locator-interop.json: Go abre los localizadores y los sobres que escribe esta librería (scripts/locator-ts-samples.mjs y scripts/locator-go-verdicts.go), de 0 bytes a 16 MiB y un byte.

    Los generadores son los de datekeys-dart con las semillas de este repositorio, y corren en una exportación de datekeys-go en spec-v0.12.

  • Lo que el autor tiene pendiente se queda como en Go: un CID no canónico pasa, https://[[2000::]/ pasa, parseInfo comprueba menos de lo que podría y openSealed lee 1 MiB.

  • Pruebas. locator.test.ts, envelope.test.ts y locator.interop.test.ts, con los cuatro módulos al 100 % de cobertura: 7 901 pruebas en total.

La especificación 0.12, aprobada (06-10-2026)

  • El autor aprobó el 6 de octubre de 2026 el borrador v0.12, tal como estaba: datekeys-go lo cierra con el tag spec-v0.12 (fe405e2). SPEC_VERSION pasa a 0.12, y testdata se sincroniza con ese tag: solo cambia el campo spec de cada fichero.
  • testing/mutation-texts.json se regenera con scripts/mutation-go-texts.go: solo cambia su campo spec.
  • La librería ya seguía el borrador: no cambia nada más. npm run verify pasa con 7 832 pruebas.

Los vectores compartidos de la llave de palabras (06-10-2026)

  • testdata se sincroniza con datekeys-go en 084728d, que añade vectors/wordkey.json: los casos de la llave de palabras que pide el §64 de la v0.11, que hasta ahora solo estaban en las pruebas de Go. Ningún otro fichero cambia.
  • Un bloque nuevo de vectors.test.ts los corre: las palabras de 45 textos, entre ellos cada espacio del §38.1 y tres que no lo son; lo que hace un escritor con 20 textos, con el texto del error de Go; y 6 identidades con su recipient, el vector del §38.1 el primero. La librería no cambia: todos coinciden.

El borrador v0.12: testdata, la regla del §72 y la nota de inspect (05-10-2026)

  • testdata se sincroniza con la cabeza de la rama v0.12 de datekeys-go (601e6d2); SPEC_VERSION sigue en 0.11 hasta que el autor apruebe el borrador. Trae:
    • los fixtures format3_unsigned, el contenido de format3_signed sin firma y con la misma P, y format3_note, con nota pública;
    • format3_seal_unsupported con seal_type 4294967295;
    • note.json, security.json con su contexto y sus líneas, los 135 casos de security_cms.json y los 218 de mutations.json, 178 de ellos del §64.
  • Los vectores de Go. mutation-texts.json se regenera con scripts/mutation-go-texts.go: cambian solo los ocho casos nuevos y el nombre del de seal_type. ibe-vectors.json gana las entradas de format3_note, format3_unsigned y el format3_seal_unsupported nuevo, sin tocar sus valores congelados.
  • La regla de los codificadores del §72, checkWrite en extension.ts, con NOTE_ID y CAPSULE_ID, como extension.CheckWrite de Go:
    • el escritor de cápsulas rechaza datekeys.note fuera del array no crítico de la cabecera o con datos que incumplen sus reglas, y datekeys.capsule en una cápsula;
    • el de .dkk rechaza una nota, y datekeys.capsule fuera de su array no crítico o sin datos;
    • los textos de error son los de Go.
  • La nota pública. checkNoteData comprueba los bytes de una nota en el orden de Go, y unusableNote distingue una nota inservible de ninguna. inspect lee la nota bajo demanda, solo si la cabecera lleva una, y la vista de inspect -json da public_note y public_note_unusable, como la CLI de Go. Por eso /inspect sigue sin cargar las tablas de Unicode.
  • Pruebas. note.json, y el bloque de mutations.json con sus recuentos nuevos: 7 759 pruebas en total.

El lector de certificados y los textos del borrador v0.12 (05-10-2026)

Los fallos T2, T6, T7 y T8, y lo que toca a TypeScript de E2, E3, E7, E8 y E9, de spec_v0.11/revision_sesion_1_2_octubre.md (en ../docs), como los arregla datekeys-go en la rama v0.12 (601e6d2), cuyos security.json y security_cms.json leen las pruebas.

  • El certificado, campo a campo, con el perfil del §29.10 del borrador v0.12: versión 3, los campos en orden, nombres de SET no vacíos, la validez en DER y sin fracción, las extensiones sin repetir y un subjectKeyIdentifier no vacío. Uno que lo incumple no decide nada salvo que lo nombre un SignerInfo, y dos copias de uno cuentan como una. El texto de un nombre sale solo de los cinco tipos de cadena, en su alfabeto, sin quitar nada y nunca de un atributo repetido. El titular es su givenName y su surname antes que su commonName, que puede llevar el NIF, y el emisor, su commonName o su organizationName, ya no el texto de todos sus atributos.
  • Identificadores, claves y sellos. Los OID se comparan por los bytes de su DER: un arco de 2³¹ o más es solo uno que la tabla no tiene. Un SET OF puede repetir un elemento, así que una TSA que manda dos veces su certificado ya no da S2. La clave RSA lleva parámetros NULL, exactamente un módulo y un exponente, y un módulo impar, y una clave de otro esquema que su algoritmo da F2, no F5. Un messageImprint de otra longitud da S3, los crls de un token no deciden nada, y los milisegundos y microsegundos de accuracy son INTEGER mínimos. der.ts comprueba las horas en sus formas de X.690 y admite los tipos de cadena restringidos, NumericString entre ellos.
  • Los textos de los veredictos. Cada nombre de un certificado va entre « y », y se muestra si cumple las reglas del autor declarado, tiene como mucho 64 puntos de código y no lleva dos espacios seguidos; si no, su SHA-256. La línea de cada firmante de F6 nombra la autoridad de su sello, y si alguna dice «antes de la fecha de apertura», la sigue «DateKeys no comprueba quién emitió los sellos.». El resultado de un firmante ajeno va en español, y una hora lleva la fracción de su sello.
  • Pruebas. cms.test.ts, der.test.ts y securitycms.test.ts portan las de internal/cms, internal/der y signature2_test.go, y la del emisor sin commonName ya no lo compara consigo mismo. vectors.test.ts lee security.json con su contexto y sus líneas, y compara en cada caso de security_cms.json los veredictos, los firmantes exigidos y los ajenos, el sello y las líneas.
  • Diferencial con Go. capsule.EvaluateSecurityIn y Verdicts.Lines de 601e6d2 dan lo mismo que evaluateSecurity y verdictLines, campo a campo, en 63 623 áreas: las de los vectores, sus mutaciones byte a byte y elemento a elemento, y áreas firmadas y selladas de verdad cuyos certificados, firmas y tokens varían campo a campo. Antes de estos cambios diferían en 13 296 de 42 986.

Arreglos de la revisión de la sesión del 1 y 2 de octubre (02-10-2026)

Los fallos T1, T3, T4, T5, T9 a T12, T14 y parte de T13 de spec_v0.11/revision_sesion_1_2_octubre.md (en ../docs). Cada texto y cada veredicto nuevo se contrastó con un oráculo de Go sobre el tag spec-v0.11.

  • CMS como lo lee Go. Un emisor que incumple las reglas del autor declarado se muestra con el SHA-256 de su Name (RawIssuer), y no con el del certificado. Una clave ECDSA solo cuenta sin comprimir, 0x04 y las dos coordenadas, como en x509.ParsePKIXPublicKey: con el punto comprimido, un firmante es «no verificable» (F5) y un sello da S1. Un UTF8String y las horas del certificado y del token conservan un U+FEFF inicial, así que ese nombre se muestra con el hash y esa hora rompe el perfil (F1, S2).
  • Lectura lineal. oidOf e intOf leen en tiempo lineal, sin un desplazamiento por byte, que tardaba unos 700 ms con 60 KB; los atributos de un tipo se añaden sin copiarse.
  • evaluateSecurity nunca lanza. Una excepción al evaluar la firma da F1, y una al evaluar el sello, S2, cada una sin tocar el otro veredicto; una al decodificar el área da X. La referencia hará lo mismo desde la v0.12.
  • Pruebas que no probaban lo que decían. Las de cms.test.ts de un segundo content-type y dos signature-time-stamp fallaban por un SET OF desordenado, y ahora fallan por la regla de recuento. La de profundidad de der.test.ts fallaba por la longitud, y ahora prueba la frontera de 32 niveles.
  • La nota pública. checkNote rechaza un texto con UTF-16 mal formado, con el texto de Go para el UTF-8 inválido, así que ninguna nota se escribe alterada. publicNote conserva un U+FEFF inicial, que la deja inservible como en Go. También lo conservan authorCode y la lectura de la respuesta de los relays de drand; el head y las rutas ya lo hacían, y sus pruebas lo fijan con los textos de Go.
  • El escritor. testVectors y areaLen salen de las opciones públicas: con ellas, cualquiera podía escribir el formato 2 o un área de 512 bytes, que delata la falta de firma (§55.2).
    • Lo que solo pide un generador de vectores es el argumento TestVectors del núcleo, que solo pasan los ayudantes de testing/encrypt.ts: encryptVectors, encryptWith y encryptFilesWith.
    • encrypt conserva la forma de capsule.Encrypt: sin generador, falla con su texto.
    • dependencies.test.ts y check-build.mjs dejan testing/ fuera de la librería y de las páginas.
  • Las opciones, como en Go. Como generador, encrypt rechaza la nota y el área con el texto de capsule.Encrypt. Los errores de la nota llevan capsule: , y las opciones se comprueban en el orden de newSealer.
  • El tamaño con nota. capsuleLength acepta la nota pública y predice exactamente el tamaño del .dkc con ella.
  • Interoperabilidad. capsule-vectors.json se regenera con el escritor actual: las seis cápsulas de formato 3 llevan el área de 32 KiB, y hay dos más con nota pública, una de 1024 bytes. Go las abre con cada credencial, encuentra el área de 32 KiB y lee la nota con Header.PublicNote. El fichero gana el texto de capsule.Encrypt para una nota en el formato 2, y los de capsule.EncryptFiles para nueve notas inválidas.
  • Documentación. El README completa la tabla de módulos (author.ts, ed25519strict.ts, der.ts, cms.ts, securitycms.ts y note.ts), los veredictos de la v0.11, las dependencias y sus guardas, y los recuentos del corpus de mutaciones; los comentarios ya no hablan del área de 512 bytes ni de una versión sin alg.
  • Pendiente, con la v0.12: el lector de certificados, cuyo perfil se fija ahora en Go (T2, T6, T7 y T8), el texto del emisor sin CN y la prueba de cms.test.ts que lo compara consigo mismo.

testdata en spec-v0.11 (01-10-2026)

  • testdata se sincroniza con el tag spec-v0.11 de datekeys-go (ae33434), y SPEC_VERSION pasa a 0.11. Trae tres fixtures (format3_signed, con firma de clave propia; format3_signed_cms, con dos certificados sellados; format3_sealed, con firma y sello RFC 3161) y tres ficheros de vectores (ed25519_strict.json, security_cms.json y locator.json). El corpus de mutaciones pasa a 210 casos, con uno fuera del §64: una firma de alg 1 que no verifica, F2. ibe-vectors.json añade los tres fixtures y rehace los de format3_signature_unsupported y format3_seal_unsupported; mutation-texts.json se rehace con el capsule.Open de esa referencia.
  • La firma de clave propia, alg 1 (§29.8, §29.9), portada: ed25519strict.ts comprueba las cuatro condiciones del perfil estricto con la aritmética de @noble/curves (que solo ofrece la ecuación con cofactor) y da la respuesta de Go en los 18 vectores de ed25519_strict.json; author.ts calcula payload_commit, control_commit, head_digest, signers_digest, AUTHOR_MESSAGE y su código, y los registros de format3_signed los confirman. evaluateSecurity(área, contexto) da F2, F3 y F4, open pasa el contexto del control y del head, y OpenOptions.authorKeys son las claves que la persona guardó. Los dos módulos usan solo @noble/curves y @noble/hashes, que ya iban en el bundle: ningún paquete nuevo, y entran en la lista de quien puede importar noble.
  • La firma con certificados, alg 2, y el sello RFC 3161, seal_type 2 (§29.10, §29.11), portados sin dependencias nuevas: der.ts comprueba el DER byte a byte, cms.ts lee la firma CMS y el token con la tabla cerrada de algoritmos (RSA PKCS #1 y PSS en BigInt, síncrono, y ECDSA con la aritmética de @noble/curves) y securitycms.ts da F1, F2, F5 y F6 con los firmantes nombrados, y S1 a S5 con la autoridad del sello. evaluateSecurity los devuelve con su detail, verdictLines escribe las líneas de F6 y S4, y open pasa la hora de la ronda. Reproducen los 22 casos de security_cms.json, con los resultados de cada firmante, y los fixtures format3_signed_cms y format3_sealed. testing/cmsbuild.ts construye firmas y tokens de prueba con WebCrypto, y cms.test.ts porta los casos hostiles de Go.
  • El escritor de la v0.11 (§29.2, §24.1, §62.1 regla 13): encryptFiles escribe el área de seguridad de 32 KiB (AREA_LEN pasa de 512 a 32768) y acepta publicNote, la extensión datekeys.note de la cabecera pública (note.ts: checkNote, newNote, publicNote, con las reglas de texto del autor declarado). Otra área solo la escribe un generador de vectores, con testVectors y areaLen, y así los tests reproducen byte a byte los fixtures que escribió un escritor de la v0.10, de 512 bytes. lengths.ts y la página calculan L con el área nueva. Una nota cambiada después de escribir la cápsula falla en el paso 15.
  • Lo que esta biblioteca no hace todavía: el escritor no firma ni pide sellos (alg 1, alg 2 y RFC 3161 solo se leen y verifican), la página no pide ni muestra la nota, y el localizador del §44.1 (la extensión datekeys.capsule de la .dkk, su sobre y su relleno) y la página de firma. locator.json solo se comprueba en su estructura.

El formato 3 de la especificación 0.10, según PLAN_formato3_ts.md (en ../docs). La versión que lo publique la decide el autor.

Llave de palabras y firma de drand

  • Llave de palabras v2, como decide el borrador de la especificación 0.11 (§38.1): la sal lleva además el capsule_id, así que las mismas palabras dan otra llave en cada cápsula; las palabras se pasan a minúsculas con la tabla de Unicode 18.0.0 de pathrule.ts, no con la de la plataforma; cuentan solo las distintas de 3 letras o más; y se rechazan los caracteres invisibles. encryptFiles recibe las palabras (words) y deriva la llave al sortear el capsule_id. /create pide escribirlas dos veces y muestra cómo se guardan. Las cápsulas hechas antes con palabras ya no se abren con ellas. El módulo pasa a src/lib/dkc/wordkey.ts.
  • /inspect pide la firma de la ronda a los relays de drand con un botón (drand.ts), además de poder pegarla.
  • Una cápsula «solo con una llave» puede abrirse con palabras que elige quien la crea, al menos 6, en vez del fichero .dkk (wordkey.ts): PBKDF2-SHA256 de 600.000 vueltas, con la red y la ronda como sal, da una clave X25519 que entra como una persona de age más. El formato no cambia; dan igual mayúsculas, acentos y espacios.

Paso 8: revisión adversarial y rediseño de /create

  • Correcciones de la revisión: el payload llega a age en trozos de un chunk (chunked), el sumidero nunca se aborta tras el commit y recibe copias, el escritor copia cada trozo que lee y acepta fuentes escritas como clase, y las extensiones de tipos incorrectos son un TypeError.
  • El sitio es siempre claro. /create se rediseña como una carta al futuro: tres preguntas, fechas rápidas, la fecha de apertura en un sobre de correo aéreo con el botón, y los detalles técnicos plegados; los mensajes ya no citan R4 ni §29.6.
  • /inspect marca el principio de un fichero como contenido del creador, sin comprobar, y conserva ZWNJ y ZWJ en el comentario y el autor.

Paso 7: /create con ficheros y carpetas

  • La página cifra ficheros y carpetas, elegidos o soltados, con un comentario y un autor declarado. Las rutas se pueden editar y se comprueban mientras se escriben, cada problema en su fila y en español; los ficheros de los sistemas quedan fuera, tachados, salvo que se marquen; y una casilla, marcada por defecto, guarda la fecha de cada fichero.
  • El tamaño exacto del .dkc antes de escribir, con los ficheros medidos una vez (measureFiles, headLengthOf y bodyLengthOf en lengths.ts), y el progreso de las dos lecturas de encryptFiles.
  • create-files.ts (la lista, sin tablas) y create-check.ts (las reglas en español, bajo demanda), al 100 %. Go abre una cápsula de la página, y /inspect también.

Paso 6: /inspect abre el formato 3

  • La página abre las cápsulas de formato 3: los ficheros van al fichero temporal por un ZipSink, o a la memoria, y se muestran primero los veredictos, después el autor declarado y el comentario, y luego cada ruta como texto, con su tamaño, su fecha y los avisos de la CLI de la referencia. Se descarga el fichero, si es el único, con el ZIP de su carpeta como segunda opción, o el ZIP de todos y cada fichero por separado.
  • files.ts (avisos, rutas y descargas) y opener.ts (OpenedFiles, la falta de espacio), cargados bajo demanda; ZipSink comprueba el espacio al empezar.
  • check-build.mjs exige que las tablas de las rutas no entren en la primera carga de ninguna página.

Paso 5: el ZIP de la página

  • zipsink.ts: ZipSink, el sumidero de la página sobre el fichero temporal de OPFS. Un fichero de un segmento va tal cual, y los demás casos a un ZIP propio, con el CRC-32 de cada entrada parcheado con una escritura posicionada y el directorio central al hacer commit; nada se publica antes del paso 18. zipOf hace el mismo ZIP en memoria.
  • tempfile.ts: el writable de un fichero temporal acepta trozos con posición (TempChunk), como FileSystemWritableFileStream.
  • Interoperabilidad: archive/zip de Go lee las muestras de la página, 65 535 entradas con ZIP64 incluidas, con sus nombres, CRC-32, tamaños, fechas y contenidos (testing/zip-vectors.json, de scripts/zip-ts-samples.mjs y scripts/zip-go-read.go).

Paso 4: la escritura del formato 3

  • writer.ts se parte como el writer de Go: newSealer comprueba las opciones que no dependen del contenido, y seal escribe la cápsula de un formato alrededor de un contenido dado en trozos. El formato 2 no cambia.
  • encryptFiles(files, opts) escribe un .dkc de formato 3 con sus ficheros, el comentario y el autor declarado, y las extensiones del head, como capsule.EncryptFiles: lee cada fichero dos veces y falla, con el texto de Go, si cambió entre las dos lecturas. fileSource hace la fuente de un File. Los sorteos ganan la sal del head.
  • lengths.ts: bodyLength, headLength, mtimeSeconds y headComment, para dar el tamaño exacto del .dkc antes de escribirlo.
  • encrypt escribe el formato 2 solo con testVectors, y sin comentario, autor ni extensiones del head, con los textos de capsule.Encrypt; las pruebas, encryptWith y los scripts de muestras lo piden.
  • /create escribe el formato 3 con encryptFiles: el fichero va bajo su nombre y con su fecha de modificación, y el plan da el tamaño exacto con bodyLength.
  • Interoperabilidad: capsule-vectors.json se regenera con seis cápsulas de formato 3 escritas por encryptFiles, que Go abre en un Sink con cada credencial, con los mismos ficheros, head y veredictos; las trece de formato 2 y los demás bloques se regeneran igual.
  • Pruebas: los cinco fixtures que escribió EncryptFiles, byte a byte; los tamaños frente a lo escrito y 300 heads aleatorios frente a encodeHead; y las entradas inválidas, con los textos que da capsule.EncryptFiles a las mismas entradas.

Paso 3: la lectura del formato 3, con testdata en spec-v0.10

  • testdata se sincroniza con el tag spec-v0.10 de datekeys-go (cc35d2c), y SPEC_VERSION pasa a 0.10.
  • framing.ts: FORMAT_3, e isPadded para los formatos 2 y 3. control.ts: la versión de schema 3, con las claves 6 y 7 de la 2.
  • open.ts y open3.ts: el paso 17 del formato 3 en sus subpasos, con la precedencia y los textos de capsule.Open. Los ficheros van a OpenOptions.sink (sink.ts: Sink y MemorySink), que solo los publica en el paso 18; sin sumidero, open rechaza con un TypeError justo tras el paso 2. Opened gana head, verdicts, areaLen y unusableHeadExtensions.
  • La página: opener.ts abre en memoria los fixtures del formato 3; el panel de apertura dice que aún no entrega sus ficheros, y los textos de los pasos nombran el formato 3.
  • Pruebas: los 21 fixtures; los 209 casos del corpus, con el texto exacto de capsule.Open, congelado con scripts/mutation-go-texts.go en testing/mutation-texts.json; las 5 110 mutaciones del diferencial; los vectores de rutas, pliegue, head y seguridad; y los textos del paso 17 con textos en claro preparados, contrastados con Go. ibe-vectors.json gana los nueve fixtures nuevos.
  • check-build.mjs impide publicar el head, la sal, el comentario y las rutas de los fixtures del formato 3.

Paso 2: el códec del formato 3

  • body.ts, security.ts y head.ts, como capsule/format3.go: la trama de BODY, los veredictos del área de seguridad con sus textos, y el head con las capas de §69.1. ERR_HEAD_INVALID es el código 19.

Pasos 0, 1 y parte del 5: las reglas de las rutas y el ZIP de la página

  • pathrule-tables.ts, generado por datekeys-go con Unicode 18.0.0 y WindowsBestFit, y pathrule.ts, las reglas de las rutas y de los textos del head con los textos de error de Go.
  • crc32.ts y zip.ts, en src/lib/inspector: la disposición del ZIP en que la página entregará los ficheros, con entradas almacenadas, nombres UTF-8, las fechas en DOS, NTFS y el sello extendido, y ZIP64.

La fase 3: la escritura del formato 2

Fase 3: la escritura de cápsulas de formato 2, según PLAN_fase3_escritura.md (v3, en ../docs). Hecha, pasos 0 a 7.

Después del paso 7: ayuda de age y el texto en claro a la vista

  • /inspect muestra el principio del texto en claro de cualquier cápsula que se abre, no solo de los fixtures, si es texto: UTF-8 imprimible, hasta 100 000 caracteres de sus primeros 128 KiB. Se ve también un texto escrito en Windows, con CR LF o BOM; la descarga conserva los bytes exactos. opener.ts da esos primeros bytes (PREVIEW_BYTES), y opening.ts decide qué se muestra (plaintextPreview).
  • Ayuda de las claves de age. En /create, un bloque plegable explica qué es un destinatario age1… y cómo se consigue con age-keygen. En /inspect, el campo de identidades dice qué pegar: la línea AGE-SECRET-KEY-1… del fichero de age-keygen, o el fichero entero.
  • Al abrir, el contenido va justo debajo del veredicto. La descarga de una cápsula sin extensión propia, como capsula-<fecha>.dkc, toma la del contenido (contentExtension): .txt, .pdf, .png, .jpg y otras por sus primeros bytes. La cápsula no guarda el nombre del fichero (§6, §55.2).
  • vite preview sirve las páginas con Cache-Control: no-cache, para que una pestaña recargada tras compilar no se quede con la página anterior, cuyos ficheros ya no existen.

Pasos 6 y 7: la página /create

  • El autor confirma las decisiones de la página con cada recomendación: time_only por defecto, la zona del dispositivo con un selector, los avisos de §53 y §50 desde 365 días, un aviso de protocolo preliminar y los nombres capsula-<apertura en UTC>.
  • /create cifra un fichero propio en un .dkc de formato 2 y, si se pide, en una .dkk portable, en el navegador y sin red. Antes de cifrar muestra el instante efectivo, la ronda, la dk1_, el tamaño exacto del .dkc y lo que deja ver hasta la fecha. El .dkc va a un fichero temporal de OPFS, o a memoria hasta 64 MiB, y la escritura se puede cancelar. La .dkk vive solo en memoria, y la página avisa si se sale sin descargarla. El resultado lleva el informe de los pasos 1 a 8 de lo escrito.
  • lengths.ts: sealedControlLength sale de writer.ts, y capsuleLength da el tamaño del .dkc antes de escribirlo; el bucle de propiedades lo comprueba en cada cápsula. datekey.ts: LONG_HORIZON_SECONDS e isLongHorizon.
  • localtime.ts, create-input.ts y creator.ts, al 100 %. /inspect limpia también la zona de crear. check-build.mjs comprueba la carga bajo demanda de las dos páginas que la tienen.
  • Comprobado en el navegador: una cápsula creada para dentro de cuatro minutos se abrió después en /inspect con el release pegado y con datekeys decrypt de Go, con el mismo contenido.
  • Una revisión adversarial encontró un fallo mayor y ocho menores, todos corregidos. El mayor: la .dkk que es la única credencial se podía borrar sin confirmación. Ahora la página la pide al olvidarla y al crear otra cápsula. Entre los menores: la zona desconocida del dispositivo pasa a UTC; el reloj de la página se lee cada segundo; hay un mensaje propio para un reloj anterior a Quicknet; lo escrito no se ofrece si los pasos 1 a 8 lo rechazan; y el foco va a «Cancelar» durante la escritura. localtime.test.ts compara la conversión con una búsqueda exhaustiva alrededor de todos los cambios de hora de 2030.

Paso 5: interoperabilidad con Go a nivel de cápsula

  • interop.test.ts y testing/capsule-vectors.json: Go abre con capsule.Open las trece cápsulas de muestra que escribe encrypt, con cada credencial, y reencodifica sus objetos a los mismos bytes. Cubren las dos políticas y las dos reglas, de 0 a 16 credenciales, los bordes de trozo y las extensiones.
  • Go rechaza cuatro mezclas de dos cápsulas con el mismo código y paso que open, codifica igual 500 entradas aleatorias de los codificadores, y da los mismos textos que esta librería con 22 recipients y 21 opciones inválidas.
  • Lo generan scripts/capsule-ts-samples.mjs y scripts/capsule-go-verdicts.go, y se congela; el test comprueba en cada ejecución los veredictos de Go y que open da lo mismo sobre los bytes congelados.

Pasos 3 y 4: el writer

  • encrypt.ts y writer.ts: encrypt(src, opts) escribe un .dkc de formato 2 y, si se pide, una .dkk portable, como capsule.Encrypt: L conocida de antemano, relleno reforzado por defecto, de 1 a 16 credenciales con señuelos en un orden uniforme, SEALED_CONTROL_LEN con la fórmula del §62.1 y las autocomprobaciones de la regla 11 y dos más. Streaming desde Uint8Array, Blob o ReadableStream, hacia memoria o hacia un WritableStream que solo se cierra con la cápsula completa y comprobada.
  • Reproduce byte a byte las secciones deterministas de los siete fixtures de formato 2 de Go, y todo lo que escribe se abre con open.
  • Tests de streaming, de errores internos con age-encryption sustituido y un bucle de propiedades (50 semillas en cada ejecución; 500 pasaron a mano).

Paso 2: piezas de apoyo

  • recipient.ts: recipients age1… como age 1.3.2, las reglas de §37 con los textos de agewrap.CheckX25519Recipient y la lista de recipients de una persona, con errores por número de línea. Sin noble.
  • random.ts: el índice sin sesgo y la permutación de Fisher–Yates del orden de los 16 huecos, con la prueba de uniformidad de Go.
  • agefile.ts: los ficheros age enteros que antes eran privados de la apertura, para compartirlos con el writer.
  • x25519.ts: newX25519Identity y x25519PublicKey, con los vectores de RFC 7748. digest.ts: sha256Hasher. datekey.ts: compareInstants, que usa open.ts, e isInstant.
  • tempfile.ts: la zona de la apertura y la de crear (OPEN_AREA, CREATE_AREA), con su limpieza por separado.
  • Guardas: solo agefile.ts, open.ts, tlock.ts, writer.ts y los tests importan age-encryption; solo encrypt.ts y testing/ importan writer.ts; index.ts no reexporta la apertura ni el writer.

0.1.0 — 29 de septiembre de 2026

Implementa la especificación DateKeys 0.9 (tag spec-v0.9 de datekeys-go) para el perfil Quicknet, y pasa todos los vectores y fixtures compartidos de datekeys-go en 7e2d83c. Hasta el 29-09-2026 implementaba la 0.8.2 (9ac9cd9).

Especificación 0.9: el formato 2

  • El prelude lee el formato de la cápsula, 1 o 2 (Prelude.format); otro VERSION es ERR_UNSUPPORTED_VERSION en el paso 2. DKC_FRAMING_VERSION desaparece: FORMAT_1, FORMAT_2 e isFormat.
  • CONTROL_CBOR se lee y se escribe para un formato, decodeControl(b, format) y encodeControl(c, format): su versión de schema es la del formato, y en el formato 2 lleva payload_length (8 bytes, hasta L_MAX) y padding (claves 6 y 7).
  • padding.ts: las reglas bloque256 y reforzado de §29.1, exactas hasta L_MAX = 2⁵³ − 2⁴⁶, y la longitud de PAYLOAD_AGE.
  • open: exactamente 16 stanzas en INNER_ACCESS_AGE del formato 2 (paso 12, ACCESS_SLOTS), P en el paso 16, y en el 17 un texto en claro de P bytes con ceros tras el contenido, del que solo se entregan los L primeros bytes, nunca el relleno. El paso 17 se registra también cuando se supera, y el 18 da los bytes de contenido, como la referencia. Opened da format, payloadLength y, en el formato 2, padding y paddedLength.
  • La vista JSON de inspect lleva format, como datekeys inspect -json.
  • La página muestra el formato de la cápsula, avisa cuando es el 1, que no oculta el número de credenciales ni la longitud exacta del contenido, y al abrir una del formato 2 da la regla de relleno y P.
  • testdata sincronizado con spec-v0.9: doce fixtures, siete de ellos del formato 2, padding.json, 125 casos de mutación y 4 380 del diferencial. ibe-vectors.json añade los siete fixtures nuevos.

Hecho

  • Codec CBOR del perfil de §58 con los textos de error de la referencia Go.
  • Schemas del Provider Profile, PUBLIC_HEADER, CONTROL_CBOR y .dkk.
  • Tramas DKC1 y DKK1, cabeceras age y DateKey (dk1_).
  • Extensiones, con los objetos y arrays de su registro.
  • La inspección de los pasos 1 a 8 de §63.
  • La página estática /inspect, sin red. Tras cada compilación se comprueban su política de seguridad y los paquetes de su bundle.
  • Fase 2:
    • dependencias de ejecución (age-encryption 0.3.1, @noble/curves y @noble/hashes 2.4.0) con sus guardas;
    • el IBE de tlock (ibe.ts) y la verificación local de releases (release.ts), contrastados con la referencia Go;
    • la apertura, pasos 9 a 18 de §63 (open.ts), con el texto en claro en memoria. Las identidades estrictas de agewrap se apoyan en x25519.ts, que abre cada stanza X25519 por separado, y en bech32.ts. Los 65 casos del corpus de mutaciones pasan por open con el código y el paso de Go, y los cinco fixtures oficiales se abren a su texto en claro;
    • @noble/ciphers 2.4.0 como dependencia directa, aprobada el 28-09-2026: la copia que ya trae age-encryption;
    • la apertura en streaming. La entrada puede ser un Blob, del que se lee solo el prefijo de los pasos 1 a 8 (prefix.ts, antes en la página) y se descifra PAYLOAD_AGE en streaming. La salida puede ser un WritableStream, que se cierra solo tras el paso 18 y se aborta ante cualquier fallo. En el navegador, con un fichero OPFS, un fallo de STREAM deja intacto su contenido anterior.
    • el cifrado del stanza tlock (encryptOnG2RFC9380) y el Recipient de OUTER_TIME_AGE (tlock.ts). Con sigma fijo, el cifrado reproduce byte a byte los vectores de Go. Además Go abre lo que cifra esta librería: el cuerpo IBE con tlock.TimeUnlock y el fichero age con agewrap.NewTimeIdentity;
    • la acción "abrir" de /inspect (paso 8), cuyo código, con noble y age-encryption, se carga bajo demanda:
      • el release lo pega quien abre (la respuesta de drand o la firma sola), o sale del registro de un fixture. La página nunca lo pide a la red y solo lee su ronda y su firma;
      • las credenciales de time_and_key son una .dkk o identidades AGE-SECRET-KEY-1…;
      • el texto en claro de un fichero propio va a un fichero temporal de OPFS que solo se confirma tras el paso 18. Se ofrece para descargar y se borra al pedirlo, con otra cápsula, al salir o en la visita siguiente;
      • readAccessKey en prefix.ts.
  • VERSION y SPEC_VERSION, también en el pie de la página.
  • licenses.txt en el sitio, con los avisos de tlock-js y age y los de cada paquete del bundle.

Powered by TurnKey Linux.