La stéganographie consiste à dissimuler un texte, une image voire un programme dans un support (ici une image).
Chaque caractère possède un code ASCII décimal qui peut être converti en nombre binaire (cf. ci-dessous "Codage des caractères en python"). Ce code binaire est une suite de '0' et de '1'.
Pour l'image ci-dessous, les pixels sont de la forme [r,g,b] où r,g,b sont des entiers codés sur 8 bits (28 valeurs entre 0 et 255).
Le principe du codage est le suivant : une valeur paire de la couche rouge par exemple est associée à la valeur '0' et une valeur impaire est associée à la valeur '1'.
Ainsi en modifiant de ± 1 la valeur de r, il est possible de coder soit un '0' soit un '1' en modifiant l'image de façon imperceptible à l'œil (puisque la valeur r varie au plus de 1).
Il suffit donc de grouper les valeurs r de 8 pixels consécutifs pour coder un caractère.
Par exemple, [123,g,b],[255,g,b],[234,g,b] code la séquence '110'
(seules les valeurs de la couche rouge sont utilisées dans cet exemple).
Tableaux numpy (ce site).
Pour traiter les pixels, il est avantageux de transformer le tableau tridimensionnel issu de l'image initiale.
Exemple avec une image 2 × 2 pixels (r,g,b) :
>>> T=np.array([[[11,12,13],[14,15,16]],[[21,22,23],[24,25,26]]])
>>> T # Tableau de 2 lignes 2 colonnes de pixels [r,g,b]
array([[[11, 12, 13],
[14, 15, 16]],
[[21, 22, 23],
[24, 25, 26]]])
Transformation en tableau bidimensionnel avec 1 unique ligne de pixels :
>>> T.reshape((4,3))
array([[11, 12, 13],
[14, 15, 16],
[21, 22, 23],
[24, 25, 26]])
Transformation en tableau unidimensionnel de valeurs :
>>> T.reshape(12)
array([11, 12, 13, 14, 15, 16, 21, 22, 23, 24, 25, 26])
Retour au tableau initial :
>>> T.reshape((2,2,3) # 2 lignes 2 colonnes de 3 valeurs
array([[[11, 12, 13],
[14, 15, 16]],
[[21, 22, 23],
[24, 25, 26]]])
Tester ces instructions.
Stéganographie, exemples sur wikipédia.
Il est impératif de sauvegarder régulièrement le code en le collant dans un fichier texte (Notepad++) ou dans l'éditeur de pyzo.
L'image utilisée est : 'http://gilles.django.group/static/images/einstein_mystery.png'
1.1 Déterminer les caractéristiques de cette image (dimensions, rgb ou rgba), ainsi que celles des pixels.
1.2 Décoder le texte caché dans l'image.
Dans cette image, le codage est effectué dans la couche correspondant au rouge.
Créer une copie du tableau initial et le transformer en tableau bidimensionnel (tableau unidimensionnel de pixels) puis écrire un script réalisant les opérations suivantes pour toutes les valeurs de la couche rouge :
- associer chaque nombre pair de la couche rouge à la chaîne '0' et chaque nombre impair à la chaîne '1' ;
- grouper ces chaînes par 8 (de façon à reconstituer un octet) ;
- convertir ces chaînes de 8 caractères en nombre décimal en utilisant int('chaine',2) ;
- convertir le nombre décimal n obtenu en caractère par chr(n).
Rq : en réalité la chaîne 'WXYZ' a été ajoutée à la fin du message, il est donc possible d'interrompre le processus dès que cette chaîne est trouvée.
1.3 Cacher un texte dans une image.
L'image utilisée est : 'http://gilles.django.group/static/images/einstein.png'
Créer une fonction txt2bin(c) renvoyant le code binaire du caratère c sur 8 bits (avec 8 digits) (cf. rappels dans le paragraphe "Codage des caractères en python").
Exemple : txt2bin('a') renvoie '01100001'
Ecrire un script réalisant les opérations suivantes pour chaque caractère de la chaîne :
- convertir le caractère en binaire sur 8 bits ;
- pour chaque bit du caractère, modifier ou non la valeur du pixel courant suivant sa parité et celle du bit considéré : bit '0' associé à une valeur paire
pour le pixel rouge et bit '1' associé à une valeur impaire pour le pixel rouge (si un bit de valeur 255 doit être modifié, enlever 1 ; ajouter 1 pour les autres valeurs) ;
Redimensionner le tableau au format initial et afficher l'image.
Tester le décodage.
L'image utilisée est : 'http://gilles.django.group/static/images/chatllenge.png'
Image originale : Keith Kissel, CC BY 2.0, via Wikimedia Commons
2.1 chatllenge : quelle image se cache derrière ce félin ?
Indications :
Les dimensions (nombre de lignes et de colonnes) de l'image cachée sont les valeurs de la couche alpha (transparence) des deux premiers pixels.
L'image est en couleur donc les trois couches de r, g, b de l'image cachée sont codées dans les trois couches de l'image fournie.
Les principes de codage sont les mêmes que dans l'exercice précédent.
On pourra écrire un fonction dec2bin(n) renvoyant une chaîne représentant le nombre n en binaire sur 8 bits avec 8 digits (utiliser la fonction format).
Exemple : dec2bin(51) renvoie '00110011'.
2.2 Ecrire le code permettant de dissimuler une image dans une autre (attention aux dimensions des images : 1 pixel de l'image à cacher est codée sur 8 pixels de l'image support).