feat: add cryptography fundamentals lab scripts

This commit is contained in:
Matias Tillerias
2026-09-28 19:56:21 -03:00
commit dbc5d68bc9
6 changed files with 1913 additions and 0 deletions

198
Lab1_XOR.py Normal file
View File

@@ -0,0 +1,198 @@
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()