Plateforme de Cryptographie Par Kaci AMAOUCHE

Exercices - Chapitre 4: Cryptographie quantique

Mettez en pratique vos connaissances sur les principes de la cryptographie quantique

Exercice 4.1 : Simulation du protocole BB84

Le protocole BB84, développé par Charles Bennett et Gilles Brassard en 1984, est le premier protocole de distribution quantique de clés. Dans cet exercice, vous allez simuler son fonctionnement.

Partie A : Mise en œuvre du protocole

Complétez le code Python suivant qui simule le protocole BB84 :


import random
import numpy as np

def bb84_simulation(n_qubits, error_rate=0, eavesdropping=False):
    """
    Simule le protocole BB84 pour la distribution quantique de clés.
    
    Args:
        n_qubits (int): Nombre de qubits à transmettre
        error_rate (float): Taux d'erreur du canal (0-1)
        eavesdropping (bool): Si True, simule la présence d'un espion (Eve)
        
    Returns:
        tuple: (clé secrète partagée, taux d'erreur estimé, taille de la clé finale)
    """
    # Bases possibles: 0 pour base rectiligne (+), 1 pour base diagonale (×)
    # États possibles: 0 et 1 dans la base choisie
    
    # 1. Alice prépare des qubits aléatoires dans des bases aléatoires
    alice_bits = [random.randint(0, 1) for _ in range(n_qubits)]
    alice_bases = [random.randint(0, 1) for _ in range(n_qubits)]
    
    # 2. Alice envoie les qubits à Bob
    transmitted_qubits = alice_bits.copy()
    
    # 3. Simulation de l'espionnage par Eve (si activé)
    if eavesdropping:
        eve_bases = [random.randint(0, 1) for _ in range(n_qubits)]
        for i in range(n_qubits):
            # Eve mesure dans une base aléatoire
            if eve_bases[i] != alice_bases[i]:
                # Si Eve utilise la mauvaise base, la mesure perturbe l'état
                transmitted_qubits[i] = random.randint(0, 1)
    
    # 4. Simulation d'erreurs de transmission
    for i in range(n_qubits):
        if random.random() < error_rate:
            transmitted_qubits[i] = 1 - transmitted_qubits[i]  # Inversion du bit
    
    # 5. Bob choisit des bases aléatoires pour mesurer
    bob_bases = [random.randint(0, 1) for _ in range(n_qubits)]
    
    # 6. Bob mesure les qubits reçus
    bob_measurements = []
    for i in range(n_qubits):
        if bob_bases[i] == alice_bases[i]:
            # Si Bob utilise la même base qu'Alice, il obtient le même résultat
            # (sauf en cas d'erreur de transmission ou d'espionnage)
            bob_measurements.append(transmitted_qubits[i])
        else:
            # Si Bob utilise une base différente, il obtient un résultat aléatoire
            bob_measurements.append(random.randint(0, 1))
    
    # 7. Alice et Bob échangent leurs bases (canal public)
    matching_bases_indices = [i for i in range(n_qubits) if alice_bases[i] == bob_bases[i]]
    
    # 8. Alice et Bob gardent uniquement les bits où ils ont utilisé la même base
    alice_key = [alice_bits[i] for i in matching_bases_indices]
    bob_key = [bob_measurements[i] for i in matching_bases_indices]
    
    # 9. Estimation du taux d'erreur (QBER)
    # Dans une implémentation réelle, ils sacrifieraient une partie de la clé pour cela
    errors = sum(a != b for a, b in zip(alice_key, bob_key))
    error_estimation = errors / len(alice_key) if alice_key else 0
    
    # 10. En pratique, ils effectueraient ensuite:
    # - Réconciliation d'erreurs (correction d'erreurs)
    # - Amplification de confidentialité (réduction des informations d'Eve)
    # Mais pour cette simulation, nous simplifions en supposant ces étapes parfaites
    
    return alice_key, error_estimation, len(alice_key)

# Test du protocole sans espionnage
key, error_rate, key_length = bb84_simulation(1000)
print(f"Sans espionnage:")
print(f"Clé d'Alice (premiers 20 bits): {key[:20]}")
print(f"Longueur de la clé: {key_length} bits")
print(f"Taux d'erreur estimé: {error_rate:.2%}")

# Test du protocole avec espionnage
key, error_rate, key_length = bb84_simulation(1000, eavesdropping=True)
print(f"\nAvec espionnage:")
print(f"Clé d'Alice (premiers 20 bits): {key[:20]}")
print(f"Longueur de la clé: {key_length} bits")
print(f"Taux d'erreur estimé: {error_rate:.2%}")
                

Partie B : Analyse de sécurité

En exécutant le code ci-dessus plusieurs fois, analysez comment le taux d'erreur est affecté par la présence d'un espion. Expliquez pourquoi et comment Alice et Bob peuvent détecter la présence d'Eve.

Partie C : Questions théoriques

Répondez aux questions suivantes :

  1. Pourquoi est-il impossible pour Eve de copier parfaitement un qubit sans le perturber ? Quel principe physique garantit cette sécurité ?
  2. Quelle est la différence fondamentale entre la sécurité du protocole BB84 et celle des cryptosystèmes classiques comme RSA ?
  3. Quelles sont les limitations pratiques qui rendent difficile l'implémentation réelle des systèmes QKD sur de longues distances ?

Exercice 4.2 : Impact de l'algorithme de Shor

L'algorithme de Shor représente une menace majeure pour la cryptographie à clé publique classique. Cet exercice vous aidera à comprendre son fonctionnement et ses implications.

Partie A : Analyse de l'algorithme

L'algorithme de Shor permet de factoriser efficacement de grands nombres sur un ordinateur quantique. Expliquez brièvement :

  1. Pourquoi la factorisation rapide de grands nombres est problématique pour RSA.
  2. Quels autres systèmes cryptographiques seraient menacés par l'algorithme de Shor et pourquoi.
  3. Comment l'algorithme de Shor utilise la transformée de Fourier quantique pour trouver la période d'une fonction.

Partie B : Évaluation de la menace

On considère des clés RSA de différentes tailles : 1024 bits, 2048 bits et 4096 bits.

  1. Combien de qubits logiques seraient théoriquement nécessaires pour casser chacune de ces clés avec l'algorithme de Shor ?
  2. Quel est l'état actuel du développement des ordinateurs quantiques en termes de nombre de qubits et de taux d'erreur ?
  3. Dans combien de temps estimez-vous que les cryptosystèmes RSA actuels pourraient être compromis par des ordinateurs quantiques ? Justifiez votre réponse.

Partie C : Étude de cas

Vous êtes responsable de la sécurité des données d'une entreprise qui doit être protégée pendant au moins 25 ans. Proposez une stratégie détaillée pour faire face à la menace quantique, en précisant :

  1. Quels algorithmes et quelles tailles de clés utiliser à court terme
  2. Quand et comment planifier la transition vers la cryptographie post-quantique
  3. Comment gérer la rétrocompatibilité pendant la période de transition

Exercice 4.3 : Cryptographie post-quantique

La cryptographie post-quantique développe des algorithmes résistants aux attaques quantiques. Cet exercice vous permettra d'explorer ce domaine émergent.

Partie A : Familles d'algorithmes

Pour chacune des familles d'algorithmes post-quantiques suivantes, décrivez brièvement le problème mathématique sous-jacent et évaluez leurs avantages et inconvénients :

  1. Cryptographie basée sur les réseaux (ex: CRYSTALS-Kyber)
  2. Cryptographie basée sur les codes (ex: Classic McEliece)
  3. Cryptographie multivariate (ex: Rainbow)
  4. Cryptographie basée sur les isogénies (ex: SIKE)
  5. Cryptographie basée sur les fonctions de hachage (ex: SPHINCS+)

Partie B : Analyse comparative

Comparez les algorithmes post-quantiques sélectionnés par le NIST (National Institute of Standards and Technology) avec RSA et les courbes elliptiques en termes de :

  1. Taille des clés et des signatures/chiffrements
  2. Performance (vitesse d'exécution)
  3. Maturité et confiance dans la sécurité
  4. Facilité d'implémentation et résistance aux attaques par canaux auxiliaires

Partie C : Implémentation expérimentale

On vous fournit le code Python suivant qui utilise la bibliothèque PyPQC pour expérimenter avec CRYSTALS-Kyber. Analysez ce code, expliquez son fonctionnement et répondez aux questions :


# Note: Ce code est conceptuel, PyPQC n'est pas une bibliothèque réelle
# En pratique, on utiliserait liboqs, PQClean ou des bibliothèques similaires

from pypqc import kyber
import time
import os

def benchmark_kyber():
    # Tester les différents niveaux de sécurité de Kyber
    security_levels = [1, 3, 5]  # Correspond à Kyber512, Kyber768, Kyber1024
    
    results = {}
    
    for level in security_levels:
        print(f"Testing Kyber at security level {level}")
        start_time = time.time()
        
        # Générer une paire de clés
        key_gen_start = time.time()
        public_key, secret_key = kyber.keygen(level)
        key_gen_time = time.time() - key_gen_start
        
        # Message à chiffrer
        message = os.urandom(32)  # 256 bits
        
        # Chiffrer le message
        encrypt_start = time.time()
        ciphertext = kyber.encrypt(public_key, message, level)
        encrypt_time = time.time() - encrypt_start
        
        # Déchiffrer le message
        decrypt_start = time.time()
        decrypted = kyber.decrypt(secret_key, ciphertext, level)
        decrypt_time = time.time() - decrypt_start
        
        # Vérifier que le déchiffrement fonctionne
        assert message == decrypted, "Decryption failed!"
        
        total_time = time.time() - start_time
        
        # Collecter les résultats
        results[level] = {
            'public_key_size': len(public_key),
            'secret_key_size': len(secret_key),
            'ciphertext_size': len(ciphertext),
            'key_gen_time': key_gen_time,
            'encrypt_time': encrypt_time,
            'decrypt_time': decrypt_time,
            'total_time': total_time
        }
    
    # Afficher les résultats
    print("\nPerformance Comparison:")
    print("-" * 80)
    print(f"{'Security Level':<15} {'PK Size':<10} {'SK Size':<10} {'CT Size':<10} {'KeyGen (ms)':<12} {'Encrypt (ms)':<12} {'Decrypt (ms)':<12}")
    print("-" * 80)
    
    for level, data in results.items():
        print(f"Kyber-{512 * (2 if level == 5 else level)}".ljust(15), end="")
        print(f"{data['public_key_size']:<10}", end="")
        print(f"{data['secret_key_size']:<10}", end="")
        print(f"{data['ciphertext_size']:<10}", end="")
        print(f"{data['key_gen_time']*1000:<12.2f}", end="")
        print(f"{data['encrypt_time']*1000:<12.2f}", end="")
        print(f"{data['decrypt_time']*1000:<12.2f}")

# Exécuter le benchmark
benchmark_kyber()
                

Questions :

  1. Quelles sont les différences de taille et de performance entre les différents niveaux de sécurité de Kyber ?
  2. Quels seraient les défis d'intégration de Kyber dans des applications existantes comme TLS ?
  3. Comment pourriez-vous modifier ce code pour implémenter une approche hybride combinant Kyber avec un échange de clés classique comme ECDH ?

Ressources complémentaires

Conseils de réussite

Pour les exercices sur la cryptographie quantique, concentrez-vous sur la compréhension des principes fondamentaux plutôt que sur les détails mathématiques complexes.

Pour l'exercice 4.1, essayez de tracer manuellement l'exécution du protocole BB84 avec un petit nombre de qubits pour mieux comprendre son fonctionnement.

Pour l'exercice 4.3, consultez la documentation du NIST sur les candidats post-quantiques pour approfondir votre compréhension des différentes approches.