GC57PrimeCrypt

Dimostrazione Video

In questo video viene presentato un esempio concreto di implementazione del metodo GC57 all’interno di un programma reale di cifratura.
Il software mostrato è il risultato di un lavoro approfondito volto a dimostrare le potenzialità della fattorizzazione istantanea di semiprimi di grandi dimensioni, resa possibile dal metodo GC57. La velocità di fattorizzazione viene integrata in un contesto di crittografia moderna, combinando il sistema con algoritmi consolidati e sicuri come SHA-3, PBKDF2 e AES.
Sotto il video è disponibile lo script Python utilizzato. Tuttavia, questo script non include le librerie contenenti i semiprimi e le chiavi, poiché sono parte fondamentale della sicurezza del sistema.

Chi desidera replicare il funzionamento completo può richiedere lo script Python per la generazione delle librerie, che verrà fornito gratuitamente con le relative istruzioni. In questo modo, ciascun utente potrà creare e conservare le proprie chiavi in modo esclusivo e personale.
Nel caso in cui il codice visualizzato non risultasse facilmente copiabile, è possibile trovarlo anche su GitHub all’indirizzo:
➡️ https://github.com/Claugo/GC57PrimeCrypt dove può essere clonato direttamente nel proprio ambiente di sviluppo (IDE).


    """
***************************************************************************************
GC57_PrimeCrypt
***************************************************************************************
Autore: [Claugo]
Versione: 1.0
Ultima modifica: [Dicembre 2024]

***************************************************************************************
Descrizione:
GC57_PrimeCrypt è un programma avanzato di crittografia che utilizza numeri primi 
di dimensioni molto grandi per garantire la sicurezza dei dati. Combina algoritmi 
crittografici moderni e robusti con un'interfaccia grafica intuitiva per l'invio e 
la ricezione sicura di file e messaggi.

***************************************************************************************
Caratteristiche principali:
1. **Sistema di codifica**:
   - I dati vengono criptati utilizzando AES (Advanced Encryption Standard) in modalità 
     EAX, che garantisce sia la confidenzialità che l'integrità dei dati.
   - La chiave AES è derivata tramite PBKDF2 (Password-Based Key Derivation Function 2) 
     con un valore di sale (salt) casuale e 100.000 iterazioni, garantendo una protezione 
     contro attacchi di forza bruta.

2. **Numeri primi e semiprimo**:
   - Il programma genera un semiprimo (prodotto di due numeri primi) che funge da 
     base per la generazione della chiave crittografica.
   - La sicurezza del sistema si basa sulla difficoltà di fattorizzare il semiprimo.

3. **Hash SHA-3**:
   - Utilizza SHA-3 (Secure Hash Algorithm 3) per generare un'impronta sicura e univoca 
     dei dati crittografici.

4. **Gestione di testo e allegati**:
   - Cripta e decripta messaggi di testo con o senza allegati.
   - Supporta file binari e di grandi dimensioni.

5. **Sicurezza fisica**:
   - L'accesso alla chiave crittografica è vincolato alla presenza di una pendrive USB 
     con un'etichetta specifica.

***************************************************************************************
Requisiti:
- Python 3.10 o superiore
- Librerie richieste:
  - tkinter (per l'interfaccia grafica)
  - Crypto (per la crittografia AES e PBKDF2)
  - hashlib (per SHA-3)
  - os, json, e altre librerie standard

***************************************************************************************
Istruzioni per l'uso:
1. Configurare le cartelle iniziali e il file CFG alla prima esecuzione.
2. Utilizzare le finestre grafiche per inviare o ricevere messaggi.
3. Assicurarsi che la pendrive USB contenente la chiave sia inserita prima di iniziare.

***************************************************************************************
Avvertenze:
- Non condividere la pendrive USB con la chiave crittografica.
- Il programma è progettato per proteggere dati sensibili; tuttavia, la sicurezza 
  dipende anche da una gestione corretta delle chiavi e dei file crittografati.

***************************************************************************************
"""

import tkinter as tk
from tkinter import messagebox, filedialog, simpledialog
import os
from math import gcd
import time
from random import randint, seed
import json
import win32api
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
from hashlib import sha3_256
from Crypto.Protocol.KDF import PBKDF2
# *******************************************************
# * controllo file CFG che serve per individuare le cartelle
# * dove prelevare i semiprimi e memorizzare il messaggio
# * e gli allegati. Nel caso non venisse trovato ne crea uno nuovo
# *******************************************************

filecfg = "GC57PCcfg"
if os.path.exists(filecfg):
    pass
else:

    def chiudi_programma():
        risposta = messagebox.askquestion("Attenzione:", "uscire dal programma?")
        if risposta == "yes":
            rootcfg.destroy()
            quit()
        else:
            return

    def salva_esci():
        controllo1 = e2_cfg.get()
        if controllo1 == "":
            messagebox.showerror("Attenzione:", "Tutti i campi devono essere compilati")
            return
        elif os.path.exists(f"{controllo1}"):
            pass
        else:
            messagebox.showerror("Attenzione:", "La cartella INVIO non esiste")
            return

        controllo2 = e3_cfg.get()
        if controllo2 == "":
            messagebox.showerror("Attenzione:", "Tutti i campi devono essere compilati")
            return
        elif os.path.exists(f"{controllo2}"):
            pass
        else:
            messagebox.showerror("Attenzione:", "La cartella RICEVE non esiste")
            return

        controllo3 = e4_cfg.get()
        if controllo3 == "":
            messagebox.showerror("Attenzione:", "Tutti i campi devono essere compilati")
            return
        elif os.path.exists(f"{controllo3}"):
            pass
        else:
            messagebox.showerror("Attenzione:", "La cartella ALLEGATI non esiste")
            return

        controllo4 = e5_cfg.get()
        if controllo4 == "":
            messagebox.showerror("Attenzione:", "Tutti i campi devono essere compilati")
            return
        elif os.path.exists(f"{controllo4}"):
            pass
        else:
            messagebox.showerror("Attenzione:", "La cartella SEMIPRIMI non esiste")
            return

        controllo5 = e6_cfg.get()
        if controllo5 == "":
            messagebox.showerror("Attenzione:", "Manca il nome PenDrive")
            return
        else:
            controllo5 = controllo5.strip().upper()

        scrivi = open("GC57PCcfg", "w")
        scrivi.write(controllo1 + "\n")
        scrivi.write(controllo2 + "\n")
        scrivi.write(controllo3 + "\n")
        scrivi.write(controllo4 + "\n")
        scrivi.write(controllo5 + "\n")
        scrivi.close()
        messagebox.showinfo("Salvataggi CFG:", "Configurazione Salvata")
        rootcfg.destroy()

    rootcfg = tk.Tk()

    # Imposta le dimensioni della finestra
    window_width = 415
    window_height = 460

    # Ottieni le dimensioni dello schermo
    screen_width = rootcfg.winfo_screenwidth()
    screen_height = rootcfg.winfo_screenheight()

    # Calcola la posizione x e y per centrare la finestra
    x = (screen_width - window_width) // 2
    y = (screen_height - window_height) // 2

    # Imposta la posizione e le dimensioni della finestra
    rootcfg.geometry(f"{window_width}x{window_height}+{x}+{y}")
    rootcfg.title("Configurazione Cartelle GC57")
    rootcfg.configure(bg="#458B74")
    colore_testo_entry = "#104E8B"
    testo = (
        "Se appare questa finestra è perché il programma viene eseguito per la prima volta in questa posizione, \
oppure il file 'GC57PCcfg' è stato cancellato\n\
Copiare e incollare con CTR-V la posizione delle cartelle:",
    )

    l1_cfg = tk.Label(
        rootcfg, text=testo, justify=tk.LEFT, font="arial 12 bold", wraplength=400
    )
    l1_cfg.place(x=10, y=20)

    px = 10
    py = 150
    l2_cfg = tk.Label(
        rootcfg,
        text="Incollare Indirizzo Cartella INVIO",
        bg="#458B74",
        font="arial 12 bold",
    )
    l2_cfg.place(x=px, y=py)
    py = py + 20
    e2_cfg = tk.Entry(rootcfg, width=40, fg=colore_testo_entry, font="arial 12")
    e2_cfg.place(x=px, y=py)

    py = py + 30
    l3_cfg = tk.Label(
        rootcfg,
        text="Incollare Indirizzo Cartella RICEVE",
        bg="#458B74",
        font="arial 12 bold",
    )
    l3_cfg.place(x=px, y=py)
    py = py + 20
    e3_cfg = tk.Entry(rootcfg, width=40, fg=colore_testo_entry, font="arial 12")
    e3_cfg.place(x=px, y=py)

    py = py + 30
    l4_cfg = tk.Label(
        rootcfg,
        text="Incollare Indirizzo Cartella ALLEGATI",
        bg="#458B74",
        font="arial 12 bold",
    )
    l4_cfg.place(x=px, y=py)
    py = py + 20
    e4_cfg = tk.Entry(rootcfg, width=40, fg=colore_testo_entry, font="arial 12")
    e4_cfg.place(x=px, y=py)

    py = py + 30
    l5_cfg = tk.Label(
        rootcfg,
        text="Incollare Indirizzo Cartella SEMIPRIMI",
        bg="#458B74",
        font="arial 12 bold",
    )
    l5_cfg.place(x=px, y=py)
    py = py + 20
    e5_cfg = tk.Entry(rootcfg, width=40, fg=colore_testo_entry, font="arial 12")
    e5_cfg.place(x=px, y=py)

    py = py + 30
    l6_cfg = tk.Label(
        rootcfg,
        text="Inserire il nome della PenDrive (Chiavi)",
        bg="#458B74",
        font="arial 12 bold",
    )
    l6_cfg.place(x=px, y=py)
    py = py + 20
    e6_cfg = tk.Entry(rootcfg, width=40, fg=colore_testo_entry, font="arial 12")
    e6_cfg.place(x=px, y=py)

    px = px + 150
    py = py + 50
    b1 = tk.Button(
        rootcfg,
        text="Salva ed Esci",
        font="arial 12 bold",
        cursor="hand1",
        bg="green",
        command=salva_esci,
    )
    b1.place(x=px, y=py)
    rootcfg.protocol("WM_DELETE_WINDOW", chiudi_programma)

    rootcfg.mainloop()

# *******************************************************
# * Carica CFG
# *******************************************************
filename = ""
apricfg = open("GC57PCcfg", "r")
programma_invia_a = apricfg.readline().strip()
programma_riceve_a = apricfg.readline().strip()
programma_allegati = apricfg.readline().strip()
programma_semiprimi = apricfg.readline().strip()
volume_label = apricfg.readline().strip()
apricfg.close()

# *******************************************************
# * Controlla la porta USB che sia inserita una pendrive
# * con il nome specificato. Questo facilita l'inserimento
# * della pennetta USB su qualsiasi computer
# *******************************************************
messagebox.showinfo("USB", "Inserisci la pen drive CODICE_S")


def get_drive_letter_by_label(label):
    try:
        # Ottenere l'elenco delle unità logiche
        drives = win32api.GetLogicalDriveStrings().split("\x00")[:-1]
        for drive in drives:
            try:
                # Controlla l'etichetta del volume per ogni unità
                volume_label = win32api.GetVolumeInformation(drive)[0]
                if volume_label == label:
                    return drive  # Restituisce la lettera dell'unità
            except Exception:
                # Ignora unità non accessibili
                continue
        return None  # Se non trova l'etichetta
    except Exception as e:
        return f"Error: {e}"


# Test
drive_letter = get_drive_letter_by_label(volume_label)
if drive_letter:
    apri_dati = drive_letter
else:
    messagebox.showerror("Attenzione", "Pennetta non trovata")
    quit()


# *************************************************
# * FINESTRA Principale: In questa finestra vengono proposti due comandi
# * uno per inviare e l'altroper ricevere i messaggi
# * Segue una piccola descrizione della codifica che viene usata per la crittografia
# *************************************************


class Finestra1(tk.Tk):
    def __init__(self):
        super().__init__()

        # ******************************************************
        # dimensione e colori
        # ******************************************************
        colorefw = "#00688B"
        colorei1 = "#FFD700"
        colorei2 = "#CDAD00"
        colorei3 = "#282828"
        coloreft = "#54FF9F"
        colorest = "#1E1E1E"

        dimensionex = 600
        dimensioney = 400
        self.title("Programma di criptazione GC57_PrimeCrypt")
        self.geometry(f"{dimensionex}x{dimensioney}")

        # Crea un canvas per disegnare
        canvas = tk.Canvas(self, width=dimensionex, height=dimensioney, bg=colorefw)
        canvas.pack()
        px = dimensionex // 2
        py = dimensioney - 320
        # Inserisci del testo
        canvas.create_text(
            px, py, text="GC57_PrimeCrypt", font=("Arial 18 bold"), fill=colorei1
        )
        py = dimensioney - 290
        canvas.create_text(
            px, py, text="Numeri Primi al servizio della Crittografia", font=("Arial 18 bold"), fill=colorei2
        )

        # Aggiungi un widget Text per il testo lungo
        text_lungo = tk.Text(self, wrap=tk.WORD, width=60, height=6, bg=colorefw, fg="black", font=("Arial", 12))
        text_lungo.place(x=28, y=dimensioney - 150)
        testo = (
            "Il programma utilizza una combinazione di SHA-3 e PBKDF2 per generare chiavi crittografiche sicure a partire da un semiprimo, "
            "creato moltiplicando due grandi numeri primi. I dati (testo e/o allegati) vengono crittografati con una chiave derivata, e il file risultante, "
            "in formato JSON, include il semiprimo per la decrittazione. La sicurezza è garantita dalla difficoltà di fattorizzare il semiprimo."
        )
        text_lungo.insert(tk.END, testo)
        text_lungo.config(state=tk.DISABLED)  # Rendi il testo non modificabile

        # Disegna una linea
        px = 0
        py = dimensioney - 250
        canvas.create_line(px, py, dimensionex, py, fill="red", width=7)
        py = dimensioney - 170
        canvas.create_line(px, py, dimensionex, py, fill="red", width=7)

        # Crea un pulsante e posizionalo all'interno del trapezio
        px = dimensionex - 430
        py = dimensioney - 210
        button1 = tk.Button(
            self,
            width=5,
            text="Invia",
            fg=colorest,
            bg=coloreft,
            font="arial 16 bold",
            command=self.apri_finestra2,
        )
        canvas.create_window(px, py, window=button1)
        px = dimensionex - 180

        button2 = tk.Button(
            self,
            width=5,
            text="Ricevi",
            fg=colorest,
            bg=coloreft,
            font="arial 16 bold",
            command=self.apri_finestra3,
        )
        canvas.create_window(px, py, window=button2)

    def apri_finestra2(self):
        finestra2 = Finestra2(self)
        finestra2.grab_set()

    def apri_finestra3(self):
        finestra3 = Finestra3(self)
        finestra3.grab_set()


# *************************************************
# * FINESTRA Invia: In questa finestra troviamo un piccolo editor di testo
# * e 4 pulsanti 'Carica allegato, Carica codifica, Invia file, crea file Hash'
# * il pulsante Carica allegato permette di caricare un file da inviare assieme al testo
# * questo file può assumere qualsiasi estensione: pdf, txt, jpg, ecc...
# *
# * Il pulsante Carica Codifica permette di selezionare un file con dentro molti semiprimi
# * questi file riportano come grandezza i bit del semiprimo, per esempio S8249b,S10200b,S11622b
# * e dentro questi file verrà caricato un semiprimo a caso da cui verranno estratti i due numeri primi
# * che serviranno a creare una chiave crittografica
# *
# * Il pulsante invia file memorizzerà nella cartella impostata il file criptato
# *
# * Crea file hash permetterà, se usato, di creare un file con lo stesso nome ma con estensione .hash
# * Questo file hash verrà spedito, a parte, prima della spedizione del file criptato
# * Questa ulteriore sicurezza permetterà di verificare che il file ricevuto sia lo stesso che è stato inviato
# *************************************************
class Finestra2(tk.Toplevel):

    def __init__(self, master):
        super().__init__(master)

        def crea_file_hash():
            filename_hash = filedialog.askopenfilename(
                title="Apri file",
                # initialdir="C:\\Users/Claugo/OneDrive/Documenti",
                initialdir=programma_invia_a,
                filetypes=(
                    ("Tutti i File", "*.json*"),
                ),
            )
            if filename_hash == "":
                return

            with open(filename_hash, "rb") as f:
                data = f.read()
            sha256_hash = sha3_256(data).hexdigest()

            file_selezionato = filename_hash.replace(".json", "") + ".hash"

            with open(file_selezionato, "w") as f:
                f.write(sha256_hash)
            messagebox.showinfo("OK","File HASH creato correttamente")
            return

        def codifica():
            if filename == "":
                messagebox.showerror(
                    "Attenzione", "Non è stata selezionata nessuna codifica"
                )
                return
            testo = tw1_invia.get("1.0", tk.END)
            if testo == "" or len(testo) < 12:
                messagebox.showerror("Attenzione", "Messaggio mancante o Troppo corto")
                return

            T = int(time.time())
            seed(T)

            with open(filename, "r") as file:
                righe = file.readlines()
                codice_random = randint(0, len(righe) - 1)  # Genera un indice casuale
                nn = righe[codice_random]
                semiprimo = int(nn.strip())
                a = semiprimo % chiave
                b = semiprimo - a
                for i in range(10):
                    r = gcd(a, b)
                    if r != 1:
                        p = r
                        q = semiprimo // p
                        break
                    a = a + chiave
                    b = b - chiave
                if r == 1:
                    messagebox.showerror("Attenzione", "Codifica non Superata")
                    return
            congiunzione = f"{p}{q}"
            congiunzione=int(congiunzione)
            congiunzione_bytes = congiunzione.to_bytes(
                (congiunzione.bit_length() + 7) // 8, byteorder="big"
            )
            # Genera hash con SHA-3

            sha3_hash = sha3_256(congiunzione_bytes).digest()

            # Deriva la chiave crittografica con PBKDF2
            salt = get_random_bytes(16)  # Genera un sale casuale
            aes_key = PBKDF2(
                sha3_hash, salt, dkLen=32, count=100000
            )  # Chiave AES a 256 bit

            # Cripta il testo con AES
            cipher = AES.new(aes_key, AES.MODE_EAX)
            ciphertext, tag = cipher.encrypt_and_digest(testo.encode("utf-8"))

            allegato_invia = e1_invia.get()
            Mdata = time.strftime("%Y%H%M")

            # Iniziamo con la condizione per verificare se c'è un allegato
            if not allegato_invia or len(allegato_invia) < 6:
                # Nessun allegato, salva solo il testo
                json_data = {
                    "semiprimo": semiprimo,
                    "salt": salt.hex(),
                    "nonce": cipher.nonce.hex(),
                    "ciphertext": ciphertext.hex(),
                    "tag": tag.hex(),
                    "allegato": False
                }
                with open(
                    f"{programma_invia_a}/GC57_PrimeCrypt_M{Mdata}_{codice_selezionato}.json", "w"
                ) as output_file:
                    json.dump(json_data, output_file)

                messagebox.showinfo(
                    "Successo", "Codifica completata e salvata in dati_criptati.json"
                )
            else:
                # C'è un allegato, leggilo e cripta
                with open(filename_allegato, "rb") as file_allegato:
                    allegato_bytes = file_allegato.read()

                # Cripta l'allegato
                cipher_allegato = AES.new(aes_key, AES.MODE_EAX)
                allegato_ciphertext, allegato_tag = cipher_allegato.encrypt_and_digest(allegato_bytes)

                # Cripta il nome dell'allegato
                nome_cipher = AES.new(aes_key, AES.MODE_EAX)
                allegato_nome_nonce = nome_cipher.nonce.hex()
                allegato_nome_ciphertext, allegato_nome_tag = nome_cipher.encrypt_and_digest(allegato_invia.encode("utf-8"))

                # Salva testo e allegato
                json_data = {
                    "semiprimo": semiprimo,
                    "salt": salt.hex(),
                    "nonce": cipher.nonce.hex(),
                    "ciphertext": ciphertext.hex(),
                    "tag": tag.hex(),
                    "allegato": True,
                    "allegato_nonce": cipher_allegato.nonce.hex(),
                    "allegato_ciphertext": allegato_ciphertext.hex(),
                    "allegato_tag": allegato_tag.hex(),
                    "allegato_nome_nonce": allegato_nome_nonce,
                    "allegato_nome_ciphertext": allegato_nome_ciphertext.hex(),
                    "allegato_nome_tag": allegato_nome_tag.hex(),
                }

                with open(
                    f"{programma_invia_a}/GC57_PrimeCrypt_M{Mdata}_{codice_selezionato}.json",
                    "w",
                ) as output_file:
                    json.dump(json_data, output_file)

                messagebox.showinfo(
                    "Successo", "Codifica completata con allegato salvata in dati_criptati.json"
                )

        def apri_allegato():
            global filename_allegato
            if e2_invia.get() == "":
                messagebox.showerror("Attenzione", "Selezionare prima tipo di codifica")
                return
            filename_allegato = filedialog.askopenfilename(
                title="Apri file",
                # initialdir="C:\\Users/Claugo/OneDrive/Documenti",
                initialdir="f:\\DCP",
                filetypes=(
                    ("Tutti i File", "*.*"),
                    ("File di testo", "*.txt"),
                    ("File PDF", "*.pdf"),
                ),
            )
            if filename_allegato == "":
                return
            else:
                cartella, allegato = os.path.split(filename_allegato)
                e1_invia.delete(0, tk.END)
                e1_invia.insert(0, allegato)

        def apri_codifica():
            global codice_selezionato, chiave, filename
            filename = filedialog.askopenfilename(
                title="Apri file",
                initialdir=f"{programma_semiprimi}",  # Cartella iniziale predefinita
                filetypes=[("File S.*", "s*.*"), ("Tutti i file", "*.*")],
            )

            if filename == "":
                messagebox.showwarning(
                    "Attenzione", "Nessuna codifica selezionata" + apri_dati
                )
                return
            semipsel = filename.split("/")
            codice_selezionato = semipsel[2]
            chiave_usb = apri_dati + "chiave_" + codice_selezionato
            if os.path.exists(chiave_usb):
                with open(chiave_usb, "r") as leggif:
                    chiave = int(leggif.readline())

            else:
                messagebox.showerror("Errore", "Dati su USB non trovati")
                return

            e2_invia.delete(0, tk.END)
            e2_invia.insert(0, codice_selezionato)

        fondo_finestra = "#2F4F4F"
        colore_testo = "#E6E6FA"
        fondo_text = "#00688B"
        self.title("Programma di Criptazione Dati - INVIA")
        self.geometry("700x600")
        self.config(bg=fondo_finestra)
        l1_invia = tk.Label(
            self,
            text="Invia GC57_PrimeCrypto",
            bg=fondo_finestra,
            font="arial 18 bold",
            fg=colore_testo,
        )
        l1_invia.place(x=190, y=20)

        tw1_invia = tk.Text(
            self,
            width=75,
            height=20,
            bg=fondo_text,
            font="helvetica, 12",
            cursor="left_ptr",
            wrap=tk.WORD,
        )
        tw1_invia.place(x=10, y=70)

        px=500
        py=480
        b1_invia = tk.Button(
            self,
            text="Invia File",
            fg="#006400",
            width=15,
            font="arial 12 bold",
            cursor="hand2",
            command=codifica,
        )
        b1_invia.place(x=px, y=py)

        py=py+50
        b4_invia = tk.Button(
            self,
            text="Crea File HASH",
            fg="#006400",
            width=15,
            font="arial 12 bold",
            cursor="hand2",
            command=crea_file_hash,
    
        )
        b4_invia.place(x=px, y=py)

        b2_invia = tk.Button(
            self,
            text="Carica Allegato",
            fg="#006400",
            width=15,
            font="arial 12 bold",
            cursor="hand2",
            command=apri_allegato,
        )
        b2_invia.place(x=10, y=490)

        b3_invia = tk.Button(
            self,
            text="Seleziona Codifica",
            fg="#006400",
            width=15,
            font="arial 12 bold",
            cursor="hand2",
            command=apri_codifica,
        )
        b3_invia.place(x=10, y=530)

        e1_invia = tk.Entry(
            self,
            width=25,
            font="arial 12 bold",
            bg=fondo_finestra,
            borderwidth=2,
            relief=tk.SUNKEN,
        )
        e1_invia.place(x=180, y=495)

        e2_invia = tk.Entry(
            self,
            width=25,
            font="arial 12 bold",
            bg=fondo_finestra,
            borderwidth=2,
            relief=tk.SUNKEN,
        )
        e2_invia.place(x=180, y=535)


# *************************************************
# * FINESTRA Ricevi: in questa finestra troveremo un editor di testo e un pulsante carica file,
# * un pulsante verifica hash, e una casella vuota con scritto 'Allegato'
# *
# * La finestra editor permetterà di vedere in chiaro il messaggio testo ricevuto
# * Nel caso il file contenga un allegato, comparirà il nome con l'estensione dell'allegato
# * e questo verrà salvato nell'apposita cartella Allegati
# *
# * Verifica hash permetterà, se presente, di verificare a chi riceve il messaggio che il file sia integro
# * e non sia stato manomesso. Questo hash verrà spedito a parte, prima della spedizione del file criptato
# * Questa ulteriore sicurezza permette di verificare che il file ricevuto sia lo stesso che è stato inviato
# *************************************************


class Finestra3(tk.Toplevel):
    def __init__(self, master):
        super().__init__(master)

        def verifica_file_hash():
            """
            Verifica che il codice hash calcolato su un file .json corrisponda
            al codice hash contenuto in un file .hash selezionato.
            """
            try:
                # Seleziona il file hash
                filename_hash = filedialog.askopenfilename(
                    title="Seleziona un file hash (.hash)",
                    initialdir=programma_invia_a,
                    filetypes=(("File HASH", "*.hash"), ("Tutti i File", "*.*")),
                )

                if not filename_hash:
                    messagebox.showwarning("Attenzione", "Nessun file selezionato.")
                    return

                # Verifica che il file selezionato abbia estensione .hash
                if not filename_hash.lower().endswith(".hash"):
                    messagebox.showerror(
                        "Errore", "Il file selezionato non ha estensione .hash!"
                    )
                    return

                # Determina il nome del corrispondente file .json
                corresponding_json = os.path.splitext(filename_hash)[0] + ".json"

                # Verifica che il file .json esista
                if not os.path.exists(corresponding_json):
                    messagebox.showerror(
                        "Errore",
                        f"Il file originale corrispondente ({os.path.basename(corresponding_json)}) non esiste!",
                    )
                    return

                # Leggi l'hash dal file .hash
                with open(filename_hash, "r") as f:
                    hash_memorizzato = f.read().strip()

                if not hash_memorizzato:
                    messagebox.showerror("Errore", "Il file .hash è vuoto!")
                    return

                # Leggi i dati del file .json
                with open(corresponding_json, "rb") as f:
                    data = f.read()

                if not data:
                    messagebox.showerror("Errore", "Il file originale è vuoto!")
                    return

                # Calcola l'hash SHA3-256 del file .json
                hash_calcolato = sha3_256(data).hexdigest()

                # Confronta gli hash
                if hash_calcolato == hash_memorizzato:
                    messagebox.showinfo("Successo", "Codice HASH corretto!")
                else:
                    messagebox.showerror("Attenzione", "Il codice HASH non corrisponde!")

            except Exception as e:
                # Gestione degli errori
                messagebox.showerror("Errore", f"Si è verificato un errore:\n{e}")

        def apri_filer():
            global codice_selezionato, chiave, filename
            filename = filedialog.askopenfilename(
                title="Apri file",
                initialdir=f"{programma_riceve_a}",  
                filetypes=(("Tutti i File", "*.json*"),),
            )

            if filename == "":
                messagebox.showerror("Attenzione", "File non selezionato")
                return

            if "\\" in filename:
                # Se il percorso utilizza '\', usa split('\\')
                semipsel = filename.split("\\")
            else:
                # Altrimenti, usa split('/')
                semipsel = filename.split("/")

            files = semipsel[-1].split("_")
            codice_selezionato = files[3].replace(".json", "")
            chiave_usb = os.path.join(apri_dati, "chiave_" + codice_selezionato)
            if os.path.exists(chiave_usb):
                with open(chiave_usb, "r") as leggif:
                    chiave = int(leggif.readline())
                    # l2.config(text=files[0] + "/" + codice_selezionato)

            else:
                messagebox.showerror("Errore", "Dati su USB non trovati")
                return

            with open(filename, "r") as file:
                dati_criptati = json.load(file)

            # Estrarre i dati principali
            semiprimo = dati_criptati.get("semiprimo")
            salt = bytes.fromhex(dati_criptati.get("salt", ""))
            nonce = bytes.fromhex(dati_criptati.get("nonce", ""))
            ciphertext = bytes.fromhex(dati_criptati.get("ciphertext", ""))
            tag = bytes.fromhex(dati_criptati.get("tag", ""))
            allegato_presente = dati_criptati.get("allegato", False)

            if not all([semiprimo, salt, nonce, ciphertext, tag]):
                messagebox.showerror(
                    "Errore",
                    "Il file non contiene tutti i dati necessari per la decriptazione",
                )
                return
            n = int(semiprimo)
            a = n % chiave
            b = n - a
            for i in range(10):
                r = gcd(a, b)
                if r != 1:
                    p = r
                    q = n // p
                    break
                a = a + chiave
                b = b - chiave
            if r == 1:
                messagebox.showerror("Attenzione", "Codifica non Superata")
                return
            congiunzione = f"{p}{q}"
            congiunzione = int(congiunzione)
            if allegato_presente==False:
                congiunzione_bytes = congiunzione.to_bytes(
                    (congiunzione.bit_length() + 7) // 8, byteorder="big"
                )
                sha3_hash = sha3_256(congiunzione_bytes).digest()
                aes_key = PBKDF2(sha3_hash, salt, dkLen=32, count=100000)
                try:
                    cipher = AES.new(aes_key, AES.MODE_EAX, nonce=nonce)
                    testo_decriptato = cipher.decrypt_and_verify(ciphertext, tag).decode("utf-8")
                    tw1_riceve.delete("1.0",tk.END)
                    tw1_riceve.insert("1.0",testo_decriptato)

                except ValueError as e:
                    messagebox.showerror("Errore", f"Errore durante la decriptazione del testo: {e}")
                    return
            else:
                # Recupera i dati dal file JSON
                congiunzione_bytes = congiunzione.to_bytes(
                    (congiunzione.bit_length() + 7) // 8, byteorder="big"
                )
                sha3_hash = sha3_256(congiunzione_bytes).digest()
                aes_key = PBKDF2(sha3_hash, salt, dkLen=32, count=100000)
                try:
                    cipher = AES.new(aes_key, AES.MODE_EAX, nonce=nonce)
                    testo_decriptato = cipher.decrypt_and_verify(ciphertext, tag).decode("utf-8")
                    tw1_riceve.delete("1.0",tk.END)
                    tw1_riceve.insert("1.0",testo_decriptato)
                except ValueError as e:
                    messagebox.showerror("Errore", f"Errore durante la decriptazione del testo: {e}")
                    return

                allegato = bytes.fromhex(dati_criptati.get("allegato_ciphertext", ""))
                allegato_tag = bytes.fromhex(dati_criptati.get("allegato_tag", ""))
                allegato_nonce = bytes.fromhex(dati_criptati.get("allegato_nonce", ""))
                allegato_nome_nonce = bytes.fromhex(dati_criptati["allegato_nome_nonce"])
                allegato_nome_ciphertext = bytes.fromhex(dati_criptati["allegato_nome_ciphertext"])
                allegato_nome_tag = bytes.fromhex(dati_criptati["allegato_nome_tag"])

                cipher_nome = AES.new(aes_key, AES.MODE_EAX, nonce=allegato_nome_nonce)
                allegato_nome = cipher_nome.decrypt_and_verify(allegato_nome_ciphertext, allegato_nome_tag).decode("utf-8")

                e1_riceve.delete(0, tk.END)
                e1_riceve.insert(0, allegato_nome)

                # Genera la chiave AES
                congiunzione_bytes = congiunzione.to_bytes(
                    (congiunzione.bit_length() + 7) // 8, byteorder="big"
                )
                sha3_hash = sha3_256(congiunzione_bytes).digest()
                aes_key = PBKDF2(sha3_hash, salt, dkLen=32, count=100000)

                try:
                    # Decripta l'allegato usando il nonce specifico
                    cipher_allegato = AES.new(aes_key, AES.MODE_EAX, nonce=allegato_nonce)
                    allegato_decriptato = cipher_allegato.decrypt_and_verify(allegato, allegato_tag)

                    # Salva l'allegato su file
                    with open(f"{programma_allegati}/{allegato_nome}", "wb") as decoded_file:
                        decoded_file.write(allegato_decriptato)

                    messagebox.showinfo("Successo", f"Allegato salvato come {allegato_nome}")

                except ValueError as e:
                    messagebox.showerror("Errore", f"Errore durante la decriptazione dell'allegato: {e}")
                    return

        fondo_finestra = "#292421"
        colore_testo = "#E6E6FA"
        fondo_text = "#00688B"

        self.title("Programma di Criptazione Dati - RICEVE")
        self.geometry("700x550")
        self.config(bg=fondo_finestra)
        l1_riceve = tk.Label(
            self,
            text="Ricevi: GC57_PrimeCrypto",
            bg=fondo_finestra,
            font="arial 18 bold",
            fg=colore_testo,
        )
        l1_riceve.place(x=190, y=20)

        tw1_riceve = tk.Text(
            self,
            width=75,
            height=20,
            bg=fondo_text,
            cursor="left_ptr",
            font="helvetica, 12",
            wrap=tk.WORD,
        )
        tw1_riceve.place(x=10, y=70)

        b1_riceve = tk.Button(
            self,
            text="Carica File",
            fg="#228B22",
            width=15,
            font="arial 12 bold",
            cursor="hand2",
            command=apri_filer,
        )
        b1_riceve.place(x=10, y=450)

        b2_riceve = tk.Button(
            self,
            text="Verifica HASH",
            fg="#228B22",
            width=15,
            font="arial 12 bold",
            cursor="hand2",
            command=verifica_file_hash,
        )
        b2_riceve.place(x=10, y=500)

        px = 450
        py = 475
        l1_riceve = tk.Label(
            self,
            text="Allegato",
            bg=fondo_finestra,
            font="arial 12 bold",
            fg=colore_testo,
        )
        l1_riceve.place(x=px, y=py)
        py = py + 30
        e1_riceve = tk.Entry(
            self,
            width=23,
            font="arial 12 bold",
            fg="#66CD00",
            justify="center",
            bg=fondo_finestra,
            borderwidth=2,
            relief=tk.SUNKEN,
        )
        e1_riceve.place(x=px, y=py)


if __name__ == "__main__":
    finestra_principale = Finestra1()
    finestra_principale.mainloop()