jwt_compact_frame/alg/
hmacs.rs

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