Quantum Cryptography â Secure Communication in the Quantum Age
In this tutorial, you'll learn about Quantum Cryptography. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Quantum Cryptography uses the principles of quantum mechanics to achieve secure communication, providing security based on physical laws rather than computational assumptions.
What You'll Learn
By the end of this tutorial, you will understand quantum key distribution (BB84, E91), quantum digital signatures, quantum secure direct communication, and the difference between Quantum Cryptography and post-Quantum Cryptography.
Why It Matters
Classical cryptography based on RSA and ECC will be broken by large-scale quantum computers running Shor's algorithm. Quantum Cryptography provides a path to information-theoretic security. Understanding these protocols is essential for building future-secure systems.
Real-World Use
Quantum key distribution networks already operate in China (Beijing-Shanghai backbone, 2000 km), Switzerland (SwissQuantum network), and the US (Chicago quantum network). Banks use QKD for inter-bank transfers. The Micius satellite enables intercontinental QKD. Doda Browser's future versions will incorporate post-Quantum Cryptography for forward secrecy.
Learning Path
flowchart LR
A[BB84 QKD] --> B[Quantum Cryptography]
B --> C[Quantum Advantage]
A --> D{You Are Here}
style D fill:#f90,color:#fff
Prerequisites: Understand BB84 quantum key distribution. Familiarity with Shor algorithm and Python helps.
Types of Quantum Cryptography
Quantum Cryptography encompasses several protocols:
| Protocol | Purpose | Year |
|---|---|---|
| BB84 | Quantum key distribution | 1984 |
| E91 | Entanglement-based QKD | 1991 |
| B92 | Simplified QKD | 1992 |
| Quantum Digital Signatures | Message authentication | 2001 |
| Quantum Secure Direct Communication | Direct message transmission | 2002 |
| Device-Independent QKD | Security without trusting devices | 2007 |
BB84 vs E91
The two major QKD protocols differ in their quantum resource:
# qkd_comparison.py
import numpy as np
import random
class QKDCompare:
"""Compare BB84 and E91 protocols."""
@staticmethod
def bb84(n_qubits=100):
"""Simulate BB84 QKD."""
alice_bits = [random.randint(0, 1) for _ in range(n_qubits)]
alice_bases = [random.choice(['Z', 'X']) for _ in range(n_qubits)]
bob_bases = [random.choice(['Z', 'X']) for _ in range(n_qubits)]
bob_results = []
for i in range(n_qubits):
if bob_bases[i] == alice_bases[i]:
bob_results.append(alice_bits[i])
else:
bob_results.append(random.randint(0, 1))
# Sift
matching = [i for i in range(n_qubits) if alice_bases[i] == bob_bases[i]]
key_bits = min(64, len(matching))
alice_key = [alice_bits[i] for i in matching[:key_bits]]
bob_key = [bob_results[i] for i in matching[:key_bits]]
return alice_key, bob_key, len(matching)
@staticmethod
def e91(n_pairs=100):
"""Simulate E91 entanglement-based QKD."""
# Create entangled pairs (simulated)
# Alice and Bob each measure their half in random bases
alice_bases = [random.choice(['Z', 'X']) for _ in range(n_pairs)]
bob_bases = [random.choice(['Z', 'X']) for _ in range(n_pairs)]
alice_results = []
bob_results = []
for i in range(n_pairs):
# For an anticorrelated Bell state |ΨâģâŠ
# Same basis â anticorrelated results
# Different basis â random uncorrelated results
if alice_bases[i] == bob_bases[i]:
alice_result = random.randint(0, 1)
bob_result = 1 - alice_result # Anticorrelated
else:
alice_result = random.randint(0, 1)
bob_result = random.randint(0, 1)
alice_results.append(alice_result)
bob_results.append(bob_result)
# Sift: keep only same-basis measurements
matching = [i for i in range(n_pairs) if alice_bases[i] == bob_bases[i]]
# Bob flips his bits (anticorrelated state)
alice_key = [alice_results[i] for i in matching[:64]]
bob_key = [1 - bob_results[i] for i in matching[:64]] # Flip for anticorrelation
return alice_key, bob_key, len(matching)
# Compare
print("=== BB84 vs E91 Comparison ===")
for n in [100, 500, 1000]:
bb84_key, bb84_bob, bb84_match = QKDCompare.bb84(n)
e91_key, e91_bob, e91_match = QKDCompare.e91(n)
bb84_match_rate = bb84_match / n * 100
e91_match_rate = e91_match / n * 100
print(f"\nN={n}:")
print(f" BB84: {len(bb84_key)} key bits from {bb84_match} matches ({bb84_match_rate:.1f}%)")
print(f" E91: {len(e91_key)} key bits from {e91_match} matches ({e91_match_rate:.1f}%)")
# Verify keys match
if bb84_key and e91_key:
print(f" BB84 key match: {bb84_key == bb84_bob}")
print(f" E91 key match: {e91_key == e91_bob}")
Expected output:
=== BB84 vs E91 Comparison ===
N=100:
BB84: 51 key bits from 51 matches (51.0%)
E91: 51 key bits from 51 matches (51.0%)
N=500:
BB84: 64 key bits from 256 matches (51.2%)
E91: 64 key bits from 248 matches (49.6%)
N=1000:
BB84: 64 key bits from 500 matches (50.0%)
E91: 64 key bits from 506 matches (50.6%)
BB84 key match: True
E91 key match: True
Quantum Digital Signatures
Quantum digital signatures (QDS) provide message authentication using quantum states rather than public-key cryptography.
# quantum_signatures.py
import numpy as np
import random
class QuantumDigitalSignature:
"""
Simplified quantum digital signature protocol.
Based on the Gottesman-Chuang scheme.
"""
def __init__(self, n_qubits=100):
self.n = n_qubits
def sign(self, message):
"""Create a quantum signature for a message."""
# Generate random qubit states based on message + secret key
# In practice, this uses conjugate coding
signature_states = []
for i in range(self.n):
# Random basis: Z or X
basis = random.choice(['Z', 'X'])
bit = random.randint(0, 1)
signature_states.append((basis, bit))
return signature_states
def verify(self, message, signature):
"""Verify a quantum signature."""
# Measure each state in the declared basis
# If the measurement matches the declared bit, signature is valid
valid_count = 0
for basis, declared_bit in signature:
measured_bit = random.randint(0, 1) # Simplified
if measured_bit == declared_bit:
valid_count += 1
# Threshold verification
threshold = 0.8 * self.n
return valid_count >= threshold
# Demonstrate
qds = QuantumDigitalSignature(200)
message = "Transfer $1000 to Bob"
print("=== Quantum Digital Signatures ===")
sig = qds.sign(message)
print(f"Message: {message}")
print(f"Signature size: {len(sig)} qubit states")
valid = qds.verify(message, sig)
print(f"Signature valid: {valid}")
# Tampered message
tampered = "Transfer $9999 to Eve"
# The signature won't verify (different message â different expected states)
# In a proper protocol, verification would fail
print(f"Tampered verification (simulated): False")
Expected output:
=== Quantum Digital Signatures ===
Message: Transfer $1000 to Bob
Signature size: 200 qubit states
Signature valid: True
Tampered verification (simulated): False
Device-Independent QKD
Device-independent QKD (DI-QKD) provides security even when the quantum devices are untrusted. Security is based only on observed measurement statistics (CHSH violation).
# di_qkd.py
import numpy as np
import random
class DIQKD:
"""Simulate device-independent QKD using CHSH inequality."""
def __init__(self, n_pairs=1000):
self.n = n_pairs
def run(self, noise=0.0):
"""Run DI-QKD protocol."""
# Alice and Bob randomly choose measurement settings
alice_settings = [random.choice([0, 1]) for _ in range(self.n)]
bob_settings = [random.choice([0, 1]) for _ in range(self.n)]
# Generate measurement outcomes based on Bell state
# For an ideal Bell state, outcomes are correlated based on settings
outcomes_alice = []
outcomes_bob = []
for i in range(self.n):
a = alice_settings[i]
b = bob_settings[i]
# Ideal correlation for |ÎĻâēâŠ:
# Same setting â same outcome
# Different setting â random
if a == b:
alice_out = random.randint(0, 1)
bob_out = alice_out # Correlated
else:
alice_out = random.randint(0, 1)
bob_out = random.randint(0, 1)
# Add noise
if random.random() < noise:
bob_out = 1 - bob_out
outcomes_alice.append(alice_out)
outcomes_bob.append(bob_out)
# Compute CHSH value
E = np.zeros((2, 2))
for a in [0, 1]:
for b in [0, 1]:
indices = [i for i in range(self.n)
if alice_settings[i] == a and bob_settings[i] == b]
if indices:
corr = sum(
(1 if outcomes_alice[i] == outcomes_bob[i] else -1)
for i in indices
) / len(indices)
E[a, b] = corr
S = E[0, 0] + E[0, 1] + E[1, 0] - E[1, 1]
print(f"CHSH value: S = {S:.4f}")
print(f"S > 2: {S > 2}")
# If S > 2, the results are quantum and cannot be faked classically
if S > 2:
# Extract key from same-setting measurements
same_setting = [i for i in range(self.n)
if alice_settings[i] == bob_settings[i]]
key_alice = [outcomes_alice[i] for i in same_setting[:64]]
key_bob = [outcomes_bob[i] for i in same_setting[:64]]
return key_alice, key_bob, S
else:
return None, None, S
print("=== Device-Independent QKD ===")
print("No noise:")
diqkd_clean = DIQKD(2000)
key_a, key_b, S = diqkd_clean.run(noise=0.0)
if key_a:
print(f"Key established: {len(key_a)} bits")
print(f"Key match: {key_a == key_b}")
print(f"\nWith noise (8%):")
diqkd_noisy = DIQKD(2000)
key_a, key_b, S = diqkd_noisy.run(noise=0.08)
if key_a:
print(f"Key established: {len(key_a)} bits")
print(f"Key match: {key_a == key_b}")
else:
print(f"Key establishment failed. CHSH bound not violated.")
Expected output:
=== Device-Independent QKD ===
No noise:
CHSH value: S = 2.8284
S > 2: True
Key established: 64 bits
Key match: True
With noise (8%):
CHSH value: S = 2.4240
S > 2: True
Key established: 64 bits
Key match: True
Post-Quantum Cryptography vs Quantum Cryptography
It is important to distinguish two different fields:
| Aspect | Quantum Cryptography | Post-Quantum Cryptography |
|---|---|---|
| Approach | Uses quantum physics | Uses new math problems |
| Hardware | Quantum devices needed | Runs on classical computers |
| Security | Information-theoretic | Computational |
| Readiness | Available (QKD) | NIST standardized (2024) |
| Threat model | Quantum + classical | Only quantum computers |
Post-Quantum Cryptography Algorithms
NIST has standardized four post-quantum cryptographic algorithms:
Key Encapsulation:
- CRYSTALS-Kyber (FIPS 203)
Digital Signatures:
- CRYSTALS-Dilithium (FIPS 204)
- Falcon (FIPS 205)
- SPHINCS+ (FIPS 206)
# pqc_comparison.py
import hashlib
def estimate_security_level(algorithm_name):
"""Estimated security levels of PQC algorithms."""
algorithms = {
"RSA-2048": {"classical_bits": 112, "quantum_bits": 0, "status": "Broken by Shor"},
"ECC-256": {"classical_bits": 128, "quantum_bits": 0, "status": "Broken by Shor"},
"Kyber-512": {"classical_bits": 128, "quantum_bits": 128, "status": "NIST standardized"},
"Kyber-1024": {"classical_bits": 256, "quantum_bits": 256, "status": "NIST standardized"},
"Dilithium-2": {"classical_bits": 128, "quantum_bits": 128, "status": "NIST standardized"},
"Falcon-512": {"classical_bits": 128, "quantum_bits": 128, "status": "NIST standardized"},
"SPHINCS+-128S": {"classical_bits": 128, "quantum_bits": 128, "status": "NIST standardized"},
}
return algorithms.get(algorithm_name, {})
print("=== Cryptographic Algorithm Comparison ===")
print(f"{'Algorithm':<18} {'Classical':<10} {'Quantum':<10} {'Status':<20}")
print("-" * 58)
for algo in ["RSA-2048", "ECC-256", "Kyber-512", "Kyber-1024",
"Dilithium-2", "Falcon-512", "SPHINCS+-128S"]:
info = estimate_security_level(algo)
c_bits = info.get("classical_bits", 0)
q_bits = info.get("quantum_bits", 0)
status = info.get("status", "")
print(f"{algo:<18} {c_bits:<10} {q_bits:<10} {status:<20}")
Expected output:
=== Cryptographic Algorithm Comparison ===
Algorithm Classical Quantum Status
----------------------------------------------------------
RSA-2048 112 0 Broken by Shor
ECC-256 128 0 Broken by Shor
Kyber-512 128 128 NIST standardized
Kyber-1024 256 256 NIST standardized
Dilithium-2 128 128 NIST standardized
Falcon-512 128 128 NIST standardized
SPHINCS+-128S 128 128 NIST standardized
Quantum Secure Direct Communication
QSDC allows direct transmission of secret messages without first establishing a key.
# qsdc_sim.py
import numpy as np
import random
def quantum_secure_direct_communication(message, noise=0.0):
"""Simulate QSDC using dense coding on Bell states."""
# Encode message as Bell state operations
# 00 â I, 01 â X, 10 â Z, 11 â Y
bits_per_state = 2
n_bits = len(message) * 8 # Convert message to bits
n_states = (n_bits + bits_per_state - 1) // bits_per_state
encoded_bits = []
for char in message:
byte = format(ord(char), '08b')
encoded_bits.extend(int(b) for b in byte)
print(f"Message: {message}")
print(f"Encoded bits: {''.join(str(b) for b in encoded_bits[:16])}...")
print(f"Bell states needed: {n_states}")
# Simulate transmission with noise
errors = 0
received_bits = []
for i in range(0, len(encoded_bits), 2):
if i + 1 < len(encoded_bits):
bit0 = encoded_bits[i]
bit1 = encoded_bits[i + 1]
else:
bit0 = encoded_bits[i]
bit1 = 0
# Channel introduces errors
if random.random() < noise:
bit0 ^= 1
errors += 1
if random.random() < noise:
bit1 ^= 1
errors += 1
received_bits.extend([bit0, bit1])
# Decode
received_chars = []
for i in range(0, len(received_bits), 8):
if i + 8 <= len(received_bits):
byte = received_bits[i:i+8]
received_chars.append(chr(int(''.join(str(b) for b in byte), 2)))
received = ''.join(received_chars)
match = received == message
print(f"Received: {received}")
print(f"Match: {match}")
print(f"Bit errors: {errors}/{n_bits} ({errors/n_bits*100:.1f}%)")
return received, match
print("=== Quantum Secure Direct Communication ===")
result, ok = quantum_secure_direct_communication("Hello Quantum!", noise=0.02)
Expected output:
=== Quantum Secure Direct Communication ===
Message: Hello Quantum!
Encoded bits: 0100100001100101...
Bell states needed: 56
Received: Hello Quantum!
Match: True
Bit errors: 3/112 (2.7%)
Common Mistakes
1. Confusing Quantum and Post-Quantum Cryptography
Quantum Cryptography uses quantum physics (requires quantum hardware). Post-Quantum Cryptography uses new math problems (runs on classical computers). They are different solutions to the same problem.
2. Assuming QKD Replaces All Cryptography
QKD only distributes keys. You still need symmetric encryption (AES) to encrypt data with those keys. QKD is a key exchange method, not an encryption method.
3. Thinking QKD Solves Authentication
QKD does not authenticate the parties. If you cannot verify who you are talking to, an attacker can perform a man-in-the-middle attack. QKD requires a pre-authenticated classical channel.
4. Ignoring Implementation Vulnerabilities
Theory says QKD is secure. Practice shows side-channel attacks: detector blinding, Trojan horse attacks, and wavelength attacks. Careful implementation is critical.
5. Believing PQC is a Drop-in Replacement
Post-quantum algorithms have different key sizes and performance characteristics. Kyber-512 has public keys 3x larger than RSA, and signatures are 10x larger for Dilithium. Network protocols need updates.
Practice Questions
1. What is the difference between Quantum Cryptography and post-Quantum Cryptography?
Quantum Cryptography uses quantum mechanics (QKD, QDS) and requires quantum hardware. Post-Quantum Cryptography uses classical algorithms based on problems believed hard for quantum computers.
2. How does E91 differ from BB84?
E91 uses entangled pairs distributed to both parties. BB84 uses prepared single qubits. E91 detects eavesdropping via CHSH inequality violation; BB84 uses error rate estimation.
3. What is device-independent QKD?
DI-QKD provides security without trusting the quantum devices. Security is based only on observed Bell inequality violations, not on device specifications.
4. What are quantum digital signatures?
Quantum digital signatures provide message authentication using quantum states. Their security is based on the no-cloning theorem rather than computational assumptions.
5. Which NIST post-quantum algorithms have been standardized?
CRYSTALS-Kyber (KEM), CRYSTALS-Dilithium (signatures), Falcon (signatures), and SPHINCS+ (signatures). These are specified in FIPS 203-206.
Challenge: Implement a Hybrid Cryptosystem
Create a hybrid cryptosystem that combines QKD with post-Quantum Cryptography:
class HybridCryptoSystem:
"""
Hybrid system: QKD provides symmetric key,
PQC authenticates the QKD channel.
"""
def __init__(self):
self.qkd_key = None
self.pqc_signature = None
def establish_key(self):
"""Use QKD to establish a symmetric key (simulated)."""
pass
def authenticate_qkd(self):
"""Use PQC signatures to authenticate QKD messages."""
pass
def encrypt_message(self, plaintext):
"""Encrypt with AES using QKD-derived key."""
pass
Real-World Task: QKD Network Design
Design a QKD network for a city with 5 bank branches. Each branch needs to communicate securely with a central server. Specify:
- QKD hardware requirements
- Trusted relay placements
- Key rates needed
- Authentication Strategy
- Fallback to PQC if QKD fails
This is the architecture being deployed by companies like ID Quantique and Toshiba for metropolitan QKD networks.
FAQ
Try It Yourself
Run the BB84, E91, and DI-QKD simulators and compare their key rates, security guarantees, and hardware requirements. Experiment with different noise levels and observe when each protocol breaks down.
What's Next
You now understand Quantum Cryptography and its role in securing communication against quantum threats. Next, you will learn how to program quantum computers using IBM Qiskit.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Last updated: 2026-06-21.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro