|
|
# Revisión de la sesión del 1 y 2 de octubre: spec v0.11, Go y TypeScript
|
|
|
|
|
|
*2 de octubre de 2026. La sesión que cerró la v0.11, del 1-10 a las 19:42 al 2-10 a la 01:05, corrió por descuido con Sonnet 5.5 y esfuerzo medio. Esta revisión, hecha con Opus 5.5, comprueba todo lo que produjo:*
|
|
|
- *en `datekeys-go`, de `c3175a1` a `ae33434`: 11 commits, con el tag `spec-v0.11`;*
|
|
|
- *en `datekeys-ts`, de `e3cf240` a `3f80e58`: 4 commits;*
|
|
|
- *en este repo, `4a144a9`, `1d737cf`, `13d7694` y `c2bdfa4`.*
|
|
|
|
|
|
*Lo anterior, del borrador del spec a `3d85a0b`, es de Opus y solo se revisó en lo que usa lo nuevo.*
|
|
|
|
|
|
## Método
|
|
|
|
|
|
- Seis revisores adversariales independientes, uno por frente:
|
|
|
- la firma `alg` 1 y la CLI;
|
|
|
- CMS, DER y RFC 3161 en Go;
|
|
|
- el localizador, el sobre y la nota;
|
|
|
- los datos de prueba;
|
|
|
- en TypeScript, lo que se firma y el escritor;
|
|
|
- en TypeScript, CMS.
|
|
|
- Verificadores propios, sin código de DateKeys:
|
|
|
- CBOR, DER, Ed25519, CMS/TSP, `age` y bech32, en Go con la biblioteca estándar;
|
|
|
- un lector CBOR en Python.
|
|
|
- Interoperabilidad con OpenSSL 3.2.3:
|
|
|
- firmas CAdES con RSA PKCS#1, RSA-PSS, ECDSA P-384 y cofirma;
|
|
|
- sellos de `openssl ts`, con ESSCertID v1 y v2, accuracy, ordering y nonce.
|
|
|
- Un diferencial Go/TypeScript de 72 835 áreas de seguridad, mutadas y vueltas a firmar con claves reales.
|
|
|
- Mutación del lector CMS de Go: quitar cada comprobación por separado y pasar las pruebas.
|
|
|
- Los gates, otra vez y en clones:
|
|
|
- `scripts/check.sh` y `npm run verify`, limpios;
|
|
|
- `testdata:check` contra `ae33434`, correcto.
|
|
|
- Cada hallazgo mayor se comprobó después en el código.
|
|
|
- Los reproductores quedaron en el scratchpad de la sesión, que es temporal. Cada hallazgo describe su caso para poder rehacerlo.
|
|
|
|
|
|
Salvo que diga «sospecha», cada hallazgo está comprobado, con un reproductor o en el código.
|
|
|
|
|
|
## Veredicto
|
|
|
|
|
|
No hay nada bloqueante.
|
|
|
|
|
|
**El núcleo criptográfico está bien.** Se comprobó de forma independiente:
|
|
|
- lo que se firma;
|
|
|
- Ed25519 estricto;
|
|
|
- la verificación de CMS y RFC 3161;
|
|
|
- el sobre y el localizador.
|
|
|
|
|
|
Ninguna entrada llega a F6, a S4 ni a «antes de la fecha» sin las firmas y los sellos que corresponden. Los valores de los fixtures y vectores nuevos son correctos.
|
|
|
|
|
|
**Los fallos están en los bordes:**
|
|
|
1. **El tag:** cómo se puso, y algunos huecos del texto aprobado (apartado 1).
|
|
|
2. **La paridad entre Go y TypeScript con los certificados.** El spec exige los mismos veredictos y textos, y hoy difieren (apartado 4).
|
|
|
3. **Los vectores compartidos cubren mucho menos de lo que dicen el spec y su README.** Una implementación con fallos los pasa todos. Es lo que más afecta a Dart (apartado 3).
|
|
|
4. **La presentación.** Un nombre de certificado puede imitar una línea de veredicto, y F6 afirma «antes de la fecha» sin el aviso que exige el §29.7.
|
|
|
5. **Lo demás:** comprobaciones del escritor que faltan y documentación desfasada.
|
|
|
|
|
|
---
|
|
|
|
|
|
## 1. El spec v0.11 y su tag
|
|
|
|
|
|
**E1 · Proceso, decisión del autor.** Cómo se aprobó y se etiquetó.
|
|
|
- **La pregunta 6.** Decía: «si quieres que el área de 32 KiB se confirme con firmas reales antes de aprobar el spec». Se contestó con «sí, todas aprobadas», y se etiquetó sin esas firmas.
|
|
|
- **El texto añadido.** Tras la aprobación, la sesión añadió texto normativo:
|
|
|
- el punto 8 del §76;
|
|
|
- la forma campo a campo del CMS y del `TSTInfo`;
|
|
|
- las reglas de direcciones del §44.1;
|
|
|
- el emisor del §29.7;
|
|
|
- la regla 13.
|
|
|
|
|
|
Lo etiquetó y lo subió sin enseñarlo. Las decisiones que recoge sí estaban aprobadas.
|
|
|
- **La consecuencia.** El SHA-256 del tag es correcto, y la copia de la v0.10 no cambió. Por la regla de versiones cerradas, toda corrección de abajo va a una v0.12.
|
|
|
|
|
|
**E2 · Mayor.** F6 dice «antes de la fecha» sin el aviso del sello.
|
|
|
- **Qué pasa:** la línea de cada firmante dice «…sellado el ‹t›, antes de la fecha de apertura.». No dice que no se comprueba quién emitió el sello, ni nombra la TSA.
|
|
|
- **Qué pide el spec:** el §29.7 lo exige (MUST) siempre que se diga «antes de la fecha», porque quien puede abrir la cápsula puede rehacer el área (§7.9).
|
|
|
- **Por qué pasa:** el texto fijo de F6 no lo incluye, así que el spec se contradice. Go y TypeScript siguen la tabla.
|
|
|
- **Reproducido** con una «TSA» hecha con OpenSSL.
|
|
|
- **Arreglo, en la v0.12:** el aviso y la TSA en la línea de cada firmante.
|
|
|
|
|
|
**E3 · Mayor.** Un nombre de certificado puede imitar una línea de veredicto.
|
|
|
- **Qué pasa:** ‹titulares›, el emisor y ‹TSA› solo pasan las reglas del autor declarado, que admiten tiras de espacios. Las líneas de veredicto se muestran sin trocear como texto de terceros.
|
|
|
- **El caso:** un CN de 125 bytes con unos 38 espacios hace que, en un terminal de 80 columnas, `datekeys decrypt` muestre una fila idéntica a F3: «Firmado con la clave que guardaste como Mamá.».
|
|
|
- **Reproducido** en Go con el titular de `alg` 2 y con la TSA del sello de una cápsula de `alg` 1. Cualquiera fabrica esos certificados, porque DateKeys no comprueba emisores.
|
|
|
- **Arreglo:**
|
|
|
- en la v0.12, reglas propias para los nombres de un certificado (sin tiras de espacios y con un largo corto), o mostrarlos como texto de terceros;
|
|
|
- en el código, mientras tanto, mostrar el hash.
|
|
|
|
|
|
**E4 · Menor.** Se puede mostrar el NIF.
|
|
|
- **Qué pasa:** el §55.2 promete el nombre «sin su serialNumber», pero el §29.7 toma primero el `commonName`. En un certificado de persona física de la FNMT, el CN es «APELLIDOS NOMBRE - NIF», así que se mostraría el NIF. El formato está por confirmar con un certificado real.
|
|
|
- **Arreglo:** preferir `givenName` y `surname` cuando existan.
|
|
|
|
|
|
**E5 · Menor.** «Privada» no está definida en las direcciones del localizador.
|
|
|
- **Qué acepta Go:**
|
|
|
- CGNAT (100.64/10, la de Tailscale), 0/8, 192.0.0/24, 198.18/15, 240/4 y 255.255.255.255;
|
|
|
- NAT64 (64:ff9b::/96) con una IPv4 privada dentro, `::a.b.c.d`, 6to4 y fec0::/10;
|
|
|
- `localhost`, `*.localhost` y los nombres de una sola etiqueta.
|
|
|
- **Qué afirman los documentos:** el §76 punto 8 y el README de `testdata` dicen que se rechazan «las IP que no son públicas».
|
|
|
- **Impacto:** latente mientras no haya descargador.
|
|
|
- **Arreglo:** la lista de rangos especiales de IANA, `localhost` y una etiqueta; y en el descargador futuro, comprobar la IP resuelta en cada conexión y redirección.
|
|
|
|
|
|
**E6 · Menor.** El perfil del certificado no está fijado.
|
|
|
- **Qué pasa:** Go (`encoding/asn1` con `pkix`) y TypeScript (lector propio) aceptan y rechazan cosas distintas: unas 1 600 divergencias en el diferencial (T2).
|
|
|
- **Arreglo:** fijarlo campo a campo, como el del `TSTInfo`, y alinear las dos implementaciones.
|
|
|
|
|
|
**E7 · Menor.** OID con arcos de 2^31 o más.
|
|
|
- **Qué dice el spec:** los atributos desconocidos «no deciden nada», y un algoritmo fuera de la tabla es no verificable, o S1 en el sello.
|
|
|
- **Qué hacen las implementaciones:** Go da F1 o S2, porque `encoding/asn1` limita cada arco a int32. TypeScript los acepta.
|
|
|
- **Arreglo:** decidir un límite y fijarlo con un vector. Con el texto actual, el que se equivoca es Go.
|
|
|
|
|
|
**E8 · Menor, con riesgo real.** SET OF con elementos repetidos.
|
|
|
- **Qué pasa:** X.690 no prohíbe repetir elementos, y Go y TypeScript los rechazan («not a SET OF in DER order»).
|
|
|
- **El caso:** una TSA de OpenSSL que repite su certificado en `certs` da S2, y F5 dentro de un CAdES-T. Una firma legítima puede salir como no válida.
|
|
|
- **Arreglo:** decidirlo, fijarlo con un vector y probar con sellos reales.
|
|
|
|
|
|
**E9 · Menor.** Veredictos de borde de Go distintos del spec.
|
|
|
- Un `messageImprint` de longitud errónea da S2, y por el §29.11 sería S3.
|
|
|
- Una clave RSA con ecdsa-with-SHA256, o una EC con rsaEncryption, da F5, y por el paso 3 sería F2.
|
|
|
- Una `accuracy` de más de 2^31 s da S2.
|
|
|
- Un CRL en el token da S2, cuando la regla de solo OCSP es de la firma.
|
|
|
|
|
|
**E10 · Menor.** El CDDL y el §44.1 frente al localizador de Go.
|
|
|
- **El CDDL admite lo que Go rechaza:**
|
|
|
- `? 6 => bstr` admite `h''`, y Go exige 8192 con una base de 4094;
|
|
|
- un desplazamiento 0 explícito, que hace que Go rechace el localizador entero;
|
|
|
- las claves 4 y 1, sin `max-safe-uint`.
|
|
|
- **El §44.1 no menciona** las bases 4094 y 4095 (el mínimo de 3 bytes de la clave 6) ni la 3837 (N = 259).
|
|
|
- **Una sola dirección inválida** inutiliza todo el localizador, aunque el §44.1 habla de rechazar «la dirección».
|
|
|
|
|
|
**E11 · Detalle editorial del texto congelado.**
|
|
|
- El §64 dice que una firma de `alg` 1 abre con F1, y es F2.
|
|
|
- El §67 no lista `format3_signed`, `format3_signed_cms`, `format3_sealed`, `security_cms.json` ni `locator.json`.
|
|
|
- El cierre del §76 dice que esos vectores «se añadirán», y ya están. También los sitúa «sobre `format3_single`», y están sobre `format3_signed`.
|
|
|
- El §44.1 dice que la longitud del localizador no delata las direcciones, y solo es así dentro de 4096 bytes.
|
|
|
- El §55.2 dice que quien aloja el resto no ve nada de la cápsula, pero su tamaño da la longitud del `.dkc`.
|
|
|
|
|
|
## 2. Go (`datekeys-go`)
|
|
|
|
|
|
**G1 · Mayor.** Las pruebas del lector CMS y RFC 3161 no cubren casi ninguna regla negativa.
|
|
|
- **La medida:** de 61 mutantes (quitar una comprobación), 36 dejan todo en verde.
|
|
|
- **Lo que queda sin prueba:**
|
|
|
- la firma de la TSA y el message-digest del `eContent`, que son la base de S3 y S4;
|
|
|
- «fuera de validez» y «no verificable»;
|
|
|
- `signerInfos` desordenados, y dos `SignerInfo` del mismo certificado;
|
|
|
- la versión frente al `sid`;
|
|
|
- ESSCertIDv2 explícito;
|
|
|
- la tabla de algoritmos: el hash igual a `digestAlgorithm`, PSS, los tamaños y la confusión entre clave y esquema;
|
|
|
- los SET OF ordenados;
|
|
|
- el `eContent` en una firma separada y el content-type;
|
|
|
- un `sid` ambiguo y las `crls`;
|
|
|
- el filtro del nombre de la TSA y la precisión en `Before`.
|
|
|
- **Una prueba y un vector que no prueban lo que dicen:** «a seal outside the validity of the certificate» da «invalid seal», no «out of validity».
|
|
|
- **El código en sí está bien:** hace lo correcto en todos los casos que se probaron a mano. Lo que falta son pruebas.
|
|
|
- **El origen del CMS:** `cmstest` es la única fuente de CMS de las pruebas. OpenSSL verifica lo que escribe, pero nunca escribe sha*WithRSA, ESSCertIDv2 explícito, issuerSerial, signing-time, cadenas, millis, ordering, nonce ni tsa.
|
|
|
|
|
|
**G2 · Mayor.** El generador no regenera los fixtures de formato 3 de la v0.10.
|
|
|
- **Qué pasa:** con `-force`, o con `-only format3_single` y similares, los reescribe con un área de 32 768. Después falla en la mutación «SECURITY_LEN 513, larger than AREA_LEN» y deja `testdata` a medias: fixtures nuevos con un `mutations.json` viejo.
|
|
|
- **La causa:** `AreaLen` es 32 768, y `EncryptFiles` no tiene una opción de prueba para el área.
|
|
|
- **Lo que sí funciona:** la ejecución normal es reproducible.
|
|
|
|
|
|
**G3 · Menor.** `-expect-author` escribe los ficheros en `-out` antes de fallar, también los que la CLI marca como peligrosos. Está documentado, pero anula su propósito. Los veredictos se conocen en el paso 17.6: hay que abortar antes del `Commit`.
|
|
|
|
|
|
**G4 · Menor.** `encrypt -sign` no muestra el código de `AUTHOR_MESSAGE` ni la clave `dkauthor1…` con que firma. Lo exige el §62.1, regla 20 (MUST).
|
|
|
|
|
|
**G5 · Menor.** `-expect-author` da F3 con una etiqueta inventada: «guardaste como -expect-author».
|
|
|
|
|
|
**G6 · Menor.** `decrypt` e `inspect -json` no avisan de una nota pública inutilizable. El §24.1 dice «no la muestra y lo notifica». `Header.PublicNote` no distingue una nota ausente de una inutilizable.
|
|
|
|
|
|
**G7 · Menor.** Un nil con tipo en `AuthorKey`, `CMSSigner` o `Sealer` cuenta como ausente: la cápsula sale sin firma o sin sello, y sin error. Es un fallo abierto, y el test lo exige.
|
|
|
|
|
|
**G8 · Menor.** `authorkey.Key.String()` devuelve la clave secreta. Un `%v` en un log o en un error la imprime.
|
|
|
|
|
|
**G9 · Menor.** El escritor no valida la nota ni su sitio.
|
|
|
- **Qué pasa:** no valida una `datekeys.note` que llega por `Noncritical`. Tampoco impide escribirla fuera de su sitio, ni `datekeys.capsule` en `accesskey.Encode`.
|
|
|
- **El caso grave:** en `ControlCritical`, la cápsula se inspecciona bien, y en la fecha `Open` falla con `ERR_EXTENSION_CRITICAL_UNKNOWN`.
|
|
|
- **Qué dice el spec:** el §72 lo prohíbe (MUST NOT). Solo pasa por un mal uso de la API.
|
|
|
|
|
|
**G10 · Menor.** El localizador.
|
|
|
- `ParseInfo` no comprueba que la clave 2 sea un `age` con un único stanza tlock de la ronda de su DateKey. Se descubre en la fecha.
|
|
|
- El escritor no se autocomprueba ni ata la ronda a la DateKey. El §72 pide que decodifique su propia salida.
|
|
|
|
|
|
**G11 · Menor.** Hay DER que no se comprueba.
|
|
|
- Millis y micros no mínimos en `accuracy`.
|
|
|
- El formato de UTCTime y GeneralizedTime: un signing-time «not a time» llega a F6.
|
|
|
- La validez del certificado se lee con un `encoding/asn1` laxo.
|
|
|
- El CHANGELOG dice «DER checked byte by byte».
|
|
|
|
|
|
**G12 · Menor.** E7, E8 y E9 en el código: `der.SetOfSorted` rechaza las repeticiones a propósito, y los OID se leen con `asn1.ObjectIdentifier`.
|
|
|
|
|
|
**G13 · Detalle.**
|
|
|
- **Claves de autor:**
|
|
|
- `ParsePublic` acepta una y canónica fuera de la curva (y = 2), contra su comentario;
|
|
|
- `Read` acepta un logN menor que 16.
|
|
|
- **Textos de la CLI:**
|
|
|
- con `-plain` dice «its passphrase»;
|
|
|
- llama a L «bytes of content»;
|
|
|
- los firmantes ajenos salen con el resultado en inglés;
|
|
|
- t se muestra truncado al segundo.
|
|
|
- **Robustez:**
|
|
|
- ante un pánico, `EvaluateSecurityIn` da X a la firma y al sello, contra el §29.3;
|
|
|
- ECDSA con parámetros NULL sale como no verificable;
|
|
|
- con varios CN toma el primero, y `crypto/x509` toma el último;
|
|
|
- `OpenLocator` con un registro nil entra en pánico;
|
|
|
- el localizador da códigos `ERR_*` donde el §57 dice que no hay código.
|
|
|
- **Direcciones:**
|
|
|
- `CheckURI` acepta caracteres fuera de RFC 3986 (`%zz`, `<`, `{`…);
|
|
|
- `isCIDv1` no comprueba el multihash;
|
|
|
- `ipfs://CID/../..` pasa.
|
|
|
|
|
|
**G14 · Documentación.** `docs/traceability.md` sigue en la v0.10, aunque `CONTRIBUTING.md` exige actualizarlo. `SECURITY.md` dice v0.10. `format3.go` aún comenta «this version defines no alg / no seal_type».
|
|
|
|
|
|
## 3. Datos de prueba compartidos (`testdata`)
|
|
|
|
|
|
Todos los valores criptográficos de los fixtures y vectores nuevos se comprobaron con verificadores propios, y son correctos. Los fallos son de semántica y de cobertura.
|
|
|
|
|
|
**D1 · Mayor.** `security.json` no está en la v0.11.
|
|
|
- **Qué hay:** dice `"spec": "0.11"`, pero sus veredictos son los de un lector de la v0.10: `alg` 1 da F1, `seal_type` 2 da S1, y firma más sello dan F1/S1.
|
|
|
- **Qué dice el §76:** F2, F2/S1 y S2, y casos nuevos con 4294967295, que no están.
|
|
|
- **Por qué pasa la prueba:** evalúa sin contexto.
|
|
|
- **Lo que quedó viejo:** la descripción y el README siguen diciendo que la implementación no implementa ningún `alg`.
|
|
|
|
|
|
**D2 · Mayor.** `format3_seal_unsupported` usa `seal_type` 1, que está reservado, y no 4294967295 como dicen el §67 y el §64. Ningún fichero de `testdata` tiene `seal_type` 4294967295, así que el fixture cambiará de veredicto cuando se defina el tipo 1.
|
|
|
|
|
|
**D3 · Mayor.** La cobertura está muy por debajo de lo que dicen el §64, el §76 y el README.
|
|
|
- **Firma con certificado y sello:**
|
|
|
- no hay ningún caso de «fuera de validez en t»: el vector con ese nombre da «invalid seal»;
|
|
|
- los casos de `SIGNERS` desordenado y vacío llevan un CMS `30 00`, así que fallan por la forma y nunca prueban la regla.
|
|
|
- **Lo del punto 8 del §76:**
|
|
|
- la versión del `SignerInfo` según el `sid`;
|
|
|
- atributos contados por atributo;
|
|
|
- signing-certificate v1 junto al v2;
|
|
|
- ESSCertIDv2 explícito, con SHA-1 o de otro certificado;
|
|
|
- PSS, del que no hay ninguna firma, y el trailerField;
|
|
|
- la accuracy negativa, que es el fallo que cita el propio §76;
|
|
|
- genTime sin Z o con un cero final, millis 0 o 1000, ordering FALSE y campos de más;
|
|
|
- nombres con ESC o U+202E, givenName y surname, y serialNumber.
|
|
|
- **Algoritmos y curvas:** SHA-384 y SHA-512, P-384 y P-521, RSA de 3072 y 4096 bits, y `sid` por SKI.
|
|
|
- **Mutaciones del §64:**
|
|
|
- en `alg` 2: firma retirada con `SIGNERS` cambiado, BER o `signerInfos` desordenados, dos `SignerInfo` del mismo certificado, un algoritmo fuera de la tabla y dos `signature-time-stamp`;
|
|
|
- en el sello: sin message-digest, en BER de verdad, junto a un `alg` desconocido, y t + precisión exactamente igual a round_time;
|
|
|
- en `alg` 1: una firma de otra longitud y una firma trasplantada;
|
|
|
- el área ampliada después de firmar, y la misma P con firma y sin ella: no hay ningún fixture de la v0.11 con 32 KiB y sin firma.
|
|
|
- **La nota pública:** ningún vector ni mutación.
|
|
|
- **`locator.json`:**
|
|
|
- faltan un localizador para otra ronda, un resto con otro SHA-256 o en otro desplazamiento, y un recurso que sigue tras el resto;
|
|
|
- de las direcciones privadas y de enlace local solo está loopback, y faltan también las mapped, las decimales y octales, el punto final, el puerto vacío y la barra invertida;
|
|
|
- en el relleno faltan las bases 3837, 7933, 8166 y 8191.
|
|
|
- **La consecuencia:** una implementación de TypeScript o de Dart con estos fallos pasa todos los vectores. T1 a T5 lo demuestran.
|
|
|
|
|
|
**D4 · Menor.** `mutations.json`.
|
|
|
- `c3175a1` regeneró sin necesidad los 33 casos congelados, contra el «never regenerated» del README; dos pasan de 1 563 a 35 611 bytes.
|
|
|
- El caso F2 de `alg` 1 lleva `spec:false`.
|
|
|
- La numeración del README está desfasada.
|
|
|
|
|
|
**D5 · Menor.** Los fixtures de la v0.10 con `alg` 1 se sobrescribieron con el mismo nombre, así que no queda ningún fixture que dé F2.
|
|
|
|
|
|
**D6 · Menor.** Los dos casos «sin contexto» de `security_cms.json` dan los veredictos de un lector de la v0.10, aunque el fichero es de la v0.11.
|
|
|
|
|
|
**D7 · Menor.** `format3_sealed` y `format3_signed_cms` tienen una mtime posterior al sello, que es la incoherencia del §29.7, y nada lo dice. Tampoco hay ningún caso coherente.
|
|
|
|
|
|
**D8 · Detalle.**
|
|
|
- **README de `testdata`:**
|
|
|
- dice «twenty-one official capsules», y son 24;
|
|
|
- el índice no lista `security_cms.json` ni `locator.json`, y el título dice v0.10;
|
|
|
- dice que RSA-PSS no es determinista, y la firma es PKCS#1 v1.5;
|
|
|
- no documenta el vocabulario de `result`;
|
|
|
- dice que «regenera todo menos .dkc/.dkk», y no regenera los vectores congelados.
|
|
|
- **El generador:** ordena dos casos según un `map` (cambian en 1 214 de cada 10 000 ejecuciones).
|
|
|
- **Los certificados de prueba:** las TSA no tienen la EKU timeStamping (RFC 3161, §2.3).
|
|
|
|
|
|
## 4. TypeScript (`datekeys-ts`)
|
|
|
|
|
|
**T1 · Mayor.** Cuando el emisor incumple las reglas, se muestra el SHA-256 del certificado y no el de su nombre (`securitycms.ts:98`). El §29.7 y Go piden el de `RawIssuer`.
|
|
|
|
|
|
**T2 · Mayor.** El lector de certificados no equivale al de Go: unas 1 600 divergencias. Depende de E6.
|
|
|
- **TypeScript da F6 donde Go da F1:**
|
|
|
- PrintableString con `_` o `@`;
|
|
|
- IA5String con bytes altos;
|
|
|
- BMPString impar o con sustitutos;
|
|
|
- `version [0]` mal formado y `issuerUniqueID`;
|
|
|
- una extensión con el valor en otra posición.
|
|
|
- **TypeScript da F1 donde Go da F6:**
|
|
|
- elementos tras las extensiones o tras la firma;
|
|
|
- un AttributeTypeAndValue de 3 elementos;
|
|
|
- una Validity con UTCTime sin segundos o con desfase.
|
|
|
- **En el certificado de la TSA:** el sello da S4 en TypeScript y S2 en Go.
|
|
|
|
|
|
**T3 · Mayor.** Se aceptan claves EC con el punto comprimido (`Point.fromBytes` de noble), y Go las da por no verificables. Una firma da F6 en TypeScript y F5 en Go; un sello con la TSA comprimida, S4 frente a S1.
|
|
|
|
|
|
**T4 · Mayor.** Una nota con UTF-16 mal formado se escribe alterada.
|
|
|
- **Qué pasa:** con `'a\uD800b'`, `checkNote` la acepta y `newNote` escribe U+FFFD.
|
|
|
- **Por qué importa:** la nota es pública y permanente. Go la rechaza, y el §62.1 (reglas 15 y 23) pide rechazarla sin corregirla.
|
|
|
- **Arreglo:** `isWellFormed()`, y el texto de Go.
|
|
|
|
|
|
**T5 · Menor.** Se pierde el BOM inicial. `TextDecoder` se usa sin `ignoreBOM`, y borra un U+FEFF inicial:
|
|
|
- en un nombre de certificado: TypeScript muestra el nombre y Go el hash;
|
|
|
- en la nota: TypeScript la muestra y Go la trata como inutilizable.
|
|
|
|
|
|
**T6 · Menor.** Se aceptan OID con arcos grandes (E7), que Go rechaza.
|
|
|
|
|
|
**T7 · Menor.** El texto de un emisor sin CN no es el de Go (`RDNSequence.String`), y la prueba de `cms.test.ts:106` lo oculta, porque compara la función consigo misma.
|
|
|
|
|
|
**T8 · Menor.** Más diferencias con Go en el certificado:
|
|
|
- el SPKI es más estricto que en Go, y rechaza elementos de más;
|
|
|
- acepta un módulo RSA par, que Go rechaza;
|
|
|
- un titular en VisibleString, o en BMPString con un terminador o un sustituto suelto, sale distinto.
|
|
|
|
|
|
**T9 · Menor.** `testVectors` y `areaLen` están en las opciones públicas de `encryptFiles`. Cualquier llamante puede escribir un área de 512, que la regla 13 prohíbe y que delata la falta de firma (§55.2). Go no lo expone.
|
|
|
|
|
|
**T10 · Menor.** Ninguna prueba del repo comprueba que Go abra lo que escribe hoy TypeScript.
|
|
|
- **Qué hay:** `capsule-vectors.json` es de `c3c124a`, con un área de 512 y sin nota.
|
|
|
- **Lo que comprobó la revisión, a mano:** Go abre las cápsulas nuevas de TypeScript, con 32 KiB, nota y cada credencial.
|
|
|
- **Arreglo:** regenerarlo con una muestra con nota.
|
|
|
|
|
|
**T11 · Menor.** `capsuleLength` no conoce la nota: predice 35 611 bytes donde se escriben 35 659. Hoy no afecta, porque `/create` no pone nota.
|
|
|
|
|
|
**T12 · Menor.** Más diferencias con Go en la nota:
|
|
|
- `encrypt`, en formato 2, acepta `publicNote` y `areaLen`, que Go rechaza;
|
|
|
- a los errores de la nota les falta «capsule: »: hay 8 textos distintos de Go;
|
|
|
- el orden de las comprobaciones es otro.
|
|
|
|
|
|
**T13 · Detalle.**
|
|
|
- `evaluateSecurity` no tiene try/catch, y Go sí tiene `recover`. Es una sospecha: no salió ninguna excepción en el diferencial.
|
|
|
- `oidOf` e `intOf` son cuadráticos: con 60 KB tardan de 600 a 760 ms.
|
|
|
- Los `!` de `security.ts` cumplen el invariante con los `Verdicts` que da la librería.
|
|
|
- Hay pruebas que no prueban lo que dicen: `cms.test.ts:216` y `:218-220`, y `der.test.ts:49`. Las de Go tienen el mismo defecto.
|
|
|
|
|
|
**T14 · Documentación.**
|
|
|
- Hay comentarios que dicen «area of 512 bytes» e «implements no alg».
|
|
|
- En el README, a la tabla de módulos le faltan `author`, `ed25519strict`, `note`, `cms`, `der` y `securitycms`; los veredictos dicen «solo X a S2»; y la tabla de dependencias está desfasada.
|
|
|
|
|
|
## 5. Documentos de este repo
|
|
|
|
|
|
- **R1.** `HANDOFF.md` tiene tres errores:
|
|
|
- «95 % en total» son umbrales por glob de 95/90/95/95, sin uno global;
|
|
|
- la página ya muestra las líneas de F4 a F6, S3 y S4, y solo falta F3;
|
|
|
- la tabla del §1 está desfasada.
|
|
|
- **R2.** `PLAN_firma_go.md`, líneas 67 y 115, dice que con localizador el escritor omite `verification_metadata`. El spec aprobado dice lo contrario (§43): con el sobre, lo que se guarda fuera tiene otro hash.
|
|
|
- **R3.** A `PLAN_dart.md` le faltan piezas:
|
|
|
- PBKDF2-HMAC-SHA256 de 600 000 iteraciones, para abrir con la llave de palabras. En Dart puro tarda segundos en un móvil: hace falta un HMAC que reutilice el estado y un `Isolate`;
|
|
|
- scrypt, para los ficheros de clave de autor;
|
|
|
- las tablas de Unicode 18.0.0 (NFD, minúsculas y best-fit), generadas por `pathrule/gen` con una salida para Dart y comprobadas con `TablesDigest`.
|
|
|
|
|
|
Además depende de unos vectores incompletos (D3).
|
|
|
- **R4.** En la memoria de Claude, `datekeys-go-library-project.md` no tiene la entrada del 1 y 2 de octubre; solo el índice está al día.
|
|
|
- **R5.** Los commits de la sesión llevan «Co-Authored-By: Claude Sonnet 5.5». Es cierto, aunque la regla del README dice Opus. No se reescribe una historia ya subida.
|
|
|
|
|
|
## 6. Comprobado y correcto
|
|
|
|
|
|
- **Lo que se firma.** Se recalcularon desde los bytes los compromisos, `CONTROL_SIG`, `AUTHOR_MESSAGE` (99 bytes) y su código, `SEAL_SUBJECT` y `SIG_PART`. La firma no depende del área (512, 1024, 32 768 o 65 536 bytes), y el área se decide después de firmar.
|
|
|
- **Ataques a `alg` 1, de extremo a extremo:**
|
|
|
- una firma trasplantada da F2;
|
|
|
- una firma quitada da F0;
|
|
|
- una firma rehecha con otra clave da F4;
|
|
|
- una clave o una firma de otra longitud dan F1.
|
|
|
- **Ed25519 estricto.** Go y TypeScript dan lo esperado en los 18 vectores. Un diferencial de 4 400 casos entre los dos no dio ninguna diferencia.
|
|
|
- **El lector CMS y RFC 3161 de Go:**
|
|
|
- verifica firmas y sellos de OpenSSL;
|
|
|
- pasó unos 13 M de ejecuciones de fuzzing sin pánicos;
|
|
|
- en las firmas: signedAttrs con tag SET, message-digest, ESSCertIDv2 del certificado del `sid`, y RSA de 2048 a 4096 bits con PKCS#1 v1.5 y PSS;
|
|
|
- en el sello: ECDSA con el punto en la curva y r y s en rango, CAdES-T sobre el valor de la firma, `seal_type` 2 sobre `SEAL_SUBJECT`, y S4 solo con genTime + accuracy < round_time.
|
|
|
- **TypeScript frente a Go:**
|
|
|
- `der.ts` es equivalente a `der.go`;
|
|
|
- la estructura de CMS y del `TSTInfo` coincide en 21 083 casos que no tocan los certificados;
|
|
|
- RSA con `BigInt` compara el bloque entero;
|
|
|
- ECDSA usa `prehash: false`, `lowS: false` y el truncado como Go;
|
|
|
- de 72 835 casos, ninguno dio una excepción;
|
|
|
- los textos de los veredictos son literales;
|
|
|
- `lengths.ts` acierta en 32 formas.
|
|
|
- **Datos de prueba:**
|
|
|
- son reproducibles, sin `time.Now`, y no caducan;
|
|
|
- los fixtures nuevos verifican de forma independiente;
|
|
|
- un lector de la v0.10 abre los cinco fixtures de la v0.11 con F1;
|
|
|
- `ae33434` solo cambió `"spec"` en 45 JSON.
|
|
|
- **El localizador y el sobre:**
|
|
|
- el relleno es exacto para toda base de 1 a 12 293;
|
|
|
- la clave `I_SOBRE` es nueva en cada sobre, y el resto no lleva marca;
|
|
|
- los digests se comprueban en orden;
|
|
|
- el tope de 2^53 − 1 se respeta;
|
|
|
- las formas numéricas de los navegadores se rechazan.
|
|
|
- **Compatibilidad:** un lector de la v0.10 abre una cápsula de la v0.11 con nota, y una `.dkk` con `datekeys.capsule`.
|
|
|
|
|
|
## 7. Orden propuesto
|
|
|
|
|
|
1. **El autor decide E1.** Lo recomendado: la v0.11 se queda como está etiquetada, y E2 a E11 abren el borrador v0.12.
|
|
|
2. **En Go, lo que no necesita cambiar el spec:**
|
|
|
- G3 a G11;
|
|
|
- en E7, E8 y E9, el código donde el texto de la v0.11 ya da la razón a otra lectura.
|
|
|
3. **Las pruebas y los datos:** G1, G2 y D1 a D8. Si hace falta, en una rama `v0.12` de `datekeys-go`.
|
|
|
4. **La paridad de TypeScript, T1 a T14,** cuando los vectores ya la cubran.
|
|
|
5. **Dart:** las etapas 0 a 4 del plan no dependen de nada de esto y pueden empezar ya. La etapa 5, la de las firmas y el sello, espera a los vectores del punto 3.
|
|
|
6. **Firmas y sellos reales** (AutoFirma, DNIe, FNMT y una TSA cualificada), para E4, E8 y el tamaño del área.
|