1use sfr_types as st;
4
5use chrono::Utc;
6use derive_new::new;
7use hmac::{Hmac, Mac};
8use sha2::Sha256;
9
10#[derive(Clone, new)]
12pub struct Validator {
13 signing_secret: String,
17
18 verification_token: String,
22
23 timestamp_range: i64,
27}
28
29impl Validator {
30 pub fn validate_request(
34 &self,
35 signature: &str,
36 timestamp: &str,
37 body: &[u8],
38 ) -> Result<(), st::Error> {
39 self.validate_timestamp(timestamp)?;
40
41 let mut mac = Hmac::<Sha256>::new_from_slice(self.signing_secret.as_bytes())
42 .map_err(st::Error::failed_to_initialize_hmac)?;
43
44 mac.update(b"v0:");
45 mac.update(timestamp.as_bytes());
46 mac.update(b":");
47 mac.update(body);
48
49 let result = mac.finalize().into_bytes();
50 let result = format!("v0={}", hex::encode(result));
51 if signature == result {
52 Ok(())
53 } else {
54 Err(st::CoreError::FailedToVerifySignature.into())
55 }
56 }
57
58 pub fn verification_token(&self, token: &str) -> Result<(), st::Error> {
60 if self.verification_token == token {
61 Ok(())
62 } else {
63 Err(st::CoreError::MismatchVerificationTokens.into())
64 }
65 }
66
67 fn validate_timestamp(&self, timestamp: &str) -> Result<(), st::Error> {
71 let timestamp: i64 = timestamp
72 .parse()
73 .map_err(|e| st::Error::parsing_number("failed to paese `timestamp`", e))?;
74 let now = Utc::now().timestamp();
75 let diff = (timestamp - now).abs();
76 if self.timestamp_range < diff {
77 Err(st::CoreError::TimestampExpired.into())
78 } else {
79 Ok(())
80 }
81 }
82}