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}