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>
This commit is contained in:
1 parent
e7b9c0c8bf
commit
abe680e853
3 files changed
+893
No files matched your search
@@ -0,0 +1,73 @@
|
|||||||
|
# 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`.
|
||||||
@@ -0,0 +1,385 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Vectores de prueba de la firma OIT de las invitaciones V1 (CONTRATO_INVITACIONES_V1 §3.1).
|
||||||
|
|
||||||
|
python3 generar.py > invitaciones-v1.json
|
||||||
|
|
||||||
|
Bytes firmados: etiqueta || 0x00 || JCS(cuerpo). Ed25519 en Python puro, sobre el código de
|
||||||
|
referencia del RFC 8032 §6, para no depender de la misma librería que se va a probar. Antes de
|
||||||
|
emitir nada se comprueba contra los vectores del RFC 8032 (TEST 1 y 2) y contra OpenSSL.
|
||||||
|
|
||||||
|
JCS (RFC 8785) se implementa sólo para el dominio de estos cuerpos: claves y cadenas ASCII y
|
||||||
|
enteros < 2^53. Para ese dominio JCS es exactamente claves ordenadas, sin espacios y los escapes
|
||||||
|
mínimos de JSON; fuera de él, este generador se niega.
|
||||||
|
|
||||||
|
Las llaves son las semillas PÚBLICAS de los TEST 1 y 2 del RFC 8032: nunca valen para nada real.
|
||||||
|
"""
|
||||||
|
import base64, calendar, hashlib, json, os, subprocess, sys, tempfile
|
||||||
|
|
||||||
|
# ── Ed25519 (RFC 8032 §6) ────────────────────────────────────────────────────
|
||||||
|
p = 2**255 - 19
|
||||||
|
L = 2**252 + 27742317777372353535851937790883648493
|
||||||
|
d = -121665 * pow(121666, p - 2, p) % p
|
||||||
|
SQRT_M1 = pow(2, (p - 1) // 4, p)
|
||||||
|
IDENTIDAD = (0, 1, 1, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def sha512_modl(b):
|
||||||
|
return int.from_bytes(hashlib.sha512(b).digest(), "little") % L
|
||||||
|
|
||||||
|
|
||||||
|
def sumar(P, Q):
|
||||||
|
A = (P[1] - P[0]) * (Q[1] - Q[0]) % p
|
||||||
|
B = (P[1] + P[0]) * (Q[1] + Q[0]) % p
|
||||||
|
C = 2 * P[3] * Q[3] * d % p
|
||||||
|
D = 2 * P[2] * Q[2] % p
|
||||||
|
E, F, G, H = B - A, D - C, D + C, B + A
|
||||||
|
return (E * F % p, G * H % p, F * G % p, E * H % p)
|
||||||
|
|
||||||
|
|
||||||
|
def multiplicar(s, P):
|
||||||
|
Q = IDENTIDAD
|
||||||
|
while s > 0:
|
||||||
|
if s & 1:
|
||||||
|
Q = sumar(Q, P)
|
||||||
|
P = sumar(P, P)
|
||||||
|
s >>= 1
|
||||||
|
return Q
|
||||||
|
|
||||||
|
|
||||||
|
def iguales(P, Q):
|
||||||
|
return (P[0] * Q[2] - Q[0] * P[2]) % p == 0 and (P[1] * Q[2] - Q[1] * P[2]) % p == 0
|
||||||
|
|
||||||
|
|
||||||
|
def recuperar_x(y, signo):
|
||||||
|
if y >= p:
|
||||||
|
return None
|
||||||
|
x2 = (y * y - 1) * pow(d * y * y + 1, p - 2, p)
|
||||||
|
if x2 == 0:
|
||||||
|
return None if signo else 0
|
||||||
|
x = pow(x2, (p + 3) // 8, p)
|
||||||
|
if (x * x - x2) % p != 0:
|
||||||
|
x = x * SQRT_M1 % p
|
||||||
|
if (x * x - x2) % p != 0:
|
||||||
|
return None
|
||||||
|
if (x & 1) != signo:
|
||||||
|
x = p - x
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
gy = 4 * pow(5, p - 2, p) % p
|
||||||
|
gx = recuperar_x(gy, 0)
|
||||||
|
BASE = (gx, gy, 1, gx * gy % p)
|
||||||
|
|
||||||
|
|
||||||
|
def comprimir(P):
|
||||||
|
zi = pow(P[2], p - 2, p)
|
||||||
|
x, y = P[0] * zi % p, P[1] * zi % p
|
||||||
|
return int.to_bytes(y | ((x & 1) << 255), 32, "little")
|
||||||
|
|
||||||
|
|
||||||
|
def descomprimir(s, laxo=False):
|
||||||
|
"""Estricto: rechaza y >= p y el «cero negativo», como el RFC 8032. Laxo: reduce y mod p,
|
||||||
|
como hacen los verificadores permisivos (ZIP-215 y otros)."""
|
||||||
|
y = int.from_bytes(s, "little")
|
||||||
|
signo = y >> 255
|
||||||
|
y &= (1 << 255) - 1
|
||||||
|
if laxo:
|
||||||
|
y %= p
|
||||||
|
x = recuperar_x(y, signo)
|
||||||
|
if x is None and laxo and signo:
|
||||||
|
x = recuperar_x(y, 0)
|
||||||
|
if x is None:
|
||||||
|
return None
|
||||||
|
return (x, y, 1, x * y % p)
|
||||||
|
|
||||||
|
|
||||||
|
def expandir(semilla):
|
||||||
|
h = hashlib.sha512(semilla).digest()
|
||||||
|
a = int.from_bytes(h[:32], "little")
|
||||||
|
a &= (1 << 254) - 8
|
||||||
|
a |= 1 << 254
|
||||||
|
return a, h[32:]
|
||||||
|
|
||||||
|
|
||||||
|
def publica(semilla):
|
||||||
|
return comprimir(multiplicar(expandir(semilla)[0], BASE))
|
||||||
|
|
||||||
|
|
||||||
|
def firmar(semilla, msg):
|
||||||
|
a, prefijo = expandir(semilla)
|
||||||
|
A = comprimir(multiplicar(a, BASE))
|
||||||
|
r = sha512_modl(prefijo + msg)
|
||||||
|
R = comprimir(multiplicar(r, BASE))
|
||||||
|
h = sha512_modl(R + A + msg)
|
||||||
|
return R + int.to_bytes((r + h * a) % L, 32, "little")
|
||||||
|
|
||||||
|
|
||||||
|
def orden_pequeno(P):
|
||||||
|
return iguales(multiplicar(8, P), IDENTIDAD)
|
||||||
|
|
||||||
|
|
||||||
|
def verificar(pub, msg, firma, modo):
|
||||||
|
"""modo «estricto»: lo que §3.1 exige (codificación canónica de A y R, S < L, A de orden no
|
||||||
|
pequeño). modo «laxo»: sólo la ecuación, con A y S tal como lleguen. Sirve para enseñar qué
|
||||||
|
vectores pasan en un verificador permisivo y por qué hace falta cada comprobación."""
|
||||||
|
if len(pub) != 32 or len(firma) != 64:
|
||||||
|
return False
|
||||||
|
estricto = modo == "estricto"
|
||||||
|
A = descomprimir(pub, laxo=not estricto)
|
||||||
|
R = descomprimir(firma[:32], laxo=not estricto)
|
||||||
|
if A is None or R is None:
|
||||||
|
return False
|
||||||
|
S = int.from_bytes(firma[32:], "little")
|
||||||
|
if estricto and (S >= L or comprimir(A) != pub or orden_pequeno(A)):
|
||||||
|
return False
|
||||||
|
h = sha512_modl(firma[:32] + pub + msg)
|
||||||
|
return iguales(multiplicar(S % L if not estricto else S, BASE), sumar(R, multiplicar(h, A)))
|
||||||
|
|
||||||
|
|
||||||
|
# ── comprobaciones del propio generador ─────────────────────────────────────
|
||||||
|
RFC = [ # RFC 8032 §7.1, TEST 1 y TEST 2
|
||||||
|
("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
|
||||||
|
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a", "",
|
||||||
|
"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155"
|
||||||
|
"5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"),
|
||||||
|
("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
|
||||||
|
"3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c", "72",
|
||||||
|
"92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da"
|
||||||
|
"085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00"),
|
||||||
|
]
|
||||||
|
for sk, pk, m, sig in RFC:
|
||||||
|
sk, m = bytes.fromhex(sk), bytes.fromhex(m)
|
||||||
|
assert publica(sk).hex() == pk, "la llave pública no coincide con el RFC 8032"
|
||||||
|
assert firmar(sk, m).hex() == sig, "la firma no coincide con el RFC 8032"
|
||||||
|
assert verificar(bytes.fromhex(pk), m, bytes.fromhex(sig), "estricto")
|
||||||
|
assert not verificar(bytes.fromhex(pk), m + b"x", bytes.fromhex(sig), "estricto")
|
||||||
|
|
||||||
|
|
||||||
|
def openssl_firma(semilla, msg):
|
||||||
|
"""Segunda implementación independiente: OpenSSL, con la misma semilla (Ed25519 es
|
||||||
|
determinista, así que la firma tiene que salir idéntica byte a byte)."""
|
||||||
|
der = bytes.fromhex("302e020100300506032b657004220420") + semilla
|
||||||
|
with tempfile.TemporaryDirectory() as t:
|
||||||
|
k, mm = os.path.join(t, "k.der"), os.path.join(t, "m")
|
||||||
|
open(k, "wb").write(der)
|
||||||
|
open(mm, "wb").write(msg)
|
||||||
|
return subprocess.run(["openssl", "pkeyutl", "-sign", "-inkey", k, "-keyform", "DER",
|
||||||
|
"-rawin", "-in", mm], capture_output=True, check=True).stdout
|
||||||
|
|
||||||
|
|
||||||
|
def openssl_verifica(pub, msg, firma):
|
||||||
|
"""Lo que dice un verificador de terceros muy usado. Medido el 2026-10-28 con OpenSSL 3.6.4:
|
||||||
|
ACEPTA F8, F9 y F10, las firmas forjadas sin llave privada."""
|
||||||
|
with tempfile.TemporaryDirectory() as t:
|
||||||
|
k, mm, ss = (os.path.join(t, n) for n in ("k.der", "m", "s"))
|
||||||
|
open(k, "wb").write(bytes.fromhex("302a300506032b6570032100") + pub)
|
||||||
|
open(mm, "wb").write(msg)
|
||||||
|
open(ss, "wb").write(firma)
|
||||||
|
return subprocess.run(["openssl", "pkeyutl", "-verify", "-pubin", "-inkey", k, "-keyform", "DER",
|
||||||
|
"-rawin", "-in", mm, "-sigfile", ss], capture_output=True).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
# ── JCS restringido ─────────────────────────────────────────────────────────
|
||||||
|
def jcs(obj):
|
||||||
|
for k, v in obj.items():
|
||||||
|
assert isinstance(k, str) and k.isascii(), k
|
||||||
|
if isinstance(v, str):
|
||||||
|
assert v.isascii() and all(0x20 <= ord(c) < 0x7f and c not in '"\\' for c in v), v
|
||||||
|
else:
|
||||||
|
assert isinstance(v, int) and not isinstance(v, bool) and 0 <= v < 2**53, v
|
||||||
|
# con claves ASCII, el orden por unidades UTF-16 de RFC 8785 es el orden de bytes
|
||||||
|
return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode()
|
||||||
|
|
||||||
|
|
||||||
|
def b64u(b):
|
||||||
|
return base64.urlsafe_b64encode(b).rstrip(b"=").decode()
|
||||||
|
|
||||||
|
|
||||||
|
def det(etiqueta, n):
|
||||||
|
"""Bytes de prueba deterministas. En producción, nonce y token son ALEATORIOS."""
|
||||||
|
return hashlib.sha256(b"origin-invitaciones/vectores-de-prueba/" + etiqueta.encode()).digest()[:n]
|
||||||
|
|
||||||
|
|
||||||
|
ETQ = {op: f"origin-invitaciones/{op}/v1" for op in ("crear", "canjear", "revocar")}
|
||||||
|
AUD = "originmetaverse.com"
|
||||||
|
TS = calendar.timegm((2026, 10, 28, 12, 0, 0))
|
||||||
|
LLAVE_A = bytes.fromhex(RFC[0][0]) # quien invita
|
||||||
|
LLAVE_B = bytes.fromhex(RFC[1][0]) # quien canjea
|
||||||
|
PUB_A, PUB_B = publica(LLAVE_A), publica(LLAVE_B)
|
||||||
|
TOKEN = b64u(det("token", 32))
|
||||||
|
HUELLA = hashlib.sha256(TOKEN.encode()).hexdigest()
|
||||||
|
assert len(TOKEN) == 43
|
||||||
|
|
||||||
|
|
||||||
|
def mensaje(etiqueta, cuerpo):
|
||||||
|
return etiqueta.encode() + b"\x00" + jcs(cuerpo)
|
||||||
|
|
||||||
|
|
||||||
|
def cuerpo(pub, nonce, **extra):
|
||||||
|
return {"aud": AUD, "ts": TS, "nonce": b64u(det(nonce, 16)), "pub": b64u(pub), **extra}
|
||||||
|
|
||||||
|
|
||||||
|
C_CREAR = cuerpo(PUB_A, "n1", destino="mundo-de-prueba.v1", expiraEn=TS + 7 * 86400)
|
||||||
|
C_CANJEAR = cuerpo(PUB_B, "n2", token=TOKEN)
|
||||||
|
C_REVOCAR = cuerpo(PUB_A, "n3", huella=HUELLA)
|
||||||
|
|
||||||
|
vectores = []
|
||||||
|
|
||||||
|
|
||||||
|
def caso(id, op, c, firma, llave_cabecera, espera, motivo, reloj=TS, nota=None, transporte=None,
|
||||||
|
firmado_con=None):
|
||||||
|
m = mensaje(ETQ[op], c)
|
||||||
|
v = {
|
||||||
|
"id": id, "operacion": op, "etiqueta": ETQ[op],
|
||||||
|
"cuerpo": c, "jcs": jcs(c).decode(),
|
||||||
|
"transporte_json": transporte if transporte is not None else jcs(c).decode(),
|
||||||
|
"mensaje_hex": m.hex(),
|
||||||
|
"cabeceras": {"Origin-Llave": b64u(llave_cabecera), "Origin-Firma": b64u(firma)},
|
||||||
|
"reloj_del_servidor": reloj,
|
||||||
|
"espera": espera, "motivo": motivo,
|
||||||
|
"verificador_estricto_acepta_la_firma": verificar(llave_cabecera, m, firma, "estricto"),
|
||||||
|
"verificador_laxo_acepta_la_firma": verificar(llave_cabecera, m, firma, "laxo"),
|
||||||
|
"openssl_acepta_la_firma": openssl_verifica(llave_cabecera, m, firma),
|
||||||
|
}
|
||||||
|
if firmado_con:
|
||||||
|
v["firmado_con"] = firmado_con
|
||||||
|
if nota:
|
||||||
|
v["nota"] = nota
|
||||||
|
vectores.append(v)
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
# Válidos
|
||||||
|
for id, op, c, sk, pub in (("V1", "crear", C_CREAR, LLAVE_A, PUB_A),
|
||||||
|
("V2", "canjear", C_CANJEAR, LLAVE_B, PUB_B),
|
||||||
|
("V3", "revocar", C_REVOCAR, LLAVE_A, PUB_A)):
|
||||||
|
f = firmar(sk, mensaje(ETQ[op], c))
|
||||||
|
assert f == openssl_firma(sk, mensaje(ETQ[op], c)), "OpenSSL no da la misma firma"
|
||||||
|
caso(id, op, c, f, pub, "acepta", "petición válida")
|
||||||
|
F_V1 = firmar(LLAVE_A, mensaje(ETQ["crear"], C_CREAR))
|
||||||
|
|
||||||
|
# El orden y los espacios del transporte no importan: el servidor re-canonicaliza.
|
||||||
|
caso("V4", "crear", C_CREAR, F_V1, PUB_A, "acepta",
|
||||||
|
"mismo V1 con otro orden de claves y espacios en el transporte: el servidor firma sobre JCS, "
|
||||||
|
"no sobre los bytes recibidos",
|
||||||
|
transporte='{ "pub": "%s", "ts": %d, "nonce": "%s", "aud": "%s", "expiraEn": %d, "destino": "%s" }'
|
||||||
|
% (C_CREAR["pub"], TS, C_CREAR["nonce"], AUD, C_CREAR["expiraEn"], C_CREAR["destino"]),
|
||||||
|
nota="usar como petición nueva; si se manda DESPUÉS de V1, es el caso F2 (nonce repetido)")
|
||||||
|
|
||||||
|
# Deben fallar
|
||||||
|
caso("F1", "crear", C_CREAR, F_V1, PUB_A, "rechaza", "nonce repetido",
|
||||||
|
nota="enviar V1 y luego exactamente la misma petición: la firma es buena, el par (pub, nonce) "
|
||||||
|
"ya se usó dentro de la ventana")
|
||||||
|
caso("F2", "crear", C_CREAR, F_V1, PUB_A, "rechaza", "ts fuera de la ventana de ±5 min",
|
||||||
|
reloj=TS + 301, nota="mismo V1, con el reloj del servidor 301 s por delante")
|
||||||
|
caso("F3", "crear", C_CREAR, F_V1, PUB_A, "rechaza", "ts fuera de la ventana de ±5 min",
|
||||||
|
reloj=TS - 301, nota="mismo V1, con el reloj del servidor 301 s por detrás (ts del futuro)")
|
||||||
|
|
||||||
|
# F4: la firma de «crear» puesta sobre un cuerpo de «canjear»
|
||||||
|
f_cruzada = firmar(LLAVE_B, mensaje(ETQ["crear"], C_CANJEAR))
|
||||||
|
caso("F4", "canjear", C_CANJEAR, f_cruzada, PUB_B, "rechaza",
|
||||||
|
"etiqueta cruzada: la firma se hizo con la etiqueta de crear sobre el cuerpo de canjear",
|
||||||
|
firmado_con=ETQ["crear"])
|
||||||
|
|
||||||
|
# F5: Origin-Llave distinta del pub firmado
|
||||||
|
caso("F5", "crear", C_CREAR, F_V1, PUB_B, "rechaza",
|
||||||
|
"Origin-Llave no es igual al pub del cuerpo (debe rechazarse ANTES de verificar)")
|
||||||
|
|
||||||
|
# F6: S no canónico (S + L): misma firma, otros bytes
|
||||||
|
S = int.from_bytes(F_V1[32:], "little")
|
||||||
|
f_mal = F_V1[:32] + int.to_bytes(S + L, 32, "little")
|
||||||
|
caso("F6", "crear", C_CREAR, f_mal, PUB_A, "rechaza", "S no canónico (S >= L): firma maleable")
|
||||||
|
|
||||||
|
# F7: firma de otro cuerpo (cambió destino después de firmar)
|
||||||
|
otro = dict(C_CREAR, destino="otro-mundo")
|
||||||
|
caso("F7", "crear", otro, F_V1, PUB_A, "rechaza", "el cuerpo no es el firmado (destino cambiado)")
|
||||||
|
|
||||||
|
|
||||||
|
# F8-F10: llaves de orden pequeño con la firma trivial, sin llave privada
|
||||||
|
def forjar(pub_bytes, etiqueta_nonce):
|
||||||
|
A = descomprimir(pub_bytes, laxo=True)
|
||||||
|
sub = [IDENTIDAD]
|
||||||
|
while True:
|
||||||
|
nxt = sumar(sub[-1], A)
|
||||||
|
if iguales(nxt, IDENTIDAD):
|
||||||
|
break
|
||||||
|
sub.append(nxt)
|
||||||
|
for intento in range(64):
|
||||||
|
c = cuerpo(pub_bytes, f"{etiqueta_nonce}-{intento}",
|
||||||
|
destino="mundo-de-prueba.v1", expiraEn=TS + 7 * 86400)
|
||||||
|
m = mensaje(ETQ["crear"], c)
|
||||||
|
for Rp in sub:
|
||||||
|
Rb = comprimir(Rp)
|
||||||
|
h = sha512_modl(Rb + pub_bytes + m)
|
||||||
|
if iguales(sumar(Rp, multiplicar(h, A)), IDENTIDAD):
|
||||||
|
return c, Rb + bytes(32)
|
||||||
|
raise SystemExit("no se pudo forjar")
|
||||||
|
|
||||||
|
|
||||||
|
for id, hexpub, nombre in (
|
||||||
|
("F8", "01" + "00" * 31, "la identidad (orden 1)"),
|
||||||
|
("F9", "ec" + "ff" * 30 + "7f", "el punto de orden 2"),
|
||||||
|
("F10", "ee" + "ff" * 30 + "7f", "una codificación NO canónica de la identidad (y = p + 1)")):
|
||||||
|
pb = bytes.fromhex(hexpub)
|
||||||
|
c, f = forjar(pb, id)
|
||||||
|
v = caso(id, "crear", c, f, pb, "rechaza",
|
||||||
|
f"llave de orden pequeño: {nombre}. Firma trivial R de orden pequeño y S = 0, hecha SIN "
|
||||||
|
"llave privada",
|
||||||
|
nota="la ecuación de verificación SE CUMPLE: sólo la rechaza la comprobación explícita "
|
||||||
|
"de llave fuerte (en la cadena, is_strong_ed25519_key; RV14)")
|
||||||
|
assert v["verificador_laxo_acepta_la_firma"], f"{id}: la forja debería pasar la ecuación"
|
||||||
|
if not v["openssl_acepta_la_firma"]:
|
||||||
|
print(f"aviso: este OpenSSL ya rechaza {id}", file=sys.stderr)
|
||||||
|
assert not v["verificador_estricto_acepta_la_firma"]
|
||||||
|
|
||||||
|
# F11: clave duplicada en el transporte
|
||||||
|
caso("F11", "crear", C_CREAR, F_V1, PUB_A, "rechaza",
|
||||||
|
"clave duplicada en el JSON recibido. JCS (RFC 8785 §3.1, I-JSON) no la admite, y JSON.parse "
|
||||||
|
"se queda en silencio con la última",
|
||||||
|
transporte=jcs(C_CREAR).decode()[:-1] + ',"destino":"otro-mundo"}')
|
||||||
|
|
||||||
|
# coherencia final
|
||||||
|
for v in vectores:
|
||||||
|
if v["espera"] == "acepta":
|
||||||
|
assert v["verificador_estricto_acepta_la_firma"], v["id"]
|
||||||
|
for id in ("F4", "F6", "F7"):
|
||||||
|
v = next(x for x in vectores if x["id"] == id)
|
||||||
|
assert not v["verificador_estricto_acepta_la_firma"], id
|
||||||
|
|
||||||
|
salida = {
|
||||||
|
"version": "origin-invitaciones/vectores/v1",
|
||||||
|
"contrato": "origin-metaverse-web docs/CONTRATO_INVITACIONES_V1.md §3.1 (d5f5f5e)",
|
||||||
|
"autora": "Charon, 2026-10-28",
|
||||||
|
"mensaje_firmado": "utf8(etiqueta) || 0x00 || JCS(cuerpo)",
|
||||||
|
"codificacion": "pub, firma, nonce y token en base64url sin relleno; huella = sha256(token) en hex",
|
||||||
|
"llaves_de_prueba": {
|
||||||
|
"A_quien_invita": {"semilla_hex": LLAVE_A.hex(), "pub_hex": PUB_A.hex(), "pub_b64url": b64u(PUB_A),
|
||||||
|
"origen": "RFC 8032 §7.1 TEST 1 (pública: nunca para nada real)"},
|
||||||
|
"B_quien_canjea": {"semilla_hex": LLAVE_B.hex(), "pub_hex": PUB_B.hex(), "pub_b64url": b64u(PUB_B),
|
||||||
|
"origen": "RFC 8032 §7.1 TEST 2"},
|
||||||
|
},
|
||||||
|
"token_de_prueba": TOKEN, "huella_de_prueba": HUELLA,
|
||||||
|
"comprobado_con": ["RFC 8032 TEST 1 y 2 (llave y firma)", "OpenSSL " + subprocess.run(
|
||||||
|
["openssl", "version"], capture_output=True, text=True).stdout.split()[1] + " (V1-V3 byte a byte)"],
|
||||||
|
"como_usarlos": [
|
||||||
|
"Cada vector es una petición: transporte_json es el cuerpo HTTP y cabeceras sus cabeceras.",
|
||||||
|
"Fija el reloj del servidor en reloj_del_servidor y empieza cada vector con el almacén de "
|
||||||
|
"nonces vacío, salvo F1, que va justo después de V1.",
|
||||||
|
"espera = acepta | rechaza. Un test que no sepa reprobar no vale: quita cada comprobación "
|
||||||
|
"y mira que su vector pase a aceptarse.",
|
||||||
|
"verificador_laxo_acepta_la_firma dice qué pasaría con un verificador permisivo: F6 y F8-F10 "
|
||||||
|
"pasan ahí. F8-F10 cumplen la ecuación: OpenSSL 3.6.4 los ACEPTA (openssl_acepta_la_firma). "
|
||||||
|
"Si tu verify con zip215:false tampoco los rechaza, falta la comprobación de llave fuerte: "
|
||||||
|
"decodificar pub, exigir que vuelva a codificarse igual y que no sea de orden pequeño.",
|
||||||
|
],
|
||||||
|
"supuestos_por_confirmar": [
|
||||||
|
"expiraEn es un instante absoluto en segundos (ts + 7 días aquí). Si es una duración, se regeneran.",
|
||||||
|
"canjear firma {aud, nonce, pub, token, ts}: pub es la OIT de quien canjea y el token (la "
|
||||||
|
"preimagen) va DENTRO de lo firmado. El §3 punto 3 dice {token, canjeadorOit, firma} y el "
|
||||||
|
"§3.1 dice «la huella del token»: con la huella sola el servidor no puede comprobar la preimagen.",
|
||||||
|
"revocar firma {aud, huella, nonce, pub, ts}, con pub igual a invitadorOit.",
|
||||||
|
],
|
||||||
|
"vectores": vectores,
|
||||||
|
}
|
||||||
|
json.dump(salida, sys.stdout, ensure_ascii=False, indent=2)
|
||||||
|
print()
|
||||||
@@ -0,0 +1,435 @@
|
|||||||
|
{
|
||||||
|
"version": "origin-invitaciones/vectores/v1",
|
||||||
|
"contrato": "origin-metaverse-web docs/CONTRATO_INVITACIONES_V1.md §3.1 (d5f5f5e)",
|
||||||
|
"autora": "Charon, 2026-10-28",
|
||||||
|
"mensaje_firmado": "utf8(etiqueta) || 0x00 || JCS(cuerpo)",
|
||||||
|
"codificacion": "pub, firma, nonce y token en base64url sin relleno; huella = sha256(token) en hex",
|
||||||
|
"llaves_de_prueba": {
|
||||||
|
"A_quien_invita": {
|
||||||
|
"semilla_hex": "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
|
||||||
|
"pub_hex": "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
|
||||||
|
"pub_b64url": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"origen": "RFC 8032 §7.1 TEST 1 (pública: nunca para nada real)"
|
||||||
|
},
|
||||||
|
"B_quien_canjea": {
|
||||||
|
"semilla_hex": "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
|
||||||
|
"pub_hex": "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
|
||||||
|
"pub_b64url": "PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw",
|
||||||
|
"origen": "RFC 8032 §7.1 TEST 2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"token_de_prueba": "HVC4TTwAc5X9awxdLTV8X9kDOcw_g3yUJSOadF_UW64",
|
||||||
|
"huella_de_prueba": "d24ddd65bd757dc69b7a630d8410556221a433c4329531d22f66d23d4348b76c",
|
||||||
|
"comprobado_con": [
|
||||||
|
"RFC 8032 TEST 1 y 2 (llave y firma)",
|
||||||
|
"OpenSSL 3.6.4 (V1-V3 byte a byte)"
|
||||||
|
],
|
||||||
|
"como_usarlos": [
|
||||||
|
"Cada vector es una petición: transporte_json es el cuerpo HTTP y cabeceras sus cabeceras.",
|
||||||
|
"Fija el reloj del servidor en reloj_del_servidor y empieza cada vector con el almacén de nonces vacío, salvo F1, que va justo después de V1.",
|
||||||
|
"espera = acepta | rechaza. Un test que no sepa reprobar no vale: quita cada comprobación y mira que su vector pase a aceptarse.",
|
||||||
|
"verificador_laxo_acepta_la_firma dice qué pasaría con un verificador permisivo: F6 y F8-F10 pasan ahí. F8-F10 cumplen la ecuación: OpenSSL 3.6.4 los ACEPTA (openssl_acepta_la_firma). Si tu verify con zip215:false tampoco los rechaza, falta la comprobación de llave fuerte: decodificar pub, exigir que vuelva a codificarse igual y que no sea de orden pequeño."
|
||||||
|
],
|
||||||
|
"supuestos_por_confirmar": [
|
||||||
|
"expiraEn es un instante absoluto en segundos (ts + 7 días aquí). Si es una duración, se regeneran.",
|
||||||
|
"canjear firma {aud, nonce, pub, token, ts}: pub es la OIT de quien canjea y el token (la preimagen) va DENTRO de lo firmado. El §3 punto 3 dice {token, canjeadorOit, firma} y el §3.1 dice «la huella del token»: con la huella sola el servidor no puede comprobar la preimagen.",
|
||||||
|
"revocar firma {aud, huella, nonce, pub, ts}, con pub igual a invitadorOit."
|
||||||
|
],
|
||||||
|
"vectores": [
|
||||||
|
{
|
||||||
|
"id": "V1",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "l-SsDclPrSgeO_MdNULifQ",
|
||||||
|
"pub": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a226c2d537344636c50725367654f5f4d644e554c696651222c22707562223a223131715941594b7843726656535f3754795751484f6737686376506170694d6c727749616150634855526f222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"Origin-Firma": "94ykfqmBeDJEvKgBdGBGmdUZFQPprWW2F79-heC7ZIn9-hwpu-aQh5vwf7MCSZ0E3ead6U9JeqJUdgY0Oc8HAg"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "acepta",
|
||||||
|
"motivo": "petición válida",
|
||||||
|
"verificador_estricto_acepta_la_firma": true,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "V2",
|
||||||
|
"operacion": "canjear",
|
||||||
|
"etiqueta": "origin-invitaciones/canjear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "ioBVewvUqbJNvOUwlE380w",
|
||||||
|
"pub": "PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw",
|
||||||
|
"token": "HVC4TTwAc5X9awxdLTV8X9kDOcw_g3yUJSOadF_UW64"
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"nonce\":\"ioBVewvUqbJNvOUwlE380w\",\"pub\":\"PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw\",\"token\":\"HVC4TTwAc5X9awxdLTV8X9kDOcw_g3yUJSOadF_UW64\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"nonce\":\"ioBVewvUqbJNvOUwlE380w\",\"pub\":\"PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw\",\"token\":\"HVC4TTwAc5X9awxdLTV8X9kDOcw_g3yUJSOadF_UW64\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63616e6a6561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c226e6f6e6365223a22696f42566577765571624a4e764f55776c4533383077222c22707562223a2250554158772d6844695671537477716e5452742d764a79594c4d3875784a614d774d3156385372305a6777222c22746f6b656e223a22485643345454774163355839617778644c54563858396b444f63775f673379554a534f6164465f55573634222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw",
|
||||||
|
"Origin-Firma": "3Z79SlRC62mrNAzMLP6o1CNbIdj2uIWlc2sWzQvQDhZP5m9dHCKPjrI3SqM33eZQRnYx7b4ouq2ARR2Yi-ujBA"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "acepta",
|
||||||
|
"motivo": "petición válida",
|
||||||
|
"verificador_estricto_acepta_la_firma": true,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "V3",
|
||||||
|
"operacion": "revocar",
|
||||||
|
"etiqueta": "origin-invitaciones/revocar/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "CbdsIxFrDPvBSN_2qYeVcw",
|
||||||
|
"pub": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"huella": "d24ddd65bd757dc69b7a630d8410556221a433c4329531d22f66d23d4348b76c"
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"huella\":\"d24ddd65bd757dc69b7a630d8410556221a433c4329531d22f66d23d4348b76c\",\"nonce\":\"CbdsIxFrDPvBSN_2qYeVcw\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"huella\":\"d24ddd65bd757dc69b7a630d8410556221a433c4329531d22f66d23d4348b76c\",\"nonce\":\"CbdsIxFrDPvBSN_2qYeVcw\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f7265766f6361722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c226875656c6c61223a2264323464646436356264373537646336396237613633306438343130353536323231613433336334333239353331643232663636643233643433343862373663222c226e6f6e6365223a22436264734978467244507642534e5f32715965566377222c22707562223a223131715941594b7843726656535f3754795751484f6737686376506170694d6c727749616150634855526f222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"Origin-Firma": "FR69zi1RDZYrREfVHhZkoO190h27Mrw74_NLPIxze79_QZMRXL8f700OPAzOu00CQLaDIuel4VD30mvqtRcHBQ"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "acepta",
|
||||||
|
"motivo": "petición válida",
|
||||||
|
"verificador_estricto_acepta_la_firma": true,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "V4",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "l-SsDclPrSgeO_MdNULifQ",
|
||||||
|
"pub": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{ \"pub\": \"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\", \"ts\": 1793188800, \"nonce\": \"l-SsDclPrSgeO_MdNULifQ\", \"aud\": \"originmetaverse.com\", \"expiraEn\": 1793793600, \"destino\": \"mundo-de-prueba.v1\" }",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a226c2d537344636c50725367654f5f4d644e554c696651222c22707562223a223131715941594b7843726656535f3754795751484f6737686376506170694d6c727749616150634855526f222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"Origin-Firma": "94ykfqmBeDJEvKgBdGBGmdUZFQPprWW2F79-heC7ZIn9-hwpu-aQh5vwf7MCSZ0E3ead6U9JeqJUdgY0Oc8HAg"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "acepta",
|
||||||
|
"motivo": "mismo V1 con otro orden de claves y espacios en el transporte: el servidor firma sobre JCS, no sobre los bytes recibidos",
|
||||||
|
"verificador_estricto_acepta_la_firma": true,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true,
|
||||||
|
"nota": "usar como petición nueva; si se manda DESPUÉS de V1, es el caso F2 (nonce repetido)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F1",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "l-SsDclPrSgeO_MdNULifQ",
|
||||||
|
"pub": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a226c2d537344636c50725367654f5f4d644e554c696651222c22707562223a223131715941594b7843726656535f3754795751484f6737686376506170694d6c727749616150634855526f222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"Origin-Firma": "94ykfqmBeDJEvKgBdGBGmdUZFQPprWW2F79-heC7ZIn9-hwpu-aQh5vwf7MCSZ0E3ead6U9JeqJUdgY0Oc8HAg"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "nonce repetido",
|
||||||
|
"verificador_estricto_acepta_la_firma": true,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true,
|
||||||
|
"nota": "enviar V1 y luego exactamente la misma petición: la firma es buena, el par (pub, nonce) ya se usó dentro de la ventana"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F2",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "l-SsDclPrSgeO_MdNULifQ",
|
||||||
|
"pub": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a226c2d537344636c50725367654f5f4d644e554c696651222c22707562223a223131715941594b7843726656535f3754795751484f6737686376506170694d6c727749616150634855526f222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"Origin-Firma": "94ykfqmBeDJEvKgBdGBGmdUZFQPprWW2F79-heC7ZIn9-hwpu-aQh5vwf7MCSZ0E3ead6U9JeqJUdgY0Oc8HAg"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793189101,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "ts fuera de la ventana de ±5 min",
|
||||||
|
"verificador_estricto_acepta_la_firma": true,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true,
|
||||||
|
"nota": "mismo V1, con el reloj del servidor 301 s por delante"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F3",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "l-SsDclPrSgeO_MdNULifQ",
|
||||||
|
"pub": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a226c2d537344636c50725367654f5f4d644e554c696651222c22707562223a223131715941594b7843726656535f3754795751484f6737686376506170694d6c727749616150634855526f222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"Origin-Firma": "94ykfqmBeDJEvKgBdGBGmdUZFQPprWW2F79-heC7ZIn9-hwpu-aQh5vwf7MCSZ0E3ead6U9JeqJUdgY0Oc8HAg"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188499,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "ts fuera de la ventana de ±5 min",
|
||||||
|
"verificador_estricto_acepta_la_firma": true,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true,
|
||||||
|
"nota": "mismo V1, con el reloj del servidor 301 s por detrás (ts del futuro)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F4",
|
||||||
|
"operacion": "canjear",
|
||||||
|
"etiqueta": "origin-invitaciones/canjear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "ioBVewvUqbJNvOUwlE380w",
|
||||||
|
"pub": "PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw",
|
||||||
|
"token": "HVC4TTwAc5X9awxdLTV8X9kDOcw_g3yUJSOadF_UW64"
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"nonce\":\"ioBVewvUqbJNvOUwlE380w\",\"pub\":\"PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw\",\"token\":\"HVC4TTwAc5X9awxdLTV8X9kDOcw_g3yUJSOadF_UW64\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"nonce\":\"ioBVewvUqbJNvOUwlE380w\",\"pub\":\"PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw\",\"token\":\"HVC4TTwAc5X9awxdLTV8X9kDOcw_g3yUJSOadF_UW64\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63616e6a6561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c226e6f6e6365223a22696f42566577765571624a4e764f55776c4533383077222c22707562223a2250554158772d6844695671537477716e5452742d764a79594c4d3875784a614d774d3156385372305a6777222c22746f6b656e223a22485643345454774163355839617778644c54563858396b444f63775f673379554a534f6164465f55573634222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw",
|
||||||
|
"Origin-Firma": "8tzYumpNlvl9a42l970jmXJB5PFYUq1pK-RJagxXDjq74a_hkeOVtDqGrOK0eopWrfNs9kWwE423IP99fsKDDg"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "etiqueta cruzada: la firma se hizo con la etiqueta de crear sobre el cuerpo de canjear",
|
||||||
|
"verificador_estricto_acepta_la_firma": false,
|
||||||
|
"verificador_laxo_acepta_la_firma": false,
|
||||||
|
"openssl_acepta_la_firma": false,
|
||||||
|
"firmado_con": "origin-invitaciones/crear/v1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F5",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "l-SsDclPrSgeO_MdNULifQ",
|
||||||
|
"pub": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a226c2d537344636c50725367654f5f4d644e554c696651222c22707562223a223131715941594b7843726656535f3754795751484f6737686376506170694d6c727749616150634855526f222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw",
|
||||||
|
"Origin-Firma": "94ykfqmBeDJEvKgBdGBGmdUZFQPprWW2F79-heC7ZIn9-hwpu-aQh5vwf7MCSZ0E3ead6U9JeqJUdgY0Oc8HAg"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "Origin-Llave no es igual al pub del cuerpo (debe rechazarse ANTES de verificar)",
|
||||||
|
"verificador_estricto_acepta_la_firma": false,
|
||||||
|
"verificador_laxo_acepta_la_firma": false,
|
||||||
|
"openssl_acepta_la_firma": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F6",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "l-SsDclPrSgeO_MdNULifQ",
|
||||||
|
"pub": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a226c2d537344636c50725367654f5f4d644e554c696651222c22707562223a223131715941594b7843726656535f3754795751484f6737686376506170694d6c727749616150634855526f222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"Origin-Firma": "94ykfqmBeDJEvKgBdGBGmdUZFQPprWW2F79-heC7ZInqzhKG1Umj33GNd1bhQnwZ3ead6U9JeqJUdgY0Oc8HEg"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "S no canónico (S >= L): firma maleable",
|
||||||
|
"verificador_estricto_acepta_la_firma": false,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F7",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "l-SsDclPrSgeO_MdNULifQ",
|
||||||
|
"pub": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"destino": "otro-mundo",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"otro-mundo\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"otro-mundo\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226f74726f2d6d756e646f222c22657870697261456e223a313739333739333630302c226e6f6e6365223a226c2d537344636c50725367654f5f4d644e554c696651222c22707562223a223131715941594b7843726656535f3754795751484f6737686376506170694d6c727749616150634855526f222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"Origin-Firma": "94ykfqmBeDJEvKgBdGBGmdUZFQPprWW2F79-heC7ZIn9-hwpu-aQh5vwf7MCSZ0E3ead6U9JeqJUdgY0Oc8HAg"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "el cuerpo no es el firmado (destino cambiado)",
|
||||||
|
"verificador_estricto_acepta_la_firma": false,
|
||||||
|
"verificador_laxo_acepta_la_firma": false,
|
||||||
|
"openssl_acepta_la_firma": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F8",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "XJeasE1GFNTkdfN8ER9KCg",
|
||||||
|
"pub": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"XJeasE1GFNTkdfN8ER9KCg\",\"pub\":\"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"XJeasE1GFNTkdfN8ER9KCg\",\"pub\":\"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a22584a656173453147464e546b64664e384552394b4367222c22707562223a2241514141414141414141414141414141414141414141414141414141414141414141414141414141414141222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
"Origin-Firma": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "llave de orden pequeño: la identidad (orden 1). Firma trivial R de orden pequeño y S = 0, hecha SIN llave privada",
|
||||||
|
"verificador_estricto_acepta_la_firma": false,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true,
|
||||||
|
"nota": "la ecuación de verificación SE CUMPLE: sólo la rechaza la comprobación explícita de llave fuerte (en la cadena, is_strong_ed25519_key; RV14)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F9",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "REfXPa4tGjc5slEI5bpktg",
|
||||||
|
"pub": "7P_______________________________________38",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"REfXPa4tGjc5slEI5bpktg\",\"pub\":\"7P_______________________________________38\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"REfXPa4tGjc5slEI5bpktg\",\"pub\":\"7P_______________________________________38\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a225245665850613474476a6335736c45493562706b7467222c22707562223a2237505f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f3338222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "7P_______________________________________38",
|
||||||
|
"Origin-Firma": "7P_______________________________________38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "llave de orden pequeño: el punto de orden 2. Firma trivial R de orden pequeño y S = 0, hecha SIN llave privada",
|
||||||
|
"verificador_estricto_acepta_la_firma": false,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true,
|
||||||
|
"nota": "la ecuación de verificación SE CUMPLE: sólo la rechaza la comprobación explícita de llave fuerte (en la cadena, is_strong_ed25519_key; RV14)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F10",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "PBi0xI2621syp0145Qoi5Q",
|
||||||
|
"pub": "7v_______________________________________38",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"PBi0xI2621syp0145Qoi5Q\",\"pub\":\"7v_______________________________________38\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"PBi0xI2621syp0145Qoi5Q\",\"pub\":\"7v_______________________________________38\",\"ts\":1793188800}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a225042693078493236323173797030313435516f693551222c22707562223a2237765f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f3338222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "7v_______________________________________38",
|
||||||
|
"Origin-Firma": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "llave de orden pequeño: una codificación NO canónica de la identidad (y = p + 1). Firma trivial R de orden pequeño y S = 0, hecha SIN llave privada",
|
||||||
|
"verificador_estricto_acepta_la_firma": false,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true,
|
||||||
|
"nota": "la ecuación de verificación SE CUMPLE: sólo la rechaza la comprobación explícita de llave fuerte (en la cadena, is_strong_ed25519_key; RV14)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "F11",
|
||||||
|
"operacion": "crear",
|
||||||
|
"etiqueta": "origin-invitaciones/crear/v1",
|
||||||
|
"cuerpo": {
|
||||||
|
"aud": "originmetaverse.com",
|
||||||
|
"ts": 1793188800,
|
||||||
|
"nonce": "l-SsDclPrSgeO_MdNULifQ",
|
||||||
|
"pub": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"destino": "mundo-de-prueba.v1",
|
||||||
|
"expiraEn": 1793793600
|
||||||
|
},
|
||||||
|
"jcs": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800}",
|
||||||
|
"transporte_json": "{\"aud\":\"originmetaverse.com\",\"destino\":\"mundo-de-prueba.v1\",\"expiraEn\":1793793600,\"nonce\":\"l-SsDclPrSgeO_MdNULifQ\",\"pub\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\",\"ts\":1793188800,\"destino\":\"otro-mundo\"}",
|
||||||
|
"mensaje_hex": "6f726967696e2d696e7669746163696f6e65732f63726561722f7631007b22617564223a226f726967696e6d65746176657273652e636f6d222c2264657374696e6f223a226d756e646f2d64652d7072756562612e7631222c22657870697261456e223a313739333739333630302c226e6f6e6365223a226c2d537344636c50725367654f5f4d644e554c696651222c22707562223a223131715941594b7843726656535f3754795751484f6737686376506170694d6c727749616150634855526f222c227473223a313739333138383830307d",
|
||||||
|
"cabeceras": {
|
||||||
|
"Origin-Llave": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
||||||
|
"Origin-Firma": "94ykfqmBeDJEvKgBdGBGmdUZFQPprWW2F79-heC7ZIn9-hwpu-aQh5vwf7MCSZ0E3ead6U9JeqJUdgY0Oc8HAg"
|
||||||
|
},
|
||||||
|
"reloj_del_servidor": 1793188800,
|
||||||
|
"espera": "rechaza",
|
||||||
|
"motivo": "clave duplicada en el JSON recibido. JCS (RFC 8785 §3.1, I-JSON) no la admite, y JSON.parse se queda en silencio con la última",
|
||||||
|
"verificador_estricto_acepta_la_firma": true,
|
||||||
|
"verificador_laxo_acepta_la_firma": true,
|
||||||
|
"openssl_acepta_la_firma": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in new issue
Block a user