shopify_rust/
hmac_wrapper.rs1use hmac::{digest::generic_array::GenericArray, Hmac, Mac, NewMac};
2use sha2::Sha256;
3
4use crate::ShopifyApp;
5
6type HmacSha256 = Hmac<Sha256>;
8
9impl ShopifyApp {
10
11 pub fn generate_hmac_bytes(
18 &self,
19 data: &[u8],
20 ) -> GenericArray<u8, <HmacSha256 as Mac>::OutputSize> {
21 let mut mac = HmacSha256::new_varkey(self.credentials.secret.as_bytes())
22 .expect("Invalid store secret");
23 mac.update(data);
24 let result = mac.finalize();
25 result.into_bytes()
26 }
27
28 pub fn generate_hmac_hex(&self, data: &[u8]) -> String {
29 let hmac_bytes = self.generate_hmac_bytes(data);
30 hex::encode(hmac_bytes)
31 }
32
33 pub fn generate_hmac_base64(&self, data: &[u8]) -> String {
34 let hmac_bytes = self.generate_hmac_bytes(data);
35 base64::encode(hmac_bytes)
36 }
37
38 pub fn valid_hmac(&self, query_params: &Vec<(String, String)>) -> bool {
39 let mut hmac = String::new();
40
41 let mut message = query_params.into_iter().fold(
42 Vec::<(&str, &str)>::with_capacity(2),
43 |mut acc, query_item| {
44 let (key, value) = query_item;
45
46 if "hmac".to_string() == *key {
47 hmac = value.to_string();
48 return acc;
49 }
50
51 acc.push((key, value));
52 acc
53 },
54 );
55
56 if message.is_empty() {
57 return false;
58 }
59
60 message.sort_by(|a, b| a.0.cmp(&b.0));
61
62 let message_str = &message.iter().fold(String::new(), |acc, (key, val)| {
63 acc + "&" + &key + "=" + &val
64 })[1..];
65
66 let encoded = self.generate_hmac_hex(message_str.as_bytes());
67
68 encoded == hmac
69 }
70}