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

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

Especificación 0.10, en la rama v0.10 — sin versión

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.
  • Lo que esta biblioteca no hace todavía: la firma con certificados (alg 2: F5, F6) y el sello RFC 3161 (S3 a S5), que lee como un lector de la v0.10 y dan F1 o S1 aquí; y el localizador del §44.1. El tipo Verdict y los textos ya los conocen. Los tests lo dicen en lugar de ocultarlo: testing/pending.ts da lo que esta biblioteca devuelve donde la referencia registra esas verificaciones, y un bloque de vectors.test.ts comprueba la estructura de los vectores que aún no se portan. Portarlas hace esas funciones la identidad.

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.

0.2.0 — sin publicar

Fase 3: la escritura de cápsulas de formato 2, según PLAN_fase3_escritura.md (v3, en ../docs). Hecha, pasos 0 a 7; la versión sigue sin publicar hasta que el autor la cierre.

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.