use ml_dsa::{
EncodedSigningKey, EncodedVerifyingKey, KeyGen, MlDsa65, Signature,
signature::{SignerMut, Verifier},
};
use rand::rngs::OsRng;
use sha3::{Digest, Sha3_512};
use sodoken::{SizedLockedArray, sign};
pub const SIGNATURE_LEN: usize = 3373;
pub struct VerifyingKey {
pub sodium_key: [u8; sign::PUBLICKEYBYTES],
pub ml_dsa_key: ml_dsa::VerifyingKey<MlDsa65>,
}
impl VerifyingKey {
pub fn encode(&self) -> Vec<u8> {
let mut ml_key = self.ml_dsa_key.encode().to_vec();
ml_key.extend(self.sodium_key);
return ml_key;
}
pub fn decode(encoded: Vec<u8>) -> Option<VerifyingKey> {
if encoded.len() <= sign::PUBLICKEYBYTES {
return None;
}
let (enc_dsa_key, enc_sodium_key) = encoded.split_at(encoded.len() - sign::PUBLICKEYBYTES);
let sodium_key: [u8; sign::PUBLICKEYBYTES] = enc_sodium_key.try_into().ok()?;
let enc_dsa_key: EncodedVerifyingKey<MlDsa65> = enc_dsa_key.try_into().ok()?;
let ml_key = ml_dsa::VerifyingKey::decode(&enc_dsa_key);
return Some(VerifyingKey {
sodium_key: sodium_key,
ml_dsa_key: ml_key,
});
}
}
pub struct SigningKey {
pub sodium_key: SizedLockedArray<{ sign::SECRETKEYBYTES }>,
pub ml_dsa_key: ml_dsa::SigningKey<MlDsa65>,
}
impl SigningKey {
pub fn encode(&mut self) -> Vec<u8> {
let mut ml_secret_key = self.ml_dsa_key.encode().to_vec();
ml_secret_key.extend(self.sodium_key.lock().as_slice());
return ml_secret_key;
}
pub fn decode(encoded: Vec<u8>) -> Option<SigningKey> {
if encoded.len() <= sign::SECRETKEYBYTES {
return None;
}
let (enc_dsa_key, enc_sodium_key) = encoded.split_at(encoded.len() - sign::SECRETKEYBYTES);
let enc_sodium_key: [u8; sign::SECRETKEYBYTES] = enc_sodium_key.try_into().ok()?;
let mut sodium_key = SizedLockedArray::new().ok()?;
sodium_key.lock().copy_from_slice(&enc_sodium_key);
let enc_dsa_key: EncodedSigningKey<MlDsa65> = enc_dsa_key.try_into().ok()?;
let ml_key = ml_dsa::SigningKey::decode(&enc_dsa_key);
return Some(SigningKey {
sodium_key: sodium_key,
ml_dsa_key: ml_key,
});
}
}
pub struct SignatureKeyPair {
pub verify_key: VerifyingKey,
pub signature_key: SigningKey,
}
impl SignatureKeyPair {
pub fn generate() -> SignatureKeyPair {
let rng = &mut OsRng;
let (sign_key, verify_key) = {
let ml_pair = MlDsa65::key_gen(rng);
let encoded_sign_key: EncodedSigningKey<MlDsa65> = ml_pair.signing_key().encode();
let encoded_verify_key: EncodedVerifyingKey<MlDsa65> = ml_pair.verifying_key().encode();
(
ml_dsa::SigningKey::decode(&encoded_sign_key),
ml_dsa::VerifyingKey::decode(&encoded_verify_key),
)
};
let mut sodium_pub = [0; sodoken::sign::PUBLICKEYBYTES];
let mut sodium_priv = sodoken::SizedLockedArray::new().unwrap();
sign::keypair(&mut sodium_pub, &mut sodium_priv.lock()).unwrap();
return SignatureKeyPair {
verify_key: VerifyingKey {
sodium_key: sodium_pub,
ml_dsa_key: verify_key,
},
signature_key: SigningKey {
sodium_key: sodium_priv,
ml_dsa_key: sign_key,
},
};
}
}
pub fn sign(key: &mut SigningKey, message: &Vec<u8>) -> Option<Vec<u8>> {
let hashed = compute_hash(message);
let mut ml_dsa_sig = key.ml_dsa_key.sign(&hashed).encode().to_vec();
let mut signature = [0; sign::SIGNATUREBYTES];
sign::sign_detached(&mut signature, &hashed, &key.sodium_key.lock()).ok()?;
ml_dsa_sig.extend(signature);
return Some(ml_dsa_sig);
}
pub fn verify(key: &VerifyingKey, message: &Vec<u8>, signature: &Vec<u8>) -> Option<bool> {
let hashed = compute_hash(message);
if signature.len() <= sign::SIGNATUREBYTES {
return None;
}
let (ml_dsa_sig, sodium_sig) = signature.split_at(signature.len() - sign::SIGNATUREBYTES);
let ml_dsa_sig: Signature<MlDsa65> = Signature::decode(ml_dsa_sig.try_into().ok()?)?;
key.ml_dsa_key.verify(&hashed, &ml_dsa_sig).ok()?;
if !sign::verify_detached(&sodium_sig.try_into().ok()?, &hashed, &key.sodium_key) {
return Some(false);
}
return Some(true);
}
fn compute_hash(message: &Vec<u8>) -> Vec<u8> {
let mut hasher = Sha3_512::new();
hasher.update(message);
hasher.finalize().to_vec()
}