Basado en "OctaPi: brute-force Enigma"
y el módulo py-enigma.
Licencia de "Creative Commons Attribution 4.0 International License" (CC BY 4.0).
Lo que vamos a hacer en este post es crear mensajes encriptados, mensajes secretos que solo tu y las personas en las que confíes podrán leer. Luego desarrollaremos código en Python para realizar un ataque criptográfico de fuerza bruta parcial sobre los mensajes de Enigma y recuperar la configuración de los rotores de la máquina.
¿Qué es la máquina Enigma y como funciona?
Antes de nada, para entender mejor el proceso, puedes ver como funciona la máquina enigma en este video.
Enigma es una máquina de cifrado que fue creada a principios del siglo XX para aplicaciones comerciales, diplomáticas y militares. Durante la Segunda Guerra Mundial, la máquina fue adoptada por el ejército alemán para comunicaciones secretas. El código de cifrado Enigma fue famoso por ser descifrado durante la guerra en Bletchley Park, el precursor del GCHQ, lo que permitió que los mensajes interceptados del ejército alemán pudieran ser descifrados y leídos. Este logro espectacular se cree que acortó la guerra, salvando muchas vidas en ambos lados del conflicto.
Desde un punto de vista eléctrico, la máquina Enigma es simplemente una batería, 26 bombillas y un circuito de interruptores. No tiene electrónica, por lo que es un dispositivo electro-mecánico. El cifrado se logra variando el camino de una corriente eléctrica a través del cableado de la máquina.
Pej. Si tecleamos una T, con una determinada configuración de los rotores, obtendremos una G como letra cifrada.
En el diagrama anterior, puedes ver cómo una letra tecleada en el teclado pasa por muchas etapas de transposición antes de ser dirigida a una bombilla en el tablero de luces que representa la letra cifrada. El usuario escribe su mensaje en texto normal en el teclado, carácter por carácter, y lee el texto cifrado a medida que cada bombilla se ilumina en el tablero de luces en respuesta. Debido a la forma en que se realiza la transposición, una letra tecleada nunca se cifra como ella misma (por ejemplo, teclear una A nunca iluminará la bombilla para A).
El diagrama puede dar la impresión de que la transposición de letras es invariable. Sin embargo, esto no es cierto; la forma en que se transponen las letras cambia con cada letra que se teclea en la máquina Enigma. ¡Eso es lo que hizo que el código Enigma fuera tan difícil de romper! La transposición cambia porque, a medida que se teclea cada letra, el camino de la corriente cambia a medida que fluye hacia las bombillas. ¿Cómo funciona esto?
Rotores y Reflectores.
En el interior de la máquina, varios rotores con 26 contactos (uno para cada letra de la A a la Z) están apilados juntos para crear el camino de la corriente a través del corazón de la máquina. Cada rueda del rotor tiene 26 contactos eléctricos en ambos lados y un enredo de cableado en el interior, de modo que las letras tecleadas se transponen de un lado al otro. En la práctica, esto significa que un rotor específico transponde la A en E, la B en K, la C en M, y así sucesivamente.
En la foto anterior, puedes ver el lio de cables dentro de una rueda de rotor expandida de una máquina Enigma capturada en la Segunda Guerra Mundial. Al apilar varios rotores y usar un reflector al final para devolver la corriente a través de los rotores, cada letra se transponde muchas veces. Al usar la máquina Enigma, se seleccionan tres rotores de cinco disponibles (también había máquinas con cuatro rotores). La configuración de transposición del reflector es fija, asegurando que la corriente regrese a través de la máquina sin invertir la transposición.
Entonces, ¿Cómo se cambia el camino de la corriente que fluye a través de estos componentes?
Movimiento de los rotores Para utilizar una transposición diferente caracter por caracter, el primer rotor avanza paso a paso a medida que se escribe cada letra del mensaje, creando una nueva ruta para la corriente cada vez. Como resultado, el usuario puede escribir 'LL' y ambas letras serán cifradas de manera diferente, por lo que el resultado podría ser 'XV'. Después de que el primer rotor haya avanzado 26 posiciones (una revolución completa), la máquina comienza a avanzar la posición del siguiente rotor, y así sucesivamente.
Posiciones iniciales de los rotores Parte de lo que hace que el cifrado de Enigma sea difícil de romper es el hecho de que cada rotor puede usarse con una posición inicial diferente. Por ejemplo, si un rotor se establece en la posición 10 al principio y se introduce la letra A en la máquina, no entrará donde la A entra por defecto, sino donde entra la J (letra 10 en el alfabeto) por defecto. Ten en cuenta que un rotor avanzará 26 pasos sin importar cuál sea su posición inicial.
Para facilitar su ajuste, el rotor está marcado con un anillo alfabético. Por lo tanto, una posición inicial de 10 se lograría ajustando el rotor de modo que la letra J sea visible; la posición inicial de "JFM" para tres rotores significaría ajustar el primer rotor a J, el segundo a F y el tercero a M.
Anillos deslizantes Además, las asignaciones de letras pueden desplazarse al girar un anillo deslizante en el rotor. Girar el anillo deslizante rota el cableado dentro del rotor. Por ejemplo, supongamos que con el anillo deslizante en la posición predeterminada, el cableado del rotor transpondría A en E, B en K, C en M, y así sucesivamente. Mover el anillo deslizante en 1 significaría que la letra A se transpondría en K, B en M, etc.
Tablero de conexiones Como si esto no fuera suficiente, la versión alemana de la máquina Enigma también incluye un tablero de conexiones (el cuadro verde más a la izquierda en el diagrama en la parte superior), que se puede ajustar manualmente para que hasta diez pares de letras se transpongan al entrar en los rotores y nuevamente al salir.
Configuraciones de cifrado Al combinar tres rotores de un conjunto de cinco, las configuraciones de rotor con 26 posiciones y el tablero de conexiones con diez pares de letras conectadas, la máquina Enigma utilizada por el ejército durante la Segunda Guerra Mundial tenía 158962555217826360000 (casi 159 quintillones) configuraciones diferentes.
El cifrado dependía de que tanto la máquina Enigma emisora como la receptora estuvieran configuradas de la misma manera. Para lograr esto, se usaban hojas de configuración secretas idénticas en las estaciones de comunicación emisoras y receptoras. Estas hojas especificaban:
- Qué rotores debían seleccionarse y en qué orden debían insertarse en la máquina.
- Cuánto debía girar cada rotor.
- Qué letras debían ser cambiadas por el tablero de conexiones.
- Qué posiciones iniciales de los rotores debían usarse.
Se utilizaban diferentes configuraciones de máquina cada día, y las posiciones iniciales de los rotores se cambiaban incluso cada seis horas, por lo que la configuración de la máquina era altamente sensible al tiempo. Por eso las hojas de configuración distribuidas por el ejército eran tan cuidadosamente guardadas.
Una línea de configuraciones de una hoja de Enigma capturada durante la Segunda Guerra Mundial
Las configuraciones en las que hemos hecho zoom son para el primer día del mes, de ahí el "1" en la segunda columna desde la izquierda.
La siguiente columna muestra que los rotores IV, I y V deben seleccionarse y usarse en ese orden.
La cuarta columna contiene las configuraciones de los anillos deslizantes: el rotor IV debe ajustarse a la posición 20, el rotor I a la posición 5 y el rotor V a la posición 10.
Luego vienen las conexiones del tablero de enchufes: S a X, K a U, Q a F, y así sucesivamente.
Finalmente, las posiciones iniciales de los rotores para los cuatro períodos de seis horas del día son "SRC", "EEJ", "FNZ" y "SZK". Además de eso, había dos reflectores, B y C, uno de los cuales se elegía para su uso. Para los programas de cifrado y descifrado aquí, asumiremos el uso del reflector B.
Clave única Para cada mensaje durante la Segunda Guerra Mundial, el remitente también seleccionaba tres caracteres por sí mismo como una clave única para ese mensaje, digamos "RPF". Cifraban esta clave usando las configuraciones de la hoja de configuraciones y anotaban el resultado, digamos "QMD". Luego procedían a cifrar su mensaje usando su clave única, en este caso "RPF", como las posiciones iniciales de los rotores, anotando el texto cifrado que la máquina devolvía. La versión cifrada de la clave, en este caso "QMD", más el texto cifrado, se enviaban al destinatario por radio.
Uso de la máquina enigma durante la segunda guerra mundial.
En la Segunda Guerra Mundial, los mensajes cifrados con Enigma solían enviarse en código Morse a través de radio de onda corta. Esto significaba que podían interceptarse fácilmente a cierta distancia, por lo que el ejército alemán dependía en gran medida de la fortaleza de la técnica de cifrado para mantener sus mensajes en secreto. Sin embargo, Gran Bretaña interceptó y descifró con éxito los mensajes en Bletchley Park.
Una transmisión cifrada con Enigma habría tenido un aspecto similar a este:
Mensaje cifrado
¿Cómo cifraban los operadores en las estaciones de comunicaciones?
Paso 1: Seleccionar los rotores y elegir una clave de mensaje de tres letras
Primero, el operador buscaba la línea en la hoja de configuraciones correspondiente al día del mes actual. Esto le indicaba cómo configurar la máquina Enigma, incluyendo qué rotores seleccionar y en qué orden colocarlos, así como la posición inicial de los rotores para el período de seis horas actual.
Paso 2: Elegir y cifrar una clave de mensaje de tres letras
Luego, el operador elegía una clave de mensaje de tres letras única y aleatoria para cada mensaje. Supongamos que pensó en "SCC" como la clave. Evidentemente, esta clave no podía enviarse abiertamente. Para cifrarla antes de la transmisión, el operador escribía "SCC" en la máquina Enigma configurada según la hoja, obteniendo (por ejemplo) "PWE" como la clave cifrada. Esta clave ya era segura para enviarse por un canal de radio.
Durante al menos una parte de la Segunda Guerra Mundial, el procedimiento militar alemán consistía en enviar y cifrar la clave del mensaje dos veces. Siguiendo nuestro ejemplo, el operador habría escrito "SCCSCC" y obtenido "PWEHVF".
¿Cuál es el problema de repetir la clave del mensaje?
Anteriormente mencionamos que ninguna letra del texto plano se cifra como sí misma. Esto significa que cualquier persona que intercepte un mensaje codificado con Enigma sabe que ninguna de las letras en la clave del mensaje descifrada puede ser la correcta. En nuestro ejemplo, interceptar la clave “PWE” nos dice que “P” no es la primera letra, “W” no es la segunda y “E” no es la tercera.
Si la clave del mensaje se envía dos veces, como solía hacer el ejército alemán, también sabemos que la primera letra no puede ser “H”, la segunda no puede ser “V” y la tercera no puede ser “F”. Esto reduce la cantidad de búsqueda necesaria para encontrar las letras del texto plano de la clave del mensaje, porque ya podemos excluir dos opciones para cada letra de la clave.
Paso 3: Cifrar el mensaje utilizando la clave del mensaje sin cifrar
Una vez que la clave del mensaje había sido elegida y cifrada, el operador ajustaba los rotores a la versión sin cifrar de la clave que había elegido y escribía el mensaje en el teclado.
Los números debían escribirse completamente en palabras, ya que la máquina Enigma no tenía teclas numéricas. Además, no había barra espaciadora, por lo que a menudo se usaba una ‘X’ para indicar un espacio. Por ejemplo, si queríamos cifrar “este mensaje es secreto”, escribiríamos “ESTEXMENSAJEXESXSECRETO”.
Paso 4: Enviar el mensaje cifrado por radio
Un operador de radio enviaba la clave cifrada y el mensaje en código Morse, utilizando distintivos de llamada y texto abreviado, de forma similar a cómo usamos abreviaturas en los mensajes de texto para reducir la cantidad de escritura necesaria.
Encriptando un mensaje.
Para poder emular la máquina "enigma" en python necesitaremos instalar el paquete py-enigma.
Crea un entorno virtual con:
python3 -m venv miEntorno
source ./miEntorno/bin/activate
pip3 install py-enigma
Luego utiliza el IDLE que más te guste y crea el archivo encriptar.py
Lo primero que tenemos que hacer es importar la clase EnigmaMachine desde el módulo py-enigma de la siguiente forma:
from enigma.machine import EnigmaMachine
Tendríamos que ver en que día del mes estamos pero para esta práctica vamos a suponer que estamos a día uno. Si consultamos la hoja de configuraciones de la máquina enigma que vimos anteriormente para el día uno vemos que son las siguientes:
En tu archivo de Python, configura un objeto EnigmaMachine utilizando los ajustes de tu hoja de configuraciones. Cada ajuste debe ser una cadena y escribirse exactamente como aparece en la hoja. Por ejemplo, los rotores se configurarían como
'IV I V'
.# configuración de la máquina enigma
machine = EnigmaMachine.from_key_sheet(
rotors='IV I IV',
reflector='B',
ring_settings='20 5 10',
plugboard_settings='SX KU QP VN JG TC LA WM OB ZF'
)
# Establecemos la posición inicial de los rotores.
machine.set_display('FNZ')
# Encripta el texto 'BFR' y guardalo como msg_key
msg_key = machine.process_text('BLA')
print(msg_key)
Si ejecutas el código deberías obtener la siguiente salida del programa:
(miEntorno) usuario:~/enigma$ python3 encriptar.py
LHU
Escribe una línea de código para restablecer las posiciones iniciales de los rotores a tu clave de cifrado (en nuestro ejemplo, “BLA”).
Luego, escribe un código para procesar el texto plano “LOSXRUSOSXSEXACERCAN” y mostrar el texto cifrado resultante.
# Encriptamos el texto plano a enviar
machine.set_display('BLA') # usamos la clave de cifrado BLA
ciphertext = machine.process_text('LOSXRUSOSXSEXACERCAN')
print(ciphertext)
Desencriptando el mensaje
LHU JLISVXPGICEZSMQFKXFK
Vamos a escribir código de Python que nos permita, utilizando el módulo Py-enigma simular que estamos usando una máquina enigma para desencriptar el mensaje.
# configuración de la máquina enigma
machine = EnigmaMachine.from_key_sheet(
rotors='IV I IV',
reflector='B',
ring_settings='20 5 10',
plugboard_settings='SX KU QP VN JG TC LA WM OB ZF'
)
# Establecemos la posición inicial de los rotores.
machine.set_display('FNZ')
# Desencriptamos el texto 'LHU' para obtener el msg_key
msg_key = machine.process_text('LHU')
print(msg_key)
# Reiniciamos la posición inicial de los rotores para decodificar el mensaje principal
# usando la clave original
machine.set_display(msg_key)
# Decodificamos el texto cifrado
texto_cifrado = 'JLISVXPGICEZSMQFKXFK'
texto_plano = machine.process_text(texto_cifrado)
print(texto_plano)
Ejemplo de procedimiento de comunicación.
La Wehrmacht tenía diversos procedimientos elaborados para transmitir y recibir mensajes. Estos procedimientos variaban según la rama del servicio y también cambiaban a lo largo de la guerra. En general, los procedimientos de la Kriegsmarine eran más complejos e implicaban no solo hojas de claves, sino también otros documentos auxiliares. Además, cada rama del ejército tenía sus propias convenciones para codificar abreviaturas, números, caracteres de espacio, nombres de lugares, etc. Palabras o frases importantes podían necesitar ser repetidas o resaltadas de alguna manera.
Supongamos que se necesita transmitir un mensaje. El operador de la máquina transmisora consulta su hoja de claves y configura su máquina de acuerdo con los ajustes diarios indicados en ella.
from enigma.machine import EnigmaMachine # Configuración de la máquina Enigma machine = EnigmaMachine.from_key_sheet( rotors='II IV V', reflector='B', ring_settings='2 21 12', plugboard_settings='AV BS CG DL FU HZ IN KM OW RX' ) # Establecemos la posición inicial de los rotores machine.set_display('WXC') # Desencriptamos el texto 'KCH' para obtener el msg_key msg_key = machine.process_text('KCH') print(msg_key)
# Reiniciamos la posición inicial de los rotores para decodificar el mensaje principal # usando la clave original machine.set_display(msg_key) # Decodificamos el texto cifrado texto_crudo = """EDPUD NRGYS ZRCXN UYTPO MRMBO FKTBZ REZKM LXLVE FGUEY SIOZV EQMIK UBPMM YLKLT TDEIS MDICA GYKUA CTCDO MOHWX MUUIA UBSTS LRNBZ SZWNR FXWFY SSXJZ VIJHI DISHP RKLKA YUPAD TXQSP INQMA TLPIF SVKDA SCTAC DPBOP VHJK""" # Unir todo el texto en una única línea y quitar espacios texto_cifrado = texto_crudo.replace("\n", "").replace(" ", "") texto_plano = machine.process_text(texto_cifrado) # Remplazamos las X por espacios texto_espaciado = texto_plano.replace("X", " ") print(texto_espaciado)
from enigma.machine import EnigmaMachine # Configuración de la máquina Enigma machine = EnigmaMachine.from_key_sheet( rotors='II IV V', reflector='B', ring_settings='2 21 12', plugboard_settings='AV BS CG DL FU HZ IN KM OW RX' ) # Establecemos la posición inicial de los rotores machine.set_display('CRS') # Desencriptamos el texto 'YPJ' para obtener el msg_key msg_key = machine.process_text('YPJ') print(msg_key) # Reiniciamos la posición inicial de los rotores para decodificar el mensaje principal # usando la clave original machine.set_display(msg_key) # Decodificamos el texto cifrado texto_crudo = """SFBWD NJUSE GQOBH KRTAR EEZMW KPPRB XOHDR OEQGB BGTQV PGVKB VVGBI MHUSZ YDAJQ IROAX SSSNR EHYGG RPISE ZBOVM QIEMM ZCYSG QDGRE RVBIL EKXYQ IRGIR QNRDN VRXCY YTNJR SBDPJ BFFKY QWFUS""" # Unir todo el texto en una única línea y quitar espacios texto_cifrado = texto_crudo.replace("\n", "").replace(" ", "") texto_plano = machine.process_text(texto_cifrado) Remplazamos las X por espacios texto_espaciado = texto_plano.replace("X", " ") print(texto_espaciado)
La traducción aproximada al español sería:
"Unidad de reconocimiento de Kurtinowa, noroeste de Sebez. Sebez en dirección de la carretera de avance Dubrowki, Opotschka. Movilizada a las 18:30 horas. Ataque del regimiento de infantería tres avanza lentamente pero de manera segura. A las 17:06 horas el regimiento de infantería tres en la carretera de avance a 16 km al este de Kamenec. Cuartel general de la división."
Algunas palabras pueden tener errores tipográficos o ser abreviaturas propias de la Wehrmacht, lo que podría afectar ligeramente la interpretación.