CIA/e-voting-system/tests/test_crypto.py
E-Voting Developer 5bebad45b8 Initial commit: Complete e-voting system with cryptography
- FastAPI backend with JWT authentication
- ElGamal, RSA-PSS, ZK-proofs crypto modules
- HTML5/JS frontend SPA
- MariaDB database with 5 tables
- Docker Compose with 3 services (frontend, backend, mariadb)
- Comprehensive tests for cryptography
- Typst technical report (30+ pages)
- Makefile with development commands
2025-11-03 16:13:08 +01:00

217 lines
6.3 KiB
Python

"""
Tests pour les primitives cryptographiques.
"""
import pytest
from src.crypto.encryption import ElGamalEncryption, HomomorphicEncryption
from src.crypto.signatures import DigitalSignature
from src.crypto.zk_proofs import ZKProofProtocol
from src.crypto.hashing import SecureHash
class TestElGamalEncryption:
"""Tests du chiffrement ElGamal"""
def setup_method(self):
self.eg = ElGamalEncryption()
def test_keygen(self):
"""Tester la génération de clés"""
pub, priv = self.eg.generate_keypair()
assert pub.p == self.eg.p
assert pub.g == self.eg.g
assert pub.h > 0
assert priv.x > 0
def test_encrypt_decrypt(self):
"""Tester le chiffrement et déchiffrement"""
pub, priv = self.eg.generate_keypair()
message = 5
# Chiffrer
ciphertext = self.eg.encrypt(pub, message)
assert ciphertext.c1 > 0
assert ciphertext.c2 > 0
# Déchiffrer
decrypted = self.eg.decrypt(priv, ciphertext, pub.p)
assert decrypted == message
def test_multiple_encryptions_different(self):
"""Vérifier que deux chiffrements du même message sont différents"""
pub, _ = self.eg.generate_keypair()
message = 3
ct1 = self.eg.encrypt(pub, message)
ct2 = self.eg.encrypt(pub, message)
# Les ciphertexts doivent être différents (probabiliste)
assert (ct1.c1, ct1.c2) != (ct2.c1, ct2.c2)
def test_homomorphic_addition(self):
"""Tester l'addition homomorphe ElGamal"""
pub, priv = self.eg.generate_keypair()
# Votes: 1 pour A, 0 pour B, 1 pour C
votes = [1, 0, 1]
# Chiffrer les votes
encrypted_votes = [self.eg.encrypt(pub, v) for v in votes]
# Additionner les votes chiffrés
hom = HomomorphicEncryption()
total_encrypted = hom.sum_encrypted_votes(encrypted_votes, pub.p)
# Déchiffrer le résultat
result = self.eg.decrypt(priv, total_encrypted, pub.p)
# Devrait égaler la somme des votes
assert result == sum(votes)
class TestDigitalSignature:
"""Tests des signatures numériques"""
def setup_method(self):
self.sig = DigitalSignature(key_size=2048)
def test_sign_verify(self):
"""Tester la signature et vérification"""
priv_key, pub_key = self.sig.generate_keypair()
message = b"Vote pour Alice"
signature = self.sig.sign(priv_key, message)
assert self.sig.verify(pub_key, message, signature)
def test_signature_tampering_detected(self):
"""Vérifier que la modification d'un message est détectée"""
priv_key, pub_key = self.sig.generate_keypair()
message = b"Vote pour Alice"
signature = self.sig.sign(priv_key, message)
# Modifier le message
tampered_message = b"Vote pour Bob"
assert not self.sig.verify(pub_key, tampered_message, signature)
def test_signature_tampering_signature_detected(self):
"""Vérifier que la modification d'une signature est détectée"""
priv_key, pub_key = self.sig.generate_keypair()
message = b"Vote pour Alice"
signature = self.sig.sign(priv_key, message)
# Modifier la signature
tampered_signature = bytes([b ^ 1 for b in signature[:10]]) + signature[10:]
assert not self.sig.verify(pub_key, message, tampered_signature)
class TestZKProofs:
"""Tests des preuves de connaissance zéro"""
def test_fiat_shamir_proof(self):
"""Tester le protocole Fiat-Shamir"""
p = 23
g = 5
secret = 7
public_key = pow(g, secret, p)
message = b"Vote valide"
# Générer la preuve
proof = ZKProofProtocol.fiat_shamir_proof(
secret, public_key, message, p, g
)
# Vérifier la preuve
valid = ZKProofProtocol.verify_proof(
proof.commitment,
proof.challenge,
proof.response,
public_key,
p,
g
)
assert valid
def test_proof_with_wrong_secret(self):
"""Vérifier qu'une preuve échoue avec le mauvais secret"""
p = 23
g = 5
secret = 7
public_key = pow(g, secret, p)
message = b"Vote valide"
# Générer une preuve avec un secret incorrect
wrong_secret = 3
proof = ZKProofProtocol.fiat_shamir_proof(
wrong_secret, public_key, message, p, g
)
# La vérification devrait échouer
valid = ZKProofProtocol.verify_proof(
proof.commitment,
proof.challenge,
proof.response,
public_key,
p,
g
)
# Vrai ou faux dépend de la probabilité, mais généralement échoue
# (test stochastique)
class TestSecureHash:
"""Tests du hachage cryptographique"""
def test_sha256_deterministic(self):
"""Verifier que SHA-256 est deterministe"""
data = "Vote electronique".encode('utf-8')
hash1 = SecureHash.sha256(data)
hash2 = SecureHash.sha256(data)
assert hash1 == hash2
assert len(hash1) == 32 # 256 bits = 32 bytes
def test_sha256_avalanche(self):
"""Verifier l'effet d'avalanche"""
data1 = b"Vote pour Alice"
data2 = b"Vote pour Bob"
hash1 = SecureHash.sha256(data1)
hash2 = SecureHash.sha256(data2)
# Les hashes doivent être très différents
differences = sum(bin(a ^ b).count('1') for a, b in zip(hash1, hash2))
assert differences > 100 # Au moins 100 bits différents
def test_derive_key(self):
"""Tester la dérivation de clé PBKDF2"""
password = b"my_secure_password"
key1, salt1 = SecureHash.derive_key(password)
key2, salt2 = SecureHash.derive_key(password)
# Les clés doivent être identiques avec le même salt
key3, _ = SecureHash.derive_key(password, salt1)
assert key1 == key3
# Mais les salts doivent être aléatoires
assert salt1 != salt2
def test_hash_bulletin(self):
"""Tester le hash du bulletin"""
hash1 = SecureHash.hash_bulletin(1, 10, 1234567890)
hash2 = SecureHash.hash_bulletin(1, 10, 1234567890)
hash3 = SecureHash.hash_bulletin(1, 11, 1234567890)
assert hash1 == hash2
assert hash1 != hash3