- Add hybrid PQC using liboqs: ML-DSA-65 (Dilithium) + ML-KEM-768 (Kyber) - Signatures: RSA-PSS + Dilithium (defense-in-depth) - Encryption: ML-KEM-768 (Kyber) + ElGamal - Tests for PQC hybrid operations - Cleanup: remove non-essential scripts and docs - Minimal, production-ready e-voting system
205 lines
7.2 KiB
Python
205 lines
7.2 KiB
Python
"""
|
|
Tests pour la cryptographie post-quantique hybride.
|
|
Valide les standards NIST FIPS 203/204/205.
|
|
"""
|
|
|
|
import pytest
|
|
from src.crypto.pqc_hybrid import PostQuantumCryptography
|
|
|
|
|
|
class TestPostQuantumCryptography:
|
|
"""Tests de la cryptographie post-quantique hybride"""
|
|
|
|
def test_hybrid_keypair_generation(self):
|
|
"""Tester la génération de paires de clés hybrides"""
|
|
keypair = PostQuantumCryptography.generate_hybrid_keypair()
|
|
|
|
# Vérifier présence de toutes les clés
|
|
assert "rsa_public_key" in keypair
|
|
assert "rsa_private_key" in keypair
|
|
assert "dilithium_public" in keypair
|
|
assert "dilithium_secret" in keypair
|
|
assert "kyber_public" in keypair
|
|
assert "kyber_secret" in keypair
|
|
assert "elgamal_public" in keypair
|
|
assert "elgamal_secret" in keypair
|
|
|
|
# Vérifier formats
|
|
assert b"-----BEGIN PUBLIC KEY-----" in keypair["rsa_public_key"]
|
|
assert b"-----BEGIN PRIVATE KEY-----" in keypair["rsa_private_key"]
|
|
assert len(keypair["dilithium_public"]) > 0
|
|
assert len(keypair["kyber_public"]) > 0
|
|
|
|
def test_hybrid_sign_and_verify(self):
|
|
"""Tester les signatures hybrides RSA-PSS + Dilithium"""
|
|
# Générer clés
|
|
keypair = PostQuantumCryptography.generate_hybrid_keypair()
|
|
|
|
# Message à signer
|
|
message = b"Vote securise avec cryptographie post-quantique"
|
|
|
|
# Signer
|
|
signatures = PostQuantumCryptography.hybrid_sign(
|
|
message,
|
|
keypair["rsa_private_key"],
|
|
keypair["dilithium_secret"]
|
|
)
|
|
|
|
# Vérifier les signatures
|
|
assert "rsa_signature" in signatures
|
|
assert "dilithium_signature" in signatures
|
|
assert len(signatures["rsa_signature"]) > 0
|
|
assert len(signatures["dilithium_signature"]) > 0
|
|
|
|
# Vérifier les deux signatures ensemble
|
|
is_valid = PostQuantumCryptography.hybrid_verify(
|
|
message,
|
|
signatures,
|
|
keypair["rsa_public_key"],
|
|
keypair["dilithium_public"]
|
|
)
|
|
assert is_valid
|
|
|
|
def test_hybrid_sign_invalid_message(self):
|
|
"""Tester que les signatures invalides sont rejetées"""
|
|
keypair = PostQuantumCryptography.generate_hybrid_keypair()
|
|
|
|
message = b"Message original"
|
|
signatures = PostQuantumCryptography.hybrid_sign(
|
|
message,
|
|
keypair["rsa_private_key"],
|
|
keypair["dilithium_secret"]
|
|
)
|
|
|
|
# Vérifier avec un message différent (doit échouer)
|
|
modified_message = b"Message modifie"
|
|
is_valid = PostQuantumCryptography.hybrid_verify(
|
|
modified_message,
|
|
signatures,
|
|
keypair["rsa_public_key"],
|
|
keypair["dilithium_public"]
|
|
)
|
|
assert not is_valid
|
|
|
|
def test_hybrid_encapsulate_decapsulate(self):
|
|
"""Tester l'encapsulation/décapsulation hybride"""
|
|
keypair = PostQuantumCryptography.generate_hybrid_keypair()
|
|
|
|
# Encapsuler un secret
|
|
result = PostQuantumCryptography.hybrid_encapsulate(
|
|
keypair["kyber_public"],
|
|
keypair["elgamal_public"]
|
|
)
|
|
|
|
assert "kyber_ciphertext" in result
|
|
assert "elgamal_ciphertext" in result
|
|
assert "combined_secret" in result
|
|
assert len(result["combined_secret"]) == 32 # 256-bit
|
|
|
|
# Décapsuler le secret
|
|
decapsulated_secret = PostQuantumCryptography.hybrid_decapsulate(
|
|
{
|
|
"kyber_ciphertext": result["kyber_ciphertext"],
|
|
"elgamal_ciphertext": result["elgamal_ciphertext"]
|
|
},
|
|
keypair["kyber_secret"],
|
|
keypair["elgamal_secret"]
|
|
)
|
|
|
|
# Les secrets doivent correspondre
|
|
assert decapsulated_secret == result["combined_secret"]
|
|
|
|
def test_algorithm_info(self):
|
|
"""Tester que les infos d'algorithmes sont correctes"""
|
|
info = PostQuantumCryptography.get_algorithm_info()
|
|
|
|
assert "signatures" in info
|
|
assert "encryption" in info
|
|
assert "hashing" in info
|
|
assert "ML-DSA-65" in info["signatures"]
|
|
assert "ML-KEM-768" in info["encryption"]
|
|
assert "SHA-256" in info["hashing"]
|
|
assert "FIPS 203" in info["encryption_status"]
|
|
assert "FIPS 204" in info["signatures_status"]
|
|
|
|
def test_multiple_signatures_different_messages(self):
|
|
"""Tester plusieurs signatures sur des messages différents"""
|
|
keypair = PostQuantumCryptography.generate_hybrid_keypair()
|
|
|
|
messages = [
|
|
b"Vote pour Alice",
|
|
b"Vote pour Bob",
|
|
b"Vote pour Charlie",
|
|
]
|
|
|
|
signatures_list = []
|
|
for msg in messages:
|
|
sig = PostQuantumCryptography.hybrid_sign(
|
|
msg,
|
|
keypair["rsa_private_key"],
|
|
keypair["dilithium_secret"]
|
|
)
|
|
signatures_list.append(sig)
|
|
|
|
# Vérifier chaque signature avec son message
|
|
for msg, sig in zip(messages, signatures_list):
|
|
is_valid = PostQuantumCryptography.hybrid_verify(
|
|
msg,
|
|
sig,
|
|
keypair["rsa_public_key"],
|
|
keypair["dilithium_public"]
|
|
)
|
|
assert is_valid
|
|
|
|
# Vérifier que les signatures ne sont pas valides avec d'autres messages
|
|
for i, (msg, sig) in enumerate(zip(messages, signatures_list)):
|
|
for j, other_msg in enumerate(messages):
|
|
if i != j:
|
|
is_valid = PostQuantumCryptography.hybrid_verify(
|
|
other_msg,
|
|
sig,
|
|
keypair["rsa_public_key"],
|
|
keypair["dilithium_public"]
|
|
)
|
|
assert not is_valid
|
|
|
|
def test_hybrid_encapsulate_multiple_times(self):
|
|
"""Tester que chaque encapsulation produit des ciphertexts différents"""
|
|
keypair = PostQuantumCryptography.generate_hybrid_keypair()
|
|
|
|
result1 = PostQuantumCryptography.hybrid_encapsulate(
|
|
keypair["kyber_public"],
|
|
keypair["elgamal_public"]
|
|
)
|
|
|
|
result2 = PostQuantumCryptography.hybrid_encapsulate(
|
|
keypair["kyber_public"],
|
|
keypair["elgamal_public"]
|
|
)
|
|
|
|
# Les ciphertexts doivent être différents (randomisés)
|
|
assert result1["kyber_ciphertext"] != result2["kyber_ciphertext"]
|
|
assert result1["combined_secret"] != result2["combined_secret"]
|
|
|
|
# Mais les deux doivent se décapsuler correctement
|
|
dec1 = PostQuantumCryptography.hybrid_decapsulate(
|
|
{
|
|
"kyber_ciphertext": result1["kyber_ciphertext"],
|
|
"elgamal_ciphertext": result1["elgamal_ciphertext"]
|
|
},
|
|
keypair["kyber_secret"],
|
|
keypair["elgamal_secret"]
|
|
)
|
|
|
|
dec2 = PostQuantumCryptography.hybrid_decapsulate(
|
|
{
|
|
"kyber_ciphertext": result2["kyber_ciphertext"],
|
|
"elgamal_ciphertext": result2["elgamal_ciphertext"]
|
|
},
|
|
keypair["kyber_secret"],
|
|
keypair["elgamal_secret"]
|
|
)
|
|
|
|
assert dec1 == result1["combined_secret"]
|
|
assert dec2 == result2["combined_secret"]
|