saorsa_pqc/pqc/
ml_dsa.rs

1//! ML-DSA-65 implementation
2
3use crate::pqc::{
4    types::{MlDsaPublicKey, MlDsaSecretKey, MlDsaSignature, PqcResult},
5    MlDsaOperations,
6};
7use fips204::ml_dsa_65;
8use fips204::traits::{SerDes, Signer, Verifier};
9use rand_core::OsRng;
10
11/// ML-DSA-65 implementation using FIPS-certified algorithm
12pub struct MlDsa65;
13
14impl Default for MlDsa65 {
15    fn default() -> Self {
16        Self::new()
17    }
18}
19
20impl MlDsa65 {
21    /// Create a new ML-DSA-65 instance
22    #[must_use]
23    pub const fn new() -> Self {
24        Self
25    }
26}
27
28impl Clone for MlDsa65 {
29    fn clone(&self) -> Self {
30        Self::new()
31    }
32}
33
34impl MlDsaOperations for MlDsa65 {
35    fn generate_keypair(&self) -> PqcResult<(MlDsaPublicKey, MlDsaSecretKey)> {
36        let (pk, sk) = ml_dsa_65::try_keygen_with_rng(&mut OsRng)
37            .map_err(|e| crate::pqc::types::PqcError::KeyGenerationFailed(e.to_string()))?;
38
39        Ok((
40            MlDsaPublicKey::from_bytes(&pk.into_bytes())?,
41            MlDsaSecretKey::from_bytes(&sk.into_bytes())?,
42        ))
43    }
44
45    fn sign(&self, secret_key: &MlDsaSecretKey, message: &[u8]) -> PqcResult<MlDsaSignature> {
46        let sk_bytes: [u8; 4032] = secret_key.as_bytes().try_into().map_err(|_| {
47            crate::pqc::types::PqcError::InvalidKeySize {
48                expected: 4032,
49                actual: secret_key.as_bytes().len(),
50            }
51        })?;
52
53        let sk = ml_dsa_65::PrivateKey::try_from_bytes(sk_bytes)
54            .map_err(|e| crate::pqc::types::PqcError::CryptoError(e.to_string()))?;
55
56        let sig = sk
57            .try_sign_with_rng(&mut OsRng, message, b"")
58            .map_err(|e| crate::pqc::types::PqcError::SigningFailed(e.to_string()))?;
59
60        MlDsaSignature::from_bytes(&sig)
61    }
62
63    fn verify(
64        &self,
65        public_key: &MlDsaPublicKey,
66        message: &[u8],
67        signature: &MlDsaSignature,
68    ) -> PqcResult<bool> {
69        let pk_bytes: [u8; 1952] = public_key.as_bytes().try_into().map_err(|_| {
70            crate::pqc::types::PqcError::InvalidKeySize {
71                expected: 1952,
72                actual: public_key.as_bytes().len(),
73            }
74        })?;
75
76        let pk = ml_dsa_65::PublicKey::try_from_bytes(pk_bytes)
77            .map_err(|e| crate::pqc::types::PqcError::CryptoError(e.to_string()))?;
78
79        let sig_bytes: [u8; 3309] = signature.as_bytes().try_into().map_err(|_| {
80            crate::pqc::types::PqcError::InvalidSignatureSize {
81                expected: 3309,
82                actual: signature.as_bytes().len(),
83            }
84        })?;
85
86        Ok(pk.verify(message, &sig_bytes, b""))
87    }
88}