jwt_compact_preview/alg/
hmacs.rs1use 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 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 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 struct Hs256Key<Sha256>([u8; 64]);
69}
70define_hmac_key! {
71 struct Hs384Key<Sha384>([u8; 128]);
73}
74define_hmac_key! {
75 struct Hs512Key<Sha512>([u8; 128]);
77}
78
79#[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#[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#[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}