Stéganographie et cryptologie visuelle
Professeurs référents :
M FAURY (ISN)
Objectif
Cryptographie et stéganographie visuelles consistent à dissimuler et/ou crypter un message dans une image.
La stéganographie est l’art de la dissimulation : son objet est de faire passer inaperçu un message dans un autre message.Cette méthode est connue depuis l’antiquité et elle a de multiples variantes, fonctions du support, de la méthode ou de la technologie existante. Dans ce projet, nous nous intéressons uniquement à la dissimulation d’une image dans une autre en en créant une troisième. En utilisant la stéganographie, on veut que, lorsqu’un autre que le destinataire découvre l’image obtenue, il ne puisse pas soupçonner l’existence d’une autre image dissimulée dans celle qu’il voit, ni même qu’il soupçonne un contenu caché. Ainsi, l’observateur ne peut pas empêcher la transmission de l’image cachée et ne peut pas non plus utiliser la même méthode pour faire passer d’autres informations fausses. Dans notre projet, pour dissimuler une image dans une autre, nous modifions les bits de poids faibles des pixels de l’image contenante.
Cependant, la stéganographie est une méthode connue et si l’on soupçonne une image de contenir une information secrète avec cette méthode, il est aisé de réussir à la récupérer. Aujourd’hui, il existe d’autres méthodes qui permettent de rendre incompréhensible une information à une personne autre que son destinataire.
La cryptologie permet de rendre un message inintelligible à autre que qui-de-droit et englobe la cryptographie, le codage et la cryptanalyse, le décodage. Elle est également connue depuis l’antiquité mais acquiert le statut de sciences seulement dans les années 1970 en devenant thème de recherche avec l’essor de l’informatique puis d’internet et des communications. Le but de la cryptologie visuelle, c’est que seul l’oeil humain doit être capable de décoder et d’authentifier le message. Dans ce projet, pour cacher une image, nous utilisons une méthode de cryptographie à clé secrète qui permet de chiffrer et déchiffrer à partir de la même clé. L’image doit être binarisée (seulement en noir et blanc, sans niveau de gris) et la clé est une image de taille proportionnelle à celle de l’image, composée aléatoirement de pixels noirs et blancs.
Présentation du projet
- Peut-on modifier une image de façon à ce que seul le destinataire puisse la voir ?
- Le projet se rapporte aux domaines de compétence suivants :
- Dimension algorithmique : Création d’algorithmes qui permettent de coder une image selon deux méthodes : stéganographie et cryptologie à clé secrète.
- Éléments de programmation : Pour la réalisation, nous utilisons le langage de programmation Python avec la distribution Pyzo qui inclut des modules complémentaires, un éditeur de programme et une console qui affiche les résultats du programme exécuté dans l’éditeur. Nous avons utilisé ces modules ou bibliothèques : imageio (pour travailler avec les images), matpotlib (pour afficher les images), pillow (pour travailler avec les images et en créer à partir de tableaux de valeurs numpy), numpy (pour créer des matrices), et tkinter (pour l’interface utilisateur).
Cahier des charges
Production finale attendue : Un algorithme qui permet de coder n’importe quelle image selon une clé donnée (ou générée aléatoirement) (cryptographie) ou bien de la cacher dans une autre (stéganographie) et de la décoder dans les deux cas.
Caractéristiques de la production finale : Fonctionne correctement, petite interface qui permet de rentrer une image ou de choisir la méthode.
Contraintes à respecter : Date butoir, travail en équipe, dossier écrit (5-10 pages)
Matériel et logiciel à mettre en œuvre : Python et éditeur d’image
Tâches à réaliser
Clémence Vessaire | Naouel Kouiss |
Codage Stéganographie | Décodage Stéganographie |
Codage Cryptographie | Décodage Cryptographie |
Algorithme de binarisation d’image | Interface utilisateur |
Dossiers personnels
Stéganographie
CODAGE
Image conteneur | Image secrète (à cacher) |
##Codage import imageio import matplotlib.pyplot as plt plt.axis("off") #pour désactiver l'affichage des axes sur Matplotlib print("Pour saisir les adresses url des images à coder, il faut les mettre entre guillemets ou apostrophes") image_1 = input("Copier l'adresse url de votre image conteneur :") image_2 = input("Copier l'adresse url de votre image secrete :") image_conteneur = imageio.imread(image_1) image_secrete = imageio.imread(image_2) def codage_image(image_conteneur, image_secrete): print("Plus le nombre de bits de poids faibles utilisés pour cacher l'image secrète est faible, mieux l'image est cachée mais plus elle en perd en qualité") choix_nombre = int(input("Choississez ce nombre entre 1 et 7 :")) dico = { 1 : 0b11111110, 2 : 0b11111100, 3 : 0b11111000, 4 : 0b11110000, 5: 0b11100000, 6 : 0b11000000, 7 : 0b10000000 } nombre = 8 - choix_nombre decalage = dico[choix_nombre] largeur = list(range(list(image_conteneur.shape)[0])) #pour obtenir une liste contenant le même nombre que la valeur de la largeur hauteur = list(range(list(image_conteneur.shape)[1])) #idem pour la hauteur for pixel_l in largeur: #sur chaque ligne for pixel_h in hauteur: #sur chaque pixel de cette ligne #fonction codage_pixel : pixel_c = list(image_conteneur[pixel_l][pixel_h]) #pour transformer les tuples des valeurs des pixels en listes modifiables pixel_s = list(image_secrete[pixel_l][pixel_h]) pixel_conteneur= [] #pixel conteneur une fois codé avec les bits de poids faible à 0 pixel_secret=[] #pixel secret une fois codé avec les bits de poids forts décalés à gauche for octet in pixel_c: #pour coder chaque octet du pixel conteneur pixel_conteneur.append(decalage & octet) #mettre a zero le bit de poids faible for octet in pixel_s: #pour coder chaque octet du pixel secret pixel_secret.append(octet>>choix_nombre) #décalage du premier octet de quatre colonnes vers la droite #etape 1 du codage finie pixel_code = list(range(len(pixel_conteneur))) for octet in pixel_code: #etape 2 du codage : pixel_code[octet] = pixel_conteneur[octet] | pixel_secret[octet] #OU logique des deux octets image_conteneur[pixel_l][pixel_h] = tuple(pixel_code) plt.imshow(image_conteneur) if image_conteneur.shape != image_secrete.shape: # on verifie que les deux images font la même taille print("Les deux images doivent avoir les mêmes dimensions") else: codage_image(image_conteneur, image_secrete)[/reveal]
Résulat :
Autres exemples :
DÉCODAGE
[reveal heading= »%image% Cliquer ici pour voir le code »]def decode_octet(octet_code): return (0b00001111 & octet_code) << 4 print(bin(decode_octet(0b01101101))) def decode_pixel(pixel_code): pixel_code = list(pixel_code) # pour mettre en liste les octets du pixel pixel_decode = [] # ça va faire un tableau dans lequel va se mettre les valeurs des pixels décodés for octet in pixel_code: #pour chaque octet du pixel pixel_decode.append((0b00001111 & octet) << 4) # mettre les bits de poids fort à 0 et puis décaler de 4 rangs vers la gauche return pixel_decode print(decode_pixel((140,213,109))) image_codee = imageio.imread("https://upload.wikimedia.org/wikipedia/commons/a/a8/Steganography_original.png") import matplotlib.pyplot as plt def decode_image(image_codee): largeur = image_codee.shape[0] #pour obtenir la valeur de la largeur hauteur = image_codee.shape[1] #pour obtenir la valeur de la hauteur for pixel_l in range(largeur): # pour chacun des pixels de la ligne for pixel_h in range(hauteur): # pour chacun des pixels de la colonne pixel_code = list(image_codee[pixel_l][pixel_h]) # donne les trois composantes RGB pixel_decode = [] # ça va faire un tableau dans lequel va se mettre les valeurs des pixels décodés for octet in pixel_code: # pour chaque octet du pixel pixel_decode.append((0b000001111 & octet) << 4) # mettre les bits de poids fort à 0 et puis décaler de 4 rangs vers la gauche image_codee[pixel_l][pixel_h] = tuple(pixel_decode) plt.imshow(image_codee) # mettre l'image codée sous plt plt.show() decode_image(image_codee) # afficher l'image décodée[/reveal]
Cryptographie
Décodage
[reveal heading= »%image% Cliquer ici pour voir le code »]import random #importation du module random i = 0 #on initialise une valeur i à 0 image_codee = [0, 1, 0, 1, 1, 0, 1, 0] #image codee cle = [] #initialisation de la cle #generation aleatoire de la cle avec le bipixel cle while i< len(image_codee)/2: #tant qu’on est pas a la fin de la liste divisee par 2 alea = random.randint(0,1) #on choisit un chiffre aleatoirement entre 0 et 1 cle.append(alea) #on l’ajoute dans cle if alea == 0: # si alea = 0, on ajoute son inverse 1 cle.append(1) elif alea == 1: #si alea = 1, on ajoute son inverse 0 cle.append(0) i +=1 #on incremente la valeur de i de +1 print(cle) image_decodee = [] #initialisation image_decodee i = 0 #on remet i à 0 while i < len(image_codee): #tant qu’on est pas a la fin de la liste if image_codee[i] ^ cle[i] == 0: #si OU exclusif entre l’image et la cle est egal a 0 alors 1 image_decodee.append(1) elif image_codee[i] ^ cle[i] == 1: #si OU exclusif entre l’image et la cle est egal a 1 alors 0 image_decodee.append(0) i += 2 #on incremente la variable de debut de +2 print(image_decodee) #on affiche l’image decode[/reveal]
Interface graphique
[reveal heading= »%image% Cliquer ici pour voir le code »]from tkinter import * #Importation du module Tkinter #from tkinter.messagebox import * #Inutile #from tkinter.filedialog import * #Inutile import imageio #Importation du module imageio import matplotlib.pyplot as plt #Importation du module matplotlib plt.axis("off") #pour désactiver les axes sur Maplotlib fenetre = Tk() # création de la première fenêtre titre = Label(fenetre, text = "Choisis ton codage ", fg = 'blue') #titre titre.pack() #Affichage du titre value = StringVar() def codage_stega(entree1, entree2): #Codage de la steganographie image_conteneur = imageio.imread(entree1.get()) #Transformation de l'adresse URL de l'image conteneur donnée par l'utilisateur en imageio image_secrete = imageio.imread(entree2.get()) #Transformation de l'adresse URL de l'image secrete donnée par l'utilisateur en imageio largeur = list(range(list(image_conteneur.shape)[0])) #pour obtenir la valeur de la largeur hauteur = list(range(list(image_conteneur.shape)[1])) #pour obtenir la valeur de la hauteur for pixel_l in largeur: #sur chaque ligne for pixel_h in hauteur: #sur chaque pixel de cette colonne pixel_1 = image_conteneur[pixel_l][pixel_h] #pour obtenir les valeurs pixel_2 = image_secrete[pixel_l][pixel_h] #fonction codage_pixel : pixel_c = list(pixel_1) #pour transformer les tuples en listes modifiables pixel_s = list(pixel_2) pixel_conteneur= [] #pixel conteneur une fois codé avec les bits de poids faible à 0 pixel_secret=[] #pixel secret une fois codé avec les bits de poids forts décalés à gauche for octet in pixel_c: #pour coder chaque octet du pixel conteneur pixel_conteneur.append(int('11110000', 2)& octet) #mettre a zero le bit de poids faible for octet in pixel_s: #pour coder chaque octet du pixel secret pixel_secret.append(octet>>4) #décalage du premier octet de quatre colonnes vers la droite #etape 1 du codage finie pixel_code = list(range(len(pixel_conteneur))) for octet in pixel_code: #etape 2 du codage : pixel_code[octet] = pixel_conteneur[octet] | pixel_secret[octet] #OU logique des deux octets image_conteneur[pixel_l][pixel_h] = tuple(pixel_code) plt.imshow(image_conteneur) plt.show() return imageio.imsave("image_conteneur.png", image_conteneur) def decodage_stega(result): #Décodage de la stéganographie image_codee = imageio.imread(result.get()) #Transformation de l'adresse URL de l'image codée donnée par l'utilisateur en imageio largeur = image_codee.shape[0] #pour obtenir la valeur de la largeur hauteur = image_codee.shape[1] #pour obtenir la valeur de la hauteur for pixel_l in range(largeur): # pour chacun des pixels de la ligne for pixel_h in range(hauteur): # pour chacun des pixels de la colonne pixel_code = list(image_codee[pixel_l][pixel_h]) # donne les trois composantes RGB pixel_decode = [] #ça va faire un tableau dans lequel vont se mettre les valeurs des pixels décodé for octet in pixel_code: #décodage pour chaque octet de chaque pixel pixel_decode.append((0b00001111 & octet) << 4) # mettre les bit de poids fort à 0 et puis décaler de 4 rangs vers la gauche image_codee[pixel_l][pixel_h] = tuple(pixel_decode) plt.imshow(image_codee) # mettre image_codee sous plt plt.show() def choix1(): #Pour le choix du codage ou du décodage pour la stéganographie fenetre1 = Tk() #Création de la fenêtre titre = Label(fenetre1, text = "Choix codage/décodage ") #titre titre.pack() #Affichage du titre cadre = Frame(fenetre1) #Cadre pour la fenêtre cadre.pack() #Affichage du cadre bouton1 = Radiobutton(fenetre1, text="Codage", variable = value, value = 0, command = recupere11) #bouton pour le choix Codage bouton2 = Radiobutton(fenetre1, text="Decodage", variable = value, value = 1, command = recupere12) #bouton pour le choix Decodage bouton1.pack() #Affichage du bouton1 bouton2.pack() #Affichage du bouton2 bouton1.select() fenetre1.mainloop() #Démarrage de la boucle Tkinter def choix2(): #Pour le choix du codage ou du décodage pour la cryptologie fenetre1 = Tk() #Définition de la fenêtre titre = Label(fenetre1, text = "Choix codage/décodage ") #titre titre.pack() #Affichage du titre cadre = Frame(fenetre1) #Cadre pour la fenêtre cadre.pack() #Affichage du cadre bouton1 = Radiobutton(fenetre1, text="Codage", variable = value, value = 0, command = recupere21) #bouton pour le choix Codage bouton2 = Radiobutton(fenetre1, text="Decodage", variable = value, value = 1, command = recupere22) #bouton pour le choix Decodage bouton1.pack() #Affichage du bouton1 bouton2.pack() #Affichage du bouton2 bouton1.select() fenetre1.mainloop() #Démarrage de la boucle Tkinter def recupere11(): #Codage Steganographie pour recuperer les images telecharger fenetre2 = Tk() #Création de la fenêtre fenetre2.title("Codage Steganographie")#Titre de la fenêtre titre1 = Label(fenetre2, text = "Insérer l'url de l'image conteneur")#Titre1 titre1.pack() #Affichage du titre1 cadre = Frame(fenetre2) #Cadre pour la fenêtre cadre.pack() #Affichage du cadre entree1 = Entry(cadre, width=30) entree1.pack() #Affichage de l'entree1 titre2 = Label(fenetre2, text = "Insérer l'url de l'image à cacher")#Titre2 titre2.pack() #Affichage du titre2 entree2 = Entry(cadre, width=30) entree2.pack()#Affichage de l'entree2 valider = Button(cadre, text="Valider", command= lambda: codage_stega(entree1, entree2))#Bouton valider valider.pack() #Affichage du bouton valider fenetre2.mainloop() #Démarrage de la boucle Tkinter def recupere12(): #Decodage Steganographie pour recuperer les images telecharger fenetre2 = Tk()#Definition de la fenêtre fenetre2.title("Decodage Steganographie ")#Titre de la fenêtre titre1 = Label(fenetre2, text = "Inserer l'url de l'image à décoder")#Titre1 titre1.pack() #Affichage du titre1 cadre = Frame(fenetre2) #Cadre pour la fenêtre cadre.pack() #Affichage du cadre entree1 = Entry(cadre, width=30) entree1.pack()#Affichage de l'entree1 valider = Button(cadre, text="Valider", command= lambda: decodage_stega(entree1)) #Bouton valider valider.pack() #Affichage du bouton valider fenetre2.mainloop() #Démarrage de la boucle Tkinter def recupere21(): #Codage crypto fenetre2 = Tk() #Définition de la fenêtre fenetre2.title("Codage Cryptologie") #Titre de la fenêtre titre = Label(fenetre2, text = "Inserer l'url de l'image à cacher") #titre titre.pack() #Affichage du titre cadre = Frame(fenetre2) #Cadre pour la fenêtre cadre.pack() #Affichage du cadre entree1 = Entry(cadre, width=30) entree1.pack()#Affichage de l'entree1 fenetre2.mainloop() #Démarrage de la boucle Tkinter def recupere22(): #Decodage crypto fenetre2 = Tk() #Définition de la fenêtre fenetre2.title("Decodage Cryptologie") #Titre de la fenêtre titre = Label(fenetre2, text = "Inserer l'url de l'image à décoder") #titre titre.pack() #Affichage du titre cadre = Frame(fenetre2) #Cadre pour la fenêtre cadre.pack() #Affichage du cadre entree1 = Entry(cadre, width=30) entree1.pack()#Affichage de l'entree1 fenetre2.mainloop() #Démarrage de la boucle Tkinter cadre = Frame(fenetre) #Cadre pour la fenêtre cadre.pack() #Affichage du cadre bouton1 = Radiobutton(fenetre, text="Steganographie", variable = value, value = 0, command = choix1) #bouton pour le choix Steganographie bouton2 = Radiobutton(fenetre, text="Cryptologie", variable = value, value = 1, command = choix2) #bouton pour le choix Cryptologie bouton1.pack()#Affichage du bouton1 bouton2.pack()#Affichage du bouton2 bouton1.select() fenetre.mainloop() #Démarrage de la boucle Tkinter[/reveal]
Ressources
- http://eduscol.education.fr/sti/sites/eduscol.education.fr.sti/files/ressources/techniques/6777/6777-190-p56.pdf
- http://www.cs.jhu.edu/~fabian/courses/CS600.624/slides/VisualCrypto.pdf
- https://wiki.inria.fr/wikis/sciencinfolycee/images/e/e9/Talk_jeremie_crypto.pdf
- https://deptinfo-ensip.univ-poitiers.fr/ENS/doku/doku.php/stu:python_gui:tuto_images
- http://dept-info.labri.fr/~namyst/ens/lycee/TD2.html
- http://dept-info.labri.fr/~namyst/ens/lycee/TD1.html
- https://python.developpez.com/faq/?page=Les-nombres
- http://python.jpvweb.com/mesrecettespython/doku.php?id=binaire
- http://pixelswap.fr/entry/python-transformer-une-image-en-niveau-de-gris-en-noir-et-blanc
- http://python-prepa.github.io/ateliers/image_tuto.html
- http://www.mathslallemand.fr/doku.php?id=isn:representation:traitement_images
- http://fsincere.free.fr/isn/python/cours_python_tkinter.php
Fichiers du projet
- Ajouter un(des) fichier(s) puis cliquer sur Téléverser.
- Rafraichir la page pour vérifier que le dépôt a bien eu lieu.