La multiplicación escalar de curva elíptica es la operación de sumar sucesivamente un punto a lo largo de una curva elíptica a sí mismo repetidamente. Se utiliza en criptografía de curva elíptica (ECC). La literatura presenta esta operación como multiplicación escalar , tal como se escribe en forma hessiana de una curva elíptica . Un nombre muy extendido para esta operación es también multiplicación de puntos de curva elíptica , pero esto puede dar la impresión errónea de ser una multiplicación entre dos puntos.
Dada una curva, E , definida por alguna ecuación en un cuerpo finito (como E : y 2 = x 3 + ax + b ), la multiplicación de puntos se define como la suma repetida de un punto a lo largo de esa curva. Denotemos como nP = P + P + P + … + P para algún escalar (entero) n y un punto P = ( x , y ) que se encuentra en la curva, E . Este tipo de curva se conoce como curva de Weierstrass.
La seguridad de los sistemas criptográficos criptográficos modernos depende de la imposibilidad de determinar n a partir de Q = nP dados los valores conocidos de Q y P si n es grande (conocido como el problema del logaritmo discreto de la curva elíptica por analogía con otros sistemas criptográficos ). Esto se debe a que la suma de dos puntos en una curva elíptica (o la suma de un punto a sí mismo) produce un tercer punto en la curva elíptica cuya ubicación no tiene una relación obvia inmediata con las ubicaciones de los dos primeros, y repetir esto muchas veces produce un punto nP que puede estar esencialmente en cualquier lugar. Intuitivamente, esto no es muy diferente del hecho de que si tuviera un punto P en un círculo, sumar 42,57 grados a su ángulo puede seguir siendo un punto "no muy lejano" de P , pero sumar 1000 o 1001 veces 42,57 grados producirá un punto que requiere un cálculo un poco más complejo para encontrar el ángulo original. Para revertir este proceso, es decir, dado Q=nP y P , y determinar n , solo se puede probar todos los n posibles , un esfuerzo que es computacionalmente intratable si n es grande.
Hay tres operaciones comúnmente definidas para los puntos de la curva elíptica: suma, duplicación y negación.
El punto en el infinito es el elemento identidad de la aritmética de curvas elípticas . Sumarle a cualquier punto da como resultado ese otro punto, incluso sumando el punto en el infinito a sí mismo. Es decir:
El punto en el infinito también se escribe como 0 .
La negación de un punto es encontrar un punto tal que al sumarlo a sí mismo resulte en un punto en el infinito ( ).
Para curvas elípticas de la forma E : y 2 = x 3 + ax + b , la negación es un punto con la misma coordenada x pero con la coordenada y negada :
Con 2 puntos distintos, P y Q , la adición se define como la negación del punto resultante de la intersección de la curva, E , y la recta definida por los puntos P y Q , dando el punto, R . [1]
Suponiendo que la curva elíptica, E , está dada por y 2 = x 3 + ax + b , esto se puede calcular como:
Estas ecuaciones son correctas cuando ninguno de los puntos es el punto en el infinito, , y si los puntos tienen coordenadas x diferentes (no son inversos mutuos). Esto es importante para el algoritmo de verificación ECDSA donde el valor hash podría ser cero.
Cuando los puntos P y Q son coincidentes (en las mismas coordenadas), la suma es similar, excepto que no existe una línea recta bien definida que pase por P , por lo que la operación se cierra utilizando un caso límite, la tangente a la curva, E , en P.
Esto se calcula como se indicó anteriormente, tomando las derivadas (dE/dx)/(dE/dy): [2]
donde a es de la ecuación definitoria de la curva, E , anterior.
La forma más sencilla de calcular una multiplicación de puntos es mediante la suma repetida. Sin embargo, existen métodos más eficientes para calcular la multiplicación.
El método más simple es el de duplicar y sumar, [3] similar al de elevar al cuadrado y multiplicar en la exponenciación modular. El algoritmo funciona de la siguiente manera:
Para calcular sP , comience con la representación binaria de s : , donde .
sea bits = bit_representation(s) # el vector de bits (de LSB a MSB) que representan s sea res = # punto en el infinito deje que temp = P # realice el seguimiento del valor P duplicado para bit en bits: si bit == 1: res = res + temp # punto añadido temperatura = temperatura + temperatura # doble volver res
sea bits = bit_representation(s) # el vector de bits (de LSB a MSB) que representan s sea i = longitud(bits) - 2 sea res = P mientras (i >= 0): # recorre desde el segundo MSB al LSB res = res + res # doble si bits[i] == 1: res = res + P # suma yo = yo - 1 volver res
Tenga en cuenta que ambos métodos iterativos anteriores son vulnerables al análisis de tiempos. Consulte la escalera de Montgomery a continuación para obtener un enfoque alternativo.
f(P, d) es Si d = 0 entonces devuelve 0 # cálculo completo De lo contrario, si d = 1 entonces devolver P De lo contrario, si d mod 2 = 1 entonces retorna point_add(P, f(P, d - 1)) # suma cuando d es impar demás devuelve f(point_double(P), d / 2) # duplicando cuando d es par
donde f es la función para multiplicar, P es la coordenada a multiplicar, d es el número de veces que se debe sumar la coordenada a sí misma. Ejemplo: 100P se puede escribir como 2(2[P + 2(2[2(P + 2P)])]) y, por lo tanto, requiere seis operaciones de doble punto y dos operaciones de suma de puntos. 100P sería igual a f(P, 100) .
Este algoritmo requiere 2 ( d ) iteraciones de duplicación y adición de puntos para calcular la multiplicación completa de puntos. Existen muchas variantes de este algoritmo, como el uso de una ventana, una ventana deslizante, NAF, NAF-w, cadenas vectoriales y escalera de Montgomery.
En la versión con ventana de este algoritmo, [3] se selecciona un tamaño de ventana w y se calculan todos los valores de para . El algoritmo ahora utiliza la representación y se convierte en
Q ← 0 para i de m a 0 hago Q ← punto_doble_repetición(Q, w) Si d i > 0 entonces Q ← point_add(Q, d i P) # utilizando el valor precalculado de d i P devolver Q
Este algoritmo tiene la misma complejidad que el enfoque de duplicar y sumar, con el beneficio de utilizar menos adiciones de puntos (que en la práctica son más lentas que la duplicación). Normalmente, el valor de w se elige para que sea bastante pequeño, lo que hace que la etapa de precomputación sea un componente trivial del algoritmo. Para las curvas recomendadas por el NIST, suele ser la mejor opción. La complejidad total para un número de n bits se mide como duplicaciones de puntos y adiciones de puntos.
En la versión de ventana deslizante, buscamos intercambiar las adiciones de puntos por los puntos dobles. Calculamos una tabla similar a la de la versión con ventana, excepto que solo calculamos los puntos para . En efecto, solo calculamos los valores para los que está configurado el bit más significativo de la ventana. Luego, el algoritmo utiliza la representación original de doble y suma de .
Q ← 0 para i desde m hasta 0 hacer si d i = 0 entonces Q ← punto_doble(Q) demás t ← extrae j (hasta w − 1) bits adicionales de d (incluido d i ) yo ← yo − j Si j < w entonces Realizar duplicación y suma utilizando t devolver Q demás Q ← punto_doble_repetición(Q, w) Q ← punto_agregar(Q, tP) devolver Q
Este algoritmo tiene la ventaja de que la etapa de precomputación es aproximadamente la mitad de compleja que el método de ventana normal, y además intercambia adiciones de puntos más lentas por duplicaciones de puntos. En efecto, hay pocas razones para usar el método de ventana en lugar de este enfoque, excepto que el primero se puede implementar en tiempo constante. El algoritmo requiere duplicaciones de puntos y, como máximo, adiciones de puntos.
En la forma no adyacente, pretendemos aprovechar el hecho de que la resta de puntos es tan fácil como la suma de puntos para realizar menos operaciones (de cualquiera de las dos) en comparación con un método de ventana deslizante. El NAF del multiplicando debe calcularse primero con el siguiente algoritmo
yo ← 0 mientras (d > 0) hacer si (d mod 2) = 1 entonces d i ← d mods 2 w d ← d − d i demás yo = 0 d ← d/2 yo ← yo + 1 devuelve (d i−1 , d i-2 , …, d 0 )
Donde la función módulo con signo mods se define como
si (d mod 2 w ) >= 2 w−1 devuelve (d mod 2 w ) − 2 w demás devuelve d mod 2 w
Esto produce el NAF necesario para realizar ahora la multiplicación. Este algoritmo requiere el cálculo previo de los puntos y sus negativos, donde es el punto que se va a multiplicar. En las curvas de Weierstrass típicas, si entonces . Por lo tanto, en esencia, los negativos son baratos de calcular. A continuación, el siguiente algoritmo calcula la multiplicación :
Q ← 0 para j ← i − 1 hasta 0 hacer Q ← punto_doble(Q) si (d j != 0) Q ← punto_agregar(Q, d j P) devolver Q
El wNAF garantiza que, en promedio, habrá una densidad de adiciones de puntos (ligeramente mejor que la ventana sin signo). Requiere una duplicación de puntos y adiciones de puntos para el cálculo previo. Luego, el algoritmo requiere duplicaciones de puntos y adiciones de puntos para el resto de la multiplicación.
Una propiedad de la NAF es que se garantiza que cada elemento distinto de cero va seguido de al menos ceros adicionales. Esto se debe a que el algoritmo borra los bits inferiores con cada resta de la salida de la función mods . Esta observación se puede utilizar para varios propósitos. Después de cada elemento distinto de cero, los ceros adicionales se pueden implicar y no es necesario almacenarlos. En segundo lugar, las divisiones seriales múltiples por 2 se pueden reemplazar por una división por después de cada elemento distinto de cero y dividir por 2 después de cada cero.
Se ha demostrado que mediante la aplicación de un ataque de canal lateral FLUSH+RELOAD en OpenSSL, se puede revelar la clave privada completa después de realizar una sincronización de caché con tan solo 200 firmas realizadas. [4]
El método de escalera de Montgomery [5] calcula la multiplicación de puntos en una cantidad fija de operaciones. Esto puede resultar beneficioso cuando se exponen las mediciones de tiempo, consumo de energía o ramificaciones a un atacante que realiza un ataque de canal lateral . El algoritmo utiliza la misma representación que la de la duplicación y la suma.
R0 ← 0 R1 ← P para i desde m hasta 0 hacer si d i = 0 entonces R 1 ← punto_agregado(R 0 , R 1 ) R 0 ← punto_doble(R 0 ) demás R 0 ← punto_agregado(R 0 , R 1 ) R 1 ← punto_doble(R 1 ) // propiedad invariante para mantener la corrección afirmar R 1 == punto_add(R 0 , P) devuelve R 0
Este algoritmo tiene en efecto la misma velocidad que el método de duplicar y sumar, excepto que calcula la misma cantidad de adiciones y duplicaciones de puntos independientemente del valor del multiplicando d . Esto significa que en este nivel el algoritmo no pierde ninguna información a través de ramificaciones o consumo de energía.
Sin embargo, se ha demostrado que mediante la aplicación de un ataque de canal lateral FLUSH+RELOAD en OpenSSL, se puede revelar la clave privada completa después de realizar un almacenamiento en caché con una sola firma a un costo muy bajo. [6]
Código de óxido para Montgomery Ladder: [7]
/// Multiplicación de puntos de operación constante. /// NOTA: no es seguro para la memoria. /// * `s`: valor escalar por el que multiplicar /// * la multiplicación se define como P₀ + P₁ + ... Pₛ fn sec_mul ( & mut self , s : big ) -> E521 { let mut r0 = get_e521_id_point ( ); let mut r1 = self.clone ( ) ; for i in ( 0 .. = s.significative_bits ( ) ) . rev ( ) { if s.get_bit ( i ) { r0 = r0.add ( & r1 ) ; r1 = r1.add ( & r1.clone ( ) ) ; } else { r1 = r0.add ( & r1 ) ; r0 = r0.add ( & r0.clone ( ) ) ; } } r0 // r0 = P * s }
Es probable que la seguridad de una implementación criptográfica se enfrente a la amenaza de los denominados ataques de temporización que explotan las características de temporización dependientes de los datos de la implementación. Las máquinas que ejecutan implementaciones criptográficas consumen cantidades variables de tiempo para procesar diferentes entradas y, por lo tanto, los tiempos varían según la clave de cifrado. Para resolver este problema, los algoritmos criptográficos se implementan de una manera que elimina la característica de temporización variable dependiente de los datos de la implementación, lo que conduce a las denominadas implementaciones de tiempo constante. Las implementaciones de software se consideran de tiempo constante en el siguiente sentido, como se indica en: [8] “ evita todas las ramas dependientes de la entrada, todos los índices de matriz dependientes de la entrada y otras instrucciones con tiempos dependientes de la entrada ”. La página de GitHub [9] enumera las reglas de codificación para las implementaciones de operaciones criptográficas y, de manera más general, para las operaciones que involucran valores secretos o sensibles.
La escalera de Montgomery es un algoritmo de solo coordenadas para la multiplicación de puntos de curva elíptica y se basa en las reglas de doble y suma sobre un conjunto específico de curvas conocidas como curva de Montgomery . El algoritmo tiene una ramificación condicional de modo que la condición depende de un bit secreto. Por lo tanto, una implementación sencilla de la escalera no será de tiempo constante y tiene el potencial de filtrar el bit secreto. Este problema se ha abordado en la literatura [10] [11] y se conocen varias implementaciones de tiempo constante. El algoritmo de escalera de Montgomery de tiempo constante es el que se muestra a continuación, que utiliza dos funciones CSwap y Ladder-Step. En el valor de retorno del algoritmo Z 2 p-2 es el valor de Z 2 -1 calculado utilizando el pequeño teorema de Fermat .
Algoritmo Montgomery-Ladder(x P ,n) entrada : un escalar de -bit y la -coordenada de un punto . salida : -coordenada de , el -múltiplo escalar de . X 1 ← x P ; X 2 ← 1; Z 2 ← 0; X 3 ← x P ; Z 3 ← 1
bit anterior ← 0 para desde abajo hasta 0 hacer bit ← valor de bit en el índice de b ← bit prevbit prevbit ← bit ( X 2 , Z 2 , X 3 , Z 3 ) ← Intercambio de CS ( X 2 , Z 2 , X 3 , Z 3 , b) ( X 2 ,Z 2 , X 3 ,Z 3 ) ← Escalera-Paso( X 2 ,Z 2 , X 3 ,Z 3 ,X 1 ) devolver X 2 Z 2 p-2
La función Ladder-Step (que se muestra a continuación) que se utiliza en el algoritmo ladder es el núcleo del algoritmo y es una forma combinada de las operaciones diferenciales de suma y duplicación. La constante de campo a 24 se define como a 24 = , donde es un parámetro de la curva de Montgomery subyacente .
Función Escalera-Paso( X 2 ,Z 2 , X 3 ,Z 3 ,X 1 ) T 1 ← X 2 + Z 2 T 2 ← X 2 - Z 2 T 3 ← X 3 + Z 3 T 4 ← X 3 - Z 3 T 5 ← T 1 2 T 6 ← T 2 2 T 2 ← T 2 · T 3 T 1 ← T 1 · T 4 T 1 ← T 1 + T 2 T 2 ← T 1 - T 2 X 3 ← T 1 2 T 2 ← T 2 2 Z 3 ← T 2 · X 1 X 2 ← T 5 · T 6 T 5 ← T 5 - T 6 T 1 ← a 24 · T 5 T 6 ← T 6 + T 1 Z 2 ← T 5 · T 6 retorno ( X 2 , Z 2 , X 3 , Z 3 )
La función CSwap gestiona la ramificación condicional y ayuda a que la escalera se ejecute siguiendo los requisitos de una implementación de tiempo constante. La función intercambia el par de elementos de campo X 2 ,Z 2 y X 3 ,Z 3 solo si = 1 y esto se hace sin filtrar ninguna información sobre el bit secreto. Se han propuesto varios métodos de implementación de CSwap en la literatura. [10] [11] Una opción menos costosa para gestionar el requisito de tiempo constante de la escalera Montgomery es la selección condicional que se formaliza a través de una función CSelect. Esta función se ha utilizado en varias optimizaciones y se ha discutido formalmente en [12].
Desde el inicio de la curva Montgomery estándar Curve25519 en el nivel de seguridad de 128 bits, ha habido varias implementaciones de software para calcular el ECDH en varias arquitecturas y para lograr el mejor rendimiento posible, los desarrolladores criptográficos han recurrido a escribir las implementaciones utilizando el lenguaje ensamblador de la arquitectura subyacente. El trabajo [13] proporcionó un par de implementaciones de ensamblaje de 64 bits dirigidas a la arquitectura AMD64. Las implementaciones se desarrollaron utilizando una herramienta conocida como qhasm [14] que puede generar programas criptográficos de lenguaje ensamblador de alta velocidad. Cabe señalar que la función CSwap se utilizó en las implementaciones de estas escaleras. Después de eso, ha habido varios intentos de optimizar la implementación de la escalera a través de programas de ensamblaje escritos a mano, de los cuales la noción de CSelect se utilizó por primera vez en [15] y luego en [16] . Además de utilizar instrucciones secuenciales, también se han utilizado instrucciones vectoriales para optimizar el cálculo de la escalera a través de varios trabajos. [17] [18] [19] [20] Junto con AMD64, también se han hecho intentos para lograr implementaciones eficientes en otras arquitecturas como ARM. Los trabajos [21] y [22] proporcionan implementaciones eficientes dirigidas a la arquitectura ARM. Las bibliotecas lib25519 [23] y [24] son dos bibliotecas de última generación que contienen implementaciones eficientes de la escalera Montgomery para Curve25519 . Sin embargo, las bibliotecas también tienen implementaciones de otras primitivas criptográficas.
Aparte de Curve25519 , ha habido varios intentos de calcular la escalera sobre otras curvas en varios niveles de seguridad. También se han estudiado en la literatura implementaciones eficientes de la escalera sobre la curva estándar Curve448 en un nivel de seguridad de 224 bits. [15] [18] [20] Se propuso una curva llamada Curve41417 que proporciona seguridad de poco más de 200 bits [25] en la que se utilizó una variante de la estrategia Karatsuba para implementar la multiplicación de campo necesaria para el software ECC relacionado. En la búsqueda de curvas de Montgomery que sean competitivas con Curve25519 y Curve448, se ha realizado una investigación y se propusieron un par de curvas junto con implementaciones secuenciales [16] y vectorizadas [20] eficientes de las escaleras correspondientes. En el nivel de seguridad de 256 bits también se han abordado implementaciones eficientes de la escalera a través de tres curvas de Montgomery diferentes. [26]