# compatibile Windows 11 # compatibile Ubuntu 24.10 # compatibile python 3.12.7 import os import hashlib from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC import base64 def genera_chiave_aes(password_testo, sale): """ Genera una chiave AES-256. """ if not isinstance(password_testo, str): raise TypeError("password_testo deve essere una stringa.") if not isinstance(sale, bytes): raise TypeError("sale deve essere di tipo bytes.") password_bytes = password_testo.encode('utf-8') kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=sale, iterations=100000, backend=default_backend() ) chiave_bytes = kdf.derive(password_bytes) return base64.urlsafe_b64encode(chiave_bytes) def crittografa_aes_cbc(plaintext_bytes, chiave_bytes, iv_bytes): """ Crittografa con AES-256 in modalità CBC con padding PKCS7. """ if not isinstance(plaintext_bytes, bytes): raise TypeError("plaintext_bytes deve essere di tipo bytes.") if not isinstance(chiave_bytes, bytes): raise TypeError("chiave_bytes deve essere di tipo bytes.") if not isinstance(iv_bytes, bytes): raise TypeError("iv_bytes deve essere di tipo bytes.") if len(base64.urlsafe_b64decode(chiave_bytes)) != 32: raise ValueError("La chiave deve essere di 32 byte (256 bit).") cipher = Cipher(algorithms.AES(base64.urlsafe_b64decode(chiave_bytes)), modes.CBC(iv_bytes), backend=default_backend()) encryptor = cipher.encryptor() padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(plaintext_bytes) + padder.finalize() ciphertext_bytes = encryptor.update(padded_data) + encryptor.finalize() return ciphertext_bytes def decrittografa_aes_cbc(ciphertext_bytes, chiave_bytes, iv_bytes): """ Decrittografa con AES-256 in modalità CBC. """ if not isinstance(ciphertext_bytes, bytes): raise TypeError("ciphertext_bytes deve essere di tipo bytes.") if not isinstance(chiave_bytes, bytes): raise TypeError("chiave_bytes deve essere di tipo bytes.") if not isinstance(iv_bytes, bytes): raise TypeError("iv_bytes deve essere di tipo bytes.") if len(base64.urlsafe_b64decode(chiave_bytes)) != 32: raise ValueError("La chiave deve essere di 32 byte (256 bit).") cipher = Cipher(algorithms.AES(base64.urlsafe_b64decode(chiave_bytes)), modes.CBC(iv_bytes), backend=default_backend()) decryptor = cipher.decryptor() padded_data = decryptor.update(ciphertext_bytes) + decryptor.finalize() # Gestisci l'eccezione ValueError direttamente try: unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() plaintext_bytes = unpadder.update(padded_data) + unpadder.finalize() except ValueError as e: # Controlla se l'errore è dovuto al padding if "bad decrypt" in str(e).lower() or "invalid padding" in str(e).lower(): #Verifica padding raise ValueError("Padding invalido o dati corrotti durante la decrittografia.") from e else: raise # Se non è un errore di padding, ri-solleva l'eccezione originale return plaintext_bytes class EncryptionManager: """Gestisce crittografia e decrittografia AES-CBC.""" def __init__(self, password_testo): if not isinstance(password_testo, str): raise TypeError("password_testo deve essere una stringa.") self.sale = os.urandom(16) self._chiave_bytes = genera_chiave_aes(password_testo, self.sale) self._iv_bytes = os.urandom(16) def get_chiave_iv_sale(self): return self._chiave_bytes, self._iv_bytes, self.sale def crittografa_messaggio(self, plaintext_bytes): return crittografa_aes_cbc(plaintext_bytes, self._chiave_bytes, self._iv_bytes) def decrittografa_messaggio(self, ciphertext_bytes): try: return decrittografa_aes_cbc(ciphertext_bytes, self._chiave_bytes, self._iv_bytes) except ValueError as e: raise ValueError(f"Errore durante la decrittografia: {e}") from e def main(): """Funzione principale e test.""" password_testo = "LaMiaPasswordSegretaMoltoForte123" manager = EncryptionManager(password_testo) chiave_bytes, iv_bytes, sale = manager.get_chiave_iv_sale() plaintext_testo = "Questo è un messaggio di testo di esempio." plaintext_bytes = plaintext_testo.encode('utf-8') ciphertext_bytes = manager.crittografa_messaggio(plaintext_bytes) print("Testo in chiaro originale:", plaintext_testo) print("Chiave (Base64URL):", chiave_bytes.decode('utf-8')) print("IV (Hex):", iv_bytes.hex()) print("Sale (Hex):", sale.hex()) print("Testo cifrato (Hex):", ciphertext_bytes.hex()) decrypted_plaintext_bytes = manager.decrittografa_messaggio(ciphertext_bytes) decrypted_plaintext_testo = decrypted_plaintext_bytes.decode('utf-8') print("Testo in chiaro decrittografato:", decrypted_plaintext_testo) if decrypted_plaintext_testo == plaintext_testo: print("\nCrittografia e decrittografia AES-CBC riuscite!") else: print("\nErrore: la decrittografia non ha prodotto il plaintext originale.") # Test automatici estesi print("\n--- Test Automatici Estesi ---") test_plaintexts = [ b"SHORT MESSAGE", b"MEDIUM MESSAGE EXAMPLE" * 10, b"LONG MESSAGE EXAMPLE" * 1000, b"" # Messaggio vuoto ] for plaintext in test_plaintexts: ciphertext = manager.crittografa_messaggio(plaintext) decrypted_plaintext = manager.decrittografa_messaggio(ciphertext) if plaintext == decrypted_plaintext: print(f"[PASS] Test con plaintext di lunghezza {len(plaintext)} bytes") else: print(f"[FAIL] Test con plaintext di lunghezza {len(plaintext)} bytes") print(f" Plaintext originale: {plaintext[:50]}...") print(f" Plaintext decrittografato: {decrypted_plaintext[:50]}...") if __name__ == "__main__": main()