shopify_rust/
hmac_wrapper.rs

1use hmac::{digest::generic_array::GenericArray, Hmac, Mac, NewMac};
2use sha2::Sha256;
3
4use crate::ShopifyApp;
5
6// Types
7type HmacSha256 = Hmac<Sha256>;
8
9impl ShopifyApp {
10
11    /// This method generates a HMAC (keyed-hash message authentication code) of the data passed as an argument as a byte array.
12    ///
13    /// The method takes a slice of bytes `&[u8]` as an argument and returns a type that implements the `GenericArray` trait from the `generic-array` crate, with the element type `u8` and the size specified by the `OutputSize` associated type of the `HmacSha256` type from the `hmac` crate.
14    ///
15    /// The method uses the `HmacSha256` type to create a new variable-length HMAC using the `HmacSha256::new_varkey` method, passing in the `secret` field of the `credentials` field of the `ShopifyApp` struct as a byte slice. The `expect` method is called on the result to unwrap the `Result` type that `new_varkey` returns, which will panic if the key is invalid.
16    
17    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}