jwt_compact/alg/
hmacs.rs

1//! JWT algorithms based on HMACs.
2
3use core::{fmt, num::NonZeroUsize};
4
5use hmac::{
6    Hmac, KeyInit as _, Mac as _,
7    digest::{CtOutput, crypto_common::typenum::Unsigned},
8};
9use rand_core::{CryptoRng, RngCore};
10use sha2::{
11    Sha256, Sha384, Sha512,
12    digest::{Output, OutputSizeUser, core_api::BlockSizeUser},
13};
14use smallvec::{SmallVec, smallvec};
15use zeroize::Zeroize;
16
17use crate::{
18    Algorithm, AlgorithmSignature,
19    alg::{SecretBytes, SigningKey, StrongKey, VerifyingKey, WeakKeyError},
20    alloc::Cow,
21    jwk::{JsonWebKey, JwkError, KeyType},
22};
23
24macro_rules! define_hmac_signature {
25    (
26        $(#[$($attr:meta)+])*
27        struct $name:ident<$digest:ident>;
28    ) => {
29        $(#[$($attr)+])*
30        #[derive(Clone, PartialEq, Eq)]
31        pub struct $name(CtOutput<Hmac<$digest>>);
32
33        impl fmt::Debug for $name {
34            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
35                formatter.debug_tuple(stringify!($name)).field(&"_").finish()
36            }
37        }
38
39        impl AlgorithmSignature for $name {
40            const LENGTH: Option<NonZeroUsize> =
41                NonZeroUsize::new(<$digest as OutputSizeUser>::OutputSize::USIZE);
42
43            fn try_from_slice(bytes: &[u8]) -> anyhow::Result<Self> {
44                let bytes: Output<$digest> = bytes.try_into()?;
45                Ok(Self(CtOutput::new(bytes)))
46            }
47
48            fn as_bytes(&self) -> Cow<'_, [u8]> {
49                Cow::Owned(self.0.clone().into_bytes().to_vec())
50            }
51        }
52    };
53}
54
55define_hmac_signature!(
56    /// Signature produced by the [`Hs256`] algorithm.
57    struct Hs256Signature<Sha256>;
58);
59define_hmac_signature!(
60    /// Signature produced by the [`Hs384`] algorithm.
61    struct Hs384Signature<Sha384>;
62);
63define_hmac_signature!(
64    /// Signature produced by the [`Hs512`] algorithm.
65    struct Hs512Signature<Sha512>;
66);
67
68macro_rules! define_hmac_key {
69    (
70        $(#[$($attr:meta)+])*
71        struct $name:ident<$digest:ident>([u8; $buffer_size:expr]);
72    ) => {
73        $(#[$($attr)+])*
74        #[derive(Clone, Zeroize)]
75        #[zeroize(drop)]
76        pub struct $name(pub(crate) SmallVec<[u8; $buffer_size]>);
77
78        impl fmt::Debug for $name {
79            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
80                formatter.debug_tuple(stringify!($name)).field(&"_").finish()
81            }
82        }
83
84        impl $name {
85            /// Generates a random key using a cryptographically secure RNG.
86            pub fn generate<R: CryptoRng + RngCore>(rng: &mut R) -> StrongKey<Self> {
87                let mut key = $name(smallvec![0; <$digest as BlockSizeUser>::BlockSize::to_usize()]);
88                rng.fill_bytes(&mut key.0);
89                StrongKey(key)
90            }
91
92            /// Creates a key from the specified `bytes`.
93            pub fn new(bytes: impl AsRef<[u8]>) -> Self {
94                Self(bytes.as_ref().into())
95            }
96
97            /// Computes HMAC with this key and the specified `message`.
98            fn hmac(&self, message: impl AsRef<[u8]>) -> CtOutput<Hmac<$digest>> {
99                let mut hmac = Hmac::<$digest>::new_from_slice(&self.0)
100                    .expect("HMACs work with any key size");
101                hmac.update(message.as_ref());
102                hmac.finalize()
103            }
104        }
105
106        impl From<&[u8]> for $name {
107            fn from(bytes: &[u8]) -> Self {
108                $name(bytes.into())
109            }
110        }
111
112        impl AsRef<[u8]> for $name {
113            fn as_ref(&self) -> &[u8] {
114                &self.0
115            }
116        }
117
118        impl AsMut<[u8]> for $name {
119            fn as_mut(&mut self) -> &mut [u8] {
120                &mut self.0
121            }
122        }
123
124        impl TryFrom<$name> for StrongKey<$name> {
125            type Error = WeakKeyError<$name>;
126
127            fn try_from(value: $name) -> Result<Self, Self::Error> {
128                if value.0.len() >= <$digest as BlockSizeUser>::BlockSize::to_usize() {
129                    Ok(StrongKey(value))
130                } else {
131                    Err(WeakKeyError(value))
132                }
133            }
134        }
135    };
136}
137
138define_hmac_key! {
139    /// Signing / verifying key for `HS256` algorithm. Zeroed on drop.
140    struct Hs256Key<Sha256>([u8; 64]);
141}
142define_hmac_key! {
143    /// Signing / verifying key for `HS384` algorithm. Zeroed on drop.
144    struct Hs384Key<Sha384>([u8; 128]);
145}
146define_hmac_key! {
147    /// Signing / verifying key for `HS512` algorithm. Zeroed on drop.
148    struct Hs512Key<Sha512>([u8; 128]);
149}
150
151/// `HS256` signing algorithm.
152///
153/// See [RFC 7518] for the algorithm specification.
154///
155/// [RFC 7518]: https://tools.ietf.org/html/rfc7518#section-3.2
156#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
157pub struct Hs256;
158
159impl Algorithm for Hs256 {
160    type SigningKey = Hs256Key;
161    type VerifyingKey = Hs256Key;
162    type Signature = Hs256Signature;
163
164    fn name(&self) -> Cow<'static, str> {
165        Cow::Borrowed("HS256")
166    }
167
168    fn sign(&self, signing_key: &Self::SigningKey, message: &[u8]) -> Self::Signature {
169        Hs256Signature(signing_key.hmac(message))
170    }
171
172    fn verify_signature(
173        &self,
174        signature: &Self::Signature,
175        verifying_key: &Self::VerifyingKey,
176        message: &[u8],
177    ) -> bool {
178        verifying_key.hmac(message) == signature.0
179    }
180}
181
182/// `HS384` signing algorithm.
183///
184/// See [RFC 7518] for the algorithm specification.
185///
186/// [RFC 7518]: https://tools.ietf.org/html/rfc7518#section-3.2
187#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
188pub struct Hs384;
189
190impl Algorithm for Hs384 {
191    type SigningKey = Hs384Key;
192    type VerifyingKey = Hs384Key;
193    type Signature = Hs384Signature;
194
195    fn name(&self) -> Cow<'static, str> {
196        Cow::Borrowed("HS384")
197    }
198
199    fn sign(&self, signing_key: &Self::SigningKey, message: &[u8]) -> Self::Signature {
200        Hs384Signature(signing_key.hmac(message))
201    }
202
203    fn verify_signature(
204        &self,
205        signature: &Self::Signature,
206        verifying_key: &Self::VerifyingKey,
207        message: &[u8],
208    ) -> bool {
209        verifying_key.hmac(message) == signature.0
210    }
211}
212
213/// `HS512` signing algorithm.
214///
215/// See [RFC 7518] for the algorithm specification.
216///
217/// [RFC 7518]: https://tools.ietf.org/html/rfc7518#section-3.2
218#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
219pub struct Hs512;
220
221impl Algorithm for Hs512 {
222    type SigningKey = Hs512Key;
223    type VerifyingKey = Hs512Key;
224    type Signature = Hs512Signature;
225
226    fn name(&self) -> Cow<'static, str> {
227        Cow::Borrowed("HS512")
228    }
229
230    fn sign(&self, signing_key: &Self::SigningKey, message: &[u8]) -> Self::Signature {
231        Hs512Signature(signing_key.hmac(message))
232    }
233
234    fn verify_signature(
235        &self,
236        signature: &Self::Signature,
237        verifying_key: &Self::VerifyingKey,
238        message: &[u8],
239    ) -> bool {
240        verifying_key.hmac(message) == signature.0
241    }
242}
243
244macro_rules! impl_key_traits {
245    ($key:ident<$alg:ident>) => {
246        impl SigningKey<$alg> for $key {
247            fn from_slice(raw: &[u8]) -> anyhow::Result<Self> {
248                Ok(Self::from(raw))
249            }
250
251            fn to_verifying_key(&self) -> Self {
252                self.clone()
253            }
254
255            fn as_bytes(&self) -> SecretBytes<'_> {
256                SecretBytes::borrowed(self.as_ref())
257            }
258        }
259
260        impl VerifyingKey<$alg> for $key {
261            fn from_slice(raw: &[u8]) -> anyhow::Result<Self> {
262                Ok(Self::from(raw))
263            }
264
265            fn as_bytes(&self) -> Cow<'_, [u8]> {
266                Cow::Borrowed(self.as_ref())
267            }
268        }
269
270        impl<'a> From<&'a $key> for JsonWebKey<'a> {
271            fn from(key: &'a $key) -> JsonWebKey<'a> {
272                JsonWebKey::Symmetric {
273                    secret: SecretBytes::borrowed(key.as_ref()),
274                }
275            }
276        }
277
278        impl TryFrom<&JsonWebKey<'_>> for $key {
279            type Error = JwkError;
280
281            fn try_from(jwk: &JsonWebKey<'_>) -> Result<Self, Self::Error> {
282                match jwk {
283                    JsonWebKey::Symmetric { secret } => Ok(Self::new(secret)),
284                    _ => Err(JwkError::key_type(jwk, KeyType::Symmetric)),
285                }
286            }
287        }
288    };
289}
290
291impl_key_traits!(Hs256Key<Hs256>);
292impl_key_traits!(Hs384Key<Hs384>);
293impl_key_traits!(Hs512Key<Hs512>);