bc_components/mldsa/
mldsa_level.rs

1use dcbor::prelude::*;
2use pqcrypto_mldsa::*;
3
4use super::{MLDSAPrivateKey, MLDSAPublicKey};
5use crate::{Error, Result};
6
7/// Security levels for the ML-DSA post-quantum digital signature algorithm.
8///
9/// ML-DSA (Module Lattice-based Digital Signature Algorithm) is a post-quantum
10/// digital signature algorithm standardized by NIST. It provides resistance
11/// against attacks from both classical and quantum computers.
12///
13/// Each security level offers different trade-offs between security,
14/// performance, and key/signature sizes:
15///
16/// - `MLDSA44`: NIST security level 2 (roughly equivalent to AES-128)
17/// - `MLDSA65`: NIST security level 3 (roughly equivalent to AES-192)
18/// - `MLDSA87`: NIST security level 5 (roughly equivalent to AES-256)
19///
20/// The numeric values (2, 3, 5) correspond to the NIST security levels and are
21/// used in CBOR serialization.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23#[repr(u32)]
24pub enum MLDSA {
25    /// ML-DSA Level 2 (NIST security level 2, roughly equivalent to AES-128)
26    MLDSA44 = 2,
27    /// ML-DSA Level 3 (NIST security level 3, roughly equivalent to AES-192)
28    MLDSA65 = 3,
29    /// ML-DSA Level 5 (NIST security level 5, roughly equivalent to AES-256)
30    MLDSA87 = 5,
31}
32
33impl MLDSA {
34    /// Generates a new ML-DSA keypair with the specified security level.
35    ///
36    /// # Returns
37    /// A tuple containing the private key and public key.
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// use bc_components::MLDSA;
43    ///
44    /// let (private_key, public_key) = MLDSA::MLDSA44.keypair();
45    /// ```
46    pub fn keypair(self) -> (MLDSAPrivateKey, MLDSAPublicKey) {
47        match self {
48            MLDSA::MLDSA44 => {
49                let (pk, sk) = mldsa44::keypair();
50                (
51                    MLDSAPrivateKey::MLDSA44(Box::new(sk)),
52                    MLDSAPublicKey::MLDSA44(Box::new(pk)),
53                )
54            }
55            MLDSA::MLDSA65 => {
56                let (pk, sk) = mldsa65::keypair();
57                (
58                    MLDSAPrivateKey::MLDSA65(Box::new(sk)),
59                    MLDSAPublicKey::MLDSA65(Box::new(pk)),
60                )
61            }
62            MLDSA::MLDSA87 => {
63                let (pk, sk) = mldsa87::keypair();
64                (
65                    MLDSAPrivateKey::MLDSA87(Box::new(sk)),
66                    MLDSAPublicKey::MLDSA87(Box::new(pk)),
67                )
68            }
69        }
70    }
71
72    /// Returns the size of a private key in bytes for this security level.
73    pub fn private_key_size(&self) -> usize {
74        match self {
75            MLDSA::MLDSA44 => mldsa44::secret_key_bytes(),
76            MLDSA::MLDSA65 => mldsa65::secret_key_bytes(),
77            MLDSA::MLDSA87 => mldsa87::secret_key_bytes(),
78        }
79    }
80
81    /// Returns the size of a public key in bytes for this security level.
82    pub fn public_key_size(&self) -> usize {
83        match self {
84            MLDSA::MLDSA44 => mldsa44::public_key_bytes(),
85            MLDSA::MLDSA65 => mldsa65::public_key_bytes(),
86            MLDSA::MLDSA87 => mldsa87::public_key_bytes(),
87        }
88    }
89
90    /// Returns the size of a signature in bytes for this security level.
91    pub fn signature_size(&self) -> usize {
92        match self {
93            MLDSA::MLDSA44 => mldsa44::signature_bytes(),
94            MLDSA::MLDSA65 => mldsa65::signature_bytes(),
95            MLDSA::MLDSA87 => mldsa87::signature_bytes(),
96        }
97    }
98}
99
100/// Converts an `MLDSA` value to CBOR.
101impl From<MLDSA> for CBOR {
102    /// Converts to the numeric security level value (2, 3, or 5).
103    fn from(level: MLDSA) -> Self { (level as u32).into() }
104}
105
106/// Attempts to convert CBOR to an `MLDSA` value.
107impl TryFrom<CBOR> for MLDSA {
108    type Error = Error;
109
110    /// Converts from a CBOR-encoded security level (2, 3, or 5).
111    ///
112    /// # Errors
113    /// Returns an error if the CBOR value doesn't represent a valid ML-DSA
114    /// level.
115    fn try_from(cbor: CBOR) -> Result<Self> {
116        let level = u32::try_from(cbor)?;
117        match level {
118            2 => Ok(MLDSA::MLDSA44),
119            3 => Ok(MLDSA::MLDSA65),
120            5 => Ok(MLDSA::MLDSA87),
121            _ => Err(Error::post_quantum(format!(
122                "Invalid MLDSA level: {}",
123                level
124            ))),
125        }
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn test_mldsa_level() {
135        let level = MLDSA::MLDSA44;
136        assert_eq!(format!("{:?}", level), "MLDSA44");
137        let cbor = CBOR::from(level);
138        let level2 = MLDSA::try_from(cbor).unwrap();
139        assert_eq!(level, level2);
140
141        let level = MLDSA::MLDSA65;
142        assert_eq!(format!("{:?}", level), "MLDSA65");
143        let cbor = CBOR::from(level);
144        let level2 = MLDSA::try_from(cbor).unwrap();
145        assert_eq!(level, level2);
146
147        let level = MLDSA::MLDSA87;
148        assert_eq!(format!("{:?}", level), "MLDSA87");
149        let cbor = CBOR::from(level);
150        let level2 = MLDSA::try_from(cbor).unwrap();
151        assert_eq!(level, level2);
152    }
153}