# BB84 quantum key distribution

The weak spot when dealing with key distribution is … the key being distributed. More precisely, you cannot know for sure the key has not been hijacked somewhere in between. If hijacked the key can be read. copied and re-sent without the receiver or sender knowing about it. Quantum computing helps here because:

• one cannot clone qubit. While bits can be cloned and re-transferred with qubits you can only instantiate new ones. Of course, new qubits with a particular value are indistinguishable of older ones with the same value but this assumes you know the value of the old ones.
• when you read a qubit you destroy it. Not literally making it disappear, but the collapse of the wave-function cannot be rolled back. A quantum state has no memory of how it was before a measurement turned (i.e. forced) it into a particular state. If you measure it, you alter it.

Taken together, this means that any attempt to measure or clone a qubit will alter the qubit or fail respectively. If two parties exchange a series of qubits they can detect if a third party tried to fiddle with the series and this is the essence of the BB84 protocol. From a philosophical point of view the essence of it relies on the fact that no-one, not anybody can see beyond the quantum randomness. When you collapse a quantum particle there is absolutely no way to tell where is will go (if using a different base than the original).

The BB84 protocol uses a quantum and a classical channel. Both channels can be completely open; anyone can listen but only the sender and receiver will in the end know the shared key. You can have only one received because any other will appear as eavesdropping. The algorithm goes as follows (using as usual Bob, Alice and Eve):

• Alice uses a light source to create a photon. Any two-state qubit realization is fine as well.
• The photon is sent through a polarizer and randomly given one of four possible polarization and bit designations — Vertical (One bit), Horizontal (Zero bit), 45 degree right (One bit), or 45 degree left (Zero bit).
• The photon travels to Bob’s location.
• Bob has two beamsplitters — a diagonal and vertical/horizontal – and two photon detectors.
• Bob randomly chooses one of the two beamsplitters and checks the photon detectors.
• The process is repeated until the entire key has been transmitted to Bob.
• Bob then tells Alice in sequence which beamsplitter he used.
• Alice compares this information with the sequence of polarizers she used to send the key.
• Alice tells Bob where in the sequence of sent photons he used the right beamsplitter.
• Now both Alice and Bob have a sequence of bits (sifted key) they both know.

The important part in this is that the actual bits, i.e. the actual measurement values, are not shared. The series of polarization filters are shared but not the bits. Together with the uncertainty inherent to the measurement it means that anyone can listen but can only guess what the measurement has yield.

Provided Alice and Bob perform this type of exchange long enough they will end up with a key of a certain length. Also note that the potential errors between the two parties could be due to the inevitable noise, so the detection of eavesdropping is bound to some threshold rathe than a true/false.

Below is a fun implementation of the B84 process. The essence of the quantum sits in the measurement function:

from numpy import matrix
from math import pow, sqrt
from random import randint
import sys, argparse

class qubit():
def __init__(self,initial_state):
if initial_state:
self.__state = matrix([[0],[1]])
else:
self.__state = matrix([[1],[0]])
self.__measured = False
self.__H = (1/sqrt(2))*matrix([[1,1],[1,-1]])
self.__X = matrix([[0,1],[1,0]])
def show(self):
aux = ""
if round(matrix([1,0])*self.__state,2):
aux += "{0}|0>".format(str(round(matrix([1,0])*self.__state,2)) if round(matrix([1,0])*self.__state,2) != 1.0 else '')
if round(matrix([0,1])*self.__state,2):
if aux:
aux += " + "
aux += "{0}|1>".format(str(round(matrix([0,1])*self.__state,2)) if round(matrix([0,1])*self.__state,2) != 1.0 else '')
return aux
def measure(self):
if self.__measured:
M = 1000000
m = randint(0,M)
self.__measured = True
if m < round(pow(matrix([1,0])*self.__state,2),2)*M:
return 0
else:
return 1
if self.__measured:
self.__state = self.__H*self.__state
def X(self):
if self.__measured:
self.__state = self.__X*self.__state

class quantum_user():
def __init__(self,name):
self.name = name
def send(self,data,basis):

assert len(data) == len(basis), "Basis and data must be the same length!"
qubits = list()
for i in range(len(data)):
if not basis[i]:
#Base computacional
if not data[i]:
qubits.append(qubit(0))
else:
qubits.append(qubit(1))
else:
if not data[i]:
aux = qubit(0)
else:
aux = qubit(1)
qubits.append(aux)
return qubits
assert len(data) == len(basis), "Basis and data must be the same length!"
bits = list()
for i in range(len(data)):
if not basis[i]:
bits.append(data[i].measure())
else:
bits.append(data[i].measure())
return bits
def generate_random_bits(N):
aux = list()
for i in range(N):
aux.append(randint(0,1))
return aux

def QKD(N,verbose=False,eve_present=False):
alice_basis = generate_random_bits(N)
alice_bits = generate_random_bits(N)
alice = quantum_user("Alice")
alice_qubits = alice.send(data=alice_bits,basis=alice_basis)
if eve_present:
eve_basis = generate_random_bits(N)
eve = quantum_user("Eve")
alice_qubits = eve.send(data=eve_bits,basis=eve_basis)
bob_basis = generate_random_bits(N)
bob = quantum_user("Bob")
alice_key = list()
bob_key = list()
for i in range(N):
if alice_basis[i] == bob_basis[i]:
alice_key.append(alice_bits[i])
bob_key.append(bob_bits[i])
if alice_key != bob_key:
key = False
length = None
print("Encription key mismatch, eve is present.")
else:
key = True
length = len(bob_key)
print("Successfully exchanged key!")
print("Key Length: " + str(length))
if verbose:
print("Alice generates {0} random basis.".format(str(N)))
input()
print(''.join(str(e) for e in alice_basis))
input()
print("Alice generates {0} random bits.".format(str(N)))
input()
print(''.join(str(e) for e in alice_bits))
input()
print("Alice sends to Bob {0} encoded Qubits.".format(str(N)))
input()
aux = ""
for q in alice_qubits:
aux += q.show() + "   "
print(aux)
input()
if eve_present:
print("Eve intercepts Qubits!")
input()
print(''.join(str(e) for e in eve_basis))
input()
print("Eve's bits.")
input()
print(''.join(str(e) for e in eve_bits))
input()
print("Bob generates {0} random basis.".format(str(N)))
input()
print(''.join(str(e) for e in bob_basis))
input()
print("Bob receives and decodes Alice's Qubits.")
input()
print(''.join(str(e) for e in bob_bits))
input()
print("Alice and Bob interchange basis through Internet and compare their basis.")
input()
return key

if __name__ == "__main__":
parser = argparse.ArgumentParser(description='BB84 QKD demonstration with Python.')
args = parser.parse_args()
assert int(args.qubits)
ret = list()
if args.iterate:
assert int(args.iterate)
N = int(args.iterate)
else:
N = 1
for i in range(N):
print("############# {0} #############".format(str(i)))
ret.append(QKD(int(args.qubits),verbose=args.verbose,eve_present=args.eve))
print("###############################".format(str(i)))
print("############################")
print("############################")
t = "{0:.2f}".format(float(ret.count(True))*100.0/float(N))
u = "{0:.2f}".format(float(ret.count(False))*100.0/float(N))
print("True: {0} <{1}%>".format(ret.count(True),str(t)))
print("False: {0} <{1}%>".format(ret.count(False),str(u)))


Read the original article by Bennett and Brassart entitled “Quantum cryptography: public key distribution and coin tossing”.

Tags: