Skip to main content

extrapaytr_crypto/
hmac_sha.rs

1use hmac::{Hmac, Mac};
2use sha2::{Sha256, Sha512};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
5pub enum CryptoError {
6    #[error("HMAC key has an invalid length")]
7    InvalidKeyLength,
8}
9
10/// Computes `HMAC-SHA256(key, message)`, returning the raw 32-byte digest.
11pub fn hmac_sha256(key: &[u8], message: &[u8]) -> Result<[u8; 32], CryptoError> {
12    let mut mac =
13        <Hmac<Sha256> as Mac>::new_from_slice(key).map_err(|_| CryptoError::InvalidKeyLength)?;
14    mac.update(message);
15    Ok(mac.finalize().into_bytes().into())
16}
17
18/// Computes `HMAC-SHA512(key, message)`, returning the raw 64-byte digest.
19pub fn hmac_sha512(key: &[u8], message: &[u8]) -> Result<[u8; 64], CryptoError> {
20    let mut mac =
21        <Hmac<Sha512> as Mac>::new_from_slice(key).map_err(|_| CryptoError::InvalidKeyLength)?;
22    mac.update(message);
23    Ok(mac.finalize().into_bytes().into())
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29
30    // RFC 4231 test case 1.
31    #[test]
32    fn hmac_sha256_rfc4231_case1() {
33        let key = [0x0bu8; 20];
34        let data = b"Hi There";
35        let expected =
36            hex::decode("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7")
37                .unwrap();
38        assert_eq!(hmac_sha256(&key, data).unwrap().to_vec(), expected);
39    }
40
41    // RFC 4231 test case 2.
42    #[test]
43    fn hmac_sha256_rfc4231_case2() {
44        let key = b"Jefe";
45        let data = b"what do ya want for nothing?";
46        let expected =
47            hex::decode("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843")
48                .unwrap();
49        assert_eq!(hmac_sha256(key, data).unwrap().to_vec(), expected);
50    }
51
52    // RFC 4231 test case 6 (key longer than block size).
53    #[test]
54    fn hmac_sha256_rfc4231_case6() {
55        let key = [0xaau8; 131];
56        let data = b"Test Using Larger Than Block-Size Key - Hash Key First";
57        let expected =
58            hex::decode("60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54")
59                .unwrap();
60        assert_eq!(hmac_sha256(&key, data).unwrap().to_vec(), expected);
61    }
62
63    // RFC 4231 test case 1 for HMAC-SHA512.
64    #[test]
65    fn hmac_sha512_rfc4231_case1() {
66        let key = [0x0bu8; 20];
67        let data = b"Hi There";
68        let expected = hex::decode(
69            "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854",
70        )
71        .unwrap();
72        assert_eq!(hmac_sha512(&key, data).unwrap().to_vec(), expected);
73    }
74
75    #[test]
76    fn empty_key_is_accepted_per_hmac_semantics() {
77        // HMAC pads/hashes the key regardless of length; an empty key is
78        // unusual but well-defined, not an error.
79        assert!(hmac_sha256(&[], b"message").is_ok());
80    }
81}