Esteganografía
Este proyecto consiste en esconder texto dentro de una imagen de tal forma que tanto el texto como la imagen no se vean (afectada en el caso de la imagen) a simple vista. Dicha técnica se conoce como Esteganografía.
Se trabajará con Python para generar un mensaje codificado, de tal forma que contenta el número de bits significativos de la imagen sobre los que se esconde el texto, el texto como tal y un mensaje de fin. Dicho mensaje de término se agregó para indicarle al código que la lectura debería llegar hasta dicho punto.
Introducción
La Esteganografía existe desde hace siglos. Según Wikipedia se aplicó en las primeras veces por Heródoto en el libro de las Historias:
“En este libro describe cómo un personaje tomó un cuadernillo de dos hojas o tablillas; rayó bien la cera que las cubría y en la madera misma grabó el mensaje y lo volvió a cubrir con cera regular.
Otra historia, en el mismo libro, relata cómo otro personaje había rasurado a navaja la cabeza de su esclavo de mayor confianza, le tatuó el mensaje en el cuero cabelludo, esperó después a que le volviera a crecer el cabello y lo mandó al receptor del mensaje, con instrucciones de que le rasuraran la cabeza.”
Imports
|
|
Imagen original
Para abrir la imagen y mostrarla, ocuparemos scikit-image.io.imread()
|
|
Preview
|
|
Lectura del archivo
El archivo se puede ver como un arreglo.
En este caso se trata de una imagen en blanco y negro, por lo que el arreglo tiene un shape
de 248x300
.
Imagen como Array
|
|
(246, 300)
array([[49, 50, 48, ..., 58, 55, 55],
[47, 47, 49, ..., 56, 57, 55],
[48, 48, 49, ..., 57, 56, 57],
...,
[71, 70, 70, ..., 70, 69, 70],
[69, 71, 71, ..., 70, 72, 71],
[69, 68, 69, ..., 71, 72, 71]], dtype=uint8)
Idea central
Imagen
Cada pixel de la imagen corresponde a un número del arreglo:
[49 50 48 … 58 55 55]
Corresponde a la primera fila de la imagen.
Binario
Cada uno de esos bits se puede representar como un número binario:
49, por ejemplo es 00110001 en binario. De esta forma, la imagen quedará como:
[ 00110001 00110010 00110000 … 00111010 00110111 00110111 ]
La idea en esteganografía, es esconder el texto en la imagen, usando los bits menos significativos.
Máscara
Si se utilizaran 2 bit significativo, los bits que se ocuparían serían los que tienen x
:
[ 001100xx 001100xx 001100xx … 001110xx 001101xx 001101xx ]
Texto
Así, para esconder una letra, por ejemplo la “h”, se debe convertir esta imagen a un número.
El número ASCII para la h es 104.
104 en binario, sería: 01101000
Esteganografía
Lo que implica, que se pueden colocar 2 números en cada pixel de la imagen
h: 01 10 10 00 en [ 001100xx 001100xx 001100xx … 001110xx 001101xx 001101xx ]:
[ 00110001 00110010 00110010 … ]
Haciendo esto y ocupando un número de bits bajo, el texto puede quedar escondido en la imagen, sin que sea vea a simple vista.
Texto de prueba
El texto se carga por open
, con permisos unicamente de lectura.
|
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus auctor maximus dignissim. Etiam luctus elit nec risus varius volutpat. Vivamus arcu ante, tempus nec luctus commodo, blandit vel neque. Pellentesque semper posuere purus, et finibus leo accumsan non. Sed sed felis vel erat pretium molestie. Nulla facilisi. Proin vel lacus et leo tristique luctus eu nec nulla. Nullam finibus lobortis porta. Vestibulum sit amet lacus eros. Aliquam tempus augue quis lacus gravida aliquet.
Maecenas eget purus est. Etiam ac lectus ac magna commodo molestie in eget ante. Vestibulum porta mauris nec risus finibus, nec elementum purus vulputate. Nunc non eros lobortis, vestibulum nibh eget, sagittis justo. Suspendisse eget tellus vitae dolor fermentum elementum. Nulla luctus nulla vel volutpat dictum. Morbi ac nulla interdum, semper lacus quis, eleifend eros.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse potenti. Suspendisse potenti. Cras eget massa urna. Pellentesque quam neque, euismod sagittis eros sed, consequat fringilla augue. In ac euismod diam. Morbi maximus semper erat, pulvinar efficitur ligula congue eu. Donec ante arcu, feugiat sit amet tincidunt in, interdum id nibh. In non posuere nisl. Vivamus vitae justo in tellus tincidunt facilisis in ut dui. Vestibulum sed mauris aliquet, blandit lacus sit amet, ultrices nisl. Aliquam eget pharetra mi, eget venenatis enim. Vivamus a placerat enim.
Donec varius aliquet velit, id mollis massa ultrices sit amet. Mauris lacus nunc, euismod non dignissim at, consectetur vitae enim. Phasellus volutpat leo urna, a posuere felis volutpat quis. Ut elementum urna sed arcu fermentum ultricies. Mauris at massa diam. Vestibulum id ipsum rutrum, ultrices metus id, pharetra tortor. Vestibulum lacinia odio nec faucibus lobortis. Curabitur convallis, velit quis feugiat scelerisque, eros elit lobortis tellus, non tempor lectus dolor non risus. Suspendisse a condimentum mauris. Cras finibus nec lacus at consequat. Donec et neque et nibh placerat efficitur vel non magna. Mauris augue nunc, placerat quis tellus eu, viverra fermentum quam. Mauris at metus pretium, rhoncus nisi vitae, scelerisque elit.
In massa erat, dignissim quis enim in, egestas aliquam dolor. Morbi condimentum blandit placerat. Cras ultricies, nisi in tincidunt facilisis, lacus nibh iaculis mi, vitae cursus quam sem sit amet urna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed nisi lectus, placerat at scelerisque sollicitudin, cursus eget erat. Cras eu volutpat nisl, sit amet tincidunt nisl. Proin a elit elit. Sed et lectus fermentum, congue est vel, mattis mi.
Codificación
El proceso de codificación y decodificación se realizarán por medio de operaciones a nivel de bits. El procedimiento se podría resumir de la siguiente manera:
- Calcular la máscara
- Generar una lista de bits que contienen el texto codificado
- Aplicar la codificacion a la imagen
Calculo de la máscara
Para calculár la máscara, se definirá una variable que indica el número de bits (nbit
) a usar. Para el valor de la máscara, se calcula el valor máximo para un byte/pixel (0-255) y se le resta el $2^nbits$. De esta forma, por ejemplo, para un nbit
de 3 se tiene una máscara de 0b 1111 1011.
Representación binaria de máscara
|
|
'100'
De largo 8
|
|
'00000100'
Completando los números anteriores
|
|
15
|
|
'00001111'
A la inversa
|
|
240
|
|
'11110000'
Refactor
|
|
Así, las máscaras que ocuparemos para los distintos bytes serán:
Test
|
|
0b11111110
0b11111101
0b11111011
0b11110111
0b11101111
0b11011111
0b10111111
0b1111111
Codificación del texto a bits
La generación de una lista con el texto codificado está compuesta de 3 partes:
- Agregar 4 bits que indican la cantidad de bits significativos con los que se encriptó la imagen.
- Convertir los caracteres (bytes) a bits.
- Agregar una marca para indicar el final del archivo.
El proceso final, debe dar por resultado una imagen del siguiente tipo:
|
|
Numero de bits | —————–Texto Codificado—————– | Final del texto |
---|
Texto Codificado
Convertir los caracteres a bits se logra de la misma manera. En la medida que se desplazan ($>>$) y se aislan ($\& 1$) se van agregando a la lista.
El código se puede apreciar a continuación.
Caracter a codificar
|
|
'h'
En números
|
|
104
En Binario
|
|
'01101000'
A Array
|
|
|
|
[0, 1, 1, 0, 1, 0, 0, 0]
Con un texto
|
|
|
|
|
|
letter h
Ordinal 104
Binary 0b1101000
Encode [0, 1, 1, 0, 1, 0, 0, 0]
Número de bits
Para agregar el número de bits significativos con los que se encriptó la imagen se desplaza hacia la derecha 1 uno la cantidad de veces que se especifica con nBits. El proceso se realiza con los operadores “>>” y $”\&”$. El código se aprecia a continuación:
|
|
Test
|
|
0 [0, 0, 0, 0]
1 [1, 0, 0, 0]
2 [0, 1, 0, 0]
3 [1, 1, 0, 0]
4 [0, 0, 1, 0]
5 [1, 0, 1, 0]
6 [0, 1, 1, 0]
7 [1, 1, 1, 0]
8 [0, 0, 0, 1]
9 [1, 0, 0, 1]
10 [0, 1, 0, 1]
11 [1, 1, 0, 1]
12 [0, 0, 1, 1]
13 [1, 0, 1, 1]
14 [0, 1, 1, 1]
15 [1, 1, 1, 1]
16 [0, 0, 0, 0]
17 [1, 0, 0, 0]
18 [0, 1, 0, 0]
19 [1, 1, 0, 0]
20 [0, 0, 1, 0]
21 [1, 0, 1, 0]
22 [0, 1, 1, 0]
23 [1, 1, 1, 0]
24 [0, 0, 0, 1]
25 [1, 0, 0, 1]
26 [0, 1, 0, 1]
27 [1, 1, 0, 1]
28 [0, 0, 1, 1]
29 [1, 0, 1, 1]
30 [0, 1, 1, 1]
31 [1, 1, 1, 1]
Final del Texto
El mismo proceso se ocupara para la marca del EOF. En ese caso se ocupó la siguiente variable.
|
|
|
|
Codificación del texto en la imagen
Para aplicar la codificación a la imagen se recorre la imagen como un arreglo de dos dimensiones, entendiendo que cada pixel se comporta como un byte, que compone una matriz que describe los distintos tonos de grises que componen la imagen. En este loop se aplica la máscara a cada byte y se agrega el texto desde la lista calculada anteriormente. El proceso se realiza por el siguiente código:
|
|
Pruebas de codificación
Ha de notarse que los primeros 4 bits, que describen los nBits, se aplican siempre al bit menos significativo para que el decodificador los pueda leer desde la misma entrada. Como se revisará más adelante, la idea de agregarlos en el LSB es no alterer visualmente la imagen.
El resultado de aplicar la codificación tiene distintos resultados según el bit significativo que se ocupe. Es recomendable aplicarlo para un N<=2. Para N mayores, la imagen empieza a distorcionarse por la modificación de los colores de la misma. En las siguientes imagenes se puede apreciar los distintos resultados:
|
|
|
|
|
|
|
|
Decodificación
Para la decodificación, se realiza el proceso inverso. En primera instancia se leen los nbits de los primeros 4 bits y posteriormente se lee el resto de los caracteres a un arreglo. Se termina este proceso cuando se encuentra el EOF designado. El proceso se puede apreciar por el siguiente código:
|
|
Decodificar texto
|
|
Decodificación de imagen
|
|
Pruebas de decodificación
|
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus auctor maximus dignissim. Etiam luctus elit nec risus varius volutpat. Vivamus arcu ante, tempus nec luctus commodo, blandit vel neque. Pellentesque semper posuere purus, et finibus leo accumsan non. Sed sed felis vel erat pretium molestie. Nulla facilisi. Proin vel lacus et leo tristique luctus eu nec nulla. Nullam finibus lobortis porta. Vestibulum sit amet lacus eros. Aliquam tempus augue quis lacus gravida aliquet.\n\nMaecenas eget purus est. Etiam ac lectus ac magna commodo molestie in eget ante. Vestibulum porta mauris nec risus finibus, nec elementum purus vulputate. Nunc non eros lobortis, vestibulum nibh eget, sagittis justo. Suspendisse eget tellus vitae dolor fermentum elementum. Nulla luctus nulla vel volutpat dictum. Morbi ac nulla interdum, semper lacus quis, eleifend eros.\n\nPellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse potenti. Suspendisse potenti. Cras eget massa urna. Pellentesque quam neque, euismod sagittis eros sed, consequat fringilla augue. In ac euismod diam. Morbi maximus semper erat, pulvinar efficitur ligula congue eu. Donec ante arcu, feugiat sit amet tincidunt in, interdum id nibh. In non posuere nisl. Vivamus vitae justo in tellus tincidunt facilisis in ut dui. Vestibulum sed mauris aliquet, blandit lacus sit amet, ultrices nisl. Aliquam eget pharetra mi, eget venenatis enim. Vivamus a placerat enim.\n\nDonec varius aliquet velit, id mollis massa ultrices sit amet. Mauris lacus nunc, euismod non dignissim at, consectetur vitae enim. Phasellus volutpat leo urna, a posuere felis volutpat quis. Ut elementum urna sed arcu fermentum ultricies. Mauris at massa diam. Vestibulum id ipsum rutrum, ultrices metus id, pharetra tortor. Vestibulum lacinia odio nec faucibus lobortis. Curabitur convallis, velit quis feugiat scelerisque, eros elit lobortis tellus, non tempor lectus dolor non risus. Suspendisse a condimentum mauris. Cras finibus nec lacus at consequat. Donec et neque et nibh placerat efficitur vel non magna. Mauris augue nunc, placerat quis tellus eu, viverra fermentum quam. Mauris at metus pretium, rhoncus nisi vitae, scelerisque elit.\n\nIn massa erat, dignissim quis enim in, egestas aliquam dolor. Morbi condimentum blandit placerat. Cras ultricies, nisi in tincidunt facilisis, lacus nibh iaculis mi, vitae cursus quam sem sit amet urna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed nisi lectus, placerat at scelerisque sollicitudin, cursus eget erat. Cras eu volutpat nisl, sit amet tincidunt nisl. Proin a elit elit. Sed et lectus fermentum, congue est vel, mattis mi.
Conclusiones
El proceso de codificación de texto en imágenes permite esconder texto. Una de las limitaciones principales tiene que ver con que el texto que se utilice debe ser menor en longitud al tamaño de la imagen.
Lo anterior podría compensarse cuando se utilizan imánenes en colores ya que se triplica la cantidad de bytes disponibles para la codificación. Si se ocupan imágenes con transparencias (alpha). Podría agregarse una dimensión más, siempre y cuando considerando que sería más oportuno hacerlo con el bit menos significativo.
Uno de los principales problemas para manejar la encriptación tuvo que ver con el órden de los bits en la lista de los bits.
Otro punto interesante fue el delimitador final que se utilizó. Se considera que podria ser posible incluir el tamaño de la imagen al principio, sin embargo esto sería variable dependiento del tamaño.
Finalmente se puede mencionar que existen varios paquetes públicos para realizar esta encriptación. Algunos con varias opciones de configuración.