Skip to main content

jwt_compact_preview/alg/
hmacs.rs

1use anyhow::bail;
2use hmac::crypto_mac::generic_array::{typenum::Unsigned, GenericArray};
3use hmac::{crypto_mac, Hmac, Mac as _, NewMac};
4use rand_core::{CryptoRng, RngCore};
5use sha2::{digest::BlockInput, Sha256, Sha384, Sha512};
6use smallvec::{smallvec, SmallVec};
7use zeroize::Zeroize;
8
9use std::{borrow::Cow, fmt};
10
11use crate::{Algorithm, AlgorithmSignature};
12
13macro_rules! define_hmac_key {
14    (
15        $(#[$($attr:meta)+])*
16        struct $name:ident<$digest:ident>([u8; $buffer_size:expr]);
17    ) => {
18        $(#[$($attr)+])*
19        #[derive(Clone, Zeroize)]
20        #[zeroize(drop)]
21        pub struct $name(pub(crate) SmallVec<[u8; $buffer_size]>);
22
23        impl fmt::Debug for $name {
24            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
25                formatter.debug_tuple(stringify!($name)).field(&"_").finish()
26            }
27        }
28
29        impl $name {
30            /// Generates a random key using a cryptographically secure RNG.
31            pub fn generate<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
32                let mut key = $name(smallvec![0; <$digest as BlockInput>::BlockSize::to_usize()]);
33                rng.fill_bytes(&mut key.0);
34                key
35            }
36
37            /// Computes HMAC with this key and the specified `message`.
38            pub fn hmac(&self, message: impl AsRef<[u8]>) -> crypto_mac::Output<Hmac<$digest>> {
39                let mut hmac = Hmac::<$digest>::new_varkey(&self.0)
40                    .expect("HMACs work with any key size");
41                hmac.update(message.as_ref());
42                hmac.finalize()
43            }
44        }
45
46        impl From<&[u8]> for $name {
47            fn from(bytes: &[u8]) -> Self {
48                $name(bytes.into())
49            }
50        }
51
52        impl AsRef<[u8]> for $name {
53            fn as_ref(&self) -> &[u8] {
54                &self.0
55            }
56        }
57
58        impl AsMut<[u8]> for $name {
59            fn as_mut(&mut self) -> &mut [u8] {
60                &mut self.0
61            }
62        }
63    };
64}
65
66define_hmac_key! {
67    /// Signing / verifying key for `HS256` algorithm. Zeroed on drop.
68    struct Hs256Key<Sha256>([u8; 64]);
69}
70define_hmac_key! {
71    /// Signing / verifying key for `HS384` algorithm. Zeroed on drop.
72    struct Hs384Key<Sha384>([u8; 128]);
73}
74define_hmac_key! {
75    /// Signing / verifying key for `HS512` algorithm. Zeroed on drop.
76    struct Hs512Key<Sha512>([u8; 128]);
77}
78
79/// `HS256` signing algorithm.
80///
81/// See [RFC 7518] for the algorithm specification.
82///
83/// [RFC 7518]: https://tools.ietf.org/html/rfc7518#section-3.2
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85pub struct Hs256;
86
87impl AlgorithmSignature for crypto_mac::Output<Hmac<Sha256>> {
88    fn try_from_slice(bytes: &[u8]) -> anyhow::Result<Self> {
89        if bytes.len() != 32 {
90            bail!("Invalid signature length");
91        }
92        Ok(crypto_mac::Output::new(GenericArray::clone_from_slice(
93            bytes,
94        )))
95    }
96
97    fn as_bytes(&self) -> Cow<'_, [u8]> {
98        Cow::Owned(self.clone().into_bytes().to_vec())
99    }
100}
101
102impl Algorithm for Hs256 {
103    type SigningKey = Hs256Key;
104    type VerifyingKey = Hs256Key;
105    type Signature = crypto_mac::Output<Hmac<Sha256>>;
106
107    fn name(&self) -> Cow<'static, str> {
108        Cow::Borrowed("HS256")
109    }
110
111    fn sign(&self, signing_key: &Self::SigningKey, message: &[u8]) -> Self::Signature {
112        signing_key.hmac(message)
113    }
114
115    fn verify_signature(
116        &self,
117        signature: &Self::Signature,
118        verifying_key: &Self::VerifyingKey,
119        message: &[u8],
120    ) -> bool {
121        verifying_key.hmac(message) == *signature
122    }
123}
124
125/// `HS384` signing algorithm.
126///
127/// See [RFC 7518] for the algorithm specification.
128///
129/// [RFC 7518]: https://tools.ietf.org/html/rfc7518#section-3.2
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
131pub struct Hs384;
132
133impl AlgorithmSignature for crypto_mac::Output<Hmac<Sha384>> {
134    fn try_from_slice(bytes: &[u8]) -> anyhow::Result<Self> {
135        if bytes.len() != 48 {
136            bail!("Invalid signature length");
137        }
138        Ok(crypto_mac::Output::new(GenericArray::clone_from_slice(
139            bytes,
140        )))
141    }
142
143    fn as_bytes(&self) -> Cow<'_, [u8]> {
144        Cow::Owned(self.clone().into_bytes().to_vec())
145    }
146}
147
148impl Algorithm for Hs384 {
149    type SigningKey = Hs384Key;
150    type VerifyingKey = Hs384Key;
151    type Signature = crypto_mac::Output<Hmac<Sha384>>;
152
153    fn name(&self) -> Cow<'static, str> {
154        Cow::Borrowed("HS384")
155    }
156
157    fn sign(&self, signing_key: &Self::SigningKey, message: &[u8]) -> Self::Signature {
158        signing_key.hmac(message)
159    }
160
161    fn verify_signature(
162        &self,
163        signature: &Self::Signature,
164        verifying_key: &Self::VerifyingKey,
165        message: &[u8],
166    ) -> bool {
167        verifying_key.hmac(message) == *signature
168    }
169}
170
171/// `HS512` signing algorithm.
172///
173/// See [RFC 7518] for the algorithm specification.
174///
175/// [RFC 7518]: https://tools.ietf.org/html/rfc7518#section-3.2
176#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
177pub struct Hs512;
178
179impl AlgorithmSignature for crypto_mac::Output<Hmac<Sha512>> {
180    fn try_from_slice(bytes: &[u8]) -> anyhow::Result<Self> {
181        if bytes.len() != 64 {
182            bail!("Invalid signature length");
183        }
184        Ok(crypto_mac::Output::new(GenericArray::clone_from_slice(
185            bytes,
186        )))
187    }
188
189    fn as_bytes(&self) -> Cow<'_, [u8]> {
190        Cow::Owned(self.clone().into_bytes().to_vec())
191    }
192}
193
194impl Algorithm for Hs512 {
195    type SigningKey = Hs512Key;
196    type VerifyingKey = Hs512Key;
197    type Signature = crypto_mac::Output<Hmac<Sha512>>;
198
199    fn name(&self) -> Cow<'static, str> {
200        Cow::Borrowed("HS512")
201    }
202
203    fn sign(&self, signing_key: &Self::SigningKey, message: &[u8]) -> Self::Signature {
204        signing_key.hmac(message)
205    }
206
207    fn verify_signature(
208        &self,
209        signature: &Self::Signature,
210        verifying_key: &Self::VerifyingKey,
211        message: &[u8],
212    ) -> bool {
213        verifying_key.hmac(message) == *signature
214    }
215}