# compatibile Windows 11 # compatibile Ubuntu 24.10 # compatibile python 3.12.7 import os #import argparse # Non più necessario from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC import base64 from cryptography.exceptions import InvalidTag # Per completezza # --- Funzioni di utilità --- def generate_aes_key(password: str, salt: bytes) -> bytes: """Genera una chiave AES-256 da una password e un sale usando PBKDF2HMAC.""" if not isinstance(password, str): raise TypeError("La password deve essere una stringa.") if not isinstance(salt, bytes): raise TypeError("Il sale deve essere di tipo bytes.") if len(salt) < 16: raise ValueError("Il sale deve essere di almeno 16 byte.") password_bytes = password.encode('utf-8') kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=480_000, backend=default_backend() ) key = kdf.derive(password_bytes) return base64.urlsafe_b64encode(key) # --- Classe EncryptionManager --- class EncryptionManager: """Gestisce la crittografia e decrittografia AES-CTR.""" def __init__(self, key: bytes, nonce: bytes = None): """ Inizializza l'EncryptionManager. """ if not isinstance(key, bytes): raise TypeError("La chiave deve essere di tipo bytes.") try: self.key_bytes = base64.urlsafe_b64decode(key) except ValueError: raise ValueError("Chiave non valida (non Base64URL).") from None if len(self.key_bytes) != 32: raise ValueError("La chiave deve essere di 32 byte (256 bit).") if nonce is None: self.nonce = os.urandom(16) else: if not isinstance(nonce, bytes): raise TypeError("Il nonce deve essere di tipo bytes.") if len(nonce) != 16: raise ValueError("Il nonce deve essere di 16 byte.") self.nonce = nonce self._aes_context = Cipher( algorithms.AES(self.key_bytes), modes.CTR(self.nonce), backend=default_backend() ) self._encryptor = self._aes_context.encryptor() self._decryptor = self._aes_context.decryptor() def encrypt(self, plaintext: bytes) -> bytes: """Crittografa il plaintext.""" if not isinstance(plaintext, bytes): raise TypeError("Il plaintext deve essere di tipo bytes.") return self._encryptor.update(plaintext) + self._encryptor.finalize() def decrypt(self, ciphertext: bytes) -> bytes: """Decrittografa il ciphertext.""" if not isinstance(ciphertext, bytes): raise TypeError("Il ciphertext deve essere di tipo bytes.") return self._decryptor.update(ciphertext) + self._decryptor.finalize() def get_nonce(self) -> bytes: """Restituisce il nonce.""" return self.nonce # --- Funzioni per la crittografia/decrittografia di file --- def encrypt_file(input_filepath: str, output_filepath: str, password: str): """Crittografa un file usando AES-CTR.""" salt = os.urandom(16) key = generate_aes_key(password, salt) manager = EncryptionManager(key) nonce = manager.get_nonce() try: with open(input_filepath, "rb") as infile, open(output_filepath, "wb") as outfile: outfile.write(salt) # Scrivi il sale outfile.write(nonce) # Scrivi il nonce while True: chunk = infile.read(4096) if not chunk: break encrypted_chunk = manager.encrypt(chunk) outfile.write(encrypted_chunk) except FileNotFoundError: raise FileNotFoundError(f"File '{input_filepath}' non trovato.") except Exception as e: raise Exception(f"Errore durante la crittografia: {e}") def decrypt_file(input_filepath: str, output_filepath: str, password: str): """Decrittografa un file crittografato con AES-CTR.""" try: with open(input_filepath, "rb") as infile, open(output_filepath, "wb") as outfile: salt = infile.read(16) if len(salt) != 16: raise ValueError("File crittografato non valido (sale mancante/corto).") nonce = infile.read(16) if len(nonce) != 16: raise ValueError("File crittografato non valido (nonce mancante/corto).") key = generate_aes_key(password, salt) manager = EncryptionManager(key, nonce=nonce) while True: chunk = infile.read(4096) if not chunk: break decrypted_chunk = manager.decrypt(chunk) outfile.write(decrypted_chunk) except FileNotFoundError: raise FileNotFoundError(f"File '{input_filepath}' non trovato.") except ValueError as e: raise e except Exception as e: raise Exception(f"Errore durante la decrittografia: {e}") def main(): """Funzione principale: gestisce input e operazioni.""" while True: action = input("Vuoi crittografare (e) o decrittografare (d) un file? (e/d/exit): ").lower() if action in ('e', 'd', 'exit'): break print("Input non valido. Inserisci 'e' per crittografare, 'd' per decrittografare, o 'exit' per uscire.") if action == 'exit': return if action == 'e': action_verb = "crittografare" elif action == 'd': action_verb = "decrittografare" # Ottieni il percorso della directory dello script script_dir = os.path.dirname(os.path.realpath(__file__)) input_filename = input(f"Inserisci il nome del file da {action_verb} (senza percorso): ") input_filepath = os.path.join(script_dir, input_filename) output_filename = input("Inserisci il nome del file di output (senza percorso): ") output_filepath = os.path.join(script_dir, output_filename) password = input("Inserisci la password: ") try: if action == 'e': encrypt_file(input_filepath, output_filepath, password) print(f"File '{input_filepath}' crittografato in '{output_filepath}'.") elif action == 'd': decrypt_file(input_filepath, output_filepath, password) print(f"File '{input_filepath}' decrittografato in '{output_filepath}'.") except Exception as e: print(f"Errore: {e}") if __name__ == "__main__": main()