bc_components/mldsa/
mldsa_level.rs

1use anyhow::{ bail, Error, Result };
2use dcbor::prelude::*;
3use pqcrypto_mldsa::*;
4
5use super::{ MLDSAPrivateKey, MLDSAPublicKey };
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, performance,
14/// 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 used
21/// 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                (MLDSAPrivateKey::MLDSA44(Box::new(sk)), MLDSAPublicKey::MLDSA44(Box::new(pk)))
51            }
52            MLDSA::MLDSA65 => {
53                let (pk, sk) = mldsa65::keypair();
54                (MLDSAPrivateKey::MLDSA65(Box::new(sk)), MLDSAPublicKey::MLDSA65(Box::new(pk)))
55            }
56            MLDSA::MLDSA87 => {
57                let (pk, sk) = mldsa87::keypair();
58                (MLDSAPrivateKey::MLDSA87(Box::new(sk)), MLDSAPublicKey::MLDSA87(Box::new(pk)))
59            }
60        }
61    }
62
63    /// Returns the size of a private key in bytes for this security level.
64    pub fn private_key_size(&self) -> usize {
65        match self {
66            MLDSA::MLDSA44 => mldsa44::secret_key_bytes(),
67            MLDSA::MLDSA65 => mldsa65::secret_key_bytes(),
68            MLDSA::MLDSA87 => mldsa87::secret_key_bytes(),
69        }
70    }
71
72    /// Returns the size of a public key in bytes for this security level.
73    pub fn public_key_size(&self) -> usize {
74        match self {
75            MLDSA::MLDSA44 => mldsa44::public_key_bytes(),
76            MLDSA::MLDSA65 => mldsa65::public_key_bytes(),
77            MLDSA::MLDSA87 => mldsa87::public_key_bytes(),
78        }
79    }
80
81    /// Returns the size of a signature in bytes for this security level.
82    pub fn signature_size(&self) -> usize {
83        match self {
84            MLDSA::MLDSA44 => mldsa44::signature_bytes(),
85            MLDSA::MLDSA65 => mldsa65::signature_bytes(),
86            MLDSA::MLDSA87 => mldsa87::signature_bytes(),
87        }
88    }
89}
90
91/// Converts an `MLDSA` value to CBOR.
92impl From<MLDSA> for CBOR {
93    /// Converts to the numeric security level value (2, 3, or 5).
94    fn from(level: MLDSA) -> Self {
95        (level as u32).into()
96    }
97}
98
99/// Attempts to convert CBOR to an `MLDSA` value.
100impl TryFrom<CBOR> for MLDSA {
101    type Error = Error;
102
103    /// Converts from a CBOR-encoded security level (2, 3, or 5).
104    ///
105    /// # Errors
106    /// Returns an error if the CBOR value doesn't represent a valid ML-DSA level.
107    fn try_from(cbor: CBOR) -> Result<Self> {
108        let level = u32::try_from(cbor)?;
109        match level {
110            2 => Ok(MLDSA::MLDSA44),
111            3 => Ok(MLDSA::MLDSA65),
112            5 => Ok(MLDSA::MLDSA87),
113            _ => bail!("Invalid MLDSA level: {}", level),
114        }
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_mldsa_level() {
124        let level = MLDSA::MLDSA44;
125        assert_eq!(format!("{:?}", level), "MLDSA44");
126        let cbor = CBOR::from(level);
127        let level2 = MLDSA::try_from(cbor).unwrap();
128        assert_eq!(level, level2);
129
130        let level = MLDSA::MLDSA65;
131        assert_eq!(format!("{:?}", level), "MLDSA65");
132        let cbor = CBOR::from(level);
133        let level2 = MLDSA::try_from(cbor).unwrap();
134        assert_eq!(level, level2);
135
136        let level = MLDSA::MLDSA87;
137        assert_eq!(format!("{:?}", level), "MLDSA87");
138        let cbor = CBOR::from(level);
139        let level2 = MLDSA::try_from(cbor).unwrap();
140        assert_eq!(level, level2);
141    }
142}