Browse Source

Add Eve and readme

master
Lev 3 years ago
parent
commit
80cd044d2c
  1. 13
      README.md
  2. 347
      charts.ipynb
  3. 2
      model/alice.py
  4. 4
      model/bob.py
  5. 10
      model/channel.py
  6. 30
      model/eve.py
  7. 69
      model/main.py

13
README.md

@ -0,0 +1,13 @@
# Квантовая криптография - менторский проект
## Симулятор
Симулятор лежит в папке `model`. Его главные элементы:
- Абстрактный класс `Channel` ([тут](model/channel.py)). В нем описаны методы, которые должны быть у канала.
Теоретически можно написать реализацию даже для реального оборудования, но тут есть только `ChannelSym` - симуляция канала.
- Класс `Alice` ([тут](model/alice.py)). Объекты этого класса используют канал (то есть объект любого класса, реализующего абстрактные методы из `Channel`) и реализуют протокол генерации ключа BB84 со стороны отправителя.
- Класс `Bob` ([тут](model/bob.py)). Как Алиса, только не Алиса, а Боб: реализует получателя в протоколе
- Класс `EveBS` ([тут](model/eve.py)). Реализует beam splitter атаку на `ChannelSym`

347
charts.ipynb

File diff suppressed because one or more lines are too long

2
model/alice.py

@ -1,5 +1,5 @@
from dataclasses import dataclass, field
from channel import Channel, QChannelImpl
from channel import Channel, ChannelSym
import random as r
import typing
from utils import bin_encode, bin_decode

4
model/bob.py

@ -1,9 +1,9 @@
from dataclasses import dataclass, field
from channel import Channel, QChannelImpl
from channel import Channel, ChannelSym
import random as r
import typing
from model.utils import bin_encode, bin_decode
from utils import bin_encode, bin_decode
@dataclass

10
model/channel.py

@ -27,7 +27,7 @@ class Channel(abc.ABC):
@dataclass
class QChannelImpl(Channel):
class ChannelSym(Channel):
# [[(value, basis)]]
bob_photons: typing.List[typing.List[typing.Tuple[bool, bool]]] = field(default_factory=list)
bob_classical_data_pool: str = ''
@ -41,12 +41,12 @@ class QChannelImpl(Channel):
# eta - the probability for a detector to react to a photon
detector_sensitivity: float = 0.8
transmittance: float = 0.8
eve: typing.Optional[typing.Any] = None
def get_number_of_photos(self) -> int:
return round(np.random.poisson(self.mu))
def send_classical(self, data: str, is_bob: bool = True):
# print(f'{["Alice", "Bob"][is_bob]} is sending data: {data}')
if is_bob:
self.bob_classical_data_pool += data
else:
@ -55,14 +55,14 @@ class QChannelImpl(Channel):
def send_bit(self, bit: bool, basis: bool):
n_photons = self.get_number_of_photos()
delta = [(bit, basis) for _i in range(n_photons)]
if self.eve is not None:
delta = self.eve.process_photons(delta)
self.bob_photons.append(delta)
def recv_classical(self, n: int, is_bob: bool = True) -> str:
pool_name = ['alice_classical_data_pool', 'bob_classical_data_pool'][not is_bob]
if n == -1:
n = len(getattr(self, pool_name))
# print(f'{["Alice", "Bob"][is_bob]} is receiving {n} '
# f'characters of data. Length now: {len(getattr(self, pool_name))}')
while len(getattr(self, pool_name)) < n:
pass
data = getattr(self, pool_name)[:n]
@ -72,6 +72,8 @@ class QChannelImpl(Channel):
def get_photon_results(self, basises: typing.Optional[typing.List[bool]]) -> typing.List[typing.Tuple[bool, bool]]:
res = list()
while len(self.bob_photons) < len(basises):
pass
for photons, basis in zip(self.bob_photons, basises):
clicks = [random.uniform(0, 1) <= self.p_dc, random.uniform(0, 1) <= self.p_dc]
for ph_bit, ph_basis in photons:

30
model/eve.py

@ -0,0 +1,30 @@
from dataclasses import dataclass, field
import typing
from random import choice, uniform
@dataclass
class EveBS:
hijack_probability: float = 0.45
my_basises: typing.List[bool] = field(default_factory=list)
obtained_data: typing.List[typing.Optional[bool]] = field(default_factory=list)
def process_photons(self, photons: typing.List[typing.Tuple[bool, bool]]) -> typing.List[typing.Tuple[bool, bool]]:
basis = choice([False, True])
self.my_basises.append(basis)
clicks = [0, 0]
passed = []
for photon in photons:
if uniform(0, 1) <= self.hijack_probability:
photon_bit, photon_basis = photon
if photon_basis == basis:
clicks[photon_bit] += 1
else:
clicks[choice([0, 1])] += 1
else:
passed.append(photon)
if any(clicks) and not all(clicks):
self.obtained_data.append(bool(clicks[1]))
else:
self.obtained_data.append(None)
return passed

69
model/main.py

@ -1,11 +1,13 @@
from alice import Alice
from bob import Bob
from channel import QChannelImpl
from channel import ChannelSym
from threading import Thread
import typing
from eve import EveBS
def run_qkd(alice, bob, n=1000):
def run_qkd(alice: Alice, bob: Bob, n: int = 1000):
alice_thread = Thread(target=lambda: alice.generate_key(n))
bob_thread = Thread(target=lambda: bob.generate_key(n))
alice_thread.start()
@ -18,25 +20,58 @@ def get_e_and_r(alice, bob, n=1000) -> typing.Tuple[float, float]:
return 1 - sum(k1 == k2 for k1, k2 in zip(alice.key, bob.key)) / len(alice.key), len(alice.key) / n
if __name__ == '__main__':
channel_parameters = {'p_opt': 0.05, 'p_dc': 0.05, 'mu': 1, 'detector_sensitivity': 0.8, 'transmittance': 0.8}
def plot(parameter, values, add_eve: bool = True):
data_r, data_e = [], []
n = 1000
for val in values:
print(val)
channel = ChannelSym(**{parameter: val})
alice, bob = Alice(channel), Bob(channel)
if add_eve:
eve = EveBS()
channel.eve = eve
run_qkd(alice, bob, n)
e, r = get_e_and_r(alice, bob, n)
data_r.append(r)
data_e.append(e)
from matplotlib import pyplot as plt
plt.plot(values, data_r)
plt.plot(values, data_e)
plt.legend(['R', 'E'])
plt.xlabel({'p_dc': '$p_{dc}$'}.get(parameter, parameter))
plt.title('$R$ and $E$ vs. ' + {'p_dc': '$p_{dc}$'}.get(parameter, parameter) + ' with Eve' * add_eve)
plt.show()
def run_one():
N = 1000
channel = QChannelImpl()
channel = ChannelSym()
alice, bob = Alice(channel), Bob(channel)
run_qkd(alice, bob)
# print(list(map(int, alice.key)))
# print(list(map(int, bob.key)))
# print('Alice bits: ', *list(map(int, alice.sent)))
# print('Bob bits: ',
# *list(map(lambda t: {(0, 1): 1, (1, 0): 0, (0, 0): 2, (1, 1): 3}[(int(t[0]), int(t[1]))], bob.my_results)))
# print('Alice basises:', *list(map(int, alice.my_basises)))
# print('Bob basises: ', *list(map(int, bob.my_basises)))
# bob_correctness = [left + right == 1 for left, right in bob.my_results]
# print(' ', *[
# int(k) if c and b1 == b2 else ' '
# for c, k, b1, b2 in zip(bob_correctness, alice.sent, bob.my_basises, alice.my_basises)])
# print(' ', *[
# {(0, 1): 1, (1, 0): 0, (0, 0): 2, (1, 1): 3}[(int(k[0]), int(k[1]))] if c and b1 == b2 else ' '
# for c, k, b1, b2 in zip(bob_correctness, bob.my_results, bob.my_basises, alice.my_basises)])
# print(f'{100 * sum(k1 == k2 for k1, k2 in zip(alice.key, bob.key)) / len(alice.key):.2f}%, key length: {len(alice.key)}')
print('Alice bits: ', *list(map(int, alice.sent)))
print('Bob bits: ',
*list(map(lambda t: {(0, 1): 1, (1, 0): 0, (0, 0): 2, (1, 1): 3}[(int(t[0]), int(t[1]))], bob.my_results)))
print('Alice basises:', *list(map(int, alice.my_basises)))
print('Bob basises: ', *list(map(int, bob.my_basises)))
bob_correctness = [left + right == 1 for left, right in bob.my_results]
print(' ', *[
int(k) if c and b1 == b2 else ' '
for c, k, b1, b2 in zip(bob_correctness, alice.sent, bob.my_basises, alice.my_basises)])
print(' ', *[
{(0, 1): 1, (1, 0): 0, (0, 0): 2, (1, 1): 3}[(int(k[0]), int(k[1]))] if c and b1 == b2 else ' '
for c, k, b1, b2 in zip(bob_correctness, bob.my_results, bob.my_basises, alice.my_basises)])
print(
f'{100 * sum(k1 == k2 for k1, k2 in zip(alice.key, bob.key)) / len(alice.key):.2f}%, '
f'key length: {len(alice.key)}')
e, r = get_e_and_r(alice, bob, N)
print(f'E: {e * 100:.1f}%, R: {r}')
if __name__ == '__main__':
import numpy as np
plot('p_dc', np.arange(0, 0.1, 0.01))

Loading…
Cancel
Save