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 , escrita en forma hessiana de curva elíptica . Un nombre muy extendido para esta operación es también multiplicación de puntos de curva elíptica , pero puede dar la impresión errónea de que se trata de una multiplicación entre dos puntos.
Dada una curva, E , definida por alguna ecuación en un campo 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. Denota 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 la ECC moderna depende de la dificultad de determinar n a partir de Q = nP dados valores conocidos de Q y P si n es grande (conocido como el problema del logaritmo discreto de 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 inmediatamente obvia con las ubicaciones de los dos primeros, y al repetir esto muchas veces over produce un punto nP que puede estar esencialmente en cualquier lugar. Intuitivamente, esto no es diferente al hecho de que si tuvieras un punto P en un círculo, sumar 42,57 grados a su ángulo aún puede ser un punto "no muy lejos" de P , pero sumar 1000 o 1001 por 42,57 grados producirá un punto que requiere un cálculo un poco más complejo para encontrar el ángulo original. Revertir este proceso, es decir, dados Q=nP y P , y determinar n , sólo se puede hacer probando todos los n posibles , un esfuerzo que es computacionalmente intratable si n es grande.
Hay tres operaciones comúnmente definidas para puntos de curvas elípticas: suma, duplicación y negación.
El punto en el infinito es el elemento de identidad de la aritmética de curvas elípticas. Agregarlo a cualquier punto da como resultado ese otro punto, incluida la adición de un punto en el infinito a sí mismo. Eso es:
El punto en el infinito también se escribe como 0 .
La negación de puntos es encontrar un punto tal que al sumarlo a sí mismo se obtenga 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 suma 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 como resultado 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 diferentes coordenadas x (no son inversas mutuas). 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 hay una línea recta bien definida que pase por P , por lo que la operación se cierra usando un caso límite, la tangente a la curva, E , en P.
Esto se calcula como arriba, tomando las derivadas (dE/dx)/(dE/dy): [2]
donde a proviene de la ecuación que define la curva, E , anterior.
La forma sencilla de calcular una multiplicación de puntos es mediante la suma repetida. Sin embargo, existen enfoques más eficientes para calcular la multiplicación.
El método más simple es el método de duplicar y sumar, [3] similar a elevar al cuadrado y multiplicar en exponenciación modular. El algoritmo funciona de la siguiente manera:
Para calcular sP , comience con la representación binaria de s :, donde .
let bits = bit_representation(s) # el vector de bits (de LSB a MSB) que representa s let res = # punto en el infinito let temp = P # pista duplicada P val por poco en bits: si bit == 1: res = res + temp # punto agregar temperatura = temperatura + temperatura # doble devolver resolución
let bits = bit_representation(s) # el vector de bits (de LSB a MSB) que representa s sea i = longitud (bits) - 2 sea res = P while (i >= 0): # atravesando del segundo MSB al LSB res = res + res # doble si bits[i] == 1: res = res + P # agregar yo = yo - 1 devolver resolución
Tenga en cuenta que los dos métodos iterativos anteriores son vulnerables al análisis de tiempo. Consulte Montgomery Ladder a continuación para conocer un enfoque alternativo.
f(P, d) es si d = 0 entonces devolver 0 # cálculo completo de lo contrario, si d = 1, entonces volver P de lo contrario, si d mod 2 = 1, entonces retorno point_add(P, f(P, d - 1)) # suma cuando d es impar demás return f(point_double(P), d / 2) # duplicar cuando d es par
donde f es la función para multiplicar, P es la coordenada para multiplicar, d es el número de veces que se suma la coordenada a sí misma. Ejemplo: 100P se puede escribir como 2(2[P + 2(2[2(P + 2P)])]) y, por lo tanto, requiere operaciones dobles de seis puntos y operaciones de suma de dos puntos. 100P sería igual a f(P, 100) .
Este algoritmo requiere iteraciones log 2 ( d ) de duplicación y suma de puntos para calcular la multiplicación de puntos completa. Existen muchas variaciones 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 en ventana de este algoritmo, [3] se selecciona un tamaño de ventana w y se calculan todos los valores de for . El algoritmo ahora usa la representación y se convierte
Q ← 0 para i de m a 0 hacer Q ← punto_doble_repetición(Q, w) si d i > 0 entonces Q ← point_add(Q, d i P) # usando el valor precalculado de d i P devolver Q
Este algoritmo tiene la misma complejidad que el enfoque de duplicar y sumar con la ventaja de utilizar menos sumas de puntos (que en la práctica son más lentas que duplicar). Normalmente, el valor de w se elige para que sea bastante pequeño, lo que hace que la etapa de precálculo sea un componente trivial del algoritmo. Para las curvas recomendadas por el NIST, suele ser la mejor selección. La complejidad total de un número de n bits se mide como puntos duplicados y sumas de puntos.
En la versión de ventana deslizante, buscamos intercambiar puntos añadidos por puntos dobles. Calculamos una tabla similar a la de la versión en ventana, excepto que solo calculamos los puntos para . Efectivamente, sólo estamos calculando los valores para los cuales está configurado el bit más significativo de la ventana. Luego, el algoritmo utiliza la representación original de duplicar y sumar .
Q ← 0 para i desde m hasta 0 hacer si di = 0 entonces Q ← punto_doble(Q) demás t ← extraer j (hasta w − 1) bits adicionales de d (incluido d i ) yo ← yo − j si j <w entonces Realice duplicar y sumar usando t devolver Q demás Q ← punto_doble_repetición(Q, w) Q ← punto_añadir(Q, tP) devolver Q
Este algoritmo tiene la ventaja de que la etapa de precálculo es aproximadamente la mitad de compleja que el método de ventana normal y, al mismo tiempo, intercambia sumas de puntos más lentas por duplicaciones de puntos. De hecho, hay pocas razones para utilizar el método de ventanas en lugar de este enfoque, excepto que el primero se puede implementar en tiempo constante. El algoritmo requiere puntos dobles y, como máximo, sumas 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 (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 re yo ← re modificaciones 2 w re ← re - re yo demás re = 0 re ← re/2 yo ← yo + 1 devolver (d i−1 , d i-2 ,…, d 0 )
Donde la función de módulo con signo mods se define como
si (d mod 2 w ) >= 2 w−1 retorno (d mod 2 w ) − 2 w demás volver 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 está el punto a multiplicar. En las curvas típicas de Weierstrass, si entonces . Entonces, en esencia, los aspectos 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 (dj ! = 0) Q ← punto_add(Q, d j P) devolver Q
La wNAF garantiza que, en promedio, habrá una densidad de puntos añadidos (ligeramente mejor que la ventana sin firmar). Requiere duplicación de 1 punto y suma de puntos para el cálculo previo. Luego, el algoritmo requiere duplicaciones y sumas de puntos para el resto de la multiplicación.
Una propiedad del NAF es que tenemos la garantía de 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, se pueden implicar ceros adicionales y no es necesario almacenarlos. En segundo lugar, las múltiples divisiones en serie 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 la sincronización de caché con tan solo 200 firmas realizadas. [4]
El método de la escalera de Montgomery [5] calcula la multiplicación de puntos en un número fijo de operaciones. Esto puede resultar beneficioso cuando la sincronización, el consumo de energía o las mediciones de rama están expuestas a que un atacante realice un ataque de canal lateral . El algoritmo utiliza la misma representación que la de duplicar y sumar.
R 0 ← 0 R 1 ← PAG para i desde m hasta 0 hacer si di = 0 entonces R 1 ← punto_añadir(R 0 , R 1 ) R 0 ← punto_doble(R 0 ) demás R 0 ← punto_añadir(R 0 , R 1 ) R 1 ← punto_doble(R 1 ) // propiedad invariante para mantener la corrección afirmar R 1 == point_add(R 0 , P) devolver R 0
En efecto, este algoritmo tiene la misma velocidad que el enfoque de duplicar y sumar, excepto que calcula el mismo número de sumas de puntos y duplicaciones independientemente del valor del multiplicando d . Esto significa que en este nivel el algoritmo no filtra ninguna información a través de sucursales 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 la sincronización de 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 (); sea mut r1 = self . clonar (); para i en ( 0 ..= s . bits_significativos ()). rev () { si s . get_bit ( i ) { r0 = r0 . agregar ( & r1 ); r1 = r1 . agregar ( & r1 . clonar ()); } más { r1 = r0 . agregar ( & r1 ); r0 = r0 . agregar ( & r0 . clonar ()); } } r0 // r0 = P * s }
Es probable que la seguridad de una implementación criptográfica se enfrente a la amenaza de los llamados ataques de sincronización que explotan las características de sincronización de la implementación que dependen de los datos. Las máquinas que ejecutan implementaciones criptográficas consumen cantidades variables de tiempo para procesar diferentes entradas, por lo que los tiempos varían según la clave de cifrado. Para resolver este problema, se implementan algoritmos criptográficos de una manera que eliminan de la implementación la característica de temporización variable dependiente de los datos, lo que conduce a las llamadas 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 reglas de codificación para implementaciones de operaciones criptográficas y, más generalmente, para operaciones que involucran valores secretos o confidenciales.
La escalera de Montgomery es un algoritmo de sólo coordenadas para la multiplicación de puntos de curvas elípticas y se basa en las reglas de duplicar y sumar sobre un conjunto específico de curvas conocido 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 requerirá un tiempo constante y tiene el potencial de filtrar la parte secreta. 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 se muestra a continuación y utiliza dos funciones CSwap y Ladder-Step. En el valor de retorno del algoritmo Z 2 p-2 se calcula el valor de Z 2 -1 utilizando el pequeño teorema de Fermat .
Entrada del algoritmo Montgomery-Ladder (x P , n) : un escalar de bits y la coordenada de un punto . salida : -coordenada de , el -veces múltiplo escalar de . X 1 ← x P ; X 2 ← 1; Z2 ← 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 bit anterior ← bit ( X 2 ,Z 2 , X 3 ,Z 3 ) ← CSwap( X 2 ,Z 2 , X 3 ,Z 3 ,b) ( X 2 ,Z 2 , X 3 ,Z 3 ) ← Escalera-Escalón( X 2 ,Z 2 , X 3 ,Z 3 ,X 1 ) regresar X 2 Z 2 p-2
La función Ladder-Step (que se muestra a continuación) utilizada dentro de la escalera 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-Escalón( 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 volver ( X 2 ,Z 2 , X 3 ,Z 3 )
La función CSwap gestiona la bifurcació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. En la literatura se han propuesto varios métodos para implementar CSwap. [10] [11] Una opción menos costosa para gestionar el requisito de tiempo constante de la escalera de Montgomery es la selección condicional que se formaliza mediante 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 de Montgomery estándar Curve25519 con un 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 en lenguaje ensamblador de alta velocidad. Cabe señalar que en las implementaciones de estas escaleras se utilizó la función CSwap. Después de eso, ha habido varios intentos de optimizar la implementación de la escalera a través de programas ensambladores escritos a mano, de los cuales la noción de CSelect se usó por primera vez en [15] y luego en [16] . Además de usar instrucciones secuenciales, también se han utilizado instrucciones vectoriales. Se utiliza 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 intentado 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 de 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 denominada 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 campos necesaria para el software ECC relacionado. En la búsqueda de curvas de Montgomery que sean competitivas con Curve25519 y Curve448, se realizaron investigaciones y se propusieron un par de curvas junto con implementaciones secuenciales eficientes [16] y vectorizadas [20] 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]