azure_core/
hmac.rs

1use crate::auth::Secret;
2#[cfg(any(feature = "hmac_rust", feature = "hmac_openssl"))]
3use crate::{
4    base64,
5    error::{ErrorKind, ResultExt},
6};
7
8/// Tries to create an HMAC SHA256 signature from the given `data` and `key`.
9/// The `key` is expected to be a base64 encoded string and will be decoded
10/// before using it for signing. The returned signature is also base64 encoded.
11///
12/// If both `hmac_rust` and `hmac_openssl` are enabled, use `hmac_openssl`.
13///
14/// # Errors
15/// - If the `key` is not a valid base64 encoded string.
16/// - If it fails to create the HMAC from the `key`.
17#[cfg(all(feature = "hmac_rust", not(feature = "hmac_openssl")))]
18pub fn hmac_sha256(data: &str, key: &Secret) -> crate::Result<String> {
19    use hmac::{Hmac, Mac};
20    use sha2::Sha256;
21    let key = base64::decode(key.secret())?;
22    let mut hmac = Hmac::<Sha256>::new_from_slice(&key)
23        .with_context(ErrorKind::DataConversion, || {
24            "failed to create hmac from key"
25        })?;
26    hmac.update(data.as_bytes());
27    let signature = hmac.finalize().into_bytes();
28    Ok(base64::encode(signature))
29}
30
31#[cfg(feature = "hmac_openssl")]
32pub fn hmac_sha256(data: &str, key: &Secret) -> crate::Result<String> {
33    use openssl::{error::ErrorStack, hash::MessageDigest, pkey::PKey, sign::Signer};
34
35    let dkey = base64::decode(key.secret())?;
36    let signature = || -> Result<Vec<u8>, ErrorStack> {
37        let pkey = PKey::hmac(&dkey)?;
38        let mut signer = Signer::new(MessageDigest::sha256(), &pkey)?;
39        signer.update(data.as_bytes())?;
40        signer.sign_to_vec()
41    }()
42    .with_context(ErrorKind::DataConversion, || {
43        "failed to create hmac from key"
44    })?;
45    Ok(base64::encode(signature))
46}
47
48#[cfg(not(any(feature = "hmac_rust", feature = "hmac_openssl")))]
49pub fn hmac_sha256(_data: &str, _key: &Secret) -> crate::Result<String> {
50    unimplemented!("An HMAC signing request was called without an hmac implementation.  Make sure to enable either the `hmac_rust` or `hmac_openssl` feature");
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_hmac_sign() {
59        let data = "create hmac signature for data";
60        let key = Secret::new("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
61
62        let sig = hmac_sha256(data, &key).unwrap();
63
64        let expected_sig = "D/y9XyIEdUzEbdV570h8dou/mfkbMA1lKCOPqPDPAd0=";
65        assert_eq!(sig, expected_sig);
66    }
67}