198 lines
8.2 KiB
Python
198 lines
8.2 KiB
Python
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() |