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 dataclasses import dataclass, field
from channel import Channel, QChannelImpl from channel import Channel, ChannelSym
import random as r import random as r
import typing import typing
from utils import bin_encode, bin_decode from utils import bin_encode, bin_decode

4
model/bob.py

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

10
model/channel.py

@ -27,7 +27,7 @@ class Channel(abc.ABC):
@dataclass @dataclass
class QChannelImpl(Channel): class ChannelSym(Channel):
# [[(value, basis)]] # [[(value, basis)]]
bob_photons: typing.List[typing.List[typing.Tuple[bool, bool]]] = field(default_factory=list) bob_photons: typing.List[typing.List[typing.Tuple[bool, bool]]] = field(default_factory=list)
bob_classical_data_pool: str = '' bob_classical_data_pool: str = ''
@ -41,12 +41,12 @@ class QChannelImpl(Channel):
# eta - the probability for a detector to react to a photon # eta - the probability for a detector to react to a photon
detector_sensitivity: float = 0.8 detector_sensitivity: float = 0.8
transmittance: float = 0.8 transmittance: float = 0.8
eve: typing.Optional[typing.Any] = None
def get_number_of_photos(self) -> int: def get_number_of_photos(self) -> int:
return round(np.random.poisson(self.mu)) return round(np.random.poisson(self.mu))
def send_classical(self, data: str, is_bob: bool = True): def send_classical(self, data: str, is_bob: bool = True):
# print(f'{["Alice", "Bob"][is_bob]} is sending data: {data}')
if is_bob: if is_bob:
self.bob_classical_data_pool += data self.bob_classical_data_pool += data
else: else:
@ -55,14 +55,14 @@ class QChannelImpl(Channel):
def send_bit(self, bit: bool, basis: bool): def send_bit(self, bit: bool, basis: bool):
n_photons = self.get_number_of_photos() n_photons = self.get_number_of_photos()
delta = [(bit, basis) for _i in range(n_photons)] 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) self.bob_photons.append(delta)
def recv_classical(self, n: int, is_bob: bool = True) -> str: def recv_classical(self, n: int, is_bob: bool = True) -> str:
pool_name = ['alice_classical_data_pool', 'bob_classical_data_pool'][not is_bob] pool_name = ['alice_classical_data_pool', 'bob_classical_data_pool'][not is_bob]
if n == -1: if n == -1:
n = len(getattr(self, pool_name)) 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: while len(getattr(self, pool_name)) < n:
pass pass
data = getattr(self, pool_name)[:n] 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]]: def get_photon_results(self, basises: typing.Optional[typing.List[bool]]) -> typing.List[typing.Tuple[bool, bool]]:
res = list() res = list()
while len(self.bob_photons) < len(basises):
pass
for photons, basis in zip(self.bob_photons, basises): for photons, basis in zip(self.bob_photons, basises):
clicks = [random.uniform(0, 1) <= self.p_dc, random.uniform(0, 1) <= self.p_dc] clicks = [random.uniform(0, 1) <= self.p_dc, random.uniform(0, 1) <= self.p_dc]
for ph_bit, ph_basis in photons: 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 alice import Alice
from bob import Bob from bob import Bob
from channel import QChannelImpl from channel import ChannelSym
from threading import Thread from threading import Thread
import typing 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)) alice_thread = Thread(target=lambda: alice.generate_key(n))
bob_thread = Thread(target=lambda: bob.generate_key(n)) bob_thread = Thread(target=lambda: bob.generate_key(n))
alice_thread.start() 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 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 N = 1000
channel = QChannelImpl() channel = ChannelSym()
alice, bob = Alice(channel), Bob(channel) alice, bob = Alice(channel), Bob(channel)
run_qkd(alice, bob) run_qkd(alice, bob)
# print(list(map(int, alice.key))) # print(list(map(int, alice.key)))
# print(list(map(int, bob.key))) # print(list(map(int, bob.key)))
# print('Alice bits: ', *list(map(int, alice.sent))) print('Alice bits: ', *list(map(int, alice.sent)))
# print('Bob bits: ', 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))) *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('Alice basises:', *list(map(int, alice.my_basises)))
# print('Bob basises: ', *list(map(int, bob.my_basises))) print('Bob basises: ', *list(map(int, bob.my_basises)))
# bob_correctness = [left + right == 1 for left, right in bob.my_results] bob_correctness = [left + right == 1 for left, right in bob.my_results]
# print(' ', *[ print(' ', *[
# int(k) if c and b1 == b2 else ' ' 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)]) for c, k, b1, b2 in zip(bob_correctness, alice.sent, bob.my_basises, alice.my_basises)])
# print(' ', *[ 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 ' ' {(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)]) 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(
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) e, r = get_e_and_r(alice, bob, N)
print(f'E: {e * 100:.1f}%, R: {r}') 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