diff --git a/task6/main.py b/task6/main.py new file mode 100644 index 0000000..cea5a87 --- /dev/null +++ b/task6/main.py @@ -0,0 +1,335 @@ +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)