Este artículo explica cómo funciona el ECDSA (Elliptic Curve Digital Signature Algorithm), así como por qué funciona.
En este tutorial, “redescubriremos” gradualmente el algoritmo desde sus principios fundamentales.
Requisitos previos
Asumimos conocimientos previos sobre
Repaso de las firmas digitales
Un algoritmo de firma digital es un protocolo que permite a un usuario otorgar un sello de aprobación criptográfico a una cadena de texto.
Dicho usuario tiene una clave pública (un par correspondiente a un punto en la curva elíptica). El punto fue generado a partir de una clave privada, la cual es un valor escalar secreto.
Dada una clave pública, un mensaje y una firma, otro usuario puede verificar que la firma fue producida por alguien que posee la clave privada correspondiente a esa clave pública, y que solo esa persona podría haber generado la firma.
Por ejemplo, las criptomonedas usan firmas digitales para verificar que un usuario realmente quería realizar una transacción. Una usuaria, Alice, puede enviar un mensaje a la blockchain diciendo “Envía 10 de mis tokens a Bob”. La red blockchain debe estar segura de que Alice realmente dio su sello de aprobación a ese mensaje y que Bob no está robando las monedas de Alice.
La firma producida debe ser específica para el mensaje y no ser aceptada para otros mensajes. De lo contrario, un atacante podría usar una firma para hacer que la víctima apruebe una transacción maliciosa. Si la firma para el mensaje “transferir 10 monedas a Bob” también pudiera usarse como un sello de aprobación para el mensaje “transferir 10 monedas a Eve”, entonces Eve podría usar la firma de la transferencia a Bob para robarle monedas a Alice.
Llamamos a la persona que genera la firma el probador o firmante y a la persona que comprueba si la tupla (Clave Pública, mensaje, firma) es válida el verificador.
En esencia, una firma digital de curva elíptica es una prueba de conocimiento de una clave privada. Específicamente, es una prueba de conocimiento del logaritmo discreto de un punto en la curva elíptica.
Para los lectores que ya han visto el algoritmo ECDSA:
pero no saben de dónde provienen las fórmulas, aprenderán el razonamiento detrás de cómo se derivaron.
Terminología y notación
Nos referimos a los puntos de la curva elíptica con letras mayúsculas, como . El generador será denominado . El valor de es conocido por todas las partes involucradas. La multiplicación entre un escalar y un punto se escribe como , lo que significa que se suma a sí mismo veces.
Dados dos puntos y , decimos que alguien conoce la relación de logaritmo discreto entre y si conoce tal que , o alternativamente, tal que .
Decimos que alguien conoce el logaritmo discreto de un punto si conoce un escalar tal que .
Nos referimos al logaritmo discreto de un punto con la versión en minúscula de la misma letra, a menos que se indique lo contrario.
Toda la aritmética se realiza en un campo finito, pero omitimos la notación por brevedad.
Intentos fallidos de firmas digitales
Enfoque ingenuo: revelar la clave privada
Queremos demostrar que conocemos la clave privada para la clave pública , donde .
El enfoque ingenuo es revelar la clave privada al verificador para que este pueda comprobar que, en efecto, .
Por supuesto, esto anula el propósito de mantener en privado.
Demostrar el conocimiento de la relación de logaritmo discreto entre y
Demostrar que conocemos es equivalente a demostrar que conocemos la relación de logaritmo discreto entre el generador y la clave pública . Es decir, se suma a sí mismo veces para obtener . Revelar la relación de logaritmo discreto entre y revela la clave privada.
Por lo tanto, en lugar de demostrar que conocemos la relación de logaritmo discreto entre el generador y la clave pública , podemos demostrar que conocemos la relación de logaritmo discreto entre y , donde es elegido por el probador y cuyo logaritmo discreto es desconocido para el verificador. Si el logaritmo discreto de es desconocido para el verificador, entonces este no puede derivar la clave privada, dada la relación de logaritmo discreto entre y .
Sea el número de veces que necesita sumarse a sí mismo para producir ; en otras palabras, . Si el probador conoce el logaritmo discreto de y , como y respectivamente, puede calcular fácilmente como .
se derivó de la siguiente manera:
Luego, el verificador puede comprobar que, en efecto, , y esto demuestra que el probador conoce la relación de logaritmo discreto entre y .
Sin embargo, este enfoque falla porque un probador malicioso puede tomar el de otra persona (del cual no conoce la clave privada), elegir un valor aleatorio , calcular y luego enviar al verificador.
Por lo tanto, el hecho de que el probador demuestre que conoce la relación de logaritmo discreto entre y no es suficiente para demostrar que conoce el logaritmo discreto de en sí. Solo demuestra que multiplicó algún por para producir .
Prevenir la elección aleatoria de
El enfoque anterior fracasó porque el probador puede presentar sin conocer realmente el logaritmo discreto de en sí.
¿Qué pasaría si, para establecer que el probador conoce el logaritmo discreto de , el probador revela (el logaritmo discreto de ) al verificador?
En este caso, el probador debe revelar tanto como al verificador. Luego, demuestra que el probador sabe cuántas veces debe sumarse a sí mismo para obtener , y demuestra que el probador conoce el logaritmo discreto de . Presentar demuestra que no fue seleccionado eligiendo un valor y sumando a sí mismo veces.
En ese caso, el verificador comprueba que
Con estas comprobaciones, el probador no puede crear o producir una relación válida sin conocer el logaritmo discreto de y el logaritmo discreto de .
Sin embargo, el verificador puede calcular la clave privada basándose en esta información.
Así que tenemos un dilema: si revelamos el logaritmo discreto de , el verificador puede calcular la clave privada. Si el probador puede elegir a voluntad, entonces puede crear firmas para claves públicas de las que no conoce el logaritmo discreto.
No podemos permitir que el verificador pueda calcular la clave privada, por lo que revelar está fuera de discusión.
Por lo tanto, nuestra solución debe incluir que el probador elija , pero debemos evitar que el probador pueda elegir de manera completamente arbitraria (como eligiendo un arbitrario y produciendo ).
En adelante, el probador debe demostrar que conoce el logaritmo discreto de y el logaritmo discreto de , pero sin revelar ninguno de los logaritmos discretos o .
Resulta que demostrar que conocemos el logaritmo discreto de dos puntos sin revelarlos es más fácil que demostrar que conocemos el logaritmo discreto de un punto sin revelar ese único logaritmo discreto.
Si el probador conoce los logaritmos discretos de y , entonces ¿qué más debe saber?
La idea clave es esta:
Si el probador conoce los logaritmos discretos de y , entonces no solo debe conocer tal que , sino que también debe conocer tal que , donde es un valor arbitrario elegido por el verificador y es un valor que el probador no puede predecir.
En otras palabras, vamos a empezar a incluir “desplazamientos” aditivos y multiplicativos en la relación entre y , y si el probador conoce los logaritmos discretos de y , entonces debe poder calcular tal que , siempre y cuando el probador sepa de cuánto es el desplazamiento .
Un desplazamiento aditivo previene las falsificaciones
Para evitar que el probador elija de modo que sea una simple multiplicación escalar de , el verificador puede inyectar un desplazamiento aditivo .
Después de que el probador envía y al verificador, el verificador responde con (un valor escalar público). El probador debe entonces producir tal que (ya no necesitamos la notación ).
Ahora el probador no controla la relación de logaritmo discreto entre y ; en su lugar, debe demostrar que conoce la relación de logaritmo discreto entre y .
Dado que el verificador ya está en posesión de y , el probador no puede simplemente elegir un aleatorio y generar tal que . El probador debe demostrar que conoce la relación de logaritmo discreto entre el nuevo punto y el original.
Si el probador conoce los logaritmos discretos de y , entonces es fácil de calcular como:
Específicamente, el probador resolvió
Resumamos el algoritmo interactivo que acabamos de crear:
- Asumimos que ambas partes están de acuerdo en (y en el grupo de la curva elíptica al que pertenece).
- El probador publica su clave pública y desea demostrar que conoce la clave privada .
- El verificador responde con un escalar .
- El probador elige un aleatorio y calcula .
- El probador calcula y envía al verificador
- El verificador comprueba si
La última comprobación funciona porque, internamente, la cancela los logaritmos discretos de y :
Defensa contra firmas falsificadas
Veamos cómo un probador malicioso podría intentar calcular sin conocer los logaritmos discretos de y , y por qué el intento falla.
El probador inventa un valor , genera y envía al verificador. Sea un valor inventado por el probador malicioso, y no el logaritmo discreto de .
El verificador responde con el escalar .
El probador malicioso debe ahora resolver la ecuación
Dado que el probador sabe que (pero no conoce ), puede intentar dos sustituciones:
- .
Sin embargo, de cualquier forma en que el probador sustituya o , se encuentra con el problema de que no puede resolver porque desconoce el logaritmo discreto de .
Sustituir
Aquí el probador sustituye la :
No podemos dividir puntos de la curva elíptica, así que para calcular la fracción anterior, necesitamos conocer :
Pero el probador no conoce , por lo que no puede calcular .
Sustituir
Ahora el probador malicioso intenta sustituir en lugar de , pero se encuentra con el problema de que no conoce el logaritmo discreto de :
Ahora el probador malicioso necesita conocer el logaritmo discreto de para calcular la fórmula anterior. Sin embargo, el probador no conoce el logaritmo discreto de ; solo sabe que es , pero no conoce . Por lo tanto, el probador malicioso no puede producir .
Por qué el desplazamiento debe ser aditivo
¿Cómo supimos que debíamos sumar a en lugar de multiplicar y pedirle al probador que propusiera un tal que ?
El problema es que, con un desplazamiento multiplicativo, el probador puede cancelar los factores de los logaritmos discretos de y .
A modo de resumen: un probador malicioso no conoce , el logaritmo discreto de , ni , el logaritmo discreto de . Sin embargo, sí sabe que es veces mayor que , donde es un número que inventó y no el verdadero logaritmo discreto de .
Si el verificador presenta y le pide al probador que proponga un tal que , entonces el probador simplemente calcula .
Esto superará la prueba del verificador:
Por lo tanto, el protocolo debe incluir un desplazamiento aditivo para que no haya una relación multiplicativa simple entre el primer punto () y el segundo punto .
Haciendo que nuestro protocolo sea no interactivo
Desafortunadamente, nuestra solución requiere que el verificador envíe después de recibir y , lo que exige que el probador y el verificador interactúen entre sí.
Si queremos hacer que nuestro protocolo no sea interactivo, entonces no puede provenir del verificador y el probador debe producirlo.
Si el probador conoce de antemano (lo cual es necesario si la prueba no es interactiva), entonces volvemos a nuestro problema original.
En este escenario, un probador malicioso puede elegir al azar, luego elegir al azar, y calcular
y luego enviar al verificador. El verificador no sabe si fue generado por un aleatorio.
Para evitar que el probador elija y la interacción con un verificador, podemos simular que el verificador elige simplemente aplicando una función hash a :
Dado que el verificador elegirá de forma aleatoria de todos modos, el probador puede calcular de manera pseudoaleatoria usando una función hash. Esta técnica se llama Fiat-Shamir Transform.
Una prueba funcional de conocimiento de una clave privada
El algoritmo es el siguiente:
- El probador elige un escalar aleatorio y calcula .
- El probador calcula .
- El probador calcula .
- El probador envía al verificador.
- El verificador calcula .
- El verificador comprueba que .
La razón por la que esto es seguro es porque un probador malicioso no puede ejecutar el paso 3 sin conocer el logaritmo discreto de y el logaritmo discreto de .
La falsificación es imposible
Supongamos que un probador malicioso elige y genera . Aquí, el probador conoce el logaritmo discreto de , pero no el logaritmo discreto de . Después de aplicar el hash a para obtener , el probador malicioso necesita calcular tal que . Sin embargo, no puede hacerlo porque desconoce el logaritmo discreto de , ya que el logaritmo discreto de es desconocido para él.
Así, hemos demostrado un algoritmo seguro para probar el conocimiento de una clave privada sin revelar la clave privada.
El algoritmo pre-ECDSA
El algoritmo que presentamos anteriormente simplemente prueba el conocimiento de una clave privada, no permite al firmante firmar un mensaje.
En ECDSA, una firma es una prueba de que conocemos el logaritmo discreto del punto generado al desplazar aditivamente nuestra clave pública por , donde es el hash del mensaje, y el logaritmo discreto de otro punto .
Sin embargo, el enfoque actual introduce una vulnerabilidad de seguridad, ya que el probador puede primero calcular y luego elegir un aleatorio y calcular . Ahora vuelve a estar completamente bajo el control del probador, por lo que no podemos confiar en que el probador realmente conozca el logaritmo discreto .
Para asegurar esto, necesitamos introducir nuevamente un poco de pseudoaleatoriedad adicional que esté fuera del control del probador.
Añadiendo la aleatoriedad
Sea un valor aleatorio derivado de aplicar un hash a . Si multiplicamos por de la siguiente manera, obtenemos un algoritmo de firma digital seguro. Al principio, esto parece crear una dependencia circular, pero en realidad es fundamental para hacer que el algoritmo sea seguro. El algoritmo para que el probador demuestre que conoce el logaritmo discreto de y es:
- El probador elige un aleatorio y publica su clave pública como .
- El probador elige un escalar aleatorio y calcula .
- El probador elige una cadena de mensaje y calcula .
- El probador calcula .
- El probador calcula .
- El probador envía al verificador
- El verificador calcula
- El verificador calcula
- El verificador comprueba que
Aunque el probador puede elegir un arbitrario, no puede controlar el logaritmo discreto del punto porque se genera aplicando un hash a . Por lo tanto, para calcular , la relación de logaritmo discreto entre y , el probador debe conocer realmente el logaritmo discreto de .
Optimizando el algoritmo pre-ECDSA
Optimización 1: Aplicar hash a es innecesario porque la multiplicación en curvas elípticas ya se comporta como una función hash
Recordemos lo que intentaba lograr. La fórmula de verificación final es:
La idea es que queremos que dependa de para que no esté completamente determinado por el valor , sobre el cual el probador tiene control total. Para calcular , el probador debe conocer el logaritmo discreto de .
Queremos una alternativa más económica al uso del hash que haga que dependa de .
Tenga en cuenta que si depende de , entonces también depende del logaritmo discreto de , , ya que determina completamente a .
Ahora considere que, si se nos da un valor escalar , el punto de la curva elíptica creado por parece aleatorio. No existe una relación aparente entre y . Esta falta de una relación aparente es lo que hace que calcular el logaritmo discreto sea difícil.
Las funciones hash también tienen una salida que parece aleatoria: no hay una relación aparente entre la entrada y la salida de un hash.
Por lo tanto, podemos tratar de la misma manera que trataríamos . Es decir, el propio se comporta como el resultado de un hash. Sin embargo, no podemos establecer que ya que no son del mismo tipo. En su lugar, simplemente podemos tomar el valor de y hacer que sea (recuerde, es un punto ).
Ahora es esencialmente el hash de , y dado que depende directamente de , se comporta como un hash de .
Por lo tanto, en lugar de calcular , establecemos (es decir, el valor del punto ).
Optimización 2: No necesitamos enviar el punto completo, solo el valor de
Los puntos de una curva elíptica constan de dos escalares: el valor y el valor del punto. Cada valor tiene solo dos valores posibles, las soluciones a .
Por lo tanto, no hay necesidad de enviar , solo porque es el valor de .
El algoritmo ECDSA completo: paso a paso
Ahora mostramos el algoritmo ECDSA estándar junto con la notación de uso más común. Notamos dónde difiere nuestra notación.
- El probador elige un aleatorio y publica su clave pública como .
- El probador elige un mensaje que desea firmar y le aplica un hash para obtener .
- El probador elige un escalar aleatorio (lo que hemos estado llamando , pero la literatura lo llama ) y calcula . De nuevo, lo que nosotros llamamos la literatura lo llama . Solo se conserva el valor de como .
- El probador calcula para como
Note que está ‘invertido’ en comparación con nuestra implementación anterior. En el algoritmo ECDSA real, el probador calcula y el verificador invierte más tarde, como veremos.
- El firmante envía al verificador.
- El verificador calcula y comprueba que el valor de sea igual a .
La fórmula funciona internamente de la siguiente manera:
Esto produce el mismo punto y, por lo tanto, la coincidirá con el valor del punto calculado mediante .
Derivando la clave pública dada una firma
Las blockchains Ethereum y Bitcoin no verifican firmas dadas la clave pública, el mensaje y la firma. En cambio, dado el mensaje y la firma, resuelven la clave pública y comprueban que la clave pública coincida con la esperada.
Para ver cómo funciona esto, podemos aplicar un poco de álgebra en la fórmula de verificación para derivar la clave pública dada la firma y el hash del mensaje.
Sin embargo, solo con no es suficiente porque corresponde a dos posibles puntos debido a los dos valores de . Para eliminar la ambigüedad, el firmante también debe enviar una variable booleana que indique qué valor de se está utilizando. Enviar la completa ocuparía más espacio.
El algoritmo ECDSA para ‘recuperar’ la clave pública dada una firma es el siguiente:
- El probador publica su clave pública como .
- El probador elige un mensaje que desea firmar y le aplica un hash para obtener .
- El probador elige un escalar aleatorio y calcula .
- El probador calcula
- El probador resuelve en como .
- El firmante envía al verificador, donde es un booleano que indica qué valor de se está utilizando.
- El verificador deriva a partir de y .
- El verificador deriva la clave pública como
y acepta la firma si el calculado coincide con la clave pública que publicó el probador.
Vulnerabilidades en ECDSA por un mal uso
Maleabilidad en ECDSA
Dada una firma , un atacante puede calcular una segunda firma tal que y que se recupere a la misma clave pública .
El atacante simplemente calcula el inverso aditivo de como y cambia el valor de para que se convierta en su inverso aditivo. Esencialmente, hemos reemplazado con y con . Dado que estos dos valores se multiplican, los factores -1 se cancelan y la clave pública recuperada es la misma:
Para prevenir este ataque, el verificador no debe aceptar una firma diferente para un mismo mensaje que ya ha visto antes. La solución más generalizada y robusta es usar un nonce, que es un número siempre creciente que el probador debe incluir en su mensaje. Un nonce (que significa ‘número usado una vez’) sirve como un identificador único para cada firma. Al exigir que el probador incorpore un nonce nuevo y mayor con cada firma, el verificador puede detectar y rechazar fácilmente los intentos de reutilizar o modificar firmas anteriores.
Si el verificador no le aplica un hash al mensaje, se pueden crear pruebas falsas.
Consideremos que un atacante puede crear un valor del cual no conocemos el logaritmo discreto, pero sí conocemos sus componentes. Por ejemplo, el atacante elige valores aleatorios y y calcula , y .
Luego, el atacante calcula y .
Podemos ver cómo funciona este ataque sustituyendo los valores falsos de y en la fórmula de verificación:
La defensa contra este ataque es simple: debe ser el resultado de que el verificador aplique un hash al mensaje. Cuando se aplica un hash a un mensaje, es extremadamente improbable que el hash sea exactamente .
Resumen
El algoritmo ECDSA es una prueba de conocimiento de la relación de logaritmo discreto entre un punto arbitrario y un punto que representa la suma del hash del mensaje multiplicado por el generador y la clave pública multiplicada por un valor pseudoaleatorio que el firmante no controla.
Específicamente, es una prueba de conocimiento de la relación de logaritmo discreto entre y .
Aunque el firmante puede elegir arbitrariamente, no puede controlar el logaritmo discreto del punto porque depende pseudoaleatoriamente de . La única manera de que el firmante pueda calcular en es si realmente conoce el logaritmo discreto de , es decir, la clave privada.
Créditos y agradecimientos
Se consultaron los siguientes recursos durante la creación de este artículo: