use rand::rngs::OsRng;
use rand::Rng;
use zeroize::Zeroize;
use crate::entropy::EntropySnapshot;
use crate::error::{KkError, Result};
use crate::kk_mix;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Basis {
Rectilinear,
Diagonal,
}
#[derive(Debug, Clone)]
pub struct Qubit {
bit: bool,
basis: Basis,
}
impl Qubit {
pub fn measure(&self, measurement_basis: Basis) -> bool {
if measurement_basis == self.basis {
self.bit
} else {
OsRng.gen_bool(0.5)
}
}
}
pub struct Alice {
pub bits: Vec<bool>,
pub bases: Vec<Basis>,
pub qubits: Vec<Qubit>,
}
pub struct Bob {
pub bases: Vec<Basis>,
pub measured_bits: Vec<bool>,
}
pub struct Bb84Result {
pub sifted_key_alice: Vec<bool>,
pub sifted_key_bob: Vec<bool>,
pub n_qubits: usize,
pub n_sifted: usize,
pub n_check_bits: usize,
pub error_rate: f64,
pub eve_detected: bool,
pub shared_key_alice: [u8; 32],
pub shared_key_bob: [u8; 32],
}
pub struct Eve {
pub bases: Vec<Basis>,
pub measured_bits: Vec<bool>,
}
pub const DEFAULT_N_QUBITS: usize = 4096;
const EVE_DETECTION_THRESHOLD: f64 = 0.10;
const CHECK_FRACTION: f64 = 0.25;
pub fn alice_prepare(n: usize) -> Alice {
let mut rng = OsRng;
let mut bits = Vec::with_capacity(n);
let mut bases = Vec::with_capacity(n);
let mut qubits = Vec::with_capacity(n);
for _ in 0..n {
let bit = rng.gen_bool(0.5);
let basis = if rng.gen_bool(0.5) {
Basis::Rectilinear
} else {
Basis::Diagonal
};
qubits.push(Qubit { bit, basis });
bits.push(bit);
bases.push(basis);
}
Alice {
bits,
bases,
qubits,
}
}
pub fn eve_intercept(qubits: &[Qubit]) -> (Eve, Vec<Qubit>) {
let mut rng = OsRng;
let mut bases = Vec::with_capacity(qubits.len());
let mut measured_bits = Vec::with_capacity(qubits.len());
let mut resent = Vec::with_capacity(qubits.len());
for qubit in qubits {
let eve_basis = if rng.gen_bool(0.5) {
Basis::Rectilinear
} else {
Basis::Diagonal
};
let eve_bit = qubit.measure(eve_basis);
resent.push(Qubit {
bit: eve_bit,
basis: eve_basis,
});
bases.push(eve_basis);
measured_bits.push(eve_bit);
}
(
Eve {
bases,
measured_bits,
},
resent,
)
}
pub fn bob_measure(qubits: &[Qubit]) -> Bob {
let mut rng = OsRng;
let mut bases = Vec::with_capacity(qubits.len());
let mut measured_bits = Vec::with_capacity(qubits.len());
for qubit in qubits {
let basis = if rng.gen_bool(0.5) {
Basis::Rectilinear
} else {
Basis::Diagonal
};
measured_bits.push(qubit.measure(basis));
bases.push(basis);
}
Bob {
bases,
measured_bits,
}
}
pub fn distill_key(alice: &Alice, bob: &Bob) -> Result<Bb84Result> {
let n = alice.bits.len();
let mut sifted_alice = Vec::new();
let mut sifted_bob = Vec::new();
for i in 0..n {
if alice.bases[i] == bob.bases[i] {
sifted_alice.push(alice.bits[i]);
sifted_bob.push(bob.measured_bits[i]);
}
}
let n_sifted = sifted_alice.len();
if n_sifted < 64 {
return Err(KkError::EntropyFailure(
"BB84: too few sifted bits for secure key".into(),
));
}
let n_check = (n_sifted as f64 * CHECK_FRACTION) as usize;
let n_check = n_check.max(16);
let mut errors = 0;
for i in 0..n_check {
if sifted_alice[i] != sifted_bob[i] {
errors += 1;
}
}
let error_rate = errors as f64 / n_check as f64;
let eve_detected = error_rate > EVE_DETECTION_THRESHOLD;
let raw_alice: Vec<bool> = sifted_alice[n_check..].to_vec();
let raw_bob: Vec<bool> = sifted_bob[n_check..].to_vec();
let key_bytes_alice = bits_to_bytes(&raw_alice);
let key_bytes_bob = bits_to_bytes(&raw_bob);
let mut shared_key_alice = [0u8; 32];
let mut shared_key_bob = [0u8; 32];
let derived_a = kk_mix::kk_kdf(&key_bytes_alice, b"BB84-KK-v1", b"KK-QKD-shared-key", 32);
shared_key_alice.copy_from_slice(&derived_a);
let derived_b = kk_mix::kk_kdf(&key_bytes_bob, b"BB84-KK-v1", b"KK-QKD-shared-key", 32);
shared_key_bob.copy_from_slice(&derived_b);
Ok(Bb84Result {
sifted_key_alice: sifted_alice,
sifted_key_bob: sifted_bob,
n_qubits: n,
n_sifted,
n_check_bits: n_check,
error_rate,
eve_detected,
shared_key_alice,
shared_key_bob,
})
}
pub fn encrypt_epsilon(qkd_key: &[u8; 32], epsilon: &EntropySnapshot) -> Vec<u8> {
let epsilon_bytes = epsilon.to_bytes();
let mut keystream = kk_mix::kk_kdf(
b"QKD-epsilon-transport",
qkd_key,
b"KK-QKD-epsilon-v1",
epsilon_bytes.len(),
);
let encrypted: Vec<u8> = epsilon_bytes
.iter()
.zip(keystream.iter())
.map(|(e, k)| e ^ k)
.collect();
keystream.zeroize();
encrypted
}
pub fn decrypt_epsilon(qkd_key: &[u8; 32], encrypted: &[u8]) -> Result<EntropySnapshot> {
let mut keystream = kk_mix::kk_kdf(
b"QKD-epsilon-transport",
qkd_key,
b"KK-QKD-epsilon-v1",
encrypted.len(),
);
let decrypted: Vec<u8> = encrypted
.iter()
.zip(keystream.iter())
.map(|(e, k)| e ^ k)
.collect();
keystream.zeroize();
EntropySnapshot::from_bytes(&decrypted)
}
fn bits_to_bytes(bits: &[bool]) -> Vec<u8> {
let n_bytes = bits.len().div_ceil(8);
let mut bytes = vec![0u8; n_bytes];
for (i, &bit) in bits.iter().enumerate() {
if bit {
bytes[i / 8] |= 1 << (7 - (i % 8));
}
}
bytes
}
#[cfg(test)]
mod tests {
use super::*;
use crate::entropy;
#[test]
fn bb84_no_eve_clean_key() {
let alice = alice_prepare(DEFAULT_N_QUBITS);
let bob = bob_measure(&alice.qubits);
let result = distill_key(&alice, &bob).unwrap();
assert!(
result.n_sifted > 1500,
"expected ~2048 sifted bits, got {}",
result.n_sifted
);
assert_eq!(result.error_rate, 0.0, "no Eve means zero errors");
assert!(!result.eve_detected);
assert_ne!(result.shared_key_alice, [0u8; 32]);
assert_eq!(result.shared_key_alice, result.shared_key_bob);
}
#[test]
fn bb84_eve_detected() {
let alice = alice_prepare(DEFAULT_N_QUBITS);
let (_eve, tampered_qubits) = eve_intercept(&alice.qubits);
let bob = bob_measure(&tampered_qubits);
let result = distill_key(&alice, &bob).unwrap();
assert!(
result.error_rate > 0.15,
"Eve should cause >15% errors, got {:.1}%",
result.error_rate * 100.0
);
assert!(result.eve_detected, "Eve should be detected");
}
#[test]
fn epsilon_encrypt_decrypt_roundtrip() {
let qkd_key = [42u8; 32];
let epsilon = entropy::gather().unwrap();
let encrypted = encrypt_epsilon(&qkd_key, &epsilon);
let decrypted = decrypt_epsilon(&qkd_key, &encrypted).unwrap();
assert_eq!(epsilon.bytes, decrypted.bytes);
assert_eq!(epsilon.timestamp_nanos, decrypted.timestamp_nanos);
}
#[test]
fn wrong_qkd_key_corrupts_epsilon() {
let real_key = [42u8; 32];
let wrong_key = [99u8; 32];
let epsilon = entropy::gather().unwrap();
let encrypted = encrypt_epsilon(&real_key, &epsilon);
let decrypted = decrypt_epsilon(&wrong_key, &encrypted).unwrap();
assert_ne!(epsilon.bytes, decrypted.bytes);
}
#[test]
fn qubit_correct_basis_deterministic() {
for bit in [true, false] {
for basis in [Basis::Rectilinear, Basis::Diagonal] {
let q = Qubit { bit, basis };
for _ in 0..100 {
assert_eq!(q.measure(basis), bit);
}
}
}
}
#[test]
fn qubit_wrong_basis_random() {
let q = Qubit {
bit: true,
basis: Basis::Rectilinear,
};
let trials = 1000;
let trues: usize = (0..trials).filter(|_| q.measure(Basis::Diagonal)).count();
assert!(
trues > 350 && trues < 650,
"wrong basis should be ~50/50, got {trues}/{trials}"
);
}
#[test]
fn full_qkd_kk_integration() {
use crate::codec::{decode_split, encode_split};
let shared_secret = b"integration-test-secret";
let plaintext = b"QKD + KK = information-theoretic security";
let alice = alice_prepare(DEFAULT_N_QUBITS);
let bob_state = bob_measure(&alice.qubits);
let qkd = distill_key(&alice, &bob_state).unwrap();
assert!(!qkd.eve_detected);
let (sealed, epsilon) = encode_split(shared_secret, plaintext).unwrap();
let encrypted_epsilon = encrypt_epsilon(&qkd.shared_key_alice, &epsilon);
let recovered_epsilon = decrypt_epsilon(&qkd.shared_key_alice, &encrypted_epsilon).unwrap();
let recovered = decode_split(shared_secret, &sealed, &recovered_epsilon).unwrap();
assert_eq!(recovered, plaintext);
}
}