Volver al blogFlujo de verificación Zero-Knowledge: el AV app oficial presenta una proof cifrada y el verifier de espuni la acepta en 317 ms

Verificamos una prueba Zero-Knowledge del AV app oficial: cómo funciona, y lo que todavía le falta a nuestro verifier


La pieza de privacidad del blueprint

El EU Age Verification Blueprint tiene una pieza de privacidad que está por encima de las demás: las Zero-Knowledge Proofs. Con el esquema longfellow-zk —diseñado por ingenieros de Google, con paper revisado y borrador en el IETF— una cartera puede demostrar que su titular es mayor de 18 años sin enseñar la credencial, sin una firma del emisor rastreable en claro, y sin que dos verificaciones distintas sean vinculables entre sí. El Annex A del blueprint lo dice explícitamente: la app de verificación de edad debería generar estas pruebas, y el relying party debería verificarlas.

Google Wallet las genera desde 2025; el AV app oficial europeo, desde principios de 2026. Nosotros queríamos entender la otra mitad —la del relying party, el negocio que tiene que comprobar la prueba— construyéndola. Así que construimos un verifier ZK independiente y lo probamos contra el artículo real.

Qué hicimos, y con qué se prueba

En un teléfono Android con el AV app oficial de referencia y una credencial de edad emitida por el emisor de test del propio blueprint, abrimos nuestra demo, pulsamos "Con tu teléfono", elegimos el AV app y confirmamos la presentación.

El resultado:

✓ aceptada · verify 317 ms · 353 KB
emisor: CN=Age Verification DS - 001,
        O=Age Verification Reference Implementation, C=EU

Ese emisor es lo que hace la prueba creíble. No es nuestro emisor de demo —el nuestro firma como "espuni demo AV issuer"—. Es el Document Signer de la implementación de referencia: la credencial era genuina, la cartera era la oficial, y la prueba criptográfica la comprobó nuestro código. 353 KB de proof, verificada en algo más de tres décimas de segundo. Y como cada ejecución de nuestra demo publica la proof, cualquiera puede descargarla y re-verificarla con el binario open-source, fuera de nuestra infraestructura.

La parte difícil no es la cartera

Es tentador pensar que lo complicado aquí es la cartera. No lo es: las carteras ya generan las pruebas. Lo difícil es el lado del relying party —pedir la prueba en el formato correcto, recibir la respuesta cifrada, descifrarla y verificar la prueba contra el circuito y la clave correctos.

Cada paso tiene su trampa. La petición viaja como un deviceRequest de ISO 18013-7 con un bloque zkRequest que anuncia qué circuitos aceptamos. La respuesta vuelve cifrada con HPKE (RFC 9180) a una clave efímera que generamos nosotros —así que implementamos y validamos HPKE contra los vectores de prueba oficiales del CFRG antes de confiar en un solo byte—. El "transcript" que liga la prueba a esta sesión concreta hay que reconstruirlo idéntico, byte a byte, o la verificación falla sin decirte por qué. Todo eso lo sacamos leyendo el código de la cartera de referencia y comprobando cada suposición contra el artículo real. Es trabajo de integración de verdad, y funciona.

Lo que todavía no hace nuestro verifier

Aquí es donde queremos ser precisos, porque en verificación de identidad la mitad honesta de una demo importa tanto como la que sale bien.

  • No validamos aún la Trusted List en el camino ZK. Sacamos la clave pública del emisor del certificado que viene en la propia respuesta y verificamos la prueba contra ella. Eso demuestra que la prueba es criptográficamente correcta respecto a esa clave, pero todavía no comprobamos que ese certificado encadene con un ancla de confianza de la AV Trusted List oficial. Nuestro flujo de verificación clásico (no-ZK) hace esa validación; llevarla al camino ZK es el siguiente paso, no algo ya hecho.
  • La frescura ya la comprobamos. El "ahora" contra el que el circuito valida el periodo de la credencial va firmado dentro de la prueba, así que no podemos sustituirlo por nuestro reloj — pero sí exigimos que ese instante esté dentro de una ventana de nuestra hora real, lo que rechaza una prueba con timestamp viejo (una credencial caducada afirmada como vigente) o futuro.
  • No somos los únicos. El propio ecosistema de referencia tiene un verifier ZK (el backend DC API construido sobre Multipaz), que además valida trust anchors. Otros actores del ecosistema también verifican ZK. Lo que aportamos no es ser los primeros, sino hacerlo como una capa gestionada para el relying party.

Por qué esto le importa a una plataforma

La dirección del ecosistema es unívoca: ZKP está en el Annex A del blueprint, en las especificaciones del EUDI, en el borrador de la segunda edición de ISO 18013-5, y Google ya lo despliega. Cuando las carteras empiecen a enviar pruebas ZK en volumen, cada plataforma tendrá que decidir: montar toda la infraestructura —circuitos archivados y versionados, validación de Trusted List, un servicio de verificación nativo, y seguir un esquema que ha roto compatibilidad tres veces en un año— o llamar a una API que absorbe esa complejidad.

Esa es la apuesta de espuni: una integración, todas las apps. La verificación clásica lleva tiempo en producción con validación de confianza completa; el verifier ZK está probado contra la cartera oficial y en camino de igualar esas garantías (Trusted List y frescura) antes de venderlo como listo. Si estás construyendo verificación de edad, hablemos.


Nota técnica: el esquema longfellow-zk está bajo dos revisiones de seguridad independientes cuyos informes aún no se han publicado; no lo describimos como "auditado". El verifier es real y reproducible —cada ejecución de la demo publica la prueba para que cualquiera pueda re-verificarla con el binario open-source—, con los límites que hemos detallado arriba.

Las fuentes de este artículo están enlazadas en el propio texto. Para el listado completo de fuentes primarias en las que se basa espuni, consulta referencias.