Skip to main content

pqc_binary_format/
algorithm.rs

1//! Algorithm identifiers for supported post-quantum cryptographic algorithms.
2
3use serde::{Deserialize, Serialize};
4
5/// Algorithm identifier type
6pub type AlgorithmId = u16;
7
8/// Supported cryptographic algorithms with unique identifiers
9///
10/// Each algorithm has a unique 16-bit identifier used in the binary format.
11/// Identifiers are grouped by algorithm family for easy categorization.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13#[repr(u16)]
14pub enum Algorithm {
15    // Classical algorithms (0x0050-0x00FF)
16    /// Classical cryptography: X25519 + Ed25519 + AES-256-GCM
17    Classical = 0x0050,
18    /// Password-based classical encryption
19    PasswordClassical = 0x0051,
20
21    // Hybrid algorithms (0x0100-0x01FF)
22    /// Hybrid: ML-KEM-1024 + X25519 + ML-DSA-87 + Ed25519 + AES-256-GCM
23    Hybrid = 0x0100,
24
25    // Pure post-quantum algorithms (0x0200-0x02FF)
26    /// Post-quantum: ML-KEM-1024 + ML-DSA-87 + AES-256-GCM
27    PostQuantum = 0x0200,
28    /// Multi-algorithm runtime selection
29    MultiAlgorithm = 0x0201,
30    /// ML-KEM-1024 with AES-256-GCM
31    MlKem1024 = 0x0202,
32    /// Multi-KEM dual layer
33    MultiKem = 0x0203,
34    /// Multi-KEM triple layer
35    MultiKemTriple = 0x0204,
36    /// Quad-layer redundant security
37    QuadLayer = 0x0205,
38    /// Lattice-Code hybrid stack
39    LatticeCodeHybrid = 0x0206,
40    /// PQ3-Stack with forward secrecy
41    Pq3Stack = 0x0207,
42
43    // Max Secure series (0x0300-0x03FF)
44    /// Max Secure: PQ Lightweight
45    MaxSecureLightweight = 0x0300,
46    /// Max Secure: Pure PQ
47    MaxSecurePurePq = 0x0301,
48    /// Max Secure: Hybrid Transition
49    MaxSecureHybrid = 0x0302,
50    /// Max Secure: Stateless
51    MaxSecureStateless = 0x0303,
52    /// Max Secure: Crypto-Agile
53    MaxSecureCryptoAgile = 0x0304,
54    /// Max Secure: PQC + Zero-Knowledge
55    MaxSecurePqcZk = 0x0305,
56    /// Max Secure: Hybrid Transition
57    MaxSecureHybridTransition = 0x0306,
58
59    // FN-DSA series (Falcon-based signatures) (0x0400-0x04FF)
60    /// FN-DSA 512: Compact
61    FnDsa512Compact = 0x0400,
62    /// FN-DSA 1024: High-Security
63    FnDsa1024Security = 0x0401,
64    /// FN-DSA: Floating-Point Hardened
65    FnDsaFpHardened = 0x0402,
66    /// FN-DSA: Dual Signature
67    FnDsaDualSignature = 0x0403,
68    /// FN-DSA: Transition Stack
69    FnDsaTransition = 0x0404,
70    /// FN-DSA + Zero-Knowledge Stack
71    FnDsaZk = 0x0405,
72    /// FN-DSA + ZK Stack Enhanced
73    FnDsaZkStack = 0x0406,
74    /// FN-DSA: Transition Stack Enhanced
75    FnDsaTransitionStack = 0x0407,
76
77    // Experimental algorithms (0x0500-0x05FF)
78    /// Quantum-Inspired Lattice Fusion
79    QuantumLatticeFusion = 0x0500,
80    /// Post-ZK Homomorphic with LFHE 2023
81    PostZkHomomorphic = 0x0501,
82    /// Quantum-Resistant Consensus
83    QuantumResistantConsensus = 0x0502,
84    /// Entropy-Orchestrated PQ Stack
85    EntropyOrchestrated = 0x0503,
86    /// Lattice-Code Hybrid FN
87    LatticeCodeHybridFn = 0x0504,
88    /// AI-Synthesized Crypto-Agile
89    AiSynthesizedCryptoAgile = 0x0505,
90    /// Experimental Engine (generic)
91    Experimental = 0x0506,
92
93    // HQC Code-Based series (NIST 2025 Backup KEM) (0x0600-0x06FF)
94    /// HQC-128 (NIST Level 1, 128-bit security)
95    Hqc128 = 0x0600,
96    /// HQC-192 (NIST Level 3, 192-bit security)
97    Hqc192 = 0x0601,
98    /// HQC-256 (NIST Level 5, 256-bit security)
99    Hqc256 = 0x0602,
100
101    // NIST ML-KEM variants (FIPS 203) (0x0700-0x07FF)
102    /// ML-KEM-512 (NIST Level 1, 128-bit security)
103    MlKem512 = 0x0700,
104    /// ML-KEM-768 (NIST Level 3, 192-bit security)
105    MlKem768 = 0x0701,
106
107    // NIST ML-DSA variants (FIPS 204) (0x0800-0x08FF)
108    /// ML-DSA-44 (NIST Level 2, 128-bit security)
109    MlDsa44 = 0x0800,
110    /// ML-DSA-65 (NIST Level 3, 192-bit security)
111    MlDsa65 = 0x0801,
112    /// ML-DSA-87 (NIST Level 5, 256-bit security)
113    MlDsa87 = 0x0802,
114
115    // NIST SLH-DSA variants (FIPS 205) (0x0900-0x09FF)
116    /// SLH-DSA-SHA2-128s (NIST Level 1, small signatures)
117    SlhDsaSha2_128s = 0x0900,
118    /// SLH-DSA-SHA2-128f (NIST Level 1, fast signatures)
119    SlhDsaSha2_128f = 0x0901,
120    /// SLH-DSA-SHA2-192s (NIST Level 3, small signatures)
121    SlhDsaSha2_192s = 0x0902,
122    /// SLH-DSA-SHA2-192f (NIST Level 3, fast signatures)
123    SlhDsaSha2_192f = 0x0903,
124    /// SLH-DSA-SHA2-256s (NIST Level 5, small signatures)
125    SlhDsaSha2_256s = 0x0904,
126    /// SLH-DSA-SHA2-256f (NIST Level 5, fast signatures)
127    SlhDsaSha2_256f = 0x0905,
128}
129
130impl Algorithm {
131    /// Convert u16 identifier to Algorithm enum
132    ///
133    /// # Example
134    ///
135    /// ```
136    /// use pqc_binary_format::Algorithm;
137    ///
138    /// let algo = Algorithm::from_id(0x0100).unwrap();
139    /// assert_eq!(algo, Algorithm::Hybrid);
140    /// ```
141    #[must_use]
142    pub fn from_id(id: u16) -> Option<Self> {
143        match id {
144            0x0050 => Some(Self::Classical),
145            0x0051 => Some(Self::PasswordClassical),
146            0x0100 => Some(Self::Hybrid),
147            0x0200 => Some(Self::PostQuantum),
148            0x0201 => Some(Self::MultiAlgorithm),
149            0x0202 => Some(Self::MlKem1024),
150            0x0203 => Some(Self::MultiKem),
151            0x0204 => Some(Self::MultiKemTriple),
152            0x0205 => Some(Self::QuadLayer),
153            0x0206 => Some(Self::LatticeCodeHybrid),
154            0x0207 => Some(Self::Pq3Stack),
155            0x0300 => Some(Self::MaxSecureLightweight),
156            0x0301 => Some(Self::MaxSecurePurePq),
157            0x0302 => Some(Self::MaxSecureHybrid),
158            0x0303 => Some(Self::MaxSecureStateless),
159            0x0304 => Some(Self::MaxSecureCryptoAgile),
160            0x0305 => Some(Self::MaxSecurePqcZk),
161            0x0306 => Some(Self::MaxSecureHybridTransition),
162            0x0400 => Some(Self::FnDsa512Compact),
163            0x0401 => Some(Self::FnDsa1024Security),
164            0x0402 => Some(Self::FnDsaFpHardened),
165            0x0403 => Some(Self::FnDsaDualSignature),
166            0x0404 => Some(Self::FnDsaTransition),
167            0x0405 => Some(Self::FnDsaZk),
168            0x0406 => Some(Self::FnDsaZkStack),
169            0x0407 => Some(Self::FnDsaTransitionStack),
170            0x0500 => Some(Self::QuantumLatticeFusion),
171            0x0501 => Some(Self::PostZkHomomorphic),
172            0x0502 => Some(Self::QuantumResistantConsensus),
173            0x0503 => Some(Self::EntropyOrchestrated),
174            0x0504 => Some(Self::LatticeCodeHybridFn),
175            0x0505 => Some(Self::AiSynthesizedCryptoAgile),
176            0x0506 => Some(Self::Experimental),
177            0x0600 => Some(Self::Hqc128),
178            0x0601 => Some(Self::Hqc192),
179            0x0602 => Some(Self::Hqc256),
180            0x0700 => Some(Self::MlKem512),
181            0x0701 => Some(Self::MlKem768),
182            0x0800 => Some(Self::MlDsa44),
183            0x0801 => Some(Self::MlDsa65),
184            0x0802 => Some(Self::MlDsa87),
185            0x0900 => Some(Self::SlhDsaSha2_128s),
186            0x0901 => Some(Self::SlhDsaSha2_128f),
187            0x0902 => Some(Self::SlhDsaSha2_192s),
188            0x0903 => Some(Self::SlhDsaSha2_192f),
189            0x0904 => Some(Self::SlhDsaSha2_256s),
190            0x0905 => Some(Self::SlhDsaSha2_256f),
191            _ => None,
192        }
193    }
194
195    /// Resolve an algorithm from its name.
196    ///
197    /// Accepts the canonical kebab-case names used by the PQCrypta API and web
198    /// UI (e.g. `"ml-kem-1024"`, `"max-secure-pure-pq"`) as well as compact
199    /// aliases with separators stripped (e.g. `"mlkem1024"`). Matching is
200    /// case-insensitive.
201    ///
202    /// # Example
203    ///
204    /// ```
205    /// use pqc_binary_format::Algorithm;
206    ///
207    /// assert_eq!(Algorithm::from_name("ml-kem-1024"), Some(Algorithm::MlKem1024));
208    /// assert_eq!(Algorithm::from_name("Max-Secure-Pure-PQ"), Some(Algorithm::MaxSecurePurePq));
209    /// assert_eq!(Algorithm::from_name("nope"), None);
210    /// ```
211    #[must_use]
212    pub fn from_name(name: &str) -> Option<Self> {
213        let lower = name.to_lowercase();
214        // Compact form: strip separators so "ml-kem-1024" == "mlkem1024".
215        let compact: String = lower.chars().filter(|c| c.is_alphanumeric()).collect();
216        Self::all().into_iter().find(|algo| {
217            let canon = algo.canonical_name();
218            let canon_compact: String = canon.chars().filter(char::is_ascii_alphanumeric).collect();
219            lower == canon || compact == canon_compact
220        })
221    }
222
223    /// Canonical kebab-case name matching the PQCrypta API / web UI identifier.
224    ///
225    /// This is the stable machine name (e.g. `"ml-kem-1024"`), distinct from
226    /// [`Algorithm::name`] which is the human-readable display label.
227    #[must_use]
228    pub const fn canonical_name(self) -> &'static str {
229        match self {
230            Self::Classical => "classical",
231            Self::PasswordClassical => "password-classical",
232            Self::Hybrid => "hybrid",
233            Self::PostQuantum => "post-quantum",
234            Self::MultiAlgorithm => "multi-algorithm",
235            Self::MlKem1024 => "ml-kem-1024",
236            Self::MultiKem => "multi-kem",
237            Self::MultiKemTriple => "multi-kem-triple",
238            Self::QuadLayer => "quad-layer",
239            Self::LatticeCodeHybrid => "lattice-code-hybrid",
240            Self::Pq3Stack => "pq3-stack",
241            Self::MaxSecureLightweight => "max-secure-lightweight",
242            Self::MaxSecurePurePq => "max-secure-pure-pq",
243            Self::MaxSecureHybrid => "max-secure-hybrid",
244            Self::MaxSecureStateless => "max-secure-stateless",
245            Self::MaxSecureCryptoAgile => "max-secure-crypto-agile",
246            Self::MaxSecurePqcZk => "max-secure-pqc-zk",
247            Self::MaxSecureHybridTransition => "max-secure-hybrid-transition",
248            Self::FnDsa512Compact => "fn-dsa-512-compact",
249            Self::FnDsa1024Security => "fn-dsa-1024-security",
250            Self::FnDsaFpHardened => "fn-dsa-fp-hardened",
251            Self::FnDsaDualSignature => "fn-dsa-dual-signature",
252            Self::FnDsaTransition => "fn-dsa-transition",
253            Self::FnDsaZk => "fn-dsa-zk",
254            Self::FnDsaZkStack => "fn-dsa-zk-stack",
255            Self::FnDsaTransitionStack => "fn-dsa-transition-stack",
256            Self::QuantumLatticeFusion => "quantum-lattice-fusion",
257            Self::PostZkHomomorphic => "post-zk-homomorphic",
258            Self::QuantumResistantConsensus => "quantum-resistant-consensus",
259            Self::EntropyOrchestrated => "entropy-orchestrated",
260            Self::LatticeCodeHybridFn => "lattice-code-hybrid-fn",
261            Self::AiSynthesizedCryptoAgile => "ai-synthesized-crypto-agile",
262            Self::Experimental => "experimental",
263            Self::Hqc128 => "hqc-128",
264            Self::Hqc192 => "hqc-192",
265            Self::Hqc256 => "hqc-256",
266            Self::MlKem512 => "ml-kem-512",
267            Self::MlKem768 => "ml-kem-768",
268            Self::MlDsa44 => "ml-dsa-44",
269            Self::MlDsa65 => "ml-dsa-65",
270            Self::MlDsa87 => "ml-dsa-87",
271            Self::SlhDsaSha2_128s => "slh-dsa-sha2-128s",
272            Self::SlhDsaSha2_128f => "slh-dsa-sha2-128f",
273            Self::SlhDsaSha2_192s => "slh-dsa-sha2-192s",
274            Self::SlhDsaSha2_192f => "slh-dsa-sha2-192f",
275            Self::SlhDsaSha2_256s => "slh-dsa-sha2-256s",
276            Self::SlhDsaSha2_256f => "slh-dsa-sha2-256f",
277        }
278    }
279
280    /// Get u16 identifier for this algorithm
281    ///
282    /// # Example
283    ///
284    /// ```
285    /// use pqc_binary_format::Algorithm;
286    ///
287    /// assert_eq!(Algorithm::Hybrid.as_id(), 0x0100);
288    /// ```
289    #[must_use]
290    pub const fn as_id(self) -> u16 {
291        self as u16
292    }
293
294    /// Get human-readable name for this algorithm
295    ///
296    /// # Example
297    ///
298    /// ```
299    /// use pqc_binary_format::Algorithm;
300    ///
301    /// assert_eq!(Algorithm::Hybrid.name(), "Hybrid");
302    /// ```
303    #[must_use]
304    pub const fn name(self) -> &'static str {
305        match self {
306            Self::Classical => "Classical",
307            Self::PasswordClassical => "Password Classical",
308            Self::Hybrid => "Hybrid",
309            Self::PostQuantum => "Post-Quantum",
310            Self::MultiAlgorithm => "Multi-Algorithm",
311            Self::MlKem1024 => "ML-KEM-1024",
312            Self::MultiKem => "Multi-KEM Dual Layer",
313            Self::MultiKemTriple => "Multi-KEM Triple Layer",
314            Self::QuadLayer => "Quad-Layer",
315            Self::LatticeCodeHybrid => "Lattice-Code Hybrid",
316            Self::Pq3Stack => "PQ3-Stack",
317            Self::MaxSecureLightweight => "Max Secure: PQ Lightweight",
318            Self::MaxSecurePurePq => "Max Secure: Pure PQ",
319            Self::MaxSecureHybrid => "Max Secure: Hybrid",
320            Self::MaxSecureStateless => "Max Secure: Stateless",
321            Self::MaxSecureCryptoAgile => "Max Secure: Crypto-Agile",
322            Self::MaxSecurePqcZk => "Max Secure: PQC + ZK",
323            Self::FnDsa512Compact => "FN-DSA 512: Compact",
324            Self::FnDsa1024Security => "FN-DSA 1024: High-Security",
325            Self::FnDsaFpHardened => "FN-DSA: Floating-Point Hardened",
326            Self::FnDsaDualSignature => "FN-DSA: Dual Signature",
327            Self::FnDsaTransition => "FN-DSA: Transition Stack",
328            Self::FnDsaZk => "FN-DSA + ZK Stack",
329            Self::FnDsaZkStack => "FN-DSA + ZK Stack Enhanced",
330            Self::FnDsaTransitionStack => "FN-DSA: Transition Stack Enhanced",
331            Self::MaxSecureHybridTransition => "Max Secure: Hybrid Transition",
332            Self::QuantumLatticeFusion => "Quantum-Inspired Lattice Fusion",
333            Self::PostZkHomomorphic => "Post-ZK Homomorphic",
334            Self::QuantumResistantConsensus => "Quantum-Resistant Consensus",
335            Self::EntropyOrchestrated => "Entropy-Orchestrated",
336            Self::LatticeCodeHybridFn => "Lattice-Code Hybrid FN",
337            Self::AiSynthesizedCryptoAgile => "AI-Synthesized Crypto-Agile",
338            Self::Experimental => "Experimental Engine",
339            Self::Hqc128 => "HQC-128",
340            Self::Hqc192 => "HQC-192",
341            Self::Hqc256 => "HQC-256",
342            Self::MlKem512 => "ML-KEM-512",
343            Self::MlKem768 => "ML-KEM-768",
344            Self::MlDsa44 => "ML-DSA-44",
345            Self::MlDsa65 => "ML-DSA-65",
346            Self::MlDsa87 => "ML-DSA-87",
347            Self::SlhDsaSha2_128s => "SLH-DSA-SHA2-128s",
348            Self::SlhDsaSha2_128f => "SLH-DSA-SHA2-128f",
349            Self::SlhDsaSha2_192s => "SLH-DSA-SHA2-192s",
350            Self::SlhDsaSha2_192f => "SLH-DSA-SHA2-192f",
351            Self::SlhDsaSha2_256s => "SLH-DSA-SHA2-256s",
352            Self::SlhDsaSha2_256f => "SLH-DSA-SHA2-256f",
353        }
354    }
355
356    /// Check if this algorithm is marked as experimental
357    ///
358    /// Experimental algorithms may have reduced security guarantees
359    /// and are intended for research purposes.
360    #[must_use]
361    pub const fn is_experimental(self) -> bool {
362        matches!(
363            self,
364            Self::QuantumLatticeFusion
365                | Self::PostZkHomomorphic
366                | Self::QuantumResistantConsensus
367                | Self::EntropyOrchestrated
368                | Self::LatticeCodeHybridFn
369                | Self::AiSynthesizedCryptoAgile
370                | Self::Experimental
371        )
372    }
373
374    /// Get all defined algorithm identifiers
375    ///
376    /// Useful for iteration and testing.
377    #[must_use]
378    pub fn all() -> Vec<Self> {
379        vec![
380            Self::Classical,
381            Self::PasswordClassical,
382            Self::Hybrid,
383            Self::PostQuantum,
384            Self::MultiAlgorithm,
385            Self::MlKem1024,
386            Self::MultiKem,
387            Self::MultiKemTriple,
388            Self::QuadLayer,
389            Self::LatticeCodeHybrid,
390            Self::Pq3Stack,
391            Self::MaxSecureLightweight,
392            Self::MaxSecurePurePq,
393            Self::MaxSecureHybrid,
394            Self::MaxSecureStateless,
395            Self::MaxSecureCryptoAgile,
396            Self::MaxSecurePqcZk,
397            Self::MaxSecureHybridTransition,
398            Self::FnDsa512Compact,
399            Self::FnDsa1024Security,
400            Self::FnDsaFpHardened,
401            Self::FnDsaDualSignature,
402            Self::FnDsaTransition,
403            Self::FnDsaZk,
404            Self::FnDsaZkStack,
405            Self::FnDsaTransitionStack,
406            Self::QuantumLatticeFusion,
407            Self::PostZkHomomorphic,
408            Self::QuantumResistantConsensus,
409            Self::EntropyOrchestrated,
410            Self::LatticeCodeHybridFn,
411            Self::AiSynthesizedCryptoAgile,
412            Self::Experimental,
413            Self::Hqc128,
414            Self::Hqc192,
415            Self::Hqc256,
416            Self::MlKem512,
417            Self::MlKem768,
418            Self::MlDsa44,
419            Self::MlDsa65,
420            Self::MlDsa87,
421            Self::SlhDsaSha2_128s,
422            Self::SlhDsaSha2_128f,
423            Self::SlhDsaSha2_192s,
424            Self::SlhDsaSha2_192f,
425            Self::SlhDsaSha2_256s,
426            Self::SlhDsaSha2_256f,
427        ]
428    }
429}
430
431#[cfg(test)]
432mod tests {
433    use super::*;
434
435    #[test]
436    fn test_algorithm_roundtrip() {
437        for algo in Algorithm::all() {
438            let id = algo.as_id();
439            let recovered = Algorithm::from_id(id).unwrap();
440            assert_eq!(algo, recovered);
441        }
442    }
443
444    #[test]
445    fn test_invalid_algorithm_id() {
446        assert!(Algorithm::from_id(0xFFFF).is_none());
447    }
448
449    #[test]
450    fn test_experimental_detection() {
451        assert!(Algorithm::QuantumLatticeFusion.is_experimental());
452        assert!(!Algorithm::Hybrid.is_experimental());
453    }
454}