diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/orion/src/hazardous/mac | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/orion/src/hazardous/mac')
-rw-r--r-- | vendor/orion/src/hazardous/mac/blake2b.rs | 435 | ||||
-rw-r--r-- | vendor/orion/src/hazardous/mac/hmac.rs | 919 | ||||
-rw-r--r-- | vendor/orion/src/hazardous/mac/mod.rs | 30 | ||||
-rw-r--r-- | vendor/orion/src/hazardous/mac/poly1305.rs | 571 |
4 files changed, 1955 insertions, 0 deletions
diff --git a/vendor/orion/src/hazardous/mac/blake2b.rs b/vendor/orion/src/hazardous/mac/blake2b.rs new file mode 100644 index 000000000..65fc86cf7 --- /dev/null +++ b/vendor/orion/src/hazardous/mac/blake2b.rs @@ -0,0 +1,435 @@ +// MIT License + +// Copyright (c) 2018-2022 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `secret_key`: The authentication key. +//! - `size`: The desired output length for the authentication tag. +//! - `data`: Data to be authenticated. +//! - `expected`: The expected authentication tag. +//! +//! # Errors: +//! An error will be returned if: +//! - `size` is 0 or greater than 64. +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) bytes of data are hashed. +//! +//! # Security: +//! - The secret key should always be generated using a CSPRNG. +//! [`SecretKey::generate()`] can be used for this. It generates +//! a secret key of 32 bytes. +//! - The minimum recommended size for a secret key is 32 bytes. +//! - The recommended minimum output size is 32. +//! - This interface only allows creating authentication tag using BLAKE2b. If hash digests are needed, +//! please refer to the [`hash::blake2::blake2b`] module. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::mac::blake2b::{Blake2b, SecretKey}; +//! +//! let key = SecretKey::generate(); +//! +//! let mut state = Blake2b::new(&key, 64)?; +//! state.update(b"Some data")?; +//! let tag = state.finalize()?; +//! +//! assert!(Blake2b::verify(&tag, &key, 64, b"Some data").is_ok()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: blake2b::Blake2b::update +//! [`reset()`]: blake2b::Blake2b::reset +//! [`finalize()`]: blake2b::Blake2b::finalize +//! [`SecretKey::generate()`]: blake2b::SecretKey::generate +//! [`hash::blake2::blake2b`]: crate::hazardous::hash::blake2::blake2b + +use crate::errors::UnknownCryptoError; +use crate::hazardous::hash::blake2::blake2b_core::{self, BLAKE2B_KEYSIZE, BLAKE2B_OUTSIZE}; +use core::ops::DerefMut; +use zeroize::Zeroizing; + +construct_secret_key! { + /// A type to represent the secret key that BLAKE2b uses for keyed mode. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `slice` is greater than 64 bytes. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, test_secret_key, 1, BLAKE2B_KEYSIZE, 32) +} + +construct_tag! { + /// A type to represent the `Tag` that BLAKE2b returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `slice` is greater than 64 bytes. + (Tag, test_tag, 1, BLAKE2B_OUTSIZE) +} + +#[derive(Debug, Clone)] +/// BLAKE2b streaming state. +pub struct Blake2b { + _state: blake2b_core::State, +} + +impl Blake2b { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize a `Blake2b` struct with a given size (in bytes) and key. + pub fn new(secret_key: &SecretKey, size: usize) -> Result<Self, UnknownCryptoError> { + Ok(Self { + _state: blake2b_core::State::_new(secret_key.unprotected_as_bytes(), size)?, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Reset to `new()` state. + pub fn reset(&mut self, secret_key: &SecretKey) -> Result<(), UnknownCryptoError> { + self._state._reset(secret_key.unprotected_as_bytes()) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a BLAKE2b tag. + pub fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> { + let mut tmp: Zeroizing<[u8; BLAKE2B_OUTSIZE]> = Zeroizing::new([0u8; BLAKE2B_OUTSIZE]); + self._state._finalize(tmp.deref_mut())?; + + Tag::from_slice(&tmp[..self._state.size]) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify a BLAKE2b tag in constant time. + pub fn verify( + expected: &Tag, + secret_key: &SecretKey, + size: usize, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + let mut ctx = Self::new(secret_key, size)?; + ctx.update(data)?; + + if &ctx.finalize()? == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } +} + +#[cfg(test)] +mod public { + mod test_streaming_interface_no_key { + use crate::errors::UnknownCryptoError; + use crate::hazardous::hash::blake2::blake2b_core::{ + compare_blake2b_states, BLAKE2B_BLOCKSIZE, BLAKE2B_OUTSIZE, + }; + use crate::hazardous::mac::blake2b::{Blake2b, SecretKey, Tag}; + use crate::test_framework::incremental_interface::{ + StreamingContextConsistencyTester, TestableStreamingContext, + }; + + const KEY: [u8; 32] = [255u8; 32]; + + impl TestableStreamingContext<Tag> for Blake2b { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + let key = SecretKey::from_slice(&KEY).unwrap(); + self.reset(&key) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Tag, UnknownCryptoError> { + let key = SecretKey::from_slice(&KEY).unwrap(); + let mut ctx = Blake2b::new(&key, BLAKE2B_OUTSIZE)?; + ctx.update(input)?; + ctx.finalize() + } + + fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Blake2b, state_2: &Blake2b) { + compare_blake2b_states(&state_1._state, &state_2._state) + } + } + + #[test] + fn default_consistency_tests() { + let key = SecretKey::from_slice(&KEY).unwrap(); + let initial_state: Blake2b = Blake2b::new(&key, BLAKE2B_OUTSIZE).unwrap(); + + let test_runner = StreamingContextConsistencyTester::<Tag, Blake2b>::new( + initial_state, + BLAKE2B_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec<u8>) -> bool { + let key = SecretKey::from_slice(&KEY).unwrap(); + let initial_state: Blake2b = Blake2b::new(&key, BLAKE2B_OUTSIZE).unwrap(); + + let test_runner = StreamingContextConsistencyTester::<Tag, Blake2b>::new( + initial_state, + BLAKE2B_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + mod test_new { + use crate::hazardous::mac::blake2b::{Blake2b, SecretKey}; + + #[test] + fn test_init_size() { + let sk = SecretKey::from_slice(&[0u8; 32]).unwrap(); + assert!(Blake2b::new(&sk, 0).is_err()); + assert!(Blake2b::new(&sk, 65).is_err()); + assert!(Blake2b::new(&sk, 1).is_ok()); + assert!(Blake2b::new(&sk, 64).is_ok()); + } + } + + #[cfg(feature = "safe_api")] + mod test_verify { + use crate::hazardous::mac::blake2b::{Blake2b, SecretKey}; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different key, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_key_false(data: Vec<u8>) -> bool { + let sk = SecretKey::generate(); + let mut state = Blake2b::new(&sk, 64).unwrap(); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + let bad_sk = SecretKey::generate(); + + Blake2b::verify(&tag, &bad_sk, 64, &data[..]).is_err() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different size, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_size_false(data: Vec<u8>, size_one: usize, size_two: usize) -> bool { + let (size_one, size_two) = match (size_one, size_two) { + (1..=64, 1..=64) => (size_one, size_two), + (_, _) => (32, 64), + }; + + let sk = SecretKey::generate(); + let mut state = Blake2b::new(&sk, size_one).unwrap(); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + + if size_one != size_two { + Blake2b::verify(&tag, &sk, size_two, &data[..]).is_err() + } else { + Blake2b::verify(&tag, &sk, size_two, &data[..]).is_ok() + } + } + } + + mod test_streaming_interface { + use crate::hazardous::hash::blake2::blake2b_core::compare_blake2b_states; + use crate::hazardous::mac::blake2b::{Blake2b, SecretKey}; + + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Testing different usage combinations of new(), update(), + /// finalize() and reset() produce the same Digest/Tag. + fn produces_same_hash(sk: &SecretKey, size: usize, data: &[u8]) { + // new(), update(), finalize() + let mut state_1 = Blake2b::new(sk, size).unwrap(); + state_1.update(data).unwrap(); + let res_1 = state_1.finalize().unwrap(); + + // new(), reset(), update(), finalize() + let mut state_2 = Blake2b::new(sk, size).unwrap(); + state_2.reset(sk).unwrap(); + state_2.update(data).unwrap(); + let res_2 = state_2.finalize().unwrap(); + + // new(), update(), reset(), update(), finalize() + let mut state_3 = Blake2b::new(sk, size).unwrap(); + state_3.update(data).unwrap(); + state_3.reset(sk).unwrap(); + state_3.update(data).unwrap(); + let res_3 = state_3.finalize().unwrap(); + + // new(), update(), finalize(), reset(), update(), finalize() + let mut state_4 = Blake2b::new(sk, size).unwrap(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(sk).unwrap(); + state_4.update(data).unwrap(); + let res_4 = state_4.finalize().unwrap(); + + assert_eq!(res_1, res_2); + assert_eq!(res_2, res_3); + assert_eq!(res_3, res_4); + + // Tests for the assumption that returning Ok() on empty update() calls + // with streaming APIs, gives the correct result. This is done by testing + // the reasoning that if update() is empty, returns Ok(), it is the same as + // calling new() -> finalize(). i.e not calling update() at all. + if data.is_empty() { + // new(), finalize() + let mut state_5 = Blake2b::new(sk, size).unwrap(); + let res_5 = state_5.finalize().unwrap(); + + // new(), reset(), finalize() + let mut state_6 = Blake2b::new(sk, size).unwrap(); + state_6.reset(sk).unwrap(); + let res_6 = state_6.finalize().unwrap(); + + // new(), update(), reset(), finalize() + let mut state_7 = Blake2b::new(sk, size).unwrap(); + state_7.update(b"Wrong data").unwrap(); + state_7.reset(sk).unwrap(); + let res_7 = state_7.finalize().unwrap(); + + assert_eq!(res_4, res_5); + assert_eq!(res_5, res_6); + assert_eq!(res_6, res_7); + } + } + + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Testing different usage combinations of new(), update(), + /// finalize() and reset() produce the same Digest/Tag. + fn produces_same_state(sk: &SecretKey, size: usize, data: &[u8]) { + // new() + let state_1 = Blake2b::new(sk, size).unwrap(); + + // new(), reset() + let mut state_2 = Blake2b::new(sk, size).unwrap(); + state_2.reset(sk).unwrap(); + + // new(), update(), reset() + let mut state_3 = Blake2b::new(sk, size).unwrap(); + state_3.update(data).unwrap(); + state_3.reset(sk).unwrap(); + + // new(), update(), finalize(), reset() + let mut state_4 = Blake2b::new(sk, size).unwrap(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(sk).unwrap(); + + compare_blake2b_states(&state_1._state, &state_2._state); + compare_blake2b_states(&state_2._state, &state_3._state); + compare_blake2b_states(&state_3._state, &state_4._state); + } + + #[test] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + fn test_produce_same_state() { + let sk = SecretKey::from_slice(b"Testing").unwrap(); + produces_same_state(&sk, 1, b"Tests"); + produces_same_state(&sk, 32, b"Tests"); + produces_same_state(&sk, 64, b"Tests"); + produces_same_state(&sk, 28, b"Tests"); + } + + #[test] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + fn test_produce_same_hash() { + let sk = SecretKey::from_slice(b"Testing").unwrap(); + produces_same_hash(&sk, 1, b"Tests"); + produces_same_hash(&sk, 32, b"Tests"); + produces_same_hash(&sk, 64, b"Tests"); + produces_same_hash(&sk, 28, b"Tests"); + + produces_same_hash(&sk, 1, b""); + produces_same_hash(&sk, 32, b""); + produces_same_hash(&sk, 64, b""); + produces_same_hash(&sk, 28, b""); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_hash_different_usage(data: Vec<u8>, size: usize) -> bool { + use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE; + + if (1..=BLAKE2B_OUTSIZE).contains(&size) { + // Will panic on incorrect results. + let sk = SecretKey::generate(); + produces_same_hash(&sk, size, &data[..]); + } + + true + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_state_different_usage(data: Vec<u8>, size: usize) -> bool { + use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE; + + if (1..=BLAKE2B_OUTSIZE).contains(&size) { + // Will panic on incorrect results. + let sk = SecretKey::generate(); + produces_same_state(&sk, size, &data[..]); + } + + true + } + } +} diff --git a/vendor/orion/src/hazardous/mac/hmac.rs b/vendor/orion/src/hazardous/mac/hmac.rs new file mode 100644 index 000000000..dea7611bb --- /dev/null +++ b/vendor/orion/src/hazardous/mac/hmac.rs @@ -0,0 +1,919 @@ +// MIT License + +// Copyright (c) 2018-2022 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `secret_key`: The authentication key. +//! - `data`: Data to be authenticated. +//! - `expected`: The expected authentication tag. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! - The HMAC does not match the expected when verifying. +//! +//! # Security: +//! - The secret key should always be generated using a CSPRNG. +//! [`SecretKey::generate()`] can be used for this. +//! - The minimum recommended size for a secret key is 64 bytes. +//! +//! # Recommendation: +//! - If you are unsure of whether to use HMAC or Poly1305, it is most often +//! easier to just use HMAC. See also [Cryptographic Right Answers]. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::mac::hmac::sha512::{HmacSha512, SecretKey}; +//! +//! let key = SecretKey::generate(); +//! +//! let mut state = HmacSha512::new(&key); +//! state.update(b"Some message.")?; +//! let tag = state.finalize()?; +//! +//! assert!(HmacSha512::verify(&tag, &key, b"Some message.").is_ok()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: hmac::sha512::HmacSha512::update +//! [`reset()`]: hmac::sha512::HmacSha512::reset +//! [`finalize()`]: hmac::sha512::HmacSha512::finalize +//! [`SecretKey::generate()`]: hmac::sha512::SecretKey::generate +//! [Cryptographic Right Answers]: https://latacora.micro.blog/2018/04/03/cryptographic-right-answers.html + +use crate::errors::UnknownCryptoError; +use zeroize::Zeroize; + +/// A trait used to define a cryptographic hash function used by HMAC. +pub(crate) trait HmacHashFunction: Clone { + /// The blocksize of the hash function. + const _BLOCKSIZE: usize; + + /// The output size of the hash function. + const _OUTSIZE: usize; + + /// Create a new instance of the hash function. + fn _new() -> Self; + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError>; + + /// Finalize the hash and put the final digest into `dest`. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError>; + + /// Compute a digest of `data` and copy it into `dest`. + fn _digest(data: &[u8], dest: &mut [u8]) -> Result<(), UnknownCryptoError>; + + #[cfg(test)] + /// Compare two Sha2 state objects to check if their fields + /// are the same. + fn compare_state_to_other(&self, other: &Self); +} + +/// A trait used to define a HMAC function. +pub(crate) trait HmacFunction { + // NOTE: Clippy complains this is not used, however it is used in both HKDF and PBKDF2. Perhaps a bug + // with min_const_generics? + #[allow(dead_code)] + /// The output size of the internal hash function used. + const HASH_FUNC_OUTSIZE: usize; + + /// Create a new instance of the HMAC function, using a `secret_key` that may or may not be padded. + fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> + where + Self: Sized; + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError>; + + /// Finalize the MAC and put the final tag into `dest`. + /// + /// NOTE: `dest` may be less than the complete output size of the hash function + /// (Self::HASH_FUNC_OUTSIZE). If that is the case, `dest.len()` bytes will be copied, + /// but `dest` should NEVER be empty. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError>; + + /// Reset the state. + fn _reset(&mut self); +} + +const IPAD: u8 = 0x36; +const OPAD: u8 = 0x5C; + +#[derive(Clone)] +pub(crate) struct Hmac<S: HmacHashFunction, const BLOCKSIZE: usize> { + working_hasher: S, + opad_hasher: S, + ipad_hasher: S, + is_finalized: bool, +} + +impl<S: HmacHashFunction, const BLOCKSIZE: usize> core::fmt::Debug for Hmac<S, BLOCKSIZE> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Hmac {{ working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: {:?} }}", + self.is_finalized + ) + } +} + +impl<S: HmacHashFunction, const BLOCKSIZE: usize> Hmac<S, BLOCKSIZE> { + // NOTE: Clippy complains this is not used, however it is used in both HKDF and PBKDF2. Perhaps a bug + // with min_const_generics? + #[allow(dead_code)] + const HASH_FUNC_OUTSIZE: usize = S::_OUTSIZE; + + /// Construct a state from a `secret_key`. The `secret_key` may be pre-padded or not. + /// + /// Ref: https://brycx.github.io/2018/08/06/hmac-and-precomputation-optimization.html + fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> { + debug_assert_eq!(S::_BLOCKSIZE, BLOCKSIZE); + let mut ipad = [IPAD; BLOCKSIZE]; + + if secret_key.len() > BLOCKSIZE { + // SK is NOT pre-padded. + debug_assert!(BLOCKSIZE > S::_OUTSIZE); + S::_digest(secret_key, &mut ipad[..S::_OUTSIZE])?; + for elem in ipad.iter_mut().take(S::_OUTSIZE) { + *elem ^= IPAD; + } + } else { + // SK has been pre-padded or SK.len() <= BLOCKSIZE. + // Because 0x00 xor IPAD = IPAD, the existence of padding bytes (0x00) + // within SK, during this operation, is inconsequential. + xor_slices!(secret_key, &mut ipad); + } + + let mut ih = S::_new(); + ih._update(&ipad)?; + + // Transform ipad into OPAD xor SK + for elem in ipad.iter_mut() { + *elem ^= IPAD ^ OPAD; + } + + let mut oh = S::_new(); + oh._update(&ipad)?; + + ipad.iter_mut().zeroize(); + + Ok(Self { + working_hasher: ih.clone(), + opad_hasher: oh, + ipad_hasher: ih, + is_finalized: false, + }) + } + + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + if self.is_finalized { + Err(UnknownCryptoError) + } else { + self.working_hasher._update(data) + } + } + + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + debug_assert!(!dest.is_empty()); + if self.is_finalized { + return Err(UnknownCryptoError); + } + + self.is_finalized = true; + let mut outer_hasher = self.opad_hasher.clone(); + self.working_hasher._finalize(dest)?; + outer_hasher._update(dest)?; + outer_hasher._finalize(dest) + } + + fn _reset(&mut self) { + self.working_hasher = self.ipad_hasher.clone(); + self.is_finalized = false; + } + + #[cfg(test)] + /// Compare two Hmac state objects to check if their fields + /// are the same. + pub(crate) fn compare_state_to_other(&self, other: &Self) { + self.working_hasher + .compare_state_to_other(&other.working_hasher); + self.opad_hasher.compare_state_to_other(&other.opad_hasher); + self.ipad_hasher.compare_state_to_other(&other.ipad_hasher); + assert_eq!(self.is_finalized, other.is_finalized); + } +} + +/// HMAC-SHA256 (Hash-based Message Authentication Code) as specified in the [RFC 2104](https://tools.ietf.org/html/rfc2104). +pub mod sha256 { + use super::*; + use crate::hazardous::hash::sha2::sha256::{self, Sha256}; + + construct_hmac_key! { + /// A type to represent the `SecretKey` that HMAC uses for authentication. + /// + /// # Note: + /// `SecretKey` pads the secret key for use with HMAC to a length of 64, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the secret key with padding. + /// + /// `len()` will return the length with padding (always 64). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, Sha256, sha256::SHA256_OUTSIZE, test_hmac_key, sha256::SHA256_BLOCKSIZE) + } + + construct_tag! { + /// A type to represent the `Tag` that HMAC returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + (Tag, test_tag, sha256::SHA256_OUTSIZE, sha256::SHA256_OUTSIZE) + } + + impl_from_trait!(Tag, sha256::SHA256_OUTSIZE); + + use super::Hmac; + + #[derive(Clone, Debug)] + /// HMAC-SHA256 streaming state. + pub struct HmacSha256 { + _state: Hmac<Sha256, { sha256::SHA256_BLOCKSIZE }>, + } + + impl HmacSha256 { + fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> { + Ok(Self { + _state: Hmac::<Sha256, { sha256::SHA256_BLOCKSIZE }>::_new(secret_key)?, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize `HmacSha256` struct with a given key. + pub fn new(secret_key: &SecretKey) -> Self { + // NOTE: `secret_key` has been pre-padded so .unwrap() is OK. + Self::_new(secret_key.unprotected_as_bytes()).unwrap() + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self._state._reset() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Return a HMAC-SHA256 tag. + pub(crate) fn _finalize_internal( + &mut self, + dest: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a HMAC-SHA256 tag. + pub fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> { + let mut dest = [0u8; sha256::SHA256_OUTSIZE]; + self._finalize_internal(&mut dest)?; + + Ok(Tag::from(dest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// One-shot function for generating an HMAC-SHA256 tag of `data`. + pub fn hmac(secret_key: &SecretKey, data: &[u8]) -> Result<Tag, UnknownCryptoError> { + let mut ctx = Self::new(secret_key); + ctx.update(data)?; + ctx.finalize() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify a HMAC-SHA256 tag in constant time. + pub fn verify( + expected: &Tag, + secret_key: &SecretKey, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + if &Self::hmac(secret_key, data)? == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + } + + impl HmacFunction for HmacSha256 { + /// The output size of the internal hash function used. + const HASH_FUNC_OUTSIZE: usize = sha256::SHA256_OUTSIZE; + + /// Create a new instance of the HMAC function, using a `secret_key` that may or may not be padded. + fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> { + Self::_new(secret_key) + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Finalize the MAC and put the final tag into `dest`. + /// + /// NOTE: `dest` may be less than the complete output size of the hash function + /// (Self::HASH_FUNC_OUTSIZE). If that is the case, `dest.len()` bytes will be copied, + /// but `dest` should NEVER be empty. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + /// Reset the state. + fn _reset(&mut self) { + self._state._reset() + } + } + + #[cfg(test)] + mod public { + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let secret_key = SecretKey::generate(); + let initial_state = HmacSha256::new(&secret_key); + let debug = format!("{:?}", initial_state); + let expected = "HmacSha256 { _state: Hmac { working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "safe_api")] + mod test_verify { + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different key, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_key_false(data: Vec<u8>) -> bool { + let sk = SecretKey::generate(); + let mut state = HmacSha256::new(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + let bad_sk = SecretKey::generate(); + + HmacSha256::verify(&tag, &bad_sk, &data[..]).is_err() + } + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + const KEY: [u8; 32] = [0u8; 32]; + + impl TestableStreamingContext<Tag> for HmacSha256 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Tag, UnknownCryptoError> { + HmacSha256::hmac(&SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> { + // This will only run verification tests on differing input. They do not + // include tests for different secret keys. + HmacSha256::verify(expected, &SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn compare_states(state_1: &HmacSha256, state_2: &HmacSha256) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state = HmacSha256::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha256>::new( + initial_state, + sha256::SHA256_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec<u8>) -> bool { + let initial_state = HmacSha256::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha256>::new( + initial_state, + sha256::SHA256_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + } +} + +/// HMAC-SHA384 (Hash-based Message Authentication Code) as specified in the [RFC 2104](https://tools.ietf.org/html/rfc2104). +pub mod sha384 { + use super::*; + use crate::hazardous::hash::sha2::sha384::{self, Sha384}; + + construct_hmac_key! { + /// A type to represent the `SecretKey` that HMAC uses for authentication. + /// + /// # Note: + /// `SecretKey` pads the secret key for use with HMAC to a length of 128, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the secret key with padding. + /// + /// `len()` will return the length with padding (always 128). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, Sha384, sha384::SHA384_OUTSIZE, test_hmac_key, sha384::SHA384_BLOCKSIZE) + } + + construct_tag! { + /// A type to represent the `Tag` that HMAC returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 48 bytes. + (Tag, test_tag, sha384::SHA384_OUTSIZE, sha384::SHA384_OUTSIZE) + } + + impl_from_trait!(Tag, sha384::SHA384_OUTSIZE); + + use super::Hmac; + + #[derive(Clone, Debug)] + /// HMAC-SHA384 streaming state. + pub struct HmacSha384 { + _state: Hmac<Sha384, { sha384::SHA384_BLOCKSIZE }>, + } + + impl HmacSha384 { + fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> { + Ok(Self { + _state: Hmac::<Sha384, { sha384::SHA384_BLOCKSIZE }>::_new(secret_key)?, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize `HmacSha384` struct with a given key. + pub fn new(secret_key: &SecretKey) -> Self { + // NOTE: `secret_key` has been pre-padded so .unwrap() is OK. + Self::_new(secret_key.unprotected_as_bytes()).unwrap() + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self._state._reset() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Return a HMAC-SHA384 tag. + pub(crate) fn _finalize_internal( + &mut self, + dest: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a HMAC-SHA384 tag. + pub fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> { + let mut dest = [0u8; sha384::SHA384_OUTSIZE]; + self._finalize_internal(&mut dest)?; + + Ok(Tag::from(dest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// One-shot function for generating an HMAC-SHA384 tag of `data`. + pub fn hmac(secret_key: &SecretKey, data: &[u8]) -> Result<Tag, UnknownCryptoError> { + let mut ctx = Self::new(secret_key); + ctx.update(data)?; + ctx.finalize() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify a HMAC-SHA384 tag in constant time. + pub fn verify( + expected: &Tag, + secret_key: &SecretKey, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + if &Self::hmac(secret_key, data)? == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + } + + impl HmacFunction for HmacSha384 { + /// The output size of the internal hash function used. + const HASH_FUNC_OUTSIZE: usize = sha384::SHA384_OUTSIZE; + + /// Create a new instance of the HMAC function, using a `secret_key` that may or may not be padded. + fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> { + Self::_new(secret_key) + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Finalize the MAC and put the final tag into `dest`. + /// + /// NOTE: `dest` may be less than the complete output size of the hash function + /// (Self::HASH_FUNC_OUTSIZE). If that is the case, `dest.len()` bytes will be copied, + /// but `dest` should NEVER be empty. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + /// Reset the state. + fn _reset(&mut self) { + self._state._reset() + } + } + + #[cfg(test)] + mod public { + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let secret_key = SecretKey::generate(); + let initial_state = HmacSha384::new(&secret_key); + let debug = format!("{:?}", initial_state); + let expected = "HmacSha384 { _state: Hmac { working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "safe_api")] + mod test_verify { + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different key, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_key_false(data: Vec<u8>) -> bool { + let sk = SecretKey::generate(); + let mut state = HmacSha384::new(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + let bad_sk = SecretKey::generate(); + + HmacSha384::verify(&tag, &bad_sk, &data[..]).is_err() + } + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + const KEY: [u8; 32] = [0u8; 32]; + + impl TestableStreamingContext<Tag> for HmacSha384 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Tag, UnknownCryptoError> { + HmacSha384::hmac(&SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> { + // This will only run verification tests on differing input. They do not + // include tests for different secret keys. + HmacSha384::verify(expected, &SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn compare_states(state_1: &HmacSha384, state_2: &HmacSha384) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state = HmacSha384::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha384>::new( + initial_state, + sha384::SHA384_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec<u8>) -> bool { + let initial_state = HmacSha384::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha384>::new( + initial_state, + sha384::SHA384_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + } +} + +/// HMAC-SHA512 (Hash-based Message Authentication Code) as specified in the [RFC 2104](https://tools.ietf.org/html/rfc2104). +pub mod sha512 { + use super::*; + use crate::hazardous::hash::sha2::sha512::{self, Sha512}; + + construct_hmac_key! { + /// A type to represent the `SecretKey` that HMAC uses for authentication. + /// + /// # Note: + /// `SecretKey` pads the secret key for use with HMAC to a length of 128, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the secret key with padding. + /// + /// `len()` will return the length with padding (always 128). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, Sha512, sha512::SHA512_OUTSIZE, test_hmac_key, sha512::SHA512_BLOCKSIZE) + } + + construct_tag! { + /// A type to represent the `Tag` that HMAC returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 64 bytes. + (Tag, test_tag, sha512::SHA512_OUTSIZE, sha512::SHA512_OUTSIZE) + } + + impl_from_trait!(Tag, sha512::SHA512_OUTSIZE); + + use super::Hmac; + + #[derive(Clone, Debug)] + /// HMAC-SHA512 streaming state. + pub struct HmacSha512 { + _state: Hmac<Sha512, { sha512::SHA512_BLOCKSIZE }>, + } + + impl HmacSha512 { + fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> { + Ok(Self { + _state: Hmac::<Sha512, { sha512::SHA512_BLOCKSIZE }>::_new(secret_key)?, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize `HmacSha512` struct with a given key. + pub fn new(secret_key: &SecretKey) -> Self { + // NOTE: `secret_key` has been pre-padded so .unwrap() is OK. + Self::_new(secret_key.unprotected_as_bytes()).unwrap() + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self._state._reset() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Return a HMAC-SHA512 tag. + pub(crate) fn _finalize_internal( + &mut self, + dest: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a HMAC-SHA512 tag. + pub fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> { + let mut dest = [0u8; sha512::SHA512_OUTSIZE]; + self._finalize_internal(&mut dest)?; + + Ok(Tag::from(dest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// One-shot function for generating an HMAC-SHA512 tag of `data`. + pub fn hmac(secret_key: &SecretKey, data: &[u8]) -> Result<Tag, UnknownCryptoError> { + let mut ctx = Self::new(secret_key); + ctx.update(data)?; + ctx.finalize() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify a HMAC-SHA512 tag in constant time. + pub fn verify( + expected: &Tag, + secret_key: &SecretKey, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + if &Self::hmac(secret_key, data)? == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + } + + impl HmacFunction for HmacSha512 { + /// The output size of the internal hash function used. + const HASH_FUNC_OUTSIZE: usize = sha512::SHA512_OUTSIZE; + + /// Create a new instance of the HMAC function, using a `secret_key` that may or may not be padded. + fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> { + Self::_new(secret_key) + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Finalize the MAC and put the final tag into `dest`. + /// + /// NOTE: `dest` may be less than the complete output size of the hash function + /// (Self::HASH_FUNC_OUTSIZE). If that is the case, `dest.len()` bytes will be copied, + /// but `dest` should NEVER be empty. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + /// Reset the state. + fn _reset(&mut self) { + self._state._reset() + } + } + + #[cfg(test)] + mod public { + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let secret_key = SecretKey::generate(); + let initial_state = HmacSha512::new(&secret_key); + let debug = format!("{:?}", initial_state); + let expected = "HmacSha512 { _state: Hmac { working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "safe_api")] + mod test_verify { + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different key, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_key_false(data: Vec<u8>) -> bool { + let sk = SecretKey::generate(); + let mut state = HmacSha512::new(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + let bad_sk = SecretKey::generate(); + + HmacSha512::verify(&tag, &bad_sk, &data[..]).is_err() + } + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + const KEY: [u8; 32] = [0u8; 32]; + + impl TestableStreamingContext<Tag> for HmacSha512 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Tag, UnknownCryptoError> { + HmacSha512::hmac(&SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> { + // This will only run verification tests on differing input. They do not + // include tests for different secret keys. + HmacSha512::verify(expected, &SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn compare_states(state_1: &HmacSha512, state_2: &HmacSha512) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state = HmacSha512::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha512>::new( + initial_state, + sha512::SHA512_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec<u8>) -> bool { + let initial_state = HmacSha512::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha512>::new( + initial_state, + sha512::SHA512_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + } +} diff --git a/vendor/orion/src/hazardous/mac/mod.rs b/vendor/orion/src/hazardous/mac/mod.rs new file mode 100644 index 000000000..f8d02c514 --- /dev/null +++ b/vendor/orion/src/hazardous/mac/mod.rs @@ -0,0 +1,30 @@ +// MIT License + +// Copyright (c) 2018-2022 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// HMAC (Hash-based Message Authentication Code) as specified in the [RFC 2104](https://tools.ietf.org/html/rfc2104). +pub mod hmac; + +/// Poly1305 as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub mod poly1305; + +/// BLAKE2b as specified in the [RFC 7693](https://tools.ietf.org/html/rfc7693). +pub mod blake2b; diff --git a/vendor/orion/src/hazardous/mac/poly1305.rs b/vendor/orion/src/hazardous/mac/poly1305.rs new file mode 100644 index 000000000..2b39421e3 --- /dev/null +++ b/vendor/orion/src/hazardous/mac/poly1305.rs @@ -0,0 +1,571 @@ +// MIT License + +// Copyright (c) 2018-2022 The orion Developers +// Based on the algorithm from https://github.com/floodyberry/poly1305-donna + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # About: +//! This implementation is based on [poly1305-donna] by Andrew Moon. +//! +//! # Parameters: +//! - `data`: Data to be authenticated. +//! - `one_time_key`: One-time key used to authenticate. +//! - `expected`: The expected tag that needs to be verified. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! - The calculated tag does not match the expected when verifying. +//! +//! # Security: +//! - A given key must never be used more than once. A unique [`OneTimeKey`], +//! for each message authenticated, is required. If a key is used more than once, +//! it reveals enough information for an attacker to forge future authentications with the same key. +//! - The one-time key should be generated using a CSPRNG. +//! [`OneTimeKey::generate()`] can be used for this. +//! +//! # Recommendation: +//! - If you are unsure of whether to use HMAC or Poly1305, it is most often +//! easier to just use HMAC. See also [Cryptographic Right Answers]. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::mac::poly1305::{OneTimeKey, Poly1305}; +//! +//! let one_time_key = OneTimeKey::generate(); +//! let msg = "Some message."; +//! +//! let mut poly1305_state = Poly1305::new(&one_time_key); +//! poly1305_state.update(msg.as_bytes())?; +//! let tag = poly1305_state.finalize()?; +//! +//! assert!(Poly1305::verify(&tag, &one_time_key, msg.as_bytes()).is_ok()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: poly1305::Poly1305::update +//! [`reset()`]: poly1305::Poly1305::reset +//! [`finalize()`]: poly1305::Poly1305::finalize +//! [`OneTimeKey::generate()`]: poly1305::OneTimeKey::generate +//! [`OneTimeKey`]: poly1305::OneTimeKey +//! [poly1305-donna]: https://github.com/floodyberry/poly1305-donna +//! [Cryptographic Right Answers]: https://latacora.micro.blog/2018/04/03/cryptographic-right-answers.html + +use crate::{ + errors::UnknownCryptoError, + util::endianness::{load_u32_le, store_u32_into_le}, +}; +use fiat_crypto::poly1305_32::{ + fiat_poly1305_add, fiat_poly1305_carry, fiat_poly1305_carry_mul, fiat_poly1305_from_bytes, + fiat_poly1305_loose_field_element, fiat_poly1305_selectznz, fiat_poly1305_subborrowx_u26, + fiat_poly1305_tight_field_element, fiat_poly1305_u1, +}; + +/// The blocksize which Poly1305 operates on. +const POLY1305_BLOCKSIZE: usize = 16; +/// The output size for Poly1305. +pub const POLY1305_OUTSIZE: usize = 16; +/// The key size for Poly1305. +pub const POLY1305_KEYSIZE: usize = 32; +/// Type for a Poly1305 tag. +type Poly1305Tag = [u8; POLY1305_OUTSIZE]; + +construct_secret_key! { + /// A type to represent the `OneTimeKey` that Poly1305 uses for authentication. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (OneTimeKey, test_one_time_key, POLY1305_KEYSIZE, POLY1305_KEYSIZE, POLY1305_KEYSIZE) +} + +impl_from_trait!(OneTimeKey, POLY1305_KEYSIZE); + +construct_tag! { + /// A type to represent the `Tag` that Poly1305 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 16 bytes. + (Tag, test_tag, POLY1305_OUTSIZE, POLY1305_OUTSIZE) +} + +impl_from_trait!(Tag, POLY1305_OUTSIZE); + +#[derive(Clone)] +/// Poly1305 streaming state. +pub struct Poly1305 { + a: [u32; 5], + r: [u32; 5], + s: [u32; 4], + leftover: usize, + buffer: [u8; POLY1305_BLOCKSIZE], + is_finalized: bool, +} + +impl Drop for Poly1305 { + fn drop(&mut self) { + use zeroize::Zeroize; + self.a.zeroize(); + self.r.zeroize(); + self.s.zeroize(); + self.buffer.zeroize(); + } +} + +impl core::fmt::Debug for Poly1305 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Poly1305 {{ a: [***OMITTED***], r: [***OMITTED***], s: [***OMITTED***], leftover: [***OMITTED***], buffer: [***OMITTED***], is_finalized: {:?} }}", + self.is_finalized + ) + } +} + +impl Poly1305 { + /// Prime 2^130-5 in little-endian. + const PRIME: [u8; 17] = [ + 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3, + ]; + + /// Process a datablock of `POLY1305_BLOCKSIZE` length. + fn process_block(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + if data.len() != POLY1305_BLOCKSIZE { + return Err(UnknownCryptoError); + } + + let mut mb = [0u8; 17]; + mb[..16].copy_from_slice(data); + // One byte is appended to detect trailing zeroes if not last chunk. + // See https://cr.yp.to/mac/poly1305-20050329.pdf, Section 2 "Conversion and padding". + mb[16] = if self.is_finalized { 0 } else { 1 }; + let mut m: fiat_poly1305_tight_field_element = [0u32; 5]; + fiat_poly1305_from_bytes(&mut m, &mb); + + // h += m + let mut h: fiat_poly1305_loose_field_element = [0u32; 5]; + fiat_poly1305_add(&mut h, &self.a, &m); + // h *= r with partial reduction modulo p + fiat_poly1305_carry_mul(&mut self.a, &h, &self.r); + + Ok(()) + } + + #[rustfmt::skip] + #[allow(clippy::identity_op)] + /// Remaining processing after all data blocks have been processed. + fn process_end_of_stream(&mut self) { + // full carry h + let mut buf_h: fiat_poly1305_tight_field_element = [0u32; 5]; + fiat_poly1305_carry(&mut buf_h, &self.a); + + // compute h + -p + let mut p: fiat_poly1305_tight_field_element = [0u32; 5]; + fiat_poly1305_from_bytes(&mut p, &Self::PRIME); + + let mut carry: fiat_poly1305_u1 = 0; + let mut g0: u32 = 0; let c = carry; fiat_poly1305_subborrowx_u26(&mut g0, &mut carry, c, buf_h[0], p[0]); + let mut g1: u32 = 0; let c = carry; fiat_poly1305_subborrowx_u26(&mut g1, &mut carry, c, buf_h[1], p[1]); + let mut g2: u32 = 0; let c = carry; fiat_poly1305_subborrowx_u26(&mut g2, &mut carry, c, buf_h[2], p[2]); + let mut g3: u32 = 0; let c = carry; fiat_poly1305_subborrowx_u26(&mut g3, &mut carry, c, buf_h[3], p[3]); + let mut g4: u32 = 0; let c = carry; fiat_poly1305_subborrowx_u26(&mut g4, &mut carry, c, buf_h[4], p[4]); + + // select h if h < p, or h + -p if h >= p + let mut ret = [0u32; 5]; + fiat_poly1305_selectznz(&mut ret, carry,&[g0, g1, g2, g3, g4], &buf_h); + + let mut h0 = ret[0]; + let mut h1 = ret[1]; + let mut h2 = ret[2]; + let mut h3 = ret[3]; + let h4 = ret[4]; + + // h = h % (2^128) + h0 = ((h0) | (h1 << 26)) & 0xffffffff; + h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; + h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; + h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; + + // mac = (h + pad) % (2^128) + let mut f: u64 = (h0 as u64) + (self.s[0] as u64); h0 = f as u32; + f = (h1 as u64) + (self.s[1] as u64) + (f >> 32); h1 = f as u32; + f = (h2 as u64) + (self.s[2] as u64) + (f >> 32); h2 = f as u32; + f = (h3 as u64) + (self.s[3] as u64) + (f >> 32); h3 = f as u32; + + // Set self.a to MAC result + self.a[0] = h0; + self.a[1] = h1; + self.a[2] = h2; + self.a[3] = h3; + } + + #[allow(clippy::unreadable_literal)] + /// Initialize a `Poly1305` struct with a given one-time key. + pub fn new(one_time_key: &OneTimeKey) -> Self { + let mut state = Self { + a: [0u32; 5], + r: [0u32; 5], + s: [0u32; 4], + leftover: 0, + buffer: [0u8; POLY1305_BLOCKSIZE], + is_finalized: false, + }; + + state.r[0] = (load_u32_le(&one_time_key.unprotected_as_bytes()[0..4])) & 0x3ffffff; + state.r[1] = (load_u32_le(&one_time_key.unprotected_as_bytes()[3..7]) >> 2) & 0x3ffff03; + state.r[2] = (load_u32_le(&one_time_key.unprotected_as_bytes()[6..10]) >> 4) & 0x3ffc0ff; + state.r[3] = (load_u32_le(&one_time_key.unprotected_as_bytes()[9..13]) >> 6) & 0x3f03fff; + state.r[4] = (load_u32_le(&one_time_key.unprotected_as_bytes()[12..16]) >> 8) & 0x00fffff; + + state.s[0] = load_u32_le(&one_time_key.unprotected_as_bytes()[16..20]); + state.s[1] = load_u32_le(&one_time_key.unprotected_as_bytes()[20..24]); + state.s[2] = load_u32_le(&one_time_key.unprotected_as_bytes()[24..28]); + state.s[3] = load_u32_le(&one_time_key.unprotected_as_bytes()[28..32]); + + state + } + + /// Update state with a `data` and pad it to blocksize with 0, if not + /// evenly divisible by blocksize. + pub(crate) fn process_pad_to_blocksize( + &mut self, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + if self.is_finalized { + return Err(UnknownCryptoError); + } + if data.is_empty() { + return Ok(()); + } + + let mut blocksize_iter = data.chunks_exact(POLY1305_BLOCKSIZE); + for block in &mut blocksize_iter { + self.process_block(block).unwrap(); + } + + let remaining = blocksize_iter.remainder(); + if !remaining.is_empty() { + let mut pad = [0u8; POLY1305_BLOCKSIZE]; + pad[..remaining.len()].copy_from_slice(remaining); + self.process_block(&pad).unwrap(); + } + + Ok(()) + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self.a = [0u32; 5]; + self.leftover = 0; + self.is_finalized = false; + self.buffer = [0u8; POLY1305_BLOCKSIZE]; + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + if self.is_finalized { + return Err(UnknownCryptoError); + } + if data.is_empty() { + return Ok(()); + } + + let mut bytes = data; + + if self.leftover != 0 { + debug_assert!(self.leftover <= POLY1305_BLOCKSIZE); + + let mut want = POLY1305_BLOCKSIZE - self.leftover; + if want > bytes.len() { + want = bytes.len(); + } + + for (idx, itm) in bytes.iter().enumerate().take(want) { + self.buffer[self.leftover + idx] = *itm; + } + + bytes = &bytes[want..]; + self.leftover += want; + if self.leftover < POLY1305_BLOCKSIZE { + return Ok(()); + } + + let tmp = self.buffer; + self.process_block(&tmp)?; + self.leftover = 0; + } + + while bytes.len() >= POLY1305_BLOCKSIZE { + self.process_block(&bytes[0..POLY1305_BLOCKSIZE])?; + bytes = &bytes[POLY1305_BLOCKSIZE..]; + } + + self.buffer[..bytes.len()].copy_from_slice(bytes); + self.leftover = bytes.len(); + + Ok(()) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a Poly1305 tag. + pub fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> { + if self.is_finalized { + return Err(UnknownCryptoError); + } + + self.is_finalized = true; + + let mut local_buffer: Poly1305Tag = self.buffer; + + if self.leftover != 0 { + local_buffer[self.leftover] = 1; + // Pad the last block with zeroes before processing it + for buf_itm in local_buffer + .iter_mut() + .take(POLY1305_BLOCKSIZE) + .skip(self.leftover + 1) + { + *buf_itm = 0u8; + } + + self.process_block(&local_buffer)?; + } + + self.process_end_of_stream(); + store_u32_into_le(&self.a[0..4], &mut local_buffer); + + Ok(Tag::from(local_buffer)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// One-shot function for generating a Poly1305 tag of `data`. + pub fn poly1305(one_time_key: &OneTimeKey, data: &[u8]) -> Result<Tag, UnknownCryptoError> { + let mut poly_1305_state = Self::new(one_time_key); + poly_1305_state.update(data)?; + poly_1305_state.finalize() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify a Poly1305 tag in constant time. + pub fn verify( + expected: &Tag, + one_time_key: &OneTimeKey, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + if &Self::poly1305(one_time_key, data)? == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let secret_key = OneTimeKey::generate(); + let initial_state = Poly1305::new(&secret_key); + let debug = format!("{:?}", initial_state); + let expected = "Poly1305 { a: [***OMITTED***], r: [***OMITTED***], s: [***OMITTED***], leftover: [***OMITTED***], buffer: [***OMITTED***], is_finalized: false }"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "safe_api")] + mod test_verify { + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different key, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_key_false(data: Vec<u8>) -> bool { + let sk = OneTimeKey::generate(); + let mut state = Poly1305::new(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + let bad_sk = OneTimeKey::generate(); + + Poly1305::verify(&tag, &bad_sk, &data[..]).is_err() + } + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::{ + StreamingContextConsistencyTester, TestableStreamingContext, + }; + + // If a Poly1305 one-time key is all 0's then the tag will also be, regardless + // of which message data has been processed. + const KEY: [u8; 32] = [24u8; 32]; + + impl TestableStreamingContext<Tag> for Poly1305 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Tag, UnknownCryptoError> { + Poly1305::poly1305(&OneTimeKey::from_slice(&KEY).unwrap(), input) + } + + fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> { + // This will only run verification tests on differing input. They do not + // include tests for different secret keys. + Poly1305::verify(expected, &OneTimeKey::from_slice(&KEY).unwrap(), input) + } + + fn compare_states(state_1: &Poly1305, state_2: &Poly1305) { + assert_eq!(state_1.a, state_2.a); + assert_eq!(state_1.r, state_2.r); + assert_eq!(state_1.s, state_2.s); + assert_eq!(state_1.leftover, state_2.leftover); + assert_eq!(state_1.buffer[..], state_2.buffer[..]); + assert_eq!(state_1.is_finalized, state_2.is_finalized); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Poly1305 = Poly1305::new(&OneTimeKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::<Tag, Poly1305>::new( + initial_state, + POLY1305_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec<u8>) -> bool { + let initial_state: Poly1305 = Poly1305::new(&OneTimeKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::<Tag, Poly1305>::new( + initial_state, + POLY1305_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_process_pad_to_blocksize { + use super::*; + + #[test] + fn test_process_err_on_finalized() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state = Poly1305::new(&sk); + + state.process_pad_to_blocksize(&[0u8; 16]).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.process_pad_to_blocksize(&[0u8; 16]).is_err()); + } + + #[test] + fn test_process_pad_no_pad() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state_pad = Poly1305::new(&sk); + let mut state_no_pad = Poly1305::new(&sk); + + // 15 missing to be evenly divisible by 16. + state_pad.process_pad_to_blocksize(&[0u8; 17]).unwrap(); + state_no_pad.process_pad_to_blocksize(&[0u8; 32]).unwrap(); + + assert_eq!( + state_no_pad.finalize().unwrap(), + state_pad.finalize().unwrap() + ); + } + } + + mod test_process_block { + use super::*; + + #[test] + fn test_process_block_len() { + let block_0 = [0u8; 0]; + let block_1 = [0u8; 15]; + let block_2 = [0u8; 17]; + let block_3 = [0u8; 16]; + + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state = Poly1305::new(&sk); + + assert!(state.process_block(&block_0).is_err()); + assert!(state.process_block(&block_1).is_err()); + assert!(state.process_block(&block_2).is_err()); + assert!(state.process_block(&block_3).is_ok()); + } + } + + mod test_process_end_of_stream { + use super::*; + + #[test] + fn test_process_no_panic() { + let block = [0u8; 16]; + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state = Poly1305::new(&sk); + // Should not panic + state.process_end_of_stream(); + state.reset(); + state.process_end_of_stream(); + + let mut state = Poly1305::new(&sk); + state.process_block(&block).unwrap(); + // Should not panic + state.process_end_of_stream(); + state.reset(); + state.process_end_of_stream(); + } + } +} |