http_sig/
algorithm.rs

1use std::fmt::Debug;
2
3use hmac::{Hmac, Mac};
4use sha2::{Digest, Sha256, Sha512};
5
6/// Implements the signing half of an HTTP signature algorithm. For symmetric
7/// algorithms the same type implements both signing and verification.
8pub trait HttpSignatureSign: Debug + Send + Sync + 'static {
9    /// Returns the encoded signature, ready for inclusion in the HTTP Authorization
10    /// header. For all currently supported signature schemes, the encoding is
11    /// specified to be base64.
12    fn http_sign(&self, bytes_to_sign: &[u8]) -> String;
13}
14
15/// Implements the verification half of an HTTP signature algorithm. For symmetric
16/// algorithms the same type implements both signing and verification.
17pub trait HttpSignatureVerify: Debug + Send + Sync + 'static {
18    /// Returns true if the signature is valid for the provided content. The
19    /// implementation should be sure to perform any comparisons in constant
20    /// time.
21    fn http_verify(&self, bytes_to_verify: &[u8], signature: &str) -> bool;
22}
23
24/// Implementations of this trait correspond to digest algorithms
25/// listed here:
26/// https://www.iana.org/assignments/http-dig-alg/http-dig-alg.xhtml
27pub trait HttpDigest: Debug + Send + Sync + 'static {
28    /// Must return the name exactly as specified in the above list of HTTP
29    /// digest algorithms.
30    fn name(&self) -> &str;
31    /// Returns the encoded digest, ready for inclusion in the HTTP Digest
32    /// header. The encoding to use is specified in the above list of HTTP digest
33    /// algorithms.
34    fn http_digest(&self, bytes_to_digest: &[u8]) -> String;
35}
36
37macro_rules! hmac_signature {
38    ($typename:ident($algorithm:ident) = $name:literal) => {
39        #[doc = "Implementation of the '"]
40        #[doc = $name]
41        #[doc = "' HTTP signature scheme."]
42        #[derive(Debug)]
43        pub struct $typename(Hmac<$algorithm>);
44
45        impl $typename {
46            /// Create a new instance of the signature scheme using the
47            /// provided key.
48            pub fn new(key: &[u8]) -> Self {
49                Self(Hmac::new_from_slice(key).expect("Hmac construction should be infallible"))
50            }
51        }
52
53        impl HttpSignatureSign for $typename {
54            fn http_sign(&self, bytes_to_sign: &[u8]) -> String {
55                let mut hmac = self.0.clone();
56                hmac.update(bytes_to_sign);
57                let tag = hmac.finalize().into_bytes();
58                base64::encode(tag)
59            }
60        }
61        impl HttpSignatureVerify for $typename {
62            fn http_verify(&self, bytes_to_verify: &[u8], signature: &str) -> bool {
63                let tag = match base64::decode(signature) {
64                    Ok(tag) => tag,
65                    Err(_) => return false,
66                };
67                let mut hmac = self.0.clone();
68                hmac.update(bytes_to_verify);
69                hmac.verify_slice(&tag).is_ok()
70            }
71        }
72    };
73}
74
75hmac_signature!(HmacSha256(Sha256) = "hmac-sha256");
76hmac_signature!(HmacSha512(Sha512) = "hmac-sha512");
77
78impl HttpDigest for Sha256 {
79    fn name(&self) -> &str {
80        "SHA-256"
81    }
82    fn http_digest(&self, bytes_to_digest: &[u8]) -> String {
83        base64::encode(Self::digest(bytes_to_digest))
84    }
85}
86
87impl HttpDigest for Sha512 {
88    fn name(&self) -> &str {
89        "SHA-512"
90    }
91    fn http_digest(&self, bytes_to_digest: &[u8]) -> String {
92        base64::encode(Self::digest(bytes_to_digest))
93    }
94}
95
96#[cfg(feature = "openssl")]
97mod openssl;
98#[cfg(feature = "openssl")]
99pub use self::openssl::*;
100
101#[cfg(all(not(feature = "openssl"), feature = "ring"))]
102mod ring;
103#[cfg(all(not(feature = "openssl"), feature = "ring"))]
104pub use self::ring::*;