Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

LACNetNetworks/interoperability-tests

Open more actions menu

Repository files navigation

Interoperabilidad DIDComm v2: Veramo ⇄ didcomm-node

Objetivo

Lograr el ciclo completo de cifrado y descifrado extremo a extremo (E2E) de mensajes DIDComm v2 (anoncrypt) entre dos motores distintos:

  • Veramo (@veramo/did-comm, JavaScript)
  • didcomm-node (binding WASM del motor de Rust didcomm)

La meta es demostrar que un mensaje cifrado por uno de los motores puede ser descifrado por el otro y viceversa, resolviendo los DIDs contra el resolver real de LACChain (https://dev-identity-resolver.l-net.io).

Se cubren las dos direcciones:

# Secuencia Cifra Descifra
1 Veramo → didcomm-node encrypt-veramo.mjs decrypt-didcomm-node.mjs
2 didcomm-node → Veramo encrypt-didcomm-node.mjs decrypt-veramo.mjs

⚠️ Fix obligatorio en @veramo/did-comm

Para la secuencia 1 (cifrar en Veramo → descifrar en didcomm-node) es necesario aplicar un fix en el módulo didcomm.js de Veramo.

Por qué

El motor didcomm-node valida el campo apv del header protegido (apv = base64url(SHA-256(kid_del_destinatario))). La versión original de @veramo/did-comm ignora el apv que se le pasa por options y no lo escribe en el header, por lo que el mensaje generado por Veramo es rechazado al desempaquetarlo con didcomm-node.

En qué consiste el fix

Se modifica @veramo/did-comm/build/didcomm.js para que respete el apv recibido por options y se lo pase al encrypter:

+ if (options.apv) protectedHeader.apv = options.apv;
...
- return a256gcmAnonEncrypterX25519WithA256KW(recipient.publicKeyBytes, recipient.kid);
+ return a256gcmAnonEncrypterX25519WithA256KW(recipient.publicKeyBytes, recipient.kid, options.apv);

Archivo ya corregido

El archivo con el fix ya aplicado está versionado en este repositorio en:

fix-veramo-didcom/@veramo/build/didcomm.js

Cómo aplicarlo

Copiar el archivo corregido sobre el de node_modules (conviene respaldar el original primero):

# Respaldo del original (si no existe ya)
cp node_modules/@veramo/did-comm/build/didcomm.js \
   node_modules/@veramo/did-comm/build/didcomm-ori.js

# Aplicar el fix
cp fix-veramo-didcom/@veramo/build/didcomm.js \
   node_modules/@veramo/did-comm/build/didcomm.js

Nota: node_modules/@veramo/did-comm/build/didcomm-ori.js es el backup del original sin modificar. Como el fix vive en node_modules, debe reaplicarse después de cada npm install.


Requisitos

  • Node.js ≥ 20.12 (se usa process.loadEnvFile() nativo para leer el .env, sin dependencias extra). Probado con Node 22.
  • Dependencias del proyecto instaladas:
npm install

Configuración: config.mjs y .env

Toda la configuración está centralizada en config.mjs, que expone un objeto agrupado por script. Cada valor se lee de una variable de entorno y, si no está definida, usa un valor por defecto (los datos reales de prueba). Por eso los scripts funcionan aunque el .env esté vacío.

  • config.mjs — carga ./.env (si existe) y resuelve cada propiedad como process.env.VARIABLE || valor_por_defecto.
  • .env — plantilla con todas las variables. Vienen vacías: al no tener valor, se aplican los defaults de config.mjs. Para sobrescribir un valor, asígnalo a la derecha del =.

🔐 Seguridad: el .env puede contener claves privadas. Si las defines, añade .env a tu .gitignore.

Variables por script

encrypt-veramo.mjs (prefijo EV_)

Variable Descripción
EV_ISSUER_DID DID del emisor
EV_ISSUER_PRIVATE_KEY Clave privada de firma (secp256k1) del emisor
EV_ISSUER_ENCRYPTION_PRIVATE_KEY Clave privada de cifrado (X25519) del emisor
EV_RECEPTOR_DID DID del destinatario al que se cifra
EV_DID_RESOLVER_URL URL del resolver de DIDs (LACChain)
EV_OUTPUT_FILE Archivo de salida del JWE

decrypt-didcomm-node.mjs (prefijo DDN_)

Variable Descripción
DDN_DID_RESOLVER_URL URL del resolver de DIDs (LACChain)
DDN_RECIPIENT_DID DID del destinatario (quien descifra)
DDN_RECIPIENT_ENCRYPTION_PRIVATE_KEY Clave privada X25519 del destinatario
DDN_PACKED_MESSAGE_FILE Archivo cifrado de entrada

encrypt-didcomm-node.mjs (prefijo EDN_)

Variable Descripción
EDN_NETWORK URL del resolver de DIDs (¡aquí es http://…/ con barra final!)
EDN_SENDER_WALLET_PRIVATE_KEY Clave privada de la wallet del emisor
EDN_SUBJECT_DID DID del destinatario al que se cifra
EDN_SENDER_PRIV_KEY_MAIL Clave privada X25519 del emisor (base64url)
EDN_OUTPUT_FILE Archivo de salida del JWE

decrypt-veramo.mjs (prefijo DV_)

Variable Descripción
DV_PRIVATE_KEY_SUBJECT Clave privada del subject (auxiliar)
DV_PRIVATE_MSG JWK X25519 del destinatario (JSON con x y d)
DV_PACKED_MESSAGE_FILE Archivo cifrado de entrada
DV_RECIPIENT_DID DID del destinatario (quien descifra)
DV_RECIPIENT_KID_FRAGMENT Fragmento del kid (p. ej. #vm-1)

Qué hace cada archivo

Archivo Rol Descripción
config.mjs Config Carga el .env y centraliza todas las constantes con valores por defecto.
.env Config Plantilla de variables de entorno (vacías → se usan los defaults).
encrypt-veramo.mjs Cifrado (Veramo) Crea un agente Veramo en memoria, resuelve el DID del receptor contra LACChain y empaqueta (anoncrypt) el mensaje. Escribe el JWE en msg-encrypted-veramo.json.
decrypt-didcomm-node.mjs Descifrado (didcomm-node) Lee msg-encrypted-veramo.json, resuelve el DID contra LACChain, normaliza el documento a JsonWebKey2020 y desempaqueta con el WASM de Rust.
encrypt-didcomm-node.mjs Cifrado (didcomm-node) Cifra (anoncrypt) con el motor WASM resolviendo el destinatario contra LACChain. Escribe el JWE en msg-encrypted-didcomm-node.json.
decrypt-veramo.mjs Descifrado (Veramo) Importa la clave del destinatario en un agente Veramo y desempaqueta msg-encrypted-didcomm-node.json.
fix-veramo-didcom/ Fix Versión corregida de @veramo/did-comm/build/didcomm.js (soporte de apv).

Secuencia de ejecución

Regla general: primero se ejecuta el encrypt-* (genera el archivo) y luego el decrypt-* correspondiente (lo lee).

Secuencia 1 — Veramo → didcomm-node

# 1) Cifrar con Veramo  -> genera msg-encrypted-veramo.json
node encrypt-veramo.mjs

# 2) Descifrar con didcomm-node  <- lee msg-encrypted-veramo.json
node decrypt-didcomm-node.mjs

Salida esperada del paso 2: el mensaje en claro {"content":"Hola mundo lnet"}.

Esta secuencia requiere el fix de Veramo descrito arriba.

Secuencia 2 — didcomm-node → Veramo

# 1) Cifrar con didcomm-node  -> genera msg-encrypted-didcomm-node.json
node encrypt-didcomm-node.mjs

# 2) Descifrar con Veramo  <- lee msg-encrypted-didcomm-node.json
node decrypt-veramo.mjs

Salida esperada del paso 2: packing: anoncrypt y la credencial verificable (ISSUED_VERIFIABLE_CREDENTIAL).


Detalle criptográfico (clave del éxito)

El acoplamiento real entre los módulos es el par DID destinatario ↔ su clave privada X25519:

  • La clave pública X25519 publicada on-chain para el destinatario debe derivar de la clave privada configurada en el lado que descifra (x25519.getPublicKey(privada) === x_publicada_en_el_DID).
  • Si cambias el destinatario en un encrypt-*, debes tener su clave privada en el decrypt-* correspondiente; de lo contrario el ECDH no cuadra y el desempaquetado falla con "unable to decrypt …".

Los datos de prueba por defecto usan el destinatario did:lac:openprotest:0x3c1a…, cuya clave privada (0x6c3f…) deriva exactamente en su clave pública on-chain, garantizando la compatibilidad E2E en ambas direcciones.

About

Tests to validate the interoperability among components used at digital wallets based on blockchain and DLT

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Morty Proxy This is a proxified and sanitized view of the page, visit original site.