import tkinter as tk from tkinter import filedialog, messagebox from PIL import Image, ImageTk import numpy as np import os import time class XORLab: def __init__(self, root): self.root = root self.root.title("Lab 1: Fundamentos XOR - One Time Pad Visual") self.root.geometry("1650x650") # Estado self.img_original = None self.img_key = None self.img_cipher = None # UI Setup frame_imgs = tk.Frame(root) frame_imgs.pack(pady=10) # Paneles para imágenes con sus botones # Panel 1: Cargar self.panel_orig, self.lbl_entropy_orig = self.create_panel(frame_imgs, "1. Mensaje (Imagen)", buttons=[("Cargar Imagen", self.load_image)]) # Panel 2: Generar Clave self.panel_key, self.lbl_entropy_key = self.create_panel(frame_imgs, "2. Clave (Ruido)", buttons=[("Generar Clave (Ruido)", self.generate_key)]) # Panel 3: Cifrar (Normal y Lento) self.panel_cipher, self.lbl_entropy_cipher = self.create_panel(frame_imgs, "3. Resultado (Cifrado)", buttons=[("Cifrar (XOR)", self.encrypt), ("Cifrado Lento (Visual)", self.encrypt_slow)]) # Panel 4: Descifrar (Normal y Lento) self.panel_decrypted, self.lbl_entropy_decrypted = self.create_panel(frame_imgs, "4. Descifrado (Original)", buttons=[("Descifrar (XOR Inverso)", self.decrypt), ("Descifrado Lento (Visual)", self.decrypt_slow)]) def create_panel(self, parent, title, buttons=[]): frame = tk.Frame(parent) frame.pack(side=tk.LEFT, padx=15) tk.Label(frame, text=title, font=("Arial", 11, "bold")).pack() canvas = tk.Canvas(frame, width=350, height=350, bg="#ddd") canvas.pack() lbl_entropy = tk.Label(frame, text="Entropía: -", font=("Arial", 10), fg="blue") lbl_entropy.pack(pady=5) for btn_text, btn_command in buttons: tk.Button(frame, text=btn_text, command=btn_command, font=("Arial", 10)).pack(pady=2, fill=tk.X) return canvas, lbl_entropy def load_image(self): path = filedialog.askopenfilename(initialdir=os.getcwd(), filetypes=[("Images", ("*.png", "*.jpg", "*.jpeg", "*.bmp", "*.PNG", "*.JPG", "*.JPEG", "*.BMP")), ("All Files", "*.*")]) if path: img = Image.open(path).resize((350, 350)).convert('RGB') self.img_original = np.array(img) self.show_image(self.panel_orig, self.lbl_entropy_orig, self.img_original) # Limpiar otros paneles self.panel_key.delete("all") self.lbl_entropy_key.config(text="Entropía: -") self.panel_cipher.delete("all") self.lbl_entropy_cipher.config(text="Entropía: -") self.panel_decrypted.delete("all") self.lbl_entropy_decrypted.config(text="Entropía: -") def generate_key(self): if self.img_original is None: messagebox.showwarning("Error", "Primero cargue una imagen.") return # Generar clave aleatoria del mismo tamaño (One Time Pad) self.img_key = np.random.randint(0, 256, self.img_original.shape, dtype=np.uint8) self.show_image(self.panel_key, self.lbl_entropy_key, self.img_key) # Limpiar resultado self.panel_cipher.delete("all") self.lbl_entropy_cipher.config(text="Entropía: -") self.panel_decrypted.delete("all") self.lbl_entropy_decrypted.config(text="Entropía: -") def encrypt(self): if self.img_original is None: messagebox.showwarning("Error", "Primero cargue una imagen.") return if self.img_key is None: messagebox.showwarning("Error", "Primero genere una clave.") return # Operación XOR self.img_cipher = np.bitwise_xor(self.img_original, self.img_key) self.show_image(self.panel_cipher, self.lbl_entropy_cipher, self.img_cipher) def encrypt_slow(self): if self.img_original is None: messagebox.showwarning("Error", "Primero cargue una imagen.") return if self.img_key is None: messagebox.showwarning("Error", "Primero genere una clave.") return # 1. Cargar imagen original en el cuadro de resultado working_img = np.copy(self.img_original) self.img_cipher = np.copy(working_img) # Inicializar self.show_image(self.panel_cipher, self.lbl_entropy_cipher, working_img) self.root.update() time.sleep(1) # 2. Aplicar XOR con delay row-by-row rows, cols, channels = working_img.shape for r in range(rows): # XOR de la fila r row_key = self.img_key[r, :, :] row_orig = self.img_original[r, :, :] xor_row = np.bitwise_xor(row_orig, row_key) # Actualizar imagen de trabajo working_img[r, :, :] = xor_row # delay cada 5 filas para balancear velocidad if r % 5 == 0: self.show_image(self.panel_cipher, self.lbl_entropy_cipher, working_img) self.root.update() # Pequeño delay time.sleep(0.1) # Final update self.img_cipher = working_img self.show_image(self.panel_cipher, self.lbl_entropy_cipher, self.img_cipher) def decrypt(self): if self.img_cipher is None or self.img_key is None: return # XOR de vuelta decrypted = np.bitwise_xor(self.img_cipher, self.img_key) # Mostrar cuarto panel self.show_image(self.panel_decrypted, self.lbl_entropy_decrypted, decrypted) def decrypt_slow(self): if self.img_cipher is None: messagebox.showwarning("Error", "Primero debe cifrar una imagen.") return if self.img_key is None: messagebox.showwarning("Error", "Falta la clave.") return # 1. Cargar el resultado (cifrado) en el cuadro descifrado working_img = np.copy(self.img_cipher) self.show_image(self.panel_decrypted, self.lbl_entropy_decrypted, working_img) self.root.update() time.sleep(1) # 2. Aplicar XOR inverso (que es igual a XOR) row-by-row rows, cols, channels = working_img.shape for r in range(rows): # XOR de la fila r row_key = self.img_key[r, :, :] row_cipher = self.img_cipher[r, :, :] xor_row = np.bitwise_xor(row_cipher, row_key) # Actualizar imagen de trabajo working_img[r, :, :] = xor_row if r % 5 == 0: self.show_image(self.panel_decrypted, self.lbl_entropy_decrypted, working_img) self.root.update() time.sleep(0.1) self.show_image(self.panel_decrypted, self.lbl_entropy_decrypted, working_img) def calculate_entropy(self, img_array): # Aplanar array para considerar todos los canales/pixeles flat_img = img_array.flatten() # Calcular histograma histogram, _ = np.histogram(flat_img, bins=256, range=(0, 256)) # Probabilidades prob = histogram / float(flat_img.size) # Filtrar ceros para log prob = prob[prob > 0] # Shannon entropy = -np.sum(prob * np.log2(prob)) return entropy def show_image(self, panel, lbl_entropy, img_array): img = Image.fromarray(img_array) img_tk = ImageTk.PhotoImage(img) panel.image = img_tk # Mantener referencia panel.create_image(175, 175, image=img_tk) # Calcular y mostrar entropía ent = self.calculate_entropy(img_array) lbl_entropy.config(text=f"Entropía: {ent:.4f} bits") if __name__ == "__main__": root = tk.Tk() app = XORLab(root) root.mainloop()