La exponenciación modular es una exponenciación realizada sobre un módulo . Es útil en informática , especialmente en el campo de la criptografía de clave pública , donde se utiliza tanto en el intercambio de claves Diffie-Hellman como en las claves públicas/privadas RSA .
La exponenciación modular es el resto cuando un número entero b (la base) se eleva a la potencia e (el exponente) y se divide por un número entero positivo m (el módulo); es decir, c = b mi mod m . De la definición de división se deduce que 0 ≤ c < m .
Por ejemplo, dado b = 5 , e = 3 y m = 13 , dividir 5 3 = 125 por 13 deja un resto de c = 8 .
La exponenciación modular se puede realizar con un exponente negativo e encontrando el inverso multiplicativo modular d de b módulo m usando el algoritmo euclidiano extendido . Eso es:
La exponenciación modular es eficiente de calcular, incluso para números enteros muy grandes. Por otro lado, se cree que es difícil calcular el logaritmo discreto modular , es decir, encontrar el exponente e cuando se dan b , c y m . Este comportamiento de función unidireccional hace que la exponenciación modular sea candidata para su uso en algoritmos criptográficos.
El método más directo para calcular un exponente modular es calcular b e directamente y luego tomar este número módulo m . Considere intentar calcular c , dado b = 4 , e = 13 y m = 497 :
Se podría usar una calculadora para calcular 4 13 ; esto equivale a 67.108.864. Tomando este valor módulo 497, se determina que la respuesta c es 445.
Tenga en cuenta que b tiene solo un dígito de longitud y que e tiene solo dos dígitos de longitud, pero el valor b e tiene 8 dígitos de longitud.
En criptografía sólida, b suele tener al menos 1024 bits . [1] Considere b = 5 × 10 76 y e = 17 , los cuales son valores perfectamente razonables. En este ejemplo, b tiene una longitud de 77 dígitos y e tiene una longitud de 2 dígitos, pero el valor b e tiene una longitud de 1304 dígitos decimales. Estos cálculos son posibles en las computadoras modernas, pero la magnitud de tales números hace que la velocidad de los cálculos se reduzca considerablemente. A medida que b y e aumentan aún más para proporcionar una mayor seguridad, el valor b e se vuelve difícil de manejar.
El tiempo necesario para realizar la exponenciación depende del entorno operativo y del procesador. El método descrito anteriormente requiere O ( e ) multiplicaciones para completarse.
Mantener los números más pequeños requiere operaciones de reducción modular adicionales, pero el tamaño reducido hace que cada operación sea más rápida, lo que ahorra tiempo (y memoria) en general.
Este algoritmo hace uso de la identidad.
El algoritmo modificado es:
Tenga en cuenta que al final de cada iteración a través del bucle, la ecuación c ≡ b e′ (mod m ) es válida. El algoritmo finaliza cuando el bucle se ha ejecutado e veces. En ese punto c contiene el resultado de b e mod m .
En resumen, este algoritmo aumenta e′ en uno hasta que sea igual a e . En cada paso, multiplicar el resultado de la iteración anterior, c , por b y realizar una operación de módulo en el producto resultante, manteniendo así el c resultante como un número entero pequeño.
Se presenta nuevamente el ejemplo b = 4 , e = 13 y m = 497 . El algoritmo realiza la iteración trece veces:
Por tanto, la respuesta final para c es 445, como en el método directo.
Al igual que el primer método, este requiere O( e ) multiplicaciones para completarse. Sin embargo, dado que los números utilizados en estos cálculos son mucho más pequeños que los números utilizados en los cálculos del primer algoritmo, el tiempo de cálculo disminuye en un factor de al menos O( e ) en este método.
En pseudocódigo, este método se puede realizar de la siguiente manera:
la función modular_pow(base, exponente, módulo) es si módulo = 1 entonces devuelve 0 c := 1 para e_prime = 0 al exponente-1 do c := (c * base) módulo mod return c
Un tercer método reduce drásticamente el número de operaciones para realizar la exponenciación modular, manteniendo al mismo tiempo la misma huella de memoria que en el método anterior. Es una combinación del método anterior y un principio más general llamado exponenciación al cuadrado (también conocida como exponenciación binaria ).
Primero, se requiere convertir el exponente e a notación binaria . Es decir, e puede escribirse como:
En tal notación, la longitud de e es n bits. a i puedo tomar el valor 0 o 1 para cualquier i tal que 0 ≤ i < n . Por definición, una norte − 1 = 1 .
El valor b e puede entonces escribirse como:
La solución c es por tanto:
El siguiente es un ejemplo en pseudocódigo basado en criptografía aplicada de Bruce Schneier . [2] Las entradas base , exponente y módulo corresponden a b , e y m en las ecuaciones dadas anteriormente.
la función modular_pow(base, exponente, módulo) es si módulo = 1 entonces devuelve 0 Afirmar :: (módulo - 1) * (módulo - 1) no desborda la base resultado := 1 base: = módulo mod base mientras que exponente> 0 hazlo si ( mod exponente 2 == 1) entonces resultado: = (resultado * base) módulo mod exponente := exponente >> 1 base := (base * base) resultado de retorno del módulo mod
Tenga en cuenta que al ingresar al bucle por primera vez, la variable de código base es equivalente a b . Sin embargo, la elevación al cuadrado repetida en la tercera línea de código garantiza que al finalizar cada bucle, la base variable sea equivalente a b 2 i mod m , donde i es el número de veces que se ha iterado el bucle. (Esto convierte a i en el siguiente bit de trabajo del exponente binario exponente , donde el bit menos significativo es el exponente 0 ).
La primera línea de código simplemente realiza la multiplicación en . si unes cero, no se ejecuta ningún código ya que esto efectivamente multiplica el total acumulado por uno. si unen lugar de uno, la base variable (que contiene el valor b 2 i mod m de la base original) simplemente se multiplica.
En este ejemplo, la base b se eleva al exponente e = 13 . El exponente es 1101 en binario. Hay cuatro dígitos binarios, por lo que el ciclo se ejecuta cuatro veces, con valores a 0 = 1, a 1 = 0, a 2 = 1 y a 3 = 1 .
Primero, inicializa el resultado en 1 y conserva el valor de b en la variable x :
Hemos terminado: R ahora es .
Aquí está el cálculo anterior, donde calculamos b = 4 elevado a e = 13 , realizado en módulo 497.
Inicializar:
Hemos terminado: R ahora es el mismo resultado obtenido en los algoritmos anteriores.
El tiempo de ejecución de este algoritmo es O ( exponente logarítmico ) . Cuando se trabaja con valores grandes de exponente , esto ofrece un beneficio de velocidad sustancial sobre los dos algoritmos anteriores, cuyo tiempo es O( exponente ) . Por ejemplo, si el exponente fuera 2 20 = 1048576, este algoritmo tendría 20 pasos en lugar de 1048576 pasos.
función modPow(b, e, m) si m == 1 entonces devuelve 0 final local r = 1 segundo = segundo % metro mientras e > 0 hazlo si e % 2 == 1 entonces r = (r*b)% metro fin segundo = (b*b) % metro e = e >> 1 --use 'e = math.floor(e / 2)' en Lua 5.2 o anterior fin regresar r fin
También podemos usar los bits del exponente en orden de izquierda a derecha. En la práctica, normalmente querríamos que el resultado fuera un módulo m . En ese caso, reduciríamos cada resultado de multiplicación (mod m ) antes de continuar. Por simplicidad, aquí se omite el cálculo del módulo. Este ejemplo muestra cómo calcular usando exponenciación binaria de izquierda a derecha. El exponente es 1101 en binario; hay 4 bits, por lo que hay 4 iteraciones.
Inicialice el resultado a 1: .
En El arte de la programación informática , vol. 2, Algoritmos seminuméricos , página 463, Donald Knuth señala que, contrariamente a algunas afirmaciones, este método no siempre proporciona el mínimo número posible de multiplicaciones. El contraejemplo más pequeño es para una potencia de 15, cuando el método binario necesita seis multiplicaciones. En su lugar, forme x 3 en dos multiplicaciones, luego x 6 elevando al cuadrado x 3 , luego x 12 elevando al cuadrado x 6 y finalmente x 15 multiplicando x 12 y x 3 , logrando así el resultado deseado con solo cinco multiplicaciones. Sin embargo, a continuación aparecen muchas páginas que describen cómo se podrían idear tales secuencias en general.
El m -ésimo término de cualquier secuencia recursiva constante (como los números de Fibonacci o los números de Perrin ) donde cada término es una función lineal de k términos anteriores se puede calcular eficientemente módulo n calculando Am mod n , donde A es el k correspondiente × k matriz complementaria . Los métodos anteriores se adaptan fácilmente a esta aplicación. Esto se puede utilizar para pruebas de primalidad de números grandes n , por ejemplo.
Un algoritmo recursivo para ModExp(A, b, c)
= Ab mod c , donde A es una matriz cuadrada.
la función Matrix_ModExp(Matrix A, int b, int c) es si b == 0 entonces devuelve I // La matriz de identidad si (b mod 2 == 1) luego devuelve (A * Matrix_ModExp(A, b - 1, c) ) mod c Matriz D := Matrix_ModExp(A, b / 2, c) retorno (D * D) mod c
El intercambio de claves Diffie-Hellman utiliza exponenciación en grupos cíclicos finitos. Los métodos anteriores para la exponenciación de matrices modulares se extienden claramente a este contexto. La multiplicación de matrices modulares C ≡ AB (mod n ) simplemente se reemplaza en todas partes por la multiplicación de grupos c = ab .
En la computación cuántica , la exponenciación modular aparece como el cuello de botella del algoritmo de Shor , donde debe calcularse mediante un circuito que consta de puertas reversibles , que pueden descomponerse en puertas cuánticas apropiadas para un dispositivo físico específico. Además, en el algoritmo de Shor es posible conocer la base y el módulo de exponenciación en cada llamada, lo que permite diversas optimizaciones del circuito. [3]
Debido a que la exponenciación modular es una operación importante en informática y existen algoritmos eficientes (ver arriba) que son mucho más rápidos que simplemente exponenciar y luego tomar el resto, muchos lenguajes de programación y bibliotecas de enteros de precisión arbitraria tienen una función dedicada para realizar la exponenciación modular. :
pow()
(exponenciación) de Python [1] toma un tercer argumento opcional, el móduloBigInteger
tiene un ModPow()
método para realizar exponenciación modularjava.math.BigInteger
tiene un modPow()
método para realizar exponenciación modularpowermod
de Symbolic Math ToolboxMath::BigInt
tiene un bmodpow()
método [2] para realizar la exponenciación modularexpmod
.big.Int
contiene un Exp()
método (exponenciación) [3] cuyo tercer parámetro, si no es nulo, es el módulobcpowmod()
tiene una función [4] para realizar exponenciación modularmpz_powm()
función [5] para realizar exponenciación modular@PowerMod()
para FileMaker Pro (con ejemplo de cifrado RSA de 1024 bits )openssl
tiene el OpenSSL::BN#mod_exp
método [6] para realizar una exponenciación modular.