Bitcoin y otros blockchains — como Liquid, de Blockstream — emplean el algoritmo de firma digital de curva elíptica (ECDSA) para verificar la propiedad y las transferencias de monedas almacenadas en el sistema. Esta fue una decisión técnica que, aparentemente, se tomó en 2008 sobre la base de los sistemas de firma digital libres de patente y de uso generalizado disponibles en ese momento. No obstante, el ECDSA posee algunas limitaciones técnicas graves. Para ser precisos, las multifirmas y las firmas threshold–aportadas no por una sola persona, sino por un quórum de partes independientes entre sí– son muy difíciles de producir con el ECDSA. Las firmas del ECDSA tienen una estructura algebraica compleja que las hace inflexibles y dificulta trabajar con ellas, por lo cual los desarrolladores se ven obligados a usar el lenguaje Script de Bitcoin para aplicaciones como los cross-chain atomic swaps (“canjes atómicos de cadenas cruzadas”) o para Lightning, que podrían implementarse de manera más compacta y privada mediante un esquema de firmas más flexible.
Sin embargo, si bien la tecnología más avanzada en cuanto a firmas digitales ha progresado mucho desde 2008, los esquemas de firma alternativos que se describen en la bibliografía dejan de lado muchos requisitos prácticos necesarios para el uso en la vida real. En particular, suelen dar por sentado que los firmantes controlan totalmente cómo y cuándo se generan sus claves; que siempre pueden generarlas de manera perfectamente aleatoria; y que tienen acceso a una memoria persistente, confiable y segura. En la práctica, los usuarios de Bitcoin suelen tener acceso restringido a sus propias claves, poco control en la generación de claves privadas y ningún control sobre uso que hacen los terceros de las direcciones que estos generan. Para responder a esas preocupaciones, inauguramos una iniciativa orientada a diseñar un nuevo esquema de firmas y un esfuerzo de ingeniería práctica significativo a fin de implementarlo de manera robusta y antifrágil.
Introducción
Durante la primera mitad del año pasado, en colaboración con Yannick Seurin y Gregory Maxwell, el criptógrafo de Blockstream Pieter Wuille y yo publicamos un nuevo esquema de multifirma llamado MuSig. Este esquema de multifirma fue capaz de brindar seguridad comprobable, incluso al enfrentarse a subconjuntos de firmantes malintencionados en connivencia mutua, y produjo firmas indistinguibles de las firmas Schnorr individuales comunes.
Desde entonces, nos estuvimos dedicando a transformar el trabajo académico sobre MuSig en código utilizable, y esta semana fusionamos ese código con secp256k1-zkp, un fork de secp256k1, la biblioteca criptográfica de alta seguridad empleada por Bitcoin Core, que hemos ampliado a fin de añadir recursos de Confidential Transaction para Elements y Liquid.
Dado que la comunidad Bitcoin está explorando el uso de las firmas Schnorr en Bitcoin, nosotros esperamos que, a la larga, nuestro código se fusione con el secp256k1 de la biblioteca upstream (ascendente) empleada por Bitcoin Core y muchos otros proyectos.
Nuestro código produce firmas acordes a la Propuesta de mejora de Bitcoin con firmas Schnorr (BIP-Schnorr), y también es capaz de producir firmas “adaptador”, que podrían posibilitar la ejecución de Lightning con scriptless script (protocolos sin scripts).
¿Por qué usar MuSig?
Como comentamos el año pasado, en la bibliografía criptográfica se presentan muchos esquemas de multifirma, y es razonable preguntarse por qué necesitábamos desarrollar el nuestro. La respuesta breve es que teníamos dos requisitos que no cumplía ninguno de los esquemas disponibles:
- Firmas cortas y de tamaño constante cuya apariencia ante los verificadores no variase, independientemente del conjunto de firmantes. En un sistema blockchain, la eficiencia de la verificación es la consideración más importante, y no tiene sentido abrumar a los verificadores con más detalles sobre la composición de los firmantes de los que son estrictamente necesarios para prevenir los robos. Un beneficio extra de las firmas MuSig es que incrementan la privacidad, dado que ocultan la política exacta del firmante.
- Seguridad comprobable en el modelo de clave pública simple. Esto quiere decir que los firmantes tienen flexibilidad total para participar de una transacción multifirma empleando pares de claves comunes, sin tener que proveer información adicional sobre la manera específica en que se produjeron, o se controlan, esas claves. En un contexto Bitcoin, puede resultar difícil aportar información sobre la generación de claves, dado que los firmantes tienen políticas de gestión de claves variadas y restrictivas. Además, la dependencia en los detalles de generación de claves puede llegar a interactuar de manera problemática con Taproot, una extensión propuesta para Bitcoin en la cual las claves de firma públicas incorporarían, semántica invisible adicional.
Por otro lado, desde que anunciamos MuSig, nos enteramos de que muchos esquemas de firma disponibles, incluida una versión anterior de MuSig que no fue publicada, ¡en realidad son inseguros! Vamos a profundizar sobre este tema en una publicación futura, pero por ahora basta decir que teníamos mucho trabajo por delante a fin de desarrollar un esquema de multifirma compatible con Bitcoin y Liquid.
Problemas y desarrollo seguro de API
Como todas las descripciones matemáticas de los protocolos de multifirma, MuSig, tal como se publicó, da por sentado que, durante todo el proceso de firma, los participantes tienen acceso a una memoria persistente que se actualiza fácilmente y que los atacantes no puedan “resetear” a estados previos. También se presume que los firmantes tienen acceso a fuentes de aleatoriedad que producen resultados uniformes al punto de ser indistinguibles. Lamentablemente, el mundo real no es tan sencillo, así que nos esforzamos mucho por diseñar una API que se pueda usar en una gran variedad de escenarios sin la posibilidad de hardware limitado ni presunciones implícitas que conduzcan a la pérdida de material privado clave.
Las firmas MuSig, al igual que las Schnorr y las del ECDSA, emplean para su construcción un valor secreto generado para la ocasión (nonce) que se debe producir de manera aleatoria y uniforme. Cualquier desviación de esa uniformidad, incluso en un solo bit, puede provocar la pérdida de claves privadas y el robo de fondos.
Nuestro objetivo de diseño principal fue crear una API resistente al mal uso y libre de “sorpresas”, y que no alentase patrones de uso peligrosos, incluso en ambientes restringidos.
Aleatoriedad uniforme
En el caso de las firmas individuales, el enfoque estándar que se aplica para lograr nonces de aleatoriedad uniforme es simple: se toman algunos datos privados y el mensaje por firmar y se los hace pasar por una función de hash criptográfico para obtener un valor uniformemente aleatorio e independiente para cada mensaje que haya que firmar.
No obstante, con las multifirmas, esa solución sencilla y robusta se vuelve riesgosa. Un firmante malintencionado podría solicitar dos multifirmas en el mismo mensaje y alterar ligeramente su propia contribución a la firma en la segunda iteración. Si el primer firmante elige su nonce mediante el hashing de información privada que acompaña al mensaje, va a terminar usando el mismo nonce en dos firmas muy diferentes; en definitiva, se trata del mismo modo de fallo que permitió que hackearan la PS3. Desafortunadamente, a diferencia del caso del firmante único, no hay una solución sencilla, porque cada firmante debe elegir su nonce antes de conocer todos los detalles de la firma que deberá producir.
Una solución tradicional a ese problema, que se usaba antes de que el hashing se volviese popular, es emplear algún hardware generador de números aleatorios. Por desgracia, dichos dispositivos son costosos y están sujetos a sesgos ambientales u otras influencias externas; pero sobre todo, no hay manera de verificar si funcionan correctamente.
A propósito de esa última afirmación sobre la verificación existen algunas soluciones creativas que vamos a abordar en una publicación futura. Por ahora, nosotros elegimos pedirles a los usuarios de API que aporten un “ID de sesión” nuevo para cada sesión de firma. Los nonces se producen mediante el hashing de los datos privados del firmante, el conjunto de firmantes, el mensaje por firmar y, por último, ese ingreso específico para la sesión. Los usuarios que tengan acceso a un generador de números aleatorios pueden usarlo para producir el ID de sesión; quienes tengan acceso a una memoria persistente pueden usar un simple contador.
No estamos conformes con la necesidad de solicitar números aleatorios o memoria persistente, pero confiamos en que nuestra investigación en curso pronto encontrará una solución verdaderamente robusta.
Ataques por repetición
Incluso si se cuenta con una fuente de aleatoriedad sólida, sigue siendo posible extraerle claves privadas a un participante de una multifirma; alcanza con repetir un protocolo de firma desde algún punto durante el transcurso del proceso. A ese tipo de ataque se lo denomina “ataque por repetición” y puede atentar contra un firmante que opere dentro de una máquina virtual reiniciable o bien alguna que permita interrumpir la firma y restaurar la sesión a partir de un estado serializable. Incluso puede ocurrir accidentalmente sin que exista un atacante activo, por ejemplo si se ponen en marcha dos máquinas virtuales clonadas desde el mismo estado, o si se ejecuta un código en una base de datos distribuida que ya no está sincronizada.
Para ser precisos, si un firmante contribuye a una multifirma y se reinicia el proceso de firma desde un punto posterior a la elección de su nonce, es posible modificar las contribuciones de los otros firmantes a la firma total a fin de llevar a cabo el mismo ataque que en la sección anterior.
Ese tipo de ataques no ocurre con las firmas únicas porque se producen en un solo paso, sin ningún estado intermedio a partir del cual reiniciar el proceso. Estos desafíos adicionales son específicos de los protocolos criptográficos de rondas múltiples.
Sin mecanismos nuevos —tema que estamos investigando activamente— no tenemos manera de proteger a los usuarios que firman desde máquinas virtuales. Cabe señalar que emplear máquinas virtuales de por sí implica menor seguridad, porque una máquina que un atacante puede reiniciar será propensa a permitir que ese mismo atacante le pueda extraer información privada directamente.
Para proteger a los firmantes que serialicen estados “caducados” y reinicien desde allí, nuestra API es incompatible con la serialización de las sesiones de firma.
Por eso, en la práctica, los usuarios de nuestro código que quieran asegurarse sesiones de firma que sobrevivan a reinicios o interrupciones causados por el suministro eléctrico —un objetivo razonable para el hardware de monedero— deben preservar la seguridad de su memoria persistente. Si dichos monederos quieren permitir múltiples sesiones de firma paralelas, van a necesitar memoria persistente adicional para cada sesión paralela.
De nuevo, estamos convencidos de que podemos eliminar esa limitación mediante los enfoques que estamos investigando activamente.
Conclusiones y lo que se viene
La anterior discusión da por sentado que los protocolos que involucran a múltiples participantes conllevan desafíos nuevos y considerablemente más difíciles que los protocolos que involucran a uno solo. En términos de complejidad matemática, MuSig es mucho más simple que, por ejemplo, Bulletproofs. Pero, en términos de complejidad de implementación, MuSig ha exigido un mayor esfuerzo y más soluciones intermedias que apunten al equilibrio entre antifragilidad y flexibilidad API.
Esta publicación solo describió las multifirmas, es decir, aquellas en las que un número n de firmantes colabora para producir una sola firma. En una publicación futura, vamos a abordar las firmas “umbral” (threshold), un concepto relacionado según el cual cualquier subconjunto de los firmantes, siempre y cuando representen la cantidad suficiente, puede producir firmas sin necesidad de que contribuya el grupo entero.
En otras publicaciones futuras, también vamos a comentar algunas técnicas para incrementar la seguridad en la producción de aleatoriedad de los nonces y para lograr que sea más verificable. En particular, el uso de una técnica llamada sign-to-contract (“firmar para contratar”) le posibilita al equipo host eliminar todo sesgo posible del generador de números aleatorios de un hardware de monedero poco confiable.
Si avanzamos un poco más y aprovechamos el potencial de las pruebas de conocimiento nulo, debería ser posible eliminar los riesgos que plantean la aleatoriedad sesgada y los ataques por repetición, suprimir el requisito de memoria persistente y reducir el protocolo MuSig de tres rondas a dos. Estamos entusiasmados por esa posibilidad y ansiamos compartir nuestros resultados a medida que se van desarrollando.
Nuestro código está disponible públicamente en GitHub, ¡y los invitamos a jugar con él y hacernos devoluciones!