""" Preuves de connaissance zéro (Zero-Knowledge Proofs). Pour démontrer la validité d'un vote sans révéler le contenu. """ from dataclasses import dataclass import random from typing import Tuple @dataclass class ZKProofChallenge: """Défi pour une preuve ZK interactive""" challenge: int @dataclass class ZKProofResponse: """Réponse à un défi ZK""" response: int @dataclass class ZKProof: """Preuve de connaissance zéro complète""" commitment: int challenge: int response: int class ZKProofProtocol: """ Protocole de Fiat-Shamir pour preuves de connaissance zéro. Cas d'usage dans le vote: - Prouver qu'on a déjà voté sans révéler le vote - Prouver la correctionction d'un bullettin """ @staticmethod def generate_proof(secret: int, p: int, g: int) -> Tuple[int, int]: """ Générer un commitment (première étape du protocole). Args: secret: Le secret à prouver (ex: clé privée) p: Module premier g: Générateur Returns: (commitment, random_value) """ r = random.randint(2, p - 2) commitment = pow(g, r, p) return commitment, r @staticmethod def generate_challenge(p: int) -> int: """Générer un défi aléatoire""" return random.randint(1, p - 2) @staticmethod def compute_response( secret: int, random_value: int, challenge: int, p: int ) -> int: """ Calculer la réponse au défi (non-interactif). response = random_value + challenge * secret """ return (random_value + challenge * secret) % (p - 1) @staticmethod def verify_proof( commitment: int, challenge: int, response: int, public_key: int, p: int, g: int ) -> bool: """ Vérifier la preuve. Vérifie que: g^response = commitment * public_key^challenge (mod p) """ left = pow(g, response, p) right = (commitment * pow(public_key, challenge, p)) % p return left == right @staticmethod def fiat_shamir_proof( secret: int, public_key: int, message: bytes, p: int, g: int ) -> ZKProof: """ Générer une preuve Fiat-Shamir non-interactive. """ # Étape 1: commitment commitment, r = ZKProofProtocol.generate_proof(secret, p, g) # Étape 2: challenge généré via hash(commitment || message) import hashlib challenge_bytes = hashlib.sha256( str(commitment).encode() + message ).digest() challenge = int.from_bytes(challenge_bytes, 'big') % (p - 1) # Étape 3: réponse response = ZKProofProtocol.compute_response( secret, r, challenge, p ) return ZKProof( commitment=commitment, challenge=challenge, response=response )