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}