Josep Portella
Criptograma inesperado
Julio de 2011
Revisado: septiembre de 2015 y enero de 2020
Esta obra está bajo una licencia de
Atribución-SinDerivadas 3.0 Creative Commons.
Llega un mensaje cifrado
—Menelao, te ha llegado un email de un tal Paris.
—¿Paris? Es un viejo amigo mío.
—Mira el contenido del email.
-----BEGIN BLAISE MESSAGE-----
B8 C0 CB B2 9F C9 B6 CC B2 BB B6 CA A6 90 C5 C4
91 C4 CF B4 D0 B6 B1 C4 9B CA B1 C3 C0 91 C3 C1
C4 CE B2 B3 BE CD C7 B5 9F 9F B6 D1 C1 C4 9E B9
B0 95 C8 BF BA C0 D1 91 CF C1 C3 D1 BC BD B6 9B
CB C5 B6 9F B9 C4 9C B4 CD BB BE B8 C4 BE BF 9F
9F CA C0 9C BF CD 8D BB C4 9B CA C5 B6 C3 C0 9F
BD B8 D3 AE BD C9 BC CC 90 BE C0 C4 AD
-----END BLAISE MESSAGE-----
—Parece que se trata de un mensaje cifrado, ¿pero por qué me lo envía?
Hace unos días me encontré con él después de años sin verle y estuvimos
charlando sobre… Ah, claro, recuerdo que le conté que últimamente no
estoy muy motivado; que no entran trabajos que supongan un reto
intelectual. Creo que debe haber enviado esto para que tenga algo
interesante en lo que pensar. ¡Muy amable de su parte!
—¿Vas a intentar descifrarlo?
—Los demás trabajos que tenemos en marcha no son especialmente urgentes,
así que no veo por qué no tendría que hacerlo. Empecemos. ¿Qué es lo que
podemos decir de este mensaje a primera vista?
—Lo más obvio es que tiene una marca de donde empieza y otra de donde
acaba.
—Sí, y además parece ser que se ha generado con un sistema de cifrado
llamado BLAISE
.
—No lo conozco.
—Yo tampoco. Busquemos en Internet a ver si podemos encontrar el
programa.
El sistema de cifrado “BLAISE”
—Ya veo, escribes un texto y una contraseña y te permite cifrarlo o
descifrarlo. Vamos a ver qué pasa si introducimos el mensaje cifrado de
Paris e intentamos descifrarlo con una contraseña cualquiera… Por
supuesto, se convierte en basura. Sigamos estudiando el mensaje cifrado.
¿Qué podemos decir sobre lo que hay entre las marcas de inicio y final
del mensaje?
—Parecen octetos en notación hexadecimal.
—Exacto. Pongamos en marcha el REPL de Racket para hacer
pruebas.
menelao@esparta:~$ racket
Welcome to Racket v7.5.
>
—Guardemos el contenido del mensaje en una variable.
(define msg "
B8 C0 CB B2 9F C9 B6 CC B2 BB B6 CA A6 90 C5 C4
91 C4 CF B4 D0 B6 B1 C4 9B CA B1 C3 C0 91 C3 C1
C4 CE B2 B3 BE CD C7 B5 9F 9F B6 D1 C1 C4 9E B9
B0 95 C8 BF BA C0 D1 91 CF C1 C3 D1 BC BD B6 9B
CB C5 B6 9F B9 C4 9C B4 CD BB BE B8 C4 BE BF 9F
9F CA C0 9C BF CD 8D BB C4 9B CA C5 B6 C3 C0 9F
BD B8 D3 AE BD C9 BC CC 90 BE C0 C4 AD
")
—Definamos un procedimiento para obtener el mensaje cifrado en crudo: tendrá que dividir la cadena por donde haya espacios o nuevas líneas y convertir cada número hexadecimal resultante en un entero.
(define (unarmor str)
(sequence-map (curryr string->number 16)
(string-split str)))
—Escribamos también un procedimiento para obtener la frecuencia de cada valor de forma ordenada.
(require math/statistics)
(define (sorted-frequencies nums)
(sort (hash->list (samples->hash nums))
< #:key cdr))
—Y obtengamos el resultado utilizando ambos procedimientos.
> (sorted-frequencies (unarmor msg))
'((158 . 1) (174 . 1) (200 . 1) (166 . 1)
(173 . 1) (179 . 1) (199 . 1) (176 . 1)
(206 . 1) (181 . 1) (149 . 1) (211 . 1)
(141 . 1) (186 . 1) (208 . 1) (204 . 2)
(207 . 2) (203 . 2) (180 . 2) (185 . 2)
(188 . 2) (177 . 2) (201 . 2) (156 . 2)
(144 . 2) (178 . 3) (187 . 3) (145 . 3)
(209 . 3) (193 . 3) (184 . 3) (191 . 3)
(155 . 3) (205 . 3) (197 . 3) (189 . 3)
(202 . 4) (195 . 4) (190 . 4) (192 . 6)
(159 . 7) (182 . 7) (196 . 9))
—Podemos ver que la frecuencia de los valores no está distribuida de
forma regular.
—¿Y qué significa eso?
—Que no se trata de un sistema criptográfico fuerte; seguramente lo ha
diseñado un aficionado.
Ingeniería inversa
—Cifrando el texto AAAA
con la contraseña AB
nos da el texto cifrado
AE AF AE AF
. Si ciframos BBBB
con la misma contraseña tenemos
AF B0 AF B0
, es decir lo mismo que antes pero con cada valor
incrementado en una unidad.
—¿Qué se deduce de esto?
—Que cada octeto del texto cifrado se obtiene aplicando una función a un
carácter del texto en claro y un carácter de la contraseña. El primer
carácter del texto con el primero de la contraseña, el segundo con el
segundo y así hasta que se acaba la contraseña. Entonces se vuelve a
empezar con el primer carácter de la contraseña. Sólo falta descubrir
una cosa: el número mágico.
—¿El número mágico?
—Un número fijo que también se usa en la función, que vamos a averiguar
restando al octeto del texto cifrado el valor de sus correspondientes
caracteres del texto en claro y de la contraseña.
> (- #xAE (char->integer #\A) (char->integer #\A))
44
> (- #xAF (char->integer #\A) (char->integer #\B))
44
—¿Lo ves? En ambos casos el número mágico es 44
. Ahora ya podemos
escribir el procedimiento de descifrado.
(define (decrypt nums keys)
(sequence-map
(lambda (n k)
(integer->char (modulo (- n k 44) 128)))
(in-parallel
nums (in-cycle (sequence-map char->integer
keys)))))
Ataque de diccionario
—Vamos a probar un ataque de diccionario. Como el programa sólo acepta palabras en mayúsculas y sin tildes, necesitaremos un procedimiento para adaptar las palabras del diccionario así como se vayan leyendo.
(define normalize-word
(compose (curryr string-replace #px"\\W+" "")
string-upcase string-normalize-nfkd))
—Y por último, un procedimiento que intenta descifrar el texto cifrado con cada una de las palabras del diccionario. En cada resultado buscará un prefijo para determinar si es la palabra correcta, y si coincide devolverá la palabra.
(define (attack nums word-list prefix)
(with-input-from-file word-list
(lambda ()
(for/first
((w (sequence-map normalize-word
(in-lines)))
#:when
(sequence-andmap
eqv? (in-parallel (decrypt nums w)
prefix)))
w))))
—Busquemos un archivo con un listado de palabras en español en Internet… Bien, este servirá. Ahora sólo
falta decidir qué prefijo ha de buscar.
—¿Qué tal si busca HOLA MENELAO
?
—Puede que funcione. Probemos.
> (attack (unarmor msg) "espanol.txt"
"HOLA MENELAO")
Texto en claro
—Vaya, me pregunto por qué Paris habrá escogido esta palabra como contraseña. Bueno, sólo queda utilizarla para descifrar el mensaje.
Menelao leyó el mensaje descifrado y durante unos segundos se quedó pensativo. De repente palideció, se levantó bruscamente y cogió el móvil.