quantrs2_core/networking/
bb84.rs1use crate::error::{QuantRS2Error, QuantRS2Result};
14use crate::networking::channel::{
15 ket_minus, ket_one, ket_plus, ket_zero, measure_computational, pure_state_density,
16 DepolarizingChannel, NoiseChannel,
17};
18use scirs2_core::ndarray::Array2;
19use scirs2_core::random::prelude::*;
20use scirs2_core::random::ChaCha20Rng;
21use scirs2_core::Complex64;
22use std::f64::consts::SQRT_2;
23
24fn seed_from_u64(seed: u64) -> [u8; 32] {
28 let mut bytes = [0u8; 32];
29 let s = seed.to_le_bytes();
30 bytes[..8].copy_from_slice(&s);
31 bytes[8..16].copy_from_slice(&s);
32 bytes[16..24].copy_from_slice(&s);
33 bytes[24..32].copy_from_slice(&s);
34 bytes
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42enum Bb84Basis {
43 Rectilinear,
45 Diagonal,
47}
48
49fn prepare_qubit(bit: bool, basis: Bb84Basis) -> [Complex64; 2] {
51 match (bit, basis) {
52 (false, Bb84Basis::Rectilinear) => ket_zero(),
53 (true, Bb84Basis::Rectilinear) => ket_one(),
54 (false, Bb84Basis::Diagonal) => ket_plus(),
55 (true, Bb84Basis::Diagonal) => ket_minus(),
56 }
57}
58
59fn measure_in_basis(rho: &Array2<Complex64>, basis: Bb84Basis, rand_val: f64) -> bool {
61 match basis {
62 Bb84Basis::Rectilinear => {
63 let (outcome, _) = measure_computational(rho, rand_val);
64 outcome
65 }
66 Bb84Basis::Diagonal => {
67 let h = 1.0 / SQRT_2;
70 let rho00 = rho[[0, 0]];
71 let rho01 = rho[[0, 1]];
72 let rho10 = rho[[1, 0]];
73 let rho11 = rho[[1, 1]];
74 let mut rho_rot = Array2::<Complex64>::zeros((2, 2));
75 rho_rot[[0, 0]] = Complex64::new(h * h * (rho00 + rho01 + rho10 + rho11).re, 0.0);
76 rho_rot[[1, 1]] = Complex64::new(h * h * (rho00 - rho01 - rho10 + rho11).re, 0.0);
77 let (outcome, _) = measure_computational(&rho_rot, rand_val);
78 outcome
79 }
80 }
81}
82
83#[derive(Debug, Clone)]
89pub struct Bb84Protocol {
90 pub n_bits: usize,
92 pub error_rate: f64,
94 pub eavesdrop_rate: f64,
96 pub rng_seed: u64,
98}
99
100#[derive(Debug, Clone)]
102pub struct Bb84Result {
103 pub raw_bits: usize,
105 pub sifted_key: Vec<bool>,
107 pub qber: f64,
109 pub secret_key: Vec<bool>,
111 pub detected_eavesdrop: bool,
113}
114
115impl Bb84Protocol {
116 pub fn new(n_bits: usize, error_rate: f64, eavesdrop_rate: f64, rng_seed: u64) -> Self {
118 Self {
119 n_bits,
120 error_rate: error_rate.clamp(0.0, 1.0),
121 eavesdrop_rate: eavesdrop_rate.clamp(0.0, 1.0),
122 rng_seed,
123 }
124 }
125
126 pub fn run(&self) -> QuantRS2Result<Bb84Result> {
128 if self.n_bits == 0 {
129 return Err(QuantRS2Error::InvalidInput(
130 "n_bits must be > 0".to_string(),
131 ));
132 }
133
134 let mut rng = ChaCha20Rng::from_seed(seed_from_u64(self.rng_seed));
135 let depolarizing = DepolarizingChannel::new(self.error_rate);
136
137 let alice_bits: Vec<bool> = (0..self.n_bits).map(|_| rng.random::<bool>()).collect();
139 let alice_bases: Vec<Bb84Basis> = (0..self.n_bits)
140 .map(|_| {
141 if rng.random::<bool>() {
142 Bb84Basis::Rectilinear
143 } else {
144 Bb84Basis::Diagonal
145 }
146 })
147 .collect();
148
149 let bob_bases: Vec<Bb84Basis> = (0..self.n_bits)
151 .map(|_| {
152 if rng.random::<bool>() {
153 Bb84Basis::Rectilinear
154 } else {
155 Bb84Basis::Diagonal
156 }
157 })
158 .collect();
159
160 let mut bob_bits: Vec<bool> = Vec::with_capacity(self.n_bits);
162
163 for i in 0..self.n_bits {
164 let alice_state = prepare_qubit(alice_bits[i], alice_bases[i]);
165
166 let eve_threshold: f64 = rng.random();
168 let state_after_eve = if eve_threshold < self.eavesdrop_rate {
169 let eve_basis = if rng.random::<bool>() {
170 Bb84Basis::Rectilinear
171 } else {
172 Bb84Basis::Diagonal
173 };
174 let eve_rho = pure_state_density(&alice_state);
175 let eve_rand: f64 = rng.random();
176 let eve_bit = measure_in_basis(&eve_rho, eve_basis, eve_rand);
177 prepare_qubit(eve_bit, eve_basis)
178 } else {
179 alice_state
180 };
181
182 let mut rho = pure_state_density(&state_after_eve);
184 depolarizing.apply(&mut rho);
185
186 let bob_rand: f64 = rng.random();
188 bob_bits.push(measure_in_basis(&rho, bob_bases[i], bob_rand));
189 }
190
191 let mut alice_sifted: Vec<bool> = Vec::new();
193 let mut bob_sifted: Vec<bool> = Vec::new();
194 for i in 0..self.n_bits {
195 if alice_bases[i] == bob_bases[i] {
196 alice_sifted.push(alice_bits[i]);
197 bob_sifted.push(bob_bits[i]);
198 }
199 }
200
201 if alice_sifted.is_empty() {
202 return Ok(Bb84Result {
203 raw_bits: self.n_bits,
204 sifted_key: vec![],
205 qber: 0.0,
206 secret_key: vec![],
207 detected_eavesdrop: false,
208 });
209 }
210
211 let sample_size = (alice_sifted.len() / 5).max(1);
213 let errors: usize = (0..sample_size)
214 .filter(|&k| alice_sifted[k] != bob_sifted[k])
215 .count();
216 let qber = errors as f64 / sample_size as f64;
217
218 let sifted_key: Vec<bool> = bob_sifted[sample_size..].to_vec();
220
221 let secret_key = privacy_amplification(&sifted_key);
223 let detected_eavesdrop = qber > 0.10;
224
225 Ok(Bb84Result {
226 raw_bits: self.n_bits,
227 sifted_key,
228 qber,
229 secret_key,
230 detected_eavesdrop,
231 })
232 }
233}
234
235fn privacy_amplification(key: &[bool]) -> Vec<bool> {
237 let n = key.len() & !1;
238 (0..n / 2).map(|i| key[2 * i] ^ key[2 * i + 1]).collect()
239}
240
241#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn bb84_no_noise_no_eve_low_qber() {
251 let proto = Bb84Protocol::new(2000, 0.0, 0.0, 7);
252 let result = proto.run().expect("bb84 run");
253 assert!(
254 result.qber < 0.05,
255 "expected low QBER without noise, got {}",
256 result.qber
257 );
258 assert!(!result.detected_eavesdrop);
259 }
260
261 #[test]
262 fn bb84_full_eavesdrop_qber_near_quarter() {
263 let proto = Bb84Protocol::new(4000, 0.0, 1.0, 13);
264 let result = proto.run().expect("bb84 run");
265 assert!(
267 result.qber > 0.15,
268 "expected QBER ≈ 0.25 with full eavesdropping, got {}",
269 result.qber
270 );
271 assert!(result.detected_eavesdrop);
272 }
273
274 #[test]
275 fn bb84_secret_key_half_sifted_length() {
276 let proto = Bb84Protocol::new(2000, 0.0, 0.0, 55);
277 let result = proto.run().expect("bb84 run");
278 if !result.sifted_key.is_empty() {
279 let expected = result.sifted_key.len() / 2;
280 assert_eq!(result.secret_key.len(), expected);
281 }
282 }
283}