extern crate alloc;
use alloc::format;
use ml_dsa::{
B32, KeyExport, KeyInit, Keypair, MlDsa87, Seed, SigningKey, VerifyingKey,
signature::{Signer, Verifier},
};
use rand_core::{CryptoRng, RngCore};
use crate::ctx::check_ctx;
use crate::error::{SigError, SigResult};
use crate::prehash::{check_prehash, frame_prehash, PreHash};
use crate::types::{SigAlgorithm, SigPublicKey, SigSecretKey, Signature};
pub struct MlDsa87Keypair {
signing_key: SigningKey<MlDsa87>,
}
impl MlDsa87Keypair {
pub const SECURITY_STRENGTH_BITS: u32 = 256;
pub fn generate<R: CryptoRng + RngCore>(rng: &mut R) -> SigResult<Self> {
let mut seed_bytes = [0u8; 32];
rng.fill_bytes(&mut seed_bytes);
let seed: Seed = seed_bytes.into();
let signing_key = SigningKey::<MlDsa87>::new(&seed);
Ok(Self { signing_key })
}
pub fn public_key(&self) -> SigPublicKey {
let vk = self.signing_key.verifying_key();
SigPublicKey::new(
SigAlgorithm::MlDsa87,
vk.to_bytes().to_vec(),
)
}
pub fn secret_key(&self) -> SigSecretKey {
SigSecretKey::new(
SigAlgorithm::MlDsa87,
self.signing_key.to_bytes().to_vec(),
)
}
pub fn sign<R: CryptoRng + RngCore>(&self, rng: &mut R, message: &[u8]) -> SigResult<Signature> {
let _ = rng;
let sig = self.signing_key.try_sign(message)
.map_err(|e| SigError::Signing(format!("{:?}", e)))?;
Ok(Signature::new(SigAlgorithm::MlDsa87, sig.encode().to_vec()))
}
pub fn sign_deterministic(&self, message: &[u8]) -> SigResult<Signature> {
let sig = self.signing_key.try_sign(message)
.map_err(|e| SigError::Signing(format!("{:?}", e)))?;
Ok(Signature::new(SigAlgorithm::MlDsa87, sig.encode().to_vec()))
}
pub fn verify(public_key: &SigPublicKey, message: &[u8], signature: &Signature) -> SigResult<()> {
if public_key.algorithm != SigAlgorithm::MlDsa87 {
return Err(SigError::InvalidPublicKey(
format!("expected ML-DSA-87 key, got {:?}", public_key.algorithm)
));
}
if signature.algorithm != SigAlgorithm::MlDsa87 {
return Err(SigError::InvalidSignature(
format!("expected ML-DSA-87 signature, got {:?}", signature.algorithm)
));
}
let vk_bytes: &[u8] = &public_key.bytes;
let vk_arr = ml_dsa::EncodedVerifyingKey::<MlDsa87>::try_from(vk_bytes)
.map_err(|_| SigError::InvalidPublicKey(
format!("ML-DSA-87 public key must be 2592 bytes, got {}", vk_bytes.len())
))?;
let vk = VerifyingKey::<MlDsa87>::decode(&vk_arr);
let sig = ml_dsa::Signature::<MlDsa87>::try_from(signature.bytes.as_slice())
.map_err(|e| SigError::InvalidSignature(format!("{:?}", e)))?;
vk.verify(message, &sig)
.map_err(|_| SigError::VerificationFailed)
}
pub fn sign_ctx<R: CryptoRng + RngCore>(&self, rng: &mut R, ctx: &[u8], message: &[u8]) -> SigResult<Signature> {
let _ = rng;
self.sign_ctx_deterministic(ctx, message)
}
pub fn sign_ctx_deterministic(&self, ctx: &[u8], message: &[u8]) -> SigResult<Signature> {
check_ctx(ctx)?;
let sig = self.signing_key.expanded_key().sign_deterministic(message, ctx)
.map_err(|e| SigError::Signing(format!("{:?}", e)))?;
Ok(Signature::new(SigAlgorithm::MlDsa87, sig.encode().to_vec()))
}
pub fn verify_ctx(public_key: &SigPublicKey, ctx: &[u8], message: &[u8], signature: &Signature) -> SigResult<()> {
check_ctx(ctx)?;
if public_key.algorithm != SigAlgorithm::MlDsa87 {
return Err(SigError::InvalidPublicKey(
format!("expected ML-DSA-87 key, got {:?}", public_key.algorithm)
));
}
if signature.algorithm != SigAlgorithm::MlDsa87 {
return Err(SigError::InvalidSignature(
format!("expected ML-DSA-87 signature, got {:?}", signature.algorithm)
));
}
let vk_bytes: &[u8] = &public_key.bytes;
let vk_arr = ml_dsa::EncodedVerifyingKey::<MlDsa87>::try_from(vk_bytes)
.map_err(|_| SigError::InvalidPublicKey(
format!("ML-DSA-87 public key must be 2592 bytes, got {}", vk_bytes.len())
))?;
let vk = VerifyingKey::<MlDsa87>::decode(&vk_arr);
let sig = ml_dsa::Signature::<MlDsa87>::try_from(signature.bytes.as_slice())
.map_err(|e| SigError::InvalidSignature(format!("{:?}", e)))?;
if vk.verify_with_context(message, ctx, &sig) {
Ok(())
} else {
Err(SigError::VerificationFailed)
}
}
pub fn sign_prehash<R: CryptoRng + RngCore>(
&self,
rng: &mut R,
ctx: &[u8],
hash: PreHash,
digest: &[u8],
) -> SigResult<Signature> {
check_ctx(ctx)?;
check_prehash(hash, digest, Self::SECURITY_STRENGTH_BITS, "ML-DSA-87")?;
let m_prime = frame_prehash(ctx, hash, digest);
let mut rnd_bytes = [0u8; 32];
rng.fill_bytes(&mut rnd_bytes);
let rnd: B32 = rnd_bytes.into();
let sig = self.signing_key.expanded_key().sign_internal(&[m_prime.as_slice()], &rnd);
Ok(Signature::new(SigAlgorithm::MlDsa87, sig.encode().to_vec()))
}
pub fn sign_prehash_deterministic(
&self,
ctx: &[u8],
hash: PreHash,
digest: &[u8],
) -> SigResult<Signature> {
check_ctx(ctx)?;
check_prehash(hash, digest, Self::SECURITY_STRENGTH_BITS, "ML-DSA-87")?;
let m_prime = frame_prehash(ctx, hash, digest);
let rnd: B32 = [0u8; 32].into();
let sig = self.signing_key.expanded_key().sign_internal(&[m_prime.as_slice()], &rnd);
Ok(Signature::new(SigAlgorithm::MlDsa87, sig.encode().to_vec()))
}
pub fn verify_prehash(
public_key: &SigPublicKey,
ctx: &[u8],
hash: PreHash,
digest: &[u8],
signature: &Signature,
) -> SigResult<()> {
check_ctx(ctx)?;
check_prehash(hash, digest, Self::SECURITY_STRENGTH_BITS, "ML-DSA-87")?;
if public_key.algorithm != SigAlgorithm::MlDsa87 {
return Err(SigError::InvalidPublicKey(
format!("expected ML-DSA-87 key, got {:?}", public_key.algorithm)
));
}
if signature.algorithm != SigAlgorithm::MlDsa87 {
return Err(SigError::InvalidSignature(
format!("expected ML-DSA-87 signature, got {:?}", signature.algorithm)
));
}
let vk_bytes: &[u8] = &public_key.bytes;
let vk_arr = ml_dsa::EncodedVerifyingKey::<MlDsa87>::try_from(vk_bytes)
.map_err(|_| SigError::InvalidPublicKey(
format!("ML-DSA-87 public key must be 2592 bytes, got {}", vk_bytes.len())
))?;
let vk = VerifyingKey::<MlDsa87>::decode(&vk_arr);
let sig = ml_dsa::Signature::<MlDsa87>::try_from(signature.bytes.as_slice())
.map_err(|e| SigError::InvalidSignature(format!("{:?}", e)))?;
let m_prime = frame_prehash(ctx, hash, digest);
if vk.verify_internal(&m_prime, &sig) {
Ok(())
} else {
Err(SigError::VerificationFailed)
}
}
pub fn from_secret_key_bytes(bytes: &[u8]) -> SigResult<Self> {
let seed_arr: [u8; 32] = bytes.try_into()
.map_err(|_| SigError::InvalidSecretKey(
format!("ML-DSA-87 seed must be 32 bytes, got {}", bytes.len())
))?;
let seed: Seed = seed_arr.into();
let signing_key = SigningKey::<MlDsa87>::new(&seed);
Ok(Self { signing_key })
}
}