httpsig/crypto/
symmetric.rs

1use super::AlgorithmName;
2use crate::{
3  error::{HttpSigError, HttpSigResult},
4  trace::*,
5};
6use base64::{engine::general_purpose, Engine as _};
7use hmac::{Hmac, Mac};
8use sha2::{Digest, Sha256};
9
10type HmacSha256 = Hmac<sha2::Sha256>;
11
12/* -------------------------------- */
13#[derive(Debug, Clone)]
14/// Shared key for http signature
15/// Name conventions follow [Section 6.2.2, RFC9421](https://datatracker.ietf.org/doc/html/rfc9421#section-6.2.2)
16pub enum SharedKey {
17  /// hmac-sha256
18  HmacSha256(Vec<u8>),
19}
20
21impl SharedKey {
22  /// Create a new shared key from base64 encoded string
23  pub fn from_base64(key: &str) -> HttpSigResult<Self> {
24    debug!("Create SharedKey from base64 string");
25    let key = general_purpose::STANDARD.decode(key)?;
26    Ok(SharedKey::HmacSha256(key))
27  }
28}
29
30impl super::SigningKey for SharedKey {
31  /// Sign the data
32  fn sign(&self, data: &[u8]) -> HttpSigResult<Vec<u8>> {
33    match self {
34      SharedKey::HmacSha256(key) => {
35        debug!("Sign HmacSha256");
36        let mut mac = HmacSha256::new_from_slice(key).unwrap();
37        mac.update(data);
38        Ok(mac.finalize().into_bytes().to_vec())
39      }
40    }
41  }
42  /// Get the key id
43  fn key_id(&self) -> String {
44    use super::VerifyingKey;
45    <Self as VerifyingKey>::key_id(self)
46  }
47  /// Get the algorithm name
48  fn alg(&self) -> AlgorithmName {
49    use super::VerifyingKey;
50    <Self as VerifyingKey>::alg(self)
51  }
52}
53impl super::VerifyingKey for SharedKey {
54  /// Verify the mac
55  fn verify(&self, data: &[u8], expected_mac: &[u8]) -> HttpSigResult<()> {
56    use super::SigningKey;
57    debug!("Verify HmacSha256");
58    let calcurated_mac = self.sign(data)?;
59    if calcurated_mac == expected_mac {
60      Ok(())
61    } else {
62      Err(HttpSigError::InvalidSignature("Invalid MAC".to_string()))
63    }
64  }
65
66  /// Get the key id
67  fn key_id(&self) -> String {
68    match self {
69      SharedKey::HmacSha256(key) => {
70        let mut hasher = <Sha256 as Digest>::new();
71        hasher.update(key);
72        let hash = hasher.finalize();
73        general_purpose::STANDARD.encode(hash)
74      }
75    }
76  }
77  /// Get the algorithm name
78  fn alg(&self) -> AlgorithmName {
79    match self {
80      SharedKey::HmacSha256(_) => AlgorithmName::HmacSha256,
81    }
82  }
83}
84
85#[cfg(test)]
86mod tests {
87  use super::*;
88
89  #[test]
90  fn symmetric_key_works() {
91    use super::super::{SigningKey, VerifyingKey};
92    let inner = b"01234567890123456789012345678901";
93    let key = SharedKey::HmacSha256(inner.to_vec());
94    let data = b"hello";
95    let signature = key.sign(data).unwrap();
96    let res = key.verify(data, &signature);
97    assert!(res.is_ok());
98  }
99}