Skip to main content

lib_q_core/
algorithm_registry.rs

1//! Algorithm registry for lib-Q
2//!
3//! This module provides a centralized registry of all supported algorithms,
4//! eliminating the need for manual enumeration and providing better maintainability.
5
6#[cfg(all(not(feature = "std"), feature = "alloc"))]
7use alloc::collections::BTreeMap as HashMap;
8#[cfg(feature = "alloc")]
9use alloc::string::ToString;
10#[cfg(feature = "alloc")]
11use alloc::vec::Vec;
12#[cfg(feature = "std")]
13#[allow(clippy::disallowed_types)]
14use std::collections::HashMap;
15
16#[cfg(any(feature = "std", feature = "alloc"))]
17use crate::Result;
18use crate::{
19    Algorithm,
20    AlgorithmCategory,
21};
22
23/// Algorithm metadata
24#[derive(Debug, Clone)]
25pub struct AlgorithmMetadata {
26    pub algorithm: Algorithm,
27    pub category: AlgorithmCategory,
28    pub security_level: u32,
29    pub name: &'static str,
30    pub description: &'static str,
31    pub enabled: bool,
32}
33
34/// Central registry of all algorithms
35#[cfg(any(feature = "std", feature = "alloc"))]
36pub struct AlgorithmRegistry {
37    #[allow(clippy::disallowed_types)]
38    algorithms: HashMap<Algorithm, AlgorithmMetadata>,
39}
40
41#[cfg(any(feature = "std", feature = "alloc"))]
42impl AlgorithmRegistry {
43    /// Create a new algorithm registry
44    pub fn new() -> Self {
45        let mut registry = Self {
46            #[allow(clippy::disallowed_types)]
47            algorithms: HashMap::new(),
48        };
49        registry.register_all();
50        registry
51    }
52
53    /// Register all supported algorithms
54    fn register_all(&mut self) {
55        // KEM algorithms
56        self.register(AlgorithmMetadata {
57            algorithm: Algorithm::MlKem512,
58            category: AlgorithmCategory::Kem,
59            security_level: 1,
60            name: "ML-KEM-512",
61            description: "CRYSTALS-ML-KEM Level 1 (128-bit security)",
62            enabled: true,
63        });
64
65        self.register(AlgorithmMetadata {
66            algorithm: Algorithm::MlKem768,
67            category: AlgorithmCategory::Kem,
68            security_level: 3,
69            name: "ML-KEM-768",
70            description: "CRYSTALS-ML-KEM Level 3 (192-bit security)",
71            enabled: true,
72        });
73
74        self.register(AlgorithmMetadata {
75            algorithm: Algorithm::MlKem1024,
76            category: AlgorithmCategory::Kem,
77            security_level: 5,
78            name: "ML-KEM-1024",
79            description: "CRYSTALS-ML-KEM Level 5 (256-bit security)",
80            enabled: true,
81        });
82
83        // CB-KEM algorithms
84        self.register(AlgorithmMetadata {
85            algorithm: Algorithm::CbKem348864,
86            category: AlgorithmCategory::Kem,
87            security_level: 1,
88            name: "CB-KEM 348864",
89            description: "CB-KEM Level 1 (128-bit security)",
90            enabled: true,
91        });
92
93        self.register(AlgorithmMetadata {
94            algorithm: Algorithm::CbKem460896,
95            category: AlgorithmCategory::Kem,
96            security_level: 3,
97            name: "CB-KEM 460896",
98            description: "CB-KEM Level 3 (192-bit security)",
99            enabled: true,
100        });
101
102        self.register(AlgorithmMetadata {
103            algorithm: Algorithm::CbKem6688128,
104            category: AlgorithmCategory::Kem,
105            security_level: 5,
106            name: "CB-KEM 6688128",
107            description: "CB-KEM Level 5 (256-bit security)",
108            enabled: true,
109        });
110
111        self.register(AlgorithmMetadata {
112            algorithm: Algorithm::CbKem6960119,
113            category: AlgorithmCategory::Kem,
114            security_level: 5,
115            name: "CB-KEM 6960119",
116            description: "CB-KEM Level 5 (256-bit security)",
117            enabled: true,
118        });
119
120        self.register(AlgorithmMetadata {
121            algorithm: Algorithm::CbKem8192128,
122            category: AlgorithmCategory::Kem,
123            security_level: 5,
124            name: "CB-KEM 8192128",
125            description: "CB-KEM Level 5 (256-bit security, higher performance)",
126            enabled: true,
127        });
128
129        // HQC algorithms
130        self.register(AlgorithmMetadata {
131            algorithm: Algorithm::Hqc128,
132            category: AlgorithmCategory::Kem,
133            security_level: 1,
134            name: "HQC-128",
135            description: "HQC Level 1 (128-bit security)",
136            enabled: true,
137        });
138
139        self.register(AlgorithmMetadata {
140            algorithm: Algorithm::Hqc192,
141            category: AlgorithmCategory::Kem,
142            security_level: 3,
143            name: "HQC-192",
144            description: "HQC Level 3 (192-bit security)",
145            enabled: true,
146        });
147
148        self.register(AlgorithmMetadata {
149            algorithm: Algorithm::Hqc256,
150            category: AlgorithmCategory::Kem,
151            security_level: 5,
152            name: "HQC-256",
153            description: "HQC Level 5 (256-bit security)",
154            enabled: true,
155        });
156
157        // Signature algorithms
158        self.register(AlgorithmMetadata {
159            algorithm: Algorithm::MlDsa44,
160            category: AlgorithmCategory::Signature,
161            security_level: 1,
162            name: "ML-DSA-44",
163            description: "CRYSTALS-ML-DSA Level 1 (128-bit security)",
164            enabled: true,
165        });
166
167        self.register(AlgorithmMetadata {
168            algorithm: Algorithm::MlDsa65,
169            category: AlgorithmCategory::Signature,
170            security_level: 3,
171            name: "ML-DSA-65",
172            description: "CRYSTALS-ML-DSA Level 3 (192-bit security)",
173            enabled: true,
174        });
175
176        self.register(AlgorithmMetadata {
177            algorithm: Algorithm::MlDsa87,
178            category: AlgorithmCategory::Signature,
179            security_level: 5,
180            name: "ML-DSA-87",
181            description: "CRYSTALS-ML-DSA Level 5 (256-bit security)",
182            enabled: true,
183        });
184
185        self.register(AlgorithmMetadata {
186            algorithm: Algorithm::FnDsa,
187            category: AlgorithmCategory::Signature,
188            security_level: 1,
189            name: "FN-DSA",
190            description: "FN-DSA (NIST-selected; FIPS 206 not yet published) - Fast Fourier Transform over NTRU-Lattice-Based Digital Signature Algorithm",
191            enabled: true,
192        });
193
194        self.register(AlgorithmMetadata {
195            algorithm: Algorithm::FnDsa512,
196            category: AlgorithmCategory::Signature,
197            security_level: 1,
198            name: "FN-DSA-512",
199            description: "FN-DSA Level 1 (128-bit security) - n=512",
200            enabled: true,
201        });
202
203        self.register(AlgorithmMetadata {
204            algorithm: Algorithm::FnDsa1024,
205            category: AlgorithmCategory::Signature,
206            security_level: 5,
207            name: "FN-DSA-1024",
208            description: "FN-DSA Level 5 (256-bit security) - n=1024",
209            enabled: true,
210        });
211
212        // SLH-DSA algorithms
213        self.register(AlgorithmMetadata {
214            algorithm: Algorithm::SlhDsaSha256128fRobust,
215            category: AlgorithmCategory::Signature,
216            security_level: 1,
217            name: "SLH-DSA-SHA256-128f-Robust",
218            description: "SLH-DSA SHA256 Level 1 (128-bit security)",
219            enabled: true,
220        });
221
222        self.register(AlgorithmMetadata {
223            algorithm: Algorithm::SlhDsaSha256192fRobust,
224            category: AlgorithmCategory::Signature,
225            security_level: 3,
226            name: "SLH-DSA-SHA256-192f-Robust",
227            description: "SLH-DSA SHA256 Level 3 (192-bit security)",
228            enabled: true,
229        });
230
231        self.register(AlgorithmMetadata {
232            algorithm: Algorithm::SlhDsaSha256256fRobust,
233            category: AlgorithmCategory::Signature,
234            security_level: 5,
235            name: "SLH-DSA-SHA256-256f-Robust",
236            description: "SLH-DSA SHA256 Level 5 (256-bit security)",
237            enabled: true,
238        });
239
240        self.register(AlgorithmMetadata {
241            algorithm: Algorithm::SlhDsaShake256128fRobust,
242            category: AlgorithmCategory::Signature,
243            security_level: 1,
244            name: "SLH-DSA-SHAKE256-128f-Robust",
245            description: "SLH-DSA SHAKE256 Level 1 (128-bit security)",
246            enabled: true,
247        });
248
249        self.register(AlgorithmMetadata {
250            algorithm: Algorithm::SlhDsaShake256192fRobust,
251            category: AlgorithmCategory::Signature,
252            security_level: 3,
253            name: "SLH-DSA-SHAKE256-192f-Robust",
254            description: "SLH-DSA SHAKE256 Level 3 (192-bit security)",
255            enabled: true,
256        });
257
258        self.register(AlgorithmMetadata {
259            algorithm: Algorithm::SlhDsaShake256256fRobust,
260            category: AlgorithmCategory::Signature,
261            security_level: 5,
262            name: "SLH-DSA-SHAKE256-256f-Robust",
263            description: "SLH-DSA SHAKE256 Level 5 (256-bit security)",
264            enabled: true,
265        });
266
267        // Reserved diversity signature: registered but DISABLED. FAEST (VOLE-in-the-Head) rests on
268        // symmetric-only assumptions — a hedge against a structured-lattice break of ML-DSA. Large
269        // and slow vs ML-DSA, so never a default; activate only on a lattice-cryptanalysis event.
270        // Identifier only (no implementation). Supersedes Picnic. See lib-q-sig/docs/FAEST_EVALUATION.md.
271        self.register(AlgorithmMetadata {
272            algorithm: Algorithm::FaestReserved,
273            category: AlgorithmCategory::Signature,
274            security_level: 3,
275            name: "FAEST-Reserved",
276            description: "RESERVED FAEST/VOLE-in-the-Head diversity signature (symmetric assumptions); disabled — activate only on a lattice-cryptanalysis event",
277            enabled: false,
278        });
279
280        // Hash algorithms
281        self.register(AlgorithmMetadata {
282            algorithm: Algorithm::Shake128,
283            category: AlgorithmCategory::Hash,
284            security_level: 0,
285            name: "SHAKE128",
286            description: "SHAKE128 hash function",
287            enabled: true,
288        });
289
290        self.register(AlgorithmMetadata {
291            algorithm: Algorithm::Shake256,
292            category: AlgorithmCategory::Hash,
293            security_level: 0,
294            name: "SHAKE256",
295            description: "SHAKE256 hash function",
296            enabled: true,
297        });
298
299        self.register(AlgorithmMetadata {
300            algorithm: Algorithm::CShake128,
301            category: AlgorithmCategory::Hash,
302            security_level: 0,
303            name: "cSHAKE128",
304            description: "cSHAKE128 customizable hash function",
305            enabled: true,
306        });
307
308        self.register(AlgorithmMetadata {
309            algorithm: Algorithm::CShake256,
310            category: AlgorithmCategory::Hash,
311            security_level: 0,
312            name: "cSHAKE256",
313            description: "cSHAKE256 customizable hash function",
314            enabled: true,
315        });
316
317        // SHA-3 algorithms
318        self.register(AlgorithmMetadata {
319            algorithm: Algorithm::Sha3_224,
320            category: AlgorithmCategory::Hash,
321            security_level: 0,
322            name: "SHA3-224",
323            description: "SHA3-224 hash function",
324            enabled: true,
325        });
326
327        self.register(AlgorithmMetadata {
328            algorithm: Algorithm::Sha3_256,
329            category: AlgorithmCategory::Hash,
330            security_level: 0,
331            name: "SHA3-256",
332            description: "SHA3-256 hash function",
333            enabled: true,
334        });
335
336        self.register(AlgorithmMetadata {
337            algorithm: Algorithm::Sha3_384,
338            category: AlgorithmCategory::Hash,
339            security_level: 0,
340            name: "SHA3-384",
341            description: "SHA3-384 hash function",
342            enabled: true,
343        });
344
345        self.register(AlgorithmMetadata {
346            algorithm: Algorithm::Sha3_512,
347            category: AlgorithmCategory::Hash,
348            security_level: 0,
349            name: "SHA3-512",
350            description: "SHA3-512 hash function",
351            enabled: true,
352        });
353
354        // KMAC algorithms
355        self.register(AlgorithmMetadata {
356            algorithm: Algorithm::Kmac128,
357            category: AlgorithmCategory::Hash,
358            security_level: 0,
359            name: "KMAC128",
360            description: "KMAC128 keyed hash function",
361            enabled: true,
362        });
363
364        self.register(AlgorithmMetadata {
365            algorithm: Algorithm::Kmac256,
366            category: AlgorithmCategory::Hash,
367            security_level: 0,
368            name: "KMAC256",
369            description: "KMAC256 keyed hash function",
370            enabled: true,
371        });
372
373        // TupleHash algorithms
374        self.register(AlgorithmMetadata {
375            algorithm: Algorithm::TupleHash128,
376            category: AlgorithmCategory::Hash,
377            security_level: 0,
378            name: "TupleHash128",
379            description: "TupleHash128 tuple hashing",
380            enabled: true,
381        });
382
383        self.register(AlgorithmMetadata {
384            algorithm: Algorithm::TupleHash256,
385            category: AlgorithmCategory::Hash,
386            security_level: 0,
387            name: "TupleHash256",
388            description: "TupleHash256 tuple hashing",
389            enabled: true,
390        });
391
392        // ParallelHash algorithms
393        self.register(AlgorithmMetadata {
394            algorithm: Algorithm::ParallelHash128,
395            category: AlgorithmCategory::Hash,
396            security_level: 0,
397            name: "ParallelHash128",
398            description: "ParallelHash128 parallel hashing",
399            enabled: true,
400        });
401
402        self.register(AlgorithmMetadata {
403            algorithm: Algorithm::ParallelHash256,
404            category: AlgorithmCategory::Hash,
405            security_level: 0,
406            name: "ParallelHash256",
407            description: "ParallelHash256 parallel hashing",
408            enabled: true,
409        });
410
411        // Keccak algorithms
412        self.register(AlgorithmMetadata {
413            algorithm: Algorithm::Keccak224,
414            category: AlgorithmCategory::Hash,
415            security_level: 0,
416            name: "Keccak-224",
417            description: "Keccak-224 hash function",
418            enabled: true,
419        });
420
421        self.register(AlgorithmMetadata {
422            algorithm: Algorithm::Keccak256,
423            category: AlgorithmCategory::Hash,
424            security_level: 0,
425            name: "Keccak-256",
426            description: "Keccak-256 hash function",
427            enabled: true,
428        });
429
430        self.register(AlgorithmMetadata {
431            algorithm: Algorithm::Keccak384,
432            category: AlgorithmCategory::Hash,
433            security_level: 0,
434            name: "Keccak-384",
435            description: "Keccak-384 hash function",
436            enabled: true,
437        });
438
439        self.register(AlgorithmMetadata {
440            algorithm: Algorithm::Keccak512,
441            category: AlgorithmCategory::Hash,
442            security_level: 0,
443            name: "Keccak-512",
444            description: "Keccak-512 hash function",
445            enabled: true,
446        });
447
448        // RFC 9861 KangarooTwelve instances
449        self.register(AlgorithmMetadata {
450            algorithm: Algorithm::Kt128,
451            category: AlgorithmCategory::Hash,
452            security_level: 0,
453            name: "KT128",
454            description: "KangarooTwelve with TurboSHAKE128 (RFC 9861)",
455            enabled: true,
456        });
457
458        self.register(AlgorithmMetadata {
459            algorithm: Algorithm::Kt256,
460            category: AlgorithmCategory::Hash,
461            security_level: 0,
462            name: "KT256",
463            description: "KangarooTwelve with TurboSHAKE256 (RFC 9861)",
464            enabled: true,
465        });
466
467        // SHA-2 algorithms
468        self.register(AlgorithmMetadata {
469            algorithm: Algorithm::Sha224,
470            category: AlgorithmCategory::Hash,
471            security_level: 0,
472            name: "SHA-224",
473            description: "SHA-224 hash function",
474            enabled: true,
475        });
476
477        self.register(AlgorithmMetadata {
478            algorithm: Algorithm::Sha256,
479            category: AlgorithmCategory::Hash,
480            security_level: 0,
481            name: "SHA-256",
482            description: "SHA-256 hash function",
483            enabled: true,
484        });
485
486        self.register(AlgorithmMetadata {
487            algorithm: Algorithm::Sha384,
488            category: AlgorithmCategory::Hash,
489            security_level: 0,
490            name: "SHA-384",
491            description: "SHA-384 hash function",
492            enabled: true,
493        });
494
495        self.register(AlgorithmMetadata {
496            algorithm: Algorithm::Sha512,
497            category: AlgorithmCategory::Hash,
498            security_level: 0,
499            name: "SHA-512",
500            description: "SHA-512 hash function",
501            enabled: true,
502        });
503
504        self.register(AlgorithmMetadata {
505            algorithm: Algorithm::Sha512_224,
506            category: AlgorithmCategory::Hash,
507            security_level: 0,
508            name: "SHA-512/224",
509            description: "SHA-512/224 hash function (truncated)",
510            enabled: true,
511        });
512
513        self.register(AlgorithmMetadata {
514            algorithm: Algorithm::Sha512_256,
515            category: AlgorithmCategory::Hash,
516            security_level: 0,
517            name: "SHA-512/256",
518            description: "SHA-512/256 hash function (truncated)",
519            enabled: true,
520        });
521
522        // TurboSHAKE algorithms
523        self.register(AlgorithmMetadata {
524            algorithm: Algorithm::TurboShake128,
525            category: AlgorithmCategory::Hash,
526            security_level: 0,
527            name: "TurboSHAKE128",
528            description: "TurboSHAKE128 extendable-output function",
529            enabled: true,
530        });
531
532        self.register(AlgorithmMetadata {
533            algorithm: Algorithm::TurboShake256,
534            category: AlgorithmCategory::Hash,
535            security_level: 0,
536            name: "TurboSHAKE256",
537            description: "TurboSHAKE256 extendable-output function",
538            enabled: true,
539        });
540
541        // AEAD algorithms
542        self.register(AlgorithmMetadata {
543            algorithm: Algorithm::Saturnin,
544            category: AlgorithmCategory::Aead,
545            security_level: 1,
546            name: "Saturnin",
547            description: "Saturnin - Lightweight post-quantum symmetric algorithm suite for IoT and constrained devices",
548            enabled: true,
549        });
550
551        self.register(AlgorithmMetadata {
552            algorithm: Algorithm::Shake256Aead,
553            category: AlgorithmCategory::Aead,
554            security_level: 1,
555            name: "SHAKE256-AEAD",
556            description: "SHAKE256-based AEAD construction using post-quantum hash function",
557            enabled: true,
558        });
559
560        self.register(AlgorithmMetadata {
561            algorithm: Algorithm::DuplexSpongeAead,
562            category: AlgorithmCategory::Aead,
563            security_level: 4,
564            name: "Duplex-Sponge-AEAD",
565            description: "Keccak-f[1600] duplex-sponge authenticated encryption (SHA-3 family permutation)",
566            enabled: true,
567        });
568
569        self.register(AlgorithmMetadata {
570            algorithm: Algorithm::TweakAead,
571            category: AlgorithmCategory::Aead,
572            security_level: 4,
573            name: "Tweak-AEAD",
574            description: "Parallel tweakable-block CTR AEAD over Keccak-f[1600] with independent 32-byte blocks",
575            enabled: true,
576        });
577
578        self.register(AlgorithmMetadata {
579            algorithm: Algorithm::RomulusN,
580            category: AlgorithmCategory::Aead,
581            security_level: 1,
582            name: "Romulus-N",
583            description: "Romulus-N nonce-based AEAD (SKINNY-128-384+), LWC v1.3",
584            enabled: true,
585        });
586
587        self.register(AlgorithmMetadata {
588            algorithm: Algorithm::RomulusM,
589            category: AlgorithmCategory::Aead,
590            security_level: 1,
591            name: "Romulus-M",
592            description: "Romulus-M misuse-resistant AEAD (SKINNY-128-384+), LWC v1.3",
593            enabled: true,
594        });
595
596        self.register(AlgorithmMetadata {
597            algorithm: Algorithm::RoccaS,
598            category: AlgorithmCategory::Aead,
599            security_level: 1,
600            name: "Rocca-S",
601            description: "Rocca-S high-throughput AES-round AEAD (IETF draft-nakano-rocca-s); 256-bit key/tag, 128-bit nonce",
602            enabled: true,
603        });
604
605        // Privacy-oriented protocol identifiers (implementations: lib-q-lattice-zkp, lib-q-ring-sig)
606        self.register(AlgorithmMetadata {
607            algorithm: Algorithm::LatticeRingSignature,
608            category: AlgorithmCategory::PrivacyProtocol,
609            security_level: 3,
610            name: "Lattice federation ring signature",
611            description: "Federation ring-style opening proofs over Ajtai commitments (lib-q-ring-sig)",
612            enabled: true,
613        });
614        self.register(AlgorithmMetadata {
615            algorithm: Algorithm::LatticeBlindIssuance,
616            category: AlgorithmCategory::PrivacyProtocol,
617            security_level: 3,
618            name: "Lattice blind issuance",
619            description: "CRS blind issuance plumbing and issuer attestation (lib-q-lattice-zkp/blind)",
620            enabled: true,
621        });
622        self.register(AlgorithmMetadata {
623            algorithm: Algorithm::LatticeAnonymousToken,
624            category: AlgorithmCategory::PrivacyProtocol,
625            security_level: 3,
626            name: "Lattice anonymous token",
627            description: "Commitment-backed anonymous token and spending proof (lib-q-lattice-zkp/token)",
628            enabled: true,
629        });
630        self.register(AlgorithmMetadata {
631            algorithm: Algorithm::LatticeNullifierRegistry,
632            category: AlgorithmCategory::PrivacyProtocol,
633            security_level: 3,
634            name: "Lattice nullifier registry",
635            description: "SHAKE256 nullifier binding for Sybil-evidence style proofs (lib-q-lattice-zkp/sigma/uniqueness)",
636            enabled: true,
637        });
638        self.register(AlgorithmMetadata {
639            algorithm: Algorithm::LatticeWitnessNullifier,
640            category: AlgorithmCategory::PrivacyProtocol,
641            security_level: 3,
642            name: "Lattice witness nullifier",
643            description: "Witness-derived SHAKE256 nullifier and opening binding (lib-q-lattice-zkp/sigma/uniqueness)",
644            enabled: true,
645        });
646        self.register(AlgorithmMetadata {
647            algorithm: Algorithm::LatticeDualRingLb,
648            category: AlgorithmCategory::PrivacyProtocol,
649            security_level: 3,
650            name: "Lattice DualRing-LB pilot",
651            description: "DualRing-LB (CCS 2021 Alg. 3) aggregated opening verify over Ajtai ring (lib-q-ring-sig/dualring_lb)",
652            enabled: true,
653        });
654        self.register(AlgorithmMetadata {
655            algorithm: Algorithm::MixOnionRouting,
656            category: AlgorithmCategory::PrivacyProtocol,
657            security_level: 3,
658            name: "Mix-layer onion routing",
659            description: "ML-KEM-768 layered encapsulation with Saturnin AEAD per hop",
660            enabled: true,
661        });
662        self.register(AlgorithmMetadata {
663            algorithm: Algorithm::SessionResumptionBinding,
664            category: AlgorithmCategory::PrivacyProtocol,
665            security_level: 3,
666            name: "Session resumption binding",
667            description: "SHAKE256 session token and stateless retry-cookie derivation",
668            enabled: true,
669        });
670    }
671
672    /// Register an algorithm
673    fn register(&mut self, metadata: AlgorithmMetadata) {
674        self.algorithms.insert(metadata.algorithm, metadata);
675    }
676
677    /// Get all supported algorithms
678    #[cfg(feature = "alloc")]
679    pub fn supported_algorithms(&self) -> Vec<Algorithm> {
680        self.algorithms
681            .values()
682            .filter(|meta| meta.enabled)
683            .map(|meta| meta.algorithm)
684            .collect()
685    }
686
687    #[cfg(not(feature = "alloc"))]
688    pub fn supported_algorithms(&self) -> &'static [Algorithm] {
689        // In no_std mode, return a static slice of enabled algorithms
690        static ALGORITHMS: &[Algorithm] = &[
691            Algorithm::MlKem512,
692            Algorithm::MlKem768,
693            Algorithm::MlKem1024,
694            Algorithm::MlDsa44,
695            Algorithm::MlDsa65,
696            Algorithm::MlDsa87,
697            Algorithm::FnDsa,
698            Algorithm::FnDsa512,
699            Algorithm::FnDsa1024,
700        ];
701        ALGORITHMS
702    }
703
704    /// Get algorithms by category
705    #[cfg(feature = "alloc")]
706    pub fn algorithms_by_category(&self, category: AlgorithmCategory) -> Vec<Algorithm> {
707        self.algorithms
708            .values()
709            .filter(|meta| meta.enabled && meta.category == category)
710            .map(|meta| meta.algorithm)
711            .collect()
712    }
713
714    #[cfg(not(feature = "alloc"))]
715    pub fn algorithms_by_category(&self, category: AlgorithmCategory) -> &'static [Algorithm] {
716        // In no_std mode, return a static slice based on category
717        match category {
718            AlgorithmCategory::Kem => &[
719                Algorithm::MlKem512,
720                Algorithm::MlKem768,
721                Algorithm::MlKem1024,
722            ],
723            AlgorithmCategory::Signature => &[
724                Algorithm::MlDsa44,
725                Algorithm::MlDsa65,
726                Algorithm::MlDsa87,
727                Algorithm::FnDsa,
728                Algorithm::FnDsa512,
729                Algorithm::FnDsa1024,
730            ],
731            AlgorithmCategory::Hash => &[
732                Algorithm::Sha224,
733                Algorithm::Sha256,
734                Algorithm::Sha384,
735                Algorithm::Sha512,
736                Algorithm::Sha512_224,
737                Algorithm::Sha512_256,
738            ],
739            AlgorithmCategory::Aead => &[
740                Algorithm::Saturnin,
741                Algorithm::Shake256Aead,
742                Algorithm::DuplexSpongeAead,
743                Algorithm::TweakAead,
744                Algorithm::RomulusN,
745                Algorithm::RomulusM,
746            ],
747            AlgorithmCategory::PrivacyProtocol => &[
748                Algorithm::LatticeRingSignature,
749                Algorithm::LatticeBlindIssuance,
750                Algorithm::LatticeAnonymousToken,
751                Algorithm::LatticeNullifierRegistry,
752                Algorithm::LatticeWitnessNullifier,
753                Algorithm::LatticeDualRingLb,
754                Algorithm::MixOnionRouting,
755                Algorithm::SessionResumptionBinding,
756            ],
757        }
758    }
759
760    /// Get algorithms by security level
761    #[cfg(feature = "alloc")]
762    pub fn algorithms_by_security_level(&self, level: u32) -> Vec<Algorithm> {
763        self.algorithms
764            .values()
765            .filter(|meta| meta.enabled && meta.security_level == level)
766            .map(|meta| meta.algorithm)
767            .collect()
768    }
769
770    #[cfg(not(feature = "alloc"))]
771    pub fn algorithms_by_security_level(&self, level: u32) -> &'static [Algorithm] {
772        security_level_algorithms(level)
773    }
774
775    /// Get algorithm metadata
776    pub fn get_metadata(&self, algorithm: &Algorithm) -> Option<&AlgorithmMetadata> {
777        self.algorithms.get(algorithm)
778    }
779
780    /// Check if algorithm is enabled
781    pub fn is_enabled(&self, algorithm: &Algorithm) -> bool {
782        self.algorithms
783            .get(algorithm)
784            .map(|meta| meta.enabled)
785            .unwrap_or(false)
786    }
787
788    /// Enable/disable an algorithm
789    pub fn set_enabled(&mut self, algorithm: Algorithm, enabled: bool) -> Result<()> {
790        if let Some(metadata) = self.algorithms.get_mut(&algorithm) {
791            metadata.enabled = enabled;
792            Ok(())
793        } else {
794            #[cfg(feature = "alloc")]
795            {
796                Err(crate::Error::UnsupportedAlgorithm {
797                    algorithm: "unsupported algorithm".to_string(),
798                })
799            }
800            #[cfg(not(feature = "alloc"))]
801            {
802                Err(crate::Error::UnsupportedAlgorithm {
803                    algorithm: "unsupported algorithm",
804                })
805            }
806        }
807    }
808}
809
810#[cfg(any(feature = "std", feature = "alloc"))]
811impl Default for AlgorithmRegistry {
812    fn default() -> Self {
813        Self::new()
814    }
815}
816
817// Global algorithm registry instance
818// Note: AlgorithmRegistry requires alloc (uses HashMap/BTreeMap)
819#[cfg(all(feature = "alloc", feature = "std"))]
820static REGISTRY: once_cell::sync::Lazy<AlgorithmRegistry> =
821    once_cell::sync::Lazy::new(AlgorithmRegistry::new);
822
823#[cfg(all(feature = "alloc", not(feature = "std"), feature = "spin"))]
824static REGISTRY: spin::Once<AlgorithmRegistry> = spin::Once::new();
825
826/// Get the global algorithm registry
827/// Requires alloc feature (registry uses HashMap/BTreeMap internally)
828#[cfg(all(feature = "alloc", feature = "std"))]
829pub fn registry() -> &'static AlgorithmRegistry {
830    &REGISTRY
831}
832
833#[cfg(all(feature = "alloc", not(feature = "std"), feature = "spin"))]
834pub fn registry() -> &'static AlgorithmRegistry {
835    REGISTRY.call_once(AlgorithmRegistry::new)
836}
837
838// registry() is not available in no_alloc mode
839// Use supported_algorithms() etc. which return static slices directly
840
841/// Get all supported algorithms
842#[cfg(all(feature = "alloc", any(feature = "std", feature = "spin")))]
843pub fn supported_algorithms() -> Vec<Algorithm> {
844    registry().supported_algorithms()
845}
846
847#[cfg(all(feature = "alloc", not(any(feature = "std", feature = "spin"))))]
848pub fn supported_algorithms() -> Vec<Algorithm> {
849    AlgorithmRegistry::new().supported_algorithms()
850}
851
852#[cfg(not(feature = "alloc"))]
853pub fn supported_algorithms() -> &'static [Algorithm] {
854    // In no_alloc mode, return static slice directly without using registry
855    static ALGORITHMS: &[Algorithm] = &[
856        Algorithm::MlKem512,
857        Algorithm::MlKem768,
858        Algorithm::MlKem1024,
859        Algorithm::MlDsa44,
860        Algorithm::MlDsa65,
861        Algorithm::MlDsa87,
862        Algorithm::FnDsa,
863        Algorithm::FnDsa512,
864        Algorithm::FnDsa1024,
865    ];
866    ALGORITHMS
867}
868
869/// Get algorithms by category
870#[cfg(all(feature = "alloc", any(feature = "std", feature = "spin")))]
871pub fn algorithms_by_category(category: AlgorithmCategory) -> Vec<Algorithm> {
872    registry().algorithms_by_category(category)
873}
874
875#[cfg(all(feature = "alloc", not(any(feature = "std", feature = "spin"))))]
876pub fn algorithms_by_category(category: AlgorithmCategory) -> Vec<Algorithm> {
877    AlgorithmRegistry::new().algorithms_by_category(category)
878}
879
880#[cfg(not(feature = "alloc"))]
881pub fn algorithms_by_category(category: AlgorithmCategory) -> &'static [Algorithm] {
882    // In no_alloc mode, return static slice directly without using registry
883    match category {
884        AlgorithmCategory::Kem => &[
885            Algorithm::MlKem512,
886            Algorithm::MlKem768,
887            Algorithm::MlKem1024,
888        ],
889        AlgorithmCategory::Signature => &[
890            Algorithm::MlDsa44,
891            Algorithm::MlDsa65,
892            Algorithm::MlDsa87,
893            Algorithm::FnDsa,
894            Algorithm::FnDsa512,
895            Algorithm::FnDsa1024,
896        ],
897        AlgorithmCategory::Hash => &[
898            Algorithm::Sha224,
899            Algorithm::Sha256,
900            Algorithm::Sha384,
901            Algorithm::Sha512,
902        ],
903        AlgorithmCategory::Aead => &[
904            Algorithm::Saturnin,
905            Algorithm::Shake256Aead,
906            Algorithm::DuplexSpongeAead,
907            Algorithm::TweakAead,
908            Algorithm::RomulusN,
909            Algorithm::RomulusM,
910        ],
911        AlgorithmCategory::PrivacyProtocol => &[
912            Algorithm::LatticeRingSignature,
913            Algorithm::LatticeBlindIssuance,
914            Algorithm::LatticeAnonymousToken,
915            Algorithm::LatticeNullifierRegistry,
916            Algorithm::LatticeWitnessNullifier,
917            Algorithm::LatticeDualRingLb,
918            Algorithm::MixOnionRouting,
919            Algorithm::SessionResumptionBinding,
920        ],
921    }
922}
923
924/// Get algorithms by security level
925#[cfg(all(feature = "alloc", any(feature = "std", feature = "spin")))]
926pub fn algorithms_by_security_level(level: u32) -> Vec<Algorithm> {
927    registry().algorithms_by_security_level(level)
928}
929
930#[cfg(all(feature = "alloc", not(any(feature = "std", feature = "spin"))))]
931pub fn algorithms_by_security_level(level: u32) -> Vec<Algorithm> {
932    AlgorithmRegistry::new().algorithms_by_security_level(level)
933}
934
935// Single source of truth for the no_std (`not(feature = "alloc")`) security-level lookup.
936//
937// These lists exist because without `alloc` the lookup cannot build a `Vec`, so it has to return
938// a `&'static [Algorithm]`. They were previously written out twice, by hand, in the two
939// `algorithms_by_security_level` bodies below, and both had drifted badly from the metadata
940// table: level 5 listed 3 algorithms where the table says 9 (every Classic McEliece entry, HQC-256
941// and both SLH-DSA-256f entries were missing), level 1 listed 8 of 13, level 3 listed 10 of 14.
942//
943// That is the same failure as the original wrong-category defect and in the same unsafe
944// direction: a no_std caller asking for the Category 5 algorithms was silently offered a subset.
945// Both bodies now return these consts, and `no_std_security_level_lists_match_the_registry`
946// pins them against the metadata table so they cannot drift again.
947// Compiled for the no_std path that uses them, and for tests, where
948// `no_std_security_level_lists_match_the_registry` compares them against the metadata table.
949// Without the `test` arm the guard could not see them from a std test run; without the
950// `not(alloc)` arm the no_std lookup would have nothing to return.
951#[cfg(any(not(feature = "alloc"), test))]
952const SECURITY_LEVEL_1_ALGORITHMS: &[Algorithm] = &[
953    Algorithm::MlKem512,
954    Algorithm::CbKem348864,
955    Algorithm::Hqc128,
956    Algorithm::MlDsa44,
957    Algorithm::FnDsa,
958    Algorithm::FnDsa512,
959    Algorithm::SlhDsaSha256128fRobust,
960    Algorithm::SlhDsaShake256128fRobust,
961    Algorithm::Saturnin,
962    Algorithm::Shake256Aead,
963    Algorithm::RomulusN,
964    Algorithm::RomulusM,
965    Algorithm::RoccaS,
966];
967
968#[cfg(any(not(feature = "alloc"), test))]
969const SECURITY_LEVEL_3_ALGORITHMS: &[Algorithm] = &[
970    Algorithm::MlKem768,
971    Algorithm::CbKem460896,
972    Algorithm::Hqc192,
973    Algorithm::MlDsa65,
974    Algorithm::SlhDsaSha256192fRobust,
975    Algorithm::SlhDsaShake256192fRobust,
976    Algorithm::LatticeRingSignature,
977    Algorithm::LatticeBlindIssuance,
978    Algorithm::LatticeAnonymousToken,
979    Algorithm::LatticeNullifierRegistry,
980    Algorithm::LatticeWitnessNullifier,
981    Algorithm::LatticeDualRingLb,
982    Algorithm::MixOnionRouting,
983    Algorithm::SessionResumptionBinding,
984];
985
986// The AEAD entries registered at 4. NIST PQC categories do not apply to AEADs, and the other
987// AEADs in the table sit at 1, so this value is questionable on its own terms -- but that is a
988// separate question from the drift being fixed here, and these two are reproduced at whatever
989// the metadata table says rather than silently renumbered.
990#[cfg(any(not(feature = "alloc"), test))]
991const SECURITY_LEVEL_4_ALGORITHMS: &[Algorithm] =
992    &[Algorithm::DuplexSpongeAead, Algorithm::TweakAead];
993
994#[cfg(any(not(feature = "alloc"), test))]
995const SECURITY_LEVEL_5_ALGORITHMS: &[Algorithm] = &[
996    Algorithm::MlKem1024,
997    Algorithm::CbKem6688128,
998    Algorithm::CbKem6960119,
999    Algorithm::CbKem8192128,
1000    Algorithm::Hqc256,
1001    Algorithm::MlDsa87,
1002    Algorithm::FnDsa1024,
1003    Algorithm::SlhDsaSha256256fRobust,
1004    Algorithm::SlhDsaShake256256fRobust,
1005];
1006
1007#[cfg(any(not(feature = "alloc"), test))]
1008fn security_level_algorithms(level: u32) -> &'static [Algorithm] {
1009    match level {
1010        1 => SECURITY_LEVEL_1_ALGORITHMS,
1011        3 => SECURITY_LEVEL_3_ALGORITHMS,
1012        4 => SECURITY_LEVEL_4_ALGORITHMS,
1013        5 => SECURITY_LEVEL_5_ALGORITHMS,
1014        _ => &[],
1015    }
1016}
1017
1018#[cfg(not(feature = "alloc"))]
1019pub fn algorithms_by_security_level(level: u32) -> &'static [Algorithm] {
1020    security_level_algorithms(level)
1021}
1022
1023#[cfg(test)]
1024mod tests {
1025    use super::*;
1026
1027    #[test]
1028    fn test_algorithm_registry() {
1029        let registry = AlgorithmRegistry::new();
1030
1031        // Test that we have algorithms
1032        let algorithms = registry.supported_algorithms();
1033        assert!(!algorithms.is_empty());
1034
1035        // Test category filtering
1036        let kem_algorithms = registry.algorithms_by_category(AlgorithmCategory::Kem);
1037        assert!(!kem_algorithms.is_empty());
1038
1039        // Test security level filtering
1040        let level1_algorithms = registry.algorithms_by_security_level(1);
1041        assert!(!level1_algorithms.is_empty());
1042
1043        // Test metadata retrieval
1044        let metadata = registry.get_metadata(&Algorithm::MlKem512);
1045        assert!(metadata.is_some());
1046        assert_eq!(metadata.unwrap().name, "ML-KEM-512");
1047    }
1048
1049    #[test]
1050    fn test_global_registry() {
1051        let algorithms = supported_algorithms();
1052        assert!(!algorithms.is_empty());
1053
1054        let kem_algorithms = algorithms_by_category(AlgorithmCategory::Kem);
1055        assert!(!kem_algorithms.is_empty());
1056    }
1057
1058    /// Regression test for card t_e3457ac8: `security_level` must track each
1059    /// algorithm's own NIST PQC security category, not an ad-hoc ordinal that
1060    /// happens to coincide with it for most entries. Every value below is
1061    /// sourced from the algorithm's own specification:
1062    ///
1063    /// - ML-KEM-512/768/1024: FIPS 203 (Categories 1/3/5)
1064    /// - ML-DSA-65/87: FIPS 204 (Categories 3/5) — ML-DSA-44 is intentionally
1065    ///   excluded: FIPS 204 claims Category 2 for it but permits a documented
1066    ///   fallback to Category 1 with a weaker RBG, so the correct registered
1067    ///   value is genuinely ambiguous without knowing which RBG posture this
1068    ///   crate assumes (see card t_e3457ac8 follow-up notes).
1069    /// - FN-DSA-512/1024 (Falcon): Categories 1/5
1070    /// - SLH-DSA SHA256/SHAKE256 -128f/-192f/-256f: FIPS 205 Table (Categories
1071    ///   1/3/5; confirmed via reference/fips205/NIST.FIPS.205.pdf sections
1072    ///   11.2.1 "SLH-DSA Using SHA2 for Security Category 1" and 11.2.2
1073    ///   "...for Security Categories 3 and 5")
1074    /// - CB-KEM 348864/460896/6688128/6960119/8192128: Classic McEliece spec
1075    ///   (Categories 1/3/5/5/5 — all three "128"-suffixed parameter sets are
1076    ///   Category 5)
1077    /// - HQC-128/192/256: HQC v5.0.0 spec Table 5 "Parameter sets for HQC"
1078    ///   (reference/hqc-avx2/hqc_specifications_2025_08_22.pdf) — HQC-1/3/5
1079    ///   map directly to NIST-1/3/5
1080    ///
1081    /// A future entry registered at the wrong level will fail this test as
1082    /// soon as it's added to the table below alongside its registration.
1083    #[test]
1084    fn test_security_levels_match_documented_nist_category() {
1085        let registry = AlgorithmRegistry::new();
1086
1087        let expected: &[(Algorithm, u32)] = &[
1088            (Algorithm::MlKem512, 1),
1089            (Algorithm::MlKem768, 3),
1090            (Algorithm::MlKem1024, 5),
1091            (Algorithm::CbKem348864, 1),
1092            (Algorithm::CbKem460896, 3),
1093            (Algorithm::CbKem6688128, 5),
1094            (Algorithm::CbKem6960119, 5),
1095            (Algorithm::CbKem8192128, 5),
1096            (Algorithm::Hqc128, 1),
1097            (Algorithm::Hqc192, 3),
1098            (Algorithm::Hqc256, 5),
1099            (Algorithm::MlDsa65, 3),
1100            (Algorithm::MlDsa87, 5),
1101            (Algorithm::FnDsa, 1),
1102            (Algorithm::FnDsa512, 1),
1103            (Algorithm::FnDsa1024, 5),
1104            (Algorithm::SlhDsaSha256128fRobust, 1),
1105            (Algorithm::SlhDsaSha256192fRobust, 3),
1106            (Algorithm::SlhDsaSha256256fRobust, 5),
1107            (Algorithm::SlhDsaShake256128fRobust, 1),
1108            (Algorithm::SlhDsaShake256192fRobust, 3),
1109            (Algorithm::SlhDsaShake256256fRobust, 5),
1110        ];
1111
1112        for (algorithm, expected_level) in expected {
1113            let metadata = registry
1114                .get_metadata(algorithm)
1115                .unwrap_or_else(|| panic!("{algorithm:?} missing from registry"));
1116            assert_eq!(
1117                metadata.security_level, *expected_level,
1118                "{algorithm:?} is registered at security_level {}, but its spec claims NIST \
1119                 Category {expected_level}",
1120                metadata.security_level
1121            );
1122        }
1123    }
1124
1125    /// The security level is stored in more than one place, and that duplication -- not the
1126    /// arithmetic -- is what let the wrong values survive: this registry's table, its three
1127    /// hand-written no_std static-slice copies, and an entirely separate hardcoded `match` in
1128    /// `lib_q_types::Algorithm::security_level`. Fixing one copy left the others disagreeing,
1129    /// silently, because nothing compared them.
1130    ///
1131    /// The test above pins the registry against the specifications. This one pins the copies
1132    /// against EACH OTHER, so a future edit to one of them cannot drift. It walks whatever the
1133    /// registry reports as supported rather than a hand-written list, so an algorithm added
1134    /// later is covered without anyone remembering to extend this test.
1135    /// The no_std lookup returns hand-written static lists because it cannot build a `Vec`.
1136    /// Nothing compared them to the metadata table, and they had drifted: level 5 listed 3
1137    /// algorithms where the table says 9, omitting every Classic McEliece entry, HQC-256 and both
1138    /// SLH-DSA-256f entries. A no_std caller asking for the strongest algorithms was handed a
1139    /// subset, silently, which is the same failure the wrong-category defect produced.
1140    ///
1141    /// This walks the metadata table rather than a hand-written expectation, so an algorithm
1142    /// added later is covered without anyone remembering to extend the lists or this test.
1143    #[test]
1144    fn no_std_security_level_lists_match_the_registry() {
1145        let registry = AlgorithmRegistry::new();
1146
1147        let mut levels_checked = 0usize;
1148        for level in [1u32, 3, 4, 5] {
1149            let mut expected: Vec<Algorithm> = registry
1150                .algorithms
1151                .values()
1152                .filter(|meta| meta.enabled && meta.security_level == level)
1153                .map(|meta| meta.algorithm)
1154                .collect();
1155            let mut actual: Vec<Algorithm> = security_level_algorithms(level).to_vec();
1156
1157            assert!(
1158                !expected.is_empty(),
1159                "level {level}: the metadata table has no enabled entry, so this comparison                  would pass while checking nothing"
1160            );
1161
1162            expected.sort_by_key(|a| format!("{a:?}"));
1163            actual.sort_by_key(|a| format!("{a:?}"));
1164            assert_eq!(
1165                actual, expected,
1166                "level {level}: the no_std static list disagrees with the metadata table.                  These are duplicate copies of one table and must be changed together."
1167            );
1168            levels_checked += 1;
1169        }
1170
1171        assert_eq!(
1172            levels_checked, 4,
1173            "expected to compare all four populated levels"
1174        );
1175
1176        // A level the table does not populate must yield nothing rather than falling through to
1177        // some other level's list.
1178        assert!(security_level_algorithms(2).is_empty());
1179        assert!(security_level_algorithms(99).is_empty());
1180    }
1181
1182    #[test]
1183    fn registry_and_types_agree_on_every_security_level() {
1184        let registry = AlgorithmRegistry::new();
1185        let algorithms = registry.supported_algorithms();
1186        assert!(
1187            !algorithms.is_empty(),
1188            "supported_algorithms() is empty, so this test would pass while checking nothing"
1189        );
1190
1191        let mut checked = 0usize;
1192        for algorithm in algorithms.iter() {
1193            let Some(metadata) = registry.get_metadata(algorithm) else {
1194                continue;
1195            };
1196            // Hashes and other entries that carry no category are registered at 0; skip them
1197            // rather than assert a level they do not claim.
1198            if metadata.security_level == 0 {
1199                continue;
1200            }
1201            assert_eq!(
1202                metadata.security_level,
1203                algorithm.security_level(),
1204                "{algorithm:?}: the registry says security_level {}, but \
1205                 lib_q_types::Algorithm::security_level() says {}. These are duplicate copies of \
1206                 one table and must be changed together.",
1207                metadata.security_level,
1208                algorithm.security_level()
1209            );
1210            checked += 1;
1211        }
1212
1213        assert!(
1214            checked > 0,
1215            "no algorithm carried a non-zero security level, so nothing was actually compared"
1216        );
1217    }
1218}