4.6 KiB
Revisión de seguridad de Fable (30-09-2026)
Revisión externa del paquete formato3_para_revision.md, copiada tal como la pasó el autor. Ese paquete ya no está en el repo: sigue en git, en el commit 56851a9, y en el artefacto privado publicado. El diseño vigente, con estos hallazgos resueltos, es formato3_diseno.md.
He leído formato3_para_revision.md entero y contrastado algunas afirmaciones con el código. Mi opinión:
Veredicto
El formato está bien diseñado; el servicio de sellado aún no es revisable. La parte criptográfica (qué se firma, qué se sella, los compromisos) no le encuentro ataque. Los problemas están en los bordes: el origen del servicio, los caracteres invisibles y la vida del sello a décadas.
Comprobado por mí:
- Toda la aritmética de tamaños cuadra (22/128/175/281, 53, 45, 146, L = 573 → P = 768, L = 1637 → P = 1792).
- noble 2.4.0 usa la ecuación con cofactor siempre (
edwards.js:659), así que el verificador propio es necesario, no un capricho. - Go trae Unicode 15.0.0 y Node 24.9 el 16.0, como dice el documento.
Hallazgos, por gravedad
| # | Gravedad | Problema | Arreglo |
|---|---|---|---|
| 1 | Mayor | Mismo origen (decisión 5). Hoy el sitio es estático y el CSP va en <meta>. Un documento servido bajo /seal/* no lleva CSP y corre en el origen que maneja plaintexts, .dkk y claves de autor |
Origen aparte (seal.…), o cabeceras forzadas en el proxy: nosniff, octet-stream, CSP: sandbox |
| 2 | Mayor | Caracteres invisibles en rutas. R4 deja pasar U+206A–206F, y ZWJ/ZWNJ están permitidos: HFS+ los ignora al comparar, así que «ab» y «ab» colisionan. Los tags U+E0020–E007F permiten nombres visualmente idénticos | R4 por propiedad (Default_Ignorable_Code_Point) con lista blanca; la clave de R7 descarta lo permitido |
| 3 | Mayor | El sello no sobrevive a DateKeys. La auditoría depende de que el registro siga publicado dentro de décadas, y el perfil anual obliga a actualizar cada lector cada año | Fichero aparte con prueba de inclusión, checkpoint y .ots; raíz offline ahora, no «más adelante» |
| 4 | Mayor | Abuso frente a «sin registros». El registro solo crece y el POST es anónimo; limitar por IP exige guardar IPs | Definirlo: estado solo en memoria, o prueba de trabajo con SHA-256 |
| 5 | Menor | El área depende de SECURITY_LEN. Un sello futuro de más de 512 bytes cambia L y delata su presencia por P |
Área fija mayor, o longitud propia en la trama |
| 6 | Menor | Orden de rutas en TypeScript. Comparar strings ordena por UTF-16, no por bytes: U+FF5E y un emoji salen al revés que en Go | Vector en paths.json |
| 7 | Menor | Suplantación en la CLI. Una línea de comentario rellena de espacios salta de renglón y el falso veredicto aparece sin el prefijo │ |
Repetir los veredictos al final, o partir líneas en la CLI |
| 8 | Menor | A1 y el tráfico. La página no hace ninguna petición hoy; un POST delata el sello | Declararlo como fuga aceptada |
| 9 | Menor | Ambigüedades: si una firma ilegible invalida también el sello; R6b con «~1» a secas y qué cuenta como carácter; mtime negativa | Tabla de verdad, expresión regular, regla de omisión |
| 10 | Menor | control_commit y head_digest sin prefijo de dominio; el directorio .datekeys-* puede coincidir con una entrada |
Prefijo; reservar ese nombre |
Sobre el documento
- Las 28 objeciones son 24: la 12, 16, 18 y 24 repiten otras. Y varias no están «corregidas» sino aceptadas (2, 9, 23). Un revisor agradece la distinción.
- Faltan definiciones para alguien de fuera: «capa 2/3/4», las referencias a § del spec, el valor de L_MAX (2⁵³ − 2⁴⁶) y para qué sirve la sal del head.
- Hay tres ficheros con el mismo texto.
para_revisiones la suma exacta de los otros dos; cualquier corrección hay que hacerla dos veces. - El sello sin perfil conocido no muestra nada. Mejor un texto que diga que hay un sello y que el lector es antiguo.
Recomendación
Separar en tres entregas, con el formato congelado desde la primera y security vacío:
- Contenedor y rutas.
- Firma y claves de autor.
- Sello, cuando exista su documento propio («Servicio de sellado v1»), con custodia de clave, monitores y hosting.
Los hallazgos 1, 3 y 4 son del servicio, no del formato, y no deberían frenar lo demás. El 2 y el 6 sí conviene cerrarlos antes de escribir el texto del spec.
No he revisado aún a fondo el ZIP byte a byte ni he probado el comportamiento de HFS+ en una máquina; lo de HFS+ lo afirmo por la lista que usa git en core.protectHFS.