Qiskit en profondeur & Cryptographie post-quantique
Maîtriser la transpilation, les primitives Sampler/Estimator de Qiskit Runtime v2, et comprendre les standards NIST de cryptographie post-quantique — sans prérequis mathématiques, avec du code Qiskit et C#.
Qiskit en profondeur
Transpilation : du circuit logique au circuit physique
Analogie développeur : la transpilation est à un circuit quantique ce qu’un compilateur C# est à votre code LINQ — elle traduit du haut niveau (portes arbitraires, connectivité totale) en instructions natives de la machine cible (portes physiques, connectivité limitée).
La transpilation se décompose en passes successives :
- Décomposition de portes — chaque porte logique (H, T, SWAP…) est traduite en combinaisons de basis gates du backend.
- Routage des qubits — insertion de portes SWAP pour respecter la connectivité physique du processeur.
- Optimisation — réduction du nombre de portes et de la profondeur du circuit.
- Mise en correspondance finale — vérification que tout est exprimé en basis gates.
Niveaux d’optimisation (0 à 3)
Qiskit propose 4 niveaux d’optimisation lors de la transpilation. Plus le niveau est élevé, plus le compilateur investit de temps pour réduire la profondeur du circuit et le nombre de portes 2-qubits (les plus coûteuses en fidélité).
| Niveau | Comportement | Usage typique |
|---|---|---|
| 0 | Décomposition minimale, pas d’optimisation | Debugging, circuits déjà optimisés |
| 1 | Optimisation légère (suppression de portes identité, fusion triviale) | Défaut — bon compromis vitesse/qualité |
| 2 | Optimisation agressive (commutation de portes, réduction de profondeur) | Circuits de taille moyenne |
| 3 | Optimisation maximale (resynthèse de blocs unitaires, exploration de routages alternatifs) | Production sur vrai matériel |
# Transpiler un circuit avec différents niveaux
from qiskit import QuantumCircuit, transpile
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke
backend = FakeSherbrooke() # Simulateur fidèle d'un processeur 127 qubits
qc = QuantumCircuit(3)
qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)
# Niveau 0 : rapide mais non optimisé
t0 = transpile(qc, backend=backend, optimization_level=0)
print(f"Niveau 0 : profondeur={t0.depth()}, ops={t0.count_ops()}")
# Niveau 3 : optimisation maximale
t3 = transpile(qc, backend=backend, optimization_level=3)
print(f"Niveau 3 : profondeur={t3.depth()}, ops={t3.count_ops()}")
# → Le niveau 3 produit généralement un circuit moins profond
Basis gates et portes natives
Chaque processeur quantique ne supporte qu’un jeu limité de portes physiques appelées basis gates. Par exemple, les processeurs IBM Eagle/Heron utilisent typiquement {ECR, ID, RZ, SX, X}. Toutes vos portes logiques sont décomposées en combinaisons de ces portes natives par le transpileur.
# Voir les basis gates d'un backend
print(backend.configuration().basis_gates)
# → ['ecr', 'id', 'rz', 'sx', 'x']
# La porte Hadamard (H) se décompose en : RZ(π) · SX · RZ(π)
# Le transpileur fait cette conversion automatiquement
Primitives Sampler et Estimator (Runtime v2)
Depuis Qiskit Runtime v2, on n’utilise plus directement backend.run(). Deux primitives standardisent l’interface :
| Primitive | Rôle | Sortie |
|---|---|---|
SamplerV2 | Échantillonne le circuit (exécution + mesure) | Distribution de probabilités (comptages par bitstring) |
EstimatorV2 | Calcule la valeur attendue ⟨O⟩ d’un observable | Valeur numérique + écart-type |
Sampler remplace backend.run(qc) pour obtenir des comptages. Estimator est indispensable pour les algorithmes variationnels (VQE, QAOA) où l’on mesure des observables comme des hamiltoniens, sans avoir à décomposer les mesures manuellement.
# Utilisation de SamplerV2 (Qiskit Runtime v2)
from qiskit import QuantumCircuit, transpile
from qiskit_ibm_runtime import SamplerV2 as Sampler
backend = FakeSherbrooke()
sampler = Sampler(mode=backend)
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
# IMPORTANT : transpiler AVANT de soumettre aux primitives
transpiled = transpile(qc, backend)
job = sampler.run([transpiled], shots=4096)
result = job.result()
# result[0].data.meas → les comptages par bitstring
Piège courant : avec Qiskit Runtime v2, il faut toujours transpiler le circuit avant de le passer à une primitive. Les primitives n’effectuent plus la transpilation automatiquement — contrairement à l’ancien
backend.run(). Oublier cette étape provoque une erreur ou un résultat incohérent.
Cryptographie post-quantique
Ce que menace l’ordinateur quantique
L’algorithme de Shor casse en temps polynomial les problèmes de factorisation (RSA) et de logarithme discret (ECC, Diffie-Hellman). Un ordinateur quantique suffisamment puissant rendrait obsolètes la majorité des systèmes à clé publique actuels.
L’algorithme de Grover offre une accélération quadratique sur la recherche exhaustive. Il ne « casse » pas AES, mais divise par deux la sécurité en bits : AES-128 passe de 2^128 à ~2^64 opérations quantiques. Parade simple : doubler la taille de clé (AES-256 reste sûr).
| Algorithme classique | Menace quantique | Impact |
|---|---|---|
| RSA (2048, 4096) | Shor | Cassé complètement |
| ECDSA / ECDH | Shor | Cassé complètement |
| Diffie-Hellman | Shor | Cassé complètement |
| AES-128 | Grover | Sécurité réduite à ~64 bits — insuffisant |
| AES-256 | Grover | Sécurité réduite à ~128 bits — toujours sûr |
| SHA-256 | Grover | Résistance préimage : ~128 bits — acceptable |
Les standards NIST de cryptographie post-quantique
Le NIST a finalisé en 2024 les premiers standards conçus pour résister aux attaques quantiques. La plupart reposent sur les réseaux euclidiens (lattice-based) :
| Standard NIST | Ancien nom | Type | Problème sous-jacent |
|---|---|---|---|
| ML-KEM (FIPS 203) | CRYSTALS-Kyber | Échange de clés (KEM) | Module Learning With Errors (MLWE) |
| ML-DSA (FIPS 204) | CRYSTALS-Dilithium | Signature numérique | Module Learning With Errors (MLWE) |
| SLH-DSA (FIPS 205) | SPHINCS+ | Signature numérique | Fonctions de hachage (stateless) |
Analogie réseaux euclidiens : imaginez un immense espace à des milliers de dimensions, rempli de points régulièrement espacés (le réseau). Trouver le point le plus proche d’une cible donnée dans cet espace est un problème que ni un ordinateur classique ni un ordinateur quantique ne sait résoudre efficacement. C’est ce qui rend ML-KEM et ML-DSA résistants.
Implications pratiques pour développeurs
# Python — échange de clés ML-KEM avec liboqs
# pip install liboqs-python
import oqs
# Alice génère une paire de clés ML-KEM-768
kem = oqs.KeyEncapsulation("ML-KEM-768")
public_key = kem.generate_keypair()
# Bob encapsule un secret partagé avec la clé publique d'Alice
ciphertext, shared_secret_bob = kem.encap_secret(public_key)
# Alice décapsule pour retrouver le même secret
shared_secret_alice = kem.decap_secret(ciphertext)
assert shared_secret_alice == shared_secret_bob
# → Les deux parties partagent un secret résistant aux attaques quantiques
// C# / .NET — signature ML-DSA avec BouncyCastle
// NuGet: BouncyCastle.Cryptography >= 2.3
using Org.BouncyCastle.Pqc.Crypto.MLDsa;
using Org.BouncyCastle.Security;
var random = new SecureRandom();
var keyGenParams = new MLDsaKeyGenerationParameters(
random, MLDsaParameters.ML_DSA_65);
var keyGen = new MLDsaKeyPairGenerator();
keyGen.Init(keyGenParams);
var keyPair = keyGen.GenerateKeyPair();
// Signer un message
var signer = new MLDsaSigner();
signer.Init(true, keyPair.Private);
byte[] message = System.Text.Encoding.UTF8.GetBytes("Données sensibles");
byte[] signature = signer.GenerateSignature(message);
// Vérifier la signature
signer.Init(false, keyPair.Public);
bool valid = signer.VerifySignature(message, signature);
// valid == true → signature post-quantique vérifiée !
« Harvest now, decrypt later » : des attaquants peuvent intercepter et stocker dès aujourd’hui des communications chiffrées (RSA, ECC), en attendant de disposer d’un ordinateur quantique pour les déchiffrer plus tard. C’est pourquoi la migration vers la crypto post-quantique est urgente — même sans ordinateur quantique disponible actuellement.