diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
commit | 9918693037dce8aa4bb6f08741b6812923486c18 (patch) | |
tree | 21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /vendor/orion/src/hazardous | |
parent | Releasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff) | |
download | rustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip |
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/orion/src/hazardous')
18 files changed, 2658 insertions, 62 deletions
diff --git a/vendor/orion/src/hazardous/aead/chacha20poly1305.rs b/vendor/orion/src/hazardous/aead/chacha20poly1305.rs index bc1175809..821125381 100644 --- a/vendor/orion/src/hazardous/aead/chacha20poly1305.rs +++ b/vendor/orion/src/hazardous/aead/chacha20poly1305.rs @@ -123,10 +123,10 @@ use core::convert::TryInto; use zeroize::Zeroizing; /// The initial counter used for encryption and decryption. -const ENC_CTR: u32 = 1; +pub(crate) const ENC_CTR: u32 = 1; /// The initial counter used for Poly1305 key generation. -const AUTH_CTR: u32 = 0; +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; @@ -147,7 +147,7 @@ pub(crate) fn poly1305_key_gen( } /// Authenticates the ciphertext, ad and their lengths. -fn process_authentication( +pub(crate) fn process_authentication( auth_ctx: &mut Poly1305, ad: &[u8], ciphertext: &[u8], diff --git a/vendor/orion/src/hazardous/cae/chacha20poly1305blake2b.rs b/vendor/orion/src/hazardous/cae/chacha20poly1305blake2b.rs new file mode 100644 index 000000000..19cef00fa --- /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 000000000..270821b61 --- /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 000000000..25d3c32ef --- /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/x25519.rs b/vendor/orion/src/hazardous/ecc/x25519.rs index 4bbbbc869..806777feb 100644 --- a/vendor/orion/src/hazardous/ecc/x25519.rs +++ b/vendor/orion/src/hazardous/ecc/x25519.rs @@ -68,6 +68,11 @@ 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; @@ -86,9 +91,9 @@ const BASEPOINT: [u8; 32] = [ /// The result of computing a shared secret with a low order point. const LOW_ORDER_POINT_RESULT: [u8; 32] = [0u8; 32]; -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy)] /// Represent an element in the curve field. -struct FieldElement([u64; 5]); +struct FieldElement(fiat_25519_tight_field_element); impl Eq for FieldElement {} @@ -99,15 +104,25 @@ impl PartialEq for FieldElement { } } +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 { - use fiat_curve25519_u64::fiat_25519_carry_mul; + 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); - let mut ret = [0u64; 5]; - fiat_25519_carry_mul(&mut ret, &self.0, &rhs.0); + fiat_25519_carry_mul(&mut ret, &self_relaxed, &rhs_relaxed); Self(ret) } @@ -118,12 +133,11 @@ impl Add for FieldElement { type Output = Self; fn add(self, rhs: Self) -> Self::Output { - use fiat_curve25519_u64::{fiat_25519_add, fiat_25519_carry}; + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + let mut ret_add = fiat_25519_loose_field_element([0u64; 5]); - let mut ret = [0u64; 5]; - fiat_25519_add(&mut ret, &self.0, &rhs.0); - let tmp = ret; - fiat_25519_carry(&mut ret, &tmp); + fiat_25519_add(&mut ret_add, &self.0, &rhs.0); + fiat_25519_carry(&mut ret, &ret_add); Self(ret) } @@ -134,12 +148,11 @@ impl Sub for FieldElement { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { - use fiat_curve25519_u64::{fiat_25519_carry, fiat_25519_sub}; + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + let mut ret_sub = fiat_25519_loose_field_element([0u64; 5]); - let mut ret = [0u64; 5]; - fiat_25519_sub(&mut ret, &self.0, &rhs.0); - let tmp = ret; - fiat_25519_carry(&mut ret, &tmp); + fiat_25519_sub(&mut ret_sub, &self.0, &rhs.0); + fiat_25519_carry(&mut ret, &ret_sub); Self(ret) } @@ -148,12 +161,16 @@ impl Sub for FieldElement { impl FieldElement { /// Create a `FieldElement` that is `0`. fn zero() -> Self { - Self([0u64, 0u64, 0u64, 0u64, 0u64]) + Self(fiat_25519_tight_field_element([ + 0u64, 0u64, 0u64, 0u64, 0u64, + ])) } /// Create a `FieldElement` that is `1`. fn one() -> Self { - Self([1u64, 0u64, 0u64, 0u64, 0u64]) + Self(fiat_25519_tight_field_element([ + 1u64, 0u64, 0u64, 0u64, 0u64, + ])) } /// Serialize the `FieldElement` as a byte-array. @@ -179,7 +196,7 @@ impl FieldElement { 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 = [0u64; 5]; + let mut ret = fiat_25519_tight_field_element([0u64; 5]); fiat_25519_from_bytes(&mut ret, &temp); Self(ret) @@ -196,26 +213,28 @@ impl FieldElement { let tmp_a = *a; let tmp_b = *b; - fiat_25519_selectznz(&mut a.0, swap, &tmp_a.0, &tmp_b.0); - fiat_25519_selectznz(&mut b.0, swap, &tmp_b.0, &tmp_a.0); + 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 { - use fiat_curve25519_u64::fiat_25519_carry_square; + let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]); + let mut ret = fiat_25519_tight_field_element([0u64; 5]); - let mut ret = [0u64; 5]; - fiat_25519_carry_square(&mut ret, &self.0); + 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 { - use fiat_curve25519_u64::fiat_25519_carry_scmul_121666; + let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]); + let mut ret = fiat_25519_tight_field_element([0u64; 5]); - let mut ret = [0u64; 5]; - fiat_25519_carry_scmul_121666(&mut ret, &self.0); + fiat_25519_relax(&mut self_relaxed, &self.0); + fiat_25519_carry_scmul_121666(&mut ret, &self_relaxed); Self(ret) } diff --git a/vendor/orion/src/hazardous/hash/mod.rs b/vendor/orion/src/hazardous/hash/mod.rs index ad3ac155e..01b04c22f 100644 --- a/vendor/orion/src/hazardous/hash/mod.rs +++ b/vendor/orion/src/hazardous/hash/mod.rs @@ -23,5 +23,8 @@ /// 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/sha256.rs b/vendor/orion/src/hazardous/hash/sha2/sha256.rs index 737660715..720d8053d 100644 --- a/vendor/orion/src/hazardous/hash/sha2/sha256.rs +++ b/vendor/orion/src/hazardous/hash/sha2/sha256.rs @@ -89,7 +89,7 @@ use super::w32::WordU32; /// SHA256 streaming state. pub(crate) struct V256; -impl Variant<WordU32, { N_CONSTS }> for V256 { +impl Variant<WordU32, N_CONSTS> for V256 { #[rustfmt::skip] #[allow(clippy::unreadable_literal)] /// The SHA256 constants as defined in FIPS 180-4. @@ -144,7 +144,7 @@ impl Variant<WordU32, { N_CONSTS }> for V256 { #[derive(Clone, Debug)] /// SHA256 streaming state. pub struct Sha256 { - pub(crate) _state: State<WordU32, V256, { SHA256_BLOCKSIZE }, { SHA256_OUTSIZE }, { N_CONSTS }>, + pub(crate) _state: State<WordU32, V256, SHA256_BLOCKSIZE, SHA256_OUTSIZE, N_CONSTS>, } impl Default for Sha256 { @@ -157,9 +157,7 @@ impl Sha256 { /// Initialize a `Sha256` struct. pub fn new() -> Self { Self { - _state: - State::<WordU32, V256, { SHA256_BLOCKSIZE }, { SHA256_OUTSIZE }, { N_CONSTS }>::_new( - ), + _state: State::<WordU32, V256, SHA256_BLOCKSIZE, SHA256_OUTSIZE, N_CONSTS>::_new(), } } diff --git a/vendor/orion/src/hazardous/hash/sha2/sha384.rs b/vendor/orion/src/hazardous/hash/sha2/sha384.rs index e4df99d7a..c0430512e 100644 --- a/vendor/orion/src/hazardous/hash/sha2/sha384.rs +++ b/vendor/orion/src/hazardous/hash/sha2/sha384.rs @@ -88,7 +88,7 @@ const N_CONSTS: usize = 80; #[derive(Clone)] pub(crate) struct V384; -impl Variant<WordU64, { N_CONSTS }> for 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; @@ -124,7 +124,7 @@ impl Variant<WordU64, { N_CONSTS }> for V384 { #[derive(Clone, Debug)] /// SHA384 streaming state. pub struct Sha384 { - pub(crate) _state: State<WordU64, V384, { SHA384_BLOCKSIZE }, { SHA384_OUTSIZE }, { N_CONSTS }>, + pub(crate) _state: State<WordU64, V384, SHA384_BLOCKSIZE, SHA384_OUTSIZE, N_CONSTS>, } impl Default for Sha384 { @@ -137,9 +137,7 @@ impl Sha384 { /// Initialize a `Sha384` struct. pub fn new() -> Self { Self { - _state: - State::<WordU64, V384, { SHA384_BLOCKSIZE }, { SHA384_OUTSIZE }, { N_CONSTS }>::_new( - ), + _state: State::<WordU64, V384, SHA384_BLOCKSIZE, SHA384_OUTSIZE, N_CONSTS>::_new(), } } diff --git a/vendor/orion/src/hazardous/hash/sha2/sha512.rs b/vendor/orion/src/hazardous/hash/sha2/sha512.rs index f582f78c2..0e865c57d 100644 --- a/vendor/orion/src/hazardous/hash/sha2/sha512.rs +++ b/vendor/orion/src/hazardous/hash/sha2/sha512.rs @@ -88,7 +88,7 @@ const N_CONSTS: usize = 80; #[derive(Clone)] pub(crate) struct V512; -impl Variant<WordU64, { N_CONSTS }> for V512 { +impl Variant<WordU64, N_CONSTS> for V512 { #[rustfmt::skip] #[allow(clippy::unreadable_literal)] /// The SHA512 constants as defined in FIPS 180-4. @@ -147,7 +147,7 @@ impl Variant<WordU64, { N_CONSTS }> for V512 { #[derive(Clone, Debug)] /// SHA512 streaming state. pub struct Sha512 { - pub(crate) _state: State<WordU64, V512, { SHA512_BLOCKSIZE }, { SHA512_OUTSIZE }, { N_CONSTS }>, + pub(crate) _state: State<WordU64, V512, SHA512_BLOCKSIZE, SHA512_OUTSIZE, N_CONSTS>, } impl Default for Sha512 { @@ -160,9 +160,7 @@ impl Sha512 { /// Initialize a `Sha512` struct. pub fn new() -> Self { Self { - _state: - State::<WordU64, V512, { SHA512_BLOCKSIZE }, { SHA512_OUTSIZE }, { N_CONSTS }>::_new( - ), + _state: State::<WordU64, V512, SHA512_BLOCKSIZE, SHA512_OUTSIZE, N_CONSTS>::_new(), } } 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 000000000..28be25deb --- /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 000000000..e235f2b65 --- /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 000000000..f972db988 --- /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 000000000..96ccdb318 --- /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 000000000..d1b243233 --- /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/kem/mod.rs b/vendor/orion/src/hazardous/kem/mod.rs new file mode 100644 index 000000000..767b65337 --- /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 000000000..0ea9609f2 --- /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/poly1305.rs b/vendor/orion/src/hazardous/mac/poly1305.rs index 29853f566..b9c974d6e 100644 --- a/vendor/orion/src/hazardous/mac/poly1305.rs +++ b/vendor/orion/src/hazardous/mac/poly1305.rs @@ -77,8 +77,8 @@ use crate::{ }; use fiat_crypto::poly1305_32::{ fiat_poly1305_add, fiat_poly1305_carry, fiat_poly1305_carry_mul, fiat_poly1305_from_bytes, - fiat_poly1305_loose_field_element, fiat_poly1305_selectznz, fiat_poly1305_subborrowx_u26, - fiat_poly1305_tight_field_element, fiat_poly1305_u1, + 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. @@ -119,8 +119,8 @@ impl_from_trait!(Tag, POLY1305_OUTSIZE); #[derive(Clone)] /// Poly1305 streaming state. pub struct Poly1305 { - a: [u32; 5], - r: [u32; 5], + a: fiat_poly1305_tight_field_element, + r: fiat_poly1305_loose_field_element, s: [u32; 4], leftover: usize, buffer: [u8; POLY1305_BLOCKSIZE], @@ -130,8 +130,8 @@ pub struct Poly1305 { impl Drop for Poly1305 { fn drop(&mut self) { use zeroize::Zeroize; - self.a.zeroize(); - self.r.zeroize(); + self.a.0.zeroize(); + self.r.0.zeroize(); self.s.zeroize(); self.buffer.zeroize(); } @@ -164,11 +164,11 @@ impl Poly1305 { // 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]; + 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]; + 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); @@ -181,11 +181,13 @@ impl Poly1305 { /// Remaining processing after all data blocks have been processed. fn process_end_of_stream(&mut self) { // full carry h - let mut buf_h: fiat_poly1305_tight_field_element = [0u32; 5]; - fiat_poly1305_carry(&mut buf_h, &self.a); + 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]; + 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; @@ -197,7 +199,7 @@ impl Poly1305 { // 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); + fiat_poly1305_selectznz(&mut ret, carry,&[g0, g1, g2, g3, g4], &buf_h.0); let mut h0 = ret[0]; let mut h1 = ret[1]; @@ -228,8 +230,8 @@ impl Poly1305 { /// Initialize a `Poly1305` struct with a given one-time key. pub fn new(one_time_key: &OneTimeKey) -> Self { let mut state = Self { - a: [0u32; 5], - r: [0u32; 5], + 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], @@ -280,7 +282,7 @@ impl Poly1305 { /// Reset to `new()` state. pub fn reset(&mut self) { - self.a = [0u32; 5]; + self.a = fiat_poly1305_tight_field_element([0u32; 5]); self.leftover = 0; self.is_finalized = false; self.buffer = [0u8; POLY1305_BLOCKSIZE]; @@ -358,7 +360,7 @@ impl Poly1305 { } self.process_end_of_stream(); - store_u32_into_le(&self.a[0..4], &mut local_buffer); + store_u32_into_le(&self.a.0[0..4], &mut local_buffer); Ok(Tag::from(local_buffer)) } @@ -455,8 +457,8 @@ mod public { } fn compare_states(state_1: &Poly1305, state_2: &Poly1305) { - assert_eq!(state_1.a, state_2.a); - assert_eq!(state_1.r, state_2.r); + assert_eq!(state_1.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[..]); diff --git a/vendor/orion/src/hazardous/mod.rs b/vendor/orion/src/hazardous/mod.rs index 25a5ae227..ff8c188ae 100644 --- a/vendor/orion/src/hazardous/mod.rs +++ b/vendor/orion/src/hazardous/mod.rs @@ -44,3 +44,10 @@ 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; |