Skip to main content

quantrs2_ml/
crypto.rs

1//! Quantum cryptography: QKD protocols and post-quantum key exchange.
2//!
3//! Provides simulations of BB84, E91, and B92 quantum key distribution
4//! protocols, plus lattice-based post-quantum key encapsulation suitable
5//! for integration with quantum-secured network protocols.
6
7use crate::error::{MLError, Result};
8use quantrs2_circuit::prelude::Circuit;
9use quantrs2_sim::statevector::StateVectorSimulator;
10use scirs2_core::ndarray::{Array1, Array2};
11use scirs2_core::random::prelude::*;
12use std::collections::HashMap;
13use std::fmt;
14
15/// Types of quantum key distribution protocols
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub enum ProtocolType {
18    /// BB84 protocol (Bennett and Brassard, 1984)
19    BB84,
20
21    /// E91 protocol (Ekert, 1991)
22    E91,
23
24    /// B92 protocol (Bennett, 1992)
25    B92,
26
27    /// BBM92 protocol (Bennett, Brassard, and Mermin, 1992)
28    BBM92,
29
30    /// SARG04 protocol (Scarani, Acin, Ribordy, and Gisin, 2004)
31    SARG04,
32}
33
34/// Represents a party in a quantum cryptographic protocol
35#[derive(Debug, Clone)]
36pub struct Party {
37    /// Party's name
38    pub name: String,
39
40    /// Party's key (if generated)
41    pub key: Option<Vec<u8>>,
42
43    /// Party's chosen bases (for BB84-like protocols)
44    pub bases: Option<Vec<usize>>,
45
46    /// Party's quantum state (if applicable)
47    pub state: Option<Vec<f64>>,
48}
49
50/// Quantum key distribution protocol
51#[derive(Debug, Clone)]
52pub struct QuantumKeyDistribution {
53    /// Type of QKD protocol
54    pub protocol: ProtocolType,
55
56    /// Number of qubits to use in the protocol
57    pub num_qubits: usize,
58
59    /// Alice party
60    pub alice: Party,
61
62    /// Bob party
63    pub bob: Party,
64
65    /// Error rate for the quantum channel
66    pub error_rate: f64,
67
68    /// Security parameter (number of bits to use for security checks)
69    pub security_bits: usize,
70}
71
72impl QuantumKeyDistribution {
73    /// Creates a new QKD protocol instance
74    pub fn new(protocol: ProtocolType, num_qubits: usize) -> Self {
75        QuantumKeyDistribution {
76            protocol,
77            num_qubits,
78            alice: Party {
79                name: "Alice".to_string(),
80                key: None,
81                bases: None,
82                state: None,
83            },
84            bob: Party {
85                name: "Bob".to_string(),
86                key: None,
87                bases: None,
88                state: None,
89            },
90            error_rate: 0.0,
91            security_bits: num_qubits / 10,
92        }
93    }
94
95    /// Sets the error rate for the quantum channel
96    pub fn with_error_rate(mut self, error_rate: f64) -> Self {
97        self.error_rate = error_rate;
98        self
99    }
100
101    /// Sets the security parameter
102    pub fn with_security_bits(mut self, security_bits: usize) -> Self {
103        self.security_bits = security_bits;
104        self
105    }
106
107    /// Distributes a key using the specified QKD protocol
108    pub fn distribute_key(&mut self) -> Result<usize> {
109        match self.protocol {
110            ProtocolType::BB84 => self.bb84_protocol(),
111            ProtocolType::E91 => self.e91_protocol(),
112            ProtocolType::B92 => self.b92_protocol(),
113            ProtocolType::BBM92 => self.bbm92_protocol(),
114            ProtocolType::SARG04 => self.sarg04_protocol(),
115        }
116    }
117
118    /// Implements the BB84 protocol
119    fn bb84_protocol(&mut self) -> Result<usize> {
120        // This is a dummy implementation
121        // In a real implementation, this would simulate the BB84 protocol
122
123        // Generate random bits for Alice
124        let alice_bits = (0..self.num_qubits)
125            .map(|_| {
126                if thread_rng().random::<f64>() > 0.5 {
127                    1u8
128                } else {
129                    0u8
130                }
131            })
132            .collect::<Vec<_>>();
133
134        // Generate random bases for Alice and Bob
135        let alice_bases = (0..self.num_qubits)
136            .map(|_| {
137                if thread_rng().random::<f64>() > 0.5 {
138                    1usize
139                } else {
140                    0usize
141                }
142            })
143            .collect::<Vec<_>>();
144
145        let bob_bases = (0..self.num_qubits)
146            .map(|_| {
147                if thread_rng().random::<f64>() > 0.5 {
148                    1usize
149                } else {
150                    0usize
151                }
152            })
153            .collect::<Vec<_>>();
154
155        // Determine where Alice and Bob used the same basis
156        let matching_bases = alice_bases
157            .iter()
158            .zip(bob_bases.iter())
159            .enumerate()
160            .filter_map(|(i, (a, b))| if a == b { Some(i) } else { None })
161            .collect::<Vec<_>>();
162
163        // Get the key bits from matching bases positions
164        let mut key_bits = Vec::new();
165        for &i in &matching_bases {
166            // Apply error rate
167            if thread_rng().random::<f64>() > self.error_rate {
168                key_bits.push(alice_bits[i]);
169            } else {
170                // Flip the bit to simulate an error
171                key_bits.push(alice_bits[i] ^ 1);
172            }
173        }
174
175        // Convert bits to bytes
176        let mut key_bytes = Vec::new();
177        for chunk in key_bits.chunks(8) {
178            let byte = chunk
179                .iter()
180                .enumerate()
181                .fold(0u8, |acc, (i, &bit)| acc | (bit << i));
182            key_bytes.push(byte);
183        }
184
185        // Store keys
186        self.alice.key = Some(key_bytes.clone());
187        self.bob.key = Some(key_bytes);
188
189        // Store bases
190        self.alice.bases = Some(alice_bases);
191        self.bob.bases = Some(bob_bases);
192
193        Ok(matching_bases.len())
194    }
195
196    /// Implements the E91 protocol
197    fn e91_protocol(&mut self) -> Result<usize> {
198        // This is a dummy implementation
199        // In a real implementation, this would simulate the E91 protocol
200        let key_length = self.num_qubits / 3; // Roughly 1/3 of qubits become key bits
201
202        // Generate random key bytes
203        let key_bytes = (0..key_length / 8 + 1)
204            .map(|_| thread_rng().random::<u8>())
205            .collect::<Vec<_>>();
206
207        // Store keys
208        self.alice.key = Some(key_bytes.clone());
209        self.bob.key = Some(key_bytes);
210
211        Ok(key_length)
212    }
213
214    /// Implements the B92 protocol
215    fn b92_protocol(&mut self) -> Result<usize> {
216        // This is a dummy implementation
217        // In a real implementation, this would simulate the B92 protocol
218        let key_length = self.num_qubits / 4; // Roughly 1/4 of qubits become key bits
219
220        // Generate random key bytes
221        let key_bytes = (0..key_length / 8 + 1)
222            .map(|_| thread_rng().random::<u8>())
223            .collect::<Vec<_>>();
224
225        // Store keys
226        self.alice.key = Some(key_bytes.clone());
227        self.bob.key = Some(key_bytes);
228
229        Ok(key_length)
230    }
231
232    /// BBM92 protocol (Bennett-Brassard-Mermin 1992) — entanglement-based QKD.
233    ///
234    /// Alice and Bob share EPR pairs; each measures independently in a randomly
235    /// chosen basis (Z or X). Bases are compared classically; matching positions
236    /// yield perfectly anti-correlated raw key bits (Alice flips hers). Retention
237    /// rate ≈ 50% (same as BB84) because each measurement in the Z/X basis is
238    /// equally likely to match the other party's choice.
239    fn bbm92_protocol(&mut self) -> Result<usize> {
240        let mut rng = thread_rng();
241
242        // Simulate entangled pair measurements: both choose basis 0 (Z) or 1 (X).
243        let alice_bases: Vec<usize> = (0..self.num_qubits)
244            .map(|_| if rng.random::<f64>() > 0.5 { 1 } else { 0 })
245            .collect();
246        let bob_bases: Vec<usize> = (0..self.num_qubits)
247            .map(|_| if rng.random::<f64>() > 0.5 { 1 } else { 0 })
248            .collect();
249
250        // Alice measures her qubit; bob's result is anti-correlated in matching basis.
251        let alice_bits: Vec<u8> = (0..self.num_qubits)
252            .map(|_| if rng.random::<f64>() > 0.5 { 1 } else { 0 })
253            .collect();
254
255        // Sifting: keep positions where bases agree.
256        let sifted_indices: Vec<usize> = (0..self.num_qubits)
257            .filter(|&i| alice_bases[i] == bob_bases[i])
258            .collect();
259        let key_length = sifted_indices.len();
260
261        // Build raw key bytes from sifted bits.
262        let key_bytes: Vec<u8> = sifted_indices
263            .chunks(8)
264            .map(|chunk| {
265                chunk.iter().enumerate().fold(0u8, |acc, (bit_pos, &idx)| {
266                    acc | (alice_bits[idx] << bit_pos)
267                })
268            })
269            .collect();
270
271        self.alice.key = Some(key_bytes.clone());
272        // Bob's key is identical after anti-correlation flip (Alice pre-flips hers).
273        self.bob.key = Some(key_bytes);
274        Ok(key_length)
275    }
276
277    /// SARG04 protocol (Scarani-Acin-Ribordy-Gisin 2004).
278    ///
279    /// SARG04 is a BB84 variant with modified sifting: Alice announces one of two
280    /// non-orthogonal state pairs to reveal her bit, making photon-number-splitting
281    /// attacks harder. Retention rate ≈ 25% (half that of BB84) because Bob's
282    /// conclusive unambiguous-state-discrimination succeeds only when his measurement
283    /// basis matches the natural eigenbasis of the announced pair.
284    fn sarg04_protocol(&mut self) -> Result<usize> {
285        let mut rng = thread_rng();
286
287        // Alice chooses random bits and random bases.
288        let alice_bits: Vec<u8> = (0..self.num_qubits)
289            .map(|_| if rng.random::<f64>() > 0.5 { 1 } else { 0 })
290            .collect();
291        let alice_bases: Vec<usize> = (0..self.num_qubits)
292            .map(|_| if rng.random::<f64>() > 0.5 { 1 } else { 0 })
293            .collect();
294
295        // Bob measures in a random basis; he succeeds (gets conclusive result)
296        // with probability 1/2 (USD strategy on a 2-state ensemble).
297        let bob_conclusive: Vec<bool> = (0..self.num_qubits)
298            .map(|_| rng.random::<f64>() > 0.5)
299            .collect();
300
301        // Only conclusive Bob measurements where bases align produce key bits.
302        let bob_bases: Vec<usize> = (0..self.num_qubits)
303            .map(|_| if rng.random::<f64>() > 0.5 { 1 } else { 0 })
304            .collect();
305        let sifted_indices: Vec<usize> = (0..self.num_qubits)
306            .filter(|&i| bob_conclusive[i] && alice_bases[i] == bob_bases[i])
307            .collect();
308        let key_length = sifted_indices.len();
309
310        let key_bytes: Vec<u8> = sifted_indices
311            .chunks(8)
312            .map(|chunk| {
313                chunk.iter().enumerate().fold(0u8, |acc, (bit_pos, &idx)| {
314                    acc | (alice_bits[idx] << bit_pos)
315                })
316            })
317            .collect();
318
319        self.alice.key = Some(key_bytes.clone());
320        self.bob.key = Some(key_bytes);
321        Ok(key_length)
322    }
323
324    /// Verifies that Alice and Bob have identical keys
325    pub fn verify_keys(&self) -> bool {
326        match (&self.alice.key, &self.bob.key) {
327            (Some(alice_key), Some(bob_key)) => alice_key == bob_key,
328            _ => false,
329        }
330    }
331
332    /// Gets Alice's key (if generated)
333    pub fn get_alice_key(&self) -> Option<Vec<u8>> {
334        self.alice.key.clone()
335    }
336
337    /// Gets Bob's key (if generated)
338    pub fn get_bob_key(&self) -> Option<Vec<u8>> {
339        self.bob.key.clone()
340    }
341}
342
343/// A from-scratch, dependency-free implementation of SHA-256 (FIPS 180-4).
344///
345/// This workspace's policy is to reuse `scirs2-core` rather than pull in new
346/// crates for array/RNG/complex-number needs, and this bundle's fix must not
347/// touch `Cargo.toml`; there is no hashing crate already in the dependency
348/// graph, so this hand-written implementation provides a real, standard,
349/// cryptographically diffusing hash function (verified against the official
350/// SHA-256 test vectors in this module's tests) in place of the previous
351/// plain byte concatenation.
352pub(crate) mod sha256 {
353    const ROUND_CONSTANTS: [u32; 64] = [
354        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
355        0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
356        0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
357        0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
358        0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
359        0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
360        0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
361        0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
362        0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
363        0xc67178f2,
364    ];
365
366    const INITIAL_HASH: [u32; 8] = [
367        0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
368        0x5be0cd19,
369    ];
370
371    /// Compute the 32-byte SHA-256 digest of `message`.
372    pub fn digest(message: &[u8]) -> [u8; 32] {
373        let bit_len = (message.len() as u64).wrapping_mul(8);
374        let mut padded = message.to_vec();
375        padded.push(0x80);
376        while padded.len() % 64 != 56 {
377            padded.push(0);
378        }
379        padded.extend_from_slice(&bit_len.to_be_bytes());
380
381        let mut hash_state = INITIAL_HASH;
382        for chunk in padded.chunks_exact(64) {
383            let mut schedule = [0u32; 64];
384            for i in 0..16 {
385                schedule[i] = u32::from_be_bytes([
386                    chunk[i * 4],
387                    chunk[i * 4 + 1],
388                    chunk[i * 4 + 2],
389                    chunk[i * 4 + 3],
390                ]);
391            }
392            for i in 16..64 {
393                let s0 = schedule[i - 15].rotate_right(7)
394                    ^ schedule[i - 15].rotate_right(18)
395                    ^ (schedule[i - 15] >> 3);
396                let s1 = schedule[i - 2].rotate_right(17)
397                    ^ schedule[i - 2].rotate_right(19)
398                    ^ (schedule[i - 2] >> 10);
399                schedule[i] = schedule[i - 16]
400                    .wrapping_add(s0)
401                    .wrapping_add(schedule[i - 7])
402                    .wrapping_add(s1);
403            }
404
405            let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h) = (
406                hash_state[0],
407                hash_state[1],
408                hash_state[2],
409                hash_state[3],
410                hash_state[4],
411                hash_state[5],
412                hash_state[6],
413                hash_state[7],
414            );
415
416            for i in 0..64 {
417                let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
418                let ch = (e & f) ^ ((!e) & g);
419                let temp1 = h
420                    .wrapping_add(s1)
421                    .wrapping_add(ch)
422                    .wrapping_add(ROUND_CONSTANTS[i])
423                    .wrapping_add(schedule[i]);
424                let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
425                let maj = (a & b) ^ (a & c) ^ (b & c);
426                let temp2 = s0.wrapping_add(maj);
427
428                h = g;
429                g = f;
430                f = e;
431                e = d.wrapping_add(temp1);
432                d = c;
433                c = b;
434                b = a;
435                a = temp1.wrapping_add(temp2);
436            }
437
438            hash_state[0] = hash_state[0].wrapping_add(a);
439            hash_state[1] = hash_state[1].wrapping_add(b);
440            hash_state[2] = hash_state[2].wrapping_add(c);
441            hash_state[3] = hash_state[3].wrapping_add(d);
442            hash_state[4] = hash_state[4].wrapping_add(e);
443            hash_state[5] = hash_state[5].wrapping_add(f);
444            hash_state[6] = hash_state[6].wrapping_add(g);
445            hash_state[7] = hash_state[7].wrapping_add(h);
446        }
447
448        let mut result = [0u8; 32];
449        for (i, word) in hash_state.iter().enumerate() {
450            result[i * 4..i * 4 + 4].copy_from_slice(&word.to_be_bytes());
451        }
452        result
453    }
454
455    #[cfg(test)]
456    mod tests {
457        use super::digest;
458
459        fn to_hex(bytes: &[u8]) -> String {
460            bytes.iter().map(|b| format!("{b:02x}")).collect()
461        }
462
463        #[test]
464        fn matches_official_test_vectors() {
465            assert_eq!(
466                to_hex(&digest(b"")),
467                "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
468            );
469            assert_eq!(
470                to_hex(&digest(b"abc")),
471                "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
472            );
473            assert_eq!(
474                to_hex(&digest(
475                    b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
476                )),
477                "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
478            );
479        }
480    }
481}
482
483/// Returns bit `bit_index` (0 = least significant bit of the first byte) of
484/// a 32-byte digest.
485fn digest_bit(digest: &[u8; 32], bit_index: usize) -> u8 {
486    let byte = digest[bit_index / 8];
487    (byte >> (bit_index % 8)) & 1
488}
489
490/// Number of message-digest bits committed to by a Lamport signature key
491/// pair, derived from the caller's requested `security_bits` and capped at
492/// 256 (the digest size of the SHA-256 hash used internally).
493fn lamport_bit_count(security_bits: usize) -> usize {
494    security_bits.clamp(8, 256)
495}
496
497/// Public-key-only half of a [`QuantumSignature`].
498///
499/// A legitimate verifier only ever needs -- and only ever has -- the public
500/// key, never the private key. Splitting this out as its own type (rather
501/// than verifying via a `QuantumSignature` that also stores the private key)
502/// makes that structurally explicit.
503#[derive(Debug, Clone, PartialEq)]
504pub struct QuantumSignatureVerifyingKey {
505    bit_count: usize,
506    public_key: Vec<[u8; 32]>,
507}
508
509impl QuantumSignatureVerifyingKey {
510    /// Verifies `signature` against `message` using only this public key.
511    pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<bool> {
512        QuantumSignature::verify_with_public_key(
513            message,
514            signature,
515            &self.public_key,
516            self.bit_count,
517        )
518    }
519
520    /// Serializes this verifying key to bytes (a big-endian bit-count prefix
521    /// followed by each 32-byte public-key entry), suitable for embedding in,
522    /// e.g., a blockchain transaction's `sender` field.
523    pub fn to_bytes(&self) -> Vec<u8> {
524        let mut bytes = Vec::with_capacity(8 + self.public_key.len() * 32);
525        bytes.extend_from_slice(&(self.bit_count as u64).to_be_bytes());
526        for entry in &self.public_key {
527            bytes.extend_from_slice(entry);
528        }
529        bytes
530    }
531
532    /// Deserializes a verifying key previously produced by [`Self::to_bytes`].
533    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
534        if bytes.len() < 8 {
535            return Err(MLError::InvalidParameter(
536                "Verifying key bytes too short".to_string(),
537            ));
538        }
539        let bit_count_bytes: [u8; 8] = bytes[0..8]
540            .try_into()
541            .map_err(|_| MLError::InvalidParameter("Malformed bit-count prefix".to_string()))?;
542        let bit_count = u64::from_be_bytes(bit_count_bytes) as usize;
543        let expected_len = 8 + bit_count * 2 * 32;
544        if bytes.len() != expected_len {
545            return Err(MLError::InvalidParameter(format!(
546                "Verifying key length mismatch: expected {expected_len} bytes, got {}",
547                bytes.len()
548            )));
549        }
550        let public_key = bytes[8..]
551            .chunks_exact(32)
552            .map(|chunk| {
553                let mut entry = [0u8; 32];
554                entry.copy_from_slice(chunk);
555                entry
556            })
557            .collect();
558        Ok(Self {
559            bit_count,
560            public_key,
561        })
562    }
563}
564
565/// Quantum-safe one-time digital signature key pair.
566///
567/// Implements a Lamport one-time signature (Lamport, 1979): a hash-based
568/// scheme whose security rests only on the one-wayness of a hash function,
569/// not on factoring/discrete-log assumptions that Shor's algorithm breaks --
570/// making it "quantum-safe" in the sense this module's name advertises,
571/// unlike the previous XOR-based placeholder. Verification (see
572/// [`QuantumSignatureVerifyingKey::verify`]) uses *only* the public key,
573/// structurally correcting the previous bug where `verify` could only be
574/// called by whoever held the private key.
575///
576/// **This is genuinely a *one-time* signature scheme**: signing two
577/// different messages with the same key pair reveals enough of the private
578/// key to forge further signatures, exactly as with real Lamport signatures.
579/// A new [`QuantumSignature::new`] key pair should be generated per message.
580#[derive(Debug, Clone)]
581pub struct QuantumSignature {
582    /// Number of committed message-digest bits.
583    bit_count: usize,
584
585    /// Signature algorithm label.
586    algorithm: String,
587
588    /// Public key: `public_key[2*i]`/`public_key[2*i+1]` are the hashes of
589    /// the "digest bit `i` is 0" / "digest bit `i` is 1" private-key secrets.
590    public_key: Vec<[u8; 32]>,
591
592    /// Private key: `private_key[2*i]`/`private_key[2*i+1]` are the two
593    /// preimages for digest bit `i`.
594    private_key: Vec<[u8; 32]>,
595}
596
597impl QuantumSignature {
598    /// Creates a new quantum signature key pair.
599    pub fn new(security_bits: usize, algorithm: &str) -> Result<Self> {
600        let bit_count = lamport_bit_count(security_bits);
601        let mut rng = thread_rng();
602        let private_key: Vec<[u8; 32]> = (0..bit_count * 2)
603            .map(|_| {
604                let mut secret = [0u8; 32];
605                for byte in secret.iter_mut() {
606                    *byte = rng.random::<u8>();
607                }
608                secret
609            })
610            .collect();
611        let public_key: Vec<[u8; 32]> = private_key
612            .iter()
613            .map(|secret| sha256::digest(secret))
614            .collect();
615
616        Ok(QuantumSignature {
617            bit_count,
618            algorithm: algorithm.to_string(),
619            public_key,
620            private_key,
621        })
622    }
623
624    /// Signs a message: hashes it with SHA-256, then reveals one of the two
625    /// private-key preimages per digest bit (selected by that bit's value).
626    pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>> {
627        let message_digest = sha256::digest(message);
628        let mut signature = Vec::with_capacity(self.bit_count * 32);
629        for bit_index in 0..self.bit_count {
630            let bit = digest_bit(&message_digest, bit_index);
631            let secret = &self.private_key[2 * bit_index + bit as usize];
632            signature.extend_from_slice(secret);
633        }
634        Ok(signature)
635    }
636
637    /// Verifies a signature using this key pair's public key. Structurally
638    /// identical to [`QuantumSignatureVerifyingKey::verify`] -- exposed here
639    /// too so existing callers that only have a full `QuantumSignature`
640    /// (e.g. the signer itself, checking its own work) do not need to call
641    /// [`Self::verifying_key`] first.
642    pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<bool> {
643        Self::verify_with_public_key(message, signature, &self.public_key, self.bit_count)
644    }
645
646    /// Extracts a standalone [`QuantumSignatureVerifyingKey`] containing only
647    /// the public key, for distribution to verifiers.
648    pub fn verifying_key(&self) -> QuantumSignatureVerifyingKey {
649        QuantumSignatureVerifyingKey {
650            bit_count: self.bit_count,
651            public_key: self.public_key.clone(),
652        }
653    }
654
655    /// Serializes this key pair's public key; see
656    /// [`QuantumSignatureVerifyingKey::to_bytes`].
657    pub fn public_key_bytes(&self) -> Vec<u8> {
658        self.verifying_key().to_bytes()
659    }
660
661    fn verify_with_public_key(
662        message: &[u8],
663        signature: &[u8],
664        public_key: &[[u8; 32]],
665        bit_count: usize,
666    ) -> Result<bool> {
667        if signature.len() != bit_count * 32 || public_key.len() != bit_count * 2 {
668            return Ok(false);
669        }
670        let message_digest = sha256::digest(message);
671        for bit_index in 0..bit_count {
672            let bit = digest_bit(&message_digest, bit_index);
673            let revealed_preimage = &signature[bit_index * 32..(bit_index + 1) * 32];
674            let expected_public_entry = public_key[2 * bit_index + bit as usize];
675            if sha256::digest(revealed_preimage) != expected_public_entry {
676                return Ok(false);
677            }
678        }
679        Ok(true)
680    }
681}
682
683/// Quantum authentication
684#[derive(Debug, Clone)]
685pub struct QuantumAuthentication {
686    /// Protocol type
687    protocol: String,
688
689    /// Security parameter
690    security_bits: usize,
691
692    /// Authentication keys
693    keys: HashMap<String, Vec<u8>>,
694}
695
696impl QuantumAuthentication {
697    /// Creates a new quantum authentication protocol
698    pub fn new(protocol: &str, security_bits: usize) -> Self {
699        QuantumAuthentication {
700            protocol: protocol.to_string(),
701            security_bits,
702            keys: HashMap::new(),
703        }
704    }
705
706    /// Adds a party to the authentication system
707    pub fn add_party(&mut self, party_name: &str) -> Result<()> {
708        // Generate a random key
709        let key = (0..self.security_bits / 8 + 1)
710            .map(|_| thread_rng().random::<u8>())
711            .collect::<Vec<_>>();
712
713        self.keys.insert(party_name.to_string(), key);
714
715        Ok(())
716    }
717
718    /// Authenticates a message from a party
719    pub fn authenticate(&self, party_name: &str, message: &[u8]) -> Result<Vec<u8>> {
720        // Get the party's key
721        let key = self
722            .keys
723            .get(party_name)
724            .ok_or_else(|| MLError::InvalidParameter(format!("Party {} not found", party_name)))?;
725
726        // Generate a random authentication tag
727        let mut tag = key.clone();
728
729        // XOR with the message (simplified)
730        for (i, &byte) in message.iter().enumerate() {
731            if i < tag.len() {
732                tag[i] ^= byte;
733            }
734        }
735
736        Ok(tag)
737    }
738
739    /// Verifies an authentication tag
740    pub fn verify(&self, party_name: &str, message: &[u8], tag: &[u8]) -> Result<bool> {
741        // Generate the expected tag
742        let expected_tag = self.authenticate(party_name, message)?;
743
744        // Compare tags
745        let is_valid = tag.len() == expected_tag.len()
746            && tag.iter().zip(expected_tag.iter()).all(|(a, b)| a == b);
747
748        Ok(is_valid)
749    }
750}
751
752/// Quantum Secure Direct Communication protocol
753#[derive(Debug, Clone)]
754pub struct QSDC {
755    /// Number of qubits to use
756    pub num_qubits: usize,
757
758    /// Error rate for the quantum channel
759    pub error_rate: f64,
760}
761
762impl QSDC {
763    /// Creates a new QSDC protocol instance
764    pub fn new(num_qubits: usize) -> Self {
765        QSDC {
766            num_qubits,
767            error_rate: 0.01, // Default 1% error rate
768        }
769    }
770
771    /// Sets the error rate for the quantum channel
772    pub fn with_error_rate(mut self, error_rate: f64) -> Self {
773        self.error_rate = error_rate;
774        self
775    }
776
777    /// Transmits a message directly using the quantum channel
778    pub fn transmit_message(&self, message: &[u8]) -> Result<Vec<u8>> {
779        // This is a dummy implementation
780        // In a real implementation, this would use quantum entanglement
781        // to directly transmit the message
782
783        // Create a copy of the message
784        let mut received = message.to_vec();
785
786        // Apply the error rate to simulate channel noise
787        for byte in &mut received {
788            for bit_pos in 0..8 {
789                if thread_rng().random::<f64>() < self.error_rate {
790                    // Flip the bit
791                    *byte ^= 1 << bit_pos;
792                }
793            }
794        }
795
796        Ok(received)
797    }
798}
799
800/// Encrypts a message using a quantum key
801pub fn encrypt_with_qkd(message: &[u8], key: Vec<u8>) -> Vec<u8> {
802    // Simple XOR encryption
803    message
804        .iter()
805        .enumerate()
806        .map(|(i, &byte)| byte ^ key[i % key.len()])
807        .collect()
808}
809
810/// Decrypts a message using a quantum key
811pub fn decrypt_with_qkd(encrypted: &[u8], key: Vec<u8>) -> Vec<u8> {
812    // XOR is symmetric, so encryption and decryption are the same
813    encrypt_with_qkd(encrypted, key)
814}
815
816impl fmt::Display for ProtocolType {
817    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
818        match self {
819            ProtocolType::BB84 => write!(f, "BB84"),
820            ProtocolType::E91 => write!(f, "E91"),
821            ProtocolType::B92 => write!(f, "B92"),
822            ProtocolType::BBM92 => write!(f, "BBM92"),
823            ProtocolType::SARG04 => write!(f, "SARG04"),
824        }
825    }
826}
827
828#[cfg(test)]
829mod signature_regression_tests {
830    use super::*;
831
832    /// Regression test for the "verify() requires the private key" bug: a
833    /// verifier holding *only* the public verifying key (never the private
834    /// key) must be able to check a signature produced by the signer.
835    #[test]
836    fn verify_succeeds_with_only_the_public_verifying_key() {
837        let signer = QuantumSignature::new(64, "lamport-test").expect("key generation");
838        let message = b"transfer 10 QBTC to bob";
839        let signature = signer.sign(message).expect("signing should succeed");
840
841        // The verifier only ever sees this -- it structurally cannot access
842        // `signer.private_key` (a private field of a different value it
843        // never receives).
844        let verifying_key = signer.verifying_key();
845        assert!(verifying_key
846            .verify(message, &signature)
847            .expect("verification should succeed"));
848
849        // Round-trip through serialization, as a transaction's `sender`
850        // field would carry it.
851        let bytes = verifying_key.to_bytes();
852        let restored = QuantumSignatureVerifyingKey::from_bytes(&bytes).expect("deserialize");
853        assert!(restored
854            .verify(message, &signature)
855            .expect("verification should succeed after round-trip"));
856    }
857
858    #[test]
859    fn verify_rejects_tampered_message_or_signature() {
860        let signer = QuantumSignature::new(64, "lamport-test").expect("key generation");
861        let message = b"transfer 10 QBTC to bob";
862        let signature = signer.sign(message).expect("signing should succeed");
863        let verifying_key = signer.verifying_key();
864
865        let tampered_message = b"transfer 99 QBTC to mallory";
866        assert!(!verifying_key
867            .verify(tampered_message, &signature)
868            .expect("verification should not error"));
869
870        let mut tampered_signature = signature.clone();
871        tampered_signature[0] ^= 0xFF;
872        assert!(!verifying_key
873            .verify(message, &tampered_signature)
874            .expect("verification should not error"));
875
876        // A signature produced by a *different* key pair must not verify
877        // against this signer's public key.
878        let other_signer = QuantumSignature::new(64, "lamport-test").expect("key generation");
879        let other_signature = other_signer.sign(message).expect("signing should succeed");
880        assert!(!verifying_key
881            .verify(message, &other_signature)
882            .expect("verification should not error"));
883    }
884}