diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 3adb2f9..30b5fdc 100644 --- a/README.md +++ b/README.md @@ -176,3 +176,7 @@ Comprueba que los ficheros coinciden con `SOURCE.json`, sin faltantes ni sobrant `.gitattributes` marca `testdata/**` como binario para que git no altere ningún byte. Copia actual: la de `testdata/SOURCE.json` (commit `692cf87`, rama `v0.8.2`). + +## Licencia + +Apache-2.0 ([LICENSE](LICENSE)), como la librería Go de referencia. El código que se derive de terceros conserva su aviso de copyright y licencia en el propio fichero; el primero previsto es el núcleo IBE de la fase 2, derivado de `tlock-js` (Apache-2.0 OR MIT). diff --git a/docs/PLAN_fase2_ibe_noble2.md b/docs/PLAN_fase2_ibe_noble2.md index 62643e5..d9766df 100644 --- a/docs/PLAN_fase2_ibe_noble2.md +++ b/docs/PLAN_fase2_ibe_noble2.md @@ -1,6 +1,6 @@ # Plan: fase 2 del SDK TypeScript. Descifrado y cifrado tlock sobre noble 2, verificación local de releases y canonicidad de puntos -Estado: v1, 26 de septiembre de 2026. Sustituye la "Fase 2, cifrar y descifrar" de la sección 8 del plan v2 (`PLAN_codec_cbor_y_pagina_svelte.md`), cuyo bloque "Piezas de `tlock-js` y `drand-client`" contenía dos errores: para Quicknet el descifrador es `decryptOnG2`, no `decryptOnG1`, y `drand-client` no hace falta. Las decisiones de la sección 2 están pendientes de confirmación del autor; el resto del plan se ejecuta tal cual una vez confirmadas. +Estado: v2, 26 de septiembre de 2026. Decisiones de la sección 2 confirmadas por el autor el 26-09-2026, con los ajustes de dos revisiones (la de Claude Opus y la de Fable), verificados contra el código, el spec y npm. Sustituye la "Fase 2, cifrar y descifrar" de la sección 8 del plan v2 (`PLAN_codec_cbor_y_pagina_svelte.md`), cuyo bloque "Piezas de `tlock-js` y `drand-client`" contenía dos errores: para Quicknet el descifrador es `decryptOnG2`, no `decryptOnG1`, y `drand-client` no hace falta. Alcance: abrir y crear cápsulas del perfil Quicknet en el navegador, sin red, sobre la librería TypeScript de `App`. El núcleo IBE se escribe en el proyecto sobre `@noble/curves` 2.x, los releases se verifican con noble y el perfil pinneado, y la envoltura `age` la pone `age-encryption`. Ni `tlock-js` ni `drand-client` entran en el bundle. @@ -25,20 +25,23 @@ Todo lo de esta tabla se ejecutó el 26-09-2026 sobre `tlock-js` 0.9.0, `@noble/ | tlock-js 0.9.0 | `gitHead` `17d817e` (18-03-2024) es el commit que pasó a `@noble/curves`; el código auditado en 2022-2023 corría sobre `@noble/bls12-381`; `fp.ts` es nuevo en 0.9.0 y cerca del 40 % de `ibe.ts` cambió después. Sin tags ni releases, master sin cambios desde entonces. Cinco dependencias de ejecución; instalación limpia de 33 paquetes y 10,6 MB. `npm audit` sin avisos. Su guarda de longitud de mensaje solo salta a partir de 512 bytes. | | Spec v0.8.2 | No nombra U, V ni W, ni sus tamaños, ni la canonicidad de las codificaciones. §12.1 pide "la codificación comprimida de BLS12-381 que usa drand", subgrupo y no infinito, sin x < p. El paso 11 de §63 solo dice que un cuerpo que no es un ciphertext tlock del scheme da `ERR_INTEGRITY`. La regla de facto es el decodificador kilic. | | Librería TypeScript hoy | `src/lib/dkc` ejecuta los pasos 1 a 8 e `inspect`; `age.ts` parsea cabeceras `age` y `checkTimeStanzas` aplica cardinalidad, tipo, argumentos, ronda y chain hash con los códigos de Go; `bls12381.ts` valida puntos comprimidos; no hay writer de cápsulas. `@noble/curves` 2.4.0 es dependencia de desarrollo y un test prohíbe importarla fuera de tests. | -| Repositorio | `main` en `ee2dba9`. Existe la rama `wip/align-3820066` de otra sesión, alineación con `datekeys-go` `3820066`, interrumpida y con tests en rojo. | +| Repositorio | `main` en `d5e3236`, en verde: la alineación con `datekeys-go` está fusionada y `testdata` sincronizado a `692cf87` (rama `v0.8.2`). La rama `wip/align-3820066` ya no existe. | +| `age-encryption` 0.3.1 | Declara `@noble/curves ^2.0.1` y `@noble/hashes ^2.0.1`, que resuelven a la 2.4.0 de `App` sin override. Pero arrastra `@noble/post-quantum` 0.5.4, que declara `@noble/curves ~2.0.0` y `@noble/hashes ~2.0.0`: habrá una segunda copia de noble 2.0.x anidada bajo post-quantum, que la usa para ML-KEM y X25519, no para BLS. Admite la Streams API para cifrar y descifrar. | --- -## 2. Decisiones que este plan asume +## 2. Decisiones -Pendientes de confirmación del autor antes del paso 0 de la sección 10. +Confirmadas por el autor el 26-09-2026 (paso 0 de la sección 10). 1. **Núcleo IBE propio** en `src/lib/dkc/ibe.ts`, derivado de `tlock-js/crypto/ibe.ts` (commit `17d817e`, licencia Apache-2.0 OR MIT, con aviso de procedencia) y con la semántica de `drand/kyber encrypt/ibe`. Sobre `@noble/curves` 2.3.0 o posterior. Se descarta parchear `tlock-js` y se descarta una puerta alrededor de `tlock-js` sobre noble 1.9.7, que dejaría dos versiones mayores de noble en el bundle. 2. **Puerta de canonicidad**: toda firma, U y clave pública pasa por `checkCompressedPoint` y solo sigue si el resultado es exactamente `'point'`. Aunque noble 2.3.0 y posteriores rechazan lo mismo, la puerta fija la precedencia y el código de error, y trata la identidad canónica antes del pairing, donde noble la acepta y falla con otro mensaje. 3. **Solo Quicknet** (`bls-unchained-g1-rfc9380`: clave pública en G2, firmas en G1, U en G2). Los otros dos schemes de drand quedan fuera de esta fase; se anota en la sección 12. -4. **Releases sin red**: la obtención (paso 9) queda fuera del SDK en esta fase. El SDK recibe el release y lo verifica localmente (paso 10). La CSP `connect-src 'self'` de la página no cambia. -5. **`age-encryption` 0.3.1** para las tres envolturas `age`, con `Identity` y `Recipient` propios para el stanza `tlock`. Confirma la decisión 4 del plan v2. -6. **Cambio de spec**: canonicidad de puntos y contenido del cuerpo del stanza tlock (sección 7), en una revisión v0.8.3 o como enmienda de v0.8.2 según decida el autor. +4. **Releases sin red en esta fase**: la obtención (paso 9) queda fuera del SDK. El SDK recibe el release y lo verifica localmente (paso 10). La CSP `connect-src 'self'` de la página no cambia. El contrato de `ReleaseSource` se fija ya (sección 5): una fuente verifica cada respuesta, y si no obtiene ningún release verificado el resultado es `ERR_RELEASE_UNAVAILABLE` en el paso 9, como el cliente drand de Go (`provider/drand/client.go`); un release entregado por quien llama que no verifica es `ERR_RELEASE_INVALID` en el paso 10. Para el producto: la página habla con un único origen, la Release API propia, que consulta varios relays y verifica en el servidor (§47, §48); el SDK ofrece además la consulta directa a drand como fuente opcional (§49 SHOULD), nunca como opción por defecto de la página. +5. **`age-encryption` 0.3.1** para las tres envolturas `age`, con `Identity` y `Recipient` propios para el stanza `tlock`. Confirma la decisión 4 del plan v2. Sin overrides de npm: el rango `^2.0.1` resuelve a 2.4.0, y la copia 2.0.x que trae `@noble/post-quantum` (que declara `~2.0.0` a propósito) se acepta si solo la usa post-quantum. Las guardas de la sección 3 lo comprueban y el paso 2 mide su coste en el bundle. +6. **Cambio de spec**: canonicidad de puntos y contenido del cuerpo del stanza tlock (sección 7), como **enmienda de v0.8.2**, que no está fusionada ni etiquetada. En curso en `datekeys-go`, rama `v0.8.2`, junto con la limpieza del texto de error de kyber. +7. **Apertura en streaming**: `PAYLOAD_AGE` se descifra con la Streams API de `age-encryption` hacia un fichero temporal en OPFS, que solo se muestra o se ofrece para descargar cuando `age` termina sin error (§56). §57 no acota `PAYLOAD_AGE`, así que el límite es la cuota de almacenamiento del navegador, que se consulta con `navigator.storage.estimate()` antes de empezar. +8. **Licencia de `App`**: Apache-2.0, como `datekeys-go`. `ibe.ts` conserva el aviso de copyright y licencia de `tlock-js` (Apache-2.0 OR MIT). --- @@ -46,7 +49,7 @@ Pendientes de confirmación del autor antes del paso 0 de la sección 10. | Paquete | Versión | Estado | |---|---|---| -| `age-encryption` | 0.3.1 | pendiente de aprobación (decisión 5). Arrastra `@noble/ciphers`, `@noble/curves` 2, `@noble/hashes` 2, `@noble/post-quantum` y `@scure/base`. | +| `age-encryption` | 0.3.1 | aprobada (decisión 5). Arrastra `@noble/ciphers`, `@noble/curves` 2, `@noble/hashes` 2, `@noble/post-quantum` 0.5.4 (con su propia copia de `@noble/curves` y `@noble/hashes` 2.0.x) y `@scure/base`. | | `@noble/curves` | 2.4.0 | ya instalada como desarrollo; pasa a ejecución. Fijada exacta. | | `@noble/hashes` | 2.4.0 | dependencia exacta de `@noble/curves` 2.4.0; se declara explícita por importarse directamente. | | `tlock-js`, `drand-client` | — | no se instalan. | @@ -55,7 +58,8 @@ Guardas, todas en tests que corren en cada ejecución: - ningún fichero de `src/` importa `tlock-js` ni `drand-client`; - `@noble/*` solo se importa desde `src/lib/dkc/ibe.ts`, `src/lib/dkc/release.ts` y los tests. El test actual "only tests import @noble/curves" de `bls12381.contrast.test.ts` pasa a una lista blanca con esos dos ficheros; -- `package-lock.json` contiene una sola versión mayor de `@noble/curves` y de `@noble/hashes`; +- `ibe.ts`, `release.ts` y el test de contraste de `bls12381.ts` resuelven `@noble/curves` y `@noble/hashes` a la copia de la raíz, exactamente 2.4.0 (2.3.0 o posterior es el mínimo por la corrección de canonicidad), y ningún fichero de `src/` importa una copia anidada; +- `package-lock.json` no contiene ninguna versión 1.x de `@noble/curves` ni de `@noble/hashes`, y cualquier copia 2.x distinta de la raíz está anidada bajo `@noble/post-quantum`; - `scripts/check-build.mjs` comprueba además que el sitio construido no contiene `tlock-js`, `drand-client` ni `@babel`, e informa del tamaño del bundle de la página de apertura. --- @@ -74,7 +78,11 @@ Guardas, todas en tests que corren en cada ejecución: - `H3(sigma, msg)`: `base = SHA-256("IBE-H3" ‖ sigma ‖ msg)`; para `i = 1 … 65534`, `d = SHA-256(uint16le(i) ‖ base)`, `d[0] >>= 1`, se acepta el primer `d` big-endian menor que `Fr.ORDER`. Si el bucle termina, error. - `H4(sigma) = SHA-256("IBE-H4" ‖ sigma)[:len]`. - `roundIdentity(round) = SHA-256(uint64be(round))`. -- Errores: una clase `IbeError` con un motivo fijo (`length`, `encoding`, `identity`, `proof`). Ningún mensaje de error incluye `sigma`, `msg`, `r` ni bytes de entrada. Es la lección del texto de error de kyber, que `datekeys-go` copia hoy a sus diagnósticos (tarea aparte en ese repositorio). +- Errores: una clase `IbeError` con un motivo fijo (`length`, `encoding`, `identity`, `proof`). Ningún mensaje de error incluye `sigma`, `msg`, `r` ni bytes de entrada. Es la lección del texto de error de kyber, que `datekeys-go` copiaba a sus diagnósticos (se corrige en la misma rama que la enmienda de la sección 7). +- `sigma` y la file key se borran (`fill(0)`) en cuanto dejan de usarse, en todos los caminos, como hace la librería con `access_material` e `I_PAYLOAD`. +- Para tests, una variante interna de `encryptOnG2RFC9380` recibe `sigma` en vez de generarlo, de modo que el cifrado se compara byte a byte con Go. No se exporta desde `index.ts`. + +**Vectores de Go.** La referencia de los tests IBE es la librería Go, no los tests de `tlock-js`, cuyo `ibe.ts` cambió en un 40 % tras la auditoría. `scripts/ibe-go-vectors.go`, como `scripts/bls12381-go-verdicts.go`, genera con kyber y tlock: los bytes de GT de e(G1, G2) y de su cuadrado, H2, H3 y H4 sobre entradas fijas, la file key de cada stanza de los fixtures con la firma de su sidecar y un cifrado con `sigma` fijo. El resultado se congela en `src/lib/dkc/testing/`. El vector de GT de `tlock-js` (`cb87319f24560b5231579a09ad79f12e`) coincide con el de kyber (comprobado el 26-09-2026) y se conserva como contraste. **Serialización del stanza.** Cuerpo `U ‖ V ‖ W` de 128 bytes para una file key de 16; argumentos `[decimal canónico de la ronda, chain hash en hexadecimal minúsculo]`. La lectura ya existe en `age.ts`; la escritura se añade en `age.ts` junto a ella. @@ -82,16 +90,16 @@ Guardas, todas en tests que corren en cada ejecución: ## 5. Verificación de releases -`src/lib/dkc/release.ts`, `verifyRelease(profile, round, release)`, en el mismo orden que `provider.Verify` de Go (`provider/provider.go:53-76`): +`src/lib/dkc/release.ts`, `verifyRelease(profile, round, release)`, en el mismo orden que `provider.Verify` de Go (`provider/provider.go:53-76`), que decodifica la clave antes de mirar la firma: 1. ronda fuera del rango del perfil → `ERR_DATEKEY_INVALID`; 2. ronda del release distinta de la ronda de la condición → `ERR_ROUND_MISMATCH`; 3. firma que no mide 48 bytes → `ERR_RELEASE_INVALID`; -4. `checkCompressedPoint('G1', firma) !== 'point'` → `ERR_RELEASE_INVALID`; -5. clave del perfil pinneado, ya validada en los pasos 1 a 8; si no decodifica → `ERR_UNKNOWN_PROFILE`; +4. clave del perfil pinneado, ya validada en los pasos 1 a 8; si no decodifica → `ERR_UNKNOWN_PROFILE`; +5. `checkCompressedPoint('G1', firma) !== 'point'` → `ERR_RELEASE_INVALID`; 6. `shortSignatures.verify(firma, shortSignatures.hash(sha256(uint64be(ronda)), DST_QUICKNET), clave)` distinto de `true` → `ERR_RELEASE_INVALID`. Una excepción de noble en este punto también es `ERR_RELEASE_INVALID`. -La interfaz `ReleaseSource` del SDK solo tiene una implementación estática en esta fase: el release embebido en el sidecar del fixture o suministrado por la aplicación. +La interfaz `ReleaseSource` del SDK solo tiene una implementación estática en esta fase: el release embebido en el sidecar del fixture o suministrado por la aplicación. Su contrato se fija ya para las fuentes con red de fases posteriores: una fuente llama a `verifyRelease` con cada respuesta y, si ninguna verifica, falla con `ERR_RELEASE_UNAVAILABLE` en el paso 9; solo un release entregado directamente por quien llama da `ERR_RELEASE_INVALID` en el paso 10. Es el comportamiento de `provider/drand/client.go` en Go. --- @@ -101,7 +109,8 @@ La interfaz `ReleaseSource` del SDK solo tiene una implementación estática en - paso 9, release por `ReleaseSource`; paso 10, `verifyRelease`; - paso 11, `OUTER_TIME_AGE` con `Decrypter` de `age-encryption` y una `Identity` propia cuyo `unwrapFileKey(stanzas)` ejecuta, en este orden, `checkTimeStanzas` (cardinalidad de §63, como `agewrap.TimeIdentity.Unwrap` en Go), `verifyRelease` otra vez, la comprobación de 128 bytes del cuerpo, `decryptOnG2` y la longitud 16 de la file key. Cada fallo de cuerpo o de descifrado es `ERR_INTEGRITY`; el MAC de la cabecera lo comprueba `age-encryption`; -- pasos 12 a 18 como en Go: estructura de política, `INNER_ACCESS_AGE` con las identidades del llamante, `CONTROL_CBOR` canónico, `header_binding` sobre los bytes exactos, identidad de payload y `PAYLOAD_AGE`. +- pasos 12 a 18 como en Go: estructura de política, `INNER_ACCESS_AGE` con las identidades del llamante, `CONTROL_CBOR` canónico, `header_binding` sobre los bytes exactos, identidad de payload y `PAYLOAD_AGE`; +- `PAYLOAD_AGE` se descifra en streaming (decisión 7): `File.slice(offset).stream()` entra en `Decrypter.decrypt`, la salida se escribe en un fichero temporal de OPFS y el plaintext solo se entrega cuando el stream termina sin error; si falla la autenticación en cualquier chunk, el fichero temporal se borra y no se muestra nada (§56). Antes de empezar se compara el tamaño de `PAYLOAD_AGE` con la cuota libre. Cifrado: `Encrypter` con un `Recipient` propio cuyo `wrapFileKey(fileKey)` llama a `encryptOnG2RFC9380(clave del perfil, roundIdentity(ronda), fileKey)` y devuelve el stanza `tlock`. La construcción completa de un `.dkc` (prelude, cabecera, control sellado) requiere el writer TypeScript, que es fase 3; en esta fase el cifrado se prueba a nivel de stanza y de fichero `age` (sección 8). @@ -125,7 +134,7 @@ Todo en un único cambio normativo, con vectores congelados, según la política ## 8. Tests -1. **`ibe.test.ts`.** Vector de GT de tlock-js (`test/crypto/ibe.test.ts` en `17d817e`: bytes de e(G1, G2), de su cuadrado y `gtToHash` de 16 bytes `cb87319f24560b5231579a09ad79f12e`). Los cinco fixtures: la file key obtenida con la firma del sidecar abre la cabecera `age`. Control negativo: serializar GT con `Fp12.toBytes` de noble da otra clave. U con c0 + p, U identidad, U negado y firma de otra ronda: rechazados con el motivo esperado. Ida y vuelta `encryptOnG2RFC9380` → `decryptOnG2` con una clave sintética. Cobertura del 100 %. +1. **`ibe.test.ts`.** Los vectores de Go de la sección 4 (GT, H2, H3, H4, las file keys de los fixtures y el cifrado con `sigma` fijo), y como contraste el de tlock-js (`gtToHash` de 16 bytes `cb87319f24560b5231579a09ad79f12e`, igual al de kyber). Los cinco fixtures: la file key obtenida con la firma del sidecar abre la cabecera `age`. Control negativo: serializar GT con `Fp12.toBytes` de noble da otra clave. U con c0 + p, U identidad, U negado y firma de otra ronda: rechazados con el motivo esperado. Ida y vuelta `encryptOnG2RFC9380` → `decryptOnG2` con una clave sintética. Cobertura del 100 %. 2. **`release.test.ts`.** Ronda 1000 con la firma real: válida; 999: inválida; codificaciones alternativas de la misma firma (sin comprimir, x + p, flag de signo alterado): `ERR_RELEASE_INVALID`; longitud 47 y 96; identidad. Cobertura del 100 %. 3. **`open.test.ts`.** Los cinco fixtures se abren con el release del sidecar y el SHA-256 del plaintext coincide con el sidecar; los `.dkk` de `time_and_key` abren con sus identidades. El corpus de mutaciones exportado, incluidas las nuevas de la sección 7, reproduce código y paso. 4. **Interoperabilidad TS → Go a nivel IBE.** `scripts/ibe-go-roundtrip.go`, como `scripts/bls12381-go-verdicts.go`: TypeScript cifra 16 bytes para la ronda 1000 con la clave de Quicknet y escribe `U ‖ V ‖ W`; Go los abre con `tlock.BytesToCiphertext` y `tlock.TimeUnlock` con la firma real y compara. Se ejecuta a mano y su resultado se congela como vector. @@ -145,8 +154,8 @@ La ruta `/inspect` gana una acción "abrir": con un fixture o un `.dkc` arrastra | Paso | Contenido | Hecho cuando | |---|---|---| -| 0 | Confirmar las decisiones de la sección 2 y aprobar las dependencias de la sección 3 | respuesta del autor por escrito | -| 1 | Precondición: `main` verde con `testdata` sincronizado al último commit de `datekeys-go` (fusión o cierre de `wip/align-3820066`) | `npm run verify` en verde en `main` | +| 0 | Confirmar las decisiones de la sección 2 y aprobar las dependencias de la sección 3 | hecho el 26-09-2026 | +| 1 | Precondición: `main` verde con `testdata` sincronizado al último commit de `datekeys-go` | cumplida en `d5e3236` (`692cf87`); se repite al sincronizar la enmienda de la sección 7 | | 2 | Dependencias y guardas | instaladas con versiones exactas; `npm audit` sin avisos; guardas en verde; un solo noble en el lockfile | | 3 | `ibe.ts` de descifrado desde la semilla, `roundIdentity`, escritura del stanza en `age.ts`, `ibe.test.ts` sin la parte de cifrado | los cinco fixtures dan la file key correcta; U no canónico e identidad rechazados; cobertura 100 % | | 4 | `release.ts` y `release.test.ts` | ronda real válida, alias rechazados | @@ -164,7 +173,7 @@ Cada paso termina con `npm run verify` en verde y un commit en Gitea. El paso 6 - **API de `age-encryption` 0.3.1** para `Identity`, `Recipient` y `Stanza`. Mitigación: comprobarla en el paquete instalado antes del paso 5 y fijar la versión exacta. - **Cambios de API de noble 2.x.** Ya ocurrió entre 1.x y 2.x. Mitigación: versión exacta, guardas y el test del vector de GT, que detecta cualquier cambio de orden o de `Fp.toBytes`. - **Orden de bytes de GT.** Es el único punto donde una implementación puede coincidir en todo lo demás y fallar aquí. Mitigación: el vector de tlock-js y el control negativo. -- **Doble noble en el bundle** si alguna dependencia futura arrastra 1.x. Mitigación: la guarda del lockfile. +- **Doble noble en el bundle.** `@noble/post-quantum` 0.5.4 ya trae su propia copia 2.0.x; si alguna dependencia futura arrastra 1.x, sería peor. Mitigación: las guardas de la sección 3 (ningún 1.x, copias 2.x distintas solo anidadas bajo post-quantum, el código BLS en la 2.4.0 exacta) y la medida del bundle en el paso 2. - **Diagnósticos con material interno.** Mitigación: la regla de errores de la sección 4 y un test que busca en los mensajes de error los bytes de `sigma`, `msg` y `r`. - **`H3` devuelve 0**, con probabilidad 2⁻²⁵⁵: `multiply(0)` lanza `RangeError`. Mitigación: tratar cualquier excepción del cálculo como `proof` y cubrirlo con un test que inyecte `r = 0`. - **Rendimiento en el navegador.** Un pairing y una multiplicación escalar en G2 rondan los 40 a 300 ms en Node; la puerta añade 11 ms por punto. Mitigación: medir en el paso 8 y, si hace falta, descifrar en un worker. diff --git a/package-lock.json b/package-lock.json index 57f9a4b..8de47a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "datekeys-app", "version": "0.0.0", + "license": "Apache-2.0", "devDependencies": { "@noble/curves": "2.4.0", "@sveltejs/adapter-static": "3.0.10", diff --git a/package.json b/package.json index 069bde7..133fc8f 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "description": "DateKeys in TypeScript: canonical CBOR codec, DKC1/DKK1 parsers, capsule inspector library and its static inspector page; later, browser encryption and decryption.", + "license": "Apache-2.0", "type": "module", "engines": { "node": ">=20"