""" Cryptographie Post-Quantique Hybride - Standards NIST FIPS 203/204/205 Combines classical et quantum-resistant cryptography: - Chiffrement: ElGamal (classique) + Kyber (post-quantique) - Signatures: RSA-PSS (classique) + Dilithium (post-quantique) - Hachage: SHA-256 (résistant aux ordinateurs quantiques pour préimage) Cette approche hybride garantit que même si l'un des systèmes est cassé, l'autre reste sûr (defense-in-depth). """ try: import oqs HAS_OQS = True except ImportError: HAS_OQS = False import os from typing import Tuple, Dict, Any from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.backends import default_backend from .encryption import ElGamalEncryption from .hashing import SecureHash class PostQuantumCryptography: """ Implémentation hybride de cryptographie post-quantique. Utilise les standards NIST FIPS 203/204/205. """ # Algorithmes post-quantiques certifiés NIST PQC_SIGN_ALG = "ML-DSA-65" # FIPS 204 - Dilithium variant PQC_KEM_ALG = "ML-KEM-768" # FIPS 203 - Kyber variant @staticmethod def generate_hybrid_keypair() -> Dict[str, Any]: """ Générer une paire de clés hybride: - Clés RSA classiques + clés Dilithium PQC - Clés ElGamal classiques + clés Kyber PQC Returns: Dict contenant toutes les clés publiques et privées """ # Générer clés RSA classiques (2048 bits) rsa_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) # Générer clés Dilithium (signatures PQC) with oqs.KeyEncapsulation(PostQuantumCryptography.PQC_SIGN_ALG) as kemsign: dilithium_public = kemsign.generate_keypair() dilithium_secret = kemsign.export_secret_key() # Générer clés Kyber (chiffrement PQC) with oqs.KeyEncapsulation(PostQuantumCryptography.PQC_KEM_ALG) as kemenc: kyber_public = kemenc.generate_keypair() kyber_secret = kemenc.export_secret_key() # Générer clés ElGamal classiques elgamal = ElGamalEncryption() elgamal_public, elgamal_secret = elgamal.generate_keypair() return { # Clés classiques "rsa_public_key": rsa_key.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ), "rsa_private_key": rsa_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() ), "elgamal_public": elgamal_public, "elgamal_secret": elgamal_secret, # Clés post-quantiques "dilithium_public": dilithium_public.hex(), # Serialisé en hex "dilithium_secret": dilithium_secret.hex(), "kyber_public": kyber_public.hex(), "kyber_secret": kyber_secret.hex(), } @staticmethod def hybrid_sign( message: bytes, rsa_private_key: bytes, dilithium_secret: str ) -> Dict[str, bytes]: """ Signer un message avec signatures hybrides: 1. Signature RSA-PSS classique 2. Signature Dilithium post-quantique Args: message: Le message à signer rsa_private_key: Clé privée RSA (PEM) dilithium_secret: Clé secrète Dilithium (hex) Returns: Dict avec les deux signatures """ from cryptography.hazmat.primitives.serialization import load_pem_private_key # Signature RSA-PSS classique rsa_key = load_pem_private_key( rsa_private_key, password=None, backend=default_backend() ) rsa_signature = rsa_key.sign( message, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA256() ) # Signature Dilithium post-quantique dilithium_secret_bytes = bytes.fromhex(dilithium_secret) with oqs.Signature(PostQuantumCryptography.PQC_SIGN_ALG) as sig: sig.secret_key = dilithium_secret_bytes dilithium_signature = sig.sign(message) return { "rsa_signature": rsa_signature, "dilithium_signature": dilithium_signature, "algorithm": "Hybrid(RSA-PSS + ML-DSA-65)" } @staticmethod def hybrid_verify( message: bytes, signatures: Dict[str, bytes], rsa_public_key: bytes, dilithium_public: str ) -> bool: """ Vérifier les signatures hybrides. Les deux signatures doivent être valides. Args: message: Le message signé signatures: Dict avec rsa_signature et dilithium_signature rsa_public_key: Clé publique RSA (PEM) dilithium_public: Clé publique Dilithium (hex) Returns: True si les deux signatures sont valides """ from cryptography.hazmat.primitives.serialization import load_pem_public_key try: # Vérifier signature RSA-PSS rsa_key = load_pem_public_key(rsa_public_key, default_backend()) rsa_key.verify( signatures["rsa_signature"], message, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA256() ) # Vérifier signature Dilithium dilithium_public_bytes = bytes.fromhex(dilithium_public) with oqs.Signature(PostQuantumCryptography.PQC_SIGN_ALG) as sig: sig.public_key = dilithium_public_bytes sig.verify(message, signatures["dilithium_signature"]) return True except Exception as e: print(f"Signature verification failed: {e}") return False @staticmethod def hybrid_encapsulate( kyber_public: str, elgamal_public: Tuple[int, int, int] ) -> Dict[str, Any]: """ Encapsuler un secret avec chiffrement hybride: 1. Kyber pour le chiffrement PQC 2. ElGamal pour le chiffrement classique Args: kyber_public: Clé publique Kyber (hex) elgamal_public: Clé publique ElGamal (p, g, h) Returns: Dict avec ciphertexts et secret encapsulé """ kyber_public_bytes = bytes.fromhex(kyber_public) # Encapsulation Kyber with oqs.KeyEncapsulation(PostQuantumCryptography.PQC_KEM_ALG) as kem: kem.public_key = kyber_public_bytes kyber_ciphertext, kyber_secret = kem.encap_secret() # Encapsulation ElGamal (chiffrement d'un secret aléatoire) message = os.urandom(32) # Secret aléatoire 256-bit elgamal = ElGamalEncryption() elgamal_ciphertext = elgamal.encrypt(elgamal_public, message) # Dériver une clé finale à partir des deux secrets combined_secret = SecureHash.sha256( kyber_secret + message ) return { "kyber_ciphertext": kyber_ciphertext.hex(), "elgamal_ciphertext": elgamal_ciphertext, "combined_secret": combined_secret, "algorithm": "Hybrid(Kyber + ElGamal)" } @staticmethod def hybrid_decapsulate( ciphertexts: Dict[str, Any], kyber_secret: str, elgamal_secret: int ) -> bytes: """ Décapsuler et récupérer le secret hybride: 1. Décapsuler Kyber 2. Décapsuler ElGamal 3. Combiner les deux secrets Args: ciphertexts: Dict avec kyber_ciphertext et elgamal_ciphertext kyber_secret: Clé secrète Kyber (hex) elgamal_secret: Clé secrète ElGamal (x) Returns: Le secret déchiffré """ kyber_secret_bytes = bytes.fromhex(kyber_secret) kyber_ciphertext_bytes = bytes.fromhex(ciphertexts["kyber_ciphertext"]) # Décapsulation Kyber with oqs.KeyEncapsulation(PostQuantumCryptography.PQC_KEM_ALG) as kem: kem.secret_key = kyber_secret_bytes kyber_shared_secret = kem.decap_secret(kyber_ciphertext_bytes) # Décapsulation ElGamal elgamal = ElGamalEncryption() elgamal_message = elgamal.decrypt( elgamal_secret, ciphertexts["elgamal_ciphertext"] ) # Combiner les secrets combined_secret = SecureHash.sha256( kyber_shared_secret + elgamal_message ) return combined_secret @staticmethod def get_algorithm_info() -> Dict[str, str]: """Afficher les informations sur les algorithmes utilisés""" return { "signatures": "Hybrid(RSA-PSS 2048-bit + ML-DSA-65/Dilithium)", "signatures_status": "FIPS 204 certified", "encryption": "Hybrid(ElGamal + ML-KEM-768/Kyber)", "encryption_status": "FIPS 203 certified", "hashing": "SHA-256", "hashing_quantum_resistance": "Quantum-resistant (preimage security)", "security_level": "Post-Quantum + Classical hybrid", "defense": "Defense-in-depth: compromise d'un système ne casse pas l'autre" }