use hmac::{Hmac, Mac}; use sha2::{Digest, Sha512}; use crate::errors::ApiError; type HashType = Sha512; pub trait CustomHash { fn updater(&self, hasher: &mut Sha512); fn hash(&self) -> Vec { let mut hasher = Sha512::new(); Self::updater(&self, &mut hasher); hasher.finalize().to_vec() } } pub struct IntegrityVerifier { mac: Hmac, } impl IntegrityVerifier { pub fn new() -> Result { Ok(Self { mac: Hmac::::new_from_slice( include_str!("./verification_secret").as_bytes(), )?, }) } pub fn sign(&self, data: &T) -> Result, ApiError> where T: CustomHash, { let mut mac = self.mac.clone(); mac.update(&data.hash()); let signature = mac.finalize_reset().into_bytes(); Ok(signature.to_vec()) } pub fn verify(&self, data: &T, signature: &[u8]) -> Result<(), ApiError> where T: CustomHash, { let mut mac = self.mac.clone(); mac.update(&data.hash()); match mac.verify_slice_reset(signature) { Ok(_) => Ok(()), Err(_e) => Err(ApiError::AccessDenied), } } } #[cfg(test)] mod test { use hmac::digest::Update; use crate::api::backend::integrity_verification::{CustomHash, IntegrityVerifier}; #[test] fn integrity_verification() { struct TestData { one: u32, two: String, } impl CustomHash for TestData { fn updater(&self, hasher: &mut sha2::Sha512) { hasher.update(&self.one.to_be_bytes()); hasher.update(&self.two.as_bytes()); } } let verifier = IntegrityVerifier::new().unwrap(); let mut item = TestData { one: 24, two: "MyTesting".to_string(), }; let signature = verifier.sign(&item).unwrap(); assert!(verifier.verify(&item, &signature).is_ok()); item.two = "Wrong".to_string(); assert!(verifier.verify(&item, &signature).is_err()); } }