jwt_compact_frame/alg/
hmacs.rs1use 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 struct Hs256Signature<Sha256>;
65);
66define_hmac_signature!(
67 struct Hs384Signature<Sha384>;
69);
70define_hmac_signature!(
71 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 #[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 pub fn new(bytes: impl AsRef<[u8]>) -> Self {
102 Self(bytes.as_ref().into())
103 }
104
105 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 struct Hs256Key<Sha256>([u8; 64]);
149}
150define_hmac_key! {
151 struct Hs384Key<Sha384>([u8; 128]);
153}
154define_hmac_key! {
155 struct Hs512Key<Sha512>([u8; 128]);
157}
158
159#[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#[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#[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>);