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    match self {
57      SharedKey::HmacSha256(key) => {
58        debug!("Verify HmacSha256");
59        let mut mac = HmacSha256::new_from_slice(key).unwrap();
60        mac.update(data);
61        mac.verify_slice(expected_mac)
62          .map_err(|_| HttpSigError::InvalidSignature("Invalid MAC".to_string()))
63      }
64    }
65  }
66
67  /// Get the key id
68  fn key_id(&self) -> String {
69    match self {
70      SharedKey::HmacSha256(key) => {
71        let mut hasher = <Sha256 as Digest>::new();
72        hasher.update(key);
73        let hash = hasher.finalize();
74        general_purpose::STANDARD.encode(hash)
75      }
76    }
77  }
78  /// Get the algorithm name
79  fn alg(&self) -> AlgorithmName {
80    match self {
81      SharedKey::HmacSha256(_) => AlgorithmName::HmacSha256,
82    }
83  }
84}
85
86#[cfg(test)]
87mod tests {
88  use super::*;
89
90  #[test]
91  fn symmetric_key_works() {
92    use super::super::{SigningKey, VerifyingKey};
93    let inner = b"01234567890123456789012345678901";
94    let key = SharedKey::HmacSha256(inner.to_vec());
95    let data = b"hello";
96    let signature = key.sign(data).unwrap();
97    let res = key.verify(data, &signature);
98    assert!(res.is_ok());
99  }
100}