diff options
Diffstat (limited to 'vendor/orion/src')
54 files changed, 21923 insertions, 0 deletions
diff --git a/vendor/orion/src/errors.rs b/vendor/orion/src/errors.rs new file mode 100644 index 0000000..33a5662 --- /dev/null +++ b/vendor/orion/src/errors.rs @@ -0,0 +1,140 @@ +// MIT License + +// Copyright (c) 2018-2023 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. + +use core::fmt; + +#[allow(clippy::derive_partial_eq_without_eq)] +/// Opaque error. +#[derive(Clone, Copy, PartialEq)] +pub struct UnknownCryptoError; + +impl fmt::Display for UnknownCryptoError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UnknownCryptoError") + } +} + +impl fmt::Debug for UnknownCryptoError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UnknownCryptoError") + } +} + +#[cfg(feature = "safe_api")] +impl std::error::Error for UnknownCryptoError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +#[cfg(feature = "safe_api")] +impl From<getrandom::Error> for UnknownCryptoError { + fn from(_: getrandom::Error) -> Self { + UnknownCryptoError + } +} + +#[cfg(feature = "safe_api")] +impl From<ct_codecs::Error> for UnknownCryptoError { + fn from(_: ct_codecs::Error) -> Self { + UnknownCryptoError + } +} + +impl From<core::num::ParseIntError> for UnknownCryptoError { + fn from(_: core::num::ParseIntError) -> Self { + UnknownCryptoError + } +} + +#[test] +#[cfg(feature = "safe_api")] +// format! is only available with std +fn test_unknown_crypto_error_debug_display() { + // Tests Debug impl through "{:?}" + let err = format!("{:?}", UnknownCryptoError); + assert_eq!(err, "UnknownCryptoError"); + // Tests Display impl through "{}" + let err = format!("{}", UnknownCryptoError); + assert_eq!(err, "UnknownCryptoError"); +} + +#[test] +#[cfg(feature = "safe_api")] +// format! is only available with std +fn test_unknown_crypto_from_getrandom() { + use core::num::NonZeroU32; + // Choose some random error code. + let err_code = NonZeroU32::new(12).unwrap(); + let err_foreign: getrandom::Error = getrandom::Error::from(err_code); + + // Tests Debug impl through "{:?}" + let err = format!("{:?}", UnknownCryptoError::from(err_foreign)); + assert_eq!(err, "UnknownCryptoError"); + // Tests Display impl through "{}" + let err = format!("{}", UnknownCryptoError::from(err_foreign)); + assert_eq!(err, "UnknownCryptoError"); +} + +#[test] +#[cfg(feature = "safe_api")] +fn test_source() { + use std::error::Error; + assert!(UnknownCryptoError.source().is_none()); +} + +#[test] +#[cfg(feature = "safe_api")] +fn test_unknown_crypto_from_decode_error() { + use ct_codecs::Error; + + let err_one = Error::InvalidInput; + let err_two = Error::Overflow; + + // Tests Debug impl through "{:?}" and Display impl though "{}" + let err = format!( + "{:?}:{}", + UnknownCryptoError::from(err_one), + UnknownCryptoError::from(err_one) + ); + assert_eq!(err, "UnknownCryptoError:UnknownCryptoError"); + let err = format!( + "{:?}:{}", + UnknownCryptoError::from(err_two), + UnknownCryptoError::from(err_two) + ); + assert_eq!(err, "UnknownCryptoError:UnknownCryptoError"); +} + +#[test] +#[cfg(feature = "safe_api")] +fn test_unknown_crypto_from_parseint_error() { + let err_foreign = "j".parse::<u32>().unwrap_err(); + + // Tests Debug impl through "{:?}" and Display impl though "{}" + let err = format!( + "{:?}:{}", + UnknownCryptoError::from(err_foreign.clone()), + UnknownCryptoError::from(err_foreign) + ); + assert_eq!(err, "UnknownCryptoError:UnknownCryptoError"); +} diff --git a/vendor/orion/src/hazardous/aead/chacha20poly1305.rs b/vendor/orion/src/hazardous/aead/chacha20poly1305.rs new file mode 100644 index 0000000..8211253 --- /dev/null +++ b/vendor/orion/src/hazardous/aead/chacha20poly1305.rs @@ -0,0 +1,421 @@ +// MIT License + +// Copyright (c) 2018-2023 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 secret key. +//! - `nonce`: The nonce value. +//! - `ad`: Additional data to authenticate (this is not encrypted and can be [`None`]). +//! - `ciphertext_with_tag`: The encrypted data with the corresponding 16 byte +//! Poly1305 tag appended to it. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the +//! `ciphertext_with_tag`/`plaintext` after encryption/decryption. +//! +//! `ad`: "A typical use for these data is to authenticate version numbers, +//! timestamps or monotonically increasing counters in order to discard previous +//! messages and prevent replay attacks." See [libsodium docs] for more information. +//! +//! `nonce`: "Counters and LFSRs are both acceptable ways of generating unique +//! nonces, as is encrypting a counter using a block cipher with a 64-bit block +//! size such as DES. Note that it is not acceptable to use a truncation of a +//! counter encrypted with block ciphers with 128-bit or 256-bit blocks, +//! because such a truncation may repeat after a short time." See [RFC] for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` + [`POLY1305_OUTSIZE`] when calling [`seal()`]. +//! - The length of `dst_out` is less than `ciphertext_with_tag` - [`POLY1305_OUTSIZE`] when +//! calling [`open()`]. +//! - The length of `ciphertext_with_tag` is not at least [`POLY1305_OUTSIZE`]. +//! - The received tag does not match the calculated tag when calling [`open()`]. +//! - `plaintext.len()` + [`POLY1305_OUTSIZE`] overflows when calling [`seal()`]. +//! - Converting `usize` to `u64` would be a lossy conversion. +//! - `plaintext.len() >` [`P_MAX`] +//! - `ad.len() >` [`A_MAX`] +//! - `ciphertext_with_tag.len() >` [`C_MAX`] +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1 * 64` bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Only a nonce for XChaCha20Poly1305 is big enough to be randomly generated +//! using a CSPRNG. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! - The length of the `plaintext` is not hidden, only its contents. +//! +//! # Recommendation: +//! - It is recommended to use [`XChaCha20Poly1305`] when possible. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::aead; +//! +//! let secret_key = aead::chacha20poly1305::SecretKey::generate(); +//! +//! // WARNING: This nonce is only meant for demonstration and should not +//! // be repeated. Please read the security section. +//! let nonce = aead::chacha20poly1305::Nonce::from([0u8; 12]); +//! let ad = "Additional data".as_bytes(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of the above message is 15 and then we accommodate 16 for the Poly1305 +//! // tag. +//! +//! let mut dst_out_ct = [0u8; 15 + 16]; +//! let mut dst_out_pt = [0u8; 15]; +//! // Encrypt and place ciphertext + tag in dst_out_ct +//! aead::chacha20poly1305::seal(&secret_key, &nonce, message, Some(&ad), &mut dst_out_ct)?; +//! // Verify tag, if correct then decrypt and place message in dst_out_pt +//! aead::chacha20poly1305::open(&secret_key, &nonce, &dst_out_ct, Some(&ad), &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt.as_ref(), message.as_ref()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: super::stream::chacha20::SecretKey::generate +//! [`XChaCha20Poly1305`]: xchacha20poly1305 +//! [`POLY1305_OUTSIZE`]: super::mac::poly1305::POLY1305_OUTSIZE +//! [`seal()`]: chacha20poly1305::seal +//! [`open()`]: chacha20poly1305::open +//! [RFC]: https://tools.ietf.org/html/rfc8439#section-3 +//! [libsodium docs]: https://download.libsodium.org/doc/secret-key_cryptography/aead#additional-data +//! [`P_MAX`]: chacha20poly1305::P_MAX +//! [`A_MAX`]: chacha20poly1305::A_MAX +//! [`C_MAX`]: chacha20poly1305::C_MAX + +pub use crate::hazardous::stream::chacha20::{Nonce, SecretKey}; +use crate::{ + errors::UnknownCryptoError, + hazardous::{ + mac::poly1305::{OneTimeKey, Poly1305, POLY1305_KEYSIZE, POLY1305_OUTSIZE}, + stream::chacha20::{self, ChaCha20, CHACHA_BLOCKSIZE}, + }, + util, +}; +use core::convert::TryInto; +use zeroize::Zeroizing; + +/// The initial counter used for encryption and decryption. +pub(crate) const ENC_CTR: u32 = 1; + +/// The initial counter used for Poly1305 key generation. +pub(crate) const AUTH_CTR: u32 = 0; + +/// The maximum size of the plaintext (see [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439#section-2.8)). +pub const P_MAX: u64 = (u32::MAX as u64) * 64; + +/// The maximum size of the ciphertext (see [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439#section-2.8)). +pub const C_MAX: u64 = P_MAX + (POLY1305_OUTSIZE as u64); + +/// The maximum size of the associated data (see [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439#section-2.8)). +pub const A_MAX: u64 = u64::MAX; + +/// Poly1305 key generation using IETF ChaCha20. +pub(crate) fn poly1305_key_gen( + ctx: &mut ChaCha20, + tmp_buffer: &mut Zeroizing<[u8; CHACHA_BLOCKSIZE]>, +) -> OneTimeKey { + ctx.keystream_block(AUTH_CTR, tmp_buffer.as_mut()); + OneTimeKey::from_slice(&tmp_buffer[..POLY1305_KEYSIZE]).unwrap() +} + +/// Authenticates the ciphertext, ad and their lengths. +pub(crate) fn process_authentication( + auth_ctx: &mut Poly1305, + ad: &[u8], + ciphertext: &[u8], +) -> Result<(), UnknownCryptoError> { + auth_ctx.process_pad_to_blocksize(ad)?; + auth_ctx.process_pad_to_blocksize(ciphertext)?; + + let (ad_len, ct_len): (u64, u64) = match (ad.len().try_into(), ciphertext.len().try_into()) { + (Ok(alen), Ok(clen)) => (alen, clen), + _ => return Err(UnknownCryptoError), + }; + + let mut tmp_pad = [0u8; 16]; + tmp_pad[0..8].copy_from_slice(&ad_len.to_le_bytes()); + tmp_pad[8..16].copy_from_slice(&ct_len.to_le_bytes()); + auth_ctx.update(tmp_pad.as_ref()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// AEAD ChaCha20Poly1305 encryption and authentication as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub fn seal( + secret_key: &SecretKey, + nonce: &Nonce, + plaintext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(plaintext.len()).map_err(|_| UnknownCryptoError)? > P_MAX { + return Err(UnknownCryptoError); + } + + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + + match plaintext.len().checked_add(POLY1305_OUTSIZE) { + Some(out_min_len) => { + if dst_out.len() < out_min_len { + return Err(UnknownCryptoError); + } + } + None => return Err(UnknownCryptoError), + }; + + let mut stream = + ChaCha20::new(secret_key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + let mut auth_ctx = Poly1305::new(&poly1305_key_gen(&mut stream, &mut tmp)); + let ad_len = ad.len(); + let ct_len = plaintext.len(); + + auth_ctx.process_pad_to_blocksize(ad)?; + + if ct_len != 0 { + for (ctr, (p_block, c_block)) in plaintext + .chunks(CHACHA_BLOCKSIZE) + .zip(dst_out.chunks_mut(CHACHA_BLOCKSIZE)) + .enumerate() + { + match ENC_CTR.checked_add(ctr as u32) { + Some(counter) => { + // See https://github.com/orion-rs/orion/issues/308 + stream.next_produceable()?; + // We know at this point that: + // 1) ENC_CTR + ctr does __not__ overflow/panic + // 2) ChaCha20 instance will not panic on the next keystream produced + + // Copy keystream directly into the dst_out block in there is + // enough space, too avoid copying from `tmp` each time and only + // on the last block instead. `c_block` must be full blocksize, + // or we leave behind keystream data in it, if `p_block` is not full length + // while `c_block` is. + if p_block.len() == CHACHA_BLOCKSIZE && c_block.len() == CHACHA_BLOCKSIZE { + stream.keystream_block(counter, c_block); + xor_slices!(p_block, c_block); + auth_ctx.update(c_block)?; + } + + // We only pad this last block to the Poly1305 blocksize since `CHACHA_BLOCKSIZE` + // already is evenly divisible by 16, so the previous full-length blocks do not matter. + if p_block.len() < CHACHA_BLOCKSIZE { + stream.keystream_block(counter, tmp.as_mut()); + xor_slices!(p_block, tmp.as_mut()); + c_block[..p_block.len()].copy_from_slice(&tmp.as_ref()[..p_block.len()]); + auth_ctx.process_pad_to_blocksize(&c_block[..p_block.len()])?; + } + } + None => return Err(UnknownCryptoError), + } + } + } + + let (adlen, ctlen): (u64, u64) = match (ad_len.try_into(), ct_len.try_into()) { + (Ok(alen), Ok(clen)) => (alen, clen), + _ => return Err(UnknownCryptoError), + }; + + let mut tmp_pad = [0u8; 16]; + tmp_pad[0..8].copy_from_slice(&adlen.to_le_bytes()); + tmp_pad[8..16].copy_from_slice(&ctlen.to_le_bytes()); + auth_ctx.update(tmp_pad.as_ref())?; + + dst_out[ct_len..(ct_len + POLY1305_OUTSIZE)] + .copy_from_slice(auth_ctx.finalize()?.unprotected_as_bytes()); + + Ok(()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// AEAD ChaCha20Poly1305 decryption and authentication as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub fn open( + secret_key: &SecretKey, + nonce: &Nonce, + ciphertext_with_tag: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(ciphertext_with_tag.len()).map_err(|_| UnknownCryptoError)? > C_MAX { + return Err(UnknownCryptoError); + } + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + if ciphertext_with_tag.len() < POLY1305_OUTSIZE { + return Err(UnknownCryptoError); + } + if dst_out.len() < ciphertext_with_tag.len() - POLY1305_OUTSIZE { + return Err(UnknownCryptoError); + } + + let mut dec_ctx = + ChaCha20::new(secret_key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + let mut auth_ctx = Poly1305::new(&poly1305_key_gen(&mut dec_ctx, &mut tmp)); + + let ciphertext_len = ciphertext_with_tag.len() - POLY1305_OUTSIZE; + process_authentication(&mut auth_ctx, ad, &ciphertext_with_tag[..ciphertext_len])?; + util::secure_cmp( + auth_ctx.finalize()?.unprotected_as_bytes(), + &ciphertext_with_tag[ciphertext_len..], + )?; + + if ciphertext_len != 0 { + dst_out[..ciphertext_len].copy_from_slice(&ciphertext_with_tag[..ciphertext_len]); + chacha20::xor_keystream( + &mut dec_ctx, + ENC_CTR, + tmp.as_mut(), + &mut dst_out[..ciphertext_len], + )?; + } + + Ok(()) +} + +// Testing public functions in the module. +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use super::*; + use crate::test_framework::aead_interface::{test_diff_params_err, AeadTestRunner}; + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_aead_interface(input: Vec<u8>, ad: Vec<u8>) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::from_slice(&[0u8; chacha20::IETF_CHACHA_NONCESIZE]).unwrap(); + AeadTestRunner( + seal, + open, + secret_key, + nonce, + &input, + None, + POLY1305_OUTSIZE, + &ad, + ); + test_diff_params_err(&seal, &open, &input, POLY1305_OUTSIZE); + true + } +} + +// Testing any test vectors that aren't put into library's /tests folder. +#[cfg(test)] +mod test_vectors { + use super::*; + + #[test] + fn rfc8439_poly1305_key_gen_1() { + let key = SecretKey::from_slice(&[0u8; 32]).unwrap(); + let nonce = Nonce::from_slice(&[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]) + .unwrap(); + let expected = [ + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, + 0xbd, 0x28, 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, 0xa8, 0x36, 0xef, 0xcc, + 0x8b, 0x77, 0x0d, 0xc7, + ]; + + let mut chacha20_ctx = + ChaCha20::new(key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + assert_eq!( + poly1305_key_gen(&mut chacha20_ctx, &mut tmp_block).unprotected_as_bytes(), + expected.as_ref() + ); + } + + #[test] + fn rfc8439_poly1305_key_gen_2() { + let key = SecretKey::from_slice(&[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + ]) + .unwrap(); + let nonce = Nonce::from_slice(&[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]) + .unwrap(); + let expected = [ + 0xec, 0xfa, 0x25, 0x4f, 0x84, 0x5f, 0x64, 0x74, 0x73, 0xd3, 0xcb, 0x14, 0x0d, 0xa9, + 0xe8, 0x76, 0x06, 0xcb, 0x33, 0x06, 0x6c, 0x44, 0x7b, 0x87, 0xbc, 0x26, 0x66, 0xdd, + 0xe3, 0xfb, 0xb7, 0x39, + ]; + + let mut chacha20_ctx = + ChaCha20::new(key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + assert_eq!( + poly1305_key_gen(&mut chacha20_ctx, &mut tmp_block).unprotected_as_bytes(), + expected.as_ref() + ); + } + + #[test] + fn rfc8439_poly1305_key_gen_3() { + let key = SecretKey::from_slice(&[ + 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, + 0xb5, 0xf0, 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, 0x9d, 0xca, 0x5c, 0xbc, + 0x20, 0x70, 0x75, 0xc0, + ]) + .unwrap(); + let nonce = Nonce::from_slice(&[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]) + .unwrap(); + let expected = [ + 0x96, 0x5e, 0x3b, 0xc6, 0xf9, 0xec, 0x7e, 0xd9, 0x56, 0x08, 0x08, 0xf4, 0xd2, 0x29, + 0xf9, 0x4b, 0x13, 0x7f, 0xf2, 0x75, 0xca, 0x9b, 0x3f, 0xcb, 0xdd, 0x59, 0xde, 0xaa, + 0xd2, 0x33, 0x10, 0xae, + ]; + + let mut chacha20_ctx = + ChaCha20::new(key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + assert_eq!( + poly1305_key_gen(&mut chacha20_ctx, &mut tmp_block).unprotected_as_bytes(), + expected.as_ref() + ); + } +} diff --git a/vendor/orion/src/hazardous/aead/mod.rs b/vendor/orion/src/hazardous/aead/mod.rs new file mode 100644 index 0000000..61d4087 --- /dev/null +++ b/vendor/orion/src/hazardous/aead/mod.rs @@ -0,0 +1,30 @@ +// MIT License + +// Copyright (c) 2018-2023 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. + +/// AEAD ChaCha20Poly1305 as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub mod chacha20poly1305; + +/// AEAD XChaCha20Poly1305 as specified in the [draft RFC](https://github.com/bikeshedders/xchacha-rfc). +pub mod xchacha20poly1305; + +/// Streaming AEAD based on XChaCha20Poly1305. +pub mod streaming; diff --git a/vendor/orion/src/hazardous/aead/streaming.rs b/vendor/orion/src/hazardous/aead/streaming.rs new file mode 100644 index 0000000..a030f06 --- /dev/null +++ b/vendor/orion/src/hazardous/aead/streaming.rs @@ -0,0 +1,1515 @@ +// MIT License + +// Copyright (c) 2019-2023 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. + +//! # About: +//! +//! This implementation is based on and compatible with the ["secretstream" API] of libsodium. +//! +//! # Parameters: +//! - `secret_key`: The secret key. +//! - `nonce`: The nonce value. +//! - `ad`: Additional data to authenticate (this is not encrypted and can be `None`). +//! - `plaintext`: The data to be encrypted. +//! - `ciphertext`: The encrypted data with, a Poly1305 tag and a [`StreamTag`] indicating its function. +//! - `dst_out`: Destination array that will hold the `ciphertext`/`plaintext` after encryption/decryption. +//! - `tag`: Indicates the type of message. The `tag` is a part of the output when encrypting. It +//! is encrypted and authenticated. +//! +//! `ad`: "A typical use for these data is to authenticate version numbers, +//! timestamps or monotonically increasing counters in order to discard previous +//! messages and prevent replay attacks." See [libsodium docs] for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` + [`ABYTES`] when calling [`seal_chunk()`]. +//! - The length of `dst_out` is less than `ciphertext` - [`ABYTES`] when calling [`open_chunk()`]. +//! - The length of the `ciphertext` is less than [`ABYTES`]. +//! - The received mac does not match the calculated mac when calling [`open_chunk()`]. This can +//! indicate a dropped or reordered message within the stream. +//! - More than `2^32-3 * 64` bytes of data are processed when sealing/opening a single chunk. +//! - [`ABYTES`] + `plaintext.len()` overflows when encrypting. +//! +//! # Panics: +//! A panic will occur if: +//! - 64 + (`ciphertext.len()` - [`ABYTES`]) overflows [`u64::MAX`] when decrypting. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given key. +//! - The nonce can be randomly generated using a CSPRNG. [`Nonce::generate()`] can be used for this. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! - The lengths of the messages are not hidden, only their contents. +//! - It is recommended to use [`StreamTag::Finish`] as the tag for the last message. This allows the +//! decrypting side to detect if messages at the end of the stream are lost. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::aead::streaming::*; +//! +//! let secret_key = SecretKey::generate(); +//! let nonce = Nonce::generate(); +//! let ad = "Additional data".as_bytes(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of the above message is 15 and then we accommodate 17 +//! // for the mac and tag. +//! let mut dst_out_ct = [0u8; 15 + ABYTES]; +//! let mut dst_out_pt = [0u8; 15]; +//! +//! let mut ctx_enc = StreamXChaCha20Poly1305::new(&secret_key, &nonce); +//! +//! // Encrypt and place tag + ciphertext + mac in dst_out_ct +//! ctx_enc.seal_chunk(message, Some(ad), &mut dst_out_ct, &StreamTag::Message)?; +//! +//! let mut ctx_dec = StreamXChaCha20Poly1305::new(&secret_key, &nonce); +//! +//! // Decrypt and save the tag the message was encrypted with. +//! let tag = ctx_dec.open_chunk(&dst_out_ct, Some(ad), &mut dst_out_pt)?; +//! +//! assert_eq!(tag, StreamTag::Message); +//! assert_eq!(dst_out_pt.as_ref(), message); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: super::stream::chacha20::SecretKey::generate +//! [`Nonce::generate()`]: super::stream::xchacha20::Nonce::generate +//! [`StreamTag`]: streaming::StreamTag +//! [`StreamTag::Finish`]: streaming::StreamTag::Finish +//! [`ABYTES`]: streaming::ABYTES +//! [`seal_chunk()`]: streaming::StreamXChaCha20Poly1305::seal_chunk +//! [`open_chunk()`]: streaming::StreamXChaCha20Poly1305::open_chunk +//! ["secretstream" API]: https://download.libsodium.org/doc/secret-key_cryptography/secretstream + +use crate::errors::UnknownCryptoError; +use crate::hazardous::aead::chacha20poly1305::poly1305_key_gen; +use crate::hazardous::mac::poly1305::{Poly1305, Tag as Poly1305Tag, POLY1305_OUTSIZE}; +pub use crate::hazardous::stream::chacha20::SecretKey; +use crate::hazardous::stream::chacha20::{ + encrypt as chacha20_enc, encrypt_in_place as chacha20_xor_stream, ChaCha20, Nonce as IETFNonce, + CHACHA_BLOCKSIZE, CHACHA_KEYSIZE, HCHACHA_NONCESIZE, IETF_CHACHA_NONCESIZE, +}; +use crate::hazardous::stream::xchacha20::subkey_and_nonce; +pub use crate::hazardous::stream::xchacha20::Nonce; +use core::convert::TryFrom; +use subtle::ConstantTimeEq; +use zeroize::{Zeroize, Zeroizing}; + +#[derive(Debug, Clone, Copy)] +/// Tag that indicates the type of message. +pub enum StreamTag { + /// A message with no special meaning. + Message, + /// Marks that the message is the end of a set of messages. Allows the decrypting site to + /// start working with this data. + Push, + /// Derives a new secret key and forgets the one used for earlier encryption/decryption + /// operations. + Rekey, + /// Indicates the end of a stream. Also does a rekey. + Finish, +} + +impl StreamTag { + #[inline] + /// Return the tag as a byte. + pub fn as_byte(&self) -> u8 { + match *self { + StreamTag::Message => 0b0000_0000, + StreamTag::Push => 0b0000_0001, + StreamTag::Rekey => 0b0000_0010, + StreamTag::Finish => 0b0000_0011, /* StreamTag::Push.as_byte() | StreamTag::Rekey.as_bytes() */ + } + } +} + +impl TryFrom<u8> for StreamTag { + type Error = UnknownCryptoError; + + fn try_from(byte: u8) -> Result<Self, Self::Error> { + match byte { + 0b0000_0000 => Ok(Self::Message), + 0b0000_0001 => Ok(Self::Push), + 0b0000_0010 => Ok(Self::Rekey), + 0b0000_0011 => Ok(Self::Finish), + _ => Err(UnknownCryptoError), + } + } +} + +impl PartialEq<StreamTag> for StreamTag { + fn eq(&self, other: &StreamTag) -> bool { + (self.as_byte().ct_eq(&other.as_byte())).into() + } +} + +/// The size of the internal counter. +const COUNTERBYTES: usize = 4; +/// The size of the internal nonce. +const INONCEBYTES: usize = 8; +/// The size of a StreamTag. +pub const TAG_SIZE: usize = 1; +/// Size of additional data appended to each message. +pub const ABYTES: usize = POLY1305_OUTSIZE + TAG_SIZE; + +/// Padding size that gives the needed bytes to pad `input` to an integral +/// multiple of 16. +fn padding(input: usize) -> usize { + if input == 0 { + return 0; + } + + let rem = input % 16; + + if rem != 0 { + 16 - rem + } else { + 0 + } +} + +/// Streaming XChaCha20Poly1305 state. +pub struct StreamXChaCha20Poly1305 { + key: SecretKey, + counter: u32, + inonce: [u8; INONCEBYTES], +} + +impl core::fmt::Debug for StreamXChaCha20Poly1305 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "StreamXChaCha20Poly1305 {{ key: [***OMITTED***], counter: [***OMITTED***], inonce: [***OMITTED***]", + ) + } +} + +impl StreamXChaCha20Poly1305 { + /// Return a nonce used with ChaCha20Poly1305 based on the internal counter + /// and inonce. + fn get_nonce(&self) -> IETFNonce { + let mut nonce = [0u8; IETF_CHACHA_NONCESIZE]; + nonce[..COUNTERBYTES].copy_from_slice(&self.counter.to_le_bytes()); + nonce[COUNTERBYTES..].copy_from_slice(&self.inonce); + + IETFNonce::from(nonce) + } + + /// Generates a Poly1305 tag for a message. + fn generate_auth_tag( + &mut self, + text: &[u8], + ad: &[u8], + msglen: usize, + block: &[u8], + textpos: usize, + ) -> Result<Poly1305Tag, UnknownCryptoError> { + debug_assert!(text.len() >= textpos + msglen); + + let mut chacha20_ctx = ChaCha20::new( + self.key.unprotected_as_bytes(), + self.get_nonce().as_ref(), + true, + ) + .unwrap(); + let mut tmp_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + let mut pad = [0u8; 16]; + let mut poly = Poly1305::new(&poly1305_key_gen(&mut chacha20_ctx, &mut tmp_block)); + + poly.process_pad_to_blocksize(ad)?; + poly.update(block)?; + poly.update(&text[textpos..(textpos + msglen)])?; + poly.update(&pad[..padding(CHACHA_BLOCKSIZE.wrapping_sub(msglen))])?; + pad[..8].copy_from_slice(&(ad.len() as u64).to_le_bytes()); + pad[8..16].copy_from_slice( + &((CHACHA_BLOCKSIZE as u64) + .checked_add(msglen as u64) + .unwrap()) + .to_le_bytes(), + ); + poly.update(&pad)?; + + poly.finalize() + } + + /// Update internal inonce and counter. Performs rekey on overflowing counter and + /// designated tags. + fn advance_state( + &mut self, + mac: &Poly1305Tag, + tag: &StreamTag, + ) -> Result<(), UnknownCryptoError> { + xor_slices!(mac.unprotected_as_bytes()[..INONCEBYTES], self.inonce); + self.counter = self.counter.wrapping_add(1); + if bool::from( + !(tag.as_byte() & StreamTag::Rekey.as_byte()).ct_eq(&0u8) | self.counter.ct_eq(&0u32), + ) { + self.rekey()?; + }; + + Ok(()) + } + + /// Initialize a `StreamXChaCha20Poly1305` struct with a given secret key and nonce. + pub fn new(secret_key: &SecretKey, nonce: &Nonce) -> Self { + let mut inonce = [0u8; INONCEBYTES]; + inonce.copy_from_slice(&nonce.as_ref()[HCHACHA_NONCESIZE..]); + + Self { + key: subkey_and_nonce(secret_key, nonce).0, + counter: 1, + inonce, + } + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Derives a new secret key used for encryption and decryption. + pub fn rekey(&mut self) -> Result<(), UnknownCryptoError> { + let mut new_key_and_inonce = [0u8; CHACHA_KEYSIZE + INONCEBYTES]; + new_key_and_inonce[..CHACHA_KEYSIZE].copy_from_slice(self.key.unprotected_as_bytes()); + new_key_and_inonce[CHACHA_KEYSIZE..].copy_from_slice(&self.inonce); + + chacha20_xor_stream(&self.key, &self.get_nonce(), 0, &mut new_key_and_inonce)?; + + self.key = SecretKey::from_slice(&new_key_and_inonce[..CHACHA_KEYSIZE]).unwrap(); + self.inonce + .copy_from_slice(&new_key_and_inonce[CHACHA_KEYSIZE..]); + self.counter = 1; + new_key_and_inonce.zeroize(); + + Ok(()) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Encrypt and authenticate a single message and tag. + pub fn seal_chunk( + &mut self, + plaintext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], + tag: &StreamTag, + ) -> Result<(), UnknownCryptoError> { + let msglen = plaintext.len(); + match ABYTES.checked_add(msglen) { + Some(out_min_len) => { + if dst_out.len() < out_min_len { + return Err(UnknownCryptoError); + } + } + None => return Err(UnknownCryptoError), + }; + + let mut block = [0u8; CHACHA_BLOCKSIZE]; + let ad = ad.unwrap_or(&[0u8; 0]); + + let macpos = TAG_SIZE + msglen; + let nonce = self.get_nonce(); + + block[0] = tag.as_byte(); + chacha20_xor_stream(&self.key, &nonce, 1, &mut block)?; + dst_out[0] = block[0]; + + if msglen != 0 { + chacha20_enc(&self.key, &nonce, 2, plaintext, &mut dst_out[TAG_SIZE..])?; + } + + let mac = self.generate_auth_tag(dst_out, ad, msglen, &block, TAG_SIZE)?; + dst_out[macpos..(macpos + POLY1305_OUTSIZE)].copy_from_slice(mac.unprotected_as_bytes()); + + self.advance_state(&mac, tag) + } + + #[allow(clippy::range_plus_one)] + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Authenticate and decrypt a single message and tag. + pub fn open_chunk( + &mut self, + ciphertext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<StreamTag, UnknownCryptoError> { + if ciphertext.len() < ABYTES { + return Err(UnknownCryptoError); + } + + let msglen = ciphertext.len() - ABYTES; + if dst_out.len() < msglen { + return Err(UnknownCryptoError); + } + + let mut block = [0u8; CHACHA_BLOCKSIZE]; + let ad = ad.unwrap_or(&[0u8; 0]); + + let macpos = TAG_SIZE + msglen; + let nonce = self.get_nonce(); + + block[0] = ciphertext[0]; + chacha20_xor_stream(&self.key, &nonce, 1, &mut block)?; + let tag = StreamTag::try_from(block[0])?; + block[0] = ciphertext[0]; + let mac = self.generate_auth_tag(ciphertext, ad, msglen, &block, TAG_SIZE)?; + if !(mac == &ciphertext[macpos..macpos + mac.len()]) { + return Err(UnknownCryptoError); + } + if msglen != 0 { + chacha20_enc( + &self.key, + &nonce, + 2, + &ciphertext[TAG_SIZE..(TAG_SIZE + msglen)], + dst_out, + )?; + } + self.advance_state(&mac, &tag)?; + + Ok(tag) + } +} + +#[cfg(test)] +mod public { + #[cfg(feature = "safe_api")] + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let secret_key = SecretKey::generate(); + let nonce = Nonce::generate(); + let initial_state = StreamXChaCha20Poly1305::new(&secret_key, &nonce); + let debug = format!("{:?}", initial_state); + let expected = "StreamXChaCha20Poly1305 { key: [***OMITTED***], counter: [***OMITTED***], inonce: [***OMITTED***]"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "safe_api")] + mod proptest { + use crate::errors::UnknownCryptoError; + use crate::hazardous::aead::streaming::{ + Nonce, SecretKey, StreamTag, StreamXChaCha20Poly1305, ABYTES, + }; + use crate::test_framework::aead_interface::*; + use core::convert::TryFrom; + + fn seal( + sk: &SecretKey, + nonce: &Nonce, + input: &[u8], + ad: Option<&[u8]>, + output: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + let mut state = StreamXChaCha20Poly1305::new(sk, nonce); + state.seal_chunk(input, ad, output, &StreamTag::Message) + } + + fn open( + sk: &SecretKey, + nonce: &Nonce, + input: &[u8], + ad: Option<&[u8]>, + output: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + let mut state = StreamXChaCha20Poly1305::new(sk, nonce); + state.open_chunk(input, ad, output)?; + + Ok(()) + } + + #[quickcheck] + fn prop_aead_interface(input: Vec<u8>, ad: Vec<u8>) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::generate(); + AeadTestRunner(seal, open, secret_key, nonce, &input, None, ABYTES, &ad); + + true + } + + #[quickcheck] + fn prop_same_input_twice_diff_output(input: Vec<u8>, ad: Vec<u8>) -> bool { + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::generate(), &Nonce::generate()); + + let mut ct1 = vec![0u8; input.len() + ABYTES]; + let mut ct2 = ct1.clone(); + + ctx.seal_chunk(&input, Some(&ad), &mut ct1, &StreamTag::Message) + .unwrap(); + ctx.seal_chunk(&input, Some(&ad), &mut ct2, &StreamTag::Message) + .unwrap(); + + ct1 != ct2 + } + + #[quickcheck] + fn prop_tag(byte: u8) -> bool { + match byte { + 0u8 => StreamTag::try_from(byte).unwrap() == StreamTag::Message, + 1u8 => StreamTag::try_from(byte).unwrap() == StreamTag::Push, + 2u8 => StreamTag::try_from(byte).unwrap() == StreamTag::Rekey, + 3u8 => StreamTag::try_from(byte).unwrap() == StreamTag::Finish, + _ => StreamTag::try_from(byte).is_err(), + } + } + } +} + +#[cfg(test)] +mod private { + use super::*; + + mod test_padding { + use super::*; + #[test] + fn test_length_padding() { + assert_eq!(padding(0), 0); + assert_eq!(padding(1), 15); + assert_eq!(padding(2), 14); + assert_eq!(padding(3), 13); + assert_eq!(padding(4), 12); + assert_eq!(padding(5), 11); + assert_eq!(padding(6), 10); + assert_eq!(padding(7), 9); + assert_eq!(padding(8), 8); + assert_eq!(padding(9), 7); + assert_eq!(padding(10), 6); + assert_eq!(padding(11), 5); + assert_eq!(padding(12), 4); + assert_eq!(padding(13), 3); + assert_eq!(padding(14), 2); + assert_eq!(padding(15), 1); + assert_eq!(padding(16), 0); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + // The usize that padding() returns should always + // be what remains to make input a multiple of 16 in length. + fn prop_padding_result(input: usize) -> bool { + let rem = padding(input); + + // NOTE: It is possible to have an input size that will + // have padding() return a remainder that cannot be added + // to the original input size without overflowing its usize (tested on 64-bit). + // This is okay, because nothing is added to the returned value of padding() + // in generate_auth_tag. It's only ever added in this test. The remainder is still + // valid regardless, which is also "proven" by the test below. + // So to test the below assumption, we can only ever do so if it doesn't overflow, + // hence the needed check. + // + // See also below test test_inputsize_79(). + // + // (Trigger here with: size = 18446744073709551601) + // (Trigger seal_chunk/open_chunk with input of size 79 (check usage of wrapping_sub)) + + match input.checked_add(rem) { + Some(res) => res % 16 == 0, + None => true, // The case we cannot test + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + // padding() should never return a usize above 15. + // The usize must always be in range of 0..=15. + fn prop_result_never_above_15(input: usize) -> bool { + padding(input) < 16 + } + } + + #[test] + fn test_inputsize_79() { + // Generated with libsodium + let sk = [ + 49u8, 50u8, 51u8, 52u8, 53u8, 54u8, 55u8, 56u8, 57u8, 97u8, 98u8, 99u8, 100u8, 101u8, + 102u8, 103u8, 104u8, 105u8, 106u8, 107u8, 108u8, 109u8, 111u8, 110u8, 112u8, 113u8, + 114u8, 115u8, 116u8, 117u8, 118u8, 0u8, + ]; + let nonce = [ + 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, + 97u8, 98u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let input: [u8; 79] = [ + 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, + 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, + 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, + 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, + 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, + 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, + 0u8, + ]; + let output: [u8; 96] = [ + 252u8, 178u8, 0u8, 210u8, 9u8, 149u8, 109u8, 242u8, 161u8, 71u8, 231u8, 3u8, 175u8, + 17u8, 24u8, 148u8, 40u8, 118u8, 80u8, 107u8, 96u8, 105u8, 191u8, 34u8, 86u8, 101u8, + 33u8, 53u8, 116u8, 51u8, 220u8, 199u8, 26u8, 140u8, 80u8, 251u8, 81u8, 125u8, 160u8, + 186u8, 197u8, 26u8, 72u8, 217u8, 22u8, 205u8, 150u8, 103u8, 233u8, 204u8, 79u8, 59u8, + 137u8, 18u8, 93u8, 25u8, 189u8, 131u8, 137u8, 231u8, 123u8, 56u8, 186u8, 215u8, 33u8, + 41u8, 241u8, 146u8, 248u8, 44u8, 253u8, 253u8, 177u8, 115u8, 51u8, 23u8, 166u8, 64u8, + 6u8, 213u8, 174u8, 254u8, 55u8, 101u8, 185u8, 178u8, 89u8, 121u8, 175u8, 221u8, 174u8, + 75u8, 188u8, 65u8, 41u8, 75u8, + ]; + + let secret_key = SecretKey::from_slice(&sk).unwrap(); + let nonce = Nonce::from_slice(&nonce).unwrap(); + + let mut state = StreamXChaCha20Poly1305::new(&secret_key, &nonce); + let mut dst_out = [0u8; 96]; + state + .seal_chunk(&input, None, &mut dst_out, &StreamTag::Message) + .unwrap(); + assert_eq!(dst_out.as_ref(), output.as_ref()); + + state = StreamXChaCha20Poly1305::new(&secret_key, &nonce); + let mut dst_out_pt = [0u8; 79]; + state.open_chunk(&output, None, &mut dst_out_pt).unwrap(); + assert_eq!(dst_out_pt.as_ref(), input.as_ref()); + } + + // Test values were generated using libsodium. See /tests/test_generation/ + + const KEY: [u8; 32] = [ + 49u8, 50u8, 51u8, 52u8, 53u8, 54u8, 55u8, 56u8, 57u8, 97u8, 98u8, 99u8, 100u8, 101u8, + 102u8, 103u8, 104u8, 105u8, 106u8, 107u8, 108u8, 109u8, 111u8, 110u8, 112u8, 113u8, 114u8, + 115u8, 116u8, 117u8, 118u8, 0u8, + ]; + const NONCE: [u8; 24] = [ + 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, + 98u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + + const DEFAULT_MSG: [u8; 51] = [ + 68u8, 101u8, 102u8, 97u8, 117u8, 108u8, 116u8, 32u8, 109u8, 101u8, 115u8, 115u8, 97u8, + 103u8, 101u8, 32u8, 116u8, 111u8, 32u8, 116u8, 101u8, 115u8, 116u8, 32u8, 115u8, 116u8, + 114u8, 101u8, 97u8, 109u8, 105u8, 110u8, 103u8, 32u8, 65u8, 69u8, 65u8, 68u8, 32u8, 101u8, + 110u8, 99u8, 114u8, 121u8, 112u8, 116u8, 105u8, 111u8, 110u8, 46u8, 0u8, + ]; + + #[test] + fn test_tag() { + assert_eq!(StreamTag::Message.as_byte(), 0u8); + assert_eq!(StreamTag::Push.as_byte(), 1u8); + assert_eq!(StreamTag::Rekey.as_byte(), 2u8); + assert_eq!(StreamTag::Finish.as_byte(), 3u8); + assert!(StreamTag::try_from(4u8).is_err()); + } + + #[test] + fn test_seal_open_with_explicit_rekey() { + // Encrypt stream + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!( + s.key, + [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, + 116u8, 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, + 219u8, 33u8, 44u8, 68u8, 91u8, 135u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8,].as_ref() + ); + + // 1st StreamTag::Message + let plaintext1: [u8; 6] = [116u8, 101u8, 115u8, 116u8, 49u8, 0u8]; + let mut out1 = [0u8; 6 + ABYTES]; + s.seal_chunk(&plaintext1, None, &mut out1, &StreamTag::Message) + .unwrap(); + assert_eq!( + s.key, + [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, + 116u8, 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, + 219u8, 33u8, 44u8, 68u8, 91u8, 135u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [2u8, 0u8, 0u8, 0u8, 88u8, 186u8, 23u8, 231u8, 10u8, 253u8, 79u8, 71u8,].as_ref() + ); + assert_eq!( + out1, + [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, + 156u8, 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ] + ); + + // 2nd StreamTag::Message + let plaintext2: [u8; 20] = [ + 116u8, 104u8, 105u8, 115u8, 32u8, 105u8, 115u8, 32u8, 108u8, 111u8, 110u8, 103u8, + 101u8, 114u8, 32u8, 116u8, 101u8, 120u8, 116u8, 0u8, + ]; + let mut out2 = [0u8; 20 + ABYTES]; + s.seal_chunk(&plaintext2, None, &mut out2, &StreamTag::Message) + .unwrap(); + assert_eq!( + s.key, + [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, + 116u8, 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, + 219u8, 33u8, 44u8, 68u8, 91u8, 135u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [3u8, 0u8, 0u8, 0u8, 73u8, 199u8, 255u8, 159u8, 213u8, 205u8, 201u8, 51u8,].as_ref() + ); + assert_eq!( + out2.as_ref(), + [ + 243u8, 52u8, 124u8, 173u8, 133u8, 44u8, 99u8, 244u8, 250u8, 89u8, 101u8, 142u8, + 59u8, 49u8, 221u8, 52u8, 176u8, 214u8, 13u8, 247u8, 86u8, 17u8, 125u8, 232u8, + 120u8, 223u8, 48u8, 134u8, 116u8, 8u8, 207u8, 180u8, 241u8, 76u8, 26u8, 33u8, + 207u8, + ] + .as_ref() + ); + + // 3rd StreamTag::Message + let plaintext3: [u8; 2] = [49u8, 0u8]; + let mut out3 = [0u8; 2 + ABYTES]; + s.seal_chunk(&plaintext3, None, &mut out3, &StreamTag::Message) + .unwrap(); + assert_eq!( + s.key, + [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, + 116u8, 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, + 219u8, 33u8, 44u8, 68u8, 91u8, 135u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [4u8, 0u8, 0u8, 0u8, 229u8, 134u8, 216u8, 143u8, 117u8, 43u8, 216u8, 142u8,].as_ref() + ); + assert_eq!( + out3.as_ref(), + [ + 237u8, 198u8, 240u8, 172u8, 65u8, 39u8, 16u8, 160u8, 230u8, 17u8, 189u8, 54u8, + 93u8, 173u8, 243u8, 103u8, 185u8, 53u8, 219u8, + ] + .as_ref() + ); + + // Explicit rekey + s.rekey().unwrap(); + assert_eq!( + s.key, + [ + 55u8, 213u8, 132u8, 57u8, 116u8, 28u8, 19u8, 214u8, 59u8, 159u8, 188u8, 185u8, + 201u8, 153u8, 70u8, 17u8, 149u8, 199u8, 55u8, 34u8, 164u8, 54u8, 200u8, 241u8, + 157u8, 71u8, 218u8, 62u8, 37u8, 37u8, 8u8, 126u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [1u8, 0u8, 0u8, 0u8, 250u8, 25u8, 191u8, 166u8, 103u8, 98u8, 187u8, 196u8,].as_ref() + ); + + // 4th StreamTag::Message + let plaintext4: [u8; 23] = [ + 102u8, 105u8, 114u8, 115u8, 116u8, 32u8, 116u8, 101u8, 120u8, 116u8, 32u8, 97u8, 102u8, + 116u8, 101u8, 114u8, 32u8, 114u8, 101u8, 107u8, 101u8, 121u8, 0u8, + ]; + let mut out4 = [0u8; 23 + ABYTES]; + s.seal_chunk(&plaintext4, None, &mut out4, &StreamTag::Message) + .unwrap(); + assert_eq!( + s.key, + [ + 55u8, 213u8, 132u8, 57u8, 116u8, 28u8, 19u8, 214u8, 59u8, 159u8, 188u8, 185u8, + 201u8, 153u8, 70u8, 17u8, 149u8, 199u8, 55u8, 34u8, 164u8, 54u8, 200u8, 241u8, + 157u8, 71u8, 218u8, 62u8, 37u8, 37u8, 8u8, 126u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [2u8, 0u8, 0u8, 0u8, 70u8, 193u8, 51u8, 16u8, 173u8, 151u8, 68u8, 48u8,].as_ref() + ); + assert_eq!( + out4.as_ref(), + [ + 210u8, 9u8, 37u8, 11u8, 182u8, 190u8, 88u8, 175u8, 0u8, 12u8, 125u8, 154u8, 63u8, + 104u8, 166u8, 255u8, 231u8, 12u8, 233u8, 57u8, 206u8, 99u8, 82u8, 23u8, 188u8, + 216u8, 140u8, 182u8, 202u8, 245u8, 255u8, 244u8, 104u8, 89u8, 216u8, 168u8, 68u8, + 130u8, 12u8, 80u8, + ] + .as_ref() + ); + + // 5th StreamTag::Message + let plaintext5: [u8; 36] = [ + 116u8, 104u8, 105u8, 115u8, 32u8, 105u8, 115u8, 32u8, 116u8, 104u8, 101u8, 32u8, 115u8, + 101u8, 99u8, 111u8, 110u8, 100u8, 32u8, 116u8, 101u8, 120u8, 116u8, 32u8, 97u8, 102u8, + 116u8, 101u8, 114u8, 32u8, 114u8, 101u8, 107u8, 101u8, 121u8, 0u8, + ]; + let mut out5 = [0u8; 36 + ABYTES]; + s.seal_chunk(&plaintext5, None, &mut out5, &StreamTag::Message) + .unwrap(); + assert_eq!( + s.key, + [ + 55u8, 213u8, 132u8, 57u8, 116u8, 28u8, 19u8, 214u8, 59u8, 159u8, 188u8, 185u8, + 201u8, 153u8, 70u8, 17u8, 149u8, 199u8, 55u8, 34u8, 164u8, 54u8, 200u8, 241u8, + 157u8, 71u8, 218u8, 62u8, 37u8, 37u8, 8u8, 126u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [3u8, 0u8, 0u8, 0u8, 119u8, 231u8, 54u8, 137u8, 64u8, 159u8, 87u8, 77u8,].as_ref() + ); + assert_eq!( + out5.as_ref(), + [ + 122u8, 17u8, 56u8, 176u8, 124u8, 172u8, 219u8, 248u8, 0u8, 37u8, 184u8, 242u8, + 65u8, 248u8, 69u8, 242u8, 158u8, 119u8, 20u8, 17u8, 225u8, 10u8, 107u8, 240u8, + 210u8, 134u8, 6u8, 182u8, 91u8, 243u8, 243u8, 20u8, 30u8, 205u8, 232u8, 167u8, + 247u8, 49u8, 38u8, 5u8, 153u8, 237u8, 8u8, 19u8, 125u8, 226u8, 190u8, 189u8, 167u8, + 33u8, 189u8, 74u8, 189u8, + ] + .as_ref() + ); + + // Decrypt stream + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + + let mut plain_out1 = [0u8; 6]; + assert_eq!( + s.open_chunk(&out1, None, &mut plain_out1).unwrap(), + StreamTag::Message + ); + assert_eq!(plain_out1.as_ref(), plaintext1.as_ref()); + + let mut plain_out2 = [0u8; 20]; + assert_eq!( + s.open_chunk(&out2, None, &mut plain_out2).unwrap(), + StreamTag::Message + ); + assert_eq!(plain_out2.as_ref(), plaintext2.as_ref()); + + let mut plain_out3 = [0u8; 2]; + assert_eq!( + s.open_chunk(&out3, None, &mut plain_out3).unwrap(), + StreamTag::Message + ); + assert_eq!(plain_out3.as_ref(), plaintext3.as_ref()); + + s.rekey().unwrap(); + + let mut plain_out4 = [0u8; 23]; + assert_eq!( + s.open_chunk(&out4, None, &mut plain_out4).unwrap(), + StreamTag::Message + ); + assert_eq!(plain_out4.as_ref(), plaintext4.as_ref()); + + let mut plain_out5 = [0u8; 36]; + assert_eq!( + s.open_chunk(&out5, None, &mut plain_out5).unwrap(), + StreamTag::Message + ); + assert_eq!(plain_out5.as_ref(), plaintext5.as_ref()); + } + + #[test] + fn test_reorder_or_drop_msg() { + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let plaintext1 = [116u8, 101u8, 115u8, 116u8, 49u8, 0u8]; + let plaintext2 = [ + 116u8, 104u8, 105u8, 115u8, 32u8, 105u8, 115u8, 32u8, 108u8, 111u8, 110u8, 103u8, + 101u8, 114u8, 32u8, 116u8, 101u8, 120u8, 116u8, 0u8, + ]; + let cipher1 = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let cipher2 = [ + 243u8, 52u8, 124u8, 173u8, 133u8, 44u8, 99u8, 244u8, 250u8, 89u8, 101u8, 142u8, 59u8, + 49u8, 221u8, 52u8, 176u8, 214u8, 13u8, 247u8, 86u8, 17u8, 125u8, 232u8, 120u8, 223u8, + 48u8, 134u8, 116u8, 8u8, 207u8, 180u8, 241u8, 76u8, 26u8, 33u8, 207u8, + ]; + + let cipher3 = [ + 237u8, 198u8, 240u8, 172u8, 65u8, 39u8, 16u8, 160u8, 230u8, 17u8, 189u8, 54u8, 93u8, + 173u8, 243u8, 103u8, 185u8, 53u8, 219u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert_eq!( + ctx.open_chunk(&cipher1, None, &mut plain_out1).unwrap(), + StreamTag::Message + ); + assert_eq!(&plain_out1, &plaintext1); + // Out of order decryption happens here + let mut plain_out3 = [0u8; 19 - ABYTES]; + assert!(ctx.open_chunk(&cipher3, None, &mut plain_out3).is_err()); + + let mut plain_out2 = [0u8; 37 - ABYTES]; + assert_eq!( + ctx.open_chunk(&cipher2, None, &mut plain_out2).unwrap(), + StreamTag::Message + ); + assert_eq!(&plain_out2, &plaintext2); + } + + #[test] + fn test_err_on_diff_tag() { + // Crate 4 different sealed chunks, sealed with the same input except for + // the StreamTag. + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut dst_out_msg = [0u8; 51 + ABYTES]; + ctx.seal_chunk( + DEFAULT_MSG.as_ref(), + None, + &mut dst_out_msg, + &StreamTag::Message, + ) + .unwrap(); + + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut dst_out_push = [0u8; 51 + ABYTES]; + ctx.seal_chunk( + DEFAULT_MSG.as_ref(), + None, + &mut dst_out_push, + &StreamTag::Push, + ) + .unwrap(); + + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut dst_out_rekey = [0u8; 51 + ABYTES]; + ctx.seal_chunk( + DEFAULT_MSG.as_ref(), + None, + &mut dst_out_rekey, + &StreamTag::Rekey, + ) + .unwrap(); + + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut dst_out_finish = [0u8; 51 + ABYTES]; + ctx.seal_chunk( + DEFAULT_MSG.as_ref(), + None, + &mut dst_out_finish, + &StreamTag::Finish, + ) + .unwrap(); + + // Swap the StreamTags, which should decrypt successfully but fail authentication as + // it does not match the Poly1305 Tag calculated. + let mut dst_out_pt = [0u8; 51]; + + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert!(ctx.open_chunk(&dst_out_msg, None, &mut dst_out_pt).is_ok()); + + dst_out_msg[0] = dst_out_push[0]; + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert!(ctx.open_chunk(&dst_out_msg, None, &mut dst_out_pt).is_err()); + + dst_out_msg[0] = dst_out_rekey[0]; + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert!(ctx.open_chunk(&dst_out_msg, None, &mut dst_out_pt).is_err()); + + dst_out_msg[0] = dst_out_finish[0]; + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert!(ctx.open_chunk(&dst_out_msg, None, &mut dst_out_pt).is_err()); + } + + #[test] + fn test_err_on_modified_message_tag() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_ok()); + + // Reset state + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + // Change tag to Push plaintext. Originally was Message and encrypted. + cipher1[0] = StreamTag::Push.as_byte(); + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_err()); + } + + #[test] + fn test_err_on_diff_secret_key() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from(NONCE)); + let cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_err()); + } + + #[test] + fn test_err_on_diff_nonce() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from([0u8; 24])); + let cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_err()); + } + + #[test] + fn test_err_on_modified_mac() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_ok()); + + // Reset state + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + // Change MAC + let macpos = cipher1.len() - 1; + cipher1[macpos] |= 0b1010_1010; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_err()); + } + + #[test] + fn test_err_on_modified_cipher() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_ok()); + + // Reset state + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + // Change something in the ciphertext + cipher1[5] |= 0b1010_1010; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_err()); + } + + #[test] + fn test_err_on_diff_ad() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + // This ciphertext was constructed without AD + let cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_ok()); + + // Reset state + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert!(s + .open_chunk(&cipher1, Some(&[1u8; 1]), &mut plain_out1) + .is_err()); + } + + #[test] + fn test_encrypting_same_message_different_output() { + let input = [0u8, 1u8, 2u8, 3u8]; + let mut cipher = [0u8; 4 + ABYTES]; + let mut cipher2 = [0u8; 4 + ABYTES]; + let mut state_enc = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + state_enc + .seal_chunk(&input, None, &mut cipher, &StreamTag::Message) + .unwrap(); + state_enc + .seal_chunk(&input, None, &mut cipher2, &StreamTag::Message) + .unwrap(); + assert_ne!(cipher, cipher2); + } + + #[test] + fn test_encrypting_same_message_explicit_rekey() { + let input = [0u8, 1u8, 2u8, 3u8]; + let mut cipher = [0u8; 4 + ABYTES]; + let mut cipher2 = [0u8; 4 + ABYTES]; + let mut state_enc = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + state_enc + .seal_chunk(&input, None, &mut cipher, &StreamTag::Message) + .unwrap(); + let mut state_enc = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + state_enc.rekey().unwrap(); + state_enc + .seal_chunk(&input, None, &mut cipher2, &StreamTag::Message) + .unwrap(); + assert_ne!(cipher, cipher2); + } + + #[test] + // This cannot be tested in AeadTestRunner as it assumes empty + // ciphertext to be invalid. + fn test_seal_empty_and_open() { + let mut state_enc = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + let mut cipher = [0u8; ABYTES]; + state_enc + .seal_chunk(&[0u8; 0], None, &mut cipher, &StreamTag::Message) + .unwrap(); + + let mut state_dec = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + + let mut out = [0u8; ABYTES]; + assert!(state_dec.open_chunk(&cipher, None, &mut out).is_ok()); + } + + #[test] + // This cannot be tested in AeadTestRunner as it assumes empty + // ciphertext to be invalid. This also tests that input to open_chunk + // requires the tagsize only, and not tagsize + 1. + fn test_seal_open_zero_length_both() { + let mut state_enc = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + let mut out = [0u8; ABYTES]; + state_enc + .seal_chunk(&[0u8; 0], None, &mut out, &StreamTag::Message) + .unwrap(); + let mut state_dec = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + let mut dst_out = [0u8; 0]; + // Only the attached tag is decrypted and authenticated. But it is + // not placed in dst_out. + assert!(state_dec.open_chunk(&out, None, &mut dst_out).is_ok()); + assert!(dst_out.is_empty()); + assert_eq!(dst_out, [0u8; 0]); + } + + #[test] + fn test_new_to_msg_with_tag_final() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let after_internal_key: [u8; 32] = [ + 168u8, 19u8, 24u8, 25u8, 196u8, 239u8, 73u8, 251u8, 36u8, 135u8, 89u8, 117u8, 2u8, + 50u8, 208u8, 173u8, 177u8, 61u8, 147u8, 201u8, 97u8, 47u8, 74u8, 149u8, 21u8, 166u8, + 227u8, 53u8, 24u8, 101u8, 251u8, 201u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 149u8, 246u8, 165u8, 228u8, 252u8, 41u8, 183u8, 89u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 255u8, 148u8, 0u8, 209u8, 14u8, 130u8, 100u8, 227u8, 231u8, 72u8, 231u8, 21u8, 186u8, + 18u8, 26u8, 148u8, 110u8, 96u8, 90u8, 46u8, 114u8, 110u8, 169u8, 51u8, 16u8, 116u8, + 48u8, 34u8, 119u8, 48u8, 212u8, 203u8, 18u8, 137u8, 21u8, 223u8, 114u8, 94u8, 129u8, + 255u8, 198u8, 22u8, 78u8, 206u8, 9u8, 223u8, 135u8, 107u8, 224u8, 192u8, 4u8, 94u8, + 100u8, 12u8, 28u8, 232u8, 44u8, 133u8, 81u8, 145u8, 176u8, 153u8, 17u8, 215u8, 180u8, + 79u8, 24u8, 79u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!(ctx.key.unprotected_as_bytes(), before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Finish) + .unwrap(); + + assert_eq!(ctx.key.unprotected_as_bytes(), after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_new_to_msg_with_tag_rekey() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let after_internal_key: [u8; 32] = [ + 39u8, 159u8, 44u8, 102u8, 206u8, 161u8, 116u8, 119u8, 163u8, 187u8, 45u8, 209u8, 172u8, + 224u8, 237u8, 93u8, 9u8, 197u8, 138u8, 242u8, 195u8, 183u8, 253u8, 169u8, 86u8, 46u8, + 161u8, 32u8, 71u8, 244u8, 51u8, 222u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 161u8, 222u8, 206u8, 42u8, 195u8, 117u8, 85u8, 88u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 254u8, 148u8, 0u8, 209u8, 14u8, 130u8, 100u8, 227u8, 231u8, 72u8, 231u8, 21u8, 186u8, + 18u8, 26u8, 148u8, 110u8, 96u8, 90u8, 46u8, 114u8, 110u8, 169u8, 51u8, 16u8, 116u8, + 48u8, 34u8, 119u8, 48u8, 212u8, 203u8, 18u8, 137u8, 21u8, 223u8, 114u8, 94u8, 129u8, + 255u8, 198u8, 22u8, 78u8, 206u8, 9u8, 223u8, 135u8, 107u8, 224u8, 192u8, 4u8, 94u8, + 187u8, 26u8, 216u8, 136u8, 169u8, 121u8, 52u8, 215u8, 102u8, 180u8, 177u8, 255u8, 69u8, + 135u8, 172u8, 22u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!(ctx.key.unprotected_as_bytes(), before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Rekey) + .unwrap(); + + assert_eq!(ctx.key.unprotected_as_bytes(), after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_new_to_msg_with_tag_final_twice() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let after_internal_key: [u8; 32] = [ + 162u8, 54u8, 231u8, 162u8, 170u8, 199u8, 53u8, 228u8, 224u8, 121u8, 138u8, 154u8, 17u8, + 252u8, 83u8, 49u8, 52u8, 25u8, 105u8, 51u8, 112u8, 3u8, 62u8, 217u8, 163u8, 194u8, + 15u8, 113u8, 155u8, 17u8, 7u8, 250u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 107u8, 7u8, 226u8, 104u8, 227u8, 227u8, 7u8, 100u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 119u8, 255u8, 30u8, 46u8, 10u8, 174u8, 158u8, 45u8, 225u8, 116u8, 158u8, 172u8, 175u8, + 198u8, 241u8, 34u8, 87u8, 52u8, 80u8, 230u8, 175u8, 133u8, 217u8, 132u8, 169u8, 75u8, + 68u8, 249u8, 86u8, 87u8, 86u8, 68u8, 33u8, 250u8, 148u8, 117u8, 43u8, 10u8, 22u8, + 125u8, 198u8, 6u8, 231u8, 5u8, 86u8, 199u8, 29u8, 214u8, 215u8, 74u8, 71u8, 244u8, + 194u8, 50u8, 98u8, 209u8, 136u8, 235u8, 79u8, 113u8, 54u8, 101u8, 105u8, 14u8, 178u8, + 146u8, 23u8, 229u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!(ctx.key.unprotected_as_bytes(), before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Finish) + .unwrap(); + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Finish) + .unwrap(); + + assert_eq!(ctx.key.unprotected_as_bytes(), after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_new_to_msg_with_tag_rekey_twice() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let after_internal_key: [u8; 32] = [ + 35u8, 207u8, 156u8, 185u8, 84u8, 18u8, 253u8, 160u8, 229u8, 242u8, 126u8, 247u8, 235u8, + 193u8, 36u8, 121u8, 42u8, 247u8, 85u8, 108u8, 107u8, 143u8, 210u8, 194u8, 109u8, 46u8, + 107u8, 47u8, 186u8, 127u8, 123u8, 46u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 198u8, 222u8, 225u8, 73u8, 93u8, 233u8, 75u8, 181u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 1u8, 171u8, 81u8, 78u8, 97u8, 128u8, 134u8, 65u8, 126u8, 237u8, 31u8, 59u8, 6u8, 76u8, + 85u8, 119u8, 69u8, 183u8, 129u8, 184u8, 101u8, 246u8, 151u8, 0u8, 92u8, 171u8, 38u8, + 164u8, 215u8, 120u8, 75u8, 169u8, 254u8, 207u8, 198u8, 138u8, 118u8, 68u8, 89u8, 231u8, + 38u8, 220u8, 26u8, 210u8, 220u8, 102u8, 8u8, 245u8, 205u8, 152u8, 39u8, 155u8, 36u8, + 115u8, 127u8, 79u8, 54u8, 246u8, 154u8, 2u8, 24u8, 208u8, 83u8, 232u8, 143u8, 234u8, + 51u8, 194u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!(ctx.key.unprotected_as_bytes(), before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Rekey) + .unwrap(); + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Rekey) + .unwrap(); + + assert_eq!(ctx.key.unprotected_as_bytes(), after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_new_to_msg_with_tag_push() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let after_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 2u8, 0u8, 0u8, 0u8, 118u8, 75u8, 245u8, 72u8, 68u8, 15u8, 117u8, 29u8, + ]; + let after_internal_counter: [u8; 4] = [2u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 253u8, 148u8, 0u8, 209u8, 14u8, 130u8, 100u8, 227u8, 231u8, 72u8, 231u8, 21u8, 186u8, + 18u8, 26u8, 148u8, 110u8, 96u8, 90u8, 46u8, 114u8, 110u8, 169u8, 51u8, 16u8, 116u8, + 48u8, 34u8, 119u8, 48u8, 212u8, 203u8, 18u8, 137u8, 21u8, 223u8, 114u8, 94u8, 129u8, + 255u8, 198u8, 22u8, 78u8, 206u8, 9u8, 223u8, 135u8, 107u8, 224u8, 192u8, 4u8, 94u8, + 23u8, 41u8, 148u8, 41u8, 38u8, 110u8, 23u8, 29u8, 29u8, 207u8, 81u8, 40u8, 215u8, + 190u8, 64u8, 222u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!(ctx.key, before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce().as_ref(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Push) + .unwrap(); + + assert_eq!(ctx.key, after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce().as_ref(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_counter_overflow_with_tag_msg() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 255u8, 255u8, 255u8, 255u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [255u8, 255u8, 255u8, 255u8]; + let after_internal_key: [u8; 32] = [ + 48u8, 109u8, 58u8, 2u8, 7u8, 92u8, 20u8, 239u8, 137u8, 218u8, 220u8, 62u8, 74u8, 47u8, + 118u8, 162u8, 61u8, 234u8, 35u8, 242u8, 40u8, 2u8, 243u8, 149u8, 188u8, 249u8, 180u8, + 242u8, 228u8, 139u8, 163u8, 76u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 36u8, 9u8, 22u8, 61u8, 226u8, 117u8, 46u8, 156u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 131u8, 93u8, 90u8, 220u8, 186u8, 163u8, 161u8, 113u8, 238u8, 31u8, 49u8, 63u8, 12u8, + 101u8, 64u8, 221u8, 255u8, 190u8, 206u8, 20u8, 155u8, 140u8, 72u8, 180u8, 4u8, 199u8, + 170u8, 178u8, 21u8, 212u8, 238u8, 60u8, 110u8, 233u8, 44u8, 24u8, 105u8, 216u8, 234u8, + 132u8, 103u8, 31u8, 222u8, 244u8, 214u8, 180u8, 224u8, 206u8, 148u8, 114u8, 100u8, + 161u8, 57u8, 6u8, 49u8, 199u8, 242u8, 58u8, 28u8, 253u8, 199u8, 16u8, 246u8, 86u8, + 116u8, 22u8, 66u8, 91u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + ctx.counter = u32::MAX; + assert_eq!(ctx.key, before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Message) + .unwrap(); + + assert_eq!(ctx.key, after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_counter_overflow_with_tag_rekey() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 255u8, 255u8, 255u8, 255u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [255u8, 255u8, 255u8, 255u8]; + let after_internal_key: [u8; 32] = [ + 115u8, 83u8, 132u8, 174u8, 130u8, 252u8, 214u8, 242u8, 239u8, 140u8, 231u8, 231u8, + 111u8, 228u8, 182u8, 88u8, 124u8, 109u8, 210u8, 61u8, 48u8, 22u8, 215u8, 232u8, 180u8, + 174u8, 180u8, 216u8, 174u8, 209u8, 222u8, 8u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 188u8, 188u8, 116u8, 239u8, 177u8, 113u8, 89u8, 218u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 129u8, 93u8, 90u8, 220u8, 186u8, 163u8, 161u8, 113u8, 238u8, 31u8, 49u8, 63u8, 12u8, + 101u8, 64u8, 221u8, 255u8, 190u8, 206u8, 20u8, 155u8, 140u8, 72u8, 180u8, 4u8, 199u8, + 170u8, 178u8, 21u8, 212u8, 238u8, 60u8, 110u8, 233u8, 44u8, 24u8, 105u8, 216u8, 234u8, + 132u8, 103u8, 31u8, 222u8, 244u8, 214u8, 180u8, 224u8, 206u8, 148u8, 114u8, 100u8, + 161u8, 107u8, 212u8, 93u8, 14u8, 123u8, 181u8, 233u8, 248u8, 139u8, 61u8, 100u8, 73u8, + 40u8, 14u8, 226u8, 118u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + ctx.counter = u32::MAX; + assert_eq!(ctx.key.unprotected_as_bytes(), before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Rekey) + .unwrap(); + + assert_eq!(ctx.key.unprotected_as_bytes(), after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_counter_overflow_with_tag_final() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 255u8, 255u8, 255u8, 255u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [255u8, 255u8, 255u8, 255u8]; + let after_internal_key: [u8; 32] = [ + 181u8, 99u8, 219u8, 38u8, 136u8, 29u8, 61u8, 72u8, 122u8, 0u8, 111u8, 182u8, 254u8, + 74u8, 225u8, 183u8, 250u8, 200u8, 34u8, 169u8, 252u8, 92u8, 107u8, 85u8, 144u8, 12u8, + 203u8, 19u8, 166u8, 41u8, 168u8, 26u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 230u8, 194u8, 178u8, 201u8, 13u8, 110u8, 57u8, 106u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 128u8, 93u8, 90u8, 220u8, 186u8, 163u8, 161u8, 113u8, 238u8, 31u8, 49u8, 63u8, 12u8, + 101u8, 64u8, 221u8, 255u8, 190u8, 206u8, 20u8, 155u8, 140u8, 72u8, 180u8, 4u8, 199u8, + 170u8, 178u8, 21u8, 212u8, 238u8, 60u8, 110u8, 233u8, 44u8, 24u8, 105u8, 216u8, 234u8, + 132u8, 103u8, 31u8, 222u8, 244u8, 214u8, 180u8, 224u8, 206u8, 148u8, 114u8, 100u8, + 161u8, 137u8, 59u8, 244u8, 49u8, 191u8, 114u8, 208u8, 246u8, 237u8, 83u8, 155u8, 66u8, + 2u8, 10u8, 178u8, 132u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + ctx.counter = u32::MAX; + assert_eq!(ctx.key, before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Finish) + .unwrap(); + + assert_eq!(ctx.key, after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_counter_overflow_with_tag_push() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 255u8, 255u8, 255u8, 255u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [255u8, 255u8, 255u8, 255u8]; + let after_internal_key: [u8; 32] = [ + 251u8, 165u8, 61u8, 114u8, 68u8, 126u8, 68u8, 202u8, 143u8, 101u8, 78u8, 242u8, 164u8, + 171u8, 209u8, 209u8, 227u8, 5u8, 181u8, 244u8, 141u8, 167u8, 137u8, 0u8, 228u8, 122u8, + 149u8, 109u8, 129u8, 240u8, 174u8, 128u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 228u8, 204u8, 203u8, 245u8, 146u8, 107u8, 101u8, 124u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 130u8, 93u8, 90u8, 220u8, 186u8, 163u8, 161u8, 113u8, 238u8, 31u8, 49u8, 63u8, 12u8, + 101u8, 64u8, 221u8, 255u8, 190u8, 206u8, 20u8, 155u8, 140u8, 72u8, 180u8, 4u8, 199u8, + 170u8, 178u8, 21u8, 212u8, 238u8, 60u8, 110u8, 233u8, 44u8, 24u8, 105u8, 216u8, 234u8, + 132u8, 103u8, 31u8, 222u8, 244u8, 214u8, 180u8, 224u8, 206u8, 148u8, 114u8, 100u8, + 161u8, 82u8, 109u8, 199u8, 234u8, 54u8, 248u8, 2u8, 251u8, 41u8, 39u8, 45u8, 80u8, + 78u8, 18u8, 18u8, 105u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + ctx.counter = u32::MAX; + assert_eq!(ctx.key, before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Push) + .unwrap(); + + assert_eq!(ctx.key, after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } +} diff --git a/vendor/orion/src/hazardous/aead/xchacha20poly1305.rs b/vendor/orion/src/hazardous/aead/xchacha20poly1305.rs new file mode 100644 index 0000000..1d110f7 --- /dev/null +++ b/vendor/orion/src/hazardous/aead/xchacha20poly1305.rs @@ -0,0 +1,153 @@ +// MIT License + +// Copyright (c) 2018-2023 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 secret key. +//! - `nonce`: The nonce value. +//! - `ad`: Additional data to authenticate (this is not encrypted and can be [`None`]). +//! - `ciphertext_with_tag`: The encrypted data with the corresponding 16 byte +//! Poly1305 tag appended to it. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the +//! `ciphertext_with_tag`/`plaintext` after encryption/decryption. +//! +//! `ad`: "A typical use for these data is to authenticate version numbers, +//! timestamps or monotonically increasing counters in order to discard previous +//! messages and prevent replay attacks." See [libsodium docs] for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` + [`POLY1305_OUTSIZE`] when calling [`seal()`]. +//! - The length of `dst_out` is less than `ciphertext_with_tag` - [`POLY1305_OUTSIZE`] when +//! calling [`open()`]. +//! - The length of the `ciphertext_with_tag` is not at least [`POLY1305_OUTSIZE`]. +//! - The received tag does not match the calculated tag when calling [`open()`]. +//! - `plaintext.len()` + [`POLY1305_OUTSIZE`] overflows when calling [`seal()`]. +//! - Converting `usize` to `u64` would be a lossy conversion. +//! - `plaintext.len() >` [`chacha20poly1305::P_MAX`] +//! - `ad.len() >` [`chacha20poly1305::A_MAX`] +//! - `ciphertext_with_tag.len() >` [`chacha20poly1305::C_MAX`] +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1 * 64` bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Only a nonce for XChaCha20Poly1305 is big enough to be randomly generated +//! using a CSPRNG. [`Nonce::generate()`] can be used for this. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! - The length of the `plaintext` is not hidden, only its contents. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::aead; +//! +//! let secret_key = aead::xchacha20poly1305::SecretKey::generate(); +//! let nonce = aead::xchacha20poly1305::Nonce::generate(); +//! let ad = "Additional data".as_bytes(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of the above message is 15 and then we accommodate 16 for the Poly1305 +//! // tag. +//! +//! let mut dst_out_ct = [0u8; 15 + 16]; +//! let mut dst_out_pt = [0u8; 15]; +//! // Encrypt and place ciphertext + tag in dst_out_ct +//! aead::xchacha20poly1305::seal(&secret_key, &nonce, message, Some(&ad), &mut dst_out_ct)?; +//! // Verify tag, if correct then decrypt and place message in dst_out_pt +//! aead::xchacha20poly1305::open(&secret_key, &nonce, &dst_out_ct, Some(&ad), &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt.as_ref(), message.as_ref()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: super::stream::chacha20::SecretKey::generate +//! [`Nonce::generate()`]: super::stream::xchacha20::Nonce::generate +//! [`POLY1305_OUTSIZE`]: super::mac::poly1305::POLY1305_OUTSIZE +//! [`seal()`]: xchacha20poly1305::seal +//! [`open()`]: xchacha20poly1305::open +//! [libsodium docs]: https://download.libsodium.org/doc/secret-key_cryptography/aead#additional-data + +use crate::hazardous::stream::xchacha20::subkey_and_nonce; +pub use crate::hazardous::stream::{chacha20::SecretKey, xchacha20::Nonce}; +use crate::{errors::UnknownCryptoError, hazardous::aead::chacha20poly1305}; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// AEAD XChaCha20Poly1305 encryption as specified in the [draft RFC](https://github.com/bikeshedders/xchacha-rfc). +pub fn seal( + secret_key: &SecretKey, + nonce: &Nonce, + plaintext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + let (subkey, ietf_nonce) = subkey_and_nonce(secret_key, nonce); + chacha20poly1305::seal(&subkey, &ietf_nonce, plaintext, ad, dst_out) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// AEAD XChaCha20Poly1305 decryption as specified in the [draft RFC](https://github.com/bikeshedders/xchacha-rfc). +pub fn open( + secret_key: &SecretKey, + nonce: &Nonce, + ciphertext_with_tag: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + let (subkey, ietf_nonce) = subkey_and_nonce(secret_key, nonce); + chacha20poly1305::open(&subkey, &ietf_nonce, ciphertext_with_tag, ad, dst_out) +} + +// Testing public functions in the module. +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use super::*; + use crate::hazardous::mac::poly1305::POLY1305_OUTSIZE; + use crate::test_framework::aead_interface::{test_diff_params_err, AeadTestRunner}; + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_aead_interface(input: Vec<u8>, ad: Vec<u8>) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::generate(); + AeadTestRunner( + seal, + open, + secret_key, + nonce, + &input, + None, + POLY1305_OUTSIZE, + &ad, + ); + test_diff_params_err(&seal, &open, &input, POLY1305_OUTSIZE); + true + } +} diff --git a/vendor/orion/src/hazardous/cae/chacha20poly1305blake2b.rs b/vendor/orion/src/hazardous/cae/chacha20poly1305blake2b.rs new file mode 100644 index 0000000..19cef00 --- /dev/null +++ b/vendor/orion/src/hazardous/cae/chacha20poly1305blake2b.rs @@ -0,0 +1,262 @@ +// MIT License + +// Copyright (c) 2023 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. + +//! # About +//! This provides a fully committing AEAD, using the CTX construction proposed by Chan and Rogaway, +//! in the ["On Committing Authenticated Encryption"] paper. Specifically, CTX is instantiated with BLAKE2b-256. +//! +//! A fully committing AEAD is important if attacks like the [partitioning oracle attack] are a part of the threat model. +//! +//! # Parameters: +//! - `secret_key`: The secret key. +//! - `nonce`: The nonce value. +//! - `ad`: Additional data to authenticate (this is not encrypted and can be [`None`]). +//! - `ciphertext_with_tag`: The encrypted data with the corresponding 32 byte +//! BLAKE2b tag appended to it. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the +//! `ciphertext_with_tag`/`plaintext` after encryption/decryption. +//! +//! `ad`: "A typical use for these data is to authenticate version numbers, +//! timestamps or monotonically increasing counters in order to discard previous +//! messages and prevent replay attacks." See [libsodium docs] for more information. +//! +//! `nonce`: "Counters and LFSRs are both acceptable ways of generating unique +//! nonces, as is encrypting a counter using a block cipher with a 64-bit block +//! size such as DES. Note that it is not acceptable to use a truncation of a +//! counter encrypted with block ciphers with 128-bit or 256-bit blocks, +//! because such a truncation may repeat after a short time." See [RFC] for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` + [`TAG_SIZE`] when calling [`seal()`]. +//! - The length of `dst_out` is less than `ciphertext_with_tag` - [`TAG_SIZE`] when +//! calling [`open()`]. +//! - The length of `ciphertext_with_tag` is not at least [`TAG_SIZE`]. +//! - The received tag does not match the calculated tag when calling [`open()`]. +//! - `plaintext.len()` + [`TAG_SIZE`] overflows when calling [`seal()`]. +//! - Converting `usize` to `u64` would be a lossy conversion. +//! - `plaintext.len() >` [`P_MAX`] +//! - `ad.len() >` [`A_MAX`] +//! - `ciphertext_with_tag.len() >` [`C_MAX`] +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1 * 64` bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Only a nonce for XChaCha20Poly1305 is big enough to be randomly generated +//! using a CSPRNG. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! - The length of the `plaintext` is not hidden, only its contents. +//! +//! # Recommendation: +//! - It is recommended to use [`XChaCha20Poly1305-BLAKE2b`] when possible. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::cae; +//! +//! let secret_key = cae::chacha20poly1305blake2b::SecretKey::generate(); +//! +//! // WARNING: This nonce is only meant for demonstration and should not +//! // be repeated. Please read the security section. +//! let nonce = cae::chacha20poly1305blake2b::Nonce::from([0u8; 12]); +//! let ad = "Additional data".as_bytes(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of the above message is 15 and then we accommodate 32 for the BLAKE2b +//! // tag. +//! +//! let mut dst_out_ct = [0u8; 15 + 32]; +//! let mut dst_out_pt = [0u8; 15]; +//! // Encrypt and place ciphertext + tag in dst_out_ct +//! cae::chacha20poly1305blake2b::seal(&secret_key, &nonce, message, Some(&ad), &mut dst_out_ct)?; +//! // Verify tag, if correct then decrypt and place message in dst_out_pt +//! cae::chacha20poly1305blake2b::open(&secret_key, &nonce, &dst_out_ct, Some(&ad), &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt.as_ref(), message.as_ref()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: super::stream::chacha20::SecretKey::generate +//! [`XChaCha20Poly1305-BLAKE2b`]: xchacha20poly1305blake2b +//! [`TAG_SIZE`]: chacha20poly1305blake2b::TAG_SIZE +//! [`seal()`]: chacha20poly1305blake2b::seal +//! [`open()`]: chacha20poly1305blake2b::open +//! [RFC]: https://tools.ietf.org/html/rfc8439#section-3 +//! [libsodium docs]: https://download.libsodium.org/doc/secret-key_cryptography/aead#additional-data +//! [`P_MAX`]: chacha20poly1305blake2b::P_MAX +//! [`A_MAX`]: chacha20poly1305blake2b::A_MAX +//! [`C_MAX`]: chacha20poly1305blake2b::C_MAX +//! ["On Committing Authenticated Encryption"]: https://eprint.iacr.org/2022/1260 +//! [partitioning oracle attack]: https://www.usenix.org/conference/usenixsecurity21/presentation/len + +use crate::errors::UnknownCryptoError; +use crate::hazardous::aead; +use crate::hazardous::aead::chacha20poly1305::{poly1305_key_gen, process_authentication, ENC_CTR}; +use crate::hazardous::hash::blake2::blake2b::Blake2b; +use crate::hazardous::mac::poly1305::Poly1305; +use crate::hazardous::mac::poly1305::POLY1305_OUTSIZE; +use crate::hazardous::stream::chacha20::{self, ChaCha20, CHACHA_BLOCKSIZE}; +use crate::util; +use zeroize::Zeroizing; + +pub use crate::hazardous::aead::chacha20poly1305::A_MAX; +pub use crate::hazardous::aead::chacha20poly1305::P_MAX; +pub use crate::hazardous::stream::chacha20::{Nonce, SecretKey}; + +/// The size of the BLAKE2b authentication tag. +pub const TAG_SIZE: usize = 32; + +/// The maximum size of the ciphertext. +pub const C_MAX: u64 = P_MAX + (TAG_SIZE as u64); + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// CTX ChaCha20Poly1305 with BLAKE2b-256. +pub fn seal( + secret_key: &SecretKey, + nonce: &Nonce, + plaintext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(plaintext.len()).map_err(|_| UnknownCryptoError)? > P_MAX { + return Err(UnknownCryptoError); + } + + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + + match plaintext.len().checked_add(TAG_SIZE) { + Some(out_min_len) => { + if dst_out.len() < out_min_len { + return Err(UnknownCryptoError); + } + } + None => return Err(UnknownCryptoError), + }; + + aead::chacha20poly1305::seal( + secret_key, + nonce, + plaintext, + Some(ad), + &mut dst_out[..plaintext.len() + POLY1305_OUTSIZE], + )?; + + let mut blake2b = Blake2b::new(32)?; + blake2b.update(secret_key.unprotected_as_bytes())?; + blake2b.update(nonce.as_ref())?; + blake2b.update(ad)?; + blake2b.update(&dst_out[plaintext.len()..plaintext.len() + POLY1305_OUTSIZE])?; + let tag = blake2b.finalize()?; + + dst_out[plaintext.len()..plaintext.len() + TAG_SIZE].copy_from_slice(tag.as_ref()); + + Ok(()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// CTX ChaCha20Poly1305 with BLAKE2b-256. +pub fn open( + secret_key: &SecretKey, + nonce: &Nonce, + ciphertext_with_tag: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(ciphertext_with_tag.len()).map_err(|_| UnknownCryptoError)? > C_MAX { + return Err(UnknownCryptoError); + } + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + if ciphertext_with_tag.len() < TAG_SIZE { + return Err(UnknownCryptoError); + } + if dst_out.len() < ciphertext_with_tag.len() - TAG_SIZE { + return Err(UnknownCryptoError); + } + + let mut blake2b = Blake2b::new(32)?; + blake2b.update(secret_key.unprotected_as_bytes())?; + blake2b.update(nonce.as_ref())?; + blake2b.update(ad)?; + + let mut dec_ctx = + ChaCha20::new(secret_key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + let mut auth_ctx = Poly1305::new(&poly1305_key_gen(&mut dec_ctx, &mut tmp)); + + let ciphertext_len = ciphertext_with_tag.len() - TAG_SIZE; + process_authentication(&mut auth_ctx, ad, &ciphertext_with_tag[..ciphertext_len])?; + + blake2b.update(auth_ctx.finalize()?.unprotected_as_bytes())?; + + util::secure_cmp( + blake2b.finalize()?.as_ref(), + &ciphertext_with_tag[ciphertext_len..], + )?; + + if ciphertext_len != 0 { + dst_out[..ciphertext_len].copy_from_slice(&ciphertext_with_tag[..ciphertext_len]); + chacha20::xor_keystream( + &mut dec_ctx, + ENC_CTR, + tmp.as_mut(), + &mut dst_out[..ciphertext_len], + )?; + } + + Ok(()) +} + +// Testing public functions in the module. +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use super::*; + use crate::test_framework::aead_interface::{test_diff_params_err, AeadTestRunner}; + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_aead_interface(input: Vec<u8>, ad: Vec<u8>) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::from_slice(&[0u8; chacha20::IETF_CHACHA_NONCESIZE]).unwrap(); + AeadTestRunner(seal, open, secret_key, nonce, &input, None, TAG_SIZE, &ad); + test_diff_params_err(&seal, &open, &input, TAG_SIZE); + true + } +} diff --git a/vendor/orion/src/hazardous/cae/mod.rs b/vendor/orion/src/hazardous/cae/mod.rs new file mode 100644 index 0000000..270821b --- /dev/null +++ b/vendor/orion/src/hazardous/cae/mod.rs @@ -0,0 +1,33 @@ +// MIT License + +// Copyright (c) 2023 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. + +#![cfg_attr(docsrs, doc(cfg(feature = "experimental")))] + +/// Fully-committing ChaCha20-Poly1305 with BLAKE2b based on the [CTX] construction by John Chan & Phillip Rogaway. +/// +/// [CTX]: https://eprint.iacr.org/2022/1260 +pub mod chacha20poly1305blake2b; + +/// Fully-committing XChaCha20-Poly1305 with BLAKE2b based on the [CTX] construction by John Chan & Phillip Rogaway. +/// +/// [CTX]: https://eprint.iacr.org/2022/1260 +pub mod xchacha20poly1305blake2b; diff --git a/vendor/orion/src/hazardous/cae/xchacha20poly1305blake2b.rs b/vendor/orion/src/hazardous/cae/xchacha20poly1305blake2b.rs new file mode 100644 index 0000000..25d3c32 --- /dev/null +++ b/vendor/orion/src/hazardous/cae/xchacha20poly1305blake2b.rs @@ -0,0 +1,246 @@ +// MIT License + +// Copyright (c) 2023 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. + +//! # About +//! This provides a fully committing AEAD, using the CTX construction proposed by Chan and Rogaway, +//! in the ["On Committing Authenticated Encryption"] paper. Specifically, CTX is instantiated with BLAKE2b-256. +//! +//! A fully committing AEAD is important if attacks like the [partitioning oracle attack] are a part of the threat model. +//! +//! # Parameters: +//! - `secret_key`: The secret key. +//! - `nonce`: The nonce value. +//! - `ad`: Additional data to authenticate (this is not encrypted and can be [`None`]). +//! - `ciphertext_with_tag`: The encrypted data with the corresponding 32 byte +//! BLAKE2b tag appended to it. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the +//! `ciphertext_with_tag`/`plaintext` after encryption/decryption. +//! +//! `ad`: "A typical use for these data is to authenticate version numbers, +//! timestamps or monotonically increasing counters in order to discard previous +//! messages and prevent replay attacks." See [libsodium docs] for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` + [`TAG_SIZE`] when calling [`seal()`]. +//! - The length of `dst_out` is less than `ciphertext_with_tag` - [`TAG_SIZE`] when +//! calling [`open()`]. +//! - The length of `ciphertext_with_tag` is not at least [`TAG_SIZE`]. +//! - The received tag does not match the calculated tag when calling [`open()`]. +//! - `plaintext.len()` + [`TAG_SIZE`] overflows when calling [`seal()`]. +//! - Converting `usize` to `u64` would be a lossy conversion. +//! - `plaintext.len() >` [`P_MAX`] +//! - `ad.len() >` [`A_MAX`] +//! - `ciphertext_with_tag.len() >` [`C_MAX`] +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1 * 64` bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Only a nonce for XChaCha20Poly1305 is big enough to be randomly generated +//! using a CSPRNG. [`Nonce::generate()`] can be used for this. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! - The length of the `plaintext` is not hidden, only its contents. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::cae; +//! +//! let secret_key = cae::xchacha20poly1305blake2b::SecretKey::generate(); +//! let nonce = cae::xchacha20poly1305blake2b::Nonce::generate(); +//! let ad = "Additional data".as_bytes(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of the above message is 15 and then we accommodate 32 for the BLAKE2b +//! // tag. +//! +//! let mut dst_out_ct = [0u8; 15 + 32]; +//! let mut dst_out_pt = [0u8; 15]; +//! // Encrypt and place ciphertext + tag in dst_out_ct +//! cae::xchacha20poly1305blake2b::seal(&secret_key, &nonce, message, Some(&ad), &mut dst_out_ct)?; +//! // Verify tag, if correct then decrypt and place message in dst_out_pt +//! cae::xchacha20poly1305blake2b::open(&secret_key, &nonce, &dst_out_ct, Some(&ad), &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt.as_ref(), message.as_ref()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: super::stream::chacha20::SecretKey::generate +//! [`Nonce::generate()`]: super::stream::xchacha20::Nonce::generate +//! [`TAG_SIZE`]: xchacha20poly1305blake2b::TAG_SIZE +//! [`seal()`]: xchacha20poly1305blake2b::seal +//! [`open()`]: xchacha20poly1305blake2b::open +//! [RFC]: https://tools.ietf.org/html/rfc8439#section-3 +//! [libsodium docs]: https://download.libsodium.org/doc/secret-key_cryptography/aead#additional-data +//! [`P_MAX`]: xchacha20poly1305blake2b::P_MAX +//! [`A_MAX`]: xchacha20poly1305blake2b::A_MAX +//! [`C_MAX`]: xchacha20poly1305blake2b::C_MAX +//! ["On Committing Authenticated Encryption"]: https://eprint.iacr.org/2022/1260 +//! [partitioning oracle attack]: https://www.usenix.org/conference/usenixsecurity21/presentation/len + +use crate::errors::UnknownCryptoError; +use crate::hazardous::aead; +pub use crate::hazardous::aead::chacha20poly1305::A_MAX; +pub use crate::hazardous::aead::chacha20poly1305::P_MAX; +use crate::hazardous::aead::chacha20poly1305::{poly1305_key_gen, process_authentication, ENC_CTR}; +pub use crate::hazardous::cae::chacha20poly1305blake2b::{C_MAX, TAG_SIZE}; +use crate::hazardous::hash::blake2::blake2b::Blake2b; +use crate::hazardous::mac::poly1305::{Poly1305, POLY1305_OUTSIZE}; +use crate::hazardous::stream::chacha20::{self, ChaCha20, CHACHA_BLOCKSIZE}; +use crate::hazardous::stream::xchacha20::subkey_and_nonce; +pub use crate::hazardous::stream::{chacha20::SecretKey, xchacha20::Nonce}; +use crate::util; +use zeroize::Zeroizing; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// CTX XChaCha20Poly1305 with BLAKE2b-256. +pub fn seal( + secret_key: &SecretKey, + nonce: &Nonce, + plaintext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(plaintext.len()).map_err(|_| UnknownCryptoError)? > P_MAX { + return Err(UnknownCryptoError); + } + + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + + match plaintext.len().checked_add(TAG_SIZE) { + Some(out_min_len) => { + if dst_out.len() < out_min_len { + return Err(UnknownCryptoError); + } + } + None => return Err(UnknownCryptoError), + }; + + let (subkey, ietf_nonce) = subkey_and_nonce(secret_key, nonce); + aead::chacha20poly1305::seal( + &subkey, + &ietf_nonce, + plaintext, + Some(ad), + &mut dst_out[..plaintext.len() + POLY1305_OUTSIZE], + )?; + + let mut blake2b = Blake2b::new(32)?; + blake2b.update(secret_key.unprotected_as_bytes())?; + blake2b.update(nonce.as_ref())?; + blake2b.update(ad)?; + blake2b.update(&dst_out[plaintext.len()..plaintext.len() + POLY1305_OUTSIZE])?; + let tag = blake2b.finalize()?; + + dst_out[plaintext.len()..plaintext.len() + TAG_SIZE].copy_from_slice(tag.as_ref()); + + Ok(()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// CTX XChaCha20Poly1305 with BLAKE2b-256. +pub fn open( + secret_key: &SecretKey, + nonce: &Nonce, + ciphertext_with_tag: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(ciphertext_with_tag.len()).map_err(|_| UnknownCryptoError)? > C_MAX { + return Err(UnknownCryptoError); + } + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + if ciphertext_with_tag.len() < TAG_SIZE { + return Err(UnknownCryptoError); + } + if dst_out.len() < ciphertext_with_tag.len() - TAG_SIZE { + return Err(UnknownCryptoError); + } + + let mut blake2b = Blake2b::new(32)?; + blake2b.update(secret_key.unprotected_as_bytes())?; + blake2b.update(nonce.as_ref())?; + blake2b.update(ad)?; + + let (subkey, ietf_nonce) = subkey_and_nonce(secret_key, nonce); + let mut dec_ctx = + ChaCha20::new(subkey.unprotected_as_bytes(), ietf_nonce.as_ref(), true).unwrap(); + let mut tmp = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + let mut auth_ctx = Poly1305::new(&poly1305_key_gen(&mut dec_ctx, &mut tmp)); + + let ciphertext_len = ciphertext_with_tag.len() - TAG_SIZE; + process_authentication(&mut auth_ctx, ad, &ciphertext_with_tag[..ciphertext_len])?; + + blake2b.update(auth_ctx.finalize()?.unprotected_as_bytes())?; + + util::secure_cmp( + blake2b.finalize()?.as_ref(), + &ciphertext_with_tag[ciphertext_len..], + )?; + + if ciphertext_len != 0 { + dst_out[..ciphertext_len].copy_from_slice(&ciphertext_with_tag[..ciphertext_len]); + chacha20::xor_keystream( + &mut dec_ctx, + ENC_CTR, + tmp.as_mut(), + &mut dst_out[..ciphertext_len], + )?; + } + + Ok(()) +} + +// Testing public functions in the module. +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use super::*; + use crate::test_framework::aead_interface::{test_diff_params_err, AeadTestRunner}; + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_aead_interface(input: Vec<u8>, ad: Vec<u8>) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::generate(); + AeadTestRunner(seal, open, secret_key, nonce, &input, None, TAG_SIZE, &ad); + test_diff_params_err(&seal, &open, &input, TAG_SIZE); + true + } +} diff --git a/vendor/orion/src/hazardous/ecc/mod.rs b/vendor/orion/src/hazardous/ecc/mod.rs new file mode 100644 index 0000000..a539b67 --- /dev/null +++ b/vendor/orion/src/hazardous/ecc/mod.rs @@ -0,0 +1,24 @@ +// MIT License + +// Copyright (c) 2021-2023 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. + +/// Diffie-Hellman key exchange over Curve25519 as specified in the [RFC 7748](https://datatracker.ietf.org/doc/html/rfc7748). +pub mod x25519; diff --git a/vendor/orion/src/hazardous/ecc/x25519.rs b/vendor/orion/src/hazardous/ecc/x25519.rs new file mode 100644 index 0000000..806777f --- /dev/null +++ b/vendor/orion/src/hazardous/ecc/x25519.rs @@ -0,0 +1,821 @@ +// MIT License + +// Copyright (c) 2021-2023 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: +//! - `private_key`: The private key used in key agreement. +//! - `public_key`: The public key used in key agreement. +//! +//! # Errors: +//! An error will be returned if: +//! - The `key_agreement()` operation results in an all-zero output. +//! +//! # Security: +//! - Multiple different `private_key`/`public_key` pairs can produce the same shared key. Therefore, +//! using the resulting `SharedKey`, directly from `key_agreement()`, is not recommended. This is handled +//! automatically in [`orion::kex`]. +//! - To securely generate a strong key, use [`PrivateKey::generate()`]. +//! +//! # Recommendation: +//! - It is recommended to use [`orion::kex`] when possible. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::ecc::x25519::{PrivateKey, PublicKey, SharedKey, key_agreement}; +//! use core::convert::TryFrom; +//! +//! // Alice generates a private key and computes the corresponding public key +//! let alice_sk = PrivateKey::generate(); +//! let alice_pk = PublicKey::try_from(&alice_sk)?; +//! +//! // Bob does the same +//! let bob_sk = PrivateKey::generate(); +//! let bob_pk = PublicKey::try_from(&bob_sk)?; +//! +//! // They both compute a shared key using the others public key +//! let alice_shared = key_agreement(&alice_sk, &bob_pk)?; +//! let bob_shared = key_agreement(&bob_sk, &alice_pk)?; +//! +//! assert_eq!(alice_shared, bob_shared); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`PrivateKey::generate()`]: crate::hazardous::ecc::x25519::PrivateKey::generate +//! [`orion::kex`]: crate::kex + +use crate::errors::UnknownCryptoError; +use crate::util::secure_cmp; +use core::ops::{Add, Mul, Sub}; + +/// Formally verified Curve25519 field arithmetic from: <https://github.com/mit-plv/fiat-crypto>. +use fiat_crypto::curve25519_64 as fiat_curve25519_u64; +use fiat_curve25519_u64::{ + fiat_25519_add, fiat_25519_carry, fiat_25519_carry_mul, fiat_25519_carry_scmul_121666, + fiat_25519_carry_square, fiat_25519_loose_field_element, fiat_25519_relax, fiat_25519_sub, + fiat_25519_tight_field_element, +}; + +/// The size of a public key used in X25519. +pub const PUBLIC_KEY_SIZE: usize = 32; + +/// The size of a private key used in X25519. +pub const PRIVATE_KEY_SIZE: usize = 32; + +/// The size of a shared key used in X25519. +pub const SHARED_KEY_SIZE: usize = 32; + +/// u-coordinate of the base point. +const BASEPOINT: [u8; 32] = [ + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +/// The result of computing a shared secret with a low order point. +const LOW_ORDER_POINT_RESULT: [u8; 32] = [0u8; 32]; + +#[derive(Clone, Copy)] +/// Represent an element in the curve field. +struct FieldElement(fiat_25519_tight_field_element); + +impl Eq for FieldElement {} + +impl PartialEq for FieldElement { + fn eq(&self, other: &Self) -> bool { + use subtle::ConstantTimeEq; + self.as_bytes().ct_eq(&other.as_bytes()).into() + } +} + +impl core::fmt::Debug for FieldElement { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "FieldElement({:?})", &self.0 .0) + } +} + +/// The function fiat_25519_carry_mul multiplies two field elements and reduces the result. +impl Mul for FieldElement { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]); + let mut rhs_relaxed = fiat_25519_loose_field_element([0u64; 5]); + + fiat_25519_relax(&mut self_relaxed, &self.0); + fiat_25519_relax(&mut rhs_relaxed, &rhs.0); + + fiat_25519_carry_mul(&mut ret, &self_relaxed, &rhs_relaxed); + + Self(ret) + } +} + +/// The function fiat_25519_add adds two field elements. +impl Add for FieldElement { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + let mut ret_add = fiat_25519_loose_field_element([0u64; 5]); + + fiat_25519_add(&mut ret_add, &self.0, &rhs.0); + fiat_25519_carry(&mut ret, &ret_add); + + Self(ret) + } +} + +/// The function fiat_25519_sub subtracts two field elements. +impl Sub for FieldElement { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + let mut ret_sub = fiat_25519_loose_field_element([0u64; 5]); + + fiat_25519_sub(&mut ret_sub, &self.0, &rhs.0); + fiat_25519_carry(&mut ret, &ret_sub); + + Self(ret) + } +} + +impl FieldElement { + /// Create a `FieldElement` that is `0`. + fn zero() -> Self { + Self(fiat_25519_tight_field_element([ + 0u64, 0u64, 0u64, 0u64, 0u64, + ])) + } + + /// Create a `FieldElement` that is `1`. + fn one() -> Self { + Self(fiat_25519_tight_field_element([ + 1u64, 0u64, 0u64, 0u64, 0u64, + ])) + } + + /// Serialize the `FieldElement` as a byte-array. + fn as_bytes(&self) -> [u8; 32] { + // The function fiat_25519_to_bytes serializes a field element to bytes in little-endian order. + use fiat_curve25519_u64::fiat_25519_to_bytes; + + let mut ret = [0u8; 32]; + fiat_25519_to_bytes(&mut ret, &self.0); + + ret + } + + /// Deserialize the `FieldElement` from a byte-array in little-endian. + /// + /// Masks the MSB in the final byte of the input bytes. + fn from_bytes(bytes: &[u8; 32]) -> Self { + // The function fiat_25519_from_bytes deserializes a field element from bytes in little-endian order + use fiat_curve25519_u64::fiat_25519_from_bytes; + + let mut temp = [0u8; 32]; + temp.copy_from_slice(bytes); + temp[31] &= 127u8; // See RFC: "When receiving such an array, implementations of X25519 + // (but not X448) MUST mask the most significant bit in the final byte." + + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + fiat_25519_from_bytes(&mut ret, &temp); + + Self(ret) + } + + /// A conditional-swap operation. + fn conditional_swap(swap: u8, a: &mut Self, b: &mut Self) { + // The function fiat_25519_selectznz is a multi-limb conditional select. + use fiat_curve25519_u64::fiat_25519_selectznz; + + // SAFETY: This is a part of fiat input bounds. + debug_assert!(swap == 1 || swap == 0); + + let tmp_a = *a; + let tmp_b = *b; + + fiat_25519_selectznz(&mut a.0 .0, swap, &tmp_a.0 .0, &tmp_b.0 .0); + fiat_25519_selectznz(&mut b.0 .0, swap, &tmp_b.0 .0, &tmp_a.0 .0); + } + + /// Square the `FieldElement` and reduce the result. + fn square(&self) -> Self { + let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]); + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + + fiat_25519_relax(&mut self_relaxed, &self.0); + fiat_25519_carry_square(&mut ret, &self_relaxed); + + Self(ret) + } + + /// Multiply the `FieldElement` by 121666 and reduce the result. + fn mul_121666(&self) -> Self { + let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]); + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + + fiat_25519_relax(&mut self_relaxed, &self.0); + fiat_25519_carry_scmul_121666(&mut ret, &self_relaxed); + + Self(ret) + } + + /// Compute the multiplicative inverse of the `FieldElement`. + /// + /// Ref: https://github.com/golang/crypto/blob/0c34fe9e7dc2486962ef9867e3edb3503537209f/curve25519/curve25519_generic.go#L718 + fn invert(&mut self) { + let mut t0 = self.square(); + let mut t1 = t0.square(); + t1 = t1.square(); + + t1 = *self * t1; + t0 = t0 * t1; + let mut t2 = t0.square(); + + t1 = t1 * t2; + t2 = t1.square(); + for _ in 1..5 { + t2 = t2.square(); + } + t1 = t2 * t1; + t2 = t1.square(); + for _ in 1..10 { + t2 = t2.square(); + } + t2 = t2 * t1; + let mut t3 = t2.square(); + for _ in 1..20 { + t3 = t3.square(); + } + t2 = t3 * t2; + t2 = t2.square(); + for _ in 1..10 { + t2 = t2.square(); + } + t1 = t2 * t1; + t2 = t1.square(); + for _ in 1..50 { + t2 = t2.square(); + } + + t2 = t2 * t1; + t3 = t2.square(); + for _ in 1..100 { + t3 = t3.square(); + } + t2 = t3 * t2; + t2 = t2.square(); + for _ in 1..50 { + t2 = t2.square(); + } + t1 = t2 * t1; + t1 = t1.square(); + for _ in 1..5 { + t1 = t1.square(); + } + + *self = t1 * t0; + } +} + +#[derive(Clone)] +/// Represents a Scalar decoded from a byte array. +struct Scalar([u8; PRIVATE_KEY_SIZE]); + +impl Drop for Scalar { + fn drop(&mut self) { + use zeroize::Zeroize; + self.0.iter_mut().zeroize(); + } +} + +impl PartialEq for Scalar { + fn eq(&self, other: &Self) -> bool { + use subtle::ConstantTimeEq; + self.0.ct_eq(&other.0).into() + } +} + +impl Eq for Scalar {} + +impl Scalar { + /// Create a scalar from some byte-array. + /// The scalar is clamped according to the RFC. + /// + /// Ref: https://www.ietf.org/rfc/rfc7748.html#section-5 + fn from_slice(slice: &[u8]) -> Result<Scalar, UnknownCryptoError> { + if slice.len() != PRIVATE_KEY_SIZE { + return Err(UnknownCryptoError); + } + + let mut ret = [0u8; PRIVATE_KEY_SIZE]; + ret.copy_from_slice(slice); + // Clamp + ret[0] &= 248; + ret[31] &= 127; + ret[31] |= 64; + + Ok(Self(ret)) + } +} + +/// Scalar multiplication using the Montgomery Ladder (a.k.a "scalarmult") +/// +/// Refs: +/// - https://eprint.iacr.org/2020/956.pdf +/// - https://eprint.iacr.org/2017/212.pdf +/// - https://github.com/golang/crypto/blob/0c34fe9e7dc2486962ef9867e3edb3503537209f/curve25519/curve25519_generic.go#L779 +fn mont_ladder(scalar: &Scalar, point: FieldElement) -> FieldElement { + let x1 = point; + let mut x2 = FieldElement::one(); + let mut x3 = x1; + let mut z3 = FieldElement::one(); + let mut z2 = FieldElement::zero(); + let mut tmp0: FieldElement; + let mut tmp1: FieldElement; + + let mut swap: u8 = 0; + + for idx in (0..=254).rev() { + let bit = (scalar.0[idx >> 3] >> (idx & 7)) & 1; + swap ^= bit; + FieldElement::conditional_swap(swap, &mut x2, &mut x3); + FieldElement::conditional_swap(swap, &mut z2, &mut z3); + swap = bit; + + tmp0 = x3 - z3; + tmp1 = x2 - z2; + x2 = x2 + z2; + z2 = x3 + z3; + z3 = tmp0 * x2; + z2 = z2 * tmp1; + tmp0 = tmp1.square(); + tmp1 = x2.square(); + x3 = z3 + z2; + z2 = z3 - z2; + x2 = tmp1 * tmp0; + tmp1 = tmp1 - tmp0; + z2 = z2.square(); + z3 = tmp1.mul_121666(); + x3 = x3.square(); + tmp0 = tmp0 + z3; + z3 = x1 * z2; + z2 = tmp1 * tmp0; + } + + FieldElement::conditional_swap(swap, &mut x2, &mut x3); + FieldElement::conditional_swap(swap, &mut z2, &mut z3); + + z2.invert(); + x2 = x2 * z2; + + x2 +} + +#[allow(clippy::derive_partial_eq_without_eq)] +// NOTE: FieldElement contains a constant-time PartialEq<FieldElement> impl. +/// A type that represents a `PublicKey` that X25519 uses. +/// +/// This type holds a field element and is used internally as the u-coordinate. +/// As the RFC mandates, the most significant bit of the last byte is masked. +/// +/// # Errors: +/// An error will be returned if: +/// - `slice` is not 32 bytes. +#[derive(PartialEq, Debug, Clone)] +pub struct PublicKey { + fe: FieldElement, +} + +impl PartialEq<&[u8]> for PublicKey { + fn eq(&self, other: &&[u8]) -> bool { + if other.len() != PUBLIC_KEY_SIZE { + return false; + } + let other: [u8; 32] = (*other).try_into().unwrap(); + + self.fe == FieldElement::from_bytes(&other) + } +} + +impl From<[u8; PUBLIC_KEY_SIZE]> for PublicKey { + #[inline] + fn from(bytes: [u8; PUBLIC_KEY_SIZE]) -> Self { + Self { + fe: FieldElement::from_bytes(&bytes), + } + } +} + +impl_try_from_trait!(PublicKey); +#[cfg(feature = "serde")] +impl_serde_traits!(PublicKey, to_bytes); + +impl TryFrom<&PrivateKey> for PublicKey { + type Error = UnknownCryptoError; + + fn try_from(private_key: &PrivateKey) -> Result<Self, Self::Error> { + // NOTE: This implementation should be identical to key_agreement() except + // for the check of a resulting low order point result. + let scalar = Scalar::from_slice(private_key.unprotected_as_bytes())?; + + Ok(PublicKey::from( + mont_ladder(&scalar, FieldElement::from_bytes(&BASEPOINT)).as_bytes(), + )) + } +} + +impl PublicKey { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result<Self, UnknownCryptoError> { + let slice_len = slice.len(); + + if slice_len != PUBLIC_KEY_SIZE { + return Err(UnknownCryptoError); + } + + Ok(Self { + fe: FieldElement::from_bytes(slice.try_into().unwrap()), + }) + } + + #[inline] + /// Return the length of the object. + pub fn len(&self) -> usize { + PUBLIC_KEY_SIZE + } + + #[inline] + /// Return `true` if this object does not hold any data, `false` otherwise. + /// + /// __NOTE__: This method should always return `false`, since there shouldn't be a way + /// to create an empty instance of this object. + pub fn is_empty(&self) -> bool { + PUBLIC_KEY_SIZE == 0 + } + + #[inline] + /// Convert this PublicKey to its byte-representation. + pub fn to_bytes(&self) -> [u8; 32] { + self.fe.as_bytes() + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +// NOTE: Scalar contains a constant-time PartialEq<Scalar> impl. +// NOTE: All newtypes impl Drop by default and Scalar has zeroizing Drop +/// A type to represent the `PrivateKey` that X25519 uses. +/// +/// This type holds a scalar and is used internally as such. The scalar held is decoded +/// (a.k.a "clamped") as mandated in the [RFC](https://datatracker.ietf.org/doc/html/rfc7748#section-5). +/// +/// # Errors: +/// An error will be returned if: +/// - `slice` is not 32 bytes. +/// +/// # Panics: +/// A panic will occur if: +/// - Failure to generate random bytes securely. +/// +/// +/// # Security: +/// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections +/// that the type implements. +/// +/// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted +/// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait +/// is implemented in such a way that the comparison happens in constant time. Thus, users should +/// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. +/// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. +/// ```rust +/// # #[cfg(feature = "safe_api")] { +/// use orion::hazardous::ecc::x25519::PrivateKey; +/// +/// // Initialize a secret key with random bytes. +/// let secret_key = PrivateKey::generate(); +/// +/// // Secure, constant-time comparison with a byte slice +/// assert_ne!(secret_key, &[0; 32][..]); +/// +/// // Secure, constant-time comparison with another SecretKey +/// assert_ne!(secret_key, PrivateKey::generate()); +/// # } +/// # Ok::<(), orion::errors::UnknownCryptoError>(()) +/// ``` +#[derive(PartialEq)] +pub struct PrivateKey { + scalar: Scalar, +} + +impl PartialEq<&[u8]> for PrivateKey { + fn eq(&self, other: &&[u8]) -> bool { + match Scalar::from_slice(other) { + Ok(other_scalar) => self.scalar == other_scalar, + Err(_) => false, + } + } +} + +impl core::fmt::Debug for PrivateKey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} {{***OMITTED***}}", stringify!(PrivateKey)) + } +} + +impl From<[u8; PRIVATE_KEY_SIZE]> for PrivateKey { + #[inline] + fn from(bytes: [u8; PRIVATE_KEY_SIZE]) -> Self { + PrivateKey { + // unwrap OK due to valid len + scalar: Scalar::from_slice(bytes.as_ref()).unwrap(), + } + } +} + +impl PrivateKey { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result<Self, UnknownCryptoError> { + Ok(Self { + scalar: Scalar::from_slice(slice)?, + }) + } + + #[inline] + /// Return the length of the object. + pub fn len(&self) -> usize { + PRIVATE_KEY_SIZE + } + + #[inline] + /// Return `true` if this object does not hold any data, `false` otherwise. + /// + /// __NOTE__: This method should always return `false`, since there shouldn't be a way + /// to create an empty instance of this object. + pub fn is_empty(&self) -> bool { + PRIVATE_KEY_SIZE == 0 + } + + #[inline] + /// Return the object as byte slice. __**Warning**__: Should not be used unless strictly + /// needed. This __**breaks protections**__ that the type implements. + pub fn unprotected_as_bytes(&self) -> &[u8] { + self.scalar.0.as_ref() + } + + #[cfg(feature = "safe_api")] + /// Randomly generate using a CSPRNG. Not available in `no_std` context. + pub fn generate() -> PrivateKey { + let mut value = [0u8; PRIVATE_KEY_SIZE]; + crate::util::secure_rand_bytes(&mut value).unwrap(); + + Self { + // unwrap OK due to valid len + scalar: Scalar::from_slice(&value).unwrap(), + } + } +} + +construct_secret_key! { + /// A type to represent the `SharedKey` that X25519 produces. + /// + /// This type simply holds bytes. Creating an instance from slices or similar, + /// performs no checks whatsoever. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + (SharedKey, test_shared_key, SHARED_KEY_SIZE, SHARED_KEY_SIZE) +} + +impl_from_trait!(SharedKey, SHARED_KEY_SIZE); + +/// X25519 (Diffie-Hellman with Montgomery form of Curve25519). +pub fn key_agreement( + private_key: &PrivateKey, + public_key: &PublicKey, +) -> Result<SharedKey, UnknownCryptoError> { + let u_coord = public_key.fe; + let field_element = mont_ladder(&private_key.scalar, u_coord).as_bytes(); + // High bit should be zero. + debug_assert_eq!(field_element[31] & 0b1000_0000u8, 0u8); + if secure_cmp(&field_element, &LOW_ORDER_POINT_RESULT).is_ok() { + return Err(UnknownCryptoError); + } + + Ok(SharedKey::from(field_element)) +} + +#[cfg(test)] +mod public { + use crate::hazardous::ecc::x25519::{ + key_agreement, PrivateKey, PublicKey, SharedKey, BASEPOINT, + }; + + #[test] + fn test_public_key_ignores_highbit() { + let u = [0u8; 32]; + + let mut msb_zero = u; + msb_zero[31] &= 127u8; + let mut msb_one = u; + msb_one[31] |= 128u8; + + // These should equal each-other. The high bits differ, but should be ignored. + assert_eq!(PublicKey::from(msb_zero), msb_one.as_ref()); + assert_eq!(PublicKey::from(msb_zero), PublicKey::from(msb_one)); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_highbit_ignored() { + // RFC 7748 dictates that the MSB of final byte must be masked when receiving a field element, + // used for agreement (public key). We check that modifying it does not impact the result of + // the agreement. + let k = PrivateKey::generate(); + let mut u = [0u8; 32]; + crate::util::secure_rand_bytes(&mut u).unwrap(); + debug_assert_ne!(u[31] & 127u8, (u[31] & 127u8) | 128u8); + + let mut u_msb_zero = u; + u_msb_zero[31] &= 127u8; + let mut u_msb_one = u; + u_msb_one[31] |= 128u8; + + // Mask bit to 0 as we do in `FieldElement::from_bytes()`. + let msb_zero = key_agreement(&k, &PublicKey::from(u_msb_zero)).unwrap(); + let msb_one = key_agreement(&k, &PublicKey::from(u_msb_one)).unwrap(); + + assert_eq!(msb_zero, msb_one); + } + + #[test] + /// Ref: https://www.ietf.org/rfc/rfc7748.html#section-5.2 + fn test_rfc_section_5() { + let mut scalar = [0u8; 32]; + let mut point = [0u8; 32]; + let mut expected = SharedKey::from([0u8; 32]); + + hex::decode_to_slice( + "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4", + &mut scalar, + ) + .unwrap(); + hex::decode_to_slice( + "e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c", + &mut point, + ) + .unwrap(); + hex::decode_to_slice( + "c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552", + &mut expected.value, + ) + .unwrap(); + + let actual = key_agreement(&PrivateKey::from(scalar), &PublicKey::from(point)).unwrap(); + assert_eq!(actual, expected); + + hex::decode_to_slice( + "4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d", + &mut scalar, + ) + .unwrap(); + hex::decode_to_slice( + "e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493", + &mut point, + ) + .unwrap(); + hex::decode_to_slice( + "95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957", + &mut expected.value, + ) + .unwrap(); + + let actual = key_agreement(&PrivateKey::from(scalar), &PublicKey::from(point)).unwrap(); + assert_eq!(actual, expected); + } + + #[test] + /// Ref: https://www.ietf.org/rfc/rfc7748.html#section-5.2 + fn test_rfc_section_5_iter() { + let mut k = BASEPOINT; + let mut u = BASEPOINT; + + // 1 iter + let ret = key_agreement(&PrivateKey::from(k), &PublicKey::from(u)).unwrap(); + u = k; + k = ret.value; + + let mut expected = SharedKey::from([0u8; 32]); + hex::decode_to_slice( + "422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079", + &mut expected.value, + ) + .unwrap(); + assert_eq!(k, expected.value, "Failed after 1 iter"); + + for _ in 0..999 { + let ret = key_agreement(&PrivateKey::from(k), &PublicKey::from(u)).unwrap(); + u = k; + k = ret.value; + } + + hex::decode_to_slice( + "684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51", + &mut expected.value, + ) + .unwrap(); + assert_eq!(k, expected.value, "Failed after 1.000 iter"); + + /* Taking a decade... + for num in 0..999000 { + let ret = key_agreement(&PrivateKey::from(k), &PublicKey::from(u)).unwrap(); + u = k; + k = ret.value; + } + + hex::decode_to_slice( + "7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424", + &mut expected.value, + ) + .unwrap(); + assert_eq!(k, expected.value, "Failed after 1.000.000 iter"); + */ + } + + #[test] + /// Ref: https://www.ietf.org/rfc/rfc7748.html#section-6.1 + fn test_rfc_section_6_pub_priv_basepoint() { + let mut alice_pub = [0u8; 32]; + let mut alice_priv = [0u8; 32]; + + let mut bob_pub = [0u8; 32]; + let mut bob_priv = [0u8; 32]; + + let mut shared = SharedKey::from([0u8; 32]); + + hex::decode_to_slice( + "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", + &mut alice_priv, + ) + .unwrap(); + hex::decode_to_slice( + "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a", + &mut alice_pub, + ) + .unwrap(); + assert_eq!( + key_agreement(&PrivateKey::from(alice_priv), &PublicKey::from(BASEPOINT)).unwrap(), + PublicKey::from(alice_pub).to_bytes().as_ref() + ); + + hex::decode_to_slice( + "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", + &mut bob_priv, + ) + .unwrap(); + hex::decode_to_slice( + "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f", + &mut bob_pub, + ) + .unwrap(); + assert_eq!( + key_agreement(&PrivateKey::from(bob_priv), &PublicKey::from(BASEPOINT)).unwrap(), + PublicKey::from(bob_pub).to_bytes().as_ref() + ); + + hex::decode_to_slice( + "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742", + &mut shared.value, + ) + .unwrap(); + assert_eq!( + key_agreement(&PrivateKey::from(alice_priv), &PublicKey::from(bob_pub)).unwrap(), + shared.value.as_ref() + ); + assert_eq!( + key_agreement(&PrivateKey::from(bob_priv), &PublicKey::from(alice_pub)).unwrap(), + shared.value.as_ref() + ); + } +} diff --git a/vendor/orion/src/hazardous/hash/blake2/blake2b.rs b/vendor/orion/src/hazardous/hash/blake2/blake2b.rs new file mode 100644 index 0000000..877f2bb --- /dev/null +++ b/vendor/orion/src/hazardous/hash/blake2/blake2b.rs @@ -0,0 +1,414 @@ +// MIT License + +// Copyright (c) 2018-2023 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: +//! - `size`: The desired output length for the digest. +//! - `data`: The data to be hashed. +//! - `expected`: The expected digest when verifying. +//! +//! # 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 recommended minimum output size is 32. +//! - This interface only allows creating hash digest using BLAKE2b. If using a secret key is desired, +//! please refer to the [`mac::blake2b`] module. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::blake2::blake2b::{Blake2b, Hasher}; +//! +//! // Using the streaming interface. +//! let mut state = Blake2b::new(64)?; +//! state.update(b"Some data")?; +//! let hash = state.finalize()?; +//! +//! // Using the `Hasher` for convenience functions. +//! let hash_one_shot = Hasher::Blake2b512.digest(b"Some data")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: blake2b::Blake2b::update +//! [`reset()`]: blake2b::Blake2b::reset +//! [`finalize()`]: blake2b::Blake2b::finalize +//! [`mac::blake2b`]: crate::hazardous::mac::blake2b + +use crate::errors::UnknownCryptoError; +use crate::hazardous::hash::blake2::blake2b_core; +use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE; + +#[cfg(feature = "safe_api")] +use std::io; + +construct_public! { + /// A type to represent the `Digest` that BLAKE2b returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `slice` is greater than 64 bytes. + (Digest, test_digest, 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). + pub fn new(size: usize) -> Result<Self, UnknownCryptoError> { + Ok(Self { + _state: blake2b_core::State::_new(&[], size)?, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Reset to `new()` state. + pub fn reset(&mut self) -> Result<(), UnknownCryptoError> { + 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) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a BLAKE2b digest. + pub fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> { + let mut tmp = [0u8; BLAKE2B_OUTSIZE]; + self._state._finalize(&mut tmp)?; + + Digest::from_slice(&tmp[..self._state.size]) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, PartialEq)] +/// Convenience functions for common BLAKE2b operations. +pub enum Hasher { + /// Blake2b with `32` as `size`. + Blake2b256, + /// Blake2b with `48` as `size`. + Blake2b384, + /// Blake2b with `64` as `size`. + Blake2b512, +} + +impl Hasher { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a digest selected by the given Blake2b variant. + pub fn digest(&self, data: &[u8]) -> Result<Digest, UnknownCryptoError> { + let size: usize = match *self { + Hasher::Blake2b256 => 32, + Hasher::Blake2b384 => 48, + Hasher::Blake2b512 => 64, + }; + + let mut state = Blake2b::new(size)?; + state.update(data)?; + state.finalize() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a `Blake2b` state selected by the given Blake2b variant. + pub fn init(&self) -> Result<Blake2b, UnknownCryptoError> { + match *self { + Hasher::Blake2b256 => Blake2b::new(32), + Hasher::Blake2b384 => Blake2b::new(48), + Hasher::Blake2b512 => Blake2b::new(64), + } + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: custom digest size. +/// ```rust +/// use orion::{ +/// hazardous::hash::blake2::blake2b::{Blake2b, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Blake2b::new(64)?; // 512-bit hash +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box<dyn std::error::Error>>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Blake2b { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Blake2b::update`](crate::hazardous::hash::blake2::blake2b::Blake2b::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +#[cfg(test)] +mod public { + mod test_streaming_interface_no_key { + use crate::errors::UnknownCryptoError; + use crate::hazardous::hash::blake2::blake2b::{Blake2b, Digest}; + use crate::hazardous::hash::blake2::blake2b_core::{ + compare_blake2b_states, BLAKE2B_BLOCKSIZE, BLAKE2B_OUTSIZE, + }; + use crate::test_framework::incremental_interface::{ + StreamingContextConsistencyTester, TestableStreamingContext, + }; + + impl TestableStreamingContext<Digest> for Blake2b { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset() + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> { + let mut ctx = Blake2b::new(BLAKE2B_OUTSIZE)?; + ctx.update(input)?; + ctx.finalize() + } + + fn verify_result(expected: &Digest, 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 initial_state: Blake2b = Blake2b::new(BLAKE2B_OUTSIZE).unwrap(); + + let test_runner = StreamingContextConsistencyTester::<Digest, 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 initial_state: Blake2b = Blake2b::new(BLAKE2B_OUTSIZE).unwrap(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Blake2b>::new( + initial_state, + BLAKE2B_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + mod test_hasher { + use crate::hazardous::hash::blake2::blake2b::Hasher; + + #[test] + fn test_hasher_interface_no_panic_and_same_result() { + let digest_256 = Hasher::Blake2b256.digest(b"Test").unwrap(); + let digest_384 = Hasher::Blake2b384.digest(b"Test").unwrap(); + let digest_512 = Hasher::Blake2b512.digest(b"Test").unwrap(); + + assert_eq!(digest_256, Hasher::Blake2b256.digest(b"Test").unwrap()); + assert_eq!(digest_384, Hasher::Blake2b384.digest(b"Test").unwrap()); + assert_eq!(digest_512, Hasher::Blake2b512.digest(b"Test").unwrap()); + + assert_ne!(digest_256, Hasher::Blake2b256.digest(b"Wrong").unwrap()); + assert_ne!(digest_384, Hasher::Blake2b384.digest(b"Wrong").unwrap()); + assert_ne!(digest_512, Hasher::Blake2b512.digest(b"Wrong").unwrap()); + + let _state_256 = Hasher::Blake2b256.init().unwrap(); + let _state_384 = Hasher::Blake2b384.init().unwrap(); + let _state_512 = Hasher::Blake2b512.init().unwrap(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Given some data, digest() should never fail in practice and should + /// produce the same output on a second call. + /// Only panics if data is unreasonably large. + fn prop_hasher_digest_no_panic_and_same_result(data: Vec<u8>) -> bool { + let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap(); + let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap(); + let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + let d256_re = Hasher::Blake2b256.digest(&data[..]).unwrap(); + let d384_re = Hasher::Blake2b384.digest(&data[..]).unwrap(); + let d512_re = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + (d256 == d256_re) && (d384 == d384_re) && (d512 == d512_re) + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Given some data, .digest() should produce the same output as when + /// calling with streaming state. + fn prop_hasher_digest_256_same_as_streaming(data: Vec<u8>) -> bool { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + + let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap(); + + let mut state = Blake2b::new(32).unwrap(); + state.update(&data[..]).unwrap(); + + d256 == state.finalize().unwrap() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Given some data, .digest() should produce the same output as when + /// calling with streaming state. + fn prop_hasher_digest_384_same_as_streaming(data: Vec<u8>) -> bool { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + + let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap(); + + let mut state = Blake2b::new(48).unwrap(); + state.update(&data[..]).unwrap(); + + d384 == state.finalize().unwrap() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Given some data, .digest() should produce the same output as when + /// calling with streaming state. + fn prop_hasher_digest_512_same_as_streaming(data: Vec<u8>) -> bool { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + + let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + let mut state = Blake2b::new(64).unwrap(); + state.update(&data[..]).unwrap(); + + d512 == state.finalize().unwrap() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Given two different data, .digest() should never produce the + /// same output. + fn prop_hasher_digest_diff_input_diff_result(data: Vec<u8>) -> bool { + let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap(); + let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap(); + let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + let d256_re = Hasher::Blake2b256.digest(b"Wrong data").unwrap(); + let d384_re = Hasher::Blake2b384.digest(b"Wrong data").unwrap(); + let d512_re = Hasher::Blake2b512.digest(b"Wrong data").unwrap(); + + (d256 != d256_re) && (d384 != d384_re) && (d512 != d512_re) + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// .init() should never fail. + fn prop_hasher_init_no_panic() -> bool { + let _d256 = Hasher::Blake2b256.init().unwrap(); + let _d384 = Hasher::Blake2b384.init().unwrap(); + let _d512 = Hasher::Blake2b512.init().unwrap(); + + true + } + } + + mod test_new { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + + #[test] + fn test_init_size() { + assert!(Blake2b::new(0).is_err()); + assert!(Blake2b::new(65).is_err()); + assert!(Blake2b::new(1).is_ok()); + assert!(Blake2b::new(64).is_ok()); + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool { + let mut hasher_a = Blake2b::new(64).unwrap(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} diff --git a/vendor/orion/src/hazardous/hash/blake2/mod.rs b/vendor/orion/src/hazardous/hash/blake2/mod.rs new file mode 100644 index 0000000..c64ab5b --- /dev/null +++ b/vendor/orion/src/hazardous/hash/blake2/mod.rs @@ -0,0 +1,442 @@ +// MIT License + +// Copyright (c) 2018-2023 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. + +/// BLAKE2b as specified in the [RFC 7693](https://tools.ietf.org/html/rfc7693). +pub mod blake2b; + +pub(crate) mod blake2b_core { + + /// The blocksize for the hash function BLAKE2b. + pub(crate) const BLAKE2B_BLOCKSIZE: usize = 128; + /// The maximum key size for the hash function BLAKE2b when used in keyed mode. + pub(crate) const BLAKE2B_KEYSIZE: usize = 64; + /// The maximum output size for the hash function BLAKE2b. + pub(crate) const BLAKE2B_OUTSIZE: usize = 64; + + use crate::errors::UnknownCryptoError; + use crate::util::endianness::load_u64_into_le; + use crate::util::u64x4::U64x4; + + #[allow(clippy::unreadable_literal)] + /// The BLAKE2b initialization vector as defined in the RFC 7693. + pub(crate) const IV: [U64x4; 2] = [ + U64x4( + 0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + ), + U64x4( + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179, + ), + ]; + + /// BLAKE2b SIGMA as defined in the RFC 7693. + const SIGMA: [[usize; 16]; 12] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + ]; + + /// Quarter round on the BLAKE2b internal matrix. + macro_rules! QROUND { + ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $s_idx:expr, $rconst1:expr, $rconst2:expr) => { + $v0 = $v0.wrapping_add($v1).wrapping_add($s_idx); + $v3 = ($v3 ^ $v0).rotate_right($rconst1); + $v2 = $v2.wrapping_add($v3); + $v1 = ($v1 ^ $v2).rotate_right($rconst2); + }; + } + + /// Perform a single round based on a message schedule selection. + macro_rules! ROUND { + ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $s_idx:expr, $m:expr) => { + let s_indexed = U64x4($m[$s_idx[0]], $m[$s_idx[2]], $m[$s_idx[4]], $m[$s_idx[6]]); + QROUND!($v0, $v1, $v2, $v3, s_indexed, 32, 24); + let s_indexed = U64x4($m[$s_idx[1]], $m[$s_idx[3]], $m[$s_idx[5]], $m[$s_idx[7]]); + QROUND!($v0, $v1, $v2, $v3, s_indexed, 16, 63); + + // Shuffle + $v1 = $v1.shl_1(); + $v2 = $v2.shl_2(); + $v3 = $v3.shl_3(); + + let s_indexed = U64x4( + $m[$s_idx[8]], + $m[$s_idx[10]], + $m[$s_idx[12]], + $m[$s_idx[14]], + ); + QROUND!($v0, $v1, $v2, $v3, s_indexed, 32, 24); + let s_indexed = U64x4( + $m[$s_idx[9]], + $m[$s_idx[11]], + $m[$s_idx[13]], + $m[$s_idx[15]], + ); + QROUND!($v0, $v1, $v2, $v3, s_indexed, 16, 63); + + // Unshuffle + $v1 = $v1.shl_3(); + $v2 = $v2.shl_2(); + $v3 = $v3.shl_1(); + }; + } + + #[derive(Clone)] + /// BLAKE2b streaming state. + pub(crate) struct State { + pub(crate) init_state: [U64x4; 2], + pub(crate) internal_state: [U64x4; 2], + pub(crate) buffer: [u8; BLAKE2B_BLOCKSIZE], + pub(crate) leftover: usize, + pub(crate) t: [u64; 2], + pub(crate) f: [u64; 2], + pub(crate) is_finalized: bool, + pub(crate) is_keyed: bool, + pub(crate) size: usize, + } + + impl Drop for State { + fn drop(&mut self) { + use zeroize::Zeroize; + self.init_state.iter_mut().zeroize(); + self.internal_state.iter_mut().zeroize(); + self.buffer.zeroize(); + } + } + + impl core::fmt::Debug for State { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "State {{ init_state: [***OMITTED***], internal_state: [***OMITTED***], buffer: \ + [***OMITTED***], leftover: {:?}, t: {:?}, f: {:?}, is_finalized: {:?}, is_keyed: \ + {:?}, size: {:?} }}", + self.leftover, self.t, self.f, self.is_finalized, self.is_keyed, self.size + ) + } + } + + impl State { + /// Increment the internal states offset value `t`. + pub(crate) fn _increment_offset(&mut self, value: u64) { + let (res, was_overflow) = self.t[0].overflowing_add(value); + self.t[0] = res; + if was_overflow { + // If this panics size limit is reached. + self.t[1] = self.t[1].checked_add(1).unwrap(); + } + } + + /// The compression function f. + pub(crate) fn _compress_f(&mut self, data: Option<&[u8]>) { + let mut m_vec = [0u64; 16]; + match data { + Some(bytes) => { + debug_assert!(bytes.len() == BLAKE2B_BLOCKSIZE); + load_u64_into_le(bytes, &mut m_vec); + } + None => load_u64_into_le(&self.buffer, &mut m_vec), + } + + let mut v0 = self.internal_state[0]; + let mut v1 = self.internal_state[1]; + let mut v2 = IV[0]; + let mut v3 = U64x4( + self.t[0] ^ IV[1].0, + self.t[1] ^ IV[1].1, + self.f[0] ^ IV[1].2, + self.f[1] ^ IV[1].3, + ); + + ROUND!(v0, v1, v2, v3, SIGMA[0], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[1], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[2], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[3], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[4], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[5], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[6], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[7], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[8], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[9], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[10], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[11], m_vec); + + self.internal_state[0] ^= v0 ^ v2; + self.internal_state[1] ^= v1 ^ v3; + } + + #[allow(clippy::unreadable_literal)] + /// Initialize a `State` struct with a given size a key and optional key. + /// An empty `secret_key` equals non-MAC mode. + pub(crate) fn _new(sk: &[u8], size: usize) -> Result<Self, UnknownCryptoError> { + if !(1..=BLAKE2B_OUTSIZE).contains(&size) { + return Err(UnknownCryptoError); + } + let is_keyed = match sk.len() { + 0 => false, + 1..=BLAKE2B_KEYSIZE => true, + _ => return Err(UnknownCryptoError), + }; + + let mut context = Self { + init_state: [U64x4::default(); 2], + internal_state: IV, + buffer: [0u8; BLAKE2B_BLOCKSIZE], + leftover: 0, + t: [0u64; 2], + f: [0u64; 2], + is_finalized: false, + is_keyed, + size, + }; + + if is_keyed { + context.is_keyed = true; + let klen = sk.len(); + context.internal_state[0].0 ^= 0x01010000 ^ ((klen as u64) << 8) ^ (size as u64); + context.init_state.copy_from_slice(&context.internal_state); + context._update(sk)?; + // The state needs updating with the secret key padded to blocksize length + let pad = [0u8; BLAKE2B_BLOCKSIZE]; + let rem = BLAKE2B_BLOCKSIZE - klen; + context._update(pad[..rem].as_ref())?; + } else { + context.internal_state[0].0 ^= 0x01010000 ^ (size as u64); + context.init_state.copy_from_slice(&context.internal_state); + } + + Ok(context) + } + + /// Reset to `_new()` state. + pub(crate) fn _reset(&mut self, sk: &[u8]) -> Result<(), UnknownCryptoError> { + // Disallow re-setting without a key if initialized with one and vice versa + match (sk.len(), self.is_keyed) { + // new with key, reset with none + (0, true) => return Err(UnknownCryptoError), + (0, false) => (), + // reset with key, new with none + (1..=BLAKE2B_KEYSIZE, false) => return Err(UnknownCryptoError), + (1..=BLAKE2B_KEYSIZE, true) => (), + (_, _) => return Err(UnknownCryptoError), + } + + self.internal_state.copy_from_slice(&self.init_state); + self.buffer = [0u8; BLAKE2B_BLOCKSIZE]; + self.leftover = 0; + self.t = [0u64; 2]; + self.f = [0u64; 2]; + self.is_finalized = false; + + if self.is_keyed { + self._update(sk)?; + // The state needs updating with the secret key padded to blocksize length + let pad = [0u8; BLAKE2B_BLOCKSIZE]; + let rem = BLAKE2B_BLOCKSIZE - sk.len(); + self._update(pad[..rem].as_ref())?; + } + + Ok(()) + } + + /// Update state with `data`. This can be called multiple times. + pub(crate) 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 <= BLAKE2B_BLOCKSIZE); + + let fill = BLAKE2B_BLOCKSIZE - self.leftover; + + if bytes.len() <= fill { + self.buffer[self.leftover..(self.leftover + bytes.len())] + .copy_from_slice(bytes); + self.leftover += bytes.len(); + return Ok(()); + } + + self.buffer[self.leftover..(self.leftover + fill)].copy_from_slice(&bytes[..fill]); + self._increment_offset(BLAKE2B_BLOCKSIZE as u64); + self._compress_f(None); + self.leftover = 0; + bytes = &bytes[fill..]; + } + + while bytes.len() > BLAKE2B_BLOCKSIZE { + self._increment_offset(BLAKE2B_BLOCKSIZE as u64); + self._compress_f(Some(bytes[..BLAKE2B_BLOCKSIZE].as_ref())); + bytes = &bytes[BLAKE2B_BLOCKSIZE..]; + } + + if !bytes.is_empty() { + debug_assert!(self.leftover == 0); + self.buffer[..bytes.len()].copy_from_slice(bytes); + self.leftover += bytes.len(); + } + + Ok(()) + } + + /// Finalize the hash and put the final digest into `dest`. + /// NOTE: Writes the full hash (as if `self.size == BLAKE2B_OUTSIZE`) into `dest`. Must be truncated + /// to `self.size` later. + pub(crate) fn _finalize( + &mut self, + dest: &mut [u8; BLAKE2B_OUTSIZE], + ) -> Result<(), UnknownCryptoError> { + debug_assert!(self.size <= BLAKE2B_OUTSIZE); + if self.is_finalized { + return Err(UnknownCryptoError); + } + + self.is_finalized = true; + + let in_buffer_len = self.leftover; + self._increment_offset(in_buffer_len as u64); + // Mark that it is the last block of data to be processed + self.f[0] = !0; + + for leftover_block in self.buffer.iter_mut().skip(in_buffer_len) { + *leftover_block = 0; + } + self._compress_f(None); + + self.internal_state[0].store_into_le(dest[..32].as_mut()); + self.internal_state[1].store_into_le(dest[32..].as_mut()); + + Ok(()) + } + } + + #[cfg(test)] + pub(crate) fn compare_blake2b_states(state_1: &State, state_2: &State) { + assert!(state_1.init_state == state_2.init_state); + assert!(state_1.internal_state == state_2.internal_state); + assert_eq!(state_1.buffer[..], state_2.buffer[..]); + assert_eq!(state_1.leftover, state_2.leftover); + assert_eq!(state_1.t, state_2.t); + assert_eq!(state_1.f, state_2.f); + assert_eq!(state_1.is_finalized, state_2.is_finalized); + assert_eq!(state_1.is_keyed, state_2.is_keyed); + assert_eq!(state_1.size, state_2.size); + } +} + +#[cfg(test)] +mod private { + use super::blake2b_core::State; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = State::_new(&[], 64).unwrap(); + let debug = format!("{:?}", initial_state); + let expected = "State { init_state: [***OMITTED***], internal_state: [***OMITTED***], buffer: [***OMITTED***], leftover: 0, t: [0, 0], f: [0, 0], is_finalized: false, is_keyed: false, size: 64 }"; + assert_eq!(debug, expected); + } + + #[test] + fn test_switching_keyed_modes_fails() { + let mut tmp = [0u8; 64]; + + let mut state_keyed = State::_new(&[0u8; 64], 64).unwrap(); + state_keyed._update(b"Tests").unwrap(); + state_keyed._finalize(&mut tmp).unwrap(); + assert!(state_keyed._reset(&[]).is_err()); + assert!(state_keyed._reset(&[0u8; 64]).is_ok()); + + let mut state = State::_new(&[], 64).unwrap(); + state._update(b"Tests").unwrap(); + state_keyed._finalize(&mut tmp).unwrap(); + assert!(state._reset(&[0u8; 64]).is_err()); + assert!(state._reset(&[]).is_ok()); + } + + mod test_increment_offset { + use crate::hazardous::hash::blake2::blake2b_core::{State, BLAKE2B_BLOCKSIZE, IV}; + use crate::util::u64x4::U64x4; + + #[test] + fn test_offset_increase_values() { + let mut context = State { + init_state: [U64x4::default(); 2], + internal_state: IV, + buffer: [0u8; BLAKE2B_BLOCKSIZE], + leftover: 0, + t: [0u64; 2], + f: [0u64; 2], + is_finalized: false, + is_keyed: false, + size: 1, + }; + + context._increment_offset(1); + assert_eq!(context.t, [1u64, 0u64]); + context._increment_offset(17); + assert_eq!(context.t, [18u64, 0u64]); + context._increment_offset(12); + assert_eq!(context.t, [30u64, 0u64]); + // Overflow + context._increment_offset(u64::MAX); + assert_eq!(context.t, [29u64, 1u64]); + } + + #[test] + #[should_panic] + fn test_panic_on_second_overflow() { + let mut context = State { + init_state: [U64x4::default(); 2], + internal_state: IV, + buffer: [0u8; BLAKE2B_BLOCKSIZE], + leftover: 0, + t: [1u64, u64::MAX], + f: [0u64; 2], + is_finalized: false, + is_keyed: false, + size: 1, + }; + + context._increment_offset(u64::MAX); + } + } +} diff --git a/vendor/orion/src/hazardous/hash/mod.rs b/vendor/orion/src/hazardous/hash/mod.rs new file mode 100644 index 0000000..01b04c2 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/mod.rs @@ -0,0 +1,30 @@ +// MIT License + +// Copyright (c) 2018-2023 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. + +/// SHA2 as specified in the [FIPS PUB 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf). +pub mod sha2; + +/// SHA3 as specified in the [FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf). +pub mod sha3; + +/// BLAKE2 hash functions. +pub mod blake2; diff --git a/vendor/orion/src/hazardous/hash/sha2/mod.rs b/vendor/orion/src/hazardous/hash/sha2/mod.rs new file mode 100644 index 0000000..cbdbb9a --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha2/mod.rs @@ -0,0 +1,1062 @@ +// MIT License + +// Copyright (c) 2020-2023 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. + +/// SHA256 as specified in the [FIPS PUB 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf). +pub mod sha256; + +/// SHA384 as specified in the [FIPS PUB 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf). +pub mod sha384; + +/// SHA512 as specified in the [FIPS PUB 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf). +pub mod sha512; + +pub(crate) mod sha2_core { + use crate::errors::UnknownCryptoError; + use core::fmt::Debug; + use core::marker::PhantomData; + use core::ops::*; + use zeroize::Zeroize; + + /// Word used within the SHA2 internal state. + pub(crate) trait Word: + Sized + + BitOr<Output = Self> + + BitAnd<Output = Self> + + BitXor<Output = Self> + + Shr<Self> + + Default + + Div<Output = Self> + + From<usize> + + Copy + + Debug + + PartialEq<Self> + + Zeroize + { + const MAX: Self; + + fn wrapping_add(&self, rhs: Self) -> Self; + + fn overflowing_add(&self, rhs: Self) -> (Self, bool); + + fn checked_add(&self, rhs: Self) -> Option<Self>; + + fn checked_shl(&self, rhs: u32) -> Option<Self>; + + fn rotate_right(&self, rhs: u32) -> Self; + + fn one() -> Self; + + fn size_of() -> usize; + + fn as_be(&self, dest: &mut [u8]); + + fn from_be(src: &[u8]) -> Self; + + #[allow(clippy::wrong_self_convention)] + fn as_be_bytes(src: &[Self], dest: &mut [u8]); + + fn from_be_bytes(src: &[u8], dest: &mut [Self]); + + #[cfg(any(debug_assertions, test))] + fn less_than_or_equal(&self, rhs: Self) -> bool; + } + + /// Trait to define a specific SHA2 variant. + pub(crate) trait Variant<W: Word, const N_CONSTS: usize>: Clone { + /// The constants as defined in FIPS 180-4. + const K: [W; N_CONSTS]; + + /// The initial hash value H(0) as defined in FIPS 180-4. + const H0: [W; 8]; + + // Because it's currently not possible to use type parameters + // in const expressions (see #![feature(const_evaluatable_checked)]), + // we can't have this trait define the blocksize or output size + // of the hash function, since array-sizes are defined by these. + // This can be accomplished once full const-generics support lands, + // and should remove the need for the const parameters in the state struct. + + fn big_sigma_0(x: W) -> W; + + fn big_sigma_1(x: W) -> W; + + fn small_sigma_0(x: W) -> W; + + fn small_sigma_1(x: W) -> W; + } + + /// The Ch function as specified in FIPS 180-4 section 4.1.3. + fn ch<W: Word>(x: W, y: W, z: W) -> W { + z ^ (x & (y ^ z)) + } + + /// The Maj function as specified in FIPS 180-4 section 4.1.3. + fn maj<W: Word>(x: W, y: W, z: W) -> W { + (x & y) | (z & (x | y)) + } + + #[derive(Clone)] + /// Core SHA2 state. + pub(crate) struct State< + W, + T, + const BLOCKSIZE: usize, + const OUTSIZE: usize, + const N_CONSTS: usize, + > + where + W: Word, + T: Variant<W, { N_CONSTS }>, + { + _variant: PhantomData<T>, + pub(crate) working_state: [W; 8], + pub(crate) buffer: [u8; BLOCKSIZE], + pub(crate) leftover: usize, + pub(crate) message_len: [W; 2], + pub(crate) is_finalized: bool, + } + + impl< + W: Word, + T: Variant<W, { N_CONSTS }>, + const BLOCKSIZE: usize, + const OUTSIZE: usize, + const N_CONSTS: usize, + > Drop for State<W, T, BLOCKSIZE, OUTSIZE, N_CONSTS> + { + fn drop(&mut self) { + self.working_state.iter_mut().zeroize(); + self.buffer.iter_mut().zeroize(); + self.message_len.iter_mut().zeroize(); + self.leftover.zeroize(); + self.is_finalized.zeroize(); + } + } + + impl< + W: Word, + T: Variant<W, { N_CONSTS }>, + const BLOCKSIZE: usize, + const OUTSIZE: usize, + const N_CONSTS: usize, + > Debug for State<W, T, BLOCKSIZE, OUTSIZE, N_CONSTS> + { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "State {{ working_state: [***OMITTED***], buffer: [***OMITTED***], leftover: {:?}, \ + message_len: {:?}, is_finalized: {:?} }}", + self.leftover, self.message_len, self.is_finalized + ) + } + } + + impl< + W: Word, + T: Variant<W, { N_CONSTS }>, + const BLOCKSIZE: usize, + const OUTSIZE: usize, + const N_CONSTS: usize, + > State<W, T, BLOCKSIZE, OUTSIZE, N_CONSTS> + { + /// Increment the message length during processing of data. + pub(crate) fn increment_mlen(&mut self, length: &W) { + // The checked shift checks that the right-hand side is a legal shift. + // The result can still overflow if length > $primitive::MAX / 8. + // Should be impossible for a user to trigger, because update() processes + // in SHA(256/384/512)_BLOCKSIZE chunks. + #[cfg(any(debug_assertions, test))] + debug_assert!(length.less_than_or_equal(W::MAX / W::from(8))); + + // left-shift to get bit-sized representation of length + // using .unwrap() because it should not panic in practice + let len = length.checked_shl(3).unwrap(); + let (res, was_overflow) = self.message_len[1].overflowing_add(len); + self.message_len[1] = res; + + if was_overflow { + // If this panics size limit is reached. + self.message_len[0] = self.message_len[0].checked_add(W::one()).unwrap(); + } + } + + #[allow(clippy::many_single_char_names)] + #[allow(clippy::too_many_arguments)] + /// Message compression adopted from [mbed + /// TLS](https://github.com/ARMmbed/mbedtls/blob/master/library/sha512.c). + pub(crate) fn compress( + a: W, + b: W, + c: W, + d: &mut W, + e: W, + f: W, + g: W, + h: &mut W, + x: W, + ki: W, + ) { + let temp1 = h + .wrapping_add(T::big_sigma_1(e)) + .wrapping_add(ch(e, f, g)) + .wrapping_add(ki) + .wrapping_add(x); + + let temp2 = T::big_sigma_0(a).wrapping_add(maj(a, b, c)); + + *d = d.wrapping_add(temp1); + *h = temp1.wrapping_add(temp2); + } + + #[rustfmt::skip] + #[allow(clippy::many_single_char_names)] + /// Process data in `self.buffer` or optionally `data`. + pub(crate) fn process(&mut self, data: Option<&[u8]>) { + let mut w = [W::default(); N_CONSTS]; + // If `data.is_none()` then we want to process leftover data within `self.buffer`. + match data { + Some(bytes) => { + debug_assert_eq!(bytes.len(), BLOCKSIZE); + W::from_be_bytes(bytes, &mut w[..16]); + } + None => W::from_be_bytes(&self.buffer, &mut w[..16]), + } + + for t in 16..T::K.len() { + w[t] = T::small_sigma_1(w[t - 2]) + .wrapping_add(w[t - 7]) + .wrapping_add(T::small_sigma_0(w[t - 15])) + .wrapping_add(w[t - 16]); + } + + let mut a = self.working_state[0]; + let mut b = self.working_state[1]; + let mut c = self.working_state[2]; + let mut d = self.working_state[3]; + let mut e = self.working_state[4]; + let mut f = self.working_state[5]; + let mut g = self.working_state[6]; + let mut h = self.working_state[7]; + + let mut t = 0; + while t < T::K.len() { + Self::compress(a, b, c, &mut d, e, f, g, &mut h, w[t], T::K[t]); t += 1; + Self::compress(h, a, b, &mut c, d, e, f, &mut g, w[t], T::K[t]); t += 1; + Self::compress(g, h, a, &mut b, c, d, e, &mut f, w[t], T::K[t]); t += 1; + Self::compress(f, g, h, &mut a, b, c, d, &mut e, w[t], T::K[t]); t += 1; + Self::compress(e, f, g, &mut h, a, b, c, &mut d, w[t], T::K[t]); t += 1; + Self::compress(d, e, f, &mut g, h, a, b, &mut c, w[t], T::K[t]); t += 1; + Self::compress(c, d, e, &mut f, g, h, a, &mut b, w[t], T::K[t]); t += 1; + Self::compress(b, c, d, &mut e, f, g, h, &mut a, w[t], T::K[t]); t += 1; + } + + self.working_state[0] = self.working_state[0].wrapping_add(a); + self.working_state[1] = self.working_state[1].wrapping_add(b); + self.working_state[2] = self.working_state[2].wrapping_add(c); + self.working_state[3] = self.working_state[3].wrapping_add(d); + self.working_state[4] = self.working_state[4].wrapping_add(e); + self.working_state[5] = self.working_state[5].wrapping_add(f); + self.working_state[6] = self.working_state[6].wrapping_add(g); + self.working_state[7] = self.working_state[7].wrapping_add(h); + } + + /// Initialize a new state. + pub(crate) fn _new() -> Self { + Self { + _variant: PhantomData::<T>, + working_state: T::H0, + buffer: [0u8; BLOCKSIZE], + leftover: 0, + message_len: [W::default(); 2], + is_finalized: false, + } + } + + /// Reset to `new()` state. + pub(crate) fn _reset(&mut self) { + self.working_state = T::H0; + self.buffer = [0u8; BLOCKSIZE]; + self.leftover = 0; + self.message_len = [W::default(); 2]; + self.is_finalized = false; + } + + /// Update state with `data`. This can be called multiple times. + pub(crate) 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 <= BLOCKSIZE); + + let mut want = 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; + self.increment_mlen(&W::from(want)); + + if self.leftover < BLOCKSIZE { + return Ok(()); + } + + self.process(None); + self.leftover = 0; + } + + while bytes.len() >= BLOCKSIZE { + self.process(Some(bytes[..BLOCKSIZE].as_ref())); + self.increment_mlen(&W::from(BLOCKSIZE)); + bytes = &bytes[BLOCKSIZE..]; + } + + if !bytes.is_empty() { + debug_assert_eq!(self.leftover, 0); + self.buffer[..bytes.len()].copy_from_slice(bytes); + self.leftover = bytes.len(); + self.increment_mlen(&W::from(bytes.len())); + } + + Ok(()) + } + + /// Finalize the hash and put the final digest into `dest`. + pub(crate) fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + // NOTE: We need to support less than OUTSIZE in HKDF through HMAC. + // debug_assert_eq!(dest.len(), OUTSIZE); + if self.is_finalized { + return Err(UnknownCryptoError); + } + + self.is_finalized = true; + // self.leftover should not be greater than SHA(256/384/512)_BLOCKSIZE + // as that would have been processed in the update call + debug_assert!(self.leftover < BLOCKSIZE); + self.buffer[self.leftover] = 0x80; + self.leftover += 1; + + for itm in self.buffer.iter_mut().skip(self.leftover) { + *itm = 0; + } + + let lenpad = W::size_of(); + // Check for available space for length padding + if (BLOCKSIZE - self.leftover) < lenpad * 2 { + self.process(None); + for itm in self.buffer.iter_mut().take(self.leftover) { + *itm = 0; + } + } + + self.message_len[0] + .as_be(&mut self.buffer[BLOCKSIZE - (lenpad * 2)..BLOCKSIZE - lenpad]); + self.message_len[1].as_be(&mut self.buffer[BLOCKSIZE - lenpad..BLOCKSIZE]); + self.process(None); + + let to_use = OUTSIZE / W::size_of(); + W::as_be_bytes(&self.working_state[..to_use], &mut dest[..OUTSIZE]); + + Ok(()) + } + + #[cfg(test)] + /// Compare two Sha2 state objects to check if their fields + /// are the same. + pub(crate) fn compare_state_to_other(&self, other: &Self) { + for idx in 0..8 { + assert_eq!(self.working_state[idx], other.working_state[idx]); + } + assert_eq!(self.buffer, other.buffer); + assert_eq!(self.leftover, other.leftover); + assert_eq!(self.message_len[0], other.message_len[0]); + assert_eq!(self.message_len[1], other.message_len[1]); + assert_eq!(self.is_finalized, other.is_finalized); + } + } +} + +pub(crate) mod w32 { + use core::convert::{From, TryFrom, TryInto}; + use core::ops::*; + use zeroize::Zeroize; + + #[derive(Debug, PartialEq, Copy, Clone, Default)] + pub(crate) struct WordU32(pub(crate) u32); + + impl Zeroize for WordU32 { + fn zeroize(&mut self) { + self.0.zeroize(); + } + } + + impl BitOr for WordU32 { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + + impl BitAnd for WordU32 { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } + } + + impl BitXor for WordU32 { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self::Output { + Self(self.0 ^ rhs.0) + } + } + + impl From<usize> for WordU32 { + fn from(value: usize) -> Self { + // NOTE: Should never panic + Self(u32::try_from(value).unwrap()) + } + } + + impl From<u32> for WordU32 { + fn from(value: u32) -> Self { + Self(value) + } + } + + impl Div<Self> for WordU32 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0 / rhs.0) + } + } + + impl Shr<WordU32> for WordU32 { + type Output = Self; + + fn shr(self, Self(rhs): Self) -> Self::Output { + let Self(lhs) = self; + Self(lhs >> rhs) + } + } + + impl super::sha2_core::Word for WordU32 { + const MAX: Self = Self(u32::MAX); + + #[inline] + fn wrapping_add(&self, rhs: Self) -> Self { + Self(self.0.wrapping_add(rhs.0)) + } + + #[inline] + fn overflowing_add(&self, rhs: Self) -> (Self, bool) { + let (res, did_overflow) = self.0.overflowing_add(rhs.0); + + (Self(res), did_overflow) + } + + #[inline] + fn checked_add(&self, rhs: Self) -> Option<Self> { + self.0.checked_add(rhs.0).map(Self) + } + + #[inline] + fn checked_shl(&self, rhs: u32) -> Option<Self> { + self.0.checked_shl(rhs).map(Self) + } + + #[inline] + fn rotate_right(&self, rhs: u32) -> Self { + Self(self.0.rotate_right(rhs)) + } + + #[inline] + fn one() -> Self { + Self(1u32) + } + + #[inline] + fn size_of() -> usize { + core::mem::size_of::<u32>() + } + + #[inline] + fn as_be(&self, dest: &mut [u8]) { + debug_assert_eq!(dest.len(), Self::size_of()); + dest.copy_from_slice(&self.0.to_be_bytes()); + } + + #[inline] + fn from_be(src: &[u8]) -> Self { + Self(u32::from_be_bytes(src.try_into().unwrap())) + } + + #[inline] + fn as_be_bytes(src: &[Self], dest: &mut [u8]) { + debug_assert_eq!(dest.len(), src.len() * Self::size_of()); + for (src_elem, dst_chunk) in src.iter().zip(dest.chunks_exact_mut(Self::size_of())) { + src_elem.as_be(dst_chunk); + } + } + + #[inline] + fn from_be_bytes(src: &[u8], dest: &mut [Self]) { + debug_assert_eq!(dest.len(), src.len() / Self::size_of()); + for (src_chunk, dst_elem) in src.chunks_exact(Self::size_of()).zip(dest.iter_mut()) { + *dst_elem = Self::from_be(src_chunk); + } + } + + #[cfg(any(debug_assertions, test))] + fn less_than_or_equal(&self, rhs: Self) -> bool { + self.0 <= rhs.0 + } + } +} + +pub(crate) mod w64 { + use core::convert::{From, TryFrom, TryInto}; + use core::ops::*; + use zeroize::Zeroize; + + #[derive(Debug, PartialEq, Copy, Clone, Default)] + pub(crate) struct WordU64(pub(crate) u64); + + impl Zeroize for WordU64 { + fn zeroize(&mut self) { + self.0.zeroize(); + } + } + + impl BitOr for WordU64 { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + + impl BitAnd for WordU64 { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } + } + + impl BitXor for WordU64 { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self::Output { + Self(self.0 ^ rhs.0) + } + } + + impl From<usize> for WordU64 { + fn from(value: usize) -> Self { + // NOTE: Should never panic + Self(u64::try_from(value).unwrap()) + } + } + + impl From<u64> for WordU64 { + fn from(value: u64) -> Self { + Self(value) + } + } + + impl Div<Self> for WordU64 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0 / rhs.0) + } + } + + impl Shr<WordU64> for WordU64 { + type Output = Self; + + fn shr(self, Self(rhs): Self) -> Self::Output { + let Self(lhs) = self; + Self(lhs >> rhs) + } + } + + impl super::sha2_core::Word for WordU64 { + const MAX: Self = Self(u64::MAX); + + #[inline] + fn wrapping_add(&self, rhs: Self) -> Self { + Self(self.0.wrapping_add(rhs.0)) + } + + #[inline] + fn overflowing_add(&self, rhs: Self) -> (Self, bool) { + let (res, did_overflow) = self.0.overflowing_add(rhs.0); + + (Self(res), did_overflow) + } + + #[inline] + fn checked_add(&self, rhs: Self) -> Option<Self> { + self.0.checked_add(rhs.0).map(Self) + } + + #[inline] + fn checked_shl(&self, rhs: u32) -> Option<Self> { + self.0.checked_shl(rhs).map(Self) + } + + #[inline] + fn rotate_right(&self, rhs: u32) -> Self { + Self(self.0.rotate_right(rhs)) + } + + #[inline] + fn one() -> Self { + Self(1u64) + } + + #[inline] + fn size_of() -> usize { + core::mem::size_of::<u64>() + } + + #[inline] + fn as_be(&self, dest: &mut [u8]) { + debug_assert_eq!(dest.len(), Self::size_of()); + dest.copy_from_slice(&self.0.to_be_bytes()); + } + + #[inline] + fn from_be(src: &[u8]) -> Self { + Self(u64::from_be_bytes(src.try_into().unwrap())) + } + + #[inline] + fn as_be_bytes(src: &[Self], dest: &mut [u8]) { + debug_assert_eq!(dest.len(), src.len() * Self::size_of()); + for (src_elem, dst_chunk) in src.iter().zip(dest.chunks_exact_mut(Self::size_of())) { + src_elem.as_be(dst_chunk); + } + } + + #[inline] + fn from_be_bytes(src: &[u8], dest: &mut [Self]) { + debug_assert_eq!(dest.len(), src.len() / Self::size_of()); + for (src_chunk, dst_elem) in src.chunks_exact(Self::size_of()).zip(dest.iter_mut()) { + *dst_elem = Self::from_be(src_chunk); + } + } + + #[cfg(any(debug_assertions, test))] + fn less_than_or_equal(&self, rhs: Self) -> bool { + self.0 <= rhs.0 + } + } +} + +#[cfg(test)] +mod test_word { + use super::sha2_core::Word; + use super::w32::WordU32; + use super::w64::WordU64; + + #[test] + #[should_panic] + #[cfg(target_pointer_width = "64")] + // We can only test this on 64-bit platforms. + // On 32-bit platforms, due to the on-by-default #[deny(arithmetic_overflow)] + // this won't compile because of `(u32::MAX as usize) + 1)`, not the from call. + fn w32_panic_on_above_from() { + let _ = WordU32::from((u32::MAX as usize) + 1); + } + + #[test] + #[should_panic] + #[cfg(target_pointer_width = "128")] + // See above note. + fn w64_panic_on_above_from() { + WordU64::from((u64::MAX as usize) + 1); + } + + #[test] + fn equiv_max() { + assert_eq!(WordU32::MAX.0, u32::MAX); + assert_eq!(WordU64::MAX.0, u64::MAX); + } + + #[test] + fn equiv_sizeof() { + assert_eq!(WordU32::size_of(), core::mem::size_of::<u32>()); + assert_eq!(WordU64::size_of(), core::mem::size_of::<u64>()); + } + + #[test] + fn equiv_one() { + assert_eq!(WordU32::one(), WordU32::from(1usize)); + assert_eq!(WordU64::one(), WordU64::from(1usize)); + } + + #[test] + fn equiv_default() { + assert_eq!(WordU32::default().0, u32::default()); + assert_eq!(WordU64::default().0, u64::default()); + } + + #[test] + fn test_results_store_and_load_u32_into_be() { + let input_0: [WordU32; 2] = [WordU32::from(777190791u32), WordU32::from(1465409568u32)]; + let input_1: [WordU32; 4] = [ + WordU32::from(3418616323u32), + WordU32::from(2289579672u32), + WordU32::from(172726903u32), + WordU32::from(1048927929u32), + ]; + let input_2: [WordU32; 6] = [ + WordU32::from(84693101u32), + WordU32::from(443297962u32), + WordU32::from(3962861724u32), + WordU32::from(3081916164u32), + WordU32::from(4167874952u32), + WordU32::from(3982893227u32), + ]; + let input_3: [WordU32; 8] = [ + WordU32::from(2761719494u32), + WordU32::from(242571916u32), + WordU32::from(3097304063u32), + WordU32::from(3924274282u32), + WordU32::from(1553851098u32), + WordU32::from(3673278295u32), + WordU32::from(3531531406u32), + WordU32::from(2347852690u32), + ]; + + let expected_0: [u8; 8] = [46, 82, 253, 135, 87, 88, 96, 32]; + let expected_1: [u8; 16] = [ + 203, 195, 242, 3, 136, 120, 54, 152, 10, 75, 154, 119, 62, 133, 94, 185, + ]; + let expected_2: [u8; 24] = [ + 5, 12, 80, 109, 26, 108, 48, 170, 236, 52, 120, 156, 183, 178, 79, 4, 248, 108, 185, + 136, 237, 102, 32, 171, + ]; + let expected_3: [u8; 32] = [ + 164, 156, 126, 198, 14, 117, 90, 140, 184, 157, 27, 255, 233, 231, 172, 106, 92, 157, + 226, 218, 218, 241, 199, 87, 210, 126, 228, 142, 139, 241, 99, 146, + ]; + + let mut actual_bytes_0 = [0u8; 8]; + let mut actual_bytes_1 = [0u8; 16]; + let mut actual_bytes_2 = [0u8; 24]; + let mut actual_bytes_3 = [0u8; 32]; + + WordU32::as_be_bytes(&input_0, &mut actual_bytes_0); + WordU32::as_be_bytes(&input_1, &mut actual_bytes_1); + WordU32::as_be_bytes(&input_2, &mut actual_bytes_2); + WordU32::as_be_bytes(&input_3, &mut actual_bytes_3); + + assert_eq!(actual_bytes_0, expected_0); + assert_eq!(actual_bytes_1, expected_1); + assert_eq!(actual_bytes_2, expected_2); + assert_eq!(actual_bytes_3, expected_3); + + let mut actual_nums_0 = [WordU32::default(); 2]; + let mut actual_nums_1 = [WordU32::default(); 4]; + let mut actual_nums_2 = [WordU32::default(); 6]; + let mut actual_nums_3 = [WordU32::default(); 8]; + + WordU32::from_be_bytes(&actual_bytes_0, &mut actual_nums_0); + WordU32::from_be_bytes(&actual_bytes_1, &mut actual_nums_1); + WordU32::from_be_bytes(&actual_bytes_2, &mut actual_nums_2); + WordU32::from_be_bytes(&actual_bytes_3, &mut actual_nums_3); + + assert_eq!(actual_nums_0, input_0); + assert_eq!(actual_nums_1, input_1); + assert_eq!(actual_nums_2, input_2); + assert_eq!(actual_nums_3, input_3); + } + + #[test] + fn test_results_store_and_load_u64_into_be() { + let input_0: [WordU64; 2] = [ + WordU64::from(588679683042986719u64), + WordU64::from(14213404201893491922u64), + ]; + let input_1: [WordU64; 4] = [ + WordU64::from(11866671478157678302u64), + WordU64::from(12365793902795026927u64), + WordU64::from(3777757590820648064u64), + WordU64::from(6594491344853184185u64), + ]; + let input_2: [WordU64; 6] = [ + WordU64::from(2101516190274184922u64), + WordU64::from(7904425905466803755u64), + WordU64::from(16590119592260157258u64), + WordU64::from(6043085125584392657u64), + WordU64::from(292831874581513482u64), + WordU64::from(1878340435767862001u64), + ]; + let input_3: [WordU64; 8] = [ + WordU64::from(10720360125345046831u64), + WordU64::from(12576204976780952869u64), + WordU64::from(2183760329755932840u64), + WordU64::from(12806242450747917237u64), + WordU64::from(17861362669514295908u64), + WordU64::from(4901620135335484985u64), + WordU64::from(3014680565865559727u64), + WordU64::from(5106077179490460734u64), + ]; + + let expected_0: [u8; 16] = [ + 8, 43, 105, 13, 130, 68, 74, 223, 197, 64, 39, 208, 214, 231, 244, 210, + ]; + let expected_1: [u8; 32] = [ + 164, 174, 226, 214, 73, 217, 22, 222, 171, 156, 32, 9, 173, 201, 241, 239, 52, 109, 74, + 131, 112, 102, 116, 128, 91, 132, 86, 240, 100, 92, 174, 185, + ]; + let expected_2: [u8; 48] = [ + 29, 42, 21, 215, 59, 6, 102, 218, 109, 178, 41, 123, 72, 190, 134, 43, 230, 59, 241, + 222, 245, 234, 63, 74, 83, 221, 89, 231, 113, 231, 145, 209, 4, 16, 89, 9, 215, 87, + 197, 10, 26, 17, 52, 172, 169, 50, 34, 241, + ]; + let expected_3: [u8; 64] = [ + 148, 198, 94, 188, 47, 116, 33, 47, 174, 135, 167, 203, 119, 135, 69, 37, 30, 78, 70, + 115, 41, 177, 56, 168, 177, 184, 233, 168, 152, 91, 131, 181, 247, 224, 78, 182, 224, + 210, 138, 100, 68, 6, 13, 139, 14, 146, 222, 57, 41, 214, 76, 0, 143, 176, 182, 175, + 70, 220, 110, 36, 63, 65, 228, 62, + ]; + + let mut actual_bytes_0 = [0u8; 16]; + let mut actual_bytes_1 = [0u8; 32]; + let mut actual_bytes_2 = [0u8; 48]; + let mut actual_bytes_3 = [0u8; 64]; + + WordU64::as_be_bytes(&input_0, &mut actual_bytes_0); + WordU64::as_be_bytes(&input_1, &mut actual_bytes_1); + WordU64::as_be_bytes(&input_2, &mut actual_bytes_2); + WordU64::as_be_bytes(&input_3, &mut actual_bytes_3); + + assert_eq!(actual_bytes_0, expected_0); + assert_eq!(actual_bytes_1, expected_1); + assert_eq!(actual_bytes_2.as_ref(), expected_2.as_ref()); + assert_eq!(actual_bytes_3.as_ref(), expected_3.as_ref()); + + let mut actual_nums_0 = [WordU64::default(); 2]; + let mut actual_nums_1 = [WordU64::default(); 4]; + let mut actual_nums_2 = [WordU64::default(); 6]; + let mut actual_nums_3 = [WordU64::default(); 8]; + + WordU64::from_be_bytes(&actual_bytes_0, &mut actual_nums_0); + WordU64::from_be_bytes(&actual_bytes_1, &mut actual_nums_1); + WordU64::from_be_bytes(&actual_bytes_2, &mut actual_nums_2); + WordU64::from_be_bytes(&actual_bytes_3, &mut actual_nums_3); + + assert_eq!(actual_nums_0, input_0); + assert_eq!(actual_nums_1, input_1); + assert_eq!(actual_nums_2, input_2); + assert_eq!(actual_nums_3, input_3); + } + + #[cfg(feature = "safe_api")] + mod proptests { + use super::*; + + #[quickcheck] + #[rustfmt::skip] + fn equiv_from(n: u32, m: u64) -> bool { + // Implicitly assume there's no panic + if WordU32::from(n).0 != n { return false; } + if WordU64::from(m).0 != m { return false; } + + true + } + + #[quickcheck] + #[rustfmt::skip] + fn equiv_ops(n1: u32, n2: u32, m1: u64, m2: u64) -> bool { + // WordU32 + let w32n1 = WordU32::from(n1); + let w32n2 = WordU32::from(n2); + + if (w32n1 | w32n2).0 != n1 | n2 { return false; } + if (w32n1 & w32n2).0 != n1 & n2 { return false; } + if (w32n1 ^ w32n2).0 != n1 ^ n2 { return false; } + // Test only specific values used with Shr (in sigma functions) + if (w32n1 >> WordU32::from(10usize)).0 != n1 >> 10 { return false; } + if (w32n1 >> WordU32::from(3usize)).0 != n1 >> 3 { return false; } + if w32n2.0 != 0 && ((w32n1 / w32n2).0 != n1 / n2) { return false } + + // WordU64 + let w64m1 = WordU64::from(m1); + let w64m2 = WordU64::from(m2); + + if (w64m1 | w64m2).0 != m1 | m2 { return false; } + if (w64m1 & w64m2).0 != m1 & m2 { return false; } + if (w64m1 ^ w64m2).0 != m1 ^ m2 { return false; } + // Test only specific values used with Shr (in sigma functions) + if (w64m1 >> WordU64::from(7usize)).0 != m1 >> 7 { return false; } + if (w64m1 >> WordU64::from(6usize)).0 != m1 >> 6 { return false; } + if w64m2.0 != 0 && ((w64m1 / w64m2).0 != m1 / m2) { return false } + + true + } + + #[quickcheck] + fn equiv_wrapping_add(n1: u32, n2: u32, m1: u64, m2: u64) -> bool { + let w32n1 = WordU32::from(n1); + let w32n2 = WordU32::from(n2); + let ret32 = w32n1.wrapping_add(w32n2).0 == n1.wrapping_add(n2); + + let w64m1 = WordU64::from(m1); + let w64m2 = WordU64::from(m2); + let ret64 = w64m1.wrapping_add(w64m2).0 == m1.wrapping_add(m2); + + ret32 && ret64 + } + + #[quickcheck] + fn equiv_overflowing_add(n1: u32, n2: u32, m1: u64, m2: u64) -> bool { + let w32n1 = WordU32::from(n1); + let w32n2 = WordU32::from(n2); + let ret32: bool = match (w32n1.overflowing_add(w32n2), n1.overflowing_add(n2)) { + ((w32, true), (n, true)) => w32.0 == n, + ((w32, false), (n, false)) => w32.0 == n, + _ => false, + }; + + let w64m1 = WordU64::from(m1); + let w64m2 = WordU64::from(m2); + let ret64: bool = match (w64m1.overflowing_add(w64m2), m1.overflowing_add(m2)) { + ((w64, true), (n, true)) => w64.0 == n, + ((w64, false), (n, false)) => w64.0 == n, + _ => false, + }; + + ret32 && ret64 + } + + #[quickcheck] + fn equiv_checked_add(n1: u32, n2: u32, m1: u64, m2: u64) -> bool { + let w32n1 = WordU32::from(n1); + let w32n2 = WordU32::from(n2); + let ret32: bool = match (w32n1.checked_add(w32n2), n1.checked_add(n2)) { + (Some(w32), Some(n)) => w32.0 == n, + (None, None) => true, + _ => false, + }; + + let w64m1 = WordU64::from(m1); + let w64m2 = WordU64::from(m2); + let ret64: bool = match (w64m1.checked_add(w64m2), m1.checked_add(m2)) { + (Some(w64), Some(n)) => w64.0 == n, + (None, None) => true, + _ => false, + }; + + ret32 && ret64 + } + + #[quickcheck] + fn equiv_checked_shl(n: u32, m: u64, x: u32) -> bool { + let w32n = WordU32::from(n); + let ret32: bool = match (w32n.checked_shl(x), n.checked_shl(x)) { + (Some(w32), Some(n1)) => w32.0 == n1, + (None, None) => true, + _ => false, + }; + + let w64m = WordU64::from(m); + let ret64: bool = match (w64m.checked_shl(x), m.checked_shl(x)) { + (Some(w64), Some(n1)) => w64.0 == n1, + (None, None) => true, + _ => false, + }; + + ret32 && ret64 + } + + #[quickcheck] + #[rustfmt::skip] + fn equiv_rotate_right(n: u32, m: u64, x: u32) -> bool { + let w32n = WordU32::from(n); + let w64m = WordU64::from(m); + + if w32n.rotate_right(x).0 != n.rotate_right(x) { return false; } + if w64m.rotate_right(x).0 != m.rotate_right(x) { return false; } + + true + } + + #[quickcheck] + #[rustfmt::skip] + fn equiv_into_from_be(n: u32, m: u64) -> bool { + + let w32n = WordU32::from(n); + let w64m = WordU64::from(m); + + let mut dest32 = [0u8; core::mem::size_of::<u32>()]; + let mut dest64 = [0u8; core::mem::size_of::<u64>()]; + w32n.as_be(&mut dest32); + w64m.as_be(&mut dest64); + + if dest32 != n.to_be_bytes() { return false; } + if dest64 != m.to_be_bytes() { return false; } + + + if w32n.0 != u32::from_be_bytes(dest32) { return false; } + if w64m.0 != u64::from_be_bytes(dest64) { return false; } + + true + } + + #[cfg(debug_assertions)] + #[quickcheck] + #[rustfmt::skip] + /// Word::less_than_or_equal() is only used for debug_assertions. + fn equiv_less_than_or_equal(n1: u32, n2: u32, m1: u64, m2: u64) -> bool { + let w32n1 = WordU32::from(n1); + let w32n2 = WordU32::from(n2); + let w64m1 = WordU64::from(m1); + let w64m2 = WordU64::from(m2); + + if w32n1.less_than_or_equal(w32n2) != (n1 <= n2) { return false; } + if w64m1.less_than_or_equal(w64m2) != (m1 <= m2) { return false; } + + true + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha2/sha256.rs b/vendor/orion/src/hazardous/hash/sha2/sha256.rs new file mode 100644 index 0000000..720d805 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha2/sha256.rs @@ -0,0 +1,421 @@ +// MIT License + +// Copyright (c) 2020-2023 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: +//! - `data`: The data to be hashed. +//! +//! # 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. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^32-1) __bits__ of data are hashed. +//! +//! # Security: +//! - SHA256 is vulnerable to length extension attacks. +//! +//! # Recommendation: +//! - It is recommended to use [BLAKE2b] when possible. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha2::sha256::Sha256; +//! +//! // Using the streaming interface +//! let mut state = Sha256::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha256::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha256::Sha256::update +//! [`reset()`]: sha256::Sha256::reset +//! [`finalize()`]: sha256::Sha256::finalize +//! [BLAKE2b]: super::blake2::blake2b + +use crate::errors::UnknownCryptoError; + +#[cfg(feature = "safe_api")] +use std::io; + +/// The blocksize for the hash function SHA256. +pub const SHA256_BLOCKSIZE: usize = 64; +/// The output size for the hash function SHA256. +pub const SHA256_OUTSIZE: usize = 32; +/// The number of constants for the hash function SHA256. +const N_CONSTS: usize = 64; + +construct_public! { + /// A type to represent the `Digest` that SHA256 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + (Digest, test_digest, SHA256_OUTSIZE, SHA256_OUTSIZE) +} + +impl_from_trait!(Digest, SHA256_OUTSIZE); + +use super::sha2_core::{State, Variant, Word}; +use super::w32::WordU32; + +#[derive(Clone)] +/// SHA256 streaming state. +pub(crate) struct V256; + +impl Variant<WordU32, N_CONSTS> for V256 { + #[rustfmt::skip] + #[allow(clippy::unreadable_literal)] + /// The SHA256 constants as defined in FIPS 180-4. + const K: [WordU32; N_CONSTS] = [ + WordU32(0x428a2f98), WordU32(0x71374491), WordU32(0xb5c0fbcf), WordU32(0xe9b5dba5), + WordU32(0x3956c25b), WordU32(0x59f111f1), WordU32(0x923f82a4), WordU32(0xab1c5ed5), + WordU32(0xd807aa98), WordU32(0x12835b01), WordU32(0x243185be), WordU32(0x550c7dc3), + WordU32(0x72be5d74), WordU32(0x80deb1fe), WordU32(0x9bdc06a7), WordU32(0xc19bf174), + WordU32(0xe49b69c1), WordU32(0xefbe4786), WordU32(0x0fc19dc6), WordU32(0x240ca1cc), + WordU32(0x2de92c6f), WordU32(0x4a7484aa), WordU32(0x5cb0a9dc), WordU32(0x76f988da), + WordU32(0x983e5152), WordU32(0xa831c66d), WordU32(0xb00327c8), WordU32(0xbf597fc7), + WordU32(0xc6e00bf3), WordU32(0xd5a79147), WordU32(0x06ca6351), WordU32(0x14292967), + WordU32(0x27b70a85), WordU32(0x2e1b2138), WordU32(0x4d2c6dfc), WordU32(0x53380d13), + WordU32(0x650a7354), WordU32(0x766a0abb), WordU32(0x81c2c92e), WordU32(0x92722c85), + WordU32(0xa2bfe8a1), WordU32(0xa81a664b), WordU32(0xc24b8b70), WordU32(0xc76c51a3), + WordU32(0xd192e819), WordU32(0xd6990624), WordU32(0xf40e3585), WordU32(0x106aa070), + WordU32(0x19a4c116), WordU32(0x1e376c08), WordU32(0x2748774c), WordU32(0x34b0bcb5), + WordU32(0x391c0cb3), WordU32(0x4ed8aa4a), WordU32(0x5b9cca4f), WordU32(0x682e6ff3), + WordU32(0x748f82ee), WordU32(0x78a5636f), WordU32(0x84c87814), WordU32(0x8cc70208), + WordU32(0x90befffa), WordU32(0xa4506ceb), WordU32(0xbef9a3f7), WordU32(0xc67178f2), + ]; + + #[rustfmt::skip] + #[allow(clippy::unreadable_literal)] + /// The SHA256 initial hash value H(0) as defined in FIPS 180-4. + const H0: [WordU32; 8] = [ + WordU32(0x6a09e667), WordU32(0xbb67ae85), WordU32(0x3c6ef372), WordU32(0xa54ff53a), + WordU32(0x510e527f), WordU32(0x9b05688c), WordU32(0x1f83d9ab), WordU32(0x5be0cd19), + ]; + + /// The Big Sigma 0 function as specified in FIPS 180-4 section 4.1.2. + fn big_sigma_0(x: WordU32) -> WordU32 { + (x.rotate_right(2)) ^ x.rotate_right(13) ^ x.rotate_right(22) + } + + /// The Big Sigma 1 function as specified in FIPS 180-4 section 4.1.2. + fn big_sigma_1(x: WordU32) -> WordU32 { + (x.rotate_right(6)) ^ x.rotate_right(11) ^ x.rotate_right(25) + } + + /// The Small Sigma 0 function as specified in FIPS 180-4 section 4.1.2. + fn small_sigma_0(x: WordU32) -> WordU32 { + (x.rotate_right(7)) ^ x.rotate_right(18) ^ (x >> WordU32(3)) + } + + /// The Small Sigma 1 function as specified in FIPS 180-4 section 4.1.2. + fn small_sigma_1(x: WordU32) -> WordU32 { + (x.rotate_right(17)) ^ x.rotate_right(19) ^ (x >> WordU32(10)) + } +} + +#[derive(Clone, Debug)] +/// SHA256 streaming state. +pub struct Sha256 { + pub(crate) _state: State<WordU32, V256, SHA256_BLOCKSIZE, SHA256_OUTSIZE, N_CONSTS>, +} + +impl Default for Sha256 { + fn default() -> Self { + Self::new() + } +} + +impl Sha256 { + /// Initialize a `Sha256` struct. + pub fn new() -> Self { + Self { + _state: State::<WordU32, V256, SHA256_BLOCKSIZE, SHA256_OUTSIZE, N_CONSTS>::_new(), + } + } + + /// 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) + } + + /// Finalize the hash and put the final digest into `dest`. + 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 SHA256 digest. + pub fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> { + let mut digest = [0u8; SHA256_OUTSIZE]; + self._finalize_internal(&mut digest)?; + + Ok(Digest::from(digest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Calculate a SHA256 digest of some `data`. + pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx.finalize() + } +} + +impl crate::hazardous::mac::hmac::HmacHashFunction for Sha256 { + /// The blocksize of the hash function. + const _BLOCKSIZE: usize = SHA256_BLOCKSIZE; + + /// The output size of the hash function. + const _OUTSIZE: usize = SHA256_OUTSIZE; + + /// Create a new instance of the hash function. + fn _new() -> Self { + Self::new() + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(data) + } + + /// Finalize the hash and put the final digest into `dest`. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._finalize_internal(dest) + } + + /// Compute a digest of `data` and copy it into `dest`. + fn _digest(data: &[u8], dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx._finalize_internal(dest) + } + + #[cfg(test)] + fn compare_state_to_other(&self, other: &Self) { + self._state.compare_state_to_other(&other._state); + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: hashing from a [`Read`](std::io::Read)er with SHA256. +/// ```rust +/// use orion::{ +/// hazardous::hash::sha2::sha256::{Sha256, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Sha256::new(); +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box<dyn std::error::Error>>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Sha256 { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Sha256::update`](crate::hazardous::hash::sha2::sha256::Sha256::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_default_equals_new() { + let new = Sha256::new(); + let default = Sha256::default(); + new._state.compare_state_to_other(&default._state); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = Sha256::new(); + let debug = format!("{:?}", initial_state); + let expected = "Sha256 { _state: State { working_state: [***OMITTED***], buffer: [***OMITTED***], leftover: 0, message_len: [WordU32(0), WordU32(0)], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + impl TestableStreamingContext<Digest> for Sha256 { + 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<Digest, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> { + Sha256::digest(input) + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual: Digest = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Sha256, state_2: &Sha256) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Sha256 = Sha256::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha256>::new( + initial_state, + 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: Sha256 = Sha256::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha256>::new( + initial_state, + SHA256_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::sha2::sha256::Sha256; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool { + let mut hasher_a = Sha256::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_increment_mlen { + use super::*; + + #[test] + fn test_mlen_increase_values() { + let mut context = Sha256::default(); + + context._state.increment_mlen(&WordU32::from(1u32)); + assert_eq!(context._state.message_len[0], WordU32::from(0u32)); + assert_eq!(context._state.message_len[1], WordU32::from(8u32)); + + context._state.increment_mlen(&WordU32::from(17u32)); + assert_eq!(context._state.message_len[0], WordU32::from(0u32)); + assert_eq!(context._state.message_len[1], WordU32::from(144u32)); + + context._state.increment_mlen(&WordU32::from(12u32)); + assert_eq!(context._state.message_len[0], WordU32::from(0u32)); + assert_eq!(context._state.message_len[1], WordU32::from(240u32)); + + // Overflow + context._state.increment_mlen(&WordU32::from(u32::MAX / 8)); + assert_eq!(context._state.message_len[0], WordU32::from(1u32)); + assert_eq!(context._state.message_len[1], WordU32::from(232u32)); + } + + #[test] + #[should_panic] + fn test_panic_on_second_overflow() { + let mut context = Sha256::default(); + context._state.message_len = [WordU32::MAX, WordU32::from(u32::MAX - 7)]; + // u32::MAX - 7, to leave so that the length represented + // in bites should overflow by exactly one. + context._state.increment_mlen(&WordU32::from(1u32)); + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha2/sha384.rs b/vendor/orion/src/hazardous/hash/sha2/sha384.rs new file mode 100644 index 0000000..c043051 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha2/sha384.rs @@ -0,0 +1,403 @@ +// MIT License + +// Copyright (c) 2020-2023 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: +//! - `data`: The data to be hashed. +//! +//! # 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. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) __bits__ of data are hashed. +//! +//! # Security: +//! - SHA384 is vulnerable to length extension attacks. +//! +//! # Recommendation: +//! - It is recommended to use [BLAKE2b] when possible. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha2::sha384::Sha384; +//! +//! // Using the streaming interface +//! let mut state = Sha384::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha384::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha384::Sha384::update +//! [`reset()`]: sha384::Sha384::reset +//! [`finalize()`]: sha384::Sha384::finalize +//! [BLAKE2b]: super::blake2::blake2b + +use crate::errors::UnknownCryptoError; + +#[cfg(feature = "safe_api")] +use std::io; + +construct_public! { + /// A type to represent the `Digest` that SHA384 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 48 bytes. + (Digest, test_digest, SHA384_OUTSIZE, SHA384_OUTSIZE) +} + +impl_from_trait!(Digest, SHA384_OUTSIZE); + +use super::sha2_core::{State, Variant}; +use super::w64::WordU64; + +/// The blocksize for the hash function SHA384. +pub const SHA384_BLOCKSIZE: usize = 128; +/// The output size for the hash function SHA384. +pub const SHA384_OUTSIZE: usize = 48; +/// The number of constants for the hash function SHA384. +const N_CONSTS: usize = 80; + +#[derive(Clone)] +pub(crate) struct V384; + +impl Variant<WordU64, N_CONSTS> for V384 { + /// The SHA384 constants as defined in FIPS 180-4. + const K: [WordU64; N_CONSTS] = super::sha512::V512::K; + + #[rustfmt::skip] + #[allow(clippy::unreadable_literal)] + /// The SHA384 initial hash value H(0) as defined in FIPS 180-4. + const H0: [WordU64; 8] = [ + WordU64(0xcbbb9d5dc1059ed8), WordU64(0x629a292a367cd507), WordU64(0x9159015a3070dd17), WordU64(0x152fecd8f70e5939), + WordU64(0x67332667ffc00b31), WordU64(0x8eb44a8768581511), WordU64(0xdb0c2e0d64f98fa7), WordU64(0x47b5481dbefa4fa4), + ]; + + /// The Big Sigma 0 function as specified in FIPS 180-4 section 4.1.3. + fn big_sigma_0(x: WordU64) -> WordU64 { + super::sha512::V512::big_sigma_0(x) + } + + /// The Big Sigma 1 function as specified in FIPS 180-4 section 4.1.3. + fn big_sigma_1(x: WordU64) -> WordU64 { + super::sha512::V512::big_sigma_1(x) + } + + /// The Small Sigma 0 function as specified in FIPS 180-4 section 4.1.3. + fn small_sigma_0(x: WordU64) -> WordU64 { + super::sha512::V512::small_sigma_0(x) + } + + /// The Small Sigma 1 function as specified in FIPS 180-4 section 4.1.3. + fn small_sigma_1(x: WordU64) -> WordU64 { + super::sha512::V512::small_sigma_1(x) + } +} + +#[derive(Clone, Debug)] +/// SHA384 streaming state. +pub struct Sha384 { + pub(crate) _state: State<WordU64, V384, SHA384_BLOCKSIZE, SHA384_OUTSIZE, N_CONSTS>, +} + +impl Default for Sha384 { + fn default() -> Self { + Self::new() + } +} + +impl Sha384 { + /// Initialize a `Sha384` struct. + pub fn new() -> Self { + Self { + _state: State::<WordU64, V384, SHA384_BLOCKSIZE, SHA384_OUTSIZE, N_CONSTS>::_new(), + } + } + + /// 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) + } + + /// Finalize the hash and put the final digest into `dest`. + 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 SHA384 digest. + pub fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> { + let mut digest = [0u8; SHA384_OUTSIZE]; + self._finalize_internal(&mut digest)?; + + Ok(Digest::from(digest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Calculate a SHA384 digest of some `data`. + pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx.finalize() + } +} + +impl crate::hazardous::mac::hmac::HmacHashFunction for Sha384 { + /// The blocksize of the hash function. + const _BLOCKSIZE: usize = SHA384_BLOCKSIZE; + + /// The output size of the hash function. + const _OUTSIZE: usize = SHA384_OUTSIZE; + + /// Create a new instance of the hash function. + fn _new() -> Self { + Self::new() + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(data) + } + + /// Finalize the hash and put the final digest into `dest`. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._finalize_internal(dest) + } + + /// Compute a digest of `data` and copy it into `dest`. + fn _digest(data: &[u8], dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx._finalize_internal(dest) + } + + #[cfg(test)] + fn compare_state_to_other(&self, other: &Self) { + self._state.compare_state_to_other(&other._state); + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: hashing from a [`Read`](std::io::Read)er with SHA384. +/// ```rust +/// use orion::{ +/// hazardous::hash::sha2::sha384::{Sha384, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Sha384::new(); +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box<dyn std::error::Error>>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Sha384 { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Sha384::update`](crate::hazardous::hash::sha2::sha384::Sha384::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_default_equals_new() { + let new = Sha384::new(); + let default = Sha384::default(); + new._state.compare_state_to_other(&default._state); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = Sha384::new(); + let debug = format!("{:?}", initial_state); + let expected = "Sha384 { _state: State { working_state: [***OMITTED***], buffer: [***OMITTED***], leftover: 0, message_len: [WordU64(0), WordU64(0)], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + impl TestableStreamingContext<Digest> for Sha384 { + 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<Digest, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> { + Sha384::digest(input) + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual: Digest = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Sha384, state_2: &Sha384) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Sha384 = Sha384::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha384>::new( + initial_state, + 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: Sha384 = Sha384::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha384>::new( + initial_state, + SHA384_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::sha2::sha384::Sha384; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool { + let mut hasher_a = Sha384::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_increment_mlen { + use super::*; + + #[test] + fn test_mlen_increase_values() { + let mut context = Sha384::default(); + + context._state.increment_mlen(&WordU64::from(1u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(8u64)); + + context._state.increment_mlen(&WordU64::from(17u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(144u64)); + + context._state.increment_mlen(&WordU64::from(12u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(240u64)); + + // Overflow + context._state.increment_mlen(&WordU64::from(u64::MAX / 8)); + assert_eq!(context._state.message_len[0], WordU64::from(1u64)); + assert_eq!(context._state.message_len[1], WordU64::from(232u64)); + } + + #[test] + #[should_panic] + fn test_panic_on_second_overflow() { + use crate::hazardous::hash::sha2::sha2_core::Word; + + let mut context = Sha384::default(); + context._state.message_len = [WordU64::MAX, WordU64::from(u64::MAX - 7)]; + // u64::MAX - 7, to leave so that the length represented + // in bites should overflow by exactly one. + context._state.increment_mlen(&WordU64::from(1u64)); + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha2/sha512.rs b/vendor/orion/src/hazardous/hash/sha2/sha512.rs new file mode 100644 index 0000000..0e865c5 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha2/sha512.rs @@ -0,0 +1,424 @@ +// MIT License + +// Copyright (c) 2018-2023 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: +//! - `data`: The data to be hashed. +//! +//! # 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. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) __bits__ of data are hashed. +//! +//! # Security: +//! - SHA512 is vulnerable to length extension attacks. +//! +//! # Recommendation: +//! - It is recommended to use [BLAKE2b] when possible. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha2::sha512::Sha512; +//! +//! // Using the streaming interface +//! let mut state = Sha512::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha512::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha512::Sha512::update +//! [`reset()`]: sha512::Sha512::reset +//! [`finalize()`]: sha512::Sha512::finalize +//! [BLAKE2b]: super::blake2::blake2b + +use crate::errors::UnknownCryptoError; + +#[cfg(feature = "safe_api")] +use std::io; + +construct_public! { + /// A type to represent the `Digest` that SHA512 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 64 bytes. + (Digest, test_digest, SHA512_OUTSIZE, SHA512_OUTSIZE) +} + +impl_from_trait!(Digest, SHA512_OUTSIZE); + +use super::sha2_core::{State, Variant, Word}; +use super::w64::WordU64; + +/// The blocksize for the hash function SHA512. +pub const SHA512_BLOCKSIZE: usize = 128; +/// The output size for the hash function SHA512. +pub const SHA512_OUTSIZE: usize = 64; +/// The number of constants for the hash function SHA512. +const N_CONSTS: usize = 80; + +#[derive(Clone)] +pub(crate) struct V512; + +impl Variant<WordU64, N_CONSTS> for V512 { + #[rustfmt::skip] + #[allow(clippy::unreadable_literal)] + /// The SHA512 constants as defined in FIPS 180-4. + const K: [WordU64; N_CONSTS] = [ + WordU64(0x428a2f98d728ae22), WordU64(0x7137449123ef65cd), WordU64(0xb5c0fbcfec4d3b2f), WordU64(0xe9b5dba58189dbbc), + WordU64(0x3956c25bf348b538), WordU64(0x59f111f1b605d019), WordU64(0x923f82a4af194f9b), WordU64(0xab1c5ed5da6d8118), + WordU64(0xd807aa98a3030242), WordU64(0x12835b0145706fbe), WordU64(0x243185be4ee4b28c), WordU64(0x550c7dc3d5ffb4e2), + WordU64(0x72be5d74f27b896f), WordU64(0x80deb1fe3b1696b1), WordU64(0x9bdc06a725c71235), WordU64(0xc19bf174cf692694), + WordU64(0xe49b69c19ef14ad2), WordU64(0xefbe4786384f25e3), WordU64(0x0fc19dc68b8cd5b5), WordU64(0x240ca1cc77ac9c65), + WordU64(0x2de92c6f592b0275), WordU64(0x4a7484aa6ea6e483), WordU64(0x5cb0a9dcbd41fbd4), WordU64(0x76f988da831153b5), + WordU64(0x983e5152ee66dfab), WordU64(0xa831c66d2db43210), WordU64(0xb00327c898fb213f), WordU64(0xbf597fc7beef0ee4), + WordU64(0xc6e00bf33da88fc2), WordU64(0xd5a79147930aa725), WordU64(0x06ca6351e003826f), WordU64(0x142929670a0e6e70), + WordU64(0x27b70a8546d22ffc), WordU64(0x2e1b21385c26c926), WordU64(0x4d2c6dfc5ac42aed), WordU64(0x53380d139d95b3df), + WordU64(0x650a73548baf63de), WordU64(0x766a0abb3c77b2a8), WordU64(0x81c2c92e47edaee6), WordU64(0x92722c851482353b), + WordU64(0xa2bfe8a14cf10364), WordU64(0xa81a664bbc423001), WordU64(0xc24b8b70d0f89791), WordU64(0xc76c51a30654be30), + WordU64(0xd192e819d6ef5218), WordU64(0xd69906245565a910), WordU64(0xf40e35855771202a), WordU64(0x106aa07032bbd1b8), + WordU64(0x19a4c116b8d2d0c8), WordU64(0x1e376c085141ab53), WordU64(0x2748774cdf8eeb99), WordU64(0x34b0bcb5e19b48a8), + WordU64(0x391c0cb3c5c95a63), WordU64(0x4ed8aa4ae3418acb), WordU64(0x5b9cca4f7763e373), WordU64(0x682e6ff3d6b2b8a3), + WordU64(0x748f82ee5defb2fc), WordU64(0x78a5636f43172f60), WordU64(0x84c87814a1f0ab72), WordU64(0x8cc702081a6439ec), + WordU64(0x90befffa23631e28), WordU64(0xa4506cebde82bde9), WordU64(0xbef9a3f7b2c67915), WordU64(0xc67178f2e372532b), + WordU64(0xca273eceea26619c), WordU64(0xd186b8c721c0c207), WordU64(0xeada7dd6cde0eb1e), WordU64(0xf57d4f7fee6ed178), + WordU64(0x06f067aa72176fba), WordU64(0x0a637dc5a2c898a6), WordU64(0x113f9804bef90dae), WordU64(0x1b710b35131c471b), + WordU64(0x28db77f523047d84), WordU64(0x32caab7b40c72493), WordU64(0x3c9ebe0a15c9bebc), WordU64(0x431d67c49c100d4c), + WordU64(0x4cc5d4becb3e42b6), WordU64(0x597f299cfc657e2a), WordU64(0x5fcb6fab3ad6faec), WordU64(0x6c44198c4a475817), + ]; + + #[rustfmt::skip] + #[allow(clippy::unreadable_literal)] + /// The SHA512 initial hash value H(0) as defined in FIPS 180-4. + const H0: [WordU64; 8] = [ + WordU64(0x6a09e667f3bcc908), WordU64(0xbb67ae8584caa73b), WordU64(0x3c6ef372fe94f82b), WordU64(0xa54ff53a5f1d36f1), + WordU64(0x510e527fade682d1), WordU64(0x9b05688c2b3e6c1f), WordU64(0x1f83d9abfb41bd6b), WordU64(0x5be0cd19137e2179), + ]; + + /// The Big Sigma 0 function as specified in FIPS 180-4 section 4.1.3. + fn big_sigma_0(x: WordU64) -> WordU64 { + (x.rotate_right(28)) ^ x.rotate_right(34) ^ x.rotate_right(39) + } + + /// The Big Sigma 1 function as specified in FIPS 180-4 section 4.1.3. + fn big_sigma_1(x: WordU64) -> WordU64 { + (x.rotate_right(14)) ^ x.rotate_right(18) ^ x.rotate_right(41) + } + + /// The Small Sigma 0 function as specified in FIPS 180-4 section 4.1.3. + fn small_sigma_0(x: WordU64) -> WordU64 { + (x.rotate_right(1)) ^ x.rotate_right(8) ^ (x >> WordU64(7)) + } + + /// The Small Sigma 1 function as specified in FIPS 180-4 section 4.1.3. + fn small_sigma_1(x: WordU64) -> WordU64 { + (x.rotate_right(19)) ^ x.rotate_right(61) ^ (x >> WordU64(6)) + } +} + +#[derive(Clone, Debug)] +/// SHA512 streaming state. +pub struct Sha512 { + pub(crate) _state: State<WordU64, V512, SHA512_BLOCKSIZE, SHA512_OUTSIZE, N_CONSTS>, +} + +impl Default for Sha512 { + fn default() -> Self { + Self::new() + } +} + +impl Sha512 { + /// Initialize a `Sha512` struct. + pub fn new() -> Self { + Self { + _state: State::<WordU64, V512, SHA512_BLOCKSIZE, SHA512_OUTSIZE, N_CONSTS>::_new(), + } + } + + /// 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) + } + + /// Finalize the hash and put the final digest into `dest`. + 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 SHA512 digest. + pub fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> { + let mut digest = [0u8; SHA512_OUTSIZE]; + self._finalize_internal(&mut digest)?; + + Ok(Digest::from(digest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Calculate a SHA512 digest of some `data`. + pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx.finalize() + } +} + +impl crate::hazardous::mac::hmac::HmacHashFunction for Sha512 { + /// The blocksize of the hash function. + const _BLOCKSIZE: usize = SHA512_BLOCKSIZE; + + /// The output size of the hash function. + const _OUTSIZE: usize = SHA512_OUTSIZE; + + /// Create a new instance of the hash function. + fn _new() -> Self { + Self::new() + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(data) + } + + /// Finalize the hash and put the final digest into `dest`. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._finalize_internal(dest) + } + + /// Compute a digest of `data` and copy it into `dest`. + fn _digest(data: &[u8], dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx._finalize_internal(dest) + } + + #[cfg(test)] + fn compare_state_to_other(&self, other: &Self) { + self._state.compare_state_to_other(&other._state); + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: hashing from a [`Read`](std::io::Read)er with SHA512. +/// ```rust +/// use orion::{ +/// hazardous::hash::sha2::sha512::{Sha512, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Sha512::new(); +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box<dyn std::error::Error>>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Sha512 { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Sha512::update`](crate::hazardous::hash::sha2::sha512::Sha512::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_default_equals_new() { + let new = Sha512::new(); + let default = Sha512::default(); + new._state.compare_state_to_other(&default._state); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = Sha512::new(); + let debug = format!("{:?}", initial_state); + let expected = "Sha512 { _state: State { working_state: [***OMITTED***], buffer: [***OMITTED***], leftover: 0, message_len: [WordU64(0), WordU64(0)], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + impl TestableStreamingContext<Digest> for Sha512 { + 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<Digest, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> { + Sha512::digest(input) + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual: Digest = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Sha512, state_2: &Sha512) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Sha512 = Sha512::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha512>::new( + initial_state, + 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: Sha512 = Sha512::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha512>::new( + initial_state, + SHA512_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::sha2::sha512::Sha512; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool { + let mut hasher_a = Sha512::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_increment_mlen { + use super::*; + + #[test] + fn test_mlen_increase_values() { + let mut context = Sha512::default(); + + context._state.increment_mlen(&WordU64::from(1u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(8u64)); + + context._state.increment_mlen(&WordU64::from(17u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(144u64)); + + context._state.increment_mlen(&WordU64::from(12u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(240u64)); + + // Overflow + context._state.increment_mlen(&WordU64::from(u64::MAX / 8)); + assert_eq!(context._state.message_len[0], WordU64::from(1u64)); + assert_eq!(context._state.message_len[1], WordU64::from(232u64)); + } + + #[test] + #[should_panic] + fn test_panic_on_second_overflow() { + let mut context = Sha512::default(); + context._state.message_len = [WordU64::MAX, WordU64::from(u64::MAX - 7)]; + // u64::MAX - 7, to leave so that the length represented + // in bites should overflow by exactly one. + context._state.increment_mlen(&WordU64::from(1u64)); + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha3/mod.rs b/vendor/orion/src/hazardous/hash/sha3/mod.rs new file mode 100644 index 0000000..28be25d --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha3/mod.rs @@ -0,0 +1,558 @@ +// MIT License + +// Copyright (c) 2023 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. + +/// SHA3-224 as specified in the [FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf). +pub mod sha3_224; + +/// SHA3-256 as specified in the [FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf). +pub mod sha3_256; + +/// SHA3-384 as specified in the [FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf). +pub mod sha3_384; + +/// SHA3-512 as specified in the [FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf). +pub mod sha3_512; + +use crate::errors::UnknownCryptoError; +use core::fmt::Debug; +use zeroize::Zeroize; + +/// Round constants. See NIST intermediate test vectors for source. +const RC: [u64; 24] = [ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +]; + +/// Rho offsets. See NIST intermediate test vectors for source. +const RHO: [u32; 24] = [ + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44, +]; + +/// Indices precomputed based on spec of Pi. +const PI: [usize; 24] = [ + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, +]; + +fn keccakf<const ROUNDS: usize>(state: &mut [u64; 25]) { + for round in 0..ROUNDS { + let mut buf = [0u64; 5]; + + theta(state, &mut buf); + rho_and_pi(state, &mut buf); + chi(state, &mut buf); + iota(state, round); + } +} + +#[allow(clippy::erasing_op)] +#[allow(clippy::identity_op)] +// Theta (θ). +fn theta(state: &mut [u64; 25], buf: &mut [u64; 5]) { + buf[0] ^= state[0 + (0 * 5)]; + buf[0] ^= state[0 + (1 * 5)]; + buf[0] ^= state[0 + (2 * 5)]; + buf[0] ^= state[0 + (3 * 5)]; + buf[0] ^= state[0 + (4 * 5)]; + + buf[1] ^= state[1 + (0 * 5)]; + buf[1] ^= state[1 + (1 * 5)]; + buf[1] ^= state[1 + (2 * 5)]; + buf[1] ^= state[1 + (3 * 5)]; + buf[1] ^= state[1 + (4 * 5)]; + + buf[2] ^= state[2 + (0 * 5)]; + buf[2] ^= state[2 + (1 * 5)]; + buf[2] ^= state[2 + (2 * 5)]; + buf[2] ^= state[2 + (3 * 5)]; + buf[2] ^= state[2 + (4 * 5)]; + + buf[3] ^= state[3 + (0 * 5)]; + buf[3] ^= state[3 + (1 * 5)]; + buf[3] ^= state[3 + (2 * 5)]; + buf[3] ^= state[3 + (3 * 5)]; + buf[3] ^= state[3 + (4 * 5)]; + + buf[4] ^= state[4 + (0 * 5)]; + buf[4] ^= state[4 + (1 * 5)]; + buf[4] ^= state[4 + (2 * 5)]; + buf[4] ^= state[4 + (3 * 5)]; + buf[4] ^= state[4 + (4 * 5)]; + + state[(0 * 5) + 0] ^= buf[(0 + 4) % 5] ^ buf[(0 + 1) % 5].rotate_left(1); + state[(1 * 5) + 0] ^= buf[(0 + 4) % 5] ^ buf[(0 + 1) % 5].rotate_left(1); + state[(2 * 5) + 0] ^= buf[(0 + 4) % 5] ^ buf[(0 + 1) % 5].rotate_left(1); + state[(3 * 5) + 0] ^= buf[(0 + 4) % 5] ^ buf[(0 + 1) % 5].rotate_left(1); + state[(4 * 5) + 0] ^= buf[(0 + 4) % 5] ^ buf[(0 + 1) % 5].rotate_left(1); + + state[(0 * 5) + 1] ^= buf[(1 + 4) % 5] ^ buf[(1 + 1) % 5].rotate_left(1); + state[(1 * 5) + 1] ^= buf[(1 + 4) % 5] ^ buf[(1 + 1) % 5].rotate_left(1); + state[(2 * 5) + 1] ^= buf[(1 + 4) % 5] ^ buf[(1 + 1) % 5].rotate_left(1); + state[(3 * 5) + 1] ^= buf[(1 + 4) % 5] ^ buf[(1 + 1) % 5].rotate_left(1); + state[(4 * 5) + 1] ^= buf[(1 + 4) % 5] ^ buf[(1 + 1) % 5].rotate_left(1); + + state[(0 * 5) + 2] ^= buf[(2 + 4) % 5] ^ buf[(2 + 1) % 5].rotate_left(1); + state[(1 * 5) + 2] ^= buf[(2 + 4) % 5] ^ buf[(2 + 1) % 5].rotate_left(1); + state[(2 * 5) + 2] ^= buf[(2 + 4) % 5] ^ buf[(2 + 1) % 5].rotate_left(1); + state[(3 * 5) + 2] ^= buf[(2 + 4) % 5] ^ buf[(2 + 1) % 5].rotate_left(1); + state[(4 * 5) + 2] ^= buf[(2 + 4) % 5] ^ buf[(2 + 1) % 5].rotate_left(1); + + state[(0 * 5) + 3] ^= buf[(3 + 4) % 5] ^ buf[(3 + 1) % 5].rotate_left(1); + state[(1 * 5) + 3] ^= buf[(3 + 4) % 5] ^ buf[(3 + 1) % 5].rotate_left(1); + state[(2 * 5) + 3] ^= buf[(3 + 4) % 5] ^ buf[(3 + 1) % 5].rotate_left(1); + state[(3 * 5) + 3] ^= buf[(3 + 4) % 5] ^ buf[(3 + 1) % 5].rotate_left(1); + state[(4 * 5) + 3] ^= buf[(3 + 4) % 5] ^ buf[(3 + 1) % 5].rotate_left(1); + + state[(0 * 5) + 4] ^= buf[(4 + 4) % 5] ^ buf[(4 + 1) % 5].rotate_left(1); + state[(1 * 5) + 4] ^= buf[(4 + 4) % 5] ^ buf[(4 + 1) % 5].rotate_left(1); + state[(2 * 5) + 4] ^= buf[(4 + 4) % 5] ^ buf[(4 + 1) % 5].rotate_left(1); + state[(3 * 5) + 4] ^= buf[(4 + 4) % 5] ^ buf[(4 + 1) % 5].rotate_left(1); + state[(4 * 5) + 4] ^= buf[(4 + 4) % 5] ^ buf[(4 + 1) % 5].rotate_left(1); +} + +// Rho (ρ) & Pi (π). +fn rho_and_pi(state: &mut [u64; 25], buf: &mut [u64; 5]) { + let mut prev = state[1]; + + buf[0] = state[PI[0]]; + state[PI[0]] = prev.rotate_left(RHO[0]); + prev = buf[0]; + + buf[0] = state[PI[1]]; + state[PI[1]] = prev.rotate_left(RHO[1]); + prev = buf[0]; + + buf[0] = state[PI[2]]; + state[PI[2]] = prev.rotate_left(RHO[2]); + prev = buf[0]; + + buf[0] = state[PI[3]]; + state[PI[3]] = prev.rotate_left(RHO[3]); + prev = buf[0]; + + buf[0] = state[PI[4]]; + state[PI[4]] = prev.rotate_left(RHO[4]); + prev = buf[0]; + + buf[0] = state[PI[5]]; + state[PI[5]] = prev.rotate_left(RHO[5]); + prev = buf[0]; + + buf[0] = state[PI[6]]; + state[PI[6]] = prev.rotate_left(RHO[6]); + prev = buf[0]; + + buf[0] = state[PI[7]]; + state[PI[7]] = prev.rotate_left(RHO[7]); + prev = buf[0]; + + buf[0] = state[PI[8]]; + state[PI[8]] = prev.rotate_left(RHO[8]); + prev = buf[0]; + + buf[0] = state[PI[9]]; + state[PI[9]] = prev.rotate_left(RHO[9]); + prev = buf[0]; + + buf[0] = state[PI[10]]; + state[PI[10]] = prev.rotate_left(RHO[10]); + prev = buf[0]; + + buf[0] = state[PI[11]]; + state[PI[11]] = prev.rotate_left(RHO[11]); + prev = buf[0]; + + buf[0] = state[PI[12]]; + state[PI[12]] = prev.rotate_left(RHO[12]); + prev = buf[0]; + + buf[0] = state[PI[13]]; + state[PI[13]] = prev.rotate_left(RHO[13]); + prev = buf[0]; + + buf[0] = state[PI[14]]; + state[PI[14]] = prev.rotate_left(RHO[14]); + prev = buf[0]; + + buf[0] = state[PI[15]]; + state[PI[15]] = prev.rotate_left(RHO[15]); + prev = buf[0]; + + buf[0] = state[PI[16]]; + state[PI[16]] = prev.rotate_left(RHO[16]); + prev = buf[0]; + + buf[0] = state[PI[17]]; + state[PI[17]] = prev.rotate_left(RHO[17]); + prev = buf[0]; + + buf[0] = state[PI[18]]; + state[PI[18]] = prev.rotate_left(RHO[18]); + prev = buf[0]; + + buf[0] = state[PI[19]]; + state[PI[19]] = prev.rotate_left(RHO[19]); + prev = buf[0]; + + buf[0] = state[PI[20]]; + state[PI[20]] = prev.rotate_left(RHO[20]); + prev = buf[0]; + + buf[0] = state[PI[21]]; + state[PI[21]] = prev.rotate_left(RHO[21]); + prev = buf[0]; + + buf[0] = state[PI[22]]; + state[PI[22]] = prev.rotate_left(RHO[22]); + prev = buf[0]; + + buf[0] = state[PI[23]]; + state[PI[23]] = prev.rotate_left(RHO[23]); +} + +#[allow(clippy::identity_op)] +// Chi (χ). +fn chi(state: &mut [u64; 25], buf: &mut [u64; 5]) { + buf[0] = state[0 + 0]; + buf[1] = state[0 + 1]; + buf[2] = state[0 + 2]; + buf[3] = state[0 + 3]; + buf[4] = state[0 + 4]; + + state[0 + 0] = buf[0] ^ ((!buf[(0 + 1) % 5]) & (buf[(0 + 2) % 5])); + state[0 + 1] = buf[1] ^ ((!buf[(1 + 1) % 5]) & (buf[(1 + 2) % 5])); + state[0 + 2] = buf[2] ^ ((!buf[(2 + 1) % 5]) & (buf[(2 + 2) % 5])); + state[0 + 3] = buf[3] ^ ((!buf[(3 + 1) % 5]) & (buf[(3 + 2) % 5])); + state[0 + 4] = buf[4] ^ ((!buf[(4 + 1) % 5]) & (buf[(4 + 2) % 5])); + + buf[0] = state[5 + 0]; + buf[1] = state[5 + 1]; + buf[2] = state[5 + 2]; + buf[3] = state[5 + 3]; + buf[4] = state[5 + 4]; + + state[5 + 0] = buf[0] ^ ((!buf[(0 + 1) % 5]) & (buf[(0 + 2) % 5])); + state[5 + 1] = buf[1] ^ ((!buf[(1 + 1) % 5]) & (buf[(1 + 2) % 5])); + state[5 + 2] = buf[2] ^ ((!buf[(2 + 1) % 5]) & (buf[(2 + 2) % 5])); + state[5 + 3] = buf[3] ^ ((!buf[(3 + 1) % 5]) & (buf[(3 + 2) % 5])); + state[5 + 4] = buf[4] ^ ((!buf[(4 + 1) % 5]) & (buf[(4 + 2) % 5])); + + buf[0] = state[10 + 0]; + buf[1] = state[10 + 1]; + buf[2] = state[10 + 2]; + buf[3] = state[10 + 3]; + buf[4] = state[10 + 4]; + + state[10 + 0] = buf[0] ^ ((!buf[(0 + 1) % 5]) & (buf[(0 + 2) % 5])); + state[10 + 1] = buf[1] ^ ((!buf[(1 + 1) % 5]) & (buf[(1 + 2) % 5])); + state[10 + 2] = buf[2] ^ ((!buf[(2 + 1) % 5]) & (buf[(2 + 2) % 5])); + state[10 + 3] = buf[3] ^ ((!buf[(3 + 1) % 5]) & (buf[(3 + 2) % 5])); + state[10 + 4] = buf[4] ^ ((!buf[(4 + 1) % 5]) & (buf[(4 + 2) % 5])); + + buf[0] = state[15 + 0]; + buf[1] = state[15 + 1]; + buf[2] = state[15 + 2]; + buf[3] = state[15 + 3]; + buf[4] = state[15 + 4]; + + state[15 + 0] = buf[0] ^ ((!buf[(0 + 1) % 5]) & (buf[(0 + 2) % 5])); + state[15 + 1] = buf[1] ^ ((!buf[(1 + 1) % 5]) & (buf[(1 + 2) % 5])); + state[15 + 2] = buf[2] ^ ((!buf[(2 + 1) % 5]) & (buf[(2 + 2) % 5])); + state[15 + 3] = buf[3] ^ ((!buf[(3 + 1) % 5]) & (buf[(3 + 2) % 5])); + state[15 + 4] = buf[4] ^ ((!buf[(4 + 1) % 5]) & (buf[(4 + 2) % 5])); + + buf[0] = state[20 + 0]; + buf[1] = state[20 + 1]; + buf[2] = state[20 + 2]; + buf[3] = state[20 + 3]; + buf[4] = state[20 + 4]; + + state[20 + 0] = buf[0] ^ ((!buf[(0 + 1) % 5]) & (buf[(0 + 2) % 5])); + state[20 + 1] = buf[1] ^ ((!buf[(1 + 1) % 5]) & (buf[(1 + 2) % 5])); + state[20 + 2] = buf[2] ^ ((!buf[(2 + 1) % 5]) & (buf[(2 + 2) % 5])); + state[20 + 3] = buf[3] ^ ((!buf[(3 + 1) % 5]) & (buf[(3 + 2) % 5])); + state[20 + 4] = buf[4] ^ ((!buf[(4 + 1) % 5]) & (buf[(4 + 2) % 5])); +} + +// Iota (ι). +fn iota(state: &mut [u64; 25], round: usize) { + debug_assert!(round <= 24); + state[0] ^= RC[round]; +} + +// <https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt> +#[test] +fn test_full_round() { + let mut state = [0u64; 25]; + let expected_state_from_zero = [ + 0xF1258F7940E1DDE7, + 0x84D5CCF933C0478A, + 0xD598261EA65AA9EE, + 0xBD1547306F80494D, + 0x8B284E056253D057, + 0xFF97A42D7F8E6FD4, + 0x90FEE5A0A44647C4, + 0x8C5BDA0CD6192E76, + 0xAD30A6F71B19059C, + 0x30935AB7D08FFC64, + 0xEB5AA93F2317D635, + 0xA9A6E6260D712103, + 0x81A57C16DBCF555F, + 0x43B831CD0347C826, + 0x01F22F1A11A5569F, + 0x05E5635A21D9AE61, + 0x64BEFEF28CC970F2, + 0x613670957BC46611, + 0xB87C5A554FD00ECB, + 0x8C3EE88A1CCF32C8, + 0x940C7922AE3A2614, + 0x1841F924A2C509E4, + 0x16F53526E70465C2, + 0x75F644E97F30A13B, + 0xEAF1FF7B5CECA249, + ]; + let expected_state_rerun = [ + 0x2D5C954DF96ECB3C, + 0x6A332CD07057B56D, + 0x093D8D1270D76B6C, + 0x8A20D9B25569D094, + 0x4F9C4F99E5E7F156, + 0xF957B9A2DA65FB38, + 0x85773DAE1275AF0D, + 0xFAF4F247C3D810F7, + 0x1F1B9EE6F79A8759, + 0xE4FECC0FEE98B425, + 0x68CE61B6B9CE68A1, + 0xDEEA66C4BA8F974F, + 0x33C43D836EAFB1F5, + 0xE00654042719DBD9, + 0x7CF8A9F009831265, + 0xFD5449A6BF174743, + 0x97DDAD33D8994B40, + 0x48EAD5FC5D0BE774, + 0xE3B8C8EE55B7B03C, + 0x91A0226E649E42E9, + 0x900E3129E7BADD7B, + 0x202A9EC5FAA3CCE8, + 0x5B3402464E1C3DB6, + 0x609F4E62A44C1059, + 0x20D06CD26A8FBF5C, + ]; + + keccakf::<24>(&mut state); + assert_eq!(&state, &expected_state_from_zero); + keccakf::<24>(&mut state); + assert_eq!(&state, &expected_state_rerun); +} + +#[derive(Clone)] +/// SHA3 streaming state. +pub(crate) struct Sha3<const RATE: usize> { + pub(crate) state: [u64; 25], + pub(crate) buffer: [u8; RATE], + pub(crate) capacity: usize, + leftover: usize, + is_finalized: bool, +} + +impl<const RATE: usize> Drop for Sha3<RATE> { + fn drop(&mut self) { + self.state.iter_mut().zeroize(); + self.buffer.iter_mut().zeroize(); + self.leftover.zeroize(); + } +} + +impl<const RATE: usize> Debug for Sha3<RATE> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "State {{ state: [***OMITTED***], buffer: [***OMITTED***], capacity: {:?}, leftover: {:?}, \ + is_finalized: {:?} }}", + self.capacity, self.leftover, self.is_finalized + ) + } +} + +impl<const RATE: usize> Sha3<RATE> { + /// Initialize a new state. + /// `capacity` should be in bytes. + pub(crate) fn _new(capacity: usize) -> Self { + Self { + state: [0u64; 25], + buffer: [0u8; RATE], + capacity, + leftover: 0, + is_finalized: false, + } + } + + /// Process data in `self.buffer` or optionally `data`. + pub(crate) fn process_block(&mut self, data: Option<&[u8]>) { + // If `data.is_none()` then we want to process leftover data within `self.buffer`. + let data_block = match data { + Some(bytes) => { + debug_assert_eq!(bytes.len(), RATE); + bytes + } + None => &self.buffer, + }; + + debug_assert_eq!(data_block.len() % 8, 0); + + // We process data in terms of bitrate, but we need to XOR in an entire Keccak state. + // So the 25 - bitrate values will be zero. That's the same as not XORing those values + // so we leave it be as this. + for (b, s) in data_block + .chunks_exact(core::mem::size_of::<u64>()) + .zip(self.state.iter_mut()) + { + *s ^= u64::from_le_bytes(b.try_into().unwrap()); + } + + keccakf::<24>(&mut self.state); + } + + /// Reset to `new()` state. + pub(crate) fn _reset(&mut self) { + self.state = [0u64; 25]; + self.buffer = [0u8; RATE]; + self.leftover = 0; + self.is_finalized = false; + } + + /// Update state with `data`. This can be called multiple times. + pub(crate) 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 <= RATE); + + let mut want = RATE - 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 < RATE { + return Ok(()); + } + + self.process_block(None); + self.leftover = 0; + } + + while bytes.len() >= RATE { + self.process_block(Some(bytes[..RATE].as_ref())); + bytes = &bytes[RATE..]; + } + + if !bytes.is_empty() { + debug_assert_eq!(self.leftover, 0); + self.buffer[..bytes.len()].copy_from_slice(bytes); + self.leftover = bytes.len(); + } + + Ok(()) + } + + /// Finalize the hash and put the final digest into `dest`. + pub(crate) fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + if self.is_finalized { + return Err(UnknownCryptoError); + } + + self.is_finalized = true; + // self.leftover should not be greater than SHA3(256/384/512)_RATE + // as that would have been processed in the update call + debug_assert!(self.leftover < RATE); + // Set padding byte and pad with zeroes after + self.buffer[self.leftover] = 0x06; + self.leftover += 1; + for itm in self.buffer.iter_mut().skip(self.leftover) { + *itm = 0; + } + + self.buffer[self.buffer.len() - 1] |= 0x80; + self.process_block(None); + + // The reason we can't work with chunks_exact here is that for SHA3-224 + // the `dest` is not evenly divisible by 8/`core::mem::size_of::<u64>()`. + for (out_chunk, state_value) in dest + .chunks_mut(core::mem::size_of::<u64>()) + .zip(self.state.iter()) + { + // We need to slice the state value in bytes here for same reason as mentioned + // above. + out_chunk.copy_from_slice(&state_value.to_le_bytes()[..out_chunk.len()]); + } + + Ok(()) + } + + #[cfg(test)] + /// Compare two Sha3 state objects to check if their fields + /// are the same. + pub(crate) fn compare_state_to_other(&self, other: &Self) { + for idx in 0..25 { + assert_eq!(self.state[idx], other.state[idx]); + } + assert_eq!(self.buffer, other.buffer); + assert_eq!(self.capacity, other.capacity); + assert_eq!(self.leftover, other.leftover); + assert_eq!(self.is_finalized, other.is_finalized); + } +} diff --git a/vendor/orion/src/hazardous/hash/sha3/sha3_224.rs b/vendor/orion/src/hazardous/hash/sha3/sha3_224.rs new file mode 100644 index 0000000..e235f2b --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha3/sha3_224.rs @@ -0,0 +1,274 @@ +// MIT License + +// Copyright (c) 2023 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: +//! - `data`: The data to be hashed. +//! +//! # 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. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha3::sha3_224::Sha3_224; +//! +//! // Using the streaming interface +//! let mut state = Sha3_224::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha3_224::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha3_224::Sha3_224::update +//! [`reset()`]: sha3_224::Sha3_224::reset +//! [`finalize()`]: sha3_224::Sha3_224::finalize + +use crate::errors::UnknownCryptoError; +#[cfg(feature = "safe_api")] +use std::io; + +use super::Sha3; + +/// Rate of SHA3-224 (equivalent to blocksize in SHA2). +pub const SHA3_224_RATE: usize = 144; + +/// Output size of SHA3-224 in bytes. +pub const SHA3_224_OUTSIZE: usize = 28; + +construct_public! { + /// A type to represent the `Digest` that SHA3-224 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 28 bytes. + (Digest, test_digest, SHA3_224_OUTSIZE, SHA3_224_OUTSIZE) +} + +impl_from_trait!(Digest, SHA3_224_OUTSIZE); + +#[derive(Clone, Debug)] +/// SHA3-224 streaming state. +pub struct Sha3_224 { + pub(crate) _state: Sha3<SHA3_224_RATE>, +} + +impl Default for Sha3_224 { + fn default() -> Self { + Self::new() + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: hashing from a [`Read`](std::io::Read)er with SHA3-224. +/// ```rust +/// use orion::{ +/// hazardous::hash::sha3::sha3_224::{Sha3_224, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Sha3_224::new(); +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box<dyn std::error::Error>>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Sha3_224 { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Sha3_224::update`](crate::hazardous::hash::sha3::sha3_224::Sha3_224::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +impl Sha3_224 { + /// Initialize a `Sha3_224` struct. + pub fn new() -> Self { + Self { + _state: Sha3::<SHA3_224_RATE>::_new(56), + } + } + + /// 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) + } + + /// Finalize the hash and put the final digest into `dest`. + 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 SHA3-224 digest. + pub fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> { + let mut digest = [0u8; SHA3_224_OUTSIZE]; + self._finalize_internal(&mut digest)?; + + Ok(Digest::from(digest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Calculate a SHA3-224 digest of some `data`. + pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx.finalize() + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_default_equals_new() { + let new = Sha3_224::new(); + let default = Sha3_224::default(); + new._state.compare_state_to_other(&default._state); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = Sha3_224::new(); + let debug = format!("{:?}", initial_state); + let expected = "Sha3_224 { _state: State { state: [***OMITTED***], buffer: [***OMITTED***], capacity: 56, leftover: 0, is_finalized: false } }"; + assert_eq!(debug, expected); + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + impl TestableStreamingContext<Digest> for Sha3_224 { + 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<Digest, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> { + Sha3_224::digest(input) + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual: Digest = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Sha3_224, state_2: &Sha3_224) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Sha3_224 = Sha3_224::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha3_224>::new( + initial_state, + SHA3_224_RATE, + ); + 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: Sha3_224 = Sha3_224::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha3_224>::new( + initial_state, + SHA3_224_RATE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::sha3::sha3_224::Sha3_224; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool { + let mut hasher_a = Sha3_224::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha3/sha3_256.rs b/vendor/orion/src/hazardous/hash/sha3/sha3_256.rs new file mode 100644 index 0000000..f972db9 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha3/sha3_256.rs @@ -0,0 +1,336 @@ +// MIT License + +// Copyright (c) 2023 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: +//! - `data`: The data to be hashed. +//! +//! # 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. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha3::sha3_256::Sha3_256; +//! +//! // Using the streaming interface +//! let mut state = Sha3_256::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha3_256::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha3_256::Sha3_256::update +//! [`reset()`]: sha3_256::Sha3_256::reset +//! [`finalize()`]: sha3_256::Sha3_256::finalize + +use crate::errors::UnknownCryptoError; +#[cfg(feature = "safe_api")] +use std::io; + +use super::Sha3; + +/// Rate of SHA3-256 (equivalent to blocksize in SHA2). +pub const SHA3_256_RATE: usize = 136; + +/// Output size of SHA3-256 in bytes. +pub const SHA3_256_OUTSIZE: usize = 32; + +construct_public! { + /// A type to represent the `Digest` that SHA3-256 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + (Digest, test_digest, SHA3_256_OUTSIZE, SHA3_256_OUTSIZE) +} + +impl_from_trait!(Digest, SHA3_256_OUTSIZE); + +#[derive(Clone, Debug)] +/// SHA3-256 streaming state. +pub struct Sha3_256 { + pub(crate) _state: Sha3<SHA3_256_RATE>, +} + +impl Default for Sha3_256 { + fn default() -> Self { + Self::new() + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: hashing from a [`Read`](std::io::Read)er with SHA3-256. +/// ```rust +/// use orion::{ +/// hazardous::hash::sha3::sha3_256::{Sha3_256, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Sha3_256::new(); +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box<dyn std::error::Error>>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Sha3_256 { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Sha3_256::update`](crate::hazardous::hash::sha3::sha3_256::Sha3_256::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +impl Sha3_256 { + /// Initialize a `Sha3_256` struct. + pub fn new() -> Self { + Self { + _state: Sha3::<{ SHA3_256_RATE }>::_new(64), + } + } + + /// 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) + } + + /// Finalize the hash and put the final digest into `dest`. + 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 SHA3-256 digest. + pub fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> { + let mut digest = [0u8; SHA3_256_OUTSIZE]; + self._finalize_internal(&mut digest)?; + + Ok(Digest::from(digest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Calculate a SHA3-256 digest of some `data`. + pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx.finalize() + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_default_equals_new() { + let new = Sha3_256::new(); + let default = Sha3_256::default(); + new._state.compare_state_to_other(&default._state); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = Sha3_256::new(); + let debug = format!("{:?}", initial_state); + let expected = "Sha3_256 { _state: State { state: [***OMITTED***], buffer: [***OMITTED***], capacity: 64, leftover: 0, is_finalized: false } }"; + assert_eq!(debug, expected); + } + + #[test] + // <https://github.com/ziglang/zig/issues/14851> + fn test_zig_cryptofuzz() { + let expected_hash: [u8; 32] = [ + 0x57, 0x80, 0x4, 0x8d, 0xfa, 0x38, 0x1a, 0x1d, 0x1, 0xc7, 0x47, 0x90, 0x6e, 0x4a, 0x8, + 0x71, 0x1d, 0xd3, 0x4f, 0xd7, 0x12, 0xec, 0xd7, 0xc6, 0x80, 0x1d, 0xd2, 0xb3, 0x8f, + 0xd8, 0x1a, 0x89, + ]; + let msg: [u8; 613] = [ + 0x97, 0xd1, 0x2d, 0x1a, 0x16, 0x2d, 0x36, 0x4d, 0x20, 0x62, 0x19, 0x0b, 0x14, 0x93, + 0xbb, 0xf8, 0x5b, 0xea, 0x04, 0xc2, 0x61, 0x8e, 0xd6, 0x08, 0x81, 0xa1, 0x1d, 0x73, + 0x27, 0x48, 0xbf, 0xa4, 0xba, 0xb1, 0x9a, 0x48, 0x9c, 0xf9, 0x9b, 0xff, 0x34, 0x48, + 0xa9, 0x75, 0xea, 0xc8, 0xa3, 0x48, 0x24, 0x9d, 0x75, 0x27, 0x48, 0xec, 0x03, 0xb0, + 0xbb, 0xdf, 0x33, 0x90, 0xe3, 0x93, 0xed, 0x68, 0x24, 0x39, 0x12, 0xdf, 0xea, 0xee, + 0x8c, 0x9f, 0x96, 0xde, 0x42, 0x46, 0x8c, 0x2b, 0x17, 0x83, 0x36, 0xfb, 0xf4, 0xf7, + 0xff, 0x79, 0xb9, 0x45, 0x41, 0xc9, 0x56, 0x1a, 0x6b, 0x0c, 0xa4, 0x1a, 0xdd, 0x6b, + 0x95, 0xe8, 0x03, 0x0f, 0x09, 0x29, 0x40, 0x1b, 0xea, 0x87, 0xfa, 0xb9, 0x18, 0xa9, + 0x95, 0x07, 0x7c, 0x2f, 0x7c, 0x33, 0xfb, 0xc5, 0x11, 0x5e, 0x81, 0x0e, 0xbc, 0xae, + 0xec, 0xb3, 0xe1, 0x4a, 0x26, 0x56, 0xe8, 0x5b, 0x11, 0x9d, 0x37, 0x06, 0x9b, 0x34, + 0x31, 0x6e, 0xa3, 0xba, 0x41, 0xbc, 0x11, 0xd8, 0xc5, 0x15, 0xc9, 0x30, 0x2c, 0x9b, + 0xb6, 0x71, 0xd8, 0x7c, 0xbc, 0x38, 0x2f, 0xd5, 0xbd, 0x30, 0x96, 0xd4, 0xa3, 0x00, + 0x77, 0x9d, 0x55, 0x4a, 0x33, 0x53, 0xb6, 0xb3, 0x35, 0x1b, 0xae, 0xe5, 0xdc, 0x22, + 0x23, 0x85, 0x95, 0x88, 0xf9, 0x3b, 0xbf, 0x74, 0x13, 0xaa, 0xcb, 0x0a, 0x60, 0x79, + 0x13, 0x79, 0xc0, 0x4a, 0x02, 0xdb, 0x1c, 0xc9, 0xff, 0x60, 0x57, 0x9a, 0x70, 0x28, + 0x58, 0x60, 0xbc, 0x57, 0x07, 0xc7, 0x47, 0x1a, 0x45, 0x71, 0x76, 0x94, 0xfb, 0x05, + 0xad, 0xec, 0x12, 0x29, 0x5a, 0x44, 0x6a, 0x81, 0xd9, 0xc6, 0xf0, 0xb6, 0x9b, 0x97, + 0x83, 0x69, 0xfb, 0xdc, 0x0d, 0x4a, 0x67, 0xbc, 0x72, 0xf5, 0x43, 0x5e, 0x9b, 0x13, + 0xf2, 0xe4, 0x6d, 0x49, 0xdb, 0x76, 0xcb, 0x42, 0x6a, 0x3c, 0x9f, 0xa1, 0xfe, 0x5e, + 0xca, 0x0a, 0xfc, 0xfa, 0x39, 0x27, 0xd1, 0x3c, 0xcb, 0x9a, 0xde, 0x4c, 0x6b, 0x09, + 0x8b, 0x49, 0xfd, 0x1e, 0x3d, 0x5e, 0x67, 0x7c, 0x57, 0xad, 0x90, 0xcc, 0x46, 0x5f, + 0x5c, 0xae, 0x6a, 0x9c, 0xb2, 0xcd, 0x2c, 0x89, 0x78, 0xcf, 0xf1, 0x49, 0x96, 0x55, + 0x1e, 0x04, 0xef, 0x0e, 0x1c, 0xde, 0x6c, 0x96, 0x51, 0x00, 0xee, 0x9a, 0x1f, 0x8d, + 0x61, 0xbc, 0xeb, 0xb1, 0xa6, 0xa5, 0x21, 0x8b, 0xa7, 0xf8, 0x25, 0x41, 0x48, 0x62, + 0x5b, 0x01, 0x6c, 0x7c, 0x2a, 0xe8, 0xff, 0xf9, 0xf9, 0x1f, 0xe2, 0x79, 0x2e, 0xd1, + 0xff, 0xa3, 0x2e, 0x1c, 0x3a, 0x1a, 0x5d, 0x2b, 0x7b, 0x87, 0x25, 0x22, 0xa4, 0x90, + 0xea, 0x26, 0x9d, 0xdd, 0x13, 0x60, 0x4c, 0x10, 0x03, 0xf6, 0x99, 0xd3, 0x21, 0x0c, + 0x69, 0xc6, 0xd8, 0xc8, 0x9e, 0x94, 0x89, 0x51, 0x21, 0xe3, 0x9a, 0xcd, 0xda, 0x54, + 0x72, 0x64, 0xae, 0x94, 0x79, 0x36, 0x81, 0x44, 0x14, 0x6d, 0x3a, 0x0e, 0xa6, 0x30, + 0xbf, 0x95, 0x99, 0xa6, 0xf5, 0x7f, 0x4f, 0xef, 0xc6, 0x71, 0x2f, 0x36, 0x13, 0x14, + 0xa2, 0x9d, 0xc2, 0x0c, 0x0d, 0x4e, 0xc0, 0x02, 0xd3, 0x6f, 0xee, 0x98, 0x5e, 0x24, + 0x31, 0x74, 0x11, 0x96, 0x6e, 0x43, 0x57, 0xe8, 0x8e, 0xa0, 0x8d, 0x3d, 0x79, 0x38, + 0x20, 0xc2, 0x0f, 0xb4, 0x75, 0x99, 0x3b, 0xb1, 0xf0, 0xe8, 0xe1, 0xda, 0xf9, 0xd4, + 0xe6, 0xd6, 0xf4, 0x8a, 0x32, 0x4a, 0x4a, 0x25, 0xa8, 0xd9, 0x60, 0xd6, 0x33, 0x31, + 0x97, 0xb9, 0xb6, 0xed, 0x5f, 0xfc, 0x15, 0xbd, 0x13, 0xc0, 0x3a, 0x3f, 0x1f, 0x2d, + 0x09, 0x1d, 0xeb, 0x69, 0x6a, 0xfe, 0xd7, 0x95, 0x3e, 0x8a, 0x4e, 0xe1, 0x6e, 0x61, + 0xb2, 0x6c, 0xe3, 0x2b, 0x70, 0x60, 0x7e, 0x8c, 0xe4, 0xdd, 0x27, 0x30, 0x7e, 0x0d, + 0xc7, 0xb7, 0x9a, 0x1a, 0x3c, 0xcc, 0xa7, 0x22, 0x77, 0x14, 0x05, 0x50, 0x57, 0x31, + 0x1b, 0xc8, 0xbf, 0xce, 0x52, 0xaf, 0x9c, 0x8e, 0x10, 0x2e, 0xd2, 0x16, 0xb6, 0x6e, + 0x43, 0x10, 0xaf, 0x8b, 0xde, 0x1d, 0x60, 0xb2, 0x7d, 0xe6, 0x2f, 0x08, 0x10, 0x12, + 0x7e, 0xb4, 0x76, 0x45, 0xb6, 0xd8, 0x9b, 0x26, 0x40, 0xa1, 0x63, 0x5c, 0x7a, 0x2a, + 0xb1, 0x8c, 0xd6, 0xa4, 0x6f, 0x5a, 0xae, 0x33, 0x7e, 0x6d, 0x71, 0xf5, 0xc8, 0x6d, + 0x80, 0x1c, 0x35, 0xfc, 0x3f, 0xc1, 0xa6, 0xc6, 0x1a, 0x15, 0x04, 0x6d, 0x76, 0x38, + 0x32, 0x95, 0xb2, 0x51, 0x1a, 0xe9, 0x3e, 0x89, 0x9f, 0x0c, 0x79, + ]; + + let mut ctx = Sha3_256::new(); + ctx.update(&msg[..64]).unwrap(); + ctx.update(&msg[64..]).unwrap(); + + assert_eq!(ctx.finalize().unwrap().as_ref(), &expected_hash); + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + impl TestableStreamingContext<Digest> for Sha3_256 { + 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<Digest, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> { + Sha3_256::digest(input) + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual: Digest = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Sha3_256, state_2: &Sha3_256) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Sha3_256 = Sha3_256::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha3_256>::new( + initial_state, + SHA3_256_RATE, + ); + 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: Sha3_256 = Sha3_256::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha3_256>::new( + initial_state, + SHA3_256_RATE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::sha3::sha3_256::Sha3_256; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool { + let mut hasher_a = Sha3_256::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha3/sha3_384.rs b/vendor/orion/src/hazardous/hash/sha3/sha3_384.rs new file mode 100644 index 0000000..96ccdb3 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha3/sha3_384.rs @@ -0,0 +1,274 @@ +// MIT License + +// Copyright (c) 2023 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: +//! - `data`: The data to be hashed. +//! +//! # 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. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha3::sha3_384::Sha3_384; +//! +//! // Using the streaming interface +//! let mut state = Sha3_384::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha3_384::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha3_384::Sha3_384::update +//! [`reset()`]: sha3_384::Sha3_384::reset +//! [`finalize()`]: sha3_384::Sha3_384::finalize + +use crate::errors::UnknownCryptoError; +#[cfg(feature = "safe_api")] +use std::io; + +use super::Sha3; + +/// Rate of SHA3-384 (equivalent to blocksize in SHA2). +pub const SHA3_384_RATE: usize = 104; + +/// Output size of SHA3-384 in bytes. +pub const SHA3_384_OUTSIZE: usize = 48; + +construct_public! { + /// A type to represent the `Digest` that SHA3-384 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 48 bytes. + (Digest, test_digest, SHA3_384_OUTSIZE, SHA3_384_OUTSIZE) +} + +impl_from_trait!(Digest, SHA3_384_OUTSIZE); + +#[derive(Clone, Debug)] +/// SHA3-384 streaming state. +pub struct Sha3_384 { + pub(crate) _state: Sha3<SHA3_384_RATE>, +} + +impl Default for Sha3_384 { + fn default() -> Self { + Self::new() + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: hashing from a [`Read`](std::io::Read)er with SHA3-384. +/// ```rust +/// use orion::{ +/// hazardous::hash::sha3::sha3_384::{Sha3_384, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Sha3_384::new(); +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box<dyn std::error::Error>>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Sha3_384 { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Sha3_384::update`](crate::hazardous::hash::sha3::sha3_384::Sha3_384::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +impl Sha3_384 { + /// Initialize a `Sha3_384` struct. + pub fn new() -> Self { + Self { + _state: Sha3::<SHA3_384_RATE>::_new(96), + } + } + + /// 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) + } + + /// Finalize the hash and put the final digest into `dest`. + 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 SHA3-384 digest. + pub fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> { + let mut digest = [0u8; SHA3_384_OUTSIZE]; + self._finalize_internal(&mut digest)?; + + Ok(Digest::from(digest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Calculate a SHA3-384 digest of some `data`. + pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx.finalize() + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_default_equals_new() { + let new = Sha3_384::new(); + let default = Sha3_384::default(); + new._state.compare_state_to_other(&default._state); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = Sha3_384::new(); + let debug = format!("{:?}", initial_state); + let expected = "Sha3_384 { _state: State { state: [***OMITTED***], buffer: [***OMITTED***], capacity: 96, leftover: 0, is_finalized: false } }"; + assert_eq!(debug, expected); + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + impl TestableStreamingContext<Digest> for Sha3_384 { + 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<Digest, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> { + Sha3_384::digest(input) + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual: Digest = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Sha3_384, state_2: &Sha3_384) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Sha3_384 = Sha3_384::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha3_384>::new( + initial_state, + SHA3_384_RATE, + ); + 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: Sha3_384 = Sha3_384::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha3_384>::new( + initial_state, + SHA3_384_RATE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::sha3::sha3_384::Sha3_384; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool { + let mut hasher_a = Sha3_384::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha3/sha3_512.rs b/vendor/orion/src/hazardous/hash/sha3/sha3_512.rs new file mode 100644 index 0000000..d1b2432 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha3/sha3_512.rs @@ -0,0 +1,274 @@ +// MIT License + +// Copyright (c) 2023 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: +//! - `data`: The data to be hashed. +//! +//! # 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. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha3::sha3_512::Sha3_512; +//! +//! // Using the streaming interface +//! let mut state = Sha3_512::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha3_512::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha3_512::Sha3_512::update +//! [`reset()`]: sha3_512::Sha3_512::reset +//! [`finalize()`]: sha3_512::Sha3_512::finalize + +use crate::errors::UnknownCryptoError; +#[cfg(feature = "safe_api")] +use std::io; + +use super::Sha3; + +/// Rate of SHA3-512 (equivalent to blocksize in SHA2). +pub const SHA3_512_RATE: usize = 72; + +/// Output size of SHA3-512 in bytes. +pub const SHA3_512_OUTSIZE: usize = 64; + +construct_public! { + /// A type to represent the `Digest` that SHA3-512 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 64 bytes. + (Digest, test_digest, SHA3_512_OUTSIZE, SHA3_512_OUTSIZE) +} + +impl_from_trait!(Digest, SHA3_512_OUTSIZE); + +#[derive(Clone, Debug)] +/// SHA3-512 streaming state. +pub struct Sha3_512 { + pub(crate) _state: Sha3<SHA3_512_RATE>, +} + +impl Default for Sha3_512 { + fn default() -> Self { + Self::new() + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: hashing from a [`Read`](std::io::Read)er with SHA3-512. +/// ```rust +/// use orion::{ +/// hazardous::hash::sha3::sha3_512::{Sha3_512, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Sha3_512::new(); +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box<dyn std::error::Error>>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Sha3_512 { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Sha3_512::update`](crate::hazardous::hash::sha3::sha3_512::Sha3_512::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +impl Sha3_512 { + /// Initialize a `Sha3_512` struct. + pub fn new() -> Self { + Self { + _state: Sha3::<SHA3_512_RATE>::_new(128), + } + } + + /// 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) + } + + /// Finalize the hash and put the final digest into `dest`. + 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 SHA3-512 digest. + pub fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> { + let mut digest = [0u8; SHA3_512_OUTSIZE]; + self._finalize_internal(&mut digest)?; + + Ok(Digest::from(digest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Calculate a SHA3-512 digest of some `data`. + pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx.finalize() + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_default_equals_new() { + let new = Sha3_512::new(); + let default = Sha3_512::default(); + new._state.compare_state_to_other(&default._state); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = Sha3_512::new(); + let debug = format!("{:?}", initial_state); + let expected = "Sha3_512 { _state: State { state: [***OMITTED***], buffer: [***OMITTED***], capacity: 128, leftover: 0, is_finalized: false } }"; + assert_eq!(debug, expected); + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + impl TestableStreamingContext<Digest> for Sha3_512 { + 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<Digest, UnknownCryptoError> { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> { + Sha3_512::digest(input) + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual: Digest = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Sha3_512, state_2: &Sha3_512) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Sha3_512 = Sha3_512::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha3_512>::new( + initial_state, + SHA3_512_RATE, + ); + 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: Sha3_512 = Sha3_512::new(); + + let test_runner = StreamingContextConsistencyTester::<Digest, Sha3_512>::new( + initial_state, + SHA3_512_RATE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::sha3::sha3_512::Sha3_512; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool { + let mut hasher_a = Sha3_512::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} diff --git a/vendor/orion/src/hazardous/kdf/argon2i.rs b/vendor/orion/src/hazardous/kdf/argon2i.rs new file mode 100644 index 0000000..840c5b8 --- /dev/null +++ b/vendor/orion/src/hazardous/kdf/argon2i.rs @@ -0,0 +1,2607 @@ +// MIT License + +// Copyright (c) 2020-2023 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. + +//! # About: +//! Argon2i version 1.3. This implementation is available with features `safe_api` and `alloc`. +//! +//! # Note: +//! This implementation only supports a single thread/lane. +//! +//! # Parameters: +//! - `expected`: The expected derived key. +//! - `password`: Password. +//! - `salt`: Salt value. +//! - `iterations`: Iteration count. +//! - `memory`: Memory size in kibibytes (KiB). +//! - `secret`: Optional secret value used for hashing. +//! - `ad`: Optional associated data used for hashing. +//! - `dst_out`: Destination buffer for the derived key. The length of the +//! derived key is implied by the length of `dst_out`. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of the `password` is greater than [`u32::MAX`]. +//! - The length of the `salt` is greater than [`u32::MAX`] or less than `8`. +//! - The length of the `secret` is greater than [`u32::MAX`]. +//! - The length of the `ad` is greater than [`u32::MAX`]. +//! - The length of `dst_out` is greater than [`u32::MAX`] or less than `4`. +//! - `iterations` is less than `1`. +//! - `memory` is less than `8`. +//! - The hashed password does not match the expected when verifying. +//! +//! # Panics: +//! A panic will occur if: +//! +//! # Security: +//! - Salts should always be generated using a CSPRNG. +//! [`secure_rand_bytes()`] can be used for this. +//! - The minimum recommended length for a salt is `16` bytes. +//! - The minimum recommended length for a hashed password is `16` bytes. +//! - The minimum recommended iteration count is `3`. +//! - Password hashes should always be compared in constant-time. +//! - Please note that when verifying, a copy of the computed password hash is placed into +//! `dst_out`. If the derived hash is considered sensitive and you want to provide defense +//! in depth against an attacker reading your application's private memory, then you as +//! the user are responsible for zeroing out this buffer (see the [`zeroize` crate]). +//! +//! The cost parameters were the recommended values at time of writing. Please be sure to also check +//! [OWASP] for the latest recommended values. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::{hazardous::kdf::argon2i, util}; +//! +//! let mut salt = [0u8; 16]; +//! util::secure_rand_bytes(&mut salt)?; +//! let password = b"Secret password"; +//! let mut dst_out = [0u8; 64]; +//! +//! argon2i::derive_key(password, &salt, 3, 1<<16, None, None, &mut dst_out)?; +//! +//! let expected_dk = dst_out; +//! +//! assert!(argon2i::verify( +//! &expected_dk, +//! password, +//! &salt, +//! 3, +//! 1<<16, +//! None, +//! None, +//! &mut dst_out +//! ) +//! .is_ok()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`secure_rand_bytes()`]: crate::util::secure_rand_bytes +//! [`zeroize` crate]: https://crates.io/crates/zeroize +//! [OWASP]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html + +use crate::errors::UnknownCryptoError; +use crate::hazardous::hash::blake2::blake2b::Blake2b; +use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE; +use crate::util; +use crate::util::endianness::{load_u64_into_le, store_u64_into_le}; +use zeroize::Zeroize; + +/// The Argon2 version (0x13). +pub const ARGON2_VERSION: u32 = 0x13; + +/// The Argon2 variant (i). +pub const ARGON2_VARIANT: u32 = 1; + +/// The amount of segments per lane, as defined in the spec. +const SEGMENTS_PER_LANE: usize = 4; + +/// The amount of lanes supported. +pub(crate) const LANES: u32 = 1; + +/// The minimum amount of memory. +pub(crate) const MIN_MEMORY: u32 = 8 * LANES; + +/// The minimum amount of iterations. +pub(crate) const MIN_ITERATIONS: u32 = 1; + +const fn lower_mult_add(x: u64, y: u64) -> u64 { + let mask = 0xFFFF_FFFFu64; + let x_l = x & mask; + let y_l = y & mask; + let xy = x_l.wrapping_mul(y_l); + x.wrapping_add(y.wrapping_add(xy.wrapping_add(xy))) +} + +/// BLAKE2 G with 64-bit multiplications. +fn g(a: &mut u64, b: &mut u64, c: &mut u64, d: &mut u64) { + *a = lower_mult_add(*a, *b); + *d = (*d ^ *a).rotate_right(32); + *c = lower_mult_add(*c, *d); + *b = (*b ^ *c).rotate_right(24); + *a = lower_mult_add(*a, *b); + *d = (*d ^ *a).rotate_right(16); + *c = lower_mult_add(*c, *d); + *b = (*b ^ *c).rotate_right(63); +} + +#[allow(clippy::too_many_arguments)] +fn permutation_p( + v0: &mut u64, + v1: &mut u64, + v2: &mut u64, + v3: &mut u64, + v4: &mut u64, + v5: &mut u64, + v6: &mut u64, + v7: &mut u64, + v8: &mut u64, + v9: &mut u64, + v10: &mut u64, + v11: &mut u64, + v12: &mut u64, + v13: &mut u64, + v14: &mut u64, + v15: &mut u64, +) { + g(v0, v4, v8, v12); + g(v1, v5, v9, v13); + g(v2, v6, v10, v14); + g(v3, v7, v11, v15); + g(v0, v5, v10, v15); + g(v1, v6, v11, v12); + g(v2, v7, v8, v13); + g(v3, v4, v9, v14); +} + +/// H0 as defined in the specification. +fn initial_hash( + hash_length: u32, + memory_kib: u32, + passes: u32, + p: &[u8], + s: &[u8], + k: &[u8], + x: &[u8], +) -> Result<[u8; 72], UnknownCryptoError> { + // We save additional 8 bytes in H0 for when the first two blocks are processed, + // so that this may contain two little-endian integers. + let mut h0 = [0u8; 72]; + let mut hasher = Blake2b::new(BLAKE2B_OUTSIZE)?; + + // Collect the first part to reduce times we update the hasher state. + h0[0..4].copy_from_slice(&LANES.to_le_bytes()); + h0[4..8].copy_from_slice(&hash_length.to_le_bytes()); + h0[8..12].copy_from_slice(&memory_kib.to_le_bytes()); + h0[12..16].copy_from_slice(&passes.to_le_bytes()); + h0[16..20].copy_from_slice(&ARGON2_VERSION.to_le_bytes()); + h0[20..24].copy_from_slice(&ARGON2_VARIANT.to_le_bytes()); + h0[24..28].copy_from_slice(&(p.len() as u32).to_le_bytes()); + + hasher.update(&h0[..28])?; + hasher.update(p)?; + hasher.update(&(s.len() as u32).to_le_bytes())?; + hasher.update(s)?; + hasher.update(&(k.len() as u32).to_le_bytes())?; + hasher.update(k)?; + hasher.update(&(x.len() as u32).to_le_bytes())?; + hasher.update(x)?; + h0[0..BLAKE2B_OUTSIZE].copy_from_slice(hasher.finalize()?.as_ref()); + + Ok(h0) +} + +/// H' as defined in the specification. +fn extended_hash(input: &[u8], dst: &mut [u8]) -> Result<(), UnknownCryptoError> { + if dst.is_empty() { + return Err(UnknownCryptoError); + } + + let outlen = dst.len() as u32; + + if dst.len() <= BLAKE2B_OUTSIZE { + let mut ctx = Blake2b::new(dst.len())?; + ctx.update(&outlen.to_le_bytes())?; + ctx.update(input)?; + dst.copy_from_slice(ctx.finalize()?.as_ref()); + } else { + let mut ctx = Blake2b::new(BLAKE2B_OUTSIZE)?; + ctx.update(&outlen.to_le_bytes())?; + ctx.update(input)?; + + let mut tmp = ctx.finalize()?; + dst[..BLAKE2B_OUTSIZE].copy_from_slice(tmp.as_ref()); + + let mut pos = BLAKE2B_OUTSIZE / 2; + let mut toproduce = dst.len() - BLAKE2B_OUTSIZE / 2; + + while toproduce > BLAKE2B_OUTSIZE { + ctx.reset()?; + ctx.update(tmp.as_ref())?; + tmp = ctx.finalize()?; + + dst[pos..(pos + BLAKE2B_OUTSIZE)].copy_from_slice(tmp.as_ref()); + pos += BLAKE2B_OUTSIZE / 2; + toproduce -= BLAKE2B_OUTSIZE / 2; + } + + ctx = Blake2b::new(toproduce)?; + ctx.update(tmp.as_ref())?; + tmp = ctx.finalize()?; + dst[pos..outlen as usize].copy_from_slice(&tmp.as_ref()[..toproduce]); + } + + Ok(()) +} + +#[rustfmt::skip] +fn fill_block(w: &mut [u64; 128]) { + + let mut v0: u64; let mut v1: u64; let mut v2: u64; let mut v3: u64; + let mut v4: u64; let mut v5: u64; let mut v6: u64; let mut v7: u64; + let mut v8: u64; let mut v9: u64; let mut v10: u64; let mut v11: u64; + let mut v12: u64; let mut v13: u64; let mut v14: u64; let mut v15: u64; + + let mut idx = 0; + + // Operate on columns. + while idx < 128 { + v0 = w[idx ]; v1 = w[idx + 1]; v2 = w[idx + 2]; v3 = w[idx + 3]; + v4 = w[idx + 4]; v5 = w[idx + 5]; v6 = w[idx + 6]; v7 = w[idx + 7]; + v8 = w[idx + 8]; v9 = w[idx + 9]; v10 = w[idx + 10]; v11 = w[idx + 11]; + v12 = w[idx + 12]; v13 = w[idx + 13]; v14 = w[idx + 14]; v15 = w[idx + 15]; + + permutation_p( + &mut v0, &mut v1, &mut v2, &mut v3, + &mut v4, &mut v5, &mut v6, &mut v7, + &mut v8, &mut v9, &mut v10, &mut v11, + &mut v12, &mut v13, &mut v14, &mut v15 + ); + + w[idx ] = v0; w[idx + 1] = v1; w[idx + 2] = v2; w[idx + 3] = v3; + w[idx + 4] = v4; w[idx + 5] = v5; w[idx + 6] = v6; w[idx + 7] = v7; + w[idx + 8] = v8; w[idx + 9] = v9; w[idx + 10] = v10; w[idx + 11] = v11; + w[idx + 12] = v12; w[idx + 13] = v13; w[idx + 14] = v14; w[idx + 15] = v15; + + idx += 16; + } + + idx = 0; + // Operate on rows. + while idx < 16 { + v0 = w[idx ]; v1 = w[idx + 1]; v2 = w[idx + 16]; v3 = w[idx + 17]; + v4 = w[idx + 32]; v5 = w[idx + 33]; v6 = w[idx + 48]; v7 = w[idx + 49]; + v8 = w[idx + 64]; v9 = w[idx + 65]; v10 = w[idx + 80]; v11 = w[idx + 81]; + v12 = w[idx + 96]; v13 = w[idx + 97]; v14 = w[idx + 112]; v15 = w[idx + 113]; + + permutation_p( + &mut v0, &mut v1, &mut v2, &mut v3, + &mut v4, &mut v5, &mut v6, &mut v7, + &mut v8, &mut v9, &mut v10, &mut v11, + &mut v12, &mut v13, &mut v14, &mut v15 + ); + + w[idx ] = v0; w[idx + 1] = v1; w[idx + 16] = v2; w[idx + 17] = v3; + w[idx + 32] = v4; w[idx + 33] = v5; w[idx + 48] = v6; w[idx + 49] = v7; + w[idx + 64] = v8; w[idx + 65] = v9; w[idx + 80] = v10; w[idx + 81] = v11; + w[idx + 96] = v12; w[idx + 97] = v13; w[idx + 112] = v14; w[idx + 113] = v15; + + idx += 2; + } +} + +/// Data-independent indexing. +struct Gidx { + block: [u64; 128], + addresses: [u64; 128], + segment_length: u32, + offset: u32, +} + +impl Gidx { + fn new(blocks: u32, passes: u32, segment_length: u32) -> Self { + let mut block = [0u64; 128]; + block[1] = 0u64; // Lane number, we only support one (0u64). + block[3] = u64::from(blocks); + block[4] = u64::from(passes); + block[5] = u64::from(ARGON2_VARIANT); // The Argon2i variant + + Self { + block, + addresses: [0u64; 128], + segment_length, + offset: 0, + } + } + + fn init(&mut self, pass_n: u32, segment_n: u32, offset: u32, tmp_block: &mut [u64; 128]) { + self.block[0] = u64::from(pass_n); + self.block[2] = u64::from(segment_n); + self.block[6] = 0u64; // Counter + self.offset = offset; + + self.next_addresses(tmp_block); + + // The existing values in self.addresses are not read + // when generating a new address block. Therefor we + // do not have to zero it out. + } + + fn next_addresses(&mut self, tmp_block: &mut [u64; 128]) { + self.block[6] += 1; + // G-two operation + tmp_block.copy_from_slice(&self.block); + fill_block(tmp_block); + xor_slices!(self.block, tmp_block); + + self.addresses.copy_from_slice(tmp_block); + fill_block(&mut self.addresses); + xor_slices!(tmp_block, self.addresses); + } + + fn get_next(&mut self, segment_idx: u32, tmp_block: &mut [u64; 128]) -> u32 { + // We get J1 and discard J2, as J2 is only relevant if we had more than + // a single lane. + let j1: u64 = self.addresses[self.offset as usize] & 0xFFFF_FFFFu64; + self.offset = (self.offset + 1) % 128; // Wrap-around on block length. + if self.offset == 0 { + self.next_addresses(tmp_block); + } + + // The Argon2 specification for this version (1.3) does not conform + // to the official reference implementation. This implementation follows + // the reference implementation and ignores the specification where they + // disagree. See https://github.com/P-H-C/phc-winner-argon2/issues/183. + + let n_blocks = self.block[3] as u32; + let pass_n = self.block[0] as u32; + let segment_n = self.block[2] as u32; + + let ref_start_pos: u32 = if pass_n == 0 && segment_n == 0 { + segment_idx - 1 + } else if pass_n == 0 { + segment_n * self.segment_length + segment_idx - 1 + } else { + n_blocks - self.segment_length + segment_idx - 1 + }; + + let mut ref_pos: u64 = (j1 * j1) >> 32; + ref_pos = (ref_start_pos as u64 * ref_pos) >> 32; + ref_pos = (ref_start_pos as u64 - 1) - ref_pos; + + if pass_n == 0 || segment_n == 3 { + ref_pos as u32 % n_blocks + } else { + (self.segment_length * (segment_n + 1) + ref_pos as u32) % n_blocks + } + } +} + +#[allow(clippy::too_many_arguments)] +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Argon2i password hashing function as described in the [P-H-C specification](https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf). +pub fn derive_key( + password: &[u8], + salt: &[u8], + iterations: u32, + memory: u32, + secret: Option<&[u8]>, + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if password.len() > 0xFFFF_FFFF { + return Err(UnknownCryptoError); + } + if salt.len() > 0xFFFF_FFFF || salt.len() < 8 { + return Err(UnknownCryptoError); + } + if iterations < MIN_ITERATIONS { + return Err(UnknownCryptoError); + } + if memory < MIN_MEMORY { + return Err(UnknownCryptoError); + } + + let k = match secret { + Some(n_val) => { + if n_val.len() > 0xFFFF_FFFF { + return Err(UnknownCryptoError); + } + + n_val + } + None => &[0u8; 0], + }; + + let x = match ad { + Some(n_val) => { + if n_val.len() > 0xFFFF_FFFF { + return Err(UnknownCryptoError); + } + + n_val + } + None => &[0u8; 0], + }; + + if dst_out.len() > 0xFFFF_FFFF || dst_out.len() < 4 { + return Err(UnknownCryptoError); + } + + // Round down to 4 * p threads + let n_blocks = memory - (memory & 3); + // Divide by 4 (SEGMENTS_PER_LANE) + let segment_length = n_blocks >> 2; + + let mut blocks = vec![[0u64; 128]; n_blocks as usize]; + + // Fill first two blocks + let mut h0 = initial_hash( + dst_out.len() as u32, + memory, + iterations, + password, + salt, + k, + x, + )?; + let mut tmp = [0u8; 1024]; + debug_assert_eq!( + h0.len(), + ((core::mem::size_of::<u32>() * 2) + BLAKE2B_OUTSIZE) + ); + debug_assert!( + h0[BLAKE2B_OUTSIZE..(BLAKE2B_OUTSIZE + core::mem::size_of::<u32>())] + == [0u8; core::mem::size_of::<u32>()] + ); // Block 0 + debug_assert!( + h0[BLAKE2B_OUTSIZE + core::mem::size_of::<u32>()..] == [0u8; core::mem::size_of::<u32>()] + ); // Lane + + // H' into the first two blocks + extended_hash(&h0, &mut tmp)?; + load_u64_into_le(&tmp, &mut blocks[0]); + h0[BLAKE2B_OUTSIZE..(BLAKE2B_OUTSIZE + core::mem::size_of::<u32>())] + .copy_from_slice(&1u32.to_le_bytes()); // Block 1 + extended_hash(&h0, &mut tmp)?; + load_u64_into_le(&tmp, &mut blocks[1]); + + let mut gidx = Gidx::new(n_blocks, iterations, segment_length); + let mut working_block = [0u64; 128]; + + for pass_n in 0..iterations as usize { + for segment_n in 0..SEGMENTS_PER_LANE { + let offset = match (pass_n, segment_n) { + (0, 0) => 2, // The first two blocks have already been processed + _ => 0, + }; + + gidx.init(pass_n as u32, segment_n as u32, offset, &mut working_block); + + for segment_idx in offset..segment_length { + let reference_idx = gidx.get_next(segment_idx, &mut working_block); + let current_idx = segment_n as u32 * segment_length + segment_idx; + let previous_idx = if current_idx > 0 { + current_idx - 1 + } else { + n_blocks - 1 + }; + + let prev_b = blocks.get(previous_idx as usize).unwrap(); + let ref_b = blocks.get(reference_idx as usize).unwrap(); + + // G-xor operation + for (el_tmp, (el_prev, el_ref)) in working_block + .iter_mut() + .zip(prev_b.iter().zip(ref_b.iter())) + { + *el_tmp = el_prev ^ el_ref; + } + let cur_b = blocks.get_mut(current_idx as usize).unwrap(); + xor_slices!(working_block, cur_b); + fill_block(&mut working_block); + xor_slices!(working_block, cur_b); + } + } + } + + store_u64_into_le(blocks.get(n_blocks as usize - 1).unwrap(), &mut tmp); + extended_hash(&tmp, dst_out)?; + + working_block.zeroize(); + tmp.zeroize(); + h0.zeroize(); + for block in blocks.iter_mut() { + block.zeroize(); + } + + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Verify Argon2i derived key in constant time. +pub fn verify( + expected: &[u8], + password: &[u8], + salt: &[u8], + iterations: u32, + memory: u32, + secret: Option<&[u8]>, + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + derive_key(password, salt, iterations, memory, secret, ad, dst_out)?; + util::secure_cmp(dst_out, expected) +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[cfg(feature = "safe_api")] + mod test_verify { + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_test_same_input_verify_true( + hlen: u32, + kib: u32, + p: Vec<u8>, + s: Vec<u8>, + k: Vec<u8>, + x: Vec<u8>, + ) -> bool { + let passes = 1; + let mem = if !(8..=4096).contains(&kib) { + 1024 + } else { + kib + }; + let salt = if s.len() < 8 { vec![37u8; 8] } else { s }; + + let mut dst_out = if !(4..=512).contains(&hlen) { + vec![0u8; 32] + } else { + vec![0u8; hlen as usize] + }; + + let mut dst_out_verify = dst_out.clone(); + derive_key(&p, &salt, passes, mem, Some(&k), Some(&x), &mut dst_out).unwrap(); + + verify( + &dst_out, + &p, + &salt, + passes, + mem, + Some(&k), + Some(&x), + &mut dst_out_verify, + ) + .is_ok() + } + } + + mod test_derive_key { + use super::*; + + #[test] + fn test_invalid_mem() { + // mem must be at least 8p, where p == threads (1) + let mut dst_out = [0u8; 32]; + assert!(derive_key(&[], &[0u8; 8], 1, 9, None, None, &mut dst_out).is_ok()); + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out).is_ok()); + assert!(derive_key(&[], &[0u8; 8], 1, 7, None, None, &mut dst_out).is_err()); + } + + #[test] + fn test_invalid_passes() { + let mut dst_out = [0u8; 32]; + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out).is_ok()); + assert!(derive_key(&[], &[0u8; 8], 0, 8, None, None, &mut dst_out).is_err()); + } + + #[test] + fn test_dst_out() { + let mut dst_out_less = [0u8; 3]; + let mut dst_out_exact = [0u8; 4]; + let mut dst_out_above = [0u8; 5]; + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out_less).is_err()); + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out_exact).is_ok()); + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out_above).is_ok()); + } + + #[test] + fn test_invalid_salt() { + let mut dst_out = [0u8; 32]; + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out).is_ok()); + assert!(derive_key(&[], &[0u8; 9], 1, 8, None, None, &mut dst_out).is_ok()); + assert!(derive_key(&[], &[0u8; 7], 1, 8, None, None, &mut dst_out).is_err()); + } + + #[test] + fn test_some_or_none_same_result() { + let mut dst_one = [0u8; 32]; + let mut dst_two = [0u8; 32]; + + derive_key(&[255u8; 16], &[1u8; 16], 1, 8, None, None, &mut dst_one).unwrap(); + derive_key( + &[255u8; 16], + &[1u8; 16], + 1, + 8, + Some(&[]), + Some(&[]), + &mut dst_two, + ) + .unwrap(); + + assert_eq!(dst_one, dst_two); + } + + #[test] + fn test_hash_1() { + let mem = 4096; + let passes = 3; + let p = [ + 191, 68, 49, 232, 45, 162, 83, 188, 177, 167, 232, 149, 172, 236, 153, 8, 237, 115, + 232, 128, 171, 254, 47, 84, 192, 208, 196, 121, 127, 221, 93, 126, + ]; + let s = [ + 52, 225, 42, 12, 59, 186, 118, 248, 198, 12, 16, 189, 191, 167, 211, 42, 89, 170, + 108, 9, 172, 4, 138, 232, 239, 58, 189, 238, 250, 33, 230, 130, + ]; + let k = [ + 124, 169, 187, 230, 55, 69, 29, 225, 228, 147, 41, 248, 255, 98, 195, 221, 202, 40, + 58, 17, 93, 122, 37, 57, 169, 9, 80, 64, 170, 177, 33, 89, + ]; + let x = [ + 129, 251, 22, 14, 88, 173, 198, 8, 123, 139, 94, 203, 61, 50, 174, 20, 153, 43, + 109, 154, 46, 8, 71, 4, 208, 83, 157, 133, 143, 171, 78, 195, + ]; + + let expected = [ + 234, 181, 45, 90, 214, 219, 1, 146, 196, 60, 104, 29, 152, 103, 82, 77, 65, 214, + 212, 55, 121, 228, 57, 189, 202, 44, 100, 103, 180, 24, 125, 50, + ]; + + let mut actual = [0u8; 32]; + derive_key(&p, &s, passes, mem, Some(&k), Some(&x), &mut actual).unwrap(); + + assert_eq!(expected.len(), actual.len()); + assert_eq!(expected.as_ref(), &actual[..]); + } + + #[test] + fn test_hash_2() { + let mem = 4096; + let passes = 3; + let p = [ + 99, 137, 197, 238, 38, 112, 35, 125, 195, 31, 121, 180, 52, 30, 19, 20, 198, 227, + 198, 9, 66, 209, 130, 225, 200, 43, 50, 221, 47, 59, 169, 160, 220, 64, 54, 202, + 55, 244, 226, 8, 225, 183, 155, 186, 56, 162, 30, 15, 12, 176, 15, 182, 243, 175, + 24, 142, 80, 247, 2, 210, 208, 57, 28, 59, + ]; + let s = [ + 94, 32, 63, 147, 115, 103, 179, 120, 17, 232, 110, 157, 92, 70, 77, 157, 82, 46, + 79, 122, 29, 191, 104, 146, 125, 208, 48, 24, 6, 8, 94, 196, 65, 238, 136, 255, + 180, 172, 187, 23, 214, 55, 18, 84, 171, 217, 253, 6, 51, 89, 173, 55, 222, 190, + 71, 183, 135, 156, 229, 77, 67, 78, 96, 90, + ]; + let k = [ + 18, 85, 39, 122, 166, 85, 120, 191, 243, 15, 174, 215, 32, 185, 255, 88, 134, 238, + 227, 159, 77, 121, 149, 134, 255, 105, 240, 88, 150, 252, 94, 158, + ]; + let x = [ + 240, 249, 197, 139, 242, 216, 12, 130, 192, 73, 44, 189, 130, 17, 225, 223, 135, + 30, 139, 255, 164, 168, 69, 140, 216, 121, 225, 194, 107, 123, 143, 120, 30, 131, + 216, 196, 200, 81, 71, 203, 26, 66, 171, 118, 236, 26, 18, 105, 100, 35, 227, 184, + 16, 108, 224, 222, 186, 255, 32, 112, 189, 13, 151, 22, + ]; + + let expected = [ + 233, 201, 213, 214, 244, 141, 73, 220, 67, 19, 106, 102, 181, 1, 197, 122, 20, 147, + 99, 245, 236, 160, 3, 213, 22, 219, 155, 217, 194, 24, 65, 204, 239, 56, 34, 160, + 140, 114, 3, 191, 247, 48, 64, 79, 125, 154, 52, 185, 0, 69, 102, 85, 183, 242, + 167, 198, 170, 1, 124, 235, 235, 3, 184, 75, + ]; + let mut actual = [0u8; 64]; + derive_key(&p, &s, passes, mem, Some(&k), Some(&x), &mut actual).unwrap(); + + assert_eq!(expected.len(), actual.len()); + assert_eq!(expected.as_ref(), &actual[..]); + } + + #[test] + fn test_hash_3() { + let mem = 4096; + let passes = 3; + let p = [ + 175, 90, 106, 212, 141, 72, 174, 254, 80, 27, 64, 221, 238, 102, 191, 242, 3, 221, + 202, 111, 10, 217, 92, 143, 15, 200, 215, 210, 199, 59, 200, 59, 98, 141, 106, 228, + 166, 184, 5, 212, 172, 114, 32, 229, 179, 111, 227, 116, 216, 95, 164, 35, 31, 204, + 132, 215, 116, 156, 32, 7, 165, 15, 231, 148, 124, 95, 115, 150, 82, 168, 34, 154, + 52, 166, 104, 28, 207, 61, 162, 198, 228, 72, 196, 200, 228, 251, 124, 231, 44, + 151, 44, 44, 24, 70, 103, 169, 44, 240, 248, 75, 208, 142, 112, 36, 25, 49, 252, + 196, 121, 203, 74, 155, 220, 58, 89, 106, 82, 9, 177, 66, 30, 12, 245, 153, 116, + 72, 233, 233, + ]; + let s = [ + 205, 79, 28, 236, 67, 87, 187, 158, 25, 126, 159, 75, 129, 34, 145, 131, 219, 240, + 243, 73, 42, 223, 247, 12, 223, 190, 218, 212, 26, 109, 15, 151, 118, 77, 210, 18, + 171, 176, 224, 127, 48, 189, 216, 225, 175, 45, 205, 3, 196, 231, 185, 203, 127, + 210, 185, 238, 122, 197, 147, 147, 35, 231, 182, 42, 254, 104, 201, 72, 213, 122, + 46, 24, 21, 80, 26, 221, 252, 117, 51, 208, 107, 147, 24, 3, 72, 222, 32, 95, 20, + 107, 111, 47, 21, 43, 174, 153, 57, 154, 199, 208, 182, 89, 36, 91, 111, 117, 1, + 254, 254, 178, 239, 204, 146, 170, 34, 121, 126, 143, 63, 88, 94, 7, 155, 2, 126, + 79, 43, 183, + ]; + let k = [ + 196, 222, 200, 1, 157, 90, 55, 246, 173, 195, 253, 212, 118, 186, 31, 189, 35, 154, + 202, 137, 60, 32, 56, 89, 179, 44, 105, 140, 185, 225, 111, 242, + ]; + let x = [ + 28, 43, 133, 219, 80, 24, 121, 131, 89, 41, 81, 230, 215, 79, 73, 60, 59, 206, 46, + 22, 241, 113, 205, 178, 219, 91, 159, 220, 225, 106, 152, 3, 187, 167, 148, 23, + 143, 89, 43, 253, 188, 87, 150, 154, 249, 44, 189, 0, 77, 237, 69, 112, 56, 71, + 131, 235, 63, 141, 7, 202, 20, 247, 110, 221, 28, 72, 38, 209, 210, 171, 163, 51, + 42, 6, 54, 121, 208, 125, 160, 105, 81, 196, 237, 22, 206, 140, 35, 89, 160, 102, + 214, 22, 105, 14, 113, 54, 96, 33, 68, 149, 253, 82, 1, 222, 90, 224, 99, 228, 219, + 230, 5, 103, 235, 206, 183, 230, 163, 177, 51, 187, 200, 207, 244, 203, 197, 56, + 24, 132, + ]; + + let expected = [ + 37, 212, 93, 127, 114, 22, 107, 220, 94, 83, 173, 159, 101, 143, 232, 110, 49, 192, + 93, 236, 251, 92, 209, 91, 145, 162, 48, 21, 140, 49, 59, 155, 48, 116, 129, 197, + 223, 12, 34, 240, 209, 200, 152, 9, 112, 175, 206, 35, 166, 229, 230, 13, 110, 89, + 211, 60, 28, 174, 248, 142, 43, 38, 87, 72, 175, 177, 244, 186, 81, 55, 111, 238, + 151, 206, 243, 145, 247, 255, 50, 126, 9, 250, 28, 80, 194, 144, 17, 173, 42, 222, + 251, 125, 11, 130, 169, 17, 17, 112, 45, 66, 129, 118, 96, 67, 36, 252, 61, 156, + 239, 121, 204, 210, 74, 162, 220, 212, 33, 239, 201, 77, 80, 231, 7, 238, 92, 151, + 51, 17, + ]; + + let mut actual = [0u8; 128]; + derive_key(&p, &s, passes, mem, Some(&k), Some(&x), &mut actual).unwrap(); + + assert_eq!(expected.len(), actual.len()); + assert_eq!(expected.as_ref(), &actual[..]); + } + + #[test] + fn test_hash_4() { + let mem = 4096; + let passes = 3; + let p = [ + 129, 27, 156, 146, 197, 105, 114, 238, 251, 207, 14, 230, 59, 139, 249, 192, 99, + 228, 195, 63, 0, 133, 24, 243, 246, 198, 53, 3, 56, 81, 225, 43, 229, 192, 145, 23, + 31, 111, 106, 70, 77, 118, 182, 116, 107, 49, 237, 149, 237, 163, 136, 12, 26, 73, + 70, 124, 87, 238, 250, 1, 79, 136, 101, 184, 173, 23, 79, 182, 171, 60, 249, 231, + 55, 169, 114, 44, 73, 163, 82, 190, 186, 224, 86, 18, 155, 129, 29, 86, 213, 238, + 217, 185, 25, 14, 86, 107, 104, 223, 89, 78, 168, 29, 96, 209, 116, 35, 224, 208, + 249, 106, 161, 58, 220, 236, 76, 142, 67, 235, 10, 79, 209, 49, 147, 59, 187, 0, + 18, 203, 124, 97, 30, 250, 208, 228, 93, 42, 161, 54, 172, 53, 64, 220, 141, 140, + 161, 12, 106, 225, 212, 66, 79, 117, 83, 228, 213, 194, 217, 90, 0, 197, 115, 31, + 194, 40, 215, 247, 45, 20, 15, 65, 9, 78, 108, 186, 76, 167, 18, 87, 214, 202, 31, + 135, 80, 188, 133, 219, 64, 254, 24, 181, 113, 154, 229, 62, 54, 242, 236, 71, 202, + 79, 233, 195, 46, 255, 10, 166, 193, 79, 211, 11, 85, 250, 191, 92, 94, 191, 85, + 219, 133, 33, 211, 134, 90, 88, 239, 179, 167, 199, 154, 163, 213, 214, 63, 25, + 245, 75, 237, 2, 76, 240, 56, 155, 153, 53, 241, 72, 250, 129, 121, 139, 205, 112, + 38, 137, 12, 8, + ]; + let s = [ + 189, 41, 181, 148, 99, 179, 60, 75, 245, 112, 106, 213, 213, 78, 183, 229, 167, 54, + 98, 246, 27, 90, 214, 60, 178, 63, 130, 229, 150, 254, 141, 126, 99, 182, 108, 217, + 134, 102, 245, 53, 136, 72, 159, 194, 239, 7, 238, 82, 96, 198, 218, 50, 80, 3, + 234, 33, 18, 68, 33, 114, 146, 158, 129, 148, 38, 200, 203, 26, 163, 3, 46, 76, + 141, 41, 14, 106, 169, 246, 216, 25, 125, 226, 110, 78, 183, 228, 119, 135, 186, + 151, 117, 32, 220, 221, 54, 19, 142, 73, 92, 167, 191, 28, 46, 244, 171, 254, 80, + 251, 245, 135, 148, 32, 167, 118, 151, 129, 27, 197, 127, 77, 165, 130, 56, 189, + 43, 69, 150, 27, 166, 224, 79, 44, 145, 144, 163, 83, 176, 39, 103, 199, 175, 234, + 30, 202, 87, 229, 38, 238, 61, 9, 82, 247, 136, 122, 94, 157, 110, 58, 198, 137, + 47, 238, 207, 72, 178, 225, 197, 37, 45, 139, 121, 24, 49, 202, 119, 58, 127, 2, + 109, 214, 104, 16, 2, 118, 238, 109, 206, 208, 105, 101, 145, 146, 152, 239, 176, + 59, 32, 224, 220, 231, 135, 11, 173, 222, 65, 154, 206, 130, 164, 243, 113, 40, 33, + 208, 85, 166, 3, 134, 139, 104, 204, 181, 43, 192, 24, 70, 249, 128, 94, 185, 217, + 163, 133, 2, 112, 38, 117, 159, 48, 145, 46, 194, 177, 73, 43, 108, 158, 195, 228, + 32, 122, 21, 185, 109, 107, 235, + ]; + let k = [ + 85, 248, 24, 79, 146, 124, 133, 99, 94, 108, 110, 97, 217, 184, 249, 109, 60, 209, + 248, 195, 45, 90, 90, 31, 61, 11, 202, 90, 122, 99, 155, 197, + ]; + let x = [ + 42, 203, 81, 203, 86, 170, 17, 4, 219, 64, 68, 44, 196, 213, 234, 193, 172, 102, + 173, 159, 41, 0, 43, 6, 149, 224, 135, 50, 224, 63, 104, 211, 226, 97, 11, 219, 95, + 46, 246, 231, 106, 107, 221, 60, 113, 107, 119, 53, 177, 70, 45, 54, 229, 165, 118, + 165, 87, 246, 180, 84, 90, 75, 122, 29, 147, 148, 250, 64, 189, 116, 238, 40, 35, + 228, 126, 242, 39, 64, 153, 67, 166, 8, 197, 0, 36, 189, 182, 171, 85, 202, 134, + 251, 73, 198, 32, 243, 17, 51, 239, 5, 100, 88, 35, 137, 190, 170, 158, 48, 45, + 144, 215, 173, 199, 235, 124, 133, 131, 117, 181, 211, 16, 124, 171, 174, 113, 189, + 79, 86, 86, 103, 223, 47, 167, 97, 85, 219, 224, 70, 160, 214, 45, 85, 249, 70, + 166, 179, 174, 53, 174, 127, 132, 238, 203, 238, 154, 25, 149, 102, 132, 183, 44, + 71, 238, 155, 158, 135, 193, 80, 115, 115, 38, 80, 161, 117, 145, 68, 48, 158, 125, + 12, 23, 230, 66, 143, 9, 29, 122, 105, 105, 103, 222, 157, 230, 253, 56, 188, 160, + 180, 244, 77, 192, 167, 145, 100, 140, 9, 55, 255, 39, 122, 191, 81, 78, 76, 97, + 170, 20, 225, 82, 151, 167, 79, 180, 95, 192, 237, 104, 20, 168, 187, 3, 157, 113, + 108, 20, 129, 179, 182, 239, 33, 192, 233, 57, 79, 242, 63, 207, 107, 10, 1, 133, + 169, 50, 67, 2, 20, + ]; + + let expected = [ + 66, 170, 191, 26, 194, 179, 196, 87, 184, 52, 195, 197, 179, 30, 53, 59, 193, 138, + 103, 175, 140, 92, 232, 252, 74, 138, 32, 61, 249, 50, 22, 21, 255, 149, 219, 8, + 135, 72, 210, 76, 172, 168, 105, 16, 114, 201, 98, 51, 31, 15, 34, 201, 58, 82, + 248, 74, 12, 163, 190, 155, 249, 214, 49, 124, 196, 163, 177, 21, 11, 245, 119, 68, + 25, 68, 220, 169, 209, 151, 123, 16, 68, 236, 2, 145, 31, 230, 80, 203, 181, 208, + 154, 193, 185, 161, 139, 253, 142, 158, 211, 17, 205, 62, 195, 202, 115, 58, 110, + 253, 249, 59, 148, 61, 35, 18, 199, 60, 183, 188, 61, 102, 89, 109, 93, 51, 144, + 254, 84, 43, 217, 203, 232, 229, 123, 126, 171, 240, 179, 26, 168, 1, 146, 13, 161, + 128, 175, 209, 18, 166, 95, 107, 19, 91, 221, 120, 44, 40, 114, 159, 77, 133, 150, + 167, 85, 46, 10, 201, 2, 41, 28, 60, 189, 129, 116, 97, 231, 174, 105, 202, 90, + 227, 99, 3, 239, 102, 175, 241, 246, 111, 20, 76, 96, 252, 49, 31, 226, 225, 33, + 118, 1, 227, 171, 251, 231, 104, 159, 60, 217, 69, 178, 226, 175, 194, 100, 77, + 160, 38, 180, 65, 210, 156, 230, 242, 61, 72, 64, 65, 1, 59, 77, 131, 196, 18, 59, + 219, 33, 159, 18, 207, 113, 249, 21, 237, 79, 143, 180, 104, 61, 84, 238, 234, 254, + 33, 84, 70, 106, 244, + ]; + + let mut actual = [0u8; 256]; + derive_key(&p, &s, passes, mem, Some(&k), Some(&x), &mut actual).unwrap(); + + assert_eq!(expected.len(), actual.len()); + assert_eq!(expected.as_ref(), &actual[..]); + } + + #[test] + fn test_hash_5() { + let mem = 4096; + let passes = 3; + let p = [ + 48, 115, 86, 156, 252, 145, 208, 204, 187, 108, 23, 254, 163, 172, 29, 63, 200, + 218, 50, 100, 108, 23, 64, 12, 70, 88, 171, 112, 154, 113, 39, 238, 202, 1, 88, + 147, 37, 107, 10, 31, 179, 27, 92, 14, 196, 104, 92, 134, 193, 139, 207, 9, 4, 60, + 7, 81, 23, 63, 171, 175, 164, 40, 126, 187, 45, 142, 34, 221, 118, 39, 70, 201, + 192, 241, 70, 225, 70, 112, 42, 222, 151, 125, 218, 98, 246, 67, 244, 154, 9, 237, + 126, 152, 110, 229, 124, 125, 242, 180, 209, 57, 227, 101, 104, 128, 72, 95, 143, + 255, 15, 246, 59, 191, 21, 242, 102, 45, 158, 170, 38, 138, 151, 96, 199, 37, 26, + 240, 239, 52, 72, 23, 227, 111, 157, 80, 241, 7, 73, 150, 22, 251, 118, 3, 99, 216, + 139, 212, 53, 49, 51, 90, 49, 81, 252, 45, 91, 36, 32, 51, 65, 121, 67, 100, 22, + 106, 0, 250, 134, 118, 175, 81, 210, 93, 102, 18, 56, 161, 24, 81, 168, 37, 19, 44, + 23, 94, 50, 190, 77, 186, 144, 2, 97, 168, 59, 210, 183, 220, 168, 176, 112, 134, + 53, 57, 152, 213, 67, 14, 132, 125, 107, 212, 159, 249, 206, 89, 161, 221, 113, + 123, 173, 79, 11, 25, 247, 8, 208, 96, 247, 45, 163, 85, 223, 174, 123, 136, 7, 67, + 73, 204, 18, 235, 182, 27, 237, 32, 106, 215, 84, 253, 72, 224, 37, 146, 199, 157, + 238, 24, 11, 57, 191, 82, 93, 51, 131, 134, 101, 253, 177, 57, 219, 202, 246, 40, + 92, 241, 123, 92, 136, 66, 230, 22, 145, 181, 111, 90, 248, 45, 156, 88, 25, 49, + 234, 64, 117, 242, 70, 210, 242, 68, 88, 62, 76, 110, 113, 119, 57, 77, 254, 242, + 13, 109, 141, 252, 21, 74, 255, 128, 136, 114, 249, 161, 81, 99, 18, 54, 206, 139, + 220, 212, 126, 31, 188, 199, 221, 177, 222, 24, 11, 234, 84, 66, 99, 5, 56, 0, 79, + 134, 97, 79, 229, 69, 153, 211, 190, 88, 116, 198, 81, 194, 196, 21, 158, 99, 140, + 227, 97, 24, 81, 134, 32, 214, 196, 25, 134, 73, 51, 83, 59, 200, 2, 176, 180, 5, + 133, 104, 255, 103, 95, 109, 184, 156, 56, 239, 186, 58, 148, 102, 108, 90, 51, 57, + 96, 134, 15, 128, 114, 165, 104, 175, 221, 223, 255, 83, 245, 19, 205, 169, 107, + 179, 200, 235, 143, 104, 111, 85, 129, 124, 237, 173, 154, 227, 14, 37, 200, 62, + 100, 236, 219, 189, 203, 164, 255, 58, 190, 226, 160, 117, 211, 77, 213, 22, 163, + 231, 226, 61, 168, 11, 207, 27, 105, 153, 210, 41, 175, 83, 204, 116, 63, 118, 196, + 142, 210, 51, 199, 180, 211, 24, 169, 214, 119, 253, 6, 233, 137, 117, 144, 104, + 97, 173, 92, 31, 15, 52, 70, 8, 52, 248, 95, 89, 134, 165, 114, 164, 9, 140, 228, + 127, 147, 6, 189, 126, 89, 0, + ]; + let s = [ + 175, 218, 81, 18, 191, 80, 114, 142, 79, 149, 196, 40, 61, 237, 245, 255, 115, 78, + 114, 250, 187, 148, 98, 141, 118, 42, 225, 26, 129, 64, 145, 107, 127, 227, 222, + 164, 224, 187, 120, 123, 82, 13, 29, 239, 137, 26, 225, 201, 211, 142, 105, 59, + 240, 206, 95, 8, 15, 98, 116, 188, 68, 173, 97, 64, 84, 229, 153, 58, 169, 24, 147, + 143, 156, 194, 247, 47, 192, 230, 102, 39, 18, 219, 243, 23, 195, 26, 121, 231, + 204, 221, 38, 53, 236, 178, 142, 161, 197, 214, 232, 125, 202, 12, 26, 255, 214, + 113, 49, 152, 193, 111, 63, 59, 84, 243, 89, 42, 129, 194, 30, 181, 222, 12, 229, + 105, 129, 216, 133, 22, 16, 118, 48, 16, 162, 99, 198, 79, 136, 91, 141, 231, 47, + 70, 107, 60, 175, 168, 139, 254, 60, 208, 8, 135, 122, 141, 83, 141, 58, 153, 235, + 116, 228, 158, 165, 178, 105, 172, 197, 232, 152, 250, 201, 20, 39, 235, 175, 245, + 128, 47, 134, 96, 163, 176, 67, 254, 36, 219, 14, 134, 118, 17, 4, 106, 74, 201, + 15, 208, 179, 255, 1, 53, 229, 86, 204, 187, 183, 214, 213, 77, 100, 79, 22, 31, + 32, 200, 134, 95, 123, 10, 74, 99, 143, 115, 183, 50, 153, 131, 247, 204, 250, 82, + 141, 68, 229, 102, 2, 120, 196, 65, 221, 234, 189, 11, 141, 183, 162, 153, 144, 22, + 190, 24, 131, 14, 125, 143, 217, 191, 11, 185, 43, 224, 156, 114, 144, 103, 183, + 122, 174, 3, 139, 218, 82, 248, 7, 20, 19, 150, 226, 139, 224, 226, 181, 64, 112, + 193, 61, 54, 52, 219, 75, 19, 217, 109, 37, 2, 172, 53, 230, 31, 153, 5, 113, 154, + 9, 176, 101, 122, 199, 1, 179, 62, 250, 113, 134, 226, 204, 209, 78, 80, 14, 231, + 115, 245, 22, 221, 99, 131, 237, 163, 133, 13, 143, 246, 57, 1, 230, 128, 225, 97, + 210, 48, 181, 3, 151, 220, 97, 231, 138, 89, 136, 201, 184, 203, 18, 32, 141, 62, + 53, 24, 84, 142, 194, 78, 77, 155, 200, 117, 57, 12, 4, 78, 206, 16, 122, 90, 156, + 132, 17, 2, 83, 74, 249, 94, 242, 142, 96, 25, 226, 125, 142, 93, 223, 178, 180, + 232, 177, 70, 188, 31, 19, 165, 200, 137, 173, 96, 189, 242, 149, 118, 100, 142, + 45, 48, 239, 14, 210, 153, 120, 243, 250, 237, 151, 244, 143, 130, 114, 0, 52, 34, + 156, 186, 3, 145, 55, 219, 153, 37, 73, 204, 152, 228, 69, 64, 42, 209, 161, 121, + 66, 85, 251, 62, 127, 18, 193, 91, 187, 198, 52, 12, 88, 125, 52, 93, 135, 60, 65, + 176, 103, 32, 24, 108, 28, 172, 208, 106, 84, 230, 191, 240, 235, 21, 180, 14, 22, + 226, 41, 15, 198, 31, 98, 184, 111, 194, 206, 72, 115, 98, 135, 90, 27, 111, 64, + 87, 149, 20, 154, 158, 65, 172, 116, 134, 62, 49, + ]; + let k = [ + 231, 130, 29, 148, 24, 187, 24, 64, 111, 18, 54, 133, 243, 193, 55, 21, 180, 71, + 147, 96, 46, 96, 27, 198, 81, 167, 14, 201, 18, 221, 209, 3, + ]; + let x = [ + 241, 250, 184, 87, 167, 198, 131, 217, 47, 151, 247, 42, 139, 44, 27, 46, 227, 157, + 35, 46, 215, 150, 10, 236, 127, 16, 142, 203, 182, 135, 242, 203, 243, 219, 210, + 228, 68, 226, 135, 113, 103, 156, 13, 125, 236, 30, 50, 85, 116, 112, 221, 90, 99, + 197, 166, 167, 75, 140, 23, 140, 225, 186, 188, 116, 10, 59, 9, 145, 252, 193, 138, + 60, 148, 23, 88, 37, 213, 11, 124, 170, 22, 155, 13, 86, 84, 167, 182, 185, 59, + 215, 9, 101, 166, 218, 167, 180, 85, 238, 121, 226, 60, 99, 244, 94, 6, 47, 189, + 187, 132, 52, 37, 171, 40, 11, 159, 54, 0, 85, 196, 198, 11, 168, 68, 50, 135, 88, + 174, 37, 226, 249, 208, 26, 224, 55, 18, 65, 152, 215, 67, 57, 128, 226, 201, 13, + 45, 193, 25, 30, 39, 155, 170, 54, 53, 57, 73, 72, 18, 134, 34, 223, 24, 6, 198, + 224, 191, 23, 53, 230, 137, 1, 184, 167, 147, 217, 203, 119, 105, 100, 19, 161, + 209, 67, 192, 50, 31, 163, 108, 161, 163, 42, 11, 167, 24, 37, 163, 23, 51, 180, + 57, 81, 179, 163, 177, 150, 61, 23, 86, 112, 149, 38, 57, 127, 175, 214, 34, 94, + 206, 91, 129, 84, 179, 45, 106, 198, 26, 6, 213, 217, 217, 71, 232, 51, 85, 106, 6, + 220, 10, 78, 252, 221, 124, 23, 192, 247, 186, 93, 190, 89, 222, 57, 186, 228, 5, + 103, 88, 144, 54, 68, 190, 154, 231, 192, 228, 156, 254, 182, 145, 101, 224, 19, + 174, 125, 54, 41, 6, 254, 215, 86, 143, 110, 102, 102, 214, 229, 39, 184, 230, 183, + 220, 190, 193, 8, 26, 246, 76, 248, 100, 253, 211, 109, 108, 137, 52, 87, 88, 53, + 87, 154, 76, 204, 101, 215, 166, 231, 226, 65, 155, 149, 74, 171, 157, 82, 238, + 228, 244, 103, 203, 243, 75, 221, 193, 215, 186, 144, 67, 151, 5, 174, 208, 182, + 22, 195, 177, 197, 180, 177, 202, 144, 94, 109, 204, 3, 118, 42, 109, 131, 132, + 153, 125, 156, 34, 18, 128, 113, 176, 42, 240, 51, 173, 116, 185, 67, 229, 11, 53, + 237, 113, 79, 166, 83, 70, 87, 83, 182, 134, 155, 206, 42, 13, 190, 19, 169, 71, + 79, 33, 112, 204, 133, 147, 135, 243, 0, 90, 105, 191, 5, 154, 124, 50, 157, 209, + 55, 254, 97, 182, 112, 86, 107, 176, 2, 46, 11, 93, 205, 91, 146, 132, 202, 84, + 196, 222, 54, 223, 182, 158, 96, 66, 248, 177, 68, 203, 168, 166, 230, 105, 215, + 27, 75, 250, 125, 183, 10, 225, 35, 91, 250, 239, 63, 136, 87, 133, 192, 93, 122, + 47, 187, 224, 158, 91, 98, 70, 36, 230, 124, 189, 55, 198, 157, 53, 140, 103, 170, + 240, 100, 55, 41, 34, 221, 122, 66, 123, 249, 127, 15, 140, 198, 215, 70, 228, 206, + 186, 195, 4, 220, 116, 198, 105, 148, 180, 170, 148, 116, + ]; + + let expected = [ + 57, 19, 91, 31, 121, 43, 175, 236, 30, 7, 247, 16, 69, 126, 137, 153, 113, 234, 23, + 97, 150, 124, 223, 248, 115, 179, 95, 64, 213, 230, 124, 203, 217, 56, 219, 160, + 56, 132, 249, 133, 214, 67, 197, 155, 191, 40, 151, 201, 159, 212, 144, 214, 71, + 146, 35, 223, 132, 164, 58, 22, 211, 23, 198, 155, 6, 152, 106, 65, 18, 81, 242, + 37, 252, 95, 118, 37, 65, 153, 138, 204, 143, 110, 17, 149, 12, 181, 31, 56, 173, + 194, 11, 65, 23, 10, 190, 66, 126, 4, 180, 53, 80, 65, 81, 140, 226, 22, 50, 16, + 182, 110, 126, 152, 180, 34, 60, 0, 19, 89, 211, 20, 199, 102, 60, 51, 106, 172, + 255, 153, 227, 230, 107, 180, 3, 181, 112, 128, 87, 67, 252, 193, 97, 171, 40, 115, + 8, 228, 234, 132, 33, 140, 206, 109, 98, 92, 221, 145, 164, 32, 190, 163, 23, 102, + 177, 230, 109, 207, 143, 42, 83, 119, 60, 13, 225, 155, 93, 147, 2, 163, 242, 241, + 41, 206, 124, 10, 150, 68, 57, 173, 120, 181, 81, 235, 237, 200, 27, 43, 118, 174, + 171, 238, 143, 242, 198, 247, 247, 114, 93, 187, 75, 165, 107, 88, 15, 245, 112, + 141, 204, 143, 46, 173, 190, 35, 190, 71, 126, 60, 61, 104, 62, 34, 71, 136, 236, + 67, 99, 112, 67, 145, 97, 15, 96, 18, 134, 192, 51, 62, 242, 195, 35, 85, 225, 155, + 139, 245, 129, 74, 86, 56, 18, 147, 222, 210, 152, 228, 252, 47, 227, 165, 109, + 106, 107, 163, 181, 34, 44, 226, 205, 34, 24, 99, 104, 226, 47, 70, 188, 9, 201, + 162, 100, 147, 24, 117, 134, 197, 169, 141, 211, 46, 77, 141, 158, 10, 126, 83, + 187, 199, 111, 227, 211, 154, 34, 14, 128, 200, 197, 76, 12, 230, 30, 24, 58, 74, + 116, 125, 80, 220, 174, 62, 204, 98, 242, 181, 67, 222, 84, 111, 35, 45, 236, 214, + 143, 204, 65, 47, 187, 8, 236, 100, 47, 32, 28, 171, 35, 14, 35, 96, 16, 6, 177, + 177, 147, 174, 180, 174, 62, 164, 232, 189, 144, 191, 209, 253, 85, 40, 205, 138, + 90, 53, 246, 29, 136, 145, 212, 151, 13, 13, 118, 73, 229, 134, 115, 103, 233, 93, + 82, 173, 51, 9, 183, 98, 70, 89, 40, 154, 252, 136, 206, 247, 190, 49, 64, 87, 236, + 209, 65, 10, 37, 0, 28, 8, 155, 45, 243, 53, 199, 96, 112, 25, 234, 128, 199, 95, + 172, 41, 141, 217, 151, 15, 124, 96, 18, 59, 33, 251, 34, 138, 60, 139, 137, 181, + 50, 111, 95, 247, 22, 110, 172, 112, 224, 130, 202, 215, 119, 211, 86, 59, 235, + 150, 55, 16, 193, 77, 240, 73, 101, 184, 57, 92, 0, 230, 186, 103, 43, 155, 225, + 210, 152, 28, 120, 138, 185, 242, 198, 18, 74, 140, 245, 130, 112, 217, 148, 129, + 224, 142, 214, 25, 81, 9, 42, 248, 39, 148, + ]; + let mut actual = [0u8; 512]; + derive_key(&p, &s, passes, mem, Some(&k), Some(&x), &mut actual).unwrap(); + + assert_eq!(expected.len(), actual.len()); + assert_eq!(expected.as_ref(), &actual[..]); + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_initial_hash { + use super::*; + + #[test] + fn initial_hash_test_1() { + let hlen = 3496473570; + let kib = 113001741; + let passes = 172774226; + let p = [ + 225, 168, 40, 211, 31, 67, 71, 99, 229, 168, 106, 43, 101, 94, 51, 219, 193, 88, + 66, 234, 43, 144, 40, 25, 24, 168, 113, 144, 211, 83, 61, 103, + ]; + let s = [ + 123, 165, 225, 133, 80, 117, 28, 160, 138, 80, 59, 206, 190, 36, 171, 53, 127, 145, + 92, 208, 96, 218, 248, 198, 48, 23, 84, 226, 55, 30, 3, 81, + ]; + let k = [ + 235, 154, 1, 51, 161, 180, 78, 36, 109, 83, 83, 163, 59, 225, 74, 104, 79, 58, 127, + 252, 144, 52, 231, 101, 224, 139, 52, 181, 171, 154, 43, 215, + ]; + let x = [ + 236, 219, 129, 50, 196, 196, 170, 157, 179, 88, 71, 155, 243, 42, 69, 108, 238, + 251, 242, 152, 38, 90, 120, 148, 236, 215, 166, 155, 49, 32, 64, 183, + ]; + + let expected = [ + 157, 152, 47, 97, 226, 116, 212, 144, 157, 93, 122, 3, 239, 211, 157, 66, 20, 33, + 133, 93, 0, 4, 53, 86, 167, 67, 88, 98, 125, 11, 137, 122, 142, 17, 16, 84, 146, + 17, 49, 11, 228, 22, 128, 161, 57, 188, 136, 75, 96, 197, 3, 206, 224, 204, 65, + 149, 190, 101, 231, 161, 232, 35, 87, 64, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + let actual = initial_hash(hlen, kib, passes, &p, &s, &k, &x).unwrap(); + assert_eq!(expected.as_ref(), actual.as_ref()); + } + + #[test] + fn initial_hash_test_2() { + let hlen = 1360327050; + let kib = 266855870; + let passes = 263947785; + let p = [ + 62, 145, 210, 51, 41, 168, 197, 154, 64, 67, 181, 144, 73, 11, 90, 166, 13, 111, + 86, 19, 81, 103, 83, 26, 140, 110, 143, 91, 235, 175, 58, 220, 123, 172, 214, 144, + 96, 251, 34, 63, 205, 120, 252, 224, 127, 254, 117, 205, 251, 191, 5, 118, 112, + 219, 91, 16, 184, 80, 197, 229, 23, 239, 138, 200, + ]; + let s = [ + 229, 128, 59, 80, 127, 134, 112, 194, 29, 49, 206, 111, 254, 195, 72, 98, 51, 39, + 50, 55, 55, 47, 68, 231, 82, 91, 229, 226, 244, 102, 59, 32, 184, 171, 121, 57, 3, + 17, 155, 176, 102, 49, 168, 247, 225, 227, 144, 26, 15, 0, 233, 123, 199, 73, 73, + 150, 137, 140, 175, 219, 91, 4, 219, 129, + ]; + let k = [ + 230, 122, 163, 153, 60, 26, 74, 62, 99, 255, 192, 203, 137, 28, 66, 180, 48, 149, + 160, 238, 39, 236, 220, 231, 11, 133, 212, 190, 162, 126, 166, 173, + ]; + let x = [ + 76, 234, 237, 208, 211, 225, 108, 104, 95, 239, 241, 60, 218, 47, 169, 88, 111, + 253, 169, 144, 188, 39, 47, 249, 196, 104, 215, 24, 167, 126, 250, 143, 174, 175, + 167, 159, 115, 77, 127, 219, 142, 76, 37, 104, 64, 174, 241, 190, 204, 160, 149, + 122, 142, 40, 42, 235, 47, 173, 11, 59, 45, 8, 133, 143, + ]; + + let expected = [ + 75, 173, 222, 46, 97, 96, 90, 145, 123, 113, 146, 135, 56, 148, 100, 59, 28, 233, + 228, 56, 215, 15, 138, 5, 90, 30, 128, 111, 131, 160, 92, 32, 97, 76, 216, 81, 134, + 15, 239, 64, 239, 203, 191, 226, 71, 213, 149, 238, 65, 124, 102, 1, 150, 230, 41, + 132, 23, 176, 221, 217, 237, 150, 154, 249, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + let actual = initial_hash(hlen, kib, passes, &p, &s, &k, &x).unwrap(); + assert_eq!(expected.as_ref(), actual.as_ref()); + } + + #[test] + fn initial_hash_test_3() { + let hlen = 710460332; + let kib = 75212384; + let passes = 113373009; + let p = [ + 168, 246, 172, 189, 26, 25, 31, 227, 200, 19, 116, 185, 146, 217, 171, 125, 243, + 174, 179, 205, 67, 207, 224, 58, 252, 44, 132, 238, 174, 187, 196, 87, 75, 2, 130, + 86, 210, 179, 68, 75, 245, 217, 253, 148, 43, 88, 95, 4, 28, 124, 121, 203, 234, + 191, 91, 36, 69, 97, 241, 15, 2, 96, 46, 144, 136, 221, 112, 40, 120, 177, 41, 176, + 201, 2, 21, 217, 40, 94, 247, 62, 75, 68, 105, 41, 89, 211, 228, 254, 159, 194, + 175, 181, 134, 15, 249, 230, 169, 62, 237, 134, 45, 16, 180, 228, 171, 220, 129, + 254, 73, 175, 56, 51, 219, 122, 237, 223, 110, 172, 144, 220, 174, 241, 138, 155, + 204, 39, 183, 156, + ]; + let s = [ + 36, 185, 70, 134, 9, 131, 213, 227, 104, 174, 196, 186, 87, 63, 161, 245, 169, 29, + 72, 60, 248, 48, 0, 27, 179, 15, 177, 233, 121, 31, 13, 19, 103, 106, 105, 187, + 243, 50, 218, 0, 214, 73, 157, 103, 160, 229, 125, 160, 213, 199, 121, 21, 153, 34, + 73, 115, 232, 217, 223, 76, 189, 187, 123, 136, 128, 127, 123, 37, 188, 216, 194, + 184, 212, 137, 197, 19, 101, 25, 141, 7, 7, 85, 192, 179, 136, 244, 104, 84, 142, + 72, 22, 162, 101, 54, 5, 106, 29, 22, 177, 47, 141, 112, 136, 153, 89, 125, 97, 25, + 203, 169, 236, 24, 27, 144, 224, 147, 125, 1, 22, 191, 120, 13, 191, 76, 63, 18, + 238, 148, + ]; + let k = [ + 243, 70, 162, 106, 101, 169, 16, 249, 31, 59, 234, 10, 196, 82, 36, 84, 153, 233, + 22, 14, 198, 100, 178, 225, 157, 177, 233, 83, 27, 133, 114, 254, + ]; + let x = [ + 156, 6, 232, 138, 61, 133, 190, 151, 160, 41, 167, 51, 218, 112, 90, 97, 32, 238, + 123, 89, 149, 121, 166, 50, 186, 121, 189, 128, 157, 235, 134, 168, 14, 193, 154, + 215, 246, 8, 104, 94, 179, 239, 93, 17, 78, 184, 192, 166, 158, 222, 175, 235, 201, + 9, 117, 81, 127, 101, 75, 124, 44, 112, 211, 224, 221, 243, 130, 33, 68, 216, 191, + 127, 61, 180, 118, 25, 233, 51, 241, 68, 92, 159, 49, 95, 146, 142, 65, 93, 113, + 97, 80, 237, 242, 15, 15, 162, 67, 84, 108, 168, 165, 20, 230, 119, 19, 90, 13, 30, + 93, 152, 103, 90, 218, 174, 147, 32, 255, 33, 15, 28, 11, 232, 126, 184, 183, 222, + 6, 111, + ]; + + let expected = [ + 201, 203, 51, 82, 36, 53, 35, 146, 170, 88, 139, 252, 221, 33, 198, 174, 96, 86, + 241, 236, 112, 62, 172, 141, 168, 39, 134, 110, 91, 103, 141, 136, 207, 165, 236, + 236, 58, 237, 193, 139, 30, 191, 244, 2, 176, 123, 134, 44, 251, 101, 255, 220, + 218, 109, 249, 231, 200, 45, 232, 240, 155, 10, 93, 111, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + let actual = initial_hash(hlen, kib, passes, &p, &s, &k, &x).unwrap(); + assert_eq!(expected.as_ref(), actual.as_ref()); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_test_same_result( + hlen: u32, + kib: u32, + passes: u32, + p: Vec<u8>, + s: Vec<u8>, + k: Vec<u8>, + x: Vec<u8>, + ) -> bool { + let first = initial_hash(hlen, kib, passes, &p, &s, &k, &x).unwrap(); + let second = initial_hash(hlen, kib, passes, &p, &s, &k, &x).unwrap(); + + first.as_ref() == second.as_ref() + } + } + + mod test_extended_hash { + use super::*; + + #[test] + fn err_on_empty_dst() { + let mut out = [0u8; 0]; + let input = [255u8; 256]; + + assert!(extended_hash(&input, &mut out).is_err()); + } + + #[test] + fn extended_hash_test_1() { + let mut out = [ + 49, 22, 190, 96, 55, 242, 247, 115, 242, 1, 96, 161, 138, 72, 108, 211, 135, 164, + 123, 9, 199, 223, 163, 248, 176, 81, 208, 255, 71, 67, 29, 215, + ]; + let input = [ + 33, 25, 138, 88, 116, 24, 7, 244, 116, 129, 14, 117, 135, 154, 207, 46, 65, 155, + 192, 39, 111, 117, 36, 109, 102, 49, 181, 172, 217, 21, 6, 201, 4, 229, 156, 175, + 201, 35, 84, 130, 195, 50, 97, 38, 137, 182, 162, 240, 16, 46, 202, 146, 2, 73, + 136, 4, 215, 200, 149, 252, 18, 47, 218, 17, + ]; + let expected = [ + 23, 122, 170, 179, 137, 61, 145, 86, 70, 228, 124, 82, 24, 135, 208, 96, 33, 127, + 145, 136, 189, 60, 123, 34, 55, 118, 245, 41, 197, 229, 209, 3, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[test] + fn extended_hash_test_2() { + let mut out = [ + 241, 8, 207, 144, 211, 141, 215, 81, 145, 190, 184, 85, 99, 72, 157, 91, 32, 190, + 241, 192, 207, 205, 157, 119, 110, 28, 49, 117, 239, 220, 185, 246, 211, 188, 166, + 238, 223, 105, 163, 231, 21, 241, 70, 115, 155, 22, 160, 23, 242, 129, 144, 216, + 190, 110, 143, 221, 54, 4, 71, 239, 101, 95, 155, 196, + ]; + let input = [ + 66, 65, 147, 227, 144, 232, 121, 134, 153, 127, 210, 161, 10, 39, 254, 174, 144, + 104, 74, 63, 126, 53, 247, 145, 227, 229, 29, 255, 140, 246, 13, 65, 179, 149, 86, + 150, 216, 81, 178, 131, 136, 40, 139, 220, 43, 185, 119, 249, 161, 244, 0, 177, + 176, 139, 164, 135, 21, 68, 105, 204, 39, 107, 73, 47, 244, 228, 117, 203, 63, 82, + 81, 196, 135, 192, 148, 245, 77, 174, 184, 84, 150, 56, 11, 183, 234, 245, 88, 182, + 248, 223, 124, 252, 170, 111, 9, 48, 22, 227, 18, 118, 136, 22, 250, 22, 108, 229, + 176, 186, 19, 45, 67, 105, 19, 45, 94, 113, 16, 116, 215, 188, 91, 105, 36, 18, 77, + 235, 195, 113, + ]; + let expected = [ + 33, 81, 20, 93, 250, 207, 85, 11, 227, 90, 81, 170, 97, 236, 60, 207, 156, 65, 52, + 186, 53, 114, 252, 33, 118, 184, 12, 21, 239, 186, 19, 84, 98, 59, 219, 146, 117, + 222, 212, 217, 233, 173, 84, 38, 188, 102, 165, 73, 137, 64, 18, 214, 51, 167, 180, + 113, 50, 196, 175, 138, 96, 109, 95, 61, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[test] + fn extended_hash_test_3() { + let mut out = [ + 70, 156, 46, 182, 87, 221, 0, 156, 124, 47, 167, 94, 57, 77, 222, 142, 130, 234, + 218, 139, 119, 27, 170, 129, 232, 219, 152, 79, 7, 237, 81, 3, 203, 33, 116, 167, + 159, 232, 31, 143, 142, 217, 118, 158, 40, 42, 42, 131, 249, 99, 63, 136, 182, 122, + 161, 8, 77, 7, 243, 7, 152, 54, 211, 102, 158, 7, 238, 103, 203, 249, 40, 204, 13, + 246, 0, 169, 235, 154, 14, 86, 4, 183, 145, 233, 248, 125, 155, 22, 8, 207, 80, 40, + 159, 55, 207, 151, 248, 170, 101, 233, 3, 68, 253, 88, 77, 164, 182, 211, 154, 101, + 210, 199, 58, 98, 110, 127, 189, 180, 158, 38, 30, 97, 124, 55, 82, 39, 183, 115, + ]; + let input = [ + 103, 132, 147, 55, 198, 201, 33, 151, 217, 248, 118, 190, 164, 159, 224, 197, 172, + 89, 93, 146, 170, 143, 72, 88, 75, 13, 41, 237, 20, 77, 117, 54, 100, 76, 198, 85, + 222, 182, 69, 119, 55, 251, 165, 141, 16, 105, 157, 25, 14, 70, 182, 131, 95, 21, + 156, 64, 3, 133, 179, 66, 9, 33, 181, 158, 165, 212, 142, 86, 22, 236, 235, 17, + 243, 34, 13, 109, 56, 111, 63, 75, 217, 153, 60, 159, 172, 233, 145, 142, 181, 136, + 210, 174, 187, 55, 153, 214, 105, 233, 196, 69, 64, 0, 59, 25, 21, 27, 233, 87, + 119, 31, 184, 15, 160, 55, 228, 132, 41, 110, 255, 79, 90, 141, 183, 156, 251, 89, + 90, 151, 199, 149, 220, 31, 85, 85, 87, 253, 79, 97, 18, 125, 251, 227, 120, 236, + 196, 203, 135, 195, 194, 160, 129, 89, 40, 111, 160, 222, 101, 149, 109, 153, 90, + 156, 220, 219, 89, 128, 8, 17, 104, 108, 145, 233, 121, 100, 124, 151, 96, 39, 187, + 132, 173, 9, 74, 154, 199, 126, 104, 241, 198, 190, 148, 221, 29, 50, 234, 19, 75, + 139, 135, 34, 247, 247, 226, 245, 142, 140, 152, 53, 210, 65, 174, 168, 70, 41, 13, + 11, 108, 29, 2, 93, 24, 156, 209, 159, 123, 80, 76, 111, 245, 39, 81, 252, 114, 82, + 175, 107, 42, 34, 131, 221, 209, 23, 231, 174, 242, 10, 17, 77, 251, 138, 239, 213, + 157, 197, 87, 96, + ]; + let expected = [ + 247, 38, 177, 59, 225, 220, 244, 197, 13, 169, 51, 184, 170, 167, 18, 78, 77, 196, + 23, 182, 207, 227, 211, 203, 66, 202, 238, 18, 72, 7, 110, 92, 162, 84, 125, 185, + 132, 129, 210, 217, 217, 93, 17, 93, 58, 18, 31, 165, 3, 194, 111, 223, 231, 8, + 120, 102, 201, 76, 149, 253, 233, 246, 199, 21, 157, 107, 186, 47, 123, 209, 94, + 151, 56, 53, 33, 8, 116, 26, 58, 53, 255, 51, 184, 18, 241, 179, 54, 15, 181, 18, + 117, 48, 83, 190, 250, 39, 126, 145, 178, 150, 185, 87, 172, 5, 176, 110, 227, 142, + 233, 63, 85, 225, 162, 85, 179, 166, 250, 222, 5, 10, 139, 187, 172, 105, 171, 171, + 140, 253, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[test] + fn extended_hash_test_4() { + let mut out = [ + 231, 57, 92, 140, 212, 20, 239, 34, 56, 165, 207, 203, 237, 63, 103, 245, 135, 137, + 173, 240, 171, 105, 161, 221, 2, 15, 36, 47, 166, 126, 151, 21, 223, 34, 8, 141, + 193, 50, 70, 138, 65, 197, 165, 167, 25, 207, 114, 124, 147, 80, 192, 179, 171, 18, + 58, 180, 3, 79, 233, 231, 156, 157, 106, 2, + ]; + let input = [ + 213, 96, 143, 63, 224, 241, 183, 146, 44, 45, 66, 96, 200, 213, 151, 108, 41, 142, + 193, 159, 45, 198, 28, 146, 65, 13, 39, 36, 153, 46, 225, 14, + ]; + let expected = [ + 174, 131, 211, 180, 105, 12, 67, 55, 16, 72, 125, 125, 211, 93, 64, 180, 179, 188, + 77, 113, 119, 181, 98, 54, 13, 146, 57, 92, 43, 232, 224, 183, 219, 138, 143, 234, + 232, 151, 33, 76, 158, 96, 170, 104, 200, 127, 55, 239, 145, 241, 224, 146, 0, 11, + 64, 16, 212, 151, 227, 7, 65, 234, 92, 45, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[test] + fn extended_hash_test_5() { + let mut out = [ + 53, 56, 9, 207, 79, 196, 6, 94, 170, 197, 204, 233, 69, 124, 20, 228, 227, 59, 102, + 30, 88, 45, 245, 144, 69, 50, 72, 163, 31, 100, 44, 12, 203, 9, 253, 13, 253, 221, + 216, 186, 92, 164, 37, 55, 195, 31, 13, 39, 110, 180, 40, 167, 40, 236, 138, 203, + 123, 174, 121, 219, 133, 211, 184, 133, 255, 239, 233, 193, 203, 90, 48, 59, 95, + 111, 55, 11, 95, 147, 178, 164, 241, 231, 109, 21, 16, 161, 192, 86, 156, 138, 137, + 224, 139, 52, 142, 192, 189, 231, 170, 54, 55, 150, 12, 122, 51, 250, 167, 127, 5, + 204, 63, 34, 71, 221, 162, 35, 33, 246, 22, 187, 187, 2, 41, 223, 81, 143, 231, 77, + ]; + let input = [ + 92, 110, 199, 162, 60, 226, 227, 26, 123, 30, 136, 146, 116, 38, 44, 194, 254, 14, + 137, 67, 183, 2, 112, 194, 30, 15, 100, 215, 248, 47, 223, 93, 156, 71, 98, 247, + 54, 74, 92, 233, 219, 165, 1, 45, 162, 225, 7, 80, 237, 172, 245, 25, 80, 162, 216, + 83, 35, 122, 156, 143, 55, 19, 5, 26, + ]; + let expected = [ + 253, 153, 55, 223, 21, 172, 36, 50, 109, 171, 45, 24, 40, 215, 239, 116, 92, 149, + 31, 40, 17, 99, 42, 25, 114, 52, 167, 230, 63, 36, 226, 178, 222, 163, 247, 175, + 100, 118, 54, 51, 223, 11, 164, 68, 126, 157, 94, 255, 196, 53, 177, 231, 81, 55, + 1, 250, 85, 91, 89, 45, 15, 121, 66, 157, 195, 162, 97, 243, 33, 195, 149, 253, + 193, 24, 150, 106, 234, 158, 122, 28, 52, 72, 48, 109, 206, 190, 116, 50, 163, 191, + 208, 86, 231, 170, 11, 210, 251, 135, 50, 46, 160, 202, 72, 101, 45, 24, 202, 72, + 210, 25, 239, 0, 229, 47, 200, 219, 202, 0, 39, 195, 197, 148, 15, 32, 211, 167, + 196, 128, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[test] + fn extended_hash_test_6() { + let mut out = [ + 253, 1, 93, 186, 15, 159, 80, 7, 174, 85, 112, 241, 193, 170, 254, 103, 204, 254, + 154, 58, 228, 243, 244, 192, 223, 174, 103, 229, 21, 66, 203, 203, 221, 186, 76, + 40, 49, 66, 170, 52, 140, 254, 142, 95, 23, 200, 19, 117, 252, 9, 144, 94, 63, 14, + 66, 162, 168, 125, 112, 76, 45, 166, 241, 179, 54, 75, 107, 140, 92, 95, 211, 138, + 209, 143, 237, 130, 180, 19, 156, 242, 65, 22, 55, 228, 23, 106, 119, 14, 140, 66, + 188, 206, 107, 93, 130, 123, 6, 10, 85, 250, 177, 195, 46, 248, 177, 195, 86, 150, + 21, 10, 160, 108, 113, 75, 253, 51, 12, 173, 254, 71, 236, 160, 176, 130, 71, 161, + 205, 146, 104, 63, 189, 90, 117, 110, 141, 16, 18, 246, 158, 71, 201, 242, 53, 169, + 5, 3, 91, 227, 157, 11, 127, 150, 180, 200, 73, 27, 2, 78, 209, 89, 93, 78, 2, 136, + 70, 46, 65, 181, 217, 158, 54, 143, 135, 229, 217, 239, 22, 175, 156, 176, 111, 54, + 141, 230, 133, 232, 137, 100, 81, 147, 71, 176, 113, 125, 88, 48, 170, 19, 156, + 184, 91, 166, 195, 251, 143, 253, 135, 107, 215, 209, 224, 40, 96, 56, 222, 118, + 247, 22, 0, 251, 215, 62, 179, 190, 112, 75, 79, 96, 148, 246, 46, 15, 91, 117, + 142, 221, 133, 155, 237, 126, 144, 104, 240, 124, 130, 222, 19, 93, 87, 120, 83, + 117, 77, 75, 105, 104, + ]; + let input = [ + 89, 106, 243, 203, 190, 196, 239, 41, 217, 53, 96, 178, 255, 156, 212, 103, 117, + 255, 25, 219, 215, 212, 74, 47, 227, 67, 151, 151, 241, 100, 32, 178, 197, 211, 63, + 206, 247, 215, 141, 236, 41, 248, 232, 241, 106, 178, 52, 133, 83, 177, 65, 177, + 253, 118, 157, 226, 225, 137, 134, 127, 231, 48, 46, 156, 51, 224, 102, 94, 205, + 30, 222, 59, 173, 243, 205, 117, 78, 112, 160, 35, 66, 220, 113, 146, 100, 194, 56, + 85, 28, 75, 57, 59, 243, 201, 250, 140, 147, 24, 253, 84, 135, 91, 221, 190, 128, + 225, 118, 27, 74, 251, 27, 182, 254, 122, 44, 48, 222, 131, 32, 176, 254, 250, 200, + 2, 38, 202, 255, 207, + ]; + let expected = [ + 22, 21, 67, 184, 94, 20, 98, 6, 113, 81, 65, 110, 70, 42, 13, 58, 26, 213, 184, + 242, 234, 133, 185, 122, 112, 235, 18, 11, 94, 199, 64, 107, 116, 55, 49, 85, 178, + 118, 146, 51, 230, 150, 214, 229, 90, 162, 178, 225, 106, 138, 169, 206, 77, 161, + 112, 162, 86, 101, 48, 90, 227, 247, 147, 186, 120, 84, 101, 196, 141, 213, 215, + 115, 201, 150, 35, 182, 156, 243, 87, 242, 165, 45, 128, 127, 70, 51, 225, 40, 27, + 250, 173, 46, 109, 116, 254, 202, 206, 112, 48, 205, 21, 164, 129, 192, 181, 119, + 195, 126, 38, 177, 107, 55, 149, 126, 227, 44, 254, 225, 104, 15, 236, 141, 233, + 110, 132, 133, 241, 17, 210, 26, 22, 175, 135, 199, 106, 200, 214, 45, 20, 83, 164, + 49, 202, 69, 203, 191, 21, 92, 101, 206, 109, 136, 144, 123, 108, 24, 121, 142, 77, + 91, 122, 248, 117, 85, 82, 181, 228, 192, 197, 111, 169, 161, 30, 12, 201, 127, 24, + 17, 185, 88, 4, 126, 83, 107, 76, 6, 6, 146, 205, 164, 202, 151, 11, 189, 205, 159, + 146, 245, 79, 13, 127, 23, 148, 219, 156, 104, 161, 201, 155, 81, 126, 57, 34, 201, + 118, 110, 163, 135, 194, 38, 9, 2, 205, 54, 192, 55, 214, 98, 23, 35, 70, 113, 120, + 68, 206, 127, 130, 174, 252, 254, 135, 37, 160, 144, 108, 29, 86, 108, 159, 148, + 221, 54, 153, 234, 194, 103, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_test_same_result(input: Vec<u8>, out: Vec<u8>) -> bool { + let mut first = out.clone(); + let mut second = out.clone(); + + if out.is_empty() && extended_hash(&input, &mut first).is_err() { + return true; + } + + extended_hash(&input, &mut first).unwrap(); + extended_hash(&input, &mut second).unwrap(); + + first == second + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_test_diff_result(input: Vec<u8>, out: Vec<u8>) -> bool { + let mut first = out.clone(); + let mut second = out.clone(); + + if out.is_empty() && extended_hash(&input, &mut first).is_err() { + return true; + } + + extended_hash(&input, &mut first).unwrap(); + extended_hash(&first, &mut second).unwrap(); + + first != second + } + } + + mod test_gidx { + use super::*; + + #[test] + fn gidx_test() { + let n_blocks = 4096; + let segment_length = 1024; + let passes = 3; + + let mut gidx = Gidx::new(n_blocks, passes, segment_length); + let mut tmp_block = [0u64; 128]; + + let offset = 2; + let pass_n = 0; + let segment_n = 0; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1022] = [ + 0, 1, 0, 3, 2, 1, 6, 3, 7, 8, 10, 11, 11, 4, 14, 7, 16, 13, 6, 17, 5, 20, 11, 19, + 20, 2, 26, 26, 22, 21, 28, 19, 31, 18, 30, 35, 31, 33, 35, 31, 28, 35, 24, 43, 41, + 42, 44, 41, 36, 32, 38, 48, 43, 53, 51, 42, 4, 44, 17, 45, 53, 49, 38, 27, 7, 24, + 53, 59, 24, 45, 19, 38, 55, 49, 74, 60, 41, 44, 17, 60, 0, 78, 45, 32, 83, 10, 18, + 65, 60, 65, 84, 26, 1, 93, 48, 67, 68, 97, 30, 76, 68, 53, 75, 42, 83, 25, 106, 2, + 108, 101, 110, 61, 34, 94, 14, 92, 26, 69, 108, 51, 94, 69, 122, 123, 120, 94, 3, + 67, 125, 39, 97, 14, 37, 41, 112, 124, 18, 125, 96, 107, 134, 134, 69, 121, 139, + 112, 52, 98, 138, 71, 129, 134, 126, 152, 153, 150, 43, 109, 152, 159, 157, 61, + 146, 153, 47, 42, 166, 78, 30, 43, 132, 171, 121, 149, 119, 172, 115, 13, 132, 140, + 165, 45, 29, 142, 76, 78, 175, 187, 150, 120, 182, 153, 192, 188, 67, 191, 193, + 125, 127, 183, 57, 138, 186, 24, 89, 40, 56, 207, 196, 64, 62, 211, 195, 70, 184, + 87, 45, 198, 53, 181, 129, 62, 164, 142, 59, 223, 173, 142, 228, 82, 230, 136, 160, + 194, 203, 235, 155, 181, 216, 143, 139, 102, 210, 180, 198, 235, 93, 177, 149, 245, + 245, 177, 252, 253, 20, 198, 143, 216, 102, 181, 91, 259, 200, 263, 106, 240, 200, + 267, 229, 160, 57, 116, 71, 203, 274, 182, 13, 237, 241, 202, 85, 278, 269, 261, + 219, 56, 283, 186, 280, 215, 265, 195, 292, 137, 293, 294, 277, 224, 106, 285, 300, + 82, 302, 289, 234, 236, 300, 242, 277, 52, 214, 244, 304, 259, 179, 235, 316, 305, + 28, 230, 242, 313, 59, 159, 133, 86, 103, 171, 225, 300, 223, 291, 233, 325, 144, + 41, 164, 261, 206, 70, 213, 44, 339, 229, 154, 279, 338, 218, 348, 326, 288, 344, + 284, 329, 257, 345, 353, 199, 276, 359, 279, 349, 8, 363, 357, 3, 364, 360, 262, + 82, 322, 366, 183, 197, 244, 375, 165, 377, 79, 106, 222, 321, 299, 61, 264, 340, + 283, 66, 382, 47, 321, 162, 374, 385, 26, 242, 392, 276, 384, 296, 306, 77, 23, + 105, 381, 316, 248, 40, 405, 112, 410, 409, 141, 261, 149, 275, 267, 39, 80, 394, + 259, 310, 421, 68, 70, 283, 361, 398, 90, 308, 83, 220, 87, 228, 342, 70, 337, 437, + 284, 179, 440, 440, 308, 149, 421, 42, 86, 419, 33, 23, 390, 132, 389, 364, 433, + 52, 222, 342, 291, 423, 459, 191, 382, 118, 463, 164, 384, 358, 150, 455, 246, 432, + 456, 473, 161, 309, 117, 109, 341, 283, 441, 40, 470, 254, 484, 36, 482, 430, 488, + 101, 484, 344, 491, 425, 474, 357, 403, 481, 195, 454, 360, 501, 470, 170, 495, + 277, 377, 441, 399, 362, 19, 436, 482, 392, 514, 305, 495, 508, 170, 102, 276, 315, + 89, 380, 433, 460, 523, 527, 50, 422, 191, 162, 532, 525, 459, 368, 71, 492, 208, + 173, 38, 443, 417, 57, 460, 521, 199, 356, 428, 180, 550, 497, 487, 546, 148, 461, + 389, 535, 360, 420, 460, 552, 449, 290, 558, 360, 154, 424, 240, 45, 428, 392, 566, + 555, 559, 109, 5, 404, 576, 118, 206, 543, 349, 173, 513, 287, 586, 380, 311, 87, + 206, 358, 182, 526, 447, 404, 437, 271, 417, 3, 237, 439, 534, 602, 231, 595, 522, + 366, 544, 491, 520, 278, 604, 607, 595, 370, 604, 509, 602, 463, 163, 270, 615, + 123, 563, 566, 626, 611, 579, 629, 58, 436, 522, 338, 527, 451, 41, 549, 635, 431, + 640, 387, 613, 571, 12, 510, 527, 593, 648, 465, 484, 625, 325, 197, 651, 617, 429, + 218, 627, 490, 656, 640, 79, 203, 480, 655, 627, 648, 648, 665, 132, 291, 667, 620, + 296, 243, 574, 673, 609, 642, 680, 484, 669, 239, 461, 678, 422, 677, 227, 430, + 503, 468, 78, 503, 112, 563, 639, 477, 232, 649, 345, 679, 473, 448, 459, 577, 18, + 315, 692, 618, 534, 656, 709, 294, 400, 648, 242, 488, 706, 198, 404, 369, 304, + 699, 698, 394, 572, 493, 216, 725, 406, 723, 548, 580, 158, 450, 731, 735, 25, 694, + 426, 741, 256, 98, 407, 12, 558, 445, 566, 748, 596, 180, 663, 663, 170, 330, 636, + 686, 738, 656, 679, 89, 605, 484, 155, 599, 619, 629, 55, 683, 474, 316, 311, 474, + 50, 21, 599, 202, 711, 477, 719, 77, 782, 737, 688, 427, 712, 165, 212, 437, 716, + 718, 792, 486, 606, 751, 61, 708, 667, 677, 116, 17, 787, 591, 678, 494, 4, 675, + 148, 796, 747, 524, 434, 603, 674, 744, 715, 629, 72, 1, 748, 735, 542, 808, 688, + 197, 826, 387, 757, 810, 41, 366, 826, 688, 814, 483, 319, 210, 37, 803, 51, 671, + 396, 690, 268, 292, 823, 599, 258, 595, 748, 844, 610, 454, 689, 62, 796, 687, 853, + 465, 257, 404, 811, 367, 665, 724, 520, 319, 667, 353, 843, 761, 870, 694, 606, + 597, 98, 867, 275, 878, 407, 389, 811, 272, 745, 416, 46, 401, 875, 316, 808, 687, + 683, 589, 392, 589, 245, 759, 637, 889, 472, 746, 363, 337, 884, 726, 481, 827, + 764, 447, 409, 911, 69, 872, 660, 567, 752, 825, 320, 495, 447, 807, 454, 838, 870, + 66, 737, 805, 906, 350, 334, 849, 713, 277, 698, 930, 795, 930, 840, 670, 933, 707, + 873, 35, 731, 149, 667, 671, 208, 833, 110, 814, 113, 880, 506, 951, 255, 48, 956, + 816, 951, 138, 274, 528, 154, 592, 778, 906, 572, 621, 955, 751, 802, 964, 801, + 891, 410, 513, 969, 896, 971, 286, 647, 76, 582, 767, 983, 822, 262, 288, 79, 895, + 973, 891, 982, 965, 813, 721, 905, 771, 981, 954, 311, 68, 912, 671, 643, 1006, 16, + 259, 49, 1009, 1012, 828, 416, 1015, 878, 802, 213, 230, 567, 392, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 0; + let segment_n = 1; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 63, 227, 849, 503, 967, 726, 979, 1023, 1025, 514, 762, 161, 674, 961, 1033, 1035, + 440, 387, 3, 622, 1041, 1030, 17, 773, 934, 469, 197, 1004, 1048, 908, 357, 827, + 537, 950, 1000, 1049, 334, 219, 1006, 758, 1059, 137, 722, 462, 401, 236, 216, + 1045, 227, 1010, 1072, 1062, 755, 771, 922, 648, 26, 59, 1048, 1078, 626, 1034, + 157, 167, 552, 835, 1087, 936, 1028, 339, 442, 303, 207, 1033, 411, 348, 672, 954, + 888, 491, 815, 937, 733, 1079, 275, 427, 1103, 148, 571, 1078, 1069, 1113, 492, + 889, 962, 733, 1117, 921, 474, 277, 934, 555, 1035, 172, 1053, 804, 1079, 708, 206, + 1044, 1125, 454, 1086, 686, 408, 1136, 335, 1001, 200, 671, 341, 1115, 1016, 1043, + 1088, 629, 782, 600, 793, 684, 1056, 671, 1040, 4, 1156, 1037, 1145, 694, 607, 804, + 1125, 127, 829, 858, 580, 782, 661, 377, 1098, 946, 913, 90, 1070, 1099, 1064, 790, + 846, 675, 1042, 724, 738, 682, 1033, 994, 997, 1111, 378, 934, 771, 629, 1120, + 1193, 1177, 33, 1183, 691, 346, 1016, 374, 475, 110, 1185, 966, 730, 1118, 1004, + 237, 347, 803, 50, 790, 1136, 797, 146, 1214, 1162, 655, 1192, 1180, 1219, 1171, + 428, 252, 552, 1221, 1164, 1223, 834, 369, 696, 1197, 1187, 483, 1179, 1236, 982, + 890, 1182, 1238, 745, 1215, 756, 1188, 1244, 1151, 712, 718, 1205, 1167, 799, 1211, + 1253, 86, 1163, 1155, 1095, 151, 255, 1160, 765, 1080, 555, 963, 150, 1197, 1189, + 945, 1154, 1268, 840, 392, 172, 1191, 1108, 1271, 1059, 768, 813, 719, 964, 824, + 577, 1205, 1077, 307, 1199, 1287, 1066, 244, 1177, 38, 1004, 1281, 1273, 518, 1077, + 1263, 1225, 1043, 1243, 1199, 1237, 17, 368, 1021, 1245, 971, 1303, 849, 808, 1312, + 1313, 1161, 1185, 1154, 1215, 1201, 1037, 1269, 324, 649, 1092, 812, 983, 1305, + 782, 1050, 1270, 1108, 1327, 1311, 503, 1135, 1045, 1326, 748, 458, 139, 685, 1146, + 1338, 1326, 334, 224, 1345, 809, 847, 678, 569, 1316, 1335, 1353, 1114, 353, 1241, + 1123, 524, 950, 298, 1058, 631, 1260, 1161, 1315, 1037, 473, 1291, 946, 911, 1187, + 33, 1372, 643, 962, 120, 1172, 341, 566, 986, 6, 888, 1118, 1322, 1054, 22, 865, + 352, 1208, 1336, 1124, 1143, 1065, 1392, 1183, 1307, 1375, 617, 1351, 139, 1383, + 1018, 1392, 1402, 1145, 471, 962, 562, 1409, 1374, 1210, 1297, 361, 1085, 1415, + 1347, 794, 87, 1073, 759, 1241, 519, 1154, 1259, 1293, 1383, 551, 409, 1429, 587, + 64, 1285, 1314, 1305, 1371, 1161, 1196, 572, 1136, 1430, 1141, 926, 1439, 1005, + 1088, 354, 574, 1044, 1002, 1285, 707, 1264, 1114, 695, 1449, 1273, 693, 362, 846, + 397, 401, 1462, 1089, 1352, 1344, 979, 152, 1303, 985, 398, 1106, 1320, 916, 680, + 1475, 1353, 1314, 578, 479, 1045, 1350, 930, 34, 1484, 1028, 131, 64, 1255, 808, + 1437, 1168, 1051, 658, 732, 1035, 1346, 199, 640, 1323, 300, 433, 730, 1503, 560, + 422, 1371, 729, 1450, 1163, 267, 1496, 1078, 467, 76, 1473, 1498, 1115, 65, 979, + 114, 551, 1482, 1166, 1509, 1469, 1526, 1440, 961, 1002, 1530, 1050, 1295, 1161, + 1299, 1332, 1038, 1263, 420, 1490, 1495, 578, 1308, 1057, 1105, 620, 965, 1281, + 789, 1100, 820, 98, 1508, 942, 176, 939, 515, 51, 997, 849, 1294, 663, 325, 405, + 1417, 1544, 1308, 1325, 1492, 790, 975, 1101, 1339, 1470, 1570, 1486, 1567, 1397, + 2, 809, 499, 1580, 1578, 1461, 867, 830, 713, 968, 1574, 1587, 136, 1410, 1, 1288, + 1186, 1222, 1029, 1326, 1590, 4, 4, 1560, 280, 385, 1462, 54, 1406, 1156, 1035, + 1608, 1126, 145, 157, 708, 241, 889, 1426, 1514, 1569, 1607, 1064, 1259, 73, 1560, + 412, 802, 623, 1578, 1624, 1617, 1265, 1002, 1383, 1479, 1235, 1499, 1326, 1556, + 616, 1567, 1611, 1627, 1611, 141, 394, 384, 1473, 1639, 1113, 549, 1544, 226, 337, + 567, 628, 1332, 1612, 1634, 1654, 8, 1016, 1172, 508, 1521, 1308, 245, 545, 1593, + 1650, 927, 1668, 1402, 421, 1438, 1024, 444, 1061, 1579, 1496, 1272, 887, 1314, + 1187, 276, 574, 472, 1457, 529, 1669, 282, 691, 1553, 1688, 1471, 1694, 1690, 1594, + 1675, 1690, 1047, 1632, 1438, 1697, 1649, 59, 1677, 1443, 1420, 1471, 1675, 418, + 259, 1085, 788, 339, 64, 987, 5, 1421, 1636, 361, 1312, 1564, 1359, 74, 1666, 653, + 1714, 1584, 1192, 341, 1471, 907, 1591, 582, 775, 1412, 1674, 505, 385, 1301, 1505, + 1299, 985, 31, 1713, 762, 1747, 1705, 1685, 917, 636, 105, 1528, 513, 881, 1414, + 1645, 1602, 1313, 1537, 1746, 1738, 1453, 1625, 1750, 552, 411, 227, 1228, 1615, + 288, 1542, 1709, 1277, 1649, 1776, 1327, 1743, 1769, 863, 242, 1005, 1748, 972, + 1332, 46, 1758, 1740, 1582, 1789, 361, 678, 250, 576, 587, 1313, 703, 663, 443, + 1754, 1422, 1561, 1800, 1034, 452, 1493, 753, 1701, 1408, 145, 60, 692, 1804, 1716, + 1749, 1778, 1516, 1176, 1736, 1224, 1750, 368, 429, 1818, 1564, 1789, 711, 1451, + 1590, 1641, 1574, 1313, 1833, 1363, 1115, 1812, 1828, 1530, 1444, 928, 1695, 1492, + 1566, 289, 1705, 1745, 591, 1664, 517, 1072, 1474, 1327, 1853, 1049, 278, 1828, + 1857, 1828, 1543, 482, 1861, 1368, 1610, 1493, 1827, 1610, 975, 1678, 873, 1868, + 1765, 1420, 974, 1037, 1244, 1816, 1873, 1625, 108, 1217, 1208, 1360, 63, 1644, + 1368, 1860, 1624, 1748, 1661, 1677, 1871, 1594, 1859, 1790, 341, 1896, 1866, 285, + 905, 1576, 1575, 232, 1828, 428, 1806, 1531, 872, 1610, 1045, 1910, 769, 1897, + 1186, 1854, 1887, 1857, 1861, 980, 1594, 1635, 1892, 722, 1515, 1842, 1139, 1026, + 1383, 1920, 1558, 1108, 1831, 1867, 1206, 1390, 1848, 1358, 861, 209, 1909, 1883, + 445, 974, 477, 1937, 1391, 1187, 1726, 1253, 47, 1027, 1886, 1760, 1157, 1546, 545, + 877, 1192, 1843, 748, 1561, 1481, 318, 1207, 1905, 733, 1879, 784, 768, 1881, 1450, + 348, 1881, 949, 1511, 1670, 1966, 492, 1748, 1061, 1612, 1399, 348, 1575, 1736, + 639, 1986, 1978, 90, 1318, 1559, 1960, 1073, 1886, 733, 284, 268, 1972, 1969, 1912, + 1992, 60, 1457, 1769, 1335, 1345, 1999, 1843, 1925, 1475, 1850, 2004, 1077, 970, + 553, 1921, 1676, 1443, 1995, 1975, 660, 1142, 2008, 1453, 933, 1983, 1195, 1906, + 645, 627, 1525, 1752, 1490, 1040, 1873, 632, 383, 1590, 1924, 165, 1961, 1543, + 1719, 1171, 1881, 2031, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 0; + let segment_n = 2; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 1171, 1043, 2046, 209, 1914, 1872, 570, 1427, 931, 1485, 2024, 1760, 2049, 1955, + 1265, 1039, 1693, 578, 1665, 1457, 1513, 1283, 2023, 1956, 1744, 1659, 2032, 145, + 1887, 1633, 2040, 1456, 440, 2044, 2080, 1856, 947, 976, 2084, 2085, 1826, 1774, + 2015, 1858, 701, 830, 2065, 1609, 1680, 1502, 653, 554, 201, 1819, 2081, 1898, 561, + 287, 1942, 969, 1981, 1300, 177, 2021, 1968, 772, 475, 1996, 1533, 1846, 1933, + 2113, 2115, 2026, 2023, 838, 1863, 857, 1470, 2030, 1990, 1339, 1055, 2021, 511, + 1197, 1776, 1718, 209, 1759, 2054, 1010, 210, 2063, 1005, 1586, 2141, 2007, 1323, + 347, 19, 68, 1796, 1041, 2053, 912, 2144, 443, 1388, 2154, 2028, 2039, 1528, 2120, + 1314, 1868, 2159, 1655, 865, 1852, 939, 129, 2168, 797, 707, 338, 1918, 1965, 1541, + 1983, 1942, 1160, 2049, 2126, 1974, 2075, 1326, 1557, 1700, 1377, 1054, 1570, 1743, + 272, 1884, 1716, 2023, 549, 1984, 1510, 634, 2182, 901, 1773, 1428, 2187, 1703, + 1906, 2148, 1394, 790, 584, 331, 2174, 1573, 1347, 1555, 1867, 156, 2206, 912, + 1728, 1803, 1883, 1111, 2136, 2221, 2173, 216, 2206, 2215, 1979, 2217, 1439, 1403, + 1019, 2150, 1978, 1823, 1838, 1244, 1679, 1882, 1848, 1752, 2241, 53, 2097, 2212, + 736, 2230, 2111, 852, 1435, 1829, 2122, 59, 233, 221, 1739, 2212, 1647, 1663, 2204, + 1739, 1911, 143, 456, 2044, 1735, 1345, 2027, 2186, 69, 540, 971, 2022, 2050, 1291, + 1159, 71, 1582, 2096, 1370, 2171, 1631, 2260, 2210, 1905, 2057, 1708, 2195, 1333, + 2141, 1605, 2286, 2211, 2261, 1737, 1239, 2166, 1901, 1709, 2205, 2147, 1918, 2292, + 1851, 1339, 2154, 2144, 1027, 197, 1856, 1543, 919, 2076, 1613, 2012, 1100, 1862, + 2316, 1717, 1044, 1999, 2298, 2268, 2228, 1454, 1540, 2046, 1667, 1703, 945, 111, + 1982, 2324, 2271, 276, 1327, 602, 1538, 1946, 1976, 2244, 2136, 495, 2205, 2333, + 1846, 27, 2332, 1047, 2039, 2116, 1116, 1933, 593, 189, 2094, 74, 1020, 1362, 1069, + 2203, 1627, 2279, 2332, 468, 2177, 2364, 2086, 1078, 1594, 1539, 2285, 64, 511, + 1509, 2155, 323, 2202, 2235, 2350, 1590, 1624, 2320, 2243, 1261, 1460, 2128, 1523, + 494, 2186, 874, 749, 1586, 694, 2319, 1978, 1365, 2239, 1042, 2098, 70, 1940, 2343, + 2249, 2152, 2259, 1306, 436, 2294, 1027, 2235, 2391, 653, 1056, 2233, 1700, 1388, + 2346, 2391, 1215, 2395, 815, 650, 2118, 1831, 2392, 1157, 1297, 2192, 2337, 2265, + 2271, 1647, 960, 448, 1558, 1546, 289, 2130, 1831, 2283, 636, 2214, 2409, 2435, + 2242, 2209, 584, 1263, 1451, 1024, 1798, 2451, 1482, 124, 1506, 2455, 1119, 1933, + 2432, 1953, 2369, 2176, 1838, 77, 788, 1787, 1848, 1711, 2178, 156, 1449, 1395, + 2166, 2156, 2161, 1706, 1007, 1225, 2456, 2463, 2412, 1660, 2428, 1643, 1518, 2034, + 2421, 651, 1381, 2388, 1425, 2062, 849, 1994, 2495, 2013, 2100, 704, 830, 2499, + 2057, 1953, 2419, 214, 1358, 2135, 2498, 2362, 2450, 1182, 1733, 299, 2392, 2514, + 2213, 1901, 1301, 791, 1521, 586, 2521, 956, 2353, 1060, 811, 2163, 1398, 1111, + 1584, 2434, 1788, 350, 814, 659, 1248, 2244, 517, 2513, 2003, 2270, 1878, 463, + 2540, 2489, 2541, 776, 607, 1098, 2374, 2197, 2551, 1493, 1182, 364, 857, 2005, + 1803, 2099, 465, 2347, 681, 2254, 1172, 1411, 2443, 2562, 437, 2259, 1535, 2570, + 2203, 2557, 2568, 2560, 2152, 2324, 1339, 2396, 121, 1663, 1236, 1577, 2199, 2249, + 2581, 2092, 2573, 2586, 224, 2216, 1958, 779, 2586, 1972, 335, 2461, 2284, 1296, + 2162, 2579, 46, 2529, 1022, 2412, 1917, 1682, 1431, 59, 2592, 2468, 2472, 2384, + 812, 866, 250, 1290, 122, 2064, 1246, 2213, 2618, 1867, 1219, 2250, 1215, 1876, + 2368, 2503, 2349, 2251, 186, 2615, 1698, 2591, 2118, 391, 2610, 1786, 2272, 709, + 1776, 1466, 2488, 2480, 2462, 2375, 1893, 2637, 2606, 2215, 1787, 1472, 2626, 955, + 1964, 2144, 2213, 2645, 2515, 2642, 520, 2354, 967, 2360, 171, 2660, 1611, 2476, + 1221, 2290, 2662, 2654, 1093, 2016, 2535, 612, 2032, 1955, 1923, 2676, 1506, 443, + 2659, 970, 1344, 2677, 2680, 1686, 54, 2433, 2472, 2187, 2485, 1061, 2216, 2572, + 2437, 1379, 2185, 1732, 2111, 2498, 1279, 1467, 2611, 2068, 2138, 2024, 2243, 2241, + 2482, 907, 339, 693, 1879, 976, 2415, 2120, 530, 2412, 2693, 278, 783, 111, 486, + 1475, 2588, 2728, 2463, 1761, 2379, 2701, 2465, 2504, 2733, 1464, 797, 2114, 1956, + 2182, 1404, 1698, 2572, 1246, 2055, 1296, 729, 2143, 1161, 1502, 2140, 2079, 1862, + 1546, 2565, 2461, 2631, 1369, 2020, 2755, 2159, 2650, 1459, 2064, 1198, 2680, 2317, + 1491, 62, 2369, 547, 539, 2576, 1553, 2633, 1269, 1720, 2768, 1754, 2246, 2393, + 1421, 2775, 621, 5, 1070, 2749, 2484, 1639, 2097, 1077, 318, 2757, 2283, 242, 743, + 2558, 30, 2711, 1584, 462, 2800, 2802, 2756, 1353, 717, 2745, 733, 1277, 2691, + 1112, 1250, 1900, 1796, 2689, 2815, 2209, 526, 2778, 2224, 2808, 1510, 2799, 2743, + 1493, 1017, 464, 2504, 2799, 2205, 2670, 1549, 2497, 2568, 228, 2735, 784, 2591, + 2027, 2799, 2389, 956, 2725, 2815, 1983, 2647, 2671, 363, 2849, 2848, 578, 1952, + 1326, 2158, 2855, 1638, 817, 2846, 1035, 2406, 984, 2299, 1777, 2084, 958, 2386, + 2866, 975, 2866, 670, 939, 2865, 1445, 2811, 2501, 2865, 2734, 2393, 204, 2835, + 1991, 2868, 1001, 2881, 2495, 1318, 581, 292, 2879, 231, 2664, 1878, 2647, 2303, + 2753, 1605, 538, 2345, 2755, 2860, 2663, 2735, 189, 2570, 2087, 162, 72, 2446, + 1728, 1153, 2198, 1049, 2875, 745, 1313, 1114, 2692, 592, 820, 469, 872, 1207, + 2756, 150, 1494, 1912, 1753, 2567, 2477, 572, 2734, 817, 519, 2471, 962, 1247, 802, + 2931, 1552, 2845, 2801, 2891, 2550, 447, 2704, 879, 2909, 1159, 2739, 1542, 2190, + 1888, 2530, 2521, 2904, 1095, 2856, 434, 368, 285, 2810, 1488, 2962, 761, 1732, + 2410, 2934, 2856, 2376, 1431, 2683, 2902, 1533, 2851, 2337, 1746, 2944, 2701, 137, + 628, 2838, 2157, 2456, 2519, 2391, 559, 961, 2962, 2378, 2727, 2841, 2200, 2701, + 1966, 2210, 2996, 2654, 1583, 2875, 2890, 2923, 2135, 2743, 2672, 86, 2986, 2691, + 2984, 2699, 2869, 179, 2419, 1993, 1523, 1378, 2696, 2188, 1774, 2330, 2582, 1958, + 2541, 2903, 3004, 487, 793, 2820, 2438, 2131, 1846, 2751, 3007, 21, 2102, 2128, + 614, 794, 3034, 129, 2965, 2963, 2474, 2575, 2850, 2659, 3024, 2553, 1912, 3048, + 2991, 754, 1039, 1208, 2939, 1302, 1746, 2418, 1473, 3058, 2931, 3060, 2368, 37, + 3021, 1483, 2941, 2676, 422, 1775, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 0; + let segment_n = 3; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 1994, 1933, 3035, 2492, 2197, 748, 349, 140, 1121, 3079, 2168, 1970, 2197, 2830, + 1473, 2995, 827, 3087, 2246, 3046, 46, 3067, 1420, 1484, 3062, 2612, 2122, 2360, + 3011, 1097, 1455, 2069, 2660, 3103, 574, 1665, 3086, 2750, 3101, 1391, 2770, 3013, + 1691, 2929, 2411, 1272, 1296, 811, 2499, 3064, 2675, 2926, 212, 399, 1439, 2894, + 2697, 1881, 2007, 2706, 3130, 3046, 1253, 1722, 2461, 3023, 3128, 2093, 2757, 897, + 3057, 1176, 2290, 1336, 3108, 3145, 1445, 2613, 3133, 2501, 1305, 1654, 851, 2608, + 2289, 1673, 1057, 1657, 140, 2399, 419, 2186, 3033, 3161, 3115, 2430, 2574, 1914, + 2974, 3134, 2458, 3073, 2879, 226, 2871, 2926, 1917, 3015, 3147, 3030, 2852, 1399, + 878, 1885, 3182, 217, 3110, 1513, 1933, 1628, 3169, 220, 1147, 3116, 3010, 438, + 2263, 2382, 2443, 440, 2907, 404, 2897, 68, 2020, 2815, 3096, 3169, 3144, 1401, + 3029, 1072, 33, 663, 890, 2932, 3200, 3096, 628, 1371, 3096, 1851, 1144, 365, 2097, + 2344, 974, 978, 2558, 135, 3055, 446, 1128, 2599, 1680, 255, 2191, 1603, 1025, + 1842, 2017, 517, 3210, 3170, 966, 790, 811, 2253, 2222, 1163, 1933, 2902, 762, 546, + 3219, 198, 1729, 1650, 3208, 1435, 2689, 1729, 3113, 2222, 3206, 2918, 584, 829, + 2437, 3113, 2805, 2742, 3172, 2823, 356, 716, 1895, 2369, 2268, 3255, 2981, 2733, + 903, 1945, 1593, 553, 32, 3105, 1759, 238, 1154, 2399, 2691, 480, 2571, 3285, 3211, + 959, 1936, 2767, 2697, 375, 2833, 3264, 2837, 3305, 2938, 3284, 1696, 801, 485, + 1911, 3282, 3221, 1002, 876, 18, 1589, 2416, 3304, 160, 3097, 1387, 3074, 3322, + 3012, 3209, 2100, 1419, 2621, 2667, 856, 1981, 1456, 1902, 1690, 1318, 2344, 620, + 1141, 2449, 3063, 2904, 2311, 2441, 2419, 1166, 1786, 3338, 563, 3266, 2275, 2559, + 3352, 3352, 2638, 1635, 1671, 2322, 2914, 3333, 2832, 2522, 3050, 2184, 3045, 1692, + 474, 293, 803, 3271, 3192, 1902, 3363, 3094, 221, 2809, 3030, 791, 2951, 1791, + 3242, 3202, 2124, 1579, 1782, 2476, 1695, 2817, 2673, 1016, 1265, 2231, 1911, 3335, + 228, 303, 1179, 1439, 1720, 3400, 2533, 2439, 3355, 2596, 3061, 119, 318, 2476, + 1347, 3194, 2759, 1670, 2601, 3399, 1707, 3046, 2575, 2810, 2234, 2321, 1292, 1207, + 3409, 2350, 3237, 2156, 3385, 3286, 2872, 2743, 1195, 1246, 972, 1649, 996, 3093, + 2833, 3340, 3171, 1957, 3304, 908, 3431, 819, 322, 3212, 3077, 1972, 2919, 2679, + 2408, 2521, 2940, 2055, 3429, 2653, 2908, 1868, 2689, 3100, 3389, 2094, 1810, 3278, + 2796, 2819, 3052, 2430, 2080, 3464, 3150, 2448, 3084, 60, 2216, 1493, 3163, 3335, + 2886, 914, 1603, 3429, 3468, 3226, 2832, 2990, 3471, 483, 2746, 3181, 943, 547, + 1556, 778, 2143, 2401, 2108, 586, 3425, 320, 2811, 3501, 3326, 1887, 3444, 876, + 1612, 664, 2896, 907, 3438, 2041, 1891, 954, 3231, 640, 734, 3458, 2878, 3408, 621, + 3011, 2434, 770, 2784, 760, 3226, 3505, 251, 3070, 2046, 95, 513, 2968, 3523, 3353, + 1673, 2599, 2740, 495, 3515, 1649, 3542, 3105, 3535, 2339, 2537, 2100, 732, 3512, + 2167, 3480, 3016, 1366, 868, 3386, 711, 1839, 3403, 3439, 2795, 3535, 1514, 1629, + 2180, 3228, 2149, 3422, 3485, 2052, 3482, 320, 3069, 1410, 1403, 1091, 3257, 3173, + 1376, 2807, 2799, 208, 1410, 1003, 1733, 695, 3398, 619, 3549, 1557, 3422, 190, + 1925, 2222, 2676, 3523, 3086, 2714, 3131, 1158, 993, 3381, 1871, 193, 3442, 671, + 3583, 2495, 3288, 3398, 2887, 3159, 1794, 2334, 3606, 3573, 2011, 1480, 2701, 2231, + 3613, 3588, 2949, 1926, 3005, 3621, 2029, 415, 2734, 43, 1361, 3358, 3574, 3398, + 3247, 3378, 632, 2396, 1477, 3174, 222, 1372, 1804, 3267, 1834, 2155, 360, 3350, + 2856, 2645, 3089, 23, 3508, 3648, 999, 3100, 2664, 3467, 1482, 3534, 2781, 3150, + 2638, 3191, 1524, 2977, 3125, 851, 1556, 2222, 3669, 1880, 722, 953, 1260, 3638, + 3533, 3430, 3478, 2298, 3458, 2906, 3654, 2653, 2603, 2776, 1028, 3002, 328, 2747, + 2779, 1072, 3184, 3554, 3374, 3156, 1384, 3100, 3346, 1729, 1349, 3615, 3697, 3535, + 2686, 3610, 2074, 230, 37, 1497, 3489, 3384, 1671, 2232, 1013, 1453, 3649, 1058, + 3243, 3550, 177, 96, 604, 821, 633, 3287, 3320, 2220, 413, 3709, 3326, 2402, 3294, + 2790, 2368, 1661, 3308, 2701, 3725, 3330, 1705, 2927, 1502, 1837, 1100, 3746, 654, + 3319, 218, 1922, 3680, 1232, 3597, 860, 2108, 2154, 1082, 1995, 3753, 3462, 2466, + 318, 1409, 3764, 2731, 3603, 2844, 3757, 2056, 2602, 3741, 1681, 800, 3723, 2870, + 3288, 2446, 2389, 3004, 3604, 3682, 3142, 3039, 3630, 1750, 1344, 3730, 1941, 2803, + 2550, 2626, 3777, 762, 497, 3359, 2122, 3429, 600, 826, 3588, 3500, 1739, 3711, + 3676, 3399, 3402, 3466, 2549, 2556, 2734, 1176, 3757, 470, 1921, 3746, 3799, 563, + 2833, 2446, 1317, 3522, 227, 3672, 3260, 3470, 3410, 1927, 2243, 3316, 2320, 635, + 2812, 1504, 3785, 3228, 304, 2769, 311, 3367, 3392, 3726, 3816, 2705, 2129, 2989, + 335, 3833, 405, 2941, 2889, 3733, 3023, 2587, 3210, 3233, 3673, 2309, 2814, 3251, + 3150, 3496, 3169, 2995, 2407, 3712, 360, 3826, 3247, 2686, 2504, 3338, 3642, 2022, + 3774, 3508, 3792, 2212, 3797, 3778, 3715, 2145, 2512, 3171, 1064, 2584, 3869, 3585, + 3851, 3735, 738, 3848, 3254, 3886, 3672, 1251, 3138, 3453, 3143, 3619, 908, 2126, + 1793, 2885, 3848, 1124, 371, 2480, 2259, 847, 2984, 1946, 2711, 895, 3895, 3913, + 606, 3555, 3842, 3282, 2123, 3518, 3750, 2224, 2785, 3325, 2308, 3635, 886, 3620, + 3155, 3829, 2319, 3874, 2562, 2655, 3836, 46, 314, 3848, 3751, 2409, 2931, 1999, + 2616, 3702, 109, 84, 1304, 1813, 3010, 3349, 3821, 3408, 3952, 3328, 3799, 2967, + 3035, 3648, 2474, 1687, 1279, 3947, 2466, 1357, 3900, 3549, 1002, 2750, 2986, 3379, + 1172, 3133, 534, 3210, 3414, 2426, 3883, 3862, 3752, 2932, 1354, 3897, 1874, 2831, + 2539, 2703, 758, 3975, 3144, 2367, 1233, 3797, 1662, 593, 3642, 2411, 2952, 337, + 2414, 3672, 3025, 2729, 1912, 2480, 3965, 3085, 3863, 1709, 3944, 1021, 542, 1782, + 1727, 3879, 2812, 3276, 4018, 3267, 3672, 1016, 3486, 3657, 3735, 516, 3565, 3432, + 2371, 2634, 2072, 551, 2391, 2460, 3462, 787, 4015, 1804, 2544, 3707, 3879, 2939, + 3612, 861, 2213, 3021, 3818, 3902, 3944, 4045, 3272, 1861, 747, 3224, 4054, 3263, + 1623, 2018, 1460, 2158, 1170, 88, 1537, 3542, 1722, 4012, 1630, 3058, 2203, 2610, + 4059, 3730, 2042, 1090, 199, 1907, 3848, 3983, 3810, 3603, 1068, 3985, 2454, 3950, + 1163, 1752, 3677, 1553, 2352, 3650, 1758, 1756, 3893, 3566, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 1; + let segment_n = 0; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 3988, 3490, 1643, 1572, 2, 2045, 1188, 3185, 1866, 1635, 3419, 1196, 3779, 2875, + 2786, 3906, 3086, 2300, 3, 3715, 3942, 3921, 4060, 3970, 22, 4095, 23, 11, 2880, 1, + 3617, 3244, 3935, 3222, 2814, 1706, 3349, 3372, 34, 1984, 1488, 3332, 3582, 3950, + 2803, 3986, 2228, 3844, 1767, 3772, 43, 3307, 3131, 3811, 3437, 3530, 2226, 4093, + 3934, 3401, 2948, 59, 4067, 3888, 32, 1302, 3233, 3650, 2178, 2376, 3271, 4094, + 2467, 2975, 3512, 3452, 3074, 3034, 57, 2174, 2062, 4072, 3086, 3175, 3988, 1343, + 48, 3438, 3295, 87, 1844, 4004, 3389, 46, 2672, 3108, 3065, 2149, 3720, 4057, 84, + 2027, 2275, 2567, 1600, 2653, 1444, 2508, 63, 3744, 1255, 3175, 49, 2728, 3522, 34, + 3046, 3728, 115, 1199, 2500, 3337, 96, 108, 28, 3502, 1317, 3739, 3318, 112, 4079, + 2308, 2026, 1040, 121, 2328, 3084, 56, 4088, 2008, 2194, 116, 1140, 2896, 135, 132, + 144, 2501, 143, 147, 3817, 2696, 2858, 3652, 3507, 19, 2382, 3342, 3957, 2451, + 3434, 1282, 1549, 4005, 3990, 3881, 2381, 3239, 2745, 2168, 2493, 4073, 3910, 109, + 110, 2368, 3030, 3839, 3248, 3830, 3761, 46, 3449, 70, 156, 2833, 2515, 3655, 2023, + 39, 3344, 180, 2053, 1684, 2909, 2463, 3773, 3520, 3941, 197, 3686, 4025, 192, + 2798, 202, 3435, 2350, 3632, 3295, 1999, 153, 2793, 3683, 209, 152, 1262, 2850, + 1430, 4028, 3260, 2211, 1700, 3592, 1209, 3571, 2943, 90, 3266, 3022, 3878, 218, + 1718, 8, 1771, 229, 229, 2940, 2986, 2155, 2464, 2345, 102, 1974, 3434, 1860, 3667, + 119, 245, 2392, 152, 1126, 3593, 3527, 3949, 2743, 2701, 3953, 3002, 221, 3610, + 2928, 1918, 1094, 261, 262, 3229, 3836, 24, 3257, 3270, 1076, 5, 2958, 3003, 4039, + 148, 2097, 2706, 3959, 3262, 3022, 3132, 56, 6, 1700, 162, 2764, 3144, 1230, 1445, + 1070, 3603, 4004, 3432, 282, 1736, 2961, 1993, 3222, 2212, 3370, 291, 268, 1639, + 3939, 2905, 1712, 1069, 1272, 3834, 2070, 286, 3625, 279, 3301, 3454, 162, 305, + 227, 302, 3646, 313, 3865, 1087, 2241, 1663, 4019, 1606, 326, 2658, 3750, 2135, + 328, 235, 2500, 2800, 279, 2614, 2567, 58, 37, 4069, 47, 3206, 342, 188, 2918, + 3825, 3460, 1495, 3265, 4031, 2745, 2380, 165, 278, 3606, 1443, 3855, 315, 2342, + 359, 303, 3392, 362, 3452, 2982, 3839, 1608, 1459, 2650, 368, 1814, 2275, 3642, + 2952, 369, 3927, 3159, 1622, 360, 334, 3930, 3472, 138, 3185, 374, 222, 3100, 1798, + 2777, 1706, 2917, 2649, 2096, 266, 394, 1427, 3497, 1292, 2206, 392, 2694, 1707, + 3555, 3896, 3302, 1105, 406, 406, 2399, 2411, 3565, 3063, 3925, 3997, 3725, 1447, + 318, 3664, 416, 2251, 131, 2895, 2403, 1350, 1749, 16, 426, 4019, 3332, 4018, 411, + 280, 2228, 3016, 3934, 435, 1872, 3959, 2684, 342, 1162, 2587, 3788, 4026, 353, + 400, 339, 2291, 413, 4077, 184, 4033, 3671, 346, 4048, 1047, 3619, 166, 3494, 2644, + 279, 2239, 456, 442, 322, 3818, 161, 1896, 3253, 438, 3402, 3643, 3521, 3839, 2954, + 203, 3217, 1807, 452, 3231, 4003, 2726, 181, 3880, 3289, 1694, 446, 474, 427, 66, + 1619, 2430, 3377, 3705, 2047, 472, 369, 120, 494, 2756, 2091, 180, 10, 1508, 1795, + 3633, 144, 337, 4051, 502, 3416, 504, 436, 1117, 3587, 3623, 2741, 3494, 502, 2813, + 115, 3231, 2974, 2351, 2250, 4063, 515, 502, 3553, 199, 1882, 3927, 2514, 3087, + 2321, 450, 92, 1073, 2338, 2016, 2364, 1586, 1697, 1759, 1708, 3172, 1211, 407, + 163, 3202, 501, 1832, 3385, 545, 4057, 215, 65, 1148, 3082, 3788, 500, 310, 2162, + 446, 1207, 384, 2331, 3702, 3438, 2386, 2511, 2751, 2311, 4016, 4038, 565, 2123, + 4005, 3441, 413, 2705, 349, 570, 2292, 568, 1324, 1481, 3874, 550, 545, 582, 3371, + 215, 592, 3841, 2807, 3537, 524, 1897, 1714, 3159, 596, 364, 1889, 3933, 457, 1317, + 2543, 1213, 2658, 181, 444, 2997, 3926, 1768, 1966, 1384, 3599, 3968, 433, 304, + 2624, 3085, 606, 3968, 1620, 114, 244, 441, 2426, 34, 2765, 2370, 4076, 3540, 438, + 602, 2860, 1727, 3750, 3888, 3848, 1457, 1851, 3201, 3435, 1397, 240, 3450, 4032, + 210, 2774, 3939, 650, 437, 315, 1374, 2614, 181, 1900, 2323, 2730, 3691, 2968, 486, + 636, 2549, 2433, 204, 268, 139, 2891, 572, 659, 3636, 568, 3588, 673, 665, 571, + 1273, 656, 1522, 3774, 433, 1740, 1225, 3837, 3810, 203, 2764, 466, 2456, 548, + 3811, 658, 647, 553, 3606, 548, 673, 3231, 253, 135, 16, 156, 2521, 3477, 108, + 2448, 670, 1430, 346, 616, 3352, 1418, 248, 2283, 717, 3306, 2386, 480, 1873, 2910, + 724, 2143, 557, 3569, 386, 2504, 728, 2769, 727, 2801, 501, 90, 680, 3921, 1721, + 711, 560, 1235, 52, 1741, 2917, 1716, 585, 744, 604, 73, 571, 338, 2417, 127, 1412, + 4086, 120, 686, 2937, 1667, 2503, 491, 754, 3941, 2376, 762, 737, 259, 528, 3686, + 3771, 1717, 761, 3968, 2662, 40, 1719, 775, 665, 4008, 706, 1365, 3115, 89, 2193, + 1136, 2328, 471, 2329, 638, 590, 66, 3378, 312, 3487, 324, 645, 639, 3706, 3909, + 4036, 1542, 692, 2490, 3814, 800, 15, 2687, 4077, 1627, 786, 568, 593, 3966, 3956, + 638, 2024, 733, 2862, 2482, 1728, 95, 613, 158, 793, 568, 3619, 55, 2964, 3020, + 794, 2147, 460, 3180, 1459, 309, 2983, 443, 3010, 3695, 2593, 790, 807, 1633, 843, + 361, 3141, 324, 499, 309, 43, 3856, 2354, 1853, 591, 1151, 687, 582, 1525, 1594, + 2164, 265, 421, 2592, 2636, 2356, 361, 834, 35, 2267, 3114, 3425, 1689, 2024, 857, + 1159, 3108, 195, 3849, 2516, 181, 202, 874, 2371, 774, 1535, 711, 794, 2686, 2642, + 3781, 2184, 1333, 2807, 524, 2409, 4083, 2988, 887, 864, 70, 3995, 3267, 1877, + 3963, 3247, 1600, 596, 307, 2273, 1344, 910, 475, 736, 2561, 304, 2636, 915, 2689, + 654, 1941, 1125, 2766, 461, 2247, 916, 3780, 3677, 454, 877, 3423, 842, 2621, 3791, + 50, 380, 3101, 844, 2594, 1216, 1812, 767, 888, 3541, 690, 233, 3272, 1511, 851, + 161, 767, 3583, 755, 1448, 1633, 907, 3078, 3961, 2910, 955, 3204, 1975, 1842, 817, + 2869, 1668, 779, 3339, 552, 969, 1058, 766, 955, 1722, 4014, 519, 2975, 1459, 874, + 2952, 947, 979, 1933, 2478, 665, 985, 982, 815, 21, 1179, 3217, 1056, 129, 815, + 1508, 739, 3801, 982, 768, 4038, 1788, 2571, 999, 2494, 3683, 1257, 1003, 751, + 4079, 1009, 184, 91, 3936, 1790, 3997, 719, 1012, 3540, 3910, 848, 980, 726, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 1; + let segment_n = 1; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 3423, 3780, 3751, 584, 2089, 3394, 594, 952, 3177, 3537, 2536, 2720, 3812, 1004, + 795, 353, 487, 27, 458, 566, 404, 3545, 945, 998, 3517, 1047, 4008, 448, 515, 999, + 1048, 939, 277, 2497, 89, 2786, 989, 420, 3571, 384, 740, 331, 2268, 2791, 843, + 518, 2447, 3880, 918, 3041, 3534, 385, 134, 3210, 2690, 3631, 3050, 839, 358, 1062, + 2949, 910, 537, 874, 2462, 3775, 842, 1083, 1063, 3225, 3141, 944, 971, 2956, 4004, + 1030, 178, 850, 2505, 3374, 474, 2961, 20, 3616, 3197, 1028, 2236, 290, 851, 561, + 934, 914, 260, 235, 1078, 931, 94, 2329, 593, 717, 2290, 869, 555, 368, 644, 3422, + 822, 3169, 1105, 21, 2107, 1109, 803, 1123, 1135, 184, 4070, 661, 3376, 853, 2888, + 3867, 1072, 1128, 965, 2138, 1119, 186, 405, 2561, 3857, 481, 2208, 287, 1137, + 2389, 132, 646, 627, 1128, 4051, 1030, 2679, 123, 63, 1120, 3701, 1135, 1166, 4029, + 2725, 1173, 2213, 2216, 465, 986, 303, 983, 846, 1069, 1096, 1172, 324, 1071, 718, + 486, 1110, 1166, 1063, 276, 1081, 3791, 2574, 180, 3812, 825, 2481, 586, 2292, + 2777, 2725, 3779, 748, 92, 1193, 1007, 3302, 568, 2694, 255, 993, 897, 3676, 226, + 1104, 2778, 2121, 1201, 1064, 2908, 1195, 1223, 1092, 3153, 3966, 1227, 3241, 1228, + 3771, 1163, 1232, 5, 989, 3734, 3190, 1138, 1101, 3105, 1118, 3935, 1122, 787, + 2214, 2745, 913, 832, 769, 651, 3543, 3967, 406, 2304, 4031, 961, 3660, 1256, 335, + 2970, 2273, 3860, 3150, 1180, 2498, 974, 966, 3133, 652, 336, 735, 439, 3049, 1273, + 3191, 3631, 875, 1272, 1277, 3960, 1232, 2577, 637, 765, 2972, 737, 3569, 991, + 2892, 3886, 2767, 3809, 834, 1278, 630, 1173, 1090, 1152, 3560, 426, 1189, 1266, + 3572, 834, 956, 953, 1303, 475, 3508, 1293, 3134, 816, 2764, 2537, 1314, 3700, + 1261, 1317, 1176, 460, 978, 1003, 1282, 1323, 3823, 651, 674, 957, 2390, 3299, 857, + 4075, 3598, 1097, 1318, 1281, 3369, 4002, 1048, 1223, 1154, 1201, 591, 589, 1280, + 345, 353, 3883, 381, 348, 3228, 969, 2067, 2092, 2454, 2600, 147, 61, 608, 1134, + 356, 945, 1230, 475, 1075, 793, 2229, 908, 3184, 3554, 334, 1324, 2059, 1373, 1163, + 2796, 1274, 2943, 1377, 3114, 3960, 493, 3433, 3792, 1107, 3051, 2727, 3814, 1139, + 1150, 2592, 1016, 1013, 2566, 2993, 1367, 3031, 919, 664, 1344, 3670, 3018, 1382, + 348, 1080, 3092, 704, 924, 1370, 289, 4084, 1219, 487, 3658, 3809, 2875, 1096, + 1276, 211, 1227, 3640, 1305, 3344, 708, 2950, 1133, 563, 339, 1355, 1429, 704, + 3219, 1405, 1408, 3986, 1047, 189, 1198, 798, 1408, 430, 301, 1292, 1373, 3113, + 2234, 1396, 707, 271, 1218, 375, 1278, 596, 788, 477, 867, 2050, 1135, 1350, 1248, + 3662, 2118, 3123, 2568, 1250, 304, 461, 3974, 1095, 2358, 307, 2605, 1155, 1042, + 1310, 439, 1441, 3956, 3397, 2696, 377, 1159, 2307, 97, 461, 1141, 57, 660, 2627, + 355, 2825, 911, 1271, 3072, 2283, 165, 1445, 1497, 467, 1477, 767, 3004, 380, 3583, + 1008, 3091, 138, 3099, 501, 998, 3858, 1269, 3670, 1436, 898, 1272, 3119, 611, + 2150, 2508, 1294, 32, 1516, 3579, 1332, 1524, 2509, 745, 678, 878, 1513, 2944, + 1302, 1259, 3425, 1509, 1534, 1537, 1151, 3462, 867, 889, 2804, 859, 4019, 1041, + 1008, 1066, 1225, 2697, 1529, 3659, 905, 1025, 22, 2701, 1172, 2802, 3831, 211, + 3590, 288, 993, 288, 2970, 1425, 1295, 3629, 849, 1116, 1403, 1248, 1572, 1521, 55, + 1570, 1234, 1269, 1560, 2831, 3335, 1336, 2891, 191, 73, 807, 619, 1339, 1283, + 2525, 1587, 3490, 1413, 692, 734, 1229, 1541, 3022, 1395, 1482, 836, 2965, 1030, + 4039, 1586, 2197, 661, 1512, 3183, 2061, 2611, 4078, 1572, 2220, 1614, 383, 1488, + 1442, 1618, 3842, 617, 716, 3901, 1623, 2467, 1230, 3782, 197, 2217, 264, 2443, + 1217, 950, 2971, 713, 1054, 3040, 1060, 299, 4040, 1472, 1439, 1387, 3819, 724, + 524, 2224, 1625, 1530, 2646, 1506, 782, 30, 2640, 1385, 1651, 2720, 433, 1194, + 1573, 1423, 1661, 487, 1284, 1598, 1382, 1273, 3468, 1015, 2545, 3891, 1563, 1655, + 836, 1298, 1066, 2554, 336, 3767, 1212, 3388, 3602, 884, 698, 2443, 1350, 194, + 1030, 846, 2706, 1590, 51, 745, 1528, 1466, 1069, 569, 2592, 1349, 1321, 4042, + 1089, 3904, 1492, 3710, 3268, 2899, 504, 1513, 3313, 3066, 3315, 1066, 1441, 1374, + 831, 2758, 776, 1711, 1546, 1657, 1182, 1699, 200, 1665, 4007, 734, 1605, 1725, + 1480, 8, 1544, 3834, 1717, 1697, 2960, 1654, 1420, 987, 89, 1352, 380, 2300, 1589, + 193, 3457, 1536, 1710, 1260, 3183, 1408, 1205, 38, 1726, 465, 711, 1181, 303, 1703, + 3837, 1343, 3576, 2656, 3250, 1001, 3675, 1353, 2677, 156, 4062, 99, 693, 2337, + 271, 3933, 2452, 183, 2611, 3123, 1746, 727, 1146, 554, 2827, 1784, 3763, 3486, + 520, 3369, 676, 758, 3872, 1261, 3440, 1268, 710, 2928, 4016, 1572, 1425, 2108, + 1333, 1632, 2949, 2341, 648, 1777, 3100, 1719, 1491, 654, 1634, 887, 517, 1789, + 1179, 1780, 131, 1788, 1278, 1820, 3597, 3244, 3527, 701, 1460, 3122, 4014, 687, + 3583, 1824, 1445, 1479, 1825, 1609, 1200, 3708, 2330, 3749, 2597, 487, 1421, 1405, + 2865, 1637, 230, 1086, 2748, 899, 1693, 1815, 1840, 4084, 1837, 1804, 2474, 1802, + 1192, 3934, 220, 3029, 1732, 461, 1828, 486, 2251, 922, 1867, 1353, 476, 503, 339, + 1808, 1823, 874, 2751, 1866, 3880, 1792, 37, 3212, 1309, 1479, 146, 3790, 290, + 1882, 2349, 1884, 1870, 1889, 1872, 1105, 1206, 1525, 2595, 687, 1261, 1826, 1740, + 1791, 1851, 3759, 3588, 1389, 1664, 1783, 26, 1626, 2808, 1840, 3685, 746, 1265, + 3869, 2599, 1495, 310, 1307, 1611, 1390, 234, 2423, 1912, 3334, 1361, 4074, 45, + 2171, 1891, 1726, 804, 1506, 3422, 1678, 1720, 1909, 1779, 3583, 718, 1551, 1800, + 1662, 1733, 1571, 1631, 1175, 1492, 1760, 2327, 2943, 1721, 3728, 1597, 1034, 991, + 153, 3318, 238, 807, 1270, 2344, 1846, 3776, 781, 332, 1622, 344, 3123, 246, 2578, + 185, 35, 1953, 1727, 2469, 2365, 757, 1960, 1257, 4041, 2354, 944, 362, 1848, 160, + 2763, 2290, 2289, 1866, 1805, 1615, 1929, 3689, 1658, 3432, 399, 3833, 246, 2218, + 1694, 3069, 1447, 2312, 1997, 1423, 1484, 1795, 2003, 303, 1688, 452, 631, 1006, + 3922, 3495, 3408, 3505, 1469, 3679, 1390, 185, 1848, 1999, 3947, 835, 1703, 1068, + 2140, 2319, 13, 3447, 3743, 3946, 345, 237, 1944, 929, 1616, 3037, 1789, 1760, + 2035, 2712, 3411, 1658, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 1; + let segment_n = 2; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 1513, 2045, 1126, 245, 3943, 2044, 513, 1861, 1947, 1963, 2051, 1644, 1154, 1569, + 1920, 1869, 2013, 1470, 2044, 406, 1797, 2023, 2060, 1863, 1686, 3886, 1888, 1995, + 428, 940, 3907, 2071, 1058, 3602, 349, 863, 1968, 1221, 557, 1582, 944, 1583, 2088, + 1142, 853, 107, 2057, 1991, 3307, 1401, 451, 1164, 889, 1924, 1462, 2081, 3412, + 2103, 506, 803, 1961, 4062, 185, 1296, 1534, 16, 2100, 1768, 1941, 277, 3997, 1009, + 387, 1785, 1396, 1628, 2113, 3422, 2122, 1218, 615, 2127, 1743, 2095, 948, 3469, + 647, 543, 1923, 135, 1474, 1126, 2124, 924, 2140, 2115, 1123, 482, 1441, 1478, + 1546, 4076, 1818, 2020, 1720, 1933, 1841, 2153, 2154, 1811, 1370, 2066, 1362, 2031, + 2152, 2095, 1788, 2163, 2080, 2077, 2119, 1220, 859, 1921, 1940, 1830, 1761, 1347, + 645, 3286, 1196, 976, 1664, 2098, 1600, 1185, 1811, 1718, 1581, 1079, 2101, 1820, + 3408, 1714, 1979, 1774, 3723, 2180, 764, 706, 1467, 2003, 428, 575, 2070, 1566, + 1824, 295, 1333, 2185, 877, 527, 889, 1473, 3166, 2064, 2190, 1681, 1529, 3957, + 2214, 1479, 294, 4051, 1063, 1418, 2112, 3085, 768, 1651, 3359, 205, 1911, 2026, + 622, 325, 1877, 2172, 2195, 3758, 1028, 1937, 3589, 2088, 1583, 2172, 3657, 1075, + 2229, 701, 1655, 2247, 60, 3949, 1936, 1145, 380, 2082, 3785, 3366, 1363, 3509, + 989, 2185, 3289, 355, 2067, 2248, 3467, 2237, 1156, 2264, 2044, 2042, 2016, 362, + 1417, 2035, 2092, 1935, 572, 190, 3761, 1993, 1514, 315, 1406, 2282, 2216, 2192, + 3292, 1234, 442, 2257, 2219, 1875, 3399, 4092, 3126, 3610, 682, 1565, 2077, 2180, + 1416, 1854, 2293, 2288, 2232, 2001, 104, 2262, 887, 1954, 2281, 3947, 244, 1339, + 1474, 571, 2300, 287, 1803, 1868, 492, 1943, 4077, 3227, 511, 2206, 3179, 3779, + 3668, 1438, 1246, 1194, 1953, 1937, 2333, 1797, 2316, 2011, 1234, 3784, 2210, 1814, + 621, 1047, 3893, 2036, 345, 3745, 819, 2326, 2338, 889, 1928, 935, 1957, 3157, 569, + 728, 2219, 3279, 1156, 1934, 324, 635, 2131, 2314, 3740, 2348, 1824, 2008, 645, + 2269, 2354, 3085, 1653, 3497, 585, 701, 3604, 3798, 1801, 3229, 533, 4, 1676, 1781, + 4088, 1897, 1803, 2316, 2150, 2045, 1951, 1958, 2079, 849, 3774, 1300, 2354, 2134, + 1313, 2227, 2101, 2198, 116, 2400, 110, 2143, 764, 468, 2042, 3435, 2374, 2236, + 1323, 956, 1738, 1775, 10, 2064, 1931, 2267, 1383, 2291, 3468, 3500, 3210, 249, + 2428, 1463, 1847, 2426, 1754, 2324, 1683, 1462, 2361, 1508, 1870, 660, 2440, 2363, + 1455, 1002, 2430, 3263, 876, 3896, 3698, 1960, 2417, 178, 1203, 3587, 883, 2090, + 2430, 2252, 285, 3825, 2383, 2395, 2389, 3183, 2199, 381, 262, 2466, 2463, 167, + 1462, 1741, 1183, 1357, 837, 2458, 2267, 3893, 2453, 631, 2427, 1576, 1366, 1468, + 534, 1315, 2317, 3613, 2466, 3995, 1715, 3409, 1110, 3513, 2191, 2158, 1674, 2429, + 2007, 1926, 2421, 2490, 3496, 1740, 2483, 34, 1103, 1845, 1313, 3405, 3538, 2073, + 2512, 2299, 279, 2389, 2303, 1221, 352, 2519, 2454, 3118, 2479, 2519, 1664, 2205, + 524, 2009, 3915, 2485, 2332, 593, 2327, 2363, 3715, 2099, 2507, 1092, 1994, 2263, + 2341, 2370, 1126, 1628, 2535, 252, 773, 3748, 2547, 711, 2025, 1273, 323, 3187, + 1140, 620, 2517, 1221, 3710, 2058, 3398, 2357, 2119, 1424, 3824, 875, 741, 1247, + 3244, 2568, 568, 2277, 2570, 905, 2255, 124, 1658, 1680, 1060, 3235, 3820, 308, + 932, 1301, 343, 2554, 361, 2358, 2559, 2589, 2416, 2591, 2553, 1777, 2470, 1277, + 2589, 1888, 2531, 263, 1292, 1961, 2079, 2597, 1948, 2603, 3547, 193, 1748, 2545, + 2553, 1079, 1147, 3603, 592, 4033, 18, 2467, 1665, 2554, 2582, 2528, 1789, 1964, + 3765, 1414, 470, 2342, 1653, 2572, 1128, 3388, 2542, 723, 903, 2594, 2122, 2376, + 1666, 2566, 2637, 3476, 3818, 2618, 2188, 3996, 2600, 549, 1600, 960, 3146, 2377, + 2650, 1152, 3394, 2569, 1943, 2275, 2627, 3707, 2651, 3554, 972, 2556, 1917, 2636, + 1191, 3930, 1159, 1016, 765, 1944, 1424, 745, 3832, 2664, 102, 3179, 2608, 2469, + 1789, 3197, 3877, 1494, 2681, 2461, 805, 2594, 1643, 2397, 3751, 1717, 2515, 2550, + 2554, 1179, 2696, 2373, 1801, 3136, 2700, 87, 2606, 428, 34, 3817, 2311, 3668, + 2464, 2466, 2083, 1214, 2009, 849, 2714, 2119, 3726, 2703, 3685, 2599, 1364, 2509, + 2601, 2544, 3525, 1573, 1414, 1405, 2418, 2667, 1536, 64, 332, 1373, 930, 2067, + 2006, 304, 3259, 2227, 1821, 2718, 2649, 2721, 2026, 3998, 3552, 2021, 2742, 2236, + 2693, 3594, 2487, 1927, 2480, 563, 3090, 1812, 2548, 2061, 2758, 2490, 3, 455, + 1663, 3641, 1832, 2737, 945, 853, 2633, 1405, 1700, 2771, 2648, 2721, 2642, 3919, + 3837, 2488, 2669, 2393, 1373, 3511, 1129, 2602, 1648, 2595, 3852, 2679, 2281, 202, + 1579, 1665, 2643, 3787, 2739, 2746, 1737, 1736, 1922, 1602, 2303, 2151, 2722, 871, + 2767, 1178, 648, 1010, 2300, 2742, 2540, 779, 2703, 2805, 2589, 3297, 729, 731, + 1806, 2406, 3255, 2607, 614, 650, 844, 479, 3314, 3703, 3179, 609, 2776, 3164, + 1720, 634, 519, 2475, 1053, 1684, 589, 2564, 3687, 2836, 1908, 2330, 1396, 2840, + 2640, 2757, 1896, 19, 3919, 2825, 864, 48, 1554, 2345, 2474, 1751, 3898, 2391, + 2354, 775, 351, 2863, 2866, 682, 2160, 144, 2824, 2871, 2865, 1096, 1667, 469, + 2869, 306, 2642, 125, 2556, 2873, 2829, 2236, 683, 579, 1650, 2797, 2038, 2144, + 3779, 1848, 2398, 2719, 1223, 2751, 2848, 2529, 3195, 1749, 2407, 604, 2898, 1417, + 2804, 1883, 2735, 2774, 2781, 2869, 2632, 2595, 2912, 3259, 3475, 895, 531, 290, + 2918, 2710, 2655, 1254, 2407, 1924, 2746, 2560, 2903, 2729, 2837, 523, 1711, 2919, + 2887, 2627, 2867, 3430, 2089, 1121, 2391, 2408, 2850, 2349, 2863, 1617, 1443, 2931, + 4093, 1911, 3908, 2746, 1924, 1760, 3498, 1425, 3294, 1569, 1981, 792, 2313, 2954, + 2904, 396, 2850, 1309, 2060, 542, 703, 2184, 2259, 2629, 1993, 1000, 1350, 802, + 2896, 2746, 2778, 2729, 285, 155, 2676, 2685, 2960, 2941, 1617, 2585, 2833, 2913, + 2594, 1245, 3191, 674, 62, 355, 2382, 2194, 2441, 2077, 2907, 2418, 283, 2897, + 1010, 2228, 2991, 2811, 2852, 2468, 2998, 2942, 2627, 2164, 2111, 1737, 1688, 243, + 1557, 2043, 446, 3135, 381, 522, 2915, 2963, 2838, 2344, 2480, 3992, 1206, 3020, + 2327, 1195, 3303, 2923, 206, 3141, 2669, 2737, 1129, 2236, 2928, 2756, 1524, 3040, + 452, 1215, 2897, 2571, 2526, 2908, 2195, 2753, 3526, 2665, 2252, 2706, 1081, 3026, + 2313, 430, 3057, 2824, 2721, 1792, 1944, 3026, 3907, 2236, 978, 2126, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 1; + let segment_n = 3; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 1181, 2089, 2541, 1308, 2531, 2731, 2917, 2994, 551, 2431, 3080, 1148, 2328, 3025, + 2798, 2494, 2871, 1571, 1922, 2617, 910, 1553, 1431, 1393, 262, 2679, 3042, 2806, + 1705, 883, 137, 1122, 3070, 2945, 2562, 3096, 2991, 2326, 786, 3079, 2432, 562, + 2555, 2598, 1499, 1226, 3065, 841, 319, 3048, 371, 3094, 3100, 2390, 370, 2207, + 2965, 1263, 2574, 1478, 2992, 1602, 2749, 2480, 3108, 3135, 190, 1490, 1794, 502, + 2878, 3054, 469, 2726, 3111, 3135, 2708, 2488, 2699, 2598, 2097, 2631, 2615, 1710, + 1203, 148, 2686, 1136, 2669, 1542, 1789, 2185, 788, 1608, 2759, 3035, 2721, 1592, + 2730, 2750, 2154, 525, 3020, 2224, 3028, 1104, 3095, 1903, 438, 3179, 2980, 1683, + 820, 2727, 3166, 1192, 811, 703, 796, 3178, 334, 3191, 3138, 3117, 2580, 2410, + 1334, 2033, 1872, 1028, 435, 2089, 96, 3135, 2081, 377, 3126, 3204, 1227, 1744, + 3134, 2021, 2370, 470, 1334, 2745, 1739, 1230, 136, 60, 3099, 2613, 3172, 352, + 1599, 2530, 2382, 3198, 1797, 2601, 3083, 2540, 1784, 3233, 1357, 1989, 3234, 3044, + 2849, 2980, 3238, 2663, 3225, 3060, 610, 2598, 2001, 2497, 2161, 771, 1965, 2229, + 681, 3135, 2302, 3245, 2783, 2965, 449, 3125, 3199, 1739, 2975, 2936, 848, 3263, + 3256, 1013, 2491, 2039, 3238, 3220, 3117, 2029, 3265, 3192, 2672, 3168, 2584, 2329, + 626, 2430, 1362, 3281, 1974, 3048, 1302, 1880, 2898, 2833, 1854, 3277, 2615, 1466, + 1451, 1874, 2165, 3032, 2678, 3186, 2011, 3111, 1031, 571, 1823, 1165, 1568, 2635, + 3079, 1125, 2177, 3219, 426, 2283, 3296, 2767, 1524, 3267, 2556, 2508, 1996, 1072, + 1692, 3231, 3266, 3117, 2604, 3327, 2315, 3024, 1584, 1357, 3312, 1939, 2889, 1216, + 1509, 3152, 1231, 1371, 1710, 1549, 3253, 3268, 2725, 578, 3263, 3132, 50, 2647, + 1680, 3265, 2508, 2445, 3325, 2833, 1517, 3192, 1894, 3305, 1371, 1918, 2651, 1630, + 70, 934, 1676, 2348, 2400, 3218, 3278, 2631, 550, 94, 3014, 924, 3376, 1301, 2836, + 1051, 1477, 2888, 2105, 2346, 1844, 2052, 2859, 1735, 1429, 2436, 3047, 2856, 806, + 670, 3225, 624, 174, 1525, 3384, 3285, 2973, 2959, 3072, 2783, 3402, 3269, 1589, + 42, 2713, 856, 1843, 2265, 3379, 3079, 3004, 3405, 2636, 1782, 3416, 937, 3009, + 895, 2832, 1730, 3264, 3364, 3049, 3420, 1286, 2841, 3110, 2738, 3227, 3260, 3431, + 713, 3360, 2348, 1585, 3428, 3202, 1035, 3186, 2066, 145, 2814, 2684, 2836, 625, + 3420, 3371, 3418, 2395, 2766, 3454, 1403, 2782, 2102, 3430, 3339, 3436, 373, 54, + 2941, 3381, 916, 3453, 283, 3466, 2354, 773, 1781, 3452, 3333, 180, 368, 2057, + 1840, 3123, 3410, 3468, 2963, 3122, 3470, 1137, 1103, 391, 924, 2782, 1611, 2943, + 3489, 1682, 2538, 963, 2769, 1713, 2728, 2258, 3411, 2651, 317, 2177, 2610, 3005, + 3470, 3475, 3419, 3040, 2506, 3378, 1722, 623, 2415, 303, 165, 3516, 946, 1442, + 527, 3319, 3472, 2312, 3506, 1820, 3095, 3522, 3516, 2522, 2477, 577, 1423, 1026, + 758, 3299, 3154, 2818, 2883, 2111, 3209, 2107, 637, 2202, 3542, 1875, 1116, 3416, + 1776, 2639, 3452, 2668, 1398, 913, 2671, 3455, 1941, 3553, 386, 3521, 2064, 1071, + 1107, 3539, 236, 3253, 3511, 42, 3156, 784, 2774, 2412, 3014, 585, 600, 325, 1593, + 3561, 2343, 3430, 2507, 3509, 2925, 3438, 2295, 3361, 3583, 2748, 1078, 3580, 1964, + 2685, 206, 2447, 1745, 1724, 3257, 1607, 3506, 534, 1324, 1215, 2820, 2281, 1874, + 1541, 3600, 2775, 2684, 2275, 463, 975, 2529, 496, 792, 3295, 3237, 2407, 2908, + 1090, 1246, 821, 955, 1722, 1455, 3618, 3624, 680, 2559, 2107, 2102, 2024, 2809, + 2216, 3458, 3620, 3229, 3427, 3446, 3537, 2424, 2833, 3438, 3629, 1625, 1486, 58, + 3577, 3444, 1249, 2847, 3103, 1864, 2747, 1459, 2584, 3613, 1045, 157, 3606, 569, + 3558, 3602, 2711, 595, 1869, 3663, 3598, 3592, 3666, 3550, 2688, 1483, 3088, 3325, + 400, 2790, 1412, 850, 726, 3072, 277, 21, 1876, 3520, 2697, 2752, 2014, 3639, 1604, + 3506, 3643, 3691, 1467, 3264, 1647, 3625, 1591, 3195, 1432, 1967, 3700, 3563, 924, + 3342, 3566, 1130, 3502, 3689, 2209, 3524, 3308, 3488, 259, 72, 2418, 2630, 3478, + 3707, 1736, 1926, 305, 2759, 1670, 2385, 3250, 2974, 3094, 2653, 1620, 1873, 1678, + 2093, 3164, 2832, 2034, 1924, 3593, 1840, 558, 2318, 2625, 1398, 3095, 1939, 3741, + 1890, 2959, 3508, 3394, 2992, 3651, 2954, 3728, 3492, 1744, 3476, 3570, 1030, 2148, + 3351, 2202, 2292, 12, 129, 3629, 3613, 1707, 3754, 3098, 3769, 3672, 3763, 781, + 1649, 416, 1027, 706, 2595, 3773, 1971, 1696, 3278, 3490, 3783, 3766, 3698, 3179, + 1650, 2181, 2424, 260, 3755, 2190, 1406, 3177, 3105, 2323, 3124, 2046, 3209, 3571, + 3702, 1657, 3011, 1578, 3252, 3456, 3194, 1251, 1973, 636, 1139, 1412, 3483, 336, + 3623, 3764, 1665, 3814, 3610, 631, 186, 2617, 2523, 2590, 3587, 3724, 2904, 3802, + 3627, 2680, 3562, 3403, 138, 1720, 1655, 3342, 1439, 989, 3376, 3836, 3839, 3544, + 2461, 3800, 3711, 3456, 1270, 966, 3726, 3659, 3773, 3852, 3515, 3755, 293, 2235, + 1752, 458, 3666, 2797, 3005, 2463, 3093, 3161, 2767, 3477, 3162, 3406, 3485, 2151, + 2023, 3032, 1855, 3464, 2437, 2708, 3813, 1847, 3339, 1085, 36, 3159, 1401, 736, + 3872, 1044, 3150, 3745, 3603, 1918, 2150, 3888, 2250, 2914, 1597, 33, 3725, 3676, + 3837, 3758, 3834, 513, 3262, 1524, 3884, 3289, 3876, 3877, 18, 56, 3293, 1101, + 1249, 3090, 3837, 1468, 3652, 120, 3537, 2991, 1170, 883, 538, 3885, 2368, 2954, + 810, 3369, 3221, 1751, 3914, 3049, 1760, 3919, 3190, 3868, 3935, 3767, 2025, 3902, + 3131, 3857, 3902, 567, 690, 2499, 637, 1555, 230, 3335, 1355, 3937, 1240, 2733, + 1503, 1042, 1877, 2097, 3959, 1686, 3863, 3838, 854, 1131, 371, 3635, 1791, 709, + 1472, 2959, 2226, 482, 1152, 1024, 3169, 3677, 3788, 1907, 3059, 1606, 3809, 2782, + 3983, 2563, 3578, 222, 3363, 1830, 861, 1397, 3990, 720, 2864, 3973, 3848, 620, + 884, 3872, 3999, 2968, 3515, 3894, 3887, 3883, 2778, 1957, 2448, 2833, 3832, 3884, + 3951, 1638, 176, 3370, 3871, 3812, 516, 2483, 133, 384, 3978, 1783, 2078, 3731, + 3199, 4026, 3973, 2744, 4018, 2598, 3490, 2800, 3695, 3891, 57, 2391, 3983, 812, + 3863, 4031, 2483, 1602, 3667, 3503, 1166, 2270, 3603, 3459, 3921, 4049, 2050, 368, + 28, 4008, 1554, 3169, 3978, 3996, 2652, 2447, 4042, 764, 1182, 732, 3878, 4026, + 1383, 3860, 4061, 1363, 3942, 2083, 2428, 934, 3865, 3540, 3307, 2368, 2666, 3483, + 4010, 2728, 1454, 2225, 3648, 3476, 3811, 3592, 3339, 3548, 3973, 3979, 4061, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 2; + let segment_n = 0; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 3058, 3853, 3996, 3939, 1160, 3958, 2783, 2771, 3906, 3745, 2022, 3383, 2446, 4078, + 2898, 4091, 2517, 1562, 1308, 3, 4075, 3762, 3623, 1619, 4055, 4090, 4071, 2811, + 2414, 3947, 3842, 26, 3829, 3742, 2823, 2080, 2772, 2814, 36, 3879, 38, 3755, 3824, + 3921, 3814, 4049, 1744, 2237, 44, 47, 3914, 4071, 3283, 41, 3991, 9, 1207, 2279, + 2572, 1204, 3898, 42, 1276, 4002, 3593, 3311, 1593, 12, 3465, 30, 1965, 27, 3744, + 1184, 1793, 1827, 3963, 1574, 2898, 2945, 75, 2729, 3982, 3178, 46, 3227, 67, 1565, + 3377, 1784, 2444, 2511, 3723, 3486, 3934, 4087, 2188, 3037, 3263, 2394, 2859, 2784, + 3427, 11, 4057, 3699, 2739, 3690, 10, 1193, 85, 3513, 3712, 3198, 3759, 36, 3752, + 4076, 2378, 22, 101, 1829, 48, 3728, 1, 2863, 55, 1750, 4017, 3894, 55, 1958, 2442, + 3950, 4024, 1767, 4082, 74, 3754, 1149, 138, 1982, 4076, 1571, 3017, 143, 3387, + 2051, 2260, 1249, 3789, 1259, 2502, 1948, 100, 3732, 3070, 155, 2362, 4, 157, 2516, + 1208, 2827, 3377, 15, 147, 1296, 2341, 3295, 1810, 167, 3910, 141, 4062, 3032, + 3592, 163, 1808, 141, 3443, 97, 3562, 58, 1525, 1235, 3953, 17, 1783, 3650, 3495, + 147, 1407, 151, 3702, 182, 3493, 3562, 80, 1394, 2525, 4020, 2600, 3701, 140, 2550, + 3581, 1661, 1216, 2051, 2569, 2315, 132, 3036, 3927, 148, 3011, 1180, 98, 211, + 1561, 3798, 3754, 3622, 1663, 179, 1493, 3975, 3365, 3797, 4039, 2775, 222, 3191, + 1207, 1989, 2631, 3464, 191, 155, 1299, 3724, 3415, 3052, 2193, 3950, 1239, 3797, + 3613, 2637, 1454, 149, 3735, 1994, 2608, 3662, 157, 51, 2561, 3755, 3913, 2933, + 4042, 4067, 2901, 3462, 3961, 2567, 2862, 169, 121, 4, 3912, 1349, 4051, 1699, 273, + 1727, 276, 1867, 3118, 2998, 172, 1680, 2118, 25, 1980, 3089, 2164, 3828, 226, 273, + 3623, 4019, 2632, 280, 2009, 141, 2790, 2501, 1636, 4062, 299, 301, 3874, 3785, + 2463, 1664, 3601, 2163, 128, 177, 3014, 1738, 4031, 2055, 3119, 220, 283, 3179, + 3725, 3460, 2479, 4055, 1767, 2109, 3384, 3505, 4016, 3655, 2880, 3629, 3110, 1403, + 1393, 1523, 1533, 3925, 2515, 281, 1705, 221, 3406, 167, 3397, 1082, 326, 248, + 4061, 3268, 348, 1919, 1040, 291, 3994, 1733, 159, 281, 3482, 3840, 3908, 322, 242, + 2896, 323, 2115, 3049, 2547, 159, 355, 1522, 2090, 3999, 3526, 269, 3234, 3864, + 2647, 4055, 353, 1348, 3530, 359, 3262, 162, 3802, 381, 3706, 289, 3107, 3746, + 3662, 3109, 371, 2049, 3413, 3415, 2118, 2636, 163, 2396, 3566, 343, 396, 1447, + 2139, 404, 1552, 2442, 1648, 1522, 259, 218, 385, 3020, 1915, 406, 2360, 1970, + 2470, 3671, 2444, 2730, 3714, 2803, 1732, 3859, 227, 4017, 420, 2443, 200, 1966, + 296, 2940, 416, 243, 138, 418, 99, 406, 148, 3108, 3354, 3598, 368, 2626, 330, + 1780, 2970, 2752, 2354, 2360, 2648, 422, 1870, 3581, 446, 1355, 2820, 1972, 277, + 1396, 3807, 3626, 323, 196, 1903, 2051, 3693, 3041, 343, 2071, 3462, 1052, 2422, + 471, 2634, 1060, 1801, 3204, 3006, 1722, 1618, 1294, 1729, 304, 1881, 3998, 2183, + 2038, 3005, 218, 99, 491, 3470, 1964, 420, 2160, 2000, 2562, 1849, 91, 148, 242, + 3840, 2665, 2523, 253, 37, 1503, 2947, 3106, 507, 1399, 1546, 3281, 1906, 241, + 1254, 166, 330, 3267, 2960, 3322, 3658, 4014, 3336, 449, 3025, 225, 2588, 3133, + 140, 2394, 2280, 30, 196, 2418, 3234, 3362, 371, 2280, 317, 2089, 2096, 3265, 135, + 2328, 1093, 1863, 3852, 3920, 3155, 462, 1941, 3730, 514, 1965, 425, 2262, 3648, + 377, 2598, 2204, 281, 467, 326, 3136, 3698, 2074, 2290, 417, 1445, 3815, 392, 531, + 2503, 3932, 360, 494, 3464, 1906, 3779, 2719, 1366, 255, 577, 2807, 3710, 1962, + 2136, 310, 333, 3721, 2256, 1954, 2880, 43, 467, 1394, 2442, 207, 3683, 372, 596, + 1258, 3204, 2256, 3816, 608, 528, 2415, 3834, 235, 3352, 2616, 3447, 3751, 3372, + 3086, 284, 573, 3016, 533, 1150, 9, 3793, 2824, 554, 1599, 2002, 588, 3541, 513, + 612, 601, 424, 4094, 3084, 34, 3438, 1802, 1325, 641, 2232, 3743, 279, 346, 570, + 1494, 1140, 291, 4041, 646, 3652, 157, 566, 2510, 2160, 3838, 1545, 1048, 566, + 3305, 584, 2678, 634, 179, 1724, 2191, 669, 3655, 595, 3555, 402, 129, 1663, 2849, + 2758, 2725, 565, 3331, 681, 279, 2112, 505, 3634, 1954, 3995, 3253, 3566, 361, 688, + 192, 570, 3814, 2131, 1564, 2718, 308, 2879, 1247, 1933, 648, 2675, 1107, 2115, + 2766, 2083, 1666, 2952, 710, 3295, 1642, 1752, 498, 130, 3500, 2187, 717, 64, 3504, + 2226, 4026, 719, 3656, 603, 176, 3668, 2410, 1248, 2643, 1850, 196, 2817, 728, 612, + 3225, 245, 648, 238, 726, 3525, 2901, 1525, 230, 540, 3479, 735, 3108, 459, 377, + 735, 1396, 3137, 3899, 669, 2199, 3822, 527, 3470, 54, 754, 3000, 2673, 2338, 600, + 3911, 1807, 4079, 3644, 413, 4078, 609, 592, 748, 2437, 1585, 2297, 334, 1828, 33, + 1760, 3046, 760, 2789, 476, 779, 353, 1736, 3326, 625, 1826, 637, 747, 568, 640, + 2909, 512, 4062, 492, 3001, 765, 194, 1369, 637, 196, 806, 3050, 586, 2583, 728, + 2073, 533, 796, 812, 2494, 3823, 729, 55, 516, 3129, 2735, 793, 6, 719, 554, 768, + 690, 3235, 818, 2714, 2818, 4050, 829, 2117, 1983, 3143, 2707, 587, 783, 1684, 460, + 2502, 278, 828, 3609, 568, 1822, 1584, 211, 604, 3429, 624, 212, 3131, 3708, 2853, + 1662, 124, 455, 1054, 433, 1751, 3257, 283, 308, 456, 1745, 1341, 183, 1182, 655, + 687, 4047, 508, 3890, 3948, 1273, 498, 1037, 774, 821, 537, 373, 586, 1924, 638, + 2047, 2263, 1582, 2722, 824, 856, 3894, 739, 477, 409, 567, 3199, 852, 359, 2558, + 445, 2936, 3781, 2128, 617, 558, 1694, 3969, 4013, 3970, 2373, 3606, 426, 3545, + 1838, 1454, 1956, 915, 1227, 3411, 863, 339, 1703, 874, 1506, 2982, 77, 3407, 4015, + 697, 192, 4054, 3737, 3634, 186, 747, 1230, 20, 938, 1206, 801, 3529, 375, 487, + 2595, 925, 2865, 1230, 2263, 948, 3773, 87, 2227, 1037, 2741, 3943, 3986, 265, 905, + 3295, 917, 497, 292, 3292, 1688, 749, 3908, 66, 2665, 469, 1291, 3939, 1166, 436, + 2058, 2046, 930, 3745, 3928, 929, 707, 3462, 3951, 3657, 308, 164, 4046, 3826, + 2108, 2555, 3633, 721, 208, 2778, 3144, 1349, 957, 2632, 256, 992, 3949, 3977, 772, + 1484, 731, 1500, 346, 1490, 353, 3955, 2943, 362, 1964, 468, 81, 549, 59, 377, 351, + 402, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 2; + let segment_n = 1; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 245, 305, 3307, 2804, 3940, 209, 4024, 3645, 611, 3393, 168, 3928, 855, 917, 956, + 2253, 2645, 956, 3289, 927, 259, 2123, 3793, 470, 886, 392, 1024, 4063, 697, 1047, + 2858, 315, 2069, 2236, 2652, 871, 682, 996, 2717, 4003, 888, 2851, 948, 563, 3795, + 749, 846, 3966, 3264, 980, 427, 2546, 527, 108, 243, 666, 2669, 3825, 3882, 374, + 1056, 2290, 1077, 162, 3737, 3960, 3477, 316, 1042, 3933, 3274, 4014, 1079, 1092, + 3586, 4061, 566, 4000, 4061, 2186, 3418, 3459, 463, 3180, 3358, 785, 439, 3488, + 910, 1093, 2716, 3731, 1061, 939, 3079, 1097, 2319, 613, 3867, 448, 1074, 176, 528, + 2936, 516, 3759, 463, 99, 3882, 22, 1031, 549, 1047, 623, 710, 1093, 2886, 584, + 1140, 3834, 3626, 1101, 2137, 2626, 590, 846, 3532, 1149, 1119, 1115, 3105, 1130, + 246, 3008, 17, 2494, 705, 199, 2383, 1105, 18, 2638, 1124, 1075, 3896, 1083, 1102, + 4040, 788, 3048, 1157, 3309, 515, 3434, 798, 83, 3501, 1179, 304, 2806, 888, 1107, + 2551, 754, 3122, 2054, 877, 3501, 1185, 3636, 1192, 3037, 623, 286, 575, 2929, 420, + 1196, 2193, 980, 1105, 2523, 432, 3219, 869, 4049, 240, 1055, 1133, 1192, 739, 863, + 2193, 2298, 925, 2196, 1148, 1030, 732, 870, 1084, 2500, 349, 1225, 1121, 4017, + 4054, 611, 623, 1022, 1195, 562, 3575, 1233, 2288, 2974, 1020, 3573, 461, 589, 417, + 47, 303, 1074, 648, 1231, 1248, 3161, 838, 688, 2296, 3065, 126, 691, 980, 2487, + 1129, 229, 3139, 151, 1136, 427, 47, 2320, 958, 239, 1268, 47, 308, 751, 1150, + 2048, 1263, 3980, 998, 1189, 2903, 3569, 1086, 1016, 3418, 2253, 3556, 4005, 1180, + 1207, 965, 923, 86, 3912, 1241, 443, 3707, 2237, 644, 1065, 3369, 2427, 1132, 44, + 1237, 3181, 1249, 1276, 1097, 1260, 174, 1300, 3652, 1297, 3291, 654, 1289, 960, + 830, 994, 2510, 1318, 499, 3525, 1265, 1141, 3494, 897, 1288, 33, 2643, 10, 2199, + 3067, 3918, 972, 1232, 645, 430, 516, 2337, 2623, 1039, 25, 725, 777, 3300, 1123, + 2342, 4025, 993, 324, 3892, 1338, 1027, 1238, 4002, 968, 1331, 37, 3431, 2581, + 3063, 3096, 1328, 2057, 1009, 563, 769, 362, 3542, 475, 965, 873, 2513, 2631, 1359, + 314, 1285, 1294, 235, 1119, 3155, 2273, 2207, 1018, 22, 590, 2601, 1139, 3405, + 1382, 168, 3976, 2053, 2563, 1034, 2179, 1325, 828, 1396, 1301, 694, 738, 890, 898, + 1389, 408, 3932, 1363, 176, 2630, 445, 3327, 3657, 1412, 3156, 831, 443, 2650, 606, + 78, 2486, 1308, 517, 974, 2541, 443, 1359, 2459, 490, 962, 4073, 950, 827, 1248, + 700, 815, 3514, 1409, 1417, 1381, 2050, 2938, 1350, 332, 3609, 2575, 1441, 4055, + 634, 2524, 4074, 1358, 1444, 1432, 1077, 590, 1424, 2313, 897, 886, 1166, 1364, + 3767, 2997, 898, 1431, 539, 1141, 3348, 2795, 868, 1232, 1269, 909, 1347, 3624, + 3245, 1044, 334, 179, 892, 109, 1470, 773, 765, 291, 2756, 2283, 913, 846, 2810, + 871, 2756, 1234, 1245, 179, 204, 2128, 1160, 3116, 1477, 2512, 390, 767, 1074, + 2523, 262, 1502, 1312, 2838, 2184, 768, 1487, 1405, 991, 1096, 2645, 2984, 3528, + 769, 3452, 2780, 1450, 3164, 1433, 4, 490, 383, 2964, 2970, 3040, 1405, 38, 552, + 1534, 758, 3430, 329, 1497, 1232, 3708, 1128, 1083, 3133, 1467, 1545, 1466, 508, + 2348, 870, 1004, 1476, 1552, 534, 3803, 1405, 3588, 3265, 3260, 674, 1439, 711, + 1137, 970, 1045, 1539, 545, 1550, 1568, 1177, 3757, 125, 1567, 895, 377, 28, 2674, + 2505, 748, 2593, 3465, 1029, 1183, 804, 3142, 1053, 1544, 1116, 494, 2078, 1120, + 1549, 2581, 2397, 3376, 2176, 636, 1093, 2823, 1503, 937, 3053, 571, 1581, 299, + 1333, 1486, 1598, 1606, 3975, 911, 1583, 1587, 3454, 1152, 1558, 970, 1550, 3723, + 2663, 1476, 1128, 1489, 1589, 1433, 3506, 2497, 1552, 1287, 1387, 55, 1434, 4005, + 3971, 3550, 1611, 459, 995, 3143, 1637, 1619, 1390, 513, 1411, 502, 3425, 1597, + 1535, 2383, 109, 1436, 3216, 1346, 1421, 3212, 1601, 616, 1443, 1648, 2301, 1645, + 1328, 1650, 82, 226, 307, 1666, 861, 414, 3262, 1014, 1003, 1473, 4035, 4079, 984, + 374, 3761, 4021, 1150, 2932, 1196, 1455, 1681, 1625, 1132, 405, 1273, 1552, 2947, + 3987, 2071, 3912, 3980, 1694, 1589, 1112, 55, 1493, 1461, 1465, 1557, 934, 1687, + 1044, 1317, 3014, 1466, 3961, 3956, 3299, 214, 2427, 3963, 3145, 676, 491, 3522, + 2841, 1588, 1706, 1694, 35, 1719, 1188, 1707, 1304, 3273, 1564, 1690, 2256, 1471, + 100, 607, 940, 2693, 1334, 1640, 852, 1738, 588, 1733, 3203, 1517, 1677, 2448, + 1742, 3501, 1714, 444, 1673, 1416, 4011, 2284, 1753, 3912, 1231, 2921, 2947, 861, + 3720, 1496, 1263, 1384, 2112, 1281, 1480, 3686, 3955, 3989, 1290, 3489, 1124, 1770, + 2283, 618, 2836, 1413, 1503, 2257, 802, 1026, 2386, 516, 1412, 2787, 502, 1167, + 3816, 1701, 1603, 2125, 316, 3629, 1473, 1426, 1452, 1478, 1747, 3456, 1184, 2563, + 1681, 3136, 1617, 1406, 3562, 866, 153, 1621, 21, 1660, 1765, 1054, 1642, 335, + 1792, 730, 3773, 2429, 1732, 1378, 1746, 947, 1431, 3103, 945, 1698, 1488, 3433, 4, + 1831, 736, 469, 3095, 303, 376, 2759, 2259, 202, 854, 1348, 2437, 2954, 1828, 1206, + 1344, 1837, 3780, 1794, 747, 3928, 1817, 929, 1662, 1772, 1199, 3262, 1297, 1191, + 1773, 2908, 1122, 1860, 1214, 2310, 2918, 1829, 3371, 24, 1868, 1777, 536, 732, + 2223, 3738, 783, 1594, 1555, 703, 1131, 1257, 2780, 1666, 1671, 2690, 1181, 1769, + 23, 1016, 1551, 3351, 1660, 1493, 106, 765, 1598, 1043, 1699, 1889, 1057, 1901, + 2070, 3725, 857, 739, 2359, 49, 1486, 293, 3395, 3384, 2468, 1867, 1631, 3838, + 1432, 1450, 822, 2971, 1303, 1847, 3460, 1621, 772, 1924, 1287, 1926, 1654, 303, + 2395, 3994, 172, 3677, 1920, 1755, 2844, 1651, 1922, 1513, 4058, 854, 3068, 2, 741, + 1739, 1296, 1794, 3229, 1872, 1924, 2254, 730, 1023, 3703, 1349, 1692, 1406, 3815, + 1492, 40, 3040, 334, 1727, 591, 1503, 1733, 2691, 3063, 1204, 2722, 1665, 3895, + 1924, 3565, 3795, 1905, 3843, 3897, 1817, 1888, 1538, 224, 1476, 2081, 1280, 3972, + 2200, 1105, 3469, 3685, 1909, 974, 2516, 382, 1389, 2366, 2750, 136, 2174, 1915, + 1622, 2519, 3999, 3674, 1649, 1706, 757, 3776, 2898, 1614, 158, 450, 15, 2013, + 1852, 1930, 438, 1851, 775, 1746, 1870, 1021, 997, 1742, 1756, 2767, 1982, 1882, + 72, 1961, 2296, 2940, 2570, 2569, 2878, 3652, 3774, 2212, 2599, 30, 2183, 248, 508, + 1543, 2967, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 2; + let segment_n = 2; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 2035, 1527, 238, 1387, 1882, 1932, 772, 2047, 1735, 918, 619, 450, 1107, 783, 1692, + 181, 1995, 1895, 1973, 2045, 630, 1584, 1150, 3855, 2055, 2068, 3155, 1949, 3884, + 2064, 1369, 3749, 1961, 1505, 2058, 3972, 1019, 3196, 1559, 334, 1041, 3306, 2080, + 3189, 1988, 770, 805, 3651, 990, 486, 981, 237, 1270, 4085, 161, 18, 1821, 1330, + 1615, 3327, 2074, 487, 1759, 1580, 241, 4002, 1819, 1152, 2113, 3801, 1057, 1844, + 3249, 214, 719, 3415, 1180, 1917, 1546, 2085, 65, 1568, 2108, 960, 1645, 1794, + 2095, 686, 1295, 1334, 1685, 1041, 1959, 594, 1253, 1184, 1896, 529, 960, 2037, + 1027, 1117, 2085, 4042, 353, 3849, 3936, 69, 1912, 2006, 3887, 502, 655, 2139, 330, + 1314, 3872, 3847, 937, 390, 1126, 2129, 252, 2078, 1960, 2134, 891, 329, 1555, + 3363, 1063, 119, 1836, 978, 1896, 2072, 1517, 1828, 3173, 2159, 1246, 987, 1801, + 1742, 1975, 1655, 539, 1680, 457, 541, 2159, 1433, 447, 484, 8, 3129, 221, 2050, + 2185, 1393, 3686, 1950, 1543, 2121, 1955, 46, 2102, 1364, 2179, 2208, 2207, 2201, + 1802, 2142, 3986, 1943, 1240, 2197, 1812, 1624, 1790, 2023, 3355, 856, 1343, 1692, + 2079, 941, 3515, 2049, 2223, 1295, 1925, 2203, 3630, 696, 1966, 1206, 598, 3214, + 1658, 723, 2223, 1747, 236, 3337, 1868, 1392, 1965, 2110, 1514, 495, 1677, 2082, + 4052, 1710, 2110, 1918, 1324, 209, 3166, 1858, 1639, 1968, 1222, 1703, 1312, 2086, + 3249, 1263, 585, 245, 1354, 1907, 1829, 1338, 2280, 2189, 2246, 2170, 4095, 2103, + 2016, 612, 1554, 408, 1300, 1448, 4004, 2247, 2196, 1611, 248, 2242, 1056, 435, + 1470, 3828, 3731, 2302, 1054, 620, 3198, 1184, 2231, 3501, 1603, 2298, 73, 1048, + 2033, 1083, 1408, 1217, 274, 685, 3146, 2276, 2318, 2267, 1215, 2226, 2318, 3464, + 2328, 83, 2117, 2084, 2224, 3485, 209, 1872, 3575, 1809, 360, 1550, 3677, 3607, + 4004, 2343, 2043, 2341, 1501, 1661, 2218, 3854, 3250, 2098, 2036, 2355, 61, 419, + 2313, 1461, 2009, 2045, 2362, 344, 1799, 1832, 1092, 3573, 984, 3644, 1308, 2253, + 3240, 2332, 1096, 1225, 1769, 2226, 2284, 1260, 2214, 1693, 1240, 1936, 1619, 2240, + 2198, 2371, 3453, 3394, 1961, 2140, 304, 728, 1263, 2390, 1404, 2280, 2397, 3945, + 2101, 2381, 309, 435, 2160, 505, 1047, 1230, 1615, 2326, 2268, 2403, 1512, 3918, + 778, 3112, 1563, 2126, 88, 1048, 2418, 1924, 2419, 3248, 2377, 2307, 2098, 2062, + 1810, 1076, 2404, 2179, 2243, 1305, 2400, 1665, 1967, 2268, 4034, 3705, 2234, 1704, + 1814, 2414, 879, 1628, 902, 835, 337, 1010, 2332, 1992, 1013, 2407, 639, 454, 2333, + 1974, 720, 2448, 894, 1865, 3662, 1562, 611, 318, 1662, 670, 2364, 1748, 2280, + 1884, 1018, 2170, 2428, 881, 422, 2403, 2181, 2003, 3416, 3384, 2156, 3463, 3256, + 951, 1222, 1660, 1499, 1802, 644, 1432, 1886, 1431, 2398, 1953, 1760, 601, 469, + 2429, 1034, 3421, 1881, 1723, 1311, 509, 1924, 147, 2266, 1958, 2473, 3313, 1730, + 2371, 1176, 2475, 1968, 4088, 1736, 2057, 2143, 3518, 1874, 2011, 1144, 912, 2251, + 1926, 3997, 232, 50, 1051, 98, 2315, 2366, 1077, 894, 3202, 1669, 547, 3956, 1429, + 2193, 1156, 330, 1568, 143, 1071, 933, 1409, 2245, 2493, 153, 3256, 3702, 2374, + 2544, 3867, 2362, 1948, 1353, 2404, 1787, 1249, 2101, 555, 3478, 572, 2413, 838, + 2510, 690, 2018, 1479, 748, 2513, 2506, 808, 2557, 1671, 2504, 2581, 2077, 1660, + 826, 636, 2531, 3192, 3694, 18, 1865, 2295, 2523, 1469, 3764, 2454, 2020, 3543, + 2202, 1490, 2596, 2474, 3426, 2381, 474, 2384, 1500, 2137, 2467, 2545, 2496, 1799, + 2462, 2319, 2497, 3108, 1526, 1792, 1522, 2558, 2217, 2621, 3115, 2406, 1886, 2361, + 1047, 117, 331, 2257, 1277, 628, 2460, 2515, 2399, 1183, 2489, 769, 3784, 2633, + 3490, 1349, 1715, 2043, 1635, 2217, 3882, 2617, 2172, 2278, 1986, 2399, 2545, 2114, + 2205, 1769, 2618, 916, 3855, 85, 4010, 2365, 688, 882, 2401, 1883, 2401, 1728, + 2630, 1495, 3974, 1969, 2163, 2672, 2407, 2293, 2398, 3379, 1552, 3484, 2286, 2467, + 127, 614, 1644, 1243, 2570, 1436, 949, 1805, 1497, 1025, 3865, 1522, 3241, 2611, + 2178, 2684, 330, 1889, 3549, 535, 2252, 3890, 2408, 1604, 2337, 450, 4062, 1590, + 2709, 2206, 2313, 270, 1379, 1281, 719, 2622, 2457, 2482, 3595, 1234, 1854, 2718, + 2147, 2719, 2164, 1910, 289, 2516, 3366, 1953, 1347, 1671, 2059, 2650, 2686, 1720, + 2714, 2138, 2636, 1658, 1030, 1365, 634, 1873, 993, 2541, 543, 2707, 590, 2636, + 647, 3604, 3337, 3418, 3921, 2480, 3347, 2422, 1033, 3756, 2354, 2732, 1562, 2687, + 3757, 3553, 1962, 2069, 2696, 1944, 94, 54, 2710, 1133, 2341, 1271, 88, 3516, 734, + 3683, 1328, 3323, 2770, 2785, 1317, 2352, 2738, 1973, 2065, 2160, 2685, 2275, 3378, + 902, 3209, 441, 2228, 1205, 368, 2566, 2136, 2800, 2804, 2551, 2196, 2354, 2559, + 2655, 2659, 1754, 1629, 1334, 2743, 2778, 1044, 766, 3295, 1868, 3241, 116, 2818, + 2670, 3890, 332, 2734, 1835, 406, 683, 2625, 2383, 2251, 3388, 3615, 2659, 680, + 3667, 1525, 567, 1707, 2400, 2095, 2261, 2368, 2584, 2111, 2580, 1070, 1023, 2796, + 2492, 1196, 1106, 615, 1094, 2030, 2219, 2254, 2830, 1822, 2229, 2851, 1889, 2753, + 1157, 2388, 1418, 367, 2816, 1513, 2865, 763, 2161, 2793, 2680, 2634, 2598, 2188, + 3423, 2871, 2839, 2427, 933, 710, 1300, 2825, 2536, 2578, 789, 1387, 2245, 2708, + 2543, 49, 2702, 1166, 862, 2898, 2203, 1409, 3405, 2297, 2572, 375, 2755, 3209, + 489, 2904, 2865, 2545, 3812, 822, 473, 3135, 2915, 342, 3509, 2847, 2793, 1574, + 2456, 2517, 1100, 1330, 892, 2836, 3089, 3665, 1413, 2887, 1303, 218, 3378, 1333, + 1253, 2290, 2727, 132, 2805, 2424, 912, 551, 298, 1178, 2084, 2862, 3739, 2637, + 1538, 2780, 818, 2950, 2909, 367, 759, 2510, 1192, 4048, 152, 964, 2960, 1699, + 2898, 2865, 2283, 2568, 1537, 3974, 3300, 2138, 2083, 890, 2798, 1547, 65, 2815, + 2105, 920, 245, 1841, 2298, 1993, 2117, 2088, 2691, 1495, 940, 2704, 338, 3322, + 3797, 562, 97, 885, 2154, 1804, 209, 2267, 1714, 2252, 977, 3183, 2821, 2269, 2493, + 2440, 1060, 2817, 323, 1022, 1216, 2998, 2843, 3595, 3945, 1765, 2894, 3175, 1471, + 3668, 1426, 2172, 1766, 2821, 1321, 1238, 2172, 2586, 82, 2691, 2744, 1962, 670, + 1797, 1041, 2964, 3210, 4078, 2710, 2481, 710, 1630, 3461, 560, 106, 2097, 2669, + 2057, 2404, 2172, 2158, 3484, 3043, 3983, 3055, 3944, 3050, 3979, 3551, 1834, 2393, + 3171, 349, 1296, 1876, 2988, 1789, 3580, 1396, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 2; + let segment_n = 3; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 1775, 3037, 2997, 1590, 386, 2714, 2140, 2437, 2432, 3079, 3079, 3044, 2964, 1089, + 398, 1962, 147, 2898, 764, 2571, 3066, 2723, 2452, 3078, 1811, 3091, 2278, 2171, + 2975, 2769, 3075, 3059, 2287, 2984, 1204, 1290, 1391, 2408, 1067, 2605, 2863, 1713, + 2479, 2690, 2005, 2619, 3116, 2718, 2142, 2468, 3112, 2400, 2547, 3093, 159, 1185, + 3126, 3080, 1683, 853, 838, 3044, 3101, 2693, 190, 2946, 1083, 1338, 470, 2521, + 2953, 2942, 2715, 3061, 3134, 3144, 832, 2058, 2223, 2002, 2015, 1887, 1236, 1367, + 3094, 3155, 419, 3084, 2718, 1890, 3158, 1458, 2741, 515, 1658, 1262, 1105, 2431, + 3166, 938, 800, 3160, 489, 3005, 3155, 2399, 3110, 1567, 3028, 2841, 3129, 841, + 3110, 3154, 3183, 2132, 37, 3127, 2310, 746, 3111, 1939, 1903, 1636, 3153, 1758, + 929, 2829, 988, 1130, 3148, 2130, 439, 1507, 1142, 1977, 784, 1131, 1494, 2481, + 1606, 1878, 2940, 955, 3087, 957, 1532, 2962, 397, 3204, 3158, 2651, 3204, 3192, + 157, 1903, 1050, 2595, 2738, 3129, 3229, 1987, 547, 2474, 3092, 3125, 3165, 3075, + 945, 2061, 2555, 1040, 2797, 823, 406, 3173, 3246, 3208, 2812, 2411, 1723, 1508, + 3145, 2944, 1977, 3208, 1036, 2971, 3093, 923, 2206, 204, 3257, 1160, 1484, 1694, + 3249, 1132, 1774, 2050, 2430, 1477, 2204, 1408, 2149, 3186, 3217, 3160, 660, 1875, + 1157, 617, 2748, 1973, 1881, 753, 314, 3030, 1987, 2162, 2711, 876, 3021, 2418, + 2394, 1813, 569, 2873, 210, 2765, 3287, 2916, 120, 3123, 3303, 2921, 1881, 751, + 1292, 1914, 3307, 1939, 2144, 1985, 3250, 645, 1324, 1965, 1193, 1043, 1264, 3143, + 310, 3247, 3085, 2670, 140, 1466, 3125, 2527, 3227, 2486, 1920, 3230, 3334, 1211, + 3241, 2593, 1396, 3339, 3116, 3164, 197, 2695, 1489, 1769, 1342, 1941, 2711, 311, + 3127, 2464, 921, 2398, 3218, 2936, 2620, 533, 2460, 1775, 1306, 1965, 3168, 1882, + 3292, 3303, 3219, 3251, 2616, 1435, 2191, 2254, 387, 3254, 2921, 3308, 2759, 851, + 1799, 3367, 3224, 1728, 2347, 1832, 3016, 3350, 2206, 3340, 1146, 3389, 3256, 1231, + 1966, 3122, 2962, 3087, 3375, 2410, 3117, 2759, 3073, 3304, 3402, 3159, 3369, 3403, + 1138, 1542, 2281, 3297, 2358, 724, 3072, 2656, 1737, 1099, 3326, 1334, 3366, 202, + 2835, 1833, 498, 3141, 226, 3077, 3125, 3364, 3287, 3214, 1519, 1703, 1715, 2652, + 1504, 3339, 2566, 3259, 1462, 2979, 3349, 1955, 1343, 3431, 1414, 3143, 3279, 583, + 1337, 2458, 3210, 1467, 3450, 1628, 3030, 2147, 1686, 3391, 1352, 1815, 792, 1189, + 3168, 3041, 3028, 202, 2700, 3414, 2447, 3422, 1267, 3200, 785, 3402, 1081, 3339, + 553, 2950, 2959, 2628, 2252, 2829, 3068, 2489, 3151, 2854, 3271, 1167, 536, 3355, + 3483, 2289, 3167, 2527, 1175, 2643, 3452, 3369, 1212, 163, 3134, 2528, 2769, 3481, + 2495, 138, 28, 3012, 2431, 610, 2772, 1706, 3507, 1733, 2968, 3495, 3309, 1860, + 225, 1301, 814, 1040, 3408, 253, 69, 3364, 2958, 666, 3395, 2936, 499, 3531, 2939, + 811, 3080, 3535, 2026, 3270, 127, 924, 3427, 1110, 3517, 3323, 3510, 1569, 3173, + 3542, 1016, 432, 3153, 2738, 798, 2811, 1199, 146, 234, 1277, 3397, 592, 3035, + 3043, 2841, 860, 2509, 2832, 3517, 3516, 2302, 3325, 1223, 3141, 3394, 1958, 257, + 2196, 958, 2652, 3416, 3265, 3446, 3546, 3335, 2798, 570, 3240, 2617, 1031, 3578, + 1768, 2767, 3587, 3588, 592, 865, 3530, 2813, 1742, 3177, 1400, 1233, 3236, 3368, + 702, 1087, 2580, 2976, 2733, 109, 3378, 3432, 2733, 3578, 3457, 537, 2644, 3587, + 1484, 3610, 3589, 3570, 1212, 2479, 1890, 2806, 490, 3274, 2528, 3628, 2317, 3618, + 3140, 2599, 3514, 3614, 3152, 353, 3186, 335, 3338, 3436, 3640, 174, 2053, 2313, + 149, 3561, 3637, 2245, 2978, 978, 3267, 818, 2328, 2442, 3164, 2826, 958, 3108, + 3639, 1313, 2787, 2502, 785, 3563, 3337, 1928, 3039, 3275, 3175, 654, 3641, 1238, + 3267, 961, 3344, 3610, 2188, 3568, 3645, 3650, 2739, 2453, 3329, 2250, 3366, 1835, + 877, 2135, 2479, 3425, 1106, 3458, 1106, 2769, 711, 2068, 3665, 560, 2255, 237, + 3356, 1259, 2466, 1327, 1673, 3654, 3275, 1711, 2422, 2215, 1551, 2230, 3142, 2559, + 1328, 2091, 2896, 768, 3705, 1066, 3184, 227, 2174, 3315, 3444, 168, 3012, 2873, + 2287, 2585, 3439, 3720, 2461, 815, 850, 3035, 802, 3348, 3699, 2864, 1015, 3178, + 1144, 3621, 2045, 3356, 1793, 1870, 2745, 550, 3654, 1373, 3653, 3687, 3236, 2760, + 1995, 1337, 3703, 3375, 2925, 3570, 3239, 3563, 3068, 3765, 3415, 1394, 2905, 3190, + 1223, 2870, 3197, 3366, 3368, 1648, 194, 2142, 2568, 3390, 2240, 2029, 3639, 3241, + 1822, 3025, 1630, 2413, 3643, 2724, 558, 2752, 2251, 2979, 3070, 2997, 3017, 2105, + 3190, 3559, 3762, 3337, 3087, 3494, 2808, 3126, 1800, 3109, 3804, 1040, 2418, 3233, + 2616, 1783, 3155, 1950, 2406, 3678, 3714, 3237, 3641, 3216, 2601, 1685, 3300, 3681, + 3030, 36, 1241, 1864, 520, 3305, 1622, 2582, 1333, 3176, 775, 3835, 2397, 2493, + 2466, 3471, 1337, 3827, 2298, 3846, 3444, 3818, 1586, 2712, 402, 2765, 2259, 1639, + 1066, 3787, 2836, 758, 3608, 2271, 399, 2943, 1712, 2736, 3656, 1345, 3594, 1382, + 624, 349, 2777, 71, 3740, 501, 1989, 1402, 2028, 449, 1984, 3091, 437, 2927, 3124, + 2515, 3856, 2, 3386, 3570, 1938, 2889, 2161, 3839, 575, 1536, 3080, 1811, 3895, + 2440, 3829, 1049, 887, 3431, 3824, 3878, 3864, 662, 3808, 3489, 3785, 2410, 3899, + 2697, 1389, 2079, 2094, 1599, 3766, 3819, 3919, 3873, 2841, 3921, 3868, 2214, 3371, + 2377, 2872, 1957, 3858, 1804, 3479, 1230, 3383, 456, 3572, 3918, 2468, 1153, 1961, + 3286, 3734, 2458, 2457, 3944, 3668, 3205, 1744, 9, 3899, 923, 890, 2710, 1022, + 3574, 3562, 1232, 1601, 2337, 3406, 3473, 3601, 1580, 3462, 0, 3268, 3290, 2073, + 2763, 1929, 3747, 659, 3692, 3555, 3758, 3605, 3220, 3153, 1570, 3333, 1437, 2664, + 2211, 1621, 3835, 2955, 3565, 3917, 1179, 3880, 1682, 439, 2077, 2153, 3976, 3962, + 62, 370, 2051, 3135, 3724, 2903, 3768, 3535, 2539, 3504, 1636, 3941, 422, 66, 2069, + 3188, 1213, 302, 2374, 3028, 3050, 3948, 4018, 1230, 3408, 3512, 2028, 2495, 4011, + 890, 1845, 3940, 3627, 1185, 1782, 3486, 3807, 1924, 854, 3047, 3497, 3875, 1892, + 687, 4027, 2591, 3639, 4032, 3278, 3094, 502, 3705, 1079, 3161, 4047, 3553, 2777, + 3264, 1325, 3845, 851, 3968, 3631, 3301, 2450, 519, 2243, 3689, 1142, 694, 3529, + 2267, 2057, 4037, 807, 3706, 2217, 2864, 3956, 3116, 1907, 4064, 3217, 3775, 3238, + 688, 1143, 599, 3050, 3856, 3094, 2281, 356, 3916, 3918, 3209, 349, 1317, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + } + } + + mod test_g { + use super::*; + + #[test] + fn g_test() { + let mut w0: u64 = 15555726891008754466; + let mut w1: u64 = 5510367530937399982; + let mut w2: u64 = 11481008432838211339; + let mut w3: u64 = 8667059981748828325; + + let r0: u64 = 12666226408741176632; + let r1: u64 = 839899491230516963; + let r2: u64 = 17298398443694995777; + let r3: u64 = 10383764314571024184; + + g(&mut w0, &mut w1, &mut w2, &mut w3); + + assert_eq!(w0, r0); + assert_eq!(w1, r1); + assert_eq!(w2, r2); + assert_eq!(w3, r3); + } + } + + mod test_p { + use super::*; + + #[test] + fn p_test() { + let mut v0: u64 = 862185360016812330; + let mut v1: u64 = 9264562855185177247; + let mut v2: u64 = 17733520444968542606; + let mut v3: u64 = 13219822890422175473; + let mut v4: u64 = 6801067205434763034; + let mut v5: u64 = 10578543507696639262; + let mut v6: u64 = 10108704228654865903; + let mut v7: u64 = 2299791359568756431; + let mut v8: u64 = 15201093463674093404; + let mut v9: u64 = 13723714563716750079; + let mut v10: u64 = 9719717710557384967; + let mut v11: u64 = 1845563056782807427; + let mut v12: u64 = 1829242492466781631; + let mut v13: u64 = 17659944659119723559; + let mut v14: u64 = 14852831888916040100; + let mut v15: u64 = 12286853237524317048; + + let r0: u64 = 560590257705063197; + let r1: u64 = 9520578903939690713; + let r2: u64 = 3436672759520932446; + let r3: u64 = 14405027955696943046; + let r4: u64 = 17277966793721620420; + let r5: u64 = 3246848157586690114; + let r6: u64 = 13237761561989265024; + let r7: u64 = 9829692378347117758; + let r8: u64 = 1155007077473720963; + let r9: u64 = 10252695060491707233; + let r10: u64 = 10189249967016125740; + let r11: u64 = 14693238843422479195; + let r12: u64 = 13413025648622208818; + let r13: u64 = 16791374424966705294; + let r14: u64 = 11596653054387906253; + let r15: u64 = 12616166200637387407; + + permutation_p( + &mut v0, &mut v1, &mut v2, &mut v3, &mut v4, &mut v5, &mut v6, &mut v7, &mut v8, + &mut v9, &mut v10, &mut v11, &mut v12, &mut v13, &mut v14, &mut v15, + ); + + assert_eq!(v0, r0); + assert_eq!(v1, r1); + assert_eq!(v2, r2); + assert_eq!(v3, r3); + assert_eq!(v4, r4); + assert_eq!(v5, r5); + assert_eq!(v6, r6); + assert_eq!(v7, r7); + assert_eq!(v8, r8); + assert_eq!(v9, r9); + assert_eq!(v10, r10); + assert_eq!(v11, r11); + assert_eq!(v12, r12); + assert_eq!(v13, r13); + assert_eq!(v14, r14); + assert_eq!(v15, r15); + } + } +} diff --git a/vendor/orion/src/hazardous/kdf/hkdf.rs b/vendor/orion/src/hazardous/kdf/hkdf.rs new file mode 100644 index 0000000..371566a --- /dev/null +++ b/vendor/orion/src/hazardous/kdf/hkdf.rs @@ -0,0 +1,522 @@ +// MIT License + +// Copyright (c) 2018-2023 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: +//! - `salt`: Salt value. +//! - `ikm`: Input keying material. +//! - `info`: Optional context and application-specific information. If [`None`] +//! then it's an empty string. +//! - `dst_out`: Destination buffer for the derived key. The length of the +//! derived key is implied by the length of `okm_out`. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than 1. +//! - The length of `dst_out` is greater than 255 * SHA(256/384/512)_OUTSIZE. +//! +//! # Security: +//! - Salts should always be generated using a CSPRNG. +//! [`secure_rand_bytes()`] can be used for this. +//! - The recommended length for a salt is 64 bytes. +//! - Even though a salt value is optional, it is strongly recommended to use one. +//! - HKDF is not suitable for password storage. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::{hazardous::kdf::hkdf, util}; +//! +//! let mut salt = [0u8; 64]; +//! util::secure_rand_bytes(&mut salt)?; +//! let mut okm_out = [0u8; 32]; +//! +//! hkdf::sha512::derive_key(&salt, "IKM".as_bytes(), None, &mut okm_out)?; +//! +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`secure_rand_bytes()`]: crate::util::secure_rand_bytes + +use crate::errors::UnknownCryptoError; +use crate::hazardous::mac::hmac; +use zeroize::Zeroize; + +/// The HKDF extract step. +/// +/// NOTE: Hmac has the output size of the hash function defined, +/// but the array initialization with the size cannot depend on a generic parameter, +/// because we don't have full support for const generics yet. +fn _extract<Hmac, const OUTSIZE: usize>( + salt: &[u8], + ikm: &[u8], +) -> Result<[u8; OUTSIZE], UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + debug_assert_eq!(OUTSIZE, Hmac::HASH_FUNC_OUTSIZE); + let mut dest = [0u8; OUTSIZE]; + + let mut ctx = Hmac::_new(salt)?; + ctx._update(ikm)?; + ctx._finalize(&mut dest)?; + + Ok(dest) +} + +/// The HKDF expand step. +fn _expand<Hmac, const OUTSIZE: usize>( + prk: &[u8], + info: Option<&[u8]>, + dest: &mut [u8], +) -> Result<(), UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + debug_assert_eq!(OUTSIZE, Hmac::HASH_FUNC_OUTSIZE); + debug_assert_eq!(prk.len(), Hmac::HASH_FUNC_OUTSIZE); + if dest.is_empty() || dest.len() > 255 * Hmac::HASH_FUNC_OUTSIZE { + return Err(UnknownCryptoError); + } + + let optional_info = info.unwrap_or(&[0u8; 0]); + let mut ctx = Hmac::_new(prk)?; + + // We require a temporary buffer in case the requested bytes + // to derive are lower than the HMAC functions output size. + let mut tmp = [0u8; OUTSIZE]; + let mut idx: u8 = 1; + for hlen_block in dest.chunks_mut(Hmac::HASH_FUNC_OUTSIZE) { + ctx._update(optional_info)?; + ctx._update(&[idx])?; + debug_assert!(!hlen_block.is_empty() && hlen_block.len() <= Hmac::HASH_FUNC_OUTSIZE); + ctx._finalize(&mut tmp)?; + hlen_block.copy_from_slice(&tmp[..hlen_block.len()]); + + if hlen_block.len() < Hmac::HASH_FUNC_OUTSIZE { + break; + } + match idx.checked_add(1) { + Some(next) => { + idx = next; + ctx._reset(); + ctx._update(hlen_block)?; + } + // If `idx` reaches 255, the maximum (255 * Hmac::HASH_FUNC_OUTSIZE) + // amount of blocks have been processed. + None => break, + }; + } + + tmp.iter_mut().zeroize(); + + Ok(()) +} + +/// Combine `extract` and `expand` to return a derived key. +/// +/// NOTE: See comment about const param at _extract function. +fn _derive_key<Hmac, const OUTSIZE: usize>( + salt: &[u8], + ikm: &[u8], + info: Option<&[u8]>, + dest: &mut [u8], +) -> Result<(), UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + _expand::<Hmac, { OUTSIZE }>(&_extract::<Hmac, { OUTSIZE }>(salt, ikm)?, info, dest) +} + +/// HKDF-HMAC-SHA256 (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the [RFC 5869](https://tools.ietf.org/html/rfc5869). +pub mod sha256 { + use super::*; + use crate::hazardous::hash::sha2::sha256::SHA256_OUTSIZE; + pub use crate::hazardous::mac::hmac::sha256::Tag; + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF extract step. + pub fn extract(salt: &[u8], ikm: &[u8]) -> Result<Tag, UnknownCryptoError> { + Ok(Tag::from(_extract::< + hmac::sha256::HmacSha256, + { SHA256_OUTSIZE }, + >(salt, ikm)?)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF expand step. + pub fn expand( + prk: &Tag, + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _expand::<hmac::sha256::HmacSha256, { SHA256_OUTSIZE }>( + prk.unprotected_as_bytes(), + info, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Combine `extract` and `expand` to return a derived key. + pub fn derive_key( + salt: &[u8], + ikm: &[u8], + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::<hmac::sha256::HmacSha256, { SHA256_OUTSIZE }>(salt, ikm, info, dst_out) + } + + #[cfg(test)] + #[cfg(feature = "safe_api")] + // Mark safe_api because currently it only contains proptests. + mod test_derive_key { + use crate::hazardous::hash::sha2::sha256::SHA256_OUTSIZE; + + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Using derive_key() should always yield the same result + /// as using extract and expand separately. + fn prop_test_derive_key_same_separate( + salt: Vec<u8>, + ikm: Vec<u8>, + info: Vec<u8>, + outsize: usize, + ) -> bool { + let outsize_checked = if outsize == 0 || outsize > 255 * SHA256_OUTSIZE { + 64 + } else { + outsize + }; + + let prk = extract(&salt[..], &ikm[..]).unwrap(); + let mut out = vec![0u8; outsize_checked]; + expand(&prk, Some(&info[..]), &mut out).unwrap(); + + let mut out_one_shot = vec![0u8; outsize_checked]; + derive_key(&salt[..], &ikm[..], Some(&info[..]), &mut out_one_shot).unwrap(); + + out == out_one_shot + } + } +} + +/// HKDF-HMAC-SHA384 (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the [RFC 5869](https://tools.ietf.org/html/rfc5869). +pub mod sha384 { + use super::*; + use crate::hazardous::hash::sha2::sha384::SHA384_OUTSIZE; + pub use crate::hazardous::mac::hmac::sha384::Tag; + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF extract step. + pub fn extract(salt: &[u8], ikm: &[u8]) -> Result<Tag, UnknownCryptoError> { + Ok(Tag::from(_extract::< + hmac::sha384::HmacSha384, + { SHA384_OUTSIZE }, + >(salt, ikm)?)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF expand step. + pub fn expand( + prk: &Tag, + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _expand::<hmac::sha384::HmacSha384, { SHA384_OUTSIZE }>( + prk.unprotected_as_bytes(), + info, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Combine `extract` and `expand` to return a derived key. + pub fn derive_key( + salt: &[u8], + ikm: &[u8], + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::<hmac::sha384::HmacSha384, { SHA384_OUTSIZE }>(salt, ikm, info, dst_out) + } + + #[cfg(test)] + #[cfg(feature = "safe_api")] + // Mark safe_api because currently it only contains proptests. + mod test_derive_key { + use crate::hazardous::hash::sha2::sha384::SHA384_OUTSIZE; + + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Using derive_key() should always yield the same result + /// as using extract and expand separately. + fn prop_test_derive_key_same_separate( + salt: Vec<u8>, + ikm: Vec<u8>, + info: Vec<u8>, + outsize: usize, + ) -> bool { + let outsize_checked = if outsize == 0 || outsize > 255 * SHA384_OUTSIZE { + 64 + } else { + outsize + }; + + let prk = extract(&salt[..], &ikm[..]).unwrap(); + let mut out = vec![0u8; outsize_checked]; + expand(&prk, Some(&info[..]), &mut out).unwrap(); + + let mut out_one_shot = vec![0u8; outsize_checked]; + derive_key(&salt[..], &ikm[..], Some(&info[..]), &mut out_one_shot).unwrap(); + + out == out_one_shot + } + } +} + +/// HKDF-HMAC-SHA512 (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the [RFC 5869](https://tools.ietf.org/html/rfc5869). +pub mod sha512 { + use super::*; + use crate::hazardous::hash::sha2::sha512::SHA512_OUTSIZE; + pub use crate::hazardous::mac::hmac::sha512::Tag; + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF extract step. + pub fn extract(salt: &[u8], ikm: &[u8]) -> Result<Tag, UnknownCryptoError> { + Ok(Tag::from(_extract::< + hmac::sha512::HmacSha512, + { SHA512_OUTSIZE }, + >(salt, ikm)?)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF expand step. + pub fn expand( + prk: &Tag, + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _expand::<hmac::sha512::HmacSha512, { SHA512_OUTSIZE }>( + prk.unprotected_as_bytes(), + info, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Combine `extract` and `expand` to return a derived key. + pub fn derive_key( + salt: &[u8], + ikm: &[u8], + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::<hmac::sha512::HmacSha512, { SHA512_OUTSIZE }>(salt, ikm, info, dst_out) + } + + #[cfg(test)] + #[cfg(feature = "safe_api")] + // Mark safe_api because currently it only contains proptests. + mod test_derive_key { + use crate::hazardous::hash::sha2::sha512::SHA512_OUTSIZE; + + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Using derive_key() should always yield the same result + /// as using extract and expand separately. + fn prop_test_derive_key_same_separate( + salt: Vec<u8>, + ikm: Vec<u8>, + info: Vec<u8>, + outsize: usize, + ) -> bool { + let outsize_checked = if outsize == 0 || outsize > 255 * SHA512_OUTSIZE { + 64 + } else { + outsize + }; + + let prk = extract(&salt[..], &ikm[..]).unwrap(); + let mut out = vec![0u8; outsize_checked]; + expand(&prk, Some(&info[..]), &mut out).unwrap(); + + let mut out_one_shot = vec![0u8; outsize_checked]; + derive_key(&salt[..], &ikm[..], Some(&info[..]), &mut out_one_shot).unwrap(); + + out == out_one_shot + } + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + use crate::hazardous::hash::sha2::{ + sha256::SHA256_OUTSIZE, sha384::SHA384_OUTSIZE, sha512::SHA512_OUTSIZE, + }; + + #[test] + fn hkdf_above_maximum_length_err() { + let mut okm_out = [0u8; 255 * SHA256_OUTSIZE + 1]; + let prk = sha256::extract(b"", b"").unwrap(); + assert!(sha256::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha256::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + + let mut okm_out = [0u8; 255 * SHA384_OUTSIZE + 1]; + let prk = sha384::extract(b"", b"").unwrap(); + assert!(sha384::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha384::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + + let mut okm_out = [0u8; 255 * SHA512_OUTSIZE + 1]; + let prk = sha512::extract(b"", b"").unwrap(); + assert!(sha512::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha512::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + } + + #[test] + fn hkdf_exact_maximum_length_ok() { + let mut okm_out = [0u8; 255 * SHA256_OUTSIZE]; + let prk = sha256::extract(b"", b"").unwrap(); + assert!(sha256::expand(&prk, Some(b""), &mut okm_out).is_ok()); + assert!(sha256::derive_key(b"", b"", Some(b""), &mut okm_out).is_ok()); + + let mut okm_out = [0u8; 255 * SHA384_OUTSIZE]; + let prk = sha384::extract(b"", b"").unwrap(); + assert!(sha384::expand(&prk, Some(b""), &mut okm_out).is_ok()); + assert!(sha384::derive_key(b"", b"", Some(b""), &mut okm_out).is_ok()); + + let mut okm_out = [0u8; 255 * SHA512_OUTSIZE]; + let prk = sha512::extract(b"", b"").unwrap(); + assert!(sha512::expand(&prk, Some(b""), &mut okm_out).is_ok()); + assert!(sha512::derive_key(b"", b"", Some(b""), &mut okm_out).is_ok()); + } + + #[test] + fn hkdf_zero_length_err() { + let mut okm_out = [0u8; 0]; + + let prk = sha256::extract(b"", b"").unwrap(); + assert!(sha256::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha256::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + + let prk = sha384::extract(b"", b"").unwrap(); + assert!(sha384::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha384::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + + let prk = sha512::extract(b"", b"").unwrap(); + assert!(sha512::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha512::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + } + + #[test] + fn hkdf_info_param() { + // Test that using None or empty array as info is the same. + let mut okm_out = [0u8; 32]; + let mut okm_out_verify = [0u8; 32]; + + let prk = sha256::extract(b"", b"").unwrap(); + assert!(sha256::expand(&prk, Some(b""), &mut okm_out).is_ok()); // Use info Some + assert!(sha256::derive_key(b"", b"", None, &mut okm_out_verify).is_ok()); + assert_eq!(okm_out, okm_out_verify); + + let prk = sha384::extract(b"", b"").unwrap(); + assert!(sha384::expand(&prk, Some(b""), &mut okm_out).is_ok()); // Use info Some + assert!(sha384::derive_key(b"", b"", None, &mut okm_out_verify).is_ok()); + assert_eq!(okm_out, okm_out_verify); + + let prk = sha512::extract(b"", b"").unwrap(); + assert!(sha512::expand(&prk, Some(b""), &mut okm_out).is_ok()); // Use info Some + assert!(sha512::derive_key(b"", b"", None, &mut okm_out_verify).is_ok()); + assert_eq!(okm_out, okm_out_verify); + } + + #[test] + fn hkdf_wrong_salt() { + let ikm = b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + let salt = b"000102030405060708090a0b0c"; + let info = b"f0f1f2f3f4f5f6f7f8f9"; + let mut okm_out = [0u8; 42]; + let mut okm_out_verify = [0u8; 42]; + + sha256::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha256::derive_key(b"", ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + + sha384::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha384::derive_key(b"", ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + + sha512::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha512::derive_key(b"", ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + } + + #[test] + fn hkdf_verify_wrong_ikm() { + let ikm = b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + let salt = b"000102030405060708090a0b0c"; + let info = b"f0f1f2f3f4f5f6f7f8f9"; + let mut okm_out = [0u8; 42]; + let mut okm_out_verify = [0u8; 42]; + + sha256::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha256::derive_key(salt, b"", Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + + sha384::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha384::derive_key(salt, b"", Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + + sha512::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha512::derive_key(salt, b"", Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + } + + #[test] + fn verify_diff_length() { + let ikm = b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + let salt = b"000102030405060708090a0b0c"; + let info = b"f0f1f2f3f4f5f6f7f8f9"; + let mut okm_out = [0u8; 42]; + let mut okm_out_verify = [0u8; 43]; + + sha256::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha256::derive_key(salt, ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out[..], okm_out_verify[..]); + + sha384::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha384::derive_key(salt, ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out[..], okm_out_verify[..]); + + sha512::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha512::derive_key(salt, ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out[..], okm_out_verify[..]); + } +} diff --git a/vendor/orion/src/hazardous/kdf/mod.rs b/vendor/orion/src/hazardous/kdf/mod.rs new file mode 100644 index 0000000..2806876 --- /dev/null +++ b/vendor/orion/src/hazardous/kdf/mod.rs @@ -0,0 +1,31 @@ +// MIT License + +// Copyright (c) 2018-2023 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. + +/// HKDF (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the [RFC 5869](https://tools.ietf.org/html/rfc5869). +pub mod hkdf; + +/// PBKDF2(Password-Based Key Derivation Function 2) as specified in the [RFC 8018](https://tools.ietf.org/html/rfc8018). +pub mod pbkdf2; + +#[cfg(any(feature = "safe_api", feature = "alloc"))] +/// Argon2i password hashing function as described in the [P-H-C specification](https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf). +pub mod argon2i; diff --git a/vendor/orion/src/hazardous/kdf/pbkdf2.rs b/vendor/orion/src/hazardous/kdf/pbkdf2.rs new file mode 100644 index 0000000..9ed23b0 --- /dev/null +++ b/vendor/orion/src/hazardous/kdf/pbkdf2.rs @@ -0,0 +1,579 @@ +// MIT License + +// Copyright (c) 2018-2023 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: +//! - `password`: Password. +//! - `salt`: Salt value. +//! - `iterations`: Iteration count. +//! - `dst_out`: Destination buffer for the derived key. The length of the +//! derived key is implied by the length of `dst_out`. +//! - `expected`: The expected derived key. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than 1. +//! - The specified iteration count is less than 1. +//! - The hashed password does not match the expected when verifying. +//! +//! # Panics: +//! A panic will occur if: +//! - The length of `dst_out` is greater than (2^32 - 1) * SHA(256/384/512)_OUTSIZE. +//! +//! # Security: +//! - Use [`Password::generate()`] to randomly generate a password of the same length as +//! the underlying SHA2 hash functions blocksize. +//! - Salts should always be generated using a CSPRNG. +//! [`secure_rand_bytes()`] can be used for this. +//! - The recommended length for a salt is 64 bytes. +//! - The iteration count should be set as high as feasible. Please check [OWASP] for +//! the recommended minimum amount (600000 at the time of writing). +//! - Please note that when verifying, a copy of the computed password hash is placed into +//! `dst_out`. If the derived hash is considered sensitive and you want to provide defense +//! in depth against an attacker reading your application's private memory, then you as +//! the user are responsible for zeroing out this buffer (see the [`zeroize` crate]). +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::{hazardous::kdf::pbkdf2, util}; +//! +//! let mut salt = [0u8; 64]; +//! util::secure_rand_bytes(&mut salt)?; +//! let password = pbkdf2::sha512::Password::from_slice("Secret password".as_bytes())?; +//! let mut dst_out = [0u8; 64]; +//! +//! pbkdf2::sha512::derive_key(&password, &salt, 10000, &mut dst_out)?; +//! +//! let expected_dk = dst_out; +//! +//! assert!(pbkdf2::sha512::verify(&expected_dk, &password, &salt, 10000, &mut dst_out).is_ok()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`Password::generate()`]: pbkdf2::sha512::Password::generate +//! [`secure_rand_bytes()`]: crate::util::secure_rand_bytes +//! [`zeroize` crate]: https://crates.io/crates/zeroize +//! [OWASP]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html + +use crate::{errors::UnknownCryptoError, hazardous::mac::hmac}; + +/// The F function as described in the RFC. +fn _function_f<Hmac>( + salt: &[u8], + iterations: usize, + index: u32, + dk_block: &mut [u8], + block_len: usize, + u_step: &mut [u8], + hmac: &mut Hmac, +) -> Result<(), UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + debug_assert_eq!(u_step.len(), Hmac::HASH_FUNC_OUTSIZE); + hmac._update(salt)?; + hmac._update(&index.to_be_bytes())?; + hmac._finalize(u_step)?; + debug_assert!(block_len <= u_step.len()); + dk_block.copy_from_slice(&u_step[..block_len]); + + if iterations > 1 { + for _ in 1..iterations { + hmac._reset(); + hmac._update(u_step)?; + hmac._finalize(u_step)?; + xor_slices!(u_step, dk_block); + } + } + + Ok(()) +} + +/// +/// +/// NOTE: Hmac has the output size of the hash function defined, +/// but the array initialization with the size cannot depend on a generic parameter, +/// because we don't have full support for const generics yet. +fn _derive_key<Hmac, const OUTSIZE: usize>( + padded_password: &[u8], + salt: &[u8], + iterations: usize, + dest: &mut [u8], +) -> Result<(), UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + debug_assert_eq!(OUTSIZE, Hmac::HASH_FUNC_OUTSIZE); + if dest.is_empty() || iterations < 1 { + return Err(UnknownCryptoError); + } + + let mut u_step = [0u8; OUTSIZE]; + let mut hmac = Hmac::_new(padded_password)?; + for (idx, dk_block) in dest.chunks_mut(Hmac::HASH_FUNC_OUTSIZE).enumerate() { + // If this panics, then the size limit for PBKDF2 is reached. + let block_idx: u32 = 1u32.checked_add(idx as u32).unwrap(); + + _function_f( + salt, + iterations, + block_idx, + dk_block, + dk_block.len(), + &mut u_step, + &mut hmac, + )?; + + hmac._reset(); + } + + Ok(()) +} + +/// +/// +/// NOTE: Hmac has the output size of the hash function defined, +/// but the array initialization with the size cannot depend on a generic parameter, +/// because we don't have full support for const generics yet. +fn _verify<Hmac, const OUTSIZE: usize>( + expected: &[u8], + padded_password: &[u8], + salt: &[u8], + iterations: usize, + dest: &mut [u8], +) -> Result<(), UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + debug_assert_eq!(OUTSIZE, Hmac::HASH_FUNC_OUTSIZE); + _derive_key::<Hmac, { OUTSIZE }>(padded_password, salt, iterations, dest)?; + crate::util::secure_cmp(expected, dest) +} + +/// PBKDF2-HMAC-SHA256 (Password-Based Key Derivation Function 2) as specified in the [RFC 8018](https://tools.ietf.org/html/rfc8018). +pub mod sha256 { + use super::*; + use crate::hazardous::hash::sha2::sha256::{self, Sha256}; + + construct_hmac_key! { + /// A type to represent the `Password` that PBKDF2 hashes. + /// + /// # Note: + /// Because `Password` is used as a `SecretKey` for HMAC during hashing, `Password` already + /// pads the given password to a length of 64, for use in HMAC, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the password with padding. + /// + /// Using `get_length()` will return the length with padding (always 64). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Password, Sha256, sha256::SHA256_OUTSIZE, test_pbkdf2_password, sha256::SHA256_BLOCKSIZE) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Derive a key using PBKDF2-HMAC-SHA256. + pub fn derive_key( + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::<hmac::sha256::HmacSha256, { sha256::SHA256_OUTSIZE }>( + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify PBKDF2-HMAC-SHA256 derived key in constant time. + pub fn verify( + expected: &[u8], + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _verify::<hmac::sha256::HmacSha256, { sha256::SHA256_OUTSIZE }>( + expected, + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } +} + +/// PBKDF2-HMAC-SHA384 (Password-Based Key Derivation Function 2) as specified in the [RFC 8018](https://tools.ietf.org/html/rfc8018). +pub mod sha384 { + use super::*; + use crate::hazardous::hash::sha2::sha384::{self, Sha384}; + + construct_hmac_key! { + /// A type to represent the `Password` that PBKDF2 hashes. + /// + /// # Note: + /// Because `Password` is used as a `SecretKey` for HMAC during hashing, `Password` already + /// pads the given password to a length of 128, for use in HMAC, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the password with padding. + /// + /// Using `get_length()` will return the length with padding (always 128). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Password, Sha384, sha384::SHA384_OUTSIZE, test_pbkdf2_password, sha384::SHA384_BLOCKSIZE) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Derive a key using PBKDF2-HMAC-SHA384. + pub fn derive_key( + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::<hmac::sha384::HmacSha384, { sha384::SHA384_OUTSIZE }>( + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify PBKDF2-HMAC-SHA384 derived key in constant time. + pub fn verify( + expected: &[u8], + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _verify::<hmac::sha384::HmacSha384, { sha384::SHA384_OUTSIZE }>( + expected, + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } +} + +/// PBKDF2-HMAC-SHA512 (Password-Based Key Derivation Function 2) as specified in the [RFC 8018](https://tools.ietf.org/html/rfc8018). +pub mod sha512 { + use super::*; + use crate::hazardous::hash::sha2::sha512::{self, Sha512}; + + construct_hmac_key! { + /// A type to represent the `Password` that PBKDF2 hashes. + /// + /// # Note: + /// Because `Password` is used as a `SecretKey` for HMAC during hashing, `Password` already + /// pads the given password to a length of 128, for use in HMAC, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the password with padding. + /// + /// Using `get_length()` will return the length with padding (always 128). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Password, Sha512, sha512::SHA512_OUTSIZE, test_pbkdf2_password, sha512::SHA512_BLOCKSIZE) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Derive a key using PBKDF2-HMAC-SHA512. + pub fn derive_key( + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::<hmac::sha512::HmacSha512, { sha512::SHA512_OUTSIZE }>( + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify PBKDF2-HMAC-SHA512 derived key in constant time. + pub fn verify( + expected: &[u8], + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _verify::<hmac::sha512::HmacSha512, { sha512::SHA512_OUTSIZE }>( + expected, + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_verify { + use super::*; + + #[test] + fn verify_true() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 128; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; + + sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap(); + assert!(sha256::verify( + &okm_out, + &password_256, + salt, + iterations, + &mut okm_out_verify + ) + .is_ok()); + + sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap(); + assert!(sha384::verify( + &okm_out, + &password_384, + salt, + iterations, + &mut okm_out_verify + ) + .is_ok()); + + sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap(); + assert!(sha512::verify( + &okm_out, + &password_512, + salt, + iterations, + &mut okm_out_verify + ) + .is_ok()); + } + + #[test] + fn verify_false_wrong_salt() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 128; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; + + sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap(); + assert!(sha256::verify( + &okm_out, + &password_256, + b"", + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap(); + assert!(sha384::verify( + &okm_out, + &password_384, + b"", + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap(); + assert!(sha512::verify( + &okm_out, + &password_512, + b"", + iterations, + &mut okm_out_verify + ) + .is_err()); + } + #[test] + fn verify_false_wrong_password() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 128; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; + + sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap(); + assert!(sha256::verify( + &okm_out, + &sha256::Password::from_slice(b"pass").unwrap(), + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap(); + assert!(sha384::verify( + &okm_out, + &sha384::Password::from_slice(b"pass").unwrap(), + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap(); + assert!(sha512::verify( + &okm_out, + &sha512::Password::from_slice(b"pass").unwrap(), + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + } + + #[test] + fn verify_diff_dklen_error() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 128; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 32]; + + sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap(); + assert!(sha256::verify( + &okm_out, + &password_256, + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap(); + assert!(sha384::verify( + &okm_out, + &password_384, + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap(); + assert!(sha512::verify( + &okm_out, + &password_512, + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + } + + #[test] + fn verify_diff_iter_error() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 128; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; + + sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap(); + assert!( + sha256::verify(&okm_out, &password_256, salt, 127, &mut okm_out_verify).is_err() + ); + + sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap(); + assert!( + sha384::verify(&okm_out, &password_384, salt, 127, &mut okm_out_verify).is_err() + ); + + sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap(); + assert!( + sha512::verify(&okm_out, &password_512, salt, 127, &mut okm_out_verify).is_err() + ); + } + } + + mod test_derive_key { + use super::*; + + #[test] + fn zero_iterations_err() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "salt".as_bytes(); + let iterations: usize = 0; + let mut okm_out = [0u8; 15]; + + assert!(sha256::derive_key(&password_256, salt, iterations, &mut okm_out).is_err()); + assert!(sha384::derive_key(&password_384, salt, iterations, &mut okm_out).is_err()); + assert!(sha512::derive_key(&password_512, salt, iterations, &mut okm_out).is_err()); + } + + #[test] + fn zero_dklen_err() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "salt".as_bytes(); + let iterations: usize = 1; + let mut okm_out = [0u8; 0]; + + assert!(sha256::derive_key(&password_256, salt, iterations, &mut okm_out).is_err()); + assert!(sha384::derive_key(&password_384, salt, iterations, &mut okm_out).is_err()); + assert!(sha512::derive_key(&password_512, salt, iterations, &mut okm_out).is_err()); + } + } +} diff --git a/vendor/orion/src/hazardous/kem/mod.rs b/vendor/orion/src/hazardous/kem/mod.rs new file mode 100644 index 0000000..767b653 --- /dev/null +++ b/vendor/orion/src/hazardous/kem/mod.rs @@ -0,0 +1,25 @@ +// MIT License + +// Copyright (c) 2023 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. + +#[cfg(feature = "safe_api")] +/// DHKEM(X25519, HKDF-SHA256) as specified in HPKE [RFC 9180](https://www.rfc-editor.org/rfc/rfc9180.html). +pub mod x25519_hkdf_sha256; diff --git a/vendor/orion/src/hazardous/kem/x25519_hkdf_sha256.rs b/vendor/orion/src/hazardous/kem/x25519_hkdf_sha256.rs new file mode 100644 index 0000000..0ea9609 --- /dev/null +++ b/vendor/orion/src/hazardous/kem/x25519_hkdf_sha256.rs @@ -0,0 +1,289 @@ +// MIT License + +// Copyright (c) 2023 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: +//! - `public_recipient`: The public X25519 key of the recipient. +//! - `public_ephemeral`: The ephemeral X25519 key fro this KEM operation. +//! - `secret_recipient`: The private X25519 of the recipient. +//! - `secret_sender`: The private X25519 of the sender. +//! +//! # Errors: +//! An error will be returned if: +//! - If a shared X25519 secret is all-zero. +//! - If `ikm.len() < 32` when calling [`derive_keypair()`]. +//! +//! # Panics: +//! A panic will occur if: +//! - [`generate()`] panics during [`encap()`], [`auth_encap()`], [`decap()`] or [`auth_decap()`]. +//! +//! # Security: +//! - The `ikm` used as input for [`derive_keypair()`] must never be reused. +//! - This KEM is vulnerable to key-compromise impersonation attacks (KCI), meaning +//! that if the recipients private key `secret_recipient` is leaked at any point, sender authentication +//! no longer holds. See [KCI section](https://www.rfc-editor.org/rfc/rfc9180.html#section-9.1.1) of the RFC +//! on recommendations on how to mitigate this. +//! - Please refer to the RFC for a detailed description of all security properties provided: <https://www.rfc-editor.org/rfc/rfc9180.html#section-9>. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::kem::x25519_hkdf_sha256::DhKem; +//! +//! let (sender_secret, sender_public) = DhKem::generate_keypair()?; +//! let (recipient_secret, recipient_public) = DhKem::generate_keypair()?; +//! +//! let (sender_shared_secret, public_eph) = +//! DhKem::auth_encap(&recipient_public, &sender_secret)?; +//! let recipient_shared_secret = DhKem::auth_decap(&public_eph, &recipient_secret, &sender_public)?; +//! +//! assert_eq!(sender_shared_secret, recipient_shared_secret); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`encap()`]: x25519_hkdf_sha256::DhKem::encap +//! [`decap()`]: x25519_hkdf_sha256::DhKem::decap +//! [`auth_encap()`]: x25519_hkdf_sha256::DhKem::auth_encap +//! [`auth_decap()`]: x25519_hkdf_sha256::DhKem::auth_decap +//! [`derive_keypair()`]: x25519_hkdf_sha256::DhKem::derive_keypair +//! [`generate()`]: crate::hazardous::ecc::x25519::PrivateKey::generate + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +use crate::errors::UnknownCryptoError; +use crate::hazardous::ecc::x25519; +use crate::hazardous::kdf::hkdf::sha256; +use zeroize::Zeroize; + +pub use crate::hazardous::ecc::x25519::PrivateKey; +pub use crate::hazardous::ecc::x25519::PublicKey; + +construct_secret_key! { + /// A type to represent the `SharedSecret` that DH-KEM(X25519, HKDF-SHA256) produces. + /// + /// This type simply holds bytes. Creating an instance from slices or similar, + /// performs no checks whatsoever. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + (SharedSecret, test_shared_key, 32, 32) +} + +/// DHKEM(X25519, HKDF-SHA256) as specified in HPKE [RFC 9180](https://www.rfc-editor.org/rfc/rfc9180.html). +pub struct DhKem {} + +impl DhKem { + /// ID for this DH-KEM. See <https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1> + pub const KEM_ID: u16 = 0x0020; + + /// Version of HPKE implemented. See <https://www.rfc-editor.org/rfc/rfc9180.html#section-4-10>. + pub const HPKE_VERSION_ID: &'static str = "HPKE-v1"; + + /// Length of bytes of a shared secret produced by this KEM. See <https://www.rfc-editor.org/rfc/rfc9180.html#section-4-1>. + const N_SECRET: u16 = 32; + + fn labeled_extract( + salt: &[u8], + label: &[u8; 7], + ikm: &[u8], + ) -> Result<sha256::Tag, UnknownCryptoError> { + let mut labeled_ikm = Vec::<u8>::new(); + labeled_ikm.extend_from_slice(Self::HPKE_VERSION_ID.as_bytes()); + labeled_ikm.extend_from_slice(b"KEM"); + labeled_ikm.extend_from_slice(&Self::KEM_ID.to_be_bytes()); + labeled_ikm.extend_from_slice(label); + labeled_ikm.extend_from_slice(ikm); + + sha256::extract(salt, &labeled_ikm) + } + + fn labeled_expand<const L: u16>( + prk: &sha256::Tag, + label: &[u8], + info: &[u8], + ) -> Result<Vec<u8>, UnknownCryptoError> { + let mut labeled_info = Vec::<u8>::new(); + labeled_info.extend_from_slice(&L.to_be_bytes()); + labeled_info.extend_from_slice(Self::HPKE_VERSION_ID.as_bytes()); + labeled_info.extend_from_slice(b"KEM"); + labeled_info.extend_from_slice(&Self::KEM_ID.to_be_bytes()); + labeled_info.extend_from_slice(label); + labeled_info.extend_from_slice(info); + + let mut out = vec![0u8; L as usize]; + sha256::expand(prk, Some(&labeled_info), &mut out)?; + + Ok(out) + } + + fn extract_and_expand(dh: &[u8], kem_context: &[u8]) -> Result<Vec<u8>, UnknownCryptoError> { + let eae_prk = Self::labeled_extract(b"", b"eae_prk", dh)?; + let shared_secret = + Self::labeled_expand::<{ Self::N_SECRET }>(&eae_prk, b"shared_secret", kem_context)?; + + Ok(shared_secret) + } + + /// Generate random X25519 keypair. + pub fn generate_keypair() -> Result<(PrivateKey, PublicKey), UnknownCryptoError> { + let sk = PrivateKey::generate(); + let pk = PublicKey::try_from(&sk)?; + + Ok((sk, pk)) + } + + /// Deterministically derive a X25519 keyapir from `ikm`. + pub fn derive_keypair(ikm: &[u8]) -> Result<(PrivateKey, PublicKey), UnknownCryptoError> { + if ikm.len() < 32 { + return Err(UnknownCryptoError); + } + + let dkp_prk = Self::labeled_extract(b"", b"dkp_prk", ikm)?; + let sk = PrivateKey::from_slice(&Self::labeled_expand::<32>(&dkp_prk, b"sk", b"")?)?; + let pk = PublicKey::try_from(&sk)?; + + Ok((sk, pk)) + } + + /// Derive ephemeral shared secret and encapsulation thereof, which can be + /// decapsulated by the holder of `public_recipient`. + pub fn encap( + public_recipient: &PublicKey, + ) -> Result<(SharedSecret, PublicKey), UnknownCryptoError> { + let secret_ephemeral = PrivateKey::generate(); + let public_ephemeral = PublicKey::try_from(&secret_ephemeral)?; + + let dh = x25519::key_agreement(&secret_ephemeral, public_recipient)?; + let mut kem_context = [0u8; 32 + 32]; + kem_context[..32].copy_from_slice(&public_ephemeral.to_bytes()); + kem_context[32..64].copy_from_slice(&public_recipient.to_bytes()); + + let shared_secret = Self::extract_and_expand(dh.unprotected_as_bytes(), &kem_context)?; + + Ok((SharedSecret::from_slice(&shared_secret)?, public_ephemeral)) + } + + /// Decapsulate `public_ephemeral` and return the shared ephemeral secrety, + /// using `secret_recipient` private key. + pub fn decap( + public_ephemeral: &PublicKey, + secret_recipient: &PrivateKey, + ) -> Result<SharedSecret, UnknownCryptoError> { + let dh = x25519::key_agreement(secret_recipient, public_ephemeral)?; + + let mut kem_context = [0u8; 32 + 32]; + kem_context[..32].copy_from_slice(&public_ephemeral.to_bytes()); + kem_context[32..64].copy_from_slice(&PublicKey::try_from(secret_recipient)?.to_bytes()); + + let shared_secret = Self::extract_and_expand(dh.unprotected_as_bytes(), &kem_context)?; + + SharedSecret::from_slice(&shared_secret) + } + + /// Equivalent to [`Self::encap()`], additionally ensuring the holder of `secret_sender` was + /// the one to generate the shared secret. + pub fn auth_encap( + public_recipient: &PublicKey, + secret_sender: &PrivateKey, + ) -> Result<(SharedSecret, PublicKey), UnknownCryptoError> { + let secret_ehemeral = PrivateKey::generate(); + let public_ephemeral = PublicKey::try_from(&secret_ehemeral)?; + + let mut dh = [0u8; 64]; + dh[..32].copy_from_slice( + x25519::key_agreement(&secret_ehemeral, public_recipient)?.unprotected_as_bytes(), + ); + dh[32..64].copy_from_slice( + x25519::key_agreement(secret_sender, public_recipient)?.unprotected_as_bytes(), + ); + + let mut kem_context = [0u8; 32 * 3]; + kem_context[..32].copy_from_slice(&public_ephemeral.to_bytes()); + kem_context[32..64].copy_from_slice(&public_recipient.to_bytes()); + kem_context[64..96].copy_from_slice(&PublicKey::try_from(secret_sender)?.to_bytes()); + + let shared_secret = Self::extract_and_expand(&dh, &kem_context)?; + dh.iter_mut().zeroize(); + + Ok((SharedSecret::from_slice(&shared_secret)?, public_ephemeral)) + } + + /// Equivalent to [`Self::decap()`], additionally ensuring the holder of `secret_sender` was + /// the one to generate the shared secret. + pub fn auth_decap( + public_ephemeral: &PublicKey, + secret_recipient: &PrivateKey, + public_sender: &PublicKey, + ) -> Result<SharedSecret, UnknownCryptoError> { + let mut dh = [0u8; 64]; + dh[..32].copy_from_slice( + x25519::key_agreement(secret_recipient, public_ephemeral)?.unprotected_as_bytes(), + ); + dh[32..64].copy_from_slice( + x25519::key_agreement(secret_recipient, public_sender)?.unprotected_as_bytes(), + ); + + let mut kem_context = [0u8; 32 * 3]; + kem_context[..32].copy_from_slice(&public_ephemeral.to_bytes()); + kem_context[32..64].copy_from_slice(&PublicKey::try_from(secret_recipient)?.to_bytes()); + kem_context[64..96].copy_from_slice(&public_sender.to_bytes()); + + let shared_secret = Self::extract_and_expand(&dh, &kem_context)?; + dh.iter_mut().zeroize(); + + SharedSecret::from_slice(&shared_secret) + } +} + +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use crate::hazardous::ecc::x25519::{PrivateKey, PublicKey}; + use crate::hazardous::kem::x25519_hkdf_sha256::*; + + #[test] + fn encap_decap_roundtrip() { + let recipient_secret = PrivateKey::generate(); + let recipient_public = PublicKey::try_from(&recipient_secret).unwrap(); + + let (shared_secret_1, public_eph) = DhKem::encap(&recipient_public).unwrap(); + let shared_secret_2 = DhKem::decap(&public_eph, &recipient_secret).unwrap(); + + assert_eq!(shared_secret_1, shared_secret_2); + } + + #[test] + fn auth_encap_decap_roundtrip() { + let sender_secret = PrivateKey::generate(); + let sender_public = PublicKey::try_from(&sender_secret).unwrap(); + + let recipient_secret = PrivateKey::generate(); + let recipient_public = PublicKey::try_from(&recipient_secret).unwrap(); + + let (shared_secret_1, public_eph) = + DhKem::auth_encap(&recipient_public, &sender_secret).unwrap(); + let shared_secret_2 = + DhKem::auth_decap(&public_eph, &recipient_secret, &sender_public).unwrap(); + + assert_eq!(shared_secret_1, shared_secret_2); + } +} diff --git a/vendor/orion/src/hazardous/mac/blake2b.rs b/vendor/orion/src/hazardous/mac/blake2b.rs new file mode 100644 index 0000000..9b9bfab --- /dev/null +++ b/vendor/orion/src/hazardous/mac/blake2b.rs @@ -0,0 +1,435 @@ +// MIT License + +// Copyright (c) 2018-2023 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 0000000..f5fecf2 --- /dev/null +++ b/vendor/orion/src/hazardous/mac/hmac.rs @@ -0,0 +1,919 @@ +// MIT License + +// Copyright (c) 2018-2023 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 0000000..114062b --- /dev/null +++ b/vendor/orion/src/hazardous/mac/mod.rs @@ -0,0 +1,30 @@ +// MIT License + +// Copyright (c) 2018-2023 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 0000000..b9c974d --- /dev/null +++ b/vendor/orion/src/hazardous/mac/poly1305.rs @@ -0,0 +1,573 @@ +// MIT License + +// Copyright (c) 2018-2023 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_relax, 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: fiat_poly1305_tight_field_element, + r: fiat_poly1305_loose_field_element, + 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.0.zeroize(); + self.r.0.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] = u8::from(!self.is_finalized); + 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]); + let mut a_relaxed = fiat_poly1305_loose_field_element([0u32; 5]); + fiat_poly1305_relax(&mut a_relaxed, &self.a); + fiat_poly1305_carry(&mut buf_h, &a_relaxed); + + // 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.0); + + 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: fiat_poly1305_tight_field_element([0u32; 5]), + r: fiat_poly1305_loose_field_element([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 = fiat_poly1305_tight_field_element([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[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.0, state_2.a.0); + assert_eq!(state_1.r.0, state_2.r.0); + 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(); + } + } +} diff --git a/vendor/orion/src/hazardous/mod.rs b/vendor/orion/src/hazardous/mod.rs new file mode 100644 index 0000000..ff8c188 --- /dev/null +++ b/vendor/orion/src/hazardous/mod.rs @@ -0,0 +1,53 @@ +// MIT License + +// Copyright (c) 2018-2023 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. + +//! ### **Caution**: +//! Usage of the `hazardous` module is __**only intended for advanced users**__. +//! `hazardous` contains implementations with a much higher degree of control. +//! It is also much easier to misuse those implementations. Only use `hazardous` +//! if absolutely necessary. + +/// AEADs (Authenticated Encryption with Associated Data). +pub mod aead; + +/// Cryptographic hash functions. +pub mod hash; + +/// MACs (Message Authentication Code). +pub mod mac; + +/// KDFs (Key Derivation Function) and PBKDFs (Password-Based Key Derivation +/// Function). +pub mod kdf; + +/// Stream ciphers. +pub mod stream; + +/// Elliptic-Curve Cryptography. +pub mod ecc; + +#[cfg(feature = "experimental")] +/// Fully-committing Authenticated Encryption. __WARNING:__ Experimental feature. +pub mod cae; + +/// Key Encapsulation Mechanisms (KEMs). +pub mod kem; diff --git a/vendor/orion/src/hazardous/stream/chacha20.rs b/vendor/orion/src/hazardous/stream/chacha20.rs new file mode 100644 index 0000000..f5c9bcd --- /dev/null +++ b/vendor/orion/src/hazardous/stream/chacha20.rs @@ -0,0 +1,1331 @@ +// MIT License + +// Copyright (c) 2018-2023 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 secret key. +//! - `nonce`: The nonce value. +//! - `initial_counter`: The initial counter value. In most cases, this is `0`. +//! - `ciphertext`: The encrypted data. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the ciphertext/plaintext after +//! encryption/decryption. +//! +//! `nonce`: "Counters and LFSRs are both acceptable ways of generating unique +//! nonces, as is encrypting a counter using a block cipher with a 64-bit block +//! size such as DES. Note that it is not acceptable to use a truncation of a +//! counter encrypted with block ciphers with 128-bit or 256-bit blocks, +//! because such a truncation may repeat after a short time." See [RFC] +//! for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` or `ciphertext`. +//! - `plaintext` or `ciphertext` is empty. +//! - The `initial_counter` is high enough to cause a potential overflow. +//! +//! Even though `dst_out` is allowed to be of greater length than `plaintext`, +//! the `ciphertext` produced by `chacha20`/`xchacha20` will always be of the +//! same length as the `plaintext`. +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1` keystream blocks are processed or more than `2^32-1 * 64` +//! bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Functions herein do not provide any data integrity. If you need +//! data integrity, which is nearly ***always the case***, you should use an +//! AEAD construction instead. See the [`aead`](super::aead) module for this. +//! - Only a nonce for XChaCha20 is big enough to be randomly generated using a CSPRNG. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! +//! # Recommendation: +//! - It is recommended to use [`XChaCha20Poly1305`] when possible. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::stream::chacha20; +//! +//! let secret_key = chacha20::SecretKey::generate(); +//! +//! // WARNING: This nonce is only meant for demonstration and should not +//! // be repeated. Please read the security section. +//! let nonce = chacha20::Nonce::from([0u8; 12]); +//! let message = "Data to protect".as_bytes(); +//! +//! // The length of this message is 15. +//! +//! let mut dst_out_pt = [0u8; 15]; +//! let mut dst_out_ct = [0u8; 15]; +//! +//! chacha20::encrypt(&secret_key, &nonce, 0, message, &mut dst_out_ct)?; +//! +//! chacha20::decrypt(&secret_key, &nonce, 0, &dst_out_ct, &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt, message); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: chacha20::SecretKey::generate() +//! [`XChaCha20Poly1305`]: super::aead::xchacha20poly1305 +//! [RFC]: https://tools.ietf.org/html/rfc8439 +use crate::errors::UnknownCryptoError; +use crate::util::endianness::load_u32_le; +use crate::util::u32x4::U32x4; +use zeroize::{Zeroize, Zeroizing}; + +/// The key size for ChaCha20. +pub const CHACHA_KEYSIZE: usize = 32; +/// The nonce size for IETF ChaCha20. +pub const IETF_CHACHA_NONCESIZE: usize = 12; +/// The blocksize which ChaCha20 operates on. +pub(crate) const CHACHA_BLOCKSIZE: usize = 64; +/// The size of the subkey that HChaCha20 returns. +const HCHACHA_OUTSIZE: usize = 32; +/// The nonce size for HChaCha20. +pub(crate) const HCHACHA_NONCESIZE: usize = 16; + +construct_secret_key! { + /// A type to represent the `SecretKey` that Chacha20, XChaCha20, ChaCha20-Poly1305 and + /// XChaCha20-Poly1305 use. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, test_secret_key, CHACHA_KEYSIZE, CHACHA_KEYSIZE, CHACHA_KEYSIZE) +} + +impl_from_trait!(SecretKey, CHACHA_KEYSIZE); + +construct_public! { + /// A type that represents a `Nonce` that ChaCha20 and ChaCha20-Poly1305 use. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 12 bytes. + (Nonce, test_nonce, IETF_CHACHA_NONCESIZE, IETF_CHACHA_NONCESIZE) +} + +impl_from_trait!(Nonce, IETF_CHACHA_NONCESIZE); + +macro_rules! ROUND { + ($r0:expr, $r1:expr, $r2:expr, $r3:expr) => { + $r0 = $r0.wrapping_add($r1); + $r3 = ($r3 ^ $r0).rotate_left(16); + + $r2 = $r2.wrapping_add($r3); + $r1 = ($r1 ^ $r2).rotate_left(12); + + $r0 = $r0.wrapping_add($r1); + $r3 = ($r3 ^ $r0).rotate_left(8); + + $r2 = $r2.wrapping_add($r3); + $r1 = ($r1 ^ $r2).rotate_left(7); + }; +} + +macro_rules! DOUBLE_ROUND { + ($r0:expr, $r1:expr, $r2:expr, $r3:expr) => { + ROUND!($r0, $r1, $r2, $r3); + + // Shuffle + $r1 = $r1.shl_1(); + $r2 = $r2.shl_2(); + $r3 = $r3.shl_3(); + + ROUND!($r0, $r1, $r2, $r3); + + // Unshuffle + $r1 = $r1.shl_3(); + $r2 = $r2.shl_2(); + $r3 = $r3.shl_1(); + }; +} +pub(crate) struct ChaCha20 { + state: [U32x4; 4], + internal_counter: u32, + is_ietf: bool, +} + +impl Drop for ChaCha20 { + fn drop(&mut self) { + self.state.iter_mut().zeroize(); + } +} + +impl ChaCha20 { + #[allow(clippy::unreadable_literal)] + /// Initialize either a ChaCha or HChaCha state with a `secret_key` and + /// `nonce`. + pub(crate) fn new(sk: &[u8], n: &[u8], is_ietf: bool) -> Result<Self, UnknownCryptoError> { + debug_assert_eq!(sk.len(), CHACHA_KEYSIZE); + if (n.len() != IETF_CHACHA_NONCESIZE) && is_ietf { + return Err(UnknownCryptoError); + } + if (n.len() != HCHACHA_NONCESIZE) && !is_ietf { + return Err(UnknownCryptoError); + } + + // Row 0 with constants. + let r0 = U32x4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574); + + // Row 1 and 2 with secret key. + let r1 = U32x4( + load_u32_le(&sk[0..4]), + load_u32_le(&sk[4..8]), + load_u32_le(&sk[8..12]), + load_u32_le(&sk[12..16]), + ); + + let r2 = U32x4( + load_u32_le(&sk[16..20]), + load_u32_le(&sk[20..24]), + load_u32_le(&sk[24..28]), + load_u32_le(&sk[28..32]), + ); + + // Row 3 with counter and nonce if IETF, + // but only nonce if HChaCha20. + let r3 = if is_ietf { + U32x4( + 0, // Default counter + load_u32_le(&n[0..4]), + load_u32_le(&n[4..8]), + load_u32_le(&n[8..12]), + ) + } else { + U32x4( + load_u32_le(&n[0..4]), + load_u32_le(&n[4..8]), + load_u32_le(&n[8..12]), + load_u32_le(&n[12..16]), + ) + }; + + Ok(Self { + state: [r0, r1, r2, r3], + internal_counter: 0, + is_ietf, + }) + } + + /// Check that we can produce one more keystream block, given the current state. + /// + /// If the internal counter would overflow, we return an error. + pub(crate) fn next_produceable(&self) -> Result<(), UnknownCryptoError> { + if self.internal_counter.checked_add(1).is_none() { + Err(UnknownCryptoError) + } else { + Ok(()) + } + } + + /// Process the next keystream and copy into destination array. + pub(crate) fn keystream_block(&mut self, block_counter: u32, inplace: &mut [u8]) { + debug_assert!(if self.is_ietf { + inplace.len() == CHACHA_BLOCKSIZE + } else { + inplace.len() == HCHACHA_OUTSIZE + }); + + if self.is_ietf { + self.state[3].0 = block_counter; + } + + // If this panics, max amount of keystream blocks + // have been retrieved. + self.internal_counter = self.internal_counter.checked_add(1).unwrap(); + + let mut wr0 = self.state[0]; + let mut wr1 = self.state[1]; + let mut wr2 = self.state[2]; + let mut wr3 = self.state[3]; + + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + + let mut iter = inplace.chunks_exact_mut(16); + + if self.is_ietf { + wr0 = wr0.wrapping_add(self.state[0]); + wr1 = wr1.wrapping_add(self.state[1]); + wr2 = wr2.wrapping_add(self.state[2]); + wr3 = wr3.wrapping_add(self.state[3]); + + wr0.store_into_le(iter.next().unwrap()); + wr1.store_into_le(iter.next().unwrap()); + wr2.store_into_le(iter.next().unwrap()); + wr3.store_into_le(iter.next().unwrap()); + } else { + wr0.store_into_le(iter.next().unwrap()); + wr3.store_into_le(iter.next().unwrap()); + } + } +} + +/// XOR keystream into destination array using a temporary buffer for each keystream block. +pub(crate) fn xor_keystream( + ctx: &mut ChaCha20, + initial_counter: u32, + tmp_block: &mut [u8], + bytes: &mut [u8], +) -> Result<(), UnknownCryptoError> { + debug_assert_eq!(tmp_block.len(), CHACHA_BLOCKSIZE); + if bytes.is_empty() { + return Err(UnknownCryptoError); + } + + for (ctr, out_block) in bytes.chunks_mut(CHACHA_BLOCKSIZE).enumerate() { + match initial_counter.checked_add(ctr as u32) { + Some(counter) => { + ctx.keystream_block(counter, tmp_block); + xor_slices!(tmp_block, out_block); + } + None => return Err(UnknownCryptoError), + } + } + + Ok(()) +} + +/// In-place IETF ChaCha20 encryption as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub(crate) fn encrypt_in_place( + secret_key: &SecretKey, + nonce: &Nonce, + initial_counter: u32, + bytes: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if bytes.is_empty() { + return Err(UnknownCryptoError); + } + + let mut ctx = ChaCha20::new(secret_key.unprotected_as_bytes(), nonce.as_ref(), true)?; + let mut keystream_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + xor_keystream(&mut ctx, initial_counter, keystream_block.as_mut(), bytes) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// IETF ChaCha20 encryption as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub fn encrypt( + secret_key: &SecretKey, + nonce: &Nonce, + initial_counter: u32, + plaintext: &[u8], + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if dst_out.len() < plaintext.len() { + return Err(UnknownCryptoError); + } + if plaintext.is_empty() { + return Err(UnknownCryptoError); + } + + let mut ctx = ChaCha20::new(secret_key.unprotected_as_bytes(), nonce.as_ref(), true)?; + let mut keystream_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + for (ctr, (p_block, c_block)) in plaintext + .chunks(CHACHA_BLOCKSIZE) + .zip(dst_out.chunks_mut(CHACHA_BLOCKSIZE)) + .enumerate() + { + match initial_counter.checked_add(ctr as u32) { + Some(counter) => { + // See https://github.com/orion-rs/orion/issues/308 + ctx.next_produceable()?; + + ctx.keystream_block(counter, keystream_block.as_mut()); + xor_slices!(p_block, keystream_block.as_mut()); + c_block[..p_block.len()] + .copy_from_slice(&keystream_block.as_ref()[..p_block.len()]); + } + None => return Err(UnknownCryptoError), + } + } + + Ok(()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// IETF ChaCha20 decryption as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub fn decrypt( + secret_key: &SecretKey, + nonce: &Nonce, + initial_counter: u32, + ciphertext: &[u8], + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + encrypt(secret_key, nonce, initial_counter, ciphertext, dst_out) +} + +/// HChaCha20 as specified in the [draft-RFC](https://github.com/bikeshedders/xchacha-rfc/blob/master). +pub(super) fn hchacha20( + secret_key: &SecretKey, + nonce: &[u8], +) -> Result<[u8; HCHACHA_OUTSIZE], UnknownCryptoError> { + let mut chacha_state = ChaCha20::new(secret_key.unprotected_as_bytes(), nonce, false)?; + let mut keystream_block = [0u8; HCHACHA_OUTSIZE]; + chacha_state.keystream_block(0, &mut keystream_block); + + Ok(keystream_block) +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[cfg(feature = "safe_api")] + #[test] + // See https://github.com/orion-rs/orion/issues/308 + fn test_plaintext_left_in_dst_out() { + let k = SecretKey::generate(); + let n = Nonce::from_slice(&[0u8; 12]).unwrap(); + let ic: u32 = u32::MAX - 1; + + let text = [b'x'; 128 + 4]; + let mut dst_out = [0u8; 128 + 4]; + + let _err = encrypt(&k, &n, ic, &text, &mut dst_out).unwrap_err(); + + assert_ne!(&[b'x'; 4], &dst_out[dst_out.len() - 4..]); + } + + #[cfg(feature = "safe_api")] + mod test_encrypt_decrypt { + use super::*; + use crate::test_framework::streamcipher_interface::*; + + impl TestingRandom for SecretKey { + fn gen() -> Self { + Self::generate() + } + } + + impl TestingRandom for Nonce { + fn gen() -> Self { + let mut n = [0u8; IETF_CHACHA_NONCESIZE]; + crate::util::secure_rand_bytes(&mut n).unwrap(); + Self::from_slice(&n).unwrap() + } + } + + #[quickcheck] + fn prop_streamcipher_interface(input: Vec<u8>, counter: u32) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::from_slice(&[0u8; IETF_CHACHA_NONCESIZE]).unwrap(); + StreamCipherTestRunner(encrypt, decrypt, secret_key, nonce, counter, &input, None); + test_diff_params_diff_output(&encrypt, &decrypt); + + true + } + } + + // hex crate uses Vec<u8>, so we need std. + mod test_hchacha20 { + use super::*; + + use hex::decode; + + #[test] + fn test_nonce_length() { + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16],).is_ok()); + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 17],).is_err()); + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 15],).is_err()); + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 0],).is_err()); + } + + #[test] + fn test_diff_keys_diff_output() { + let keystream1 = + hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).unwrap(); + + let keystream2 = + hchacha20(&SecretKey::from_slice(&[1u8; 32]).unwrap(), &[0u8; 16]).unwrap(); + + assert_ne!(keystream1, keystream2); + } + + #[test] + fn test_diff_nonce_diff_output() { + let keystream1 = + hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).unwrap(); + + let keystream2 = + hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[1u8; 16]).unwrap(); + + assert_ne!(keystream1, keystream2); + } + + pub fn hchacha_test_runner(key: &str, nonce: &str, output_expected: &str) { + let actual: [u8; 32] = hchacha20( + &SecretKey::from_slice(&decode(key).unwrap()).unwrap(), + &decode(nonce).unwrap(), + ) + .unwrap(); + + assert_eq!(&actual, &decode(output_expected).unwrap()[..]); + } + + // Testing against Monocypher-generated test vectors + // https://github.com/LoupVaillant/Monocypher/tree/master/tests/gen + // Pulled at commit: https://github.com/LoupVaillant/Monocypher/commit/39b164a5bf715d1a62689203b059144df76d98e2 + + #[test] + fn test_case_0() { + let key = "e4e4c4054fe35a75d9c0f679ad8770d8227e68e4c1e68ce67ee88e6be251a207"; + let nonce = "48b3753cff3a6d990163e6b60da1e4e5"; + let expected_output = + "d805447c583fd97a07a2b7ab66be621ad0fa32d63d86ac20588da90b87c1907b"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_1() { + let key = "d6a2df78c16c96a52d4fb01ea4ecf70e81ac001b08d6577bd91ce991c4c45c46"; + let nonce = "bc84d5465fc9139bf17042ae7313181f"; + let expected_output = + "66d1fd5e89a564b55ccf0c339455449c20dfbc9d17081c85fbb430a157777be9"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_2() { + let key = "7afb217bd1eceeac1e133aaa9edb441fa88ea3ae0eaa06cb9911b6d218570f92"; + let nonce = "4a70a7e992b43e0b18578e892e954c40"; + let expected_output = + "41119e28a00a9d3f24b1910495f3058f9db83cbcf12889de84a2fcd7de8dc31b"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_3() { + let key = "a51abdb5a85d300c32f391c45d6ef4db043ddcf4214f24ea6ef6b181071f299a"; + let nonce = "a254a4606ab6a058e0c6fb5598218db7"; + let expected_output = + "04c2f31fdcc7013ac7d10ec82e8d3628c9ab23b08bbf95d6d77ad2dec7e865d6"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_4() { + let key = "1deb473f7d04c152e7e857736715dc7b788aca39a3c96a878019e8999c815c57"; + let nonce = "23dbfbde05e6c71f118afc0dedb5b9f8"; + let expected_output = + "75e9a94daf28b6b8634823325c61cdcb2beeb17a8f7554cc6d5b1b1d2e3592cf"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_5() { + let key = "dea398b2d764bca68dfc023a9821939d389e38a072cf1b413bb1517c3fe83abe"; + let nonce = "bb1cdf3a218abb1b0c01da64c24f59ee"; + let expected_output = + "65a20993e8e69de41d38e94c0796cb7baccd6d80a6e4084e65d0d574fbcb7311"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_6() { + let key = "d19cfb8cb3940aba546f0be57895e2cc869fe55aab069c5abcf9e7ba6444a846"; + let nonce = "e5d73f1c8c5376c1220ff3d9d53eeb65"; + let expected_output = + "a345f5f10ec20b4a744634fbb94e94c9425699b4d57ffeab5403b8fbfb85bae7"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_7() { + let key = "cc53599f40d6c8348c353b00172655236cddcd1879ca1f04b35f91adab70b81f"; + let nonce = "504035fc169964a5ae985e6c11b0b7bb"; + let expected_output = + "11dda56dce88c92641177e2a6e21b11c5ca794912b3bceb9ccb375c87bcc7968"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_8() { + let key = "18a51fd77fbffd722aa220efdd8947ca5a5c7fb1c2ebdb9ad1f603801ff22e80"; + let nonce = "314f716af9c22022fa159dbb4b4d3153"; + let expected_output = + "14759f0e978a9f45a4696739fecb590b4ba6f06536384225333cccba074c8a68"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_9() { + let key = "f999b20ab4769eb1d01c057c5295ed042b4536561dce32478b113adb5b605cac"; + let nonce = "75bcfcacb5e3e811b78e72e398fdd118"; + let expected_output = + "564eb6b2ac2b92270af7c0b054cc7a721313e4ed3651b0970db9dfcdfda27220"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_10() { + let key = "bf04c6a7ed0756a3533e3dca02109e1830b739210bd8bffe6a8a542980bd73e9"; + let nonce = "ca43cdd4eb7173476862df6d2458d6c7"; + let expected_output = + "4f8975d01fb3525a60de55c61190471e86b95cb3e835374d58b003f55eb9819a"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_11() { + let key = "4739a0ad2169b9c89edd74e16fbcecc748c25dc338041fc34af0f1bda20eaf3f"; + let nonce = "ff7b372aa801eb98a1298bc610280737"; + let expected_output = + "06ccde41d10d6466859927bfc9a476dbc84064838ec721261cb548c18bd14c67"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_12() { + let key = "50831c8cb43cd6822bf3f6fae0801cb6c843d8066b07346635365fb7d6ee54e5"; + let nonce = "c9cd6f05d76b2bd4caec8d80b58235cb"; + let expected_output = + "6ed040d7721395fb2c74c8afe252a169ded78e6f2f889e8fb0ec1490533a8154"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_13() { + let key = "4268543ab0eb865a948cc5b5f6e31f05f8146bd9495acc459d6d200005ee72c3"; + let nonce = "bc3e4ae3badfd79adfe46b2ae1045f78"; + let expected_output = + "19b839a6d3424cf2a52d301e70e76cb77368cf9f60945bf43ce4c657aeb1d157"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_14() { + let key = "382e04c969df1a2d6a963a79c58401770a383248b5d70bb4adedcbe520fed634"; + let nonce = "f513b8c2ea6ab37fe633ba7302a5db6c"; + let expected_output = + "fd0739819bae6c98cbde7cb50a80e8d0b359567c50cec1ca7e985745c1cedb3a"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_15() { + let key = "2aa209e24478fa1bd6f6ffabe98555e034342cbec07364c54d1e407e282ef08e"; + let nonce = "dbfdbde936c9d42df58ae15889f5c939"; + let expected_output = + "f5047baa0acf9a603415a09b64268d77712ae902c73490e9c53db593765726db"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_16() { + let key = "a3087eaeac1f2a58e2c2763d01b55744c4a65f4db93adff0078c63f090fb607a"; + let nonce = "90c87defd622e5f55977877cec9ed883"; + let expected_output = + "1d882fa80248882c6bc311a693ebd06b8c09aa2776e6e90df523d12bfeeed77a"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_17() { + let key = "12b0411228540cd6dde6e84cd2da59b1871db119e3298e3c12fe8200a47eddf0"; + let nonce = "49c971cd99f694e3b2a5e25fa37aedf0"; + let expected_output = + "69bb83ccb7bc4deaf60cfe168cb11fad4257222c3523c2d08922564ac0fb74d2"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_18() { + let key = "1bf32e7c679a3187e22a635d301ce98ad000ca301049f2e891e403250c3358fc"; + let nonce = "2030b227bb96e93b88f419afe9f9d660"; + let expected_output = + "d0ed414a875a81db1e4cff7609afdbb2ffcdd575ebc17543fb92de53c6487efb"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_19() { + let key = "e013761228051ec5a8f0c093b33fc60e2cd7a9c845434e95d4319d79d1bdaa8f"; + let nonce = "73853fbd9958e9ffc23a0ecbb7b48dbb"; + let expected_output = + "e3f6c6da6c0300103d665dd877a8b62e23b1361bf3af5bbc2310502131d69be8"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_20() { + let key = "a63672d582bb83d92249800324cbc9a6e5b37d36887e7c79093f58ef8f1a0015"; + let nonce = "85321bfee1714260dd6130cc768d20b1"; + let expected_output = + "97e05360aca70058389d93be38d49fa26df01a4d3b4c4f10c3ec31e0ed64f08e"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_21() { + let key = "4d3850f0eec0f8f349110e751c16cdb5ed05516df17479937d942c90eb1fb181"; + let nonce = "3062bd3f3f6b7668cd8fd3afce0cc752"; + let expected_output = + "77513195542b2ab157cb2e6870c5b1ba143a8423ad276a64152ab923c6f54c06"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_22() { + let key = "9b87dfc58eceb951e1e53d9e94793329199c42d004bc0f0dab3adf0cd702e99e"; + let nonce = "fa5ef6e59d3b201680f8e2d5a4ef7f23"; + let expected_output = + "56a208bd87c5b486b5de50fbe4c1c476532f874147eba529cbb0cbeae8f09b94"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_23() { + let key = "f1b6a8e102670a3829a995ae23fbc3a5639e028cd2b5f71bb90c7a1e4a8a0501"; + let nonce = "7d26e3afc3a88541f6c3f45d71f8a3cc"; + let expected_output = + "a02140057f889e7ab36b4a5066e376dff248d13bd8072c384e23bd8fe4bf7047"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_24() { + let key = "31a063ea4aad1b4d00db6f5228e9b9b1561a7f61812b8b79e6af4292580d02ea"; + let nonce = "4f6266d04244303304510272e383eaa5"; + let expected_output = + "d610d44b8b3c14c7d3782f73405637fd14b7fada717665a9acbd4df6daa89adc"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_25() { + let key = "1a8ea7099a74bafa3375b210653a0d2f40b15afd725cf5065066be1cb803dc15"; + let nonce = "8865ed8d7cca72dcf2b7c6b5d0d045bf"; + let expected_output = + "f10cce296197a056bedbee166183ad6aaa56bdb21c3459296ca54c0bb78317d1"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_26() { + let key = "32b063d3da484ba1843e071b61c49ce7f30ba18a4f7ef2730ecd785494839966"; + let nonce = "f593168e17311913753c59593fc66cb6"; + let expected_output = + "f18115a9568724c25184728f563b65b737219cb0df1b3ce19a8bdcbdf7b8b2be"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_27() { + let key = "64c1572251132fc28bf37fd8e96f2327cf7948a1126fd37175a91f483d6b3ad9"; + let nonce = "2308df7e6daa8bf3efde75f80ad72a49"; + let expected_output = + "06a24cb90abe94cf3ee8e429d8197bc42bc769fbe81119156274f9692aa017a2"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_28() { + let key = "ae0794009e21ad33fa4141fe5fa79fed12f6a20f51614dc130f45598e92549b1"; + let nonce = "13ed6185724507e7fa5a7e8a75b2c7a3"; + let expected_output = + "51d1aec8d64d20e448a377bfa83ccbf71a73a3ad00d062bf6b83c549a7296ef1"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_29() { + let key = "ad700919f36a46ea0ffa680857e30188f8a03c7c4b6c11bc39aececec2668723"; + let nonce = "3682d31887277028e2fd286f2654c681"; + let expected_output = + "a24610a94968df2dc9d197cd0bc55cab08c9dabd444c0efcd2a47fd37016382e"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_30() { + let key = "efd9e7ed6b340874e897337d4dcc672811a6cf4b69086e0a57c266424dc1d10e"; + let nonce = "cbaf0c822cce9e4f17b19e0ece39c180"; + let expected_output = + "6f94a0f8ed7f3fe5ebaa3b8caba016ab64373ffc3c7b1c86e6787f31b4a905ec"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_31() { + let key = "a4c756c03c19900280ff6cdebe5174d507c6e0860c38c3537176c58965b74a56"; + let nonce = "c52b3151bb8a149cf4f82158d57c823f"; + let expected_output = + "50ea3d4f6a45e4a062b2d966e63cac51e093dfb6ab9df6d16bb109bc177b0a38"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_32() { + let key = "3a90c6b427912226ff604d9abee1fb8c8d35530a0cd5808e53e308ac580f7318"; + let nonce = "fe2ab2a4933b5d90db718aa3440fbe9b"; + let expected_output = + "2b57adcc5d26060383c87ef7e055f9aca4addcb2646cbf2cff4edc3f17b72ad5"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_33() { + let key = "a17f09716219bdffc93a189e410a6a3e6477fbb05c7c35956c3c0c5f342355fa"; + let nonce = "0850307998642501c025e3873ebac3cc"; + let expected_output = + "d3a58c49e9fe1ecf2eca169f4d4131cde27279053d562d0429a08ec701aaa39e"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_34() { + let key = "d749d8379ae6d830f785ec104897bd723d34ad20c9d36bfe371df46aebc6d459"; + let nonce = "5d490a770bee4dd0be6a5a0b5e95645c"; + let expected_output = + "c278c0079bd656f1dadf3dec692f19f25339c6557542181716d2a41379740bf2"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_35() { + let key = "7dcbc03c27010df3320fe75b0a3ecc8983ad94217e80348fd0f3f54e54b95bb5"; + let nonce = "48dc2225a264443732b41b861590358d"; + let expected_output = + "b244c408c74f3dcb8bcb72f834a054c554edad0363d761847003dab003ac6848"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_36() { + let key = "543894006b73f3d70fc04b15d0c2a5dfa650be5044fb5061811b866be7f9d623"; + let nonce = "fcb077ee19421610aeb263c57faef006"; + let expected_output = + "fb20ea177cb7225c87122f285d92faf0c2033e2497575f74505255b6d3dfcb96"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_37() { + let key = "62d424c07a7aa5005068b262251c0667a4e2e4b12f5df7f509564517887e370b"; + let nonce = "425fabab1ce9e733ab2911b42074414e"; + let expected_output = + "3a5eb5552cdd267c05c1e4fe936ce8f0eaf7279ff328ed9a42d6d83f7b30416c"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_38() { + let key = "387d7247fa5055489bbd4b7d4de256de723566c1c2d3ecee8c10e7d98233dbef"; + let nonce = "90494951ec91a843f6701f8216a7326b"; + let expected_output = + "8c4bc60a1e05004ec93aef4ae162aeff43d679ea1ba048739c700d6a168bc6cc"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_39() { + let key = "241fd57f32e09976de4054797b9aee820e0de381d02852ac13f511918267b703"; + let nonce = "7330e60ba1c5875a0275f8ccc75cbe98"; + let expected_output = + "9e724c5b0321e2528278a501108f1ae8a14dffaea9b6b138eacef3bd8d4dda41"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_40() { + let key = "7c12457eb5614f87f1fdc40118906d02c602059d48ae05ae62d3d607d6bf63c6"; + let nonce = "760b802483b0e3aaa9dd4f79c6c5e93e"; + let expected_output = + "e5b86f76fbc1f488c44e4d7f304736b752ab6cfb99fcf6910668eeefa4b67c2a"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_41() { + let key = "6b51da45018c6bde108f81f9abfa23640b83cfe3fed34bcf6640bf0baf647daf"; + let nonce = "e9bc99acee972b5a152efa3e69e50f34"; + let expected_output = + "1032b5d539b1c8cd6e0be96db443a08fc759bea8988384435c03b5f00b6e485f"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_42() { + let key = "3bc12887fec8e70db73b4b48dce564d83786aca4c6b7e224163ea928771fde37"; + let nonce = "78c453b35d98deced812fc5685843565"; + let expected_output = + "2279b063dab4c73a96abe02175e694662c65d09eb5889234293c7a1f2911e13d"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_43() { + let key = "b73d097601d3558278bd9d7327de5fdaa2b842050b370e837ef811a496169d5f"; + let nonce = "f768878766c08c45561fdc2aad6469c1"; + let expected_output = + "a8e85a6ab627f08ad415649a9cf9998f4b1065030f3c844e31c8185036af7558"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_44() { + let key = "1380c3d3f873c7233c541ea4c43824ecd8bf7e11ac8486208fb685218d46736e"; + let nonce = "51103d1fae0e8e368f25480ee7328381"; + let expected_output = + "9b84e50804449b594a54240741e21d75d31050d2612f4cbc651fea2f25bd9c1f"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_45() { + let key = "c2f8b252a18a29c44dbfbb62cbe6c3dfd4db55378734d8110b8f20f1d1ada6dd"; + let nonce = "d4da48fb09c06580eb46bbc5ca62bfab"; + let expected_output = + "315c3fe1009e438762a72f27e7a68b8ccb2c0b60bf79cb6e48123db0c42d4aeb"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_46() { + let key = "40b184271b73b710d40cb63435042c9b526d1e5c3a77bfc516a2bcb4cc27ecae"; + let nonce = "b3451318590c84e311dd1e876f527d81"; + let expected_output = + "cbbde3a3412504c1f684aa273ee691159edc9f44e306360278d63d4ee2f1faa4"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_47() { + let key = "ec81df06c7e426b729aebb02be30c846eb228490df4a0e6c688aaaa6bf05d144"; + let nonce = "28335f2652926bfdfe32dfd789173ba8"; + let expected_output = + "522b522e4cf9aa1e80126a446ed7b9665af3e781a3d5afdce43a5fe0cdbd4351"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_48() { + let key = "60fa0114802ee333d7c49ccaad8108db470c882514716592e57aba26bb75049b"; + let nonce = "75db088bd1a89c6a67fb76b96c987478"; + let expected_output = + "e004cc12dfdb74268e59958385e2a1c6ff31e31664838971629f5bbf88f4ed51"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_49() { + let key = "bfba2449a607f3cca1c911d3b7d9cb972bcd84b0246189c7820032e031949f1e"; + let nonce = "97e8ad5eb5a75cc805900850969de48e"; + let expected_output = + "19faebfbb954552fcfbf9b91f271c9397a15c641733c394a9cb731c286c68645"; + hchacha_test_runner(key, nonce, expected_output); + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_init_state { + use super::*; + + #[test] + fn test_nonce_length() { + assert!(ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; 15], true).is_err()); + assert!(ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; 10], true).is_err()); + assert!( + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE], true).is_ok() + ); + + assert!(ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; 15], false).is_err()); + assert!(ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; 17], false).is_err()); + assert!( + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; HCHACHA_NONCESIZE], false).is_ok() + ); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_test_nonce_length_ietf(nonce: Vec<u8>) -> bool { + if nonce.len() == IETF_CHACHA_NONCESIZE { + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &nonce[..], true).is_ok() + } else { + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &nonce[..], true).is_err() + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + // Always fail to initialize state while the nonce is not + // the correct length. If it is correct length, never panic. + fn prop_test_nonce_length_hchacha(nonce: Vec<u8>) -> bool { + if nonce.len() == HCHACHA_NONCESIZE { + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &nonce, false).is_ok() + } else { + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &nonce, false).is_err() + } + } + } + + mod test_encrypt_in_place { + use super::*; + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn test_xor_keystream_err_bad_tmp() { + let mut ctx = + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE], true).unwrap(); + let mut tmp = [0u8; CHACHA_BLOCKSIZE - 1]; + let mut out = [0u8; CHACHA_BLOCKSIZE]; + xor_keystream(&mut ctx, 0, &mut tmp, &mut out).unwrap(); + } + + #[test] + fn test_xor_keystream_err_empty_input() { + let mut ctx = + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE], true).unwrap(); + let mut tmp = [0u8; CHACHA_BLOCKSIZE]; + let mut out = [0u8; 0]; + assert!(xor_keystream(&mut ctx, 0, &mut tmp, &mut out).is_err()); + } + + #[test] + fn test_enc_in_place_err_empty_input() { + let n = Nonce::from([0u8; IETF_CHACHA_NONCESIZE]); + let sk = SecretKey::from([0u8; CHACHA_KEYSIZE]); + let mut out = [0u8; 0]; + assert!(encrypt_in_place(&sk, &n, 0, &mut out).is_err()); + } + } + + mod test_keystream_block { + use super::*; + + #[test] + fn test_xor_keystream_block_ignore_counter_when_hchacha() { + let mut chacha_state_hchacha = + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; HCHACHA_NONCESIZE], false).unwrap(); + + let mut hchacha_keystream_block_zero = [0u8; HCHACHA_OUTSIZE]; + let mut hchacha_keystream_block_max = [0u8; HCHACHA_OUTSIZE]; + + chacha_state_hchacha.keystream_block(0, &mut hchacha_keystream_block_zero); + chacha_state_hchacha.keystream_block(u32::MAX, &mut hchacha_keystream_block_max); + + assert_eq!(hchacha_keystream_block_zero, hchacha_keystream_block_max); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn test_xor_keystream_block_invalid_blocksize_ietf() { + let mut chacha_state_ietf = + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE], true).unwrap(); + + let mut ietf_keystream_block = [0u8; CHACHA_BLOCKSIZE]; + let mut hchacha_keystream_block = [0u8; HCHACHA_OUTSIZE]; + + chacha_state_ietf.keystream_block(0, &mut ietf_keystream_block); + chacha_state_ietf.keystream_block(0, &mut hchacha_keystream_block); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn test_xor_keystream_block_invalid_blocksize_hchacha() { + let mut chacha_state_hchacha = + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; HCHACHA_NONCESIZE], false).unwrap(); + + let mut ietf_keystream_block = [0u8; CHACHA_BLOCKSIZE]; + let mut hchacha_keystream_block = [0u8; HCHACHA_OUTSIZE]; + + chacha_state_hchacha.keystream_block(0, &mut hchacha_keystream_block); + chacha_state_hchacha.keystream_block(0, &mut ietf_keystream_block); + } + + #[test] + #[should_panic] + fn test_xor_keystream_panic_on_too_much_keystream_data_ietf() { + let mut chacha_state_ietf = ChaCha20 { + state: [ + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + ], + internal_counter: (u32::MAX - 128), + is_ietf: true, + }; + + let mut keystream_block = [0u8; CHACHA_BLOCKSIZE]; + + for amount in 0..(128 + 1) { + chacha_state_ietf.keystream_block(amount, &mut keystream_block); + } + } + + #[test] + #[should_panic] + fn test_xor_keystream_panic_on_too_much_keystream_data_hchacha() { + let mut chacha_state_ietf = ChaCha20 { + state: [ + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + ], + internal_counter: (u32::MAX - 128), + is_ietf: false, + }; + + let mut keystream_block = [0u8; HCHACHA_OUTSIZE]; + + for _ in 0..(128 + 1) { + chacha_state_ietf.keystream_block(0, &mut keystream_block); + } + } + + #[test] + fn test_error_if_internal_counter_would_overflow() { + let mut chacha_state = ChaCha20 { + state: [ + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + ], + internal_counter: (u32::MAX - 2), + is_ietf: false, + }; + + assert!(chacha_state.next_produceable().is_ok()); + chacha_state.internal_counter += 1; + assert!(chacha_state.next_produceable().is_ok()); + chacha_state.internal_counter += 1; + assert!(chacha_state.next_produceable().is_err()); + } + } +} + +// Testing any test vectors that aren't put into library's /tests folder. +#[cfg(test)] +mod test_vectors { + use super::*; + + // NOTE: These PartialEq implementation should only be available in testing. + #[cfg(test)] + impl PartialEq for U32x4 { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 && self.1 == other.1 && self.2 == other.2 && self.3 == other.3 + } + } + + // Convenience function for testing. + fn init(key: &[u8], nonce: &[u8]) -> Result<ChaCha20, UnknownCryptoError> { + ChaCha20::new(key, nonce, true) + } + #[test] + fn rfc8439_chacha20_block_results() { + let key = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, + 0x71, 0xc4, 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, + 0xc3, 0xd4, 0x6c, 0x4e, 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, + 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2, 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, + 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e, + ]; + + let expected_init = [ + U32x4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574), + U32x4(0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c), + U32x4(0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c), + U32x4(0x00000001, 0x09000000, 0x4a000000, 0x00000000), + ]; + // Test initial key-setup + let mut state = init(&key, &nonce).unwrap(); + // Set block counter + state.state[3].0 = 1; + assert!(state.state[..] == expected_init[..]); + + let mut kb = [0u8; 64]; + state.keystream_block(1, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_1() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, + 0xbd, 0x28, 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, 0xa8, 0x36, 0xef, 0xcc, + 0x8b, 0x77, 0x0d, 0xc7, 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, 0x77, 0x24, + 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, + 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut kb = [0u8; 64]; + state.keystream_block(0, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_2() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x9f, 0x07, 0xe7, 0xbe, 0x55, 0x51, 0x38, 0x7a, 0x98, 0xba, 0x97, 0x7c, 0x73, 0x2d, + 0x08, 0x0d, 0xcb, 0x0f, 0x29, 0xa0, 0x48, 0xe3, 0x65, 0x69, 0x12, 0xc6, 0x53, 0x3e, + 0x32, 0xee, 0x7a, 0xed, 0x29, 0xb7, 0x21, 0x76, 0x9c, 0xe6, 0x4e, 0x43, 0xd5, 0x71, + 0x33, 0xb0, 0x74, 0xd8, 0x39, 0xd5, 0x31, 0xed, 0x1f, 0x28, 0x51, 0x0a, 0xfb, 0x45, + 0xac, 0xe1, 0x0a, 0x1f, 0x4b, 0x79, 0x4d, 0x6f, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut kb = [0u8; 64]; + state.keystream_block(1, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_3() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x3a, 0xeb, 0x52, 0x24, 0xec, 0xf8, 0x49, 0x92, 0x9b, 0x9d, 0x82, 0x8d, 0xb1, 0xce, + 0xd4, 0xdd, 0x83, 0x20, 0x25, 0xe8, 0x01, 0x8b, 0x81, 0x60, 0xb8, 0x22, 0x84, 0xf3, + 0xc9, 0x49, 0xaa, 0x5a, 0x8e, 0xca, 0x00, 0xbb, 0xb4, 0xa7, 0x3b, 0xda, 0xd1, 0x92, + 0xb5, 0xc4, 0x2f, 0x73, 0xf2, 0xfd, 0x4e, 0x27, 0x36, 0x44, 0xc8, 0xb3, 0x61, 0x25, + 0xa6, 0x4a, 0xdd, 0xeb, 0x00, 0x6c, 0x13, 0xa0, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut kb = [0u8; 64]; + state.keystream_block(1, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_4() { + let key = [ + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x72, 0xd5, 0x4d, 0xfb, 0xf1, 0x2e, 0xc4, 0x4b, 0x36, 0x26, 0x92, 0xdf, 0x94, 0x13, + 0x7f, 0x32, 0x8f, 0xea, 0x8d, 0xa7, 0x39, 0x90, 0x26, 0x5e, 0xc1, 0xbb, 0xbe, 0xa1, + 0xae, 0x9a, 0xf0, 0xca, 0x13, 0xb2, 0x5a, 0xa2, 0x6c, 0xb4, 0xa6, 0x48, 0xcb, 0x9b, + 0x9d, 0x1b, 0xe6, 0x5b, 0x2c, 0x09, 0x24, 0xa6, 0x6c, 0x54, 0xd5, 0x45, 0xec, 0x1b, + 0x73, 0x74, 0xf4, 0x87, 0x2e, 0x99, 0xf0, 0x96, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut kb = [0u8; 64]; + state.keystream_block(2, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_5() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]; + let expected = [ + 0xc2, 0xc6, 0x4d, 0x37, 0x8c, 0xd5, 0x36, 0x37, 0x4a, 0xe2, 0x04, 0xb9, 0xef, 0x93, + 0x3f, 0xcd, 0x1a, 0x8b, 0x22, 0x88, 0xb3, 0xdf, 0xa4, 0x96, 0x72, 0xab, 0x76, 0x5b, + 0x54, 0xee, 0x27, 0xc7, 0x8a, 0x97, 0x0e, 0x0e, 0x95, 0x5c, 0x14, 0xf3, 0xa8, 0x8e, + 0x74, 0x1b, 0x97, 0xc2, 0x86, 0xf7, 0x5f, 0x8f, 0xc2, 0x99, 0xe8, 0x14, 0x83, 0x62, + 0xfa, 0x19, 0x8a, 0x39, 0x53, 0x1b, 0xed, 0x6d, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut kb = [0u8; 64]; + state.keystream_block(0, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_key_schedule() { + let key = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, + ]; + // First block setup expected + let first_state = [ + U32x4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574), + U32x4(0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c), + U32x4(0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c), + U32x4(0x00000001, 0x00000000, 0x4a000000, 0x00000000), + ]; + // Second block setup expected + let second_state = [ + U32x4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574), + U32x4(0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c), + U32x4(0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c), + U32x4(0x00000002, 0x00000000, 0x4a000000, 0x00000000), + ]; + + // Expected keystream + let expected_keystream = [ + 0x22, 0x4f, 0x51, 0xf3, 0x40, 0x1b, 0xd9, 0xe1, 0x2f, 0xde, 0x27, 0x6f, 0xb8, 0x63, + 0x1d, 0xed, 0x8c, 0x13, 0x1f, 0x82, 0x3d, 0x2c, 0x06, 0xe2, 0x7e, 0x4f, 0xca, 0xec, + 0x9e, 0xf3, 0xcf, 0x78, 0x8a, 0x3b, 0x0a, 0xa3, 0x72, 0x60, 0x0a, 0x92, 0xb5, 0x79, + 0x74, 0xcd, 0xed, 0x2b, 0x93, 0x34, 0x79, 0x4c, 0xba, 0x40, 0xc6, 0x3e, 0x34, 0xcd, + 0xea, 0x21, 0x2c, 0x4c, 0xf0, 0x7d, 0x41, 0xb7, 0x69, 0xa6, 0x74, 0x9f, 0x3f, 0x63, + 0x0f, 0x41, 0x22, 0xca, 0xfe, 0x28, 0xec, 0x4d, 0xc4, 0x7e, 0x26, 0xd4, 0x34, 0x6d, + 0x70, 0xb9, 0x8c, 0x73, 0xf3, 0xe9, 0xc5, 0x3a, 0xc4, 0x0c, 0x59, 0x45, 0x39, 0x8b, + 0x6e, 0xda, 0x1a, 0x83, 0x2c, 0x89, 0xc1, 0x67, 0xea, 0xcd, 0x90, 0x1d, 0x7e, 0x2b, + 0xf3, 0x63, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut actual_keystream = [0u8; 128]; + + state.keystream_block(1, &mut actual_keystream[..64]); + assert!(first_state == state.state); + + state.keystream_block(2, &mut actual_keystream[64..]); + assert!(second_state == state.state); + + assert_eq!( + actual_keystream[..expected_keystream.len()].as_ref(), + expected_keystream.as_ref() + ); + } +} diff --git a/vendor/orion/src/hazardous/stream/mod.rs b/vendor/orion/src/hazardous/stream/mod.rs new file mode 100644 index 0000000..a211b7d --- /dev/null +++ b/vendor/orion/src/hazardous/stream/mod.rs @@ -0,0 +1,27 @@ +// MIT License + +// Copyright (c) 2018-2023 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. + +/// IETF ChaCha20 as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub mod chacha20; + +/// XChaCha20 as specified in the [draft-irtf-cfrg-xchacha-03](https://tools.ietf.org/html/draft-irtf-cfrg-xchacha-03). +pub mod xchacha20; diff --git a/vendor/orion/src/hazardous/stream/xchacha20.rs b/vendor/orion/src/hazardous/stream/xchacha20.rs new file mode 100644 index 0000000..b8940aa --- /dev/null +++ b/vendor/orion/src/hazardous/stream/xchacha20.rs @@ -0,0 +1,176 @@ +// MIT License + +// Copyright (c) 2018-2023 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 secret key. +//! - `nonce`: The nonce value. +//! - `initial_counter`: The initial counter value. In most cases, this is `0`. +//! - `ciphertext`: The encrypted data. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the ciphertext/plaintext after +//! encryption/decryption. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` or `ciphertext`. +//! - `plaintext` or `ciphertext` is empty. +//! - The `initial_counter` is high enough to cause a potential overflow. +//! +//! Even though `dst_out` is allowed to be of greater length than `plaintext`, +//! the `ciphertext` produced by `chacha20`/`xchacha20` will always be of the +//! same length as the `plaintext`. +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1 * 64` bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Functions herein do not provide any data integrity. If you need +//! data integrity, which is nearly ***always the case***, you should use an +//! AEAD construction instead. See the [`aead`](super::aead) module for this. +//! - Only a nonce for XChaCha20 is big enough to be randomly generated using a +//! CSPRNG. [`Nonce::generate()`] can be used for this. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! +//! # Recommendation: +//! - It is recommended to use [`XChaCha20Poly1305`] when possible. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::stream::xchacha20; +//! +//! let secret_key = xchacha20::SecretKey::generate(); +//! let nonce = xchacha20::Nonce::generate(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of this message is 15 +//! +//! let mut dst_out_pt = [0u8; 15]; +//! let mut dst_out_ct = [0u8; 15]; +//! +//! xchacha20::encrypt(&secret_key, &nonce, 0, message, &mut dst_out_ct)?; +//! +//! xchacha20::decrypt(&secret_key, &nonce, 0, &dst_out_ct, &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt, message); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: xchacha20::SecretKey::generate() +//! [`Nonce::generate()`]: xchacha20::Nonce::generate() +//! [`XChaCha20Poly1305`]: super::aead::xchacha20poly1305 +pub use crate::hazardous::stream::chacha20::SecretKey; +use crate::{ + errors::UnknownCryptoError, + hazardous::stream::chacha20::{self, Nonce as IETFNonce, IETF_CHACHA_NONCESIZE}, +}; + +/// The nonce size for XChaCha20. +pub const XCHACHA_NONCESIZE: usize = 24; + +construct_public! { + /// A type that represents a `Nonce` that XChaCha20, XChaCha20-Poly1305 use. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 24 bytes. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Nonce, test_nonce, XCHACHA_NONCESIZE, XCHACHA_NONCESIZE, XCHACHA_NONCESIZE) +} + +impl_from_trait!(Nonce, XCHACHA_NONCESIZE); + +/// Generate a subkey using HChaCha20 for XChaCha20 and corresponding nonce. +pub(crate) fn subkey_and_nonce(secret_key: &SecretKey, nonce: &Nonce) -> (SecretKey, IETFNonce) { + // .unwrap() should not be able to panic because we pass a 16-byte nonce. + let subkey: SecretKey = + SecretKey::from(chacha20::hchacha20(secret_key, &nonce.as_ref()[0..16]).unwrap()); + let mut prefixed_nonce = [0u8; IETF_CHACHA_NONCESIZE]; + prefixed_nonce[4..IETF_CHACHA_NONCESIZE].copy_from_slice(&nonce.as_ref()[16..24]); + + (subkey, IETFNonce::from(prefixed_nonce)) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// XChaCha20 encryption as specified in the [draft RFC](https://tools.ietf.org/html/draft-irtf-cfrg-xchacha-03). +pub fn encrypt( + secret_key: &SecretKey, + nonce: &Nonce, + initial_counter: u32, + plaintext: &[u8], + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + let (subkey, ietf_nonce) = subkey_and_nonce(secret_key, nonce); + + chacha20::encrypt(&subkey, &ietf_nonce, initial_counter, plaintext, dst_out) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// XChaCha20 decryption as specified in the [draft RFC](https://tools.ietf.org/html/draft-irtf-cfrg-xchacha-03). +pub fn decrypt( + secret_key: &SecretKey, + nonce: &Nonce, + initial_counter: u32, + ciphertext: &[u8], + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + encrypt(secret_key, nonce, initial_counter, ciphertext, dst_out) +} + +// Testing public functions in the module. +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use super::*; + + mod test_encrypt_decrypt { + use super::*; + use crate::test_framework::streamcipher_interface::*; + + impl TestingRandom for Nonce { + fn gen() -> Self { + Self::generate() + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_streamcipher_interface(input: Vec<u8>, counter: u32) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::generate(); + StreamCipherTestRunner(encrypt, decrypt, secret_key, nonce, counter, &input, None); + test_diff_params_diff_output(&encrypt, &decrypt); + + true + } + } +} diff --git a/vendor/orion/src/high_level/aead.rs b/vendor/orion/src/high_level/aead.rs new file mode 100644 index 0000000..4159ef8 --- /dev/null +++ b/vendor/orion/src/high_level/aead.rs @@ -0,0 +1,631 @@ +// MIT License + +// Copyright (c) 2020-2023 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. + +//! Authenticated secret-key encryption. +//! +//! # Use case: +//! `orion::aead` can be used to encrypt data in a way that detects if the +//! encrypted data has been tampered with before decrypting it. +//! +//! An example of this could be sending messages across networks, where +//! confidentiality and authenticity of these messages is required. +//! +//! # About: +//! - Both one-shot functions and a [`streaming`] API are provided. +//! - The nonce is automatically generated. +//! - Returns a vector where the first 24 bytes are the nonce and the rest is +//! the authenticated ciphertext with the last 16 bytes being the corresponding Poly1305 tag. +//! - Uses XChaCha20Poly1305 with no additional data. +//! - When using [`seal`] and [`open`] then the separation of tags, nonces and +//! ciphertext are automatically handled. +//! +//! # Parameters: +//! - `plaintext`: The data to be encrypted. +//! - `secret_key`: The secret key used to encrypt the `plaintext`. +//! - `ciphertext_with_tag_and_nonce`: The data to be decrypted with the first +//! 24 bytes being the nonce and the last 16 bytes being the corresponding Poly1305 tag. +//! +//! # Errors: +//! An error will be returned if: +//! - `secret_key` is not 32 bytes. +//! - The `plaintext` is empty. +//! - `ciphertext_with_tag_and_nonce` is less than 41 bytes +//! ([`XCHACHA_NONCESIZE`] + [`POLY1305_OUTSIZE`] + 1). +//! - The received tag does not match the calculated tag when calling [`open`]. +//! - `plaintext.len()` + [`XCHACHA_NONCESIZE`] + [`POLY1305_OUTSIZE`] overflows when calling [`seal`]. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2^32-1 * 64 bytes of data are processed. +//! - Failure to generate random bytes securely. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, +//! the security of all data that has been encrypted with that given key is +//! compromised. +//! - To securely generate a strong key, use [`SecretKey::default()`]. +//! - The length of the `plaintext` is not hidden, only its contents. +//! +//! # Example: +//! ```rust +//! use orion::aead; +//! +//! let secret_key = aead::SecretKey::default(); +//! let ciphertext = aead::seal(&secret_key, b"Secret message")?; +//! let decrypted_data = aead::open(&secret_key, &ciphertext)?; +//! +//! assert_eq!(decrypted_data, b"Secret message"); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use super::hltypes::SecretKey; +use crate::{ + errors::UnknownCryptoError, + hazardous::{ + aead, + mac::poly1305::POLY1305_OUTSIZE, + stream::{ + chacha20, + xchacha20::{Nonce, XCHACHA_NONCESIZE}, + }, + }, +}; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Authenticated encryption using XChaCha20Poly1305. +pub fn seal(secret_key: &SecretKey, plaintext: &[u8]) -> Result<Vec<u8>, UnknownCryptoError> { + if plaintext.is_empty() { + return Err(UnknownCryptoError); + } + + let out_len = match plaintext + .len() + .checked_add(XCHACHA_NONCESIZE + POLY1305_OUTSIZE) + { + Some(min_out_len) => min_out_len, + None => return Err(UnknownCryptoError), + }; + + let mut dst_out = vec![0u8; out_len]; + let nonce = Nonce::generate(); + dst_out[..XCHACHA_NONCESIZE].copy_from_slice(nonce.as_ref()); + + aead::xchacha20poly1305::seal( + &chacha20::SecretKey::from_slice(secret_key.unprotected_as_bytes())?, + &nonce, + plaintext, + None, + &mut dst_out[XCHACHA_NONCESIZE..], + )?; + + Ok(dst_out) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Authenticated decryption using XChaCha20Poly1305. +pub fn open( + secret_key: &SecretKey, + ciphertext_with_tag_and_nonce: &[u8], +) -> Result<Vec<u8>, UnknownCryptoError> { + // Avoid empty ciphertexts + if ciphertext_with_tag_and_nonce.len() <= (XCHACHA_NONCESIZE + POLY1305_OUTSIZE) { + return Err(UnknownCryptoError); + } + + let mut dst_out = + vec![0u8; ciphertext_with_tag_and_nonce.len() - (XCHACHA_NONCESIZE + POLY1305_OUTSIZE)]; + + aead::xchacha20poly1305::open( + &chacha20::SecretKey::from_slice(secret_key.unprotected_as_bytes())?, + &Nonce::from_slice(&ciphertext_with_tag_and_nonce[..XCHACHA_NONCESIZE])?, + &ciphertext_with_tag_and_nonce[XCHACHA_NONCESIZE..], + None, + &mut dst_out, + )?; + + Ok(dst_out) +} + +pub mod streaming { + //! Streaming AEAD based on XChaCha20Poly1305. + //! + //! # Use case: + //! This can be used to encrypt and authenticate a stream of data. It prevents the + //! modification, reordering, dropping or duplication of messages. Nonce management is handled automatically. + //! + //! An example of this could be the encryption of files that are too large to encrypt in one piece. + //! + //! # About: + //! This implementation is based on and compatible with the ["secretstream" API] of libsodium. + //! + //! # Parameters: + //! - `secret_key`: The secret key. + //! - `nonce`: The nonce value. + //! - `plaintext`: The data to be encrypted. + //! - `ciphertext`: The encrypted data with a Poly1305 tag and a [`StreamTag`] indicating its function. + //! - `tag`: Indicates the type of message. The `tag` is a part of the output when encrypting. It + //! is encrypted and authenticated. + //! + //! # Errors: + //! An error will be returned if: + //! - `secret_key` is not 32 bytes. + //! - The length of `ciphertext` is not at least [`ABYTES`]. + //! - The received mac does not match the calculated mac when decrypting. This can indicate + //! a dropped or reordered message within the stream. + //! - More than 2^32-3 * 64 bytes of data are processed when encrypting/decrypting a single chunk. + //! - [`ABYTES`] + `plaintext.len()` overflows when encrypting. + //! + //! # Panics: + //! A panic will occur if: + //! - 64 + (`ciphertext.len()` - [`ABYTES`]) overflows when decrypting. + //! - Failure to generate random bytes securely. + //! + //! # Security: + //! - It is critical for security that a given nonce is not re-used with a given + //! key. + //! - To securely generate a strong key, use [`SecretKey::generate()`]. + //! - The length of the messages is leaked. + //! - It is recommended to use `StreamTag::Finish` as tag for the last message. This allows the + //! decrypting side to detect if messages at the end of the stream are lost. + //! + //! # Example: + //! ```rust + //! use orion::aead::streaming::*; + //! use orion::aead::SecretKey; + //! + //! let chunk_size: usize = 128; // The size of the chunks you wish to split the stream into. + //! let src = [255u8; 4096]; // Some example input stream. + //! let mut out: Vec<Vec<u8>> = Vec::with_capacity(4096 / 128); + //! + //! let secret_key = SecretKey::default(); + //! + //! // Encryption: + //! let (mut sealer, nonce) = StreamSealer::new(&secret_key)?; + //! + //! for (n_chunk, src_chunk) in src.chunks(chunk_size).enumerate() { + //! let encrypted_chunk = + //! if src_chunk.len() != chunk_size || n_chunk + 1 == src.len() / chunk_size { + //! // We've reached the end of the input source, + //! // so we mark it with the Finish tag. + //! sealer.seal_chunk(src_chunk, &StreamTag::Finish)? + //! } else { + //! // Just a normal chunk + //! sealer.seal_chunk(src_chunk, &StreamTag::Message)? + //! }; + //! // Save the encrypted chunk somewhere + //! out.push(encrypted_chunk); + //! } + //! + //! // Decryption: + //! let mut opener = StreamOpener::new(&secret_key, &nonce)?; + //! + //! for (n_chunk, src_chunk) in out.iter().enumerate() { + //! let (_decrypted_chunk, tag) = opener.open_chunk(src_chunk)?; + //! + //! if src_chunk.len() != chunk_size + ABYTES || n_chunk + 1 == out.len() { + //! // We've reached the end of the input source, + //! // so we check if the last chunk is also set as Finish. + //! assert_eq!(tag, StreamTag::Finish, "Stream has been truncated!"); + //! } + //! } + //! + //! # Ok::<(), orion::errors::UnknownCryptoError>(()) + //! ``` + //! [`ABYTES`]: crate::hazardous::aead::streaming::ABYTES + //! [`StreamTag`]: crate::hazardous::aead::streaming::StreamTag + //! [`SecretKey::generate()`]: super::SecretKey::generate + //! ["secretstream" API]: https://download.libsodium.org/doc/secret-key_cryptography/secretstream + + use super::*; + pub use crate::hazardous::aead::streaming::Nonce; + pub use crate::hazardous::aead::streaming::StreamTag; + pub use crate::hazardous::aead::streaming::ABYTES; + + #[derive(Debug)] + /// Streaming authenticated encryption. + pub struct StreamSealer { + internal_sealer: aead::streaming::StreamXChaCha20Poly1305, + } + + impl StreamSealer { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize a `StreamSealer` struct with a given key. + pub fn new(secret_key: &SecretKey) -> Result<(Self, Nonce), UnknownCryptoError> { + let nonce = Nonce::generate(); + let sk = &aead::streaming::SecretKey::from_slice(secret_key.unprotected_as_bytes())?; + + let sealer = Self { + internal_sealer: aead::streaming::StreamXChaCha20Poly1305::new(sk, &nonce), + }; + Ok((sealer, nonce)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Encrypts `plaintext`. The `StreamTag` indicates the type of message. + pub fn seal_chunk( + &mut self, + plaintext: &[u8], + tag: &StreamTag, + ) -> Result<Vec<u8>, UnknownCryptoError> { + let sealed_chunk_len = plaintext.len().checked_add(ABYTES); + if sealed_chunk_len.is_none() { + return Err(UnknownCryptoError); + } + + let mut sealed_chunk = vec![0u8; sealed_chunk_len.unwrap()]; + self.internal_sealer + .seal_chunk(plaintext, None, &mut sealed_chunk, tag)?; + + Ok(sealed_chunk) + } + } + + #[derive(Debug)] + /// Streaming authenticated decryption. + pub struct StreamOpener { + internal_sealer: aead::streaming::StreamXChaCha20Poly1305, + } + + impl StreamOpener { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize a `StreamOpener` struct with a given key and nonce. + pub fn new(secret_key: &SecretKey, nonce: &Nonce) -> Result<Self, UnknownCryptoError> { + let sk = &chacha20::SecretKey::from_slice(secret_key.unprotected_as_bytes())?; + + Ok(Self { + internal_sealer: aead::streaming::StreamXChaCha20Poly1305::new(sk, nonce), + }) + } + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Decrypts `ciphertext`. Returns the decrypted data and the `StreamTag` indicating the type of message. + pub fn open_chunk( + &mut self, + ciphertext: &[u8], + ) -> Result<(Vec<u8>, StreamTag), UnknownCryptoError> { + if ciphertext.len() < ABYTES { + return Err(UnknownCryptoError); + } + + let mut opened_chunk = vec![0u8; ciphertext.len() - ABYTES]; + let tag = self + .internal_sealer + .open_chunk(ciphertext, None, &mut opened_chunk)?; + + Ok((opened_chunk, tag)) + } + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_seal_open { + use super::*; + + #[test] + fn test_auth_enc_encryption_decryption() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + + let dst_ciphertext = seal(&key, plaintext).unwrap(); + assert_eq!(dst_ciphertext.len(), plaintext.len() + (24 + 16)); + let dst_plaintext = open(&key, &dst_ciphertext).unwrap(); + assert_eq!(plaintext, &dst_plaintext[..]); + } + + #[test] + fn test_auth_enc_plaintext_empty_err() { + let key = SecretKey::default(); + let plaintext = "".as_bytes(); + + assert!(seal(&key, plaintext).is_err()); + } + + #[test] + fn test_auth_enc_ciphertext_less_than_41_err() { + let key = SecretKey::default(); + let ciphertext = [0u8; XCHACHA_NONCESIZE + POLY1305_OUTSIZE]; + + assert!(open(&key, &ciphertext).is_err()); + } + + #[test] + fn test_modified_nonce_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = seal(&key, plaintext).unwrap(); + // Modify nonce + dst_ciphertext[10] ^= 1; + assert!(open(&key, &dst_ciphertext).is_err()); + } + + #[test] + fn test_modified_ciphertext_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = seal(&key, plaintext).unwrap(); + // Modify ciphertext + dst_ciphertext[25] ^= 1; + assert!(open(&key, &dst_ciphertext).is_err()); + } + + #[test] + fn test_modified_tag_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = seal(&key, plaintext).unwrap(); + let dst_ciphertext_len = dst_ciphertext.len(); + // Modify tag + dst_ciphertext[dst_ciphertext_len - 6] ^= 1; + assert!(open(&key, &dst_ciphertext).is_err()); + } + + #[test] + fn test_diff_secret_key_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + + let dst_ciphertext = seal(&key, plaintext).unwrap(); + let bad_key = SecretKey::default(); + assert!(open(&bad_key, &dst_ciphertext).is_err()); + } + + #[test] + fn test_secret_length_err() { + let key = SecretKey::generate(31).unwrap(); + let plaintext = "Secret message".as_bytes(); + + assert!(seal(&key, plaintext).is_err()); + assert!(open(&key, plaintext).is_err()); + } + } + + mod test_stream_seal_open { + use super::streaming::*; + use super::*; + + #[test] + fn test_auth_enc_encryption_decryption() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let plaintext = "Secret message".as_bytes(); + + let dst_ciphertext = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + assert_eq!(dst_ciphertext.len(), plaintext.len() + 17); + let (dst_plaintext, tag) = opener.open_chunk(&dst_ciphertext).unwrap(); + assert_eq!(plaintext, &dst_plaintext[..]); + assert_eq!(tag, StreamTag::Message); + } + + #[test] + fn test_seal_chunk_plaintext_empty_ok() { + let key = SecretKey::default(); + let (mut sealer, _) = StreamSealer::new(&key).unwrap(); + let plaintext = "".as_bytes(); + + assert!(sealer.seal_chunk(plaintext, &StreamTag::Message).is_ok()); + } + + #[test] + fn test_open_chunk_less_than_abytes_err() { + let key = SecretKey::default(); + let ciphertext = [0u8; ABYTES - 1]; + let (_, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + + assert!(opener.open_chunk(&ciphertext).is_err()); + } + + #[test] + fn test_open_chunk_abytes_exact_ok() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let ciphertext = sealer + .seal_chunk("".as_bytes(), &StreamTag::Message) + .unwrap(); + let (pt, tag) = opener.open_chunk(&ciphertext).unwrap(); + + assert!(pt.is_empty()); + assert_eq!(tag.as_byte(), 0u8); + } + + #[test] + fn test_modified_tag_err() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + // Modify tag + dst_ciphertext[0] ^= 1; + assert!(opener.open_chunk(&dst_ciphertext).is_err()); + } + + #[test] + fn test_modified_ciphertext_err() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + // Modify ciphertext + dst_ciphertext[1] ^= 1; + assert!(opener.open_chunk(&dst_ciphertext).is_err()); + } + + #[test] + fn test_modified_mac_err() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + // Modify mac + let macpos = dst_ciphertext.len() - 1; + dst_ciphertext[macpos] ^= 1; + assert!(opener.open_chunk(&dst_ciphertext).is_err()); + } + + #[test] + fn test_diff_secret_key_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let bad_key = SecretKey::default(); + let mut opener = StreamOpener::new(&bad_key, &nonce).unwrap(); + + let dst_ciphertext = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + + assert!(opener.open_chunk(&dst_ciphertext).is_err()); + } + + #[test] + fn test_secret_length_err() { + let key = SecretKey::generate(31).unwrap(); + assert!(StreamSealer::new(&key).is_err()); + assert!(StreamOpener::new(&key, &Nonce::generate()).is_err()); + } + + #[test] + fn same_input_generates_different_ciphertext() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let plaintext = "Secret message 1".as_bytes(); + let cipher1 = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + let cipher2 = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + assert_ne!(cipher1, cipher2); + + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let (dec1, tag1) = opener.open_chunk(&cipher1).unwrap(); + let (dec2, tag2) = opener.open_chunk(&cipher2).unwrap(); + assert_eq!(plaintext, &dec1[..]); + assert_eq!(plaintext, &dec2[..]); + assert_eq!(tag1, StreamTag::Message); + assert_eq!(tag2, StreamTag::Message); + } + + #[test] + fn same_input_on_same_init_different_ct() { + // Two sealers initialized that encrypt the same plaintext + // should produce different ciphertexts because the nonce + // is randomly generated. + let key = SecretKey::default(); + let (mut sealer_first, _) = StreamSealer::new(&key).unwrap(); + let (mut sealer_second, _) = StreamSealer::new(&key).unwrap(); + let plaintext = "Secret message 1".as_bytes(); + + let cipher1 = sealer_first + .seal_chunk(plaintext, &StreamTag::Message) + .unwrap(); + let cipher2 = sealer_second + .seal_chunk(plaintext, &StreamTag::Message) + .unwrap(); + assert_ne!(cipher1, cipher2); + } + + #[test] + fn test_stream_seal_and_open() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let plaintext1 = "Secret message 1".as_bytes(); + let plaintext2 = "Secret message 2".as_bytes(); + let plaintext3 = "Secret message 3".as_bytes(); + let cipher1 = sealer.seal_chunk(plaintext1, &StreamTag::Message).unwrap(); + let cipher2 = sealer.seal_chunk(plaintext2, &StreamTag::Finish).unwrap(); + let cipher3 = sealer.seal_chunk(plaintext3, &StreamTag::Message).unwrap(); + + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let (dec1, tag1) = opener.open_chunk(&cipher1).unwrap(); + let (dec2, tag2) = opener.open_chunk(&cipher2).unwrap(); + let (dec3, tag3) = opener.open_chunk(&cipher3).unwrap(); + assert_eq!(plaintext1, &dec1[..]); + assert_eq!(plaintext2, &dec2[..]); + assert_eq!(plaintext3, &dec3[..]); + assert_eq!(tag1, StreamTag::Message); + assert_eq!(tag2, StreamTag::Finish); + assert_eq!(tag3, StreamTag::Message); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_stream_seal_open_same_input(input: Vec<u8>) -> bool { + let key = SecretKey::default(); + + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let ct = sealer.seal_chunk(&input[..], &StreamTag::Message).unwrap(); + + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let (pt_decrypted, tag) = opener.open_chunk(&ct).unwrap(); + + input == pt_decrypted && tag == StreamTag::Message + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + // Sealing input, and then opening should always yield the same input. + fn prop_seal_open_same_input(input: Vec<u8>) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let sk = SecretKey::default(); + + let ct = seal(&sk, &pt).unwrap(); + let pt_decrypted = open(&sk, &ct).unwrap(); + + pt == pt_decrypted + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + // Sealing input, modifying the tag and then opening should + // always fail due to authentication. + fn prop_fail_on_diff_key(input: Vec<u8>) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let sk = SecretKey::default(); + let sk2 = SecretKey::default(); + let ct = seal(&sk, &pt).unwrap(); + + open(&sk2, &ct).is_err() + } + } +} diff --git a/vendor/orion/src/high_level/auth.rs b/vendor/orion/src/high_level/auth.rs new file mode 100644 index 0000000..7c61f9b --- /dev/null +++ b/vendor/orion/src/high_level/auth.rs @@ -0,0 +1,212 @@ +// MIT License + +// Copyright (c) 2020-2023 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. + +//! Message authentication. +//! +//! # Use case: +//! `orion::auth` can be used to ensure message integrity and authenticity by +//! using a secret key. +//! +//! An example of this could be securing APIs by having a user of a given API +//! sign their API request and having the API server verify these signed API +//! requests. +//! +//! # About: +//! - Uses BLAKE2b-256 in keyed mode. +//! +//! # Parameters: +//! - `secret_key`: Secret key used to authenticate `data`. +//! - `data`: Data to be authenticated. +//! - `expected`: The expected authentication [`Tag`]. +//! +//! # Errors: +//! An error will be returned if: +//! - The calculated [`Tag`] does not match the expected. +//! - The [`SecretKey`] supplied is less than 32 bytes or greater than 64 bytes. +//! - The expected [`Tag`] is not 32 bytes when verifying. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) bytes of data are authenticated. +//! +//! # Security: +//! - The secret key should always be generated using a CSPRNG. +//! [`SecretKey::default()`] can be used for +//! this; it will generate a [`SecretKey`] of 32 bytes. +//! - The required minimum length for a [`SecretKey`] is 32 bytes. +//! +//! # Example: +//! ```rust +//! use orion::auth; +//! +//! // There exists a shared key between the user and API server +//! let key = auth::SecretKey::default(); +//! +//! // User generates message and authentication tag +//! let msg = "Some message.".as_bytes(); +//! let expected_tag = auth::authenticate(&key, msg)?; +//! +//! // API server verifies the authenticity of the message with the tag +//! assert!(auth::authenticate_verify(&expected_tag, &key, &msg).is_ok()); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use super::hltypes::SecretKey; +pub use crate::hazardous::mac::blake2b::Tag; +use crate::{ + errors::UnknownCryptoError, + hazardous::mac::blake2b::{self, Blake2b}, +}; + +/// The Tag size (bytes) to be output by BLAKE2b in keyed mode. +const BLAKE2B_TAG_SIZE: usize = 32; +/// The minimum `SecretKey` size (bytes) to be used by BLAKE2b in keyed mode. +const BLAKE2B_MIN_KEY_SIZE: usize = 32; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Authenticate a message using BLAKE2b-256 in keyed mode. +pub fn authenticate(secret_key: &SecretKey, data: &[u8]) -> Result<Tag, UnknownCryptoError> { + if secret_key.len() < BLAKE2B_MIN_KEY_SIZE { + return Err(UnknownCryptoError); + } + let blake2b_secret_key = blake2b::SecretKey::from_slice(secret_key.unprotected_as_bytes())?; + let mut state = Blake2b::new(&blake2b_secret_key, BLAKE2B_TAG_SIZE)?; + state.update(data)?; + state.finalize() +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Authenticate and verify a message using BLAKE2b-256 in keyed mode. +pub fn authenticate_verify( + expected: &Tag, + secret_key: &SecretKey, + data: &[u8], +) -> Result<(), UnknownCryptoError> { + if secret_key.len() < BLAKE2B_MIN_KEY_SIZE || expected.len() != BLAKE2B_TAG_SIZE { + return Err(UnknownCryptoError); + } + let key = blake2b::SecretKey::from_slice(secret_key.unprotected_as_bytes())?; + Blake2b::verify(expected, &key, BLAKE2B_TAG_SIZE, data) +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_auth_and_verify { + use super::*; + #[test] + fn test_authenticate_verify_bad_key() { + let sec_key_correct = SecretKey::generate(64).unwrap(); + let sec_key_false = SecretKey::default(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + let mac_bob = authenticate(&sec_key_correct, &msg).unwrap(); + + assert!(authenticate_verify(&mac_bob, &sec_key_correct, &msg).is_ok()); + assert!(authenticate_verify(&mac_bob, &sec_key_false, &msg).is_err()); + } + + #[test] + fn test_authenticate_verify_bad_msg() { + let sec_key = SecretKey::generate(64).unwrap(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + let mac_bob = authenticate(&sec_key, &msg).unwrap(); + + assert!(authenticate_verify(&mac_bob, &sec_key, &msg).is_ok()); + assert!(authenticate_verify(&mac_bob, &sec_key, b"bad msg").is_err()); + } + + #[test] + fn test_authenticate_key_too_small() { + let sec_key = SecretKey::generate(31).unwrap(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + + assert!(authenticate(&sec_key, &msg).is_err()); + } + + #[test] + fn test_authenticate_verify_key_too_small() { + let sec_key = SecretKey::generate(31).unwrap(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + let mac = Tag::from_slice(&[0u8; 32][..]).unwrap(); + + assert!(authenticate_verify(&mac, &sec_key, &msg).is_err()); + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Authentication and verifying that tag with the same parameters + /// should always be true. + fn prop_authenticate_verify(input: Vec<u8>) -> bool { + let sk = SecretKey::default(); + let tag = authenticate(&sk, &input[..]).unwrap(); + authenticate_verify(&tag, &sk, &input[..]).is_ok() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Authentication and verifying that tag with a different key should + /// never be true. + fn prop_verify_fail_diff_key(input: Vec<u8>) -> bool { + let sk = SecretKey::default(); + let sk2 = SecretKey::default(); + let tag = authenticate(&sk, &input[..]).unwrap(); + + authenticate_verify(&tag, &sk2, &input[..]).is_err() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Authentication and verifying that tag with different input should + /// never be true. + fn prop_verify_fail_diff_input(input: Vec<u8>) -> bool { + let sk = SecretKey::default(); + let tag = authenticate(&sk, &input[..]).unwrap(); + + authenticate_verify(&tag, &sk, b"Completely wrong input").is_err() + } + + use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_KEYSIZE; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Verify the bounds of 32..=64 (inclusive) for the `SecretKey` used + /// in `authenticate/authenticate_verify`. + fn prop_authenticate_key_size(input: Vec<u8>) -> bool { + let sec_key_res = SecretKey::from_slice(&input); + if input.is_empty() || input.len() >= u32::MAX as usize { + return sec_key_res.is_err(); + } + let sec_key = sec_key_res.unwrap(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + let auth_res = authenticate(&sec_key, &msg); + if input.len() >= BLAKE2B_MIN_KEY_SIZE && input.len() <= BLAKE2B_KEYSIZE { + auth_res.is_ok() + } else { + auth_res.is_err() + } + } +} diff --git a/vendor/orion/src/high_level/hash.rs b/vendor/orion/src/high_level/hash.rs new file mode 100644 index 0000000..78b4bda --- /dev/null +++ b/vendor/orion/src/high_level/hash.rs @@ -0,0 +1,134 @@ +// MIT License + +// Copyright (c) 2020-2023 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. + +//! Hashing. +//! +//! # Use case: +//! `orion::hash` can be used to hash some given data. +//! +//! An example of this could be using hashes of files to ensure integrity. +//! Meaning, checking if a file has been modified since the time the hash was +//! recorded. +//! +//! If you are looking for a keyed hash, please see the [`orion::auth`](super::auth) module. +//! +//! # About: +//! - Uses BLAKE2b with an output size of 32 bytes (i.e BLAKE2b-256). +//! +//! # Parameters: +//! - `data`: The data to be hashed. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) bytes of data are hashed. +//! +//! # Security: +//! - This interface does not support supplying BLAKE2b with a secret key, and +//! the hashes retrieved +//! from using `orion::hash` are therefore not suitable as MACs. +//! - BLAKE2b is not suitable for password hashing. See [`orion::pwhash`](super::pwhash) +//! instead. +//! +//! # Examples +//! +//! ## Hashing in-memory data +//! ```rust +//! use orion::hash::{digest, Digest}; +//! +//! let hash: Digest = digest(b"Some data")?; +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! +//! ## Hashing data from an arbitrary reader +//! ```rust +//! use orion::hash::{digest_from_reader, Digest}; +//! +//! // `reader` could instead be `File::open("file.txt")?` +//! let reader = std::io::Cursor::new(b"some data"); +//! let hash: Digest = digest_from_reader(reader)?; +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use crate::hazardous::hash::blake2::blake2b::Digest; +use crate::{errors::UnknownCryptoError, hazardous::hash::blake2::blake2b}; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Hashing using BLAKE2b-256. +pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> { + blake2b::Hasher::Blake2b256.digest(data) +} + +/// Hash data from a [`Read`](std::io::Read)` type using BLAKE2b-256. +/// +/// See the [module-level docs](crate::hash) for an example of how to use this function. +/// Internally calls [`std::io::copy`]() to move data from the reader into the Blake2b writer. +/// Note that the [`std::io::copy`]() function buffers reads, so passing in a +/// [`BufReader`](std::io::BufReader) may be unnecessary. +/// +/// For lower-level control over reads, writes, buffer sizes, *etc.*, consider using the +/// [`Blake2b`](crate::hazardous::hash::blake2::blake2b::Blake2b) type and its +/// [`Write`](std::io::Write) implementation directly. See `Blake2b`'s `Write` implementation +/// and/or its `Write` documentation for an example. +/// +/// ## Errors: +/// This function will only ever return the [`std::io::ErrorKind::Other`]() +/// variant when it returns an error. Additionally, this will always contain Orion's +/// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. +/// +/// Note that if an error is returned, data may still have been consumed from the given reader. +#[cfg(feature = "safe_api")] +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +pub fn digest_from_reader(mut reader: impl std::io::Read) -> Result<Digest, UnknownCryptoError> { + let mut hasher = blake2b::Blake2b::new(32)?; + std::io::copy(&mut reader, &mut hasher).map_err(|_| UnknownCryptoError)?; + hasher.finalize() +} + +// Testing public functions in the module. +#[cfg(feature = "safe_api")] +#[cfg(test)] +mod public { + use super::*; + + #[quickcheck] + /// Hashing twice with same input should always produce same output. + fn prop_digest_same_result(input: Vec<u8>) -> bool { + digest(&input[..]).unwrap() == digest(&input[..]).unwrap() + } + + #[quickcheck] + /// Hashing all input should be the same as wrapping it in a + /// cursor and using digest_from_reader. + fn prop_digest_same_as_digest_from_reader(input: Vec<u8>) -> bool { + let digest_a = digest_from_reader(std::io::Cursor::new(&input)).unwrap(); + let digest_b = digest(&input).unwrap(); + digest_a == digest_b + } + + #[quickcheck] + /// Hashing twice with different input should never produce same output. + fn prop_digest_diff_result(input: Vec<u8>) -> bool { + digest(&input[..]).unwrap() != digest(b"Completely wrong input").unwrap() + } +} diff --git a/vendor/orion/src/high_level/hltypes.rs b/vendor/orion/src/high_level/hltypes.rs new file mode 100644 index 0000000..419cb22 --- /dev/null +++ b/vendor/orion/src/high_level/hltypes.rs @@ -0,0 +1,76 @@ +// MIT License + +// Copyright (c) 2020-2023 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. + +/// These are the different types used by the high-level interface. They are not +/// used in `hazardous`. +use crate::errors::UnknownCryptoError; + +construct_secret_key_variable_size! { + /// A type to represent a secret key. + /// + /// As default it will randomly generate a `SecretKey` of 32 bytes. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `length` is 0. + /// - `length` is not less than [`isize::MAX`]. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, test_secret_key, 32) +} + +construct_salt_variable_size! { + /// A type to represent the `Salt` that Argon2i uses during key derivation. + /// + /// As default it will randomly generate a `Salt` of 16 bytes. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `length` is 0. + /// - `length` is not less than [`isize::MAX`]. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Salt, test_salt, 16) +} + +construct_secret_key_variable_size! { + /// A type to represent the `Password` that Argon2i hashes and uses for key derivation. + /// + /// As default it will randomly generate a `Password` of 32 bytes. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `length` is 0. + /// - `length` is not less than [`isize::MAX`]. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Password, test_password, 32) +} diff --git a/vendor/orion/src/high_level/kdf.rs b/vendor/orion/src/high_level/kdf.rs new file mode 100644 index 0000000..87f0192 --- /dev/null +++ b/vendor/orion/src/high_level/kdf.rs @@ -0,0 +1,222 @@ +// MIT License + +// Copyright (c) 2020-2023 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. + +//! Key derivation. +//! +//! # Use case: +//! `orion::kdf` can be used to derive higher-entropy keys from low-entropy +//! keys. Also known as key stretching. +//! +//! An example of this could be deriving a key from a user-submitted password +//! and using this derived key in disk encryption. +//! +//! # About: +//! - Uses Argon2i. +//! +//! # Note: +//! This implementation only supports a single thread/lane. +//! +//! # Parameters: +//! - `password`: The low-entropy input key to be used in key derivation. +//! - `salt`: The salt used for the key derivation. +//! - `iterations`: Iterations cost parameter for Argon2i. +//! - `memory`: Memory (in kibibytes (KiB)) cost parameter for Argon2i. +//! - `length`: The desired length of the derived key. +//! +//! # Errors: +//! An error will be returned if: +//! - `iterations` is less than 3. +//! - `length` is less than 4. +//! - `memory` is less than 8. +//! - The length of the `password` is greater than [`isize::MAX`]. +//! - The length of the `salt` is greater than [`isize::MAX`] or less than `8`. +//! +//! # Security: +//! - Choosing the correct cost parameters is important for security. Please refer to +//! [libsodium's docs] for a description of how to do this. +//! - The salt should always be generated using a CSPRNG. [`Salt::default()`] +//! can be used for this, it will generate a [`Salt`] of 16 bytes. +//! - The recommended minimum size for a salt is 16 bytes. +//! - The recommended minimum size for a derived key is 16 bytes. +//! +//! If the concrete cost parameters needed are unclear, please refer to [OWASP] for recommended minimum values. +//! +//! # Example: +//! ```rust +//! use orion::kdf; +//! +//! let user_password = kdf::Password::from_slice(b"User password")?; +//! let salt = kdf::Salt::default(); +//! +//! let derived_key = kdf::derive_key(&user_password, &salt, 3, 1<<16, 32)?; +//! +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [libsodium's docs]: https://download.libsodium.org/doc/password_hashing/default_phf#guidelines-for-choosing-the-parameters +//! [OWASP]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use super::hltypes::{Password, Salt, SecretKey}; +use crate::{errors::UnknownCryptoError, hazardous::kdf::argon2i, pwhash::MIN_ITERATIONS}; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Derive a key using Argon2i. +pub fn derive_key( + password: &Password, + salt: &Salt, + iterations: u32, + memory: u32, + length: u32, +) -> Result<SecretKey, UnknownCryptoError> { + if iterations < MIN_ITERATIONS { + return Err(UnknownCryptoError); + } + + let mut dk = SecretKey::from_slice(&vec![0u8; length as usize])?; + + argon2i::derive_key( + password.unprotected_as_bytes(), + salt.as_ref(), + iterations, + memory, + None, + None, + &mut dk.value, + )?; + + Ok(dk) +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_derive_key_and_verify { + use super::*; + + #[test] + fn test_derive_key() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 16]).unwrap(); + let dk_first = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_second = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + + assert_eq!(dk_first, dk_second); + } + + #[test] + fn test_derive_key_err_diff_iter() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let dk = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_diff_iter = derive_key(&password, &salt, 4, 1024, 32).unwrap(); + + assert_ne!(dk, dk_diff_iter); + } + + #[test] + fn test_derive_key_err_diff_mem() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let dk = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_diff_mem = derive_key(&password, &salt, 3, 512, 32).unwrap(); + + assert_ne!(dk, dk_diff_mem); + } + + #[test] + fn test_derive_key_err_diff_salt() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let dk = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_diff_salt = derive_key( + &password, + &Salt::from_slice(&[1u8; 64]).unwrap(), + 3, + 1024, + 32, + ) + .unwrap(); + + assert_ne!(dk, dk_diff_salt); + } + + #[test] + fn test_derive_key_err_diff_len() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let dk = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_diff_len = derive_key(&password, &salt, 3, 1024, 64).unwrap(); + + assert_ne!(dk, dk_diff_len); + } + + #[test] + fn test_derive_key_err_diff_pass() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let dk = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_diff_pass = derive_key( + &Password::from_slice(&[1u8; 64]).unwrap(), + &salt, + 3, + 1024, + 32, + ) + .unwrap(); + + assert_ne!(dk, dk_diff_pass); + } + + #[test] + fn test_derive_key_bad_length() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + + assert!(derive_key(&password, &salt, 3, 1024, 3).is_err()); + assert!(derive_key(&password, &salt, 3, 1024, 4).is_ok()); + assert!(derive_key(&password, &salt, 3, 1024, 5).is_ok()); + } + + #[test] + fn test_derive_key_bad_iter() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 16]).unwrap(); + + assert!(derive_key(&password, &salt, 2, 1024, 32).is_err()); + assert!(derive_key(&password, &salt, 3, 1024, 32).is_ok()); + assert!(derive_key(&password, &salt, 4, 1024, 32).is_ok()); + } + + #[test] + fn test_derive_key_bad_mem() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 16]).unwrap(); + + assert!(derive_key(&password, &salt, 3, 7, 32).is_err()); + assert!(derive_key(&password, &salt, 3, 8, 32).is_ok()); + assert!(derive_key(&password, &salt, 3, 9, 32).is_ok()); + } + } +} diff --git a/vendor/orion/src/high_level/kex.rs b/vendor/orion/src/high_level/kex.rs new file mode 100644 index 0000000..787f514 --- /dev/null +++ b/vendor/orion/src/high_level/kex.rs @@ -0,0 +1,539 @@ +// MIT License + +// Copyright (c) 2021-2023 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. + +//! Ephemeral key exchange. +//! +//! # Use case: +//! `orion::kex` can be used to establish a pair of shared keys between two parties. +//! +//! # About: +//! - Both [`EphemeralClientSession`] and [`EphemeralServerSession`] consume `self` when shared keys +//! are being established. You can therefore never use the same private key for more than a single +//! key exchange. +//! +//! This implementation is based on and compatible with the +//! [key exchange API](https://doc.libsodium.org/key_exchange) of libsodium. +//! +//! # Parameters: +//! - `server_public_key`: The server's public key used to establish the client's shared session keys. +//! - `client_public_key`: The client's public key used to establish the server's shared session keys. +//! +//! # Errors: +//! An error will be returned if: +//! - If the key exchange results in an all-zero output. +//! +//! # Panics: +//! A panic will occur if: +//! - Failure to generate random bytes securely. +//! +//! # Security: +//! - The API is designed to be ephemeral and a [`PrivateKey`] should not be used more than once. +//! +//! # Example: +//! ```rust +//! use orion::kex::*; +//! use orion::aead; +//! +//! /// The server initializes their ephemeral session keys +//! let session_server = EphemeralServerSession::new()?; +//! let server_public_key = session_server.public_key(); +//! +//! /// The client initializes their ephemeral session keys +//! let session_client = EphemeralClientSession::new()?; +//! let client_public_key = session_client.public_key().clone(); +//! +//! let client_keys: SessionKeys = session_client +//! .establish_with_server(server_public_key)?; +//! +//! let server_keys: SessionKeys = session_server +//! .establish_with_client(&client_public_key)?; +//! +//! assert_eq!(client_keys.receiving(), server_keys.transport()); +//! assert_eq!(client_keys.transport(), server_keys.receiving()); +//! +//! // The client can now "send" encrypted data to the server and vice versa +//! +//! // Client sends an encrypted message which the server decrypts: +//! let client_msg = aead::seal(client_keys.transport(), b"Hello, server!")?; +//! assert_eq!(aead::open(server_keys.receiving(), &client_msg)?, b"Hello, server!"); +//! +//! // Server responds and client decrypts the received message: +//! let server_msg = aead::seal(server_keys.transport(), b"Hello, client!")?; +//! assert_eq!(aead::open(client_keys.receiving(), &server_msg)?, b"Hello, client!"); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use super::hltypes::SecretKey; +pub use crate::hazardous::ecc::x25519::PrivateKey; +pub use crate::hazardous::ecc::x25519::PublicKey; + +use crate::errors::UnknownCryptoError; +use crate::hazardous::ecc::x25519; +use crate::hazardous::hash::blake2::blake2b::{Blake2b, Digest}; +use core::convert::TryFrom; + +#[derive(Debug, PartialEq)] +/// A key pair used to establish shared keys for a single session. +pub struct EphemeralClientSession { + private_key: PrivateKey, + public_key: PublicKey, +} + +impl EphemeralClientSession { + /// Generate a new random key pair. + pub fn new() -> Result<Self, UnknownCryptoError> { + let privkey = PrivateKey::generate(); + let pubkey: PublicKey = PublicKey::try_from(&privkey)?; + + Ok(Self { + private_key: privkey, + public_key: pubkey, + }) + } + + /// Get a reference to the [`PublicKey`]. + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } + + /// Get a reference to the [`PrivateKey`]. + pub fn private_key(&self) -> &PrivateKey { + &self.private_key + } + + /// Establish session keys with a server. This moves `self` to ensure that the keys + /// generated with [`Self::new()`] are only used for this key exchange, thus remaining ephemeral. + pub fn establish_with_server( + self, + server_public_key: &PublicKey, + ) -> Result<SessionKeys, UnknownCryptoError> { + let q = x25519::key_agreement(&self.private_key, server_public_key)?; + let keys = establish_session_keys(&q, &self.public_key, server_public_key)?; + + Ok(SessionKeys { + rx: SecretKey::from_slice(&keys.as_ref()[..32])?, + tx: SecretKey::from_slice(&keys.as_ref()[32..])?, + }) + } +} + +#[derive(Debug, PartialEq)] +/// A key pair used to establish shared keys for a single session. +pub struct EphemeralServerSession { + private_key: PrivateKey, + public_key: PublicKey, +} + +impl EphemeralServerSession { + /// Generate a new random key pair. + pub fn new() -> Result<Self, UnknownCryptoError> { + let privkey = PrivateKey::generate(); + let pubkey: PublicKey = PublicKey::try_from(&privkey)?; + + Ok(Self { + private_key: privkey, + public_key: pubkey, + }) + } + + /// Get a reference to the [`PublicKey`]. + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } + + /// Get a reference to the [`PrivateKey`]. + pub fn private_key(&self) -> &PrivateKey { + &self.private_key + } + + /// Establish session keys with a client. This moves `self` to ensure that the keys + /// generated with [`Self::new()`] are only used for this key exchange, thus remaining ephemeral. + pub fn establish_with_client( + self, + client_public_key: &PublicKey, + ) -> Result<SessionKeys, UnknownCryptoError> { + let q = x25519::key_agreement(&self.private_key, client_public_key)?; + let keys = establish_session_keys(&q, client_public_key, &self.public_key)?; + + Ok(SessionKeys { + rx: SecretKey::from_slice(&keys.as_ref()[32..])?, + tx: SecretKey::from_slice(&keys.as_ref()[..32])?, + }) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, PartialEq)] +/// A set of shared secrets for either transmitting to this entity or send to another party. +pub struct SessionKeys { + rx: SecretKey, + tx: SecretKey, +} + +impl SessionKeys { + /// Get the shared secret intended to be used for receiving data from the other party. + pub fn receiving(&self) -> &SecretKey { + &self.rx + } + + /// Get the shared secret intended to be used for transporting data to the other party. + pub fn transport(&self) -> &SecretKey { + &self.tx + } +} + +/// Using BLAKE2b, derive two shared secret from a scalarmult computation. +fn establish_session_keys( + shared_secret: &x25519::SharedKey, + client_pk: &PublicKey, + server_pk: &PublicKey, +) -> Result<Digest, UnknownCryptoError> { + let mut ctx = Blake2b::new(64)?; + ctx.update(shared_secret.unprotected_as_bytes())?; + ctx.update(&client_pk.to_bytes())?; + ctx.update(&server_pk.to_bytes())?; + ctx.finalize() +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_basic_key_exchange() { + let session_server = EphemeralServerSession::new().unwrap(); + let server_public_key = session_server.public_key(); + + let session_client = EphemeralClientSession::new().unwrap(); + let client_public_key = session_client.public_key().clone(); + + assert_ne!(session_client.private_key(), session_server.private_key()); + + let client = session_client + .establish_with_server(server_public_key) + .unwrap(); + let server = session_server + .establish_with_client(&client_public_key) + .unwrap(); + + assert_eq!(client.receiving(), server.transport()); + assert_eq!(client.transport(), server.receiving()); + + assert_ne!(client.receiving(), server.receiving()); + assert_ne!(client.transport(), server.transport()); + } + + #[test] + fn test_error_on_low_order_public() { + // Taken from: https://github.com/jedisct1/libsodium/blob/master/test/default/kx.c + let low_order_public: [u8; 32] = [ + 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, + 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, + 0x5f, 0x49, 0xb8, 0x00, + ]; + let server_low_order_pk = PublicKey::from_slice(&low_order_public).unwrap(); + + let session_client = EphemeralClientSession::new().unwrap(); + assert!(session_client + .establish_with_server(&server_low_order_pk) + .is_err()); + } + + // The following are tests generated with sodiumoxide to test basic compatability with libsodium API. + #[test] + fn libsodium_compat_test_1() { + let client_pk = "299283d8713b7d430376cb257e13cd5ad1a6e5ebe6135417f4bb3b45bf42f31a"; + let client_sk = "e026533c3efa096ce9c4d77ad7c3d6948af2f9ef628b88430228ca0465ec35b9"; + let server_pk = "1716d0c006e5f3c2240b2ccec9357dbd04030f51d3e584923e70823cd6fcab1c"; + let server_sk = "55a94da5003d7807850938e84a5082d3deba8e5bbf5c50f814e8160270c165b4"; + let client_rx = "37830d33c5de06fbe246db5803ed70284fe9ab78bc6b896a3db3a9b8db50418b"; + let client_tx = "201d1bb45d4b9164f269d59cc00ba1a49c1924c27485bb6e5cc77ea4cc38ec7e"; + let server_rx = "201d1bb45d4b9164f269d59cc00ba1a49c1924c27485bb6e5cc77ea4cc38ec7e"; + let server_tx = "37830d33c5de06fbe246db5803ed70284fe9ab78bc6b896a3db3a9b8db50418b"; + + let client_public = PublicKey::from_slice(&hex::decode(client_pk).unwrap()).unwrap(); + let client_secret = PrivateKey::from_slice(&hex::decode(client_sk).unwrap()).unwrap(); + let server_public = PublicKey::from_slice(&hex::decode(server_pk).unwrap()).unwrap(); + let server_secret = PrivateKey::from_slice(&hex::decode(server_sk).unwrap()).unwrap(); + + let client_recv = SecretKey::from_slice(&hex::decode(client_rx).unwrap()).unwrap(); + let client_trans = SecretKey::from_slice(&hex::decode(client_tx).unwrap()).unwrap(); + let server_recv = SecretKey::from_slice(&hex::decode(server_rx).unwrap()).unwrap(); + let server_trans = SecretKey::from_slice(&hex::decode(server_tx).unwrap()).unwrap(); + + let session_client = EphemeralClientSession { + private_key: client_secret, + public_key: client_public.clone(), + }; + + let session_server = EphemeralServerSession { + private_key: server_secret, + public_key: server_public.clone(), + }; + + let expected_client_shared = SessionKeys { + rx: client_recv, + tx: client_trans, + }; + + let expected_server_shared = SessionKeys { + rx: server_recv, + tx: server_trans, + }; + + assert_eq!( + session_client + .establish_with_server(&server_public) + .unwrap(), + expected_client_shared + ); + assert_eq!( + session_server + .establish_with_client(&client_public) + .unwrap(), + expected_server_shared + ); + } + + #[test] + fn libsodium_compat_test_2() { + let client_pk = "31f8eed74832d31b106702ecdf464a54e9fb9514d241473c5b51deb0d126893a"; + let client_sk = "dcb760f46480ea5c2647e8dcbdc30fff8da6811712b4d5c7144aca1b72d6adb7"; + let server_pk = "ccf7a7d5d2973f517a2276f0ca6c15da3c90a85db12e3ec171c4441c2b48f15d"; + let server_sk = "e73ebfde3907296e5452e1f22ea1a85d4f3cdbf3ff9f099d45a0853d3b87d64f"; + let client_rx = "25789992d2eac8bc0e1c3322d9b8e26050064ea3cead77ca2cf36966dea54186"; + let client_tx = "f69cf60f763fb2a9c47dc1b3237983ef79cecd26205c68f9c16e91db6c8f3f18"; + let server_rx = "f69cf60f763fb2a9c47dc1b3237983ef79cecd26205c68f9c16e91db6c8f3f18"; + let server_tx = "25789992d2eac8bc0e1c3322d9b8e26050064ea3cead77ca2cf36966dea54186"; + + let client_public = PublicKey::from_slice(&hex::decode(client_pk).unwrap()).unwrap(); + let client_secret = PrivateKey::from_slice(&hex::decode(client_sk).unwrap()).unwrap(); + let server_public = PublicKey::from_slice(&hex::decode(server_pk).unwrap()).unwrap(); + let server_secret = PrivateKey::from_slice(&hex::decode(server_sk).unwrap()).unwrap(); + + let client_recv = SecretKey::from_slice(&hex::decode(client_rx).unwrap()).unwrap(); + let client_trans = SecretKey::from_slice(&hex::decode(client_tx).unwrap()).unwrap(); + let server_recv = SecretKey::from_slice(&hex::decode(server_rx).unwrap()).unwrap(); + let server_trans = SecretKey::from_slice(&hex::decode(server_tx).unwrap()).unwrap(); + + let session_client = EphemeralClientSession { + private_key: client_secret, + public_key: client_public.clone(), + }; + + let session_server = EphemeralServerSession { + private_key: server_secret, + public_key: server_public.clone(), + }; + + let expected_client_shared = SessionKeys { + rx: client_recv, + tx: client_trans, + }; + + let expected_server_shared = SessionKeys { + rx: server_recv, + tx: server_trans, + }; + + assert_eq!( + session_client + .establish_with_server(&server_public) + .unwrap(), + expected_client_shared + ); + assert_eq!( + session_server + .establish_with_client(&client_public) + .unwrap(), + expected_server_shared + ); + } + + #[test] + fn libsodium_compat_test_3() { + let client_pk = "57f57eda618289c8f55dee0a8069405d9874684282b8380878b180719055333a"; + let client_sk = "7d613b91a1cf6a34787362229cfaf6d50613e276a20ef59eea7f02d9236cd9f7"; + let server_pk = "997f12b2ef5ba2c639c4dc39f159ce5169b60b9a3b65365f958cfb822e37b513"; + let server_sk = "c3156f11e0cc31ffb92dfd5e780738011cfe80cc4184f5a3f190892528a9bac3"; + let client_rx = "89f90402d56d5e184b1682c21583e695560e0ab54459d09a51a596a8d33293da"; + let client_tx = "547c1f1be7abe8d10bf92fb19f79edd2139441b4faa54976b5db90a50b7244c4"; + let server_rx = "547c1f1be7abe8d10bf92fb19f79edd2139441b4faa54976b5db90a50b7244c4"; + let server_tx = "89f90402d56d5e184b1682c21583e695560e0ab54459d09a51a596a8d33293da"; + + let client_public = PublicKey::from_slice(&hex::decode(client_pk).unwrap()).unwrap(); + let client_secret = PrivateKey::from_slice(&hex::decode(client_sk).unwrap()).unwrap(); + let server_public = PublicKey::from_slice(&hex::decode(server_pk).unwrap()).unwrap(); + let server_secret = PrivateKey::from_slice(&hex::decode(server_sk).unwrap()).unwrap(); + + let client_recv = SecretKey::from_slice(&hex::decode(client_rx).unwrap()).unwrap(); + let client_trans = SecretKey::from_slice(&hex::decode(client_tx).unwrap()).unwrap(); + let server_recv = SecretKey::from_slice(&hex::decode(server_rx).unwrap()).unwrap(); + let server_trans = SecretKey::from_slice(&hex::decode(server_tx).unwrap()).unwrap(); + + let session_client = EphemeralClientSession { + private_key: client_secret, + public_key: client_public.clone(), + }; + + let session_server = EphemeralServerSession { + private_key: server_secret, + public_key: server_public.clone(), + }; + + let expected_client_shared = SessionKeys { + rx: client_recv, + tx: client_trans, + }; + + let expected_server_shared = SessionKeys { + rx: server_recv, + tx: server_trans, + }; + + assert_eq!( + session_client + .establish_with_server(&server_public) + .unwrap(), + expected_client_shared + ); + assert_eq!( + session_server + .establish_with_client(&client_public) + .unwrap(), + expected_server_shared + ); + } + + #[test] + fn libsodium_compat_test_4() { + let client_pk = "2df30ccfd5eb6cb1ae5428dd06129a22fe8eac2b8b0cfcc1876bbaeb2b515703"; + let client_sk = "52b73937f462130c82c427d68b26b689d3c020169909fea3043882654fa8e1a3"; + let server_pk = "ef09a7379139627b2a13d0376f7fea1e4e2c27859757b74282b4368d2701de1c"; + let server_sk = "384de0b04d358ef0a99cec457507b83a9fcff0c9a2875d1fc771c1b203eb90f5"; + let client_rx = "738d3ff37e8b5d58daf888111359693042508617ef088c2048c0d87bc002ca38"; + let client_tx = "fd1ab19e5c6ac0c5508ba129ded170a25c04f6f1ab9ccc3e66cd73988ade8471"; + let server_rx = "fd1ab19e5c6ac0c5508ba129ded170a25c04f6f1ab9ccc3e66cd73988ade8471"; + let server_tx = "738d3ff37e8b5d58daf888111359693042508617ef088c2048c0d87bc002ca38"; + + let client_public = PublicKey::from_slice(&hex::decode(client_pk).unwrap()).unwrap(); + let client_secret = PrivateKey::from_slice(&hex::decode(client_sk).unwrap()).unwrap(); + let server_public = PublicKey::from_slice(&hex::decode(server_pk).unwrap()).unwrap(); + let server_secret = PrivateKey::from_slice(&hex::decode(server_sk).unwrap()).unwrap(); + + let client_recv = SecretKey::from_slice(&hex::decode(client_rx).unwrap()).unwrap(); + let client_trans = SecretKey::from_slice(&hex::decode(client_tx).unwrap()).unwrap(); + let server_recv = SecretKey::from_slice(&hex::decode(server_rx).unwrap()).unwrap(); + let server_trans = SecretKey::from_slice(&hex::decode(server_tx).unwrap()).unwrap(); + + let session_client = EphemeralClientSession { + private_key: client_secret, + public_key: client_public.clone(), + }; + + let session_server = EphemeralServerSession { + private_key: server_secret, + public_key: server_public.clone(), + }; + + let expected_client_shared = SessionKeys { + rx: client_recv, + tx: client_trans, + }; + + let expected_server_shared = SessionKeys { + rx: server_recv, + tx: server_trans, + }; + + assert_eq!( + session_client + .establish_with_server(&server_public) + .unwrap(), + expected_client_shared + ); + assert_eq!( + session_server + .establish_with_client(&client_public) + .unwrap(), + expected_server_shared + ); + } + + #[test] + fn libsodium_compat_test_5() { + let client_pk = "c4be3dfb50430e57313b6eeafc40e9b432120c4dd3d34ca6dedae1391b898c43"; + let client_sk = "23702adbbab5918b682b2a1b2b27c634865b3dcf51ed81e287da30edd4f7ef39"; + let server_pk = "a241a5a8c16e3731ddc8d3e5e5890f85b9de2c87095be3239379b3c62f73f949"; + let server_sk = "530493dddf346371ce562557d5b0ff40ddbff038cf6a20187c16510ce57cc93f"; + let client_rx = "8fa8dfc483108262b058b60b11e2f9b5b47287061bde785827afafb102a09ec7"; + let client_tx = "a01332e4cb85b2bfac65f86936f27058b339889442c13eee06414bfb2d68c58b"; + let server_rx = "a01332e4cb85b2bfac65f86936f27058b339889442c13eee06414bfb2d68c58b"; + let server_tx = "8fa8dfc483108262b058b60b11e2f9b5b47287061bde785827afafb102a09ec7"; + + let client_public = PublicKey::from_slice(&hex::decode(client_pk).unwrap()).unwrap(); + let client_secret = PrivateKey::from_slice(&hex::decode(client_sk).unwrap()).unwrap(); + let server_public = PublicKey::from_slice(&hex::decode(server_pk).unwrap()).unwrap(); + let server_secret = PrivateKey::from_slice(&hex::decode(server_sk).unwrap()).unwrap(); + + let client_recv = SecretKey::from_slice(&hex::decode(client_rx).unwrap()).unwrap(); + let client_trans = SecretKey::from_slice(&hex::decode(client_tx).unwrap()).unwrap(); + let server_recv = SecretKey::from_slice(&hex::decode(server_rx).unwrap()).unwrap(); + let server_trans = SecretKey::from_slice(&hex::decode(server_tx).unwrap()).unwrap(); + + let session_client = EphemeralClientSession { + private_key: client_secret, + public_key: client_public.clone(), + }; + + let session_server = EphemeralServerSession { + private_key: server_secret, + public_key: server_public.clone(), + }; + + let expected_client_shared = SessionKeys { + rx: client_recv, + tx: client_trans, + }; + + let expected_server_shared = SessionKeys { + rx: server_recv, + tx: server_trans, + }; + + assert_eq!( + session_client + .establish_with_server(&server_public) + .unwrap(), + expected_client_shared + ); + assert_eq!( + session_server + .establish_with_client(&client_public) + .unwrap(), + expected_server_shared + ); + } +} diff --git a/vendor/orion/src/high_level/mod.rs b/vendor/orion/src/high_level/mod.rs new file mode 100644 index 0000000..25080e8 --- /dev/null +++ b/vendor/orion/src/high_level/mod.rs @@ -0,0 +1,29 @@ +// MIT License + +// Copyright (c) 2020-2023 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. + +pub mod aead; +pub mod auth; +pub mod hash; +mod hltypes; +pub mod kdf; +pub mod kex; +pub mod pwhash; diff --git a/vendor/orion/src/high_level/pwhash.rs b/vendor/orion/src/high_level/pwhash.rs new file mode 100644 index 0000000..3daee39 --- /dev/null +++ b/vendor/orion/src/high_level/pwhash.rs @@ -0,0 +1,990 @@ +// MIT License + +// Copyright (c) 2020-2023 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. + +//! Password hashing and verification. +//! +//! # Use case: +//! `orion::pwhash` is suitable for securely storing passwords. +//! +//! An example of this would be needing to store user passwords (from a sign-up +//! at a webstore) in a server database, +//! where a potential disclosure of the data in this database should not result +//! in the user's actual passwords being disclosed as well. +//! +//! # About: +//! - Uses Argon2i. +//! - A salt of 16 bytes is automatically generated. +//! - The password hash length is set to 32. +//! +//! [`PasswordHash`] provides two ways of retrieving the hashed password: +//! - [`PasswordHash::unprotected_as_encoded()`] returns the hashed password in an encoded form. +//! The encoding specifies the settings used to hash the password. +//! - [`PasswordHash::unprotected_as_bytes()`] returns only the hashed password in raw bytes. +//! +//! The following is an example of how the encoded password hash might look: +//! ```text +//! $argon2i$v=19$m=8192,t=3,p=1$c21hbGxzYWx0$lmO1aPPy3x0CcvrKpFLi1TL/uSVJ/eO5hPHiWZFaWvY +//! ``` +//! +//! See a more detailed description of the [encoding format here]. +//! +//! # Note: +//! This implementation only supports a single thread/lane. +//! +//! # Parameters: +//! - `password`: The password to be hashed. +//! - `expected`: The expected password hash. +//! - `iterations`: Iterations cost parameter for Argon2i. +//! - `memory`: Memory (in kibibytes (KiB)) cost parameter for Argon2i. +//! +//! # Errors: +//! An error will be returned if: +//! - `memory` is less than 8. +//! - `iterations` is less than 3. +//! - The length of the `password` is greater than [`isize::MAX`]. +//! - The password hash does not match `expected`. +//! +//! # Panics: +//! A panic will occur if: +//! - Failure to generate random bytes securely. +//! +//! # Security: +//! - [`PasswordHash::unprotected_as_encoded()`] and [`PasswordHash::unprotected_as_bytes()`] should never +//! be used to compare password hashes, as these will not run in constant-time. +//! Either use [`hash_password_verify()`] or compare two [`PasswordHash`]es. +//! - Choosing the correct cost parameters is important for security. Please refer to [libsodium's docs] +//! for a description of how to do this. +//! +//! If the concrete cost parameters needed are unclear, please refer to [OWASP] for recommended minimum values. +//! +//! # Example: +//! ```rust +//! use orion::pwhash; +//! +//! let password = pwhash::Password::from_slice(b"Secret password")?; +//! +//! let hash = pwhash::hash_password(&password, 3, 1<<16)?; +//! assert!(pwhash::hash_password_verify(&hash, &password).is_ok()); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [encoding format here]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md +//! [libsodium's docs]: https://download.libsodium.org/doc/password_hashing/default_phf#guidelines-for-choosing-the-parameters +//! [OWASP]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use super::hltypes::Password; +use super::hltypes::Salt; +use crate::{ + errors::UnknownCryptoError, + hazardous::kdf::argon2i::{self, LANES, MIN_MEMORY}, +}; +use ct_codecs::{Base64NoPadding, Decoder, Encoder}; +use zeroize::Zeroizing; + +#[cfg(feature = "serde")] +use serde::{ + de::{self, Deserialize, Deserializer}, + ser::{Serialize, Serializer}, +}; + +/// The length of the salt used for password hashing. +pub const SALT_LENGTH: usize = 16; + +/// The length of the hashed password. +pub const PWHASH_LENGTH: usize = 32; + +/// Minimum amount of iterations. +pub(crate) const MIN_ITERATIONS: u32 = 3; + +/// A type to represent the `PasswordHash` that Argon2i returns when used for password hashing. +/// +/// +/// # Errors: +/// An error will be returned if: +/// - The encoded password hash contains whitespace. +/// - The encoded password hash has a parallelism count other than 1. +/// - The encoded password contains any other fields than: The algorithm name, +/// version, m, t, p and the salt and password hash. +/// - The encoded password hash contains invalid Base64 encoding. +/// - Any decimal parameter value, such as m, contains leading zeroes and is longer +/// than a single character. +/// - `iterations` is less than 3. +/// - `memory` is less than 8. +/// - `password` is not 32 bytes. +/// - `salt` is not 16 bytes. +/// - The encoded password hash contains numerical values that cannot +/// be represented as a `u32`. +/// - The encoded password hash length is less than [`PasswordHash::MIN_ENCODED_LEN`] or greater than [`PasswordHash::MAX_ENCODED_LEN`]. +/// - The parameters in the encoded password hash are not correctly ordered. The ordering must be: +/// `$argon2i$v=19$m=<value>,t=<value>,p=<value>$<salt>$<hash>` +/// # Panics: +/// A panic will occur if: +/// - Overflowing calculations happen on `usize` when decoding the password and salt from Base64. +/// +/// # Security: +/// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections +/// that the type implements. +/// - Never use `unprotected_as_bytes()` or `unprotected_as_encoded()` to compare password hashes, +/// as that will not run in constant-time. Compare `PasswordHash`es directly using `==` instead. +/// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted +/// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait +/// is implemented in such a way that the comparison happens in constant time. Thus, users should +/// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. +/// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. +/// ```rust +/// use orion::hazardous::mac::hmac::sha512::Tag; +/// # use orion::errors::UnknownCryptoError; +/// +/// # fn main() -> Result<(), Box<UnknownCryptoError>> { +/// // Initialize an arbitrary, 64-byte tag. +/// let tag = Tag::from_slice(&[1; 64])?; +/// +/// // Secure, constant-time comparison with a byte slice +/// assert_eq!(tag, &[1; 64][..]); +/// +/// // Secure, constant-time comparison with another Tag +/// assert_eq!(tag, Tag::from_slice(&[1; 64])?); +/// # Ok(()) +/// # } +/// ``` +pub struct PasswordHash { + encoded_password_hash: String, + password_hash: Vec<u8>, + salt: Salt, + iterations: u32, + memory: u32, +} + +#[allow(clippy::len_without_is_empty)] +impl PasswordHash { + /// Given a 16-byte salt (22 characters encoded) and 32-byte password hash (43 characters encoded), + /// and parameters (m, t) in decimal representation of 1..10 in length, 92 is the minimum length for an encoded password hash. + pub const MIN_ENCODED_LEN: usize = 92; + + /// Given a 16-byte salt (22 characters encoded) and 32-byte password hash (43 characters encoded), + /// and parameters (m, t) in decimal representation of 1..10 in length, 110 is the maximum length for an encoded password hash. + pub const MAX_ENCODED_LEN: usize = 110; + + /// Parse a decimal parameter value to a u32. Returns an error on overflow + /// and if the value has leading zeroes. + fn parse_decimal_value(value: &str) -> Result<u32, UnknownCryptoError> { + // See: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#decimal-encoding + if value.len() > 1 && value.starts_with('0') { + return Err(UnknownCryptoError); + } + // .parse::<T>() detects overflows (in debug and release builds) + // and rejects empty strings. If the value contains spaces, parsing + // also fails. + Ok(value.parse::<u32>()?) + } + + /// Encode password hash, salt and parameters for storage. + fn encode( + password_hash: &[u8], + salt: &[u8], + iterations: u32, + memory: u32, + ) -> Result<String, UnknownCryptoError> { + Ok(format!( + "$argon2i$v=19$m={},t={},p=1${}${}", + memory, + iterations, + Base64NoPadding::encode_to_string(salt)?, + Base64NoPadding::encode_to_string(password_hash)?, + )) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from given byte slice and parameters. + pub fn from_slice( + password_hash: &[u8], + salt: &[u8], + iterations: u32, + memory: u32, + ) -> Result<Self, UnknownCryptoError> { + if password_hash.len() != PWHASH_LENGTH { + return Err(UnknownCryptoError); + } + if salt.len() != SALT_LENGTH { + return Err(UnknownCryptoError); + } + if iterations < MIN_ITERATIONS { + return Err(UnknownCryptoError); + } + if memory < MIN_MEMORY { + return Err(UnknownCryptoError); + } + + let encoded_password_hash = Self::encode(password_hash, salt, iterations, memory)?; + + Ok(Self { + encoded_password_hash, + password_hash: password_hash.into(), + salt: Salt::from_slice(salt)?, + iterations, + memory, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from encoded password hash. + pub fn from_encoded(password_hash: &str) -> Result<Self, UnknownCryptoError> { + if password_hash.len() > Self::MAX_ENCODED_LEN + || password_hash.len() < Self::MIN_ENCODED_LEN + { + return Err(UnknownCryptoError); + } + + if password_hash.contains(' ') { + return Err(UnknownCryptoError); + } + + let parts_split = password_hash.split('$').collect::<Vec<&str>>(); + if parts_split.len() != 6 { + return Err(UnknownCryptoError); + } + let mut parts = parts_split.into_iter(); + if parts.next() != Some("") { + return Err(UnknownCryptoError); + } + if parts.next() != Some("argon2i") { + return Err(UnknownCryptoError); + } + if parts.next() != Some("v=19") { + return Err(UnknownCryptoError); + } + + // Splits as ["m", "X", "t", "Y", "p", "Z"] where m=X, t=Y and p=Z. + let param_parts_split = parts + .next() + .unwrap() + .split(|v| v == '=' || v == ',') + .collect::<Vec<&str>>(); + if param_parts_split.len() != 6 { + return Err(UnknownCryptoError); + } + let mut param_parts = param_parts_split.into_iter(); + + if param_parts.next() != Some("m") { + return Err(UnknownCryptoError); + } + + let memory = Self::parse_decimal_value(param_parts.next().unwrap())?; + if memory < MIN_MEMORY { + return Err(UnknownCryptoError); + } + + if param_parts.next() != Some("t") { + return Err(UnknownCryptoError); + } + let iterations = Self::parse_decimal_value(param_parts.next().unwrap())?; + if iterations < MIN_ITERATIONS { + return Err(UnknownCryptoError); + } + + if param_parts.next() != Some("p") { + return Err(UnknownCryptoError); + } + let lanes = Self::parse_decimal_value(param_parts.next().unwrap())?; + if lanes != LANES { + return Err(UnknownCryptoError); + } + + let salt = Base64NoPadding::decode_to_vec(parts.next().unwrap(), None)?; + if salt.len() != SALT_LENGTH { + return Err(UnknownCryptoError); + } + let password_hash_raw = Base64NoPadding::decode_to_vec(parts.next().unwrap(), None)?; + if password_hash_raw.len() != PWHASH_LENGTH { + return Err(UnknownCryptoError); + } + + Ok(Self { + encoded_password_hash: password_hash.into(), + password_hash: password_hash_raw, + salt: Salt::from_slice(&salt)?, + iterations, + memory, + }) + } + + #[inline] + /// Return encoded password hash. __**Warning**__: Should not be used to verify + /// password hashes. This __**breaks protections**__ that the type implements. + pub fn unprotected_as_encoded(&self) -> &str { + self.encoded_password_hash.as_ref() + } + + #[inline] + /// Return the password hash as byte slice. __**Warning**__: Should not be used unless strictly + /// needed. This __**breaks protections**__ that the type implements. + pub fn unprotected_as_bytes(&self) -> &[u8] { + self.password_hash.as_ref() + } + + #[inline] + /// Return the length of the password hash. + pub fn len(&self) -> usize { + self.password_hash.len() + } + + #[inline] + /// Return `true` if the password hash is empty, `false` otherwise. + /// + /// __NOTE__: This method should always return `false`, since there shouldn't be a way + /// to create an empty password hash. + pub fn is_empty(&self) -> bool { + debug_assert_eq!(self.encoded_password_hash.is_empty(), self.password_hash.is_empty(), + "Both the encoded password hash and the raw hash must be non-empty or empty at the same time."); + self.password_hash.is_empty() + } +} + +impl core::fmt::Debug for PasswordHash { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "PasswordHash {{ encoded_password_hash: [***OMITTED***], password_hash: [***OMITTED***], iterations: \ + {:?}, memory: {:?} }}", + self.iterations, self.memory + ) + } +} + +impl_ct_partialeq_trait!(PasswordHash, unprotected_as_bytes); + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +/// `PasswordHash` serializes as would a [`String`](std::string::String). Note that +/// the serialized type likely does not have the same protections that Orion +/// provides, such as constant-time operations. A good rule of thumb is to only +/// serialize these types for storage. Don't operate on the serialized types. +impl Serialize for PasswordHash { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let encoded_string = self.unprotected_as_encoded(); + serializer.serialize_str(encoded_string) + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +/// `PasswordHash` deserializes from a [`String`](std::string::String). +impl<'de> Deserialize<'de> for PasswordHash { + fn deserialize<D>(deserializer: D) -> Result<PasswordHash, D::Error> + where + D: Deserializer<'de>, + { + let encoded_str = String::deserialize(deserializer)?; + PasswordHash::from_encoded(&encoded_str).map_err(de::Error::custom) + } +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Hash a password using Argon2i. +pub fn hash_password( + password: &Password, + iterations: u32, + memory: u32, +) -> Result<PasswordHash, UnknownCryptoError> { + if iterations < MIN_ITERATIONS { + return Err(UnknownCryptoError); + } + + // Cannot panic as this is a valid size. + let salt = Salt::generate(SALT_LENGTH).unwrap(); + let mut buffer = Zeroizing::new([0u8; PWHASH_LENGTH]); + + argon2i::derive_key( + password.unprotected_as_bytes(), + salt.as_ref(), + iterations, + memory, + None, + None, + buffer.as_mut(), + )?; + + PasswordHash::from_slice(buffer.as_ref(), salt.as_ref(), iterations, memory) +} + +/// Hash and verify a password using Argon2i. The Argon2i parameters `iterations` +/// and `memory` will be pulled from the `expected: &PasswordHash` argument. If +/// you want to manually specify the iterations and memory for Argon2i to use in +/// hashing the `password` argument, see the +/// [`hazardous::kdf`](crate::hazardous::kdf::argon2i) module. +/// +/// # Example: +/// ```rust +/// use orion::pwhash; +/// +/// let password = pwhash::Password::from_slice(b"Secret password")?; +/// let wrong_password = pwhash::Password::from_slice(b"hunter2")?; +/// +/// // Pretend these are stored somewhere and out-of-mind, e.g. in a database. +/// let hash1 = pwhash::hash_password(&password, 3, 1<<15)?; +/// let hash2 = pwhash::hash_password(&password, 4, 2<<15)?; +/// +/// // We don't have to remember which password used what parameters when it's +/// // time to verify them. Both will correctly return `Ok(())`. +/// assert!(pwhash::hash_password_verify(&hash1, &password).is_ok()); +/// assert!(pwhash::hash_password_verify(&hash2, &password).is_ok()); +/// +/// // The only way to get a failing result is to use the wrong password. +/// assert!(pwhash::hash_password_verify(&hash1, &wrong_password).is_err()); +/// # Ok::<(), orion::errors::UnknownCryptoError>(()) +/// ``` +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +pub fn hash_password_verify( + expected: &PasswordHash, + password: &Password, +) -> Result<(), UnknownCryptoError> { + let mut buffer = Zeroizing::new([0u8; PWHASH_LENGTH]); + + argon2i::verify( + expected.unprotected_as_bytes(), + password.unprotected_as_bytes(), + expected.salt.as_ref(), + expected.iterations, + expected.memory, + None, + None, + buffer.as_mut(), + ) +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let valid = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let password_hash = PasswordHash::from_encoded(valid).unwrap(); + let debug = format!("{:?}", password_hash); + let expected = "PasswordHash { encoded_password_hash: [***OMITTED***], password_hash: [***OMITTED***], iterations: 3, memory: 65536 }"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "serde")] + mod test_serde_impls { + use super::*; + + #[test] + fn test_valid_deserialization() { + let encoded_hash = "$argon2i$v=19$m=65536,t=3,p=1$c29tZXNhbHRzb21lc2FsdA$fRsRY9PAt5H+qAKuXRzL0/6JbFShsCd62W5aHzESk/c"; + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + let deserialized: PasswordHash = + serde_json::from_str(format!("\"{}\"", encoded_hash).as_str()).unwrap(); + assert_eq!(deserialized, expected); + } + + #[test] + fn test_valid_serialization() { + let encoded_hash = "$argon2i$v=19$m=65536,t=3,p=1$c29tZXNhbHRzb21lc2FsdA$fRsRY9PAt5H+qAKuXRzL0/6JbFShsCd62W5aHzESk/c"; + let hash = PasswordHash::from_encoded(encoded_hash).unwrap(); + let serialized: String = serde_json::to_string(&hash).unwrap(); + assert_eq!(serialized, format!("\"{}\"", encoded_hash)); + } + } + + /// The tests herein were generated with the CLI tool from the reference implementation at: + /// https://github.com/P-H-C/phc-winner-argon2/commit/62358ba2123abd17fccf2a108a301d4b52c01a7c + mod test_encoding_from_ref { + use super::*; + use hex; + + #[test] + fn test_encoding_and_verify_1() { + let password = Password::from_slice(b"password").unwrap(); + let raw_hash = + hex::decode("7d1b1163d3c0b791fea802ae5d1ccbd3fe896c54a1b0277ad96e5a1f311293f7") + .unwrap(); + let encoded_hash = "$argon2i$v=19$m=65536,t=3,p=1$c29tZXNhbHRzb21lc2FsdA$fRsRY9PAt5H+qAKuXRzL0/6JbFShsCd62W5aHzESk/c"; + + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + assert_eq!(expected.unprotected_as_bytes(), &raw_hash[..]); + assert!(hash_password_verify(&expected, &password).is_ok()); + } + + #[test] + fn test_encoding_and_verify_2() { + let password = Password::from_slice(b"passwordPASSWORDPassword").unwrap(); + let raw_hash = + hex::decode("ed4b0fd657e165f9ffe90f66ff315fbec878e629f03b2d6468d4b17a50c796aa") + .unwrap(); + let encoded_hash = "$argon2i$v=19$m=65536,t=3,p=1$c29tZXNhbHRzb21lc2FsdA$7UsP1lfhZfn/6Q9m/zFfvsh45inwOy1kaNSxelDHlqo"; + + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + assert_eq!(expected.unprotected_as_bytes(), &raw_hash[..]); + assert!(hash_password_verify(&expected, &password).is_ok()); + } + + #[test] + fn test_encoding_and_verify_3() { + // Different salt from test 2 + let password = Password::from_slice(b"passwordPASSWORDPassword").unwrap(); + let raw_hash = + hex::decode("fa9ea96fecd0998251d698c1303edda4df3889a39bfa87cd5e7b8656ef61b510") + .unwrap(); + let encoded_hash = "$argon2i$v=19$m=65536,t=3,p=1$U29tZVNhbHRTb21lU2FsdA$+p6pb+zQmYJR1pjBMD7dpN84iaOb+ofNXnuGVu9htRA"; + + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + assert_eq!(expected.unprotected_as_bytes(), &raw_hash[..]); + assert!(hash_password_verify(&expected, &password).is_ok()); + } + + #[test] + fn test_encoding_and_verify_4() { + let password = Password::from_slice(b"passwordPASSWORDPassword").unwrap(); + let raw_hash = + hex::decode("fb3e0cdf7b10970bf6711c151861851566006f8986c9109ba2cdd5d98f9ca9d7") + .unwrap(); + let encoded_hash = "$argon2i$v=19$m=256,t=3,p=1$c29tZXNhbHRzb21lc2FsdA$+z4M33sQlwv2cRwVGGGFFWYAb4mGyRCbos3V2Y+cqdc"; + + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + assert_eq!(expected.unprotected_as_bytes(), &raw_hash[..]); + assert!(hash_password_verify(&expected, &password).is_ok()); + } + + #[test] + fn test_encoding_and_verify_5() { + let password = Password::from_slice(b"passwordPASSWORDPassword").unwrap(); + let raw_hash = + hex::decode("9b134c4c1c34e66170d1088c18be3a8e0f4a1837d4c069703ce62f85248b1e8f") + .unwrap(); + let encoded_hash = "$argon2i$v=19$m=256,t=4,p=1$c29tZXNhbHRzb21lc2FsdA$mxNMTBw05mFw0QiMGL46jg9KGDfUwGlwPOYvhSSLHo8"; + + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + assert_eq!(expected.unprotected_as_bytes(), &raw_hash[..]); + assert!(hash_password_verify(&expected, &password).is_ok()); + } + } + + mod test_password_hash { + use super::*; + + #[test] + fn test_password_hash_eq() { + let password_hash = + PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 1 << 16).unwrap(); + assert_eq!(password_hash.len(), 32); + assert_eq!(password_hash.unprotected_as_bytes(), &[0u8; 32]); + + let password_hash_again = + PasswordHash::from_encoded(password_hash.unprotected_as_encoded()).unwrap(); + assert_eq!(password_hash, password_hash_again); + } + + #[test] + fn test_password_hash_ne() { + let password_hash = + PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 1 << 16).unwrap(); + assert_eq!(password_hash.len(), 32); + assert_eq!(password_hash.unprotected_as_bytes(), &[0u8; 32]); + + let password_hash_again = + PasswordHash::from_slice(&[1u8; 32], &[0u8; 16], 3, 1 << 16).unwrap(); + + assert_ne!(password_hash, password_hash_again); + } + + #[test] + fn test_valid_encoded_password() { + let valid = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + assert!(PasswordHash::from_encoded(valid).is_ok()); + } + + #[test] + fn test_bad_encoding_missing_dollar() { + let first_missing = "argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let second_missing = "$argon2iv=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let third_missing = "$argon2i$v=19m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let fourth_missing = "$argon2i$v=19$m=65536,t=3,p=1cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let fifth_missing = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(first_missing).is_err()); + assert!(PasswordHash::from_encoded(second_missing).is_err()); + assert!(PasswordHash::from_encoded(third_missing).is_err()); + assert!(PasswordHash::from_encoded(fourth_missing).is_err()); + assert!(PasswordHash::from_encoded(fifth_missing).is_err()); + } + + #[test] + fn test_bad_encoding_missing_comma() { + let first_missing = "$argon2i$v=19$m=65536t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let second_missing = "$argon2i$v=19$m=65536,t=3p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(first_missing).is_err()); + assert!(PasswordHash::from_encoded(second_missing).is_err()); + } + + #[test] + fn test_bad_encoding_missing_equals() { + let first_missing = "$argon2i$v19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let second_missing = "$argon2$iv=19$m65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let third_missing = "$argon2i$v=19$m=65536,t3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let fourth_missing = "$argon2i$v=19$m=65536,t=3,p1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(first_missing).is_err()); + assert!(PasswordHash::from_encoded(second_missing).is_err()); + assert!(PasswordHash::from_encoded(third_missing).is_err()); + assert!(PasswordHash::from_encoded(fourth_missing).is_err()); + } + + #[test] + fn test_bad_encoding_whitespace() { + let first = "$argon2i$v=19$m=65536,t=3, p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let second = " $argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let third = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA "; + + assert!(PasswordHash::from_encoded(first).is_err()); + assert!(PasswordHash::from_encoded(second).is_err()); + assert!(PasswordHash::from_encoded(third).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_threads() { + let one = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let zero = "$argon2i$v=19$m=65536,t=3,p=0$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let two = "$argon2i$v=19$m=65536,t=3,p=2$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(one).is_ok()); + assert!(PasswordHash::from_encoded(zero).is_err()); + assert!(PasswordHash::from_encoded(two).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_memory() { + let exact_min = "$argon2i$v=19$m=8,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let less = "$argon2i$v=19$m=7,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + // Throws error during parsing as u32 + let u32_overflow = format!("$argon2i$v=19$m={},t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA", u64::MAX); + + assert!(PasswordHash::from_encoded(exact_min).is_ok()); + assert!(PasswordHash::from_encoded(less).is_err()); + assert!(PasswordHash::from_encoded(&u32_overflow).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_iterations() { + let exact_min = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let less = "$argon2i$v=19$m=65536,t=2,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + // Throws error during parsing as u32 + let u32_overflow = format!("$argon2i$v=19$m=65536,t={},p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA", u64::MAX); + + assert!(PasswordHash::from_encoded(exact_min).is_ok()); + assert!(PasswordHash::from_encoded(less).is_err()); + assert!(PasswordHash::from_encoded(&u32_overflow).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_algo() { + let argon2id = "$argon2id$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let argon2d = "$argon2d$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let nothing = "$$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(argon2d).is_err()); + assert!(PasswordHash::from_encoded(argon2id).is_err()); + assert!(PasswordHash::from_encoded(nothing).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_version() { + let v13 = "$argon2i$v=13$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let v0 = "$argon2i$v=0$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let nothing = "$argon2i$v=$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(v13).is_err()); + assert!(PasswordHash::from_encoded(v0).is_err()); + assert!(PasswordHash::from_encoded(nothing).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_order() { + let version_first = "$v=19$argon2i$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let t_before_m = "$argon2i$v=19$t=3,m=65536,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let p_before_t = "$argon2i$v=19$m=65536,p=1,t=3$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let p_before_m = "$argon2i$v=19$p=1,m=65536,t=3$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let pass_before_salt = "$argon2i$v=19$m=65536,t=3,p=1$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA$cHBwcHBwcHBwcHBwcHBwcA"; + let salt_first = "$cHBwcHBwcHBwcHBwcHBwcA$argon2i$v=19$m=65536,t=3,p=1$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let pass_first = "$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA"; + + assert!(PasswordHash::from_encoded(version_first).is_err()); + assert!(PasswordHash::from_encoded(t_before_m).is_err()); + assert!(PasswordHash::from_encoded(p_before_t).is_err()); + assert!(PasswordHash::from_encoded(p_before_m).is_err()); + assert!(PasswordHash::from_encoded(pass_before_salt).is_err()); + assert!(PasswordHash::from_encoded(salt_first).is_err()); + assert!(PasswordHash::from_encoded(pass_first).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_salt() { + let exact = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let nothing = + "$argon2i$v=19$m=65536,t=3,p=1$$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let above = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcAA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(exact).is_ok()); + assert!(PasswordHash::from_encoded(nothing).is_err()); + assert!(PasswordHash::from_encoded(above).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_password() { + let exact = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let nothing = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$"; + let above = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAA"; + + assert!(PasswordHash::from_encoded(exact).is_ok()); + assert!(PasswordHash::from_encoded(nothing).is_err()); + assert!(PasswordHash::from_encoded(above).is_err()); + } + + #[test] + fn test_bad_encoding_bad_parsing_integers() { + let j_instead_of_mem = "$argon2i$v=19$m=j,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(j_instead_of_mem).is_err()); + } + + #[test] + fn test_bad_encoding_first_not_empty() { + // Nothing should precede "$argon2i" + let non_empty_first = "apples$argon2i$v=19$m=4096,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(non_empty_first).is_err()); + } + + #[test] + fn test_bad_encoding_bad_p() { + let p_is_j = "$argon2i$v=19$m=4096,t=3,j=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let p_gone = "$argon2i$v=19$m=4096,t=3,=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(p_is_j).is_err()); + assert!(PasswordHash::from_encoded(p_gone).is_err()); + } + + #[test] + fn test_decimal_value_reject_leading_zeroes() { + // https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#decimal-encoding + // According to the specification, the decimal parameters may not start with 0, if there is more than + // one character in the string. .parse::<u32>() will ignore leading 0's, so it will parse "0032" -> 32u32. + // Test here that these cases are detected and rejected by returning an error. + let valid = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let invalid0 = "$argon2i$v=019$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let invalid1 = "$argon2i$v=19$m=065536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let invalid2 = "$argon2i$v=19$m=65536,t=03,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let invalid3 = "$argon2i$v=19$m=65536,t=3,p=01$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(valid).is_ok()); + assert!(PasswordHash::from_encoded(invalid0).is_err()); + assert!(PasswordHash::from_encoded(invalid1).is_err()); + assert!(PasswordHash::from_encoded(invalid2).is_err()); + assert!(PasswordHash::from_encoded(invalid3).is_err()); + } + + #[test] + fn test_bounds_max_min_encoded_len() { + let minimum = "$argon2i$v=19$m=8,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + assert_eq!(minimum.len(), PasswordHash::MIN_ENCODED_LEN); + let maximum = "$argon2i$v=19$m=1111111111,t=1111111111,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + assert_eq!(maximum.len(), PasswordHash::MAX_ENCODED_LEN); + + // salt removed one char + let less = "$argon2i$v=19$m=8,t=3,p=1$cHBwcHBwcHBwcHBwcHBwc$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + assert_eq!(less.len(), PasswordHash::MIN_ENCODED_LEN - 1); + // salt added one char + let more = "$argon2i$v=19$m=1111111111,t=1111111111,p=1$cHBwcHBwcHBwcHBwcHBwcAA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + assert_eq!(more.len(), PasswordHash::MAX_ENCODED_LEN + 1); + + assert!(PasswordHash::from_encoded(minimum).is_ok()); + assert!(PasswordHash::from_encoded(maximum).is_ok()); + assert!(PasswordHash::from_encoded(less).is_err()); + assert!(PasswordHash::from_encoded(more).is_err()); + } + + #[test] + fn test_from_slice_password() { + assert!(PasswordHash::from_slice(&[0u8; 31], &[0u8; 16], 3, 1 << 16).is_err()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 1 << 16).is_ok()); + assert!(PasswordHash::from_slice(&[0u8; 33], &[0u8; 16], 3, 1 << 16).is_err()); + } + + #[test] + fn test_from_slice_salt() { + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 15], 3, 1 << 16).is_err()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 1 << 16).is_ok()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 17], 3, 1 << 16).is_err()); + } + + #[test] + fn test_from_slice_mem() { + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 7).is_err()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 8).is_ok()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 9).is_ok()); + } + + #[test] + fn test_from_slice_bad_iter() { + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 2, 1 << 16).is_err()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 1 << 16).is_ok()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 4, 1 << 16).is_ok()); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// If valid params then it's always valid to encode/decode. + fn prop_always_produce_valid_encoding( + password: Vec<u8>, + salt: Vec<u8>, + iterations: u32, + memory: u32, + ) -> bool { + let res = PasswordHash::from_slice(&password[..], &salt[..], iterations, memory); + if res.is_ok() { + assert!(PasswordHash::from_encoded(res.unwrap().unprotected_as_encoded()).is_ok()); + } + + true + } + } + + mod test_pwhash_and_verify { + use super::*; + + #[test] + fn test_argon2i_verify() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let dk = hash_password(&password, 3, 4096).unwrap(); + + assert!(hash_password_verify(&dk, &password).is_ok()); + } + + #[test] + fn test_argon2i_verify_err_modified_password() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let mut pwd_mod = dk.unprotected_as_bytes().to_vec(); + pwd_mod[0..32].copy_from_slice(&[0u8; 32]); + let modified = PasswordHash::from_slice(&pwd_mod, dk.salt.as_ref(), 3, 4096).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_verify_err_modified_memory() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let encoded = dk.unprotected_as_encoded(); + + let mut modified = encoded.to_string(); + let memory_offset = modified.find("$m=4096").unwrap(); + modified.replace_range(memory_offset..memory_offset + 7, "$m=2048"); + + let modified = PasswordHash::from_encoded(&modified).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_verify_err_modified_iterations() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let encoded = dk.unprotected_as_encoded(); + + let mut modified = encoded.to_string(); + let iterations_offset = modified.find(",t=3").unwrap(); + modified.replace_range(iterations_offset..iterations_offset + 4, ",t=4"); + + let modified = PasswordHash::from_encoded(&modified).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_verify_err_modified_memory_and_iterations() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let encoded = dk.unprotected_as_encoded(); + + let mut modified = encoded.to_string(); + let memory_offset = modified.find("$m=4096").unwrap(); + let iterations_offset = modified.find(",t=3").unwrap(); + modified.replace_range(memory_offset..memory_offset + 7, "$m=2048"); + modified.replace_range(iterations_offset..iterations_offset + 4, ",t=4"); + + let modified = PasswordHash::from_encoded(&modified).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_verify_err_modified_salt() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let mut salt_mod = dk.salt.as_ref().to_vec(); + salt_mod[0..16].copy_from_slice(&[0u8; 16]); + let modified = + PasswordHash::from_slice(dk.unprotected_as_bytes(), &salt_mod, 3, 4096).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_verify_err_modified_salt_and_password() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let mut pwd_mod = dk.unprotected_as_bytes().to_vec(); + let mut salt_mod = dk.salt.as_ref().to_vec(); + pwd_mod[0..32].copy_from_slice(&[0u8; 32]); + salt_mod[0..16].copy_from_slice(&[0u8; 16]); + let modified = PasswordHash::from_slice(&pwd_mod, &salt_mod, 3, 4096).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_invalid_iterations() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + assert!(hash_password(&password, MIN_ITERATIONS - 1, 4096).is_err()); + } + + #[test] + fn test_argon2i_invalid_memory() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + assert!(hash_password(&password, MIN_ITERATIONS, MIN_MEMORY - 1).is_err()); + } + } +} diff --git a/vendor/orion/src/lib.rs b/vendor/orion/src/lib.rs new file mode 100644 index 0000000..5064526 --- /dev/null +++ b/vendor/orion/src/lib.rs @@ -0,0 +1,117 @@ +// MIT License + +// Copyright (c) 2018-2023 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. + +//! A usable pure-Rust cryptography library. +//! +//! ## Authenticated secret-key encryption +//! [`orion::aead`] offers authenticated secret-key encryption using +//! XChaCha20Poly1305. +//! +//! ## Password hashing and verification +//! [`orion::pwhash`] offers password hashing and verification using Argon2i. +//! +//! ## Key derivation +//! [`orion::kdf`] offers key derivation using Argon2i. +//! +//! ## Message authentication +//! [`orion::auth`] offers message authentication and verification using BLAKE2b. +//! +//! ## Hashing +//! [`orion::hash`] offers hashing using BLAKE2b. +//! +//! ## Key exchange +//! [`orion::kex`] offers ephemeral key exchange using X25519 and BLAKE2b. +//! +//! ### A note on `no_std`: +//! When Orion is used in a `no_std` context, the high-level API is not available, since it relies on access to the systems random number generator. +//! +//! More information about Orion is available in the [wiki]. +//! +//! [`orion::aead`]: crate::aead +//! [`orion::pwhash`]: crate::pwhash +//! [`orion::kdf`]: crate::kdf +//! [`orion::auth`]: crate::auth +//! [`orion::hash`]: crate::hash +//! [`orion::kex`]: crate::kex +//! [wiki]: https://github.com/orion-rs/orion/wiki + +#![cfg_attr(not(feature = "safe_api"), no_std)] +#![forbid(unsafe_code)] +#![deny(clippy::mem_forget)] +#![warn( + missing_docs, + rust_2018_idioms, + trivial_casts, + unused_qualifications, + overflowing_literals +)] +#![cfg_attr(docsrs, feature(doc_cfg))] + +#[cfg(test)] +#[cfg(feature = "safe_api")] +extern crate quickcheck; +#[cfg(test)] +#[cfg(feature = "safe_api")] +#[macro_use(quickcheck)] +extern crate quickcheck_macros; + +#[cfg(feature = "alloc")] +#[cfg_attr(feature = "alloc", macro_use)] +extern crate alloc; + +#[macro_use] +mod typedefs; + +#[macro_use] +/// Utilities such as constant-time comparison. +pub mod util; + +/// Errors for Orion's cryptographic operations. +pub mod errors; + +/// \[__**Caution**__\] Low-level API. +pub mod hazardous; + +#[cfg(feature = "safe_api")] +mod high_level; + +#[cfg(feature = "safe_api")] +pub use high_level::hash; + +#[cfg(feature = "safe_api")] +pub use high_level::aead; + +#[cfg(feature = "safe_api")] +pub use high_level::auth; + +#[cfg(feature = "safe_api")] +pub use high_level::pwhash; + +#[cfg(feature = "safe_api")] +pub use high_level::kdf; + +#[cfg(feature = "safe_api")] +pub use high_level::kex; + +#[doc(hidden)] +/// Testing framework. +pub mod test_framework; diff --git a/vendor/orion/src/test_framework/aead_interface.rs b/vendor/orion/src/test_framework/aead_interface.rs new file mode 100644 index 0000000..b19c44f --- /dev/null +++ b/vendor/orion/src/test_framework/aead_interface.rs @@ -0,0 +1,379 @@ +// MIT License + +// Copyright (c) 2019-2023 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. + +#![allow(non_snake_case)] +#[cfg(feature = "safe_api")] +use crate::errors::UnknownCryptoError; + +#[cfg(test)] +#[cfg(feature = "safe_api")] +use crate::test_framework::streamcipher_interface::TestingRandom; + +#[allow(clippy::too_many_arguments)] +#[cfg(feature = "safe_api")] +/// Test runner for AEADs. +pub fn AeadTestRunner<Sealer, Opener, Key, Nonce>( + sealer: Sealer, + opener: Opener, + key: Key, + nonce: Nonce, + input: &[u8], + expected_ct_with_tag: Option<&[u8]>, + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + seal_dst_out_length(&sealer, &key, &nonce, input, tag_size, aad); + open_dst_out_length(&sealer, &opener, &key, &nonce, input, tag_size, aad); + open_modified_tag_err(&sealer, &opener, &key, &nonce, input, tag_size, aad); + open_modified_ciphertext_err(&sealer, &opener, &key, &nonce, input, tag_size, aad); + open_modified_aad_err(&sealer, &opener, &key, &nonce, input, tag_size, aad); + none_or_empty_some_aad_same_result(&sealer, &opener, &key, &nonce, input, tag_size); + seal_open_equals_expected( + &sealer, + &opener, + &key, + &nonce, + input, + expected_ct_with_tag, + tag_size, + aad, + ); + seal_plaintext_length(&sealer, &key, &nonce, tag_size, aad); + open_ciphertext_with_tag_length(&sealer, &opener, &key, &nonce, tag_size, aad); +} + +#[cfg(feature = "safe_api")] +/// Related bug: <https://github.com/orion-rs/orion/issues/52> +/// Test dst_out mutable array sizes when using seal(). +fn seal_dst_out_length<Sealer, Key, Nonce>( + sealer: &Sealer, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + assert!(sealer(key, nonce, input, default_aad, &mut dst_out_ct).is_ok()); + + let mut dst_out_ct_more = vec![0u8; input.len() + (tag_size + 1)]; + // Related bug: #52 + assert!(sealer(key, nonce, input, default_aad, &mut dst_out_ct_more).is_ok()); + + let mut dst_out_ct_more_double = vec![0u8; input.len() + (tag_size * 2)]; + // Related bug: #52 + assert!(sealer(key, nonce, input, default_aad, &mut dst_out_ct_more_double).is_ok()); + + let mut dst_out_ct_less = vec![0u8; input.len() + (tag_size - 1)]; + assert!(sealer(key, nonce, input, default_aad, &mut dst_out_ct_less).is_err()); +} + +#[cfg(feature = "safe_api")] +/// Related bug: <https://github.com/orion-rs/orion/issues/52> +/// Test input sizes when using seal(). +fn seal_plaintext_length<Sealer, Key, Nonce>( + sealer: &Sealer, + key: &Key, + nonce: &Nonce, + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let input_0 = vec![0u8; 0]; + let mut dst_out_ct_0 = vec![0u8; input_0.len() + tag_size]; + assert!(sealer(key, nonce, &input_0, default_aad, &mut dst_out_ct_0).is_ok()); + + let input_1 = vec![0u8; 1]; + let mut dst_out_ct_1 = vec![0u8; input_1.len() + tag_size]; + assert!(sealer(key, nonce, &input_1, default_aad, &mut dst_out_ct_1).is_ok()); + + let input_128 = vec![0u8; 128]; + let mut dst_out_ct_128 = vec![0u8; input_128.len() + tag_size]; + assert!(sealer(key, nonce, &input_128, default_aad, &mut dst_out_ct_128).is_ok()); +} + +#[cfg(feature = "safe_api")] +/// Related bug: <https://github.com/orion-rs/orion/issues/52> +/// Test dst_out mutable array sizes when using open(). +fn open_dst_out_length<Sealer, Opener, Key, Nonce>( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + sealer(key, nonce, input, default_aad, &mut dst_out_ct).unwrap(); + + let mut dst_out_pt = vec![0u8; input.len()]; + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt).is_ok()); + + let mut dst_out_pt_0 = [0u8; 0]; + let empty_out_res = opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt_0); + if input.is_empty() { + assert!(empty_out_res.is_ok()); + } else { + assert!(empty_out_res.is_err()); + } + + if !input.is_empty() { + let mut dst_out_pt_less = vec![0u8; input.len() - 1]; + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt_less).is_err()); + } + + let mut dst_out_pt_more = vec![0u8; input.len() + 1]; + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt_more).is_ok()); +} + +#[cfg(feature = "safe_api")] +/// Test input sizes when using open(). +fn open_ciphertext_with_tag_length<Sealer, Opener, Key, Nonce>( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + let mut dst_out_pt = vec![0u8; tag_size]; + + assert!(opener(key, nonce, &[0u8; 0], default_aad, &mut dst_out_pt).is_err()); + + assert!(opener( + key, + nonce, + &vec![0u8; tag_size - 1], + default_aad, + &mut dst_out_pt + ) + .is_err()); + + let mut dst_out_ct = vec![0u8; tag_size]; + sealer(key, nonce, &[0u8; 0], default_aad, &mut dst_out_ct).unwrap(); + + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt).is_ok()); +} + +#[allow(clippy::too_many_arguments)] +#[cfg(feature = "safe_api")] +/// Test that sealing and opening produces the expected ciphertext. +fn seal_open_equals_expected<Sealer, Opener, Key, Nonce>( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + expected_ct_with_tag: Option<&[u8]>, + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + sealer(key, nonce, input, default_aad, &mut dst_out_ct).unwrap(); + if let Some(expected) = expected_ct_with_tag { + assert_eq!(expected, &dst_out_ct[..]); + } + + let mut dst_out_pt = input.to_vec(); + opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt).unwrap(); + assert_eq!(input, &dst_out_pt[..]); + if let Some(expected) = expected_ct_with_tag { + opener(key, nonce, expected, default_aad, &mut dst_out_pt).unwrap(); + assert_eq!(input, &dst_out_pt[..]); + } +} + +#[cfg(feature = "safe_api")] +/// When opening sealed data with a modified tag, an error should be returned. +fn open_modified_tag_err<Sealer, Opener, Key, Nonce>( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + sealer(key, nonce, input, default_aad, &mut dst_out_ct).unwrap(); + // Modify the first byte of the authentication tag. + dst_out_ct[input.len() + 1] ^= 1; + + let mut dst_out_pt = input.to_vec(); + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt).is_err()); +} + +#[cfg(feature = "safe_api")] +/// When opening sealed data with a modified ciphertext, an error should be returned. +fn open_modified_ciphertext_err<Sealer, Opener, Key, Nonce>( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut input = input; + if input.is_empty() { + input = &[0u8; 1]; + } + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + sealer(key, nonce, input, default_aad, &mut dst_out_ct).unwrap(); + // Modify the first byte of the ciphertext. + dst_out_ct[0] ^= 1; + + let mut dst_out_pt = input.to_vec(); + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt).is_err()); +} + +#[cfg(feature = "safe_api")] +/// When opening sealed data with modified aad, an error should be returned. +fn open_modified_aad_err<Sealer, Opener, Key, Nonce>( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + sealer(key, nonce, input, default_aad, &mut dst_out_ct).unwrap(); + + let mut dst_out_pt = input.to_vec(); + assert!(opener(key, nonce, &dst_out_ct, Some(b"BAD AAD"), &mut dst_out_pt).is_err()); +} + +#[cfg(feature = "safe_api")] +/// Using None or Some with empty slice should produce the exact same result. +fn none_or_empty_some_aad_same_result<Sealer, Opener, Key, Nonce>( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut dst_out_ct_none = vec![0u8; input.len() + tag_size]; + let mut dst_out_ct_some_empty = vec![0u8; input.len() + tag_size]; + + sealer(key, nonce, input, None, &mut dst_out_ct_none).unwrap(); + sealer( + key, + nonce, + input, + Some(&[0u8; 0]), + &mut dst_out_ct_some_empty, + ) + .unwrap(); + + assert_eq!(dst_out_ct_none, dst_out_ct_some_empty); + + let mut dst_out_pt = vec![0u8; input.len()]; + assert!(opener( + key, + nonce, + &dst_out_ct_none, + Some(&[0u8; 0]), + &mut dst_out_pt + ) + .is_ok()); + assert!(opener(key, nonce, &dst_out_ct_some_empty, None, &mut dst_out_pt).is_ok()); +} + +#[cfg(test)] +#[cfg(feature = "safe_api")] +/// Test that sealing and opening with different secret-key/nonce yields an error. +pub fn test_diff_params_err<Sealer, Opener, Key, Nonce>( + sealer: &Sealer, + opener: &Opener, + input: &[u8], + tag_size: usize, +) where + Key: TestingRandom + PartialEq<Key>, + Nonce: TestingRandom + PartialEq<Nonce>, + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let sk1 = Key::gen(); + let sk2 = Key::gen(); + assert!(sk1 != sk2); + + let n1 = Nonce::gen(); + let n2 = Nonce::gen(); + assert!(n1 != n2); + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + let mut dst_out_pt = vec![0u8; input.len()]; + + // Different secret key + sealer(&sk1, &n1, input, None, &mut dst_out_ct).unwrap(); + assert!(opener(&sk2, &n1, &dst_out_ct, None, &mut dst_out_pt).is_err()); + + // Different nonce + sealer(&sk1, &n1, input, None, &mut dst_out_ct).unwrap(); + assert!(opener(&sk1, &n2, &dst_out_ct, None, &mut dst_out_pt).is_err()); +} diff --git a/vendor/orion/src/test_framework/incremental_interface.rs b/vendor/orion/src/test_framework/incremental_interface.rs new file mode 100644 index 0000000..6bb14b9 --- /dev/null +++ b/vendor/orion/src/test_framework/incremental_interface.rs @@ -0,0 +1,343 @@ +// MIT License + +// Copyright (c) 2019-2023 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. + +use crate::errors::UnknownCryptoError; +use core::marker::PhantomData; + +/// Trait to define default streaming contexts that can be tested. +pub trait TestableStreamingContext<T: PartialEq> { + /// Streaming context function to reset the internal state. + fn reset(&mut self) -> Result<(), UnknownCryptoError>; + + /// Streaming context function to update the internal state. + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError>; + + /// Streaming context function to finalize the internal state. + fn finalize(&mut self) -> Result<T, UnknownCryptoError>; + + /// Streaming context function to combine new(), update() and finalize() from the internal state. + fn one_shot(input: &[u8]) -> Result<T, UnknownCryptoError>; + + /// Streaming context function to verify pre-computed results. + fn verify_result(expected: &T, input: &[u8]) -> Result<(), UnknownCryptoError>; + + /// Testing utility-function that compares the internal state to another. + fn compare_states(state_1: &Self, state_2: &Self); +} + +#[allow(dead_code)] // Allow because blocksize field is only used with std. +/// A streaming context tester. +pub struct StreamingContextConsistencyTester<R, T> { + _return_type: PhantomData<R>, + // The initial context to base further calls upon. + _initial_context: T, + blocksize: usize, +} + +impl<R, T> StreamingContextConsistencyTester<R, T> +where + R: PartialEq + core::fmt::Debug, + T: TestableStreamingContext<R> + Clone, +{ + /// The streaming interface tester is created utilizing an initialized + /// streaming state. + pub fn new(streaming_context: T, blocksize: usize) -> Self { + Self { + _return_type: PhantomData, + _initial_context: streaming_context, + blocksize, + } + } + + // Default input to process. + // The number 37 has no particular meaning. + const DEFAULT_INPUT: [u8; 37] = [255u8; 37]; + + #[cfg(feature = "safe_api")] + /// Run all consistency tests given some input data. + /// Usually used with quickcheck. + pub fn run_all_tests_property(&self, data: &[u8]) { + self.consistency(data); + self.consistency(&[0u8; 0]); + self.produces_same_state(data); + + // Following test requires std. + self.incremental_and_one_shot(data); + + self.double_finalize_with_reset_no_update_ok(data); + self.double_finalize_with_reset_ok(data); + self.double_finalize_err(data); + self.update_after_finalize_with_reset_ok(data); + self.update_after_finalize_err(data); + self.double_reset_ok(data); + self.immediate_finalize(); + Self::verify_same_input_ok(data); + Self::verify_diff_input_err(data); + } + + #[cfg(feature = "safe_api")] + /// Used when quickcheck is not available to generate input. + /// Default input data is used instead. Requires std. + pub fn run_all_tests(&self) { + self.consistency(&Self::DEFAULT_INPUT); + self.consistency(&[0u8; 0]); + self.produces_same_state(&Self::DEFAULT_INPUT); + + // Following test requires std. + self.incremental_processing_with_leftover(); + + self.incremental_and_one_shot(&Self::DEFAULT_INPUT); + self.double_finalize_with_reset_no_update_ok(&Self::DEFAULT_INPUT); + self.double_finalize_with_reset_ok(&Self::DEFAULT_INPUT); + self.double_finalize_err(&Self::DEFAULT_INPUT); + self.update_after_finalize_with_reset_ok(&Self::DEFAULT_INPUT); + self.update_after_finalize_err(&Self::DEFAULT_INPUT); + self.double_reset_ok(&Self::DEFAULT_INPUT); + self.immediate_finalize(); + Self::verify_same_input_ok(&Self::DEFAULT_INPUT); + Self::verify_diff_input_err(&Self::DEFAULT_INPUT); + } + + #[cfg(not(feature = "safe_api"))] + /// Used when quickcheck is not available to generate input. + /// Default input data is used instead. Without std. + pub fn run_all_tests(&self) { + self.consistency(&Self::DEFAULT_INPUT); + self.consistency(&[0u8; 0]); + self.produces_same_state(&Self::DEFAULT_INPUT); + self.incremental_and_one_shot(&Self::DEFAULT_INPUT); + self.double_finalize_with_reset_no_update_ok(&Self::DEFAULT_INPUT); + self.double_finalize_with_reset_ok(&Self::DEFAULT_INPUT); + self.double_finalize_err(&Self::DEFAULT_INPUT); + self.update_after_finalize_with_reset_ok(&Self::DEFAULT_INPUT); + self.update_after_finalize_err(&Self::DEFAULT_INPUT); + self.double_reset_ok(&Self::DEFAULT_INPUT); + self.immediate_finalize(); + Self::verify_same_input_ok(&Self::DEFAULT_INPUT); + Self::verify_diff_input_err(&Self::DEFAULT_INPUT); + } + + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Testing different usage combinations of new(), update(), + /// finalize() and reset() produce the same output. + /// + /// It is important to ensure this is also called with empty + /// `data`. + fn consistency(&self, data: &[u8]) { + // new(), update(), finalize() + let mut state_1 = self._initial_context.clone(); + state_1.update(data).unwrap(); + let res_1 = state_1.finalize().unwrap(); + + // new(), reset(), update(), finalize() + let mut state_2 = self._initial_context.clone(); + state_2.reset().unwrap(); + state_2.update(data).unwrap(); + let res_2 = state_2.finalize().unwrap(); + + // new(), update(), reset(), update(), finalize() + let mut state_3 = self._initial_context.clone(); + state_3.update(data).unwrap(); + state_3.reset().unwrap(); + state_3.update(data).unwrap(); + let res_3 = state_3.finalize().unwrap(); + + // new(), update(), finalize(), reset(), update(), finalize() + let mut state_4 = self._initial_context.clone(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset().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 = self._initial_context.clone(); + let res_5 = state_5.finalize().unwrap(); + + // new(), reset(), finalize() + let mut state_6 = self._initial_context.clone(); + state_6.reset().unwrap(); + let res_6 = state_6.finalize().unwrap(); + + // new(), update(), reset(), finalize() + let mut state_7 = self._initial_context.clone(); + state_7.update(b"WRONG DATA").unwrap(); + state_7.reset().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 output. + fn produces_same_state(&self, data: &[u8]) { + // new() + let state_1 = self._initial_context.clone(); + + // new(), reset() + let mut state_2 = self._initial_context.clone(); + state_2.reset().unwrap(); + + // new(), update(), reset() + let mut state_3 = self._initial_context.clone(); + state_3.update(data).unwrap(); + state_3.reset().unwrap(); + + // new(), update(), finalize(), reset() + let mut state_4 = self._initial_context.clone(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset().unwrap(); + + T::compare_states(&state_1, &state_2); + T::compare_states(&state_2, &state_3); + T::compare_states(&state_3, &state_4); + } + + #[cfg(feature = "safe_api")] + /// Test for issues when incrementally processing data + /// with leftover in the internal buffer. It should produce + /// the same results as processing the same data in a single pass. + fn incremental_processing_with_leftover(&self) { + for len in 0..self.blocksize * 4 { + let data = vec![0u8; len]; + let mut state = self._initial_context.clone(); + let mut other_data: Vec<u8> = Vec::new(); + + other_data.extend_from_slice(&data); + state.update(&data).unwrap(); + + if data.len() > self.blocksize { + other_data.extend_from_slice(b""); + state.update(b"").unwrap(); + } + if data.len() > self.blocksize * 2 { + other_data.extend_from_slice(b"Extra"); + state.update(b"Extra").unwrap(); + } + if data.len() > self.blocksize * 3 { + other_data.extend_from_slice(&[0u8; 256]); + state.update(&[0u8; 256]).unwrap(); + } + + let streaming_result = state.finalize().unwrap(); + let one_shot_result = T::one_shot(&other_data).unwrap(); + + assert_eq!(streaming_result, one_shot_result); + } + } + + /// new(), update(), finalize() == one_shot() + fn incremental_and_one_shot(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let streaming_result = state.finalize().unwrap(); + let one_shot_result = T::one_shot(data).unwrap(); + + assert_eq!(streaming_result, one_shot_result); + } + + /// new(), update(), finalize(), reset(), finalize(): OK + fn double_finalize_with_reset_no_update_ok(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset().unwrap(); + assert!(state.finalize().is_ok()); + } + + /// new(), update(), finalize(), reset(), update(), finalize(): OK + fn double_finalize_with_reset_ok(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset().unwrap(); + state.update(data).unwrap(); + assert!(state.finalize().is_ok()); + } + + /// new(), update(), finalize(), finalize(): ERR + fn double_finalize_err(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.finalize().is_err()); + } + + /// new(), update(), finalize(), reset(), update(): OK + fn update_after_finalize_with_reset_ok(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset().unwrap(); + assert!(state.update(data).is_ok()); + } + + /// Related bug: https://github.com/orion-rs/orion/issues/28 + /// new(), update(), finalize(), update(): ERR + fn update_after_finalize_err(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.update(data).is_err()); + } + + /// reset(), reset(): OK + fn double_reset_ok(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset().unwrap(); + assert!(state.reset().is_ok()); + } + + /// new(), finalize(): OK + fn immediate_finalize(&self) { + let mut state = self._initial_context.clone(); + assert!(state.finalize().is_ok()); + } + + /// Using the same input should always result in a successful verification. + pub fn verify_same_input_ok(data: &[u8]) { + let expected = T::one_shot(data).unwrap(); + assert!(T::verify_result(&expected, data).is_ok()); + } + + /// Using different input should result in a failed verification. + pub fn verify_diff_input_err(data: &[u8]) { + let expected = T::one_shot(data).unwrap(); + assert!(T::verify_result(&expected, b"Bad data").is_err()); + } +} diff --git a/vendor/orion/src/test_framework/mod.rs b/vendor/orion/src/test_framework/mod.rs new file mode 100644 index 0000000..afe9361 --- /dev/null +++ b/vendor/orion/src/test_framework/mod.rs @@ -0,0 +1,30 @@ +// MIT License + +// Copyright (c) 2019-2023 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. + +/// Tests for a streaming context that offers incremental processing. +pub mod incremental_interface; + +/// Tests for AEAD interfaces such as `chacha20poly1305`. +pub mod aead_interface; + +/// Tests for stream ciphers such as `chacha20`. +pub mod streamcipher_interface; diff --git a/vendor/orion/src/test_framework/streamcipher_interface.rs b/vendor/orion/src/test_framework/streamcipher_interface.rs new file mode 100644 index 0000000..d46e202 --- /dev/null +++ b/vendor/orion/src/test_framework/streamcipher_interface.rs @@ -0,0 +1,305 @@ +// MIT License + +// Copyright (c) 2019-2023 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. + +#![allow(non_snake_case)] + +#[cfg(feature = "safe_api")] +use crate::errors::UnknownCryptoError; + +#[cfg(test)] +#[cfg(feature = "safe_api")] +pub trait TestingRandom { + /// Randomly generate self. + fn gen() -> Self; +} + +#[cfg(feature = "safe_api")] +/// Test runner for stream ciphers. +pub fn StreamCipherTestRunner<Encryptor, Decryptor, Key, Nonce>( + encryptor: Encryptor, + decryptor: Decryptor, + key: Key, + nonce: Nonce, + counter: u32, + input: &[u8], + expected_ct: Option<&[u8]>, +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + if !input.is_empty() { + encrypt_decrypt_out_length(&encryptor, &decryptor, &key, &nonce, input); + encrypt_decrypt_equals_expected( + &encryptor, + &decryptor, + &key, + &nonce, + counter, + input, + expected_ct, + ); + } + + encrypt_decrypt_input_empty(&encryptor, &decryptor, &key, &nonce); + initial_counter_overflow_err(&encryptor, &decryptor, &key, &nonce); + initial_counter_max_ok(&encryptor, &decryptor, &key, &nonce); +} + +#[cfg(feature = "safe_api")] +/// Given a input length `a` find out how many times +/// the initial counter on encrypt()/decrypt() would +/// increase. +fn counter_increase_times(a: f32) -> u32 { + // Otherwise a overflowing subtraction would happen + if a <= 64f32 { + return 0; + } + + let check_with_floor = (a / 64f32).floor(); + let actual = a / 64f32; + + assert!(actual >= check_with_floor); + // Subtract one because the first 64 in length + // the counter does not increase + if actual > check_with_floor { + (actual.ceil() as u32) - 1 + } else { + (actual as u32) - 1 + } +} + +#[cfg(feature = "safe_api")] +fn return_if_counter_will_overflow<Encryptor, Decryptor, Key, Nonce>( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, + counter: u32, + input: &[u8], +) -> bool +where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + assert!(!input.is_empty()); + let mut dst_out = vec![0u8; input.len()]; + + // Overflow will occur and the operation should fail + let enc_res = encryptor(key, nonce, counter, &[0u8; 0], &mut dst_out).is_err(); + let dec_res = decryptor(key, nonce, counter, &[0u8; 0], &mut dst_out).is_err(); + + enc_res && dec_res +} + +#[cfg(feature = "safe_api")] +fn encrypt_decrypt_input_empty<Encryptor, Decryptor, Key, Nonce>( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut dst_out = [0u8; 64]; + assert!(encryptor(key, nonce, 0, &[0u8; 0], &mut dst_out).is_err()); + assert!(decryptor(key, nonce, 0, &[0u8; 0], &mut dst_out).is_err()); +} + +#[cfg(feature = "safe_api")] +fn encrypt_decrypt_out_length<Encryptor, Decryptor, Key, Nonce>( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, + input: &[u8], +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + assert!(!input.is_empty()); + + let mut dst_out_empty = vec![0u8; 0]; + assert!(encryptor(key, nonce, 0, input, &mut dst_out_empty).is_err()); + assert!(decryptor(key, nonce, 0, input, &mut dst_out_empty).is_err()); + + let mut dst_out_less = vec![0u8; input.len() - 1]; + assert!(encryptor(key, nonce, 0, input, &mut dst_out_less).is_err()); + assert!(decryptor(key, nonce, 0, input, &mut dst_out_less).is_err()); + + let mut dst_out_exact = vec![0u8; input.len()]; + assert!(encryptor(key, nonce, 0, input, &mut dst_out_exact).is_ok()); + assert!(decryptor(key, nonce, 0, input, &mut dst_out_exact).is_ok()); + + let mut dst_out_greater = vec![0u8; input.len() + 1]; + assert!(encryptor(key, nonce, 0, input, &mut dst_out_greater).is_ok()); + assert!(decryptor(key, nonce, 0, input, &mut dst_out_greater).is_ok()); +} + +#[cfg(feature = "safe_api")] +/// Test that encrypting and decrypting produces expected plaintext/ciphertext. +fn encrypt_decrypt_equals_expected<Encryptor, Decryptor, Key, Nonce>( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, + counter: u32, + input: &[u8], + expected_ct: Option<&[u8]>, +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + assert!(!input.is_empty()); + + // Check if the counter would overflow. If yes, ensure that both encryptor and + // decryptor returned errors. + if counter_increase_times(input.len() as f32) + .checked_add(counter) + .is_none() + { + assert!(return_if_counter_will_overflow( + encryptor, decryptor, key, nonce, counter, input + )); + + return; + } + + let mut dst_out_ct = vec![0u8; input.len()]; + encryptor(key, nonce, counter, input, &mut dst_out_ct).unwrap(); + if let Some(expected_result) = expected_ct { + assert_eq!(expected_result, &dst_out_ct[..]); + } + + let mut dst_out_pt = vec![0u8; input.len()]; + decryptor(key, nonce, counter, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_eq!(input, &dst_out_pt[..]); + if let Some(expected_result) = expected_ct { + decryptor(key, nonce, counter, expected_result, &mut dst_out_pt).unwrap(); + assert_eq!(input, &dst_out_pt[..]); + } +} + +#[cfg(feature = "safe_api")] +/// Test that a initial counter will not overflow the internal. +fn initial_counter_overflow_err<Encryptor, Decryptor, Key, Nonce>( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut dst_out = [0u8; 128]; + assert!(encryptor( + key, + nonce, + u32::MAX, + &[0u8; 65], // CHACHA_BLOCKSIZE + 1 one to trigger internal block counter addition. + &mut dst_out + ) + .is_err()); + assert!(decryptor( + key, + nonce, + u32::MAX, + &[0u8; 65], // CHACHA_BLOCKSIZE + 1 one to trigger internal block counter addition. + &mut dst_out + ) + .is_err()); +} + +#[cfg(feature = "safe_api")] +/// Test that processing one block does not fail on the largest possible initial block counter. +fn initial_counter_max_ok<Encryptor, Decryptor, Key, Nonce>( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut dst_out = [0u8; 64]; + assert!(encryptor( + key, + nonce, + u32::MAX, + &[0u8; 64], // Only needs to process one keystream + &mut dst_out + ) + .is_ok()); + assert!(decryptor( + key, + nonce, + u32::MAX, + &[0u8; 64], // Only needs to process one keystream + &mut dst_out + ) + .is_ok()); +} + +#[cfg(test)] +#[cfg(feature = "safe_api")] +/// Test that encrypting using different secret-key/nonce/initial-counter combinations yields different +/// ciphertexts. +pub fn test_diff_params_diff_output<Encryptor, Decryptor, Key, Nonce>( + encryptor: &Encryptor, + decryptor: &Decryptor, +) where + Key: TestingRandom + PartialEq<Key>, + Nonce: TestingRandom + PartialEq<Nonce>, + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let input = &[0u8; 16]; + + let sk1 = Key::gen(); + let sk2 = Key::gen(); + assert!(sk1 != sk2); + + let n1 = Nonce::gen(); + let n2 = Nonce::gen(); + assert!(n1 != n2); + + let c1 = 0u32; + let c2 = 1u32; + + let mut dst_out_ct = vec![0u8; input.len()]; + let mut dst_out_pt = vec![0u8; input.len()]; + + // Different secret key + encryptor(&sk1, &n1, c1, input, &mut dst_out_ct).unwrap(); + decryptor(&sk2, &n1, c1, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_ne!(&dst_out_pt[..], input); + + // Different nonce + encryptor(&sk1, &n1, c1, input, &mut dst_out_ct).unwrap(); + decryptor(&sk1, &n2, c1, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_ne!(&dst_out_pt[..], input); + + // Different initial counter + encryptor(&sk1, &n1, c1, input, &mut dst_out_ct).unwrap(); + decryptor(&sk1, &n1, c2, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_ne!(&dst_out_pt[..], input); +} diff --git a/vendor/orion/src/typedefs.rs b/vendor/orion/src/typedefs.rs new file mode 100644 index 0000000..6471c30 --- /dev/null +++ b/vendor/orion/src/typedefs.rs @@ -0,0 +1,993 @@ +// MIT License + +// Copyright (c) 2018-2023 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. + +/// +/// Trait implementation macros + +#[cfg(feature = "safe_api")] +/// Macro that implements the `Default` trait using a CSPRNG. +macro_rules! impl_default_trait (($name:ident, $size:expr) => ( + impl Default for $name { + #[cfg(feature = "safe_api")] + /// Randomly generate using a CSPRNG with recommended size. Not available in `no_std` context. + fn default() -> $name { + let mut value = vec![0u8; $size]; + crate::util::secure_rand_bytes(&mut value).unwrap(); + + $name { value, original_length: $size } + } + } +)); + +/// Macro that implements the `PartialEq` trait on a object called `$name` that +/// provides a given $bytes_function to return a slice. This `PartialEq` will +/// execute in constant-time. +/// +/// This also provides an empty `Eq` implementation. +macro_rules! impl_ct_partialeq_trait (($name:ident, $bytes_function:ident) => ( + impl PartialEq<$name> for $name { + fn eq(&self, other: &$name) -> bool { + use subtle::ConstantTimeEq; + + (self.$bytes_function() + .ct_eq(other.$bytes_function())).into() + } + } + + impl Eq for $name {} + + impl PartialEq<&[u8]> for $name { + fn eq(&self, other: &&[u8]) -> bool { + use subtle::ConstantTimeEq; + + (self.$bytes_function() + .ct_eq(*other)).into() + } + } +)); + +/// Macro that implements the `Debug` trait on a object called `$name`. +/// This `Debug` will omit any fields of object `$name` to avoid them being +/// written to logs. +macro_rules! impl_omitted_debug_trait (($name:ident) => ( + impl core::fmt::Debug for $name { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} {{***OMITTED***}}", stringify!($name)) + } + } +)); + +/// Macro that implements the `Debug` trait on a object called `$name`. +macro_rules! impl_normal_debug_trait (($name:ident) => ( + impl core::fmt::Debug for $name { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} {:?}", stringify!($name), &self.value[..]) + } + } +)); + +/// Macro that implements the `serde::{Serialize, Deserialize}` traits. +#[cfg(feature = "serde")] +macro_rules! impl_serde_traits (($name:ident, $bytes_function:ident) => ( + + #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] + /// This type tries to serialize as a `&[u8]` would. Note that the serialized + /// type likely does not have the same protections that Orion provides, such + /// as constant-time operations. A good rule of thumb is to only serialize + /// these types for storage. Don't operate on the serialized types. + impl serde::Serialize for $name { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::ser::Serializer, + { + let bytes: &[u8] = &self.$bytes_function(); + bytes.serialize(serializer) + } + } + + #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] + /// This type tries to deserialize as a `Vec<u8>` would. If it succeeds, the digest + /// will be built using `Self::from_slice`. + /// + /// Note that **this allocates** once to store the referenced bytes on the heap. + impl<'de> serde::Deserialize<'de> for $name { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::de::Deserializer<'de>, + { + let bytes = Vec::<u8>::deserialize(deserializer)?; + std::convert::TryFrom::try_from(bytes.as_slice()).map_err(serde::de::Error::custom) + } + } +)); + +/// Macro that implements the `Drop` trait on a object called `$name` which has +/// a field `value`. This `Drop` will zero out the field `value` when the +/// objects destructor is called. +macro_rules! impl_drop_trait (($name:ident) => ( + impl Drop for $name { + fn drop(&mut self) { + use zeroize::Zeroize; + self.value.iter_mut().zeroize(); + } + } +)); + +/// Macro that implements the `AsRef<[u8]>` trait on a object called `$name` +/// which has fields `value` and `original_length`. This will return the inner +/// `value` as a byte slice, and should only be implemented on public types +/// which don't have any special protections. +macro_rules! impl_asref_trait (($name:ident) => ( + impl AsRef<[u8]> for $name { + #[inline] + fn as_ref(&self) -> &[u8] { + self.value[..self.original_length].as_ref() + } + } +)); + +/// Macro that implements the `From<[T]>` trait on a object called `$name` +/// which has fields `value` and `original_length`. It implements From +/// based on `$size` and this macro should, in most cases, only be used for +/// types which have a fixed-length. +macro_rules! impl_from_trait (($name:ident, $size:expr) => ( + impl From<[u8; $size]> for $name { + #[inline] + /// Make an object from a byte array. + fn from(bytes: [u8; $size]) -> $name { + $name { + value: bytes, + original_length: $size + } + } + } +)); + +/// Macro that implements `TryFrom<&[u8]>` on an object called `$name` that +/// implements the method `from_slice`. +macro_rules! impl_try_from_trait (($name:ident) => ( + /// Delegates to `from_slice` implementation + impl TryFrom<&[u8]> for $name { + type Error = UnknownCryptoError; + fn try_from(slice: &[u8]) -> Result<Self, Self::Error> { + Self::from_slice(slice) + } + } +)); + +/// +/// Function implementation macros + +/// Macro to implement a `from_slice()` function. Returns `UnknownCryptoError` +/// if the slice length is not accepted. +/// $lower_bound and $upper_bound is the inclusive range of which a slice might +/// be acceptable in length. If a slice may only be a fixed size, $lower_bound +/// and $upper_bound should be the same. The `value` field will always be allocated with +/// a size of $upper_bound. +macro_rules! func_from_slice (($name:ident, $lower_bound:expr, $upper_bound:expr) => ( + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> { + + let slice_len = slice.len(); + + if !($lower_bound..=$upper_bound).contains(&slice_len) { + return Err(UnknownCryptoError); + } + + let mut value = [0u8; $upper_bound]; + value[..slice_len].copy_from_slice(slice); + + Ok($name { value, original_length: slice_len }) + } +)); + +#[cfg(feature = "safe_api")] +/// Macro to implement a `from_slice()` function. Returns `UnknownCryptoError` +/// if the slice is empty. +macro_rules! func_from_slice_variable_size (($name:ident) => ( + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + #[cfg(feature = "safe_api")] + /// Construct from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> { + // See issue on `isize` limit: https://github.com/orion-rs/orion/issues/130 + if slice.is_empty() || slice.len() > (isize::MAX as usize) { + return Err(UnknownCryptoError); + } + + Ok($name { value: Vec::from(slice), original_length: slice.len() }) + } +)); + +/// Macro to implement a `unprotected_as_bytes()` function for objects that +/// implement extra protections. Typically used on objects that implement +/// `Drop`. +macro_rules! func_unprotected_as_bytes (() => ( + #[inline] + /// Return the object as byte slice. __**Warning**__: Should not be used unless strictly + /// needed. This __**breaks protections**__ that the type implements. + pub fn unprotected_as_bytes(&self) -> &[u8] { + self.value[..self.original_length].as_ref() + } +)); + +/// Macro to implement a `len()` function which will return the original_length +/// field. Meaning the amount of bytes the newtype was created from. +macro_rules! func_len (() => ( + #[inline] + /// Return the length of the object. + pub fn len(&self) -> usize { + self.original_length + } +)); + +/// Macro to implement an `is_empty()` function which will return `true` if `self.len() == 0`. +macro_rules! func_is_empty (() => ( + #[inline] + /// Return `true` if this object does not hold any data, `false` otherwise. + /// + /// __NOTE__: This method should always return `false`, since there shouldn't be a way + /// to create an empty instance of this object. + pub fn is_empty(&self) -> bool { + self.original_length == 0 + } +)); + +/// Macro to implement a `generate()` function for objects that benefit from +/// having a CSPRNG available to generate data of a fixed length $gen_length. +macro_rules! func_generate (($name:ident, $upper_bound:expr, $gen_length:expr) => ( + #[cfg(feature = "safe_api")] + /// Randomly generate using a CSPRNG. Not available in `no_std` context. + pub fn generate() -> $name { + let mut value = [0u8; $upper_bound]; + // This will not panic on size, unless the newtype has been defined with $upper_bound + // or $gen_length equal to 0. + crate::util::secure_rand_bytes(&mut value[..$gen_length]).unwrap(); + + $name { value, original_length: $gen_length } + } +)); + +#[cfg(feature = "safe_api")] +/// Macro to implement a `generate()` function for objects that benefit from +/// having a CSPRNG available to generate data of a variable length. +macro_rules! func_generate_variable_size (($name:ident) => ( + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + #[cfg(feature = "safe_api")] + /// Randomly generate using a CSPRNG. Not available in `no_std` context. + pub fn generate(length: usize) -> Result<$name, UnknownCryptoError> { + // See issue on `isize` limit: https://github.com/orion-rs/orion/issues/130 + if length < 1 || length > (isize::MAX as usize) { + return Err(UnknownCryptoError); + } + + let mut value = vec![0u8; length]; + // This cannot panic on size input due to above length checks. + crate::util::secure_rand_bytes(&mut value).unwrap(); + + Ok($name { value, original_length: length }) + } +)); + +/// +/// Test implementation macros + +#[cfg(test)] +#[cfg(feature = "serde")] +macro_rules! test_serde_impls (($name:ident, $gen_length:expr) => ( + #[test] + fn test_serde_serialized_equivalence_to_bytes_fn() { + let bytes = &[38u8; $gen_length][..]; + let orion_type = $name::from_slice(bytes).unwrap(); + let serialized_from_bytes = serde_json::to_value(bytes).unwrap(); + let serialized_from_orion_type = serde_json::to_value(&orion_type).unwrap(); + assert_eq!(serialized_from_bytes, serialized_from_orion_type); + } + + #[test] + fn test_serde_deserialized_equivalence_to_bytes_fn() { + let bytes = &[38u8; $gen_length][..]; + let serialized_from_bytes = serde_json::to_value(bytes).unwrap(); + let orion_type: $name = serde_json::from_value(serialized_from_bytes).unwrap(); + assert_eq!(orion_type, bytes); + } +)); + +#[cfg(test)] +macro_rules! test_bound_parameters (($name:ident, $lower_bound:expr, $upper_bound:expr, $gen_length:expr) => ( + #[test] + fn test_bound_params() { + // $lower_bound: + assert!($lower_bound <= $upper_bound); + // $upper_bound: + // $gen_length: + assert!($gen_length <= $upper_bound); + assert!($gen_length >= $lower_bound); + } +)); + +#[cfg(test)] +macro_rules! test_partial_eq (($name:ident, $upper_bound:expr) => ( + #[test] + fn test_partial_eq() { + // PartialEq<Self> + assert_eq!($name::from_slice(&[0u8; $upper_bound]).unwrap(), $name::from_slice(&[0u8; $upper_bound]).unwrap()); + assert_ne!($name::from_slice(&[0u8; $upper_bound]).unwrap(), $name::from_slice(&[1u8; $upper_bound]).unwrap()); + // PartialEq<&[u8]> + assert_eq!($name::from_slice(&[0u8; $upper_bound]).unwrap(), [0u8; $upper_bound].as_ref()); + assert_ne!($name::from_slice(&[0u8; $upper_bound]).unwrap(), [1u8; $upper_bound].as_ref()); + } +)); + +#[cfg(test)] +macro_rules! test_from_slice (($name:ident, $lower_bound:expr, $upper_bound:expr) => ( + #[test] + fn test_from_slice() { + assert!($name::from_slice(&[0u8; $upper_bound]).is_ok()); + assert!($name::from_slice(&[0u8; $lower_bound]).is_ok()); + + assert!($name::from_slice(&[0u8; $upper_bound + 1]).is_err()); + assert!($name::from_slice(&[0u8; $lower_bound - 1]).is_err()); + assert!($name::from_slice(&[0u8; 0]).is_err()); + + // Test non-fixed-length definitions + if $upper_bound != $lower_bound { + assert!($name::from_slice(&[0u8; $upper_bound - 1]).is_ok()); + assert!($name::from_slice(&[0u8; $lower_bound + 1]).is_ok()); + } + } +)); + +#[cfg(test)] +macro_rules! test_as_bytes_and_get_length (($name:ident, $lower_bound:expr, $upper_bound:expr, $bytes_function:ident) => ( + #[test] + fn test_as_bytes() { + let test_upper = $name::from_slice(&[0u8; $upper_bound]).unwrap(); + let test_lower = $name::from_slice(&[0u8; $lower_bound]).unwrap(); + + assert_eq!(test_upper.$bytes_function().len(), test_upper.len()); + assert_eq!(test_upper.len(), $upper_bound); + + assert_eq!(test_lower.$bytes_function().len(), test_lower.len()); + assert_eq!(test_lower.len(), $lower_bound); + + assert_eq!(test_upper.is_empty(), false); + assert_eq!(test_lower.is_empty(), false); + + // Test non-fixed-length definitions + if $lower_bound != $upper_bound { + let test_upper = $name::from_slice(&[0u8; $upper_bound - 1]).unwrap(); + let test_lower = $name::from_slice(&[0u8; $lower_bound + 1]).unwrap(); + + assert_eq!(test_upper.$bytes_function().len(), test_upper.len()); + assert_eq!(test_upper.len(), $upper_bound - 1); + + assert_eq!(test_lower.$bytes_function().len(), test_lower.len()); + assert_eq!(test_lower.len(), $lower_bound + 1); + + assert_eq!(test_upper.is_empty(), false); + assert_eq!(test_lower.is_empty(), false); + } + } +)); + +#[cfg(test)] +#[cfg(feature = "safe_api")] +macro_rules! test_generate (($name:ident, $gen_length:expr) => ( + #[test] + #[cfg(feature = "safe_api")] + fn test_generate() { + let test_zero = $name::from_slice(&[0u8; $gen_length]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate(); + assert_ne!(test_zero, test_rand); + // A random generated one should always be $gen_length in length. + assert_eq!(test_rand.len(), $gen_length); + } +)); + +#[cfg(test)] +#[cfg(feature = "safe_api")] +macro_rules! test_omitted_debug (($name:ident, $upper_bound:expr) => ( + #[test] + #[cfg(feature = "safe_api")] + // format! is only available with std + fn test_omitted_debug() { + let secret = format!("{:?}", [0u8; $upper_bound].as_ref()); + let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $upper_bound]).unwrap()); + assert_eq!(test_debug_contents.contains(&secret), false); + } +)); + +#[cfg(test)] +#[cfg(feature = "safe_api")] +macro_rules! test_normal_debug (($name:ident, $upper_bound:expr) => ( + #[test] + #[cfg(feature = "safe_api")] + // format! is only available with std + fn test_normal_debug() { + let public = format!("{:?}", [0u8; $upper_bound].as_ref()); + let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $upper_bound]).unwrap()); + assert_eq!(test_debug_contents.contains(&public), true); + } +)); + +#[cfg(test)] +#[cfg(feature = "safe_api")] +macro_rules! test_from_slice_variable (($name:ident) => ( + #[test] + #[cfg(feature = "safe_api")] + fn test_from_slice_variable() { + assert!($name::from_slice(&[0u8; 512]).is_ok()); + assert!($name::from_slice(&[0u8; 256]).is_ok()); + assert!($name::from_slice(&[0u8; 1]).is_ok()); + assert!($name::from_slice(&[0u8; 0]).is_err()); + } +)); + +#[cfg(test)] +#[cfg(feature = "safe_api")] +macro_rules! test_generate_variable (($name:ident) => ( + #[test] + #[cfg(feature = "safe_api")] + fn test_generate_variable() { + assert!($name::generate(0).is_err()); + assert!($name::generate((isize::MAX as usize) + 1).is_err()); + assert!($name::generate(1).is_ok()); + assert!($name::generate(64).is_ok()); + + let test_zero = $name::from_slice(&[0u8; 128]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate(128).unwrap(); + assert_ne!(test_zero, test_rand); + assert_eq!(test_rand.len(), 128); + } +)); + +/// +/// Newtype implementation macros + +/// Macro to construct a type containing sensitive data, using a fixed-size +/// array. +/// +/// - $name: The name for the newtype. +/// +/// - $test_module_name: The name for the newtype's testing module (usually +/// "test_$name"). +/// +/// - $lower_bound/$upper_bound: An inclusive range that defines what length a +/// secret value might be. Used to validate length of `slice` in from_slice(). +/// $upper_bound also defines the `value` field array allocation size. +/// +/// - $gen_length: The amount of data to be randomly generated when using +/// generate(). +macro_rules! construct_secret_key { + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr)) => ( + $(#[$meta])* + /// + /// # Security: + /// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections + /// that the type implements. + /// + /// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted + /// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait + /// is implemented in such a way that the comparison happens in constant time. Thus, users should + /// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. + /// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. + /// ```rust + /// # #[cfg(feature = "safe_api")] { + /// use orion::hazardous::stream::chacha20::SecretKey; + /// + /// // Initialize a secret key with random bytes. + /// let secret_key = SecretKey::generate(); + /// + /// // Secure, constant-time comparison with a byte slice + /// assert_ne!(secret_key, &[0; 32][..]); + /// + /// // Secure, constant-time comparison with another SecretKey + /// assert_ne!(secret_key, SecretKey::generate()); + /// # } + /// # Ok::<(), orion::errors::UnknownCryptoError>(()) + /// ``` + pub struct $name { + value: [u8; $upper_bound], + original_length: usize, + } + + impl_omitted_debug_trait!($name); + impl_drop_trait!($name); + impl_ct_partialeq_trait!($name, unprotected_as_bytes); + + impl $name { + func_from_slice!($name, $lower_bound, $upper_bound); + func_unprotected_as_bytes!(); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + + test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound); + test_from_slice!($name, $lower_bound, $upper_bound); + test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, unprotected_as_bytes); + test_partial_eq!($name, $upper_bound); + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_omitted_debug!($name, $upper_bound); + } + } + ); + + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr, $gen_length:expr)) => ( + $(#[$meta])* + /// + /// # Security: + /// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections + /// that the type implements. + /// + /// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted + /// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait + /// is implemented in such a way that the comparison happens in constant time. Thus, users should + /// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. + /// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. + /// ```rust + /// # #[cfg(feature = "safe_api")] { + /// use orion::hazardous::stream::chacha20::SecretKey; + /// + /// // Initialize a secret key with random bytes. + /// let secret_key = SecretKey::generate(); + /// + /// // Secure, constant-time comparison with a byte slice + /// assert_ne!(secret_key, &[0; 32][..]); + /// + /// // Secure, constant-time comparison with another SecretKey + /// assert_ne!(secret_key, SecretKey::generate()); + /// # } + /// # Ok::<(), orion::errors::UnknownCryptoError>(()) + /// ``` + pub struct $name { + value: [u8; $upper_bound], + original_length: usize, + } + + impl_omitted_debug_trait!($name); + impl_drop_trait!($name); + impl_ct_partialeq_trait!($name, unprotected_as_bytes); + + impl $name { + func_from_slice!($name, $lower_bound, $upper_bound); + func_unprotected_as_bytes!(); + func_generate!($name, $upper_bound, $gen_length); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + + test_bound_parameters!($name, $lower_bound, $upper_bound, $gen_length); + test_from_slice!($name, $lower_bound, $upper_bound); + test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, unprotected_as_bytes); + test_partial_eq!($name, $upper_bound); + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_generate!($name, $gen_length); + test_omitted_debug!($name, $upper_bound); + } + } + ); +} + +/// Macro to construct a public type containing non-sensitive data, using a +/// fixed-size array. +/// +/// - $name: The name for the newtype. +/// +/// - $test_module_name: The name for the newtype's testing module (usually +/// "test_$name"). +/// +/// - $lower_bound/$upper_bound: An inclusive range that defines what length a +/// public value might be. Used to validate length of `slice` in from_slice(). +/// $upper_bound also defines the `value` field array allocation size. +/// +/// - $gen_length: The amount of data to be randomly generated when using +/// generate(). If not supplied, the public newtype will not have a +/// `generate()` function available. +macro_rules! construct_public { + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr)) => ( + #[derive(Clone, Copy)] + $(#[$meta])* + /// + pub struct $name { + pub(crate) value: [u8; $upper_bound], + original_length: usize, + } + + impl_ct_partialeq_trait!($name, as_ref); + impl_normal_debug_trait!($name); + impl_try_from_trait!($name); + impl_asref_trait!($name); + + #[cfg(feature = "serde")] + impl_serde_traits!($name, as_ref); + + impl $name { + func_from_slice!($name, $lower_bound, $upper_bound); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + // Replace $gen_length with $upper_bound since this doesn't have + // generate() function. + test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound); + test_from_slice!($name, $lower_bound, $upper_bound); + test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, as_ref); + test_partial_eq!($name, $upper_bound); + + #[cfg(feature = "serde")] + test_serde_impls!($name, $upper_bound); + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_normal_debug!($name, $upper_bound); + } + } + ); + + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr, $gen_length:expr)) => ( + #[derive(Clone, Copy)] + $(#[$meta])* + /// + pub struct $name { + pub(crate) value: [u8; $upper_bound], + original_length: usize, + } + + impl_ct_partialeq_trait!($name, as_ref); + impl_normal_debug_trait!($name); + impl_try_from_trait!($name); + impl_asref_trait!($name); + + #[cfg(feature = "serde")] + impl_serde_traits!($name, as_ref); + + impl $name { + func_from_slice!($name, $lower_bound, $upper_bound); + func_generate!($name, $upper_bound, $gen_length); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound); + test_from_slice!($name, $lower_bound, $upper_bound); + test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, as_ref); + test_partial_eq!($name, $upper_bound); + + #[cfg(feature = "serde")] + test_serde_impls!($name, $upper_bound); + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_normal_debug!($name, $upper_bound); + test_generate!($name, $gen_length); + } + } + ); +} + +/// Macro to construct a tag type that MACs return. +macro_rules! construct_tag { + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr)) => ( + #[derive(Clone)] + $(#[$meta])* + /// + /// # Security: + /// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections + /// that the type implements. + /// + /// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted + /// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait + /// is implemented in such a way that the comparison happens in constant time. Thus, users should + /// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. + /// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. + /// ```rust + /// use orion::hazardous::mac::hmac::sha512::Tag; + /// + /// // Initialize an arbitrary, 64-byte tag. + /// let tag = Tag::from_slice(&[1; 64])?; + /// + /// // Secure, constant-time comparison with a byte slice + /// assert_eq!(tag, &[1; 64][..]); + /// + /// // Secure, constant-time comparison with another Tag + /// assert_eq!(tag, Tag::from_slice(&[1; 64])?); + /// # Ok::<(), orion::errors::UnknownCryptoError>(()) + /// ``` + pub struct $name { + value: [u8; $upper_bound], + original_length: usize, + } + + impl_omitted_debug_trait!($name); + impl_drop_trait!($name); + impl_ct_partialeq_trait!($name, unprotected_as_bytes); + impl_try_from_trait!($name); + + #[cfg(feature = "serde")] + impl_serde_traits!($name, unprotected_as_bytes); + + impl $name { + func_from_slice!($name, $lower_bound, $upper_bound); + func_unprotected_as_bytes!(); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + // Replace $gen_length with $upper_bound since a tag doesn't have + // generate() function. + test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound); + test_from_slice!($name, $lower_bound, $upper_bound); + test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, unprotected_as_bytes); + test_partial_eq!($name, $upper_bound); + + #[cfg(feature = "serde")] + test_serde_impls!($name, $upper_bound); + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_omitted_debug!($name, $upper_bound); + } + } + ); +} + +/// Macro to construct a secret key used for HMAC. This pre-pads the given key +/// to the required length specified by the HMAC specifications. +macro_rules! construct_hmac_key { + ($(#[$meta:meta])* + ($name:ident, $sha2:ident, $sha2_outsize:expr, $test_module_name:ident, $size:expr)) => ( + $(#[$meta])* + /// + /// # Security: + /// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections + /// that the type implements. + /// + /// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted + /// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait + /// is implemented in such a way that the comparison happens in constant time. Thus, users should + /// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. + /// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. + /// ```rust + /// # #[cfg(feature = "safe_api")] { + /// use orion::hazardous::mac::hmac::sha512::SecretKey; + /// + /// // Initialize a secret key with random bytes. + /// let secret_key = SecretKey::generate(); + /// + /// // Secure, constant-time comparison with a byte slice + /// assert_ne!(secret_key, &[0; 32][..]); + /// + /// // Secure, constant-time comparison with another SecretKey + /// assert_ne!(secret_key, SecretKey::generate()); + /// # } + /// # Ok::<(), orion::errors::UnknownCryptoError>(()) + /// ``` + pub struct $name { + value: [u8; $size], + original_length: usize, + } + + impl_omitted_debug_trait!($name); + impl_drop_trait!($name); + impl_ct_partialeq_trait!($name, unprotected_as_bytes); + + impl $name { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> { + let mut secret_key = [0u8; $size]; + + let slice_len = slice.len(); + + if slice_len > $size { + secret_key[..$sha2_outsize].copy_from_slice(&$sha2::digest(slice)?.as_ref()); + } else { + secret_key[..slice_len].copy_from_slice(slice); + } + + Ok($name { value: secret_key, original_length: $size }) + } + + func_unprotected_as_bytes!(); + func_generate!($name, $size, $size); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + test_as_bytes_and_get_length!($name, $size, $size, unprotected_as_bytes); + test_partial_eq!($name, $size); + + #[test] + fn test_key_size() { + assert!($name::from_slice(&[0u8; $size]).is_ok()); + assert!($name::from_slice(&[0u8; $size - $size]).is_ok()); + assert!($name::from_slice(&[0u8; $size + 1]).is_ok()); + } + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_generate!($name, $size); + test_omitted_debug!($name, $size); + } + } + ); +} + +#[cfg(feature = "safe_api")] +/// Macro to construct a type containing sensitive data which is stored on the +/// heap. +macro_rules! construct_secret_key_variable_size { + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $default_size:expr)) => ( + #[cfg(feature = "safe_api")] + $(#[$meta])* + /// + /// # Security: + /// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections + /// that the type implements. + /// + /// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted + /// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait + /// is implemented in such a way that the comparison happens in constant time. Thus, users should + /// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. + /// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. + /// ```rust + /// # #[cfg(feature = "safe_api")] { + /// use orion::pwhash::Password; + /// + /// // Initialize a password with 32 random bytes. + /// let password = Password::generate(32)?; + /// + /// // Secure, constant-time comparison with a byte slice + /// assert_ne!(password, &[0; 32][..]); + /// + /// // Secure, constant-time comparison with another Password + /// assert_ne!(password, Password::generate(32)?); + /// # } + /// # Ok::<(), orion::errors::UnknownCryptoError>(()) + /// ``` + pub struct $name { + pub(crate) value: Vec<u8>, + original_length: usize, + } + + impl_omitted_debug_trait!($name); + impl_drop_trait!($name); + impl_ct_partialeq_trait!($name, unprotected_as_bytes); + impl_default_trait!($name, $default_size); + + impl $name { + func_from_slice_variable_size!($name); + func_unprotected_as_bytes!(); + func_len!(); + func_is_empty!(); + func_generate_variable_size!($name); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + + test_from_slice_variable!($name); + test_as_bytes_and_get_length!($name, 1, $default_size + 1, unprotected_as_bytes); + test_generate_variable!($name); + test_omitted_debug!($name, $default_size); + test_partial_eq!($name, $default_size); + } + ); +} + +#[cfg(feature = "safe_api")] +/// Macro to construct a type containing non-sensitive which is stored on the +/// heap. +macro_rules! construct_salt_variable_size { + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $default_size:expr)) => ( + #[cfg(feature = "safe_api")] + $(#[$meta])* + /// + pub struct $name { + value: Vec<u8>, + original_length: usize, + } + + impl_normal_debug_trait!($name); + impl_default_trait!($name, $default_size); + impl_ct_partialeq_trait!($name, as_ref); + impl_asref_trait!($name); + impl_try_from_trait!($name); + + #[cfg(feature = "serde")] + impl_serde_traits!($name, as_ref); + + impl $name { + func_from_slice_variable_size!($name); + func_len!(); + func_is_empty!(); + func_generate_variable_size!($name); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + + test_from_slice_variable!($name); + test_as_bytes_and_get_length!($name, 1, $default_size + 1, as_ref); + test_generate_variable!($name); + test_partial_eq!($name, $default_size); + test_normal_debug!($name, $default_size); + + #[cfg(feature = "serde")] + test_serde_impls!($name, $default_size); + } + ); +} diff --git a/vendor/orion/src/util/endianness.rs b/vendor/orion/src/util/endianness.rs new file mode 100644 index 0000000..b8d0d92 --- /dev/null +++ b/vendor/orion/src/util/endianness.rs @@ -0,0 +1,371 @@ +// MIT License + +// Copyright (c) 2019-2023 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. + +use core::convert::TryInto; +use core::mem; + +macro_rules! impl_store_into { + ($type_alias:ty, $conv_function:ident, $func_name:ident) => { + /// Store bytes in `src` in `dst`. + pub fn $func_name(src: &[$type_alias], dst: &mut [u8]) { + let type_alias_len = mem::size_of::<$type_alias>(); + // The length of src must be evenly divisible with the length of dst, + // making sure .chunks_exact() leaves no remainder. + assert_eq!(mem::size_of_val(src), dst.len()); + + for (src_elem, dst_chunk) in src.iter().zip(dst.chunks_exact_mut(type_alias_len)) { + dst_chunk.copy_from_slice(&src_elem.$conv_function()); + } + } + }; +} + +macro_rules! impl_load_into { + ($type_alias:ty, $type_alias_expr:ident, $conv_function:ident, $func_name:ident) => { + /// Load bytes in `src` into `dst`. + pub fn $func_name(src: &[u8], dst: &mut [$type_alias]) { + let type_alias_len = mem::size_of::<$type_alias>(); + // The length of src must be evenly divisible with the length of dst, + // making sure .chunks_exact() leaves no remainder. + assert_eq!(mem::size_of_val(dst), src.len()); + + for (src_chunk, dst_elem) in src.chunks_exact(type_alias_len).zip(dst.iter_mut()) { + // The above assert and this debug assert should prove that .unwrap() + // cannot panic using TryInto. + debug_assert_eq!(src_chunk.len(), type_alias_len); + *dst_elem = $type_alias_expr::$conv_function(src_chunk.try_into().unwrap()); + } + } + }; +} + +macro_rules! impl_load { + ($type_alias:ty, $type_alias_expr:ident, $conv_function:ident, $func_name:ident) => { + /// Convert bytes in `src` to a given primitive. + pub fn $func_name(src: &[u8]) -> $type_alias { + // Satisfying this assert should prove that using TryInto + // cannot panic. + assert_eq!(mem::size_of::<$type_alias>(), src.len()); + $type_alias_expr::$conv_function(src.try_into().unwrap()) + } + }; +} + +impl_load!(u32, u32, from_le_bytes, load_u32_le); + +#[cfg(test)] +impl_load_into!(u32, u32, from_le_bytes, load_u32_into_le); + +impl_load_into!(u64, u64, from_le_bytes, load_u64_into_le); + +impl_store_into!(u32, to_le_bytes, store_u32_into_le); + +#[cfg(any(feature = "safe_api", feature = "alloc", test))] +impl_store_into!(u64, to_le_bytes, store_u64_into_le); + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + macro_rules! test_empty_src_panic { + ($test_name:ident, $src_val:expr, $dst_val:expr, $func_to_test:expr) => { + #[test] + #[should_panic] + fn $test_name() { + let mut dst_load = $dst_val; + $func_to_test($src_val, &mut dst_load); + } + }; + } + + macro_rules! test_dst_length_panic { + ($test_name:ident, $src_val:expr, $dst_val:expr, $func_to_test:expr) => { + #[test] + #[should_panic] + fn $test_name() { + let mut dst_load = $dst_val; + $func_to_test($src_val, &mut dst_load); + } + }; + } + + macro_rules! test_dst_length_ok { + ($test_name:ident, $src_val:expr, $dst_val:expr, $func_to_test:expr) => { + #[test] + fn $test_name() { + let mut dst_load = $dst_val; + $func_to_test($src_val, &mut dst_load); + } + }; + } + + test_empty_src_panic! {test_panic_empty_load_u32_le, &[0u8; 0], [0u32; 4], load_u32_into_le} + test_empty_src_panic! {test_panic_empty_load_u64_le, &[0u8; 0], [0u64; 4], load_u64_into_le} + + test_empty_src_panic! {test_panic_empty_store_u32_le, &[0u32; 0], [0u8; 24], store_u32_into_le} + test_empty_src_panic! {test_panic_empty_store_u64_le, &[0u64; 0], [0u8; 24], store_u64_into_le} + + // -1 too low + test_dst_length_panic! {test_dst_length_load_u32_le_low, &[0u8; 64], [0u32; 15], load_u32_into_le} + test_dst_length_panic! {test_dst_length_load_u64_le_low, &[0u8; 64], [0u64; 7], load_u64_into_le} + + test_dst_length_panic! {test_dst_length_store_u32_le_low, &[0u32; 15], [0u8; 64], store_u32_into_le} + test_dst_length_panic! {test_dst_length_store_u64_le_low, &[0u64; 7], [0u8; 64], store_u64_into_le} + + // +1 too high + test_dst_length_panic! {test_dst_length_load_u32_le_high, &[0u8; 64], [0u32; 17], load_u32_into_le} + test_dst_length_panic! {test_dst_length_load_u64_le_high, &[0u8; 64], [0u64; 9], load_u64_into_le} + + test_dst_length_panic! {test_dst_length_store_u32_le_high, &[0u32; 17], [0u8; 64], store_u32_into_le} + test_dst_length_panic! {test_dst_length_store_u64_le_high, &[0u64; 9], [0u8; 64], store_u64_into_le} + + // Ok + test_dst_length_ok! {test_dst_length_load_u32_le_ok, &[0u8; 64], [0u32; 16], load_u32_into_le} + test_dst_length_ok! {test_dst_length_load_u64_le_ok, &[0u8; 64], [0u64; 8], load_u64_into_le} + + test_dst_length_ok! {test_dst_length_store_u32_le_ok, &[0u32; 16], [0u8; 64], store_u32_into_le} + test_dst_length_ok! {test_dst_length_store_u64_le_ok, &[0u64; 8], [0u8; 64], store_u64_into_le} + + #[test] + #[should_panic] + fn test_load_single_src_high() { + load_u32_le(&[0u8; 5]); + } + + #[test] + #[should_panic] + fn test_load_single_src_low() { + load_u32_le(&[0u8; 3]); + } + + #[test] + fn test_load_single_src_ok() { + load_u32_le(&[0u8; 4]); + } + + #[test] + fn test_results_store_and_load_u32_into_le() { + let input_0: [u32; 2] = [777190791, 1465409568]; + let input_1: [u32; 4] = [3418616323, 2289579672, 172726903, 1048927929]; + let input_2: [u32; 6] = [ + 84693101, 443297962, 3962861724, 3081916164, 4167874952, 3982893227, + ]; + let input_3: [u32; 8] = [ + 2761719494, 242571916, 3097304063, 3924274282, 1553851098, 3673278295, 3531531406, + 2347852690, + ]; + + let expected_0: [u8; 8] = [135, 253, 82, 46, 32, 96, 88, 87]; + let expected_1: [u8; 16] = [ + 3, 242, 195, 203, 152, 54, 120, 136, 119, 154, 75, 10, 185, 94, 133, 62, + ]; + let expected_2: [u8; 24] = [ + 109, 80, 12, 5, 170, 48, 108, 26, 156, 120, 52, 236, 4, 79, 178, 183, 136, 185, 108, + 248, 171, 32, 102, 237, + ]; + let expected_3: [u8; 32] = [ + 198, 126, 156, 164, 140, 90, 117, 14, 255, 27, 157, 184, 106, 172, 231, 233, 218, 226, + 157, 92, 87, 199, 241, 218, 142, 228, 126, 210, 146, 99, 241, 139, + ]; + + let mut actual_bytes_0 = [0u8; 8]; + let mut actual_bytes_1 = [0u8; 16]; + let mut actual_bytes_2 = [0u8; 24]; + let mut actual_bytes_3 = [0u8; 32]; + + store_u32_into_le(&input_0, &mut actual_bytes_0); + store_u32_into_le(&input_1, &mut actual_bytes_1); + store_u32_into_le(&input_2, &mut actual_bytes_2); + store_u32_into_le(&input_3, &mut actual_bytes_3); + + assert_eq!(actual_bytes_0, expected_0); + assert_eq!(actual_bytes_1, expected_1); + assert_eq!(actual_bytes_2, expected_2); + assert_eq!(actual_bytes_3, expected_3); + + let mut actual_nums_0 = [0u32; 2]; + let mut actual_nums_1 = [0u32; 4]; + let mut actual_nums_2 = [0u32; 6]; + let mut actual_nums_3 = [0u32; 8]; + + load_u32_into_le(&actual_bytes_0, &mut actual_nums_0); + load_u32_into_le(&actual_bytes_1, &mut actual_nums_1); + load_u32_into_le(&actual_bytes_2, &mut actual_nums_2); + load_u32_into_le(&actual_bytes_3, &mut actual_nums_3); + + assert_eq!(actual_nums_0, input_0); + assert_eq!(actual_nums_1, input_1); + assert_eq!(actual_nums_2, input_2); + assert_eq!(actual_nums_3, input_3); + } + + #[test] + fn test_results_store_and_load_u64_into_le() { + let input_0: [u64; 2] = [3449173576222258260, 2574723713182514848]; + let input_1: [u64; 4] = [ + 18418572897904167042, + 8576666536239673655, + 11410394363908906546, + 7465319841649779999, + ]; + let input_2: [u64; 6] = [ + 9356732802025012686, + 185726711773006573, + 11478604380402216982, + 11229612629557120299, + 2892361689551487626, + 11014300370630005317, + ]; + let input_3: [u64; 8] = [ + 9519534723912119720, + 6001603601558183532, + 8164850737304360888, + 571607234094878696, + 4752095875230140457, + 13190954815003641110, + 16657196750477544576, + 10329042493888204415, + ]; + + let expected_0: [u8; 16] = [ + 84, 52, 100, 211, 23, 237, 221, 47, 160, 190, 4, 95, 147, 65, 187, 35, + ]; + let expected_1: [u8; 32] = [ + 130, 8, 55, 1, 119, 234, 155, 255, 55, 177, 139, 9, 198, 112, 6, 119, 50, 222, 232, 23, + 56, 221, 89, 158, 31, 229, 53, 208, 215, 36, 154, 103, + ]; + let expected_2: [u8; 48] = [ + 206, 61, 242, 202, 232, 202, 217, 129, 237, 10, 136, 216, 117, 213, 147, 2, 22, 240, + 29, 35, 222, 49, 76, 159, 43, 13, 254, 133, 48, 153, 215, 155, 138, 66, 170, 219, 161, + 187, 35, 40, 69, 210, 218, 176, 212, 167, 218, 152, + ]; + let expected_3: [u8; 64] = [ + 168, 53, 199, 13, 101, 46, 28, 132, 108, 158, 148, 129, 173, 250, 73, 83, 184, 215, 28, + 129, 124, 96, 79, 113, 232, 207, 68, 59, 192, 193, 238, 7, 41, 8, 177, 85, 5, 214, 242, + 65, 22, 69, 133, 252, 131, 175, 15, 183, 128, 76, 1, 226, 48, 64, 42, 231, 127, 14, 31, + 46, 108, 33, 88, 143, + ]; + + let mut actual_bytes_0 = [0u8; 16]; + let mut actual_bytes_1 = [0u8; 32]; + let mut actual_bytes_2 = [0u8; 48]; + let mut actual_bytes_3 = [0u8; 64]; + + store_u64_into_le(&input_0, &mut actual_bytes_0); + store_u64_into_le(&input_1, &mut actual_bytes_1); + store_u64_into_le(&input_2, &mut actual_bytes_2); + store_u64_into_le(&input_3, &mut actual_bytes_3); + + assert_eq!(actual_bytes_0, expected_0); + assert_eq!(actual_bytes_1, expected_1); + assert_eq!(actual_bytes_2.as_ref(), expected_2.as_ref()); + assert_eq!(actual_bytes_3.as_ref(), expected_3.as_ref()); + + let mut actual_nums_0 = [0u64; 2]; + let mut actual_nums_1 = [0u64; 4]; + let mut actual_nums_2 = [0u64; 6]; + let mut actual_nums_3 = [0u64; 8]; + + load_u64_into_le(&actual_bytes_0, &mut actual_nums_0); + load_u64_into_le(&actual_bytes_1, &mut actual_nums_1); + load_u64_into_le(&actual_bytes_2, &mut actual_nums_2); + load_u64_into_le(&actual_bytes_3, &mut actual_nums_3); + + assert_eq!(actual_nums_0, input_0); + assert_eq!(actual_nums_1, input_1); + assert_eq!(actual_nums_2, input_2); + assert_eq!(actual_nums_3, input_3); + } + + #[test] + fn test_results_load_u32() { + let input_0: [u8; 4] = [203, 12, 195, 63]; + let expected_0: u32 = 1069747403; + + assert_eq!(load_u32_le(&input_0), expected_0); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Load and store should not change the result. + fn prop_load_store_u32_le(src: Vec<u8>) -> bool { + if !src.is_empty() && src.len() % 4 == 0 { + let mut dst_load = vec![0u32; src.len() / 4]; + load_u32_into_le(&src[..], &mut dst_load); + // Test that loading a single also is working correctly + dst_load[0] = load_u32_le(&src[..4]); + let mut dst_store = src.clone(); + store_u32_into_le(&dst_load[..], &mut dst_store); + + dst_store == src + } else { + // Otherwise above functions panic. + true + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Load and store should not change the result. + fn prop_load_store_u64_le(src: Vec<u8>) -> bool { + if !src.is_empty() && src.len() % 8 == 0 { + let mut dst_load = vec![0u64; src.len() / 8]; + load_u64_into_le(&src[..], &mut dst_load); + let mut dst_store = src.clone(); + store_u64_into_le(&dst_load[..], &mut dst_store); + + dst_store == src + } else { + // Otherwise above functions panic. + true + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Store and load should not change the result. + fn prop_store_load_u32_le(src: Vec<u32>) -> bool { + let mut dst_store = vec![0u8; src.len() * 4]; + store_u32_into_le(&src[..], &mut dst_store); + let mut dst_load = src.clone(); + load_u32_into_le(&dst_store[..], &mut dst_load); + if dst_store.len() >= 4 { + // Test that loading a single also is working correctly + dst_load[0] = load_u32_le(&dst_store[..4]); + } + + dst_load == src + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Store and load should not change the result. + fn prop_store_load_u64_le(src: Vec<u64>) -> bool { + let mut dst_store = vec![0u8; src.len() * 8]; + store_u64_into_le(&src[..], &mut dst_store); + let mut dst_load = src.clone(); + load_u64_into_le(&dst_store[..], &mut dst_load); + + dst_load == src + } +} diff --git a/vendor/orion/src/util/mod.rs b/vendor/orion/src/util/mod.rs new file mode 100644 index 0000000..27fc291 --- /dev/null +++ b/vendor/orion/src/util/mod.rs @@ -0,0 +1,186 @@ +// MIT License + +// Copyright (c) 2018-2023 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. + +use crate::errors; +use subtle::ConstantTimeEq; + +/// xor_slices!(src, destination): XOR $src into $destination slice. +/// Uses iter() and .zip(), so it short-circuits on the slice that has +/// the smallest length. +macro_rules! xor_slices { + ($src:expr, $destination:expr) => { + for (inplace, _src_elem) in $destination.iter_mut().zip($src.iter()) { + *inplace ^= _src_elem; + } + }; +} + +pub(crate) mod endianness; +pub(crate) mod u32x4; +pub(crate) mod u64x4; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +#[cfg(feature = "safe_api")] +/// Generate random bytes using a CSPRNG. Not available in `no_std` context. +/// +/// # About: +/// This function can be used to generate cryptographic keys, salts or other +/// values that rely on strong randomness. Please note that most keys and other +/// types used throughout Orion, implement their own `generate()` function and +/// it is strongly preferred to use those, compared to [`secure_rand_bytes()`]. +/// +/// This uses [`getrandom`]. +/// +/// # Parameters: +/// - `dst`: Destination buffer for the randomly generated bytes. The amount of +/// bytes to be generated is +/// implied by the length of `dst`. +/// +/// # Errors: +/// An error will be returned if: +/// - `dst` is empty. +/// +/// # Panics: +/// A panic will occur if: +/// - Failure to generate random bytes securely. +/// - The platform is not supported by [`getrandom`]. +/// +/// # Example: +/// ```rust +/// use orion::util; +/// +/// let mut salt = [0u8; 64]; +/// util::secure_rand_bytes(&mut salt)?; +/// # Ok::<(), orion::errors::UnknownCryptoError>(()) +/// ``` +/// [`getrandom`]: https://github.com/rust-random/getrandom +pub fn secure_rand_bytes(dst: &mut [u8]) -> Result<(), errors::UnknownCryptoError> { + if dst.is_empty() { + return Err(errors::UnknownCryptoError); + } + + getrandom::getrandom(dst).unwrap(); + + Ok(()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Compare two equal length slices in constant time. +/// +/// # About: +/// Compare two equal length slices, in constant time, using the +/// [subtle](https://github.com/dalek-cryptography/subtle) crate. +/// +/// # Parameters: +/// - `a`: The first slice used in the comparison. +/// - `b`: The second slice used in the comparison. +/// +/// # Errors: +/// An error will be returned if: +/// - `a` and `b` do not have the same length. +/// - `a` is not equal to `b`. +/// +/// # Example: +/// ```rust +/// # #[cfg(feature = "safe_api")] { +/// use orion::util; +/// +/// let mut rnd_bytes = [0u8; 64]; +/// assert!(util::secure_cmp(&rnd_bytes, &[0u8; 64]).is_ok()); +/// +/// util::secure_rand_bytes(&mut rnd_bytes)?; +/// assert!(util::secure_cmp(&rnd_bytes, &[0u8; 64]).is_err()); +/// # } +/// # Ok::<(), orion::errors::UnknownCryptoError>(()) +/// ``` +pub fn secure_cmp(a: &[u8], b: &[u8]) -> Result<(), errors::UnknownCryptoError> { + if a.ct_eq(b).into() { + Ok(()) + } else { + Err(errors::UnknownCryptoError) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "safe_api")] + #[test] + fn rand_key_len_ok() { + let mut dst = [0u8; 64]; + secure_rand_bytes(&mut dst).unwrap(); + } + + #[cfg(feature = "safe_api")] + #[test] + fn rand_key_len_error() { + let mut dst = [0u8; 0]; + assert!(secure_rand_bytes(&mut dst).is_err()); + + let err = secure_rand_bytes(&mut dst).unwrap_err(); + assert_eq!(err, errors::UnknownCryptoError); + } + + #[test] + fn test_ct_eq_ok() { + let buf_1 = [0x06; 10]; + let buf_2 = [0x06; 10]; + + assert!(secure_cmp(&buf_1, &buf_2).is_ok()); + assert!(secure_cmp(&buf_2, &buf_1).is_ok()); + } + + #[test] + fn test_ct_eq_diff_len() { + let buf_1 = [0x06; 10]; + let buf_2 = [0x06; 5]; + + assert!(secure_cmp(&buf_1, &buf_2).is_err()); + assert!(secure_cmp(&buf_2, &buf_1).is_err()); + } + + #[test] + fn test_ct_ne() { + let buf_1 = [0x06; 10]; + let buf_2 = [0x76; 10]; + + assert!(secure_cmp(&buf_1, &buf_2).is_err()); + assert!(secure_cmp(&buf_2, &buf_1).is_err()); + } + + #[test] + fn test_ct_ne_reg() { + assert!(secure_cmp(&[0], &[0, 1]).is_err()); + assert!(secure_cmp(&[0, 1], &[0]).is_err()); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_secure_cmp(a: Vec<u8>, b: Vec<u8>) -> bool { + if a == b { + secure_cmp(&a, &b).is_ok() + } else { + secure_cmp(&a, &b).is_err() + } + } +} diff --git a/vendor/orion/src/util/u32x4.rs b/vendor/orion/src/util/u32x4.rs new file mode 100644 index 0000000..c1ff1bc --- /dev/null +++ b/vendor/orion/src/util/u32x4.rs @@ -0,0 +1,98 @@ +// MIT License + +// Copyright (c) 2019-2023 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. + +#[derive(Clone, Copy)] +pub(crate) struct U32x4( + pub(crate) u32, + pub(crate) u32, + pub(crate) u32, + pub(crate) u32, +); + +impl core::ops::BitXor for U32x4 { + type Output = Self; + + #[must_use] + fn bitxor(self, _rhs: Self) -> Self::Output { + Self( + self.0 ^ _rhs.0, + self.1 ^ _rhs.1, + self.2 ^ _rhs.2, + self.3 ^ _rhs.3, + ) + } +} + +impl zeroize::Zeroize for U32x4 { + fn zeroize(&mut self) { + self.0.zeroize(); + self.1.zeroize(); + self.2.zeroize(); + self.3.zeroize(); + } +} + +impl U32x4 { + #[must_use] + pub(crate) const fn wrapping_add(self, _rhs: Self) -> Self { + Self( + self.0.wrapping_add(_rhs.0), + self.1.wrapping_add(_rhs.1), + self.2.wrapping_add(_rhs.2), + self.3.wrapping_add(_rhs.3), + ) + } + + #[must_use] + pub(crate) const fn shl_1(self) -> Self { + Self(self.1, self.2, self.3, self.0) + } + + #[must_use] + pub(crate) const fn shl_2(self) -> Self { + Self(self.2, self.3, self.0, self.1) + } + + #[must_use] + pub(crate) const fn shl_3(self) -> Self { + Self(self.3, self.0, self.1, self.2) + } + + #[must_use] + pub(crate) const fn rotate_left(self, n: u32) -> Self { + Self( + self.0.rotate_left(n), + self.1.rotate_left(n), + self.2.rotate_left(n), + self.3.rotate_left(n), + ) + } + + pub(crate) fn store_into_le(&self, slice_in: &mut [u8]) { + debug_assert_eq!(slice_in.len(), core::mem::size_of::<u32>() * 4); + let mut iter = slice_in.chunks_exact_mut(core::mem::size_of::<u32>()); + iter.next().unwrap().copy_from_slice(&self.0.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.1.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.2.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.3.to_le_bytes()); + } +} diff --git a/vendor/orion/src/util/u64x4.rs b/vendor/orion/src/util/u64x4.rs new file mode 100644 index 0000000..bc181c4 --- /dev/null +++ b/vendor/orion/src/util/u64x4.rs @@ -0,0 +1,114 @@ +// MIT License + +// Copyright (c) 2019-2023 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. + +#[derive(Clone, Copy, Default)] +pub(crate) struct U64x4( + pub(crate) u64, + pub(crate) u64, + pub(crate) u64, + pub(crate) u64, +); + +impl core::ops::BitXor for U64x4 { + type Output = Self; + + #[must_use] + fn bitxor(self, _rhs: Self) -> Self::Output { + Self( + self.0 ^ _rhs.0, + self.1 ^ _rhs.1, + self.2 ^ _rhs.2, + self.3 ^ _rhs.3, + ) + } +} + +impl core::ops::BitXorAssign for U64x4 { + fn bitxor_assign(&mut self, _rhs: Self) { + self.0 ^= _rhs.0; + self.1 ^= _rhs.1; + self.2 ^= _rhs.2; + self.3 ^= _rhs.3; + } +} + +impl zeroize::Zeroize for U64x4 { + fn zeroize(&mut self) { + self.0.zeroize(); + self.1.zeroize(); + self.2.zeroize(); + self.3.zeroize(); + } +} + +#[cfg(test)] +impl PartialEq<U64x4> for U64x4 { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 && self.1 == other.1 && self.2 == other.2 && self.3 == other.3 + } +} + +impl U64x4 { + #[must_use] + pub(crate) const fn wrapping_add(self, _rhs: Self) -> Self { + Self( + self.0.wrapping_add(_rhs.0), + self.1.wrapping_add(_rhs.1), + self.2.wrapping_add(_rhs.2), + self.3.wrapping_add(_rhs.3), + ) + } + + #[must_use] + pub(crate) const fn shl_1(self) -> Self { + Self(self.1, self.2, self.3, self.0) + } + + #[must_use] + pub(crate) const fn shl_2(self) -> Self { + Self(self.2, self.3, self.0, self.1) + } + + #[must_use] + pub(crate) const fn shl_3(self) -> Self { + Self(self.3, self.0, self.1, self.2) + } + + #[must_use] + pub(crate) const fn rotate_right(self, n: u32) -> Self { + Self( + self.0.rotate_right(n), + self.1.rotate_right(n), + self.2.rotate_right(n), + self.3.rotate_right(n), + ) + } + + pub(crate) fn store_into_le(self, slice_in: &mut [u8]) { + debug_assert_eq!(slice_in.len(), core::mem::size_of::<u64>() * 4); + let mut iter = slice_in.chunks_exact_mut(core::mem::size_of::<u64>()); + iter.next().unwrap().copy_from_slice(&self.0.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.1.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.2.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.3.to_le_bytes()); + } +} |