Skip to main content

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(alg: &AlgorithmName, key: &str) -> HttpSigResult<Self> {
24    debug!("Create SharedKey from base64 string");
25    let key = general_purpose::STANDARD.decode(key)?;
26    match alg {
27      AlgorithmName::HmacSha256 => Ok(SharedKey::HmacSha256(key)),
28      _ => Err(HttpSigError::InvalidAlgorithmName(format!(
29        "Unsupported algorithm for SharedKey: {}",
30        alg
31      ))),
32    }
33  }
34}
35
36impl super::SigningKey for SharedKey {
37  /// Sign the data
38  fn sign(&self, data: &[u8]) -> HttpSigResult<Vec<u8>> {
39    match self {
40      SharedKey::HmacSha256(key) => {
41        debug!("Sign HmacSha256");
42        let mut mac = HmacSha256::new_from_slice(key).unwrap();
43        mac.update(data);
44        Ok(mac.finalize().into_bytes().to_vec())
45      }
46    }
47  }
48  /// Get the key id
49  fn key_id(&self) -> String {
50    use super::VerifyingKey;
51    <Self as VerifyingKey>::key_id(self)
52  }
53  /// Get the algorithm name
54  fn alg(&self) -> AlgorithmName {
55    use super::VerifyingKey;
56    <Self as VerifyingKey>::alg(self)
57  }
58}
59impl super::VerifyingKey for SharedKey {
60  /// Verify the mac
61  fn verify(&self, data: &[u8], expected_mac: &[u8]) -> HttpSigResult<()> {
62    match self {
63      SharedKey::HmacSha256(key) => {
64        debug!("Verify HmacSha256");
65        let mut mac = HmacSha256::new_from_slice(key).unwrap();
66        mac.update(data);
67        mac
68          .verify_slice(expected_mac)
69          .map_err(|_| HttpSigError::InvalidSignature("Invalid MAC".to_string()))
70      }
71    }
72  }
73
74  /// Get the key id
75  fn key_id(&self) -> String {
76    match self {
77      SharedKey::HmacSha256(key) => {
78        let mut hasher = <Sha256 as Digest>::new();
79        hasher.update(key);
80        let hash = hasher.finalize();
81        general_purpose::STANDARD.encode(hash)
82      }
83    }
84  }
85  /// Get the algorithm name
86  fn alg(&self) -> AlgorithmName {
87    match self {
88      SharedKey::HmacSha256(_) => AlgorithmName::HmacSha256,
89    }
90  }
91}
92
93#[cfg(test)]
94mod tests {
95  use super::*;
96
97  #[test]
98  fn symmetric_key_works() {
99    use super::super::{SigningKey, VerifyingKey};
100    let inner = b"01234567890123456789012345678901";
101    let key = SharedKey::HmacSha256(inner.to_vec());
102    let data = b"hello";
103    let signature = key.sign(data).unwrap();
104    let res = key.verify(data, &signature);
105    assert!(res.is_ok());
106  }
107}