Files
2025-10-07 19:09:08 +03:00

336 lines
15 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import cirq
import numpy as np
def bitstring(bits):
"""Преобразует список битов в строку, состоящую из символов '0' и '1'.
Аргументы:
bits -- список или массив битов (0 или 1)
Возвращает:
строку, например, '0101101'
"""
return "".join(str(int(b)) for b in bits)
class BB84(object):
def __init__(self, repetitions=1, random_seed=None):
"""
Инициализация протокола BB84.
repetitions -- число повторений измерений (обычно 1, для статистики можно больше)
random_seed -- зерно генератора случайных чисел для воспроизводимости (если None, сгенерируется случайно)
"""
super().__init__()
if random_seed is None:
random_seed = np.random.randint(0, 2**31 - 1) # Генерируем случайное зерно
np.random.seed(random_seed) # Настраиваем генератор случайных чисел
self.repetitions = repetitions # Количество повторов симуляции схемы
self.circuits = {} # Словарь для хранения схем с разными названиями
def setup_demo(self, num_qubits):
"""
Генерация случайных параметров для одной сессии BB84.
Выбираются:
- Базисы Алисы (0 - вычислительный, 1 - базис Адамара)
- Состояния Алисы для каждого кубита (0 или 1)
- Базисы Боба (случайные 0 или 1)
- Базисы Евы (случайные 0 или 1)
Также вычисляется ожидаемый секретный ключ для совпадающих базисов Алисы и Боба
"""
self.alice_basis = [
np.random.randint(0, 2) for _ in range(num_qubits)
] # 0 или 1
self.alice_state = [
np.random.randint(0, 2) for _ in range(num_qubits)
] # 0 или 1
self.bob_basis = [np.random.randint(0, 2) for _ in range(num_qubits)]
self.eve_basis = [np.random.randint(0, 2) for _ in range(num_qubits)]
# Формируем ожидаемый ключ - отбираем биты Алисы там, где базисы совпали с Бобом
self.expected_key = bitstring(
[
self.alice_state[i]
for i in range(num_qubits)
if self.alice_basis[i] == self.bob_basis[i]
]
)
def make_bb84_circuit(
self, num_qubits, sender_basis, receiver_basis, sender_state, name="1"
):
"""
Построение квантовой схемы для протокола BB84.
Параметры:
num_qubits -- число кубитов
sender_basis -- базисы отправителя (0 - вычислительный, 1 - Адамара)
receiver_basis -- базисы получателя (0 или 1)
sender_state -- биты состояния отправителя (0 или 1)
name -- название схемы (ключ в словаре circuits)
Алгоритм:
1) Отправитель подготавливает кубиты:
- Применяет X к кубитам с состоянием 1 (переключение |0> -> |1>)
- Применяет H к кубитам, если базис Адамара (1)
2) Получатель измеряет кубиты:
- Применяет H перед измерением, если базис Адамара (1)
3) Все кубиты измеряются
"""
self.qubits = cirq.LineQubit.range(num_qubits) # Создание линии кубитов
self.circuits[name] = cirq.Circuit() # Инициализация пустой схемы
sender_flips = [] # Операции X для изменения состояния с |0> на |1>
sender_rotations = [] # Операции H для переключения базиса
# Цикл по кубитам на стороне отправителя
for index in range(num_qubits):
if sender_state[index] == 1:
sender_flips.append(
cirq.X(self.qubits[index])
) # Применяем X, если бит 1
if sender_basis[index] == 1:
sender_rotations.append(
cirq.H(self.qubits[index])
) # Применяем H, если выбран базис Адамара
# Добавляем операции отправителя в схему как отдельные "слои" времени (Moments),
# что соответствует выполнению этих операций одновременно независимо для разных кубитов
self.circuits[name].append(cirq.Moment(sender_flips))
self.circuits[name].append(cirq.Moment(sender_rotations))
# Операции получателя - подготовка базиса измерения
recv_basis_choice = []
for index, rbasis in enumerate(receiver_basis):
if rbasis == 1:
recv_basis_choice.append(
cirq.H(self.qubits[index])
) # Применяем H для базиса Адамара перед измерением
self.circuits[name].append(
cirq.Moment(recv_basis_choice)
) # Добавляем перед измерением
self.circuits[name].append(
cirq.Moment(cirq.measure_each(*self.qubits))
) # Измеряем все кубиты
def simulate_base_protocol(self, num_qubits=8, print_legend=None):
"""
Симуляция базового протокола BB84 без присутствия перехватчика (Евы).
- Генерация параметров
- Создание схемы для Алисы и Боба
- Симуляция измерений
- Выделение ключа на основе совпадения базисов
- Печать схемы и результатов
"""
self.setup_demo(num_qubits)
# Строим схему взаимодействия Алисы и Боба
self.make_bb84_circuit(
num_qubits, self.alice_basis, self.bob_basis, self.alice_state, name="base"
)
# Запуск симуляции схемы c заданным числом повторений
result = cirq.Simulator().run(
program=self.circuits["base"], repetitions=self.repetitions
)
# Получение бит результата измерений: для каждого кубита берётся первый результат первого повторения (.item())
result_bitstring = bitstring(
[int(result.measurements[str(q)][0].item()) for q in self.qubits]
)
# Формируем ключ Боба только для тех позиций, где базисы совпали с Алисой
obtained_key = "".join(
[
result_bitstring[i]
for i in range(num_qubits)
if self.alice_basis[i] == self.bob_basis[i]
]
)
# Выводим схему и результаты
print("########## Печать схемы ##########")
print(self.circuits["base"])
print("########## Печать результатов ##########")
self.print_results(
self.alice_basis,
self.bob_basis,
self.alice_state,
self.expected_key,
obtained_key,
)
print(
"Общий секретный ключ успешно распределен и может использоваться для шифрования."
)
def simulate_eavesdropped_protocol(self, num_qubits=8, print_legend=None):
"""
Симуляция протокола BB84 с перехватом.
- Генерация параметров
- Симуляция перехвата Евы между Алисой и Бобом
- Симуляция передачи от Евы к Бобу
- Формирование ключей по разным базисам
- Печать схемы и результатов
"""
self.setup_demo(num_qubits)
# Создаем схему передачи Алиса -> Ева (с их базисами)
self.make_bb84_circuit(
num_qubits,
self.alice_basis,
self.eve_basis,
self.alice_state,
name="alice_eve",
)
# Симуляция измерений Евы
result = cirq.Simulator().run(
program=self.circuits["alice_eve"], repetitions=self.repetitions
)
# Ева сохраняет измеренные биты
eve_state = [int(result.measurements[str(q)][0].item()) for q in self.qubits]
# Ева формирует новую схему для передачи Бобу, используя свои измерения и выбранный базис
self.make_bb84_circuit(
num_qubits, self.eve_basis, self.bob_basis, eve_state, name="eve_bob"
)
# Симуляция измерений Боба
result = cirq.Simulator().run(
program=self.circuits["eve_bob"], repetitions=self.repetitions
)
result_bitstring = bitstring(
[int(result.measurements[str(q)][0].item()) for q in self.qubits]
)
# Формируем ключи для анализа совпадения (Алиса-Ева и Алиса-Боб)
intercepted_key = "".join(
[
result_bitstring[i]
for i in range(num_qubits)
if self.alice_basis[i] == self.eve_basis[i]
]
)
obtained_key = "".join(
[
result_bitstring[i]
for i in range(num_qubits)
if self.alice_basis[i] == self.bob_basis[i]
]
)
# Объединяем цепочки схем Алиса->Ева и Ева->Боб для вывода
circuit = self.circuits["alice_eve"] + self.circuits["eve_bob"]
print("########## Печать схемы ##########")
print(circuit)
print("########## Печать результатов ##########")
self.print_eavesdropped_results(
self.alice_basis,
self.bob_basis,
self.eve_basis,
self.alice_state,
eve_state,
self.expected_key,
obtained_key,
)
print("Ева попыталась подслушать и повторно отправить кубиты.")
print("Это приводит к ошибкам в ключе, которые обнаруживает Боб.")
def print_results(
self, alice_basis, bob_basis, alice_state, expected_key, obtained_key
):
"""
Вывод результатов базового протокола без перехвата.
Печатаются:
- исходное сообщение Алисы,
- базисы Алисы и Боба,
- совпадения базисов (в виде X),
- ключи Алисы и Боба.
"""
num_qubits = len(alice_basis)
basis_match = "".join(
["X" if alice_basis[i] == bob_basis[i] else "_" for i in range(num_qubits)]
)
alice_basis_str = "".join(
["C" if alice_basis[i] == 0 else "H" for i in range(num_qubits)]
)
bob_basis_str = "".join(
["C" if bob_basis[i] == 0 else "H" for i in range(num_qubits)]
)
print(f"Только Алиса знает сообщение:\t{bitstring(alice_state)}")
print("Базисы Алисы и Боба (C - вычислительный, H - Адамара):")
print(f"Алиса: {alice_basis_str}")
print(f"Боб: {bob_basis_str}")
print(f"Совпадение базисов: {basis_match}")
print(f"Ключ Алисы: {expected_key}")
print(f"Ключ Боба: {obtained_key}")
def print_eavesdropped_results(
self,
alice_basis,
bob_basis,
eve_basis,
alice_state,
eve_state,
expected_key,
obtained_key,
):
"""
Вывод результатов протокола с перехватом.
Печатаются:
- исходное сообщение Алисы,
- предположение Евы (ее измеренные биты),
- базисы Алисы, Евы и Боба,
- совпадения базисов,
- ключи Алисы, Евы (ставится равным ключу Алисы, тк она пытается угадать),
и ключ Боба.
"""
num_qubits = len(alice_basis)
eve_basis_match = "".join(
["X" if alice_basis[i] == eve_basis[i] else "_" for i in range(num_qubits)]
)
bob_basis_match = "".join(
["X" if alice_basis[i] == bob_basis[i] else "_" for i in range(num_qubits)]
)
alice_basis_str = "".join(
["C" if alice_basis[i] == 0 else "H" for i in range(num_qubits)]
)
eve_basis_str = "".join(
["C" if eve_basis[i] == 0 else "H" for i in range(num_qubits)]
)
bob_basis_str = "".join(
["C" if bob_basis[i] == 0 else "H" for i in range(num_qubits)]
)
print(f"Только Алиса знает сообщение:\t{bitstring(alice_state)}")
print(f"Предположение Евы о cообщении:\t{bitstring(eve_state)}")
print("Базисы (C - вычислительный, H - Адамара):")
print(f"Алиса: {alice_basis_str}")
print(f"Ева: {eve_basis_str}")
print(f"Боб: {bob_basis_str}")
print(f"Совпадение базисов Алисы и Евы: {eve_basis_match}")
print(f"Совпадение базисов Алисы и Боба: {bob_basis_match}")
print(f"Ключ Алисы: {expected_key}")
print(f"Ключ Евы: {expected_key}") # По идее, Ева пытается угадать ключ Алисы
print(f"Ключ Боба: {obtained_key}")
if __name__ == "__main__":
bb84 = BB84()
print("=== БАЗОВЫЙ ПРОТОКОЛ BB84 ===")
bb84.simulate_base_protocol(num_qubits=8, print_legend=True)
print("\n\n=== ПРОТОКОЛ BB84 С ПЕРЕХВАТОМ ===")
bb84.simulate_eavesdropped_protocol(num_qubits=8, print_legend=False)