Hace poco en Kriptópolis se publicó una entrada sobre el cifrado XOR. Aparentemente, este cifrado que en teoría es muy bueno, no era tan bueno a la hora de cifrar información que un humano pudiera reconocer. Así un archivo de sonido podía reconocerse después de usarse este cifrado «infalible» o al menos podía intuirse el contenido que se intentaba cifrar. En los comentarios a la entrada aparecían también ejemplos de imágenes que eran fácilmente reconocibles después de aplicarle este cifrado.
Tras mi primer contacto con este cifrado y después de mucho pensar no encontré ningún motivo por el que no debía funcionar. Así que pensé que lo mejor era probarlo yo mismo. Como siempre eché mano de mi amigo Python.
Las librerías que he usado son numpy y scikits.audiolab:
- Numpy es la librería favorita para trabajar eficientemente con conjuntos ordenados de números como vectores, matrices, etc. No está escrita íntegramente en Python con lo que puede requerir cierta compilación dependiendo de la versión que se instale aunque suele poder instalarse con facilidad.
- Audiolab es una librería para manipular archivos de sonido usando numpy. El manejo es sencillo viendo los ejemplos. Tampoco está escrita íntegramente en Python. Para la instalación tiene como dependencia la librería «libsndfile». Es posible instalarla manualmente en un Mac usando Macports.
Como entrada utilizamos el mismo archivo (1234) que se usaba como prueba en la entrada de Kriptopolis. Sin embargo el formato del archivo es «wav» en lugar de mp3 para evitar problemas con la lectura/escritura o algún tipo de efecto debido a la codificación.
El código es el siguiente:
import numpy as np
from scikits.audiolab import Format, Sndfile
fi = Sndfile("1234.wav",'r')
sr = fi.samplerate
ch = fi.channels
frames = fi.nframes
data = fi.read_frames(frames, dtype=np.int16)
maxint = np.iinfo(np.int16).max
minint = np.iinfo(np.int16).min
ruido = np.random.randint(minint,maxint+1,frames)
salida = np.bitwise_xor(data,ruido)
f_test = Sndfile("1234_test.wav",'w',Format('wav'),ch,sr)
data_test = data/float(maxint)
f_test.write_frames(data_test)
f_test.close()
f_ruido = Sndfile("ruido.wav",'w',Format('wav'),ch,sr)
data_ruido = ruido/float(maxint)
f_ruido.write_frames(data_ruido)
f_ruido.close()
f_xor = Sndfile("1234_xor.wav",'w',Format('wav'),ch,sr)
data_xor = salida/float(maxint)
f_xor.write_frames(data_xor)
f_xor.close() |
import numpy as np
from scikits.audiolab import Format, Sndfile
fi = Sndfile("1234.wav",'r')
sr = fi.samplerate
ch = fi.channels
frames = fi.nframes
data = fi.read_frames(frames, dtype=np.int16)
maxint = np.iinfo(np.int16).max
minint = np.iinfo(np.int16).min
ruido = np.random.randint(minint,maxint+1,frames)
salida = np.bitwise_xor(data,ruido)
f_test = Sndfile("1234_test.wav",'w',Format('wav'),ch,sr)
data_test = data/float(maxint)
f_test.write_frames(data_test)
f_test.close()
f_ruido = Sndfile("ruido.wav",'w',Format('wav'),ch,sr)
data_ruido = ruido/float(maxint)
f_ruido.write_frames(data_ruido)
f_ruido.close()
f_xor = Sndfile("1234_xor.wav",'w',Format('wav'),ch,sr)
data_xor = salida/float(maxint)
f_xor.write_frames(data_xor)
f_xor.close()
Tras importar las librerías leemos el archivo de entrada. Después generamos el ruido aleatorio (pseudoaleatorio en realidad) que combinaremos con la entrada. Se generan tres archivos de salida:
- 1234_test es una prueba similar al archivo de entrada que se usa para comprobar que no hay ningún fallo.
- ruido es el archivo de audio correspondiente a la base aleatoria.
- 1234_xor es el archivo de salida codificado usando el cifrado XOR.
Como se puede comprobar el fichero de salida no parece presentar ningún patrón de sonido reconocible.
A la hora de escribir un comentario a la entrada de Kriptópolis me encuentro con que lopezvit también tuvo la misma idea hace unos días y comprobó el cifrado usando Matlab obteniendo los mismos resultados.
Lo más probable sería que hubiera habido un fallo a la hora de aplicar el cifrado a los archivos originales de la entrada o a las imágenes de los comentarios. El error podría estar en la generación de los números aleatorios o en la aplicación del XOR. Un simple truncado numérico de los datos del ruido podría producir un efecto parecido.
Para completar también he probado a reconstruir el fichero original usando el ruido y el archivo cifrado. Como el formato wav no tiene perdidas debido a la codificación esto se puede hacer fácilmente. El código es este:
import numpy as np
from scikits.audiolab import Format, Sndfile
f_clave = Sndfile("ruido.wav",'r')
sr = f_clave.samplerate
ch = f_clave.channels
frames = f_clave.nframes
ruido = f_clave.read_frames(f_clave.nframes, dtype=np.int16)
maxint = np.iinfo(np.int16).max
fi = Sndfile("1234_xor.wav",'r')
data = fi.read_frames(fi.nframes, dtype=np.int16)
salida = np.bitwise_xor(data,ruido)
f_xor = Sndfile("1234_rec.wav",'w',Format('wav'),ch,sr)
data_xor = salida/float(maxint)
f_xor.write_frames(data_xor)
f_xor.close() |
import numpy as np
from scikits.audiolab import Format, Sndfile
f_clave = Sndfile("ruido.wav",'r')
sr = f_clave.samplerate
ch = f_clave.channels
frames = f_clave.nframes
ruido = f_clave.read_frames(f_clave.nframes, dtype=np.int16)
maxint = np.iinfo(np.int16).max
fi = Sndfile("1234_xor.wav",'r')
data = fi.read_frames(fi.nframes, dtype=np.int16)
salida = np.bitwise_xor(data,ruido)
f_xor = Sndfile("1234_rec.wav",'w',Format('wav'),ch,sr)
data_xor = salida/float(maxint)
f_xor.write_frames(data_xor)
f_xor.close()
El archivo 1234_rec.wav es exactamente igual que el original.
En definitiva, ya conozco un método de cifrado nuevo y he comprobado que, aparentemente, funciona.
Social