Criptografía 101: Firmas de Umbral
Este artículo forma parte de una serie más larga sobre criptografía. Si este es el primer artículo con el que te encuentras, te recomiendo comenzar desde el principio de la serie.
En el artículo anterior, aprendimos sobre polinomios y vimos un par de sus aplicaciones.
Sin embargo, esta nueva pieza de criptografía parece estar desconectada de todo lo que hemos aprendido hasta ahora. No obstante, hay formas interesantes de combinarla con conceptos previos, como las firmas digitales.
Este artículo estará dedicado exclusivamente a explicar un ejemplo de esto, donde combinaremos las técnicas de firma habituales con polinomios, para crear un interesante esquema híbrido. Trabajaremos en el contexto de curvas elípticas, y usaremos y para denotar generadores de grupo para un grupo .
Basaré ligeramente mi explicación en este artículo, con algunos ajustes con la esperanza de hacer las cosas más fáciles de entender.
Aun así, una advertencia amistosa: las firmas de umbral (threshold) no son simples de explicar. Hay muchos matices que tendremos que aclarar. Así que primero veamos un breve resumen de qué son y qué hacen, antes de sumergirnos realmente en las matemáticas detrás de ellas.
Justo los Firmantes Necesarios
Las firmas de umbral son un tipo de multifirma, lo que significa que se requieren múltiples participantes para firmar un mensaje. Pero esta vez, no es necesario que todos los participantes lo hagan.
Imagina un grupo de diez personas que tienen privilegios de administrador para una aplicación. Para realizar alguna acción sensible, requerimos al menos tres aprobaciones.
De esta manera, no tenemos que molestar a todos los administradores al mismo tiempo; basta con un subconjunto de firmantes disponibles.
Esto se siente como una mejora al esquema de multifirma que exploramos anteriormente, donde se requería que todos los firmantes participaran. Pero en realidad, lograr este resultado implica flexionar algunos músculos criptográficos adicionales.

Podemos dividir las firmas de umbral en tres pasos principales o algoritmos (como la mayoría de los esquemas de firma):
- Generación de claves (KeyGen), que es un algoritmo que genera un par de claves privada compartida y pública,
- Firma, donde un mensaje se procesa junto con la clave privada y un nonce para obtener un par ,
- Y verificación, el paso donde la firma se valida contra el mensaje y la clave pública.
Cuando trabajamos con esquemas de firma de umbral, los pasos de generación de claves y firma, que eran bastante directos en ejemplos anteriores, serán reemplazados por procesos más complejos que involucran comunicaciones entre los firmantes. Afortunadamente, la verificación sigue siendo la misma, por lo que nuestro enfoque estará en los dos primeros pasos.
Nota que la idea de requerir un umbral de firmantes es muy reminiscente de la cantidad mínima de puntos para reconstruir un polinomio mediante interpolación. Y de hecho, esto está en el núcleo de cómo funcionan las firmas de umbral.
Además, para mantener las cosas claras, debemos decir que los firmantes o participantes están ordenados. Cada uno tendrá un identificador o índice, que va desde hasta , el número total de participantes.
Nuestros objetivos están establecidos, y la introducción ha terminado. ¿Vamos a lo bueno?

Preliminares
En los protocolos de umbral, compartir información es clave. En última instancia, la capacidad de un conjunto de firmantes para compartir información es lo que les permitirá producir firmas.
Ya hemos visto que compartir un secreto se puede lograr con la Compartición de Secretos de Shamir (SSS). La idea era evaluar un polinomio en muchos valores diferentes y compartir los resultados como puntos. Y con esos puntos, podríamos interpolar el polinomio original.
Pero hay un problema. ¿Cómo puede un receptor verificar si los valores que recibe están calculados correctamente? O, en otras palabras, ¿hay alguna forma de probar que los puntos están efectivamente relacionados con el polinomio original?
Y te puedes estar preguntando por qué los valores podrían ser incorrectos. ¿Por qué alguien enviaría un valor erróneo? Hay al menos dos razones a considerar: errores en la comunicación y actividad maliciosa. Es posible que un atacante esté tratando de romper nuestro modelo de firma; no podemos esperar necesariamente que todos se comporten correctamente, y debemos tomar medidas para mitigar este posible escenario.
Para abordar esta preocupación, necesitaremos una nueva herramienta.
Compartición de Secretos Aleatoria Verificable
Lo que hacemos es pedirle al compartidor que haga un compromiso. Esto vinculará al compartidor con su información secreta (su polinomio), para que más tarde simplemente no pueda producir valores inválidos.
Esta idea es la que está detrás de la Compartición de Secretos Aleatoria Verificable (VRSS por sus siglas en inglés), una especie de reemplazo directo para SSS. Lo que queremos hacer es comprometernos con los coeficientes de nuestro polinomio, y para esto, necesitaremos no uno, sino dos de ellos:
¿Por qué, preguntas? Porque los compromisos necesitan ser ocultadores. No queremos revelar los coeficientes, así que sus compromisos deben estar cegados. ¡Los coeficientes del segundo polinomio son de hecho estos factores de cegado!
Así que usando nuestros generadores de grupo, cada participante calcula y transmite los valores de compromiso , uno para cada uno de los coeficientes en los polinomios:
¡Genial! Ahora todo lo que queda es compartir. Y para esto, todos necesitarán evaluar su polinomio. Como cada participante tiene un índice , podemos elegir evaluar la parte para un jugador objetivo en su índice, así .
Esto significa que los individuos obtendrán y de algún otro participante .

Y al recibir estos valores, el participante puede validarlos de la siguiente manera:
¡Eso es básicamente todo! Ahora tenemos un mecanismo para compartir información secreta, de una manera que es verificable. Con esta herramienta, podemos comenzar a construir nuestro esquema de firma.
Generación de Claves
Dado que hay múltiples partes involucradas en las firmas de umbral, cada participante tendrá un entero como su parte (o pieza) de una clave privada.
Sin embargo, esto no es un entero elegido al azar, como de costumbre. En cambio, los participantes se involucran en un proceso donde interactúan entre sí, produciendo finalmente su parte de la clave privada. Este tipo de protocolos se llaman algoritmos de Generación de Claves Distribuida (DKG).
Y podemos usar VRSS para construir nuestro algoritmo. ¡Qué conveniente!

La idea es que cada participante recibirá una parte de cada uno de sus pares, y usarán estos valores verificados para calcular su parte de la clave privada:
Es posible que algunos valores no pasen la verificación; algunas partes pueden ser descalificadas en este caso. Es por eso que VRSS es importante.
Aún así, seguiremos el camino feliz para mantener las cosas relativamente simples.
El resultado del DKG es una pieza de una clave privada compartida, . Ninguna de las partes involucradas en este protocolo conoce este valor, solo sus piezas.

Finalmente, necesitamos una clave pública. Y para esto, cada participante transmite su coeficiente secreto independiente o cero, . Este secreto no puede ser revelado como tal, y por lo tanto, se transmite como una parte de clave pública:
Creo que es bastante extraño ver que la parte de la clave pública se calcule así. ¡Apuesto a que esperabas ver ahí dentro!
Hay una buena razón para que no se use de esa manera. Volveremos a esta afirmación más tarde, porque necesitaremos definir un par de cosas para entender lo que realmente está sucediendo.

Una vez que todas las partes son públicas, cada participante puede calcular la clave pública global de forma independiente:
Para concluir, aquí va un breve resumen de este paso:
La generación de claves implica que las partes se comuniquen entre sí, generando piezas de una clave privada compartida. Ningún jugador conoce la clave privada compartida. También se calcula una clave pública asociada con el secreto compartido.
Con todas las claves en su lugar, todo lo que queda es firmar.
Firmando un Mensaje
Lo primero que necesitaremos es un mensaje para firmar. Pero esto no es trivial, porque todos necesitan estar de acuerdo en un mensaje. No cubriremos cómo sucede esto; simplemente asumamos que todos conocen el mensaje que se está firmando.
En ECDSA, un firmante normalmente elegiría un nonce aleatorio , y calcularía un desafío en consecuencia, produciendo una firma como esta:
Pero como ya hemos visto, así no es como tiende a operar la criptografía de umbral. En cambio, un grupo de firmantes se comunicará entre sí para producir una firma. Y lo primero que necesitarán es un nonce.
Afortunadamente, ya tenemos una herramienta para generar un valor distribuido: ¡DKG! Digamos que los firmantes ejecutan una ronda de DKG, obteniendo una parte , y un desafío asociado:
Para el cálculo de la firma, todos utilizarán la coordenada x de , que denotaremos como .
Construyendo la Firma
Como probablemente puedas adivinar a estas alturas, la generación de la firma también se realiza en partes. Y nuevamente, la idea es que solo cuando está disponible un cierto número de partes, podremos producir una firma válida a través de la agregación de estas partes calculadas independientemente.
Nuestro requisito es el siguiente: las partes de firma deberían interpolar a la firma final , que debería pasar la verificación cuando se usa la clave pública . Lo más natural que podemos hacer aquí es adaptar la expresión de para que use partes de sus componentes en su lugar:
¿Pero esto tiene sentido? Aquí, tenemos una suma, multiplicaciones e incluso inversos modulares. No parece obvio asumir que esto funcionará así sin más.
Parece justo que examinemos esta expresión y verifiquemos que funciona correctamente. Y realmente, no es tan complicado como podrías imaginar. Tomémoslo con calma, paso a paso.

Interpolando Sumas
Para empezar, digamos que tenemos dos polinomios y . Si los evaluamos en diferentes valores , obtenemos conjuntos de puntos de la forma y . Por conveniencia, denotémoslos como y :
Sabemos que cualquier subconjunto de t puntos de estos puntos nos permite interpolar los polinomios originales. Y si definimos el término independiente de como , podríamos escribir que:
Como recordatorio, en el contexto de compartir secretos, generalmente estamos interesados en el término independiente. Es por eso que cuando decimos que interpolamos algunos valores, podemos referirnos a la salida como solo este coeficiente independiente o cero, y no a todo el polinomio. ¡Pero realmente, la salida completa es todo el polinomio!
De manera similar, asumamos que tenemos puntos donde tiene un término independiente , y por lo tanto:
Ahora, ¿qué sucede si sumamos los polinomios y ?

Podemos sumar término por término, y terminamos con un polinomio con el mismo grado que los originales (), pero donde el término independiente es . Además, dado que , entonces todos los puntos que interpolan a , que son , se pueden calcular como . Y así:
¡Y eso es genial! Esto significa que esencialmente podemos sumar partes de un polinomio, y al interpolar, obtendremos como resultado la suma de los secretos compartidos. ¡Estupendo!
Ahora podemos analizar el cálculo de la clave pública. Recuerda que la parte se calcula como la suma de los valores .
Debido a esto, es esencialmente una parte de una suma de polinomios, cuyo término independiente será la suma de todos los términos . Lo que significa que ¡el resultado de interpolar todos los producirá esa suma!

Y esa es la razón por la cual la clave pública se calcula de la manera en que lo hace. ¡Todo cuadra!
Interpolando Productos
Los productos son ligeramente más complicados. Cuando multiplicamos y , el polinomio resultante tendrá el doble del grado. Y debido a esto, necesitamos el doble de puntos para la interpolación:
Y esto no es realmente óptimo, porque ahora necesitamos más valores para interpolar, lo que significa más comunicaciones entre pares.
Independientemente de este inconveniente, la buena noticia es que esto se comporta como esperamos: cuando multiplicamos , los términos independientes también se multiplican, ¡y nuestros valores también interpolarán a !
También vale la pena mencionar: cuando multiplicamos partes por una constante, la interpolación resultante también se multiplicará por ella:

Así que si tomamos nuestras partes como , entonces la interpolación producirá . Bastante sencillo, ¿verdad?

Muy bien, solo nos queda un caso más por analizar. El dolor y el sufrimiento terminarán pronto, lo prometo.
Interpolando Inversos
Realmente, todo lo que nos falta es manejar ese maldito inverso modular. Lo que queremos es producir valores que, cuando se interpolen, produzcan . Esto llevará un par de pasos. Sí.
- En primer lugar, todos ejecutarán una ronda de VRSS para producir partes . Por supuesto, estas partes interpolan así:
- A continuación, cada participante calculará y transmitirá:
- Dado que es el resultado de una multiplicación, al recibir partes, cualquiera podría interpolar este valor:
- Finalmente, cada participante calcula de esta manera:
¿Cómo funciona esta magia, te preguntas? Bueno, considera esto: actúa como un término constante al interpolar los valores . Y debido a esto, terminamos con:
¡Y como por arte de magia, hemos construido valores que interpolan al inverso de !

¡Eso es todo lo que vamos a necesitar! Revisemos nuestra calculación de firma con nuestras conclusiones recién encontradas.
Volviendo a la Firma
Si pudiéramos reconstruir cada secreto compartido, entonces el cálculo de la firma ocurriría como en el ECDSA estándar:
Pero en la práctica, no queremos que eso suceda, y solo tenemos partes. Así que nos preguntamos si esto:
también se interpola a . Y la respuesta es un rotundo sí, porque solo estamos tratando con sumas, productos e inversos, y ya sabemos cómo se comportan estos.
Quizás el único problema aquí es que como estamos tratando con un producto de partes (el término ), necesitaremos como partes para interpolar. Pero dejando eso de lado, ¡estamos seguros de que interpolar los valores producirá el valor esperado de !
Diferentes protocolos pueden hacer uso de varias técnicas para tratar de mitigar la necesidad de puntos adicionales para la interpolación, e idealmente, querríamos mantener ese número lo más cercano posible a . Además, cuantos menos pasos de comunicación se necesiten, mejor.
Para finalizar, cuando cada participante calcula su parte , simplemente la transmite. Y cuando hay suficientes partes disponibles, cualquiera puede interpolar, producir y generar .
¡Y ahí lo tienes! Simple, ¿verdad?

Estoy bromeando, por supuesto: esto es cualquier cosa menos simple. Pero la idea general es lo que realmente es la conclusión clave:
Durante la firma, las partes se comunican nuevamente entre sí, generando piezas de una firma compartida. Cuando hay suficientes piezas disponibles, se puede construir la firma final; con menos piezas de las necesarias, simplemente no es posible.
La verificación ocurre como de costumbre, porque la salida de la firma es simplemente el par .
Resumen
Creo que este es el artículo más técnicamente cargado que he escrito hasta ahora. Traté de mantenerlo lo más simple posible, pero hay algunas cosas que simplemente no podemos evitar explicar. Al menos, espero que esto arroje algo de luz sobre algunos aspectos que, según mi experiencia, no suelen explicarse en detalle.
🔥 Importante: En realidad hay una vulnerabilidad bastante grande en el proceso que describí, donde las partes de la clave privada se filtran al compartir .
Esto se aborda en el artículo que usé como guía, y la solución es bastante simple. Así que, por favor, no uses este artículo para construir tus firmas de umbral, ¡y tal vez consulta el artículo real en su lugar!
Diseñar este tipo de protocolos de Computación Multi-Parte, donde hay comunicación entre las partes, requiere considerar que puede existir actividad maliciosa. Así que en general, estos protocolos están llenos de rondas de descalificación, pruebas de corrección, tal vez incluso algo de cifrado y otras cosas divertidas. Realmente, son una consolidación de muchas técnicas diferentes, y tienden a ser complejos.
La Computación Multi-Parte es, en general, bastante compleja.
Este es realmente el esquema más simple que pude encontrar en términos de qué herramientas se necesitan, y se presenta principalmente con la esperanza de ilustrar los componentes principales de estas técnicas. Y esto significa que cuantas más herramientas podamos acumular, más fácil será elaborar esquemas más complejos.
Dicho esto, nuestro viaje continuará presentando otra herramienta extremadamente poderosa, que desbloqueará nuevos tipos de funcionalidad: emparejamientos. ¡Nos vemos en el próximo artículo!