summaryrefslogtreecommitdiffstats
path: root/vendor/orion/src/hazardous/mac
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/orion/src/hazardous/mac
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-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.rs435
-rw-r--r--vendor/orion/src/hazardous/mac/hmac.rs919
-rw-r--r--vendor/orion/src/hazardous/mac/mod.rs30
-rw-r--r--vendor/orion/src/hazardous/mac/poly1305.rs571
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();
+ }
+ }
+}