use crate::ml_dsa::{MlDsaError, MlDsaKeyPair, MlDsaPublicKey};
use blake3::Hasher;
use rand_core::{CryptoRng, RngCore};
use subtle::{Choice, ConstantTimeEq};
use thiserror::Error;
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Debug, Error)]
pub enum FingerprintError {
#[error("Invalid fingerprint data")]
InvalidData,
#[error("Failed to generate fingerprint")]
GenerationFailed,
#[error("Failed to verify fingerprint")]
VerificationFailed,
#[error("ML-DSA error: {0}")]
MlDsaError(#[from] MlDsaError),
}
#[derive(Debug, Zeroize, ZeroizeOnDrop)]
pub struct Fingerprint {
data: Vec<u8>,
signature: Vec<u8>,
}
impl Fingerprint {
pub fn generate<R: CryptoRng + RngCore>(
data: &[u8],
rng: &mut R,
) -> Result<(Self, MlDsaPublicKey), FingerprintError> {
let keypair = MlDsaKeyPair::generate(rng)?;
let public_key = MlDsaPublicKey::from_bytes(keypair.public_key())?;
let mut hasher = Hasher::new();
hasher.update(data);
let mut fingerprint_data = vec![0u8; 64];
hasher.finalize_xof().fill(&mut fingerprint_data);
let signature = keypair.sign(&fingerprint_data, rng)?;
Ok((
Self {
data: fingerprint_data,
signature,
},
public_key,
))
}
pub fn verify(&self, public_key: &MlDsaPublicKey) -> Result<(), FingerprintError> {
public_key.verify(&self.data, &self.signature)?;
Ok(())
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn signature(&self) -> &[u8] {
&self.signature
}
}
impl ConstantTimeEq for Fingerprint {
fn ct_eq(&self, other: &Self) -> Choice {
let data_eq = self.data.ct_eq(&other.data);
let sig_eq = self.signature.ct_eq(&other.signature);
data_eq & sig_eq
}
}
impl PartialEq for Fingerprint {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).unwrap_u8() == 1
}
}
impl Eq for Fingerprint {}