QuantumCrypt / src /bb84.py
marlonsousa's picture
Upload 17 files
0173bbf verified
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.visualization import circuit_drawer
from qiskit_aer import Aer
import numpy as np
import random
from typing import Dict, List, Tuple, Optional
import logging
from .base import QuantumProtocol
class BB84Protocol(QuantumProtocol):
"""
Implementação do protocolo BB84 para distribuição quântica de chaves.
Herda da classe base QuantumProtocol e implementa correção de erros quânticos.
"""
def __init__(self, key_length: int = 128, error_correction: bool = True):
"""
Inicializa o protocolo BB84.
Args:
key_length (int): Tamanho desejado da chave final em bits
error_correction (bool): Se True, utiliza correção de erros quânticos
"""
super().__init__(key_length) # Chama o construtor da classe base
self.error_correction = error_correction
self.simulator = Aer.get_backend('qasm_simulator')
self.current_circuit = None
def _prepare_qubit(self, bit: int, basis: str) -> QuantumCircuit:
"""
Prepara um único qubit no estado e base especificados.
Args:
bit (int): Bit a ser codificado (0 ou 1)
basis (str): Base de medição ('Z' para rectilinear ou 'X' para diagonal)
Returns:
QuantumCircuit: Circuito quântico preparado
"""
if self.error_correction:
# Circuito com correção de erros (código de repetição de 3 qubits)
qr = QuantumRegister(3, 'q')
cr = ClassicalRegister(1, 'c')
qc = QuantumCircuit(qr, cr)
# Prepara o estado base
if bit == 1:
qc.x(qr[0])
# Aplica o código de repetição
qc.cx(qr[0], qr[1]) # CNOT para copiar o estado
qc.cx(qr[0], qr[2])
# Aplica a transformação de base se necessário
if basis == 'X':
qc.h(qr) # Aplica H em todos os qubits
else:
# Circuito simples sem correção
qr = QuantumRegister(1, 'q')
cr = ClassicalRegister(1, 'c')
qc = QuantumCircuit(qr, cr)
# Prepara o estado
if bit == 1:
qc.x(qr[0])
if basis == 'X':
qc.h(qr[0])
self.current_circuit = qc
return qc
def _measure_qubit(self, circuit: QuantumCircuit, basis: str) -> QuantumCircuit:
"""
Adiciona medição em uma base específica ao circuito.
Args:
circuit (QuantumCircuit): Circuito a ser medido
basis (str): Base de medição ('Z' ou 'X')
Returns:
QuantumCircuit: Circuito com medição adicionada
"""
if self.error_correction:
# Medição com correção de erros
if basis == 'X':
circuit.h(range(3)) # Aplica H em todos os qubits para base X
# Medição em todos os qubits
circuit.measure_all()
else:
# Medição simples
if basis == 'X':
circuit.h(0)
circuit.measure([0], [0])
return circuit
def _error_correction_decode(self, measurements: List[int]) -> int:
"""
Decodifica medições usando voto majoritário para correção de erros.
Args:
measurements (List[int]): Lista de medições dos qubits
Returns:
int: Bit decodificado
"""
return 1 if sum(measurements) > len(measurements)/2 else 0
def generate_random_bits(self, n: int) -> List[int]:
"""
Gera uma lista de bits aleatórios.
Args:
n (int): Número de bits a serem gerados
Returns:
List[int]: Lista de bits aleatórios
"""
return [random.randint(0, 1) for _ in range(n)]
def generate_random_bases(self, n: int) -> List[str]:
"""
Gera uma lista de bases aleatórias.
Args:
n (int): Número de bases a serem geradas
Returns:
List[str]: Lista de bases aleatórias ('Z' ou 'X')
"""
return [random.choice(['Z', 'X']) for _ in range(n)]
def simulate_transmission(self, bits: List[int], bases: List[str]) -> Tuple[List[int], List[str]]:
"""
Simula a transmissão dos qubits de Alice para Bob.
Args:
bits (List[int]): Bits a serem transmitidos
bases (List[str]): Bases usadas para codificação
Returns:
Tuple[List[int], List[str]]: Medições de Bob e bases escolhidas
"""
bob_bases = self.generate_random_bases(len(bits))
bob_measurements = []
for i in range(len(bits)):
# Alice prepara o qubit
circuit = self._prepare_qubit(bits[i], bases[i])
# Bob mede o qubit
circuit = self._measure_qubit(circuit, bob_bases[i])
# Executa a simulação
job = self.simulator.run(circuit, shots=1)
result = job.result()
counts = list(result.get_counts().keys())[0]
if self.error_correction:
# Decodifica as medições com correção de erros
measurements = [int(bit) for bit in counts]
measured_bit = self._error_correction_decode(measurements)
else:
measured_bit = int(counts)
bob_measurements.append(measured_bit)
return bob_measurements, bob_bases
def sift_key(self, alice_bits: List[int], alice_bases: List[str],
bob_measurements: List[int], bob_bases: List[str]) -> Tuple[List[int], List[int]]:
"""
Realiza o processo de peneiramento da chave.
Args:
alice_bits (List[int]): Bits originais de Alice
alice_bases (List[str]): Bases usadas por Alice
bob_measurements (List[int]): Medições de Bob
bob_bases (List[str]): Bases usadas por Bob
Returns:
Tuple[List[int], List[int]]: Chaves peneiradas de Alice e Bob
"""
alice_key = []
bob_key = []
for i in range(len(alice_bits)):
if alice_bases[i] == bob_bases[i]:
alice_key.append(alice_bits[i])
bob_key.append(bob_measurements[i])
return alice_key, bob_key
def check_eavesdropping(self, alice_key: List[int], bob_key: List[int],
sample_size: float = 0.25) -> bool:
"""
Verifica a presença de um espião comparando uma amostra das chaves.
Args:
alice_key (List[int]): Chave de Alice
bob_key (List[int]): Chave de Bob
sample_size (float): Proporção da chave a ser verificada
Returns:
bool: True se não houver evidência de espionagem, False caso contrário
"""
if len(alice_key) != len(bob_key):
return False
sample_length = int(len(alice_key) * sample_size)
indices = random.sample(range(len(alice_key)), sample_length)
differences = 0
for i in indices:
if alice_key[i] != bob_key[i]:
differences += 1
error_rate = differences / sample_length
self.logger.info(f"Taxa de erro na amostra: {error_rate:.2%}")
# Define um limite aceitável de erro (5%)
return error_rate < 0.05
def get_circuit_visualization(self) -> str:
"""
Implementação do método abstrato da classe base.
Retorna a visualização do circuito atual em ASCII.
Returns:
str: Representação ASCII do circuito
"""
if self.current_circuit:
return circuit_drawer(self.current_circuit, output='text')
return "Nenhum circuito disponível"
def generate_key(self) -> Dict[str, List[int]]:
"""
Implementação do método abstrato da classe base.
Executa o protocolo BB84 completo para gerar uma chave compartilhada.
Returns:
Dict[str, List[int]]: Dicionário contendo as chaves finais de Alice e Bob
"""
# Fase 1: Preparação
raw_bits = self.generate_random_bits(self.key_length * 4)
alice_bases = self.generate_random_bases(len(raw_bits))
self.logger.info(f"Iniciando protocolo BB84 para gerar chave de {self.key_length} bits")
self.logger.info(f"Correção de erros: {'Ativada' if self.error_correction else 'Desativada'}")
# Fase 2: Transmissão
bob_measurements, bob_bases = self.simulate_transmission(raw_bits, alice_bases)
# Fase 3: Peneiramento
alice_key, bob_key = self.sift_key(raw_bits, alice_bases, bob_measurements, bob_bases)
self.logger.info(f"Chave peneirada gerada com {len(alice_key)} bits")
# Fase 4: Detecção de espionagem
if not self.check_eavesdropping(alice_key, bob_key):
self.logger.error("Possível tentativa de espionagem detectada!")
return {"alice": [], "bob": []}
# Fase 5: Finalização
final_key_length = min(self.key_length, len(alice_key))
alice_final_key = alice_key[:final_key_length]
bob_final_key = bob_key[:final_key_length]
self.logger.info(f"Protocolo BB84 concluído com sucesso. "
f"Chave final de {final_key_length} bits gerada.")
return {
"alice": alice_final_key,
"bob": bob_final_key
}
def get_key_statistics(self, alice_key: List[int], bob_key: List[int]) -> Dict:
"""
Calcula estatísticas sobre as chaves geradas.
Args:
alice_key (List[int]): Chave de Alice
bob_key (List[int]): Chave de Bob
Returns:
Dict: Dicionário com estatísticas das chaves
"""
if not alice_key or not bob_key:
return {
"key_length": 0,
"error_rate": 1.0,
"matching_rate": 0.0,
"entropy": 0.0
}
differences = sum(a != b for a, b in zip(alice_key, bob_key))
error_rate = differences / len(alice_key)
# Calcula a entropia da chave
ones = sum(alice_key) / len(alice_key)
zeros = 1 - ones
if zeros == 0 or ones == 0:
entropy = 0
else:
entropy = -(ones * np.log2(ones) + zeros * np.log2(zeros))
return {
"key_length": len(alice_key),
"error_rate": error_rate,
"matching_rate": 1 - error_rate,
"entropy": entropy
}
def main():
"""Função principal para demonstração do protocolo."""
# Criar uma instância do protocolo
bb84 = BB84Protocol(key_length=64, error_correction=True)
# Gerar uma chave compartilhada
result = bb84.generate_key()
# Verificar se as chaves são idênticas
if result["alice"] and result["bob"]:
print("\nChave de Alice:", "".join(map(str, result["alice"])))
print("Chave de Bob: ", "".join(map(str, result["bob"])))
# Obter e mostrar estatísticas
stats = bb84.get_key_statistics(result["alice"], result["bob"])
print("\nEstatísticas da chave:")
print(f"Tamanho: {stats['key_length']} bits")
print(f"Taxa de erro: {stats['error_rate']:.2%}")
print(f"Taxa de correspondência: {stats['matching_rate']:.2%}")
print(f"Entropia: {stats['entropy']:.2f} bits")
# Mostrar visualização do último circuito
print("\nÚltimo circuito gerado:")
print(bb84.get_circuit_visualization())
else:
print("Falha na geração da chave - possível tentativa de espionagem detectada.")