-
-
Notifications
You must be signed in to change notification settings - Fork 50.5k
Expand file tree
/
Copy pathe91_qkd.py.DISABLED.txt
More file actions
208 lines (173 loc) · 8.03 KB
/
e91_qkd.py.DISABLED.txt
File metadata and controls
208 lines (173 loc) · 8.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
"""
Implements the E91 quantum key distribution (QKD) protocol.
This protocol uses the principles of quantum entanglement and the violation of
Bell's inequality to securely distribute a secret key
between two parties (Alice and Bob) and to detect
the presence of an eavesdropper (Eve).
How it works:
1. A source (Charlie) generates pairs of entangled qubits in a Bell
state and sends one qubit of each pair to Alice and the other to Bob.
2. Alice and Bob each independently and randomly choose to measure their
incoming qubits in one of three predefined measurement bases.
3. After all measurements are complete, they communicate over a public
channel to compare the bases they chose for each qubit.
4. They divide their measurement results into two sets:
a) Cases where their chosen bases were "compatible" are used to
generate a secret key. Due to entanglement, their results in these
cases should be perfectly correlated.
b) Cases where their bases were "incompatible" are used to test for
eavesdropping by calculating the CHSH inequality parameter 'S'.
5. Quantum mechanics predicts that for an entangled system, |S| can reach
2*sqrt(2) (~2.828), whereas any classical (or eavesdropped) system is
bound by |S| <= 2. If their calculated S-value significantly violates
the classical bound, they can be confident that no eavesdropping
occurred and their generated key is secure.
Reference: https://en.wikipedia.org/wiki/Quantum_key_distribution#E91_protocol:_Artur_Ekert_.281991.29
"""
import random
import numpy as np
from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister
from qiskit_aer import AerSimulator
def e91_protocol(n_bits: int = 2000) -> dict:
"""
Simulates the E91 QKD protocol for a specified number of bits.
Args:
n_bits: The total number of entangled pairs to be generated and
measured. This determines the potential length of the raw key.
Returns:
A dictionary containing the simulation results:
- "alice_key": Alice's final, sifted secret key.
- "bob_key": Bob's final, sifted secret key.
- "s_value": The calculated CHSH inequality parameter 'S'.
- "eavesdropper_detected": A boolean indicating if |S| <= 2.
- "key_match": A boolean indicating if Alice's and Bob's keys match.
- "key_length": The final length of the sifted keys.
>>> e91_protocol(10) # doctest: +SKIP
{'alice_key': '10', 'bob_key': '10', 's_value': 2.0, ...}
>>> e91_protocol(-10)
Traceback (most recent call last):
...
ValueError: Number of bits must be > 0.
>>> e91_protocol('abc')
Traceback (most recent call last):
...
TypeError: Number of bits must be an integer.
>>> e91_protocol(10001)
Traceback (most recent call last):
...
ValueError: Number of bits is too large to simulate efficiently (>10000).
"""
# --- Input Validation ---
if not isinstance(n_bits, int):
raise TypeError("Number of bits must be an integer.")
if n_bits <= 0:
raise ValueError("Number of bits must be > 0.")
if n_bits > 10000:
raise ValueError(
"Number of bits is too large to simulate efficiently (>10000)."
)
# Define the measurement angles for Alice and Bob's bases as constants.
# The keys correspond to the basis name, and values are angles in radians.
alice_bases = {"A1": 0, "A2": np.pi / 8, "A3": np.pi / 4}
bob_bases = {"B1": np.pi / 8, "B2": np.pi / 4, "B3": 3 * np.pi / 8}
# Lists to store the choices and results for each bit.
alice_chosen_bases, bob_chosen_bases = [], []
alice_results, bob_results = [], []
# Get the quantum simulator backend.
backend = AerSimulator()
for _ in range(n_bits):
# Alice and Bob randomly choose their measurement bases.
alice_basis_name = random.choice(list(alice_bases.keys()))
bob_basis_name = random.choice(list(bob_bases.keys()))
alice_angle = alice_bases[alice_basis_name]
bob_angle = bob_bases[bob_basis_name]
# Create a quantum circuit for one entangled pair.
qr = QuantumRegister(2, "q")
cr = ClassicalRegister(2, "c")
circuit = QuantumCircuit(qr, cr)
# Create a Bell state |Φ+⟩ = (|00⟩ + |11⟩)/sqrt(2)
circuit.h(qr[0])
circuit.cx(qr[0], qr[1])
# Apply rotations to simulate measurements in the chosen bases.
circuit.ry(-2 * alice_angle, qr[0])
circuit.ry(-2 * bob_angle, qr[1])
# Measure the qubits.
circuit.measure(qr, cr)
# Execute the circuit and get the result.
job = backend.run(circuit, shots=1)
result = next(iter(job.result().get_counts().keys()))
# Store choices and results. Qiskit's bit order is reversed.
alice_chosen_bases.append(alice_basis_name)
bob_chosen_bases.append(bob_basis_name)
alice_results.append(int(result[1]))
bob_results.append(int(result[0]))
# Sift for generating the secret key.
# The key is formed when Alice and Bob choose compatible bases.
# Here, compatible means A2/B1 or A3/B2, where angles are identical.
alice_key, bob_key = [], []
for i in range(n_bits):
is_a2b1 = alice_chosen_bases[i] == "A2" and bob_chosen_bases[i] == "B1"
is_a3b2 = alice_chosen_bases[i] == "A3" and bob_chosen_bases[i] == "B2"
if is_a2b1 or is_a3b2:
alice_key.append(alice_results[i])
bob_key.append(bob_results[i])
# Sift for the CHSH inequality test (Eve detection).
# We use four specific combinations of bases for the test:
# a = A1, a' = A3 | b = B1, b' = B3
chsh_correlations: dict[str, list[int]] = {
"ab": [],
"ab_": [],
"a_b": [],
"a_b_": [],
}
for i in range(n_bits):
# Convert results {0, 1} to {-1, 1} for calculating correlation.
a_val = 1 if alice_results[i] == 0 else -1
b_val = 1 if bob_results[i] == 0 else -1
product = a_val * b_val # +1 if correlated, -1 if anti-correlated
alice_basis = alice_chosen_bases[i]
bob_basis = bob_chosen_bases[i]
if alice_basis == "A1" and bob_basis == "B1":
chsh_correlations["ab"].append(product)
elif alice_basis == "A1" and bob_basis == "B3":
chsh_correlations["ab_"].append(product)
elif alice_basis == "A3" and bob_basis == "B1":
chsh_correlations["a_b"].append(product)
elif alice_basis == "A3" and bob_basis == "B3":
chsh_correlations["a_b_"].append(product)
# Calculate the expectation value (average correlation) for each combination.
e: dict[str, float] = {}
for key, values in chsh_correlations.items():
e[key] = np.mean(values) if values else 0.0
# Calculate the S-value: S = e(a,b) - e(a,b') + e(a',b) + e(a',b')
s_value = (
e.get("ab", 0.0) - e.get("ab_", 0.0) + e.get("a_b", 0.0) + e.get("a_b_", 0.0)
)
# Check for eavesdropper: |S| > 2 indicates security.
eavesdropper_detected = abs(s_value) <= 2
return {
"alice_key": "".join(map(str, alice_key)),
"bob_key": "".join(map(str, bob_key)),
"s_value": s_value,
"eavesdropper_detected": eavesdropper_detected,
"key_match": alice_key == bob_key,
"key_length": len(alice_key),
}
if __name__ == "__main__":
# num_bits is initialized to 2000
num_bits = 2000
results = e91_protocol(num_bits)
print(f"CHSH S-value: {results['s_value']:.4f}")
print(f"Eavesdropper detected: {results['eavesdropper_detected']}")
print(f"Final key length: {results['key_length']}")
print(f"Keys match: {results['key_match']}")
if (
not results["eavesdropper_detected"]
and results["key_match"]
and results["key_length"] > 0
):
print("\nProtocol successful! Secret key generated securely.")
print(f" Alice's key: {results['alice_key']}")
print(f" Bob's key: {results['bob_key']}")
else:
print("\nProtocol failed or eavesdropper detected. Key discarded.")