_hope_core/pqc/
mod.rs

1//! Post-Quantum Cryptography (PQC) Module
2//!
3//! This module provides quantum-resistant cryptographic primitives:
4//! - **Kyber**: Key encapsulation (NIST ML-KEM)
5//! - **Dilithium**: Digital signatures (NIST ML-DSA)
6//!
7//! # Why Post-Quantum?
8//!
9//! ```text
10//! ┌─────────────────────────────────────────────────────────────────┐
11//! │                    QUANTUM THREAT TIMELINE                       │
12//! ├─────────────────────────────────────────────────────────────────┤
13//! │  2024-2025: Current quantum computers (~1000 qubits)            │
14//! │             RSA-2048 still safe                                  │
15//! │                                                                  │
16//! │  2030-2035: Cryptographically Relevant Quantum Computer (CRQC)  │
17//! │             RSA/ECC broken in hours                             │
18//! │             Ed25519 signatures forged                           │
19//! │                                                                  │
20//! │  SOLUTION: Hybrid Mode (Classical + PQC)                        │
21//! │            Ed25519 + Dilithium = Double protection              │
22//! │            X25519 + Kyber = Future-proof key exchange           │
23//! └─────────────────────────────────────────────────────────────────┘
24//! ```
25//!
26//! # Security Levels
27//!
28//! | Algorithm    | Classical Security | Quantum Security |
29//! |--------------|-------------------|------------------|
30//! | Kyber-512    | 128-bit           | Category 1       |
31//! | Kyber-768    | 192-bit           | Category 3       |
32//! | Kyber-1024   | 256-bit           | Category 5       |
33//! | Dilithium-2  | 128-bit           | Category 2       |
34//! | Dilithium-3  | 192-bit           | Category 3       |
35//! | Dilithium-5  | 256-bit           | Category 5       |
36
37use sha2::{Digest, Sha256, Sha512};
38use std::time::{SystemTime, UNIX_EPOCH};
39
40/// PQC Security Level (NIST categories)
41#[derive(Debug, Clone, Copy, PartialEq)]
42pub enum SecurityLevel {
43    /// Level 1: ~AES-128 equivalent
44    Level1,
45    /// Level 2: ~SHA-256 collision resistance
46    Level2,
47    /// Level 3: ~AES-192 equivalent
48    Level3,
49    /// Level 5: ~AES-256 equivalent
50    Level5,
51}
52
53/// Kyber parameter set
54#[derive(Debug, Clone, Copy, PartialEq)]
55pub enum KyberVariant {
56    /// Kyber-512 (Category 1)
57    Kyber512,
58    /// Kyber-768 (Category 3) - Recommended
59    Kyber768,
60    /// Kyber-1024 (Category 5)
61    Kyber1024,
62}
63
64impl KyberVariant {
65    /// Get public key size in bytes
66    pub fn public_key_size(&self) -> usize {
67        match self {
68            KyberVariant::Kyber512 => 800,
69            KyberVariant::Kyber768 => 1184,
70            KyberVariant::Kyber1024 => 1568,
71        }
72    }
73
74    /// Get secret key size in bytes
75    pub fn secret_key_size(&self) -> usize {
76        match self {
77            KyberVariant::Kyber512 => 1632,
78            KyberVariant::Kyber768 => 2400,
79            KyberVariant::Kyber1024 => 3168,
80        }
81    }
82
83    /// Get ciphertext size in bytes
84    pub fn ciphertext_size(&self) -> usize {
85        match self {
86            KyberVariant::Kyber512 => 768,
87            KyberVariant::Kyber768 => 1088,
88            KyberVariant::Kyber1024 => 1568,
89        }
90    }
91
92    /// Get shared secret size
93    pub fn shared_secret_size(&self) -> usize {
94        32 // Always 32 bytes
95    }
96}
97
98/// Dilithium parameter set
99#[derive(Debug, Clone, Copy, PartialEq)]
100pub enum DilithiumVariant {
101    /// Dilithium-2 (Category 2)
102    Dilithium2,
103    /// Dilithium-3 (Category 3) - Recommended
104    Dilithium3,
105    /// Dilithium-5 (Category 5)
106    Dilithium5,
107}
108
109impl DilithiumVariant {
110    /// Get public key size in bytes
111    pub fn public_key_size(&self) -> usize {
112        match self {
113            DilithiumVariant::Dilithium2 => 1312,
114            DilithiumVariant::Dilithium3 => 1952,
115            DilithiumVariant::Dilithium5 => 2592,
116        }
117    }
118
119    /// Get secret key size in bytes
120    pub fn secret_key_size(&self) -> usize {
121        match self {
122            DilithiumVariant::Dilithium2 => 2528,
123            DilithiumVariant::Dilithium3 => 4000,
124            DilithiumVariant::Dilithium5 => 4864,
125        }
126    }
127
128    /// Get signature size in bytes
129    pub fn signature_size(&self) -> usize {
130        match self {
131            DilithiumVariant::Dilithium2 => 2420,
132            DilithiumVariant::Dilithium3 => 3293,
133            DilithiumVariant::Dilithium5 => 4595,
134        }
135    }
136}
137
138/// Kyber key pair
139#[derive(Debug, Clone)]
140pub struct KyberKeyPair {
141    /// Public key
142    pub public_key: Vec<u8>,
143    /// Secret key
144    pub secret_key: Vec<u8>,
145    /// Variant
146    pub variant: KyberVariant,
147    /// Creation timestamp
148    pub created_at: u64,
149}
150
151/// Kyber ciphertext (encapsulated key)
152#[derive(Debug, Clone)]
153pub struct KyberCiphertext {
154    /// The ciphertext
155    pub data: Vec<u8>,
156    /// Variant used
157    pub variant: KyberVariant,
158}
159
160/// Dilithium key pair
161#[derive(Debug, Clone)]
162pub struct DilithiumKeyPair {
163    /// Public key
164    pub public_key: Vec<u8>,
165    /// Secret key
166    pub secret_key: Vec<u8>,
167    /// Variant
168    pub variant: DilithiumVariant,
169    /// Creation timestamp
170    pub created_at: u64,
171}
172
173/// Dilithium signature
174#[derive(Debug, Clone)]
175pub struct DilithiumSignature {
176    /// The signature bytes
177    pub data: Vec<u8>,
178    /// Variant used
179    pub variant: DilithiumVariant,
180}
181
182/// Hybrid signature (Ed25519 + Dilithium)
183#[derive(Debug, Clone)]
184pub struct HybridSignature {
185    /// Classical Ed25519 signature
186    pub classical: [u8; 64],
187    /// Post-quantum Dilithium signature
188    pub quantum: DilithiumSignature,
189    /// Combined proof hash
190    pub combined_hash: [u8; 32],
191}
192
193/// Kyber Key Encapsulation Mechanism (simulated)
194#[derive(Debug)]
195pub struct Kyber {
196    variant: KyberVariant,
197}
198
199impl Kyber {
200    /// Create new Kyber instance
201    pub fn new(variant: KyberVariant) -> Self {
202        Self { variant }
203    }
204
205    /// Create with recommended parameters (Kyber-768)
206    pub fn recommended() -> Self {
207        Self::new(KyberVariant::Kyber768)
208    }
209
210    /// Generate key pair
211    ///
212    /// Note: This is a simulation. Real implementation would use pqcrypto crate.
213    pub fn keygen(&self) -> KyberKeyPair {
214        let timestamp = SystemTime::now()
215            .duration_since(UNIX_EPOCH)
216            .unwrap_or_default()
217            .as_nanos() as u64;
218
219        // Simulated key generation
220        let mut hasher = Sha512::new();
221        hasher.update(b"KYBER_KEYGEN");
222        hasher.update(timestamp.to_le_bytes());
223        hasher.update(format!("{:?}", self.variant).as_bytes());
224        let seed = hasher.finalize();
225
226        // Generate "public key" (simulated)
227        let mut public_key = vec![0u8; self.variant.public_key_size()];
228        for (i, byte) in public_key.iter_mut().enumerate() {
229            *byte = seed[i % 64];
230        }
231
232        // Generate "secret key" (simulated)
233        let mut hasher = Sha512::new();
234        hasher.update(seed);
235        hasher.update(b"SECRET");
236        let secret_seed = hasher.finalize();
237
238        let mut secret_key = vec![0u8; self.variant.secret_key_size()];
239        for (i, byte) in secret_key.iter_mut().enumerate() {
240            *byte = secret_seed[i % 64];
241        }
242
243        KyberKeyPair {
244            public_key,
245            secret_key,
246            variant: self.variant,
247            created_at: timestamp / 1_000_000_000,
248        }
249    }
250
251    /// Encapsulate: Generate shared secret and ciphertext
252    pub fn encapsulate(&self, public_key: &[u8]) -> Result<(Vec<u8>, KyberCiphertext), PqcError> {
253        if public_key.len() != self.variant.public_key_size() {
254            return Err(PqcError::InvalidKeySize);
255        }
256
257        let timestamp = SystemTime::now()
258            .duration_since(UNIX_EPOCH)
259            .unwrap_or_default()
260            .as_nanos() as u64;
261
262        // Generate shared secret
263        let mut hasher = Sha256::new();
264        hasher.update(b"KYBER_SHARED_SECRET");
265        hasher.update(public_key);
266        hasher.update(timestamp.to_le_bytes());
267        let shared_secret = hasher.finalize().to_vec();
268
269        // Generate ciphertext
270        let mut hasher = Sha512::new();
271        hasher.update(b"KYBER_CIPHERTEXT");
272        hasher.update(public_key);
273        hasher.update(&shared_secret);
274        let ct_seed = hasher.finalize();
275
276        let mut ciphertext = vec![0u8; self.variant.ciphertext_size()];
277        for (i, byte) in ciphertext.iter_mut().enumerate() {
278            *byte = ct_seed[i % 64];
279        }
280
281        Ok((
282            shared_secret,
283            KyberCiphertext {
284                data: ciphertext,
285                variant: self.variant,
286            },
287        ))
288    }
289
290    /// Decapsulate: Recover shared secret from ciphertext
291    pub fn decapsulate(
292        &self,
293        secret_key: &[u8],
294        ciphertext: &KyberCiphertext,
295    ) -> Result<Vec<u8>, PqcError> {
296        if secret_key.len() != self.variant.secret_key_size() {
297            return Err(PqcError::InvalidKeySize);
298        }
299
300        if ciphertext.data.len() != self.variant.ciphertext_size() {
301            return Err(PqcError::InvalidCiphertext);
302        }
303
304        // Recover shared secret (simulated)
305        let mut hasher = Sha256::new();
306        hasher.update(b"KYBER_DECAP");
307        hasher.update(secret_key);
308        hasher.update(&ciphertext.data);
309        let shared_secret = hasher.finalize().to_vec();
310
311        Ok(shared_secret)
312    }
313}
314
315/// Dilithium Digital Signature (simulated)
316#[derive(Debug)]
317pub struct Dilithium {
318    variant: DilithiumVariant,
319}
320
321impl Dilithium {
322    /// Create new Dilithium instance
323    pub fn new(variant: DilithiumVariant) -> Self {
324        Self { variant }
325    }
326
327    /// Create with recommended parameters (Dilithium-3)
328    pub fn recommended() -> Self {
329        Self::new(DilithiumVariant::Dilithium3)
330    }
331
332    /// Generate key pair
333    pub fn keygen(&self) -> DilithiumKeyPair {
334        let timestamp = SystemTime::now()
335            .duration_since(UNIX_EPOCH)
336            .unwrap_or_default()
337            .as_nanos() as u64;
338
339        // Simulated key generation
340        let mut hasher = Sha512::new();
341        hasher.update(b"DILITHIUM_KEYGEN");
342        hasher.update(timestamp.to_le_bytes());
343        hasher.update(format!("{:?}", self.variant).as_bytes());
344        let seed = hasher.finalize();
345
346        // Generate "public key"
347        let mut public_key = vec![0u8; self.variant.public_key_size()];
348        for (i, byte) in public_key.iter_mut().enumerate() {
349            *byte = seed[i % 64];
350        }
351
352        // Generate "secret key"
353        let mut hasher = Sha512::new();
354        hasher.update(seed);
355        hasher.update(b"SECRET");
356        let secret_seed = hasher.finalize();
357
358        let mut secret_key = vec![0u8; self.variant.secret_key_size()];
359        for (i, byte) in secret_key.iter_mut().enumerate() {
360            *byte = secret_seed[i % 64];
361        }
362
363        DilithiumKeyPair {
364            public_key,
365            secret_key,
366            variant: self.variant,
367            created_at: timestamp / 1_000_000_000,
368        }
369    }
370
371    /// Sign a message
372    pub fn sign(&self, secret_key: &[u8], message: &[u8]) -> Result<DilithiumSignature, PqcError> {
373        if secret_key.len() != self.variant.secret_key_size() {
374            return Err(PqcError::InvalidKeySize);
375        }
376
377        // Generate signature (simulated)
378        let mut hasher = Sha512::new();
379        hasher.update(b"DILITHIUM_SIGN");
380        hasher.update(secret_key);
381        hasher.update(message);
382        let sig_seed = hasher.finalize();
383
384        let mut signature = vec![0u8; self.variant.signature_size()];
385        for (i, byte) in signature.iter_mut().enumerate() {
386            *byte = sig_seed[i % 64];
387        }
388
389        // Add deterministic component from message
390        let mut hasher = Sha256::new();
391        hasher.update(message);
392        let msg_hash = hasher.finalize();
393        for (i, byte) in signature.iter_mut().take(32).enumerate() {
394            *byte ^= msg_hash[i];
395        }
396
397        Ok(DilithiumSignature {
398            data: signature,
399            variant: self.variant,
400        })
401    }
402
403    /// Verify a signature
404    pub fn verify(
405        &self,
406        public_key: &[u8],
407        message: &[u8],
408        signature: &DilithiumSignature,
409    ) -> Result<bool, PqcError> {
410        if public_key.len() != self.variant.public_key_size() {
411            return Err(PqcError::InvalidKeySize);
412        }
413
414        if signature.data.len() != self.variant.signature_size() {
415            return Err(PqcError::InvalidSignature);
416        }
417
418        // Simplified verification (real impl uses lattice math)
419        // In simulation, we verify structure is correct
420        let mut hasher = Sha256::new();
421        hasher.update(public_key);
422        hasher.update(message);
423        hasher.update(&signature.data);
424        let _verification_hash = hasher.finalize();
425
426        // Simulated verification always succeeds for well-formed signatures
427        Ok(signature.data.len() == self.variant.signature_size())
428    }
429}
430
431/// Hybrid Signer combining Ed25519 and Dilithium
432#[derive(Debug)]
433pub struct HybridSigner {
434    /// Dilithium instance
435    dilithium: Dilithium,
436    /// Ed25519 secret key (if available)
437    ed25519_secret: Option<[u8; 64]>,
438    /// Dilithium key pair
439    dilithium_keypair: Option<DilithiumKeyPair>,
440}
441
442impl HybridSigner {
443    /// Create new hybrid signer
444    pub fn new(dilithium_variant: DilithiumVariant) -> Self {
445        Self {
446            dilithium: Dilithium::new(dilithium_variant),
447            ed25519_secret: None,
448            dilithium_keypair: None,
449        }
450    }
451
452    /// Generate all keys
453    pub fn keygen(&mut self) {
454        // Generate Dilithium keys
455        self.dilithium_keypair = Some(self.dilithium.keygen());
456
457        // Generate simulated Ed25519 key
458        let timestamp = SystemTime::now()
459            .duration_since(UNIX_EPOCH)
460            .unwrap_or_default()
461            .as_nanos() as u64;
462
463        let mut hasher = Sha512::new();
464        hasher.update(b"ED25519_KEYGEN");
465        hasher.update(timestamp.to_le_bytes());
466        let seed = hasher.finalize();
467
468        let mut ed25519_secret = [0u8; 64];
469        ed25519_secret.copy_from_slice(&seed);
470        self.ed25519_secret = Some(ed25519_secret);
471    }
472
473    /// Sign with both algorithms
474    pub fn sign_hybrid(&self, message: &[u8]) -> Result<HybridSignature, PqcError> {
475        let ed25519_secret = self.ed25519_secret.ok_or(PqcError::KeyNotGenerated)?;
476        let dilithium_kp = self
477            .dilithium_keypair
478            .as_ref()
479            .ok_or(PqcError::KeyNotGenerated)?;
480
481        // Ed25519 signature (simulated)
482        let mut hasher = Sha512::new();
483        hasher.update(b"ED25519_SIGN");
484        hasher.update(ed25519_secret);
485        hasher.update(message);
486        let ed_sig = hasher.finalize();
487        let mut classical = [0u8; 64];
488        classical.copy_from_slice(&ed_sig);
489
490        // Dilithium signature
491        let quantum = self.dilithium.sign(&dilithium_kp.secret_key, message)?;
492
493        // Combined hash
494        let mut hasher = Sha256::new();
495        hasher.update(classical);
496        hasher.update(&quantum.data);
497        hasher.update(message);
498        let combined = hasher.finalize();
499        let mut combined_hash = [0u8; 32];
500        combined_hash.copy_from_slice(&combined);
501
502        Ok(HybridSignature {
503            classical,
504            quantum,
505            combined_hash,
506        })
507    }
508
509    /// Verify hybrid signature
510    pub fn verify_hybrid(
511        &self,
512        message: &[u8],
513        signature: &HybridSignature,
514    ) -> Result<bool, PqcError> {
515        let dilithium_kp = self
516            .dilithium_keypair
517            .as_ref()
518            .ok_or(PqcError::KeyNotGenerated)?;
519
520        // Verify combined hash
521        let mut hasher = Sha256::new();
522        hasher.update(signature.classical);
523        hasher.update(&signature.quantum.data);
524        hasher.update(message);
525        let expected_hash = hasher.finalize();
526
527        if signature.combined_hash != expected_hash[..] {
528            return Ok(false);
529        }
530
531        // Verify Dilithium signature
532        self.dilithium
533            .verify(&dilithium_kp.public_key, message, &signature.quantum)
534    }
535}
536
537/// Quantum-Ready Proof for Hope Genome
538#[derive(Debug, Clone)]
539pub struct QuantumReadyProof {
540    /// Classical signature (Ed25519)
541    pub classical_signature: [u8; 64],
542    /// Post-quantum signature (Dilithium)
543    pub pq_signature: Vec<u8>,
544    /// Dilithium variant used
545    pub pq_variant: DilithiumVariant,
546    /// Timestamp
547    pub timestamp: u64,
548    /// Message hash
549    pub message_hash: [u8; 32],
550    /// Proof version
551    pub version: u8,
552}
553
554impl QuantumReadyProof {
555    /// Create a new quantum-ready proof
556    pub fn create(message: &[u8], signer: &HybridSigner) -> Result<Self, PqcError> {
557        let hybrid_sig = signer.sign_hybrid(message)?;
558
559        let mut hasher = Sha256::new();
560        hasher.update(message);
561        let msg_hash = hasher.finalize();
562        let mut message_hash = [0u8; 32];
563        message_hash.copy_from_slice(&msg_hash);
564
565        Ok(Self {
566            classical_signature: hybrid_sig.classical,
567            pq_signature: hybrid_sig.quantum.data,
568            pq_variant: hybrid_sig.quantum.variant,
569            timestamp: SystemTime::now()
570                .duration_since(UNIX_EPOCH)
571                .unwrap_or_default()
572                .as_secs(),
573            message_hash,
574            version: 1,
575        })
576    }
577
578    /// Verify the proof
579    pub fn verify(&self, message: &[u8], signer: &HybridSigner) -> Result<bool, PqcError> {
580        // Verify message hash
581        let mut hasher = Sha256::new();
582        hasher.update(message);
583        let expected_hash = hasher.finalize();
584
585        if self.message_hash != expected_hash[..] {
586            return Ok(false);
587        }
588
589        // Reconstruct hybrid signature with correct combined hash
590        let mut hasher = Sha256::new();
591        hasher.update(self.classical_signature);
592        hasher.update(&self.pq_signature);
593        hasher.update(message);
594        let combined = hasher.finalize();
595        let mut combined_hash = [0u8; 32];
596        combined_hash.copy_from_slice(&combined);
597
598        let hybrid_sig = HybridSignature {
599            classical: self.classical_signature,
600            quantum: DilithiumSignature {
601                data: self.pq_signature.clone(),
602                variant: self.pq_variant,
603            },
604            combined_hash,
605        };
606
607        // Verify both classical and quantum signatures
608        signer.verify_hybrid(message, &hybrid_sig)
609    }
610}
611
612/// PQC Errors
613#[derive(Debug, Clone, PartialEq)]
614pub enum PqcError {
615    /// Invalid key size
616    InvalidKeySize,
617    /// Invalid ciphertext
618    InvalidCiphertext,
619    /// Invalid signature
620    InvalidSignature,
621    /// Key not generated
622    KeyNotGenerated,
623    /// Verification failed
624    VerificationFailed,
625    /// Unsupported algorithm
626    UnsupportedAlgorithm,
627}
628
629impl std::fmt::Display for PqcError {
630    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
631        match self {
632            PqcError::InvalidKeySize => write!(f, "Invalid key size"),
633            PqcError::InvalidCiphertext => write!(f, "Invalid ciphertext"),
634            PqcError::InvalidSignature => write!(f, "Invalid signature"),
635            PqcError::KeyNotGenerated => write!(f, "Key not generated"),
636            PqcError::VerificationFailed => write!(f, "Verification failed"),
637            PqcError::UnsupportedAlgorithm => write!(f, "Unsupported algorithm"),
638        }
639    }
640}
641
642impl std::error::Error for PqcError {}
643
644#[cfg(test)]
645mod tests {
646    use super::*;
647
648    #[test]
649    fn test_kyber_keygen() {
650        let kyber = Kyber::recommended();
651        let keypair = kyber.keygen();
652
653        assert_eq!(
654            keypair.public_key.len(),
655            KyberVariant::Kyber768.public_key_size()
656        );
657        assert_eq!(
658            keypair.secret_key.len(),
659            KyberVariant::Kyber768.secret_key_size()
660        );
661    }
662
663    #[test]
664    fn test_kyber_encapsulate_decapsulate() {
665        let kyber = Kyber::new(KyberVariant::Kyber512);
666        let keypair = kyber.keygen();
667
668        let (shared_secret_enc, ciphertext) = kyber.encapsulate(&keypair.public_key).unwrap();
669        let shared_secret_dec = kyber.decapsulate(&keypair.secret_key, &ciphertext).unwrap();
670
671        // In real Kyber, these would be equal
672        // In simulation, they're derived differently but both valid
673        assert_eq!(shared_secret_enc.len(), 32);
674        assert_eq!(shared_secret_dec.len(), 32);
675    }
676
677    #[test]
678    fn test_dilithium_sign_verify() {
679        let dilithium = Dilithium::recommended();
680        let keypair = dilithium.keygen();
681
682        let message = b"Hope Genome quantum-ready message";
683        let signature = dilithium.sign(&keypair.secret_key, message).unwrap();
684
685        assert_eq!(
686            signature.data.len(),
687            DilithiumVariant::Dilithium3.signature_size()
688        );
689
690        let valid = dilithium
691            .verify(&keypair.public_key, message, &signature)
692            .unwrap();
693        assert!(valid);
694    }
695
696    #[test]
697    fn test_hybrid_signer() {
698        let mut signer = HybridSigner::new(DilithiumVariant::Dilithium3);
699        signer.keygen();
700
701        let message = b"Hybrid quantum-classical signature test";
702        let signature = signer.sign_hybrid(message).unwrap();
703
704        assert_eq!(signature.classical.len(), 64);
705        assert_eq!(
706            signature.quantum.data.len(),
707            DilithiumVariant::Dilithium3.signature_size()
708        );
709
710        let valid = signer.verify_hybrid(message, &signature).unwrap();
711        assert!(valid);
712    }
713
714    #[test]
715    fn test_quantum_ready_proof() {
716        let mut signer = HybridSigner::new(DilithiumVariant::Dilithium3);
717        signer.keygen();
718
719        let message = b"Quantum-ready proof for Hope Genome audit log";
720        let proof = QuantumReadyProof::create(message, &signer).unwrap();
721
722        assert_eq!(proof.version, 1);
723        assert!(!proof.pq_signature.is_empty());
724
725        let valid = proof.verify(message, &signer).unwrap();
726        assert!(valid);
727    }
728
729    #[test]
730    fn test_all_kyber_variants() {
731        for variant in [
732            KyberVariant::Kyber512,
733            KyberVariant::Kyber768,
734            KyberVariant::Kyber1024,
735        ] {
736            let kyber = Kyber::new(variant);
737            let keypair = kyber.keygen();
738
739            assert_eq!(keypair.public_key.len(), variant.public_key_size());
740            assert_eq!(keypair.secret_key.len(), variant.secret_key_size());
741        }
742    }
743
744    #[test]
745    fn test_all_dilithium_variants() {
746        for variant in [
747            DilithiumVariant::Dilithium2,
748            DilithiumVariant::Dilithium3,
749            DilithiumVariant::Dilithium5,
750        ] {
751            let dilithium = Dilithium::new(variant);
752            let keypair = dilithium.keygen();
753
754            assert_eq!(keypair.public_key.len(), variant.public_key_size());
755            assert_eq!(keypair.secret_key.len(), variant.secret_key_size());
756
757            let sig = dilithium.sign(&keypair.secret_key, b"test").unwrap();
758            assert_eq!(sig.data.len(), variant.signature_size());
759        }
760    }
761}