Files
arkhe-origin-chain/docs/vectores/invitaciones-v1/LEEME.md
T
CharonandClaude Opus 5.5 abe680e853 docs(vectores): la firma OIT de las invitaciones V1, con los casos que deben fallar
Para Irhis y el servicio de invitaciones de originmetaverse.com (CONTRATO_INVITACIONES_V1 §3.1,
origin-metaverse-web d5f5f5e). Ella escribe sus tests antes que el servicio.

- Firma sobre etiqueta || 0x00 || JCS(cuerpo).
- 4 vectores que deben aceptarse y 11 que deben rechazarse: nonce repetido, ts fuera de
  ventana, etiqueta cruzada, llave de cabecera distinta, S no canonico, cuerpo cambiado, tres
  llaves de orden pequeno con firma forjada sin llave privada, y clave JSON duplicada.
- Ed25519 en Python puro (referencia del RFC 8032), comprobado con los TEST 1 y 2 del RFC y
  con OpenSSL byte a byte. Salida determinista.

Medido: OpenSSL 3.6.4 ACEPTA las tres firmas forjadas (F8-F10). La verificacion sola no basta;
hace falta la comprobacion de llave fuerte, como is_strong_ed25519_key en la cadena (RV14).

Contrafactual del generador: sin la comprobacion de orden pequeno, la de S < L o la etiqueta, se
niega a emitir. Las comprobaciones de codificacion canonica se solapan con la de orden pequeno en
F10 y ningun vector puede separarlas (ver LEEME).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 04:27:14 -05:00

3.1 KiB

Vectores de la firma OIT de las invitaciones V1

Charon ⚓, 2026-10-28. Para Irhis 🛰️ y el servicio de invitaciones de originmetaverse.com (origin-metaverse-web, docs/CONTRATO_INVITACIONES_V1.md §3.1).

python3 -I generar.py > invitaciones-v1.json   # determinista: el sha no cambia entre corridas

Necesita sólo Python 3 y openssl en el PATH. Ed25519 va en Python puro, sobre el código de referencia del RFC 8032 §6, para no depender de la misma librería que se quiere probar. Antes de emitir nada el generador se comprueba de dos maneras: reproduce las llaves y las firmas de los TEST 1 y 2 del RFC 8032, y OpenSSL da las mismas firmas byte a byte.

Qué hay

Todos los vectores firman utf8(etiqueta) || 0x00 || JCS(cuerpo).

  • Aceptar:
    • V1 crear, V2 canjear y V3 revocar;
    • V4: el mismo V1 con otro orden de claves y espacios en el transporte (el servidor re-canonicaliza).
  • Rechazar:
    • F1 nonce repetido;
    • F2 y F3 ts a ±301 s;
    • F4 la etiqueta de crear sobre un cuerpo de canjear;
    • F5 Origin-Llave distinta del pub firmado;
    • F6 S no canónico (S + L);
    • F7 cuerpo cambiado después de firmar;
    • F8-F10 llaves de orden pequeño con firma forjada sin llave privada;
    • F11 clave duplicada en el JSON.

Las llaves son las semillas públicas de los TEST 1 y 2 del RFC 8032: no valen para nada real.

Lo que medí

OpenSSL 3.6.4 ACEPTA F8, F9 y F10 (openssl_acepta_la_firma en cada vector). Son las tres llaves de RV14:

  • la identidad (01 00..00);
  • el punto de orden 2 (ec ff..ff 7f);
  • una codificación NO canónica de la identidad (ee ff..ff 7f).

Con R de orden pequeño y S = 0 la ecuación de verificación se cumple. Por eso una verificación «estricta» puede no bastar. Sólo frena estas firmas la comprobación explícita de llave fuerte, la misma que hace la cadena (is_strong_ed25519_key en crates/origin-chain-agent-identity/src/lib.rs):

  1. decodificar pub;
  2. exigir que vuelva a codificarse igual;
  3. exigir que el punto no sea de orden pequeño.

No he medido @noble/curves (no tengo permiso para descargarlo). El vector te lo dice en tu máquina.

Contrafactual del propio generador. Si se quita la comprobación de orden pequeño, la de S < L o la etiqueta, el generador se niega a emitir: reprueba F8-F9, F6 y F4.

La decodificación canónica y la recodificación se cubren entre sí, y las dos se solapan con la de orden pequeño en F10. Ningún vector puede separarlas. Las únicas llaves con dos codificaciones son los puntos con y < 19 (y + p < 2^255), y de ninguno se conoce la llave privada. Quitar una sola de esas tres no tumba nada; quitar las tres, sí.

Supuestos por confirmar (Irhis)

  1. expiraEn es un instante absoluto en segundos. Si es una duración, se regeneran.

  2. Canjear firma {aud, nonce, pub, token, ts}:

    • pub es la OIT de quien canjea;
    • el token (la preimagen) va dentro de lo firmado.

    El §3 del contrato manda {token, canjeadorOit, firma} y el §3.1 habla de «la huella del token». Con la huella sola el servidor no puede comprobar la preimagen.

  3. Revocar firma {aud, huella, nonce, pub, ts}, con pub igual a invitadorOit.