Téléverser les fichiers vers "/"
This commit is contained in:
parent
17e606fbf5
commit
1bd286f19c
1
build.bat
Normal file
1
build.bat
Normal file
|
|
@ -0,0 +1 @@
|
|||
python3 -m PyInstaller --onefile --windowed --icon=mirage.ico --name=Mirage mirage.py
|
||||
1
build.sh
Normal file
1
build.sh
Normal file
|
|
@ -0,0 +1 @@
|
|||
python3 -m PyInstaller --onefile --windowed --icon=mirage_icon.ico --add-data "mirage_icon.ico:." --name=Mirage mirage.py
|
||||
BIN
mirage.ico
Normal file
BIN
mirage.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
282
mirage.py
Normal file
282
mirage.py
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox, scrolledtext
|
||||
from PIL import Image, ImageTk
|
||||
import numpy as np
|
||||
from cryptography.fernet import Fernet
|
||||
import base64
|
||||
import hashlib
|
||||
import sys, os
|
||||
from datetime import datetime
|
||||
|
||||
# --- Fonctions de chiffrement/déchiffrement ---
|
||||
def generate_key_from_password(password: str) -> bytes:
|
||||
"""Génère une clé Fernet à partir d'un mot de passe."""
|
||||
digest = hashlib.sha256(password.encode()).digest()
|
||||
return base64.urlsafe_b64encode(digest)
|
||||
|
||||
def encrypt_text(text: str, key: bytes) -> bytes:
|
||||
"""Chiffre le texte avec Fernet."""
|
||||
f = Fernet(key)
|
||||
return f.encrypt(text.encode('utf-8'))
|
||||
|
||||
def decrypt_text(encrypted_text: bytes, key: bytes) -> str:
|
||||
"""Déchiffre le texte avec Fernet."""
|
||||
f = Fernet(key)
|
||||
return f.decrypt(encrypted_text).decode('utf-8')
|
||||
|
||||
# --- Fonctions de stéganographie ---
|
||||
def text_to_bin(text: str) -> str:
|
||||
"""Convertit du texte en binaire."""
|
||||
return ''.join(format(ord(c), '08b') for c in text)
|
||||
|
||||
def bin_to_text(binary: str) -> str:
|
||||
"""Convertit du binaire en texte."""
|
||||
return ''.join(chr(int(binary[i:i+8], 2)) for i in range(0, len(binary), 8))
|
||||
|
||||
def insert_text_in_image(image_path: str, output_path: str, text: str, password: str) -> tuple:
|
||||
try:
|
||||
key = generate_key_from_password(password)
|
||||
encrypted_text = encrypt_text(text, key)
|
||||
binary_text = text_to_bin(encrypted_text.decode('utf-8'))
|
||||
binary_text += '1111111111111110' # Délimiteur de fin
|
||||
|
||||
img = Image.open(image_path)
|
||||
if img.mode != "RGB":
|
||||
img = img.convert("RGB") # Force RGB
|
||||
pixels = np.array(img)
|
||||
flat_pixels = pixels.flatten()
|
||||
|
||||
if len(binary_text) > len(flat_pixels):
|
||||
return (False, "L'image est trop petite pour contenir le texte.")
|
||||
|
||||
for i in range(len(binary_text)):
|
||||
flat_pixels[i] = (flat_pixels[i] & 0xFE) | int(binary_text[i])
|
||||
|
||||
new_pixels = flat_pixels.reshape(pixels.shape)
|
||||
new_img = Image.fromarray(new_pixels.astype('uint8')) # Assure-toi que le type est uint8
|
||||
new_img.save(output_path, format="PNG") # Sauvegarde en PNG
|
||||
return (True, f"Texte inséré avec succès dans {output_path}")
|
||||
except Exception as e:
|
||||
return (False, f"Erreur : {str(e)}")
|
||||
|
||||
def extract_text_from_image(image_path: str, password: str) -> tuple:
|
||||
"""Extrait le texte chiffré d'une image."""
|
||||
try:
|
||||
img = Image.open(image_path)
|
||||
pixels = np.array(img)
|
||||
flat_pixels = pixels.flatten()
|
||||
|
||||
binary_text = ''
|
||||
for pixel in flat_pixels:
|
||||
binary_text += str(pixel & 1)
|
||||
|
||||
# Trouver le délimiteur de fin
|
||||
delimiter = '1111111111111110'
|
||||
end_index = binary_text.find(delimiter)
|
||||
if end_index == -1:
|
||||
return (False, "Aucun texte trouvé dans l'image.")
|
||||
|
||||
binary_text = binary_text[:end_index]
|
||||
encrypted_text = bin_to_text(binary_text).encode('utf-8')
|
||||
|
||||
key = generate_key_from_password(password)
|
||||
decrypted_text = decrypt_text(encrypted_text, key)
|
||||
return (True, decrypted_text)
|
||||
except Exception as e:
|
||||
return (False, f"Erreur : {str(e)}")
|
||||
|
||||
def generate_output_filename(input_path: str) -> str:
|
||||
"""Génère un nom de fichier de sortie."""
|
||||
base, ext = os.path.splitext(input_path)
|
||||
return f"{base}_stego{ext}"
|
||||
|
||||
def resource_path(relative_path):
|
||||
""" Gère les chemins pour PyInstaller """
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
return os.path.join(sys._MEIPASS, relative_path)
|
||||
return os.path.join(os.path.abspath("."), relative_path)
|
||||
|
||||
# --- Interface graphique ---
|
||||
class SteganographyApp:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("Mirage")
|
||||
|
||||
icon_path = resource_path("mirage.ico")
|
||||
try:
|
||||
self.root.iconphoto(True, tk.PhotoImage(file=icon_path))
|
||||
except:
|
||||
print(f"Icône non trouvée à {icon_path}")
|
||||
|
||||
self.image_path = tk.StringVar()
|
||||
self.output_path = tk.StringVar()
|
||||
self.password = tk.StringVar()
|
||||
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
# Frame principal
|
||||
main_frame = ttk.Frame(self.root, padding="10")
|
||||
main_frame.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# Onglets
|
||||
notebook = ttk.Notebook(main_frame)
|
||||
notebook.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# Onglet pour insérer du texte
|
||||
insert_tab = ttk.Frame(notebook)
|
||||
notebook.add(insert_tab, text="Insérer du texte")
|
||||
|
||||
# Onglet pour extraire du texte
|
||||
extract_tab = ttk.Frame(notebook)
|
||||
notebook.add(extract_tab, text="Extraire du texte")
|
||||
|
||||
# Onglet Aide
|
||||
help_tab = ttk.Frame(notebook)
|
||||
notebook.add(help_tab, text="Aide")
|
||||
self.setup_help_tab(help_tab)
|
||||
|
||||
# Widgets pour l'onglet Insérer
|
||||
ttk.Label(insert_tab, text="Image source:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
|
||||
ttk.Entry(insert_tab, textvariable=self.image_path).grid(row=0, column=1, sticky=tk.EW, padx=5, pady=5)
|
||||
ttk.Button(insert_tab, text="Parcourir", command=self.browse_image_insert).grid(row=0, column=2, padx=5, pady=5)
|
||||
|
||||
ttk.Label(insert_tab, text="Image de sortie:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=5)
|
||||
ttk.Entry(insert_tab, textvariable=self.output_path).grid(row=1, column=1, sticky=tk.EW, padx=5, pady=5)
|
||||
ttk.Button(insert_tab, text="Parcourir", command=self.browse_output_insert).grid(row=1, column=2, padx=5, pady=5)
|
||||
ttk.Button(insert_tab, text="Générer un nom", command=self.generate_output_name).grid(row=1, column=3, padx=5, pady=5)
|
||||
|
||||
ttk.Label(insert_tab, text="Texte à cacher:").grid(row=2, column=0, sticky=tk.W, padx=5, pady=5)
|
||||
self.text_to_hide = scrolledtext.ScrolledText(insert_tab, height=10)
|
||||
self.text_to_hide.grid(row=2, column=1, columnspan=3, sticky=tk.EW, padx=5, pady=5)
|
||||
|
||||
ttk.Label(insert_tab, text="Mot de passe:").grid(row=3, column=0, sticky=tk.W, padx=5, pady=5)
|
||||
ttk.Entry(insert_tab, textvariable=self.password, show="*").grid(row=3, column=1, sticky=tk.EW, padx=5, pady=5)
|
||||
|
||||
ttk.Button(insert_tab, text="Insérer le texte", command=self.insert_text).grid(row=4, column=1, pady=10)
|
||||
|
||||
# Widgets pour l'onglet Extraire
|
||||
ttk.Label(extract_tab, text="Image avec texte caché:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
|
||||
ttk.Entry(extract_tab, textvariable=self.image_path).grid(row=0, column=1, sticky=tk.EW, padx=5, pady=5)
|
||||
ttk.Button(extract_tab, text="Parcourir", command=self.browse_image_extract).grid(row=0, column=2, padx=5, pady=5)
|
||||
|
||||
ttk.Label(extract_tab, text="Mot de passe:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=5)
|
||||
ttk.Entry(extract_tab, textvariable=self.password, show="*").grid(row=1, column=1, sticky=tk.EW, padx=5, pady=5)
|
||||
|
||||
ttk.Button(extract_tab, text="Extraire le texte", command=self.extract_text).grid(row=2, column=1, pady=10)
|
||||
|
||||
ttk.Label(extract_tab, text="Texte extrait:").grid(row=3, column=0, sticky=tk.W, padx=5, pady=5)
|
||||
self.result_area = scrolledtext.ScrolledText(extract_tab, height=10)
|
||||
self.result_area.grid(row=3, column=1, columnspan=3, sticky=tk.EW, padx=5, pady=5)
|
||||
|
||||
# Configuration des colonnes et lignes
|
||||
insert_tab.columnconfigure(1, weight=1)
|
||||
extract_tab.columnconfigure(1, weight=1)
|
||||
|
||||
def setup_help_tab(self, help_tab):
|
||||
"""Configure l'onglet Aide avec le contenu du README."""
|
||||
help_text = scrolledtext.ScrolledText(help_tab, height=20, wrap=tk.WORD)
|
||||
help_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
help_content = """Mirage - V1.00 - Outil de stéganographie et chiffrement
|
||||
|
||||
📌 DESCRIPTION
|
||||
Ce projet est un outil graphique qui permet de :
|
||||
- Chiffrer et déchiffrer du texte avec l'algorithme Fernet
|
||||
- Cacher et extraire du texte dans des images (stéganographie)
|
||||
|
||||
🛠 PRÉREQUIS
|
||||
- Python 3.8+
|
||||
- Pillow 9.0.0+ (traitement des images)
|
||||
- numpy 1.21.0+ (calculs numériques)
|
||||
- cryptography 36.0.0+ (chiffrement Fernet)
|
||||
- tkinter (inclus avec Python)
|
||||
|
||||
🚀 UTILISATION
|
||||
1. Lancer l'application : python3 mirage.py
|
||||
2. Onglet "Insérer du texte" :
|
||||
- Sélectionnez une image source
|
||||
- Spécifiez l'image de sortie
|
||||
- Entrez le texte à cacher
|
||||
- Entrez un mot de passe
|
||||
- Cliquez sur "Insérer le texte"
|
||||
3. Onglet "Extraire du texte" :
|
||||
- Sélectionnez une image avec texte caché
|
||||
- Entrez le mot de passe
|
||||
- Cliquez sur "Extraire le texte"
|
||||
|
||||
🔐 SÉCURITÉ
|
||||
- Le chiffrement utilise une clé générée à partir de votre mot de passe
|
||||
- La clé est dérivée en SHA-256 et encodée en base64
|
||||
- Utilisez des mots de passe forts pour une meilleure sécurité
|
||||
|
||||
⚠️ LIMITATIONS
|
||||
- L'image doit être suffisamment grande pour contenir le texte
|
||||
- Le mot de passe est nécessaire pour extraire le texte
|
||||
|
||||
📧 CONTACT
|
||||
Pour toute question ou support : f4iyt@free.fr
|
||||
|
||||
📜 LICENCE
|
||||
Ce projet est sous licence MIT."""
|
||||
help_text.insert("1.0", help_content)
|
||||
help_text.config(state=tk.DISABLED)
|
||||
|
||||
def browse_image_insert(self):
|
||||
filename = filedialog.askopenfilename(filetypes=[("Images", "*.png *.jpg *.jpeg *.bmp")])
|
||||
if filename:
|
||||
self.image_path.set(filename)
|
||||
self.output_path.set(generate_output_filename(filename))
|
||||
|
||||
def browse_output_insert(self):
|
||||
filename = filedialog.asksaveasfilename(defaultextension=".png", filetypes=[("Images", "*.png *.jpg *.jpeg *.bmp")])
|
||||
if filename:
|
||||
self.output_path.set(filename)
|
||||
|
||||
def generate_output_name(self):
|
||||
input_path = self.image_path.get()
|
||||
if input_path:
|
||||
self.output_path.set(generate_output_filename(input_path))
|
||||
|
||||
def browse_image_extract(self):
|
||||
filename = filedialog.askopenfilename(filetypes=[("Images", "*.png *.jpg *.jpeg *.bmp")])
|
||||
if filename:
|
||||
self.image_path.set(filename)
|
||||
|
||||
# Fonctions pour insérer et extraire le texte
|
||||
def insert_text(self):
|
||||
image_path = self.image_path.get()
|
||||
output_path = self.output_path.get()
|
||||
text = self.text_to_hide.get("1.0", tk.END).strip()
|
||||
password = self.password.get()
|
||||
|
||||
if not all([image_path, output_path, text, password]):
|
||||
messagebox.showerror("Erreur", "Tous les champs sont obligatoires !")
|
||||
return
|
||||
|
||||
success, message = insert_text_in_image(image_path, output_path, text, password)
|
||||
if success:
|
||||
messagebox.showinfo("Succès", message)
|
||||
else:
|
||||
messagebox.showerror("Erreur", message)
|
||||
|
||||
def extract_text(self):
|
||||
image_path = self.image_path.get()
|
||||
password = self.password.get()
|
||||
|
||||
if not all([image_path, password]):
|
||||
messagebox.showerror("Erreur", "Tous les champs sont obligatoires !")
|
||||
return
|
||||
|
||||
success, message = extract_text_from_image(image_path, password)
|
||||
if success:
|
||||
self.result_area.delete("1.0", tk.END)
|
||||
self.result_area.insert("1.0", message)
|
||||
else:
|
||||
messagebox.showerror("Erreur", message)
|
||||
|
||||
# --- Lancement de l'application ---
|
||||
if __name__ == "__main__":
|
||||
root = tk.Tk()
|
||||
app = SteganographyApp(root)
|
||||
root.mainloop()
|
||||
3
requierments.txt
Normal file
3
requierments.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pillow>=9.0.0
|
||||
numpy>=1.21.0
|
||||
cryptography>=36.0.0
|
||||
Loading…
Reference in New Issue
Block a user