use ml_dsa::{MlDsa87, EncodedVerifyingKey, KeyGen};
use ml_dsa::signature::{Signer, Verifier};
use rand::rngs::OsRng;
use sha3::{Sha3_256, Digest};
use zeroize::Zeroizing;
use crate::error::{QVError, QVResult};
pub const SIG_LEN: usize = 4627;
pub const VK_LEN: usize = 2592;
pub const SEED_LEN: usize = 32;
pub struct QVSigningKey {
inner: ml_dsa::SigningKey<MlDsa87>,
}
pub struct QVVerifyingKey {
inner: ml_dsa::VerifyingKey<MlDsa87>,
}
impl QVSigningKey {
pub fn from_bytes(seed: &[u8]) -> QVResult<Self> {
if seed.len() != SEED_LEN {
return Err(QVError::KeyGenFailed);
}
let arr: ml_dsa::B32 = seed.try_into().map_err(|_| QVError::KeyGenFailed)?;
Ok(QVSigningKey { inner: MlDsa87::from_seed(&arr) })
}
pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
Zeroizing::new(self.inner.to_seed().to_vec())
}
}
impl QVVerifyingKey {
pub fn from_bytes(bytes: &[u8]) -> QVResult<Self> {
let arr = EncodedVerifyingKey::<MlDsa87>::try_from(bytes)
.map_err(|_| QVError::KeyGenFailed)?;
Ok(QVVerifyingKey { inner: ml_dsa::VerifyingKey::decode(&arr) })
}
pub fn to_bytes(&self) -> Vec<u8> {
self.inner.encode().to_vec()
}
}
pub fn generate_keypair() -> QVResult<(QVSigningKey, QVVerifyingKey)> {
use rand::RngCore;
let mut seed_bytes = [0u8; 32];
OsRng.fill_bytes(&mut seed_bytes);
let seed: ml_dsa::B32 = seed_bytes.into();
let sk_inner = MlDsa87::from_seed(&seed);
let vk_inner = ml_dsa::signature::Keypair::verifying_key(&sk_inner);
Ok((QVSigningKey { inner: sk_inner }, QVVerifyingKey { inner: vk_inner }))
}
pub fn sign(sk: &QVSigningKey, message: &[u8]) -> QVResult<Vec<u8>> {
let sig: ml_dsa::Signature<MlDsa87> = sk.inner.try_sign(message)
.map_err(|_| QVError::SignatureInvalid)?;
Ok(sig.encode().to_vec())
}
pub fn verify(vk: &QVVerifyingKey, message: &[u8], signature_bytes: &[u8]) -> QVResult<()> {
let sig = ml_dsa::Signature::<MlDsa87>::try_from(signature_bytes)
.map_err(|_| QVError::SignatureInvalid)?;
vk.inner.verify(message, &sig).map_err(|_| QVError::SignatureInvalid)
}
pub fn sha3_256(data: &[u8]) -> [u8; 32] {
let mut h = Sha3_256::new();
h.update(data);
h.finalize().into()
}