Claves de juguete: esta demo implementa el McEliece de 1978 con claves pequeñas y un generador pseudoaleatorio con semilla (para poder repetirlas y compartirlas). No la uses para proteger nada. Todo se calcula en tu navegador.
Parámetros y semilla
GF(16) entero como soporte; k = 8: se cifra un byte por bloque.
1 Genera las claves
La clave privada es un código de Goppa: un código corrector de errores que sabes decodificar deprisa. La pública es ese mismo código disfrazado para que parezca uno cualquiera, que es difícil de decodificar. Cada paso se despliega.
1.1 El cuerpo \mathrm{GF}(2^{4}) y su polinomio primitivo
Los números con los que trabajamos son los de \mathrm{GF}(2^{4}): polinomios en x con coeficientes 0 o 1, módulo el polinomio primitivo f(x) = x^{4} + x + 1. Tiene 2^{4} = 16 elementos: el 0 y las potencias \alpha^0, \dots, \alpha^{14}, donde \alpha es una raíz de f.
Cada elemento se escribe con 4 bits (los coeficientes de 1, x, x², …). Eso permite convertir una matriz sobre el cuerpo en una matriz binaria.
1.2 El polinomio de Goppa g(z), irreducible de grado t = 2
g(z) = z^{2} + \alpha^{9}\,z + 1. Se prueban polinomios mónicos de grado 2 al azar hasta encontrar uno irreducible (sin factores): han hecho falta 6 intentos.
Irreducible garantiza que el código corrige 2 errores y que el algoritmo de Patterson funciona.
1.3 El soporte L: 16 elementos del cuerpo
L = (\alpha_0, \dots, \alpha_{15}): elementos distintos del cuerpo que no son raíces de g. Cada uno es el «nombre» de una posición del mensaje.
1.4 La matriz de control H y su versión binaria
H = \left( \alpha_j^{\,i} / g(\alpha_j) \right)_{0 \le i < t,\quad 0 \le j < n}, de 2 filas y 16 columnas, sobre \mathrm{GF}(2^{4}).
Un vector binario c está en el código si y solo si H c^{T} = 0. Cada elemento se desdobla en sus 4 bits y sale una matriz binaria H_{\text{bin}} de
8 × 16 (rango 8).
| 1 | α^6 | α^14 | α^9 | α^14 | α | α^3 | α^3 | α^6 | α^5 | 1 | α^13 | α^9 | α^5 | α^13 | α |
| 0 | α^6 | 1 | α^11 | α^2 | α^5 | α^8 | α^9 | α^13 | α^13 | α^9 | α^8 | α^5 | α^2 | α^11 | 1 |
1.5 La matriz generadora G (8 × 16)
Sus 8 filas son una base del código: k = n - \operatorname{rango}(H_{\text{bin}}) = 16 - 8 = 8 .
Las columnas en verde forman un conjunto de información: ahí G es la identidad, así que el mensaje se lee directamente.
Comprobación: H_{\text{bin}}\, G^{T} = 0 ✓.
1.6 El disfraz: S (invertible, 8 × 8) y P (permutación, 16 × 16)
S mezcla las filas de G (cambia de base el mismo código) y P reordena las columnas. S se elige al azar entre las matrices binarias hasta que sea invertible: 1 intento.
S
P (un 1 por fila y por columna): la fila i tiene su 1 en la columna π(i), con π = (15, 8, 14, 0, 10, 3, 12, 9, 4, 6, 1, 5, 11, 7, 2, 13)
1.7 La clave pública G′ = S · G · P
G' = S\,G\,P: sigue generando un código equivalente al de Goppa, pero ya no se ve su estructura; parece una matriz aleatoria. Es la clave pública, junto con t = 2.
Ocupa 8 × 16 = 128 bits (16 bytes). La privada es (g, L, S, P).
2 Cifra un mensaje
El texto pasa a bytes (UTF-8) y se corta en bloques de 8 bits: un byte (una letra) por bloque. Cada bloque m se cifra por separado.
| Bloque | m (8 bits) | e (peso 2) | c = mG′ + e (16 bits) |
|---|---|---|---|
01001000 | 2, 12 | 1011100100001100 | |
01101111 | 8, 10 | 1100101010101010 | |
01101100 | 8, 15 | 1001111111110101 | |
01100001 | 7, 14 | 0000000011110011 |
Pulsa un bloque de la tabla para ver su detalle (aquí, el bloque 1 de 4) en los pasos de cifrado, descifrado y ataque.
Detalle del bloque 1
- El mensaje
m01001000 m\,G': una palabra de tu código público 1001100100000100- El error
e: exactamente 2 bits a 1, en posiciones al azar (numeradas desde 0) 0010000000001000 c = m\,G' + e: el texto cifrado (en rojo, los bits que el error ha cambiado) 1011100100001100
3 Descifra (con la clave privada)
Sigue el bloque 1. Solo el dueño de la clave conoce P, S y el código de Goppa.
- Deshaz la permutación:
y = c\,P^{-1} = (m S)\,G + e P^{-1}. El error cambia de sitio pero sigue pesando 2. 0001011010000111
3.2 El algoritmo de Patterson, paso a paso
Hay que averiguar dónde está e P^{-1}. Se calcula el síndrome de y, y con él un polinomio localizador de errores \sigma cuyas raíces (en el soporte) son las posiciones del error.
- Síndrome
S(z) = \sum_{y_j = 1} \frac{1}{z - \alpha_j} \bmod gS(z) = \alpha^{8}\,z + \alpha^{12} - Inverso
T(z) = S(z)^{-1} \bmod gT(z) = \alpha^{8}\,z + \alpha^{7} T(z) + z\alpha^{2}\,z + \alpha^{7}- Se escribe
T + z = Q_0^2 + z\,Q_1^2y se toma la raíz cuadradaR = Q_0 + \sqrt{z}\,Q_1 \bmod g(en característica 2 siempre existe):Q_0 = \alpha^{11},\quad Q_1 = \alpha,\quad \sqrt z = \alpha^{3}\,z + \alpha^{3}
R(z) = \alpha^{4}\,z + \alpha^{13} - Euclides extendido sobre
(g, R), hasta un resto de grado ≤ ⌊t/2⌋ = 1: aparecenaybcona \equiv b R \pmod g.cociente resto r u v z^2 + α^9·z + 1 1 0 α^4·z + α^13 0 1 a = \alpha^{4}\,z + \alpha^{13},\quad b = 1- El localizador
\sigma(z) = a(z)^2 + z\,b(z)^2\sigma(z) = \alpha^{8}\,z^{2} + z + \alpha^{11} - Raíces de
\sigmaentre los elementos del soporte → posiciones del error:\sigma(\alpha^{5}) = 0→ posición 6\sigma(\alpha^{13}) = 0→ posición 14
- Se quita el error:
y + e P^{-1} = (m S)\,G, una palabra del código de Goppa. 0001010010000101 - Como
Ges la identidad en el conjunto de informaciónJ, las coordenadas de la palabra enJsonm S(8 bits): 00010001 - Y se multiplica por la inversa de
S:m = (m S)\,S^{-1}. 01001000
4 Sé el atacante
Eres el atacante: solo tienes la clave pública G′ y el texto cifrado del bloque 1. El ataque de Prange elige 8 columnas al azar (un «conjunto de información»); si el error no cae en ninguna de ellas, con esas 8 columnas puedes despejar el mensaje con álgebra lineal y comprobar que lo que sobra pesa 2. Cada intento acierta con probabilidad baja; se repite hasta que sale.
Intentos esperados con estos parámetros: ≈ 15.
De juguete a real: la tabla de extrapolación
| Parámetros | n, t, k | Prange: iteraciones (log₂) | Prange: operaciones (log₂) | Prange: tiempo | Stern: operaciones (log₂) | Stern: tiempo | Clave pública, forma de 1978 (G′, k × n bits) | Clave pública de Classic McEliece (T) | Texto cifrado (Classic) |
|---|---|---|---|---|---|---|---|---|---|
| De juguete (el de arriba) | 16, 2, 8 | 2,1 | 11,7 | menos de 1 ms | 12,6 (p = 1, ℓ = 5) | menos de 1 ms | 16 B | — | — |
| McEliece 1978 | 1024, 50, 524 | 53,6 | 81,1 | 10⁷ años | 63,5 (p = 3, ℓ = 27) | 42,4 años | 65,5 KiB | — | — |
| mceliece348864 | 3488, 64, 2720 | 142,8 | 173,6 | 10³⁵ años | 147,4 (p = 3, ℓ = 36) | 10²⁷ años | 1,13 MiB | 255 KiB | 96 B |
| mceliece460896 | 4608, 96, 3360 | 184,9 | 217,4 | 10⁴⁸ años | 189,2 (p = 3, ℓ = 37) | 10³⁹ años | 1,85 MiB | 511,9 KiB | 156 B |
| mceliece6688128 | 6688, 128, 5024 | 262,4 | 296,3 | 10⁷² años | 264,8 (p = 5, ℓ = 57) | 10⁶² años | 4,01 MiB | 1020,5 KiB | 208 B |
| mceliece6960119 | 6960, 119, 5413 | 263,4 | 297,2 | 10⁷² años | 265,3 (p = 6, ℓ = 66) | 10⁶² años | 4,49 MiB | 1022,8 KiB | 194 B |
| mceliece8192128 | 8192, 128, 6528 | 300,1 | 334,4 | 10⁸³ años | 300,4 (p = 8, ℓ = 85) | 10⁷³ años | 6,38 MiB | 1,29 MiB | 208 B |
- Prange es el ataque genérico básico, no el mejor. Stern (1989) y sus sucesores lo mejoran mucho; por eso hay una columna con Stern, en un modelo sencillo y sin sus mejoras posteriores. Los parámetros reales se eligen frente a ataques todavía mejores y con modelos de coste más finos (memoria incluida), así que estas cifras son órdenes de magnitud y no el nivel de seguridad que declara Classic McEliece.
- La lección: con parámetros de juguete, el ataque acaba al instante (pruébalo arriba). Pasar a n = 3488 o más convierte el mismo ataque en un cálculo fuera de alcance. La seguridad de McEliece es esa distancia, no un secreto del algoritmo.
- El precio es el tamaño: la clave pública de Classic McEliece ocupa de cientos de kilobytes a más de un megabyte (en 1978 bastaban unos 65 KiB con n = 1024), pero el texto cifrado es muy corto.
- «Clave pública de Classic McEliece» es la matriz T de la versión de Niederreiter con clave sistemática: no es exactamente la G′ de esta demo (columna anterior), que se da para comparar.
¿Y los ataques de 2025-2026?
En 2025 y 2026 se han publicado trabajos nuevos contra los códigos de Goppa binarios de Classic McEliece. Lo demostrado es un distinguidor: permite reconocer que una clave pública no es una matriz aleatoria, pero no descifra nada. Las recuperaciones de clave son heurísticas, con estimaciones de coste por debajo del nivel de seguridad declarado en el modelo de operaciones de bit (que no cobra la memoria): se ha estrechado el margen, pero no hay ningún ataque práctico. El BSI alemán desaconseja Classic McEliece para desarrollos nuevos desde el 1 de octubre de 2026.
Esta demo es el McEliece original de 1978, no Classic McEliece. Léelo con calma en el artículo, con las fuentes.