1use 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 struct Hs256Signature<Sha256>;
58);
59define_hmac_signature!(
60 struct Hs384Signature<Sha384>;
62);
63define_hmac_signature!(
64 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 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 pub fn new(bytes: impl AsRef<[u8]>) -> Self {
94 Self(bytes.as_ref().into())
95 }
96
97 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 struct Hs256Key<Sha256>([u8; 64]);
141}
142define_hmac_key! {
143 struct Hs384Key<Sha384>([u8; 128]);
145}
146define_hmac_key! {
147 struct Hs512Key<Sha512>([u8; 128]);
149}
150
151#[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#[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#[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>);