wolf_crypto/mac/hmac/algo.rs
1//! Collection of marker types and their associated keys which denote the hashing function.
2
3use core::marker::PhantomData;
4use wolf_crypto_sys::{
5 WC_SHA224, WC_SHA256, WC_SHA384, WC_SHA512,
6 WC_SHA3_224, WC_SHA3_256, WC_SHA3_384, WC_SHA3_512
7};
8
9use zeroize::Zeroize;
10
11use crate::sealed::HmacSealed as Sealed;
12use crate::sealed::HmacDigestSealed as SealedDigest;
13
14use crate::buf::InvalidSize;
15use crate::can_cast_u32;
16use crate::Fips;
17use core::fmt;
18
19non_fips! {
20 use wolf_crypto_sys::{WC_MD5, WC_SHA};
21}
22
23/// Represents a valid key size for the associated hashing algorithm.
24pub trait KeySz : Sealed {
25 /// Returns the associated size as a `u32`.
26 ///
27 /// This size is equivalent to the digest size of the hash function.
28 #[must_use]
29 fn size() -> u32;
30}
31
32/// Represents a valid key for the associated hashing algorithm..
33pub trait GenericKey : Sealed {
34 /// The desired size of the key.
35 type Size: KeySz;
36
37 #[doc(hidden)]
38 #[must_use]
39 fn ptr(&self) -> *const u8;
40
41 /// Returns the size of the key in bytes.
42 fn size(&self) -> u32;
43
44 /// Zeroes the memory of the key if is owned.
45 fn cleanup(self);
46}
47
48/// Represents the hex-encoded output digest of the `HMAC` hash functions.
49pub trait HexDigest : SealedDigest + AsRef<[u8]> + AsMut<[u8]> + Copy {
50 /// The associated hex-decoded digest type.
51 type Digest: Digest;
52
53 #[doc(hidden)]
54 #[must_use]
55 fn zeroes() -> Self;
56}
57
58/// Represents the output digest of the `HMAC` hash function.
59pub trait Digest : Sealed + AsRef<[u8]> + AsMut<[u8]> + Copy {
60 /// The associated hex-encoded digest type.
61 type Hex: HexDigest;
62
63 #[doc(hidden)]
64 #[must_use]
65 fn zeroes() -> Self;
66 /// Returns the size of the digest in bytes.
67 #[must_use]
68 fn size() -> u32;
69 #[doc(hidden)]
70 #[must_use]
71 fn ptr(&mut self) -> *mut u8;
72}
73
74/// Indicates the hashing algorithm to use with message authentication codes and key derivation
75/// functions.
76pub trait Hash : Sealed {
77 /// Represents the output digest of the hash function.
78 type Digest: Digest;
79
80 /// The associated key length for this hashing function.
81 ///
82 /// In [`RFC2104`, Section 3 `Keys`][1], it states that the key for `HMAC` can be of any length,
83 /// **however** keys less than length `L` (the length of the output (SHA256 being 256 bits)) are
84 /// strongly discouraged and considered insecure.
85 ///
86 /// This library does not support using keys which do not follow this recommendation in the
87 /// secure API. If you are not able to follow these best practices, see [`InsecureKey`],
88 /// though this is strongly discouraged.
89 ///
90 /// All modern usages of `HMAC`, for example in TLS, use the same key length as the digest
91 /// length (`L`).
92 ///
93 /// These recommendations remain unaltered for key derivation functions.
94 ///
95 /// ## Larger Keys
96 ///
97 /// As pointed out in [`RFC2104`, section 3 `Keys`][1] the provided key material may be larger
98 /// than the length of the output. This can be done via the [`KeySlice`] type. In general there
99 /// won't be any real advantage to this, however this is with an exception, as stated:
100 ///
101 /// ```txt
102 /// A longer key may be advisable if the randomness of the key is
103 /// considered weak.
104 /// ```
105 ///
106 /// Keys larger than the digest / hash output size will be hashed.
107 ///
108 /// [1]: https://www.rfc-editor.org/rfc/rfc2104#section-3
109 type KeyLen: KeySz;
110
111 /// Writes the algorithm name into `f`.
112 fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result;
113
114 #[doc(hidden)]
115 #[must_use]
116 fn type_id() -> core::ffi::c_int;
117}
118
119// Key sizes correspond to the digest size pursuant to RFC2104, and all relevant
120// NIST SP recommendations.
121macro_rules! make_digest {
122 ($(($name:ident, $sz:literal)),* $(,)?) => {
123 $(
124 impl SealedDigest for [u8; crate::ct::hex_encode_len($sz)] {}
125
126 impl HexDigest for [u8; crate::ct::hex_encode_len($sz)] {
127 type Digest = [u8; $sz];
128
129 #[inline]
130 fn zeroes() -> Self {
131 [0u8; crate::ct::hex_encode_len($sz)]
132 }
133 }
134
135 impl Sealed for [u8; $sz] {}
136 impl Digest for [u8; $sz] {
137 type Hex = [u8; crate::ct::hex_encode_len($sz)];
138
139 #[inline]
140 fn zeroes() -> Self {
141 [0u8; $sz]
142 }
143 #[inline]
144 fn size() -> u32 {
145 $sz
146 }
147 #[inline]
148 fn ptr(&mut self) -> *mut u8 {
149 self.as_mut_ptr()
150 }
151 }
152
153 #[doc = concat!(
154 "Generic representation of a ", stringify!($sz), " byte key for `HMAC` or KDFs."
155 )]
156 #[doc = ""]
157 #[doc = "It is strongly recommended that the key length is equivalent "]
158 #[doc = "to the hash functions digest size. (SHA256 means 256 bit (32 byte) key)."]
159 pub struct $name;
160
161 impl Sealed for $name {}
162 impl KeySz for $name {
163 #[inline]
164 fn size() -> u32 {
165 Self::SIZE
166 }
167 }
168
169 impl $name {
170 /// The associated `u32` representation.
171 pub const SIZE: u32 = $sz;
172 pub(crate) const USIZE: usize = $sz;
173 }
174
175 impl GenericKey for [u8; $sz] {
176 type Size = $name;
177
178 #[inline]
179 fn ptr(&self) -> *const u8 {
180 self.as_ptr()
181 }
182
183 #[inline]
184 fn size(&self) -> u32 {
185 <$name>::SIZE
186 }
187
188 #[inline]
189 fn cleanup(mut self) {
190 self.zeroize();
191 }
192 }
193
194 impl Sealed for &[u8; $sz] {}
195
196 impl GenericKey for &[u8; $sz] {
197 type Size = $name;
198
199 #[inline]
200 fn ptr(&self) -> *const u8 {
201 self.as_ptr()
202 }
203
204 #[inline]
205 fn size(&self) -> u32 {
206 <$name>::SIZE
207 }
208
209 #[inline(always)]
210 fn cleanup(self) {}
211 }
212 )*
213 };
214}
215
216/// Represents a key for the associated hashing algorithm which has a length greater than or
217/// equal to the length of the hash function's digest.
218#[repr(transparent)]
219pub struct KeySlice<'k, SZ: KeySz> {
220 inner: &'k [u8],
221 _min_size: PhantomData<SZ>
222}
223
224impl<'k, SZ: KeySz> Sealed for KeySlice<'k, SZ> {}
225
226impl<'k, SZ: KeySz> GenericKey for KeySlice<'k, SZ> {
227 type Size = SZ;
228
229 #[inline]
230 fn ptr(&self) -> *const u8 {
231 self.inner.as_ptr()
232 }
233
234 #[inline]
235 fn size(&self) -> u32 {
236 // KeySlice cannot be constructed with a slice which has a length greater than u32::MAX.
237 self.inner.len() as u32
238 }
239
240 #[inline(always)]
241 fn cleanup(self) {}
242}
243
244impl<'k, SZ: KeySz> KeySlice<'k, SZ> {
245 /// Try creating a new `KeySlice` instance.
246 ///
247 /// # Errors
248 ///
249 /// - If the length of the `slice` is less than the [`SZ::size`][1].
250 /// - If the length of the `slice` is greater than [`u32::MAX`].
251 ///
252 /// [1]: KeySz::size
253 #[inline]
254 pub fn new(slice: &'k [u8]) -> Result<Self, InvalidSize> {
255 if slice.len() < SZ::size() as usize || !can_cast_u32(slice.len()) {
256 Err(InvalidSize)
257 } else {
258 Ok(Self { inner: slice, _min_size: PhantomData })
259 }
260 }
261}
262
263impl<'k, SZ: KeySz> TryFrom<&'k [u8]> for KeySlice<'k, SZ> {
264 type Error = InvalidSize;
265
266 /// Try creating a new `KeySlice` instance.
267 ///
268 /// # Errors
269 ///
270 /// - If the length of the `slice` is less than the [`SZ::size`][1].
271 /// - If the length of the `slice` is greater than [`u32::MAX`].
272 ///
273 /// [1]: KeySz::size
274 #[inline]
275 fn try_from(value: &'k [u8]) -> Result<Self, Self::Error> {
276 Self::new(value)
277 }
278}
279
280/// Represents a key associated with the desired hashing function which can be **insecure**.
281///
282/// # Security
283///
284/// It is **not recommended** to use this unless you have a very good reason. This reason in general
285/// should be legacy system compatibility, modern systems without this constant
286/// **should not leverage this**, instead, use the [`KeySlice`], or provide the exact key
287/// corresponding to the digest size of the underlying hashing function.
288///
289/// # FIPS Compliance
290///
291/// Using this plays into `FIPS` compliance, **without** this crate's `allow-non-fips` feature
292/// enabled, this **cannot be constructed with a key smaller than the acceptable FIPS standard**
293/// of 14 bytes.
294///
295/// For more information, See [FIPS 198-1, Section 3 Cryptographic Keys][1] reference to
296/// [NIST SP 800-107][2]. Which discusses this minimum security strength of 112 bits (14 bytes) in
297/// [SP 800-107, Section 5.2 Digital Signatures][3] and [SP 800-107 Section 5.3.2 The HMAC Key][4].
298///
299/// [1]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf#%5B%7B%22num%22%3A20%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C0%2C792%2Cnull%5D
300/// [2]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-107r1.pdf
301/// [3]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-107r1.pdf#%5B%7B%22num%22%3A28%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C88%2C463%2C0%5D
302/// [4]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-107r1.pdf#%5B%7B%22num%22%3A35%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C88%2C139%2C0%5D
303#[repr(transparent)]
304pub struct InsecureKey<'k, SZ: KeySz> {
305 inner: &'k [u8],
306 _min_size: PhantomData<SZ>
307}
308
309impl<'k, SZ: KeySz> InsecureKey<'k, SZ> {
310 /// Minimum Size without FIPS requirement (1)
311 #[cfg(feature = "allow-non-fips")]
312 const MIN_SIZE: usize = 1;
313 /// Minimum Size with FIPS requirement (14)
314 #[cfg(not(feature = "allow-non-fips"))]
315 const MIN_SIZE: usize = 14;
316
317 #[inline]
318 #[must_use]
319 const fn new_predicate(len: usize) -> bool {
320 (len >= Self::MIN_SIZE) && can_cast_u32(len)
321 }
322
323 /// Create a new [`InsecureKey`] instance.
324 ///
325 /// # Security
326 ///
327 /// Please read the [`InsecureKey`]'s type documentation regarding security, and why it is
328 /// strongly recommended to use safer, more secure alternatives like [`KeySlice`] or passing
329 /// a key of the underlying hash functions digest length for compile-time checks.
330 ///
331 /// # Errors
332 ///
333 /// This will return `InvalidSize` on conditions dependent on the `allow-non-fips` feature
334 /// flag.
335 ///
336 /// - `allow-non-fips` enabled:
337 /// This will return `InvalidSize` if the provided key is empty.
338 /// - `allow-non-fips` disabled:
339 /// Pursuant to the FIPS requirements for HMAC and KDFs (for more information again read the
340 /// [`InsecureKey`]'s type documentation), this will return `InvalidSize` if the provided
341 /// key is shorter than the minimum acceptable FIPS standard of 14 bytes.
342 /// - any configuration:
343 /// Regardless of the enabled feature flags, if the length of the key is greater than
344 /// [`u32::MAX`] this will return `InvalidSize`.
345 pub const fn new(slice: &'k [u8]) -> Result<Self, InvalidSize> {
346 if Self::new_predicate(slice.len()) {
347 Ok(Self { inner: slice, _min_size: PhantomData })
348 } else {
349 Err(InvalidSize)
350 }
351 }
352}
353
354impl<'k, SZ: KeySz> Sealed for InsecureKey<'k, SZ> {}
355
356impl<'k, SZ: KeySz> GenericKey for InsecureKey<'k, SZ> {
357 type Size = SZ;
358
359 #[inline]
360 fn ptr(&self) -> *const u8 {
361 self.inner.as_ptr()
362 }
363
364 #[inline]
365 fn size(&self) -> u32 {
366 // InsecureKey cannot be constructed with a slice which has a length greater than u32::MAX.
367 self.inner.len() as u32
368 }
369
370 #[inline(always)]
371 fn cleanup(self) {}
372}
373
374impl<'k, SZ: KeySz> TryFrom<&'k [u8]> for InsecureKey<'k, SZ> {
375 type Error = InvalidSize;
376
377 /// Create a new [`InsecureKey`] instance.
378 ///
379 /// # Security
380 ///
381 /// Please read the [`InsecureKey`]'s type documentation regarding security, and why it is
382 /// strongly recommended to use safer, more secure alternatives like [`KeySlice`] or passing
383 /// a key of the underlying hash functions digest length for compile-time checks.
384 ///
385 /// # Errors
386 ///
387 /// This will return `InvalidSize` on conditions dependent on the `allow-non-fips` feature
388 /// flag.
389 ///
390 /// - `allow-non-fips` enabled:
391 /// This will return `InvalidSize` if the provided key is empty.
392 /// - `allow-non-fips` disabled:
393 /// Pursuant to the FIPS requirements for HMAC and KDFs (for more information again read the
394 /// [`InsecureKey`]'s type documentation), this will return `InvalidSize` if the provided
395 /// key is shorter than the minimum acceptable FIPS standard of 14 bytes.
396 /// - any configuration:
397 /// Regardless of the enabled feature flags, if the length of the key is greater than
398 /// [`u32::MAX`] this will return `InvalidSize`.
399 #[inline]
400 fn try_from(value: &'k [u8]) -> Result<Self, Self::Error> {
401 Self::new(value)
402 }
403}
404
405macro_rules! make_algo_type {
406 ($((
407 $(#[$meta:meta])*
408 $name:ident,
409 $sz:ident,
410 $wc_ty:ident
411 $(, $fips_trait:ident)?
412 )),* $(,)?) => {
413 $(
414 $(#[$meta])*
415 pub struct $name;
416 impl Sealed for $name {}
417 impl $crate::sealed::Sealed for $name {}
418 $(
419 impl $crate::sealed::FipsSealed for $name {}
420 impl $fips_trait for $name {}
421 )?
422
423 impl Hash for $name {
424 type Digest = [u8; $sz::USIZE];
425 type KeyLen = $sz;
426
427 #[doc = concat!("Writes \"", stringify!($name), "\" to `f`.")]
428 #[inline]
429 fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
430 f.write_str(stringify!($name))
431 }
432
433 #[inline]
434 fn type_id() -> ::core::ffi::c_int {
435 // This is a silly assertion as the maximum constant for wc_ty is 13.
436 debug_assert!($wc_ty <= i32::MAX as ::core::ffi::c_uint);
437 $wc_ty as ::core::ffi::c_int
438 }
439 }
440
441 impl Sealed for $crate::hash::$name {}
442
443 impl Hash for $crate::hash::$name {
444 type Digest = [u8; $sz::USIZE];
445 type KeyLen = $sz;
446
447 #[doc = concat!("Writes \"", stringify!($name), "\" to `f`.")]
448 #[inline]
449 fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
450 f.write_str(stringify!($name))
451 }
452
453 #[inline]
454 fn type_id() -> ::core::ffi::c_int {
455 // This is a silly assertion as the maximum constant for wc_ty is 13.
456 debug_assert!($wc_ty <= i32::MAX as ::core::ffi::c_uint);
457 $wc_ty as ::core::ffi::c_int
458 }
459 }
460 )*
461 };
462}
463
464#[cfg_attr(docsrs, doc(cfg(feature = "allow-non-fips")))]
465#[cfg(feature = "allow-non-fips")]
466make_digest! { (U16, 16), (U20, 20) }
467
468make_digest! { (U28, 28), (U32, 32), (U48, 48), (U64, 64) }
469
470#[cfg_attr(docsrs, doc(cfg(feature = "allow-non-fips")))]
471#[cfg(feature = "allow-non-fips")]
472make_algo_type! {
473 (
474 /// The `MD5` Hash Function Marker Type.
475 ///
476 /// `MD5` should be [considered cryptographically broken and unsuitable for further use][1].
477 /// Collision attacks against `MD5` are both practical and trivial, theoretical attacks
478 /// against `MD5` have been found.
479 ///
480 /// `MD5` is included in this library for legacy reasons only.
481 ///
482 /// [1]: https://www.kb.cert.org/vuls/id/836068
483 Md5, U16, WC_MD5
484 ),
485 (
486 /// The `SHA-1` Hash Function Marker Type.
487 ///
488 /// The SHA-1 algorithm is included in this library for legacy reasons only. It is
489 /// cryptographically broken and should not be used for any security-critical or modern
490 /// applications, especially digital signatures or certificate validation.
491 ///
492 /// The U.S. National Institute of Standards and Technology (NIST) has officially deprecated
493 /// SHA-1 for all digital signature use cases as of 2011. As of 2022, NIST recommends
494 /// transitioning all applications from SHA-1 to SHA-2 or SHA-3 family of hash functions.
495 Sha, U20, WC_SHA
496 )
497}
498
499make_algo_type! {
500 (
501 /// The `SHA224` Hash Function Marker Type.
502 Sha224, U28, WC_SHA224, Fips
503 ),
504 (
505 /// The `SHA256` Hash Function Marker Type.
506 Sha256, U32, WC_SHA256, Fips
507 ),
508 (
509 /// The `SHA384` Hash Function Marker Type.
510 Sha384, U48, WC_SHA384, Fips
511 ),
512 (
513 /// The `SHA512` Hash Function Marker Type.
514 Sha512, U64, WC_SHA512, Fips
515 ),
516 (
517 /// The `SHA3-224` Hash Function Marker Type.
518 Sha3_224, U28, WC_SHA3_224, Fips
519 ),
520 (
521 /// The `SHA3-256` Hash Function Marker Type.
522 Sha3_256, U32, WC_SHA3_256, Fips
523 ),
524 (
525 /// The `SHA3-384` Hash Function Marker Type.
526 Sha3_384, U48, WC_SHA3_384, Fips
527 ),
528 (
529 /// The `SHA3-512` Hash Function Marker Type.
530 Sha3_512, U64, WC_SHA3_512, Fips
531 )
532}