Distribución Cuántica de Claves: Protocolo BB84
La Distribución Cuántica de Claves (QKD, por sus siglas en inglés) es un método para compartir claves criptográficas que utiliza principios de la mecánica cuántica para garantizar la seguridad. A diferencia de la criptografía post-cuántica, que utiliza matemáticas clásicas resistentes a ataques cuánticos, QKD aprovecha las propiedades fundamentales de la física cuántica.
El protocolo BB84, propuesto por Bennett y Brassard en 1984, fue el primer protocolo QKD y sigue siendo uno de los más importantes. En este ejemplo, exploraremos cómo funciona BB84 y veremos una implementación simplificada que ilustra sus principios fundamentales.
Implementación Simplificada del Protocolo BB84
El siguiente código es una implementación didáctica simplificada del protocolo BB84. No es una implementación real de QKD (que requeriría hardware cuántico), sino una simulación para ilustrar los conceptos.
"""
Simulación didáctica del protocolo BB84 para Distribución Cuántica de Claves
NOTA: Esta implementación es solo para fines educativos y simula los efectos cuánticos.
"""
import numpy as np
import random
import hashlib
from enum import Enum
class Basis(Enum):
RECTILINEAR = 0 # Base + (horizontal/vertical)
DIAGONAL = 1 # Base × (diagonal)
class Qubit:
def __init__(self, bit_value, basis):
"""
Inicializa un qubit con un valor de bit y una base de medición
Args:
bit_value (int): 0 o 1
basis (Basis): Base de preparación (RECTILINEAR o DIAGONAL)
"""
self.bit_value = bit_value
self.basis = basis
def measure(self, measurement_basis):
"""
Mide el qubit en la base especificada
Args:
measurement_basis (Basis): Base de medición
Returns:
int: Resultado de la medición (0 o 1)
"""
if measurement_basis == self.basis:
# Si medimos en la misma base, obtenemos el valor original
return self.bit_value
else:
# Si medimos en una base diferente, obtenemos un resultado aleatorio
return random.randint(0, 1)
def __str__(self):
basis_symbol = "+" if self.basis == Basis.RECTILINEAR else "×"
return f"{self.bit_value}{basis_symbol}"
class QuantumChannel:
def __init__(self, error_rate=0.0, eve_present=False):
"""
Inicializa un canal cuántico con una tasa de error y presencia de espía
Args:
error_rate (float): Tasa de error del canal (0.0 a 1.0)
eve_present (bool): Si hay un espía (Eve) en el canal
"""
self.error_rate = error_rate
self.eve_present = eve_present
self.eve_bases = [] # Bases que Eve usará para medir
def transmit(self, qubits):
"""
Transmite qubits a través del canal
Args:
qubits (list): Lista de objetos Qubit
Returns:
list: Lista de qubits potencialmente alterados
"""
transmitted_qubits = []
# Si Eve está presente, genera bases aleatorias para medir
if self.eve_present:
self.eve_bases = [random.choice(list(Basis)) for _ in range(len(qubits))]
self.eve_measurements = []
for i, qubit in enumerate(qubits):
# Copia del qubit original
transmitted = Qubit(qubit.bit_value, qubit.basis)
# Si Eve está presente, intercepta y mide el qubit
if self.eve_present:
eve_measurement = qubit.measure(self.eve_bases[i])
self.eve_measurements.append(eve_measurement)
# Eve reenvía un nuevo qubit basado en su medición
transmitted = Qubit(eve_measurement, self.eve_bases[i])
# Simular errores del canal
if random.random() < self.error_rate:
# Invertir el bit con probabilidad error_rate
transmitted.bit_value = 1 - transmitted.bit_value
transmitted_qubits.append(transmitted)
return transmitted_qubits
class BB84Protocol:
def __init__(self, num_bits=1000, error_rate=0.0, eve_present=False):
"""
Inicializa el protocolo BB84
Args:
num_bits (int): Número de bits a transmitir
error_rate (float): Tasa de error del canal
eve_present (bool): Si hay un espía en el canal
"""
self.num_bits = num_bits
self.channel = QuantumChannel(error_rate, eve_present)
# Datos de Alice
self.alice_bits = []
self.alice_bases = []
# Datos de Bob
self.bob_bases = []
self.bob_measurements = []
# Datos de Eve (si está presente)
self.eve_bases = []
self.eve_measurements = []
# Resultados del protocolo
self.matching_bases_indices = []
self.sample_indices = []
self.key_indices = []
self.error_rate = 0.0
self.final_key = []
def alice_prepares_qubits(self):
"""Alice prepara qubits aleatorios en bases aleatorias"""
self.alice_bits = [random.randint(0, 1) for _ in range(self.num_bits)]
self.alice_bases = [random.choice(list(Basis)) for _ in range(self.num_bits)]
qubits = [Qubit(self.alice_bits[i], self.alice_bases[i])
for i in range(self.num_bits)]
return qubits
def bob_measures_qubits(self, qubits):
"""Bob mide los qubits recibidos en bases aleatorias"""
self.bob_bases = [random.choice(list(Basis)) for _ in range(len(qubits))]
self.bob_measurements = [qubits[i].measure(self.bob_bases[i])
for i in range(len(qubits))]
def compare_bases(self):
"""Alice y Bob comparan sus bases y descartan las no coincidentes"""
self.matching_bases_indices = [i for i in range(self.num_bits)
if self.alice_bases[i] == self.bob_bases[i]]
print(f"Bases coincidentes: {len(self.matching_bases_indices)}/{self.num_bits}")
def estimate_error_rate(self, sample_size=100):
"""
Alice y Bob estiman la tasa de error sacrificando algunos bits
Args:
sample_size (int): Número de bits a sacrificar para la estimación
"""
# Asegurarse de que no intentamos muestrear más bits de los disponibles
sample_size = min(sample_size, len(self.matching_bases_indices))
# Seleccionar índices aleatorios para el muestreo
self.sample_indices = random.sample(self.matching_bases_indices, sample_size)
# Contar errores en los bits de muestra
errors = sum(1 for i in self.sample_indices
if self.alice_bits[i] != self.bob_measurements[i])
self.error_rate = errors / sample_size
print(f"Tasa de error estimada: {self.error_rate:.2%}")
# Determinar si la tasa de error es aceptable (típicamente < 11%)
return self.error_rate < 0.11
def generate_key(self):
"""Alice y Bob generan la clave final con los bits restantes"""
# Excluir los bits usados para la estimación de error
self.key_indices = [i for i in self.matching_bases_indices
if i not in self.sample_indices]
# Extraer los bits para la clave final
self.final_key = [self.alice_bits[i] for i in self.key_indices]
# En una implementación real, se aplicaría:
# 1. Reconciliación de información (corrección de errores)
# 2. Amplificación de privacidad (hash)
# Simulamos la amplificación de privacidad con un hash simple
key_string = ''.join(str(bit) for bit in self.final_key)
hashed_key = hashlib.sha256(key_string.encode()).hexdigest()
print(f"Longitud de la clave final: {len(self.final_key)} bits")
print(f"Muestra de la clave: {''.join(str(bit) for bit in self.final_key[:16])}...")
print(f"Clave hash: {hashed_key}")
return self.final_key
def run_protocol(self):
"""Ejecuta el protocolo BB84 completo"""
print("=== Iniciando Protocolo BB84 ===")
# Paso 1: Alice prepara qubits
print("\nPaso 1: Alice prepara qubits")
qubits = self.alice_prepares_qubits()
# Paso 2: Alice envía qubits a Bob a través del canal cuántico
print("\nPaso 2: Transmisión por canal cuántico")
received_qubits = self.channel.transmit(qubits)
# Si Eve está presente, guardamos sus datos
if self.channel.eve_present:
self.eve_bases = self.channel.eve_bases
self.eve_measurements = self.channel.eve_measurements
# Paso 3: Bob mide los qubits recibidos
print("\nPaso 3: Bob mide los qubits recibidos")
self.bob_measures_qubits(received_qubits)
# Paso 4: Alice y Bob comparan bases
print("\nPaso 4: Comparación de bases")
self.compare_bases()
# Paso 5: Estimación de la tasa de error
print("\nPaso 5: Estimación de la tasa de error")
error_acceptable = self.estimate_error_rate()
if not error_acceptable:
print("\n⚠️ Tasa de error demasiado alta. Posible presencia de espía.")
print("Protocolo abortado.")
return None
# Paso 6: Generación de la clave final
print("\nPaso 6: Generación de la clave final")
key = self.generate_key()
print("\n=== Protocolo BB84 completado con éxito ===")
return key
# Ejemplo de uso
if __name__ == "__main__":
# Ejecutar protocolo sin espía
print("\n=== EJECUCIÓN SIN ESPÍA ===")
protocol = BB84Protocol(num_bits=1000, error_rate=0.05, eve_present=False)
key_without_eve = protocol.run_protocol()
# Ejecutar protocolo con espía
print("\n\n=== EJECUCIÓN CON ESPÍA ===")
protocol_with_eve = BB84Protocol(num_bits=1000, error_rate=0.05, eve_present=True)
key_with_eve = protocol_with_eve.run_protocol()
Explicación del Protocolo BB84
Fundamentos Cuánticos
El protocolo BB84 se basa en dos principios fundamentales de la mecánica cuántica:
- Principio de incertidumbre de Heisenberg: No es posible medir simultáneamente ciertas propiedades complementarias de un sistema cuántico con precisión arbitraria.
- Teorema de no clonación: Es imposible crear una copia exacta de un estado cuántico desconocido.
Estos principios garantizan que cualquier intento de interceptar la comunicación cuántica alterará inevitablemente los estados, permitiendo detectar la presencia de un espía.
Pasos del Protocolo BB84
Paso 1: Preparación de Qubits
Alice genera dos secuencias aleatorias:
- Una secuencia de bits (0s y 1s)
- Una secuencia de bases (rectilinear + y diagonal ×)
Para cada bit, Alice prepara un qubit en la base correspondiente:
- Base rectilinear (+): |0⟩ para bit 0, |1⟩ para bit 1
- Base diagonal (×): |+⟩ para bit 0, |−⟩ para bit 1
Paso 2: Transmisión Cuántica
Alice envía los qubits a Bob a través de un canal cuántico.
Si Eve intenta interceptar los qubits, debe elegir una base para medirlos:
- Si elige la misma base que Alice, obtiene el bit correcto pero altera el estado
- Si elige una base diferente, obtiene un resultado aleatorio y altera el estado
Paso 3: Medición de Qubits
Bob genera una secuencia aleatoria de bases y mide cada qubit recibido en la base correspondiente.
Resultados de la medición:
- Si Bob usa la misma base que Alice, obtiene el bit original con alta probabilidad
- Si Bob usa una base diferente, obtiene un resultado aleatorio (0 o 1 con igual probabilidad)
Paso 4: Comparación de Bases
Alice y Bob revelan públicamente las bases que utilizaron (pero no los bits).
Descartan todos los bits donde usaron bases diferentes, conservando solo aquellos donde coincidieron las bases.
Aproximadamente el 50% de los bits se conservan en este paso.
Paso 5: Estimación de Error
Alice y Bob revelan públicamente una muestra aleatoria de sus bits coincidentes para estimar la tasa de error.
Si la tasa de error es mayor que un umbral (típicamente 11%), sospechan la presencia de un espía y abortan el protocolo.
La presencia de Eve introduce errores porque:
- Eve elige la base incorrecta aproximadamente el 50% del tiempo
- Cuando Eve elige la base incorrecta, introduce un error del 50%
- Esto resulta en una tasa de error del 25% cuando Eve intercepta todos los qubits
Paso 6: Procesamiento Final
Si la tasa de error es aceptable, Alice y Bob:
- Realizan reconciliación de información (corrección de errores)
- Aplican amplificación de privacidad (funciones hash) para eliminar cualquier información parcial que Eve pudiera haber obtenido
El resultado es una clave secreta compartida que puede usarse para cifrado simétrico.
QKD vs. Criptografía Post-Cuántica
| Característica | Distribución Cuántica de Claves (QKD) | Criptografía Post-Cuántica (PQC) |
|---|---|---|
| Base de seguridad | Leyes de la física cuántica | Problemas matemáticos computacionalmente difíciles |
| Hardware requerido | Dispositivos cuánticos especializados | Hardware convencional |
| Distancia | Limitada (típicamente <100 km sin repetidores) | Ilimitada |
| Tasa de transferencia | Baja (kbps o menos) | Alta (comparable a criptografía clásica) |
| Infraestructura | Requiere canales cuánticos dedicados | Compatible con infraestructura existente |
| Aplicación | Solo distribución de claves | Cifrado, firmas, intercambio de claves |
| Madurez | Implementaciones comerciales limitadas | Estándares emergentes (NIST) |
| Seguridad probada | Seguridad incondicional (teóricamente) | Seguridad computacional (basada en supuestos) |
Aplicaciones Prácticas de QKD
A pesar de sus limitaciones, QKD ya se está utilizando en varios contextos:
- Redes financieras: Bancos y bolsas de valores para proteger transacciones de alta seguridad.
- Infraestructuras críticas: Protección de comunicaciones en redes eléctricas, plantas nucleares y sistemas de control industrial.
- Comunicaciones gubernamentales: Agencias de inteligencia y defensa para comunicaciones ultra-seguras.
- Redes metropolitanas: Implementaciones en ciudades como Tokio, Viena, Boston y Pekín.
- Comunicaciones satelitales: China ha demostrado QKD vía satélite (Micius) a distancias de más de 1,200 km.
El futuro de QKD incluye el desarrollo de repetidores cuánticos y redes cuánticas que podrían extender significativamente su alcance y aplicabilidad.