<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Criptografía on kenji.blog</title><link>http://kenji.blog/es/categories/criptograf%C3%ADa/</link><description>Recent content in Criptografía on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 22:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/categories/criptograf%C3%ADa/index.xml" rel="self" type="application/rss+xml"/><item><title>Fundamentos e implementación de criptografía utilizando el pequeño teorema de Fermat</title><link>http://kenji.blog/es/p/fermats-little-theorem-cryptography-implementation/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/fermats-little-theorem-cryptography-implementation/</guid><description>&lt;img src="http://kenji.blog/p/fermats-little-theorem-cryptography-implementation/img/eyecatch.jpg" alt="Featured image of post Fundamentos e implementación de criptografía utilizando el pequeño teorema de Fermat" />&lt;h2 id="1-introducción-el-misterio-matemático-que-sustenta-la-criptografía-moderna">1. Introducción: El misterio matemático que sustenta la criptografía moderna
&lt;/h2>&lt;p>En la sociedad digital moderna, especialmente en las comunicaciones a través de Internet, la &amp;ldquo;criptografía&amp;rdquo; se ha convertido en una tecnología base indispensable. El hecho de que podamos navegar de forma segura por sitios web a través de HTTPS en nuestros navegadores web, realizar transacciones financieras en la banca en línea e intercambiar mensajes privados en aplicaciones de mensajería, se debe a que detrás funcionan protocolos criptográficos respaldados por teorías matemáticas avanzadas. Entre ellos, la &amp;ldquo;criptografía de clave pública&amp;rdquo; desempeña un papel particularmente importante, y su representante principal es el &lt;strong>cifrado RSA&lt;/strong>.&lt;/p>
&lt;p>La seguridad y validez de muchos algoritmos criptográficos, incluido el cifrado RSA, dependen en gran medida de un teorema muy hermoso y poderoso descubierto por el matemático francés del siglo XVII Pierre de Fermat. Este es el &lt;strong>pequeño teorema de Fermat (Fermat&amp;rsquo;s Little Theorem)&lt;/strong>. Además, el teorema de Leonhard Euler, que es una generalización de este, también juega un papel decisivo en la teoría criptográfica.&lt;/p>
&lt;p>En este artículo, explicaremos exhaustivamente desde los fundamentos cómo el descubrimiento de las matemáticas puras, el pequeño teorema de Fermat, se aplica a la tecnología criptográfica práctica moderna, especialmente a la &amp;ldquo;prueba de primalidad&amp;rdquo; y al &amp;ldquo;cifrado RSA&amp;rdquo;. Será una guía técnica muy detallada que cubrirá las demostraciones matemáticas, los mecanismos de cifrado y descifrado, y las implementaciones de algoritmos específicos utilizando C++ y Python.&lt;/p>
&lt;hr>
&lt;h2 id="2-fundamentos-de-las-congruencias-y-la-aritmética-modular">2. Fundamentos de las congruencias y la aritmética modular
&lt;/h2>&lt;p>Para entender el pequeño teorema de Fermat, primero debemos familiarizarnos con el concepto matemático de la &amp;ldquo;aritmética modular (congruencias)&amp;rdquo;. La aritmética modular es un sistema de cálculo que se centra en el &amp;ldquo;resto&amp;rdquo; después de dividir por un número fijo (llamado módulo). Dado que es un cálculo similar a la esfera de un reloj (que da una vuelta cada 12 horas), también se le llama &amp;ldquo;aritmética del reloj&amp;rdquo;.&lt;/p>
&lt;p>Cuando el resto de dividir los enteros $a$ y $b$ por un entero positivo $n$ es igual, se escribe matemáticamente de la siguiente manera:&lt;/p>
$$
a \equiv b \pmod n
$$
&lt;p>Esto se lee como &amp;ldquo;$a$ y $b$ son congruentes módulo $n$&amp;rdquo;. Por ejemplo, el resto de dividir 17 entre 5 es 2, y el resto de dividir 12 entre 5 también es 2. Por lo tanto, se puede escribir de la siguiente manera:&lt;/p>
$$
17 \equiv 12 \pmod 5 \equiv 2 \pmod 5
$$
&lt;p>En la aritmética modular, las cuatro operaciones aritméticas básicas (suma, resta y multiplicación) se mantienen tal cual.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Adición&lt;/strong>: Si $a \equiv b \pmod n$ y $c \equiv d \pmod n$, entonces $a + c \equiv b + d \pmod n$&lt;/li>
&lt;li>&lt;strong>Sustracción&lt;/strong>: Si $a \equiv b \pmod n$ y $c \equiv d \pmod n$, entonces $a - c \equiv b - d \pmod n$&lt;/li>
&lt;li>&lt;strong>Multiplicación&lt;/strong>: Si $a \equiv b \pmod n$ y $c \equiv d \pmod n$, entonces $a \times c \equiv b \times d \pmod n$&lt;/li>
&lt;li>&lt;strong>Exponenciación&lt;/strong>: Si $a \equiv b \pmod n$, entonces para cualquier número natural $k$, $a^k \equiv b^k \pmod n$&lt;/li>
&lt;/ol>
&lt;p>Sin embargo, hay que tener cuidado con la &lt;strong>división&lt;/strong>. En general, incluso si $a \times c \equiv b \times c \pmod n$, no podemos dividir ambos lados por $c$ y concluir que $a \equiv b \pmod n$. Esto solo es válido cuando $c$ y $n$ son coprimos (su máximo común divisor es 1). Este concepto de &amp;ldquo;inverso modular&amp;rdquo; será extremadamente importante en la generación de claves del cifrado RSA, que discutiremos más adelante.&lt;/p>
&lt;hr>
&lt;h2 id="3-contexto-matemático-y-demostración-del-pequeño-teorema-de-fermat">3. Contexto matemático y demostración del pequeño teorema de Fermat
&lt;/h2>&lt;p>Habiendo cubierto los fundamentos de la aritmética modular, echemos un vistazo al tema principal, el pequeño teorema de Fermat.&lt;/p>
&lt;h3 id="31-definición-del-teorema">3.1 Definición del teorema
&lt;/h3>&lt;p>El pequeño teorema de Fermat se formula de la siguiente manera:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Pequeño teorema de Fermat (Fermat&amp;rsquo;s Little Theorem)&lt;/strong>
Sea $p$ un número primo y sea $a$ cualquier número entero que no sea un múltiplo de $p$ (es decir, $a$ y $p$ son coprimos). Entonces, se cumple la siguiente congruencia:
&lt;/p>
$$ a^{p-1} \equiv 1 \pmod p $$
&lt;/blockquote>
&lt;p>También es común expresarlo en una forma que se aplica a todos los enteros $a$, eliminando la condición &amp;ldquo;que $a$ no sea un múltiplo de $p$&amp;rdquo;. En ese caso, multiplicamos ambos lados por $a$ para obtener lo siguiente:&lt;/p>
$$
a^p \equiv a \pmod p
$$
&lt;h3 id="32-confirmación-con-ejemplos-concretos">3.2 Confirmación con ejemplos concretos
&lt;/h3>&lt;p>Confirmemos si el teorema realmente se cumple utilizando números concretos.
Sea el número primo $p = 5$. Entonces $p-1 = 4$. Elegimos como $a$ enteros que no sean múltiplos de $p$.&lt;/p>
&lt;ul>
&lt;li>Caso $a = 2$: $2^{5-1} = 2^4 = 16$. $16 \div 5 = 3$ resto $1$. Por lo tanto, $16 \equiv 1 \pmod 5$. (Se cumple)&lt;/li>
&lt;li>Caso $a = 3$: $3^{5-1} = 3^4 = 81$. $81 \div 5 = 16$ resto $1$. Por lo tanto, $81 \equiv 1 \pmod 5$. (Se cumple)&lt;/li>
&lt;li>Caso $a = 4$: $4^{5-1} = 4^4 = 256$. $256 \div 5 = 51$ resto $1$. Por lo tanto, $256 \equiv 1 \pmod 5$. (Se cumple)&lt;/li>
&lt;/ul>
&lt;p>De esta manera, no importa qué $a$ elijas (siempre que no sea un múltiplo de 5), al elevarlo a la cuarta potencia y dividirlo por 5, el resto será siempre 1. Parece magia, pero esto proviene de las hermosas propiedades que poseen los números primos.&lt;/p>
&lt;h3 id="33-demostración-matemática-del-teorema">3.3 Demostración matemática del teorema
&lt;/h3>&lt;p>¿Por qué se cumple esto? Aquí presentamos una demostración elegante utilizando conjuntos de clases de residuos.&lt;/p>
&lt;p>Consideremos el conjunto $S = \{1, 2, 3, \dots, p-1\}$. Estos son los representantes de los números enteros cuyo resto al dividir por $p$ va de $1$ a $p-1$.
Ahora, consideramos un nuevo conjunto $T$ donde multiplicamos cada elemento por un entero $a$ que es coprimo con $p$.
&lt;/p>
$$ T = \{1a, 2a, 3a, \dots, (p-1)a\} $$
&lt;p>Consideremos los restos al dividir cada elemento de este conjunto $T$ por $p$. Sorprendentemente, estos restos coinciden completamente con el conjunto de elementos del conjunto original $S$, aunque el orden pueda cambiar.
Porque:&lt;/p>
&lt;ol>
&lt;li>Ningún elemento de $T$ será un múltiplo de $p$ (porque ni $a$ ni los elementos originales son múltiplos de $p$).&lt;/li>
&lt;li>No existen dos elementos diferentes en $T$ que sean congruentes módulo $p$. Si supusiéramos que $ia \equiv ja \pmod p$ ($i \neq j$), dado que $a$ y $p$ son coprimos, podríamos dividir por $a$ y obtener $i \equiv j \pmod p$, lo que es una contradicción.&lt;/li>
&lt;/ol>
&lt;p>Por lo tanto, el producto de todos los elementos de $S$ y el producto de todos los elementos de $T$ son congruentes módulo $p$.&lt;/p>
$$
(1a) \times (2a) \times \dots \times ((p-1)a) \equiv 1 \times 2 \times \dots \times (p-1) \pmod p
$$
&lt;p>Reorganizando el lado izquierdo, tenemos $p-1$ veces $a$, por lo que:&lt;/p>
$$
a^{p-1} \cdot (p-1)! \equiv (p-1)! \pmod p
$$
&lt;p>Dado que $(p-1)!$ es coprimo con $p$, podemos dividir ambos lados por $(p-1)!$, y finalmente se deriva el siguiente teorema:&lt;/p>
$$
a^{p-1} \equiv 1 \pmod p
$$
&lt;p>Esta es la demostración del pequeño teorema de Fermat.&lt;/p>
&lt;hr>
&lt;h2 id="4-función-totiente-de-euler-y-teorema-de-euler">4. Función totiente de Euler y teorema de Euler
&lt;/h2>&lt;p>El pequeño teorema de Fermat es un teorema sobre &amp;ldquo;números primos $p$&amp;rdquo;, pero fue Leonhard Euler quien generalizó esto a &amp;ldquo;cualquier entero positivo $n$&amp;rdquo;. Esta extensión es esencial para entender el cifrado RSA.&lt;/p>
&lt;h3 id="41-función-totiente-de-euler-phin">4.1 Función totiente de Euler $\phi(n)$
&lt;/h3>&lt;p>La función totiente de Euler (o función $\phi$ de Euler) $\phi(n)$ es una función que representa &amp;ldquo;la cantidad de enteros del $1$ al $n$ que son coprimos con $n$&amp;rdquo;.&lt;/p>
&lt;ul>
&lt;li>En el caso de un número primo $p$, todos los enteros del $1$ al $p-1$ son coprimos con $p$, por lo que $\phi(p) = p - 1$.&lt;/li>
&lt;li>Para dos números primos diferentes $p$ y $q$, y su producto $n = p \times q$, $\phi(n)$ se puede calcular con una fórmula muy simple:
$$ \phi(p \times q) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$&lt;/li>
&lt;/ul>
&lt;p>Esta propiedad es la lógica fundamental en la generación de claves del cifrado RSA.&lt;/p>
&lt;h3 id="42-teorema-de-euler">4.2 Teorema de Euler
&lt;/h3>&lt;p>Euler generalizó el pequeño teorema de Fermat de la siguiente manera:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Teorema de Euler (Euler&amp;rsquo;s Theorem)&lt;/strong>
Para cualquier entero positivo $n$ y un entero $a$ coprimo con $n$, se cumple lo siguiente:
&lt;/p>
$$ a^{\phi(n)} \equiv 1 \pmod n $$
&lt;/blockquote>
&lt;p>Si $n$ es un número primo $p$, entonces $\phi(p) = p - 1$, por lo que esto se convierte en el pequeño teorema de Fermat mismo ($a^{p-1} \equiv 1 \pmod p$). En otras palabras, el pequeño teorema de Fermat no es más que un caso especial del teorema de Euler.&lt;/p>
&lt;hr>
&lt;h2 id="5-encontrar-números-primos-gigantes-prueba-de-primalidad-de-fermat">5. Encontrar números primos gigantes: Prueba de primalidad de Fermat
&lt;/h2>&lt;p>En las tecnologías criptográficas (como el cifrado RSA y el intercambio de claves de Diffie-Hellman), es necesario encontrar rápidamente &amp;ldquo;números primos gigantes&amp;rdquo; de cientos de dígitos. Sin embargo, para determinar si un número gigantesco $N$ es primo, el método de &amp;ldquo;división por tentativa&amp;rdquo; (intentar dividir por todos los números desde $2$ hasta $\sqrt{N}$) tomaría un tiempo comparable a la edad del universo.&lt;/p>
&lt;p>Ahí es donde entra en juego la &lt;strong>prueba de primalidad de Fermat (Fermat Primality Test)&lt;/strong>, un &amp;ldquo;método de prueba de primalidad probabilística&amp;rdquo; que aprovecha el pequeño teorema de Fermat a la inversa.&lt;/p>
&lt;h3 id="51-qué-es-un-método-de-prueba-de-primalidad-probabilística">5.1 ¿Qué es un método de prueba de primalidad probabilística?
&lt;/h3>&lt;p>Según el pequeño teorema de Fermat, si $p$ es primo, para cualquier $a$ ($1 &lt; a &lt; p$), se debe cumplir obligatoriamente $a^{p-1} \equiv 1 \pmod p$.
Tomando la contrapuesta de esto, podemos decir que &amp;ldquo;si para algún $a$, $a^{p-1} \not\equiv 1 \pmod p$, entonces $p$ &lt;strong>definitivamente no es primo (es un número compuesto)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;p>Por lo tanto, si queremos determinar si $N$ es primo, elegimos varios $a$ al azar, calculamos $a^{N-1} \pmod N$ y verificamos si el resultado es $1$. Si se obtiene una respuesta distinta de $1$ incluso una vez, se confirma que $N$ es un número compuesto. Si el resultado es $1$ repetidas veces, podemos juzgar con alta probabilidad que $N$ es &amp;ldquo;probablemente un número primo&amp;rdquo;.&lt;/p>
&lt;h3 id="52-explicación-del-algoritmo-y-diagrama-de-flujo">5.2 Explicación del algoritmo y diagrama de flujo
&lt;/h3>&lt;p>El algoritmo de la prueba de Fermat es el siguiente:&lt;/p>
&lt;div class="mermaid">flowchart TD
Start["Inicio"] --> Input["Ingresar el número p a probar y la cantidad de pruebas k"]
Input --> LoopStart["Bucle desde i = 0 hasta k-1"]
LoopStart --> Condition{"¿i &lt; k?"}
Condition -- "Sí" --> RandomA["Elegir un entero aleatorio a en el rango 1 &lt; a &lt; p-1"]
RandomA --> Calc["Calcular la exponenciación modular a^(p-1) mod p"]
Calc --> CheckPrime{"¿El resultado es 1?"}
CheckPrime -- "No" --> ReturnComposite["p es un número compuesto (confirmado)"]
CheckPrime -- "Sí" --> Increment["Incrementar i"]
Increment --> Condition
Condition -- "No" --> ReturnPrime["p es probablemente un número primo (probabilístico)"]
ReturnComposite --> End["Fin"]
ReturnPrime --> End&lt;/div>
&lt;h3 id="53-la-trampa-de-los-números-de-carmichael-pseudoprimos">5.3 La trampa de los números de Carmichael (pseudoprimos)
&lt;/h3>&lt;p>La prueba de Fermat es muy rápida, pero tiene un defecto importante. Existen números diabólicos que, a pesar de ser números compuestos, cumplen que $a^{N-1} \equiv 1 \pmod N$ para todos los $a$. Estos se denominan &lt;strong>números de Carmichael (Carmichael numbers)&lt;/strong>. El número de Carmichael más pequeño es $561$ ($3 \times 11 \times 17$).&lt;/p>
&lt;p>Debido a la existencia de los números de Carmichael, una prueba de Fermat pura por sí sola no puede determinar la primalidad de manera absoluta. Por lo tanto, en los sistemas criptográficos reales (como OpenSSL), se utiliza de forma estándar la &lt;strong>prueba de primalidad de Miller-Rabin&lt;/strong>, que es una versión mejorada de la prueba de Fermat. La prueba de Miller-Rabin puede desenmascarar los números de Carmichael, reduciendo la probabilidad de falsos positivos prácticamente a cero.&lt;/p>
&lt;h3 id="54-exponenciación-modular-rápida-método-de-cuadratura-sucesiva">5.4 Exponenciación modular rápida (método de cuadratura sucesiva)
&lt;/h3>&lt;p>En el algoritmo de la prueba de primalidad, es necesario calcular $a^{N-1} \pmod N$, pero si $N$ es gigantesco, $a^{N-1}$ se convierte en un número con una cantidad astronómica de dígitos que no cabe en la memoria de la computadora.
Esto se resuelve mediante el &lt;strong>método de cuadratura sucesiva (Exponentiation by Squaring)&lt;/strong> o exponenciación modular. Al tomar el módulo (mod N) en cada paso del cálculo, el valor siempre se mantiene menor que $N$, permitiendo que el cálculo sea extremadamente rápido (complejidad temporal $O(\log N)$).&lt;/p>
&lt;hr>
&lt;h2 id="6-implementación-de-la-prueba-de-primalidad-y-la-exponenciación-modular">6. Implementación de la prueba de primalidad y la exponenciación modular
&lt;/h2>&lt;p>Ahora, intentemos implementar la prueba de primalidad de Fermat y el método de cuadratura sucesiva en C++ y Python.&lt;/p>
&lt;h3 id="61-implementación-en-c">6.1 Implementación en C++
&lt;/h3>&lt;p>En C++, los tipos enteros estándar son propensos al desbordamiento (overflow), por lo que se requiere una biblioteca de enteros de precisión múltiple (como GMP) para manejar números enormes. Sin embargo, aquí mostraremos una implementación dentro del rango de enteros de 64 bits (&lt;code>unsigned long long&lt;/code>) para comprender el algoritmo.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;random&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Exponenciación modular rápida (a^b mod m) - Método de cuadratura sucesiva
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="nf">power_mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Si el bit menos significativo de b es 1, multiplicar a al resultado
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">result&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Extensión a 128 bits para evitar desbordamiento
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Elevar a al cuadrado
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Desplazamiento a la derecha de b (dividir a la mitad)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">/=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Prueba de primalidad de Fermat
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">iterations&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">random_device&lt;/span> &lt;span class="n">rd&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mt19937_64&lt;/span> &lt;span class="n">gen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rd&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uniform_int_distribution&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">iterations&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gen&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Si a^(p-1) mod p no es 1, es un número compuesto
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">power_mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Probablemente primo
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1000000007&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Número primo conocido
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">num&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is probably prime.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is composite.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-implementación-en-python">6.2 Implementación en Python
&lt;/h3>&lt;p>Los tipos enteros estándar de Python soportan enteros de precisión múltiple, por lo que no hay necesidad de preocuparse por el desbordamiento de dígitos. Además, la función incorporada de Python &lt;code>pow(a, b, m)&lt;/code> utiliza internamente el método de cuadratura sucesiva, por lo que es extremadamente rápida.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">random&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">iterations&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Prueba de primalidad probabilística usando el test de Fermat
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">iterations&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Elegir un número aleatorio a entre 2 y p-2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Calcular a^(p-1) mod p. La función pow incorporada es rápida.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span> &lt;span class="c1"># Compuesto confirmado&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span> &lt;span class="c1"># Probablemente primo&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Prueba&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">number_to_test&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">104729&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> es probablemente primo.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> es un número compuesto.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-aplicación-al-cifrado-rsa-donde-fermat-y-euler-convergen">7. Aplicación al cifrado RSA: Donde Fermat y Euler convergen
&lt;/h2>&lt;p>La mayor aplicación del pequeño teorema de Fermat (y del teorema de Euler) es el &lt;strong>cifrado RSA&lt;/strong>, desarrollado en 1977 por Rivest, Shamir y Adleman.
El cifrado RSA es un sistema revolucionario de &amp;ldquo;criptografía de clave pública&amp;rdquo;, que realiza un mecanismo en el que la clave para cifrar (clave pública) se publica en todo el mundo, mientras que la clave para descifrar (clave privada) es conocida únicamente por el propio receptor.&lt;/p>
&lt;p>Esta asimetría se basa en la seguridad computacional de que &amp;ldquo;la factorización en números primos de números compuestos enormes es extremadamente difícil&amp;rdquo;.&lt;/p>
&lt;h3 id="71-funcionamiento-del-cifrado-rsa-generación-de-claves-cifrado-descifrado">7.1 Funcionamiento del cifrado RSA (generación de claves, cifrado, descifrado)
&lt;/h3>&lt;p>Veamos el flujo de comunicación general del cifrado RSA con un diagrama de secuencia de Mermaid.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Alice["Alice (Receptora)"]
participant Bob["Bob (Remitente)"]
Alice->>Alice: "Generar primos gigantes p, q"
Alice->>Alice: "Calcular N = p * q, φ(N) = (p-1)(q-1)"
Alice->>Alice: "Calcular clave pública e y clave privada d (e*d ≡ 1 mod φ(N))"
Alice->>Bob: "Enviar clave pública (N, e)"
Note over Bob: "Preparar texto plano M (M &lt; N)"
Bob->>Bob: "Calcular texto cifrado C = M^e mod N"
Bob->>Alice: "Enviar texto cifrado C"
Alice->>Alice: "Calcular texto plano M = C^d mod N y descifrar"&lt;/div>
&lt;p>A continuación, se explican los pasos matemáticos detallados.&lt;/p>
&lt;h4 id="paso-1-generación-de-claves-tarea-de-la-receptora-alice">Paso 1: Generación de claves (tarea de la receptora Alice)
&lt;/h4>&lt;ol>
&lt;li>Generar aleatoriamente dos números primos enormes, $p$ y $q$ (aquí se utiliza el método de prueba de primalidad mencionado anteriormente).&lt;/li>
&lt;li>Calcular su producto $N = p \times q$. Este $N$ se hace público.&lt;/li>
&lt;li>Utilizando la función totiente de Euler, calcular $\phi(N) = (p-1)(q-1)$.&lt;/li>
&lt;li>Elegir un número entero $e$ (exponente público) que sea coprimo con $\phi(N)$ (a menudo se usa $e = 65537$).&lt;/li>
&lt;li>Calcular el inverso modular $d$ (exponente privado) de $e$. Es decir, encontrar $d$ que cumpla lo siguiente:
$$ e \cdot d \equiv 1 \pmod{\phi(N)} $$
Para este cálculo, se utiliza el &lt;strong>algoritmo de Euclides extendido&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>Con esto, la &lt;strong>clave pública es $(N, e)$&lt;/strong> y la &lt;strong>clave privada es $(N, d)$&lt;/strong>. (Se deben descartar inmediatamente u ocultar estrictamente $p, q$ y $\phi(N)$).&lt;/p>
&lt;h4 id="paso-2-cifrado-tarea-del-remitente-bob">Paso 2: Cifrado (tarea del remitente Bob)
&lt;/h4>&lt;p>Supongamos que Bob quiere enviar el mensaje $M$ a Alice ($M$ es el texto convertido en valor numérico, donde $0 \le M &lt; N$).
Bob utiliza la clave pública de Alice $(N, e)$ para realizar el siguiente cálculo y crear el texto cifrado $C$.&lt;/p>
$$
C \equiv M^e \pmod N
$$
&lt;p>Este $C$ se transmite a Alice a través de la red.&lt;/p>
&lt;h4 id="paso-3-descifrado-tarea-de-la-receptora-alice">Paso 3: Descifrado (tarea de la receptora Alice)
&lt;/h4>&lt;p>Alice, habiendo recibido el texto cifrado $C$, utiliza la clave privada $d$ que solo ella conoce para realizar el siguiente cálculo.&lt;/p>
$$
M' \equiv C^d \pmod N
$$
&lt;p>Sorprendentemente, este resultado del cálculo $M'$ coincide exactamente con el mensaje original $M$.&lt;/p>
&lt;h3 id="72-por-qué-se-puede-descifrar-demostración-matemática">7.2 ¿Por qué se puede descifrar? (Demostración matemática)
&lt;/h3>&lt;p>Aquí es donde el pequeño teorema de Fermat (teorema de Euler) demuestra su verdadero valor. ¿Por qué $C^d \pmod N$ vuelve a $M$?&lt;/p>
&lt;p>Vamos a expandir la fórmula de descifrado.
Dado que $C \equiv M^e \pmod N$,
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>En el paso de generación de claves, elegimos $d$ de manera que $e \cdot d \equiv 1 \pmod{\phi(N)}$. Esto significa que existe un cierto entero $k$ tal que se puede escribir como sigue:
&lt;/p>
$$ e \cdot d = 1 + k \cdot \phi(N) $$
&lt;p>Sustituimos esto en la ecuación anterior.
&lt;/p>
$$ M^{ed} = M^{1 + k \cdot \phi(N)} = M \cdot M^{k \cdot \phi(N)} = M \cdot (M^{\phi(N)})^k \pmod N $$
&lt;p>Aquí es donde entra el &lt;strong>teorema de Euler&lt;/strong> ($M^{\phi(N)} \equiv 1 \pmod N$). (※Estrictamente hablando, $M$ y $N$ necesitan ser coprimos, pero en RSA la probabilidad de que $M$ y $N$ no sean coprimos es astronómicamente baja, y además, se puede demostrar que es válido incluso si no son coprimos utilizando el teorema chino del resto).&lt;/p>
&lt;p>Aplicando el teorema de Euler, dado que $M^{\phi(N)} \equiv 1$,
&lt;/p>
$$ M \cdot (1)^k \equiv M \pmod N $$
&lt;p>¡$M$ ha sido restaurado magníficamente! Las propiedades de los números descubiertas por Fermat y Euler hace cientos de años garantizan perfectamente la confidencialidad de las comunicaciones digitales modernas.&lt;/p>
&lt;hr>
&lt;h2 id="8-implementación-de-juguete-del-cifrado-rsa-python">8. Implementación de juguete del cifrado RSA (Python)
&lt;/h2>&lt;p>Como es difícil captar la idea solo con teoría, intentemos implementar el proceso de generación de claves, cifrado y descifrado del cifrado RSA usando Python. Esta es una &amp;ldquo;implementación de juguete&amp;rdquo; con fines educativos, pero las matemáticas utilizadas son exactamente las mismas que las reales.&lt;/p>
&lt;p>También incluiremos en la implementación el &amp;ldquo;algoritmo de Euclides extendido&amp;rdquo; para encontrar el inverso modular $d$.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">random&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Encontrar el máximo común divisor&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Algoritmo de Euclides extendido (encontrar x, y tal que ax + by = gcd(a,b))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Se usa para encontrar d donde e*d ≡ 1 (mod φ(N))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">Exception&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;El inverso modular no existe&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">phi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Función de generación de números primos (versión simple: genera primos pequeños)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getrandbits&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Determinación simple en lugar de la prueba de Fermat mencionada anteriormente&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">p&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Generación de par de claves RSA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">generate_keypair&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Asegurarse de que p y q no sean iguales&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># A menudo se usan primos como 65537 para e, pero aquí se elige al azar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randrange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randrange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Cálculo de la clave privada d&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Clave pública (e, n), Clave privada (d, n)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">encrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">plaintext&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Calcular plaintext^e mod n&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cipher&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">char&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">char&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">plaintext&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">cipher&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">decrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sk&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ciphertext&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Calcular cipher^d mod n, y volver al carácter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">plain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">chr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">char&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">char&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">ciphertext&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plain&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Ejemplo de ejecución&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;--- Implementación de juguete de cifrado RSA ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">public_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">private_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_keypair&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># Uso de primos de 12 bits&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Clave pública (e, n): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">public_key&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Clave privada (d, n): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">private_key&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Hello Math!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">Mensaje original: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Cifrado&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">encrypted_msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">encrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">public_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Texto cifrado: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">encrypted_msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Descifrado&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">decrypted_msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">decrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">private_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">encrypted_msg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Mensaje descifrado: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Al ejecutar este código, puedes confirmar cómo una matriz de caracteres se convierte en una matriz de números desconocidos (texto cifrado), y cómo la clave privada la restaura magníficamente a la cadena original.&lt;/p>
&lt;hr>
&lt;h2 id="9-conclusión-la-intersección-entre-la-belleza-matemática-y-la-utilidad-práctica">9. Conclusión: La intersección entre la belleza matemática y la utilidad práctica
&lt;/h2>&lt;p>En el siglo XVII, cuando Pierre de Fermat descubrió este &amp;ldquo;pequeño teorema&amp;rdquo;, nadie pensaba que sería útil para nada. El propio Fermat investigó la teoría de números por pura curiosidad matemática.&lt;/p>
&lt;p>Sin embargo, unos 300 años más tarde, en la década de 1970, en los albores de las redes informáticas, el teorema de Fermat hizo un regreso dramático como una tecnología criptográfica indispensable para establecer protocolos de comunicación seguros. La tecnología de prueba de primalidad basada en el pequeño teorema de Fermat, y el cifrado RSA basado en el teorema de Euler, sostienen literalmente la infraestructura moderna de Internet.&lt;/p>
&lt;p>Los mensajes de WhatsApp que enviamos de forma casual todos los días, y nuestras compras en Amazon, todo está bailando sobre esta fórmula simple y hermosa: $a^{p-1} \equiv 1 \pmod p$. El pequeño teorema de Fermat nos enseña que por muy abstractas que sean las matemáticas, siempre llegará un momento en el que sean útiles para la humanidad.&lt;/p>
&lt;p>Al estudiar programación o teoría criptográfica, comprender la estructura matemática que sirve como base será una gran herramienta para comprender profundamente el comportamiento de las bibliotecas que se proporcionan como cajas negras y para diseñar sistemas más seguros.&lt;/p></description></item><item><title>【Explicación matemática】Explicando el mecanismo del cifrado RSA para que hasta un estudiante de secundaria pueda entenderlo</title><link>http://kenji.blog/es/p/rsa-encryption-math-explained-for-beginners/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/rsa-encryption-math-explained-for-beginners/</guid><description>&lt;img src="http://kenji.blog/p/rsa-encryption-math-explained-for-beginners/img/eyecatch.jpg" alt="Featured image of post 【Explicación matemática】Explicando el mecanismo del cifrado RSA para que hasta un estudiante de secundaria pueda entenderlo" />&lt;p>Una de las tecnologías que sustenta la seguridad de la sociedad de internet desde su base es el &amp;ldquo;cifrado RSA&amp;rdquo;. Gran parte de las comunicaciones que usamos casualmente todos los días, como los pagos con tarjeta de crédito en compras en línea, los intercambios de mensajes en redes sociales con amigos y el envío o recepción de información confidencial de las empresas, están protegidas por este cifrado RSA o sus tecnologías sucesoras.&lt;/p>
&lt;p>Sin embargo, al escuchar &amp;ldquo;cifrado&amp;rdquo;, podrías imaginar máquinas de cifrado complejas como las que aparecen en las películas de espías, o matemáticas súper avanzadas que solo un puñado de genios puede entender. Es cierto que la teoría criptográfica moderna se basa en matemáticas avanzadas, pero &lt;strong>el mecanismo fundamental del cifrado RSA se puede entender perfectamente si tienes conocimientos de las matemáticas que se aprenden en la escuela secundaria (propiedades de los números enteros, números primos, congruencias, etc.)&lt;/strong>.&lt;/p>
&lt;p>En este artículo, tomando como punto de partida los conocimientos de matemáticas de secundaria, explicaremos exhaustivamente y paso a paso en qué principios matemáticos opera el cifrado RSA y por qué es tan difícil de descifrar. Explicaremos cuidadosamente con ejemplos concretos para que incluso aquellos a los que no se les dan muy bien las matemáticas puedan entenderlo.&lt;/p>
&lt;hr>
&lt;h2 id="1-cifrado-de-clave-simétrica-y-cifrado-de-clave-pública">1. Cifrado de clave simétrica y cifrado de clave pública
&lt;/h2>&lt;p>Antes de entrar en el mecanismo matemático del cifrado RSA, primero organicemos el concepto básico de la criptografía. Los sistemas criptográficos se dividen a grandes rasgos en dos tipos: &amp;ldquo;cifrado de clave simétrica&amp;rdquo; y &amp;ldquo;cifrado de clave pública&amp;rdquo;.&lt;/p>
&lt;h3 id="11-limitaciones-del-cifrado-de-clave-simétrica">1.1 Limitaciones del cifrado de clave simétrica
&lt;/h3>&lt;p>Muchos de los cifrados que se han utilizado desde la antigüedad son lo que se llama &amp;ldquo;sistemas de cifrado de clave simétrica&amp;rdquo;. Este es un sistema donde &lt;strong>se usa la misma clave para &amp;ldquo;cifrar (convertir un mensaje en un texto cifrado secreto)&amp;rdquo; y para &amp;ldquo;descifrar (devolver el texto cifrado al mensaje original)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Por ejemplo, supongamos que Alice envía una carta secreta a Bob. Alice pone la carta en una caja y la cierra usando un candado (clave simétrica). Para que Bob pueda abrir esa caja, necesita tener la misma clave que usó Alice.&lt;/p>
&lt;p>Este sistema tiene un gran problema: &amp;ldquo;El problema de la distribución de claves&amp;rdquo;. Cuando Alice y Bob, que están muy lejos, se comunican por primera vez, ¿cómo pueden compartir la clave sin que sea interceptada? Si la clave es robada por un tercero mientras se envía por correo, todas las comunicaciones cifradas posteriores quedarán completamente expuestas.&lt;/p>
&lt;h3 id="12-un-invento-revolucionario-sistema-de-cifrado-de-clave-pública">1.2 Un invento revolucionario: &amp;ldquo;Sistema de cifrado de clave pública&amp;rdquo;
&lt;/h3>&lt;p>Para resolver este problema de distribución de claves, se ideó el &amp;ldquo;sistema de cifrado de clave pública&amp;rdquo;. El cifrado RSA también es un tipo de este sistema.&lt;/p>
&lt;p>En el sistema de cifrado de clave pública, &lt;strong>se utilizan dos claves diferentes: &amp;ldquo;una clave para cifrar (clave pública)&amp;rdquo; y &amp;ldquo;una clave para descifrar (clave privada)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>El receptor, Bob, crea un par de &amp;ldquo;clave pública&amp;rdquo; y &amp;ldquo;clave privada&amp;rdquo;.&lt;/li>
&lt;li>Bob publica la &amp;ldquo;clave pública&amp;rdquo; al mundo entero (no importa quién la obtenga).&lt;/li>
&lt;li>La remitente, Alice, cifra el mensaje usando la &amp;ldquo;clave pública&amp;rdquo; de Bob y lo envía.&lt;/li>
&lt;li>El mensaje cifrado solo puede ser descifrado con la &amp;ldquo;clave privada&amp;rdquo; que únicamente Bob posee.&lt;/li>
&lt;/ol>
&lt;p>Usando la analogía del candado, Bob fabrica muchos &amp;ldquo;candados abiertos (clave pública)&amp;rdquo; y los esparce por todo el mundo. Alice pone el mensaje dirigido a Bob en una caja y la cierra usando uno de los candados que encontró de Bob. Una vez que el candado se cierra, solo se puede abrir con la &amp;ldquo;llave maestra (clave privada)&amp;rdquo; que tiene Bob. Aunque alguien robe la caja en el camino, no podrá abrirla sin la llave maestra.&lt;/p>
&lt;div class="mermaid">graph TD
A["Alice (Remitente)"] --> B["Texto plano (Mensaje)"]
B --> C["Proceso de cifrado"]
D["Clave pública de Bob (Disponible para cualquiera)"] --> C
C --> E["Enviado a través de internet: Texto cifrado"]
E --> F["Proceso de descifrado"]
G["Clave privada de Bob (Solo Bob la tiene)"] --> F
F --> H["Texto plano restaurado (Mensaje)"]
H --> I["Bob (Receptor)"]&lt;/div>
&lt;p>Para hacer realidad este revolucionario sistema, se necesita una especie de &lt;strong>&amp;ldquo;función unidireccional (un rompecabezas matemático de un solo sentido)&amp;rdquo;&lt;/strong> que signifique &amp;ldquo;es fácil cifrar con la clave pública, pero es absolutamente imposible descifrar sin la clave privada&amp;rdquo;. Las piezas del rompecabezas elegidas para esto fueron los conocidos &amp;ldquo;números primos&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="2-fundamento-matemático-1-que-sustenta-el-cifrado-rsa-números-primos-y-factorización-en-números-primos">2. Fundamento matemático 1 que sustenta el cifrado RSA: Números primos y factorización en números primos
&lt;/h2>&lt;p>La seguridad del cifrado RSA se basa en el hecho matemático de que &lt;strong>&amp;ldquo;la factorización en números primos de números gigantescos es extremadamente difícil&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;h3 id="21-qué-son-los-números-primos">2.1 Qué son los números primos
&lt;/h3>&lt;p>Los números primos son &amp;ldquo;números naturales mayores que 1 que solo se pueden dividir exactamente por 1 y por sí mismos&amp;rdquo;.
Ejemplo: $2, 3, 5, 7, 11, 13, 17, 19, 23...$&lt;/p>
&lt;p>Los números primos son como los &amp;ldquo;átomos&amp;rdquo; de todos los números enteros. Cualquier número natural se puede descomponer en forma de multiplicación de números primos. A esto se le llama &lt;strong>factorización en números primos&lt;/strong>. Por ejemplo, ignorando el orden, se sabe que cualquier número se puede factorizar de una sola manera, como $60 = 2^2 \times 3 \times 5$; esto se conoce como el &amp;ldquo;Teorema fundamental de la aritmética&amp;rdquo;.&lt;/p>
&lt;h3 id="22-la-dificultad-de-la-factorización-en-números-primos-función-unidireccional">2.2 La dificultad de la factorización en números primos (Función unidireccional)
&lt;/h3>&lt;p>Lo importante aquí es la asimetría: &lt;strong>&amp;ldquo;multiplicar es fácil, pero factorizar es difícil&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Por ejemplo, intenta calcular mentalmente la siguiente multiplicación de dos números primos:
$11 \times 13 = ?$
Es fácil, ¿verdad? La respuesta es $143$.&lt;/p>
&lt;p>Ahora, ¿qué tal el siguiente número?
Factoriza $323$ en números primos.
¿Cómo te va? Seguramente te tomará un poco de tiempo. (La respuesta es $17 \times 19$).&lt;/p>
&lt;p>Si los números son pequeños, un humano puede calcularlos de alguna manera, pero cuando los números se vuelven grandes, calcularlos se vuelve explosivamente difícil, incluso usando computadoras. En el cifrado RSA dominante actual, se utiliza un número $N = p \times q$ que es el resultado de multiplicar dos números primos colosales de 2048 bits (aproximadamente 600 dígitos en base 10), $p$ y $q$.&lt;/p>
&lt;p>Cuando se dan dos números primos gigantes $p$ y $q$, calcular $N$ toma solo un instante (menos de un milisegundo) para una computadora. Sin embargo, a la inversa, si solo se da $N$, encontrar los $p$ y $q$ originales tomaría tanto tiempo que ni siquiera la supercomputadora más rápida actual podría resolverlo en billones de años.&lt;/p>
&lt;p>Esta &lt;strong>&amp;ldquo;asimetría del cálculo (el camino de ida es fácil, el de vuelta es difícil)&amp;rdquo;&lt;/strong> se convierte en la base que crea la relación entre la clave pública y la clave privada.&lt;/p>
&lt;hr>
&lt;h2 id="3-fundamento-matemático-2-que-sustenta-el-cifrado-rsa-congruencias-aritmética-modular">3. Fundamento matemático 2 que sustenta el cifrado RSA: Congruencias (Aritmética modular)
&lt;/h2>&lt;p>Los cálculos del cifrado RSA no se realizan en el mundo de las sumas y multiplicaciones habituales donde los números crecen infinitamente, sino en el mundo del &amp;ldquo;resto&amp;rdquo; después de dividir por un número determinado. A esto se le llama &lt;strong>congruencia (aritmética modular)&lt;/strong>.&lt;/p>
&lt;h3 id="31-las-matemáticas-del-reloj">3.1 Las matemáticas del reloj
&lt;/h3>&lt;p>La aritmética modular a menudo se compara con las &amp;ldquo;matemáticas del reloj&amp;rdquo;. Si ahora son las 10, ¿qué hora será 5 horas después? $10 + 5 = 15$ en punto, pero en un reloj normal de 12 horas, respondemos &amp;ldquo;las 3&amp;rdquo;. Esto se debe a que el resto de dividir 15 entre 12 es 3.&lt;/p>
&lt;p>En el mundo de las matemáticas, esto se escribe de la siguiente manera:
&lt;/p>
$$ 15 \equiv 3 \pmod{12} $$
&lt;p>
Se lee como &amp;ldquo;15 es congruente con 3 módulo 12 (el resto de dividirlos por 12 es igual)&amp;rdquo;.&lt;/p>
&lt;h3 id="32-propiedades-básicas-de-las-congruencias">3.2 Propiedades básicas de las congruencias
&lt;/h3>&lt;p>Las congruencias tienen propiedades muy útiles y similares a las ecuaciones ($=$). Sea $N$ el módulo (número divisor).
Cuando $a \equiv b \pmod N$ y $c \equiv d \pmod N$, se cumple lo siguiente:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Suma:&lt;/strong> $a + c \equiv b + d \pmod N$&lt;/li>
&lt;li>&lt;strong>Resta:&lt;/strong> $a - c \equiv b - d \pmod N$&lt;/li>
&lt;li>&lt;strong>Multiplicación:&lt;/strong> $a \times c \equiv b \times d \pmod N$&lt;/li>
&lt;li>&lt;strong>Potenciación:&lt;/strong> $a^k \equiv b^k \pmod N$ ($k$ es un número natural)&lt;/li>
&lt;/ol>
&lt;p>Especialmente importante es la propiedad de la &amp;ldquo;potenciación&amp;rdquo;. Esto significa que &lt;strong>&amp;ldquo;el resto de una potencia es igual a la potencia del resto&amp;rdquo;&lt;/strong>.
Por ejemplo, supongamos que queremos encontrar el resto de dividir $7^{100}$ entre $5$. Sería muy difícil multiplicar $7$ cien veces y luego dividirlo entre $5$, pero usando la propiedad de las congruencias, como $7 \equiv 2 \pmod 5$, entonces $7^{100} \equiv 2^{100} \pmod 5$, lo que simplifica drásticamente el cálculo. Dado que en el mundo de la criptografía se manejan potencias de números muy grandes, esta propiedad es indispensable.&lt;/p>
&lt;hr>
&lt;h2 id="4-fundamento-matemático-3-que-sustenta-el-cifrado-rsa-la-función-de-euler-y-el-teorema-de-euler">4. Fundamento matemático 3 que sustenta el cifrado RSA: La función de Euler y el Teorema de Euler
&lt;/h2>&lt;p>A partir de aquí entran las matemáticas mágicas que son el núcleo del cifrado RSA. Aparece el &amp;ldquo;Teorema de Euler&amp;rdquo;, que es una generalización del &amp;ldquo;Pequeño teorema de Fermat&amp;rdquo;.&lt;/p>
&lt;h3 id="41-la-función-indicatriz-de-euler-phin">4.1 La función indicatriz de Euler $\phi(N)$
&lt;/h3>&lt;p>La función indicatriz de Euler (función $\phi$) es una función que, para un número natural dado $N$, devuelve &lt;strong>&amp;ldquo;la cantidad de números naturales desde el 1 hasta $N$ que son coprimos (su máximo común divisor es 1) con $N$&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Veamos algunos ejemplos:&lt;/p>
&lt;ul>
&lt;li>$\phi(5)$: Entre 1, 2, 3, 4, 5, los que son coprimos con 5 son 1, 2, 3 y 4 (son 4 números). Por lo tanto, $\phi(5) = 4$.&lt;/li>
&lt;li>$\phi(6)$: Entre 1, 2, 3, 4, 5, 6, los que son coprimos con 6 son 1 y 5 (son 2 números). Por lo tanto, $\phi(6) = 2$.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>[Propiedad especial en el caso de números primos]&lt;/strong>
Si $p$ es un número primo, todos los números del 1 al $p-1$ son coprimos con $p$. Por lo tanto,
&lt;/p>
$$ \phi(p) = p - 1 $$
&lt;p>&lt;strong>[Propiedad especial en el caso del producto de números primos]&lt;/strong>
Para dos números primos diferentes $p$ y $q$, si definimos $N = p \times q$, $\phi(N)$ se puede calcular fácilmente de la siguiente manera:
&lt;/p>
$$ \phi(N) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$
&lt;p>
Esta propiedad funciona como la &amp;ldquo;puerta trasera secreta (trapdoor)&amp;rdquo; del cifrado RSA. Una persona (el creador de la clave) que conoce $p$ y $q$ puede calcular $\phi(N)$ en un instante, pero un tercero que solo conoce $N$ no puede encontrar $\phi(N)$ a menos que factorice $N$ en números primos.&lt;/p>
&lt;h3 id="42-el-teorema-de-euler">4.2 El Teorema de Euler
&lt;/h3>&lt;p>Leonhard Euler demostró un teorema hermoso usando esta $\phi(N)$.&lt;/p>
&lt;p>&lt;strong>Teorema de Euler:&lt;/strong>
Cuando dos enteros $a$ y $N$ son coprimos, se cumple la siguiente congruencia:
&lt;/p>
$$ a^{\phi(N)} \equiv 1 \pmod N $$
&lt;p>Esta es una propiedad sorprendente que dice que &amp;ldquo;si multiplicas un número $a$ por sí mismo $\phi(N)$ veces y lo divides por $N$, el resto siempre será $1$&amp;rdquo;. (Cuando $N$ es un número primo $p$, se convierte en $a^{p-1} \equiv 1 \pmod p$, lo que se conoce como el Pequeño teorema de Fermat).&lt;/p>
&lt;p>Modifiquemos este teorema de Euler. Multiplicamos ambos lados por $a$ una vez más:
&lt;/p>
$$ a^{\phi(N) + 1} \equiv a \pmod N $$
&lt;p>Además, para cualquier entero $k$, dado que $a^{k \cdot \phi(N)}$ también se convierte en $1^k = 1$, se cumple la siguiente ecuación:
&lt;/p>
$$ a^{k \cdot \phi(N) + 1} \equiv a \pmod N $$
&lt;p>¡Esta fórmula es precisamente el principio fundamental que hace posible la magia del cifrado RSA de &lt;strong>&amp;ldquo;volver al estado original al cifrar y descifrar&amp;rdquo;&lt;/strong>!&lt;/p>
&lt;hr>
&lt;h2 id="5-el-algoritmo-del-cifrado-rsa-pasos-de-generación-de-claves-cifrado-y-descifrado">5. El algoritmo del cifrado RSA: Pasos de generación de claves, cifrado y descifrado
&lt;/h2>&lt;p>Ahora que tenemos los conocimientos básicos, veamos los pasos específicos del cifrado RSA. El cifrado RSA se divide a grandes rasgos en tres fases: &amp;ldquo;1. Generación de claves&amp;rdquo;, &amp;ldquo;2. Cifrado&amp;rdquo; y &amp;ldquo;3. Descifrado&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. Elegir números primos p, q"] --> A2["Calcular N = p × q"]
A1 --> A3["Calcular φ(N) = (p-1)(q-1)"]
A3 --> A4["Elegir e que sea coprimo con φ(N)"]
A3 --> A5["Calcular d tal que e × d ≡ 1 (mod φ(N))"]
A2 --> A6["Clave pública (N, e)"]
A4 --> A6
A5 --> A7["Clave privada d"]
B1["2. Mensaje en texto plano M"] --> B2["Calcular C ≡ M^e (mod N)"]
A6 -.-> B2
B2 --> B3["Enviar texto cifrado C"]
B3 --> C1["3. Texto cifrado recibido C"]
C1 --> C2["Calcular M ≡ C^d (mod N)"]
A7 -.-> C2
C2 --> C3["Obtener mensaje en texto plano original M"]&lt;/div>
&lt;h3 id="51-generación-de-claves-key-generation">5.1 Generación de claves (Key Generation)
&lt;/h3>&lt;p>El receptor, Bob, genera su propia &amp;ldquo;clave pública&amp;rdquo; y &amp;ldquo;clave privada&amp;rdquo;.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Selección de números primos:&lt;/strong> Elige dos números primos grandes $p$ y $q$ al azar.&lt;/li>
&lt;li>&lt;strong>Cálculo del módulo $N$:&lt;/strong> Calcula $N = p \times q$. Este $N$ se hará público.&lt;/li>
&lt;li>&lt;strong>Cálculo de $\phi(N)$:&lt;/strong> Calcula la función de Euler $\phi(N) = (p - 1)(q - 1)$. Este es un número secreto solo de Bob.&lt;/li>
&lt;li>&lt;strong>Selección de la clave pública $e$:&lt;/strong> Elige un entero $e$ tal que $1 &lt; e &lt; \phi(N)$ y que sea coprimo con $\phi(N)$.&lt;/li>
&lt;li>&lt;strong>Cálculo de la clave privada $d$:&lt;/strong> Encuentra un entero $d$ que cumpla la siguiente condición:
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
En otras palabras, es &amp;ldquo;un número $d$ tal que al dividir $e \times d$ entre $\phi(N)$, el resto es $1$&amp;rdquo;.&lt;/li>
&lt;/ol>
&lt;p>Con esto, la preparación de las claves está completa.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Clave pública:&lt;/strong> El par $(N, e)$. Se hace pública a todo el mundo.&lt;/li>
&lt;li>&lt;strong>Clave privada:&lt;/strong> $d$. No se le dice absolutamente a nadie.&lt;/li>
&lt;/ul>
&lt;h3 id="52-cifrado-encryption">5.2 Cifrado (Encryption)
&lt;/h3>&lt;p>Supongamos que Alice quiere enviarle a Bob el mensaje secreto $M$. (Donde $M$ es el texto convertido en un número y $0 \le M &lt; N$). Alice calcula lo siguiente usando la clave pública de Bob $(N, e)$:&lt;/p>
$$ C \equiv M^e \pmod N $$
&lt;p>Calcula &amp;ldquo;el resto $C$ de dividir el mensaje $M$ elevado a la potencia de $e$ entre $N$&amp;rdquo;. Este $C$ es el texto cifrado.&lt;/p>
&lt;h3 id="53-descifrado-decryption">5.3 Descifrado (Decryption)
&lt;/h3>&lt;p>Bob recibe el texto cifrado $C$. Bob calcula lo siguiente usando la clave privada $d$:&lt;/p>
$$ M \equiv C^d \pmod N $$
&lt;p>¡Al calcular &amp;ldquo;el resto de dividir el texto cifrado $C$ elevado a la potencia de $d$ entre $N$&amp;rdquo;, mágicamente se restaura el mensaje original $M$!&lt;/p>
&lt;hr>
&lt;h2 id="6-por-qué-el-descifrado-devuelve-el-texto-original-demostración-matemática">6. ¿Por qué el descifrado devuelve el texto original? (Demostración matemática)
&lt;/h2>&lt;p>Quizás te preguntes, &amp;ldquo;¿Por qué al elevar $C$ a la potencia de $d$ se vuelve al $M$ original?&amp;rdquo;. Aquí es donde el &amp;ldquo;Teorema de Euler&amp;rdquo; mencionado antes demuestra su poder.&lt;/p>
&lt;p>Sustituyamos la fórmula de cifrado $C = M^e$ en la fórmula de cálculo de descifrado $C^d \pmod N$.
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>Aquí, recuerda el paso 5 de la generación de claves. Cuando Bob creó $d$, lo eligió para que $e \times d \equiv 1 \pmod{\phi(N)}$. Esto significa que &amp;ldquo;$ed$ es un múltiplo de $\phi(N)$ más $1$&amp;rdquo;. Usando un número entero $k$, se puede escribir de la siguiente manera:
&lt;/p>
$$ ed = k \cdot \phi(N) + 1 $$
&lt;p>Sustituimos esto en la parte del exponente y lo descomponemos usando las leyes de los exponentes:
&lt;/p>
$$ M^{ed} = M^{k \cdot \phi(N) + 1} = M^{k \cdot \phi(N)} \times M^1 = (M^{\phi(N)})^k \times M $$
&lt;p>Aquí, asumiendo que el mensaje $M$ y $N$ son coprimos, según el &lt;strong>Teorema de Euler&lt;/strong>, $M^{\phi(N)} \equiv 1 \pmod N$.
&lt;/p>
$$ (M^{\phi(N)})^k \times M \equiv 1^k \times M \equiv M \pmod N $$
&lt;p>Por lo tanto, la siguiente fórmula se cumple perfectamente:
&lt;/p>
$$ C^d \equiv M \pmod N $$
&lt;p>Dado que Alice no conoce $d$ y un espía tampoco conoce $d$, ¡el único que puede extraer $M$ de $C$ es Bob, que tiene $d$!&lt;/p>
&lt;hr>
&lt;h2 id="7-ejemplo-concreto-experimentemos-el-rsa-calculando-a-mano-con-números-primos-pequeños">7. Ejemplo concreto: Experimentemos el RSA calculando a mano con números primos pequeños
&lt;/h2>&lt;p>Intentemos comunicarnos de forma cifrada desde Alice a Bob utilizando números pequeños (números primos) reales.&lt;/p>
&lt;p>&lt;strong>[Fase de generación de claves de Bob]&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Elige dos números primos $p=11$, $q=13$.&lt;/li>
&lt;li>Calcula $N = 11 \times 13 = 143$.&lt;/li>
&lt;li>Calcula $\phi(N) = (11 - 1) \times (13 - 1) = 10 \times 12 = 120$.&lt;/li>
&lt;li>Elige una clave pública $e$ coprima con $\phi(N)=120$. Aquí elegimos $e=7$.&lt;/li>
&lt;li>Calcula la clave privada $d$. Buscamos $d$ tal que $7 \times d \equiv 1 \pmod{120}$.
En la ecuación $7d = 120k + 1$, cuando $k=6$, esto es $721$, y $721 \div 7 = 103$.
Por lo tanto, $d = 103$.&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>Clave pública: $(N=143, e=7)$&lt;/li>
&lt;li>Clave privada: $d=103$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>[Fase de cifrado de Alice]&lt;/strong>
Supongamos que queremos enviar el mensaje $M = 9$.
Fórmula: $C \equiv 9^7 \pmod{143}$
$9^7 = 4,782,969$. Dividiendo esto por 143, da $33447$ con un resto de $48$.
El texto cifrado resultó ser $C = 48$.&lt;/p>
&lt;p>&lt;strong>[Fase de descifrado de Bob]&lt;/strong>
Bob recibe el texto cifrado $C = 48$ y lo descifra usando la clave privada $d = 103$.
Fórmula: $M \equiv 48^{103} \pmod{143}$
Si ejecutas &lt;code>(48 ** 103) % 143&lt;/code> en una calculadora, ¡el resultado es espectacularmente &amp;ldquo;&lt;strong>9&lt;/strong>&amp;rdquo;! Pudo recibir el mensaje original sin problemas.&lt;/p>
&lt;hr>
&lt;h2 id="8-cómo-calcular-la-clave-privada-d-algoritmo-de-euclides-extendido">8. Cómo calcular la clave privada $d$: Algoritmo de Euclides extendido
&lt;/h2>&lt;p>En el ejemplo de cálculo a mano, buscamos $k$ por intuición para encontrar $d=103$, pero cuando los números tienen cientos de dígitos, este método es imposible. En los programas reales, se utiliza un algoritmo llamado &lt;strong>&amp;ldquo;Algoritmo de Euclides extendido&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Resolver $7d \equiv 1 \pmod{120}$ es lo mismo que encontrar enteros $d, y$ que cumplan con $7d + 120y = 1$. Se puede encontrar esto mecánicamente haciendo el proceso inverso del algoritmo de Euclides.&lt;/p>
&lt;ol>
&lt;li>$120 \div 7 = 17$ con resto $1$&lt;/li>
&lt;li>Transformando esto, $1 = 120 - 17 \times 7$&lt;/li>
&lt;li>En otras palabras, $-17 \times 7 \equiv 1 \pmod{120}$&lt;/li>
&lt;/ol>
&lt;p>$-17$ significa lo mismo que $120 - 17 = 103$ en el mundo del módulo $120$. Por lo tanto, $d = 103$ se encuentra en un instante. Este método puede calcular muy rápido sin importar cuán gigante sea el número.&lt;/p>
&lt;hr>
&lt;h2 id="9-otra-cara-del-cifrado-rsa-firmas-digitales">9. Otra cara del cifrado RSA: Firmas digitales
&lt;/h2>&lt;p>Lo maravilloso del cifrado RSA es que, invirtiendo los roles de la clave pública y la clave privada, también se puede utilizar como &lt;strong>&amp;ldquo;firma digital&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>En el cifrado, era &amp;ldquo;cifrar con clave pública $\Rightarrow$ descifrar con clave privada&amp;rdquo;, pero
en las firmas digitales, se siguen los pasos de &amp;ldquo;cifrar con clave privada $\Rightarrow$ descifrar con clave pública&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. Alice crea una firma con su clave privada"] --> A2["S ≡ M^d (mod N)"]
A2 --> A3["Envía el mensaje M y la firma S"]
A3 --> B1["2. Bob verifica la firma con la clave pública"]
B1 --> B2["Calcula M' ≡ S^e (mod N)"]
B2 --> B3["Comprueba si M' y M coinciden"]&lt;/div>
&lt;p>Alice convierte un mensaje utilizando su propia clave privada $d$ (esta es la firma $S$) y lo envía a Bob. Bob utiliza la clave pública de Alice $e$ para calcular la verificación. Si el resultado del cálculo coincide con el mensaje original, se prueba simultáneamente que &amp;ldquo;son datos que solo se pueden crear con la clave privada de Alice&amp;rdquo; y que &amp;ldquo;el mensaje no ha sido alterado en el camino&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="10-experimentando-el-cifrado-rsa-con-programación">10. Experimentando el cifrado RSA con programación
&lt;/h2>&lt;p>El cálculo de potencias, que es difícil de hacer a mano, se puede implementar muy fácilmente utilizando Python. El siguiente es un código de Python que permite experimentar la lógica central del cifrado RSA.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Calcular el máximo común divisor&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Encontrar la clave privada d (Usando la función integrada desde Python 3.8)&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Generación de claves&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Clave pública: (N=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, e=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">), Clave privada: d=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Cifrado&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Texto cifrado: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Descifrado&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">decrypted_message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Mensaje descifrado: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>La función &lt;code>pow(base, exp, mod)&lt;/code> de Python utiliza internamente un algoritmo rápido llamado &amp;ldquo;exponenciación binaria&amp;rdquo; (repetir cuadrados), por lo que incluso con números de cientos de dígitos, el cálculo finaliza en un instante.&lt;/p>
&lt;hr>
&lt;h2 id="11-resumen-y-futuras-tecnologías-criptográficas">11. Resumen y futuras tecnologías criptográficas
&lt;/h2>&lt;p>Hemos desentrañado el mecanismo del cifrado RSA basándonos en los conocimientos de matemáticas de secundaria.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>La dificultad de la factorización en números primos:&lt;/strong> $p \times q = N$ es fácil, pero encontrar $p, q$ a partir de $N$ es muy difícil.&lt;/li>
&lt;li>&lt;strong>Congruencias y el Teorema de Euler:&lt;/strong> A través de la ley $a^{\phi(N)} \equiv 1 \pmod N$, se completa la puerta trasera mágica que dice &amp;ldquo;al elevar a una potencia con un número determinado, se vuelve al original&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Clave pública y clave privada:&lt;/strong> Cualquiera puede cifrar, pero solo el receptor legítimo puede descifrar.&lt;/li>
&lt;/ol>
&lt;p>El $N$ del cifrado RSA utilizado actualmente tiene más de 600 dígitos, y tomaría más tiempo que la edad del universo factorizarlo en números primos, incluso si se movilizaran todas las supercomputadoras del mundo. Sin embargo, si la &amp;ldquo;computadora cuántica&amp;rdquo;, cuya investigación ha avanzado en los últimos años, se vuelve práctica en el futuro, existe la posibilidad de que esta factorización en números primos se resuelva en un instante mediante el &amp;ldquo;algoritmo de Shor&amp;rdquo;. Por lo tanto, actualmente se está avanzando a un ritmo acelerado en todo el mundo en el desarrollo de la &amp;ldquo;criptografía poscuántica&amp;rdquo; que ni siquiera las computadoras cuánticas puedan descifrar.&lt;/p>
&lt;p>Las matemáticas avanzadas, que a menudo se consideran &amp;ldquo;inútiles&amp;rdquo;, en realidad protegen nuestra vida diaria desde su base. El cifrado RSA es el mejor material de enseñanza que nos enseña esa profundidad y belleza de las matemáticas. Sería genial si pudieras sentir, aunque sea un poco, la diversión de la criptografía y las matemáticas a través de este artículo.&lt;/p></description></item><item><title>¿Existe un algoritmo que supere a GNFS (Criba General del Cuerpo de Números)?</title><link>http://kenji.blog/es/p/beyond-gnfs-integer-factorization-algorithms/</link><pubDate>Fri, 11 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/beyond-gnfs-integer-factorization-algorithms/</guid><description>&lt;img src="http://kenji.blog/p/beyond-gnfs-integer-factorization-algorithms/img/eyecatch.jpg" alt="Featured image of post ¿Existe un algoritmo que supere a GNFS (Criba General del Cuerpo de Números)?" />&lt;h2 id="1-introducción-la-factorización-de-enteros-y-la-base-de-la-criptografía-moderna">1. Introducción: La factorización de enteros y la base de la criptografía moderna
&lt;/h2>&lt;p>La seguridad de las comunicaciones por internet en la sociedad moderna depende en gran medida de la seguridad del cifrado RSA, que es un sistema de cifrado de clave pública. Y la seguridad del cifrado RSA se basa en la suposición matemática de la &amp;ldquo;dificultad de factorizar enormes números compuestos&amp;rdquo;. Si se descubriera un algoritmo de factorización de enteros extremadamente eficiente, la infraestructura de comunicaciones de todo el mundo se derrumbaría desde sus cimientos.&lt;/p>
&lt;p>Actualmente, en la factorización de enteros enormes utilizando ordenadores clásicos, el algoritmo más rápido y poderoso que reina es la &lt;strong>Criba General del Cuerpo de Números (GNFS: General Number Field Sieve)&lt;/strong>. GNFS nació como una extensión de la Criba Especial del Cuerpo de Números (SNFS) propuesta a finales de la década de 1980, y hasta el día de hoy ha establecido récords de factorización de enormes números compuestos como RSA-768 y RSA-250.&lt;/p>
&lt;p>Sin embargo, los criptógrafos y matemáticos siempre se han planteado las siguientes preguntas: &amp;ldquo;¿Existe algún algoritmo clásico que supere a GNFS?&amp;rdquo;, &amp;ldquo;¿Dónde están los límites de los ordenadores clásicos?&amp;rdquo; y &amp;ldquo;¿Cómo cambiarán los ordenadores cuánticos esta situación?&amp;rdquo;.&lt;/p>
&lt;p>En este artículo, analizaremos en profundidad la profunda estructura matemática detrás de GNFS y realizaremos un análisis técnico detallado de la selección de polinomios, el proceso de cribado y los pasos de álgebra lineal utilizando el algoritmo de Wiedemann por bloques. Además, consideraremos los métodos de extensión de GNFS a través de las mejoras de Coppersmith y otros, y compararemos y explicaremos las diferencias decisivas entre los algoritmos clásicos de tiempo subexponencial (Sub-exponential time) y los algoritmos cuánticos de tiempo polinómico desde una perspectiva matemática.&lt;/p>
&lt;hr>
&lt;h2 id="2-complejidad-asintótica-y-notación-l-l-notation">2. Complejidad asintótica y notación L (L-notation)
&lt;/h2>&lt;p>Al evaluar la complejidad de los algoritmos de factorización de enteros, en lugar de utilizar la notación estándar de tiempo polinómico (como $O(n^k)$), se utiliza la &lt;strong>notación L (L-notation)&lt;/strong> para expresar el tiempo subexponencial con respecto al número de dígitos de la entrada $n$. La notación L se define de la siguiente manera:&lt;/p>
$$
L_n[\alpha, c] = \exp \left( (c + o(1)) (\ln n)^\alpha (\ln \ln n)^{1-\alpha} \right)
$$
&lt;p>Aquí, $n$ es el número entero a factorizar, y $\ln n$ es el logaritmo natural, el cual es proporcional a la longitud de bits de $n$.&lt;/p>
&lt;ul>
&lt;li>Caso $\alpha = 0$: $L_n[0, c] = \exp(c \ln \ln n) = (\ln n)^c$, lo que representa un &lt;strong>tiempo polinómico (Polynomial time)&lt;/strong> con respecto a la longitud de bits.&lt;/li>
&lt;li>Caso $\alpha = 1$: $L_n[1, c] = \exp(c \ln n) = n^c$, lo que representa un &lt;strong>tiempo exponencial (Exponential time)&lt;/strong> con respecto a la longitud de bits.&lt;/li>
&lt;li>Caso $0 &lt; \alpha &lt; 1$: Se sitúa entre el tiempo polinómico y el tiempo exponencial, lo que resulta en un &lt;strong>tiempo subexponencial (Sub-exponential time)&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>La historia de la evolución de los algoritmos de factorización de enteros ha sido también la historia de la reducción gradual de este valor $\alpha$.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Método de Fracciones Continuas (CFRAC) y Criba Cuadrática de Polinomios Múltiples (MPQS)&lt;/strong>: Pertenecen a la clase de $\alpha = 1/2$, y su complejidad es de alrededor de $L_n[1/2, 1]$.&lt;/li>
&lt;li>&lt;strong>Criba General del Cuerpo de Números (GNFS)&lt;/strong>: Alcanzó $\alpha = 1/3$, y cuenta con la complejidad de $L_n[1/3, (64/9)^{1/3}]$, la más rápida entre los algoritmos clásicos conocidos actualmente.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="3-visión-general-del-algoritmo-gnfs-criba-general-del-cuerpo-de-números-y-su-estructura-matemática">3. Visión general del algoritmo GNFS (Criba General del Cuerpo de Números) y su estructura matemática
&lt;/h2>&lt;p>GNFS tiene una base matemática sumamente compleja y avanzada. La idea básica se encuentra en la extensión del pequeño teorema de Fermat y la criba cuadrática (QS), y consiste en encontrar un par no trivial $(X, Y)$ que satisfaga la congruencia $X^2 \equiv Y^2 \pmod n$ y que $X \not\equiv \pm Y \pmod n$, para luego derivar un factor $\gcd(X-Y, n)$ de $n$.&lt;/p>
&lt;p>Sin embargo, la esencia de GNFS es que no realiza esto solo en el cuerpo de los números racionales $\mathbb{Q}$, sino que busca simultáneamente &amp;ldquo;números suaves (Smooth numbers)&amp;rdquo; tanto en un cuerpo de extensión llamado cuerpo de números algebraicos (Algebraic Number Field) $\mathbb{Q}(\alpha)$ como en el cuerpo de los números racionales, y construye la relación de congruencia a través de un homomorfismo.&lt;/p>
&lt;p>El proceso de GNFS se divide principalmente en 5 fases.&lt;/p>
&lt;div class="mermaid">graph TD
A["Problema de factorización (entrada n)"] --> B["1. Selección de polinomios (Polynomial Selection)"]
B --> C["2. Fase de cribado (Sieving Phase)"]
C --> D["3. Fase de filtrado (Filtering Phase)"]
D --> E["4. Fase de álgebra lineal (Linear Algebra Phase)"]
E --> F["5. Fase de raíz cuadrada (Square Root Phase)"]
F --> G["Salida de factores primos p, q"]&lt;/div>
&lt;h3 id="31-fase-1-selección-de-polinomios-polynomial-selection">3.1 Fase 1: Selección de polinomios (Polynomial Selection)
&lt;/h3>&lt;p>El éxito de GNFS depende en gran medida de la elección adecuada de los polinomios. El objetivo es encontrar dos polinomios irreducibles, $f_1(x)$ (lado racional) y $f_2(x)$ (lado algebraico), que compartan una raíz común $m$. Es decir, satisfacen:
$f_1(m) \equiv f_2(m) \equiv 0 \pmod n$&lt;/p>
&lt;p>Generalmente, para el polinomio del lado racional se elige una expresión lineal $f_1(x) = x - m$, y para el polinomio del lado algebraico $f_2(x)$ se elige un polinomio mónico de grado $d$ (típicamente 5 o 6). El enfoque más clásico es el &lt;strong>método de base $m$ (Base-$m$ method)&lt;/strong>.
Se elige un entero $m = \lfloor n^{1/(d+1)} \rfloor$ que se aproxima a $n$ elevado a $1/(d+1)$, y se expande $n$ en base $m$.
$n = c_d m^d + c_{d-1} m^{d-1} + \dots + c_1 m + c_0$
De este modo, se obtiene el polinomio $f_2(x) = c_d x^d + c_{d-1} x^{d-1} + \dots + c_0$. Obviamente, $f_2(m) = n \equiv 0 \pmod n$.&lt;/p>
&lt;p>Sin embargo, en las implementaciones modernas se utiliza el &lt;strong>algoritmo de Kleinjung&lt;/strong>. Éste optimiza las propiedades algebraicas (como el valor $E$ de Murphy o el valor $\alpha$) evitando que los coeficientes del polinomio se vuelvan extremadamente grandes (optimización de asimetría o Skewness), y busca polinomios que faciliten la generación de números suaves en el proceso de cribado. Solamente en este paso se invierten enormes recursos computacionales.&lt;/p>
&lt;h3 id="32-fase-2-fase-de-cribado-sieving-phase">3.2 Fase 2: Fase de cribado (Sieving Phase)
&lt;/h3>&lt;p>Una vez que se deciden los polinomios, se entra en la fase de &amp;ldquo;cribado (Sieving)&amp;rdquo;, que es la que soporta la mayor carga computacional del algoritmo. Aquí se busca un par $(a, b)$. Se requiere que este par sea coprimo, y que los dos valores siguientes sean simultáneamente &amp;ldquo;suaves (Smooth)&amp;rdquo;.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Norma en el lado racional&lt;/strong>: $F_1(a, b) = b \cdot f_1(a/b) = a - bm$&lt;/li>
&lt;li>&lt;strong>Norma en el lado algebraico&lt;/strong>: $F_2(a, b) = b^d \cdot f_2(a/b)$&lt;/li>
&lt;/ol>
&lt;p>&amp;ldquo;Suave&amp;rdquo; significa que puede ser factorizado únicamente por números primos que se encuentren por debajo de un límite especificado (Sieve bound). Se preparan una base de factores (Factor base) para el lado racional y otra para el lado algebraico, y sobre un enorme espacio de búsqueda, se descubren eficientemente los números suaves utilizando un enfoque similar a la Criba de Eratóstenes.
En la actualidad, la técnica principal es el método conocido como &lt;strong>criba de retículos (Lattice Sieving)&lt;/strong>, en la cual, al fijar un número primo específico $q$, se criban únicamente los pares $(a, b)$ de una subred que hacen que tanto el lado racional como el algebraico sean múltiplos de $q$, logrando así una eficiencia extremadamente alta.&lt;/p>
&lt;h3 id="33-fase-3-fase-de-filtrado-filtering-phase">3.3 Fase 3: Fase de filtrado (Filtering Phase)
&lt;/h3>&lt;p>El número de relaciones suaves encontradas en el proceso de cribado puede alcanzar cientos o miles de millones. Sin embargo, estas también incluyen mucha información inútil.
El objetivo del filtrado es construir una enorme matriz dispersa (Sparse Matrix) y, al mismo tiempo, reducir su dimensión lo máximo posible.&lt;/p>
&lt;p>Específicamente se realizan las siguientes operaciones:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Eliminación de relaciones únicas (Singleton removal)&lt;/strong>: Se eliminan las relaciones que contienen factores primos que aparecen solo una vez.&lt;/li>
&lt;li>&lt;strong>Eliminación y fusión de clanes (Clique removal / Merging)&lt;/strong>: Las relaciones con factores primos que aparecen dos o más veces se multiplican entre sí para eliminar variables, contrayéndolas en un sistema de ecuaciones más denso, pero de menor dimensión.&lt;/li>
&lt;/ul>
&lt;p>Como resultado, una matriz de miles de millones de filas se comprime a una enorme matriz dispersa $\mathbf{A}$ de unos pocos millones de filas (cuyos elementos son 0 y 1 sobre el cuerpo $\mathbb{F}_2$).&lt;/p>
&lt;h3 id="34-fase-4-fase-de-álgebra-lineal-linear-algebra-phase">3.4 Fase 4: Fase de álgebra lineal (Linear Algebra Phase)
&lt;/h3>&lt;p>Aquí se busca un vector solución no trivial $\mathbf{x}$ de la ecuación $\mathbf{A} \mathbf{x} \equiv \mathbf{0} \pmod 2$. En otras palabras, se trata del problema de encontrar el espacio nulo izquierdo (Left Nullspace) de la enorme matriz dispersa.&lt;/p>
&lt;p>Debido a que el tamaño de la matriz es extremadamente grande, es absolutamente imposible calcularlo con la eliminación Gaussiana habitual ($O(N^3)$). En su lugar, se emplean métodos iterativos que son un tipo de método de subespacio de Krylov. Históricamente, se ha utilizado el &lt;strong>método de Lanczos por bloques (Block Lanczos)&lt;/strong>, pero en el entorno de computación distribuida actual, el &lt;strong>algoritmo de Wiedemann por bloques (Block Wiedemann Algorithm)&lt;/strong>, que puede reducir drásticamente los cuellos de botella de la comunicación, es el predominante.&lt;/p>
&lt;p>El método de Wiedemann por bloques calcula el polinomio mínimo a partir de la matriz $\mathbf{A}$ y una secuencia de vectores, y utiliza el algoritmo de Berlekamp-Massey para construir la base del espacio nulo. Este paso es sumamente difícil de paralelizar y es uno de los mayores cuellos de botella de GNFS, ya que requiere redes de comunicación estrechamente acopladas de supercomputadoras o clústeres a gran escala.&lt;/p>
&lt;h3 id="35-fase-5-fase-de-raíz-cuadrada-square-root-phase">3.5 Fase 5: Fase de raíz cuadrada (Square Root Phase)
&lt;/h3>&lt;p>A partir de la solución del álgebra lineal, se construye un producto que será un &amp;ldquo;cuadrado perfecto&amp;rdquo; tanto en el lado racional como en el algebraico.
En el lado racional, el producto $\prod (a-bm)$ se convierte en el cuadrado $X^2$ de algún entero $X$, y en el lado algebraico, el producto de los ideales correspondientes se convierte en un cuadrado perfecto $\gamma^2$ sobre el cuerpo de los números algebraicos.
Al calcular este $\gamma$ en el cuerpo de los números algebraicos y aplicar el homomorfismo $\phi: \alpha \mapsto m \pmod n$ hacia el anillo de los enteros racionales, se obtiene la congruencia:
$X^2 \equiv \phi(\gamma)^2 \equiv Y^2 \pmod n$&lt;/p>
&lt;p>Para el cálculo de la raíz cuadrada en el cuerpo de los números algebraicos se utilizan algoritmos complejos como el &lt;strong>método de Montgomery (Montgomery&amp;rsquo;s Method)&lt;/strong>, lo cual exige un profundo conocimiento de la teoría de números algebraicos. Finalmente, se calcula $\gcd(X-Y, n)$ y, si se obtiene un factor no trivial, la factorización de enteros estará completada.&lt;/p>
&lt;hr>
&lt;h2 id="4-existe-un-algoritmo-clásico-que-supere-a-gnfs">4. ¿Existe un algoritmo clásico que supere a GNFS?
&lt;/h2>&lt;p>Hasta la fecha, no se ha descubierto ningún algoritmo clásico para la factorización de enteros generales cuya complejidad asintótica caiga por debajo de $L_n[1/3, c]$. Sin embargo, existen varios algoritmos derivados e intentos de superar los límites teóricos y prácticos.&lt;/p>
&lt;h3 id="41-criba-múltiple-del-cuerpo-de-números-mnfs-multiple-number-field-sieve">4.1 Criba Múltiple del Cuerpo de Números (MNFS: Multiple Number Field Sieve)
&lt;/h3>&lt;p>Como un enfoque extendido de GNFS, existe la &lt;strong>Criba Múltiple del Cuerpo de Números (MNFS)&lt;/strong> de D. Coppersmith. En GNFS se utilizan 2 polinomios (lado racional y lado algebraico), pero en MNFS se utilizan varios polinomios algebraicos diferentes simultáneamente contra un solo polinomio racional.&lt;/p>
$$ f_1(x), f_{2,1}(x), f_{2,2}(x), \dots, f_{2,V}(x) $$
&lt;p>Al utilizar múltiples cuerpos algebraicos, se puede aumentar drásticamente la probabilidad de que &amp;ldquo;sea suave en alguno de los cuerpos algebraicos&amp;rdquo; en cada paso del cribado. Coppersmith logró reducir ligeramente la constante $c$ de la complejidad computacional $L_n[1/3, c]$ mediante este enfoque.
Específicamente, se ha demostrado teóricamente que mientras la constante de GNFS es $c = (64/9)^{1/3} \approx 1.923$, al optimizar MNFS, el costo computacional puede reducirse hasta un valor cercano a $c \approx 1.902$.
Sin embargo, en la práctica, la carga adicional (overhead) de gestionar múltiples cuerpos es enorme y no se ha logrado un avance decisivo frente al módulo RSA a una escala de aplicación práctica.&lt;/p>
&lt;h3 id="42-es-posible-un-algoritmo-de-la-clase-l_n14">4.2 ¿Es posible un algoritmo de la clase $L_n[1/4]$?
&lt;/h3>&lt;p>Con respecto a los límites de los algoritmos clásicos de factorización de enteros, un tema largamente debatido entre los matemáticos es la pregunta: &amp;ldquo;¿Existe un algoritmo con exponente $\alpha = 1/4$?&amp;rdquo;.
El actual GNFS y sus derivados están fuertemente condicionados por el marco de &amp;ldquo;búsqueda de suavidad&amp;rdquo; a través de cribas, y se cree ampliamente que dentro de este paradigma, $\alpha = 1/3$ es el límite. A partir del análisis de la probabilidad de distribución de los enteros suaves usando la función de Dickman (Dickman function), se cree que con la actual combinación de construcción de cuerpos algebraicos y cribas, sin importar cómo se optimice, no se puede superar la barrera de $O(L_n[1/3])$.&lt;/p>
&lt;p>Si existiera un algoritmo de $L_n[1/4]$, o incluso un algoritmo clásico de tiempo polinómico, tendría que depender de una nueva estructura matemática completamente diferente del enfoque &amp;ldquo;basado en suavidad&amp;rdquo; como GNFS, que en la actualidad la humanidad no puede ni siquiera imaginar (por ejemplo, un enfoque geométrico-algebraico más avanzado, como el algoritmo de Schoof para la criptografía de curva elíptica). Sin embargo, por el momento, no hay indicios de ello.&lt;/p>
&lt;hr>
&lt;h2 id="5-el-gran-avance-gracias-a-la-computación-cuántica-el-algoritmo-de-shor">5. El gran avance gracias a la computación cuántica: El algoritmo de Shor
&lt;/h2>&lt;p>Mientras que los ordenadores clásicos se enfrentan a la barrera de $L_n[1/3]$, el &lt;strong>algoritmo de Shor (Shor&amp;rsquo;s Algorithm)&lt;/strong>, presentado por Peter Shor en 1994, destruyó esta barrera al cambiar fundamentalmente el propio modelo de cálculo.&lt;/p>
&lt;h3 id="51-el-impacto-del-tiempo-polinómico-cuántico">5.1 El impacto del tiempo polinómico cuántico
&lt;/h3>&lt;p>El algoritmo de Shor reduce el problema de factorización de enteros al &amp;ldquo;Problema de encontrar el orden (Order Finding Problem)&amp;rdquo;. Dado cierto entero $a$, es el problema de encontrar el período (orden) $r$ de la función $f(x) = a^x \pmod n$.
Un ordenador clásico requiere un tiempo exponencial para encontrar este período, pero usando la &lt;strong>Estimación Cuántica de Fase (QPE: Quantum Phase Estimation)&lt;/strong> y la &lt;strong>Transformada Cuántica de Fourier (QFT: Quantum Fourier Transform)&lt;/strong> en un ordenador cuántico, se puede evaluar de forma paralela en la superposición (Superposition) de todos los estados y extraer el período $r$ con alta probabilidad.&lt;/p>
&lt;p>Desde el punto de vista de la complejidad computacional, el tiempo de ejecución del algoritmo de Shor es de &lt;strong>tiempo polinómico cuántico&lt;/strong>, concretamente de la siguiente manera:
&lt;/p>
$$ O((\log n)^3) $$
&lt;p>
Teniendo en cuenta las implementaciones de circuitos optimizadas recientemente, se dice que es posible reducirlo hasta $O((\log n)^2 \log \log n)$.&lt;/p>
&lt;div class="mermaid">graph LR
A["Algoritmo clásico (GNFS)"] -->|Límite| B["Tiempo subexponencial L_n[1/3]"]
C["Algoritmo cuántico (Shor)"] -->|Gran avance| D["Tiempo polinómico O((log n)^3)"]
B --> E["Uso continuo del cifrado RSA (Aumento de longitud de clave)"]
D --> F["Colapso total del cifrado RSA"]&lt;/div>
&lt;h3 id="52-tiempo-subexponencial-clásico-vs-tiempo-polinómico-cuántico">5.2 Tiempo subexponencial clásico vs Tiempo polinómico cuántico
&lt;/h3>&lt;p>La diferencia entre estas dos clases de complejidad computacional tiene un significado crucial en la seguridad de la criptografía del mundo real.&lt;/p>
&lt;p>Por ejemplo, considere el caso de factorizar RSA-2048 (un número compuesto de 2048 bits).&lt;/p>
&lt;ul>
&lt;li>&lt;strong>GNFS (Clásico)&lt;/strong>: Al sustituir $n \approx 2^{2048}$ en $L_n[1/3, 1.923]$, se requieren aproximadamente $2^{112}$ operaciones. Esta es una cantidad astronómica de cálculos que tomaría más del tiempo de vida del universo, incluso combinando todos los recursos de computación de la Tierra en la actualidad.&lt;/li>
&lt;li>&lt;strong>Algoritmo de Shor (Cuántico)&lt;/strong>: Con el algoritmo de $O((\log n)^3)$, tomaría aproximadamente $2048^3 \approx 8.5 \times 10^9$ operaciones de puertas lógicas. Esto significa que si existiera el hardware adecuado (un ordenador cuántico universal con millones de cúbits físicos y capacidad de corrección de errores), el cálculo se completaría en tan solo unas horas o unos días.&lt;/li>
&lt;/ul>
&lt;p>El cambio de paradigma del tiempo subexponencial de &amp;ldquo;exponente $\alpha=1/3$&amp;rdquo; al &amp;ldquo;tiempo polinómico&amp;rdquo; neutraliza la estrategia tradicional de la criptografía de garantizar la seguridad extendiendo la longitud de la clave.&lt;/p>
&lt;hr>
&lt;h2 id="6-conclusión-perspectivas-hacia-la-próxima-generación">6. Conclusión: Perspectivas hacia la próxima generación
&lt;/h2>&lt;p>El consenso actual en la comunidad científica frente a la pregunta &amp;ldquo;¿Existe algún algoritmo clásico que supere a GNFS?&amp;rdquo; es el siguiente:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Las mejoras prácticas continúan, pero no hay un salto asintótico&lt;/strong>: Los intentos de mejorar el término constante $c$ de GNFS continúan, como en el caso de MNFS, la optimización de la selección de polinomios y la paralelización del algoritmo de Wiedemann por bloques. Sin embargo, se considera que la posibilidad de descubrir un algoritmo clásico que caiga por debajo de $\alpha = 1/3$ es extremadamente baja.&lt;/li>
&lt;li>&lt;strong>La seguridad de RSA en ordenadores clásicos sigue siendo robusta&lt;/strong>: La complejidad computacional de GNFS sigue siendo inmensa, y RSA-2048 y RSA-4096 seguirán siendo seguros frente a ataques a través de ordenadores clásicos durante las próximas décadas.&lt;/li>
&lt;li>&lt;strong>La verdadera amenaza es el algoritmo cuántico&lt;/strong>: El que ha cruzado la barrera de la complejidad computacional es el algoritmo de Shor, basado en los principios de la mecánica cuántica. Debido a esto, el mundo se está viendo obligado a migrar hacia la criptografía poscuántica (PQC: Post-Quantum Cryptography). La vanguardia de la criptografía actual consiste en la transición a nuevos problemas matemáticos, como la criptografía basada en retículos y la criptografía basada en hash, que se consideran difíciles de descifrar (imposibles de resolver en tiempo polinómico) incluso para los ordenadores cuánticos.&lt;/li>
&lt;/ol>
&lt;p>La Criba General del Cuerpo de Números (GNFS) es uno de los &amp;ldquo;mayores logros&amp;rdquo; alcanzados por la humanidad al desafiar los límites de las matemáticas clásicas y el diseño de algoritmos. Comprender la profunda estructura matemática de GNFS no es solo aprender sobre la historia del criptoanálisis, sino que también es un viaje de exploración intelectual para entrar en contacto con la belleza de la teoría de la complejidad computacional y la teoría de los números algebraicos. Hasta el día en que los ordenadores cuánticos se implementen de forma práctica, GNFS seguramente seguirá defendiendo su trono como el algoritmo de factorización de enteros más poderoso.&lt;/p></description></item></channel></rss>