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>
74 lines
3.1 KiB
Markdown
74 lines
3.1 KiB
Markdown
# 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).
|
|
|
|
```sh
|
|
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`.
|