use super::constants::{
ML_DSA_87_PRIVATE_KEY_SIZE, ML_DSA_87_PUBLIC_KEY_SIZE, ML_DSA_87_SIGNATURE_SIZE,
};
use base64::{Engine as _, engine::general_purpose::STANDARD as B64};
use fips204::ml_dsa_87;
use fips204::traits::{KeyGen, SerDes, Signer, Verifier};
use std::error::Error;
use tracing::{debug, trace, warn};
#[must_use = "generated keys must be stored securely"]
pub fn generate_keys() -> Result<(Vec<u8>, Vec<u8>), Box<dyn std::error::Error>> {
let (pk, sk) = ml_dsa_87::KG::try_keygen()?;
let sk_bytes = sk.into_bytes().to_vec();
let pk_bytes = pk.into_bytes().to_vec();
trace!(
sk_len = sk_bytes.len(),
pk_len = pk_bytes.len(),
"ML-DSA-87 keypair generated"
);
Ok((sk_bytes, pk_bytes))
}
#[must_use = "signature must be stored or transmitted"]
pub fn sign_string(secret_key: Vec<u8>, data: &String) -> Result<String, Box<dyn Error>> {
trace!(data_len = data.len(), "ML-DSA-87 signing starting");
let sk_array: [u8; ML_DSA_87_PRIVATE_KEY_SIZE] =
secret_key.try_into().map_err(|v: Vec<u8>| {
format!(
"Invalid private key length for ML-DSA-87: expected {} bytes, got {} bytes",
ML_DSA_87_PRIVATE_KEY_SIZE,
v.len()
)
})?;
let sk = ml_dsa_87::PrivateKey::try_from_bytes(sk_array)?;
let sig = sk.try_sign(data.as_bytes(), b"")?; let encoded = B64.encode(sig);
trace!(signature_len = encoded.len(), "ML-DSA-87 signing completed");
Ok(encoded)
}
#[must_use = "signature verification result must be checked"]
pub fn verify_string(
public_key: Vec<u8>,
data: &str,
signature_base64: &str,
) -> Result<(), Box<dyn Error>> {
trace!(
data_len = data.len(),
public_key_len = public_key.len(),
"ML-DSA-87 verification starting"
);
let pk_array: [u8; ML_DSA_87_PUBLIC_KEY_SIZE] =
public_key.try_into().map_err(|v: Vec<u8>| {
format!(
"Invalid public key length for ML-DSA-87: expected {} bytes, got {} bytes",
ML_DSA_87_PUBLIC_KEY_SIZE,
v.len()
)
})?;
let pk = ml_dsa_87::PublicKey::try_from_bytes(pk_array)?;
let sig_bytes = B64.decode(signature_base64)?;
let sig_array: [u8; ML_DSA_87_SIGNATURE_SIZE] =
sig_bytes.try_into().map_err(|v: Vec<u8>| {
format!(
"Invalid signature length for ML-DSA-87: expected {} bytes, got {} bytes",
ML_DSA_87_SIGNATURE_SIZE,
v.len()
)
})?;
if pk.verify(data.as_bytes(), &sig_array, b"") {
debug!("ML-DSA-87 signature verification succeeded");
Ok(())
} else {
warn!("ML-DSA-87 signature verification failed");
Err("ML-DSA signature verification failed".into())
}
}