diff --git a/alpha_0.1.3_release_notes.md b/alpha_0.1.3_release_notes.md index 57f9e97c..dc060831 100644 --- a/alpha_0.1.3_release_notes.md +++ b/alpha_0.1.3_release_notes.md @@ -7,3 +7,7 @@ * bug fixes to the way SHA3/SHAKE handled absorbing and squeezing a partial final byte. * Design discussions about whether core::traits::XOF (in the abstract) should allow interleaving absorb -> squeeze -> absorb (ie "absorb-after-squeeze). Outcome: absorb-after-squeeze forbidden. Could be changed in the future. +* Re-arranged the HMAC and HKDF crates so that they are utility crates, and the pub types HMAC_SHA256, HMAC_SHA3_256, + HKDF_SHA256, and so on now live in the `bouncycastle_sha2::hmac`, `bouncycastle_sha3::hmac`, and + `bouncycastle_sha2::hkdf` namespaces. At the same time, adjustments were made to the MAX_SECURITY_STRENGTH of the HMAC + and HKDF algorithms because SP 800-107r1 allows them to be higher than what we had previously. \ No newline at end of file diff --git a/crypto/factory/tests/mac_factory_tests.rs b/crypto/factory/tests/mac_factory_tests.rs index 912a7587..c379ab02 100644 --- a/crypto/factory/tests/mac_factory_tests.rs +++ b/crypto/factory/tests/mac_factory_tests.rs @@ -10,16 +10,19 @@ mod hash_factory_tests { #[test] fn sha2_hash_tests() { - // HMAC-SHA224 - let key = KeyMaterial::<32>::from_bytes_as_type( - &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), + // HMAC-SHA224, RFC 4231 Test Case 6. MACFactory has no weak-key constructor, so this + // needs a vector whose key reaches the strength HMAC-SHA224 claims; Test Case 1's + // 20-byte key does not. + let key = KeyMaterial::<131>::from_bytes_as_type( + &hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + .unwrap(), KeyType::MACKey, ) .unwrap(); let hmac = MACFactory::new("HMAC-SHA224", &key).unwrap(); assert!(hmac.verify( - b"Hi There", - &hex::decode("896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22").unwrap(), + b"Test Using Larger Than Block-Size Key - Hash Key First", + &hex::decode("95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e").unwrap(), )); // TODO: at least one test for each type diff --git a/crypto/hkdf/src/lib.rs b/crypto/hkdf/src/lib.rs index 8d7dc8ac..51c6210f 100644 --- a/crypto/hkdf/src/lib.rs +++ b/crypto/hkdf/src/lib.rs @@ -47,6 +47,7 @@ //! use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; //! use bouncycastle_core::traits::{KDF, SuspendableKeyed}; //! use bouncycastle_hkdf::HKDF; +//! use bouncycastle_sha2::hmac::HMAC_SHA384Params; //! use bouncycastle_sha2::{SHA384, SUSPENDED_SHA512_STATE_LEN}; //! //! // SHA-384 is a member of the SHA-512 family, so its suspended state is the SHA-512 one. @@ -54,7 +55,7 @@ //! //! #[allow(non_camel_case_types)] //! pub type HKDF_SHA384 = -//! HKDF; +//! HKDF; //! //! pub const HKDF_SHA384_NAME: &str = "HKDF-SHA384"; //! @@ -115,7 +116,7 @@ use bouncycastle_core::suspendable_state::{add_lib_ver, check_lib_ver}; use bouncycastle_core::traits::{ Hash, HashAlgParams, KDF, MAC, SecurityStrength, Suspendable, SuspendableKeyed, }; -use bouncycastle_hmac::HMAC; +use bouncycastle_hmac::{HMAC, HMACParams}; use bouncycastle_utils::{max, min}; use std::marker::PhantomData; // Imports needed only for docs @@ -160,13 +161,14 @@ pub const MAX_HMAC_OUTPUT_LEN: usize = 64; #[derive(Clone)] pub struct HKDF< H: Hash + HashAlgParams + Default, + PARAMS: HMACParams, const HASH_STATE_LEN: usize, const HKDF_STATE_LEN: usize, > { // Optional because an HMAC cannot be constructed until a key is provided // to initialize it with. // None must correspond to a state of Uninitialized. - hmac: Option>, + hmac: Option>, entropy: HkdfEntropyTracker, state: HkdfStates, } @@ -244,16 +246,24 @@ impl HkdfEntropyTracker { } } -impl - Default for HKDF +impl< + H: Hash + HashAlgParams + Default, + PARAMS: HMACParams, + const HASH_STATE_LEN: usize, + const HKDF_STATE_LEN: usize, +> Default for HKDF { fn default() -> Self { Self::new() } } -impl - HKDF +impl< + H: Hash + HashAlgParams + Default, + PARAMS: HMACParams, + const HASH_STATE_LEN: usize, + const HKDF_STATE_LEN: usize, +> HKDF { /// Get a new, uninstantiated HKDF object. pub fn new() -> Self { @@ -399,7 +409,7 @@ impl::new(&prk_as_mac_key) + let mut hmac = HMAC::::new(&prk_as_mac_key) .map_err(|_| KeyMaterialError::GenericError("HMAC initialization failed"))?; hmac.do_update(&T[..t_len]); hmac.do_update(info); @@ -418,7 +428,7 @@ impl::new(&prk_as_mac_key)?; + let mut hmac = HMAC::::new(&prk_as_mac_key)?; hmac.do_update(&T[..t_len]); hmac.do_update(info); hmac.do_update(&[i]); @@ -488,7 +498,7 @@ impl::new_allow_weak_key(salt)?); + self.hmac = Some(HMAC::::new_allow_weak_key(salt)?); let additional_entropy = self.entropy.credit_entropy(salt); self.state = HkdfStates::Initialized; @@ -521,7 +531,7 @@ impl = self.hmac.as_mut().unwrap(); + let hmac_ref: &mut HMAC = self.hmac.as_mut().unwrap(); hmac_ref.do_update(ikm.ref_to_bytes()); // self.hmac.as_mut().unwrap().do_update(ikm.ref_to_bytes()); @@ -610,8 +620,12 @@ impl - KDF for HKDF +impl< + H: Hash + HashAlgParams + Default, + PARAMS: HMACParams, + const HASH_STATE_LEN: usize, + const HKDF_STATE_LEN: usize, +> KDF for HKDF { /// This invokes [`HKDF::extract_and_expand_out`] with a zero salt and using the provided key as ikm. /// This provides a fixed-length output, which may be truncated as needed. @@ -737,8 +751,8 @@ impl SuspendableKeyed - for HKDF +impl + SuspendableKeyed for HKDF where H: Hash + HashAlgParams + Default + Suspendable, { @@ -786,7 +800,7 @@ where let hmac = match state[3] { 0 => None, // infallible: the sub-slice is exactly HASH_STATE_LEN bytes by const construction. - 1 => Some(HMAC::::from_suspended( + 1 => Some(HMAC::::from_suspended( state[4..4 + HASH_STATE_LEN].try_into().unwrap(), salt, )?), diff --git a/crypto/hkdf/tests/hkdf_tests.rs b/crypto/hkdf/tests/hkdf_tests.rs index 896c0a08..993fc037 100644 --- a/crypto/hkdf/tests/hkdf_tests.rs +++ b/crypto/hkdf/tests/hkdf_tests.rs @@ -744,21 +744,22 @@ mod hkdf_tests { // A helper that exercises the full round-trip for one HKDF variant. A concrete `&KeyMaterial128` // works for `do_extract_init` (which wants a `Sized` `&impl KeyMaterialTrait`) and coerces to // `&dyn KeyMaterialTrait` for the serialization APIs. - fn round_trip( + fn round_trip( salt: &KeyMaterial128, part1: &[u8], part2: &[u8], ) where H: Hash + HashAlgParams + Default, - HKDF: Clone + SuspendableKeyed, + P: bouncycastle_hmac::HMACParams, + HKDF: Clone + SuspendableKeyed, { - let hkdf = HKDF::::new(); + let hkdf = HKDF::::new(); // it can be serialized pre-init, which is kinda a no-op, but at least it works. let serialized_state = hkdf.suspend(); assert_eq!(serialized_state.len(), LEN); let mut hkdf = - HKDF::::from_suspended(serialized_state, salt).unwrap(); + HKDF::::from_suspended(serialized_state, salt).unwrap(); hkdf.do_extract_init(salt).unwrap(); hkdf.do_extract_update_bytes(part1).unwrap(); @@ -775,19 +776,25 @@ mod hkdf_tests { // resume (re-supplying the salt), feed the identical remaining IKM, and compare PRKs let mut resumed = - HKDF::::from_suspended(serialized_state, salt).unwrap(); + HKDF::::from_suspended(serialized_state, salt).unwrap(); resumed.do_extract_update_bytes(part2).unwrap(); let prk_resumed = resumed.do_extract_final().unwrap(); assert_eq!(prk.ref_to_bytes(), prk_resumed.ref_to_bytes()); } - round_trip::( - &salt, part1, part2, - ); - round_trip::( - &salt, part1, part2, - ); + round_trip::< + SUSPENDED_SHA256_STATE_LEN, + SUSPENDED_HKDF_SHA256_STATE_LEN, + SHA256, + bouncycastle_sha2::hmac::HMAC_SHA256Params, + >(&salt, part1, part2); + round_trip::< + SUSPENDED_SHA512_STATE_LEN, + SUSPENDED_HKDF_SHA512_STATE_LEN, + SHA512, + bouncycastle_sha2::hmac::HMAC_SHA512Params, + >(&salt, part1, part2); // Test the guard for invalid states // testing just on HKDF_SHA256 diff --git a/crypto/hmac/Cargo.toml b/crypto/hmac/Cargo.toml index 44d1eed1..7f46211b 100644 --- a/crypto/hmac/Cargo.toml +++ b/crypto/hmac/Cargo.toml @@ -6,15 +6,3 @@ edition.workspace = true [dependencies] bouncycastle-core.workspace = true bouncycastle-utils.workspace = true - -# bouncycastle-sha2, -sha3 and -rng are dev-dependencies so that the tests, benches and doc examples -# here can still exercise HMAC over the library's own hashes; Cargo permits cycles through -# dev-dependencies. -# todo -- we're about to change that and move them to their respective crates in the next phase. -[dev-dependencies] -bouncycastle-core-test-framework.workspace = true -criterion.workspace = true -bouncycastle-hex.workspace = true -bouncycastle-rng.workspace = true -bouncycastle-sha2.workspace = true -bouncycastle-sha3.workspace = true diff --git a/crypto/hmac/src/lib.rs b/crypto/hmac/src/lib.rs index 8de8bc23..3f8d92a7 100644 --- a/crypto/hmac/src/lib.rs +++ b/crypto/hmac/src/lib.rs @@ -2,8 +2,8 @@ //! taking into account NIST Implementation Guidance in FIPS 140-2 IG A.8 and NIST SP 800-107-r1. //! //! This is a utility crate and is not intended to be used directly. It provides [`HMAC`] -- the -//! construction, generic over any struct that implements [`Hash`] and [`HMACParams`], the extension -//! point through which a hash declares the metadata from which the HMAC instance is built. +//! construction, generic over any struct that implements [`Hash`], paired with an [`HMACParams`] +//! marker type that carries the metadata the resulting instantiation reports about itself. //! The library provides the following concrete instantiations of HMAC: //! //! | Hash family | Instantiations | @@ -11,8 +11,9 @@ //! | SHA-2 | `bouncycastle_sha2::hmac` -- `HMAC_SHA224` .. `HMAC_SHA512_256` | //! | SHA-3 | `bouncycastle_sha3::hmac` -- `HMAC_SHA3_224` .. `HMAC_SHA3_512` | //! -//! Although users are free to implement [`Hash`] and [`HMACParams`] for a a hash function not included with the library, -//! and will then be able to instantiate [`HMAC`] for it as well. +//! Users are free to implement [`Hash`] for a hash function not included with the library and +//! declare their own [`HMACParams`] marker type for it, and will then be able to instantiate +//! [`HMAC`] over it as well. //! //! # Instantiating HMAC over a Hash //! @@ -22,19 +23,20 @@ //! rather than deriving it from the hash's OID. Supplying that metadata is what makes an HMAC a //! first-class algorithm in this library rather than an anonymous `HMAC`. //! -//! There are four steps, of which only the second is mandatory: +//! That metadata is supplied by a *params marker type*. +//! +//! There are four steps to implementing a new HMAC type: //! //! 1. Have a hash type that implements [`Hash`] + [`HashAlgParams`] + [`Default`]. Implementing //! [`Hash`] is documented in `bouncycastle-core`; nothing about it is HMAC-specific. -//! 2. Implement [`HMACParams`] for that hash type, supplying the HMAC's name, claimed security -//! strength and OID, plus the key type that [`HMAC::keygen_from_rng`] should return (typically a -//! `KeyMaterial` for an L that matches the size of the underlying hash function. This allows -//! this crate to provide blanket [`Algorithm`], [`AlgorithmOID`] and [`HMAC::keygen_from_rng`] impls. -//! 3. Publish a type alias for the instantiation, passing [`HashAlgParams::BLOCK_LEN`] as the key -//! buffer length. Per RFC 2104 a key no longer than the hash's block is used verbatim, and only -//! longer keys are pre-hashed down to the output length, so the buffer must hold a full block. -//! Reading the length off the hash rather than writing a literal means the two cannot drift apart. -//! 4. Optionally publish the suspended-state length as a constant. [`SuspendableKeyed`] is +//! 2. Declare a params type for the instantiation and give it [`Algorithm`] (the name and claimed +//! strength), [`AlgorithmOID`] (the OID and its DER encoding) and [`HMACParams`] (the key type +//! that [`HMAC::keygen_from_rng`] should return, and the internal key buffer). This crate then +//! provides the blanket [`Algorithm`], [`AlgorithmOID`] and [`HMAC::keygen_from_rng`] impls for +//! any [`HMAC`]. Take the buffer from [`HashAlgParams::BLOCK_LEN`] rather than writing a literal, +//! so it cannot drift away from the hash it has to hold a block of. +//! 3. Optionally, publish a type alias pairing the hash with the params. +//! 4. Optionally, publish the suspended-state length as a convenience constant. [`SuspendableKeyed`] is //! implemented automatically for any hash that implements [`Suspendable`], and HMAC's suspended //! state is exactly the inner hash's -- the key is deliberately excluded -- so the constant is //! just an alias for the hash's own. @@ -44,37 +46,60 @@ //! As an example, the `bouncycastle-sha2` crate follows exactly the recipe above; its entry for SHA-256 reduces to: //! //! ```rust,ignore -//! pub type HMAC_SHA256 = HMAC::BLOCK_LEN }>; +//! /// The parameters for HMAC-SHA256. +//! #[derive(Clone)] +//! pub struct HMAC_SHA256Params; //! -//! impl HMACParams for SHA256 { -//! type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; -//! const HMAC_ALG_NAME: &'static str = "HMAC-SHA256"; -//! const HMAC_MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit; -//! /// Defined in RFC 4231: id-hmacWithSHA256 { digestAlgorithm 9 } -//! const HMAC_OID: &'static [u32] = &[1, 2, 840, 113549, 2, 9]; -//! const HMAC_OID_DER: &'static [u8] = +//! impl Algorithm for HMAC_SHA256Params { +//! const ALG_NAME: &'static str = "HMAC-SHA256"; +//! const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit; +//! } +//! +//! /// Defined in RFC 4231: id-hmacWithSHA256 { digestAlgorithm 9 } +//! impl AlgorithmOID for HMAC_SHA256Params { +//! const OID: &'static [u32] = &[1, 2, 840, 113549, 2, 9]; +//! const OID_DER: &'static [u8] = //! &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09]; //! } //! +//! impl HMACParams for HMAC_SHA256Params { +//! type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; +//! type KeyBuf = [u8; ::BLOCK_LEN]; +//! } +//! +//! pub type HMAC_SHA256 = HMAC; +//! //! pub const SUSPENDED_HMAC_SHA256_STATE_LEN: usize = SUSPENDED_SHA256_STATE_LEN; //! ``` //! //! [`HMACParams`] is deliberately **not** sealed, so the same recipe works for a hash function //! defined in any other crate. Simply follow the recipe above! //! +//! Every [`HMAC`] carries a params type; there is no anonymous form. That includes an HMAC used as +//! an internal construction step: HKDF's extract phase is `PRK = HMAC-Hash(salt, IKM)` (RFC 5869 +//! Section 2.2), so the HMAC inside `HKDF_SHA256` really is HMAC-SHA256 and names +//! `HMAC_SHA256Params` accordingly rather than hiding behind a placeholder. +//! //! # Security Considerations //! //! These apply to every instantiation; the hash crates' `hmac` modules repeat the ones that matter //! most in day-to-day use. //! -//! * [`HMACParams::HMAC_MAX_SECURITY_STRENGTH`] is a claim that [`MAC::new`] enforces against the -//! key's tagged strength, and that [`HMAC::keygen_from_rng`] enforces against the RNG's. Declaring -//! a strength the underlying hash cannot support does not make the construction stronger, it just -//! makes the check wrong. NIST SP 800-107-r1 Section 5.3.4 gives the ceiling: the effective -//! strength is `min(strength of K, 2C)` for an internal chaining value of `C` bits. -//! * [`MAC::new_allow_weak_key`] deliberately skips the key-strength check. It exists for protocols -//! that call for a weak or all-zero key -- an all-zero HKDF salt, for example -- and should not be -//! used to silence an error from [`MAC::new`]. +//! * The strength an HMAC claims is declared by its params, and is the most the instantiation can +//! deliver. It is not the underlying hash's collision strength: NIST SP 800-107-r1 Section 5.3.4 +//! puts it at `min(strength of K, 2C)` for a `C`-bit chaining value, and footnote 4 there rules +//! collision attacks out of scope entirely. Declaring a strength the construction cannot support +//! does not make it stronger, it just makes the two gates below wrong. +//! * Both gates follow from that claim, and both ask the same question: can this input actually +//! back the strength the algorithm advertises? [`MAC::new`] checks the key's tagged strength and +//! [`HMAC::keygen_from_rng`] checks the RNG's, each refusing anything weaker. So HMAC-SHA256 +//! wants a 256-bit key and a 256-bit generator. Truncating the *output* still lowers the strength +//! of a given tag, which is why this is a ceiling rather than a guarantee. +//! * The escape hatch is on the key only. [`MAC::new_allow_weak_key`] skips the key check for +//! protocols that require a weak or all-zero key, such as an all-zero HKDF salt. +//! [`HMAC::keygen_from_rng`] has no equivalent, because a generator cannot be asked to produce +//! entropy it does not have; the key it returned would carry a tag that overstates it. +//! It should not be used merely to silence an error from [`MAC::new`]. //! * Verification via [`MAC::verify`] / [`MAC::do_verify_final`] uses a constant-time comparison. //! Recomputing the MAC and comparing it with `==` leaks how many leading bytes matched. //! * [`MIN_FIPS_DIGEST_LEN`] (4 bytes) is the shortest truncation this crate will produce, per @@ -93,88 +118,61 @@ use bouncycastle_core::errors::{KeyMaterialError, MACError, RNGError, SuspendableError}; use bouncycastle_core::key_material::{KeyMaterialTrait, KeyType}; use bouncycastle_core::traits::{ - Algorithm, AlgorithmOID, Hash, HashAlgParams, MAC, RNG, SecurityStrength, Suspendable, - SuspendableKeyed, + Algorithm, AlgorithmOID, Hash, MAC, RNG, SecurityStrength, Suspendable, SuspendableKeyed, }; +use bouncycastle_utils::secret::ZeroizablePrimitive; use bouncycastle_utils::{ct, secret::Secret}; use core::fmt::{Debug, Display, Formatter}; +use core::marker::PhantomData; + +/*** Imports needed for docs ***/ +#[allow(unused_imports)] +use bouncycastle_core::traits::HashAlgParams; /*** Parameters ***/ -/// The HMAC-specific parameters for one underlying hash function. -/// -/// [`HMAC`] itself is fully generic: it works with any [`Hash`], including hashes supplied by crates -/// outside this library. What HMAC cannot derive on its own is the *metadata* of the resulting -/// construction -- this trait supplies exactly that metadata, so the blanket [`Algorithm`], [`AlgorithmOID`] and -/// [`HMAC::keygen_from_rng`] can be impl'd generically rather than being written out once -/// per hash. -/// -/// Each hash crate is expected to implement this trait for its own hash types and publishes the resulting type -/// alias. For example,`HMAC_SHA256` lives in `bouncycastle_sha2::hmac` and `HMAC_SHA3_256` in -/// `bouncycastle_sha3::hmac`. -/// -/// The block length and the generated-key length do not need to be restated here since they are already -/// carried by the hash itself as [`HashAlgParams::BLOCK_LEN`] and [`HashAlgParams::OUTPUT_LEN`]. -pub trait HMACParams: Hash + HashAlgParams + Default { - /// The key type produced by [`HMAC::keygen_from_rng`], sized to this hash's output length. +/// The metadata of one concrete HMAC instantiation, supplied as a marker type. +pub trait HMACParams: Algorithm + AlgorithmOID { + /// The type of key that this HMAC instance needs, sized to the underlying hash's output length. /// - /// Implementors should set this to `KeyMaterial<{Self::OUTPUT_LEN}>`. + /// Implementors should set this to `KeyMaterial<{ ::OUTPUT_LEN }>`. /// // todo: once rust stabilizes generic_const_exprs, delete this and return // `KeyMaterial<{Self::OUTPUT_LEN}>` from `keygen_from_rng` instead. type MACKey: KeyMaterialTrait + Default; - /// The name of the HMAC over this hash, as reported by [`Algorithm::ALG_NAME`]. - const HMAC_ALG_NAME: &'static str; - /// The strength claimed by the HMAC over this hash, as reported by - /// [`Algorithm::MAX_SECURITY_STRENGTH`]. - const HMAC_MAX_SECURITY_STRENGTH: SecurityStrength; - /// The OID of the HMAC over this hash in component form, as reported by [`AlgorithmOID::OID`]. - const HMAC_OID: &'static [u32]; - /// The DER encoding of [`HMACParams::HMAC_OID`], as reported by [`AlgorithmOID::OID_DER`]. - const HMAC_OID_DER: &'static [u8]; -} - -impl Algorithm for HMAC { - const ALG_NAME: &'static str = HASH::HMAC_ALG_NAME; - const MAX_SECURITY_STRENGTH: SecurityStrength = HASH::HMAC_MAX_SECURITY_STRENGTH; -} - -impl AlgorithmOID for HMAC { - const OID: &'static [u32] = HASH::HMAC_OID; - const OID_DER: &'static [u8] = HASH::HMAC_OID_DER; + /// The internal key buffer. + /// + /// It must be able to hold a key up to the *block length* of the underlying hash: per RFC 2104 + /// a key no longer than the block is used verbatim, and only longer keys are pre-hashed down to + /// the output length, so the buffer has to fit a whole block. + /// + /// Implementors should set this to `[u8; { ::BLOCK_LEN }]`. + type KeyBuf: ZeroizablePrimitive + AsRef<[u8]> + AsMut<[u8]>; } -// The internal key buffer must be able to hold a key up to the *block length* of the underlying hash: -// per RFC 2104, a key no longer than the block is used verbatim (only longer keys are pre-hashed down -// to the output length). So the buffer size is a const parameter of the struct, set per hash to its -// block length by the type aliases. -// -// The default is used only when `HMAC` is written without an explicit buffer size; it is the -// largest block length across all supported hashes, so it is always large enough. -const LARGEST_HASHER_BLOCK_LEN: usize = 144; - /// Internal struct for HKDF. /// HMAC implements RFC 2104. /// Can, in theory, be instantiated with hash functions other than the ones provided by this crate (even custom ones). #[derive(Clone)] -pub struct HMAC { +pub struct HMAC { + _phantom_params: PhantomData, hasher: HASH, - // todo: once rust stable merges generic_const_exprs, we can remove this hack and delete the KEY_BUF_LEN param. - // key: [u8; HASH::OUTPUT_LEN]; - key: Secret<[u8; KEY_BUF_LEN]>, + // Sized by the params ([`HMACParams::KeyBuf`]), which take it from the hash's block length, so + // the caller cannot pick a buffer that does not fit the hash. + key: Secret, key_len: Secret, // Doing it this way to avoid needing a vec, so that this can be made no_std friendly. } -impl Debug for HMAC { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "HMAC-{} instance", HASH::ALG_NAME,) +impl Debug for HMAC { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{} instance", PARAMS::ALG_NAME,) } } -impl Display for HMAC { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "HMAC-{} instance", HASH::ALG_NAME,) +impl Display for HMAC { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{} instance", PARAMS::ALG_NAME,) } } @@ -194,13 +192,13 @@ const OPAD_BYTE: u8 = 0x5C; /// = 32 bits / 8 = 4 bytes; pub const MIN_FIPS_DIGEST_LEN: usize = 4; -impl HMAC { +impl HMAC { fn pad_key_into_hasher(&mut self, padding: u8) { // TODO: it would be nice to be able to statically extract the length of HASH and not need a Vec or over-sized array here. // TODO: make this no_std-friendly let mut padded = vec![0u8; self.hasher.block_bitlen() / 8]; - padded[..*self.key_len].copy_from_slice(&self.key[..*self.key_len]); + padded[..*self.key_len].copy_from_slice(&self.key.as_ref()[..*self.key_len]); // XXX: easier way to xor over Vec? for entry in &mut padded { @@ -219,16 +217,16 @@ impl HMAC { fn load_key_material(&mut self, key_bytes: &[u8]) { if key_bytes.len() > self.hasher.block_bitlen() / 8 { // then we have to pre-hash it -- use a new instance of the hasher rather than the internal one - HASH::default().hash_out(key_bytes, &mut self.key[..self.hasher.output_len()]); + HASH::default().hash_out(key_bytes, &mut self.key.as_mut()[..self.hasher.output_len()]); *self.key_len = self.hasher.output_len(); } else { - self.key[..key_bytes.len()].copy_from_slice(key_bytes); + self.key.as_mut()[..key_bytes.len()].copy_from_slice(key_bytes); *self.key_len = key_bytes.len(); } // Just as a sanity-check. assert!( - *self.key_len <= KEY_BUF_LEN, + *self.key_len <= self.key.as_ref().len(), "Fatal error: Key length exceeds HMAC internal buffer length" ); } @@ -255,7 +253,7 @@ impl HMAC { self.pad_key_into_hasher(IPAD_BYTE); // check that the key had enough security level - if !allow_weak_keys && key.security_strength() < HASH::default().max_security_strength() { + if !allow_weak_keys && key.security_strength() < PARAMS::MAX_SECURITY_STRENGTH { Err(KeyMaterialError::SecurityStrength( "HMAC::init(): provided key has a lower security strength than the instantiated HMAC", ))? @@ -299,15 +297,25 @@ impl HMAC { // TODO: This is essentially a "batch mode" where you want to perform many MACs or Verifications with the same key // TODO: against different data. -impl MAC for HMAC { +impl MAC for HMAC { fn new(key: &impl KeyMaterialTrait) -> Result { - let mut hmac = Self { hasher: HASH::default(), key: Secret::new(), key_len: Secret::new() }; + let mut hmac = Self { + _phantom_params: PhantomData, + hasher: HASH::default(), + key: Secret::new(), + key_len: Secret::new(), + }; hmac.init(key, false)?; Ok(hmac) } fn new_allow_weak_key(key: &impl KeyMaterialTrait) -> Result { - let mut hmac = Self { hasher: HASH::default(), key: Secret::new(), key_len: Secret::new() }; + let mut hmac = Self { + _phantom_params: PhantomData, + hasher: HASH::default(), + key: Secret::new(), + key_len: Secret::new(), + }; hmac.init(key, true)?; Ok(hmac) } @@ -360,7 +368,9 @@ impl MAC for HMAC SecurityStrength { - HASH::default().max_security_strength() + // Same source as the key check in `init()`, so the strength this reports and the strength + // that gets enforced cannot drift apart. + PARAMS::MAX_SECURITY_STRENGTH } } @@ -381,9 +391,9 @@ impl MAC for HMAC, -> SuspendableKeyed for HMAC + PARAMS: HMACParams, +> SuspendableKeyed for HMAC { // HMAC accepts any key material, so the key type is the trait object `dyn KeyMaterialTrait` // rather than a single concrete key type. The key is only used (by reference) to reload the key @@ -408,7 +418,12 @@ impl< // Re-load the key material exactly as `new()` did (pre-hashing an over-length key), but do // NOT re-absorb `K ⊕ ipad` — the deserialized hasher already contains it. The key is only // needed for the outer `K ⊕ opad` step at finalization. - let mut hmac = HMAC { hasher, key: Secret::new(), key_len: Secret::new() }; + let mut hmac = HMAC { + _phantom_params: PhantomData, + hasher, + key: Secret::new(), + key_len: Secret::new(), + }; hmac.load_key_material(key.ref_to_bytes()); Ok(hmac) @@ -417,26 +432,55 @@ impl< /* KeyGen functions */ -impl HMAC { +impl HMAC { /// Generates a key of the appropriate length for this HMAC from the provided RNG, tagged /// [`KeyType::MACKey`] and ready to hand to [`MAC::new`]. /// /// The key length is the underlying hash's output length ([`HashAlgParams::OUTPUT_LEN`], carried /// as [`HMACParams::MACKey`]); see that associated type for why. /// + /// # Contract + /// + /// The returned key is tagged at this HMAC's [`Algorithm::MAX_SECURITY_STRENGTH`], and that tag + /// has to be backed by real entropy -- A generator cannot produce + /// more entropy than it has -- so this refuses any RNG that cannot supply it: if + /// [`RNG::security_strength`] is below the claimed strength you get + /// [`RNGError::SecurityStrengthInsufficientForAlgorithm`] and no key. + /// + /// In practice this means a 256-bit generator for HMAC-SHA256 and above. `DefaultRNG` is + /// `HashDRBG_SHA512` and qualifies for every instantiation in this library; `HashDRBG_SHA256` + /// offers 128 bits and so is accepted only where the HMAC claims no more than that. + /// + /// There is deliberately no weak-RNG escape hatch, unlike [`MAC::new_allow_weak_key`]. A + /// caller who genuinely wants a key weaker than the algorithm claims can build one themselves + /// and pass it to [`MAC::new_allow_weak_key`], which makes that choice visible at the call site. + /// // Dev note: done this way to avoid this crate needing a dependency on the `bouncycastle-rng` crate, // which itself has a dependency on `bouncycastle-sha2` which depends on this hmac crate, // which creates a circular cargo dependency. - pub fn keygen_from_rng(rng: &mut dyn RNG) -> Result { + pub fn keygen_from_rng(rng: &mut dyn RNG) -> Result { // Refuse to generate a key from an RNG that cannot back the strength this HMAC claims; // otherwise the key's tagged security strength would overstate its true entropy. - if rng.security_strength() < HASH::HMAC_MAX_SECURITY_STRENGTH { + if rng.security_strength() < PARAMS::MAX_SECURITY_STRENGTH { return Err(RNGError::SecurityStrengthInsufficientForAlgorithm); } - let mut key = HASH::MACKey::default(); + let mut key = PARAMS::MACKey::default(); rng.fill_keymaterial_out(&mut key)?; key.set_key_type(KeyType::MACKey)?; Ok(key) } } + +impl Algorithm for HMAC { + const ALG_NAME: &'static str = PARAMS::ALG_NAME; + // An HMAC claims the security strength of the hash underneath it. Reading it off the hash rather + // than restating it per instantiation keeps the advertised strength identical to the one + // `HMAC::init` enforces against the key, which it takes from `P::max_security_strength`. + const MAX_SECURITY_STRENGTH: SecurityStrength = PARAMS::MAX_SECURITY_STRENGTH; +} + +impl AlgorithmOID for HMAC { + const OID: &'static [u32] = PARAMS::OID; + const OID_DER: &'static [u8] = PARAMS::OID_DER; +} diff --git a/crypto/sha2/Cargo.toml b/crypto/sha2/Cargo.toml index affcde1b..7f2b441c 100644 --- a/crypto/sha2/Cargo.toml +++ b/crypto/sha2/Cargo.toml @@ -12,6 +12,7 @@ bouncycastle-utils.workspace = true [dev-dependencies] criterion.workspace = true bouncycastle-core-test-framework.workspace = true +bouncycastle-hex.workspace = true bouncycastle-rng.workspace = true [[bench]] diff --git a/crypto/sha2/src/hkdf.rs b/crypto/sha2/src/hkdf.rs index 7ca19650..f5887ee1 100644 --- a/crypto/sha2/src/hkdf.rs +++ b/crypto/sha2/src/hkdf.rs @@ -186,18 +186,17 @@ //! //! | Object | Size (bytes) | //! |---------------------------------------------------|--------------| -//! | `HKDF_SHA256` | 296 | -//! | `HKDF_SHA512` | 392 | +//! | `HKDF_SHA256` | 216 | +//! | `HKDF_SHA512` | 376 | //! | Suspended `HKDF_SHA256` state | 122 | //! | Suspended `HKDF_SHA512` state | 218 | //! -//! The object is an `Option` of the inner extract-phase HMAC -- 272 bytes for SHA-256, 368 for +//! The object is an `Option` of the inner extract-phase HMAC -- 192 bytes for SHA-256, 352 for //! SHA-512 -- plus 24 bytes of bookkeeping (the entropy counter, the accumulated security strength -//! and the state-machine tag, with padding). Note that the inner HMAC is written as `HMAC`, which -//! takes the *default* key buffer length: the largest block length across all supported hashes -//! (144 bytes) rather than the 64 or 128 that SHA-256 and SHA-512 actually need. So the inner -//! `HMAC` is 264 bytes where the published [`crate::hmac::HMAC_SHA256`] is 184, and an -//! `HKDF_SHA256` is correspondingly larger than the HMAC it is built on. +//! and the state-machine tag, with padding). The inner HMAC is the published +//! [`crate::hmac::HMAC_SHA256`] rather than an anonymous one, so its key buffer is sized to the +//! hash's block length like any other: an `HKDF_SHA256` is 32 bytes larger than the HMAC it is +//! built on, not 112. //! //! The suspended state is the inner HMAC's suspended state (which is the hash's) plus 14 bytes; the //! salt is deliberately excluded and must be re-supplied on resume. @@ -245,7 +244,17 @@ pub const SUSPENDED_HKDF_SHA512_STATE_LEN: usize = SUSPENDED_HMAC_SHA512_STATE_L /*** Type aliases ***/ /// Public type for HKDF using SHA256. #[allow(non_camel_case_types)] -pub type HKDF_SHA256 = HKDF; +pub type HKDF_SHA256 = HKDF< + SHA256, + crate::hmac::HMAC_SHA256Params, + SUSPENDED_SHA256_STATE_LEN, + SUSPENDED_HKDF_SHA256_STATE_LEN, +>; /// Public type for HKDF using SHA512. #[allow(non_camel_case_types)] -pub type HKDF_SHA512 = HKDF; +pub type HKDF_SHA512 = HKDF< + SHA512, + crate::hmac::HMAC_SHA512Params, + SUSPENDED_SHA512_STATE_LEN, + SUSPENDED_HKDF_SHA512_STATE_LEN, +>; diff --git a/crypto/sha2/src/hmac.rs b/crypto/sha2/src/hmac.rs index a94bb19b..de2ebc3a 100644 --- a/crypto/sha2/src/hmac.rs +++ b/crypto/sha2/src/hmac.rs @@ -4,14 +4,17 @@ //! Uses [`bouncycastle_hmac`] to provide the HMAC-SHA2 instantiations: [`HMAC_SHA224`], //! [`HMAC_SHA256`], [`HMAC_SHA384`] and [`HMAC_SHA512`]. //! -//! HMAC itself is implemented generically in [`bouncycastle_hmac`]; this module supplies the -//! SHA-2-specific parameters via [`HMACParams`] and publishes the resulting type aliases, so that +//! HMAC itself is implemented generically in [`bouncycastle_hmac`]; this module declares one +//! [`HMACParams`] marker type per instantiation (such as [`HMAC_SHA256Params`]), carrying that +//! HMAC's name, claimed strength, OID and key type, and publishes the type alias pairing each +//! marker with its hash. This mirrors how the hashes themselves are built, where `SHA256` is +//! `SHA256Internal`. The upshot is that //! HMAC over a SHA2 hash is found in this crate, and [`bouncycastle_hmac`] serves as a utility crate //! rather than as part of library's public API. //! -//! The key buffer length of each alias is the underlying hash's block length: per RFC 2104, a key no -//! longer than the block is used verbatim, and only longer keys are pre-hashed down to the output -//! length, so the buffer must be able to hold a full block. It is taken from +//! Each params type sizes the internal key buffer to its hash's block length: per RFC 2104, a key +//! no longer than the block is used verbatim, and only longer keys are pre-hashed down to the +//! output length, so the buffer must be able to hold a full block. It is taken from //! [`HashAlgParams::BLOCK_LEN`] rather than restated as a literal so the two cannot drift apart. //! //! # Usage @@ -41,10 +44,10 @@ //! ``` //! use bouncycastle_core::key_material::KeyMaterial256; //! use bouncycastle_core::traits::MAC; -//! use bouncycastle_rng::HashDRBG_SHA256; +//! use bouncycastle_rng::DefaultRNG; //! use bouncycastle_sha2::hmac::HMAC_SHA256; //! -//! let mut rng = HashDRBG_SHA256::new_from_os(); +//! let mut rng = DefaultRNG::new_from_os(); //! let key: KeyMaterial256 = HMAC_SHA256::keygen_from_rng(&mut rng) //! .expect("Will only fail if the system RNG can't start up."); //! @@ -61,7 +64,8 @@ //! use bouncycastle_sha2::hmac::HMAC_SHA256; //! //! let key = KeyMaterial256::from_bytes_as_type( -//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", +//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\ +//! \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", //! KeyType::MACKey).unwrap(); //! //! let hmac = HMAC_SHA256::new(&key).expect( @@ -77,10 +81,10 @@ //! ``` //! use bouncycastle_core::key_material::KeyMaterial256; //! use bouncycastle_core::traits::MAC; -//! use bouncycastle_rng::HashDRBG_SHA256; +//! use bouncycastle_rng::DefaultRNG; //! use bouncycastle_sha2::hmac::HMAC_SHA256; //! -//! let mut rng = HashDRBG_SHA256::new_from_os(); +//! let mut rng = DefaultRNG::new_from_os(); //! let key: KeyMaterial256 = HMAC_SHA256::keygen_from_rng(&mut rng) //! .expect("Will only fail if the system RNG can't start up."); //! @@ -95,10 +99,10 @@ //! ``` //! use bouncycastle_core::key_material::KeyMaterial256; //! use bouncycastle_core::traits::MAC; -//! use bouncycastle_rng::HashDRBG_SHA256; +//! use bouncycastle_rng::DefaultRNG; //! use bouncycastle_sha2::hmac::HMAC_SHA256; //! -//! let mut rng = HashDRBG_SHA256::new_from_os(); +//! let mut rng = DefaultRNG::new_from_os(); //! let key: KeyMaterial256 = HMAC_SHA256::keygen_from_rng(&mut rng) //! .expect("Will only fail if the system RNG can't start up."); //! @@ -124,7 +128,8 @@ //! // For this example to work, we are hard-coding both the key and the MAC value that it generates //! // for this data. //! let key = KeyMaterial256::from_bytes_as_type( -//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", +//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\ +//! \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", //! KeyType::MACKey).unwrap(); //! //! let data: &[u8] = b"Hello, world!"; @@ -132,8 +137,8 @@ //! // .verify() returns a bool: true if the MAC is valid, false otherwise. //! if HMAC_SHA256::new(&key).unwrap() //! .verify(data, -//! b"\xa2\xd1\x2e\xcf\xfc\x41\xba\xf1\x23\xd6\x3e\x44\xfc\x27\x88\x90 -//! \x47\xcd\x08\xe7\x05\xd7\x0f\xa3\xb8\xaa\x8a\x5c\x18\x7c\x6c\xa9" +//! b"\x76\xd0\x69\x2c\x75\x6f\x89\x94\x96\xf3\x51\x63\x6a\x69\x69\xe5 +//! \x4e\xbf\xb2\x3a\xbb\x09\xfd\x61\x40\x86\x13\x6a\xc9\xab\x26\x77" //! ) //! { //! println!("MAC is valid!"); @@ -153,12 +158,13 @@ //! // For this example to work, we are hard-coding both the key and the MAC value that it generates //! // for this data. //! let key = KeyMaterial256::from_bytes_as_type( -//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", +//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\ +//! \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", //! KeyType::MACKey).unwrap(); //! let mut hmac = HMAC_SHA256::new(&key).unwrap(); //! hmac.do_update(b"Hello,"); //! hmac.do_update(b" world!"); -//! if hmac.do_verify_final(b"\xa2\xd1\x2e\xcf\xfc\x41\xba\xf1\x23\xd6\x3e\x44\xfc\x27\x88\x90\x47\xcd\x08\xe7\x05\xd7\x0f\xa3\xb8\xaa\x8a\x5c\x18\x7c\x6c\xa9" +//! if hmac.do_verify_final(b"\x76\xd0\x69\x2c\x75\x6f\x89\x94\x96\xf3\x51\x63\x6a\x69\x69\xe5\x4e\xbf\xb2\x3a\xbb\x09\xfd\x61\x40\x86\x13\x6a\xc9\xab\x26\x77" //! ) //! { //! println!("MAC is valid!"); @@ -189,7 +195,8 @@ //! let msg_part2 = b" jumped over the lazy dog"; //! //! let key = KeyMaterial256::from_bytes_as_type( -//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", +//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\ +//! \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", //! KeyType::MACKey).unwrap(); //! //! let mut hmac = HMAC_SHA256::new(&key).unwrap(); @@ -229,10 +236,25 @@ //! //! # Security Considerations //! -//! * The key must carry at least the security strength claimed by the HMAC, and [`MAC::new`] -//! enforces that. [`MAC::new_allow_weak_key`] deliberately skips the check; use it only where a -//! weak or all-zero key is called for by the protocol (an all-zero HKDF salt, for example), not to -//! silence an error. +//! * Each of these HMACs claims the strength NIST SP 800-107r1 Section 5.3.4 gives it, which is +//! `min(strength of K, 2C)` and works out to the key length for the whole SHA-2 family: 224 bits +//! for HMAC-SHA224 and 256 or more for the rest. `SecurityStrength` has no 224-bit category and +//! tops out at 256, so the declared values are `_192bit` for HMAC-SHA224 and `_256bit` for +//! HMAC-SHA256, HMAC-SHA384 and HMAC-SHA512. Note these are *not* the underlying hashes' +//! collision strengths, which are half as large; footnote 4 of that section puts collision +//! attacks out of scope for HMAC. +//! * The key must carry at least the strength claimed by the HMAC, and [`MAC::new`] enforces that. +//! A 20-byte key is therefore no longer enough for HMAC-SHA256; a full 32-byte key is. +//! [`MAC::new_allow_weak_key`] deliberately skips the check; use it only where a weak or all-zero +//! key is called for by the protocol (an all-zero HKDF salt, or a fixed test vector such as +//! RFC 4231's 20-byte keys), not to silence an error. +//! * The same rule applies to the generator. [`HMAC::keygen_from_rng`] tags the key it returns at +//! the HMAC's claimed strength, so it refuses any RNG that cannot back that tag: a 256-bit +//! generator is required for HMAC-SHA256 and above, and `bouncycastle_rng::DefaultRNG` is +//! `HashDRBG_SHA512` and qualifies for all of them. `HashDRBG_SHA256` offers 128 bits and is now +//! refused by every HMAC in this module. There is no weak-RNG opt-out, because a generator cannot +//! be asked for entropy it does not have; build the key yourself and use +//! [`MAC::new_allow_weak_key`] if that is genuinely what you want. //! * Verify with [`MAC::verify`] or [`MAC::do_verify_final`] rather than computing the MAC yourself //! and comparing: those use a constant-time comparison, while `==` on the byte slices leaks how //! many leading bytes matched. @@ -247,7 +269,7 @@ use crate::{SHA224, SHA256, SHA384, SHA512}; use crate::{SUSPENDED_SHA256_STATE_LEN, SUSPENDED_SHA512_STATE_LEN}; use bouncycastle_core::key_material::KeyMaterial; -use bouncycastle_core::traits::{HashAlgParams, SecurityStrength}; +use bouncycastle_core::traits::{Algorithm, AlgorithmOID, HashAlgParams, SecurityStrength}; use bouncycastle_hmac::{HMAC, HMACParams}; /*** Imports needed for docs ***/ @@ -268,59 +290,116 @@ pub const HMAC_SHA384_NAME: &str = "HMAC-SHA384"; /// pub const HMAC_SHA512_NAME: &str = "HMAC-SHA512"; -/*** Type aliases ***/ -/// Public type for HMAC using SHA224. +/*** Params types and type aliases ***/ + +/// The parameters for HMAC-SHA224 -- see [`HMAC_SHA224`]. +#[derive(Clone)] #[allow(non_camel_case_types)] -pub type HMAC_SHA224 = HMAC::BLOCK_LEN }>; -impl HMACParams for SHA224 { +pub struct HMAC_SHA224Params; + +impl Algorithm for HMAC_SHA224Params { + const ALG_NAME: &'static str = HMAC_SHA224_NAME; + // SP 800-107r1 s.5.3.4: min(strength of K, 2C). SHA-224 has C = 256, so 2C = 512, + // and the key is OUTPUT_LEN = 224 bits, so the key binds: 224 bits, rounded down to the nearest category. + const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit; +} + +/// Defined in RFC 4231: id-hmacWithSHA224 { digestAlgorithm 8 } +impl AlgorithmOID for HMAC_SHA224Params { + const OID: &'static [u32] = &[1, 2, 840, 113549, 2, 8]; + const OID_DER: &'static [u8] = &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x08]; +} + +impl HMACParams for HMAC_SHA224Params { type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; - const HMAC_ALG_NAME: &'static str = HMAC_SHA224_NAME; - const HMAC_MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_112bit; - /// Defined in RFC 4231: id-hmacWithSHA224 { digestAlgorithm 8 } - const HMAC_OID: &'static [u32] = &[1, 2, 840, 113549, 2, 8]; - const HMAC_OID_DER: &'static [u8] = - &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x08]; + type KeyBuf = [u8; ::BLOCK_LEN]; } -/// Public type for HMAC using SHA256. +/// Public type for HMAC using SHA224. #[allow(non_camel_case_types)] -pub type HMAC_SHA256 = HMAC::BLOCK_LEN }>; -impl HMACParams for SHA256 { +pub type HMAC_SHA224 = HMAC; + +/// The parameters for HMAC-SHA256 -- see [`HMAC_SHA256`]. +#[derive(Clone)] +#[allow(non_camel_case_types)] +pub struct HMAC_SHA256Params; + +impl Algorithm for HMAC_SHA256Params { + const ALG_NAME: &'static str = HMAC_SHA256_NAME; + // SP 800-107r1 s.5.3.4: min(strength of K, 2C). SHA-256 has C = 256, so 2C = 512, + // and the key is OUTPUT_LEN = 256 bits, so the key binds: 256 bits. + const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; +} + +/// Defined in RFC 4231: id-hmacWithSHA256 { digestAlgorithm 9 } +impl AlgorithmOID for HMAC_SHA256Params { + const OID: &'static [u32] = &[1, 2, 840, 113549, 2, 9]; + const OID_DER: &'static [u8] = &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09]; +} + +impl HMACParams for HMAC_SHA256Params { type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; - const HMAC_ALG_NAME: &'static str = HMAC_SHA256_NAME; - const HMAC_MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit; - /// Defined in RFC 4231: id-hmacWithSHA256 { digestAlgorithm 9 } - const HMAC_OID: &'static [u32] = &[1, 2, 840, 113549, 2, 9]; - const HMAC_OID_DER: &'static [u8] = - &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09]; + type KeyBuf = [u8; ::BLOCK_LEN]; } -/// Public type for HMAC using SHA384. +/// Public type for HMAC using SHA256. #[allow(non_camel_case_types)] -pub type HMAC_SHA384 = HMAC::BLOCK_LEN }>; -impl HMACParams for SHA384 { +pub type HMAC_SHA256 = HMAC; + +/// The parameters for HMAC-SHA384 -- see [`HMAC_SHA384`]. +#[derive(Clone)] +#[allow(non_camel_case_types)] +pub struct HMAC_SHA384Params; + +impl Algorithm for HMAC_SHA384Params { + const ALG_NAME: &'static str = HMAC_SHA384_NAME; + // SP 800-107r1 s.5.3.4: min(strength of K, 2C). SHA-384 has C = 512, so 2C = 1024, + // and the key is OUTPUT_LEN = 384 bits, so the key binds: 384 bits, capped at the top of `SecurityStrength`. + const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; +} + +/// Defined in RFC 4231: id-hmacWithSHA384 { digestAlgorithm 10 } +impl AlgorithmOID for HMAC_SHA384Params { + const OID: &'static [u32] = &[1, 2, 840, 113549, 2, 10]; + const OID_DER: &'static [u8] = &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x0a]; +} + +impl HMACParams for HMAC_SHA384Params { type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; - const HMAC_ALG_NAME: &'static str = HMAC_SHA384_NAME; - const HMAC_MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit; - /// Defined in RFC 4231: id-hmacWithSHA384 { digestAlgorithm 10 } - const HMAC_OID: &'static [u32] = &[1, 2, 840, 113549, 2, 10]; - const HMAC_OID_DER: &'static [u8] = - &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x0a]; + type KeyBuf = [u8; ::BLOCK_LEN]; } -/// Public type for HMAC using SHA512. +/// Public type for HMAC using SHA384. +#[allow(non_camel_case_types)] +pub type HMAC_SHA384 = HMAC; + +/// The parameters for HMAC-SHA512 -- see [`HMAC_SHA512`]. +#[derive(Clone)] #[allow(non_camel_case_types)] -pub type HMAC_SHA512 = HMAC::BLOCK_LEN }>; -impl HMACParams for SHA512 { +pub struct HMAC_SHA512Params; + +impl Algorithm for HMAC_SHA512Params { + const ALG_NAME: &'static str = HMAC_SHA512_NAME; + // SP 800-107r1 s.5.3.4: min(strength of K, 2C). SHA-512 has C = 512, so 2C = 1024, + // and the key is OUTPUT_LEN = 512 bits, so the key binds: 512 bits, capped at the top of `SecurityStrength`. + const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; +} + +/// Defined in RFC 4231: id-hmacWithSHA512 { digestAlgorithm 11 } +impl AlgorithmOID for HMAC_SHA512Params { + const OID: &'static [u32] = &[1, 2, 840, 113549, 2, 11]; + const OID_DER: &'static [u8] = &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x0b]; +} + +impl HMACParams for HMAC_SHA512Params { type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; - const HMAC_ALG_NAME: &'static str = HMAC_SHA512_NAME; - const HMAC_MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; - /// Defined in RFC 4231: id-hmacWithSHA512 { digestAlgorithm 11 } - const HMAC_OID: &'static [u32] = &[1, 2, 840, 113549, 2, 11]; - const HMAC_OID_DER: &'static [u8] = - &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x0b]; + type KeyBuf = [u8; ::BLOCK_LEN]; } +/// Public type for HMAC using SHA512. +#[allow(non_camel_case_types)] +pub type HMAC_SHA512 = HMAC; + /*** Serialized-state length constants ***/ // HMAC's suspended state is exactly the inner hasher's state -- the key is deliberately excluded and // must be re-supplied on resume -- so each of these is the underlying hash's own state length. diff --git a/crypto/sha2/src/lib.rs b/crypto/sha2/src/lib.rs index c53544d7..ed8e729b 100644 --- a/crypto/sha2/src/lib.rs +++ b/crypto/sha2/src/lib.rs @@ -47,36 +47,29 @@ //! //! See [hkdf] //! -//! # Memory Usage +//! # Cloning mid-stream //! -//! No heap memory is used by the algorithms themselves; the `Vec`-returning convenience methods -//! allocate only the output buffer, and the `*_out` variants allocate nothing. +//! Sometimes it is necessary to clone the state of the hash function after absorbing some input, for +//! example, if you have absorbed a large file and need to hash it with two different trailer suffixes. //! -//! | Object | Size (bytes) | -//! |----------------------------------------------------------|--------------| -//! | `SHA224`, `SHA256` | 112 | -//! | `SHA384`, `SHA512` | 208 | -//! | Suspended `SHA224`/`SHA256` state | 108 | -//! | Suspended `SHA384`/`SHA512` state | 204 | +//! The [`SHA256Internal`] and [`SHA512Internal`] structs impl [`Clone`] for this purpose. //! -//! The object holds the 8-word chaining value plus one block of buffered input. The compression -//! function additionally uses a 64-word (SHA-256 family, 256 bytes) or 80-word (SHA-512 family, -//! 640 bytes) message schedule on the stack for the duration of a call. +//! ```rust +//! use bouncycastle_core::traits::Hash; +//! use bouncycastle_sha2 as sha2; //! -//! # Security Considerations +//! let mut sha2 = sha2::SHA256::new(); +//! sha2.do_update(b"Some input"); +//! // Clone it so that we can finish this instance, and then continue feeding in more input. +//! let mut sha2_2 = sha2.clone(); //! -//! * SHA-224/256/384/512 offer 112/128/192/256 bits of collision resistance respectively. -//! * SHA-2 is a Merkle–Damgård construction and is therefore subject to length-extension: -//! `H(k || m)` is not a secure MAC. Use HMAC ([`crate::hmac`]) for keyed hashing. -//! * SHA-224 and SHA-384 are truncations of SHA-256 and SHA-512 with distinct initial values, and -//! are not vulnerable to length extension in the same direct way, but should still not be used as -//! `H(k || m)` MACs. -//! * The chaining value and input buffer are held in [`bouncycastle_utils::secret::Secret`] and -//! zeroized on drop. Transient copies (working variables and message schedule) in registers/stack -//! locals during compression are not zeroized. -//! * The implementation contains no data-dependent branches or table lookups. -//! * Messages up to 2^64 bytes are supported (FIPS 180-4 permits 2^64 bits for SHA-224/256 and -//! 2^128 bits for SHA-384/512; the SHA-512 family limit here is 2^67 bits). +//! // Finish the first instance +//! let output: Vec = sha2.do_final(); +//! +//! // Feed more into the second instance then squeeze it +//! sha2_2.do_update(b"Some more input"); +//! let output2 = sha2_2.do_final(); +//! ``` //! //! # Suspending and resuming execution //! @@ -108,6 +101,37 @@ //! sha2_resumed.do_update(msg_part2); //! let h: Vec = sha2_resumed.do_final(); //! ``` +//! +//! # Memory Usage +//! +//! No heap memory is used by the algorithms themselves; the `Vec`-returning convenience methods +//! allocate only the output buffer, and the `*_out` variants allocate nothing. +//! +//! | Object | Size (bytes) | +//! |----------------------------------------------------------|--------------| +//! | `SHA224`, `SHA256` | 112 | +//! | `SHA384`, `SHA512` | 208 | +//! | Suspended `SHA224`/`SHA256` state | 108 | +//! | Suspended `SHA384`/`SHA512` state | 204 | +//! +//! The object holds the 8-word chaining value plus one block of buffered input. The compression +//! function additionally uses a 64-word (SHA-256 family, 256 bytes) or 80-word (SHA-512 family, +//! 640 bytes) message schedule on the stack for the duration of a call. +//! +//! # Security Considerations +//! +//! * SHA-224/256/384/512 offer 112/128/192/256 bits of collision resistance respectively. +//! * SHA-2 is a Merkle–Damgård construction and is therefore subject to length-extension: +//! `H(k || m)` is not a secure MAC. Use HMAC ([`crate::hmac`]) for keyed hashing. +//! * SHA-224 and SHA-384 are truncations of SHA-256 and SHA-512 with distinct initial values, and +//! are not vulnerable to length extension in the same direct way, but should still not be used as +//! `H(k || m)` MACs. +//! * The chaining value and input buffer are held in [`bouncycastle_utils::secret::Secret`] and +//! zeroized on drop. Transient copies (working variables and message schedule) in registers/stack +//! locals during compression are not zeroized. +//! * The implementation contains no data-dependent branches or table lookups. +//! * Messages up to 2^64 bytes are supported (FIPS 180-4 permits 2^64 bits for SHA-224/256 and +//! 2^128 bits for SHA-384/512; the SHA-512 family limit here is 2^67 bits). #![forbid(unsafe_code)] #![forbid(missing_docs)] diff --git a/crypto/hmac/tests/hmac_tests.rs b/crypto/sha2/tests/hmac_tests.rs similarity index 77% rename from crypto/hmac/tests/hmac_tests.rs rename to crypto/sha2/tests/hmac_tests.rs index 0cfbe415..94f83c4a 100644 --- a/crypto/hmac/tests/hmac_tests.rs +++ b/crypto/sha2/tests/hmac_tests.rs @@ -1,20 +1,18 @@ #[cfg(test)] -mod hmac_tests { +mod hmac_sha2_tests { use bouncycastle_core::errors::{KeyMaterialError, MACError, RNGError}; use bouncycastle_core::key_material; use bouncycastle_core::key_material::{ KeyMaterial, KeyMaterial256, KeyMaterial512, KeyMaterialTrait, KeyType, }; - use bouncycastle_core::traits::{Algorithm, Hash, MAC, SecurityStrength}; + use bouncycastle_core::traits::{Algorithm, AlgorithmOID, Hash, MAC, SecurityStrength}; use bouncycastle_core_test_framework::DUMMY_SEED; use bouncycastle_core_test_framework::mac::TestFrameworkMAC; use bouncycastle_hex as hex; - use bouncycastle_hmac::*; + use bouncycastle_hmac::{HMAC, MIN_FIPS_DIGEST_LEN}; use bouncycastle_rng::{HashDRBG_SHA256, HashDRBG_SHA512}; use bouncycastle_sha2::hmac::*; use bouncycastle_sha2::*; - use bouncycastle_sha3::hmac::*; - use bouncycastle_sha3::{SHA3_224, SHA3_256, SHA3_384, SHA3_512}; #[test] fn simple_tests() { @@ -27,7 +25,7 @@ mod hmac_tests { assert_eq!(zero_length_key.key_len(), 0); assert_eq!(zero_length_key.key_type(), KeyType::MACKey); - let mut mac = HMAC::::new_allow_weak_key(&zero_length_key).unwrap(); + let mut mac = HMAC_SHA256::new_allow_weak_key(&zero_length_key).unwrap(); mac.do_update("Hi There".as_bytes()); let output = mac.do_final(); assert_eq!(output, b"\xe4\x84\x11\x26\x27\x15\xc8\x37\x0c\xd5\xe7\xbf\x8e\x82\xbe\xf5\x3b\xd5\x37\x12\xd0\x07\xf3\x42\x93\x51\x84\x3b\x77\xc7\xbb\x9b"); @@ -38,7 +36,7 @@ mod hmac_tests { KeyType::MACKey, ) .unwrap(); - let mut mac = HMAC::::new(&key).unwrap(); + let mut mac = HMAC_SHA224::new_allow_weak_key(&key).unwrap(); mac.do_update(b"Hi There"); let output = mac.do_final(); assert_eq!(output, b"\x89\x6f\xb1\x12\x8a\xbb\xdf\x19\x68\x32\x10\x7c\xd4\x9d\xf3\x3f\x47\xb4\xb1\x16\x99\x12\xba\x4f\x53\x68\x4b\x22"); @@ -49,7 +47,7 @@ mod hmac_tests { KeyType::MACKey, ) .unwrap(); - let mac = HMAC::::new(&key).unwrap(); + let mac = HMAC_SHA256::new_allow_weak_key(&key).unwrap(); // mac.do_update(b"").unwrap(); let output = mac.do_final(); assert_eq!( @@ -59,37 +57,6 @@ mod hmac_tests { ); } - #[test] - fn test_type_aliases() { - let key = KeyMaterial512::from_bytes_as_type( - b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - KeyType::MACKey).unwrap(); - - _ = HMAC::::new(&key).unwrap(); - _ = HMAC_SHA224::new(&key).unwrap(); - - _ = HMAC::::new(&key).unwrap(); - _ = HMAC_SHA256::new(&key).unwrap(); - - _ = HMAC::::new(&key).unwrap(); - _ = HMAC_SHA384::new(&key).unwrap(); - - _ = HMAC::::new(&key).unwrap(); - _ = HMAC_SHA512::new(&key).unwrap(); - - _ = HMAC::::new(&key).unwrap(); - _ = HMAC_SHA3_224::new(&key).unwrap(); - - _ = HMAC::::new(&key).unwrap(); - _ = HMAC_SHA3_256::new(&key).unwrap(); - - _ = HMAC::::new(&key).unwrap(); - _ = HMAC_SHA3_384::new(&key).unwrap(); - - _ = HMAC::::new(&key).unwrap(); - _ = HMAC_SHA3_512::new(&key).unwrap(); - } - #[test] fn constructor_tests() { let short_key = KeyMaterial256::from_bytes_as_type( @@ -99,7 +66,7 @@ mod hmac_tests { .unwrap(); assert_eq!(short_key.security_strength(), SecurityStrength::_112bit); // key is too short, so it is expected to fail - match HMAC::::new(&short_key) { + match HMAC_SHA256::new(&short_key) { Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* good */ } _ => panic!( "This should have thrown a KeyMaterialError::SecurityStrength error but it didn't" @@ -107,15 +74,18 @@ mod hmac_tests { } // It works after allowing weak keys - HMAC::::new_allow_weak_key(&short_key).unwrap(); + HMAC_SHA256::new_allow_weak_key(&short_key).unwrap(); - // It works with a long enough key + // It works with a long enough key. HMAC-SHA256 claims 256 bits (SP 800-107r1 s.5.3.4), so + // "long enough" is a full 32-byte key. let key = KeyMaterial256::from_bytes_as_type( - &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), + &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") + .unwrap(), KeyType::MACKey, ) .unwrap(); - HMAC::::new(&key).unwrap(); + assert_eq!(key.security_strength(), SecurityStrength::_256bit); + HMAC_SHA256::new(&key).unwrap(); } #[test] @@ -159,13 +129,6 @@ mod hmac_tests { mac.do_update(b"Hi There"); let tag = mac.do_final(); assert!(HMAC_SHA512::new(&key).unwrap().verify(b"Hi There", &tag)); - - // SHA3-224 has the largest block (144 bytes); a 143-byte key exercises the top of the range. - let key = KeyMaterial::<200>::from_bytes_as_type(&[0x0B; 143], KeyType::MACKey).unwrap(); - let mut mac = HMAC_SHA3_224::new(&key).unwrap(); - mac.do_update(b"Hi There"); - let tag = mac.do_final(); - assert!(HMAC_SHA3_224::new(&key).unwrap().verify(b"Hi There", &tag)); } #[test] @@ -191,7 +154,7 @@ mod hmac_tests { assert_eq!(key.security_strength(), SecurityStrength::_256bit); key.set_security_strength(SecurityStrength::_128bit).unwrap(); // The call should fail, as the key's security strength is set below the required threshold - match HMAC::::new(&key) { + match HMAC_SHA512::new(&key) { Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* fine */ } _ => { panic!( @@ -200,17 +163,17 @@ mod hmac_tests { } } // It passes after setting .allow_weak_keys() - let mut hmac = HMAC::::new_allow_weak_key(&key).unwrap(); + let mut hmac = HMAC_SHA512::new_allow_weak_key(&key).unwrap(); hmac.do_update(b"Hi There"); hmac.do_final(); // one-shot APIs still work with a weak key - let out = HMAC::::new_allow_weak_key(&key).unwrap().mac(b"Hi There"); - assert!(HMAC::::new_allow_weak_key(&key).unwrap().verify(b"Hi There", &out)); + let out = HMAC_SHA512::new_allow_weak_key(&key).unwrap().mac(b"Hi There"); + assert!(HMAC_SHA512::new_allow_weak_key(&key).unwrap().verify(b"Hi There", &out)); // likewise with pre-allocated buffers let mut out = [0u8; 64]; - HMAC::::new_allow_weak_key(&key).unwrap().mac_out(b"Hi There", &mut out).unwrap(); - assert!(HMAC::::new_allow_weak_key(&key).unwrap().verify(b"Hi There", &out)); + HMAC_SHA512::new_allow_weak_key(&key).unwrap().mac_out(b"Hi There", &mut out).unwrap(); + assert!(HMAC_SHA512::new_allow_weak_key(&key).unwrap().verify(b"Hi There", &out)); } #[test] @@ -222,23 +185,25 @@ mod hmac_tests { .unwrap(); // get the known-good output - let out = HMAC::::new(&key).unwrap().mac(b"Hi There"); + let out = HMAC_SHA224::new_allow_weak_key(&key).unwrap().mac(b"Hi There"); // test output that's the wrong length, should simply return False - let mut mac = HMAC::::new(&key).unwrap(); + let mut mac = HMAC_SHA224::new_allow_weak_key(&key).unwrap(); mac.do_update(b"Hi There"); assert!(!mac.do_verify_final(&out[..out.len() - 1])); // test output that's the right length but wrong value -- do_verify - let mut mac = HMAC::::new(&key).unwrap(); + let mut mac = HMAC_SHA224::new_allow_weak_key(&key).unwrap(); mac.do_update(b"Hi There"); assert!(!mac.do_verify_final(&[0x01_u8; 28])); // test output that's the right length but wrong value -- static verify - assert!(!HMAC_SHA224::new(&key).unwrap().verify(b"Hi There", &[0x01_u8; 28])); + assert!( + !HMAC_SHA224::new_allow_weak_key(&key).unwrap().verify(b"Hi There", &[0x01_u8; 28]) + ); // error case: test that it'll refuse to truncate below MIN_FIPS_DIGEST_LEN - let mut mac = HMAC::::new(&key).unwrap(); + let mut mac = HMAC_SHA224::new_allow_weak_key(&key).unwrap(); mac.do_update(b"Hi There"); let mut out = vec![0u8; MIN_FIPS_DIGEST_LEN - 1]; match mac.do_final_out(&mut out) { @@ -252,27 +217,12 @@ mod hmac_tests { } // success case: ... but it will truncate to exactly MIN_FIPS_DIGEST_LEN - let mut mac = HMAC::::new(&key).unwrap(); + let mut mac = HMAC_SHA224::new_allow_weak_key(&key).unwrap(); mac.do_update(b"Hi There"); let mut out = vec![0u8; MIN_FIPS_DIGEST_LEN]; let bytes_written = mac.do_final_out(&mut out).unwrap(); assert_eq!(bytes_written, MIN_FIPS_DIGEST_LEN); assert_eq!(&out, b"\x89\x6f\xb1\x12"); - - // fail case: mac value is correct but truncated - let mac = HMAC_SHA3_224::new(&key).unwrap(); - let mut mac_val = mac.mac(b"Polly want a cracker?"); - let verifier = HMAC_SHA3_224::new(&key).unwrap(); - assert!(verifier.verify(b"Polly want a cracker?", &mac_val)); - - // truncation of the mac value is considered a fail - let verifier = HMAC_SHA3_224::new(&key).unwrap(); - assert!(!verifier.verify(b"Polly want a cracker?", &mac_val[..mac_val.len() - 1])); - - // .. as is some extra bytes at the end - let verifier = HMAC_SHA3_224::new(&key).unwrap(); - mac_val.extend_from_slice(&[0u8; 4]); - assert!(!verifier.verify(b"Polly want a cracker?", &mac_val)); } #[test] @@ -282,10 +232,18 @@ mod hmac_tests { assert_eq!(HMAC_SHA256::ALG_NAME, HMAC_SHA256_NAME); assert_eq!(HMAC_SHA384::ALG_NAME, HMAC_SHA384_NAME); assert_eq!(HMAC_SHA512::ALG_NAME, HMAC_SHA512_NAME); - assert_eq!(HMAC_SHA3_224::ALG_NAME, HMAC_SHA3_224_NAME); - assert_eq!(HMAC_SHA3_256::ALG_NAME, HMAC_SHA3_256_NAME); - assert_eq!(HMAC_SHA3_384::ALG_NAME, HMAC_SHA3_384_NAME); - assert_eq!(HMAC_SHA3_512::ALG_NAME, HMAC_SHA3_512_NAME); + + assert_eq!(HMAC_SHA224::OID, [1, 2, 840, 113549, 2, 8]); + assert_eq!(HMAC_SHA256::OID, [1, 2, 840, 113549, 2, 9]); + assert_eq!(HMAC_SHA384::OID, [1, 2, 840, 113549, 2, 10]); + assert_eq!(HMAC_SHA512::OID, [1, 2, 840, 113549, 2, 11]); + + // Per SP 800-107r1 s.5.3.4 these are min(strength of K, 2C), which for every SHA-2 HMAC + // resolves to the key: OUTPUT_LEN bits, rounded down to a representable category. + assert_eq!(HMAC_SHA224::MAX_SECURITY_STRENGTH, SecurityStrength::_192bit); // 224 bits + assert_eq!(HMAC_SHA256::MAX_SECURITY_STRENGTH, SecurityStrength::_256bit); // 256 bits + assert_eq!(HMAC_SHA384::MAX_SECURITY_STRENGTH, SecurityStrength::_256bit); // 384, capped + assert_eq!(HMAC_SHA512::MAX_SECURITY_STRENGTH, SecurityStrength::_256bit); // 512, capped } #[cfg(test)] @@ -304,7 +262,7 @@ mod hmac_tests { assert_eq!(zero_length_key.key_len(), 0); assert_eq!(zero_length_key.key_type(), KeyType::MACKey); - test_framework.test_mac::>( + test_framework.test_mac::( &zero_length_key, b"Hello, world", &hex::decode("57454372e6a8780b11150274d7056c6fbcffef902f9c23f24fbbfee9").unwrap(), @@ -312,7 +270,7 @@ mod hmac_tests { // RFC4231 Test Case 1 let test_framework = TestFrameworkMAC::new(); - test_framework.test_mac::>( + test_framework.test_mac::( &KeyMaterial256::from_bytes_as_type( &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), KeyType::MACKey, @@ -323,7 +281,7 @@ mod hmac_tests { ); // RFC4231 Test Case 2 -- Test with a key shorter than the length of the HMAC output. - test_framework.test_mac::>( + test_framework.test_mac::( &KeyMaterial256::from_bytes_as_type(b"Jefe", KeyType::MACKey).unwrap(), b"what do ya want for nothing?", &hex::decode("a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44").unwrap(), @@ -331,14 +289,14 @@ mod hmac_tests { // RFC4231 Test Case 3 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd").unwrap(), &hex::decode("7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea").unwrap(), ); // RFC4231 Test Case 4 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd").unwrap(), &hex::decode("6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a").unwrap(), ); @@ -350,13 +308,16 @@ mod hmac_tests { ) .unwrap(); let mut out = [0u8; 128 / 8]; - HMAC::::new(&key).unwrap().mac_out(b"Test With Truncation", &mut out).unwrap(); + HMAC_SHA224::new_allow_weak_key(&key) + .unwrap() + .mac_out(b"Test With Truncation", &mut out) + .unwrap(); assert_eq!(&Vec::from(out), &hex::decode("0e2aea68a90c8d37c988bcdb9fca6fa8").unwrap()); // RFC4231 Test Case 6 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). let key = KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(); - test_framework.test_mac::>( + test_framework.test_mac::( &key, b"Test Using Larger Than Block-Size Key - Hash Key First", &hex::decode("95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e").unwrap(), @@ -364,7 +325,7 @@ mod hmac_tests { // RFC4231 Test Case 7 -- Test with a key and data that is larger than 128 bytes (= block-size // of SHA-384 and SHA-512) - test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", &hex::decode("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1").unwrap(), ); @@ -382,7 +343,7 @@ mod hmac_tests { assert_eq!(zero_length_key.key_len(), 0); assert_eq!(zero_length_key.key_type(), KeyType::MACKey); - test_framework.test_mac::>( + test_framework.test_mac::( &zero_length_key, b"Hello, world", &hex::decode("c0fa4c55880318c31c1020e7a2cf830c2c695716387795c7a0eb918ba84e4bf0") @@ -391,7 +352,7 @@ mod hmac_tests { // RFC4231 Test Case 1 let test_framework = TestFrameworkMAC::new(); - test_framework.test_mac::>( + test_framework.test_mac::( &KeyMaterial256::from_bytes_as_type( &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), KeyType::MACKey, @@ -403,7 +364,7 @@ mod hmac_tests { ); // RFC4231 Test Case 2 -- Test with a key shorter than the length of the HMAC output. - test_framework.test_mac::>( + test_framework.test_mac::( &KeyMaterial256::from_bytes_as_type(b"Jefe", KeyType::MACKey).unwrap(), b"what do ya want for nothing?", &hex::decode("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843") @@ -412,14 +373,14 @@ mod hmac_tests { // RFC4231 Test Case 3 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd").unwrap(), &hex::decode("773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe").unwrap(), ); // RFC4231 Test Case 4 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd").unwrap(), &hex::decode("82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b").unwrap(), ); @@ -431,19 +392,22 @@ mod hmac_tests { ) .unwrap(); let mut out = [0u8; 128 / 8]; - HMAC::::new(&key).unwrap().mac_out(b"Test With Truncation", &mut out).unwrap(); + HMAC_SHA256::new_allow_weak_key(&key) + .unwrap() + .mac_out(b"Test With Truncation", &mut out) + .unwrap(); assert_eq!(&Vec::from(out), &hex::decode("a3b6167473100ee06e0c796c2955552b").unwrap()); // RFC4231 Test Case 6 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"Test Using Larger Than Block-Size Key - Hash Key First", &hex::decode("60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54").unwrap(), ); // RFC4231 Test Case 7 -- Test with a key and data that is larger than 128 bytes (= block-size // of SHA-384 and SHA-512) - test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", &hex::decode("9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2").unwrap(), ); @@ -461,7 +425,7 @@ mod hmac_tests { assert_eq!(zero_length_key.key_len(), 0); assert_eq!(zero_length_key.key_type(), KeyType::MACKey); - test_framework.test_mac::>( + test_framework.test_mac::( &zero_length_key, b"Hello, world", &hex::decode("fbd41442f749049355175277afbaff610539e5bfa874c9cf86ef867a43a30b09a5eac6578d5c0cb1ceddc95f97598af7").unwrap(), @@ -469,7 +433,7 @@ mod hmac_tests { // RFC4231 Test Case 1 let test_framework = TestFrameworkMAC::new(); - test_framework.test_mac::>( + test_framework.test_mac::( &KeyMaterial256::from_bytes_as_type( &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), KeyType::MACKey, @@ -479,7 +443,7 @@ mod hmac_tests { ); // RFC4231 Test Case 2 -- Test with a key shorter than the length of the HMAC output. - test_framework.test_mac::>( + test_framework.test_mac::( &KeyMaterial256::from_bytes_as_type(b"Jefe", KeyType::MACKey).unwrap(), b"what do ya want for nothing?", &hex::decode("af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649").unwrap(), @@ -487,14 +451,14 @@ mod hmac_tests { // RFC4231 Test Case 3 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd").unwrap(), &hex::decode("88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27").unwrap(), ); // RFC4231 Test Case 4 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd").unwrap(), &hex::decode("3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb").unwrap(), ); @@ -507,20 +471,20 @@ mod hmac_tests { .unwrap(); let mut out = [0u8; 128 / 8]; // Key is shorter than HMAC security strength, so it needs to use new_allow_weak_keys() - let hmac = HMAC::::new_allow_weak_key(&key).unwrap(); + let hmac = HMAC_SHA384::new_allow_weak_key(&key).unwrap(); hmac.mac_out(b"Test With Truncation", &mut out).unwrap(); assert_eq!(&Vec::from(out), &hex::decode("3abf34c3503b2a23a46efc619baef897").unwrap()); // RFC4231 Test Case 6 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"Test Using Larger Than Block-Size Key - Hash Key First", &hex::decode("4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952").unwrap(), ); // RFC4231 Test Case 7 -- Test with a key and data that is larger than 128 bytes (= block-size // of SHA-384 and SHA-512) - test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", &hex::decode("6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e").unwrap(), ); @@ -538,7 +502,7 @@ mod hmac_tests { assert_eq!(zero_length_key.key_len(), 0); assert_eq!(zero_length_key.key_type(), KeyType::MACKey); - test_framework.test_mac::>( + test_framework.test_mac::( &zero_length_key, b"Hello, world", &hex::decode("e8f7176e01bf9bb883f71f42c143681e86cfafe0b61f3bc0d824e2cde13b5f80199e82d865aebb725461c86a54086aeacac37a86a9f1cf07db567ba5a10f1cc1").unwrap(), @@ -546,7 +510,7 @@ mod hmac_tests { // RFC4231 Test Case 1 let test_framework = TestFrameworkMAC::new(); - test_framework.test_mac::>( + test_framework.test_mac::( &KeyMaterial256::from_bytes_as_type( &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), KeyType::MACKey, @@ -557,7 +521,7 @@ mod hmac_tests { ); // RFC4231 Test Case 2 -- Test with a key shorter than the length of the HMAC output. - test_framework.test_mac::>( + test_framework.test_mac::( &KeyMaterial256::from_bytes_as_type(b"Jefe", KeyType::MACKey).unwrap(), b"what do ya want for nothing?", &hex::decode("164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737").unwrap(), @@ -565,14 +529,14 @@ mod hmac_tests { // RFC4231 Test Case 3 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd").unwrap(), &hex::decode("fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb").unwrap(), ); // RFC4231 Test Case 4 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd").unwrap(), &hex::decode("b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd").unwrap(), ); @@ -585,20 +549,20 @@ mod hmac_tests { .unwrap(); let mut out = [0u8; 128 / 8]; // Key is shorter than HMAC security strength, so need to use new_allow_weak_keys() - let hmac = HMAC::::new_allow_weak_key(&key).unwrap(); + let hmac = HMAC_SHA512::new_allow_weak_key(&key).unwrap(); hmac.mac_out(b"Test With Truncation", &mut out).unwrap(); assert_eq!(&Vec::from(out), &hex::decode("415fad6271580a531d4179bc891d87a6").unwrap()); // RFC4231 Test Case 6 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). - test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"Test Using Larger Than Block-Size Key - Hash Key First", &hex::decode("80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598").unwrap(), ); // RFC4231 Test Case 7 -- Test with a key and data that is larger than 128 bytes (= block-size // of SHA-384 and SHA-512) - test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), + test_framework.test_mac::(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", &hex::decode("e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58").unwrap(), ); @@ -660,7 +624,6 @@ mod hmac_tests { round_trip(HMAC_SHA256::new(&key).unwrap(), &key, msg); round_trip(HMAC_SHA512::new(&key).unwrap(), &key, msg); - round_trip(HMAC_SHA3_256::new(&key).unwrap(), &key, msg); // test suspend / resume with a key larger than block size let long_key = @@ -676,7 +639,7 @@ mod hmac_tests { KeyType::MACKey, ) .unwrap(); - let hmac = HMAC_SHA256::new(&key).unwrap(); + let hmac = HMAC_SHA256::new_allow_weak_key(&key).unwrap(); // test fmt let fmt_str = format!("{}", &hmac); @@ -692,7 +655,7 @@ mod hmac_tests { /// * `HMAC::new(&key)` accepts the freshly generated key, without error. /// /// HashDRBG_SHA512 is used throughout because it is the only built-in DRBG that meets the - /// 256-bit strength that HMAC-SHA512 and HMAC-SHA3-512 claim; see `keygen_rejects_weak_rng`. + /// 256-bit strength that HMAC-SHA512 claims; see `keygen_rejects_weak_rng`. macro_rules! keygen_test { ($test_name:ident, $hmac:ident, $n:literal) => { #[test] @@ -716,27 +679,29 @@ mod hmac_tests { keygen_test!(keygen_hmac_sha256, HMAC_SHA256, 32); keygen_test!(keygen_hmac_sha384, HMAC_SHA384, 48); keygen_test!(keygen_hmac_sha512, HMAC_SHA512, 64); - keygen_test!(keygen_hmac_sha3_224, HMAC_SHA3_224, 28); - keygen_test!(keygen_hmac_sha3_256, HMAC_SHA3_256, 32); - keygen_test!(keygen_hmac_sha3_384, HMAC_SHA3_384, 48); - keygen_test!(keygen_hmac_sha3_512, HMAC_SHA3_512, 64); /// `keygen_from_rng` must refuse an RNG whose security strength is below the strength the HMAC /// claims, otherwise the returned key would be tagged stronger than the entropy behind it. - /// HashDRBG_SHA256 offers 128 bits, which is enough for HMAC-SHA256 but not for HMAC-SHA512. + /// HashDRBG_SHA256 offers 128 bits. Since SP 800-107r1 s.5.3.4 puts every SHA-2 HMAC at 192 + /// bits or more, it is now too weak for all of them, and a 256-bit generator is required. + /// `DefaultRNG` resolves to HashDRBG_SHA512 and so qualifies out of the box. #[test] fn keygen_rejects_weak_rng() { - let mut weak_rng = HashDRBG_SHA256::new_from_os(); - assert!( - matches!( - HMAC_SHA512::keygen_from_rng(&mut weak_rng), - Err(RNGError::SecurityStrengthInsufficientForAlgorithm) - ), - "a 128-bit RNG must not be accepted for a 256-bit HMAC" - ); + for refused in [ + HMAC_SHA224::keygen_from_rng(&mut HashDRBG_SHA256::new_from_os()).err(), + HMAC_SHA256::keygen_from_rng(&mut HashDRBG_SHA256::new_from_os()).err(), + HMAC_SHA384::keygen_from_rng(&mut HashDRBG_SHA256::new_from_os()).err(), + HMAC_SHA512::keygen_from_rng(&mut HashDRBG_SHA256::new_from_os()).err(), + ] { + assert!( + matches!(refused, Some(RNGError::SecurityStrengthInsufficientForAlgorithm)), + "a 128-bit RNG must not be accepted for an HMAC claiming 192 bits or more" + ); + } - let mut ok_rng = HashDRBG_SHA256::new_from_os(); + // A 256-bit generator backs every SHA-2 HMAC in the crate. + let mut ok_rng = HashDRBG_SHA512::new_from_os(); HMAC_SHA256::keygen_from_rng(&mut ok_rng) - .expect("a 128-bit RNG is sufficient for a 128-bit HMAC"); + .expect("a 256-bit RNG is sufficient for a 256-bit HMAC"); } } diff --git a/crypto/sha3/src/hmac.rs b/crypto/sha3/src/hmac.rs index 7a7240a4..4e36dbf3 100644 --- a/crypto/sha3/src/hmac.rs +++ b/crypto/sha3/src/hmac.rs @@ -4,12 +4,15 @@ //! Uses [`bouncycastle_hmac`] to provide the HMAC-SHA3 instantiations: [`HMAC_SHA3_224`], //! [`HMAC_SHA3_256`], [`HMAC_SHA3_384`] and [`HMAC_SHA3_512`]. //! -//! HMAC itself is implemented generically in [`bouncycastle_hmac`]; this module supplies the -//! SHA-3-specific parameters via [`HMACParams`] and publishes the resulting type aliases, so that +//! HMAC itself is implemented generically in [`bouncycastle_hmac`]; this module declares one +//! [`HMACParams`] marker type per instantiation (such as [`HMAC_SHA3_256Params`]), carrying that +//! HMAC's name, claimed strength, OID and key type, and publishes the type alias pairing each +//! marker with its hash. This mirrors how the hashes themselves are built, where `SHA256` is +//! `SHA256Internal`. The upshot is that //! HMAC over a SHA3 hash is found in this crate, and [`bouncycastle_hmac`] serves as a utility crate //! rather than as part of library's public API. //! -//! The key buffer length of each alias is the underlying hash's block length: per RFC 2104, a key no +//! Each params type sizes the internal key buffer to its hash's block length: per RFC 2104, a key no //! longer than the block is used verbatim, and only longer keys are pre-hashed down to the output //! length, so the buffer must be able to hold a full block. It is taken from //! [`HashAlgParams::BLOCK_LEN`] -- the values FIPS 202 Table 3 ("Input block sizes for HMAC") gives @@ -65,7 +68,8 @@ //! use bouncycastle_sha3::hmac::HMAC_SHA3_256; //! //! let key = KeyMaterial256::from_bytes_as_type( -//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", +//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\ +//! \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", //! KeyType::MACKey).unwrap(); //! //! let hmac = HMAC_SHA3_256::new(&key).expect( @@ -128,7 +132,8 @@ //! // For this example to work, we are hard-coding both the key and the MAC value that it generates //! // for this data. //! let key = KeyMaterial256::from_bytes_as_type( -//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", +//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\ +//! \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", //! KeyType::MACKey).unwrap(); //! //! let data: &[u8] = b"Hello, world!"; @@ -136,8 +141,8 @@ //! // .verify() returns a bool: true if the MAC is valid, false otherwise. //! if HMAC_SHA3_256::new(&key).unwrap() //! .verify(data, -//! b"\x9c\x49\x05\x83\xff\xf3\x59\x6a\x59\x01\x4a\x0d\x95\xb4\x64\x00 -//! \x7d\x5b\xb7\x40\xb3\x84\x20\x7a\x3c\x76\x76\xd8\xc9\x93\xda\xd7" +//! b"\x5d\x16\xf1\xc4\xcc\x22\x83\x8a\xd0\x53\xe6\xb6\x9b\xb2\xd1\x5a +//! \x2a\x79\x35\x76\xb0\x80\x7d\xec\x50\x78\xa1\x36\x99\x33\x7d\xfd" //! ) //! { //! println!("MAC is valid!"); @@ -157,12 +162,13 @@ //! // For this example to work, we are hard-coding both the key and the MAC value that it generates //! // for this data. //! let key = KeyMaterial256::from_bytes_as_type( -//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", +//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\ +//! \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", //! KeyType::MACKey).unwrap(); //! let mut hmac = HMAC_SHA3_256::new(&key).unwrap(); //! hmac.do_update(b"Hello,"); //! hmac.do_update(b" world!"); -//! if hmac.do_verify_final(b"\x9c\x49\x05\x83\xff\xf3\x59\x6a\x59\x01\x4a\x0d\x95\xb4\x64\x00\x7d\x5b\xb7\x40\xb3\x84\x20\x7a\x3c\x76\x76\xd8\xc9\x93\xda\xd7" +//! if hmac.do_verify_final(b"\x5d\x16\xf1\xc4\xcc\x22\x83\x8a\xd0\x53\xe6\xb6\x9b\xb2\xd1\x5a\x2a\x79\x35\x76\xb0\x80\x7d\xec\x50\x78\xa1\x36\x99\x33\x7d\xfd" //! ) //! { //! println!("MAC is valid!"); @@ -193,7 +199,8 @@ //! let msg_part2 = b" jumped over the lazy dog"; //! //! let key = KeyMaterial256::from_bytes_as_type( -//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", +//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\ +//! \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", //! KeyType::MACKey).unwrap(); //! //! let mut hmac = HMAC_SHA3_256::new(&key).unwrap(); @@ -234,9 +241,27 @@ //! //! # Security Considerations //! -//! * The key must carry at least the security strength claimed by the HMAC, and [`MAC::new`] -//! enforces that. [`MAC::new_allow_weak_key`] deliberately skips the check; use it only where a -//! weak or all-zero key is called for by the protocol, not to silence an error. +//! * Each of these HMACs claims the strength NIST SP 800-107r1 Section 5.3.4 gives it, which is +//! `min(strength of K, 2C)` and works out to the key length for the whole family: 224 bits for +//! HMAC-SHA3-224 and 256 or more for the rest. `SecurityStrength` has no 224-bit category and +//! tops out at 256, so the declared values are `_192bit` for HMAC-SHA3-224 and `_256bit` for +//! HMAC-SHA3-256, HMAC-SHA3-384 and HMAC-SHA3-512. Note these are *not* the underlying hashes' +//! collision strengths, which are half as large; footnote 4 of that section puts collision +//! attacks out of scope for HMAC. +//! * That figure is an **extrapolation**. SP 800-107r1 is older than SHA-3 and does not cover it: +//! `C` there is the FIPS 180-4 chaining value, which a sponge does not have. See the note above +//! the params types for the analogue used and why the choice does not change the answer. +//! * The key must carry at least the strength claimed by the HMAC, and [`MAC::new`] enforces that. +//! A 20-byte key is therefore not enough for HMAC-SHA3-256; a full 32-byte key is. +//! [`MAC::new_allow_weak_key`] deliberately skips the check; use it only where a weak or all-zero +//! key is called for by the protocol, or by a fixed test vector, not to silence an error. +//! * The same rule applies to the generator. [`HMAC::keygen_from_rng`] tags the key it returns at +//! the HMAC's claimed strength, so it refuses any RNG that cannot back that tag: a 256-bit +//! generator is required for HMAC-SHA3-256 and above, and `bouncycastle_rng::DefaultRNG` is +//! `HashDRBG_SHA512` and qualifies for all of them. `HashDRBG_SHA256` offers 128 bits and is +//! refused by every HMAC in this module. There is no weak-RNG opt-out, because a generator cannot +//! be asked for entropy it does not have; build the key yourself and use +//! [`MAC::new_allow_weak_key`] if that is genuinely what you want. //! * Verify with [`MAC::verify`] or [`MAC::do_verify_final`] rather than computing the MAC yourself //! and comparing: those use a constant-time comparison, while `==` on the byte slices leaks how //! many leading bytes matched. @@ -254,7 +279,7 @@ use crate::SUSPENDED_SHA3_STATE_LEN; use crate::{SHA3_224, SHA3_256, SHA3_384, SHA3_512}; use bouncycastle_core::key_material::KeyMaterial; -use bouncycastle_core::traits::{HashAlgParams, SecurityStrength}; +use bouncycastle_core::traits::{Algorithm, AlgorithmOID, HashAlgParams, SecurityStrength}; use bouncycastle_hmac::{HMAC, HMACParams}; /*** Imports needed for docs ***/ @@ -275,59 +300,124 @@ pub const HMAC_SHA3_384_NAME: &str = "HMAC-SHA3-384"; /// pub const HMAC_SHA3_512_NAME: &str = "HMAC-SHA3-512"; -/*** Type aliases ***/ -/// Public type for HMAC using SHA3_224. +/*** Params types and type aliases ***/ + +/// The parameters for HMAC-SHA3_224 -- see [`HMAC_SHA3_224`]. +#[derive(Clone)] #[allow(non_camel_case_types)] -pub type HMAC_SHA3_224 = HMAC::BLOCK_LEN }>; -impl HMACParams for SHA3_224 { - type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; - const HMAC_ALG_NAME: &'static str = HMAC_SHA3_224_NAME; - const HMAC_MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_112bit; - /// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-224 { hashAlgs 13 } - const HMAC_OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 13]; - const HMAC_OID_DER: &'static [u8] = +pub struct HMAC_SHA3_224Params; + +impl Algorithm for HMAC_SHA3_224Params { + const ALG_NAME: &'static str = HMAC_SHA3_224_NAME; + // SP 800-107r1 s.5.3.4, extrapolated from SHA2 to SHA3: min(strength of K, 2C). + // SHA3-224 has capacity c = 448, so 2c = 896, and the key is OUTPUT_LEN = 224 bits, + // so the key binds: 224 bits, rounded down to the nearest category. + const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit; +} + +/// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-224 { hashAlgs 13 } +impl AlgorithmOID for HMAC_SHA3_224Params { + const OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 13]; + const OID_DER: &'static [u8] = &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0d]; } -/// Public type for HMAC using SHA3_256. +impl HMACParams for HMAC_SHA3_224Params { + type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; + type KeyBuf = [u8; ::BLOCK_LEN]; +} + +/// Public type for HMAC using SHA3_224. #[allow(non_camel_case_types)] -pub type HMAC_SHA3_256 = HMAC::BLOCK_LEN }>; -impl HMACParams for SHA3_256 { - type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; - const HMAC_ALG_NAME: &'static str = HMAC_SHA3_256_NAME; - const HMAC_MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit; - /// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-256 { hashAlgs 14 } - const HMAC_OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 14]; - const HMAC_OID_DER: &'static [u8] = +pub type HMAC_SHA3_224 = HMAC; + +/// The parameters for HMAC-SHA3_256 -- see [`HMAC_SHA3_256`]. +#[derive(Clone)] +#[allow(non_camel_case_types)] +pub struct HMAC_SHA3_256Params; + +impl Algorithm for HMAC_SHA3_256Params { + const ALG_NAME: &'static str = HMAC_SHA3_256_NAME; + // SP 800-107r1 s.5.3.4, extrapolated from SHA2 to SHA3: min(strength of K, 2C). + // SHA3-256 has capacity c = 512, so 2c = 1024, and the key is OUTPUT_LEN = 256 bits, + // so the key binds: 256 bits. + const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; +} + +/// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-256 { hashAlgs 14 } +impl AlgorithmOID for HMAC_SHA3_256Params { + const OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 14]; + const OID_DER: &'static [u8] = &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0e]; } -/// Public type for HMAC using SHA3_384. +impl HMACParams for HMAC_SHA3_256Params { + type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; + type KeyBuf = [u8; ::BLOCK_LEN]; +} + +/// Public type for HMAC using SHA3_256. #[allow(non_camel_case_types)] -pub type HMAC_SHA3_384 = HMAC::BLOCK_LEN }>; -impl HMACParams for SHA3_384 { - type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; - const HMAC_ALG_NAME: &'static str = HMAC_SHA3_384_NAME; - const HMAC_MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit; - /// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-384 { hashAlgs 15 } - const HMAC_OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 15]; - const HMAC_OID_DER: &'static [u8] = +pub type HMAC_SHA3_256 = HMAC; + +/// The parameters for HMAC-SHA3_384 -- see [`HMAC_SHA3_384`]. +#[derive(Clone)] +#[allow(non_camel_case_types)] +pub struct HMAC_SHA3_384Params; + +impl Algorithm for HMAC_SHA3_384Params { + const ALG_NAME: &'static str = HMAC_SHA3_384_NAME; + // SP 800-107r1 s.5.3.4, extrapolated from SHA2 to SHA3: min(strength of K, 2C). + // SHA3-384 has capacity c = 768, so 2c = 1536, and the key is OUTPUT_LEN = 384 bits, + // so the key binds: 384 bits, capped at the top of `SecurityStrength`. + const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; +} + +/// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-384 { hashAlgs 15 } +impl AlgorithmOID for HMAC_SHA3_384Params { + const OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 15]; + const OID_DER: &'static [u8] = &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0f]; } -/// Public type for HMAC using SHA3_512. +impl HMACParams for HMAC_SHA3_384Params { + type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; + type KeyBuf = [u8; ::BLOCK_LEN]; +} + +/// Public type for HMAC using SHA3_384. #[allow(non_camel_case_types)] -pub type HMAC_SHA3_512 = HMAC::BLOCK_LEN }>; -impl HMACParams for SHA3_512 { - type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; - const HMAC_ALG_NAME: &'static str = HMAC_SHA3_512_NAME; - const HMAC_MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; - /// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-512 { hashAlgs 16 } - const HMAC_OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 16]; - const HMAC_OID_DER: &'static [u8] = +pub type HMAC_SHA3_384 = HMAC; + +/// The parameters for HMAC-SHA3_512 -- see [`HMAC_SHA3_512`]. +#[derive(Clone)] +#[allow(non_camel_case_types)] +pub struct HMAC_SHA3_512Params; + +impl Algorithm for HMAC_SHA3_512Params { + const ALG_NAME: &'static str = HMAC_SHA3_512_NAME; + // SP 800-107r1 s.5.3.4, extrapolated from SHA2 to SHA3: min(strength of K, 2C). + // SHA3-512 has capacity c = 1024, so 2c = 2048, and the key is OUTPUT_LEN = 512 bits, + // so the key binds: 512 bits, capped at the top of `SecurityStrength`. + const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; +} + +/// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-512 { hashAlgs 16 } +impl AlgorithmOID for HMAC_SHA3_512Params { + const OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 16]; + const OID_DER: &'static [u8] = &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x10]; } +impl HMACParams for HMAC_SHA3_512Params { + type MACKey = KeyMaterial<{ ::OUTPUT_LEN }>; + type KeyBuf = [u8; ::BLOCK_LEN]; +} + +/// Public type for HMAC using SHA3_512. +#[allow(non_camel_case_types)] +pub type HMAC_SHA3_512 = HMAC; + /*** Serialized-state length constants ***/ // HMAC's suspended state is exactly the inner hasher's state -- the key is deliberately excluded and // must be re-supplied on resume -- so each of these is the underlying hash's own state length. All diff --git a/crypto/sha3/src/lib.rs b/crypto/sha3/src/lib.rs index 22c1dc0b..f2152646 100644 --- a/crypto/sha3/src/lib.rs +++ b/crypto/sha3/src/lib.rs @@ -118,11 +118,35 @@ //! ## HMAC //! See [hmac]. //! +//! # Cloning mid-stream +//! +//! Sometimes it is necessary to clone the state of the hash function after absorbing some input, for +//! example, if you have absorbed a large file and need to hash it with two different trailer suffixes. +//! +//! The [`SHA3Internal`] struct impls [`Clone`] for this purpose. +//! +//! ```rust +//! use bouncycastle_core::traits::Hash; +//! use bouncycastle_sha3 as sha3; +//! +//! let mut sha3 = sha3::SHA3_256::new(); +//! sha3.do_update(b"Some input"); +//! // Clone it so that we can finish this instance, and then continue feeding in more input. +//! let mut sha3_2 = sha3.clone(); +//! +//! // Finish the first instance +//! let output: Vec = sha3.do_final(); +//! +//! // Feed more into the second instance then squeeze it +//! sha3_2.do_update(b"Some more input"); +//! let output2 = sha3_2.do_final(); +//! ``` +//! //! # Suspending and resuming execution //! -//! When hashing a large message, it can be advantageous to be able to suspend the operation -//! to a cache and resume it later; for example if waiting for the message to stream over a slow network -//! connection. +//! Suspending is similar to cloning, but compresses the hash function's state into a byte array that can be +//! cached and then resumed at a later time, potentially from a different machine. The typical usage for this +//! is when waiting for a slow IO operation. //! //! For this reason, all SHA3 algorithms impl [`Suspendable`]. //! diff --git a/crypto/sha3/tests/hmac_tests.rs b/crypto/sha3/tests/hmac_tests.rs new file mode 100644 index 00000000..600b40b4 --- /dev/null +++ b/crypto/sha3/tests/hmac_tests.rs @@ -0,0 +1,155 @@ +#[cfg(test)] +mod hmac_sha3_tests { + use bouncycastle_core::key_material::{KeyMaterial, KeyMaterial256, KeyMaterialTrait, KeyType}; + use bouncycastle_core::traits::{Algorithm, AlgorithmOID, MAC, SecurityStrength}; + use bouncycastle_core_test_framework::DUMMY_SEED; + use bouncycastle_rng::HashDRBG_SHA512; + use bouncycastle_sha3::hmac::*; + + #[test] + fn long_key() { + // Regression test: a key just under the maximum length before HMAC will hash it down. + // (RFC 2104 only pre-hashes keys *longer* than the block). + // This test is designed to detect an overflow-write and panic on HMAC's internal key buffer. + + // SHA3-224 has the largest block (144 bytes); a 143-byte key exercises the top of the range. + let key = KeyMaterial::<200>::from_bytes_as_type(&[0x0B; 143], KeyType::MACKey).unwrap(); + let mut mac = HMAC_SHA3_224::new(&key).unwrap(); + mac.do_update(b"Hi There"); + let tag = mac.do_final(); + assert!(HMAC_SHA3_224::new(&key).unwrap().verify(b"Hi There", &tag)); + } + + #[test] + fn negative_tests() { + let key = KeyMaterial256::from_bytes_as_type( + b"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + KeyType::MACKey, + ) + .unwrap(); + + // fail case: mac value is correct but truncated. The 20-byte key is below the strength + // HMAC-SHA3-224 claims, so these opt in to a weak key. + let mac = HMAC_SHA3_224::new_allow_weak_key(&key).unwrap(); + let mut mac_val = mac.mac(b"Polly want a cracker?"); + let verifier = HMAC_SHA3_224::new_allow_weak_key(&key).unwrap(); + assert!(verifier.verify(b"Polly want a cracker?", &mac_val)); + + // truncation of the mac value is considered a fail + let verifier = HMAC_SHA3_224::new_allow_weak_key(&key).unwrap(); + assert!(!verifier.verify(b"Polly want a cracker?", &mac_val[..mac_val.len() - 1])); + + // .. as is some extra bytes at the end + let verifier = HMAC_SHA3_224::new_allow_weak_key(&key).unwrap(); + mac_val.extend_from_slice(&[0u8; 4]); + assert!(!verifier.verify(b"Polly want a cracker?", &mac_val)); + } + + #[test] + fn algorithm_tests() { + // Test the type aliases and string constants + assert_eq!(HMAC_SHA3_224::ALG_NAME, HMAC_SHA3_224_NAME); + assert_eq!(HMAC_SHA3_256::ALG_NAME, HMAC_SHA3_256_NAME); + assert_eq!(HMAC_SHA3_384::ALG_NAME, HMAC_SHA3_384_NAME); + assert_eq!(HMAC_SHA3_512::ALG_NAME, HMAC_SHA3_512_NAME); + + assert_eq!(HMAC_SHA3_224::OID, [2, 16, 840, 1, 101, 3, 4, 2, 13]); + assert_eq!(HMAC_SHA3_256::OID, [2, 16, 840, 1, 101, 3, 4, 2, 14]); + assert_eq!(HMAC_SHA3_384::OID, [2, 16, 840, 1, 101, 3, 4, 2, 15]); + assert_eq!(HMAC_SHA3_512::OID, [2, 16, 840, 1, 101, 3, 4, 2, 16]); + + // Per SP 800-107r1 s.5.3.4, extrapolated to SHA-3: min(strength of K, 2C) resolves to the + // key in every case, so these are OUTPUT_LEN rounded down to a representable category. + assert_eq!(HMAC_SHA3_224::MAX_SECURITY_STRENGTH, SecurityStrength::_192bit); // 224 bits + assert_eq!(HMAC_SHA3_256::MAX_SECURITY_STRENGTH, SecurityStrength::_256bit); // 256 bits + assert_eq!(HMAC_SHA3_384::MAX_SECURITY_STRENGTH, SecurityStrength::_256bit); // 384, capped + assert_eq!(HMAC_SHA3_512::MAX_SECURITY_STRENGTH, SecurityStrength::_256bit); // 512, capped + } + + #[test] + fn suspendable_keyed_state() { + use bouncycastle_core::errors::SuspendableError; + use bouncycastle_core::suspendable_state::LIB_VERSION; + use bouncycastle_core::traits::SuspendableKeyed; + use bouncycastle_core_test_framework::suspendable_state::TestFrameworkSuspendableKeyedState; + + let key = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED[..32], KeyType::MACKey).unwrap(); + let msg = b"Colorless green ideas sleep furiously"; + + // A helper that exercises the full round-trip for one HMAC variant. HMAC is keyed, so the + // key is NOT in the serialized state -- it is re-supplied (by reference) to + // from_serialized_state. + // The `+ 'static` on the trait object matches the associated type `type Key = dyn + // KeyMaterialTrait` (a bare `dyn` in an associated type defaults to `'static`). The concrete + // key types are owned, so they satisfy it. + fn round_trip( + mut hmac: H, + key: &(dyn KeyMaterialTrait + 'static), + input: &[u8], + ) where + H: MAC + Clone + SuspendableKeyed, + { + hmac.do_update(&input[..10]); + + // do the default trait-conformance tests + TestFrameworkSuspendableKeyedState::new().test(&hmac, key); + + // serialize the in-progress state (on a clone), then finish the original + let serialized_state = hmac.clone().suspend(); + + // the serialized state carries the library version header (from the inner hash) + let header: [u8; 3] = serialized_state[..3].try_into().unwrap(); + assert_eq!(header, <[u8; 3]>::from(LIB_VERSION)); + + hmac.do_update(&input[10..]); + let expected = hmac.do_final(); + + // rebuild from the serialized state (re-supplying the key), feed the identical remaining + // input, and confirm the MAC matches + let mut from_state = H::from_suspended(serialized_state, key).unwrap(); + from_state.do_update(&input[10..]); + assert_eq!(expected, from_state.do_final()); + + // a state whose version header is zeroed must be rejected (delegated to the hash's impl) + let mut busted = serialized_state; + busted[..3].copy_from_slice(&[0, 0, 0]); + match H::from_suspended(busted, key) { + Err(SuspendableError::IncompatibleVersion) => { /* good */ } + _ => panic!("Expected IncompatibleVersion for a zeroed version header"), + } + } + + round_trip(HMAC_SHA3_256::new(&key).unwrap(), &key, msg); + } + + /// Exercises the `keygen_from_rng()` function of each HMAC type alias: + /// * the generated key must not be the all-zero array, + /// * `keygen_from_rng()` returns a ready-to-use `KeyType::MACKey` key, so that + /// * `HMAC::new(&key)` accepts the freshly generated key, without error. + /// + /// HashDRBG_SHA512 is used throughout because it is the only built-in DRBG that meets the + /// 256-bit strength that HMAC-SHA3-512 claims. + macro_rules! keygen_test { + ($test_name:ident, $hmac:ident, $n:literal) => { + #[test] + fn $test_name() { + let mut rng = HashDRBG_SHA512::new_from_os(); + let key = $hmac::keygen_from_rng(&mut rng).expect("keygen_from_rng should succeed"); + + assert_eq!(key.key_len(), $n, "key should be the hash's output length"); + assert_eq!(key.key_type(), KeyType::MACKey, "keygen should return a MAC key"); + assert!( + key.ref_to_bytes().iter().any(|&b| b != 0), + "keygen produced an all-zero key" + ); + + $hmac::new(&key).expect("HMAC::new should accept a freshly generated key"); + } + }; + } + + keygen_test!(keygen_hmac_sha3_224, HMAC_SHA3_224, 28); + keygen_test!(keygen_hmac_sha3_256, HMAC_SHA3_256, 32); + keygen_test!(keygen_hmac_sha3_384, HMAC_SHA3_384, 48); + keygen_test!(keygen_hmac_sha3_512, HMAC_SHA3_512, 64); +}