Exploring the BB84 Quantum Key Distribution Protocol with Amazon Braket
--
Quantum Key Distribution (QKD) stands at the forefront of quantum cryptography, offering a theoretically unbreakable method for secure communication. Among various QKD protocols, BB84, introduced by Charles Bennett and Gilles Brassard in 1984, is a pioneering and widely studied protocol. In this post, we delve into an implementation of the BB84 protocol using Amazon Braket’s SDK, which serves as a powerful tool for exploring quantum computing concepts.
The Core Idea Behind BB84
BB84 leverages the fundamental principles of quantum mechanics — the superposition of states and the no-cloning theorem — to enable two parties (traditionally named Alice and Bob) to generate a shared, secret random key. This key can then be used for encrypting and decrypting messages, ensuring secure communication.
The protocol operates under the premise that quantum information, unlike classical information, cannot be precisely measured or replicated without disturbing its original state. This characteristic makes eavesdropping detectable.
Implementing BB84 in Amazon Braket
We begin our journey by setting up our environment in Amazon Braket and defining the number of qubits (N
) we plan to use. For simplicity, we choose 8 qubits, but this number can be adjusted based on the capabilities of the quantum simulator or the quantum device in use.
import numpy as np
import time
from braket.circuits import Circuit
from braket.devices import LocalSimulator
import matplotlib.pyplot as plt
# Dynamic random seed for varied outcomes
np.random.seed(int(time.time()))
N = 8 # Number of qubits
Step 1: Encoding Alice’s Message
Alice begins by preparing a sequence of qubits. For each qubit, she randomly chooses a bit value (0 or 1) and a basis (Z or X). If she chooses the Z basis, the qubit is prepared in one of the two computational basis states (|0⟩ or |1⟩). If she chooses the X basis, the qubit is prepared in one of the two superposition states (|+⟩ or |−⟩).
This step is crucial as it utilizes the quantum property of superposition, where a qubit can exist in multiple states simultaneously until measured.
def encode_message(bits, bases, N):
message = []
for i in range(N):
qc = Circuit()
# Apply gates based on Alice's bits and bases
if bases[i] == 0: # Z-basis
if bits[i] == 1:
qc.x(0)
else: # X-basis
if bits[i] == 0:
qc.h(0)
else:
qc.x(0).h(0)
# If no gate is applied (i.e., bit and basis are both 0), add an identity gate
if len(qc.instructions) == 0:
qc.i(0)
message.append(qc)
return message
Step 2: Bob’s Measurement
Bob, who receives these qubits from Alice, independently chooses a measurement basis for each qubit without knowing Alice’s choice. He then measures each qubit accordingly. The probabilistic nature of quantum mechanics means that if Bob’s basis choice matches Alice’s, he’ll correctly deduce the bit value. If not, the measurement result is random.
def measure_message(message, bases, N):
measurements = []
device = LocalSimulator()
for i in range(N):
qc = message[i].copy() # Creating a copy of the circuit for measurement
if bases[i] == 1: # X-basis chosen by Bob
qc.h(0) # Applying Hadamard gate to measure in X-basis
# The simulator automatically measures all qubits at the end of the circuit
task = device.run(qc, shots=1) # Running the circuit on the simulator for a single shot
result = task.result() # Getting the result
measured_bit = result.measurements[0][0] # Extracting Bob's measured bit
measurements.append(measured_bit)
return measurements
Step 3: Sifting to Form the Secret Key
After the measurement, Alice and Bob communicate over a classical channel to compare their basis choices. They discard any bits where their bases did not match. This process, known as sifting, results in a shorter but correlated sequence of bits — the raw key.
Interestingly, due to the random basis choices, approximately half of the bits are discarded during sifting, leaving them with a shared key roughly 50% the length of the original sequence.
def sift_key(a_bases, b_bases, bits, N):
sifted_key = []
for i in range(N):
if i < len(a_bases) and i < len(b_bases) and i < len(bits):
if a_bases[i] == b_bases[i]: # Keeping only bits with matching bases
sifted_key.append(bits[i])
return sifted_key
Running and Storing Results
We are ready to get our key. This segment of the code encapsulates the transition from random bit generation to the creation of a shared secret key, demonstrating the fascinating interplay of quantum mechanics and cryptography.
# Alice's random bits and bases
alice_bits = np.random.randint(2, size=N)
alice_bases = np.random.randint(2, size=N)
# Encoding Alice's message
alice_message = encode_message(alice_bits, alice_bases, N)
# Bob's bases for measuring the message
bob_bases = np.random.randint(2, size=N)
bob_measurements = measure_message(alice_message, bob_bases, N)
# Sifting the key to get the shared secret key
alice_key = sift_key(alice_bases, bob_bases, alice_bits, N)
bob_key = sift_key(alice_bases, bob_bases, bob_measurements, N)
Visualizing the Key Distillation Process
To better understand the process, we visualize the stages using a bar chart. This chart illustrates the number of initial bits, the number of bits post-basis comparison, and the length of the final key.
initial_bits = N
matching_bases_bits = sum(a_base == b_base for a_base, b_base in zip(alice_bases, bob_bases))
final_key_bits = len(alice_key)
# Specify different colors for each bar
colors = ['skyblue', 'salmon', 'lightgreen']
plt.bar(['Initial Bits', 'Matching Bases', 'Final Key'],
[initial_bits, matching_bases_bits, final_key_bits],
color=colors)
plt.ylabel('Number of Bits')
plt.title('BB84 Key Distillation Process')
plt.show()
print("Alice's key:", alice_key)
print("Bob's key:", bob_key)
print("\nExplanation:")
print(f"Initially, Alice and Bob each generate {initial_bits} bits.")
print(f"After comparing bases, the number of bits with matching bases is {matching_bases_bits}.")
print("During the sifting process, bits with non-matching bases are discarded.")
print(f"The final distilled key contains approximately {final_key_bits} bits, roughly 50% of the initial bits.")
print(f"If there is an evesdropping attempt, or in real life implementations, the final key is always smaller than the matching bases.")
Final Thoughts
The resulting keys from Alice and Bob should be identical if there was no eavesdropping or interference. In practical applications, additional steps like error correction and privacy amplification are essential to ensure the key’s security and integrity.
This exploration with Amazon Braket provides a glimpse into the fascinating world of quantum cryptography, demonstrating the unique properties of quantum mechanics that make secure quantum communication a reality.