From 2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:47:55 +0200 Subject: Adding upstream version 0.70.1+ds1. Signed-off-by: Daniel Baumann --- vendor/orion/.cargo-checksum.json | 1 + vendor/orion/CHANGELOG.md | 620 +++++ vendor/orion/CONTRIBUTING.md | 58 + vendor/orion/Cargo.toml | 103 + vendor/orion/LICENSE | 21 + vendor/orion/README.md | 72 + vendor/orion/SECURITY.md | 37 + vendor/orion/benches/bench.rs | 488 ++++ vendor/orion/debian/patches/disable-benches.patch | 23 + vendor/orion/debian/patches/series | 1 + vendor/orion/deny.toml | 37 + vendor/orion/src/errors.rs | 140 ++ .../orion/src/hazardous/aead/chacha20poly1305.rs | 421 ++++ vendor/orion/src/hazardous/aead/mod.rs | 30 + vendor/orion/src/hazardous/aead/streaming.rs | 1515 ++++++++++++ .../orion/src/hazardous/aead/xchacha20poly1305.rs | 153 ++ .../src/hazardous/cae/chacha20poly1305blake2b.rs | 262 ++ vendor/orion/src/hazardous/cae/mod.rs | 33 + .../src/hazardous/cae/xchacha20poly1305blake2b.rs | 246 ++ vendor/orion/src/hazardous/ecc/mod.rs | 24 + vendor/orion/src/hazardous/ecc/x25519.rs | 821 ++++++ vendor/orion/src/hazardous/hash/blake2/blake2b.rs | 414 ++++ vendor/orion/src/hazardous/hash/blake2/mod.rs | 442 ++++ vendor/orion/src/hazardous/hash/mod.rs | 30 + vendor/orion/src/hazardous/hash/sha2/mod.rs | 1062 ++++++++ vendor/orion/src/hazardous/hash/sha2/sha256.rs | 421 ++++ vendor/orion/src/hazardous/hash/sha2/sha384.rs | 403 +++ vendor/orion/src/hazardous/hash/sha2/sha512.rs | 424 ++++ vendor/orion/src/hazardous/hash/sha3/mod.rs | 558 +++++ vendor/orion/src/hazardous/hash/sha3/sha3_224.rs | 274 ++ vendor/orion/src/hazardous/hash/sha3/sha3_256.rs | 336 +++ vendor/orion/src/hazardous/hash/sha3/sha3_384.rs | 274 ++ vendor/orion/src/hazardous/hash/sha3/sha3_512.rs | 274 ++ vendor/orion/src/hazardous/kdf/argon2i.rs | 2607 ++++++++++++++++++++ vendor/orion/src/hazardous/kdf/hkdf.rs | 522 ++++ vendor/orion/src/hazardous/kdf/mod.rs | 31 + vendor/orion/src/hazardous/kdf/pbkdf2.rs | 579 +++++ vendor/orion/src/hazardous/kem/mod.rs | 25 + .../orion/src/hazardous/kem/x25519_hkdf_sha256.rs | 289 +++ vendor/orion/src/hazardous/mac/blake2b.rs | 435 ++++ vendor/orion/src/hazardous/mac/hmac.rs | 919 +++++++ vendor/orion/src/hazardous/mac/mod.rs | 30 + vendor/orion/src/hazardous/mac/poly1305.rs | 573 +++++ vendor/orion/src/hazardous/mod.rs | 53 + vendor/orion/src/hazardous/stream/chacha20.rs | 1331 ++++++++++ vendor/orion/src/hazardous/stream/mod.rs | 27 + vendor/orion/src/hazardous/stream/xchacha20.rs | 176 ++ vendor/orion/src/high_level/aead.rs | 631 +++++ vendor/orion/src/high_level/auth.rs | 212 ++ vendor/orion/src/high_level/hash.rs | 134 + vendor/orion/src/high_level/hltypes.rs | 76 + vendor/orion/src/high_level/kdf.rs | 222 ++ vendor/orion/src/high_level/kex.rs | 539 ++++ vendor/orion/src/high_level/mod.rs | 29 + vendor/orion/src/high_level/pwhash.rs | 990 ++++++++ vendor/orion/src/lib.rs | 117 + vendor/orion/src/test_framework/aead_interface.rs | 379 +++ .../src/test_framework/incremental_interface.rs | 343 +++ vendor/orion/src/test_framework/mod.rs | 30 + .../src/test_framework/streamcipher_interface.rs | 305 +++ vendor/orion/src/typedefs.rs | 993 ++++++++ vendor/orion/src/util/endianness.rs | 371 +++ vendor/orion/src/util/mod.rs | 186 ++ vendor/orion/src/util/u32x4.rs | 98 + vendor/orion/src/util/u64x4.rs | 114 + 65 files changed, 23384 insertions(+) create mode 100644 vendor/orion/.cargo-checksum.json create mode 100644 vendor/orion/CHANGELOG.md create mode 100644 vendor/orion/CONTRIBUTING.md create mode 100644 vendor/orion/Cargo.toml create mode 100644 vendor/orion/LICENSE create mode 100644 vendor/orion/README.md create mode 100644 vendor/orion/SECURITY.md create mode 100644 vendor/orion/benches/bench.rs create mode 100644 vendor/orion/debian/patches/disable-benches.patch create mode 100644 vendor/orion/debian/patches/series create mode 100644 vendor/orion/deny.toml create mode 100644 vendor/orion/src/errors.rs create mode 100644 vendor/orion/src/hazardous/aead/chacha20poly1305.rs create mode 100644 vendor/orion/src/hazardous/aead/mod.rs create mode 100644 vendor/orion/src/hazardous/aead/streaming.rs create mode 100644 vendor/orion/src/hazardous/aead/xchacha20poly1305.rs create mode 100644 vendor/orion/src/hazardous/cae/chacha20poly1305blake2b.rs create mode 100644 vendor/orion/src/hazardous/cae/mod.rs create mode 100644 vendor/orion/src/hazardous/cae/xchacha20poly1305blake2b.rs create mode 100644 vendor/orion/src/hazardous/ecc/mod.rs create mode 100644 vendor/orion/src/hazardous/ecc/x25519.rs create mode 100644 vendor/orion/src/hazardous/hash/blake2/blake2b.rs create mode 100644 vendor/orion/src/hazardous/hash/blake2/mod.rs create mode 100644 vendor/orion/src/hazardous/hash/mod.rs create mode 100644 vendor/orion/src/hazardous/hash/sha2/mod.rs create mode 100644 vendor/orion/src/hazardous/hash/sha2/sha256.rs create mode 100644 vendor/orion/src/hazardous/hash/sha2/sha384.rs create mode 100644 vendor/orion/src/hazardous/hash/sha2/sha512.rs create mode 100644 vendor/orion/src/hazardous/hash/sha3/mod.rs create mode 100644 vendor/orion/src/hazardous/hash/sha3/sha3_224.rs create mode 100644 vendor/orion/src/hazardous/hash/sha3/sha3_256.rs create mode 100644 vendor/orion/src/hazardous/hash/sha3/sha3_384.rs create mode 100644 vendor/orion/src/hazardous/hash/sha3/sha3_512.rs create mode 100644 vendor/orion/src/hazardous/kdf/argon2i.rs create mode 100644 vendor/orion/src/hazardous/kdf/hkdf.rs create mode 100644 vendor/orion/src/hazardous/kdf/mod.rs create mode 100644 vendor/orion/src/hazardous/kdf/pbkdf2.rs create mode 100644 vendor/orion/src/hazardous/kem/mod.rs create mode 100644 vendor/orion/src/hazardous/kem/x25519_hkdf_sha256.rs create mode 100644 vendor/orion/src/hazardous/mac/blake2b.rs create mode 100644 vendor/orion/src/hazardous/mac/hmac.rs create mode 100644 vendor/orion/src/hazardous/mac/mod.rs create mode 100644 vendor/orion/src/hazardous/mac/poly1305.rs create mode 100644 vendor/orion/src/hazardous/mod.rs create mode 100644 vendor/orion/src/hazardous/stream/chacha20.rs create mode 100644 vendor/orion/src/hazardous/stream/mod.rs create mode 100644 vendor/orion/src/hazardous/stream/xchacha20.rs create mode 100644 vendor/orion/src/high_level/aead.rs create mode 100644 vendor/orion/src/high_level/auth.rs create mode 100644 vendor/orion/src/high_level/hash.rs create mode 100644 vendor/orion/src/high_level/hltypes.rs create mode 100644 vendor/orion/src/high_level/kdf.rs create mode 100644 vendor/orion/src/high_level/kex.rs create mode 100644 vendor/orion/src/high_level/mod.rs create mode 100644 vendor/orion/src/high_level/pwhash.rs create mode 100644 vendor/orion/src/lib.rs create mode 100644 vendor/orion/src/test_framework/aead_interface.rs create mode 100644 vendor/orion/src/test_framework/incremental_interface.rs create mode 100644 vendor/orion/src/test_framework/mod.rs create mode 100644 vendor/orion/src/test_framework/streamcipher_interface.rs create mode 100644 vendor/orion/src/typedefs.rs create mode 100644 vendor/orion/src/util/endianness.rs create mode 100644 vendor/orion/src/util/mod.rs create mode 100644 vendor/orion/src/util/u32x4.rs create mode 100644 vendor/orion/src/util/u64x4.rs (limited to 'vendor/orion') diff --git a/vendor/orion/.cargo-checksum.json b/vendor/orion/.cargo-checksum.json new file mode 100644 index 0000000..9dd0693 --- /dev/null +++ b/vendor/orion/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{},"package":"7abdb10181903c8c4b016ba45d6d6d5af1a1e2a461aa4763a83b87f5df4695e5"} \ No newline at end of file diff --git a/vendor/orion/CHANGELOG.md b/vendor/orion/CHANGELOG.md new file mode 100644 index 0000000..11105d2 --- /dev/null +++ b/vendor/orion/CHANGELOG.md @@ -0,0 +1,620 @@ +### 0.17.6 + +__Date:__ September 19, 2023. + +__Changelog:__ +- Bump MSRV to `1.70.0`. +- Bump `fiat-crypto` to `0.2.1`. + +### 0.17.5 + +__Date:__ July 4, 2023. + +__Changelog:__ +- Add `experimental` crate feature. +- Add support for fully-committing AEAD variants based on CTX ([#324](https://github.com/orion-rs/orion/pull/324)). +- Add support for SHA3 ([#327](https://github.com/orion-rs/orion/pull/327)). +- Bump MSRV to `1.64`. +- Add support for DHKEM(X25519, HKDF-SHA256) from HPKE [RFC 9180](https://www.rfc-editor.org/rfc/rfc9180). + +### 0.17.4 + +__Date:__ March 4, 2023. + +__Changelog:__ + +- Update Wycheproof test vectors ([#320](https://github.com/orion-rs/orion/issues/320)). +- Switch from `actions-rs/tarpaulin` to `cargo-tarpaulin` ([#322](https://github.com/orion-rs/orion/pull/322)) +- Update documentation for PBKDF2 and Argon2i cost parameter selection ([#316](https://github.com/orion-rs/orion/pull/316), [#321](https://github.com/orion-rs/orion/pull/321)). +- Remove `cargo-audit` which was redundant to `cargo-deny` ([#311](https://github.com/orion-rs/orion/issues/311)). +- Bump MSRV to `1.59.0`. +- Remove `html_root_url` ([#325](https://github.com/orion-rs/orion/pull/325)). + +### 0.17.3 + +__Date:__ December 7, 2022. + +__Changelog:__ + +- Fix misuse issue in (X)ChaCha20 and (X)ChaCha20-Poly1305 APIs ([#308](https://github.com/orion-rs/orion/issues/308)). +- Add benchmark check test without running any actual benchmarks ([#307](https://github.com/orion-rs/orion/pull/307)). +- Improve `Balek2b::new()` docs ([#303](https://github.com/orion-rs/orion/pull/303)). +- Migrated to Rust Edition 2021 ([#237](https://github.com/orion-rs/orion/issues/237)). +- MSRV bumped to `1.57.0` and `criterion` updated ([#299](https://github.com/orion-rs/orion/pull/299)). +- Added `serde` doc feature-tag to `PasswordHash` ser/deser impls ([#297](https://github.com/orion-rs/orion/pull/297)). + +### 0.17.2 + +__Date:__ August 16, 2022. + +__Changelog:__ + +- BLAKE2b `Hasher` enum now implements `Debug + PartialEq` ([#278](https://github.com/orion-rs/orion/issues/278) (by [@black-eagle17](https://github.com/black-eagle17))). +- Removed unmaintained `audit-check` and replaced with `cargo-deny` ([#292](https://github.com/orion-rs/orion/pull/292)). +- Allow Unicode-DFS-2016 license in dev-dependency tree ([#291](https://github.com/orion-rs/orion/pull/291)). + +### 0.17.1 + +__Date:__ January 30, 2022. + +__Changelog:__ + +- Use fiat-crypto from their provided crate on crates.io ([#201](https://github.com/orion-rs/orion/issues/201)) (by [Vince Mutolo](https://github.com/vlmutolo)). +- Doc-tests no longer fail if run with `cargo test --no-default-features`, as the erroneous usages have been feature-gated ([#254](https://github.com/orion-rs/orion/issues/254)). +- Specify MSRV in `Cargo.toml` via `rust-version` field ([#250](https://github.com/orion-rs/orion/issues/250)). +- `audit-check` GitHub Action added in addition to `cargo-audit` ([#257](https://github.com/orion-rs/orion/issues/257)). +- Updated copyright year to 2022 ([#267](https://github.com/orion-rs/orion/issues/267)). +- Implement `std::io::Write` for BLAKE2 and SHA2, also adding `orion::hash::digest_from_reader` ([#228](https://github.com/orion-rs/orion/pull/228)) (by [Vince Mutolo](https://github.com/vlmutolo)). +- Implement Poly1305 using fiat-crypto ([#198](https://github.com/orion-rs/orion/issues/198)). +- Correct capitalization of crate name in docs, README and wiki ([#259](https://github.com/orion-rs/orion/issues/259)). +- Fix the benchmarking targets that failed to compile after `0.17.0` ([#270](https://github.com/orion-rs/orion/pull/270)). +- Various internal cleanups and improvements. + +### 0.17.0 + +__Date:__ November 24, 2021. + +__Changelog:__ + +- [Breaking change] Keyed and non-keyed BLAKE2b have been split into two separate modules (`orion::hazardous::mac::blake2b` and `orion::hazardous::hash::blake2::blake2b` respectively). The keyed now returns a `Tag` instead of `Digest` ([#208](https://github.com/orion-rs/orion/issues/208)). +- [Breaking change] `Tag`s (not only those used by BLAKE2b, but all) now implement `Drop` but no longer implement `Copy` ([#208](https://github.com/orion-rs/orion/issues/208)). +- [Breaking change] `seal_chunk()` used in streaming AEAD now take `StreamTag` by reference ([#212](https://github.com/orion-rs/orion/issues/212)) (by [24seconds](https://github.com/24seconds)). + +### 0.16.1 + +__Date:__ November 3, 2021. + +__Changelog:__ + +- Add support for X25519 using fiat-crypto Curve25519 field arithmetic (new modules `orion::hazardous::ecc` and `orion::kex`) ([#197](https://github.com/orion-rs/orion/pull/197)). +- Implement serde `Serialize` and `Deserialize` for relevant types ([#192](https://github.com/orion-rs/orion/issues/192)) (by [Vince Mutolo](https://github.com/vlmutolo)). +- Fix incorrect documentation of SHA256 streaming state ([#196](https://github.com/orion-rs/orion/issues/196)). +- Add `is_empty()` to newtypes ([#206](https://github.com/orion-rs/orion/pull/206)). +- Add documentation for correct use of streaming AEAD API with `StreamTag::Finish` ([#139](https://github.com/orion-rs/orion/issues/139)). +- Convert uses of `assert!(a == b)` to `assert_eq!(a, b)` where possible ([#210](https://github.com/orion-rs/orion/issues/210)) (by [Emmanuel Leblond](https://github.com/touilleMan)). +- Derive `Clone` + `Copy` for `StreamTag` ([#211](https://github.com/orion-rs/orion/issues/211)) (by [24seconds](https://github.com/24seconds)). +- Harden security of GitHub Actions CI/CD ([#200](https://github.com/orion-rs/orion/issues/200)) (by [Vince Mutolo](https://github.com/vlmutolo)). +- Re-export HMAC `Tag`s used in their corresponding HKDF API ([#224](https://github.com/orion-rs/orion/issues/224)). +- Fix warnings from CI jobs and bump MSRV to `1.52.0` ([#222](https://github.com/orion-rs/orion/issues/222)) ([#223](https://github.com/orion-rs/orion/issues/223)). +- Update benchmarks ([#214](https://github.com/orion-rs/orion/issues/214)). +- Render feature badges for API on docs.rs ([#238](https://github.com/orion-rs/orion/issues/238)). +- Add new Crate Features page to wiki ([#215](https://github.com/orion-rs/orion/issues/215)). + +### 0.16.0 + +__Date:__ March 29, 2021. + +__Changelog:__ + +- [Breaking change] Moved all libraries to the https://github.com/orion-rs organization and added [Vince Mutolo](https://github.com/vlmutolo) as a maintainer ([#191](https://github.com/orion-rs/orion/issues/191)). +- [Breaking change] Use Argon2i parameters from PasswordHash in `pwhash::hash_password_verify()` ([#138](https://github.com/orion-rs/orion/issues/138)) (by [Vince Mutolo](https://github.com/vlmutolo)). +- [Breaking change] Limit high-level, variable-length newtype's input to `isize::MAX` ([#130](https://github.com/orion-rs/orion/issues/130)). +- [Breaking change] Add support for SHA256 and SHA384 ([#152](https://github.com/orion-rs/orion/issues/152), [#181](https://github.com/orion-rs/orion/pull/181), [#162](https://github.com/orion-rs/orion/issues/162), [#183](https://github.com/orion-rs/orion/pull/183)). +- [Breaking change] Add support for HMAC-SHA(256/384), PBKDF2-HMAC-SHA(256/384) and HKDF-HMAC-SHA(256/384) ([#171](https://github.com/orion-rs/orion/pull/171), [#153](https://github.com/orion-rs/orion/issues/153), [#154](https://github.com/orion-rs/orion/issues/154), [#170](https://github.com/orion-rs/orion/issues/170)). +- [Breaking change] Remove `orion::kdf::derive_key_verify()` and `orion::hazardous::kdf::hkdf::verify()` ([#179](https://github.com/orion-rs/orion/issues/179), [#184](https://github.com/orion-rs/orion/pull/184)). +- [Breaking change] Convert `StreamTag` used in `orion::hazardous::aead::streaming` and `orion::aead::streaming` to lower-case acronyms (i.e `StreamTag::MESSAGE` -> `StreamTag::Message`) ([#190](https://github.com/orion-rs/orion/pull/190)). +- Use new intra-doc links ([#134](https://github.com/orion-rs/orion/issues/134), [#185](https://github.com/orion-rs/orion/pull/185)) along with other small improvements to documentation. +- Update fuzzing targets (#[182](https://github.com/orion-rs/orion/issues/182)). +- Add documentation for user-awareness of potential sensitive data in out-parameters during password-hash verification ([#178](https://github.com/orion-rs/orion/issues/178), [#187](https://github.com/orion-rs/orion/pull/187)) (contrib. by [Vince Mutolo](https://github.com/vlmutolo)). +- Replace `base64` dependency with `ct-codecs` to support constant-time encoding & decoding in `orion::pwhash::PasswordHash` ([#188](https://github.com/orion-rs/orion/issues/188), [#189](https://github.com/orion-rs/orion/pull/189)). +- Refactor property-based tests to use the `#[quickcheck]` attribute, introducing `quickcheck_macros` as a dev-dependency ([#180](https://github.com/orion-rs/orion/pull/180)). +- Bump MSRV to `1.51.0`. + +### 0.15.6 + +__Date:__ February 9, 2021. + +__Changelog:__ + +- The entire CI infrastructure has been moved to GitHub Actions (removing AppVeyor and Travis CI). +- Add `cargo-deny` to CI jobs ([#174](https://github.com/brycx/orion/pull/174)). +- Refactoring of code related to testing and reading test vectors ([#136](https://github.com/brycx/orion/pull/136), [#143](https://github.com/brycx/orion/pull/143)). +- Add new public Matrix room for discussion ([#144](https://github.com/brycx/orion/issues/144)). +- Internal documentation improvements and clippy improvements (by [u5surf](https://github.com/u5surf)). +- Update and correct license years ([#164](https://github.com/brycx/orion/pull/164)). +- Update `quickcheck`. +- Fix documentation on the `generate()` output-size for HMAC-based secret key newtypes which was incorrect ([#169](https://github.com/brycx/orion/issues/169)). +- Improve the usage example in `orion::auth` ([Vince Mutolo](https://github.com/vlmutolo)). +- Add GitHub issue templates for bugs and feature requests ([#155](https://github.com/brycx/orion/pull/155)). +- Add `SECURITY.md`, specifying a disclosure policy, threat-model and information regarding yanking ([#163](https://github.com/brycx/orion/pull/163)). + +### 0.15.5 + +__Date:__ October 13, 2020. + +__Changelog:__ + +- Documentation improvements. +- Update `base64` to `0.13.0`. + +### 0.15.4 + +__Date:__ September 25, 2020. + +__Changelog:__ + +- Empty plaintexts are now allowed for `hazardous::aead` ([#127](https://github.com/brycx/orion/pull/127)). +- Update `getrandom` to `0.2`. +- Bump MSRV to `1.41` due to bump in `subtle`. + +### 0.15.3 + +__Date:__ August 8, 2020. + +__Changelog:__ + +- Documentation improvements. +- Argon2i is now available in a `no_std` context, using the new `alloc` feature ([#126](https://github.com/brycx/orion/pull/126)). +- `release` and `bench` profiles now use the default LTO (thin local LTO) instead of fat LTO. + +### 0.15.2 + +__Date:__ June 7, 2020. + +__Changelog:__ + +- Remove old `no_std` feature from CONTRIBUTING guidelines. +- Improve documentation and code around HKDFs maximum output length. +- Move clippy, rustfmt and basic tests to GitHub Actions ([#122](https://github.com/brycx/orion/pull/122)). +- Add random secret-key/nonce tests to AEADs and stream ciphers ([#123](https://github.com/brycx/orion/pull/123)). +- Address various clippy warnings. + +### 0.15.1 + +__Date:__ March 9, 2020. + +__Changelog:__ + +- Update `base64` dependency from `0.11.0` to `0.12.0`. +- Documentation improvements. + +### 0.15.0 + +__Date:__ February 25, 2020. + +__Changelog:__ + +- [Breaking change] `secure_cmp` and all verification functions now return `Result<(), UnknownCryptoError>` instead of `Result` ([#97](https://github.com/brycx/orion/issues/97)). +- [Breaking change] HChaCha20 is no longer public. +- [Breaking change] The default size of a randomly generated secret key in `hazardous::hash::blake2b` is now 32 bytes instead of 64 bytes ([#88](https://github.com/brycx/orion/pull/88#issuecomment-529423151)). +- [Breaking change] `orion::auth` now uses BLAKE2b in keyed-mode as MAC ([#88](https://github.com/brycx/orion/pull/88), by [Vince Mutolo](https://github.com/vlmutolo)). +- [Breaking change] The public API for structs used with incremental processing has been changed ([#106](https://github.com/brycx/orion/issues/106) and [#87](https://github.com/brycx/orion/pull/87)). +- [Breaking change] Support for Argon2i(single-threaded) has been added. This is now used in the `orion::kdf` and `orion::pwhash` modules ([#113](https://github.com/brycx/orion/pull/113)). +- [Breaking change] `chacha20::keystream_block` is no longer available. +- [Breaking change] Uses of (X)ChaCha20Poly1305 will return an error if a `usize` to `u64` conversion would be lossy. +- [Breaking change] orion is now `no_std`-compatible on stable Rust and the `no_std` and `nightly` features have been removed ([#111](https://github.com/brycx/orion/pull/111)). +- libsodium-compatible, streaming AEAD based on XChaCha20Poly1305 (libsodiums "secretstream") ([#99](https://github.com/brycx/orion/pull/99) and [#108](https://github.com/brycx/orion/pull/108), by [snsmac](https://github.com/snsmac)). +- Switch to Criterion for benchmarks. +- Add contribution guidelines in `CONTRIBUTING.md`. +- Move the changelog to a `CHANGELOG.md` file. +- Add test vectors to XChaCha20. +- Improvements to `secure_cmp` ([#93](https://github.com/brycx/orion/pull/93), by [snsmac](https://github.com/snsmac)) +- Add explicit security warnings to `#[must_use]` public APIs that return a `Result` ([#95](https://github.com/brycx/orion/pull/95), by [Cole Lawrence](https://github.com/colelawrence)) +- Cleanup in the orion-dudect tests and add tests for newtype `PartialEq<&[u8]>` impl. +- Remove hardcoded docs.rs links in the documentation ([#100](https://github.com/brycx/orion/pull/100), by [Kyle Schreiber](https://github.com/finfet)). +- Previously, the documentation for `util::secure_rand_bytes` stated that a panic would occur if the function failed to generate random bytes without throwing an error, which was not the case. This has been corrected. +- Add `Blake2b::verify` to fuzzing targets. +- orion-dudect now also tests for constant-time execution in CI on OSX and Windows platforms. +- Testing constant-time execution with WASM at [orion-sidefuzz](https://github.com/brycx/orion-sidefuzz). +- New testing framework which has greatly reduced the amount of duplicate testing code ([#96](https://github.com/brycx/orion/pull/96)). +- Document and test MSRV ([#104](https://github.com/brycx/orion/issues/104)). +- orion is now listed as an alternative to the old `rust-crypto` crate on [RustSec](https://rustsec.org/advisories/RUSTSEC-2016-0005.html). +- `UnknownCryptoError` now implements `std::error::Error` for better interoperability with error-handling crates. +- Added new test vectors from Wycheproof for ChaCha20Poly1305, XChaCha20Poly1305, HMAC-SHA512 and HKDF-HMAC-SHA512 ([#116](https://github.com/brycx/orion/pull/116)). +- `#![deny(warnings)]` has been removed and replaced with flags in CI build jobs. +- GitHub actions are used for daily security audit for the `crates-published` branch. Travis CI runs only weekly on `crates-published` branch now (daily before). +- Removed inlining attributes that did not provide any performance improvements when tested with benchmarks ([commit](https://github.com/brycx/orion/commit/eea1899c0b2967c17c0ee6d05559065c3f67c7d5)). +- Various performance improvements. +- Various improvements to fuzzing targets. +- Various improvements to tests. + +### 0.14.5 [Yanked] + +__Date:__ January 25, 2020. + +__Changelog:__ + +- Fix `nightly` build breakage. + +### 0.14.4 [Yanked] + +__Date:__ August 21, 2019. + +__Changelog:__ + +- Reduce the amount of allocations throughout most of orion. +- Vectorize the ChaCha20 implementation providing ~6% performance improvement for (X)ChaCha20Poly1305 and ~11.5% for (X)ChaCha20. +- Documentation improvements. + +### 0.14.3 [Yanked] + +__Date:__ August 1, 2019. + +__Changelog:__ + +- Improved performance for ChaCha20Poly1305/XChaCha20Poly1305 when AAD is empty. +- Refactoring of streaming contexts used by SHA512, BLAKE2b and Poly1305. +- Implement `PartialEq<&[u8]>` for all newtypes and provide documentation for usage of such (by [Vince Mutolo](https://github.com/vlmutolo)). +- Switched to stable rustfmt. +- Fix use of now deprecated (since `v0.1.7`) `getrandom` errors. +- Updated fuzzing targets in orion-fuzz. + +### 0.14.2 [Yanked] + +__Date:__ June 10, 2019. + +__Changelog:__ + +- Improved performance on all implementations, most notably: ~30% in ChaCha20/XChaCha20 and ~20% in ChaCha20Poly1305/XChaCha20Poly1305. +- Updated `zeroize` dependency. +- Testing WebAssembly (`wasm32-unknown-unknown`) support in CI. +- Improved documentation. + +### 0.14.1 [Yanked] + +__Date:__ May 27, 2019. + +__Changelog:__ + +- Update `zeroize` dependency. +- Improvements to documentation. + +### 0.14.0 [Yanked] + +__Date:__ May 4, 2019. + +__Changelog:__ + +- [Breaking change] Function `as_bytes()` for public newtypes are replaced with `AsRef<>` trait implementations. This means all `as_bytes()` calls need to be replaced with `as_ref()`. +- [Breaking change] The `SecretKey` for BLAKE2b is longer padded with zeroes to the length of the blocksize. Thus, the `SecretKey` no longer has a `get_original_length()` function, but the same result will be represented by the `get_length()` function instead. +- [Breaking change] All calls to `as_ref()` and `unprotected_as_bytes()` return the newtypes data with what it was initialized, regardless of padding. (With the exception of HMAC) +- [Breaking change] All calls to `get_length()` return the length of the newtype with what is what initialized, regardless of padding. (With the exception of HMAC) +- [Breaking change] All newtypes that offer `generate()` now panic if the RNG fails to initialize of read from its source. This also means that newtype `generate()` functions, that do not take in a size parameter, no longer return a `Result`. +- [Breaking change] `ValidationCryptoError` and `FinalizationCryptoError` have been removed. Though this doesn't mean that there is less information available, see [issue here](https://github.com/brycx/orion/issues/64). +- [Breaking change] Support for cSHAKE256 has been dropped, also meaning orion no longer depends on tiny-keccak. 8% decrease in `unsafe` code in dependencies. +- All fuzzing targets in `fuzz` that used libFuzzer have been deprecated in favor of those in [orion-fuzz](https://github.com/brycx/orion-fuzz) using honggfuzz-rs. +- Improvements to fuzzing targets in orion-fuzz. +- [Automated testing in CI, for constant-time execution](https://github.com/brycx/orion-dudect). +- Added `From<[u8; C]>` trait implementations for C-length fixed-sized newtypes, so that the caller may avoid using `Result` when not working with slices. +- [Breaking change] Module `hazardous::constants` has been removed and all types made private. Only a select number of constants have been re-exported in their respective modules. See [here for more information](https://github.com/brycx/orion/pull/72). +- It is now strictly advised against using orion in debug mode, for what is meant to be production use. Using `opt-level = 0` with orion, is also advised against. See [security section](https://github.com/brycx/orion/wiki/Security#release-and-codegen-options). +- `rand_os` has been replaced with `getrandom`. +- Improvements to documentation examples as they no longer use `.unwrap()` but `?` instead. + +### 0.13.4 [Yanked] + +__Date:__ April 1, 2019. + +__Changelog:__ + +- Fix build for latest nightly. + +### 0.13.3 [Yanked] + +__Date:__ March 31, 2019. + +__Changelog:__ + +- Updated `zeroize` to `0.6.0`. +- Added a small number of tests. +- Improvement to constant-time interfaces ([#66](https://github.com/brycx/orion/pull/66)). + +### 0.13.2 [Yanked] + +__Date:__ March 13, 2019. + +__Changelog:__ + +- PBKDF2 and BLAKE2b now panic on lengths exceeding (2^32-1) * 64 and 2*(2^64-1), respectively. +- ChaCha20 length constrictions are now equivalent to those of the RFC and panics on trying to process more than 2^32-1 keystream blocks. +- Documentation improvements. +- OpenSSL test vectors for BLAKE2b. + +__Note__: Strictly speaking, the first two changes are breaking, but because of the unlikeliness that this has an effect on anybody, they were not marked as such. + +### 0.13.1 [Yanked] + +__Date:__ February 16, 2019. + +__Changelog:__ + +- Documentation improvements ([#60](https://github.com/brycx/orion/issues/60)). + +### 0.13.0 [Yanked] + +__Date:__ February 10, 2019. + +__Changelog:__ + +- [Breaking change]: `orion::hazardous::hash::sha512` previously used the same `Digest` as BLAKE2b. This is no longer the case, making it impossible to specify a non fixed-length hash as `Digest` with SHA512. +- [Breaking change]: `HLEN` constant renamed to `SHA512_OUTSIZE` and `SHA2_BLOCKSIZE` constant renamed to `SHA512_BLOCKSIZE`. +- Added `POLY1305_OUTSIZE` constant. +- Improved documentation for high-level `Password`, `SecretKey` in `hazardous`s `hmac` and `blake2b`, as well as `Password` in `pbkdf2` of `hazardous`. +- Added AppVeyor builds and testing for Windows MSVC with Visual Studio 2017. + +### 0.12.6 [Yanked] + +__Date:__ February 8, 2019. + +__Changelog:__ + +- Switched to zeroize in favor of clear_on_drop, such that using orion on stable Rust no longer requires a C compiler. +- Fuzzing with honggfuzz-rs. + +### 0.12.5 [Yanked] + +__Date:__ February 4, 2019. + +__Changelog:__ + +- Refactored HMAC and improved performance for PBKDF2 by ~50%. +- Removed `byteorder` dependency using instead the endianness conversion functions that came with Rust 1.32. + +### 0.12.4 [Yanked] + +__Date:__ January 31, 2019. + +__Changelog:__ + +- Fixes a bug where hashing, with BLAKE2b, over 2^64-1 bytes of data would cause an overflowing addition on debug builds. +- Fixes a bug where hashing, with SHA512, over 2^64-1 bytes of data would not result in the counter being correctly incremented. +- Added property-based testing, using QuickCheck, to most of the library and improved testing for the library in general. +- `PartialEq` is now implemented for `orion::kdf::Salt` and `Nonce` in both `chacha20` and `xchacha20`. +- Added `get_length()` for `blake2b::Digest`. +- Updated fuzzing dependencies. + +### 0.12.3 [Yanked] + +__Date:__ January 29, 2019. + +__Changelog:__ + +- Improved compilation time. +- Bugfix [#50](https://github.com/brycx/orion/issues/50). +- Update `byteorder` and `serde_json` dependencies (fixes build-failures related to `rand_core`). + +### 0.12.2 [Yanked] + +__Date:__ January 26, 2019. + +__Changelog:__ + +- Fix a [bug](https://github.com/brycx/orion/issues/52) that lead to panics when using `out` parameters, with `seal()`/`open()` in `hazardous`, with a length above a given point. + +### 0.12.1 [Yanked] + +__Date:__ January 16, 2019. + +__Changelog:__ + +- Switched `rand` dependency out with `rand_os`. + +### 0.12.0 [Yanked] + +__Date:__ December 29, 2018. + +__Changelog:__ + +- [Breaking change]: All high-level functions now return a Result. +- [Breaking change]: `Password` in `pbkdf2`, `SecretKey` and `hmac()` of `hmac` and `extract()` of `hkdf` in `hazardous` now return a Result. +- [Breaking change]: Limit all `generate()` taking a `length` parameter, and `orion::kdf` calls to a length of less than `u32::max_value()` as maximum. +- [Breaking change]: `orion::kdf` and `orion::pwhash` take a new `Password` parameter that is heap-allocated and returns a Result. +- Removed `sha2` dependency and `ring` dev-dependency. `sha2` has been replaced with orion's own SHA512 implementation. +- Added support for BLAKE2b and SHA512. +- Updated to Rust 2018 Edition. +- Better performance for HMAC, HKDF and PBKDF2. + +Thanks to Gabe Langlais for valuable feedback, especially on the API design. + +### 0.11.2 [Yanked] + +__Date:__ December 22, 2018. + +__Changelog:__ + +- Security fix: [#46](https://github.com/brycx/orion/issues/46) ([RUSTSEC-2018-0012](https://rustsec.org/advisories/RUSTSEC-2018-0012.html), [CVE-2018-20999](https://nvd.nist.gov/vuln/detail/CVE-2018-20999)). +- Updated subtle dependency. + +### 0.11.0 [Yanked] + +__Date:__ November 24, 2018. + +__Changelog:__ + +- Fix [missing error propagation](https://github.com/brycx/orion/issues/40) in `v0.10`. + +### 0.10.0 [Yanked] + +__Date:__ November 23, 2018. + +__Changelog:__ + +- New types for secret keys, nonces, tags, etc. This greatly increases misuse-resistance, usability and safety. To read more about the types and how they are implemented, see the [wiki section](https://github.com/brycx/orion/wiki/Design). +- `default` API has been dropped. All high-level functionality is now accessible through these interfaces: `orion::aead`, `orion::auth`, `orion::kdf` and `orion::pwhash`. +- AEAD interfaces in `hazardous` and in the high-level API (previously `default::encrypt`, etc.) have been renamed to `seal` and `open` to reflect the authentication and hopefully increase familiarity. +- `finalize_to_dst()` has been dropped for HMAC. +- Adaption of the `#[must_use]` attribute. +- Documentation improvements. +- HKDF and cSHAKE dropped from high-level API. +- High-level PBKDF2 now uses 64 byte salts and 64 byte password hashes and the iteration count has been made available for users to control. +- Argument `info` for HKDF and `ad` for AEADs are now `Option`. +- `util::gen_rand_key` and `util::compare_ct` are now `util::secure_rand_bytes` and `util::secure_cmp`. +- The password length enforcement in high-level PBKDF2 API has been removed. +- All other public types (eg. `CShake`, `Hmac` and `Poly1305`) now implement `Debug`. +- Using `clear_on_drop` to wipe memory in favor of `seckey`. +- New features `nightly` and `no_std`. To use orion in a `no_std` context, some dependency specifications are needed. Refer to the README for these. +- Major improvements to error propagation. + +### 0.9.1 [Yanked] + +__Date:__ November 11, 2018. + +__Changelog:__ + +- Fix bug in double-HMAC verification in the default API +- Documentation improvements + +### 0.9.0 [Yanked] + +__Date:__ November 4, 2018. + +__Changelog:__ + +- Added support for HChaCha20, XChaCha20 and AEAD XChaCha20Poly1305. +- The `default` APIs encryption/decryption interface has been reintroduced, now offering +authenticated encryption through the AEAD XChaCha20Poly1305 implementation. +- Most of the library's structure has been revamped. +- Major additions to the project wiki detailing testing and some information regarding dependencies and security. +- Improved fuzzing targets and overall test suite. +- Documentation improvements. + +### 0.8.0 [Yanked] + +__Date:__ October 7, 2018. + +__Changelog:__ + +- Added AEAD ChaCha20Poly1305 from [RFC 8439](https://tools.ietf.org/html/rfc8439) +- Added `keystream_block()` public function to retrieve a keystream from `chacha20` +- Added Poly1305 from [RFC 8439](https://tools.ietf.org/html/rfc8439) +- `default::encrypt` and `default::decrypt` removed until orion offers XChaCha20 with Poly1305 +- Documentation improvement +- Updated `sha2` dependency + +### 0.7.4 [Yanked] + +__Date:__ September 27, 2018. + +__Changelog:__ + +- Fix bug in PBKDF2 (See [issue](https://github.com/brycx/orion/issues/30)) + +### 0.7.3 [Yanked] + +__Date:__ September 26, 2018. + +__Changelog:__ + +- Update `subtle` dependency + +### 0.7.2 [Yanked] + +__Date:__ September 26, 2018. + +__Changelog:__ + +- Fuzz test improvements +- Documentation improvements + +### 0.7.1 [Yanked] + +__Date:__ September 20, 2018. + +__Changelog:__ + +- `default::chacha20_*` initial counter set to 0 + +### 0.7.0 [Yanked] + +__Date:__ September 17, 2018. + +__Changelog:__ + +- Added `FinalizationCryptoError` which means `cshake` and `hmac` now return a `Result` on finalization and update function calls. +- Added the ChaCha20 algorithm from the [RCF 8439](https://tools.ietf.org/html/rfc8439). +- Fix failed builds for `no_std`. +- Fix a bug where a user could call `update()` after finalization on both `cshake` and `hmac`. +- `cshake_verify()` function dropped from default API. +- Documentation improvement. + +### 0.6.1 [Yanked] + +__Date:__ September 5, 2018. + +__Changelog:__ + +- Update `subtle` dependency + +### 0.6.0 [Yanked] + +__Date:__ August 31, 2018. + +__Changelog:__ + +- Fix: `byteorder` and `rand` imported correctly for `no_std` +- Add default feature `safe_api`, meaning that for `no_std`, import orion with default features disabled +- Due to dependency fixing, Double HMAC Verification is now only done in the `safe_api` +- `gen_rand_key` now only available with `safe_api` + +### 0.5.2 [Yanked] + +__Date:__ August 22, 2018. + +__Changelog:__ + +- Replaced `byte-tools` with `byteorder` crate as `byte-tools` no longer offers the required functionality + +### 0.5.1 [Yanked] + +__Date:__ August 20, 2018. + +__Changelog:__ + +- Added `reset()` function to cSHAKE +- Added finalization check for HMAC and cSHAKE, making it impossible to call finalization functions twice without a reset in between. Preventing misuse. + +### 0.5.0 [Yanked] + +__Date:__ August 13, 2018. + +__Changelog:__ + +- Support for SHA256, SHA384, SHA512/256 and cSHAKE128 dropped. +- Support for `#![no_std]` added. +- HMAC streaming API. +- HMAC now uses SHA512. +- Switched out `clear_on_drop` with `seckey`. +- Switched out `constant_time_eq` with `subtle`. +- cSHAKE streaming API. +- `default::pbkdf2` no longer appends salt to password before hashing due to some problems integrating this using `#![no_std]`. This might be re-introduced later on. +- `orion::core` renamed to `orion::utilities`. +- cSHAKE verification function removed from hazardous. + +Performance improvements compared to v0.4.3: + +- HMAC: ~10% performance improvement +- HKDF: ~5% performance improvement +- PBKDF2: ~15% performance improvement +- cSHAKE: ~11% performance improvement + +This was benchmarked on a MacBook Air 1,6 GHz Intel Core i5, 4GB. + +### 0.4.3 [Yanked] + +__Date:__ August 8, 2018. + +__Changelog:__ + +- Updated dependency +- Adopted faster HMAC key padding steps from `rigel` crate, avoiding allocation as before but without the `Cow` borrow +- Memory and performance improvement to the PBKDF2 implementation by avoiding many useless allocations diff --git a/vendor/orion/CONTRIBUTING.md b/vendor/orion/CONTRIBUTING.md new file mode 100644 index 0000000..11b056d --- /dev/null +++ b/vendor/orion/CONTRIBUTING.md @@ -0,0 +1,58 @@ + +### 1. What to contribute +There are a variety of things that one may contribute to this project. Contributions of any kind are always welcomed. + +#### 1.1 Features and Improvements +Improvements to the library can come in many flavors: performance, security, usability, etc. +An improvement that doesn't add new features or are breaking changes, may be submitted as pull requests directly. + +Features that add new functionality or are breaking changes should preferably be discussed in a separate issue first. + +See [Rust API Guidelines](https://rust-lang-nursery.github.io/api-guidelines/checklist.html) and [Secure Rust Guidelines](https://anssi-fr.github.io/rust-guide/) for some checklists on how to implement new features. + +#### 1.2 Testing +One way to contribute to testing is by writing new unit-tests to cover code that isn't already being tested. Improvements to existing unit-tests is also an option. + +Adding test vectors (located in `/tests`) is also a good way to improve the testing of the library. Test vectors can be official or generated using another crypto library. + +#### 1.3 Fuzzing +Fuzzing is an important part of testing this library. Contributions to this aspect can come in two ways: 1) Running the fuzzing targets, updating the corpus and reporting any issues found and 2) Overall improvements to the fuzzing targets. + +Please refer to the [orion-fuzz](https://github.com/orion-rs/orion-fuzz) repository when working with fuzzing. + +#### 1.4 Documentation +Quality of documentation is a vital part of this project. Contributions to this could include adding documentation where such is missing, clarifying documentation that is unclear or improving examples. + +Try to make changes adhere to the [Rust API Guidelines](https://rust-lang-nursery.github.io/api-guidelines/checklist.html) as much as possible. + +### 2. Bug Reports and Feature Requests +There are templates for both these scenarios, please see the `.github/ISSUE_TEMPLATE/` directory. + +A bug report or feature request should _ideally_ follow the provided templates. It's not a strict requirement but in most cases, more information about the bug or feature makes it easier to fix/evaluate. + +### 3. Pull Requests + +Before submitting a pull request, please make sure you have done the following: + +- [ ] Explain what the pull request changes, in the description of the GitHub PR, or link to the relevant issue. + +- [ ] A change or addition of functionality is covered by unit-tests. + +- [ ] Ensure that all tests pass when running: + + - `cargo test` + - `cargo +nightly test --no-default-features` + +- [ ] The formatting is correct and clippy does not show warnings by running: + + - `cargo clippy` + - `cargo fmt` + +- [ ] If you have changed or added tests, you can make sure these also pass CI by checking: + - `cargo clippy --tests` + +- [ ] If the pull request is a bugfix, try to include a regression test for the bug. + +All pull requests should be opened against the `master` branch. + +If your pull request is still work-in-progress, make the title of the pull request start with `WIP:` or open it as a draft via GitHub. \ No newline at end of file diff --git a/vendor/orion/Cargo.toml b/vendor/orion/Cargo.toml new file mode 100644 index 0000000..7d43b65 --- /dev/null +++ b/vendor/orion/Cargo.toml @@ -0,0 +1,103 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.70" +name = "orion" +version = "0.17.6" +authors = ["brycx "] +exclude = [ + ".gitignore", + ".travis.yml", + "tests/*", +] +description = "Usable, easy and safe pure-Rust crypto" +documentation = "https://docs.rs/orion" +readme = "README.md" +keywords = [ + "cryptography", + "crypto", + "aead", + "hash", + "mac", +] +categories = [ + "cryptography", + "no-std", +] +license = "MIT" +repository = "https://github.com/orion-rs/orion" +autobenches = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[profile.dev] +opt-level = 1 + +[dependencies.ct-codecs] +version = "1.1.1" +optional = true + +[dependencies.fiat-crypto] +version = "0.2.1" +default-features = false + +[dependencies.getrandom] +version = "0.2.0" +optional = true + +[dependencies.serde] +version = "1.0.124" +features = ["alloc"] +optional = true +default-features = false + +[dependencies.subtle] +version = "^2.2.2" +default-features = false + +[dependencies.zeroize] +version = "1.1.0" +default-features = false + +[dev-dependencies.criterion] +version = "0.5.0" + +[dev-dependencies.hex] +version = "0.4.0" + +[dev-dependencies.quickcheck] +version = "1" + +[dev-dependencies.quickcheck_macros] +version = "1" + +[dev-dependencies.serde] +version = "1.0" +features = ["derive"] + +[dev-dependencies.serde_json] +version = "1.0.41" + +[features] +alloc = [] +default = ["safe_api"] +experimental = [] +safe_api = [ + "getrandom", + "ct-codecs", +] diff --git a/vendor/orion/LICENSE b/vendor/orion/LICENSE new file mode 100644 index 0000000..178fc15 --- /dev/null +++ b/vendor/orion/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2023 The orion Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/orion/README.md b/vendor/orion/README.md new file mode 100644 index 0000000..cfc9fe8 --- /dev/null +++ b/vendor/orion/README.md @@ -0,0 +1,72 @@ +# orion +[![Tests](https://github.com/orion-rs/orion/workflows/Tests/badge.svg)](https://github.com/orion-rs/orion/actions) [![Daily tests](https://github.com/orion-rs/orion/workflows/Daily%20tests/badge.svg)](https://github.com/orion-rs/orion/actions) [![dudect](https://github.com/orion-rs/orion-dudect/workflows/dudect/badge.svg)](https://github.com/orion-rs/orion-dudect/actions) [![Audit](https://github.com/orion-rs/orion/actions/workflows/audit_check.yml/badge.svg)](https://github.com/orion-rs/orion/actions/workflows/audit_check.yml) [![codecov](https://codecov.io/gh/orion-rs/orion/branch/master/graph/badge.svg)](https://codecov.io/gh/orion-rs/orion) [![Documentation](https://docs.rs/orion/badge.svg)](https://docs.rs/orion/) [![Crates.io](https://img.shields.io/crates/v/orion.svg)](https://crates.io/crates/orion) [![Safety Dance](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) [![MSRV](https://img.shields.io/badge/MSRV-1.70-informational.svg)](https://img.shields.io/badge/MSRV-1.70-informational) [![Matrix](https://img.shields.io/matrix/orion-rs:matrix.org.svg?logo=matrix)](https://matrix.to/#/#orion-rs:matrix.org) + +### About +Orion is a cryptography library written in pure Rust. It aims to provide easy and usable crypto while trying to minimize the use of unsafe code. You can read more about Orion in the [wiki](https://github.com/orion-rs/orion/wiki). + +Currently supports: +* **AEAD**: (X)ChaCha20-Poly1305. +* **Hashing**: BLAKE2b, SHA2, SHA3. +* **KDF**: HKDF, PBKDF2, Argon2i. +* **Key exchange**: X25519. +* **MAC**: HMAC, Poly1305. +* **Stream ciphers**: (X)ChaCha20. +* **KEM**: DHKEM(X25519, HKDF-SHA256). + +Experimental support (with `experimental` feature enabled): +* **Committing AEAD**: (X)ChaCha20-Poly1305-BLAKE2b. + +### Security +This library has **not undergone any third-party security audit**. Usage is at **own risk**. + +Orion uses formally verified arithmetic, generated by Fiat Crypto, for the X25519 and Poly1305 implementations. + +See the [SECURITY.md](https://github.com/orion-rs/orion/blob/master/SECURITY.md) regarding recommendations on correct use, reporting security issues and more. Additional information about security regarding Orion is available in the [wiki](https://github.com/orion-rs/orion/wiki/Security). + +### Minimum Supported Rust Version +Rust 1.70 or later is supported however, the majority of testing happens with latest stable Rust. + +MSRV may be changed at any point and will not be considered a SemVer breaking change. + +### Crate Features + +- `default`/`safe_api`: All functionality, requires `std`. +- `serde`: Requires either `alloc` or `default`/`safe_api`. +- `alloc`: Argon2i in `hazardous` when `default`/`safe_api` is not available. +- `no_std`: Implicit feature that represents no heap allocations. Enabled by disabling default features and not selecting any additional features. +- `experimental`: These APIs may contain breaking changes in any non SemVer-breaking crate releases. + +More detailed explanation of the features in the [wiki](https://github.com/orion-rs/orion/wiki/Crate-features). + +### Documentation +Can be viewed [here](https://docs.rs/orion) or built with: + +``` +RUSTDOCFLAGS='--cfg docsrs' cargo +nightly doc --no-deps --all-features +``` + +### Tests and Fuzzing +The [wiki](https://github.com/orion-rs/orion/wiki/Testing-suite) has details on how Orion is tested. To run all tests: +``` +cargo test +``` + +Fuzzing is done using [honggfuzz-rs](https://github.com/rust-fuzz/honggfuzz-rs) in [orion-fuzz](https://github.com/orion-rs/orion-fuzz). See [orion-fuzz](https://github.com/orion-rs/orion-fuzz) on how to start fuzzing Orion. + +Constant-time execution tests can be found at [orion-dudect](https://github.com/orion-rs/orion-dudect) and [orion-sidefuzz](https://github.com/orion-rs/orion-sidefuzz). + +### Benchmarks +An overview of the performance that can be expected from Orion can be [seen here](https://github.com/orion-rs/orion/wiki/Benchmarks). + +The library can be benchmarked with [Criterion](https://github.com/bheisler/criterion.rs) as below. All benchmarking tests are located in `benches/`. +``` +cargo bench +``` +### Changelog +Please refer to the [CHANGELOG.md](https://github.com/orion-rs/orion/blob/master/CHANGELOG.md) list. + +### Contributing +Please refer to the guidelines in [CONTRIBUTING.md](https://github.com/orion-rs/orion/blob/master/CONTRIBUTING.md) for information on how to contribute to Orion. + +### License +Orion is licensed under the MIT license. See the `LICENSE` file for more information. diff --git a/vendor/orion/SECURITY.md b/vendor/orion/SECURITY.md new file mode 100644 index 0000000..ade4da3 --- /dev/null +++ b/vendor/orion/SECURITY.md @@ -0,0 +1,37 @@ +### Reporting security issues +All security issues should be reported using either GitHub [private vulnerability reporting](https://github.com/orion-rs/orion/security/advisories/new) or email the author at [brycx@protonmail.com](mailto:brycx@protonmail.com). + +We try to follow the [RFPolicy](https://en.wikipedia.org/wiki/RFPolicy), but with an initial response time of 2 weeks maximum. In practice, however, the initial response will most often be faster. + +Please clearly indicate in the subject line, that it is about a security issue. Providing many details about the issue makes it easier and faster to fix. + +Once a security issue has been confirmed and a fixed version has been released, an advisory will be submitted to the [RustSec Advisory Database](https://rustsec.org/). + +Thank you for taking the time to report and improve this project! + +### Threat model +The following are threats, which are considered out-of-scope for Orion. + +- Any side-channel other than timing-based +- Hardware-related issues +- Leaking sensitive memory[1] +- Timing-based side-channels when not building in release mode + +[1] Wiping sensitive memory is performed on a best-effort approach. However, sensitive memory being wiped or not leaked, cannot be guaranteed. See more in the [wiki](https://github.com/orion-rs/orion/wiki/Security#memory). + +### Supported versions +Currently, only the latest version, released on [crates.io](https://crates.io/crates/orion), receives testing and is supported with security fixes. + +There is no guarantee that a version, containing a security fix, will be SemVer-compatible to the previous one. + +Backporting security fixes to older versions will be considered on an ad hoc basis. + +### Yanking policy +Any version which is affected by a security issue, will be yanked. Even though we try to provide it, there is no guarantee that a SemVer-compatible version, containing a fix, will be available at the time of yanking. + +### Recommended best practices +These are recommendations on how to use Orion correctly: + +- Use `cargo audit` to ensure the current version has no published security vulnerabilities +- Never use `opt-level=0`, always build in release mode +- Always use the latest version of Orion diff --git a/vendor/orion/benches/bench.rs b/vendor/orion/benches/bench.rs new file mode 100644 index 0000000..a9416b3 --- /dev/null +++ b/vendor/orion/benches/bench.rs @@ -0,0 +1,488 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +extern crate criterion; +extern crate orion; + +use criterion::*; + +use orion::hazardous::{ + aead::{chacha20poly1305, xchacha20poly1305}, + hash::*, + kdf::{argon2i, hkdf, pbkdf2}, + mac::{hmac, poly1305}, + stream::*, +}; + +static INPUT_SIZES: [usize; 3] = [64 * 1024, 128 * 1024, 256 * 1024]; + +mod mac { + use super::*; + + pub fn bench_poly1305(c: &mut Criterion) { + let mut group = c.benchmark_group("Poly1305"); + let key = poly1305::OneTimeKey::generate(); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("compute mac", *size), + &input, + |b, input_message| { + b.iter(|| poly1305::Poly1305::poly1305(&key, &input_message).unwrap()) + }, + ); + } + } + + pub fn bench_hmac_sha256(c: &mut Criterion) { + let mut group = c.benchmark_group("HMAC-SHA256"); + // NOTE: Setting the key like this will pad it for HMAC. + // Padding is therefor not included in benchmarks. + let key = hmac::sha256::SecretKey::generate(); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("compute mac", *size), + &input, + |b, input_message| { + b.iter(|| hmac::sha256::HmacSha256::hmac(&key, &input_message).unwrap()) + }, + ); + } + } + + pub fn bench_hmac_sha512(c: &mut Criterion) { + let mut group = c.benchmark_group("HMAC-SHA512"); + // NOTE: Setting the key like this will pad it for HMAC. + // Padding is therefor not included in benchmarks. + let key = hmac::sha512::SecretKey::generate(); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("compute mac", *size), + &input, + |b, input_message| { + b.iter(|| hmac::sha512::HmacSha512::hmac(&key, &input_message).unwrap()) + }, + ); + } + } + + criterion_group! { + name = mac_benches; + config = Criterion::default(); + targets = + bench_poly1305, + bench_hmac_sha256, + bench_hmac_sha512, + } +} + +mod aead { + use super::*; + + pub fn bench_chacha20poly1305(c: &mut Criterion) { + let mut group = c.benchmark_group("ChaCha20-Poly1305"); + let key = chacha20poly1305::SecretKey::generate(); + let nonce = chacha20poly1305::Nonce::from_slice(&[0u8; 12]).unwrap(); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + let mut out = vec![0u8; input.len() + 16]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("encrypt", *size), + &input, + |b, input_message| { + b.iter(|| { + chacha20poly1305::seal(&key, &nonce, &input_message, None, &mut out) + .unwrap() + }) + }, + ); + } + } + + pub fn bench_xchacha20poly1305(c: &mut Criterion) { + let mut group = c.benchmark_group("XChaCha20-Poly1305"); + let key = xchacha20poly1305::SecretKey::generate(); + let nonce = xchacha20poly1305::Nonce::generate(); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + let mut out = vec![0u8; input.len() + 16]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("encrypt", *size), + &input, + |b, input_message| { + b.iter(|| { + xchacha20poly1305::seal(&key, &nonce, &input_message, None, &mut out) + .unwrap() + }) + }, + ); + } + } + + criterion_group! { + name = aead_benches; + config = Criterion::default(); + targets = + bench_chacha20poly1305, + bench_xchacha20poly1305, + } +} + +mod hash { + use super::*; + + pub fn bench_sha256(c: &mut Criterion) { + let mut group = c.benchmark_group("SHA256"); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("compute hash", *size), + &input, + |b, input_message| b.iter(|| sha2::sha256::Sha256::digest(&input_message).unwrap()), + ); + } + } + + pub fn bench_sha384(c: &mut Criterion) { + let mut group = c.benchmark_group("SHA384"); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("compute hash", *size), + &input, + |b, input_message| b.iter(|| sha2::sha384::Sha384::digest(&input_message).unwrap()), + ); + } + } + + pub fn bench_sha512(c: &mut Criterion) { + let mut group = c.benchmark_group("SHA512"); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("compute hash", *size), + &input, + |b, input_message| b.iter(|| sha2::sha512::Sha512::digest(&input_message).unwrap()), + ); + } + } + + pub fn bench_blake2b_512(c: &mut Criterion) { + let mut group = c.benchmark_group("BLAKE2b-512"); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("compute hash", *size), + &input, + |b, input_message| { + b.iter(|| { + blake2::blake2b::Hasher::Blake2b512 + .digest(&input_message) + .unwrap() + }) + }, + ); + } + } + + criterion_group! { + name = hash_benches; + config = Criterion::default(); + targets = + bench_sha256, + bench_sha384, + bench_sha512, + bench_blake2b_512, + } +} + +mod stream { + use super::*; + + pub fn bench_chacha20(c: &mut Criterion) { + let mut group = c.benchmark_group("ChaCha20"); + let key = chacha20poly1305::SecretKey::generate(); + let nonce = chacha20poly1305::Nonce::from([0u8; 12]); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + let mut out = vec![0u8; input.len()]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("xor-stream", *size), + &input, + |b, input_message| { + b.iter(|| chacha20::encrypt(&key, &nonce, 0, &input_message, &mut out).unwrap()) + }, + ); + } + } + + pub fn bench_xchacha20(c: &mut Criterion) { + let mut group = c.benchmark_group("XChaCha20"); + let key = xchacha20::SecretKey::generate(); + let nonce = xchacha20::Nonce::generate(); + + for size in INPUT_SIZES.iter() { + let input = vec![0u8; *size]; + let mut out = vec![0u8; input.len()]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("xor-stream", *size), + &input, + |b, input_message| { + b.iter(|| { + xchacha20::encrypt(&key, &nonce, 0, &input_message, &mut out).unwrap() + }) + }, + ); + } + } + + criterion_group! { + name = stream_benches; + config = Criterion::default(); + targets = + bench_chacha20, + bench_xchacha20, + } +} + +mod kdf { + use super::*; + + static OKM_SIZES: [usize; 1] = [512]; + static PBKDF2_ITERATIONS: [usize; 1] = [10000]; + + pub fn bench_hkdf_sha256(c: &mut Criterion) { + let mut group = c.benchmark_group("HKDF-HMAC-SHA256"); + + let ikm = vec![0u8; 64]; + let salt = ikm.clone(); + + for size in OKM_SIZES.iter() { + let mut okm_out = vec![0u8; *size]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("derive bytes", *size), + &ikm, + |b, input_ikm| { + b.iter(|| { + hkdf::sha256::derive_key(&salt, input_ikm, None, &mut okm_out).unwrap() + }) + }, + ); + } + } + + pub fn bench_hkdf_sha512(c: &mut Criterion) { + let mut group = c.benchmark_group("HKDF-HMAC-SHA512"); + + let ikm = vec![0u8; 64]; + let salt = ikm.clone(); + + for size in OKM_SIZES.iter() { + let mut okm_out = vec![0u8; *size]; + + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("derive bytes", *size), + &ikm, + |b, input_ikm| { + b.iter(|| { + hkdf::sha512::derive_key(&salt, input_ikm, None, &mut okm_out).unwrap() + }) + }, + ); + } + } + + pub fn bench_pbkdf2_sha256(c: &mut Criterion) { + let mut group = c.benchmark_group("PBKDF2-HMAC-SHA256"); + // 10 is the lowest acceptable sample size. + group.sample_size(10); + group.measurement_time(core::time::Duration::new(30, 0)); + + let ikm = vec![0u8; 64]; + let salt = ikm.clone(); + + for iterations in PBKDF2_ITERATIONS.iter() { + let mut dk_out = vec![0u8; 64]; + + // NOTE: The password newtype creation is included + // as this pads the salt for HMAC internally. + group.bench_with_input( + BenchmarkId::new("derive 64 bytes", *iterations), + &iterations, + |b, iter_count| { + b.iter(|| { + pbkdf2::sha256::derive_key( + &pbkdf2::sha256::Password::from_slice(&salt).unwrap(), + &ikm, + **iter_count, + &mut dk_out, + ) + .unwrap() + }) + }, + ); + } + } + + pub fn bench_pbkdf2_sha512(c: &mut Criterion) { + let mut group = c.benchmark_group("PBKDF2-HMAC-SHA512"); + // 10 is the lowest acceptable sample size. + group.sample_size(10); + group.measurement_time(core::time::Duration::new(30, 0)); + + let ikm = vec![0u8; 64]; + let salt = ikm.clone(); + + for iterations in PBKDF2_ITERATIONS.iter() { + let mut dk_out = vec![0u8; 64]; + + // NOTE: The password newtype creation is included + // as this pads the salt for HMAC internally. + group.bench_with_input( + BenchmarkId::new("derive 64 bytes", *iterations), + &iterations, + |b, iter_count| { + b.iter(|| { + pbkdf2::sha512::derive_key( + &pbkdf2::sha512::Password::from_slice(&salt).unwrap(), + &ikm, + **iter_count, + &mut dk_out, + ) + .unwrap() + }) + }, + ); + } + } + + pub fn bench_argon2i(c: &mut Criterion) { + let mut group = c.benchmark_group("Argon2i"); + + let iter = 3; + let mem = 128; + let password = [0u8; 16]; + let salt = [0u8; 16]; + let mut dk_out = [0u8; 32]; + + group.throughput(Throughput::Bytes(mem as u64)); + + group.bench_with_input( + BenchmarkId::new( + "derive bytes", + format!("iter: {}, mem (KiB): {}", iter, mem), + ), + &salt, + |b, _| { + b.iter(|| { + argon2i::derive_key(&password, &salt, iter, mem, None, None, &mut dk_out) + .unwrap() + }) + }, + ); + } + + criterion_group! { + name = kdf_benches; + config = Criterion::default(); + targets = + bench_argon2i, + bench_hkdf_sha256, + bench_hkdf_sha512, + bench_pbkdf2_sha256, + bench_pbkdf2_sha512, + } +} + +mod ecc { + use super::*; + use core::convert::TryFrom; + use orion::hazardous::ecc::x25519; + + pub fn bench_x25519(c: &mut Criterion) { + let mut group = c.benchmark_group("X25519"); + + let alice_sk = x25519::PrivateKey::generate(); + let alice_pk = x25519::PublicKey::try_from(&alice_sk).unwrap(); + + group.sample_size(100); + group.bench_function("key_agreement", move |b| { + b.iter_with_setup( + || x25519::PrivateKey::generate(), + |bob_sk| x25519::key_agreement(&bob_sk, &alice_pk).unwrap(), + ) + }); + } + + criterion_group! { + name = ecc_benches; + config = Criterion::default(); + targets = + bench_x25519 + } +} + +criterion_main!( + mac::mac_benches, + aead::aead_benches, + hash::hash_benches, + stream::stream_benches, + kdf::kdf_benches, + ecc::ecc_benches, +); diff --git a/vendor/orion/debian/patches/disable-benches.patch b/vendor/orion/debian/patches/disable-benches.patch new file mode 100644 index 0000000..fafbee5 --- /dev/null +++ b/vendor/orion/debian/patches/disable-benches.patch @@ -0,0 +1,23 @@ +Index: orion/Cargo.toml +=================================================================== +--- orion.orig/Cargo.toml ++++ orion/Cargo.toml +@@ -36,6 +36,7 @@ categories = [ + ] + license = "MIT" + repository = "https://github.com/orion-rs/orion" ++autobenches = false + + [package.metadata.docs.rs] + all-features = true +@@ -47,10 +48,6 @@ rustdoc-args = [ + [profile.dev] + opt-level = 1 + +-[[bench]] +-name = "bench" +-harness = false +- + [dependencies.ct-codecs] + version = "1.1.1" + optional = true diff --git a/vendor/orion/debian/patches/series b/vendor/orion/debian/patches/series new file mode 100644 index 0000000..40cc453 --- /dev/null +++ b/vendor/orion/debian/patches/series @@ -0,0 +1 @@ +disable-benches.patch diff --git a/vendor/orion/deny.toml b/vendor/orion/deny.toml new file mode 100644 index 0000000..eb9e60c --- /dev/null +++ b/vendor/orion/deny.toml @@ -0,0 +1,37 @@ +targets = [ + { triple = "x86_64-unknown-linux-gnu" }, + { triple = "x86_64-unknown-linux-musl" }, + { triple = "x86_64-apple-darwin" }, + { triple = "x86_64-pc-windows-msvc" }, +] + +[advisories] +unmaintained = "deny" +yanked = "deny" +notice = "deny" +unsound = "deny" +vulnerability = "deny" +ignore = [] + +[licenses] +unlicensed = "deny" +# We want really high confidence when inferring licenses from text +confidence-threshold = 0.93 +allow = [ + "MIT", + "BSD-3-Clause", + "Apache-2.0", +] +exceptions = [ + { allow = [ + "Unicode-DFS-2016", + ], name = "unicode-ident" }, +] + +[bans] +multiple-versions = "allow" # We don't maintain Cargo lockfile, so this isn't really feasible to deny +wildcards = "deny" # Dependencies should not have be specified with '*' + +[sources] +unknown-registry = "deny" # crates.io is allowed and a known register by default +unknown-git = "deny" \ No newline at end of file diff --git a/vendor/orion/src/errors.rs b/vendor/orion/src/errors.rs new file mode 100644 index 0000000..33a5662 --- /dev/null +++ b/vendor/orion/src/errors.rs @@ -0,0 +1,140 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use core::fmt; + +#[allow(clippy::derive_partial_eq_without_eq)] +/// Opaque error. +#[derive(Clone, Copy, PartialEq)] +pub struct UnknownCryptoError; + +impl fmt::Display for UnknownCryptoError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UnknownCryptoError") + } +} + +impl fmt::Debug for UnknownCryptoError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UnknownCryptoError") + } +} + +#[cfg(feature = "safe_api")] +impl std::error::Error for UnknownCryptoError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +#[cfg(feature = "safe_api")] +impl From for UnknownCryptoError { + fn from(_: getrandom::Error) -> Self { + UnknownCryptoError + } +} + +#[cfg(feature = "safe_api")] +impl From for UnknownCryptoError { + fn from(_: ct_codecs::Error) -> Self { + UnknownCryptoError + } +} + +impl From for UnknownCryptoError { + fn from(_: core::num::ParseIntError) -> Self { + UnknownCryptoError + } +} + +#[test] +#[cfg(feature = "safe_api")] +// format! is only available with std +fn test_unknown_crypto_error_debug_display() { + // Tests Debug impl through "{:?}" + let err = format!("{:?}", UnknownCryptoError); + assert_eq!(err, "UnknownCryptoError"); + // Tests Display impl through "{}" + let err = format!("{}", UnknownCryptoError); + assert_eq!(err, "UnknownCryptoError"); +} + +#[test] +#[cfg(feature = "safe_api")] +// format! is only available with std +fn test_unknown_crypto_from_getrandom() { + use core::num::NonZeroU32; + // Choose some random error code. + let err_code = NonZeroU32::new(12).unwrap(); + let err_foreign: getrandom::Error = getrandom::Error::from(err_code); + + // Tests Debug impl through "{:?}" + let err = format!("{:?}", UnknownCryptoError::from(err_foreign)); + assert_eq!(err, "UnknownCryptoError"); + // Tests Display impl through "{}" + let err = format!("{}", UnknownCryptoError::from(err_foreign)); + assert_eq!(err, "UnknownCryptoError"); +} + +#[test] +#[cfg(feature = "safe_api")] +fn test_source() { + use std::error::Error; + assert!(UnknownCryptoError.source().is_none()); +} + +#[test] +#[cfg(feature = "safe_api")] +fn test_unknown_crypto_from_decode_error() { + use ct_codecs::Error; + + let err_one = Error::InvalidInput; + let err_two = Error::Overflow; + + // Tests Debug impl through "{:?}" and Display impl though "{}" + let err = format!( + "{:?}:{}", + UnknownCryptoError::from(err_one), + UnknownCryptoError::from(err_one) + ); + assert_eq!(err, "UnknownCryptoError:UnknownCryptoError"); + let err = format!( + "{:?}:{}", + UnknownCryptoError::from(err_two), + UnknownCryptoError::from(err_two) + ); + assert_eq!(err, "UnknownCryptoError:UnknownCryptoError"); +} + +#[test] +#[cfg(feature = "safe_api")] +fn test_unknown_crypto_from_parseint_error() { + let err_foreign = "j".parse::().unwrap_err(); + + // Tests Debug impl through "{:?}" and Display impl though "{}" + let err = format!( + "{:?}:{}", + UnknownCryptoError::from(err_foreign.clone()), + UnknownCryptoError::from(err_foreign) + ); + assert_eq!(err, "UnknownCryptoError:UnknownCryptoError"); +} diff --git a/vendor/orion/src/hazardous/aead/chacha20poly1305.rs b/vendor/orion/src/hazardous/aead/chacha20poly1305.rs new file mode 100644 index 0000000..8211253 --- /dev/null +++ b/vendor/orion/src/hazardous/aead/chacha20poly1305.rs @@ -0,0 +1,421 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `secret_key`: The secret key. +//! - `nonce`: The nonce value. +//! - `ad`: Additional data to authenticate (this is not encrypted and can be [`None`]). +//! - `ciphertext_with_tag`: The encrypted data with the corresponding 16 byte +//! Poly1305 tag appended to it. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the +//! `ciphertext_with_tag`/`plaintext` after encryption/decryption. +//! +//! `ad`: "A typical use for these data is to authenticate version numbers, +//! timestamps or monotonically increasing counters in order to discard previous +//! messages and prevent replay attacks." See [libsodium docs] for more information. +//! +//! `nonce`: "Counters and LFSRs are both acceptable ways of generating unique +//! nonces, as is encrypting a counter using a block cipher with a 64-bit block +//! size such as DES. Note that it is not acceptable to use a truncation of a +//! counter encrypted with block ciphers with 128-bit or 256-bit blocks, +//! because such a truncation may repeat after a short time." See [RFC] for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` + [`POLY1305_OUTSIZE`] when calling [`seal()`]. +//! - The length of `dst_out` is less than `ciphertext_with_tag` - [`POLY1305_OUTSIZE`] when +//! calling [`open()`]. +//! - The length of `ciphertext_with_tag` is not at least [`POLY1305_OUTSIZE`]. +//! - The received tag does not match the calculated tag when calling [`open()`]. +//! - `plaintext.len()` + [`POLY1305_OUTSIZE`] overflows when calling [`seal()`]. +//! - Converting `usize` to `u64` would be a lossy conversion. +//! - `plaintext.len() >` [`P_MAX`] +//! - `ad.len() >` [`A_MAX`] +//! - `ciphertext_with_tag.len() >` [`C_MAX`] +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1 * 64` bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Only a nonce for XChaCha20Poly1305 is big enough to be randomly generated +//! using a CSPRNG. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! - The length of the `plaintext` is not hidden, only its contents. +//! +//! # Recommendation: +//! - It is recommended to use [`XChaCha20Poly1305`] when possible. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::aead; +//! +//! let secret_key = aead::chacha20poly1305::SecretKey::generate(); +//! +//! // WARNING: This nonce is only meant for demonstration and should not +//! // be repeated. Please read the security section. +//! let nonce = aead::chacha20poly1305::Nonce::from([0u8; 12]); +//! let ad = "Additional data".as_bytes(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of the above message is 15 and then we accommodate 16 for the Poly1305 +//! // tag. +//! +//! let mut dst_out_ct = [0u8; 15 + 16]; +//! let mut dst_out_pt = [0u8; 15]; +//! // Encrypt and place ciphertext + tag in dst_out_ct +//! aead::chacha20poly1305::seal(&secret_key, &nonce, message, Some(&ad), &mut dst_out_ct)?; +//! // Verify tag, if correct then decrypt and place message in dst_out_pt +//! aead::chacha20poly1305::open(&secret_key, &nonce, &dst_out_ct, Some(&ad), &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt.as_ref(), message.as_ref()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: super::stream::chacha20::SecretKey::generate +//! [`XChaCha20Poly1305`]: xchacha20poly1305 +//! [`POLY1305_OUTSIZE`]: super::mac::poly1305::POLY1305_OUTSIZE +//! [`seal()`]: chacha20poly1305::seal +//! [`open()`]: chacha20poly1305::open +//! [RFC]: https://tools.ietf.org/html/rfc8439#section-3 +//! [libsodium docs]: https://download.libsodium.org/doc/secret-key_cryptography/aead#additional-data +//! [`P_MAX`]: chacha20poly1305::P_MAX +//! [`A_MAX`]: chacha20poly1305::A_MAX +//! [`C_MAX`]: chacha20poly1305::C_MAX + +pub use crate::hazardous::stream::chacha20::{Nonce, SecretKey}; +use crate::{ + errors::UnknownCryptoError, + hazardous::{ + mac::poly1305::{OneTimeKey, Poly1305, POLY1305_KEYSIZE, POLY1305_OUTSIZE}, + stream::chacha20::{self, ChaCha20, CHACHA_BLOCKSIZE}, + }, + util, +}; +use core::convert::TryInto; +use zeroize::Zeroizing; + +/// The initial counter used for encryption and decryption. +pub(crate) const ENC_CTR: u32 = 1; + +/// The initial counter used for Poly1305 key generation. +pub(crate) const AUTH_CTR: u32 = 0; + +/// The maximum size of the plaintext (see [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439#section-2.8)). +pub const P_MAX: u64 = (u32::MAX as u64) * 64; + +/// The maximum size of the ciphertext (see [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439#section-2.8)). +pub const C_MAX: u64 = P_MAX + (POLY1305_OUTSIZE as u64); + +/// The maximum size of the associated data (see [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439#section-2.8)). +pub const A_MAX: u64 = u64::MAX; + +/// Poly1305 key generation using IETF ChaCha20. +pub(crate) fn poly1305_key_gen( + ctx: &mut ChaCha20, + tmp_buffer: &mut Zeroizing<[u8; CHACHA_BLOCKSIZE]>, +) -> OneTimeKey { + ctx.keystream_block(AUTH_CTR, tmp_buffer.as_mut()); + OneTimeKey::from_slice(&tmp_buffer[..POLY1305_KEYSIZE]).unwrap() +} + +/// Authenticates the ciphertext, ad and their lengths. +pub(crate) fn process_authentication( + auth_ctx: &mut Poly1305, + ad: &[u8], + ciphertext: &[u8], +) -> Result<(), UnknownCryptoError> { + auth_ctx.process_pad_to_blocksize(ad)?; + auth_ctx.process_pad_to_blocksize(ciphertext)?; + + let (ad_len, ct_len): (u64, u64) = match (ad.len().try_into(), ciphertext.len().try_into()) { + (Ok(alen), Ok(clen)) => (alen, clen), + _ => return Err(UnknownCryptoError), + }; + + let mut tmp_pad = [0u8; 16]; + tmp_pad[0..8].copy_from_slice(&ad_len.to_le_bytes()); + tmp_pad[8..16].copy_from_slice(&ct_len.to_le_bytes()); + auth_ctx.update(tmp_pad.as_ref()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// AEAD ChaCha20Poly1305 encryption and authentication as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub fn seal( + secret_key: &SecretKey, + nonce: &Nonce, + plaintext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(plaintext.len()).map_err(|_| UnknownCryptoError)? > P_MAX { + return Err(UnknownCryptoError); + } + + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + + match plaintext.len().checked_add(POLY1305_OUTSIZE) { + Some(out_min_len) => { + if dst_out.len() < out_min_len { + return Err(UnknownCryptoError); + } + } + None => return Err(UnknownCryptoError), + }; + + let mut stream = + ChaCha20::new(secret_key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + let mut auth_ctx = Poly1305::new(&poly1305_key_gen(&mut stream, &mut tmp)); + let ad_len = ad.len(); + let ct_len = plaintext.len(); + + auth_ctx.process_pad_to_blocksize(ad)?; + + if ct_len != 0 { + for (ctr, (p_block, c_block)) in plaintext + .chunks(CHACHA_BLOCKSIZE) + .zip(dst_out.chunks_mut(CHACHA_BLOCKSIZE)) + .enumerate() + { + match ENC_CTR.checked_add(ctr as u32) { + Some(counter) => { + // See https://github.com/orion-rs/orion/issues/308 + stream.next_produceable()?; + // We know at this point that: + // 1) ENC_CTR + ctr does __not__ overflow/panic + // 2) ChaCha20 instance will not panic on the next keystream produced + + // Copy keystream directly into the dst_out block in there is + // enough space, too avoid copying from `tmp` each time and only + // on the last block instead. `c_block` must be full blocksize, + // or we leave behind keystream data in it, if `p_block` is not full length + // while `c_block` is. + if p_block.len() == CHACHA_BLOCKSIZE && c_block.len() == CHACHA_BLOCKSIZE { + stream.keystream_block(counter, c_block); + xor_slices!(p_block, c_block); + auth_ctx.update(c_block)?; + } + + // We only pad this last block to the Poly1305 blocksize since `CHACHA_BLOCKSIZE` + // already is evenly divisible by 16, so the previous full-length blocks do not matter. + if p_block.len() < CHACHA_BLOCKSIZE { + stream.keystream_block(counter, tmp.as_mut()); + xor_slices!(p_block, tmp.as_mut()); + c_block[..p_block.len()].copy_from_slice(&tmp.as_ref()[..p_block.len()]); + auth_ctx.process_pad_to_blocksize(&c_block[..p_block.len()])?; + } + } + None => return Err(UnknownCryptoError), + } + } + } + + let (adlen, ctlen): (u64, u64) = match (ad_len.try_into(), ct_len.try_into()) { + (Ok(alen), Ok(clen)) => (alen, clen), + _ => return Err(UnknownCryptoError), + }; + + let mut tmp_pad = [0u8; 16]; + tmp_pad[0..8].copy_from_slice(&adlen.to_le_bytes()); + tmp_pad[8..16].copy_from_slice(&ctlen.to_le_bytes()); + auth_ctx.update(tmp_pad.as_ref())?; + + dst_out[ct_len..(ct_len + POLY1305_OUTSIZE)] + .copy_from_slice(auth_ctx.finalize()?.unprotected_as_bytes()); + + Ok(()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// AEAD ChaCha20Poly1305 decryption and authentication as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub fn open( + secret_key: &SecretKey, + nonce: &Nonce, + ciphertext_with_tag: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(ciphertext_with_tag.len()).map_err(|_| UnknownCryptoError)? > C_MAX { + return Err(UnknownCryptoError); + } + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + if ciphertext_with_tag.len() < POLY1305_OUTSIZE { + return Err(UnknownCryptoError); + } + if dst_out.len() < ciphertext_with_tag.len() - POLY1305_OUTSIZE { + return Err(UnknownCryptoError); + } + + let mut dec_ctx = + ChaCha20::new(secret_key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + let mut auth_ctx = Poly1305::new(&poly1305_key_gen(&mut dec_ctx, &mut tmp)); + + let ciphertext_len = ciphertext_with_tag.len() - POLY1305_OUTSIZE; + process_authentication(&mut auth_ctx, ad, &ciphertext_with_tag[..ciphertext_len])?; + util::secure_cmp( + auth_ctx.finalize()?.unprotected_as_bytes(), + &ciphertext_with_tag[ciphertext_len..], + )?; + + if ciphertext_len != 0 { + dst_out[..ciphertext_len].copy_from_slice(&ciphertext_with_tag[..ciphertext_len]); + chacha20::xor_keystream( + &mut dec_ctx, + ENC_CTR, + tmp.as_mut(), + &mut dst_out[..ciphertext_len], + )?; + } + + Ok(()) +} + +// Testing public functions in the module. +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use super::*; + use crate::test_framework::aead_interface::{test_diff_params_err, AeadTestRunner}; + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_aead_interface(input: Vec, ad: Vec) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::from_slice(&[0u8; chacha20::IETF_CHACHA_NONCESIZE]).unwrap(); + AeadTestRunner( + seal, + open, + secret_key, + nonce, + &input, + None, + POLY1305_OUTSIZE, + &ad, + ); + test_diff_params_err(&seal, &open, &input, POLY1305_OUTSIZE); + true + } +} + +// Testing any test vectors that aren't put into library's /tests folder. +#[cfg(test)] +mod test_vectors { + use super::*; + + #[test] + fn rfc8439_poly1305_key_gen_1() { + let key = SecretKey::from_slice(&[0u8; 32]).unwrap(); + let nonce = Nonce::from_slice(&[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]) + .unwrap(); + let expected = [ + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, + 0xbd, 0x28, 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, 0xa8, 0x36, 0xef, 0xcc, + 0x8b, 0x77, 0x0d, 0xc7, + ]; + + let mut chacha20_ctx = + ChaCha20::new(key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + assert_eq!( + poly1305_key_gen(&mut chacha20_ctx, &mut tmp_block).unprotected_as_bytes(), + expected.as_ref() + ); + } + + #[test] + fn rfc8439_poly1305_key_gen_2() { + let key = SecretKey::from_slice(&[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + ]) + .unwrap(); + let nonce = Nonce::from_slice(&[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]) + .unwrap(); + let expected = [ + 0xec, 0xfa, 0x25, 0x4f, 0x84, 0x5f, 0x64, 0x74, 0x73, 0xd3, 0xcb, 0x14, 0x0d, 0xa9, + 0xe8, 0x76, 0x06, 0xcb, 0x33, 0x06, 0x6c, 0x44, 0x7b, 0x87, 0xbc, 0x26, 0x66, 0xdd, + 0xe3, 0xfb, 0xb7, 0x39, + ]; + + let mut chacha20_ctx = + ChaCha20::new(key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + assert_eq!( + poly1305_key_gen(&mut chacha20_ctx, &mut tmp_block).unprotected_as_bytes(), + expected.as_ref() + ); + } + + #[test] + fn rfc8439_poly1305_key_gen_3() { + let key = SecretKey::from_slice(&[ + 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, + 0xb5, 0xf0, 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, 0x9d, 0xca, 0x5c, 0xbc, + 0x20, 0x70, 0x75, 0xc0, + ]) + .unwrap(); + let nonce = Nonce::from_slice(&[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]) + .unwrap(); + let expected = [ + 0x96, 0x5e, 0x3b, 0xc6, 0xf9, 0xec, 0x7e, 0xd9, 0x56, 0x08, 0x08, 0xf4, 0xd2, 0x29, + 0xf9, 0x4b, 0x13, 0x7f, 0xf2, 0x75, 0xca, 0x9b, 0x3f, 0xcb, 0xdd, 0x59, 0xde, 0xaa, + 0xd2, 0x33, 0x10, 0xae, + ]; + + let mut chacha20_ctx = + ChaCha20::new(key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + assert_eq!( + poly1305_key_gen(&mut chacha20_ctx, &mut tmp_block).unprotected_as_bytes(), + expected.as_ref() + ); + } +} diff --git a/vendor/orion/src/hazardous/aead/mod.rs b/vendor/orion/src/hazardous/aead/mod.rs new file mode 100644 index 0000000..61d4087 --- /dev/null +++ b/vendor/orion/src/hazardous/aead/mod.rs @@ -0,0 +1,30 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// AEAD ChaCha20Poly1305 as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub mod chacha20poly1305; + +/// AEAD XChaCha20Poly1305 as specified in the [draft RFC](https://github.com/bikeshedders/xchacha-rfc). +pub mod xchacha20poly1305; + +/// Streaming AEAD based on XChaCha20Poly1305. +pub mod streaming; diff --git a/vendor/orion/src/hazardous/aead/streaming.rs b/vendor/orion/src/hazardous/aead/streaming.rs new file mode 100644 index 0000000..a030f06 --- /dev/null +++ b/vendor/orion/src/hazardous/aead/streaming.rs @@ -0,0 +1,1515 @@ +// MIT License + +// Copyright (c) 2019-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # About: +//! +//! This implementation is based on and compatible with the ["secretstream" API] of libsodium. +//! +//! # Parameters: +//! - `secret_key`: The secret key. +//! - `nonce`: The nonce value. +//! - `ad`: Additional data to authenticate (this is not encrypted and can be `None`). +//! - `plaintext`: The data to be encrypted. +//! - `ciphertext`: The encrypted data with, a Poly1305 tag and a [`StreamTag`] indicating its function. +//! - `dst_out`: Destination array that will hold the `ciphertext`/`plaintext` after encryption/decryption. +//! - `tag`: Indicates the type of message. The `tag` is a part of the output when encrypting. It +//! is encrypted and authenticated. +//! +//! `ad`: "A typical use for these data is to authenticate version numbers, +//! timestamps or monotonically increasing counters in order to discard previous +//! messages and prevent replay attacks." See [libsodium docs] for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` + [`ABYTES`] when calling [`seal_chunk()`]. +//! - The length of `dst_out` is less than `ciphertext` - [`ABYTES`] when calling [`open_chunk()`]. +//! - The length of the `ciphertext` is less than [`ABYTES`]. +//! - The received mac does not match the calculated mac when calling [`open_chunk()`]. This can +//! indicate a dropped or reordered message within the stream. +//! - More than `2^32-3 * 64` bytes of data are processed when sealing/opening a single chunk. +//! - [`ABYTES`] + `plaintext.len()` overflows when encrypting. +//! +//! # Panics: +//! A panic will occur if: +//! - 64 + (`ciphertext.len()` - [`ABYTES`]) overflows [`u64::MAX`] when decrypting. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given key. +//! - The nonce can be randomly generated using a CSPRNG. [`Nonce::generate()`] can be used for this. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! - The lengths of the messages are not hidden, only their contents. +//! - It is recommended to use [`StreamTag::Finish`] as the tag for the last message. This allows the +//! decrypting side to detect if messages at the end of the stream are lost. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::aead::streaming::*; +//! +//! let secret_key = SecretKey::generate(); +//! let nonce = Nonce::generate(); +//! let ad = "Additional data".as_bytes(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of the above message is 15 and then we accommodate 17 +//! // for the mac and tag. +//! let mut dst_out_ct = [0u8; 15 + ABYTES]; +//! let mut dst_out_pt = [0u8; 15]; +//! +//! let mut ctx_enc = StreamXChaCha20Poly1305::new(&secret_key, &nonce); +//! +//! // Encrypt and place tag + ciphertext + mac in dst_out_ct +//! ctx_enc.seal_chunk(message, Some(ad), &mut dst_out_ct, &StreamTag::Message)?; +//! +//! let mut ctx_dec = StreamXChaCha20Poly1305::new(&secret_key, &nonce); +//! +//! // Decrypt and save the tag the message was encrypted with. +//! let tag = ctx_dec.open_chunk(&dst_out_ct, Some(ad), &mut dst_out_pt)?; +//! +//! assert_eq!(tag, StreamTag::Message); +//! assert_eq!(dst_out_pt.as_ref(), message); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: super::stream::chacha20::SecretKey::generate +//! [`Nonce::generate()`]: super::stream::xchacha20::Nonce::generate +//! [`StreamTag`]: streaming::StreamTag +//! [`StreamTag::Finish`]: streaming::StreamTag::Finish +//! [`ABYTES`]: streaming::ABYTES +//! [`seal_chunk()`]: streaming::StreamXChaCha20Poly1305::seal_chunk +//! [`open_chunk()`]: streaming::StreamXChaCha20Poly1305::open_chunk +//! ["secretstream" API]: https://download.libsodium.org/doc/secret-key_cryptography/secretstream + +use crate::errors::UnknownCryptoError; +use crate::hazardous::aead::chacha20poly1305::poly1305_key_gen; +use crate::hazardous::mac::poly1305::{Poly1305, Tag as Poly1305Tag, POLY1305_OUTSIZE}; +pub use crate::hazardous::stream::chacha20::SecretKey; +use crate::hazardous::stream::chacha20::{ + encrypt as chacha20_enc, encrypt_in_place as chacha20_xor_stream, ChaCha20, Nonce as IETFNonce, + CHACHA_BLOCKSIZE, CHACHA_KEYSIZE, HCHACHA_NONCESIZE, IETF_CHACHA_NONCESIZE, +}; +use crate::hazardous::stream::xchacha20::subkey_and_nonce; +pub use crate::hazardous::stream::xchacha20::Nonce; +use core::convert::TryFrom; +use subtle::ConstantTimeEq; +use zeroize::{Zeroize, Zeroizing}; + +#[derive(Debug, Clone, Copy)] +/// Tag that indicates the type of message. +pub enum StreamTag { + /// A message with no special meaning. + Message, + /// Marks that the message is the end of a set of messages. Allows the decrypting site to + /// start working with this data. + Push, + /// Derives a new secret key and forgets the one used for earlier encryption/decryption + /// operations. + Rekey, + /// Indicates the end of a stream. Also does a rekey. + Finish, +} + +impl StreamTag { + #[inline] + /// Return the tag as a byte. + pub fn as_byte(&self) -> u8 { + match *self { + StreamTag::Message => 0b0000_0000, + StreamTag::Push => 0b0000_0001, + StreamTag::Rekey => 0b0000_0010, + StreamTag::Finish => 0b0000_0011, /* StreamTag::Push.as_byte() | StreamTag::Rekey.as_bytes() */ + } + } +} + +impl TryFrom for StreamTag { + type Error = UnknownCryptoError; + + fn try_from(byte: u8) -> Result { + match byte { + 0b0000_0000 => Ok(Self::Message), + 0b0000_0001 => Ok(Self::Push), + 0b0000_0010 => Ok(Self::Rekey), + 0b0000_0011 => Ok(Self::Finish), + _ => Err(UnknownCryptoError), + } + } +} + +impl PartialEq for StreamTag { + fn eq(&self, other: &StreamTag) -> bool { + (self.as_byte().ct_eq(&other.as_byte())).into() + } +} + +/// The size of the internal counter. +const COUNTERBYTES: usize = 4; +/// The size of the internal nonce. +const INONCEBYTES: usize = 8; +/// The size of a StreamTag. +pub const TAG_SIZE: usize = 1; +/// Size of additional data appended to each message. +pub const ABYTES: usize = POLY1305_OUTSIZE + TAG_SIZE; + +/// Padding size that gives the needed bytes to pad `input` to an integral +/// multiple of 16. +fn padding(input: usize) -> usize { + if input == 0 { + return 0; + } + + let rem = input % 16; + + if rem != 0 { + 16 - rem + } else { + 0 + } +} + +/// Streaming XChaCha20Poly1305 state. +pub struct StreamXChaCha20Poly1305 { + key: SecretKey, + counter: u32, + inonce: [u8; INONCEBYTES], +} + +impl core::fmt::Debug for StreamXChaCha20Poly1305 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "StreamXChaCha20Poly1305 {{ key: [***OMITTED***], counter: [***OMITTED***], inonce: [***OMITTED***]", + ) + } +} + +impl StreamXChaCha20Poly1305 { + /// Return a nonce used with ChaCha20Poly1305 based on the internal counter + /// and inonce. + fn get_nonce(&self) -> IETFNonce { + let mut nonce = [0u8; IETF_CHACHA_NONCESIZE]; + nonce[..COUNTERBYTES].copy_from_slice(&self.counter.to_le_bytes()); + nonce[COUNTERBYTES..].copy_from_slice(&self.inonce); + + IETFNonce::from(nonce) + } + + /// Generates a Poly1305 tag for a message. + fn generate_auth_tag( + &mut self, + text: &[u8], + ad: &[u8], + msglen: usize, + block: &[u8], + textpos: usize, + ) -> Result { + debug_assert!(text.len() >= textpos + msglen); + + let mut chacha20_ctx = ChaCha20::new( + self.key.unprotected_as_bytes(), + self.get_nonce().as_ref(), + true, + ) + .unwrap(); + let mut tmp_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + let mut pad = [0u8; 16]; + let mut poly = Poly1305::new(&poly1305_key_gen(&mut chacha20_ctx, &mut tmp_block)); + + poly.process_pad_to_blocksize(ad)?; + poly.update(block)?; + poly.update(&text[textpos..(textpos + msglen)])?; + poly.update(&pad[..padding(CHACHA_BLOCKSIZE.wrapping_sub(msglen))])?; + pad[..8].copy_from_slice(&(ad.len() as u64).to_le_bytes()); + pad[8..16].copy_from_slice( + &((CHACHA_BLOCKSIZE as u64) + .checked_add(msglen as u64) + .unwrap()) + .to_le_bytes(), + ); + poly.update(&pad)?; + + poly.finalize() + } + + /// Update internal inonce and counter. Performs rekey on overflowing counter and + /// designated tags. + fn advance_state( + &mut self, + mac: &Poly1305Tag, + tag: &StreamTag, + ) -> Result<(), UnknownCryptoError> { + xor_slices!(mac.unprotected_as_bytes()[..INONCEBYTES], self.inonce); + self.counter = self.counter.wrapping_add(1); + if bool::from( + !(tag.as_byte() & StreamTag::Rekey.as_byte()).ct_eq(&0u8) | self.counter.ct_eq(&0u32), + ) { + self.rekey()?; + }; + + Ok(()) + } + + /// Initialize a `StreamXChaCha20Poly1305` struct with a given secret key and nonce. + pub fn new(secret_key: &SecretKey, nonce: &Nonce) -> Self { + let mut inonce = [0u8; INONCEBYTES]; + inonce.copy_from_slice(&nonce.as_ref()[HCHACHA_NONCESIZE..]); + + Self { + key: subkey_and_nonce(secret_key, nonce).0, + counter: 1, + inonce, + } + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Derives a new secret key used for encryption and decryption. + pub fn rekey(&mut self) -> Result<(), UnknownCryptoError> { + let mut new_key_and_inonce = [0u8; CHACHA_KEYSIZE + INONCEBYTES]; + new_key_and_inonce[..CHACHA_KEYSIZE].copy_from_slice(self.key.unprotected_as_bytes()); + new_key_and_inonce[CHACHA_KEYSIZE..].copy_from_slice(&self.inonce); + + chacha20_xor_stream(&self.key, &self.get_nonce(), 0, &mut new_key_and_inonce)?; + + self.key = SecretKey::from_slice(&new_key_and_inonce[..CHACHA_KEYSIZE]).unwrap(); + self.inonce + .copy_from_slice(&new_key_and_inonce[CHACHA_KEYSIZE..]); + self.counter = 1; + new_key_and_inonce.zeroize(); + + Ok(()) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Encrypt and authenticate a single message and tag. + pub fn seal_chunk( + &mut self, + plaintext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], + tag: &StreamTag, + ) -> Result<(), UnknownCryptoError> { + let msglen = plaintext.len(); + match ABYTES.checked_add(msglen) { + Some(out_min_len) => { + if dst_out.len() < out_min_len { + return Err(UnknownCryptoError); + } + } + None => return Err(UnknownCryptoError), + }; + + let mut block = [0u8; CHACHA_BLOCKSIZE]; + let ad = ad.unwrap_or(&[0u8; 0]); + + let macpos = TAG_SIZE + msglen; + let nonce = self.get_nonce(); + + block[0] = tag.as_byte(); + chacha20_xor_stream(&self.key, &nonce, 1, &mut block)?; + dst_out[0] = block[0]; + + if msglen != 0 { + chacha20_enc(&self.key, &nonce, 2, plaintext, &mut dst_out[TAG_SIZE..])?; + } + + let mac = self.generate_auth_tag(dst_out, ad, msglen, &block, TAG_SIZE)?; + dst_out[macpos..(macpos + POLY1305_OUTSIZE)].copy_from_slice(mac.unprotected_as_bytes()); + + self.advance_state(&mac, tag) + } + + #[allow(clippy::range_plus_one)] + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Authenticate and decrypt a single message and tag. + pub fn open_chunk( + &mut self, + ciphertext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result { + if ciphertext.len() < ABYTES { + return Err(UnknownCryptoError); + } + + let msglen = ciphertext.len() - ABYTES; + if dst_out.len() < msglen { + return Err(UnknownCryptoError); + } + + let mut block = [0u8; CHACHA_BLOCKSIZE]; + let ad = ad.unwrap_or(&[0u8; 0]); + + let macpos = TAG_SIZE + msglen; + let nonce = self.get_nonce(); + + block[0] = ciphertext[0]; + chacha20_xor_stream(&self.key, &nonce, 1, &mut block)?; + let tag = StreamTag::try_from(block[0])?; + block[0] = ciphertext[0]; + let mac = self.generate_auth_tag(ciphertext, ad, msglen, &block, TAG_SIZE)?; + if !(mac == &ciphertext[macpos..macpos + mac.len()]) { + return Err(UnknownCryptoError); + } + if msglen != 0 { + chacha20_enc( + &self.key, + &nonce, + 2, + &ciphertext[TAG_SIZE..(TAG_SIZE + msglen)], + dst_out, + )?; + } + self.advance_state(&mac, &tag)?; + + Ok(tag) + } +} + +#[cfg(test)] +mod public { + #[cfg(feature = "safe_api")] + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let secret_key = SecretKey::generate(); + let nonce = Nonce::generate(); + let initial_state = StreamXChaCha20Poly1305::new(&secret_key, &nonce); + let debug = format!("{:?}", initial_state); + let expected = "StreamXChaCha20Poly1305 { key: [***OMITTED***], counter: [***OMITTED***], inonce: [***OMITTED***]"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "safe_api")] + mod proptest { + use crate::errors::UnknownCryptoError; + use crate::hazardous::aead::streaming::{ + Nonce, SecretKey, StreamTag, StreamXChaCha20Poly1305, ABYTES, + }; + use crate::test_framework::aead_interface::*; + use core::convert::TryFrom; + + fn seal( + sk: &SecretKey, + nonce: &Nonce, + input: &[u8], + ad: Option<&[u8]>, + output: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + let mut state = StreamXChaCha20Poly1305::new(sk, nonce); + state.seal_chunk(input, ad, output, &StreamTag::Message) + } + + fn open( + sk: &SecretKey, + nonce: &Nonce, + input: &[u8], + ad: Option<&[u8]>, + output: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + let mut state = StreamXChaCha20Poly1305::new(sk, nonce); + state.open_chunk(input, ad, output)?; + + Ok(()) + } + + #[quickcheck] + fn prop_aead_interface(input: Vec, ad: Vec) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::generate(); + AeadTestRunner(seal, open, secret_key, nonce, &input, None, ABYTES, &ad); + + true + } + + #[quickcheck] + fn prop_same_input_twice_diff_output(input: Vec, ad: Vec) -> bool { + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::generate(), &Nonce::generate()); + + let mut ct1 = vec![0u8; input.len() + ABYTES]; + let mut ct2 = ct1.clone(); + + ctx.seal_chunk(&input, Some(&ad), &mut ct1, &StreamTag::Message) + .unwrap(); + ctx.seal_chunk(&input, Some(&ad), &mut ct2, &StreamTag::Message) + .unwrap(); + + ct1 != ct2 + } + + #[quickcheck] + fn prop_tag(byte: u8) -> bool { + match byte { + 0u8 => StreamTag::try_from(byte).unwrap() == StreamTag::Message, + 1u8 => StreamTag::try_from(byte).unwrap() == StreamTag::Push, + 2u8 => StreamTag::try_from(byte).unwrap() == StreamTag::Rekey, + 3u8 => StreamTag::try_from(byte).unwrap() == StreamTag::Finish, + _ => StreamTag::try_from(byte).is_err(), + } + } + } +} + +#[cfg(test)] +mod private { + use super::*; + + mod test_padding { + use super::*; + #[test] + fn test_length_padding() { + assert_eq!(padding(0), 0); + assert_eq!(padding(1), 15); + assert_eq!(padding(2), 14); + assert_eq!(padding(3), 13); + assert_eq!(padding(4), 12); + assert_eq!(padding(5), 11); + assert_eq!(padding(6), 10); + assert_eq!(padding(7), 9); + assert_eq!(padding(8), 8); + assert_eq!(padding(9), 7); + assert_eq!(padding(10), 6); + assert_eq!(padding(11), 5); + assert_eq!(padding(12), 4); + assert_eq!(padding(13), 3); + assert_eq!(padding(14), 2); + assert_eq!(padding(15), 1); + assert_eq!(padding(16), 0); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + // The usize that padding() returns should always + // be what remains to make input a multiple of 16 in length. + fn prop_padding_result(input: usize) -> bool { + let rem = padding(input); + + // NOTE: It is possible to have an input size that will + // have padding() return a remainder that cannot be added + // to the original input size without overflowing its usize (tested on 64-bit). + // This is okay, because nothing is added to the returned value of padding() + // in generate_auth_tag. It's only ever added in this test. The remainder is still + // valid regardless, which is also "proven" by the test below. + // So to test the below assumption, we can only ever do so if it doesn't overflow, + // hence the needed check. + // + // See also below test test_inputsize_79(). + // + // (Trigger here with: size = 18446744073709551601) + // (Trigger seal_chunk/open_chunk with input of size 79 (check usage of wrapping_sub)) + + match input.checked_add(rem) { + Some(res) => res % 16 == 0, + None => true, // The case we cannot test + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + // padding() should never return a usize above 15. + // The usize must always be in range of 0..=15. + fn prop_result_never_above_15(input: usize) -> bool { + padding(input) < 16 + } + } + + #[test] + fn test_inputsize_79() { + // Generated with libsodium + let sk = [ + 49u8, 50u8, 51u8, 52u8, 53u8, 54u8, 55u8, 56u8, 57u8, 97u8, 98u8, 99u8, 100u8, 101u8, + 102u8, 103u8, 104u8, 105u8, 106u8, 107u8, 108u8, 109u8, 111u8, 110u8, 112u8, 113u8, + 114u8, 115u8, 116u8, 117u8, 118u8, 0u8, + ]; + let nonce = [ + 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, + 97u8, 98u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let input: [u8; 79] = [ + 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, + 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, + 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, + 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, + 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, + 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, 101u8, 102u8, 98u8, 101u8, + 0u8, + ]; + let output: [u8; 96] = [ + 252u8, 178u8, 0u8, 210u8, 9u8, 149u8, 109u8, 242u8, 161u8, 71u8, 231u8, 3u8, 175u8, + 17u8, 24u8, 148u8, 40u8, 118u8, 80u8, 107u8, 96u8, 105u8, 191u8, 34u8, 86u8, 101u8, + 33u8, 53u8, 116u8, 51u8, 220u8, 199u8, 26u8, 140u8, 80u8, 251u8, 81u8, 125u8, 160u8, + 186u8, 197u8, 26u8, 72u8, 217u8, 22u8, 205u8, 150u8, 103u8, 233u8, 204u8, 79u8, 59u8, + 137u8, 18u8, 93u8, 25u8, 189u8, 131u8, 137u8, 231u8, 123u8, 56u8, 186u8, 215u8, 33u8, + 41u8, 241u8, 146u8, 248u8, 44u8, 253u8, 253u8, 177u8, 115u8, 51u8, 23u8, 166u8, 64u8, + 6u8, 213u8, 174u8, 254u8, 55u8, 101u8, 185u8, 178u8, 89u8, 121u8, 175u8, 221u8, 174u8, + 75u8, 188u8, 65u8, 41u8, 75u8, + ]; + + let secret_key = SecretKey::from_slice(&sk).unwrap(); + let nonce = Nonce::from_slice(&nonce).unwrap(); + + let mut state = StreamXChaCha20Poly1305::new(&secret_key, &nonce); + let mut dst_out = [0u8; 96]; + state + .seal_chunk(&input, None, &mut dst_out, &StreamTag::Message) + .unwrap(); + assert_eq!(dst_out.as_ref(), output.as_ref()); + + state = StreamXChaCha20Poly1305::new(&secret_key, &nonce); + let mut dst_out_pt = [0u8; 79]; + state.open_chunk(&output, None, &mut dst_out_pt).unwrap(); + assert_eq!(dst_out_pt.as_ref(), input.as_ref()); + } + + // Test values were generated using libsodium. See /tests/test_generation/ + + const KEY: [u8; 32] = [ + 49u8, 50u8, 51u8, 52u8, 53u8, 54u8, 55u8, 56u8, 57u8, 97u8, 98u8, 99u8, 100u8, 101u8, + 102u8, 103u8, 104u8, 105u8, 106u8, 107u8, 108u8, 109u8, 111u8, 110u8, 112u8, 113u8, 114u8, + 115u8, 116u8, 117u8, 118u8, 0u8, + ]; + const NONCE: [u8; 24] = [ + 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, 98u8, 97u8, + 98u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + + const DEFAULT_MSG: [u8; 51] = [ + 68u8, 101u8, 102u8, 97u8, 117u8, 108u8, 116u8, 32u8, 109u8, 101u8, 115u8, 115u8, 97u8, + 103u8, 101u8, 32u8, 116u8, 111u8, 32u8, 116u8, 101u8, 115u8, 116u8, 32u8, 115u8, 116u8, + 114u8, 101u8, 97u8, 109u8, 105u8, 110u8, 103u8, 32u8, 65u8, 69u8, 65u8, 68u8, 32u8, 101u8, + 110u8, 99u8, 114u8, 121u8, 112u8, 116u8, 105u8, 111u8, 110u8, 46u8, 0u8, + ]; + + #[test] + fn test_tag() { + assert_eq!(StreamTag::Message.as_byte(), 0u8); + assert_eq!(StreamTag::Push.as_byte(), 1u8); + assert_eq!(StreamTag::Rekey.as_byte(), 2u8); + assert_eq!(StreamTag::Finish.as_byte(), 3u8); + assert!(StreamTag::try_from(4u8).is_err()); + } + + #[test] + fn test_seal_open_with_explicit_rekey() { + // Encrypt stream + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!( + s.key, + [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, + 116u8, 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, + 219u8, 33u8, 44u8, 68u8, 91u8, 135u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8,].as_ref() + ); + + // 1st StreamTag::Message + let plaintext1: [u8; 6] = [116u8, 101u8, 115u8, 116u8, 49u8, 0u8]; + let mut out1 = [0u8; 6 + ABYTES]; + s.seal_chunk(&plaintext1, None, &mut out1, &StreamTag::Message) + .unwrap(); + assert_eq!( + s.key, + [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, + 116u8, 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, + 219u8, 33u8, 44u8, 68u8, 91u8, 135u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [2u8, 0u8, 0u8, 0u8, 88u8, 186u8, 23u8, 231u8, 10u8, 253u8, 79u8, 71u8,].as_ref() + ); + assert_eq!( + out1, + [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, + 156u8, 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ] + ); + + // 2nd StreamTag::Message + let plaintext2: [u8; 20] = [ + 116u8, 104u8, 105u8, 115u8, 32u8, 105u8, 115u8, 32u8, 108u8, 111u8, 110u8, 103u8, + 101u8, 114u8, 32u8, 116u8, 101u8, 120u8, 116u8, 0u8, + ]; + let mut out2 = [0u8; 20 + ABYTES]; + s.seal_chunk(&plaintext2, None, &mut out2, &StreamTag::Message) + .unwrap(); + assert_eq!( + s.key, + [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, + 116u8, 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, + 219u8, 33u8, 44u8, 68u8, 91u8, 135u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [3u8, 0u8, 0u8, 0u8, 73u8, 199u8, 255u8, 159u8, 213u8, 205u8, 201u8, 51u8,].as_ref() + ); + assert_eq!( + out2.as_ref(), + [ + 243u8, 52u8, 124u8, 173u8, 133u8, 44u8, 99u8, 244u8, 250u8, 89u8, 101u8, 142u8, + 59u8, 49u8, 221u8, 52u8, 176u8, 214u8, 13u8, 247u8, 86u8, 17u8, 125u8, 232u8, + 120u8, 223u8, 48u8, 134u8, 116u8, 8u8, 207u8, 180u8, 241u8, 76u8, 26u8, 33u8, + 207u8, + ] + .as_ref() + ); + + // 3rd StreamTag::Message + let plaintext3: [u8; 2] = [49u8, 0u8]; + let mut out3 = [0u8; 2 + ABYTES]; + s.seal_chunk(&plaintext3, None, &mut out3, &StreamTag::Message) + .unwrap(); + assert_eq!( + s.key, + [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, + 116u8, 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, + 219u8, 33u8, 44u8, 68u8, 91u8, 135u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [4u8, 0u8, 0u8, 0u8, 229u8, 134u8, 216u8, 143u8, 117u8, 43u8, 216u8, 142u8,].as_ref() + ); + assert_eq!( + out3.as_ref(), + [ + 237u8, 198u8, 240u8, 172u8, 65u8, 39u8, 16u8, 160u8, 230u8, 17u8, 189u8, 54u8, + 93u8, 173u8, 243u8, 103u8, 185u8, 53u8, 219u8, + ] + .as_ref() + ); + + // Explicit rekey + s.rekey().unwrap(); + assert_eq!( + s.key, + [ + 55u8, 213u8, 132u8, 57u8, 116u8, 28u8, 19u8, 214u8, 59u8, 159u8, 188u8, 185u8, + 201u8, 153u8, 70u8, 17u8, 149u8, 199u8, 55u8, 34u8, 164u8, 54u8, 200u8, 241u8, + 157u8, 71u8, 218u8, 62u8, 37u8, 37u8, 8u8, 126u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [1u8, 0u8, 0u8, 0u8, 250u8, 25u8, 191u8, 166u8, 103u8, 98u8, 187u8, 196u8,].as_ref() + ); + + // 4th StreamTag::Message + let plaintext4: [u8; 23] = [ + 102u8, 105u8, 114u8, 115u8, 116u8, 32u8, 116u8, 101u8, 120u8, 116u8, 32u8, 97u8, 102u8, + 116u8, 101u8, 114u8, 32u8, 114u8, 101u8, 107u8, 101u8, 121u8, 0u8, + ]; + let mut out4 = [0u8; 23 + ABYTES]; + s.seal_chunk(&plaintext4, None, &mut out4, &StreamTag::Message) + .unwrap(); + assert_eq!( + s.key, + [ + 55u8, 213u8, 132u8, 57u8, 116u8, 28u8, 19u8, 214u8, 59u8, 159u8, 188u8, 185u8, + 201u8, 153u8, 70u8, 17u8, 149u8, 199u8, 55u8, 34u8, 164u8, 54u8, 200u8, 241u8, + 157u8, 71u8, 218u8, 62u8, 37u8, 37u8, 8u8, 126u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [2u8, 0u8, 0u8, 0u8, 70u8, 193u8, 51u8, 16u8, 173u8, 151u8, 68u8, 48u8,].as_ref() + ); + assert_eq!( + out4.as_ref(), + [ + 210u8, 9u8, 37u8, 11u8, 182u8, 190u8, 88u8, 175u8, 0u8, 12u8, 125u8, 154u8, 63u8, + 104u8, 166u8, 255u8, 231u8, 12u8, 233u8, 57u8, 206u8, 99u8, 82u8, 23u8, 188u8, + 216u8, 140u8, 182u8, 202u8, 245u8, 255u8, 244u8, 104u8, 89u8, 216u8, 168u8, 68u8, + 130u8, 12u8, 80u8, + ] + .as_ref() + ); + + // 5th StreamTag::Message + let plaintext5: [u8; 36] = [ + 116u8, 104u8, 105u8, 115u8, 32u8, 105u8, 115u8, 32u8, 116u8, 104u8, 101u8, 32u8, 115u8, + 101u8, 99u8, 111u8, 110u8, 100u8, 32u8, 116u8, 101u8, 120u8, 116u8, 32u8, 97u8, 102u8, + 116u8, 101u8, 114u8, 32u8, 114u8, 101u8, 107u8, 101u8, 121u8, 0u8, + ]; + let mut out5 = [0u8; 36 + ABYTES]; + s.seal_chunk(&plaintext5, None, &mut out5, &StreamTag::Message) + .unwrap(); + assert_eq!( + s.key, + [ + 55u8, 213u8, 132u8, 57u8, 116u8, 28u8, 19u8, 214u8, 59u8, 159u8, 188u8, 185u8, + 201u8, 153u8, 70u8, 17u8, 149u8, 199u8, 55u8, 34u8, 164u8, 54u8, 200u8, 241u8, + 157u8, 71u8, 218u8, 62u8, 37u8, 37u8, 8u8, 126u8, + ] + .as_ref() + ); + assert_eq!( + s.get_nonce(), + [3u8, 0u8, 0u8, 0u8, 119u8, 231u8, 54u8, 137u8, 64u8, 159u8, 87u8, 77u8,].as_ref() + ); + assert_eq!( + out5.as_ref(), + [ + 122u8, 17u8, 56u8, 176u8, 124u8, 172u8, 219u8, 248u8, 0u8, 37u8, 184u8, 242u8, + 65u8, 248u8, 69u8, 242u8, 158u8, 119u8, 20u8, 17u8, 225u8, 10u8, 107u8, 240u8, + 210u8, 134u8, 6u8, 182u8, 91u8, 243u8, 243u8, 20u8, 30u8, 205u8, 232u8, 167u8, + 247u8, 49u8, 38u8, 5u8, 153u8, 237u8, 8u8, 19u8, 125u8, 226u8, 190u8, 189u8, 167u8, + 33u8, 189u8, 74u8, 189u8, + ] + .as_ref() + ); + + // Decrypt stream + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + + let mut plain_out1 = [0u8; 6]; + assert_eq!( + s.open_chunk(&out1, None, &mut plain_out1).unwrap(), + StreamTag::Message + ); + assert_eq!(plain_out1.as_ref(), plaintext1.as_ref()); + + let mut plain_out2 = [0u8; 20]; + assert_eq!( + s.open_chunk(&out2, None, &mut plain_out2).unwrap(), + StreamTag::Message + ); + assert_eq!(plain_out2.as_ref(), plaintext2.as_ref()); + + let mut plain_out3 = [0u8; 2]; + assert_eq!( + s.open_chunk(&out3, None, &mut plain_out3).unwrap(), + StreamTag::Message + ); + assert_eq!(plain_out3.as_ref(), plaintext3.as_ref()); + + s.rekey().unwrap(); + + let mut plain_out4 = [0u8; 23]; + assert_eq!( + s.open_chunk(&out4, None, &mut plain_out4).unwrap(), + StreamTag::Message + ); + assert_eq!(plain_out4.as_ref(), plaintext4.as_ref()); + + let mut plain_out5 = [0u8; 36]; + assert_eq!( + s.open_chunk(&out5, None, &mut plain_out5).unwrap(), + StreamTag::Message + ); + assert_eq!(plain_out5.as_ref(), plaintext5.as_ref()); + } + + #[test] + fn test_reorder_or_drop_msg() { + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let plaintext1 = [116u8, 101u8, 115u8, 116u8, 49u8, 0u8]; + let plaintext2 = [ + 116u8, 104u8, 105u8, 115u8, 32u8, 105u8, 115u8, 32u8, 108u8, 111u8, 110u8, 103u8, + 101u8, 114u8, 32u8, 116u8, 101u8, 120u8, 116u8, 0u8, + ]; + let cipher1 = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let cipher2 = [ + 243u8, 52u8, 124u8, 173u8, 133u8, 44u8, 99u8, 244u8, 250u8, 89u8, 101u8, 142u8, 59u8, + 49u8, 221u8, 52u8, 176u8, 214u8, 13u8, 247u8, 86u8, 17u8, 125u8, 232u8, 120u8, 223u8, + 48u8, 134u8, 116u8, 8u8, 207u8, 180u8, 241u8, 76u8, 26u8, 33u8, 207u8, + ]; + + let cipher3 = [ + 237u8, 198u8, 240u8, 172u8, 65u8, 39u8, 16u8, 160u8, 230u8, 17u8, 189u8, 54u8, 93u8, + 173u8, 243u8, 103u8, 185u8, 53u8, 219u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert_eq!( + ctx.open_chunk(&cipher1, None, &mut plain_out1).unwrap(), + StreamTag::Message + ); + assert_eq!(&plain_out1, &plaintext1); + // Out of order decryption happens here + let mut plain_out3 = [0u8; 19 - ABYTES]; + assert!(ctx.open_chunk(&cipher3, None, &mut plain_out3).is_err()); + + let mut plain_out2 = [0u8; 37 - ABYTES]; + assert_eq!( + ctx.open_chunk(&cipher2, None, &mut plain_out2).unwrap(), + StreamTag::Message + ); + assert_eq!(&plain_out2, &plaintext2); + } + + #[test] + fn test_err_on_diff_tag() { + // Crate 4 different sealed chunks, sealed with the same input except for + // the StreamTag. + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut dst_out_msg = [0u8; 51 + ABYTES]; + ctx.seal_chunk( + DEFAULT_MSG.as_ref(), + None, + &mut dst_out_msg, + &StreamTag::Message, + ) + .unwrap(); + + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut dst_out_push = [0u8; 51 + ABYTES]; + ctx.seal_chunk( + DEFAULT_MSG.as_ref(), + None, + &mut dst_out_push, + &StreamTag::Push, + ) + .unwrap(); + + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut dst_out_rekey = [0u8; 51 + ABYTES]; + ctx.seal_chunk( + DEFAULT_MSG.as_ref(), + None, + &mut dst_out_rekey, + &StreamTag::Rekey, + ) + .unwrap(); + + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut dst_out_finish = [0u8; 51 + ABYTES]; + ctx.seal_chunk( + DEFAULT_MSG.as_ref(), + None, + &mut dst_out_finish, + &StreamTag::Finish, + ) + .unwrap(); + + // Swap the StreamTags, which should decrypt successfully but fail authentication as + // it does not match the Poly1305 Tag calculated. + let mut dst_out_pt = [0u8; 51]; + + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert!(ctx.open_chunk(&dst_out_msg, None, &mut dst_out_pt).is_ok()); + + dst_out_msg[0] = dst_out_push[0]; + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert!(ctx.open_chunk(&dst_out_msg, None, &mut dst_out_pt).is_err()); + + dst_out_msg[0] = dst_out_rekey[0]; + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert!(ctx.open_chunk(&dst_out_msg, None, &mut dst_out_pt).is_err()); + + dst_out_msg[0] = dst_out_finish[0]; + ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert!(ctx.open_chunk(&dst_out_msg, None, &mut dst_out_pt).is_err()); + } + + #[test] + fn test_err_on_modified_message_tag() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_ok()); + + // Reset state + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + // Change tag to Push plaintext. Originally was Message and encrypted. + cipher1[0] = StreamTag::Push.as_byte(); + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_err()); + } + + #[test] + fn test_err_on_diff_secret_key() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from(NONCE)); + let cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_err()); + } + + #[test] + fn test_err_on_diff_nonce() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from([0u8; 24])); + let cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_err()); + } + + #[test] + fn test_err_on_modified_mac() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_ok()); + + // Reset state + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + // Change MAC + let macpos = cipher1.len() - 1; + cipher1[macpos] |= 0b1010_1010; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_err()); + } + + #[test] + fn test_err_on_modified_cipher() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + let mut cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_ok()); + + // Reset state + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + // Change something in the ciphertext + cipher1[5] |= 0b1010_1010; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_err()); + } + + #[test] + fn test_err_on_diff_ad() { + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + // This ciphertext was constructed without AD + let cipher1: [u8; 23] = [ + 252u8, 164u8, 0u8, 196u8, 27u8, 198u8, 8u8, 57u8, 216u8, 118u8, 134u8, 104u8, 156u8, + 45u8, 71u8, 161u8, 199u8, 28u8, 79u8, 145u8, 19u8, 239u8, 4u8, + ]; + let mut plain_out1 = [0u8; 23 - ABYTES]; + assert!(s.open_chunk(&cipher1, None, &mut plain_out1).is_ok()); + + // Reset state + let mut s = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert!(s + .open_chunk(&cipher1, Some(&[1u8; 1]), &mut plain_out1) + .is_err()); + } + + #[test] + fn test_encrypting_same_message_different_output() { + let input = [0u8, 1u8, 2u8, 3u8]; + let mut cipher = [0u8; 4 + ABYTES]; + let mut cipher2 = [0u8; 4 + ABYTES]; + let mut state_enc = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + state_enc + .seal_chunk(&input, None, &mut cipher, &StreamTag::Message) + .unwrap(); + state_enc + .seal_chunk(&input, None, &mut cipher2, &StreamTag::Message) + .unwrap(); + assert_ne!(cipher, cipher2); + } + + #[test] + fn test_encrypting_same_message_explicit_rekey() { + let input = [0u8, 1u8, 2u8, 3u8]; + let mut cipher = [0u8; 4 + ABYTES]; + let mut cipher2 = [0u8; 4 + ABYTES]; + let mut state_enc = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + state_enc + .seal_chunk(&input, None, &mut cipher, &StreamTag::Message) + .unwrap(); + let mut state_enc = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + state_enc.rekey().unwrap(); + state_enc + .seal_chunk(&input, None, &mut cipher2, &StreamTag::Message) + .unwrap(); + assert_ne!(cipher, cipher2); + } + + #[test] + // This cannot be tested in AeadTestRunner as it assumes empty + // ciphertext to be invalid. + fn test_seal_empty_and_open() { + let mut state_enc = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + let mut cipher = [0u8; ABYTES]; + state_enc + .seal_chunk(&[0u8; 0], None, &mut cipher, &StreamTag::Message) + .unwrap(); + + let mut state_dec = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + + let mut out = [0u8; ABYTES]; + assert!(state_dec.open_chunk(&cipher, None, &mut out).is_ok()); + } + + #[test] + // This cannot be tested in AeadTestRunner as it assumes empty + // ciphertext to be invalid. This also tests that input to open_chunk + // requires the tagsize only, and not tagsize + 1. + fn test_seal_open_zero_length_both() { + let mut state_enc = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + let mut out = [0u8; ABYTES]; + state_enc + .seal_chunk(&[0u8; 0], None, &mut out, &StreamTag::Message) + .unwrap(); + let mut state_dec = + StreamXChaCha20Poly1305::new(&SecretKey::from([0u8; 32]), &Nonce::from([0u8; 24])); + let mut dst_out = [0u8; 0]; + // Only the attached tag is decrypted and authenticated. But it is + // not placed in dst_out. + assert!(state_dec.open_chunk(&out, None, &mut dst_out).is_ok()); + assert!(dst_out.is_empty()); + assert_eq!(dst_out, [0u8; 0]); + } + + #[test] + fn test_new_to_msg_with_tag_final() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let after_internal_key: [u8; 32] = [ + 168u8, 19u8, 24u8, 25u8, 196u8, 239u8, 73u8, 251u8, 36u8, 135u8, 89u8, 117u8, 2u8, + 50u8, 208u8, 173u8, 177u8, 61u8, 147u8, 201u8, 97u8, 47u8, 74u8, 149u8, 21u8, 166u8, + 227u8, 53u8, 24u8, 101u8, 251u8, 201u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 149u8, 246u8, 165u8, 228u8, 252u8, 41u8, 183u8, 89u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 255u8, 148u8, 0u8, 209u8, 14u8, 130u8, 100u8, 227u8, 231u8, 72u8, 231u8, 21u8, 186u8, + 18u8, 26u8, 148u8, 110u8, 96u8, 90u8, 46u8, 114u8, 110u8, 169u8, 51u8, 16u8, 116u8, + 48u8, 34u8, 119u8, 48u8, 212u8, 203u8, 18u8, 137u8, 21u8, 223u8, 114u8, 94u8, 129u8, + 255u8, 198u8, 22u8, 78u8, 206u8, 9u8, 223u8, 135u8, 107u8, 224u8, 192u8, 4u8, 94u8, + 100u8, 12u8, 28u8, 232u8, 44u8, 133u8, 81u8, 145u8, 176u8, 153u8, 17u8, 215u8, 180u8, + 79u8, 24u8, 79u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!(ctx.key.unprotected_as_bytes(), before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Finish) + .unwrap(); + + assert_eq!(ctx.key.unprotected_as_bytes(), after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_new_to_msg_with_tag_rekey() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let after_internal_key: [u8; 32] = [ + 39u8, 159u8, 44u8, 102u8, 206u8, 161u8, 116u8, 119u8, 163u8, 187u8, 45u8, 209u8, 172u8, + 224u8, 237u8, 93u8, 9u8, 197u8, 138u8, 242u8, 195u8, 183u8, 253u8, 169u8, 86u8, 46u8, + 161u8, 32u8, 71u8, 244u8, 51u8, 222u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 161u8, 222u8, 206u8, 42u8, 195u8, 117u8, 85u8, 88u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 254u8, 148u8, 0u8, 209u8, 14u8, 130u8, 100u8, 227u8, 231u8, 72u8, 231u8, 21u8, 186u8, + 18u8, 26u8, 148u8, 110u8, 96u8, 90u8, 46u8, 114u8, 110u8, 169u8, 51u8, 16u8, 116u8, + 48u8, 34u8, 119u8, 48u8, 212u8, 203u8, 18u8, 137u8, 21u8, 223u8, 114u8, 94u8, 129u8, + 255u8, 198u8, 22u8, 78u8, 206u8, 9u8, 223u8, 135u8, 107u8, 224u8, 192u8, 4u8, 94u8, + 187u8, 26u8, 216u8, 136u8, 169u8, 121u8, 52u8, 215u8, 102u8, 180u8, 177u8, 255u8, 69u8, + 135u8, 172u8, 22u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!(ctx.key.unprotected_as_bytes(), before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Rekey) + .unwrap(); + + assert_eq!(ctx.key.unprotected_as_bytes(), after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_new_to_msg_with_tag_final_twice() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let after_internal_key: [u8; 32] = [ + 162u8, 54u8, 231u8, 162u8, 170u8, 199u8, 53u8, 228u8, 224u8, 121u8, 138u8, 154u8, 17u8, + 252u8, 83u8, 49u8, 52u8, 25u8, 105u8, 51u8, 112u8, 3u8, 62u8, 217u8, 163u8, 194u8, + 15u8, 113u8, 155u8, 17u8, 7u8, 250u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 107u8, 7u8, 226u8, 104u8, 227u8, 227u8, 7u8, 100u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 119u8, 255u8, 30u8, 46u8, 10u8, 174u8, 158u8, 45u8, 225u8, 116u8, 158u8, 172u8, 175u8, + 198u8, 241u8, 34u8, 87u8, 52u8, 80u8, 230u8, 175u8, 133u8, 217u8, 132u8, 169u8, 75u8, + 68u8, 249u8, 86u8, 87u8, 86u8, 68u8, 33u8, 250u8, 148u8, 117u8, 43u8, 10u8, 22u8, + 125u8, 198u8, 6u8, 231u8, 5u8, 86u8, 199u8, 29u8, 214u8, 215u8, 74u8, 71u8, 244u8, + 194u8, 50u8, 98u8, 209u8, 136u8, 235u8, 79u8, 113u8, 54u8, 101u8, 105u8, 14u8, 178u8, + 146u8, 23u8, 229u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!(ctx.key.unprotected_as_bytes(), before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Finish) + .unwrap(); + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Finish) + .unwrap(); + + assert_eq!(ctx.key.unprotected_as_bytes(), after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_new_to_msg_with_tag_rekey_twice() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let after_internal_key: [u8; 32] = [ + 35u8, 207u8, 156u8, 185u8, 84u8, 18u8, 253u8, 160u8, 229u8, 242u8, 126u8, 247u8, 235u8, + 193u8, 36u8, 121u8, 42u8, 247u8, 85u8, 108u8, 107u8, 143u8, 210u8, 194u8, 109u8, 46u8, + 107u8, 47u8, 186u8, 127u8, 123u8, 46u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 198u8, 222u8, 225u8, 73u8, 93u8, 233u8, 75u8, 181u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 1u8, 171u8, 81u8, 78u8, 97u8, 128u8, 134u8, 65u8, 126u8, 237u8, 31u8, 59u8, 6u8, 76u8, + 85u8, 119u8, 69u8, 183u8, 129u8, 184u8, 101u8, 246u8, 151u8, 0u8, 92u8, 171u8, 38u8, + 164u8, 215u8, 120u8, 75u8, 169u8, 254u8, 207u8, 198u8, 138u8, 118u8, 68u8, 89u8, 231u8, + 38u8, 220u8, 26u8, 210u8, 220u8, 102u8, 8u8, 245u8, 205u8, 152u8, 39u8, 155u8, 36u8, + 115u8, 127u8, 79u8, 54u8, 246u8, 154u8, 2u8, 24u8, 208u8, 83u8, 232u8, 143u8, 234u8, + 51u8, 194u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!(ctx.key.unprotected_as_bytes(), before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Rekey) + .unwrap(); + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Rekey) + .unwrap(); + + assert_eq!(ctx.key.unprotected_as_bytes(), after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_new_to_msg_with_tag_push() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let after_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 2u8, 0u8, 0u8, 0u8, 118u8, 75u8, 245u8, 72u8, 68u8, 15u8, 117u8, 29u8, + ]; + let after_internal_counter: [u8; 4] = [2u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 253u8, 148u8, 0u8, 209u8, 14u8, 130u8, 100u8, 227u8, 231u8, 72u8, 231u8, 21u8, 186u8, + 18u8, 26u8, 148u8, 110u8, 96u8, 90u8, 46u8, 114u8, 110u8, 169u8, 51u8, 16u8, 116u8, + 48u8, 34u8, 119u8, 48u8, 212u8, 203u8, 18u8, 137u8, 21u8, 223u8, 114u8, 94u8, 129u8, + 255u8, 198u8, 22u8, 78u8, 206u8, 9u8, 223u8, 135u8, 107u8, 224u8, 192u8, 4u8, 94u8, + 23u8, 41u8, 148u8, 41u8, 38u8, 110u8, 23u8, 29u8, 29u8, 207u8, 81u8, 40u8, 215u8, + 190u8, 64u8, 222u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + assert_eq!(ctx.key, before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce().as_ref(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Push) + .unwrap(); + + assert_eq!(ctx.key, after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce().as_ref(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_counter_overflow_with_tag_msg() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 255u8, 255u8, 255u8, 255u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [255u8, 255u8, 255u8, 255u8]; + let after_internal_key: [u8; 32] = [ + 48u8, 109u8, 58u8, 2u8, 7u8, 92u8, 20u8, 239u8, 137u8, 218u8, 220u8, 62u8, 74u8, 47u8, + 118u8, 162u8, 61u8, 234u8, 35u8, 242u8, 40u8, 2u8, 243u8, 149u8, 188u8, 249u8, 180u8, + 242u8, 228u8, 139u8, 163u8, 76u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 36u8, 9u8, 22u8, 61u8, 226u8, 117u8, 46u8, 156u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 131u8, 93u8, 90u8, 220u8, 186u8, 163u8, 161u8, 113u8, 238u8, 31u8, 49u8, 63u8, 12u8, + 101u8, 64u8, 221u8, 255u8, 190u8, 206u8, 20u8, 155u8, 140u8, 72u8, 180u8, 4u8, 199u8, + 170u8, 178u8, 21u8, 212u8, 238u8, 60u8, 110u8, 233u8, 44u8, 24u8, 105u8, 216u8, 234u8, + 132u8, 103u8, 31u8, 222u8, 244u8, 214u8, 180u8, 224u8, 206u8, 148u8, 114u8, 100u8, + 161u8, 57u8, 6u8, 49u8, 199u8, 242u8, 58u8, 28u8, 253u8, 199u8, 16u8, 246u8, 86u8, + 116u8, 22u8, 66u8, 91u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + ctx.counter = u32::MAX; + assert_eq!(ctx.key, before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Message) + .unwrap(); + + assert_eq!(ctx.key, after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_counter_overflow_with_tag_rekey() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 255u8, 255u8, 255u8, 255u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [255u8, 255u8, 255u8, 255u8]; + let after_internal_key: [u8; 32] = [ + 115u8, 83u8, 132u8, 174u8, 130u8, 252u8, 214u8, 242u8, 239u8, 140u8, 231u8, 231u8, + 111u8, 228u8, 182u8, 88u8, 124u8, 109u8, 210u8, 61u8, 48u8, 22u8, 215u8, 232u8, 180u8, + 174u8, 180u8, 216u8, 174u8, 209u8, 222u8, 8u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 188u8, 188u8, 116u8, 239u8, 177u8, 113u8, 89u8, 218u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 129u8, 93u8, 90u8, 220u8, 186u8, 163u8, 161u8, 113u8, 238u8, 31u8, 49u8, 63u8, 12u8, + 101u8, 64u8, 221u8, 255u8, 190u8, 206u8, 20u8, 155u8, 140u8, 72u8, 180u8, 4u8, 199u8, + 170u8, 178u8, 21u8, 212u8, 238u8, 60u8, 110u8, 233u8, 44u8, 24u8, 105u8, 216u8, 234u8, + 132u8, 103u8, 31u8, 222u8, 244u8, 214u8, 180u8, 224u8, 206u8, 148u8, 114u8, 100u8, + 161u8, 107u8, 212u8, 93u8, 14u8, 123u8, 181u8, 233u8, 248u8, 139u8, 61u8, 100u8, 73u8, + 40u8, 14u8, 226u8, 118u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + ctx.counter = u32::MAX; + assert_eq!(ctx.key.unprotected_as_bytes(), before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Rekey) + .unwrap(); + + assert_eq!(ctx.key.unprotected_as_bytes(), after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_counter_overflow_with_tag_final() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 255u8, 255u8, 255u8, 255u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [255u8, 255u8, 255u8, 255u8]; + let after_internal_key: [u8; 32] = [ + 181u8, 99u8, 219u8, 38u8, 136u8, 29u8, 61u8, 72u8, 122u8, 0u8, 111u8, 182u8, 254u8, + 74u8, 225u8, 183u8, 250u8, 200u8, 34u8, 169u8, 252u8, 92u8, 107u8, 85u8, 144u8, 12u8, + 203u8, 19u8, 166u8, 41u8, 168u8, 26u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 230u8, 194u8, 178u8, 201u8, 13u8, 110u8, 57u8, 106u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 128u8, 93u8, 90u8, 220u8, 186u8, 163u8, 161u8, 113u8, 238u8, 31u8, 49u8, 63u8, 12u8, + 101u8, 64u8, 221u8, 255u8, 190u8, 206u8, 20u8, 155u8, 140u8, 72u8, 180u8, 4u8, 199u8, + 170u8, 178u8, 21u8, 212u8, 238u8, 60u8, 110u8, 233u8, 44u8, 24u8, 105u8, 216u8, 234u8, + 132u8, 103u8, 31u8, 222u8, 244u8, 214u8, 180u8, 224u8, 206u8, 148u8, 114u8, 100u8, + 161u8, 137u8, 59u8, 244u8, 49u8, 191u8, 114u8, 208u8, 246u8, 237u8, 83u8, 155u8, 66u8, + 2u8, 10u8, 178u8, 132u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + ctx.counter = u32::MAX; + assert_eq!(ctx.key, before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Finish) + .unwrap(); + + assert_eq!(ctx.key, after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } + + #[test] + fn test_counter_overflow_with_tag_push() { + let before_internal_key: [u8; 32] = [ + 23u8, 45u8, 143u8, 75u8, 14u8, 65u8, 110u8, 208u8, 6u8, 34u8, 38u8, 33u8, 64u8, 116u8, + 179u8, 244u8, 8u8, 121u8, 32u8, 23u8, 87u8, 135u8, 147u8, 246u8, 88u8, 52u8, 219u8, + 33u8, 44u8, 68u8, 91u8, 135u8, + ]; + let before_internal_nonce: [u8; 12] = [ + 255u8, 255u8, 255u8, 255u8, 97u8, 98u8, 97u8, 97u8, 98u8, 97u8, 98u8, 0u8, + ]; + let before_internal_counter: [u8; 4] = [255u8, 255u8, 255u8, 255u8]; + let after_internal_key: [u8; 32] = [ + 251u8, 165u8, 61u8, 114u8, 68u8, 126u8, 68u8, 202u8, 143u8, 101u8, 78u8, 242u8, 164u8, + 171u8, 209u8, 209u8, 227u8, 5u8, 181u8, 244u8, 141u8, 167u8, 137u8, 0u8, 228u8, 122u8, + 149u8, 109u8, 129u8, 240u8, 174u8, 128u8, + ]; + let after_internal_nonce: [u8; 12] = [ + 1u8, 0u8, 0u8, 0u8, 228u8, 204u8, 203u8, 245u8, 146u8, 107u8, 101u8, 124u8, + ]; + let after_internal_counter: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + let out: [u8; 68] = [ + 130u8, 93u8, 90u8, 220u8, 186u8, 163u8, 161u8, 113u8, 238u8, 31u8, 49u8, 63u8, 12u8, + 101u8, 64u8, 221u8, 255u8, 190u8, 206u8, 20u8, 155u8, 140u8, 72u8, 180u8, 4u8, 199u8, + 170u8, 178u8, 21u8, 212u8, 238u8, 60u8, 110u8, 233u8, 44u8, 24u8, 105u8, 216u8, 234u8, + 132u8, 103u8, 31u8, 222u8, 244u8, 214u8, 180u8, 224u8, 206u8, 148u8, 114u8, 100u8, + 161u8, 82u8, 109u8, 199u8, 234u8, 54u8, 248u8, 2u8, 251u8, 41u8, 39u8, 45u8, 80u8, + 78u8, 18u8, 18u8, 105u8, + ]; + + let mut ctx = StreamXChaCha20Poly1305::new(&SecretKey::from(KEY), &Nonce::from(NONCE)); + ctx.counter = u32::MAX; + assert_eq!(ctx.key, before_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), before_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(before_internal_counter)); + + let mut actual = [0u8; 68]; + ctx.seal_chunk(DEFAULT_MSG.as_ref(), None, &mut actual, &StreamTag::Push) + .unwrap(); + + assert_eq!(ctx.key, after_internal_key.as_ref()); + assert_eq!(ctx.get_nonce(), after_internal_nonce.as_ref()); + assert_eq!(ctx.counter, u32::from_le_bytes(after_internal_counter)); + assert_eq!(actual.as_ref(), out.as_ref()); + } +} diff --git a/vendor/orion/src/hazardous/aead/xchacha20poly1305.rs b/vendor/orion/src/hazardous/aead/xchacha20poly1305.rs new file mode 100644 index 0000000..1d110f7 --- /dev/null +++ b/vendor/orion/src/hazardous/aead/xchacha20poly1305.rs @@ -0,0 +1,153 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `secret_key`: The secret key. +//! - `nonce`: The nonce value. +//! - `ad`: Additional data to authenticate (this is not encrypted and can be [`None`]). +//! - `ciphertext_with_tag`: The encrypted data with the corresponding 16 byte +//! Poly1305 tag appended to it. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the +//! `ciphertext_with_tag`/`plaintext` after encryption/decryption. +//! +//! `ad`: "A typical use for these data is to authenticate version numbers, +//! timestamps or monotonically increasing counters in order to discard previous +//! messages and prevent replay attacks." See [libsodium docs] for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` + [`POLY1305_OUTSIZE`] when calling [`seal()`]. +//! - The length of `dst_out` is less than `ciphertext_with_tag` - [`POLY1305_OUTSIZE`] when +//! calling [`open()`]. +//! - The length of the `ciphertext_with_tag` is not at least [`POLY1305_OUTSIZE`]. +//! - The received tag does not match the calculated tag when calling [`open()`]. +//! - `plaintext.len()` + [`POLY1305_OUTSIZE`] overflows when calling [`seal()`]. +//! - Converting `usize` to `u64` would be a lossy conversion. +//! - `plaintext.len() >` [`chacha20poly1305::P_MAX`] +//! - `ad.len() >` [`chacha20poly1305::A_MAX`] +//! - `ciphertext_with_tag.len() >` [`chacha20poly1305::C_MAX`] +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1 * 64` bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Only a nonce for XChaCha20Poly1305 is big enough to be randomly generated +//! using a CSPRNG. [`Nonce::generate()`] can be used for this. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! - The length of the `plaintext` is not hidden, only its contents. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::aead; +//! +//! let secret_key = aead::xchacha20poly1305::SecretKey::generate(); +//! let nonce = aead::xchacha20poly1305::Nonce::generate(); +//! let ad = "Additional data".as_bytes(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of the above message is 15 and then we accommodate 16 for the Poly1305 +//! // tag. +//! +//! let mut dst_out_ct = [0u8; 15 + 16]; +//! let mut dst_out_pt = [0u8; 15]; +//! // Encrypt and place ciphertext + tag in dst_out_ct +//! aead::xchacha20poly1305::seal(&secret_key, &nonce, message, Some(&ad), &mut dst_out_ct)?; +//! // Verify tag, if correct then decrypt and place message in dst_out_pt +//! aead::xchacha20poly1305::open(&secret_key, &nonce, &dst_out_ct, Some(&ad), &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt.as_ref(), message.as_ref()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: super::stream::chacha20::SecretKey::generate +//! [`Nonce::generate()`]: super::stream::xchacha20::Nonce::generate +//! [`POLY1305_OUTSIZE`]: super::mac::poly1305::POLY1305_OUTSIZE +//! [`seal()`]: xchacha20poly1305::seal +//! [`open()`]: xchacha20poly1305::open +//! [libsodium docs]: https://download.libsodium.org/doc/secret-key_cryptography/aead#additional-data + +use crate::hazardous::stream::xchacha20::subkey_and_nonce; +pub use crate::hazardous::stream::{chacha20::SecretKey, xchacha20::Nonce}; +use crate::{errors::UnknownCryptoError, hazardous::aead::chacha20poly1305}; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// AEAD XChaCha20Poly1305 encryption as specified in the [draft RFC](https://github.com/bikeshedders/xchacha-rfc). +pub fn seal( + secret_key: &SecretKey, + nonce: &Nonce, + plaintext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + let (subkey, ietf_nonce) = subkey_and_nonce(secret_key, nonce); + chacha20poly1305::seal(&subkey, &ietf_nonce, plaintext, ad, dst_out) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// AEAD XChaCha20Poly1305 decryption as specified in the [draft RFC](https://github.com/bikeshedders/xchacha-rfc). +pub fn open( + secret_key: &SecretKey, + nonce: &Nonce, + ciphertext_with_tag: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + let (subkey, ietf_nonce) = subkey_and_nonce(secret_key, nonce); + chacha20poly1305::open(&subkey, &ietf_nonce, ciphertext_with_tag, ad, dst_out) +} + +// Testing public functions in the module. +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use super::*; + use crate::hazardous::mac::poly1305::POLY1305_OUTSIZE; + use crate::test_framework::aead_interface::{test_diff_params_err, AeadTestRunner}; + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_aead_interface(input: Vec, ad: Vec) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::generate(); + AeadTestRunner( + seal, + open, + secret_key, + nonce, + &input, + None, + POLY1305_OUTSIZE, + &ad, + ); + test_diff_params_err(&seal, &open, &input, POLY1305_OUTSIZE); + true + } +} diff --git a/vendor/orion/src/hazardous/cae/chacha20poly1305blake2b.rs b/vendor/orion/src/hazardous/cae/chacha20poly1305blake2b.rs new file mode 100644 index 0000000..19cef00 --- /dev/null +++ b/vendor/orion/src/hazardous/cae/chacha20poly1305blake2b.rs @@ -0,0 +1,262 @@ +// MIT License + +// Copyright (c) 2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # About +//! This provides a fully committing AEAD, using the CTX construction proposed by Chan and Rogaway, +//! in the ["On Committing Authenticated Encryption"] paper. Specifically, CTX is instantiated with BLAKE2b-256. +//! +//! A fully committing AEAD is important if attacks like the [partitioning oracle attack] are a part of the threat model. +//! +//! # Parameters: +//! - `secret_key`: The secret key. +//! - `nonce`: The nonce value. +//! - `ad`: Additional data to authenticate (this is not encrypted and can be [`None`]). +//! - `ciphertext_with_tag`: The encrypted data with the corresponding 32 byte +//! BLAKE2b tag appended to it. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the +//! `ciphertext_with_tag`/`plaintext` after encryption/decryption. +//! +//! `ad`: "A typical use for these data is to authenticate version numbers, +//! timestamps or monotonically increasing counters in order to discard previous +//! messages and prevent replay attacks." See [libsodium docs] for more information. +//! +//! `nonce`: "Counters and LFSRs are both acceptable ways of generating unique +//! nonces, as is encrypting a counter using a block cipher with a 64-bit block +//! size such as DES. Note that it is not acceptable to use a truncation of a +//! counter encrypted with block ciphers with 128-bit or 256-bit blocks, +//! because such a truncation may repeat after a short time." See [RFC] for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` + [`TAG_SIZE`] when calling [`seal()`]. +//! - The length of `dst_out` is less than `ciphertext_with_tag` - [`TAG_SIZE`] when +//! calling [`open()`]. +//! - The length of `ciphertext_with_tag` is not at least [`TAG_SIZE`]. +//! - The received tag does not match the calculated tag when calling [`open()`]. +//! - `plaintext.len()` + [`TAG_SIZE`] overflows when calling [`seal()`]. +//! - Converting `usize` to `u64` would be a lossy conversion. +//! - `plaintext.len() >` [`P_MAX`] +//! - `ad.len() >` [`A_MAX`] +//! - `ciphertext_with_tag.len() >` [`C_MAX`] +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1 * 64` bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Only a nonce for XChaCha20Poly1305 is big enough to be randomly generated +//! using a CSPRNG. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! - The length of the `plaintext` is not hidden, only its contents. +//! +//! # Recommendation: +//! - It is recommended to use [`XChaCha20Poly1305-BLAKE2b`] when possible. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::cae; +//! +//! let secret_key = cae::chacha20poly1305blake2b::SecretKey::generate(); +//! +//! // WARNING: This nonce is only meant for demonstration and should not +//! // be repeated. Please read the security section. +//! let nonce = cae::chacha20poly1305blake2b::Nonce::from([0u8; 12]); +//! let ad = "Additional data".as_bytes(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of the above message is 15 and then we accommodate 32 for the BLAKE2b +//! // tag. +//! +//! let mut dst_out_ct = [0u8; 15 + 32]; +//! let mut dst_out_pt = [0u8; 15]; +//! // Encrypt and place ciphertext + tag in dst_out_ct +//! cae::chacha20poly1305blake2b::seal(&secret_key, &nonce, message, Some(&ad), &mut dst_out_ct)?; +//! // Verify tag, if correct then decrypt and place message in dst_out_pt +//! cae::chacha20poly1305blake2b::open(&secret_key, &nonce, &dst_out_ct, Some(&ad), &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt.as_ref(), message.as_ref()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: super::stream::chacha20::SecretKey::generate +//! [`XChaCha20Poly1305-BLAKE2b`]: xchacha20poly1305blake2b +//! [`TAG_SIZE`]: chacha20poly1305blake2b::TAG_SIZE +//! [`seal()`]: chacha20poly1305blake2b::seal +//! [`open()`]: chacha20poly1305blake2b::open +//! [RFC]: https://tools.ietf.org/html/rfc8439#section-3 +//! [libsodium docs]: https://download.libsodium.org/doc/secret-key_cryptography/aead#additional-data +//! [`P_MAX`]: chacha20poly1305blake2b::P_MAX +//! [`A_MAX`]: chacha20poly1305blake2b::A_MAX +//! [`C_MAX`]: chacha20poly1305blake2b::C_MAX +//! ["On Committing Authenticated Encryption"]: https://eprint.iacr.org/2022/1260 +//! [partitioning oracle attack]: https://www.usenix.org/conference/usenixsecurity21/presentation/len + +use crate::errors::UnknownCryptoError; +use crate::hazardous::aead; +use crate::hazardous::aead::chacha20poly1305::{poly1305_key_gen, process_authentication, ENC_CTR}; +use crate::hazardous::hash::blake2::blake2b::Blake2b; +use crate::hazardous::mac::poly1305::Poly1305; +use crate::hazardous::mac::poly1305::POLY1305_OUTSIZE; +use crate::hazardous::stream::chacha20::{self, ChaCha20, CHACHA_BLOCKSIZE}; +use crate::util; +use zeroize::Zeroizing; + +pub use crate::hazardous::aead::chacha20poly1305::A_MAX; +pub use crate::hazardous::aead::chacha20poly1305::P_MAX; +pub use crate::hazardous::stream::chacha20::{Nonce, SecretKey}; + +/// The size of the BLAKE2b authentication tag. +pub const TAG_SIZE: usize = 32; + +/// The maximum size of the ciphertext. +pub const C_MAX: u64 = P_MAX + (TAG_SIZE as u64); + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// CTX ChaCha20Poly1305 with BLAKE2b-256. +pub fn seal( + secret_key: &SecretKey, + nonce: &Nonce, + plaintext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(plaintext.len()).map_err(|_| UnknownCryptoError)? > P_MAX { + return Err(UnknownCryptoError); + } + + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + + match plaintext.len().checked_add(TAG_SIZE) { + Some(out_min_len) => { + if dst_out.len() < out_min_len { + return Err(UnknownCryptoError); + } + } + None => return Err(UnknownCryptoError), + }; + + aead::chacha20poly1305::seal( + secret_key, + nonce, + plaintext, + Some(ad), + &mut dst_out[..plaintext.len() + POLY1305_OUTSIZE], + )?; + + let mut blake2b = Blake2b::new(32)?; + blake2b.update(secret_key.unprotected_as_bytes())?; + blake2b.update(nonce.as_ref())?; + blake2b.update(ad)?; + blake2b.update(&dst_out[plaintext.len()..plaintext.len() + POLY1305_OUTSIZE])?; + let tag = blake2b.finalize()?; + + dst_out[plaintext.len()..plaintext.len() + TAG_SIZE].copy_from_slice(tag.as_ref()); + + Ok(()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// CTX ChaCha20Poly1305 with BLAKE2b-256. +pub fn open( + secret_key: &SecretKey, + nonce: &Nonce, + ciphertext_with_tag: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(ciphertext_with_tag.len()).map_err(|_| UnknownCryptoError)? > C_MAX { + return Err(UnknownCryptoError); + } + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + if ciphertext_with_tag.len() < TAG_SIZE { + return Err(UnknownCryptoError); + } + if dst_out.len() < ciphertext_with_tag.len() - TAG_SIZE { + return Err(UnknownCryptoError); + } + + let mut blake2b = Blake2b::new(32)?; + blake2b.update(secret_key.unprotected_as_bytes())?; + blake2b.update(nonce.as_ref())?; + blake2b.update(ad)?; + + let mut dec_ctx = + ChaCha20::new(secret_key.unprotected_as_bytes(), nonce.as_ref(), true).unwrap(); + let mut tmp = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + let mut auth_ctx = Poly1305::new(&poly1305_key_gen(&mut dec_ctx, &mut tmp)); + + let ciphertext_len = ciphertext_with_tag.len() - TAG_SIZE; + process_authentication(&mut auth_ctx, ad, &ciphertext_with_tag[..ciphertext_len])?; + + blake2b.update(auth_ctx.finalize()?.unprotected_as_bytes())?; + + util::secure_cmp( + blake2b.finalize()?.as_ref(), + &ciphertext_with_tag[ciphertext_len..], + )?; + + if ciphertext_len != 0 { + dst_out[..ciphertext_len].copy_from_slice(&ciphertext_with_tag[..ciphertext_len]); + chacha20::xor_keystream( + &mut dec_ctx, + ENC_CTR, + tmp.as_mut(), + &mut dst_out[..ciphertext_len], + )?; + } + + Ok(()) +} + +// Testing public functions in the module. +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use super::*; + use crate::test_framework::aead_interface::{test_diff_params_err, AeadTestRunner}; + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_aead_interface(input: Vec, ad: Vec) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::from_slice(&[0u8; chacha20::IETF_CHACHA_NONCESIZE]).unwrap(); + AeadTestRunner(seal, open, secret_key, nonce, &input, None, TAG_SIZE, &ad); + test_diff_params_err(&seal, &open, &input, TAG_SIZE); + true + } +} diff --git a/vendor/orion/src/hazardous/cae/mod.rs b/vendor/orion/src/hazardous/cae/mod.rs new file mode 100644 index 0000000..270821b --- /dev/null +++ b/vendor/orion/src/hazardous/cae/mod.rs @@ -0,0 +1,33 @@ +// MIT License + +// Copyright (c) 2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#![cfg_attr(docsrs, doc(cfg(feature = "experimental")))] + +/// Fully-committing ChaCha20-Poly1305 with BLAKE2b based on the [CTX] construction by John Chan & Phillip Rogaway. +/// +/// [CTX]: https://eprint.iacr.org/2022/1260 +pub mod chacha20poly1305blake2b; + +/// Fully-committing XChaCha20-Poly1305 with BLAKE2b based on the [CTX] construction by John Chan & Phillip Rogaway. +/// +/// [CTX]: https://eprint.iacr.org/2022/1260 +pub mod xchacha20poly1305blake2b; diff --git a/vendor/orion/src/hazardous/cae/xchacha20poly1305blake2b.rs b/vendor/orion/src/hazardous/cae/xchacha20poly1305blake2b.rs new file mode 100644 index 0000000..25d3c32 --- /dev/null +++ b/vendor/orion/src/hazardous/cae/xchacha20poly1305blake2b.rs @@ -0,0 +1,246 @@ +// MIT License + +// Copyright (c) 2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # About +//! This provides a fully committing AEAD, using the CTX construction proposed by Chan and Rogaway, +//! in the ["On Committing Authenticated Encryption"] paper. Specifically, CTX is instantiated with BLAKE2b-256. +//! +//! A fully committing AEAD is important if attacks like the [partitioning oracle attack] are a part of the threat model. +//! +//! # Parameters: +//! - `secret_key`: The secret key. +//! - `nonce`: The nonce value. +//! - `ad`: Additional data to authenticate (this is not encrypted and can be [`None`]). +//! - `ciphertext_with_tag`: The encrypted data with the corresponding 32 byte +//! BLAKE2b tag appended to it. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the +//! `ciphertext_with_tag`/`plaintext` after encryption/decryption. +//! +//! `ad`: "A typical use for these data is to authenticate version numbers, +//! timestamps or monotonically increasing counters in order to discard previous +//! messages and prevent replay attacks." See [libsodium docs] for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` + [`TAG_SIZE`] when calling [`seal()`]. +//! - The length of `dst_out` is less than `ciphertext_with_tag` - [`TAG_SIZE`] when +//! calling [`open()`]. +//! - The length of `ciphertext_with_tag` is not at least [`TAG_SIZE`]. +//! - The received tag does not match the calculated tag when calling [`open()`]. +//! - `plaintext.len()` + [`TAG_SIZE`] overflows when calling [`seal()`]. +//! - Converting `usize` to `u64` would be a lossy conversion. +//! - `plaintext.len() >` [`P_MAX`] +//! - `ad.len() >` [`A_MAX`] +//! - `ciphertext_with_tag.len() >` [`C_MAX`] +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1 * 64` bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Only a nonce for XChaCha20Poly1305 is big enough to be randomly generated +//! using a CSPRNG. [`Nonce::generate()`] can be used for this. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! - The length of the `plaintext` is not hidden, only its contents. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::cae; +//! +//! let secret_key = cae::xchacha20poly1305blake2b::SecretKey::generate(); +//! let nonce = cae::xchacha20poly1305blake2b::Nonce::generate(); +//! let ad = "Additional data".as_bytes(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of the above message is 15 and then we accommodate 32 for the BLAKE2b +//! // tag. +//! +//! let mut dst_out_ct = [0u8; 15 + 32]; +//! let mut dst_out_pt = [0u8; 15]; +//! // Encrypt and place ciphertext + tag in dst_out_ct +//! cae::xchacha20poly1305blake2b::seal(&secret_key, &nonce, message, Some(&ad), &mut dst_out_ct)?; +//! // Verify tag, if correct then decrypt and place message in dst_out_pt +//! cae::xchacha20poly1305blake2b::open(&secret_key, &nonce, &dst_out_ct, Some(&ad), &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt.as_ref(), message.as_ref()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: super::stream::chacha20::SecretKey::generate +//! [`Nonce::generate()`]: super::stream::xchacha20::Nonce::generate +//! [`TAG_SIZE`]: xchacha20poly1305blake2b::TAG_SIZE +//! [`seal()`]: xchacha20poly1305blake2b::seal +//! [`open()`]: xchacha20poly1305blake2b::open +//! [RFC]: https://tools.ietf.org/html/rfc8439#section-3 +//! [libsodium docs]: https://download.libsodium.org/doc/secret-key_cryptography/aead#additional-data +//! [`P_MAX`]: xchacha20poly1305blake2b::P_MAX +//! [`A_MAX`]: xchacha20poly1305blake2b::A_MAX +//! [`C_MAX`]: xchacha20poly1305blake2b::C_MAX +//! ["On Committing Authenticated Encryption"]: https://eprint.iacr.org/2022/1260 +//! [partitioning oracle attack]: https://www.usenix.org/conference/usenixsecurity21/presentation/len + +use crate::errors::UnknownCryptoError; +use crate::hazardous::aead; +pub use crate::hazardous::aead::chacha20poly1305::A_MAX; +pub use crate::hazardous::aead::chacha20poly1305::P_MAX; +use crate::hazardous::aead::chacha20poly1305::{poly1305_key_gen, process_authentication, ENC_CTR}; +pub use crate::hazardous::cae::chacha20poly1305blake2b::{C_MAX, TAG_SIZE}; +use crate::hazardous::hash::blake2::blake2b::Blake2b; +use crate::hazardous::mac::poly1305::{Poly1305, POLY1305_OUTSIZE}; +use crate::hazardous::stream::chacha20::{self, ChaCha20, CHACHA_BLOCKSIZE}; +use crate::hazardous::stream::xchacha20::subkey_and_nonce; +pub use crate::hazardous::stream::{chacha20::SecretKey, xchacha20::Nonce}; +use crate::util; +use zeroize::Zeroizing; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// CTX XChaCha20Poly1305 with BLAKE2b-256. +pub fn seal( + secret_key: &SecretKey, + nonce: &Nonce, + plaintext: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(plaintext.len()).map_err(|_| UnknownCryptoError)? > P_MAX { + return Err(UnknownCryptoError); + } + + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + + match plaintext.len().checked_add(TAG_SIZE) { + Some(out_min_len) => { + if dst_out.len() < out_min_len { + return Err(UnknownCryptoError); + } + } + None => return Err(UnknownCryptoError), + }; + + let (subkey, ietf_nonce) = subkey_and_nonce(secret_key, nonce); + aead::chacha20poly1305::seal( + &subkey, + &ietf_nonce, + plaintext, + Some(ad), + &mut dst_out[..plaintext.len() + POLY1305_OUTSIZE], + )?; + + let mut blake2b = Blake2b::new(32)?; + blake2b.update(secret_key.unprotected_as_bytes())?; + blake2b.update(nonce.as_ref())?; + blake2b.update(ad)?; + blake2b.update(&dst_out[plaintext.len()..plaintext.len() + POLY1305_OUTSIZE])?; + let tag = blake2b.finalize()?; + + dst_out[plaintext.len()..plaintext.len() + TAG_SIZE].copy_from_slice(tag.as_ref()); + + Ok(()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// CTX XChaCha20Poly1305 with BLAKE2b-256. +pub fn open( + secret_key: &SecretKey, + nonce: &Nonce, + ciphertext_with_tag: &[u8], + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if u64::try_from(ciphertext_with_tag.len()).map_err(|_| UnknownCryptoError)? > C_MAX { + return Err(UnknownCryptoError); + } + let ad = ad.unwrap_or(&[0u8; 0]); + #[allow(clippy::absurd_extreme_comparisons)] + if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX { + return Err(UnknownCryptoError); + } + if ciphertext_with_tag.len() < TAG_SIZE { + return Err(UnknownCryptoError); + } + if dst_out.len() < ciphertext_with_tag.len() - TAG_SIZE { + return Err(UnknownCryptoError); + } + + let mut blake2b = Blake2b::new(32)?; + blake2b.update(secret_key.unprotected_as_bytes())?; + blake2b.update(nonce.as_ref())?; + blake2b.update(ad)?; + + let (subkey, ietf_nonce) = subkey_and_nonce(secret_key, nonce); + let mut dec_ctx = + ChaCha20::new(subkey.unprotected_as_bytes(), ietf_nonce.as_ref(), true).unwrap(); + let mut tmp = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + let mut auth_ctx = Poly1305::new(&poly1305_key_gen(&mut dec_ctx, &mut tmp)); + + let ciphertext_len = ciphertext_with_tag.len() - TAG_SIZE; + process_authentication(&mut auth_ctx, ad, &ciphertext_with_tag[..ciphertext_len])?; + + blake2b.update(auth_ctx.finalize()?.unprotected_as_bytes())?; + + util::secure_cmp( + blake2b.finalize()?.as_ref(), + &ciphertext_with_tag[ciphertext_len..], + )?; + + if ciphertext_len != 0 { + dst_out[..ciphertext_len].copy_from_slice(&ciphertext_with_tag[..ciphertext_len]); + chacha20::xor_keystream( + &mut dec_ctx, + ENC_CTR, + tmp.as_mut(), + &mut dst_out[..ciphertext_len], + )?; + } + + Ok(()) +} + +// Testing public functions in the module. +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use super::*; + use crate::test_framework::aead_interface::{test_diff_params_err, AeadTestRunner}; + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_aead_interface(input: Vec, ad: Vec) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::generate(); + AeadTestRunner(seal, open, secret_key, nonce, &input, None, TAG_SIZE, &ad); + test_diff_params_err(&seal, &open, &input, TAG_SIZE); + true + } +} diff --git a/vendor/orion/src/hazardous/ecc/mod.rs b/vendor/orion/src/hazardous/ecc/mod.rs new file mode 100644 index 0000000..a539b67 --- /dev/null +++ b/vendor/orion/src/hazardous/ecc/mod.rs @@ -0,0 +1,24 @@ +// MIT License + +// Copyright (c) 2021-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// Diffie-Hellman key exchange over Curve25519 as specified in the [RFC 7748](https://datatracker.ietf.org/doc/html/rfc7748). +pub mod x25519; diff --git a/vendor/orion/src/hazardous/ecc/x25519.rs b/vendor/orion/src/hazardous/ecc/x25519.rs new file mode 100644 index 0000000..806777f --- /dev/null +++ b/vendor/orion/src/hazardous/ecc/x25519.rs @@ -0,0 +1,821 @@ +// MIT License + +// Copyright (c) 2021-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `private_key`: The private key used in key agreement. +//! - `public_key`: The public key used in key agreement. +//! +//! # Errors: +//! An error will be returned if: +//! - The `key_agreement()` operation results in an all-zero output. +//! +//! # Security: +//! - Multiple different `private_key`/`public_key` pairs can produce the same shared key. Therefore, +//! using the resulting `SharedKey`, directly from `key_agreement()`, is not recommended. This is handled +//! automatically in [`orion::kex`]. +//! - To securely generate a strong key, use [`PrivateKey::generate()`]. +//! +//! # Recommendation: +//! - It is recommended to use [`orion::kex`] when possible. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::ecc::x25519::{PrivateKey, PublicKey, SharedKey, key_agreement}; +//! use core::convert::TryFrom; +//! +//! // Alice generates a private key and computes the corresponding public key +//! let alice_sk = PrivateKey::generate(); +//! let alice_pk = PublicKey::try_from(&alice_sk)?; +//! +//! // Bob does the same +//! let bob_sk = PrivateKey::generate(); +//! let bob_pk = PublicKey::try_from(&bob_sk)?; +//! +//! // They both compute a shared key using the others public key +//! let alice_shared = key_agreement(&alice_sk, &bob_pk)?; +//! let bob_shared = key_agreement(&bob_sk, &alice_pk)?; +//! +//! assert_eq!(alice_shared, bob_shared); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`PrivateKey::generate()`]: crate::hazardous::ecc::x25519::PrivateKey::generate +//! [`orion::kex`]: crate::kex + +use crate::errors::UnknownCryptoError; +use crate::util::secure_cmp; +use core::ops::{Add, Mul, Sub}; + +/// Formally verified Curve25519 field arithmetic from: . +use fiat_crypto::curve25519_64 as fiat_curve25519_u64; +use fiat_curve25519_u64::{ + fiat_25519_add, fiat_25519_carry, fiat_25519_carry_mul, fiat_25519_carry_scmul_121666, + fiat_25519_carry_square, fiat_25519_loose_field_element, fiat_25519_relax, fiat_25519_sub, + fiat_25519_tight_field_element, +}; + +/// The size of a public key used in X25519. +pub const PUBLIC_KEY_SIZE: usize = 32; + +/// The size of a private key used in X25519. +pub const PRIVATE_KEY_SIZE: usize = 32; + +/// The size of a shared key used in X25519. +pub const SHARED_KEY_SIZE: usize = 32; + +/// u-coordinate of the base point. +const BASEPOINT: [u8; 32] = [ + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +/// The result of computing a shared secret with a low order point. +const LOW_ORDER_POINT_RESULT: [u8; 32] = [0u8; 32]; + +#[derive(Clone, Copy)] +/// Represent an element in the curve field. +struct FieldElement(fiat_25519_tight_field_element); + +impl Eq for FieldElement {} + +impl PartialEq for FieldElement { + fn eq(&self, other: &Self) -> bool { + use subtle::ConstantTimeEq; + self.as_bytes().ct_eq(&other.as_bytes()).into() + } +} + +impl core::fmt::Debug for FieldElement { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "FieldElement({:?})", &self.0 .0) + } +} + +/// The function fiat_25519_carry_mul multiplies two field elements and reduces the result. +impl Mul for FieldElement { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]); + let mut rhs_relaxed = fiat_25519_loose_field_element([0u64; 5]); + + fiat_25519_relax(&mut self_relaxed, &self.0); + fiat_25519_relax(&mut rhs_relaxed, &rhs.0); + + fiat_25519_carry_mul(&mut ret, &self_relaxed, &rhs_relaxed); + + Self(ret) + } +} + +/// The function fiat_25519_add adds two field elements. +impl Add for FieldElement { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + let mut ret_add = fiat_25519_loose_field_element([0u64; 5]); + + fiat_25519_add(&mut ret_add, &self.0, &rhs.0); + fiat_25519_carry(&mut ret, &ret_add); + + Self(ret) + } +} + +/// The function fiat_25519_sub subtracts two field elements. +impl Sub for FieldElement { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + let mut ret_sub = fiat_25519_loose_field_element([0u64; 5]); + + fiat_25519_sub(&mut ret_sub, &self.0, &rhs.0); + fiat_25519_carry(&mut ret, &ret_sub); + + Self(ret) + } +} + +impl FieldElement { + /// Create a `FieldElement` that is `0`. + fn zero() -> Self { + Self(fiat_25519_tight_field_element([ + 0u64, 0u64, 0u64, 0u64, 0u64, + ])) + } + + /// Create a `FieldElement` that is `1`. + fn one() -> Self { + Self(fiat_25519_tight_field_element([ + 1u64, 0u64, 0u64, 0u64, 0u64, + ])) + } + + /// Serialize the `FieldElement` as a byte-array. + fn as_bytes(&self) -> [u8; 32] { + // The function fiat_25519_to_bytes serializes a field element to bytes in little-endian order. + use fiat_curve25519_u64::fiat_25519_to_bytes; + + let mut ret = [0u8; 32]; + fiat_25519_to_bytes(&mut ret, &self.0); + + ret + } + + /// Deserialize the `FieldElement` from a byte-array in little-endian. + /// + /// Masks the MSB in the final byte of the input bytes. + fn from_bytes(bytes: &[u8; 32]) -> Self { + // The function fiat_25519_from_bytes deserializes a field element from bytes in little-endian order + use fiat_curve25519_u64::fiat_25519_from_bytes; + + let mut temp = [0u8; 32]; + temp.copy_from_slice(bytes); + temp[31] &= 127u8; // See RFC: "When receiving such an array, implementations of X25519 + // (but not X448) MUST mask the most significant bit in the final byte." + + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + fiat_25519_from_bytes(&mut ret, &temp); + + Self(ret) + } + + /// A conditional-swap operation. + fn conditional_swap(swap: u8, a: &mut Self, b: &mut Self) { + // The function fiat_25519_selectznz is a multi-limb conditional select. + use fiat_curve25519_u64::fiat_25519_selectznz; + + // SAFETY: This is a part of fiat input bounds. + debug_assert!(swap == 1 || swap == 0); + + let tmp_a = *a; + let tmp_b = *b; + + fiat_25519_selectznz(&mut a.0 .0, swap, &tmp_a.0 .0, &tmp_b.0 .0); + fiat_25519_selectznz(&mut b.0 .0, swap, &tmp_b.0 .0, &tmp_a.0 .0); + } + + /// Square the `FieldElement` and reduce the result. + fn square(&self) -> Self { + let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]); + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + + fiat_25519_relax(&mut self_relaxed, &self.0); + fiat_25519_carry_square(&mut ret, &self_relaxed); + + Self(ret) + } + + /// Multiply the `FieldElement` by 121666 and reduce the result. + fn mul_121666(&self) -> Self { + let mut self_relaxed = fiat_25519_loose_field_element([0u64; 5]); + let mut ret = fiat_25519_tight_field_element([0u64; 5]); + + fiat_25519_relax(&mut self_relaxed, &self.0); + fiat_25519_carry_scmul_121666(&mut ret, &self_relaxed); + + Self(ret) + } + + /// Compute the multiplicative inverse of the `FieldElement`. + /// + /// Ref: https://github.com/golang/crypto/blob/0c34fe9e7dc2486962ef9867e3edb3503537209f/curve25519/curve25519_generic.go#L718 + fn invert(&mut self) { + let mut t0 = self.square(); + let mut t1 = t0.square(); + t1 = t1.square(); + + t1 = *self * t1; + t0 = t0 * t1; + let mut t2 = t0.square(); + + t1 = t1 * t2; + t2 = t1.square(); + for _ in 1..5 { + t2 = t2.square(); + } + t1 = t2 * t1; + t2 = t1.square(); + for _ in 1..10 { + t2 = t2.square(); + } + t2 = t2 * t1; + let mut t3 = t2.square(); + for _ in 1..20 { + t3 = t3.square(); + } + t2 = t3 * t2; + t2 = t2.square(); + for _ in 1..10 { + t2 = t2.square(); + } + t1 = t2 * t1; + t2 = t1.square(); + for _ in 1..50 { + t2 = t2.square(); + } + + t2 = t2 * t1; + t3 = t2.square(); + for _ in 1..100 { + t3 = t3.square(); + } + t2 = t3 * t2; + t2 = t2.square(); + for _ in 1..50 { + t2 = t2.square(); + } + t1 = t2 * t1; + t1 = t1.square(); + for _ in 1..5 { + t1 = t1.square(); + } + + *self = t1 * t0; + } +} + +#[derive(Clone)] +/// Represents a Scalar decoded from a byte array. +struct Scalar([u8; PRIVATE_KEY_SIZE]); + +impl Drop for Scalar { + fn drop(&mut self) { + use zeroize::Zeroize; + self.0.iter_mut().zeroize(); + } +} + +impl PartialEq for Scalar { + fn eq(&self, other: &Self) -> bool { + use subtle::ConstantTimeEq; + self.0.ct_eq(&other.0).into() + } +} + +impl Eq for Scalar {} + +impl Scalar { + /// Create a scalar from some byte-array. + /// The scalar is clamped according to the RFC. + /// + /// Ref: https://www.ietf.org/rfc/rfc7748.html#section-5 + fn from_slice(slice: &[u8]) -> Result { + if slice.len() != PRIVATE_KEY_SIZE { + return Err(UnknownCryptoError); + } + + let mut ret = [0u8; PRIVATE_KEY_SIZE]; + ret.copy_from_slice(slice); + // Clamp + ret[0] &= 248; + ret[31] &= 127; + ret[31] |= 64; + + Ok(Self(ret)) + } +} + +/// Scalar multiplication using the Montgomery Ladder (a.k.a "scalarmult") +/// +/// Refs: +/// - https://eprint.iacr.org/2020/956.pdf +/// - https://eprint.iacr.org/2017/212.pdf +/// - https://github.com/golang/crypto/blob/0c34fe9e7dc2486962ef9867e3edb3503537209f/curve25519/curve25519_generic.go#L779 +fn mont_ladder(scalar: &Scalar, point: FieldElement) -> FieldElement { + let x1 = point; + let mut x2 = FieldElement::one(); + let mut x3 = x1; + let mut z3 = FieldElement::one(); + let mut z2 = FieldElement::zero(); + let mut tmp0: FieldElement; + let mut tmp1: FieldElement; + + let mut swap: u8 = 0; + + for idx in (0..=254).rev() { + let bit = (scalar.0[idx >> 3] >> (idx & 7)) & 1; + swap ^= bit; + FieldElement::conditional_swap(swap, &mut x2, &mut x3); + FieldElement::conditional_swap(swap, &mut z2, &mut z3); + swap = bit; + + tmp0 = x3 - z3; + tmp1 = x2 - z2; + x2 = x2 + z2; + z2 = x3 + z3; + z3 = tmp0 * x2; + z2 = z2 * tmp1; + tmp0 = tmp1.square(); + tmp1 = x2.square(); + x3 = z3 + z2; + z2 = z3 - z2; + x2 = tmp1 * tmp0; + tmp1 = tmp1 - tmp0; + z2 = z2.square(); + z3 = tmp1.mul_121666(); + x3 = x3.square(); + tmp0 = tmp0 + z3; + z3 = x1 * z2; + z2 = tmp1 * tmp0; + } + + FieldElement::conditional_swap(swap, &mut x2, &mut x3); + FieldElement::conditional_swap(swap, &mut z2, &mut z3); + + z2.invert(); + x2 = x2 * z2; + + x2 +} + +#[allow(clippy::derive_partial_eq_without_eq)] +// NOTE: FieldElement contains a constant-time PartialEq impl. +/// A type that represents a `PublicKey` that X25519 uses. +/// +/// This type holds a field element and is used internally as the u-coordinate. +/// As the RFC mandates, the most significant bit of the last byte is masked. +/// +/// # Errors: +/// An error will be returned if: +/// - `slice` is not 32 bytes. +#[derive(PartialEq, Debug, Clone)] +pub struct PublicKey { + fe: FieldElement, +} + +impl PartialEq<&[u8]> for PublicKey { + fn eq(&self, other: &&[u8]) -> bool { + if other.len() != PUBLIC_KEY_SIZE { + return false; + } + let other: [u8; 32] = (*other).try_into().unwrap(); + + self.fe == FieldElement::from_bytes(&other) + } +} + +impl From<[u8; PUBLIC_KEY_SIZE]> for PublicKey { + #[inline] + fn from(bytes: [u8; PUBLIC_KEY_SIZE]) -> Self { + Self { + fe: FieldElement::from_bytes(&bytes), + } + } +} + +impl_try_from_trait!(PublicKey); +#[cfg(feature = "serde")] +impl_serde_traits!(PublicKey, to_bytes); + +impl TryFrom<&PrivateKey> for PublicKey { + type Error = UnknownCryptoError; + + fn try_from(private_key: &PrivateKey) -> Result { + // NOTE: This implementation should be identical to key_agreement() except + // for the check of a resulting low order point result. + let scalar = Scalar::from_slice(private_key.unprotected_as_bytes())?; + + Ok(PublicKey::from( + mont_ladder(&scalar, FieldElement::from_bytes(&BASEPOINT)).as_bytes(), + )) + } +} + +impl PublicKey { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result { + let slice_len = slice.len(); + + if slice_len != PUBLIC_KEY_SIZE { + return Err(UnknownCryptoError); + } + + Ok(Self { + fe: FieldElement::from_bytes(slice.try_into().unwrap()), + }) + } + + #[inline] + /// Return the length of the object. + pub fn len(&self) -> usize { + PUBLIC_KEY_SIZE + } + + #[inline] + /// Return `true` if this object does not hold any data, `false` otherwise. + /// + /// __NOTE__: This method should always return `false`, since there shouldn't be a way + /// to create an empty instance of this object. + pub fn is_empty(&self) -> bool { + PUBLIC_KEY_SIZE == 0 + } + + #[inline] + /// Convert this PublicKey to its byte-representation. + pub fn to_bytes(&self) -> [u8; 32] { + self.fe.as_bytes() + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +// NOTE: Scalar contains a constant-time PartialEq impl. +// NOTE: All newtypes impl Drop by default and Scalar has zeroizing Drop +/// A type to represent the `PrivateKey` that X25519 uses. +/// +/// This type holds a scalar and is used internally as such. The scalar held is decoded +/// (a.k.a "clamped") as mandated in the [RFC](https://datatracker.ietf.org/doc/html/rfc7748#section-5). +/// +/// # Errors: +/// An error will be returned if: +/// - `slice` is not 32 bytes. +/// +/// # Panics: +/// A panic will occur if: +/// - Failure to generate random bytes securely. +/// +/// +/// # Security: +/// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections +/// that the type implements. +/// +/// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted +/// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait +/// is implemented in such a way that the comparison happens in constant time. Thus, users should +/// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. +/// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. +/// ```rust +/// # #[cfg(feature = "safe_api")] { +/// use orion::hazardous::ecc::x25519::PrivateKey; +/// +/// // Initialize a secret key with random bytes. +/// let secret_key = PrivateKey::generate(); +/// +/// // Secure, constant-time comparison with a byte slice +/// assert_ne!(secret_key, &[0; 32][..]); +/// +/// // Secure, constant-time comparison with another SecretKey +/// assert_ne!(secret_key, PrivateKey::generate()); +/// # } +/// # Ok::<(), orion::errors::UnknownCryptoError>(()) +/// ``` +#[derive(PartialEq)] +pub struct PrivateKey { + scalar: Scalar, +} + +impl PartialEq<&[u8]> for PrivateKey { + fn eq(&self, other: &&[u8]) -> bool { + match Scalar::from_slice(other) { + Ok(other_scalar) => self.scalar == other_scalar, + Err(_) => false, + } + } +} + +impl core::fmt::Debug for PrivateKey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} {{***OMITTED***}}", stringify!(PrivateKey)) + } +} + +impl From<[u8; PRIVATE_KEY_SIZE]> for PrivateKey { + #[inline] + fn from(bytes: [u8; PRIVATE_KEY_SIZE]) -> Self { + PrivateKey { + // unwrap OK due to valid len + scalar: Scalar::from_slice(bytes.as_ref()).unwrap(), + } + } +} + +impl PrivateKey { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result { + Ok(Self { + scalar: Scalar::from_slice(slice)?, + }) + } + + #[inline] + /// Return the length of the object. + pub fn len(&self) -> usize { + PRIVATE_KEY_SIZE + } + + #[inline] + /// Return `true` if this object does not hold any data, `false` otherwise. + /// + /// __NOTE__: This method should always return `false`, since there shouldn't be a way + /// to create an empty instance of this object. + pub fn is_empty(&self) -> bool { + PRIVATE_KEY_SIZE == 0 + } + + #[inline] + /// Return the object as byte slice. __**Warning**__: Should not be used unless strictly + /// needed. This __**breaks protections**__ that the type implements. + pub fn unprotected_as_bytes(&self) -> &[u8] { + self.scalar.0.as_ref() + } + + #[cfg(feature = "safe_api")] + /// Randomly generate using a CSPRNG. Not available in `no_std` context. + pub fn generate() -> PrivateKey { + let mut value = [0u8; PRIVATE_KEY_SIZE]; + crate::util::secure_rand_bytes(&mut value).unwrap(); + + Self { + // unwrap OK due to valid len + scalar: Scalar::from_slice(&value).unwrap(), + } + } +} + +construct_secret_key! { + /// A type to represent the `SharedKey` that X25519 produces. + /// + /// This type simply holds bytes. Creating an instance from slices or similar, + /// performs no checks whatsoever. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + (SharedKey, test_shared_key, SHARED_KEY_SIZE, SHARED_KEY_SIZE) +} + +impl_from_trait!(SharedKey, SHARED_KEY_SIZE); + +/// X25519 (Diffie-Hellman with Montgomery form of Curve25519). +pub fn key_agreement( + private_key: &PrivateKey, + public_key: &PublicKey, +) -> Result { + let u_coord = public_key.fe; + let field_element = mont_ladder(&private_key.scalar, u_coord).as_bytes(); + // High bit should be zero. + debug_assert_eq!(field_element[31] & 0b1000_0000u8, 0u8); + if secure_cmp(&field_element, &LOW_ORDER_POINT_RESULT).is_ok() { + return Err(UnknownCryptoError); + } + + Ok(SharedKey::from(field_element)) +} + +#[cfg(test)] +mod public { + use crate::hazardous::ecc::x25519::{ + key_agreement, PrivateKey, PublicKey, SharedKey, BASEPOINT, + }; + + #[test] + fn test_public_key_ignores_highbit() { + let u = [0u8; 32]; + + let mut msb_zero = u; + msb_zero[31] &= 127u8; + let mut msb_one = u; + msb_one[31] |= 128u8; + + // These should equal each-other. The high bits differ, but should be ignored. + assert_eq!(PublicKey::from(msb_zero), msb_one.as_ref()); + assert_eq!(PublicKey::from(msb_zero), PublicKey::from(msb_one)); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_highbit_ignored() { + // RFC 7748 dictates that the MSB of final byte must be masked when receiving a field element, + // used for agreement (public key). We check that modifying it does not impact the result of + // the agreement. + let k = PrivateKey::generate(); + let mut u = [0u8; 32]; + crate::util::secure_rand_bytes(&mut u).unwrap(); + debug_assert_ne!(u[31] & 127u8, (u[31] & 127u8) | 128u8); + + let mut u_msb_zero = u; + u_msb_zero[31] &= 127u8; + let mut u_msb_one = u; + u_msb_one[31] |= 128u8; + + // Mask bit to 0 as we do in `FieldElement::from_bytes()`. + let msb_zero = key_agreement(&k, &PublicKey::from(u_msb_zero)).unwrap(); + let msb_one = key_agreement(&k, &PublicKey::from(u_msb_one)).unwrap(); + + assert_eq!(msb_zero, msb_one); + } + + #[test] + /// Ref: https://www.ietf.org/rfc/rfc7748.html#section-5.2 + fn test_rfc_section_5() { + let mut scalar = [0u8; 32]; + let mut point = [0u8; 32]; + let mut expected = SharedKey::from([0u8; 32]); + + hex::decode_to_slice( + "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4", + &mut scalar, + ) + .unwrap(); + hex::decode_to_slice( + "e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c", + &mut point, + ) + .unwrap(); + hex::decode_to_slice( + "c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552", + &mut expected.value, + ) + .unwrap(); + + let actual = key_agreement(&PrivateKey::from(scalar), &PublicKey::from(point)).unwrap(); + assert_eq!(actual, expected); + + hex::decode_to_slice( + "4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d", + &mut scalar, + ) + .unwrap(); + hex::decode_to_slice( + "e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493", + &mut point, + ) + .unwrap(); + hex::decode_to_slice( + "95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957", + &mut expected.value, + ) + .unwrap(); + + let actual = key_agreement(&PrivateKey::from(scalar), &PublicKey::from(point)).unwrap(); + assert_eq!(actual, expected); + } + + #[test] + /// Ref: https://www.ietf.org/rfc/rfc7748.html#section-5.2 + fn test_rfc_section_5_iter() { + let mut k = BASEPOINT; + let mut u = BASEPOINT; + + // 1 iter + let ret = key_agreement(&PrivateKey::from(k), &PublicKey::from(u)).unwrap(); + u = k; + k = ret.value; + + let mut expected = SharedKey::from([0u8; 32]); + hex::decode_to_slice( + "422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079", + &mut expected.value, + ) + .unwrap(); + assert_eq!(k, expected.value, "Failed after 1 iter"); + + for _ in 0..999 { + let ret = key_agreement(&PrivateKey::from(k), &PublicKey::from(u)).unwrap(); + u = k; + k = ret.value; + } + + hex::decode_to_slice( + "684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51", + &mut expected.value, + ) + .unwrap(); + assert_eq!(k, expected.value, "Failed after 1.000 iter"); + + /* Taking a decade... + for num in 0..999000 { + let ret = key_agreement(&PrivateKey::from(k), &PublicKey::from(u)).unwrap(); + u = k; + k = ret.value; + } + + hex::decode_to_slice( + "7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424", + &mut expected.value, + ) + .unwrap(); + assert_eq!(k, expected.value, "Failed after 1.000.000 iter"); + */ + } + + #[test] + /// Ref: https://www.ietf.org/rfc/rfc7748.html#section-6.1 + fn test_rfc_section_6_pub_priv_basepoint() { + let mut alice_pub = [0u8; 32]; + let mut alice_priv = [0u8; 32]; + + let mut bob_pub = [0u8; 32]; + let mut bob_priv = [0u8; 32]; + + let mut shared = SharedKey::from([0u8; 32]); + + hex::decode_to_slice( + "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", + &mut alice_priv, + ) + .unwrap(); + hex::decode_to_slice( + "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a", + &mut alice_pub, + ) + .unwrap(); + assert_eq!( + key_agreement(&PrivateKey::from(alice_priv), &PublicKey::from(BASEPOINT)).unwrap(), + PublicKey::from(alice_pub).to_bytes().as_ref() + ); + + hex::decode_to_slice( + "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", + &mut bob_priv, + ) + .unwrap(); + hex::decode_to_slice( + "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f", + &mut bob_pub, + ) + .unwrap(); + assert_eq!( + key_agreement(&PrivateKey::from(bob_priv), &PublicKey::from(BASEPOINT)).unwrap(), + PublicKey::from(bob_pub).to_bytes().as_ref() + ); + + hex::decode_to_slice( + "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742", + &mut shared.value, + ) + .unwrap(); + assert_eq!( + key_agreement(&PrivateKey::from(alice_priv), &PublicKey::from(bob_pub)).unwrap(), + shared.value.as_ref() + ); + assert_eq!( + key_agreement(&PrivateKey::from(bob_priv), &PublicKey::from(alice_pub)).unwrap(), + shared.value.as_ref() + ); + } +} diff --git a/vendor/orion/src/hazardous/hash/blake2/blake2b.rs b/vendor/orion/src/hazardous/hash/blake2/blake2b.rs new file mode 100644 index 0000000..877f2bb --- /dev/null +++ b/vendor/orion/src/hazardous/hash/blake2/blake2b.rs @@ -0,0 +1,414 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `size`: The desired output length for the digest. +//! - `data`: The data to be hashed. +//! - `expected`: The expected digest when verifying. +//! +//! # Errors: +//! An error will be returned if: +//! - `size` is 0 or greater than 64. +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) bytes of data are hashed. +//! +//! # Security: +//! - The recommended minimum output size is 32. +//! - This interface only allows creating hash digest using BLAKE2b. If using a secret key is desired, +//! please refer to the [`mac::blake2b`] module. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::blake2::blake2b::{Blake2b, Hasher}; +//! +//! // Using the streaming interface. +//! let mut state = Blake2b::new(64)?; +//! state.update(b"Some data")?; +//! let hash = state.finalize()?; +//! +//! // Using the `Hasher` for convenience functions. +//! let hash_one_shot = Hasher::Blake2b512.digest(b"Some data")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: blake2b::Blake2b::update +//! [`reset()`]: blake2b::Blake2b::reset +//! [`finalize()`]: blake2b::Blake2b::finalize +//! [`mac::blake2b`]: crate::hazardous::mac::blake2b + +use crate::errors::UnknownCryptoError; +use crate::hazardous::hash::blake2::blake2b_core; +use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE; + +#[cfg(feature = "safe_api")] +use std::io; + +construct_public! { + /// A type to represent the `Digest` that BLAKE2b returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `slice` is greater than 64 bytes. + (Digest, test_digest, 1, BLAKE2B_OUTSIZE) +} + +#[derive(Debug, Clone)] +/// BLAKE2b streaming state. +pub struct Blake2b { + _state: blake2b_core::State, +} + +impl Blake2b { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize a `Blake2b` struct with a given size (in bytes). + pub fn new(size: usize) -> Result { + Ok(Self { + _state: blake2b_core::State::_new(&[], size)?, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Reset to `new()` state. + pub fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self._state._reset(&[]) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a BLAKE2b digest. + pub fn finalize(&mut self) -> Result { + let mut tmp = [0u8; BLAKE2B_OUTSIZE]; + self._state._finalize(&mut tmp)?; + + Digest::from_slice(&tmp[..self._state.size]) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, PartialEq)] +/// Convenience functions for common BLAKE2b operations. +pub enum Hasher { + /// Blake2b with `32` as `size`. + Blake2b256, + /// Blake2b with `48` as `size`. + Blake2b384, + /// Blake2b with `64` as `size`. + Blake2b512, +} + +impl Hasher { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a digest selected by the given Blake2b variant. + pub fn digest(&self, data: &[u8]) -> Result { + let size: usize = match *self { + Hasher::Blake2b256 => 32, + Hasher::Blake2b384 => 48, + Hasher::Blake2b512 => 64, + }; + + let mut state = Blake2b::new(size)?; + state.update(data)?; + state.finalize() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a `Blake2b` state selected by the given Blake2b variant. + pub fn init(&self) -> Result { + match *self { + Hasher::Blake2b256 => Blake2b::new(32), + Hasher::Blake2b384 => Blake2b::new(48), + Hasher::Blake2b512 => Blake2b::new(64), + } + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: custom digest size. +/// ```rust +/// use orion::{ +/// hazardous::hash::blake2::blake2b::{Blake2b, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Blake2b::new(64)?; // 512-bit hash +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Blake2b { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Blake2b::update`](crate::hazardous::hash::blake2::blake2b::Blake2b::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +#[cfg(test)] +mod public { + mod test_streaming_interface_no_key { + use crate::errors::UnknownCryptoError; + use crate::hazardous::hash::blake2::blake2b::{Blake2b, Digest}; + use crate::hazardous::hash::blake2::blake2b_core::{ + compare_blake2b_states, BLAKE2B_BLOCKSIZE, BLAKE2B_OUTSIZE, + }; + use crate::test_framework::incremental_interface::{ + StreamingContextConsistencyTester, TestableStreamingContext, + }; + + impl TestableStreamingContext for Blake2b { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset() + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + let mut ctx = Blake2b::new(BLAKE2B_OUTSIZE)?; + ctx.update(input)?; + ctx.finalize() + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Blake2b, state_2: &Blake2b) { + compare_blake2b_states(&state_1._state, &state_2._state) + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Blake2b = Blake2b::new(BLAKE2B_OUTSIZE).unwrap(); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + BLAKE2B_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec) -> bool { + let initial_state: Blake2b = Blake2b::new(BLAKE2B_OUTSIZE).unwrap(); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + BLAKE2B_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + mod test_hasher { + use crate::hazardous::hash::blake2::blake2b::Hasher; + + #[test] + fn test_hasher_interface_no_panic_and_same_result() { + let digest_256 = Hasher::Blake2b256.digest(b"Test").unwrap(); + let digest_384 = Hasher::Blake2b384.digest(b"Test").unwrap(); + let digest_512 = Hasher::Blake2b512.digest(b"Test").unwrap(); + + assert_eq!(digest_256, Hasher::Blake2b256.digest(b"Test").unwrap()); + assert_eq!(digest_384, Hasher::Blake2b384.digest(b"Test").unwrap()); + assert_eq!(digest_512, Hasher::Blake2b512.digest(b"Test").unwrap()); + + assert_ne!(digest_256, Hasher::Blake2b256.digest(b"Wrong").unwrap()); + assert_ne!(digest_384, Hasher::Blake2b384.digest(b"Wrong").unwrap()); + assert_ne!(digest_512, Hasher::Blake2b512.digest(b"Wrong").unwrap()); + + let _state_256 = Hasher::Blake2b256.init().unwrap(); + let _state_384 = Hasher::Blake2b384.init().unwrap(); + let _state_512 = Hasher::Blake2b512.init().unwrap(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Given some data, digest() should never fail in practice and should + /// produce the same output on a second call. + /// Only panics if data is unreasonably large. + fn prop_hasher_digest_no_panic_and_same_result(data: Vec) -> bool { + let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap(); + let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap(); + let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + let d256_re = Hasher::Blake2b256.digest(&data[..]).unwrap(); + let d384_re = Hasher::Blake2b384.digest(&data[..]).unwrap(); + let d512_re = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + (d256 == d256_re) && (d384 == d384_re) && (d512 == d512_re) + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Given some data, .digest() should produce the same output as when + /// calling with streaming state. + fn prop_hasher_digest_256_same_as_streaming(data: Vec) -> bool { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + + let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap(); + + let mut state = Blake2b::new(32).unwrap(); + state.update(&data[..]).unwrap(); + + d256 == state.finalize().unwrap() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Given some data, .digest() should produce the same output as when + /// calling with streaming state. + fn prop_hasher_digest_384_same_as_streaming(data: Vec) -> bool { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + + let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap(); + + let mut state = Blake2b::new(48).unwrap(); + state.update(&data[..]).unwrap(); + + d384 == state.finalize().unwrap() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Given some data, .digest() should produce the same output as when + /// calling with streaming state. + fn prop_hasher_digest_512_same_as_streaming(data: Vec) -> bool { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + + let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + let mut state = Blake2b::new(64).unwrap(); + state.update(&data[..]).unwrap(); + + d512 == state.finalize().unwrap() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Given two different data, .digest() should never produce the + /// same output. + fn prop_hasher_digest_diff_input_diff_result(data: Vec) -> bool { + let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap(); + let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap(); + let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + let d256_re = Hasher::Blake2b256.digest(b"Wrong data").unwrap(); + let d384_re = Hasher::Blake2b384.digest(b"Wrong data").unwrap(); + let d512_re = Hasher::Blake2b512.digest(b"Wrong data").unwrap(); + + (d256 != d256_re) && (d384 != d384_re) && (d512 != d512_re) + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// .init() should never fail. + fn prop_hasher_init_no_panic() -> bool { + let _d256 = Hasher::Blake2b256.init().unwrap(); + let _d384 = Hasher::Blake2b384.init().unwrap(); + let _d512 = Hasher::Blake2b512.init().unwrap(); + + true + } + } + + mod test_new { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + + #[test] + fn test_init_size() { + assert!(Blake2b::new(0).is_err()); + assert!(Blake2b::new(65).is_err()); + assert!(Blake2b::new(1).is_ok()); + assert!(Blake2b::new(64).is_ok()); + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec) -> bool { + let mut hasher_a = Blake2b::new(64).unwrap(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} diff --git a/vendor/orion/src/hazardous/hash/blake2/mod.rs b/vendor/orion/src/hazardous/hash/blake2/mod.rs new file mode 100644 index 0000000..c64ab5b --- /dev/null +++ b/vendor/orion/src/hazardous/hash/blake2/mod.rs @@ -0,0 +1,442 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// BLAKE2b as specified in the [RFC 7693](https://tools.ietf.org/html/rfc7693). +pub mod blake2b; + +pub(crate) mod blake2b_core { + + /// The blocksize for the hash function BLAKE2b. + pub(crate) const BLAKE2B_BLOCKSIZE: usize = 128; + /// The maximum key size for the hash function BLAKE2b when used in keyed mode. + pub(crate) const BLAKE2B_KEYSIZE: usize = 64; + /// The maximum output size for the hash function BLAKE2b. + pub(crate) const BLAKE2B_OUTSIZE: usize = 64; + + use crate::errors::UnknownCryptoError; + use crate::util::endianness::load_u64_into_le; + use crate::util::u64x4::U64x4; + + #[allow(clippy::unreadable_literal)] + /// The BLAKE2b initialization vector as defined in the RFC 7693. + pub(crate) const IV: [U64x4; 2] = [ + U64x4( + 0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + ), + U64x4( + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179, + ), + ]; + + /// BLAKE2b SIGMA as defined in the RFC 7693. + const SIGMA: [[usize; 16]; 12] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + ]; + + /// Quarter round on the BLAKE2b internal matrix. + macro_rules! QROUND { + ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $s_idx:expr, $rconst1:expr, $rconst2:expr) => { + $v0 = $v0.wrapping_add($v1).wrapping_add($s_idx); + $v3 = ($v3 ^ $v0).rotate_right($rconst1); + $v2 = $v2.wrapping_add($v3); + $v1 = ($v1 ^ $v2).rotate_right($rconst2); + }; + } + + /// Perform a single round based on a message schedule selection. + macro_rules! ROUND { + ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $s_idx:expr, $m:expr) => { + let s_indexed = U64x4($m[$s_idx[0]], $m[$s_idx[2]], $m[$s_idx[4]], $m[$s_idx[6]]); + QROUND!($v0, $v1, $v2, $v3, s_indexed, 32, 24); + let s_indexed = U64x4($m[$s_idx[1]], $m[$s_idx[3]], $m[$s_idx[5]], $m[$s_idx[7]]); + QROUND!($v0, $v1, $v2, $v3, s_indexed, 16, 63); + + // Shuffle + $v1 = $v1.shl_1(); + $v2 = $v2.shl_2(); + $v3 = $v3.shl_3(); + + let s_indexed = U64x4( + $m[$s_idx[8]], + $m[$s_idx[10]], + $m[$s_idx[12]], + $m[$s_idx[14]], + ); + QROUND!($v0, $v1, $v2, $v3, s_indexed, 32, 24); + let s_indexed = U64x4( + $m[$s_idx[9]], + $m[$s_idx[11]], + $m[$s_idx[13]], + $m[$s_idx[15]], + ); + QROUND!($v0, $v1, $v2, $v3, s_indexed, 16, 63); + + // Unshuffle + $v1 = $v1.shl_3(); + $v2 = $v2.shl_2(); + $v3 = $v3.shl_1(); + }; + } + + #[derive(Clone)] + /// BLAKE2b streaming state. + pub(crate) struct State { + pub(crate) init_state: [U64x4; 2], + pub(crate) internal_state: [U64x4; 2], + pub(crate) buffer: [u8; BLAKE2B_BLOCKSIZE], + pub(crate) leftover: usize, + pub(crate) t: [u64; 2], + pub(crate) f: [u64; 2], + pub(crate) is_finalized: bool, + pub(crate) is_keyed: bool, + pub(crate) size: usize, + } + + impl Drop for State { + fn drop(&mut self) { + use zeroize::Zeroize; + self.init_state.iter_mut().zeroize(); + self.internal_state.iter_mut().zeroize(); + self.buffer.zeroize(); + } + } + + impl core::fmt::Debug for State { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "State {{ init_state: [***OMITTED***], internal_state: [***OMITTED***], buffer: \ + [***OMITTED***], leftover: {:?}, t: {:?}, f: {:?}, is_finalized: {:?}, is_keyed: \ + {:?}, size: {:?} }}", + self.leftover, self.t, self.f, self.is_finalized, self.is_keyed, self.size + ) + } + } + + impl State { + /// Increment the internal states offset value `t`. + pub(crate) fn _increment_offset(&mut self, value: u64) { + let (res, was_overflow) = self.t[0].overflowing_add(value); + self.t[0] = res; + if was_overflow { + // If this panics size limit is reached. + self.t[1] = self.t[1].checked_add(1).unwrap(); + } + } + + /// The compression function f. + pub(crate) fn _compress_f(&mut self, data: Option<&[u8]>) { + let mut m_vec = [0u64; 16]; + match data { + Some(bytes) => { + debug_assert!(bytes.len() == BLAKE2B_BLOCKSIZE); + load_u64_into_le(bytes, &mut m_vec); + } + None => load_u64_into_le(&self.buffer, &mut m_vec), + } + + let mut v0 = self.internal_state[0]; + let mut v1 = self.internal_state[1]; + let mut v2 = IV[0]; + let mut v3 = U64x4( + self.t[0] ^ IV[1].0, + self.t[1] ^ IV[1].1, + self.f[0] ^ IV[1].2, + self.f[1] ^ IV[1].3, + ); + + ROUND!(v0, v1, v2, v3, SIGMA[0], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[1], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[2], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[3], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[4], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[5], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[6], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[7], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[8], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[9], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[10], m_vec); + ROUND!(v0, v1, v2, v3, SIGMA[11], m_vec); + + self.internal_state[0] ^= v0 ^ v2; + self.internal_state[1] ^= v1 ^ v3; + } + + #[allow(clippy::unreadable_literal)] + /// Initialize a `State` struct with a given size a key and optional key. + /// An empty `secret_key` equals non-MAC mode. + pub(crate) fn _new(sk: &[u8], size: usize) -> Result { + if !(1..=BLAKE2B_OUTSIZE).contains(&size) { + return Err(UnknownCryptoError); + } + let is_keyed = match sk.len() { + 0 => false, + 1..=BLAKE2B_KEYSIZE => true, + _ => return Err(UnknownCryptoError), + }; + + let mut context = Self { + init_state: [U64x4::default(); 2], + internal_state: IV, + buffer: [0u8; BLAKE2B_BLOCKSIZE], + leftover: 0, + t: [0u64; 2], + f: [0u64; 2], + is_finalized: false, + is_keyed, + size, + }; + + if is_keyed { + context.is_keyed = true; + let klen = sk.len(); + context.internal_state[0].0 ^= 0x01010000 ^ ((klen as u64) << 8) ^ (size as u64); + context.init_state.copy_from_slice(&context.internal_state); + context._update(sk)?; + // The state needs updating with the secret key padded to blocksize length + let pad = [0u8; BLAKE2B_BLOCKSIZE]; + let rem = BLAKE2B_BLOCKSIZE - klen; + context._update(pad[..rem].as_ref())?; + } else { + context.internal_state[0].0 ^= 0x01010000 ^ (size as u64); + context.init_state.copy_from_slice(&context.internal_state); + } + + Ok(context) + } + + /// Reset to `_new()` state. + pub(crate) fn _reset(&mut self, sk: &[u8]) -> Result<(), UnknownCryptoError> { + // Disallow re-setting without a key if initialized with one and vice versa + match (sk.len(), self.is_keyed) { + // new with key, reset with none + (0, true) => return Err(UnknownCryptoError), + (0, false) => (), + // reset with key, new with none + (1..=BLAKE2B_KEYSIZE, false) => return Err(UnknownCryptoError), + (1..=BLAKE2B_KEYSIZE, true) => (), + (_, _) => return Err(UnknownCryptoError), + } + + self.internal_state.copy_from_slice(&self.init_state); + self.buffer = [0u8; BLAKE2B_BLOCKSIZE]; + self.leftover = 0; + self.t = [0u64; 2]; + self.f = [0u64; 2]; + self.is_finalized = false; + + if self.is_keyed { + self._update(sk)?; + // The state needs updating with the secret key padded to blocksize length + let pad = [0u8; BLAKE2B_BLOCKSIZE]; + let rem = BLAKE2B_BLOCKSIZE - sk.len(); + self._update(pad[..rem].as_ref())?; + } + + Ok(()) + } + + /// Update state with `data`. This can be called multiple times. + pub(crate) fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + if self.is_finalized { + return Err(UnknownCryptoError); + } + if data.is_empty() { + return Ok(()); + } + + let mut bytes = data; + + if self.leftover != 0 { + debug_assert!(self.leftover <= BLAKE2B_BLOCKSIZE); + + let fill = BLAKE2B_BLOCKSIZE - self.leftover; + + if bytes.len() <= fill { + self.buffer[self.leftover..(self.leftover + bytes.len())] + .copy_from_slice(bytes); + self.leftover += bytes.len(); + return Ok(()); + } + + self.buffer[self.leftover..(self.leftover + fill)].copy_from_slice(&bytes[..fill]); + self._increment_offset(BLAKE2B_BLOCKSIZE as u64); + self._compress_f(None); + self.leftover = 0; + bytes = &bytes[fill..]; + } + + while bytes.len() > BLAKE2B_BLOCKSIZE { + self._increment_offset(BLAKE2B_BLOCKSIZE as u64); + self._compress_f(Some(bytes[..BLAKE2B_BLOCKSIZE].as_ref())); + bytes = &bytes[BLAKE2B_BLOCKSIZE..]; + } + + if !bytes.is_empty() { + debug_assert!(self.leftover == 0); + self.buffer[..bytes.len()].copy_from_slice(bytes); + self.leftover += bytes.len(); + } + + Ok(()) + } + + /// Finalize the hash and put the final digest into `dest`. + /// NOTE: Writes the full hash (as if `self.size == BLAKE2B_OUTSIZE`) into `dest`. Must be truncated + /// to `self.size` later. + pub(crate) fn _finalize( + &mut self, + dest: &mut [u8; BLAKE2B_OUTSIZE], + ) -> Result<(), UnknownCryptoError> { + debug_assert!(self.size <= BLAKE2B_OUTSIZE); + if self.is_finalized { + return Err(UnknownCryptoError); + } + + self.is_finalized = true; + + let in_buffer_len = self.leftover; + self._increment_offset(in_buffer_len as u64); + // Mark that it is the last block of data to be processed + self.f[0] = !0; + + for leftover_block in self.buffer.iter_mut().skip(in_buffer_len) { + *leftover_block = 0; + } + self._compress_f(None); + + self.internal_state[0].store_into_le(dest[..32].as_mut()); + self.internal_state[1].store_into_le(dest[32..].as_mut()); + + Ok(()) + } + } + + #[cfg(test)] + pub(crate) fn compare_blake2b_states(state_1: &State, state_2: &State) { + assert!(state_1.init_state == state_2.init_state); + assert!(state_1.internal_state == state_2.internal_state); + assert_eq!(state_1.buffer[..], state_2.buffer[..]); + assert_eq!(state_1.leftover, state_2.leftover); + assert_eq!(state_1.t, state_2.t); + assert_eq!(state_1.f, state_2.f); + assert_eq!(state_1.is_finalized, state_2.is_finalized); + assert_eq!(state_1.is_keyed, state_2.is_keyed); + assert_eq!(state_1.size, state_2.size); + } +} + +#[cfg(test)] +mod private { + use super::blake2b_core::State; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = State::_new(&[], 64).unwrap(); + let debug = format!("{:?}", initial_state); + let expected = "State { init_state: [***OMITTED***], internal_state: [***OMITTED***], buffer: [***OMITTED***], leftover: 0, t: [0, 0], f: [0, 0], is_finalized: false, is_keyed: false, size: 64 }"; + assert_eq!(debug, expected); + } + + #[test] + fn test_switching_keyed_modes_fails() { + let mut tmp = [0u8; 64]; + + let mut state_keyed = State::_new(&[0u8; 64], 64).unwrap(); + state_keyed._update(b"Tests").unwrap(); + state_keyed._finalize(&mut tmp).unwrap(); + assert!(state_keyed._reset(&[]).is_err()); + assert!(state_keyed._reset(&[0u8; 64]).is_ok()); + + let mut state = State::_new(&[], 64).unwrap(); + state._update(b"Tests").unwrap(); + state_keyed._finalize(&mut tmp).unwrap(); + assert!(state._reset(&[0u8; 64]).is_err()); + assert!(state._reset(&[]).is_ok()); + } + + mod test_increment_offset { + use crate::hazardous::hash::blake2::blake2b_core::{State, BLAKE2B_BLOCKSIZE, IV}; + use crate::util::u64x4::U64x4; + + #[test] + fn test_offset_increase_values() { + let mut context = State { + init_state: [U64x4::default(); 2], + internal_state: IV, + buffer: [0u8; BLAKE2B_BLOCKSIZE], + leftover: 0, + t: [0u64; 2], + f: [0u64; 2], + is_finalized: false, + is_keyed: false, + size: 1, + }; + + context._increment_offset(1); + assert_eq!(context.t, [1u64, 0u64]); + context._increment_offset(17); + assert_eq!(context.t, [18u64, 0u64]); + context._increment_offset(12); + assert_eq!(context.t, [30u64, 0u64]); + // Overflow + context._increment_offset(u64::MAX); + assert_eq!(context.t, [29u64, 1u64]); + } + + #[test] + #[should_panic] + fn test_panic_on_second_overflow() { + let mut context = State { + init_state: [U64x4::default(); 2], + internal_state: IV, + buffer: [0u8; BLAKE2B_BLOCKSIZE], + leftover: 0, + t: [1u64, u64::MAX], + f: [0u64; 2], + is_finalized: false, + is_keyed: false, + size: 1, + }; + + context._increment_offset(u64::MAX); + } + } +} diff --git a/vendor/orion/src/hazardous/hash/mod.rs b/vendor/orion/src/hazardous/hash/mod.rs new file mode 100644 index 0000000..01b04c2 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/mod.rs @@ -0,0 +1,30 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// SHA2 as specified in the [FIPS PUB 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf). +pub mod sha2; + +/// SHA3 as specified in the [FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf). +pub mod sha3; + +/// BLAKE2 hash functions. +pub mod blake2; diff --git a/vendor/orion/src/hazardous/hash/sha2/mod.rs b/vendor/orion/src/hazardous/hash/sha2/mod.rs new file mode 100644 index 0000000..cbdbb9a --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha2/mod.rs @@ -0,0 +1,1062 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// SHA256 as specified in the [FIPS PUB 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf). +pub mod sha256; + +/// SHA384 as specified in the [FIPS PUB 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf). +pub mod sha384; + +/// SHA512 as specified in the [FIPS PUB 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf). +pub mod sha512; + +pub(crate) mod sha2_core { + use crate::errors::UnknownCryptoError; + use core::fmt::Debug; + use core::marker::PhantomData; + use core::ops::*; + use zeroize::Zeroize; + + /// Word used within the SHA2 internal state. + pub(crate) trait Word: + Sized + + BitOr + + BitAnd + + BitXor + + Shr + + Default + + Div + + From + + Copy + + Debug + + PartialEq + + Zeroize + { + const MAX: Self; + + fn wrapping_add(&self, rhs: Self) -> Self; + + fn overflowing_add(&self, rhs: Self) -> (Self, bool); + + fn checked_add(&self, rhs: Self) -> Option; + + fn checked_shl(&self, rhs: u32) -> Option; + + fn rotate_right(&self, rhs: u32) -> Self; + + fn one() -> Self; + + fn size_of() -> usize; + + fn as_be(&self, dest: &mut [u8]); + + fn from_be(src: &[u8]) -> Self; + + #[allow(clippy::wrong_self_convention)] + fn as_be_bytes(src: &[Self], dest: &mut [u8]); + + fn from_be_bytes(src: &[u8], dest: &mut [Self]); + + #[cfg(any(debug_assertions, test))] + fn less_than_or_equal(&self, rhs: Self) -> bool; + } + + /// Trait to define a specific SHA2 variant. + pub(crate) trait Variant: Clone { + /// The constants as defined in FIPS 180-4. + const K: [W; N_CONSTS]; + + /// The initial hash value H(0) as defined in FIPS 180-4. + const H0: [W; 8]; + + // Because it's currently not possible to use type parameters + // in const expressions (see #![feature(const_evaluatable_checked)]), + // we can't have this trait define the blocksize or output size + // of the hash function, since array-sizes are defined by these. + // This can be accomplished once full const-generics support lands, + // and should remove the need for the const parameters in the state struct. + + fn big_sigma_0(x: W) -> W; + + fn big_sigma_1(x: W) -> W; + + fn small_sigma_0(x: W) -> W; + + fn small_sigma_1(x: W) -> W; + } + + /// The Ch function as specified in FIPS 180-4 section 4.1.3. + fn ch(x: W, y: W, z: W) -> W { + z ^ (x & (y ^ z)) + } + + /// The Maj function as specified in FIPS 180-4 section 4.1.3. + fn maj(x: W, y: W, z: W) -> W { + (x & y) | (z & (x | y)) + } + + #[derive(Clone)] + /// Core SHA2 state. + pub(crate) struct State< + W, + T, + const BLOCKSIZE: usize, + const OUTSIZE: usize, + const N_CONSTS: usize, + > + where + W: Word, + T: Variant, + { + _variant: PhantomData, + pub(crate) working_state: [W; 8], + pub(crate) buffer: [u8; BLOCKSIZE], + pub(crate) leftover: usize, + pub(crate) message_len: [W; 2], + pub(crate) is_finalized: bool, + } + + impl< + W: Word, + T: Variant, + const BLOCKSIZE: usize, + const OUTSIZE: usize, + const N_CONSTS: usize, + > Drop for State + { + fn drop(&mut self) { + self.working_state.iter_mut().zeroize(); + self.buffer.iter_mut().zeroize(); + self.message_len.iter_mut().zeroize(); + self.leftover.zeroize(); + self.is_finalized.zeroize(); + } + } + + impl< + W: Word, + T: Variant, + const BLOCKSIZE: usize, + const OUTSIZE: usize, + const N_CONSTS: usize, + > Debug for State + { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "State {{ working_state: [***OMITTED***], buffer: [***OMITTED***], leftover: {:?}, \ + message_len: {:?}, is_finalized: {:?} }}", + self.leftover, self.message_len, self.is_finalized + ) + } + } + + impl< + W: Word, + T: Variant, + const BLOCKSIZE: usize, + const OUTSIZE: usize, + const N_CONSTS: usize, + > State + { + /// Increment the message length during processing of data. + pub(crate) fn increment_mlen(&mut self, length: &W) { + // The checked shift checks that the right-hand side is a legal shift. + // The result can still overflow if length > $primitive::MAX / 8. + // Should be impossible for a user to trigger, because update() processes + // in SHA(256/384/512)_BLOCKSIZE chunks. + #[cfg(any(debug_assertions, test))] + debug_assert!(length.less_than_or_equal(W::MAX / W::from(8))); + + // left-shift to get bit-sized representation of length + // using .unwrap() because it should not panic in practice + let len = length.checked_shl(3).unwrap(); + let (res, was_overflow) = self.message_len[1].overflowing_add(len); + self.message_len[1] = res; + + if was_overflow { + // If this panics size limit is reached. + self.message_len[0] = self.message_len[0].checked_add(W::one()).unwrap(); + } + } + + #[allow(clippy::many_single_char_names)] + #[allow(clippy::too_many_arguments)] + /// Message compression adopted from [mbed + /// TLS](https://github.com/ARMmbed/mbedtls/blob/master/library/sha512.c). + pub(crate) fn compress( + a: W, + b: W, + c: W, + d: &mut W, + e: W, + f: W, + g: W, + h: &mut W, + x: W, + ki: W, + ) { + let temp1 = h + .wrapping_add(T::big_sigma_1(e)) + .wrapping_add(ch(e, f, g)) + .wrapping_add(ki) + .wrapping_add(x); + + let temp2 = T::big_sigma_0(a).wrapping_add(maj(a, b, c)); + + *d = d.wrapping_add(temp1); + *h = temp1.wrapping_add(temp2); + } + + #[rustfmt::skip] + #[allow(clippy::many_single_char_names)] + /// Process data in `self.buffer` or optionally `data`. + pub(crate) fn process(&mut self, data: Option<&[u8]>) { + let mut w = [W::default(); N_CONSTS]; + // If `data.is_none()` then we want to process leftover data within `self.buffer`. + match data { + Some(bytes) => { + debug_assert_eq!(bytes.len(), BLOCKSIZE); + W::from_be_bytes(bytes, &mut w[..16]); + } + None => W::from_be_bytes(&self.buffer, &mut w[..16]), + } + + for t in 16..T::K.len() { + w[t] = T::small_sigma_1(w[t - 2]) + .wrapping_add(w[t - 7]) + .wrapping_add(T::small_sigma_0(w[t - 15])) + .wrapping_add(w[t - 16]); + } + + let mut a = self.working_state[0]; + let mut b = self.working_state[1]; + let mut c = self.working_state[2]; + let mut d = self.working_state[3]; + let mut e = self.working_state[4]; + let mut f = self.working_state[5]; + let mut g = self.working_state[6]; + let mut h = self.working_state[7]; + + let mut t = 0; + while t < T::K.len() { + Self::compress(a, b, c, &mut d, e, f, g, &mut h, w[t], T::K[t]); t += 1; + Self::compress(h, a, b, &mut c, d, e, f, &mut g, w[t], T::K[t]); t += 1; + Self::compress(g, h, a, &mut b, c, d, e, &mut f, w[t], T::K[t]); t += 1; + Self::compress(f, g, h, &mut a, b, c, d, &mut e, w[t], T::K[t]); t += 1; + Self::compress(e, f, g, &mut h, a, b, c, &mut d, w[t], T::K[t]); t += 1; + Self::compress(d, e, f, &mut g, h, a, b, &mut c, w[t], T::K[t]); t += 1; + Self::compress(c, d, e, &mut f, g, h, a, &mut b, w[t], T::K[t]); t += 1; + Self::compress(b, c, d, &mut e, f, g, h, &mut a, w[t], T::K[t]); t += 1; + } + + self.working_state[0] = self.working_state[0].wrapping_add(a); + self.working_state[1] = self.working_state[1].wrapping_add(b); + self.working_state[2] = self.working_state[2].wrapping_add(c); + self.working_state[3] = self.working_state[3].wrapping_add(d); + self.working_state[4] = self.working_state[4].wrapping_add(e); + self.working_state[5] = self.working_state[5].wrapping_add(f); + self.working_state[6] = self.working_state[6].wrapping_add(g); + self.working_state[7] = self.working_state[7].wrapping_add(h); + } + + /// Initialize a new state. + pub(crate) fn _new() -> Self { + Self { + _variant: PhantomData::, + working_state: T::H0, + buffer: [0u8; BLOCKSIZE], + leftover: 0, + message_len: [W::default(); 2], + is_finalized: false, + } + } + + /// Reset to `new()` state. + pub(crate) fn _reset(&mut self) { + self.working_state = T::H0; + self.buffer = [0u8; BLOCKSIZE]; + self.leftover = 0; + self.message_len = [W::default(); 2]; + self.is_finalized = false; + } + + /// Update state with `data`. This can be called multiple times. + pub(crate) fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + if self.is_finalized { + return Err(UnknownCryptoError); + } + if data.is_empty() { + return Ok(()); + } + + let mut bytes = data; + + if self.leftover != 0 { + debug_assert!(self.leftover <= BLOCKSIZE); + + let mut want = BLOCKSIZE - self.leftover; + if want > bytes.len() { + want = bytes.len(); + } + + for (idx, itm) in bytes.iter().enumerate().take(want) { + self.buffer[self.leftover + idx] = *itm; + } + + bytes = &bytes[want..]; + self.leftover += want; + self.increment_mlen(&W::from(want)); + + if self.leftover < BLOCKSIZE { + return Ok(()); + } + + self.process(None); + self.leftover = 0; + } + + while bytes.len() >= BLOCKSIZE { + self.process(Some(bytes[..BLOCKSIZE].as_ref())); + self.increment_mlen(&W::from(BLOCKSIZE)); + bytes = &bytes[BLOCKSIZE..]; + } + + if !bytes.is_empty() { + debug_assert_eq!(self.leftover, 0); + self.buffer[..bytes.len()].copy_from_slice(bytes); + self.leftover = bytes.len(); + self.increment_mlen(&W::from(bytes.len())); + } + + Ok(()) + } + + /// Finalize the hash and put the final digest into `dest`. + pub(crate) fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + // NOTE: We need to support less than OUTSIZE in HKDF through HMAC. + // debug_assert_eq!(dest.len(), OUTSIZE); + if self.is_finalized { + return Err(UnknownCryptoError); + } + + self.is_finalized = true; + // self.leftover should not be greater than SHA(256/384/512)_BLOCKSIZE + // as that would have been processed in the update call + debug_assert!(self.leftover < BLOCKSIZE); + self.buffer[self.leftover] = 0x80; + self.leftover += 1; + + for itm in self.buffer.iter_mut().skip(self.leftover) { + *itm = 0; + } + + let lenpad = W::size_of(); + // Check for available space for length padding + if (BLOCKSIZE - self.leftover) < lenpad * 2 { + self.process(None); + for itm in self.buffer.iter_mut().take(self.leftover) { + *itm = 0; + } + } + + self.message_len[0] + .as_be(&mut self.buffer[BLOCKSIZE - (lenpad * 2)..BLOCKSIZE - lenpad]); + self.message_len[1].as_be(&mut self.buffer[BLOCKSIZE - lenpad..BLOCKSIZE]); + self.process(None); + + let to_use = OUTSIZE / W::size_of(); + W::as_be_bytes(&self.working_state[..to_use], &mut dest[..OUTSIZE]); + + Ok(()) + } + + #[cfg(test)] + /// Compare two Sha2 state objects to check if their fields + /// are the same. + pub(crate) fn compare_state_to_other(&self, other: &Self) { + for idx in 0..8 { + assert_eq!(self.working_state[idx], other.working_state[idx]); + } + assert_eq!(self.buffer, other.buffer); + assert_eq!(self.leftover, other.leftover); + assert_eq!(self.message_len[0], other.message_len[0]); + assert_eq!(self.message_len[1], other.message_len[1]); + assert_eq!(self.is_finalized, other.is_finalized); + } + } +} + +pub(crate) mod w32 { + use core::convert::{From, TryFrom, TryInto}; + use core::ops::*; + use zeroize::Zeroize; + + #[derive(Debug, PartialEq, Copy, Clone, Default)] + pub(crate) struct WordU32(pub(crate) u32); + + impl Zeroize for WordU32 { + fn zeroize(&mut self) { + self.0.zeroize(); + } + } + + impl BitOr for WordU32 { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + + impl BitAnd for WordU32 { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } + } + + impl BitXor for WordU32 { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self::Output { + Self(self.0 ^ rhs.0) + } + } + + impl From for WordU32 { + fn from(value: usize) -> Self { + // NOTE: Should never panic + Self(u32::try_from(value).unwrap()) + } + } + + impl From for WordU32 { + fn from(value: u32) -> Self { + Self(value) + } + } + + impl Div for WordU32 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0 / rhs.0) + } + } + + impl Shr for WordU32 { + type Output = Self; + + fn shr(self, Self(rhs): Self) -> Self::Output { + let Self(lhs) = self; + Self(lhs >> rhs) + } + } + + impl super::sha2_core::Word for WordU32 { + const MAX: Self = Self(u32::MAX); + + #[inline] + fn wrapping_add(&self, rhs: Self) -> Self { + Self(self.0.wrapping_add(rhs.0)) + } + + #[inline] + fn overflowing_add(&self, rhs: Self) -> (Self, bool) { + let (res, did_overflow) = self.0.overflowing_add(rhs.0); + + (Self(res), did_overflow) + } + + #[inline] + fn checked_add(&self, rhs: Self) -> Option { + self.0.checked_add(rhs.0).map(Self) + } + + #[inline] + fn checked_shl(&self, rhs: u32) -> Option { + self.0.checked_shl(rhs).map(Self) + } + + #[inline] + fn rotate_right(&self, rhs: u32) -> Self { + Self(self.0.rotate_right(rhs)) + } + + #[inline] + fn one() -> Self { + Self(1u32) + } + + #[inline] + fn size_of() -> usize { + core::mem::size_of::() + } + + #[inline] + fn as_be(&self, dest: &mut [u8]) { + debug_assert_eq!(dest.len(), Self::size_of()); + dest.copy_from_slice(&self.0.to_be_bytes()); + } + + #[inline] + fn from_be(src: &[u8]) -> Self { + Self(u32::from_be_bytes(src.try_into().unwrap())) + } + + #[inline] + fn as_be_bytes(src: &[Self], dest: &mut [u8]) { + debug_assert_eq!(dest.len(), src.len() * Self::size_of()); + for (src_elem, dst_chunk) in src.iter().zip(dest.chunks_exact_mut(Self::size_of())) { + src_elem.as_be(dst_chunk); + } + } + + #[inline] + fn from_be_bytes(src: &[u8], dest: &mut [Self]) { + debug_assert_eq!(dest.len(), src.len() / Self::size_of()); + for (src_chunk, dst_elem) in src.chunks_exact(Self::size_of()).zip(dest.iter_mut()) { + *dst_elem = Self::from_be(src_chunk); + } + } + + #[cfg(any(debug_assertions, test))] + fn less_than_or_equal(&self, rhs: Self) -> bool { + self.0 <= rhs.0 + } + } +} + +pub(crate) mod w64 { + use core::convert::{From, TryFrom, TryInto}; + use core::ops::*; + use zeroize::Zeroize; + + #[derive(Debug, PartialEq, Copy, Clone, Default)] + pub(crate) struct WordU64(pub(crate) u64); + + impl Zeroize for WordU64 { + fn zeroize(&mut self) { + self.0.zeroize(); + } + } + + impl BitOr for WordU64 { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + + impl BitAnd for WordU64 { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } + } + + impl BitXor for WordU64 { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self::Output { + Self(self.0 ^ rhs.0) + } + } + + impl From for WordU64 { + fn from(value: usize) -> Self { + // NOTE: Should never panic + Self(u64::try_from(value).unwrap()) + } + } + + impl From for WordU64 { + fn from(value: u64) -> Self { + Self(value) + } + } + + impl Div for WordU64 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0 / rhs.0) + } + } + + impl Shr for WordU64 { + type Output = Self; + + fn shr(self, Self(rhs): Self) -> Self::Output { + let Self(lhs) = self; + Self(lhs >> rhs) + } + } + + impl super::sha2_core::Word for WordU64 { + const MAX: Self = Self(u64::MAX); + + #[inline] + fn wrapping_add(&self, rhs: Self) -> Self { + Self(self.0.wrapping_add(rhs.0)) + } + + #[inline] + fn overflowing_add(&self, rhs: Self) -> (Self, bool) { + let (res, did_overflow) = self.0.overflowing_add(rhs.0); + + (Self(res), did_overflow) + } + + #[inline] + fn checked_add(&self, rhs: Self) -> Option { + self.0.checked_add(rhs.0).map(Self) + } + + #[inline] + fn checked_shl(&self, rhs: u32) -> Option { + self.0.checked_shl(rhs).map(Self) + } + + #[inline] + fn rotate_right(&self, rhs: u32) -> Self { + Self(self.0.rotate_right(rhs)) + } + + #[inline] + fn one() -> Self { + Self(1u64) + } + + #[inline] + fn size_of() -> usize { + core::mem::size_of::() + } + + #[inline] + fn as_be(&self, dest: &mut [u8]) { + debug_assert_eq!(dest.len(), Self::size_of()); + dest.copy_from_slice(&self.0.to_be_bytes()); + } + + #[inline] + fn from_be(src: &[u8]) -> Self { + Self(u64::from_be_bytes(src.try_into().unwrap())) + } + + #[inline] + fn as_be_bytes(src: &[Self], dest: &mut [u8]) { + debug_assert_eq!(dest.len(), src.len() * Self::size_of()); + for (src_elem, dst_chunk) in src.iter().zip(dest.chunks_exact_mut(Self::size_of())) { + src_elem.as_be(dst_chunk); + } + } + + #[inline] + fn from_be_bytes(src: &[u8], dest: &mut [Self]) { + debug_assert_eq!(dest.len(), src.len() / Self::size_of()); + for (src_chunk, dst_elem) in src.chunks_exact(Self::size_of()).zip(dest.iter_mut()) { + *dst_elem = Self::from_be(src_chunk); + } + } + + #[cfg(any(debug_assertions, test))] + fn less_than_or_equal(&self, rhs: Self) -> bool { + self.0 <= rhs.0 + } + } +} + +#[cfg(test)] +mod test_word { + use super::sha2_core::Word; + use super::w32::WordU32; + use super::w64::WordU64; + + #[test] + #[should_panic] + #[cfg(target_pointer_width = "64")] + // We can only test this on 64-bit platforms. + // On 32-bit platforms, due to the on-by-default #[deny(arithmetic_overflow)] + // this won't compile because of `(u32::MAX as usize) + 1)`, not the from call. + fn w32_panic_on_above_from() { + let _ = WordU32::from((u32::MAX as usize) + 1); + } + + #[test] + #[should_panic] + #[cfg(target_pointer_width = "128")] + // See above note. + fn w64_panic_on_above_from() { + WordU64::from((u64::MAX as usize) + 1); + } + + #[test] + fn equiv_max() { + assert_eq!(WordU32::MAX.0, u32::MAX); + assert_eq!(WordU64::MAX.0, u64::MAX); + } + + #[test] + fn equiv_sizeof() { + assert_eq!(WordU32::size_of(), core::mem::size_of::()); + assert_eq!(WordU64::size_of(), core::mem::size_of::()); + } + + #[test] + fn equiv_one() { + assert_eq!(WordU32::one(), WordU32::from(1usize)); + assert_eq!(WordU64::one(), WordU64::from(1usize)); + } + + #[test] + fn equiv_default() { + assert_eq!(WordU32::default().0, u32::default()); + assert_eq!(WordU64::default().0, u64::default()); + } + + #[test] + fn test_results_store_and_load_u32_into_be() { + let input_0: [WordU32; 2] = [WordU32::from(777190791u32), WordU32::from(1465409568u32)]; + let input_1: [WordU32; 4] = [ + WordU32::from(3418616323u32), + WordU32::from(2289579672u32), + WordU32::from(172726903u32), + WordU32::from(1048927929u32), + ]; + let input_2: [WordU32; 6] = [ + WordU32::from(84693101u32), + WordU32::from(443297962u32), + WordU32::from(3962861724u32), + WordU32::from(3081916164u32), + WordU32::from(4167874952u32), + WordU32::from(3982893227u32), + ]; + let input_3: [WordU32; 8] = [ + WordU32::from(2761719494u32), + WordU32::from(242571916u32), + WordU32::from(3097304063u32), + WordU32::from(3924274282u32), + WordU32::from(1553851098u32), + WordU32::from(3673278295u32), + WordU32::from(3531531406u32), + WordU32::from(2347852690u32), + ]; + + let expected_0: [u8; 8] = [46, 82, 253, 135, 87, 88, 96, 32]; + let expected_1: [u8; 16] = [ + 203, 195, 242, 3, 136, 120, 54, 152, 10, 75, 154, 119, 62, 133, 94, 185, + ]; + let expected_2: [u8; 24] = [ + 5, 12, 80, 109, 26, 108, 48, 170, 236, 52, 120, 156, 183, 178, 79, 4, 248, 108, 185, + 136, 237, 102, 32, 171, + ]; + let expected_3: [u8; 32] = [ + 164, 156, 126, 198, 14, 117, 90, 140, 184, 157, 27, 255, 233, 231, 172, 106, 92, 157, + 226, 218, 218, 241, 199, 87, 210, 126, 228, 142, 139, 241, 99, 146, + ]; + + let mut actual_bytes_0 = [0u8; 8]; + let mut actual_bytes_1 = [0u8; 16]; + let mut actual_bytes_2 = [0u8; 24]; + let mut actual_bytes_3 = [0u8; 32]; + + WordU32::as_be_bytes(&input_0, &mut actual_bytes_0); + WordU32::as_be_bytes(&input_1, &mut actual_bytes_1); + WordU32::as_be_bytes(&input_2, &mut actual_bytes_2); + WordU32::as_be_bytes(&input_3, &mut actual_bytes_3); + + assert_eq!(actual_bytes_0, expected_0); + assert_eq!(actual_bytes_1, expected_1); + assert_eq!(actual_bytes_2, expected_2); + assert_eq!(actual_bytes_3, expected_3); + + let mut actual_nums_0 = [WordU32::default(); 2]; + let mut actual_nums_1 = [WordU32::default(); 4]; + let mut actual_nums_2 = [WordU32::default(); 6]; + let mut actual_nums_3 = [WordU32::default(); 8]; + + WordU32::from_be_bytes(&actual_bytes_0, &mut actual_nums_0); + WordU32::from_be_bytes(&actual_bytes_1, &mut actual_nums_1); + WordU32::from_be_bytes(&actual_bytes_2, &mut actual_nums_2); + WordU32::from_be_bytes(&actual_bytes_3, &mut actual_nums_3); + + assert_eq!(actual_nums_0, input_0); + assert_eq!(actual_nums_1, input_1); + assert_eq!(actual_nums_2, input_2); + assert_eq!(actual_nums_3, input_3); + } + + #[test] + fn test_results_store_and_load_u64_into_be() { + let input_0: [WordU64; 2] = [ + WordU64::from(588679683042986719u64), + WordU64::from(14213404201893491922u64), + ]; + let input_1: [WordU64; 4] = [ + WordU64::from(11866671478157678302u64), + WordU64::from(12365793902795026927u64), + WordU64::from(3777757590820648064u64), + WordU64::from(6594491344853184185u64), + ]; + let input_2: [WordU64; 6] = [ + WordU64::from(2101516190274184922u64), + WordU64::from(7904425905466803755u64), + WordU64::from(16590119592260157258u64), + WordU64::from(6043085125584392657u64), + WordU64::from(292831874581513482u64), + WordU64::from(1878340435767862001u64), + ]; + let input_3: [WordU64; 8] = [ + WordU64::from(10720360125345046831u64), + WordU64::from(12576204976780952869u64), + WordU64::from(2183760329755932840u64), + WordU64::from(12806242450747917237u64), + WordU64::from(17861362669514295908u64), + WordU64::from(4901620135335484985u64), + WordU64::from(3014680565865559727u64), + WordU64::from(5106077179490460734u64), + ]; + + let expected_0: [u8; 16] = [ + 8, 43, 105, 13, 130, 68, 74, 223, 197, 64, 39, 208, 214, 231, 244, 210, + ]; + let expected_1: [u8; 32] = [ + 164, 174, 226, 214, 73, 217, 22, 222, 171, 156, 32, 9, 173, 201, 241, 239, 52, 109, 74, + 131, 112, 102, 116, 128, 91, 132, 86, 240, 100, 92, 174, 185, + ]; + let expected_2: [u8; 48] = [ + 29, 42, 21, 215, 59, 6, 102, 218, 109, 178, 41, 123, 72, 190, 134, 43, 230, 59, 241, + 222, 245, 234, 63, 74, 83, 221, 89, 231, 113, 231, 145, 209, 4, 16, 89, 9, 215, 87, + 197, 10, 26, 17, 52, 172, 169, 50, 34, 241, + ]; + let expected_3: [u8; 64] = [ + 148, 198, 94, 188, 47, 116, 33, 47, 174, 135, 167, 203, 119, 135, 69, 37, 30, 78, 70, + 115, 41, 177, 56, 168, 177, 184, 233, 168, 152, 91, 131, 181, 247, 224, 78, 182, 224, + 210, 138, 100, 68, 6, 13, 139, 14, 146, 222, 57, 41, 214, 76, 0, 143, 176, 182, 175, + 70, 220, 110, 36, 63, 65, 228, 62, + ]; + + let mut actual_bytes_0 = [0u8; 16]; + let mut actual_bytes_1 = [0u8; 32]; + let mut actual_bytes_2 = [0u8; 48]; + let mut actual_bytes_3 = [0u8; 64]; + + WordU64::as_be_bytes(&input_0, &mut actual_bytes_0); + WordU64::as_be_bytes(&input_1, &mut actual_bytes_1); + WordU64::as_be_bytes(&input_2, &mut actual_bytes_2); + WordU64::as_be_bytes(&input_3, &mut actual_bytes_3); + + assert_eq!(actual_bytes_0, expected_0); + assert_eq!(actual_bytes_1, expected_1); + assert_eq!(actual_bytes_2.as_ref(), expected_2.as_ref()); + assert_eq!(actual_bytes_3.as_ref(), expected_3.as_ref()); + + let mut actual_nums_0 = [WordU64::default(); 2]; + let mut actual_nums_1 = [WordU64::default(); 4]; + let mut actual_nums_2 = [WordU64::default(); 6]; + let mut actual_nums_3 = [WordU64::default(); 8]; + + WordU64::from_be_bytes(&actual_bytes_0, &mut actual_nums_0); + WordU64::from_be_bytes(&actual_bytes_1, &mut actual_nums_1); + WordU64::from_be_bytes(&actual_bytes_2, &mut actual_nums_2); + WordU64::from_be_bytes(&actual_bytes_3, &mut actual_nums_3); + + assert_eq!(actual_nums_0, input_0); + assert_eq!(actual_nums_1, input_1); + assert_eq!(actual_nums_2, input_2); + assert_eq!(actual_nums_3, input_3); + } + + #[cfg(feature = "safe_api")] + mod proptests { + use super::*; + + #[quickcheck] + #[rustfmt::skip] + fn equiv_from(n: u32, m: u64) -> bool { + // Implicitly assume there's no panic + if WordU32::from(n).0 != n { return false; } + if WordU64::from(m).0 != m { return false; } + + true + } + + #[quickcheck] + #[rustfmt::skip] + fn equiv_ops(n1: u32, n2: u32, m1: u64, m2: u64) -> bool { + // WordU32 + let w32n1 = WordU32::from(n1); + let w32n2 = WordU32::from(n2); + + if (w32n1 | w32n2).0 != n1 | n2 { return false; } + if (w32n1 & w32n2).0 != n1 & n2 { return false; } + if (w32n1 ^ w32n2).0 != n1 ^ n2 { return false; } + // Test only specific values used with Shr (in sigma functions) + if (w32n1 >> WordU32::from(10usize)).0 != n1 >> 10 { return false; } + if (w32n1 >> WordU32::from(3usize)).0 != n1 >> 3 { return false; } + if w32n2.0 != 0 && ((w32n1 / w32n2).0 != n1 / n2) { return false } + + // WordU64 + let w64m1 = WordU64::from(m1); + let w64m2 = WordU64::from(m2); + + if (w64m1 | w64m2).0 != m1 | m2 { return false; } + if (w64m1 & w64m2).0 != m1 & m2 { return false; } + if (w64m1 ^ w64m2).0 != m1 ^ m2 { return false; } + // Test only specific values used with Shr (in sigma functions) + if (w64m1 >> WordU64::from(7usize)).0 != m1 >> 7 { return false; } + if (w64m1 >> WordU64::from(6usize)).0 != m1 >> 6 { return false; } + if w64m2.0 != 0 && ((w64m1 / w64m2).0 != m1 / m2) { return false } + + true + } + + #[quickcheck] + fn equiv_wrapping_add(n1: u32, n2: u32, m1: u64, m2: u64) -> bool { + let w32n1 = WordU32::from(n1); + let w32n2 = WordU32::from(n2); + let ret32 = w32n1.wrapping_add(w32n2).0 == n1.wrapping_add(n2); + + let w64m1 = WordU64::from(m1); + let w64m2 = WordU64::from(m2); + let ret64 = w64m1.wrapping_add(w64m2).0 == m1.wrapping_add(m2); + + ret32 && ret64 + } + + #[quickcheck] + fn equiv_overflowing_add(n1: u32, n2: u32, m1: u64, m2: u64) -> bool { + let w32n1 = WordU32::from(n1); + let w32n2 = WordU32::from(n2); + let ret32: bool = match (w32n1.overflowing_add(w32n2), n1.overflowing_add(n2)) { + ((w32, true), (n, true)) => w32.0 == n, + ((w32, false), (n, false)) => w32.0 == n, + _ => false, + }; + + let w64m1 = WordU64::from(m1); + let w64m2 = WordU64::from(m2); + let ret64: bool = match (w64m1.overflowing_add(w64m2), m1.overflowing_add(m2)) { + ((w64, true), (n, true)) => w64.0 == n, + ((w64, false), (n, false)) => w64.0 == n, + _ => false, + }; + + ret32 && ret64 + } + + #[quickcheck] + fn equiv_checked_add(n1: u32, n2: u32, m1: u64, m2: u64) -> bool { + let w32n1 = WordU32::from(n1); + let w32n2 = WordU32::from(n2); + let ret32: bool = match (w32n1.checked_add(w32n2), n1.checked_add(n2)) { + (Some(w32), Some(n)) => w32.0 == n, + (None, None) => true, + _ => false, + }; + + let w64m1 = WordU64::from(m1); + let w64m2 = WordU64::from(m2); + let ret64: bool = match (w64m1.checked_add(w64m2), m1.checked_add(m2)) { + (Some(w64), Some(n)) => w64.0 == n, + (None, None) => true, + _ => false, + }; + + ret32 && ret64 + } + + #[quickcheck] + fn equiv_checked_shl(n: u32, m: u64, x: u32) -> bool { + let w32n = WordU32::from(n); + let ret32: bool = match (w32n.checked_shl(x), n.checked_shl(x)) { + (Some(w32), Some(n1)) => w32.0 == n1, + (None, None) => true, + _ => false, + }; + + let w64m = WordU64::from(m); + let ret64: bool = match (w64m.checked_shl(x), m.checked_shl(x)) { + (Some(w64), Some(n1)) => w64.0 == n1, + (None, None) => true, + _ => false, + }; + + ret32 && ret64 + } + + #[quickcheck] + #[rustfmt::skip] + fn equiv_rotate_right(n: u32, m: u64, x: u32) -> bool { + let w32n = WordU32::from(n); + let w64m = WordU64::from(m); + + if w32n.rotate_right(x).0 != n.rotate_right(x) { return false; } + if w64m.rotate_right(x).0 != m.rotate_right(x) { return false; } + + true + } + + #[quickcheck] + #[rustfmt::skip] + fn equiv_into_from_be(n: u32, m: u64) -> bool { + + let w32n = WordU32::from(n); + let w64m = WordU64::from(m); + + let mut dest32 = [0u8; core::mem::size_of::()]; + let mut dest64 = [0u8; core::mem::size_of::()]; + w32n.as_be(&mut dest32); + w64m.as_be(&mut dest64); + + if dest32 != n.to_be_bytes() { return false; } + if dest64 != m.to_be_bytes() { return false; } + + + if w32n.0 != u32::from_be_bytes(dest32) { return false; } + if w64m.0 != u64::from_be_bytes(dest64) { return false; } + + true + } + + #[cfg(debug_assertions)] + #[quickcheck] + #[rustfmt::skip] + /// Word::less_than_or_equal() is only used for debug_assertions. + fn equiv_less_than_or_equal(n1: u32, n2: u32, m1: u64, m2: u64) -> bool { + let w32n1 = WordU32::from(n1); + let w32n2 = WordU32::from(n2); + let w64m1 = WordU64::from(m1); + let w64m2 = WordU64::from(m2); + + if w32n1.less_than_or_equal(w32n2) != (n1 <= n2) { return false; } + if w64m1.less_than_or_equal(w64m2) != (m1 <= m2) { return false; } + + true + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha2/sha256.rs b/vendor/orion/src/hazardous/hash/sha2/sha256.rs new file mode 100644 index 0000000..720d805 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha2/sha256.rs @@ -0,0 +1,421 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `data`: The data to be hashed. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^32-1) __bits__ of data are hashed. +//! +//! # Security: +//! - SHA256 is vulnerable to length extension attacks. +//! +//! # Recommendation: +//! - It is recommended to use [BLAKE2b] when possible. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha2::sha256::Sha256; +//! +//! // Using the streaming interface +//! let mut state = Sha256::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha256::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha256::Sha256::update +//! [`reset()`]: sha256::Sha256::reset +//! [`finalize()`]: sha256::Sha256::finalize +//! [BLAKE2b]: super::blake2::blake2b + +use crate::errors::UnknownCryptoError; + +#[cfg(feature = "safe_api")] +use std::io; + +/// The blocksize for the hash function SHA256. +pub const SHA256_BLOCKSIZE: usize = 64; +/// The output size for the hash function SHA256. +pub const SHA256_OUTSIZE: usize = 32; +/// The number of constants for the hash function SHA256. +const N_CONSTS: usize = 64; + +construct_public! { + /// A type to represent the `Digest` that SHA256 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + (Digest, test_digest, SHA256_OUTSIZE, SHA256_OUTSIZE) +} + +impl_from_trait!(Digest, SHA256_OUTSIZE); + +use super::sha2_core::{State, Variant, Word}; +use super::w32::WordU32; + +#[derive(Clone)] +/// SHA256 streaming state. +pub(crate) struct V256; + +impl Variant for V256 { + #[rustfmt::skip] + #[allow(clippy::unreadable_literal)] + /// The SHA256 constants as defined in FIPS 180-4. + const K: [WordU32; N_CONSTS] = [ + WordU32(0x428a2f98), WordU32(0x71374491), WordU32(0xb5c0fbcf), WordU32(0xe9b5dba5), + WordU32(0x3956c25b), WordU32(0x59f111f1), WordU32(0x923f82a4), WordU32(0xab1c5ed5), + WordU32(0xd807aa98), WordU32(0x12835b01), WordU32(0x243185be), WordU32(0x550c7dc3), + WordU32(0x72be5d74), WordU32(0x80deb1fe), WordU32(0x9bdc06a7), WordU32(0xc19bf174), + WordU32(0xe49b69c1), WordU32(0xefbe4786), WordU32(0x0fc19dc6), WordU32(0x240ca1cc), + WordU32(0x2de92c6f), WordU32(0x4a7484aa), WordU32(0x5cb0a9dc), WordU32(0x76f988da), + WordU32(0x983e5152), WordU32(0xa831c66d), WordU32(0xb00327c8), WordU32(0xbf597fc7), + WordU32(0xc6e00bf3), WordU32(0xd5a79147), WordU32(0x06ca6351), WordU32(0x14292967), + WordU32(0x27b70a85), WordU32(0x2e1b2138), WordU32(0x4d2c6dfc), WordU32(0x53380d13), + WordU32(0x650a7354), WordU32(0x766a0abb), WordU32(0x81c2c92e), WordU32(0x92722c85), + WordU32(0xa2bfe8a1), WordU32(0xa81a664b), WordU32(0xc24b8b70), WordU32(0xc76c51a3), + WordU32(0xd192e819), WordU32(0xd6990624), WordU32(0xf40e3585), WordU32(0x106aa070), + WordU32(0x19a4c116), WordU32(0x1e376c08), WordU32(0x2748774c), WordU32(0x34b0bcb5), + WordU32(0x391c0cb3), WordU32(0x4ed8aa4a), WordU32(0x5b9cca4f), WordU32(0x682e6ff3), + WordU32(0x748f82ee), WordU32(0x78a5636f), WordU32(0x84c87814), WordU32(0x8cc70208), + WordU32(0x90befffa), WordU32(0xa4506ceb), WordU32(0xbef9a3f7), WordU32(0xc67178f2), + ]; + + #[rustfmt::skip] + #[allow(clippy::unreadable_literal)] + /// The SHA256 initial hash value H(0) as defined in FIPS 180-4. + const H0: [WordU32; 8] = [ + WordU32(0x6a09e667), WordU32(0xbb67ae85), WordU32(0x3c6ef372), WordU32(0xa54ff53a), + WordU32(0x510e527f), WordU32(0x9b05688c), WordU32(0x1f83d9ab), WordU32(0x5be0cd19), + ]; + + /// The Big Sigma 0 function as specified in FIPS 180-4 section 4.1.2. + fn big_sigma_0(x: WordU32) -> WordU32 { + (x.rotate_right(2)) ^ x.rotate_right(13) ^ x.rotate_right(22) + } + + /// The Big Sigma 1 function as specified in FIPS 180-4 section 4.1.2. + fn big_sigma_1(x: WordU32) -> WordU32 { + (x.rotate_right(6)) ^ x.rotate_right(11) ^ x.rotate_right(25) + } + + /// The Small Sigma 0 function as specified in FIPS 180-4 section 4.1.2. + fn small_sigma_0(x: WordU32) -> WordU32 { + (x.rotate_right(7)) ^ x.rotate_right(18) ^ (x >> WordU32(3)) + } + + /// The Small Sigma 1 function as specified in FIPS 180-4 section 4.1.2. + fn small_sigma_1(x: WordU32) -> WordU32 { + (x.rotate_right(17)) ^ x.rotate_right(19) ^ (x >> WordU32(10)) + } +} + +#[derive(Clone, Debug)] +/// SHA256 streaming state. +pub struct Sha256 { + pub(crate) _state: State, +} + +impl Default for Sha256 { + fn default() -> Self { + Self::new() + } +} + +impl Sha256 { + /// Initialize a `Sha256` struct. + pub fn new() -> Self { + Self { + _state: State::::_new(), + } + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self._state._reset(); + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Finalize the hash and put the final digest into `dest`. + pub(crate) fn _finalize_internal(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a SHA256 digest. + pub fn finalize(&mut self) -> Result { + let mut digest = [0u8; SHA256_OUTSIZE]; + self._finalize_internal(&mut digest)?; + + Ok(Digest::from(digest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Calculate a SHA256 digest of some `data`. + pub fn digest(data: &[u8]) -> Result { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx.finalize() + } +} + +impl crate::hazardous::mac::hmac::HmacHashFunction for Sha256 { + /// The blocksize of the hash function. + const _BLOCKSIZE: usize = SHA256_BLOCKSIZE; + + /// The output size of the hash function. + const _OUTSIZE: usize = SHA256_OUTSIZE; + + /// Create a new instance of the hash function. + fn _new() -> Self { + Self::new() + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(data) + } + + /// Finalize the hash and put the final digest into `dest`. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._finalize_internal(dest) + } + + /// Compute a digest of `data` and copy it into `dest`. + fn _digest(data: &[u8], dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx._finalize_internal(dest) + } + + #[cfg(test)] + fn compare_state_to_other(&self, other: &Self) { + self._state.compare_state_to_other(&other._state); + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: hashing from a [`Read`](std::io::Read)er with SHA256. +/// ```rust +/// use orion::{ +/// hazardous::hash::sha2::sha256::{Sha256, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Sha256::new(); +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Sha256 { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Sha256::update`](crate::hazardous::hash::sha2::sha256::Sha256::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_default_equals_new() { + let new = Sha256::new(); + let default = Sha256::default(); + new._state.compare_state_to_other(&default._state); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = Sha256::new(); + let debug = format!("{:?}", initial_state); + let expected = "Sha256 { _state: State { working_state: [***OMITTED***], buffer: [***OMITTED***], leftover: 0, message_len: [WordU32(0), WordU32(0)], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + impl TestableStreamingContext for Sha256 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + Sha256::digest(input) + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual: Digest = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Sha256, state_2: &Sha256) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Sha256 = Sha256::new(); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + SHA256_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec) -> bool { + let initial_state: Sha256 = Sha256::new(); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + SHA256_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::sha2::sha256::Sha256; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec) -> bool { + let mut hasher_a = Sha256::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_increment_mlen { + use super::*; + + #[test] + fn test_mlen_increase_values() { + let mut context = Sha256::default(); + + context._state.increment_mlen(&WordU32::from(1u32)); + assert_eq!(context._state.message_len[0], WordU32::from(0u32)); + assert_eq!(context._state.message_len[1], WordU32::from(8u32)); + + context._state.increment_mlen(&WordU32::from(17u32)); + assert_eq!(context._state.message_len[0], WordU32::from(0u32)); + assert_eq!(context._state.message_len[1], WordU32::from(144u32)); + + context._state.increment_mlen(&WordU32::from(12u32)); + assert_eq!(context._state.message_len[0], WordU32::from(0u32)); + assert_eq!(context._state.message_len[1], WordU32::from(240u32)); + + // Overflow + context._state.increment_mlen(&WordU32::from(u32::MAX / 8)); + assert_eq!(context._state.message_len[0], WordU32::from(1u32)); + assert_eq!(context._state.message_len[1], WordU32::from(232u32)); + } + + #[test] + #[should_panic] + fn test_panic_on_second_overflow() { + let mut context = Sha256::default(); + context._state.message_len = [WordU32::MAX, WordU32::from(u32::MAX - 7)]; + // u32::MAX - 7, to leave so that the length represented + // in bites should overflow by exactly one. + context._state.increment_mlen(&WordU32::from(1u32)); + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha2/sha384.rs b/vendor/orion/src/hazardous/hash/sha2/sha384.rs new file mode 100644 index 0000000..c043051 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha2/sha384.rs @@ -0,0 +1,403 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `data`: The data to be hashed. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) __bits__ of data are hashed. +//! +//! # Security: +//! - SHA384 is vulnerable to length extension attacks. +//! +//! # Recommendation: +//! - It is recommended to use [BLAKE2b] when possible. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha2::sha384::Sha384; +//! +//! // Using the streaming interface +//! let mut state = Sha384::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha384::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha384::Sha384::update +//! [`reset()`]: sha384::Sha384::reset +//! [`finalize()`]: sha384::Sha384::finalize +//! [BLAKE2b]: super::blake2::blake2b + +use crate::errors::UnknownCryptoError; + +#[cfg(feature = "safe_api")] +use std::io; + +construct_public! { + /// A type to represent the `Digest` that SHA384 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 48 bytes. + (Digest, test_digest, SHA384_OUTSIZE, SHA384_OUTSIZE) +} + +impl_from_trait!(Digest, SHA384_OUTSIZE); + +use super::sha2_core::{State, Variant}; +use super::w64::WordU64; + +/// The blocksize for the hash function SHA384. +pub const SHA384_BLOCKSIZE: usize = 128; +/// The output size for the hash function SHA384. +pub const SHA384_OUTSIZE: usize = 48; +/// The number of constants for the hash function SHA384. +const N_CONSTS: usize = 80; + +#[derive(Clone)] +pub(crate) struct V384; + +impl Variant for V384 { + /// The SHA384 constants as defined in FIPS 180-4. + const K: [WordU64; N_CONSTS] = super::sha512::V512::K; + + #[rustfmt::skip] + #[allow(clippy::unreadable_literal)] + /// The SHA384 initial hash value H(0) as defined in FIPS 180-4. + const H0: [WordU64; 8] = [ + WordU64(0xcbbb9d5dc1059ed8), WordU64(0x629a292a367cd507), WordU64(0x9159015a3070dd17), WordU64(0x152fecd8f70e5939), + WordU64(0x67332667ffc00b31), WordU64(0x8eb44a8768581511), WordU64(0xdb0c2e0d64f98fa7), WordU64(0x47b5481dbefa4fa4), + ]; + + /// The Big Sigma 0 function as specified in FIPS 180-4 section 4.1.3. + fn big_sigma_0(x: WordU64) -> WordU64 { + super::sha512::V512::big_sigma_0(x) + } + + /// The Big Sigma 1 function as specified in FIPS 180-4 section 4.1.3. + fn big_sigma_1(x: WordU64) -> WordU64 { + super::sha512::V512::big_sigma_1(x) + } + + /// The Small Sigma 0 function as specified in FIPS 180-4 section 4.1.3. + fn small_sigma_0(x: WordU64) -> WordU64 { + super::sha512::V512::small_sigma_0(x) + } + + /// The Small Sigma 1 function as specified in FIPS 180-4 section 4.1.3. + fn small_sigma_1(x: WordU64) -> WordU64 { + super::sha512::V512::small_sigma_1(x) + } +} + +#[derive(Clone, Debug)] +/// SHA384 streaming state. +pub struct Sha384 { + pub(crate) _state: State, +} + +impl Default for Sha384 { + fn default() -> Self { + Self::new() + } +} + +impl Sha384 { + /// Initialize a `Sha384` struct. + pub fn new() -> Self { + Self { + _state: State::::_new(), + } + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self._state._reset(); + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Finalize the hash and put the final digest into `dest`. + pub(crate) fn _finalize_internal(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a SHA384 digest. + pub fn finalize(&mut self) -> Result { + let mut digest = [0u8; SHA384_OUTSIZE]; + self._finalize_internal(&mut digest)?; + + Ok(Digest::from(digest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Calculate a SHA384 digest of some `data`. + pub fn digest(data: &[u8]) -> Result { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx.finalize() + } +} + +impl crate::hazardous::mac::hmac::HmacHashFunction for Sha384 { + /// The blocksize of the hash function. + const _BLOCKSIZE: usize = SHA384_BLOCKSIZE; + + /// The output size of the hash function. + const _OUTSIZE: usize = SHA384_OUTSIZE; + + /// Create a new instance of the hash function. + fn _new() -> Self { + Self::new() + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(data) + } + + /// Finalize the hash and put the final digest into `dest`. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._finalize_internal(dest) + } + + /// Compute a digest of `data` and copy it into `dest`. + fn _digest(data: &[u8], dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx._finalize_internal(dest) + } + + #[cfg(test)] + fn compare_state_to_other(&self, other: &Self) { + self._state.compare_state_to_other(&other._state); + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: hashing from a [`Read`](std::io::Read)er with SHA384. +/// ```rust +/// use orion::{ +/// hazardous::hash::sha2::sha384::{Sha384, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Sha384::new(); +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Sha384 { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Sha384::update`](crate::hazardous::hash::sha2::sha384::Sha384::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_default_equals_new() { + let new = Sha384::new(); + let default = Sha384::default(); + new._state.compare_state_to_other(&default._state); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = Sha384::new(); + let debug = format!("{:?}", initial_state); + let expected = "Sha384 { _state: State { working_state: [***OMITTED***], buffer: [***OMITTED***], leftover: 0, message_len: [WordU64(0), WordU64(0)], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + impl TestableStreamingContext for Sha384 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + Sha384::digest(input) + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual: Digest = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Sha384, state_2: &Sha384) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Sha384 = Sha384::new(); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + SHA384_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec) -> bool { + let initial_state: Sha384 = Sha384::new(); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + SHA384_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::sha2::sha384::Sha384; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec) -> bool { + let mut hasher_a = Sha384::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_increment_mlen { + use super::*; + + #[test] + fn test_mlen_increase_values() { + let mut context = Sha384::default(); + + context._state.increment_mlen(&WordU64::from(1u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(8u64)); + + context._state.increment_mlen(&WordU64::from(17u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(144u64)); + + context._state.increment_mlen(&WordU64::from(12u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(240u64)); + + // Overflow + context._state.increment_mlen(&WordU64::from(u64::MAX / 8)); + assert_eq!(context._state.message_len[0], WordU64::from(1u64)); + assert_eq!(context._state.message_len[1], WordU64::from(232u64)); + } + + #[test] + #[should_panic] + fn test_panic_on_second_overflow() { + use crate::hazardous::hash::sha2::sha2_core::Word; + + let mut context = Sha384::default(); + context._state.message_len = [WordU64::MAX, WordU64::from(u64::MAX - 7)]; + // u64::MAX - 7, to leave so that the length represented + // in bites should overflow by exactly one. + context._state.increment_mlen(&WordU64::from(1u64)); + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha2/sha512.rs b/vendor/orion/src/hazardous/hash/sha2/sha512.rs new file mode 100644 index 0000000..0e865c5 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha2/sha512.rs @@ -0,0 +1,424 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `data`: The data to be hashed. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) __bits__ of data are hashed. +//! +//! # Security: +//! - SHA512 is vulnerable to length extension attacks. +//! +//! # Recommendation: +//! - It is recommended to use [BLAKE2b] when possible. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha2::sha512::Sha512; +//! +//! // Using the streaming interface +//! let mut state = Sha512::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha512::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha512::Sha512::update +//! [`reset()`]: sha512::Sha512::reset +//! [`finalize()`]: sha512::Sha512::finalize +//! [BLAKE2b]: super::blake2::blake2b + +use crate::errors::UnknownCryptoError; + +#[cfg(feature = "safe_api")] +use std::io; + +construct_public! { + /// A type to represent the `Digest` that SHA512 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 64 bytes. + (Digest, test_digest, SHA512_OUTSIZE, SHA512_OUTSIZE) +} + +impl_from_trait!(Digest, SHA512_OUTSIZE); + +use super::sha2_core::{State, Variant, Word}; +use super::w64::WordU64; + +/// The blocksize for the hash function SHA512. +pub const SHA512_BLOCKSIZE: usize = 128; +/// The output size for the hash function SHA512. +pub const SHA512_OUTSIZE: usize = 64; +/// The number of constants for the hash function SHA512. +const N_CONSTS: usize = 80; + +#[derive(Clone)] +pub(crate) struct V512; + +impl Variant for V512 { + #[rustfmt::skip] + #[allow(clippy::unreadable_literal)] + /// The SHA512 constants as defined in FIPS 180-4. + const K: [WordU64; N_CONSTS] = [ + WordU64(0x428a2f98d728ae22), WordU64(0x7137449123ef65cd), WordU64(0xb5c0fbcfec4d3b2f), WordU64(0xe9b5dba58189dbbc), + WordU64(0x3956c25bf348b538), WordU64(0x59f111f1b605d019), WordU64(0x923f82a4af194f9b), WordU64(0xab1c5ed5da6d8118), + WordU64(0xd807aa98a3030242), WordU64(0x12835b0145706fbe), WordU64(0x243185be4ee4b28c), WordU64(0x550c7dc3d5ffb4e2), + WordU64(0x72be5d74f27b896f), WordU64(0x80deb1fe3b1696b1), WordU64(0x9bdc06a725c71235), WordU64(0xc19bf174cf692694), + WordU64(0xe49b69c19ef14ad2), WordU64(0xefbe4786384f25e3), WordU64(0x0fc19dc68b8cd5b5), WordU64(0x240ca1cc77ac9c65), + WordU64(0x2de92c6f592b0275), WordU64(0x4a7484aa6ea6e483), WordU64(0x5cb0a9dcbd41fbd4), WordU64(0x76f988da831153b5), + WordU64(0x983e5152ee66dfab), WordU64(0xa831c66d2db43210), WordU64(0xb00327c898fb213f), WordU64(0xbf597fc7beef0ee4), + WordU64(0xc6e00bf33da88fc2), WordU64(0xd5a79147930aa725), WordU64(0x06ca6351e003826f), WordU64(0x142929670a0e6e70), + WordU64(0x27b70a8546d22ffc), WordU64(0x2e1b21385c26c926), WordU64(0x4d2c6dfc5ac42aed), WordU64(0x53380d139d95b3df), + WordU64(0x650a73548baf63de), WordU64(0x766a0abb3c77b2a8), WordU64(0x81c2c92e47edaee6), WordU64(0x92722c851482353b), + WordU64(0xa2bfe8a14cf10364), WordU64(0xa81a664bbc423001), WordU64(0xc24b8b70d0f89791), WordU64(0xc76c51a30654be30), + WordU64(0xd192e819d6ef5218), WordU64(0xd69906245565a910), WordU64(0xf40e35855771202a), WordU64(0x106aa07032bbd1b8), + WordU64(0x19a4c116b8d2d0c8), WordU64(0x1e376c085141ab53), WordU64(0x2748774cdf8eeb99), WordU64(0x34b0bcb5e19b48a8), + WordU64(0x391c0cb3c5c95a63), WordU64(0x4ed8aa4ae3418acb), WordU64(0x5b9cca4f7763e373), WordU64(0x682e6ff3d6b2b8a3), + WordU64(0x748f82ee5defb2fc), WordU64(0x78a5636f43172f60), WordU64(0x84c87814a1f0ab72), WordU64(0x8cc702081a6439ec), + WordU64(0x90befffa23631e28), WordU64(0xa4506cebde82bde9), WordU64(0xbef9a3f7b2c67915), WordU64(0xc67178f2e372532b), + WordU64(0xca273eceea26619c), WordU64(0xd186b8c721c0c207), WordU64(0xeada7dd6cde0eb1e), WordU64(0xf57d4f7fee6ed178), + WordU64(0x06f067aa72176fba), WordU64(0x0a637dc5a2c898a6), WordU64(0x113f9804bef90dae), WordU64(0x1b710b35131c471b), + WordU64(0x28db77f523047d84), WordU64(0x32caab7b40c72493), WordU64(0x3c9ebe0a15c9bebc), WordU64(0x431d67c49c100d4c), + WordU64(0x4cc5d4becb3e42b6), WordU64(0x597f299cfc657e2a), WordU64(0x5fcb6fab3ad6faec), WordU64(0x6c44198c4a475817), + ]; + + #[rustfmt::skip] + #[allow(clippy::unreadable_literal)] + /// The SHA512 initial hash value H(0) as defined in FIPS 180-4. + const H0: [WordU64; 8] = [ + WordU64(0x6a09e667f3bcc908), WordU64(0xbb67ae8584caa73b), WordU64(0x3c6ef372fe94f82b), WordU64(0xa54ff53a5f1d36f1), + WordU64(0x510e527fade682d1), WordU64(0x9b05688c2b3e6c1f), WordU64(0x1f83d9abfb41bd6b), WordU64(0x5be0cd19137e2179), + ]; + + /// The Big Sigma 0 function as specified in FIPS 180-4 section 4.1.3. + fn big_sigma_0(x: WordU64) -> WordU64 { + (x.rotate_right(28)) ^ x.rotate_right(34) ^ x.rotate_right(39) + } + + /// The Big Sigma 1 function as specified in FIPS 180-4 section 4.1.3. + fn big_sigma_1(x: WordU64) -> WordU64 { + (x.rotate_right(14)) ^ x.rotate_right(18) ^ x.rotate_right(41) + } + + /// The Small Sigma 0 function as specified in FIPS 180-4 section 4.1.3. + fn small_sigma_0(x: WordU64) -> WordU64 { + (x.rotate_right(1)) ^ x.rotate_right(8) ^ (x >> WordU64(7)) + } + + /// The Small Sigma 1 function as specified in FIPS 180-4 section 4.1.3. + fn small_sigma_1(x: WordU64) -> WordU64 { + (x.rotate_right(19)) ^ x.rotate_right(61) ^ (x >> WordU64(6)) + } +} + +#[derive(Clone, Debug)] +/// SHA512 streaming state. +pub struct Sha512 { + pub(crate) _state: State, +} + +impl Default for Sha512 { + fn default() -> Self { + Self::new() + } +} + +impl Sha512 { + /// Initialize a `Sha512` struct. + pub fn new() -> Self { + Self { + _state: State::::_new(), + } + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self._state._reset(); + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Finalize the hash and put the final digest into `dest`. + pub(crate) fn _finalize_internal(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a SHA512 digest. + pub fn finalize(&mut self) -> Result { + let mut digest = [0u8; SHA512_OUTSIZE]; + self._finalize_internal(&mut digest)?; + + Ok(Digest::from(digest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Calculate a SHA512 digest of some `data`. + pub fn digest(data: &[u8]) -> Result { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx.finalize() + } +} + +impl crate::hazardous::mac::hmac::HmacHashFunction for Sha512 { + /// The blocksize of the hash function. + const _BLOCKSIZE: usize = SHA512_BLOCKSIZE; + + /// The output size of the hash function. + const _OUTSIZE: usize = SHA512_OUTSIZE; + + /// Create a new instance of the hash function. + fn _new() -> Self { + Self::new() + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(data) + } + + /// Finalize the hash and put the final digest into `dest`. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._finalize_internal(dest) + } + + /// Compute a digest of `data` and copy it into `dest`. + fn _digest(data: &[u8], dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + let mut ctx = Self::new(); + ctx.update(data)?; + ctx._finalize_internal(dest) + } + + #[cfg(test)] + fn compare_state_to_other(&self, other: &Self) { + self._state.compare_state_to_other(&other._state); + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] +/// Example: hashing from a [`Read`](std::io::Read)er with SHA512. +/// ```rust +/// use orion::{ +/// hazardous::hash::sha2::sha512::{Sha512, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Sha512::new(); +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Sha512 { + /// Update the hasher's internal state with *all* of the bytes given. + /// If this function returns the `Ok` variant, it's guaranteed that it + /// will contain the length of the buffer passed to [`Write`](std::io::Write). + /// Note that this function is just a small wrapper over + /// [`Sha512::update`](crate::hazardous::hash::sha2::sha512::Sha512::update). + /// + /// ## Errors: + /// This function will only ever return the [`std::io::ErrorKind::Other`]() + /// variant when it returns an error. Additionally, this will always contain Orion's + /// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. + fn write(&mut self, bytes: &[u8]) -> io::Result { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + /// This type doesn't buffer writes, so flushing is a no-op. + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_default_equals_new() { + let new = Sha512::new(); + let default = Sha512::default(); + new._state.compare_state_to_other(&default._state); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let initial_state = Sha512::new(); + let debug = format!("{:?}", initial_state); + let expected = "Sha512 { _state: State { working_state: [***OMITTED***], buffer: [***OMITTED***], leftover: 0, message_len: [WordU64(0), WordU64(0)], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + impl TestableStreamingContext for Sha512 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + Sha512::digest(input) + } + + fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual: Digest = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Sha512, state_2: &Sha512) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Sha512 = Sha512::new(); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + SHA512_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec) -> bool { + let initial_state: Sha512 = Sha512::new(); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + SHA512_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::sha2::sha512::Sha512; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec) -> bool { + let mut hasher_a = Sha512::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_increment_mlen { + use super::*; + + #[test] + fn test_mlen_increase_values() { + let mut context = Sha512::default(); + + context._state.increment_mlen(&WordU64::from(1u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(8u64)); + + context._state.increment_mlen(&WordU64::from(17u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(144u64)); + + context._state.increment_mlen(&WordU64::from(12u64)); + assert_eq!(context._state.message_len[0], WordU64::from(0u64)); + assert_eq!(context._state.message_len[1], WordU64::from(240u64)); + + // Overflow + context._state.increment_mlen(&WordU64::from(u64::MAX / 8)); + assert_eq!(context._state.message_len[0], WordU64::from(1u64)); + assert_eq!(context._state.message_len[1], WordU64::from(232u64)); + } + + #[test] + #[should_panic] + fn test_panic_on_second_overflow() { + let mut context = Sha512::default(); + context._state.message_len = [WordU64::MAX, WordU64::from(u64::MAX - 7)]; + // u64::MAX - 7, to leave so that the length represented + // in bites should overflow by exactly one. + context._state.increment_mlen(&WordU64::from(1u64)); + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha3/mod.rs b/vendor/orion/src/hazardous/hash/sha3/mod.rs new file mode 100644 index 0000000..28be25d --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha3/mod.rs @@ -0,0 +1,558 @@ +// MIT License + +// Copyright (c) 2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// SHA3-224 as specified in the [FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf). +pub mod sha3_224; + +/// SHA3-256 as specified in the [FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf). +pub mod sha3_256; + +/// SHA3-384 as specified in the [FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf). +pub mod sha3_384; + +/// SHA3-512 as specified in the [FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf). +pub mod sha3_512; + +use crate::errors::UnknownCryptoError; +use core::fmt::Debug; +use zeroize::Zeroize; + +/// Round constants. See NIST intermediate test vectors for source. +const RC: [u64; 24] = [ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +]; + +/// Rho offsets. See NIST intermediate test vectors for source. +const RHO: [u32; 24] = [ + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44, +]; + +/// Indices precomputed based on spec of Pi. +const PI: [usize; 24] = [ + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, +]; + +fn keccakf(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]; +} + +// +#[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 { + pub(crate) state: [u64; 25], + pub(crate) buffer: [u8; RATE], + pub(crate) capacity: usize, + leftover: usize, + is_finalized: bool, +} + +impl Drop for Sha3 { + fn drop(&mut self) { + self.state.iter_mut().zeroize(); + self.buffer.iter_mut().zeroize(); + self.leftover.zeroize(); + } +} + +impl Debug for Sha3 { + 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 Sha3 { + /// 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::()) + .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::()`. + for (out_chunk, state_value) in dest + .chunks_mut(core::mem::size_of::()) + .zip(self.state.iter()) + { + // We need to slice the state value in bytes here for same reason as mentioned + // above. + out_chunk.copy_from_slice(&state_value.to_le_bytes()[..out_chunk.len()]); + } + + Ok(()) + } + + #[cfg(test)] + /// Compare two Sha3 state objects to check if their fields + /// are the same. + pub(crate) fn compare_state_to_other(&self, other: &Self) { + for idx in 0..25 { + assert_eq!(self.state[idx], other.state[idx]); + } + assert_eq!(self.buffer, other.buffer); + assert_eq!(self.capacity, other.capacity); + assert_eq!(self.leftover, other.leftover); + assert_eq!(self.is_finalized, other.is_finalized); + } +} diff --git a/vendor/orion/src/hazardous/hash/sha3/sha3_224.rs b/vendor/orion/src/hazardous/hash/sha3/sha3_224.rs new file mode 100644 index 0000000..e235f2b --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha3/sha3_224.rs @@ -0,0 +1,274 @@ +// MIT License + +// Copyright (c) 2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `data`: The data to be hashed. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha3::sha3_224::Sha3_224; +//! +//! // Using the streaming interface +//! let mut state = Sha3_224::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha3_224::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha3_224::Sha3_224::update +//! [`reset()`]: sha3_224::Sha3_224::reset +//! [`finalize()`]: sha3_224::Sha3_224::finalize + +use crate::errors::UnknownCryptoError; +#[cfg(feature = "safe_api")] +use std::io; + +use super::Sha3; + +/// Rate of SHA3-224 (equivalent to blocksize in SHA2). +pub const SHA3_224_RATE: usize = 144; + +/// Output size of SHA3-224 in bytes. +pub const SHA3_224_OUTSIZE: usize = 28; + +construct_public! { + /// A type to represent the `Digest` that SHA3-224 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 28 bytes. + (Digest, test_digest, SHA3_224_OUTSIZE, SHA3_224_OUTSIZE) +} + +impl_from_trait!(Digest, SHA3_224_OUTSIZE); + +#[derive(Clone, Debug)] +/// SHA3-224 streaming state. +pub struct Sha3_224 { + pub(crate) _state: Sha3, +} + +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>(()) +/// ``` +#[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 { + 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::::_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 { + 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 { + 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 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 { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + 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::::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) -> bool { + let initial_state: Sha3_224 = Sha3_224::new(); + + let test_runner = StreamingContextConsistencyTester::::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) -> bool { + let mut hasher_a = Sha3_224::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha3/sha3_256.rs b/vendor/orion/src/hazardous/hash/sha3/sha3_256.rs new file mode 100644 index 0000000..f972db9 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha3/sha3_256.rs @@ -0,0 +1,336 @@ +// MIT License + +// Copyright (c) 2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `data`: The data to be hashed. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha3::sha3_256::Sha3_256; +//! +//! // Using the streaming interface +//! let mut state = Sha3_256::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha3_256::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha3_256::Sha3_256::update +//! [`reset()`]: sha3_256::Sha3_256::reset +//! [`finalize()`]: sha3_256::Sha3_256::finalize + +use crate::errors::UnknownCryptoError; +#[cfg(feature = "safe_api")] +use std::io; + +use super::Sha3; + +/// Rate of SHA3-256 (equivalent to blocksize in SHA2). +pub const SHA3_256_RATE: usize = 136; + +/// Output size of SHA3-256 in bytes. +pub const SHA3_256_OUTSIZE: usize = 32; + +construct_public! { + /// A type to represent the `Digest` that SHA3-256 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + (Digest, test_digest, SHA3_256_OUTSIZE, SHA3_256_OUTSIZE) +} + +impl_from_trait!(Digest, SHA3_256_OUTSIZE); + +#[derive(Clone, Debug)] +/// SHA3-256 streaming state. +pub struct Sha3_256 { + pub(crate) _state: Sha3, +} + +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>(()) +/// ``` +#[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 { + 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 { + 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 { + 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] + // + 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 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 { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + 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::::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) -> bool { + let initial_state: Sha3_256 = Sha3_256::new(); + + let test_runner = StreamingContextConsistencyTester::::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) -> bool { + let mut hasher_a = Sha3_256::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha3/sha3_384.rs b/vendor/orion/src/hazardous/hash/sha3/sha3_384.rs new file mode 100644 index 0000000..96ccdb3 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha3/sha3_384.rs @@ -0,0 +1,274 @@ +// MIT License + +// Copyright (c) 2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `data`: The data to be hashed. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha3::sha3_384::Sha3_384; +//! +//! // Using the streaming interface +//! let mut state = Sha3_384::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha3_384::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha3_384::Sha3_384::update +//! [`reset()`]: sha3_384::Sha3_384::reset +//! [`finalize()`]: sha3_384::Sha3_384::finalize + +use crate::errors::UnknownCryptoError; +#[cfg(feature = "safe_api")] +use std::io; + +use super::Sha3; + +/// Rate of SHA3-384 (equivalent to blocksize in SHA2). +pub const SHA3_384_RATE: usize = 104; + +/// Output size of SHA3-384 in bytes. +pub const SHA3_384_OUTSIZE: usize = 48; + +construct_public! { + /// A type to represent the `Digest` that SHA3-384 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 48 bytes. + (Digest, test_digest, SHA3_384_OUTSIZE, SHA3_384_OUTSIZE) +} + +impl_from_trait!(Digest, SHA3_384_OUTSIZE); + +#[derive(Clone, Debug)] +/// SHA3-384 streaming state. +pub struct Sha3_384 { + pub(crate) _state: Sha3, +} + +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>(()) +/// ``` +#[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 { + 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::::_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 { + 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 { + 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 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 { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + 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::::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) -> bool { + let initial_state: Sha3_384 = Sha3_384::new(); + + let test_runner = StreamingContextConsistencyTester::::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) -> bool { + let mut hasher_a = Sha3_384::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} diff --git a/vendor/orion/src/hazardous/hash/sha3/sha3_512.rs b/vendor/orion/src/hazardous/hash/sha3/sha3_512.rs new file mode 100644 index 0000000..d1b2432 --- /dev/null +++ b/vendor/orion/src/hazardous/hash/sha3/sha3_512.rs @@ -0,0 +1,274 @@ +// MIT License + +// Copyright (c) 2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `data`: The data to be hashed. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! +//! # Example: +//! ```rust +//! use orion::hazardous::hash::sha3::sha3_512::Sha3_512; +//! +//! // Using the streaming interface +//! let mut state = Sha3_512::new(); +//! state.update(b"Hello world")?; +//! let hash = state.finalize()?; +//! +//! // Using the one-shot function +//! let hash_one_shot = Sha3_512::digest(b"Hello world")?; +//! +//! assert_eq!(hash, hash_one_shot); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: sha3_512::Sha3_512::update +//! [`reset()`]: sha3_512::Sha3_512::reset +//! [`finalize()`]: sha3_512::Sha3_512::finalize + +use crate::errors::UnknownCryptoError; +#[cfg(feature = "safe_api")] +use std::io; + +use super::Sha3; + +/// Rate of SHA3-512 (equivalent to blocksize in SHA2). +pub const SHA3_512_RATE: usize = 72; + +/// Output size of SHA3-512 in bytes. +pub const SHA3_512_OUTSIZE: usize = 64; + +construct_public! { + /// A type to represent the `Digest` that SHA3-512 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 64 bytes. + (Digest, test_digest, SHA3_512_OUTSIZE, SHA3_512_OUTSIZE) +} + +impl_from_trait!(Digest, SHA3_512_OUTSIZE); + +#[derive(Clone, Debug)] +/// SHA3-512 streaming state. +pub struct Sha3_512 { + pub(crate) _state: Sha3, +} + +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>(()) +/// ``` +#[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 { + 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::::_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 { + 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 { + 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 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 { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + 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::::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) -> bool { + let initial_state: Sha3_512 = Sha3_512::new(); + + let test_runner = StreamingContextConsistencyTester::::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) -> bool { + let mut hasher_a = Sha3_512::new(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } +} diff --git a/vendor/orion/src/hazardous/kdf/argon2i.rs b/vendor/orion/src/hazardous/kdf/argon2i.rs new file mode 100644 index 0000000..840c5b8 --- /dev/null +++ b/vendor/orion/src/hazardous/kdf/argon2i.rs @@ -0,0 +1,2607 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # About: +//! Argon2i version 1.3. This implementation is available with features `safe_api` and `alloc`. +//! +//! # Note: +//! This implementation only supports a single thread/lane. +//! +//! # Parameters: +//! - `expected`: The expected derived key. +//! - `password`: Password. +//! - `salt`: Salt value. +//! - `iterations`: Iteration count. +//! - `memory`: Memory size in kibibytes (KiB). +//! - `secret`: Optional secret value used for hashing. +//! - `ad`: Optional associated data used for hashing. +//! - `dst_out`: Destination buffer for the derived key. The length of the +//! derived key is implied by the length of `dst_out`. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of the `password` is greater than [`u32::MAX`]. +//! - The length of the `salt` is greater than [`u32::MAX`] or less than `8`. +//! - The length of the `secret` is greater than [`u32::MAX`]. +//! - The length of the `ad` is greater than [`u32::MAX`]. +//! - The length of `dst_out` is greater than [`u32::MAX`] or less than `4`. +//! - `iterations` is less than `1`. +//! - `memory` is less than `8`. +//! - The hashed password does not match the expected when verifying. +//! +//! # Panics: +//! A panic will occur if: +//! +//! # Security: +//! - Salts should always be generated using a CSPRNG. +//! [`secure_rand_bytes()`] can be used for this. +//! - The minimum recommended length for a salt is `16` bytes. +//! - The minimum recommended length for a hashed password is `16` bytes. +//! - The minimum recommended iteration count is `3`. +//! - Password hashes should always be compared in constant-time. +//! - Please note that when verifying, a copy of the computed password hash is placed into +//! `dst_out`. If the derived hash is considered sensitive and you want to provide defense +//! in depth against an attacker reading your application's private memory, then you as +//! the user are responsible for zeroing out this buffer (see the [`zeroize` crate]). +//! +//! The cost parameters were the recommended values at time of writing. Please be sure to also check +//! [OWASP] for the latest recommended values. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::{hazardous::kdf::argon2i, util}; +//! +//! let mut salt = [0u8; 16]; +//! util::secure_rand_bytes(&mut salt)?; +//! let password = b"Secret password"; +//! let mut dst_out = [0u8; 64]; +//! +//! argon2i::derive_key(password, &salt, 3, 1<<16, None, None, &mut dst_out)?; +//! +//! let expected_dk = dst_out; +//! +//! assert!(argon2i::verify( +//! &expected_dk, +//! password, +//! &salt, +//! 3, +//! 1<<16, +//! None, +//! None, +//! &mut dst_out +//! ) +//! .is_ok()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`secure_rand_bytes()`]: crate::util::secure_rand_bytes +//! [`zeroize` crate]: https://crates.io/crates/zeroize +//! [OWASP]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html + +use crate::errors::UnknownCryptoError; +use crate::hazardous::hash::blake2::blake2b::Blake2b; +use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE; +use crate::util; +use crate::util::endianness::{load_u64_into_le, store_u64_into_le}; +use zeroize::Zeroize; + +/// The Argon2 version (0x13). +pub const ARGON2_VERSION: u32 = 0x13; + +/// The Argon2 variant (i). +pub const ARGON2_VARIANT: u32 = 1; + +/// The amount of segments per lane, as defined in the spec. +const SEGMENTS_PER_LANE: usize = 4; + +/// The amount of lanes supported. +pub(crate) const LANES: u32 = 1; + +/// The minimum amount of memory. +pub(crate) const MIN_MEMORY: u32 = 8 * LANES; + +/// The minimum amount of iterations. +pub(crate) const MIN_ITERATIONS: u32 = 1; + +const fn lower_mult_add(x: u64, y: u64) -> u64 { + let mask = 0xFFFF_FFFFu64; + let x_l = x & mask; + let y_l = y & mask; + let xy = x_l.wrapping_mul(y_l); + x.wrapping_add(y.wrapping_add(xy.wrapping_add(xy))) +} + +/// BLAKE2 G with 64-bit multiplications. +fn g(a: &mut u64, b: &mut u64, c: &mut u64, d: &mut u64) { + *a = lower_mult_add(*a, *b); + *d = (*d ^ *a).rotate_right(32); + *c = lower_mult_add(*c, *d); + *b = (*b ^ *c).rotate_right(24); + *a = lower_mult_add(*a, *b); + *d = (*d ^ *a).rotate_right(16); + *c = lower_mult_add(*c, *d); + *b = (*b ^ *c).rotate_right(63); +} + +#[allow(clippy::too_many_arguments)] +fn permutation_p( + v0: &mut u64, + v1: &mut u64, + v2: &mut u64, + v3: &mut u64, + v4: &mut u64, + v5: &mut u64, + v6: &mut u64, + v7: &mut u64, + v8: &mut u64, + v9: &mut u64, + v10: &mut u64, + v11: &mut u64, + v12: &mut u64, + v13: &mut u64, + v14: &mut u64, + v15: &mut u64, +) { + g(v0, v4, v8, v12); + g(v1, v5, v9, v13); + g(v2, v6, v10, v14); + g(v3, v7, v11, v15); + g(v0, v5, v10, v15); + g(v1, v6, v11, v12); + g(v2, v7, v8, v13); + g(v3, v4, v9, v14); +} + +/// H0 as defined in the specification. +fn initial_hash( + hash_length: u32, + memory_kib: u32, + passes: u32, + p: &[u8], + s: &[u8], + k: &[u8], + x: &[u8], +) -> Result<[u8; 72], UnknownCryptoError> { + // We save additional 8 bytes in H0 for when the first two blocks are processed, + // so that this may contain two little-endian integers. + let mut h0 = [0u8; 72]; + let mut hasher = Blake2b::new(BLAKE2B_OUTSIZE)?; + + // Collect the first part to reduce times we update the hasher state. + h0[0..4].copy_from_slice(&LANES.to_le_bytes()); + h0[4..8].copy_from_slice(&hash_length.to_le_bytes()); + h0[8..12].copy_from_slice(&memory_kib.to_le_bytes()); + h0[12..16].copy_from_slice(&passes.to_le_bytes()); + h0[16..20].copy_from_slice(&ARGON2_VERSION.to_le_bytes()); + h0[20..24].copy_from_slice(&ARGON2_VARIANT.to_le_bytes()); + h0[24..28].copy_from_slice(&(p.len() as u32).to_le_bytes()); + + hasher.update(&h0[..28])?; + hasher.update(p)?; + hasher.update(&(s.len() as u32).to_le_bytes())?; + hasher.update(s)?; + hasher.update(&(k.len() as u32).to_le_bytes())?; + hasher.update(k)?; + hasher.update(&(x.len() as u32).to_le_bytes())?; + hasher.update(x)?; + h0[0..BLAKE2B_OUTSIZE].copy_from_slice(hasher.finalize()?.as_ref()); + + Ok(h0) +} + +/// H' as defined in the specification. +fn extended_hash(input: &[u8], dst: &mut [u8]) -> Result<(), UnknownCryptoError> { + if dst.is_empty() { + return Err(UnknownCryptoError); + } + + let outlen = dst.len() as u32; + + if dst.len() <= BLAKE2B_OUTSIZE { + let mut ctx = Blake2b::new(dst.len())?; + ctx.update(&outlen.to_le_bytes())?; + ctx.update(input)?; + dst.copy_from_slice(ctx.finalize()?.as_ref()); + } else { + let mut ctx = Blake2b::new(BLAKE2B_OUTSIZE)?; + ctx.update(&outlen.to_le_bytes())?; + ctx.update(input)?; + + let mut tmp = ctx.finalize()?; + dst[..BLAKE2B_OUTSIZE].copy_from_slice(tmp.as_ref()); + + let mut pos = BLAKE2B_OUTSIZE / 2; + let mut toproduce = dst.len() - BLAKE2B_OUTSIZE / 2; + + while toproduce > BLAKE2B_OUTSIZE { + ctx.reset()?; + ctx.update(tmp.as_ref())?; + tmp = ctx.finalize()?; + + dst[pos..(pos + BLAKE2B_OUTSIZE)].copy_from_slice(tmp.as_ref()); + pos += BLAKE2B_OUTSIZE / 2; + toproduce -= BLAKE2B_OUTSIZE / 2; + } + + ctx = Blake2b::new(toproduce)?; + ctx.update(tmp.as_ref())?; + tmp = ctx.finalize()?; + dst[pos..outlen as usize].copy_from_slice(&tmp.as_ref()[..toproduce]); + } + + Ok(()) +} + +#[rustfmt::skip] +fn fill_block(w: &mut [u64; 128]) { + + let mut v0: u64; let mut v1: u64; let mut v2: u64; let mut v3: u64; + let mut v4: u64; let mut v5: u64; let mut v6: u64; let mut v7: u64; + let mut v8: u64; let mut v9: u64; let mut v10: u64; let mut v11: u64; + let mut v12: u64; let mut v13: u64; let mut v14: u64; let mut v15: u64; + + let mut idx = 0; + + // Operate on columns. + while idx < 128 { + v0 = w[idx ]; v1 = w[idx + 1]; v2 = w[idx + 2]; v3 = w[idx + 3]; + v4 = w[idx + 4]; v5 = w[idx + 5]; v6 = w[idx + 6]; v7 = w[idx + 7]; + v8 = w[idx + 8]; v9 = w[idx + 9]; v10 = w[idx + 10]; v11 = w[idx + 11]; + v12 = w[idx + 12]; v13 = w[idx + 13]; v14 = w[idx + 14]; v15 = w[idx + 15]; + + permutation_p( + &mut v0, &mut v1, &mut v2, &mut v3, + &mut v4, &mut v5, &mut v6, &mut v7, + &mut v8, &mut v9, &mut v10, &mut v11, + &mut v12, &mut v13, &mut v14, &mut v15 + ); + + w[idx ] = v0; w[idx + 1] = v1; w[idx + 2] = v2; w[idx + 3] = v3; + w[idx + 4] = v4; w[idx + 5] = v5; w[idx + 6] = v6; w[idx + 7] = v7; + w[idx + 8] = v8; w[idx + 9] = v9; w[idx + 10] = v10; w[idx + 11] = v11; + w[idx + 12] = v12; w[idx + 13] = v13; w[idx + 14] = v14; w[idx + 15] = v15; + + idx += 16; + } + + idx = 0; + // Operate on rows. + while idx < 16 { + v0 = w[idx ]; v1 = w[idx + 1]; v2 = w[idx + 16]; v3 = w[idx + 17]; + v4 = w[idx + 32]; v5 = w[idx + 33]; v6 = w[idx + 48]; v7 = w[idx + 49]; + v8 = w[idx + 64]; v9 = w[idx + 65]; v10 = w[idx + 80]; v11 = w[idx + 81]; + v12 = w[idx + 96]; v13 = w[idx + 97]; v14 = w[idx + 112]; v15 = w[idx + 113]; + + permutation_p( + &mut v0, &mut v1, &mut v2, &mut v3, + &mut v4, &mut v5, &mut v6, &mut v7, + &mut v8, &mut v9, &mut v10, &mut v11, + &mut v12, &mut v13, &mut v14, &mut v15 + ); + + w[idx ] = v0; w[idx + 1] = v1; w[idx + 16] = v2; w[idx + 17] = v3; + w[idx + 32] = v4; w[idx + 33] = v5; w[idx + 48] = v6; w[idx + 49] = v7; + w[idx + 64] = v8; w[idx + 65] = v9; w[idx + 80] = v10; w[idx + 81] = v11; + w[idx + 96] = v12; w[idx + 97] = v13; w[idx + 112] = v14; w[idx + 113] = v15; + + idx += 2; + } +} + +/// Data-independent indexing. +struct Gidx { + block: [u64; 128], + addresses: [u64; 128], + segment_length: u32, + offset: u32, +} + +impl Gidx { + fn new(blocks: u32, passes: u32, segment_length: u32) -> Self { + let mut block = [0u64; 128]; + block[1] = 0u64; // Lane number, we only support one (0u64). + block[3] = u64::from(blocks); + block[4] = u64::from(passes); + block[5] = u64::from(ARGON2_VARIANT); // The Argon2i variant + + Self { + block, + addresses: [0u64; 128], + segment_length, + offset: 0, + } + } + + fn init(&mut self, pass_n: u32, segment_n: u32, offset: u32, tmp_block: &mut [u64; 128]) { + self.block[0] = u64::from(pass_n); + self.block[2] = u64::from(segment_n); + self.block[6] = 0u64; // Counter + self.offset = offset; + + self.next_addresses(tmp_block); + + // The existing values in self.addresses are not read + // when generating a new address block. Therefor we + // do not have to zero it out. + } + + fn next_addresses(&mut self, tmp_block: &mut [u64; 128]) { + self.block[6] += 1; + // G-two operation + tmp_block.copy_from_slice(&self.block); + fill_block(tmp_block); + xor_slices!(self.block, tmp_block); + + self.addresses.copy_from_slice(tmp_block); + fill_block(&mut self.addresses); + xor_slices!(tmp_block, self.addresses); + } + + fn get_next(&mut self, segment_idx: u32, tmp_block: &mut [u64; 128]) -> u32 { + // We get J1 and discard J2, as J2 is only relevant if we had more than + // a single lane. + let j1: u64 = self.addresses[self.offset as usize] & 0xFFFF_FFFFu64; + self.offset = (self.offset + 1) % 128; // Wrap-around on block length. + if self.offset == 0 { + self.next_addresses(tmp_block); + } + + // The Argon2 specification for this version (1.3) does not conform + // to the official reference implementation. This implementation follows + // the reference implementation and ignores the specification where they + // disagree. See https://github.com/P-H-C/phc-winner-argon2/issues/183. + + let n_blocks = self.block[3] as u32; + let pass_n = self.block[0] as u32; + let segment_n = self.block[2] as u32; + + let ref_start_pos: u32 = if pass_n == 0 && segment_n == 0 { + segment_idx - 1 + } else if pass_n == 0 { + segment_n * self.segment_length + segment_idx - 1 + } else { + n_blocks - self.segment_length + segment_idx - 1 + }; + + let mut ref_pos: u64 = (j1 * j1) >> 32; + ref_pos = (ref_start_pos as u64 * ref_pos) >> 32; + ref_pos = (ref_start_pos as u64 - 1) - ref_pos; + + if pass_n == 0 || segment_n == 3 { + ref_pos as u32 % n_blocks + } else { + (self.segment_length * (segment_n + 1) + ref_pos as u32) % n_blocks + } + } +} + +#[allow(clippy::too_many_arguments)] +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Argon2i password hashing function as described in the [P-H-C specification](https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf). +pub fn derive_key( + password: &[u8], + salt: &[u8], + iterations: u32, + memory: u32, + secret: Option<&[u8]>, + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if password.len() > 0xFFFF_FFFF { + return Err(UnknownCryptoError); + } + if salt.len() > 0xFFFF_FFFF || salt.len() < 8 { + return Err(UnknownCryptoError); + } + if iterations < MIN_ITERATIONS { + return Err(UnknownCryptoError); + } + if memory < MIN_MEMORY { + return Err(UnknownCryptoError); + } + + let k = match secret { + Some(n_val) => { + if n_val.len() > 0xFFFF_FFFF { + return Err(UnknownCryptoError); + } + + n_val + } + None => &[0u8; 0], + }; + + let x = match ad { + Some(n_val) => { + if n_val.len() > 0xFFFF_FFFF { + return Err(UnknownCryptoError); + } + + n_val + } + None => &[0u8; 0], + }; + + if dst_out.len() > 0xFFFF_FFFF || dst_out.len() < 4 { + return Err(UnknownCryptoError); + } + + // Round down to 4 * p threads + let n_blocks = memory - (memory & 3); + // Divide by 4 (SEGMENTS_PER_LANE) + let segment_length = n_blocks >> 2; + + let mut blocks = vec![[0u64; 128]; n_blocks as usize]; + + // Fill first two blocks + let mut h0 = initial_hash( + dst_out.len() as u32, + memory, + iterations, + password, + salt, + k, + x, + )?; + let mut tmp = [0u8; 1024]; + debug_assert_eq!( + h0.len(), + ((core::mem::size_of::() * 2) + BLAKE2B_OUTSIZE) + ); + debug_assert!( + h0[BLAKE2B_OUTSIZE..(BLAKE2B_OUTSIZE + core::mem::size_of::())] + == [0u8; core::mem::size_of::()] + ); // Block 0 + debug_assert!( + h0[BLAKE2B_OUTSIZE + core::mem::size_of::()..] == [0u8; core::mem::size_of::()] + ); // Lane + + // H' into the first two blocks + extended_hash(&h0, &mut tmp)?; + load_u64_into_le(&tmp, &mut blocks[0]); + h0[BLAKE2B_OUTSIZE..(BLAKE2B_OUTSIZE + core::mem::size_of::())] + .copy_from_slice(&1u32.to_le_bytes()); // Block 1 + extended_hash(&h0, &mut tmp)?; + load_u64_into_le(&tmp, &mut blocks[1]); + + let mut gidx = Gidx::new(n_blocks, iterations, segment_length); + let mut working_block = [0u64; 128]; + + for pass_n in 0..iterations as usize { + for segment_n in 0..SEGMENTS_PER_LANE { + let offset = match (pass_n, segment_n) { + (0, 0) => 2, // The first two blocks have already been processed + _ => 0, + }; + + gidx.init(pass_n as u32, segment_n as u32, offset, &mut working_block); + + for segment_idx in offset..segment_length { + let reference_idx = gidx.get_next(segment_idx, &mut working_block); + let current_idx = segment_n as u32 * segment_length + segment_idx; + let previous_idx = if current_idx > 0 { + current_idx - 1 + } else { + n_blocks - 1 + }; + + let prev_b = blocks.get(previous_idx as usize).unwrap(); + let ref_b = blocks.get(reference_idx as usize).unwrap(); + + // G-xor operation + for (el_tmp, (el_prev, el_ref)) in working_block + .iter_mut() + .zip(prev_b.iter().zip(ref_b.iter())) + { + *el_tmp = el_prev ^ el_ref; + } + let cur_b = blocks.get_mut(current_idx as usize).unwrap(); + xor_slices!(working_block, cur_b); + fill_block(&mut working_block); + xor_slices!(working_block, cur_b); + } + } + } + + store_u64_into_le(blocks.get(n_blocks as usize - 1).unwrap(), &mut tmp); + extended_hash(&tmp, dst_out)?; + + working_block.zeroize(); + tmp.zeroize(); + h0.zeroize(); + for block in blocks.iter_mut() { + block.zeroize(); + } + + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Verify Argon2i derived key in constant time. +pub fn verify( + expected: &[u8], + password: &[u8], + salt: &[u8], + iterations: u32, + memory: u32, + secret: Option<&[u8]>, + ad: Option<&[u8]>, + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + derive_key(password, salt, iterations, memory, secret, ad, dst_out)?; + util::secure_cmp(dst_out, expected) +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[cfg(feature = "safe_api")] + mod test_verify { + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_test_same_input_verify_true( + hlen: u32, + kib: u32, + p: Vec, + s: Vec, + k: Vec, + x: Vec, + ) -> bool { + let passes = 1; + let mem = if !(8..=4096).contains(&kib) { + 1024 + } else { + kib + }; + let salt = if s.len() < 8 { vec![37u8; 8] } else { s }; + + let mut dst_out = if !(4..=512).contains(&hlen) { + vec![0u8; 32] + } else { + vec![0u8; hlen as usize] + }; + + let mut dst_out_verify = dst_out.clone(); + derive_key(&p, &salt, passes, mem, Some(&k), Some(&x), &mut dst_out).unwrap(); + + verify( + &dst_out, + &p, + &salt, + passes, + mem, + Some(&k), + Some(&x), + &mut dst_out_verify, + ) + .is_ok() + } + } + + mod test_derive_key { + use super::*; + + #[test] + fn test_invalid_mem() { + // mem must be at least 8p, where p == threads (1) + let mut dst_out = [0u8; 32]; + assert!(derive_key(&[], &[0u8; 8], 1, 9, None, None, &mut dst_out).is_ok()); + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out).is_ok()); + assert!(derive_key(&[], &[0u8; 8], 1, 7, None, None, &mut dst_out).is_err()); + } + + #[test] + fn test_invalid_passes() { + let mut dst_out = [0u8; 32]; + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out).is_ok()); + assert!(derive_key(&[], &[0u8; 8], 0, 8, None, None, &mut dst_out).is_err()); + } + + #[test] + fn test_dst_out() { + let mut dst_out_less = [0u8; 3]; + let mut dst_out_exact = [0u8; 4]; + let mut dst_out_above = [0u8; 5]; + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out_less).is_err()); + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out_exact).is_ok()); + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out_above).is_ok()); + } + + #[test] + fn test_invalid_salt() { + let mut dst_out = [0u8; 32]; + assert!(derive_key(&[], &[0u8; 8], 1, 8, None, None, &mut dst_out).is_ok()); + assert!(derive_key(&[], &[0u8; 9], 1, 8, None, None, &mut dst_out).is_ok()); + assert!(derive_key(&[], &[0u8; 7], 1, 8, None, None, &mut dst_out).is_err()); + } + + #[test] + fn test_some_or_none_same_result() { + let mut dst_one = [0u8; 32]; + let mut dst_two = [0u8; 32]; + + derive_key(&[255u8; 16], &[1u8; 16], 1, 8, None, None, &mut dst_one).unwrap(); + derive_key( + &[255u8; 16], + &[1u8; 16], + 1, + 8, + Some(&[]), + Some(&[]), + &mut dst_two, + ) + .unwrap(); + + assert_eq!(dst_one, dst_two); + } + + #[test] + fn test_hash_1() { + let mem = 4096; + let passes = 3; + let p = [ + 191, 68, 49, 232, 45, 162, 83, 188, 177, 167, 232, 149, 172, 236, 153, 8, 237, 115, + 232, 128, 171, 254, 47, 84, 192, 208, 196, 121, 127, 221, 93, 126, + ]; + let s = [ + 52, 225, 42, 12, 59, 186, 118, 248, 198, 12, 16, 189, 191, 167, 211, 42, 89, 170, + 108, 9, 172, 4, 138, 232, 239, 58, 189, 238, 250, 33, 230, 130, + ]; + let k = [ + 124, 169, 187, 230, 55, 69, 29, 225, 228, 147, 41, 248, 255, 98, 195, 221, 202, 40, + 58, 17, 93, 122, 37, 57, 169, 9, 80, 64, 170, 177, 33, 89, + ]; + let x = [ + 129, 251, 22, 14, 88, 173, 198, 8, 123, 139, 94, 203, 61, 50, 174, 20, 153, 43, + 109, 154, 46, 8, 71, 4, 208, 83, 157, 133, 143, 171, 78, 195, + ]; + + let expected = [ + 234, 181, 45, 90, 214, 219, 1, 146, 196, 60, 104, 29, 152, 103, 82, 77, 65, 214, + 212, 55, 121, 228, 57, 189, 202, 44, 100, 103, 180, 24, 125, 50, + ]; + + let mut actual = [0u8; 32]; + derive_key(&p, &s, passes, mem, Some(&k), Some(&x), &mut actual).unwrap(); + + assert_eq!(expected.len(), actual.len()); + assert_eq!(expected.as_ref(), &actual[..]); + } + + #[test] + fn test_hash_2() { + let mem = 4096; + let passes = 3; + let p = [ + 99, 137, 197, 238, 38, 112, 35, 125, 195, 31, 121, 180, 52, 30, 19, 20, 198, 227, + 198, 9, 66, 209, 130, 225, 200, 43, 50, 221, 47, 59, 169, 160, 220, 64, 54, 202, + 55, 244, 226, 8, 225, 183, 155, 186, 56, 162, 30, 15, 12, 176, 15, 182, 243, 175, + 24, 142, 80, 247, 2, 210, 208, 57, 28, 59, + ]; + let s = [ + 94, 32, 63, 147, 115, 103, 179, 120, 17, 232, 110, 157, 92, 70, 77, 157, 82, 46, + 79, 122, 29, 191, 104, 146, 125, 208, 48, 24, 6, 8, 94, 196, 65, 238, 136, 255, + 180, 172, 187, 23, 214, 55, 18, 84, 171, 217, 253, 6, 51, 89, 173, 55, 222, 190, + 71, 183, 135, 156, 229, 77, 67, 78, 96, 90, + ]; + let k = [ + 18, 85, 39, 122, 166, 85, 120, 191, 243, 15, 174, 215, 32, 185, 255, 88, 134, 238, + 227, 159, 77, 121, 149, 134, 255, 105, 240, 88, 150, 252, 94, 158, + ]; + let x = [ + 240, 249, 197, 139, 242, 216, 12, 130, 192, 73, 44, 189, 130, 17, 225, 223, 135, + 30, 139, 255, 164, 168, 69, 140, 216, 121, 225, 194, 107, 123, 143, 120, 30, 131, + 216, 196, 200, 81, 71, 203, 26, 66, 171, 118, 236, 26, 18, 105, 100, 35, 227, 184, + 16, 108, 224, 222, 186, 255, 32, 112, 189, 13, 151, 22, + ]; + + let expected = [ + 233, 201, 213, 214, 244, 141, 73, 220, 67, 19, 106, 102, 181, 1, 197, 122, 20, 147, + 99, 245, 236, 160, 3, 213, 22, 219, 155, 217, 194, 24, 65, 204, 239, 56, 34, 160, + 140, 114, 3, 191, 247, 48, 64, 79, 125, 154, 52, 185, 0, 69, 102, 85, 183, 242, + 167, 198, 170, 1, 124, 235, 235, 3, 184, 75, + ]; + let mut actual = [0u8; 64]; + derive_key(&p, &s, passes, mem, Some(&k), Some(&x), &mut actual).unwrap(); + + assert_eq!(expected.len(), actual.len()); + assert_eq!(expected.as_ref(), &actual[..]); + } + + #[test] + fn test_hash_3() { + let mem = 4096; + let passes = 3; + let p = [ + 175, 90, 106, 212, 141, 72, 174, 254, 80, 27, 64, 221, 238, 102, 191, 242, 3, 221, + 202, 111, 10, 217, 92, 143, 15, 200, 215, 210, 199, 59, 200, 59, 98, 141, 106, 228, + 166, 184, 5, 212, 172, 114, 32, 229, 179, 111, 227, 116, 216, 95, 164, 35, 31, 204, + 132, 215, 116, 156, 32, 7, 165, 15, 231, 148, 124, 95, 115, 150, 82, 168, 34, 154, + 52, 166, 104, 28, 207, 61, 162, 198, 228, 72, 196, 200, 228, 251, 124, 231, 44, + 151, 44, 44, 24, 70, 103, 169, 44, 240, 248, 75, 208, 142, 112, 36, 25, 49, 252, + 196, 121, 203, 74, 155, 220, 58, 89, 106, 82, 9, 177, 66, 30, 12, 245, 153, 116, + 72, 233, 233, + ]; + let s = [ + 205, 79, 28, 236, 67, 87, 187, 158, 25, 126, 159, 75, 129, 34, 145, 131, 219, 240, + 243, 73, 42, 223, 247, 12, 223, 190, 218, 212, 26, 109, 15, 151, 118, 77, 210, 18, + 171, 176, 224, 127, 48, 189, 216, 225, 175, 45, 205, 3, 196, 231, 185, 203, 127, + 210, 185, 238, 122, 197, 147, 147, 35, 231, 182, 42, 254, 104, 201, 72, 213, 122, + 46, 24, 21, 80, 26, 221, 252, 117, 51, 208, 107, 147, 24, 3, 72, 222, 32, 95, 20, + 107, 111, 47, 21, 43, 174, 153, 57, 154, 199, 208, 182, 89, 36, 91, 111, 117, 1, + 254, 254, 178, 239, 204, 146, 170, 34, 121, 126, 143, 63, 88, 94, 7, 155, 2, 126, + 79, 43, 183, + ]; + let k = [ + 196, 222, 200, 1, 157, 90, 55, 246, 173, 195, 253, 212, 118, 186, 31, 189, 35, 154, + 202, 137, 60, 32, 56, 89, 179, 44, 105, 140, 185, 225, 111, 242, + ]; + let x = [ + 28, 43, 133, 219, 80, 24, 121, 131, 89, 41, 81, 230, 215, 79, 73, 60, 59, 206, 46, + 22, 241, 113, 205, 178, 219, 91, 159, 220, 225, 106, 152, 3, 187, 167, 148, 23, + 143, 89, 43, 253, 188, 87, 150, 154, 249, 44, 189, 0, 77, 237, 69, 112, 56, 71, + 131, 235, 63, 141, 7, 202, 20, 247, 110, 221, 28, 72, 38, 209, 210, 171, 163, 51, + 42, 6, 54, 121, 208, 125, 160, 105, 81, 196, 237, 22, 206, 140, 35, 89, 160, 102, + 214, 22, 105, 14, 113, 54, 96, 33, 68, 149, 253, 82, 1, 222, 90, 224, 99, 228, 219, + 230, 5, 103, 235, 206, 183, 230, 163, 177, 51, 187, 200, 207, 244, 203, 197, 56, + 24, 132, + ]; + + let expected = [ + 37, 212, 93, 127, 114, 22, 107, 220, 94, 83, 173, 159, 101, 143, 232, 110, 49, 192, + 93, 236, 251, 92, 209, 91, 145, 162, 48, 21, 140, 49, 59, 155, 48, 116, 129, 197, + 223, 12, 34, 240, 209, 200, 152, 9, 112, 175, 206, 35, 166, 229, 230, 13, 110, 89, + 211, 60, 28, 174, 248, 142, 43, 38, 87, 72, 175, 177, 244, 186, 81, 55, 111, 238, + 151, 206, 243, 145, 247, 255, 50, 126, 9, 250, 28, 80, 194, 144, 17, 173, 42, 222, + 251, 125, 11, 130, 169, 17, 17, 112, 45, 66, 129, 118, 96, 67, 36, 252, 61, 156, + 239, 121, 204, 210, 74, 162, 220, 212, 33, 239, 201, 77, 80, 231, 7, 238, 92, 151, + 51, 17, + ]; + + let mut actual = [0u8; 128]; + derive_key(&p, &s, passes, mem, Some(&k), Some(&x), &mut actual).unwrap(); + + assert_eq!(expected.len(), actual.len()); + assert_eq!(expected.as_ref(), &actual[..]); + } + + #[test] + fn test_hash_4() { + let mem = 4096; + let passes = 3; + let p = [ + 129, 27, 156, 146, 197, 105, 114, 238, 251, 207, 14, 230, 59, 139, 249, 192, 99, + 228, 195, 63, 0, 133, 24, 243, 246, 198, 53, 3, 56, 81, 225, 43, 229, 192, 145, 23, + 31, 111, 106, 70, 77, 118, 182, 116, 107, 49, 237, 149, 237, 163, 136, 12, 26, 73, + 70, 124, 87, 238, 250, 1, 79, 136, 101, 184, 173, 23, 79, 182, 171, 60, 249, 231, + 55, 169, 114, 44, 73, 163, 82, 190, 186, 224, 86, 18, 155, 129, 29, 86, 213, 238, + 217, 185, 25, 14, 86, 107, 104, 223, 89, 78, 168, 29, 96, 209, 116, 35, 224, 208, + 249, 106, 161, 58, 220, 236, 76, 142, 67, 235, 10, 79, 209, 49, 147, 59, 187, 0, + 18, 203, 124, 97, 30, 250, 208, 228, 93, 42, 161, 54, 172, 53, 64, 220, 141, 140, + 161, 12, 106, 225, 212, 66, 79, 117, 83, 228, 213, 194, 217, 90, 0, 197, 115, 31, + 194, 40, 215, 247, 45, 20, 15, 65, 9, 78, 108, 186, 76, 167, 18, 87, 214, 202, 31, + 135, 80, 188, 133, 219, 64, 254, 24, 181, 113, 154, 229, 62, 54, 242, 236, 71, 202, + 79, 233, 195, 46, 255, 10, 166, 193, 79, 211, 11, 85, 250, 191, 92, 94, 191, 85, + 219, 133, 33, 211, 134, 90, 88, 239, 179, 167, 199, 154, 163, 213, 214, 63, 25, + 245, 75, 237, 2, 76, 240, 56, 155, 153, 53, 241, 72, 250, 129, 121, 139, 205, 112, + 38, 137, 12, 8, + ]; + let s = [ + 189, 41, 181, 148, 99, 179, 60, 75, 245, 112, 106, 213, 213, 78, 183, 229, 167, 54, + 98, 246, 27, 90, 214, 60, 178, 63, 130, 229, 150, 254, 141, 126, 99, 182, 108, 217, + 134, 102, 245, 53, 136, 72, 159, 194, 239, 7, 238, 82, 96, 198, 218, 50, 80, 3, + 234, 33, 18, 68, 33, 114, 146, 158, 129, 148, 38, 200, 203, 26, 163, 3, 46, 76, + 141, 41, 14, 106, 169, 246, 216, 25, 125, 226, 110, 78, 183, 228, 119, 135, 186, + 151, 117, 32, 220, 221, 54, 19, 142, 73, 92, 167, 191, 28, 46, 244, 171, 254, 80, + 251, 245, 135, 148, 32, 167, 118, 151, 129, 27, 197, 127, 77, 165, 130, 56, 189, + 43, 69, 150, 27, 166, 224, 79, 44, 145, 144, 163, 83, 176, 39, 103, 199, 175, 234, + 30, 202, 87, 229, 38, 238, 61, 9, 82, 247, 136, 122, 94, 157, 110, 58, 198, 137, + 47, 238, 207, 72, 178, 225, 197, 37, 45, 139, 121, 24, 49, 202, 119, 58, 127, 2, + 109, 214, 104, 16, 2, 118, 238, 109, 206, 208, 105, 101, 145, 146, 152, 239, 176, + 59, 32, 224, 220, 231, 135, 11, 173, 222, 65, 154, 206, 130, 164, 243, 113, 40, 33, + 208, 85, 166, 3, 134, 139, 104, 204, 181, 43, 192, 24, 70, 249, 128, 94, 185, 217, + 163, 133, 2, 112, 38, 117, 159, 48, 145, 46, 194, 177, 73, 43, 108, 158, 195, 228, + 32, 122, 21, 185, 109, 107, 235, + ]; + let k = [ + 85, 248, 24, 79, 146, 124, 133, 99, 94, 108, 110, 97, 217, 184, 249, 109, 60, 209, + 248, 195, 45, 90, 90, 31, 61, 11, 202, 90, 122, 99, 155, 197, + ]; + let x = [ + 42, 203, 81, 203, 86, 170, 17, 4, 219, 64, 68, 44, 196, 213, 234, 193, 172, 102, + 173, 159, 41, 0, 43, 6, 149, 224, 135, 50, 224, 63, 104, 211, 226, 97, 11, 219, 95, + 46, 246, 231, 106, 107, 221, 60, 113, 107, 119, 53, 177, 70, 45, 54, 229, 165, 118, + 165, 87, 246, 180, 84, 90, 75, 122, 29, 147, 148, 250, 64, 189, 116, 238, 40, 35, + 228, 126, 242, 39, 64, 153, 67, 166, 8, 197, 0, 36, 189, 182, 171, 85, 202, 134, + 251, 73, 198, 32, 243, 17, 51, 239, 5, 100, 88, 35, 137, 190, 170, 158, 48, 45, + 144, 215, 173, 199, 235, 124, 133, 131, 117, 181, 211, 16, 124, 171, 174, 113, 189, + 79, 86, 86, 103, 223, 47, 167, 97, 85, 219, 224, 70, 160, 214, 45, 85, 249, 70, + 166, 179, 174, 53, 174, 127, 132, 238, 203, 238, 154, 25, 149, 102, 132, 183, 44, + 71, 238, 155, 158, 135, 193, 80, 115, 115, 38, 80, 161, 117, 145, 68, 48, 158, 125, + 12, 23, 230, 66, 143, 9, 29, 122, 105, 105, 103, 222, 157, 230, 253, 56, 188, 160, + 180, 244, 77, 192, 167, 145, 100, 140, 9, 55, 255, 39, 122, 191, 81, 78, 76, 97, + 170, 20, 225, 82, 151, 167, 79, 180, 95, 192, 237, 104, 20, 168, 187, 3, 157, 113, + 108, 20, 129, 179, 182, 239, 33, 192, 233, 57, 79, 242, 63, 207, 107, 10, 1, 133, + 169, 50, 67, 2, 20, + ]; + + let expected = [ + 66, 170, 191, 26, 194, 179, 196, 87, 184, 52, 195, 197, 179, 30, 53, 59, 193, 138, + 103, 175, 140, 92, 232, 252, 74, 138, 32, 61, 249, 50, 22, 21, 255, 149, 219, 8, + 135, 72, 210, 76, 172, 168, 105, 16, 114, 201, 98, 51, 31, 15, 34, 201, 58, 82, + 248, 74, 12, 163, 190, 155, 249, 214, 49, 124, 196, 163, 177, 21, 11, 245, 119, 68, + 25, 68, 220, 169, 209, 151, 123, 16, 68, 236, 2, 145, 31, 230, 80, 203, 181, 208, + 154, 193, 185, 161, 139, 253, 142, 158, 211, 17, 205, 62, 195, 202, 115, 58, 110, + 253, 249, 59, 148, 61, 35, 18, 199, 60, 183, 188, 61, 102, 89, 109, 93, 51, 144, + 254, 84, 43, 217, 203, 232, 229, 123, 126, 171, 240, 179, 26, 168, 1, 146, 13, 161, + 128, 175, 209, 18, 166, 95, 107, 19, 91, 221, 120, 44, 40, 114, 159, 77, 133, 150, + 167, 85, 46, 10, 201, 2, 41, 28, 60, 189, 129, 116, 97, 231, 174, 105, 202, 90, + 227, 99, 3, 239, 102, 175, 241, 246, 111, 20, 76, 96, 252, 49, 31, 226, 225, 33, + 118, 1, 227, 171, 251, 231, 104, 159, 60, 217, 69, 178, 226, 175, 194, 100, 77, + 160, 38, 180, 65, 210, 156, 230, 242, 61, 72, 64, 65, 1, 59, 77, 131, 196, 18, 59, + 219, 33, 159, 18, 207, 113, 249, 21, 237, 79, 143, 180, 104, 61, 84, 238, 234, 254, + 33, 84, 70, 106, 244, + ]; + + let mut actual = [0u8; 256]; + derive_key(&p, &s, passes, mem, Some(&k), Some(&x), &mut actual).unwrap(); + + assert_eq!(expected.len(), actual.len()); + assert_eq!(expected.as_ref(), &actual[..]); + } + + #[test] + fn test_hash_5() { + let mem = 4096; + let passes = 3; + let p = [ + 48, 115, 86, 156, 252, 145, 208, 204, 187, 108, 23, 254, 163, 172, 29, 63, 200, + 218, 50, 100, 108, 23, 64, 12, 70, 88, 171, 112, 154, 113, 39, 238, 202, 1, 88, + 147, 37, 107, 10, 31, 179, 27, 92, 14, 196, 104, 92, 134, 193, 139, 207, 9, 4, 60, + 7, 81, 23, 63, 171, 175, 164, 40, 126, 187, 45, 142, 34, 221, 118, 39, 70, 201, + 192, 241, 70, 225, 70, 112, 42, 222, 151, 125, 218, 98, 246, 67, 244, 154, 9, 237, + 126, 152, 110, 229, 124, 125, 242, 180, 209, 57, 227, 101, 104, 128, 72, 95, 143, + 255, 15, 246, 59, 191, 21, 242, 102, 45, 158, 170, 38, 138, 151, 96, 199, 37, 26, + 240, 239, 52, 72, 23, 227, 111, 157, 80, 241, 7, 73, 150, 22, 251, 118, 3, 99, 216, + 139, 212, 53, 49, 51, 90, 49, 81, 252, 45, 91, 36, 32, 51, 65, 121, 67, 100, 22, + 106, 0, 250, 134, 118, 175, 81, 210, 93, 102, 18, 56, 161, 24, 81, 168, 37, 19, 44, + 23, 94, 50, 190, 77, 186, 144, 2, 97, 168, 59, 210, 183, 220, 168, 176, 112, 134, + 53, 57, 152, 213, 67, 14, 132, 125, 107, 212, 159, 249, 206, 89, 161, 221, 113, + 123, 173, 79, 11, 25, 247, 8, 208, 96, 247, 45, 163, 85, 223, 174, 123, 136, 7, 67, + 73, 204, 18, 235, 182, 27, 237, 32, 106, 215, 84, 253, 72, 224, 37, 146, 199, 157, + 238, 24, 11, 57, 191, 82, 93, 51, 131, 134, 101, 253, 177, 57, 219, 202, 246, 40, + 92, 241, 123, 92, 136, 66, 230, 22, 145, 181, 111, 90, 248, 45, 156, 88, 25, 49, + 234, 64, 117, 242, 70, 210, 242, 68, 88, 62, 76, 110, 113, 119, 57, 77, 254, 242, + 13, 109, 141, 252, 21, 74, 255, 128, 136, 114, 249, 161, 81, 99, 18, 54, 206, 139, + 220, 212, 126, 31, 188, 199, 221, 177, 222, 24, 11, 234, 84, 66, 99, 5, 56, 0, 79, + 134, 97, 79, 229, 69, 153, 211, 190, 88, 116, 198, 81, 194, 196, 21, 158, 99, 140, + 227, 97, 24, 81, 134, 32, 214, 196, 25, 134, 73, 51, 83, 59, 200, 2, 176, 180, 5, + 133, 104, 255, 103, 95, 109, 184, 156, 56, 239, 186, 58, 148, 102, 108, 90, 51, 57, + 96, 134, 15, 128, 114, 165, 104, 175, 221, 223, 255, 83, 245, 19, 205, 169, 107, + 179, 200, 235, 143, 104, 111, 85, 129, 124, 237, 173, 154, 227, 14, 37, 200, 62, + 100, 236, 219, 189, 203, 164, 255, 58, 190, 226, 160, 117, 211, 77, 213, 22, 163, + 231, 226, 61, 168, 11, 207, 27, 105, 153, 210, 41, 175, 83, 204, 116, 63, 118, 196, + 142, 210, 51, 199, 180, 211, 24, 169, 214, 119, 253, 6, 233, 137, 117, 144, 104, + 97, 173, 92, 31, 15, 52, 70, 8, 52, 248, 95, 89, 134, 165, 114, 164, 9, 140, 228, + 127, 147, 6, 189, 126, 89, 0, + ]; + let s = [ + 175, 218, 81, 18, 191, 80, 114, 142, 79, 149, 196, 40, 61, 237, 245, 255, 115, 78, + 114, 250, 187, 148, 98, 141, 118, 42, 225, 26, 129, 64, 145, 107, 127, 227, 222, + 164, 224, 187, 120, 123, 82, 13, 29, 239, 137, 26, 225, 201, 211, 142, 105, 59, + 240, 206, 95, 8, 15, 98, 116, 188, 68, 173, 97, 64, 84, 229, 153, 58, 169, 24, 147, + 143, 156, 194, 247, 47, 192, 230, 102, 39, 18, 219, 243, 23, 195, 26, 121, 231, + 204, 221, 38, 53, 236, 178, 142, 161, 197, 214, 232, 125, 202, 12, 26, 255, 214, + 113, 49, 152, 193, 111, 63, 59, 84, 243, 89, 42, 129, 194, 30, 181, 222, 12, 229, + 105, 129, 216, 133, 22, 16, 118, 48, 16, 162, 99, 198, 79, 136, 91, 141, 231, 47, + 70, 107, 60, 175, 168, 139, 254, 60, 208, 8, 135, 122, 141, 83, 141, 58, 153, 235, + 116, 228, 158, 165, 178, 105, 172, 197, 232, 152, 250, 201, 20, 39, 235, 175, 245, + 128, 47, 134, 96, 163, 176, 67, 254, 36, 219, 14, 134, 118, 17, 4, 106, 74, 201, + 15, 208, 179, 255, 1, 53, 229, 86, 204, 187, 183, 214, 213, 77, 100, 79, 22, 31, + 32, 200, 134, 95, 123, 10, 74, 99, 143, 115, 183, 50, 153, 131, 247, 204, 250, 82, + 141, 68, 229, 102, 2, 120, 196, 65, 221, 234, 189, 11, 141, 183, 162, 153, 144, 22, + 190, 24, 131, 14, 125, 143, 217, 191, 11, 185, 43, 224, 156, 114, 144, 103, 183, + 122, 174, 3, 139, 218, 82, 248, 7, 20, 19, 150, 226, 139, 224, 226, 181, 64, 112, + 193, 61, 54, 52, 219, 75, 19, 217, 109, 37, 2, 172, 53, 230, 31, 153, 5, 113, 154, + 9, 176, 101, 122, 199, 1, 179, 62, 250, 113, 134, 226, 204, 209, 78, 80, 14, 231, + 115, 245, 22, 221, 99, 131, 237, 163, 133, 13, 143, 246, 57, 1, 230, 128, 225, 97, + 210, 48, 181, 3, 151, 220, 97, 231, 138, 89, 136, 201, 184, 203, 18, 32, 141, 62, + 53, 24, 84, 142, 194, 78, 77, 155, 200, 117, 57, 12, 4, 78, 206, 16, 122, 90, 156, + 132, 17, 2, 83, 74, 249, 94, 242, 142, 96, 25, 226, 125, 142, 93, 223, 178, 180, + 232, 177, 70, 188, 31, 19, 165, 200, 137, 173, 96, 189, 242, 149, 118, 100, 142, + 45, 48, 239, 14, 210, 153, 120, 243, 250, 237, 151, 244, 143, 130, 114, 0, 52, 34, + 156, 186, 3, 145, 55, 219, 153, 37, 73, 204, 152, 228, 69, 64, 42, 209, 161, 121, + 66, 85, 251, 62, 127, 18, 193, 91, 187, 198, 52, 12, 88, 125, 52, 93, 135, 60, 65, + 176, 103, 32, 24, 108, 28, 172, 208, 106, 84, 230, 191, 240, 235, 21, 180, 14, 22, + 226, 41, 15, 198, 31, 98, 184, 111, 194, 206, 72, 115, 98, 135, 90, 27, 111, 64, + 87, 149, 20, 154, 158, 65, 172, 116, 134, 62, 49, + ]; + let k = [ + 231, 130, 29, 148, 24, 187, 24, 64, 111, 18, 54, 133, 243, 193, 55, 21, 180, 71, + 147, 96, 46, 96, 27, 198, 81, 167, 14, 201, 18, 221, 209, 3, + ]; + let x = [ + 241, 250, 184, 87, 167, 198, 131, 217, 47, 151, 247, 42, 139, 44, 27, 46, 227, 157, + 35, 46, 215, 150, 10, 236, 127, 16, 142, 203, 182, 135, 242, 203, 243, 219, 210, + 228, 68, 226, 135, 113, 103, 156, 13, 125, 236, 30, 50, 85, 116, 112, 221, 90, 99, + 197, 166, 167, 75, 140, 23, 140, 225, 186, 188, 116, 10, 59, 9, 145, 252, 193, 138, + 60, 148, 23, 88, 37, 213, 11, 124, 170, 22, 155, 13, 86, 84, 167, 182, 185, 59, + 215, 9, 101, 166, 218, 167, 180, 85, 238, 121, 226, 60, 99, 244, 94, 6, 47, 189, + 187, 132, 52, 37, 171, 40, 11, 159, 54, 0, 85, 196, 198, 11, 168, 68, 50, 135, 88, + 174, 37, 226, 249, 208, 26, 224, 55, 18, 65, 152, 215, 67, 57, 128, 226, 201, 13, + 45, 193, 25, 30, 39, 155, 170, 54, 53, 57, 73, 72, 18, 134, 34, 223, 24, 6, 198, + 224, 191, 23, 53, 230, 137, 1, 184, 167, 147, 217, 203, 119, 105, 100, 19, 161, + 209, 67, 192, 50, 31, 163, 108, 161, 163, 42, 11, 167, 24, 37, 163, 23, 51, 180, + 57, 81, 179, 163, 177, 150, 61, 23, 86, 112, 149, 38, 57, 127, 175, 214, 34, 94, + 206, 91, 129, 84, 179, 45, 106, 198, 26, 6, 213, 217, 217, 71, 232, 51, 85, 106, 6, + 220, 10, 78, 252, 221, 124, 23, 192, 247, 186, 93, 190, 89, 222, 57, 186, 228, 5, + 103, 88, 144, 54, 68, 190, 154, 231, 192, 228, 156, 254, 182, 145, 101, 224, 19, + 174, 125, 54, 41, 6, 254, 215, 86, 143, 110, 102, 102, 214, 229, 39, 184, 230, 183, + 220, 190, 193, 8, 26, 246, 76, 248, 100, 253, 211, 109, 108, 137, 52, 87, 88, 53, + 87, 154, 76, 204, 101, 215, 166, 231, 226, 65, 155, 149, 74, 171, 157, 82, 238, + 228, 244, 103, 203, 243, 75, 221, 193, 215, 186, 144, 67, 151, 5, 174, 208, 182, + 22, 195, 177, 197, 180, 177, 202, 144, 94, 109, 204, 3, 118, 42, 109, 131, 132, + 153, 125, 156, 34, 18, 128, 113, 176, 42, 240, 51, 173, 116, 185, 67, 229, 11, 53, + 237, 113, 79, 166, 83, 70, 87, 83, 182, 134, 155, 206, 42, 13, 190, 19, 169, 71, + 79, 33, 112, 204, 133, 147, 135, 243, 0, 90, 105, 191, 5, 154, 124, 50, 157, 209, + 55, 254, 97, 182, 112, 86, 107, 176, 2, 46, 11, 93, 205, 91, 146, 132, 202, 84, + 196, 222, 54, 223, 182, 158, 96, 66, 248, 177, 68, 203, 168, 166, 230, 105, 215, + 27, 75, 250, 125, 183, 10, 225, 35, 91, 250, 239, 63, 136, 87, 133, 192, 93, 122, + 47, 187, 224, 158, 91, 98, 70, 36, 230, 124, 189, 55, 198, 157, 53, 140, 103, 170, + 240, 100, 55, 41, 34, 221, 122, 66, 123, 249, 127, 15, 140, 198, 215, 70, 228, 206, + 186, 195, 4, 220, 116, 198, 105, 148, 180, 170, 148, 116, + ]; + + let expected = [ + 57, 19, 91, 31, 121, 43, 175, 236, 30, 7, 247, 16, 69, 126, 137, 153, 113, 234, 23, + 97, 150, 124, 223, 248, 115, 179, 95, 64, 213, 230, 124, 203, 217, 56, 219, 160, + 56, 132, 249, 133, 214, 67, 197, 155, 191, 40, 151, 201, 159, 212, 144, 214, 71, + 146, 35, 223, 132, 164, 58, 22, 211, 23, 198, 155, 6, 152, 106, 65, 18, 81, 242, + 37, 252, 95, 118, 37, 65, 153, 138, 204, 143, 110, 17, 149, 12, 181, 31, 56, 173, + 194, 11, 65, 23, 10, 190, 66, 126, 4, 180, 53, 80, 65, 81, 140, 226, 22, 50, 16, + 182, 110, 126, 152, 180, 34, 60, 0, 19, 89, 211, 20, 199, 102, 60, 51, 106, 172, + 255, 153, 227, 230, 107, 180, 3, 181, 112, 128, 87, 67, 252, 193, 97, 171, 40, 115, + 8, 228, 234, 132, 33, 140, 206, 109, 98, 92, 221, 145, 164, 32, 190, 163, 23, 102, + 177, 230, 109, 207, 143, 42, 83, 119, 60, 13, 225, 155, 93, 147, 2, 163, 242, 241, + 41, 206, 124, 10, 150, 68, 57, 173, 120, 181, 81, 235, 237, 200, 27, 43, 118, 174, + 171, 238, 143, 242, 198, 247, 247, 114, 93, 187, 75, 165, 107, 88, 15, 245, 112, + 141, 204, 143, 46, 173, 190, 35, 190, 71, 126, 60, 61, 104, 62, 34, 71, 136, 236, + 67, 99, 112, 67, 145, 97, 15, 96, 18, 134, 192, 51, 62, 242, 195, 35, 85, 225, 155, + 139, 245, 129, 74, 86, 56, 18, 147, 222, 210, 152, 228, 252, 47, 227, 165, 109, + 106, 107, 163, 181, 34, 44, 226, 205, 34, 24, 99, 104, 226, 47, 70, 188, 9, 201, + 162, 100, 147, 24, 117, 134, 197, 169, 141, 211, 46, 77, 141, 158, 10, 126, 83, + 187, 199, 111, 227, 211, 154, 34, 14, 128, 200, 197, 76, 12, 230, 30, 24, 58, 74, + 116, 125, 80, 220, 174, 62, 204, 98, 242, 181, 67, 222, 84, 111, 35, 45, 236, 214, + 143, 204, 65, 47, 187, 8, 236, 100, 47, 32, 28, 171, 35, 14, 35, 96, 16, 6, 177, + 177, 147, 174, 180, 174, 62, 164, 232, 189, 144, 191, 209, 253, 85, 40, 205, 138, + 90, 53, 246, 29, 136, 145, 212, 151, 13, 13, 118, 73, 229, 134, 115, 103, 233, 93, + 82, 173, 51, 9, 183, 98, 70, 89, 40, 154, 252, 136, 206, 247, 190, 49, 64, 87, 236, + 209, 65, 10, 37, 0, 28, 8, 155, 45, 243, 53, 199, 96, 112, 25, 234, 128, 199, 95, + 172, 41, 141, 217, 151, 15, 124, 96, 18, 59, 33, 251, 34, 138, 60, 139, 137, 181, + 50, 111, 95, 247, 22, 110, 172, 112, 224, 130, 202, 215, 119, 211, 86, 59, 235, + 150, 55, 16, 193, 77, 240, 73, 101, 184, 57, 92, 0, 230, 186, 103, 43, 155, 225, + 210, 152, 28, 120, 138, 185, 242, 198, 18, 74, 140, 245, 130, 112, 217, 148, 129, + 224, 142, 214, 25, 81, 9, 42, 248, 39, 148, + ]; + let mut actual = [0u8; 512]; + derive_key(&p, &s, passes, mem, Some(&k), Some(&x), &mut actual).unwrap(); + + assert_eq!(expected.len(), actual.len()); + assert_eq!(expected.as_ref(), &actual[..]); + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_initial_hash { + use super::*; + + #[test] + fn initial_hash_test_1() { + let hlen = 3496473570; + let kib = 113001741; + let passes = 172774226; + let p = [ + 225, 168, 40, 211, 31, 67, 71, 99, 229, 168, 106, 43, 101, 94, 51, 219, 193, 88, + 66, 234, 43, 144, 40, 25, 24, 168, 113, 144, 211, 83, 61, 103, + ]; + let s = [ + 123, 165, 225, 133, 80, 117, 28, 160, 138, 80, 59, 206, 190, 36, 171, 53, 127, 145, + 92, 208, 96, 218, 248, 198, 48, 23, 84, 226, 55, 30, 3, 81, + ]; + let k = [ + 235, 154, 1, 51, 161, 180, 78, 36, 109, 83, 83, 163, 59, 225, 74, 104, 79, 58, 127, + 252, 144, 52, 231, 101, 224, 139, 52, 181, 171, 154, 43, 215, + ]; + let x = [ + 236, 219, 129, 50, 196, 196, 170, 157, 179, 88, 71, 155, 243, 42, 69, 108, 238, + 251, 242, 152, 38, 90, 120, 148, 236, 215, 166, 155, 49, 32, 64, 183, + ]; + + let expected = [ + 157, 152, 47, 97, 226, 116, 212, 144, 157, 93, 122, 3, 239, 211, 157, 66, 20, 33, + 133, 93, 0, 4, 53, 86, 167, 67, 88, 98, 125, 11, 137, 122, 142, 17, 16, 84, 146, + 17, 49, 11, 228, 22, 128, 161, 57, 188, 136, 75, 96, 197, 3, 206, 224, 204, 65, + 149, 190, 101, 231, 161, 232, 35, 87, 64, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + let actual = initial_hash(hlen, kib, passes, &p, &s, &k, &x).unwrap(); + assert_eq!(expected.as_ref(), actual.as_ref()); + } + + #[test] + fn initial_hash_test_2() { + let hlen = 1360327050; + let kib = 266855870; + let passes = 263947785; + let p = [ + 62, 145, 210, 51, 41, 168, 197, 154, 64, 67, 181, 144, 73, 11, 90, 166, 13, 111, + 86, 19, 81, 103, 83, 26, 140, 110, 143, 91, 235, 175, 58, 220, 123, 172, 214, 144, + 96, 251, 34, 63, 205, 120, 252, 224, 127, 254, 117, 205, 251, 191, 5, 118, 112, + 219, 91, 16, 184, 80, 197, 229, 23, 239, 138, 200, + ]; + let s = [ + 229, 128, 59, 80, 127, 134, 112, 194, 29, 49, 206, 111, 254, 195, 72, 98, 51, 39, + 50, 55, 55, 47, 68, 231, 82, 91, 229, 226, 244, 102, 59, 32, 184, 171, 121, 57, 3, + 17, 155, 176, 102, 49, 168, 247, 225, 227, 144, 26, 15, 0, 233, 123, 199, 73, 73, + 150, 137, 140, 175, 219, 91, 4, 219, 129, + ]; + let k = [ + 230, 122, 163, 153, 60, 26, 74, 62, 99, 255, 192, 203, 137, 28, 66, 180, 48, 149, + 160, 238, 39, 236, 220, 231, 11, 133, 212, 190, 162, 126, 166, 173, + ]; + let x = [ + 76, 234, 237, 208, 211, 225, 108, 104, 95, 239, 241, 60, 218, 47, 169, 88, 111, + 253, 169, 144, 188, 39, 47, 249, 196, 104, 215, 24, 167, 126, 250, 143, 174, 175, + 167, 159, 115, 77, 127, 219, 142, 76, 37, 104, 64, 174, 241, 190, 204, 160, 149, + 122, 142, 40, 42, 235, 47, 173, 11, 59, 45, 8, 133, 143, + ]; + + let expected = [ + 75, 173, 222, 46, 97, 96, 90, 145, 123, 113, 146, 135, 56, 148, 100, 59, 28, 233, + 228, 56, 215, 15, 138, 5, 90, 30, 128, 111, 131, 160, 92, 32, 97, 76, 216, 81, 134, + 15, 239, 64, 239, 203, 191, 226, 71, 213, 149, 238, 65, 124, 102, 1, 150, 230, 41, + 132, 23, 176, 221, 217, 237, 150, 154, 249, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + let actual = initial_hash(hlen, kib, passes, &p, &s, &k, &x).unwrap(); + assert_eq!(expected.as_ref(), actual.as_ref()); + } + + #[test] + fn initial_hash_test_3() { + let hlen = 710460332; + let kib = 75212384; + let passes = 113373009; + let p = [ + 168, 246, 172, 189, 26, 25, 31, 227, 200, 19, 116, 185, 146, 217, 171, 125, 243, + 174, 179, 205, 67, 207, 224, 58, 252, 44, 132, 238, 174, 187, 196, 87, 75, 2, 130, + 86, 210, 179, 68, 75, 245, 217, 253, 148, 43, 88, 95, 4, 28, 124, 121, 203, 234, + 191, 91, 36, 69, 97, 241, 15, 2, 96, 46, 144, 136, 221, 112, 40, 120, 177, 41, 176, + 201, 2, 21, 217, 40, 94, 247, 62, 75, 68, 105, 41, 89, 211, 228, 254, 159, 194, + 175, 181, 134, 15, 249, 230, 169, 62, 237, 134, 45, 16, 180, 228, 171, 220, 129, + 254, 73, 175, 56, 51, 219, 122, 237, 223, 110, 172, 144, 220, 174, 241, 138, 155, + 204, 39, 183, 156, + ]; + let s = [ + 36, 185, 70, 134, 9, 131, 213, 227, 104, 174, 196, 186, 87, 63, 161, 245, 169, 29, + 72, 60, 248, 48, 0, 27, 179, 15, 177, 233, 121, 31, 13, 19, 103, 106, 105, 187, + 243, 50, 218, 0, 214, 73, 157, 103, 160, 229, 125, 160, 213, 199, 121, 21, 153, 34, + 73, 115, 232, 217, 223, 76, 189, 187, 123, 136, 128, 127, 123, 37, 188, 216, 194, + 184, 212, 137, 197, 19, 101, 25, 141, 7, 7, 85, 192, 179, 136, 244, 104, 84, 142, + 72, 22, 162, 101, 54, 5, 106, 29, 22, 177, 47, 141, 112, 136, 153, 89, 125, 97, 25, + 203, 169, 236, 24, 27, 144, 224, 147, 125, 1, 22, 191, 120, 13, 191, 76, 63, 18, + 238, 148, + ]; + let k = [ + 243, 70, 162, 106, 101, 169, 16, 249, 31, 59, 234, 10, 196, 82, 36, 84, 153, 233, + 22, 14, 198, 100, 178, 225, 157, 177, 233, 83, 27, 133, 114, 254, + ]; + let x = [ + 156, 6, 232, 138, 61, 133, 190, 151, 160, 41, 167, 51, 218, 112, 90, 97, 32, 238, + 123, 89, 149, 121, 166, 50, 186, 121, 189, 128, 157, 235, 134, 168, 14, 193, 154, + 215, 246, 8, 104, 94, 179, 239, 93, 17, 78, 184, 192, 166, 158, 222, 175, 235, 201, + 9, 117, 81, 127, 101, 75, 124, 44, 112, 211, 224, 221, 243, 130, 33, 68, 216, 191, + 127, 61, 180, 118, 25, 233, 51, 241, 68, 92, 159, 49, 95, 146, 142, 65, 93, 113, + 97, 80, 237, 242, 15, 15, 162, 67, 84, 108, 168, 165, 20, 230, 119, 19, 90, 13, 30, + 93, 152, 103, 90, 218, 174, 147, 32, 255, 33, 15, 28, 11, 232, 126, 184, 183, 222, + 6, 111, + ]; + + let expected = [ + 201, 203, 51, 82, 36, 53, 35, 146, 170, 88, 139, 252, 221, 33, 198, 174, 96, 86, + 241, 236, 112, 62, 172, 141, 168, 39, 134, 110, 91, 103, 141, 136, 207, 165, 236, + 236, 58, 237, 193, 139, 30, 191, 244, 2, 176, 123, 134, 44, 251, 101, 255, 220, + 218, 109, 249, 231, 200, 45, 232, 240, 155, 10, 93, 111, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + let actual = initial_hash(hlen, kib, passes, &p, &s, &k, &x).unwrap(); + assert_eq!(expected.as_ref(), actual.as_ref()); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_test_same_result( + hlen: u32, + kib: u32, + passes: u32, + p: Vec, + s: Vec, + k: Vec, + x: Vec, + ) -> bool { + let first = initial_hash(hlen, kib, passes, &p, &s, &k, &x).unwrap(); + let second = initial_hash(hlen, kib, passes, &p, &s, &k, &x).unwrap(); + + first.as_ref() == second.as_ref() + } + } + + mod test_extended_hash { + use super::*; + + #[test] + fn err_on_empty_dst() { + let mut out = [0u8; 0]; + let input = [255u8; 256]; + + assert!(extended_hash(&input, &mut out).is_err()); + } + + #[test] + fn extended_hash_test_1() { + let mut out = [ + 49, 22, 190, 96, 55, 242, 247, 115, 242, 1, 96, 161, 138, 72, 108, 211, 135, 164, + 123, 9, 199, 223, 163, 248, 176, 81, 208, 255, 71, 67, 29, 215, + ]; + let input = [ + 33, 25, 138, 88, 116, 24, 7, 244, 116, 129, 14, 117, 135, 154, 207, 46, 65, 155, + 192, 39, 111, 117, 36, 109, 102, 49, 181, 172, 217, 21, 6, 201, 4, 229, 156, 175, + 201, 35, 84, 130, 195, 50, 97, 38, 137, 182, 162, 240, 16, 46, 202, 146, 2, 73, + 136, 4, 215, 200, 149, 252, 18, 47, 218, 17, + ]; + let expected = [ + 23, 122, 170, 179, 137, 61, 145, 86, 70, 228, 124, 82, 24, 135, 208, 96, 33, 127, + 145, 136, 189, 60, 123, 34, 55, 118, 245, 41, 197, 229, 209, 3, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[test] + fn extended_hash_test_2() { + let mut out = [ + 241, 8, 207, 144, 211, 141, 215, 81, 145, 190, 184, 85, 99, 72, 157, 91, 32, 190, + 241, 192, 207, 205, 157, 119, 110, 28, 49, 117, 239, 220, 185, 246, 211, 188, 166, + 238, 223, 105, 163, 231, 21, 241, 70, 115, 155, 22, 160, 23, 242, 129, 144, 216, + 190, 110, 143, 221, 54, 4, 71, 239, 101, 95, 155, 196, + ]; + let input = [ + 66, 65, 147, 227, 144, 232, 121, 134, 153, 127, 210, 161, 10, 39, 254, 174, 144, + 104, 74, 63, 126, 53, 247, 145, 227, 229, 29, 255, 140, 246, 13, 65, 179, 149, 86, + 150, 216, 81, 178, 131, 136, 40, 139, 220, 43, 185, 119, 249, 161, 244, 0, 177, + 176, 139, 164, 135, 21, 68, 105, 204, 39, 107, 73, 47, 244, 228, 117, 203, 63, 82, + 81, 196, 135, 192, 148, 245, 77, 174, 184, 84, 150, 56, 11, 183, 234, 245, 88, 182, + 248, 223, 124, 252, 170, 111, 9, 48, 22, 227, 18, 118, 136, 22, 250, 22, 108, 229, + 176, 186, 19, 45, 67, 105, 19, 45, 94, 113, 16, 116, 215, 188, 91, 105, 36, 18, 77, + 235, 195, 113, + ]; + let expected = [ + 33, 81, 20, 93, 250, 207, 85, 11, 227, 90, 81, 170, 97, 236, 60, 207, 156, 65, 52, + 186, 53, 114, 252, 33, 118, 184, 12, 21, 239, 186, 19, 84, 98, 59, 219, 146, 117, + 222, 212, 217, 233, 173, 84, 38, 188, 102, 165, 73, 137, 64, 18, 214, 51, 167, 180, + 113, 50, 196, 175, 138, 96, 109, 95, 61, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[test] + fn extended_hash_test_3() { + let mut out = [ + 70, 156, 46, 182, 87, 221, 0, 156, 124, 47, 167, 94, 57, 77, 222, 142, 130, 234, + 218, 139, 119, 27, 170, 129, 232, 219, 152, 79, 7, 237, 81, 3, 203, 33, 116, 167, + 159, 232, 31, 143, 142, 217, 118, 158, 40, 42, 42, 131, 249, 99, 63, 136, 182, 122, + 161, 8, 77, 7, 243, 7, 152, 54, 211, 102, 158, 7, 238, 103, 203, 249, 40, 204, 13, + 246, 0, 169, 235, 154, 14, 86, 4, 183, 145, 233, 248, 125, 155, 22, 8, 207, 80, 40, + 159, 55, 207, 151, 248, 170, 101, 233, 3, 68, 253, 88, 77, 164, 182, 211, 154, 101, + 210, 199, 58, 98, 110, 127, 189, 180, 158, 38, 30, 97, 124, 55, 82, 39, 183, 115, + ]; + let input = [ + 103, 132, 147, 55, 198, 201, 33, 151, 217, 248, 118, 190, 164, 159, 224, 197, 172, + 89, 93, 146, 170, 143, 72, 88, 75, 13, 41, 237, 20, 77, 117, 54, 100, 76, 198, 85, + 222, 182, 69, 119, 55, 251, 165, 141, 16, 105, 157, 25, 14, 70, 182, 131, 95, 21, + 156, 64, 3, 133, 179, 66, 9, 33, 181, 158, 165, 212, 142, 86, 22, 236, 235, 17, + 243, 34, 13, 109, 56, 111, 63, 75, 217, 153, 60, 159, 172, 233, 145, 142, 181, 136, + 210, 174, 187, 55, 153, 214, 105, 233, 196, 69, 64, 0, 59, 25, 21, 27, 233, 87, + 119, 31, 184, 15, 160, 55, 228, 132, 41, 110, 255, 79, 90, 141, 183, 156, 251, 89, + 90, 151, 199, 149, 220, 31, 85, 85, 87, 253, 79, 97, 18, 125, 251, 227, 120, 236, + 196, 203, 135, 195, 194, 160, 129, 89, 40, 111, 160, 222, 101, 149, 109, 153, 90, + 156, 220, 219, 89, 128, 8, 17, 104, 108, 145, 233, 121, 100, 124, 151, 96, 39, 187, + 132, 173, 9, 74, 154, 199, 126, 104, 241, 198, 190, 148, 221, 29, 50, 234, 19, 75, + 139, 135, 34, 247, 247, 226, 245, 142, 140, 152, 53, 210, 65, 174, 168, 70, 41, 13, + 11, 108, 29, 2, 93, 24, 156, 209, 159, 123, 80, 76, 111, 245, 39, 81, 252, 114, 82, + 175, 107, 42, 34, 131, 221, 209, 23, 231, 174, 242, 10, 17, 77, 251, 138, 239, 213, + 157, 197, 87, 96, + ]; + let expected = [ + 247, 38, 177, 59, 225, 220, 244, 197, 13, 169, 51, 184, 170, 167, 18, 78, 77, 196, + 23, 182, 207, 227, 211, 203, 66, 202, 238, 18, 72, 7, 110, 92, 162, 84, 125, 185, + 132, 129, 210, 217, 217, 93, 17, 93, 58, 18, 31, 165, 3, 194, 111, 223, 231, 8, + 120, 102, 201, 76, 149, 253, 233, 246, 199, 21, 157, 107, 186, 47, 123, 209, 94, + 151, 56, 53, 33, 8, 116, 26, 58, 53, 255, 51, 184, 18, 241, 179, 54, 15, 181, 18, + 117, 48, 83, 190, 250, 39, 126, 145, 178, 150, 185, 87, 172, 5, 176, 110, 227, 142, + 233, 63, 85, 225, 162, 85, 179, 166, 250, 222, 5, 10, 139, 187, 172, 105, 171, 171, + 140, 253, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[test] + fn extended_hash_test_4() { + let mut out = [ + 231, 57, 92, 140, 212, 20, 239, 34, 56, 165, 207, 203, 237, 63, 103, 245, 135, 137, + 173, 240, 171, 105, 161, 221, 2, 15, 36, 47, 166, 126, 151, 21, 223, 34, 8, 141, + 193, 50, 70, 138, 65, 197, 165, 167, 25, 207, 114, 124, 147, 80, 192, 179, 171, 18, + 58, 180, 3, 79, 233, 231, 156, 157, 106, 2, + ]; + let input = [ + 213, 96, 143, 63, 224, 241, 183, 146, 44, 45, 66, 96, 200, 213, 151, 108, 41, 142, + 193, 159, 45, 198, 28, 146, 65, 13, 39, 36, 153, 46, 225, 14, + ]; + let expected = [ + 174, 131, 211, 180, 105, 12, 67, 55, 16, 72, 125, 125, 211, 93, 64, 180, 179, 188, + 77, 113, 119, 181, 98, 54, 13, 146, 57, 92, 43, 232, 224, 183, 219, 138, 143, 234, + 232, 151, 33, 76, 158, 96, 170, 104, 200, 127, 55, 239, 145, 241, 224, 146, 0, 11, + 64, 16, 212, 151, 227, 7, 65, 234, 92, 45, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[test] + fn extended_hash_test_5() { + let mut out = [ + 53, 56, 9, 207, 79, 196, 6, 94, 170, 197, 204, 233, 69, 124, 20, 228, 227, 59, 102, + 30, 88, 45, 245, 144, 69, 50, 72, 163, 31, 100, 44, 12, 203, 9, 253, 13, 253, 221, + 216, 186, 92, 164, 37, 55, 195, 31, 13, 39, 110, 180, 40, 167, 40, 236, 138, 203, + 123, 174, 121, 219, 133, 211, 184, 133, 255, 239, 233, 193, 203, 90, 48, 59, 95, + 111, 55, 11, 95, 147, 178, 164, 241, 231, 109, 21, 16, 161, 192, 86, 156, 138, 137, + 224, 139, 52, 142, 192, 189, 231, 170, 54, 55, 150, 12, 122, 51, 250, 167, 127, 5, + 204, 63, 34, 71, 221, 162, 35, 33, 246, 22, 187, 187, 2, 41, 223, 81, 143, 231, 77, + ]; + let input = [ + 92, 110, 199, 162, 60, 226, 227, 26, 123, 30, 136, 146, 116, 38, 44, 194, 254, 14, + 137, 67, 183, 2, 112, 194, 30, 15, 100, 215, 248, 47, 223, 93, 156, 71, 98, 247, + 54, 74, 92, 233, 219, 165, 1, 45, 162, 225, 7, 80, 237, 172, 245, 25, 80, 162, 216, + 83, 35, 122, 156, 143, 55, 19, 5, 26, + ]; + let expected = [ + 253, 153, 55, 223, 21, 172, 36, 50, 109, 171, 45, 24, 40, 215, 239, 116, 92, 149, + 31, 40, 17, 99, 42, 25, 114, 52, 167, 230, 63, 36, 226, 178, 222, 163, 247, 175, + 100, 118, 54, 51, 223, 11, 164, 68, 126, 157, 94, 255, 196, 53, 177, 231, 81, 55, + 1, 250, 85, 91, 89, 45, 15, 121, 66, 157, 195, 162, 97, 243, 33, 195, 149, 253, + 193, 24, 150, 106, 234, 158, 122, 28, 52, 72, 48, 109, 206, 190, 116, 50, 163, 191, + 208, 86, 231, 170, 11, 210, 251, 135, 50, 46, 160, 202, 72, 101, 45, 24, 202, 72, + 210, 25, 239, 0, 229, 47, 200, 219, 202, 0, 39, 195, 197, 148, 15, 32, 211, 167, + 196, 128, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[test] + fn extended_hash_test_6() { + let mut out = [ + 253, 1, 93, 186, 15, 159, 80, 7, 174, 85, 112, 241, 193, 170, 254, 103, 204, 254, + 154, 58, 228, 243, 244, 192, 223, 174, 103, 229, 21, 66, 203, 203, 221, 186, 76, + 40, 49, 66, 170, 52, 140, 254, 142, 95, 23, 200, 19, 117, 252, 9, 144, 94, 63, 14, + 66, 162, 168, 125, 112, 76, 45, 166, 241, 179, 54, 75, 107, 140, 92, 95, 211, 138, + 209, 143, 237, 130, 180, 19, 156, 242, 65, 22, 55, 228, 23, 106, 119, 14, 140, 66, + 188, 206, 107, 93, 130, 123, 6, 10, 85, 250, 177, 195, 46, 248, 177, 195, 86, 150, + 21, 10, 160, 108, 113, 75, 253, 51, 12, 173, 254, 71, 236, 160, 176, 130, 71, 161, + 205, 146, 104, 63, 189, 90, 117, 110, 141, 16, 18, 246, 158, 71, 201, 242, 53, 169, + 5, 3, 91, 227, 157, 11, 127, 150, 180, 200, 73, 27, 2, 78, 209, 89, 93, 78, 2, 136, + 70, 46, 65, 181, 217, 158, 54, 143, 135, 229, 217, 239, 22, 175, 156, 176, 111, 54, + 141, 230, 133, 232, 137, 100, 81, 147, 71, 176, 113, 125, 88, 48, 170, 19, 156, + 184, 91, 166, 195, 251, 143, 253, 135, 107, 215, 209, 224, 40, 96, 56, 222, 118, + 247, 22, 0, 251, 215, 62, 179, 190, 112, 75, 79, 96, 148, 246, 46, 15, 91, 117, + 142, 221, 133, 155, 237, 126, 144, 104, 240, 124, 130, 222, 19, 93, 87, 120, 83, + 117, 77, 75, 105, 104, + ]; + let input = [ + 89, 106, 243, 203, 190, 196, 239, 41, 217, 53, 96, 178, 255, 156, 212, 103, 117, + 255, 25, 219, 215, 212, 74, 47, 227, 67, 151, 151, 241, 100, 32, 178, 197, 211, 63, + 206, 247, 215, 141, 236, 41, 248, 232, 241, 106, 178, 52, 133, 83, 177, 65, 177, + 253, 118, 157, 226, 225, 137, 134, 127, 231, 48, 46, 156, 51, 224, 102, 94, 205, + 30, 222, 59, 173, 243, 205, 117, 78, 112, 160, 35, 66, 220, 113, 146, 100, 194, 56, + 85, 28, 75, 57, 59, 243, 201, 250, 140, 147, 24, 253, 84, 135, 91, 221, 190, 128, + 225, 118, 27, 74, 251, 27, 182, 254, 122, 44, 48, 222, 131, 32, 176, 254, 250, 200, + 2, 38, 202, 255, 207, + ]; + let expected = [ + 22, 21, 67, 184, 94, 20, 98, 6, 113, 81, 65, 110, 70, 42, 13, 58, 26, 213, 184, + 242, 234, 133, 185, 122, 112, 235, 18, 11, 94, 199, 64, 107, 116, 55, 49, 85, 178, + 118, 146, 51, 230, 150, 214, 229, 90, 162, 178, 225, 106, 138, 169, 206, 77, 161, + 112, 162, 86, 101, 48, 90, 227, 247, 147, 186, 120, 84, 101, 196, 141, 213, 215, + 115, 201, 150, 35, 182, 156, 243, 87, 242, 165, 45, 128, 127, 70, 51, 225, 40, 27, + 250, 173, 46, 109, 116, 254, 202, 206, 112, 48, 205, 21, 164, 129, 192, 181, 119, + 195, 126, 38, 177, 107, 55, 149, 126, 227, 44, 254, 225, 104, 15, 236, 141, 233, + 110, 132, 133, 241, 17, 210, 26, 22, 175, 135, 199, 106, 200, 214, 45, 20, 83, 164, + 49, 202, 69, 203, 191, 21, 92, 101, 206, 109, 136, 144, 123, 108, 24, 121, 142, 77, + 91, 122, 248, 117, 85, 82, 181, 228, 192, 197, 111, 169, 161, 30, 12, 201, 127, 24, + 17, 185, 88, 4, 126, 83, 107, 76, 6, 6, 146, 205, 164, 202, 151, 11, 189, 205, 159, + 146, 245, 79, 13, 127, 23, 148, 219, 156, 104, 161, 201, 155, 81, 126, 57, 34, 201, + 118, 110, 163, 135, 194, 38, 9, 2, 205, 54, 192, 55, 214, 98, 23, 35, 70, 113, 120, + 68, 206, 127, 130, 174, 252, 254, 135, 37, 160, 144, 108, 29, 86, 108, 159, 148, + 221, 54, 153, 234, 194, 103, + ]; + extended_hash(&input, &mut out).unwrap(); + assert_eq!(expected.as_ref(), out.as_ref()); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_test_same_result(input: Vec, out: Vec) -> bool { + let mut first = out.clone(); + let mut second = out.clone(); + + if out.is_empty() && extended_hash(&input, &mut first).is_err() { + return true; + } + + extended_hash(&input, &mut first).unwrap(); + extended_hash(&input, &mut second).unwrap(); + + first == second + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_test_diff_result(input: Vec, out: Vec) -> bool { + let mut first = out.clone(); + let mut second = out.clone(); + + if out.is_empty() && extended_hash(&input, &mut first).is_err() { + return true; + } + + extended_hash(&input, &mut first).unwrap(); + extended_hash(&first, &mut second).unwrap(); + + first != second + } + } + + mod test_gidx { + use super::*; + + #[test] + fn gidx_test() { + let n_blocks = 4096; + let segment_length = 1024; + let passes = 3; + + let mut gidx = Gidx::new(n_blocks, passes, segment_length); + let mut tmp_block = [0u64; 128]; + + let offset = 2; + let pass_n = 0; + let segment_n = 0; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1022] = [ + 0, 1, 0, 3, 2, 1, 6, 3, 7, 8, 10, 11, 11, 4, 14, 7, 16, 13, 6, 17, 5, 20, 11, 19, + 20, 2, 26, 26, 22, 21, 28, 19, 31, 18, 30, 35, 31, 33, 35, 31, 28, 35, 24, 43, 41, + 42, 44, 41, 36, 32, 38, 48, 43, 53, 51, 42, 4, 44, 17, 45, 53, 49, 38, 27, 7, 24, + 53, 59, 24, 45, 19, 38, 55, 49, 74, 60, 41, 44, 17, 60, 0, 78, 45, 32, 83, 10, 18, + 65, 60, 65, 84, 26, 1, 93, 48, 67, 68, 97, 30, 76, 68, 53, 75, 42, 83, 25, 106, 2, + 108, 101, 110, 61, 34, 94, 14, 92, 26, 69, 108, 51, 94, 69, 122, 123, 120, 94, 3, + 67, 125, 39, 97, 14, 37, 41, 112, 124, 18, 125, 96, 107, 134, 134, 69, 121, 139, + 112, 52, 98, 138, 71, 129, 134, 126, 152, 153, 150, 43, 109, 152, 159, 157, 61, + 146, 153, 47, 42, 166, 78, 30, 43, 132, 171, 121, 149, 119, 172, 115, 13, 132, 140, + 165, 45, 29, 142, 76, 78, 175, 187, 150, 120, 182, 153, 192, 188, 67, 191, 193, + 125, 127, 183, 57, 138, 186, 24, 89, 40, 56, 207, 196, 64, 62, 211, 195, 70, 184, + 87, 45, 198, 53, 181, 129, 62, 164, 142, 59, 223, 173, 142, 228, 82, 230, 136, 160, + 194, 203, 235, 155, 181, 216, 143, 139, 102, 210, 180, 198, 235, 93, 177, 149, 245, + 245, 177, 252, 253, 20, 198, 143, 216, 102, 181, 91, 259, 200, 263, 106, 240, 200, + 267, 229, 160, 57, 116, 71, 203, 274, 182, 13, 237, 241, 202, 85, 278, 269, 261, + 219, 56, 283, 186, 280, 215, 265, 195, 292, 137, 293, 294, 277, 224, 106, 285, 300, + 82, 302, 289, 234, 236, 300, 242, 277, 52, 214, 244, 304, 259, 179, 235, 316, 305, + 28, 230, 242, 313, 59, 159, 133, 86, 103, 171, 225, 300, 223, 291, 233, 325, 144, + 41, 164, 261, 206, 70, 213, 44, 339, 229, 154, 279, 338, 218, 348, 326, 288, 344, + 284, 329, 257, 345, 353, 199, 276, 359, 279, 349, 8, 363, 357, 3, 364, 360, 262, + 82, 322, 366, 183, 197, 244, 375, 165, 377, 79, 106, 222, 321, 299, 61, 264, 340, + 283, 66, 382, 47, 321, 162, 374, 385, 26, 242, 392, 276, 384, 296, 306, 77, 23, + 105, 381, 316, 248, 40, 405, 112, 410, 409, 141, 261, 149, 275, 267, 39, 80, 394, + 259, 310, 421, 68, 70, 283, 361, 398, 90, 308, 83, 220, 87, 228, 342, 70, 337, 437, + 284, 179, 440, 440, 308, 149, 421, 42, 86, 419, 33, 23, 390, 132, 389, 364, 433, + 52, 222, 342, 291, 423, 459, 191, 382, 118, 463, 164, 384, 358, 150, 455, 246, 432, + 456, 473, 161, 309, 117, 109, 341, 283, 441, 40, 470, 254, 484, 36, 482, 430, 488, + 101, 484, 344, 491, 425, 474, 357, 403, 481, 195, 454, 360, 501, 470, 170, 495, + 277, 377, 441, 399, 362, 19, 436, 482, 392, 514, 305, 495, 508, 170, 102, 276, 315, + 89, 380, 433, 460, 523, 527, 50, 422, 191, 162, 532, 525, 459, 368, 71, 492, 208, + 173, 38, 443, 417, 57, 460, 521, 199, 356, 428, 180, 550, 497, 487, 546, 148, 461, + 389, 535, 360, 420, 460, 552, 449, 290, 558, 360, 154, 424, 240, 45, 428, 392, 566, + 555, 559, 109, 5, 404, 576, 118, 206, 543, 349, 173, 513, 287, 586, 380, 311, 87, + 206, 358, 182, 526, 447, 404, 437, 271, 417, 3, 237, 439, 534, 602, 231, 595, 522, + 366, 544, 491, 520, 278, 604, 607, 595, 370, 604, 509, 602, 463, 163, 270, 615, + 123, 563, 566, 626, 611, 579, 629, 58, 436, 522, 338, 527, 451, 41, 549, 635, 431, + 640, 387, 613, 571, 12, 510, 527, 593, 648, 465, 484, 625, 325, 197, 651, 617, 429, + 218, 627, 490, 656, 640, 79, 203, 480, 655, 627, 648, 648, 665, 132, 291, 667, 620, + 296, 243, 574, 673, 609, 642, 680, 484, 669, 239, 461, 678, 422, 677, 227, 430, + 503, 468, 78, 503, 112, 563, 639, 477, 232, 649, 345, 679, 473, 448, 459, 577, 18, + 315, 692, 618, 534, 656, 709, 294, 400, 648, 242, 488, 706, 198, 404, 369, 304, + 699, 698, 394, 572, 493, 216, 725, 406, 723, 548, 580, 158, 450, 731, 735, 25, 694, + 426, 741, 256, 98, 407, 12, 558, 445, 566, 748, 596, 180, 663, 663, 170, 330, 636, + 686, 738, 656, 679, 89, 605, 484, 155, 599, 619, 629, 55, 683, 474, 316, 311, 474, + 50, 21, 599, 202, 711, 477, 719, 77, 782, 737, 688, 427, 712, 165, 212, 437, 716, + 718, 792, 486, 606, 751, 61, 708, 667, 677, 116, 17, 787, 591, 678, 494, 4, 675, + 148, 796, 747, 524, 434, 603, 674, 744, 715, 629, 72, 1, 748, 735, 542, 808, 688, + 197, 826, 387, 757, 810, 41, 366, 826, 688, 814, 483, 319, 210, 37, 803, 51, 671, + 396, 690, 268, 292, 823, 599, 258, 595, 748, 844, 610, 454, 689, 62, 796, 687, 853, + 465, 257, 404, 811, 367, 665, 724, 520, 319, 667, 353, 843, 761, 870, 694, 606, + 597, 98, 867, 275, 878, 407, 389, 811, 272, 745, 416, 46, 401, 875, 316, 808, 687, + 683, 589, 392, 589, 245, 759, 637, 889, 472, 746, 363, 337, 884, 726, 481, 827, + 764, 447, 409, 911, 69, 872, 660, 567, 752, 825, 320, 495, 447, 807, 454, 838, 870, + 66, 737, 805, 906, 350, 334, 849, 713, 277, 698, 930, 795, 930, 840, 670, 933, 707, + 873, 35, 731, 149, 667, 671, 208, 833, 110, 814, 113, 880, 506, 951, 255, 48, 956, + 816, 951, 138, 274, 528, 154, 592, 778, 906, 572, 621, 955, 751, 802, 964, 801, + 891, 410, 513, 969, 896, 971, 286, 647, 76, 582, 767, 983, 822, 262, 288, 79, 895, + 973, 891, 982, 965, 813, 721, 905, 771, 981, 954, 311, 68, 912, 671, 643, 1006, 16, + 259, 49, 1009, 1012, 828, 416, 1015, 878, 802, 213, 230, 567, 392, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 0; + let segment_n = 1; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 63, 227, 849, 503, 967, 726, 979, 1023, 1025, 514, 762, 161, 674, 961, 1033, 1035, + 440, 387, 3, 622, 1041, 1030, 17, 773, 934, 469, 197, 1004, 1048, 908, 357, 827, + 537, 950, 1000, 1049, 334, 219, 1006, 758, 1059, 137, 722, 462, 401, 236, 216, + 1045, 227, 1010, 1072, 1062, 755, 771, 922, 648, 26, 59, 1048, 1078, 626, 1034, + 157, 167, 552, 835, 1087, 936, 1028, 339, 442, 303, 207, 1033, 411, 348, 672, 954, + 888, 491, 815, 937, 733, 1079, 275, 427, 1103, 148, 571, 1078, 1069, 1113, 492, + 889, 962, 733, 1117, 921, 474, 277, 934, 555, 1035, 172, 1053, 804, 1079, 708, 206, + 1044, 1125, 454, 1086, 686, 408, 1136, 335, 1001, 200, 671, 341, 1115, 1016, 1043, + 1088, 629, 782, 600, 793, 684, 1056, 671, 1040, 4, 1156, 1037, 1145, 694, 607, 804, + 1125, 127, 829, 858, 580, 782, 661, 377, 1098, 946, 913, 90, 1070, 1099, 1064, 790, + 846, 675, 1042, 724, 738, 682, 1033, 994, 997, 1111, 378, 934, 771, 629, 1120, + 1193, 1177, 33, 1183, 691, 346, 1016, 374, 475, 110, 1185, 966, 730, 1118, 1004, + 237, 347, 803, 50, 790, 1136, 797, 146, 1214, 1162, 655, 1192, 1180, 1219, 1171, + 428, 252, 552, 1221, 1164, 1223, 834, 369, 696, 1197, 1187, 483, 1179, 1236, 982, + 890, 1182, 1238, 745, 1215, 756, 1188, 1244, 1151, 712, 718, 1205, 1167, 799, 1211, + 1253, 86, 1163, 1155, 1095, 151, 255, 1160, 765, 1080, 555, 963, 150, 1197, 1189, + 945, 1154, 1268, 840, 392, 172, 1191, 1108, 1271, 1059, 768, 813, 719, 964, 824, + 577, 1205, 1077, 307, 1199, 1287, 1066, 244, 1177, 38, 1004, 1281, 1273, 518, 1077, + 1263, 1225, 1043, 1243, 1199, 1237, 17, 368, 1021, 1245, 971, 1303, 849, 808, 1312, + 1313, 1161, 1185, 1154, 1215, 1201, 1037, 1269, 324, 649, 1092, 812, 983, 1305, + 782, 1050, 1270, 1108, 1327, 1311, 503, 1135, 1045, 1326, 748, 458, 139, 685, 1146, + 1338, 1326, 334, 224, 1345, 809, 847, 678, 569, 1316, 1335, 1353, 1114, 353, 1241, + 1123, 524, 950, 298, 1058, 631, 1260, 1161, 1315, 1037, 473, 1291, 946, 911, 1187, + 33, 1372, 643, 962, 120, 1172, 341, 566, 986, 6, 888, 1118, 1322, 1054, 22, 865, + 352, 1208, 1336, 1124, 1143, 1065, 1392, 1183, 1307, 1375, 617, 1351, 139, 1383, + 1018, 1392, 1402, 1145, 471, 962, 562, 1409, 1374, 1210, 1297, 361, 1085, 1415, + 1347, 794, 87, 1073, 759, 1241, 519, 1154, 1259, 1293, 1383, 551, 409, 1429, 587, + 64, 1285, 1314, 1305, 1371, 1161, 1196, 572, 1136, 1430, 1141, 926, 1439, 1005, + 1088, 354, 574, 1044, 1002, 1285, 707, 1264, 1114, 695, 1449, 1273, 693, 362, 846, + 397, 401, 1462, 1089, 1352, 1344, 979, 152, 1303, 985, 398, 1106, 1320, 916, 680, + 1475, 1353, 1314, 578, 479, 1045, 1350, 930, 34, 1484, 1028, 131, 64, 1255, 808, + 1437, 1168, 1051, 658, 732, 1035, 1346, 199, 640, 1323, 300, 433, 730, 1503, 560, + 422, 1371, 729, 1450, 1163, 267, 1496, 1078, 467, 76, 1473, 1498, 1115, 65, 979, + 114, 551, 1482, 1166, 1509, 1469, 1526, 1440, 961, 1002, 1530, 1050, 1295, 1161, + 1299, 1332, 1038, 1263, 420, 1490, 1495, 578, 1308, 1057, 1105, 620, 965, 1281, + 789, 1100, 820, 98, 1508, 942, 176, 939, 515, 51, 997, 849, 1294, 663, 325, 405, + 1417, 1544, 1308, 1325, 1492, 790, 975, 1101, 1339, 1470, 1570, 1486, 1567, 1397, + 2, 809, 499, 1580, 1578, 1461, 867, 830, 713, 968, 1574, 1587, 136, 1410, 1, 1288, + 1186, 1222, 1029, 1326, 1590, 4, 4, 1560, 280, 385, 1462, 54, 1406, 1156, 1035, + 1608, 1126, 145, 157, 708, 241, 889, 1426, 1514, 1569, 1607, 1064, 1259, 73, 1560, + 412, 802, 623, 1578, 1624, 1617, 1265, 1002, 1383, 1479, 1235, 1499, 1326, 1556, + 616, 1567, 1611, 1627, 1611, 141, 394, 384, 1473, 1639, 1113, 549, 1544, 226, 337, + 567, 628, 1332, 1612, 1634, 1654, 8, 1016, 1172, 508, 1521, 1308, 245, 545, 1593, + 1650, 927, 1668, 1402, 421, 1438, 1024, 444, 1061, 1579, 1496, 1272, 887, 1314, + 1187, 276, 574, 472, 1457, 529, 1669, 282, 691, 1553, 1688, 1471, 1694, 1690, 1594, + 1675, 1690, 1047, 1632, 1438, 1697, 1649, 59, 1677, 1443, 1420, 1471, 1675, 418, + 259, 1085, 788, 339, 64, 987, 5, 1421, 1636, 361, 1312, 1564, 1359, 74, 1666, 653, + 1714, 1584, 1192, 341, 1471, 907, 1591, 582, 775, 1412, 1674, 505, 385, 1301, 1505, + 1299, 985, 31, 1713, 762, 1747, 1705, 1685, 917, 636, 105, 1528, 513, 881, 1414, + 1645, 1602, 1313, 1537, 1746, 1738, 1453, 1625, 1750, 552, 411, 227, 1228, 1615, + 288, 1542, 1709, 1277, 1649, 1776, 1327, 1743, 1769, 863, 242, 1005, 1748, 972, + 1332, 46, 1758, 1740, 1582, 1789, 361, 678, 250, 576, 587, 1313, 703, 663, 443, + 1754, 1422, 1561, 1800, 1034, 452, 1493, 753, 1701, 1408, 145, 60, 692, 1804, 1716, + 1749, 1778, 1516, 1176, 1736, 1224, 1750, 368, 429, 1818, 1564, 1789, 711, 1451, + 1590, 1641, 1574, 1313, 1833, 1363, 1115, 1812, 1828, 1530, 1444, 928, 1695, 1492, + 1566, 289, 1705, 1745, 591, 1664, 517, 1072, 1474, 1327, 1853, 1049, 278, 1828, + 1857, 1828, 1543, 482, 1861, 1368, 1610, 1493, 1827, 1610, 975, 1678, 873, 1868, + 1765, 1420, 974, 1037, 1244, 1816, 1873, 1625, 108, 1217, 1208, 1360, 63, 1644, + 1368, 1860, 1624, 1748, 1661, 1677, 1871, 1594, 1859, 1790, 341, 1896, 1866, 285, + 905, 1576, 1575, 232, 1828, 428, 1806, 1531, 872, 1610, 1045, 1910, 769, 1897, + 1186, 1854, 1887, 1857, 1861, 980, 1594, 1635, 1892, 722, 1515, 1842, 1139, 1026, + 1383, 1920, 1558, 1108, 1831, 1867, 1206, 1390, 1848, 1358, 861, 209, 1909, 1883, + 445, 974, 477, 1937, 1391, 1187, 1726, 1253, 47, 1027, 1886, 1760, 1157, 1546, 545, + 877, 1192, 1843, 748, 1561, 1481, 318, 1207, 1905, 733, 1879, 784, 768, 1881, 1450, + 348, 1881, 949, 1511, 1670, 1966, 492, 1748, 1061, 1612, 1399, 348, 1575, 1736, + 639, 1986, 1978, 90, 1318, 1559, 1960, 1073, 1886, 733, 284, 268, 1972, 1969, 1912, + 1992, 60, 1457, 1769, 1335, 1345, 1999, 1843, 1925, 1475, 1850, 2004, 1077, 970, + 553, 1921, 1676, 1443, 1995, 1975, 660, 1142, 2008, 1453, 933, 1983, 1195, 1906, + 645, 627, 1525, 1752, 1490, 1040, 1873, 632, 383, 1590, 1924, 165, 1961, 1543, + 1719, 1171, 1881, 2031, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 0; + let segment_n = 2; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 1171, 1043, 2046, 209, 1914, 1872, 570, 1427, 931, 1485, 2024, 1760, 2049, 1955, + 1265, 1039, 1693, 578, 1665, 1457, 1513, 1283, 2023, 1956, 1744, 1659, 2032, 145, + 1887, 1633, 2040, 1456, 440, 2044, 2080, 1856, 947, 976, 2084, 2085, 1826, 1774, + 2015, 1858, 701, 830, 2065, 1609, 1680, 1502, 653, 554, 201, 1819, 2081, 1898, 561, + 287, 1942, 969, 1981, 1300, 177, 2021, 1968, 772, 475, 1996, 1533, 1846, 1933, + 2113, 2115, 2026, 2023, 838, 1863, 857, 1470, 2030, 1990, 1339, 1055, 2021, 511, + 1197, 1776, 1718, 209, 1759, 2054, 1010, 210, 2063, 1005, 1586, 2141, 2007, 1323, + 347, 19, 68, 1796, 1041, 2053, 912, 2144, 443, 1388, 2154, 2028, 2039, 1528, 2120, + 1314, 1868, 2159, 1655, 865, 1852, 939, 129, 2168, 797, 707, 338, 1918, 1965, 1541, + 1983, 1942, 1160, 2049, 2126, 1974, 2075, 1326, 1557, 1700, 1377, 1054, 1570, 1743, + 272, 1884, 1716, 2023, 549, 1984, 1510, 634, 2182, 901, 1773, 1428, 2187, 1703, + 1906, 2148, 1394, 790, 584, 331, 2174, 1573, 1347, 1555, 1867, 156, 2206, 912, + 1728, 1803, 1883, 1111, 2136, 2221, 2173, 216, 2206, 2215, 1979, 2217, 1439, 1403, + 1019, 2150, 1978, 1823, 1838, 1244, 1679, 1882, 1848, 1752, 2241, 53, 2097, 2212, + 736, 2230, 2111, 852, 1435, 1829, 2122, 59, 233, 221, 1739, 2212, 1647, 1663, 2204, + 1739, 1911, 143, 456, 2044, 1735, 1345, 2027, 2186, 69, 540, 971, 2022, 2050, 1291, + 1159, 71, 1582, 2096, 1370, 2171, 1631, 2260, 2210, 1905, 2057, 1708, 2195, 1333, + 2141, 1605, 2286, 2211, 2261, 1737, 1239, 2166, 1901, 1709, 2205, 2147, 1918, 2292, + 1851, 1339, 2154, 2144, 1027, 197, 1856, 1543, 919, 2076, 1613, 2012, 1100, 1862, + 2316, 1717, 1044, 1999, 2298, 2268, 2228, 1454, 1540, 2046, 1667, 1703, 945, 111, + 1982, 2324, 2271, 276, 1327, 602, 1538, 1946, 1976, 2244, 2136, 495, 2205, 2333, + 1846, 27, 2332, 1047, 2039, 2116, 1116, 1933, 593, 189, 2094, 74, 1020, 1362, 1069, + 2203, 1627, 2279, 2332, 468, 2177, 2364, 2086, 1078, 1594, 1539, 2285, 64, 511, + 1509, 2155, 323, 2202, 2235, 2350, 1590, 1624, 2320, 2243, 1261, 1460, 2128, 1523, + 494, 2186, 874, 749, 1586, 694, 2319, 1978, 1365, 2239, 1042, 2098, 70, 1940, 2343, + 2249, 2152, 2259, 1306, 436, 2294, 1027, 2235, 2391, 653, 1056, 2233, 1700, 1388, + 2346, 2391, 1215, 2395, 815, 650, 2118, 1831, 2392, 1157, 1297, 2192, 2337, 2265, + 2271, 1647, 960, 448, 1558, 1546, 289, 2130, 1831, 2283, 636, 2214, 2409, 2435, + 2242, 2209, 584, 1263, 1451, 1024, 1798, 2451, 1482, 124, 1506, 2455, 1119, 1933, + 2432, 1953, 2369, 2176, 1838, 77, 788, 1787, 1848, 1711, 2178, 156, 1449, 1395, + 2166, 2156, 2161, 1706, 1007, 1225, 2456, 2463, 2412, 1660, 2428, 1643, 1518, 2034, + 2421, 651, 1381, 2388, 1425, 2062, 849, 1994, 2495, 2013, 2100, 704, 830, 2499, + 2057, 1953, 2419, 214, 1358, 2135, 2498, 2362, 2450, 1182, 1733, 299, 2392, 2514, + 2213, 1901, 1301, 791, 1521, 586, 2521, 956, 2353, 1060, 811, 2163, 1398, 1111, + 1584, 2434, 1788, 350, 814, 659, 1248, 2244, 517, 2513, 2003, 2270, 1878, 463, + 2540, 2489, 2541, 776, 607, 1098, 2374, 2197, 2551, 1493, 1182, 364, 857, 2005, + 1803, 2099, 465, 2347, 681, 2254, 1172, 1411, 2443, 2562, 437, 2259, 1535, 2570, + 2203, 2557, 2568, 2560, 2152, 2324, 1339, 2396, 121, 1663, 1236, 1577, 2199, 2249, + 2581, 2092, 2573, 2586, 224, 2216, 1958, 779, 2586, 1972, 335, 2461, 2284, 1296, + 2162, 2579, 46, 2529, 1022, 2412, 1917, 1682, 1431, 59, 2592, 2468, 2472, 2384, + 812, 866, 250, 1290, 122, 2064, 1246, 2213, 2618, 1867, 1219, 2250, 1215, 1876, + 2368, 2503, 2349, 2251, 186, 2615, 1698, 2591, 2118, 391, 2610, 1786, 2272, 709, + 1776, 1466, 2488, 2480, 2462, 2375, 1893, 2637, 2606, 2215, 1787, 1472, 2626, 955, + 1964, 2144, 2213, 2645, 2515, 2642, 520, 2354, 967, 2360, 171, 2660, 1611, 2476, + 1221, 2290, 2662, 2654, 1093, 2016, 2535, 612, 2032, 1955, 1923, 2676, 1506, 443, + 2659, 970, 1344, 2677, 2680, 1686, 54, 2433, 2472, 2187, 2485, 1061, 2216, 2572, + 2437, 1379, 2185, 1732, 2111, 2498, 1279, 1467, 2611, 2068, 2138, 2024, 2243, 2241, + 2482, 907, 339, 693, 1879, 976, 2415, 2120, 530, 2412, 2693, 278, 783, 111, 486, + 1475, 2588, 2728, 2463, 1761, 2379, 2701, 2465, 2504, 2733, 1464, 797, 2114, 1956, + 2182, 1404, 1698, 2572, 1246, 2055, 1296, 729, 2143, 1161, 1502, 2140, 2079, 1862, + 1546, 2565, 2461, 2631, 1369, 2020, 2755, 2159, 2650, 1459, 2064, 1198, 2680, 2317, + 1491, 62, 2369, 547, 539, 2576, 1553, 2633, 1269, 1720, 2768, 1754, 2246, 2393, + 1421, 2775, 621, 5, 1070, 2749, 2484, 1639, 2097, 1077, 318, 2757, 2283, 242, 743, + 2558, 30, 2711, 1584, 462, 2800, 2802, 2756, 1353, 717, 2745, 733, 1277, 2691, + 1112, 1250, 1900, 1796, 2689, 2815, 2209, 526, 2778, 2224, 2808, 1510, 2799, 2743, + 1493, 1017, 464, 2504, 2799, 2205, 2670, 1549, 2497, 2568, 228, 2735, 784, 2591, + 2027, 2799, 2389, 956, 2725, 2815, 1983, 2647, 2671, 363, 2849, 2848, 578, 1952, + 1326, 2158, 2855, 1638, 817, 2846, 1035, 2406, 984, 2299, 1777, 2084, 958, 2386, + 2866, 975, 2866, 670, 939, 2865, 1445, 2811, 2501, 2865, 2734, 2393, 204, 2835, + 1991, 2868, 1001, 2881, 2495, 1318, 581, 292, 2879, 231, 2664, 1878, 2647, 2303, + 2753, 1605, 538, 2345, 2755, 2860, 2663, 2735, 189, 2570, 2087, 162, 72, 2446, + 1728, 1153, 2198, 1049, 2875, 745, 1313, 1114, 2692, 592, 820, 469, 872, 1207, + 2756, 150, 1494, 1912, 1753, 2567, 2477, 572, 2734, 817, 519, 2471, 962, 1247, 802, + 2931, 1552, 2845, 2801, 2891, 2550, 447, 2704, 879, 2909, 1159, 2739, 1542, 2190, + 1888, 2530, 2521, 2904, 1095, 2856, 434, 368, 285, 2810, 1488, 2962, 761, 1732, + 2410, 2934, 2856, 2376, 1431, 2683, 2902, 1533, 2851, 2337, 1746, 2944, 2701, 137, + 628, 2838, 2157, 2456, 2519, 2391, 559, 961, 2962, 2378, 2727, 2841, 2200, 2701, + 1966, 2210, 2996, 2654, 1583, 2875, 2890, 2923, 2135, 2743, 2672, 86, 2986, 2691, + 2984, 2699, 2869, 179, 2419, 1993, 1523, 1378, 2696, 2188, 1774, 2330, 2582, 1958, + 2541, 2903, 3004, 487, 793, 2820, 2438, 2131, 1846, 2751, 3007, 21, 2102, 2128, + 614, 794, 3034, 129, 2965, 2963, 2474, 2575, 2850, 2659, 3024, 2553, 1912, 3048, + 2991, 754, 1039, 1208, 2939, 1302, 1746, 2418, 1473, 3058, 2931, 3060, 2368, 37, + 3021, 1483, 2941, 2676, 422, 1775, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 0; + let segment_n = 3; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 1994, 1933, 3035, 2492, 2197, 748, 349, 140, 1121, 3079, 2168, 1970, 2197, 2830, + 1473, 2995, 827, 3087, 2246, 3046, 46, 3067, 1420, 1484, 3062, 2612, 2122, 2360, + 3011, 1097, 1455, 2069, 2660, 3103, 574, 1665, 3086, 2750, 3101, 1391, 2770, 3013, + 1691, 2929, 2411, 1272, 1296, 811, 2499, 3064, 2675, 2926, 212, 399, 1439, 2894, + 2697, 1881, 2007, 2706, 3130, 3046, 1253, 1722, 2461, 3023, 3128, 2093, 2757, 897, + 3057, 1176, 2290, 1336, 3108, 3145, 1445, 2613, 3133, 2501, 1305, 1654, 851, 2608, + 2289, 1673, 1057, 1657, 140, 2399, 419, 2186, 3033, 3161, 3115, 2430, 2574, 1914, + 2974, 3134, 2458, 3073, 2879, 226, 2871, 2926, 1917, 3015, 3147, 3030, 2852, 1399, + 878, 1885, 3182, 217, 3110, 1513, 1933, 1628, 3169, 220, 1147, 3116, 3010, 438, + 2263, 2382, 2443, 440, 2907, 404, 2897, 68, 2020, 2815, 3096, 3169, 3144, 1401, + 3029, 1072, 33, 663, 890, 2932, 3200, 3096, 628, 1371, 3096, 1851, 1144, 365, 2097, + 2344, 974, 978, 2558, 135, 3055, 446, 1128, 2599, 1680, 255, 2191, 1603, 1025, + 1842, 2017, 517, 3210, 3170, 966, 790, 811, 2253, 2222, 1163, 1933, 2902, 762, 546, + 3219, 198, 1729, 1650, 3208, 1435, 2689, 1729, 3113, 2222, 3206, 2918, 584, 829, + 2437, 3113, 2805, 2742, 3172, 2823, 356, 716, 1895, 2369, 2268, 3255, 2981, 2733, + 903, 1945, 1593, 553, 32, 3105, 1759, 238, 1154, 2399, 2691, 480, 2571, 3285, 3211, + 959, 1936, 2767, 2697, 375, 2833, 3264, 2837, 3305, 2938, 3284, 1696, 801, 485, + 1911, 3282, 3221, 1002, 876, 18, 1589, 2416, 3304, 160, 3097, 1387, 3074, 3322, + 3012, 3209, 2100, 1419, 2621, 2667, 856, 1981, 1456, 1902, 1690, 1318, 2344, 620, + 1141, 2449, 3063, 2904, 2311, 2441, 2419, 1166, 1786, 3338, 563, 3266, 2275, 2559, + 3352, 3352, 2638, 1635, 1671, 2322, 2914, 3333, 2832, 2522, 3050, 2184, 3045, 1692, + 474, 293, 803, 3271, 3192, 1902, 3363, 3094, 221, 2809, 3030, 791, 2951, 1791, + 3242, 3202, 2124, 1579, 1782, 2476, 1695, 2817, 2673, 1016, 1265, 2231, 1911, 3335, + 228, 303, 1179, 1439, 1720, 3400, 2533, 2439, 3355, 2596, 3061, 119, 318, 2476, + 1347, 3194, 2759, 1670, 2601, 3399, 1707, 3046, 2575, 2810, 2234, 2321, 1292, 1207, + 3409, 2350, 3237, 2156, 3385, 3286, 2872, 2743, 1195, 1246, 972, 1649, 996, 3093, + 2833, 3340, 3171, 1957, 3304, 908, 3431, 819, 322, 3212, 3077, 1972, 2919, 2679, + 2408, 2521, 2940, 2055, 3429, 2653, 2908, 1868, 2689, 3100, 3389, 2094, 1810, 3278, + 2796, 2819, 3052, 2430, 2080, 3464, 3150, 2448, 3084, 60, 2216, 1493, 3163, 3335, + 2886, 914, 1603, 3429, 3468, 3226, 2832, 2990, 3471, 483, 2746, 3181, 943, 547, + 1556, 778, 2143, 2401, 2108, 586, 3425, 320, 2811, 3501, 3326, 1887, 3444, 876, + 1612, 664, 2896, 907, 3438, 2041, 1891, 954, 3231, 640, 734, 3458, 2878, 3408, 621, + 3011, 2434, 770, 2784, 760, 3226, 3505, 251, 3070, 2046, 95, 513, 2968, 3523, 3353, + 1673, 2599, 2740, 495, 3515, 1649, 3542, 3105, 3535, 2339, 2537, 2100, 732, 3512, + 2167, 3480, 3016, 1366, 868, 3386, 711, 1839, 3403, 3439, 2795, 3535, 1514, 1629, + 2180, 3228, 2149, 3422, 3485, 2052, 3482, 320, 3069, 1410, 1403, 1091, 3257, 3173, + 1376, 2807, 2799, 208, 1410, 1003, 1733, 695, 3398, 619, 3549, 1557, 3422, 190, + 1925, 2222, 2676, 3523, 3086, 2714, 3131, 1158, 993, 3381, 1871, 193, 3442, 671, + 3583, 2495, 3288, 3398, 2887, 3159, 1794, 2334, 3606, 3573, 2011, 1480, 2701, 2231, + 3613, 3588, 2949, 1926, 3005, 3621, 2029, 415, 2734, 43, 1361, 3358, 3574, 3398, + 3247, 3378, 632, 2396, 1477, 3174, 222, 1372, 1804, 3267, 1834, 2155, 360, 3350, + 2856, 2645, 3089, 23, 3508, 3648, 999, 3100, 2664, 3467, 1482, 3534, 2781, 3150, + 2638, 3191, 1524, 2977, 3125, 851, 1556, 2222, 3669, 1880, 722, 953, 1260, 3638, + 3533, 3430, 3478, 2298, 3458, 2906, 3654, 2653, 2603, 2776, 1028, 3002, 328, 2747, + 2779, 1072, 3184, 3554, 3374, 3156, 1384, 3100, 3346, 1729, 1349, 3615, 3697, 3535, + 2686, 3610, 2074, 230, 37, 1497, 3489, 3384, 1671, 2232, 1013, 1453, 3649, 1058, + 3243, 3550, 177, 96, 604, 821, 633, 3287, 3320, 2220, 413, 3709, 3326, 2402, 3294, + 2790, 2368, 1661, 3308, 2701, 3725, 3330, 1705, 2927, 1502, 1837, 1100, 3746, 654, + 3319, 218, 1922, 3680, 1232, 3597, 860, 2108, 2154, 1082, 1995, 3753, 3462, 2466, + 318, 1409, 3764, 2731, 3603, 2844, 3757, 2056, 2602, 3741, 1681, 800, 3723, 2870, + 3288, 2446, 2389, 3004, 3604, 3682, 3142, 3039, 3630, 1750, 1344, 3730, 1941, 2803, + 2550, 2626, 3777, 762, 497, 3359, 2122, 3429, 600, 826, 3588, 3500, 1739, 3711, + 3676, 3399, 3402, 3466, 2549, 2556, 2734, 1176, 3757, 470, 1921, 3746, 3799, 563, + 2833, 2446, 1317, 3522, 227, 3672, 3260, 3470, 3410, 1927, 2243, 3316, 2320, 635, + 2812, 1504, 3785, 3228, 304, 2769, 311, 3367, 3392, 3726, 3816, 2705, 2129, 2989, + 335, 3833, 405, 2941, 2889, 3733, 3023, 2587, 3210, 3233, 3673, 2309, 2814, 3251, + 3150, 3496, 3169, 2995, 2407, 3712, 360, 3826, 3247, 2686, 2504, 3338, 3642, 2022, + 3774, 3508, 3792, 2212, 3797, 3778, 3715, 2145, 2512, 3171, 1064, 2584, 3869, 3585, + 3851, 3735, 738, 3848, 3254, 3886, 3672, 1251, 3138, 3453, 3143, 3619, 908, 2126, + 1793, 2885, 3848, 1124, 371, 2480, 2259, 847, 2984, 1946, 2711, 895, 3895, 3913, + 606, 3555, 3842, 3282, 2123, 3518, 3750, 2224, 2785, 3325, 2308, 3635, 886, 3620, + 3155, 3829, 2319, 3874, 2562, 2655, 3836, 46, 314, 3848, 3751, 2409, 2931, 1999, + 2616, 3702, 109, 84, 1304, 1813, 3010, 3349, 3821, 3408, 3952, 3328, 3799, 2967, + 3035, 3648, 2474, 1687, 1279, 3947, 2466, 1357, 3900, 3549, 1002, 2750, 2986, 3379, + 1172, 3133, 534, 3210, 3414, 2426, 3883, 3862, 3752, 2932, 1354, 3897, 1874, 2831, + 2539, 2703, 758, 3975, 3144, 2367, 1233, 3797, 1662, 593, 3642, 2411, 2952, 337, + 2414, 3672, 3025, 2729, 1912, 2480, 3965, 3085, 3863, 1709, 3944, 1021, 542, 1782, + 1727, 3879, 2812, 3276, 4018, 3267, 3672, 1016, 3486, 3657, 3735, 516, 3565, 3432, + 2371, 2634, 2072, 551, 2391, 2460, 3462, 787, 4015, 1804, 2544, 3707, 3879, 2939, + 3612, 861, 2213, 3021, 3818, 3902, 3944, 4045, 3272, 1861, 747, 3224, 4054, 3263, + 1623, 2018, 1460, 2158, 1170, 88, 1537, 3542, 1722, 4012, 1630, 3058, 2203, 2610, + 4059, 3730, 2042, 1090, 199, 1907, 3848, 3983, 3810, 3603, 1068, 3985, 2454, 3950, + 1163, 1752, 3677, 1553, 2352, 3650, 1758, 1756, 3893, 3566, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 1; + let segment_n = 0; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 3988, 3490, 1643, 1572, 2, 2045, 1188, 3185, 1866, 1635, 3419, 1196, 3779, 2875, + 2786, 3906, 3086, 2300, 3, 3715, 3942, 3921, 4060, 3970, 22, 4095, 23, 11, 2880, 1, + 3617, 3244, 3935, 3222, 2814, 1706, 3349, 3372, 34, 1984, 1488, 3332, 3582, 3950, + 2803, 3986, 2228, 3844, 1767, 3772, 43, 3307, 3131, 3811, 3437, 3530, 2226, 4093, + 3934, 3401, 2948, 59, 4067, 3888, 32, 1302, 3233, 3650, 2178, 2376, 3271, 4094, + 2467, 2975, 3512, 3452, 3074, 3034, 57, 2174, 2062, 4072, 3086, 3175, 3988, 1343, + 48, 3438, 3295, 87, 1844, 4004, 3389, 46, 2672, 3108, 3065, 2149, 3720, 4057, 84, + 2027, 2275, 2567, 1600, 2653, 1444, 2508, 63, 3744, 1255, 3175, 49, 2728, 3522, 34, + 3046, 3728, 115, 1199, 2500, 3337, 96, 108, 28, 3502, 1317, 3739, 3318, 112, 4079, + 2308, 2026, 1040, 121, 2328, 3084, 56, 4088, 2008, 2194, 116, 1140, 2896, 135, 132, + 144, 2501, 143, 147, 3817, 2696, 2858, 3652, 3507, 19, 2382, 3342, 3957, 2451, + 3434, 1282, 1549, 4005, 3990, 3881, 2381, 3239, 2745, 2168, 2493, 4073, 3910, 109, + 110, 2368, 3030, 3839, 3248, 3830, 3761, 46, 3449, 70, 156, 2833, 2515, 3655, 2023, + 39, 3344, 180, 2053, 1684, 2909, 2463, 3773, 3520, 3941, 197, 3686, 4025, 192, + 2798, 202, 3435, 2350, 3632, 3295, 1999, 153, 2793, 3683, 209, 152, 1262, 2850, + 1430, 4028, 3260, 2211, 1700, 3592, 1209, 3571, 2943, 90, 3266, 3022, 3878, 218, + 1718, 8, 1771, 229, 229, 2940, 2986, 2155, 2464, 2345, 102, 1974, 3434, 1860, 3667, + 119, 245, 2392, 152, 1126, 3593, 3527, 3949, 2743, 2701, 3953, 3002, 221, 3610, + 2928, 1918, 1094, 261, 262, 3229, 3836, 24, 3257, 3270, 1076, 5, 2958, 3003, 4039, + 148, 2097, 2706, 3959, 3262, 3022, 3132, 56, 6, 1700, 162, 2764, 3144, 1230, 1445, + 1070, 3603, 4004, 3432, 282, 1736, 2961, 1993, 3222, 2212, 3370, 291, 268, 1639, + 3939, 2905, 1712, 1069, 1272, 3834, 2070, 286, 3625, 279, 3301, 3454, 162, 305, + 227, 302, 3646, 313, 3865, 1087, 2241, 1663, 4019, 1606, 326, 2658, 3750, 2135, + 328, 235, 2500, 2800, 279, 2614, 2567, 58, 37, 4069, 47, 3206, 342, 188, 2918, + 3825, 3460, 1495, 3265, 4031, 2745, 2380, 165, 278, 3606, 1443, 3855, 315, 2342, + 359, 303, 3392, 362, 3452, 2982, 3839, 1608, 1459, 2650, 368, 1814, 2275, 3642, + 2952, 369, 3927, 3159, 1622, 360, 334, 3930, 3472, 138, 3185, 374, 222, 3100, 1798, + 2777, 1706, 2917, 2649, 2096, 266, 394, 1427, 3497, 1292, 2206, 392, 2694, 1707, + 3555, 3896, 3302, 1105, 406, 406, 2399, 2411, 3565, 3063, 3925, 3997, 3725, 1447, + 318, 3664, 416, 2251, 131, 2895, 2403, 1350, 1749, 16, 426, 4019, 3332, 4018, 411, + 280, 2228, 3016, 3934, 435, 1872, 3959, 2684, 342, 1162, 2587, 3788, 4026, 353, + 400, 339, 2291, 413, 4077, 184, 4033, 3671, 346, 4048, 1047, 3619, 166, 3494, 2644, + 279, 2239, 456, 442, 322, 3818, 161, 1896, 3253, 438, 3402, 3643, 3521, 3839, 2954, + 203, 3217, 1807, 452, 3231, 4003, 2726, 181, 3880, 3289, 1694, 446, 474, 427, 66, + 1619, 2430, 3377, 3705, 2047, 472, 369, 120, 494, 2756, 2091, 180, 10, 1508, 1795, + 3633, 144, 337, 4051, 502, 3416, 504, 436, 1117, 3587, 3623, 2741, 3494, 502, 2813, + 115, 3231, 2974, 2351, 2250, 4063, 515, 502, 3553, 199, 1882, 3927, 2514, 3087, + 2321, 450, 92, 1073, 2338, 2016, 2364, 1586, 1697, 1759, 1708, 3172, 1211, 407, + 163, 3202, 501, 1832, 3385, 545, 4057, 215, 65, 1148, 3082, 3788, 500, 310, 2162, + 446, 1207, 384, 2331, 3702, 3438, 2386, 2511, 2751, 2311, 4016, 4038, 565, 2123, + 4005, 3441, 413, 2705, 349, 570, 2292, 568, 1324, 1481, 3874, 550, 545, 582, 3371, + 215, 592, 3841, 2807, 3537, 524, 1897, 1714, 3159, 596, 364, 1889, 3933, 457, 1317, + 2543, 1213, 2658, 181, 444, 2997, 3926, 1768, 1966, 1384, 3599, 3968, 433, 304, + 2624, 3085, 606, 3968, 1620, 114, 244, 441, 2426, 34, 2765, 2370, 4076, 3540, 438, + 602, 2860, 1727, 3750, 3888, 3848, 1457, 1851, 3201, 3435, 1397, 240, 3450, 4032, + 210, 2774, 3939, 650, 437, 315, 1374, 2614, 181, 1900, 2323, 2730, 3691, 2968, 486, + 636, 2549, 2433, 204, 268, 139, 2891, 572, 659, 3636, 568, 3588, 673, 665, 571, + 1273, 656, 1522, 3774, 433, 1740, 1225, 3837, 3810, 203, 2764, 466, 2456, 548, + 3811, 658, 647, 553, 3606, 548, 673, 3231, 253, 135, 16, 156, 2521, 3477, 108, + 2448, 670, 1430, 346, 616, 3352, 1418, 248, 2283, 717, 3306, 2386, 480, 1873, 2910, + 724, 2143, 557, 3569, 386, 2504, 728, 2769, 727, 2801, 501, 90, 680, 3921, 1721, + 711, 560, 1235, 52, 1741, 2917, 1716, 585, 744, 604, 73, 571, 338, 2417, 127, 1412, + 4086, 120, 686, 2937, 1667, 2503, 491, 754, 3941, 2376, 762, 737, 259, 528, 3686, + 3771, 1717, 761, 3968, 2662, 40, 1719, 775, 665, 4008, 706, 1365, 3115, 89, 2193, + 1136, 2328, 471, 2329, 638, 590, 66, 3378, 312, 3487, 324, 645, 639, 3706, 3909, + 4036, 1542, 692, 2490, 3814, 800, 15, 2687, 4077, 1627, 786, 568, 593, 3966, 3956, + 638, 2024, 733, 2862, 2482, 1728, 95, 613, 158, 793, 568, 3619, 55, 2964, 3020, + 794, 2147, 460, 3180, 1459, 309, 2983, 443, 3010, 3695, 2593, 790, 807, 1633, 843, + 361, 3141, 324, 499, 309, 43, 3856, 2354, 1853, 591, 1151, 687, 582, 1525, 1594, + 2164, 265, 421, 2592, 2636, 2356, 361, 834, 35, 2267, 3114, 3425, 1689, 2024, 857, + 1159, 3108, 195, 3849, 2516, 181, 202, 874, 2371, 774, 1535, 711, 794, 2686, 2642, + 3781, 2184, 1333, 2807, 524, 2409, 4083, 2988, 887, 864, 70, 3995, 3267, 1877, + 3963, 3247, 1600, 596, 307, 2273, 1344, 910, 475, 736, 2561, 304, 2636, 915, 2689, + 654, 1941, 1125, 2766, 461, 2247, 916, 3780, 3677, 454, 877, 3423, 842, 2621, 3791, + 50, 380, 3101, 844, 2594, 1216, 1812, 767, 888, 3541, 690, 233, 3272, 1511, 851, + 161, 767, 3583, 755, 1448, 1633, 907, 3078, 3961, 2910, 955, 3204, 1975, 1842, 817, + 2869, 1668, 779, 3339, 552, 969, 1058, 766, 955, 1722, 4014, 519, 2975, 1459, 874, + 2952, 947, 979, 1933, 2478, 665, 985, 982, 815, 21, 1179, 3217, 1056, 129, 815, + 1508, 739, 3801, 982, 768, 4038, 1788, 2571, 999, 2494, 3683, 1257, 1003, 751, + 4079, 1009, 184, 91, 3936, 1790, 3997, 719, 1012, 3540, 3910, 848, 980, 726, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 1; + let segment_n = 1; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 3423, 3780, 3751, 584, 2089, 3394, 594, 952, 3177, 3537, 2536, 2720, 3812, 1004, + 795, 353, 487, 27, 458, 566, 404, 3545, 945, 998, 3517, 1047, 4008, 448, 515, 999, + 1048, 939, 277, 2497, 89, 2786, 989, 420, 3571, 384, 740, 331, 2268, 2791, 843, + 518, 2447, 3880, 918, 3041, 3534, 385, 134, 3210, 2690, 3631, 3050, 839, 358, 1062, + 2949, 910, 537, 874, 2462, 3775, 842, 1083, 1063, 3225, 3141, 944, 971, 2956, 4004, + 1030, 178, 850, 2505, 3374, 474, 2961, 20, 3616, 3197, 1028, 2236, 290, 851, 561, + 934, 914, 260, 235, 1078, 931, 94, 2329, 593, 717, 2290, 869, 555, 368, 644, 3422, + 822, 3169, 1105, 21, 2107, 1109, 803, 1123, 1135, 184, 4070, 661, 3376, 853, 2888, + 3867, 1072, 1128, 965, 2138, 1119, 186, 405, 2561, 3857, 481, 2208, 287, 1137, + 2389, 132, 646, 627, 1128, 4051, 1030, 2679, 123, 63, 1120, 3701, 1135, 1166, 4029, + 2725, 1173, 2213, 2216, 465, 986, 303, 983, 846, 1069, 1096, 1172, 324, 1071, 718, + 486, 1110, 1166, 1063, 276, 1081, 3791, 2574, 180, 3812, 825, 2481, 586, 2292, + 2777, 2725, 3779, 748, 92, 1193, 1007, 3302, 568, 2694, 255, 993, 897, 3676, 226, + 1104, 2778, 2121, 1201, 1064, 2908, 1195, 1223, 1092, 3153, 3966, 1227, 3241, 1228, + 3771, 1163, 1232, 5, 989, 3734, 3190, 1138, 1101, 3105, 1118, 3935, 1122, 787, + 2214, 2745, 913, 832, 769, 651, 3543, 3967, 406, 2304, 4031, 961, 3660, 1256, 335, + 2970, 2273, 3860, 3150, 1180, 2498, 974, 966, 3133, 652, 336, 735, 439, 3049, 1273, + 3191, 3631, 875, 1272, 1277, 3960, 1232, 2577, 637, 765, 2972, 737, 3569, 991, + 2892, 3886, 2767, 3809, 834, 1278, 630, 1173, 1090, 1152, 3560, 426, 1189, 1266, + 3572, 834, 956, 953, 1303, 475, 3508, 1293, 3134, 816, 2764, 2537, 1314, 3700, + 1261, 1317, 1176, 460, 978, 1003, 1282, 1323, 3823, 651, 674, 957, 2390, 3299, 857, + 4075, 3598, 1097, 1318, 1281, 3369, 4002, 1048, 1223, 1154, 1201, 591, 589, 1280, + 345, 353, 3883, 381, 348, 3228, 969, 2067, 2092, 2454, 2600, 147, 61, 608, 1134, + 356, 945, 1230, 475, 1075, 793, 2229, 908, 3184, 3554, 334, 1324, 2059, 1373, 1163, + 2796, 1274, 2943, 1377, 3114, 3960, 493, 3433, 3792, 1107, 3051, 2727, 3814, 1139, + 1150, 2592, 1016, 1013, 2566, 2993, 1367, 3031, 919, 664, 1344, 3670, 3018, 1382, + 348, 1080, 3092, 704, 924, 1370, 289, 4084, 1219, 487, 3658, 3809, 2875, 1096, + 1276, 211, 1227, 3640, 1305, 3344, 708, 2950, 1133, 563, 339, 1355, 1429, 704, + 3219, 1405, 1408, 3986, 1047, 189, 1198, 798, 1408, 430, 301, 1292, 1373, 3113, + 2234, 1396, 707, 271, 1218, 375, 1278, 596, 788, 477, 867, 2050, 1135, 1350, 1248, + 3662, 2118, 3123, 2568, 1250, 304, 461, 3974, 1095, 2358, 307, 2605, 1155, 1042, + 1310, 439, 1441, 3956, 3397, 2696, 377, 1159, 2307, 97, 461, 1141, 57, 660, 2627, + 355, 2825, 911, 1271, 3072, 2283, 165, 1445, 1497, 467, 1477, 767, 3004, 380, 3583, + 1008, 3091, 138, 3099, 501, 998, 3858, 1269, 3670, 1436, 898, 1272, 3119, 611, + 2150, 2508, 1294, 32, 1516, 3579, 1332, 1524, 2509, 745, 678, 878, 1513, 2944, + 1302, 1259, 3425, 1509, 1534, 1537, 1151, 3462, 867, 889, 2804, 859, 4019, 1041, + 1008, 1066, 1225, 2697, 1529, 3659, 905, 1025, 22, 2701, 1172, 2802, 3831, 211, + 3590, 288, 993, 288, 2970, 1425, 1295, 3629, 849, 1116, 1403, 1248, 1572, 1521, 55, + 1570, 1234, 1269, 1560, 2831, 3335, 1336, 2891, 191, 73, 807, 619, 1339, 1283, + 2525, 1587, 3490, 1413, 692, 734, 1229, 1541, 3022, 1395, 1482, 836, 2965, 1030, + 4039, 1586, 2197, 661, 1512, 3183, 2061, 2611, 4078, 1572, 2220, 1614, 383, 1488, + 1442, 1618, 3842, 617, 716, 3901, 1623, 2467, 1230, 3782, 197, 2217, 264, 2443, + 1217, 950, 2971, 713, 1054, 3040, 1060, 299, 4040, 1472, 1439, 1387, 3819, 724, + 524, 2224, 1625, 1530, 2646, 1506, 782, 30, 2640, 1385, 1651, 2720, 433, 1194, + 1573, 1423, 1661, 487, 1284, 1598, 1382, 1273, 3468, 1015, 2545, 3891, 1563, 1655, + 836, 1298, 1066, 2554, 336, 3767, 1212, 3388, 3602, 884, 698, 2443, 1350, 194, + 1030, 846, 2706, 1590, 51, 745, 1528, 1466, 1069, 569, 2592, 1349, 1321, 4042, + 1089, 3904, 1492, 3710, 3268, 2899, 504, 1513, 3313, 3066, 3315, 1066, 1441, 1374, + 831, 2758, 776, 1711, 1546, 1657, 1182, 1699, 200, 1665, 4007, 734, 1605, 1725, + 1480, 8, 1544, 3834, 1717, 1697, 2960, 1654, 1420, 987, 89, 1352, 380, 2300, 1589, + 193, 3457, 1536, 1710, 1260, 3183, 1408, 1205, 38, 1726, 465, 711, 1181, 303, 1703, + 3837, 1343, 3576, 2656, 3250, 1001, 3675, 1353, 2677, 156, 4062, 99, 693, 2337, + 271, 3933, 2452, 183, 2611, 3123, 1746, 727, 1146, 554, 2827, 1784, 3763, 3486, + 520, 3369, 676, 758, 3872, 1261, 3440, 1268, 710, 2928, 4016, 1572, 1425, 2108, + 1333, 1632, 2949, 2341, 648, 1777, 3100, 1719, 1491, 654, 1634, 887, 517, 1789, + 1179, 1780, 131, 1788, 1278, 1820, 3597, 3244, 3527, 701, 1460, 3122, 4014, 687, + 3583, 1824, 1445, 1479, 1825, 1609, 1200, 3708, 2330, 3749, 2597, 487, 1421, 1405, + 2865, 1637, 230, 1086, 2748, 899, 1693, 1815, 1840, 4084, 1837, 1804, 2474, 1802, + 1192, 3934, 220, 3029, 1732, 461, 1828, 486, 2251, 922, 1867, 1353, 476, 503, 339, + 1808, 1823, 874, 2751, 1866, 3880, 1792, 37, 3212, 1309, 1479, 146, 3790, 290, + 1882, 2349, 1884, 1870, 1889, 1872, 1105, 1206, 1525, 2595, 687, 1261, 1826, 1740, + 1791, 1851, 3759, 3588, 1389, 1664, 1783, 26, 1626, 2808, 1840, 3685, 746, 1265, + 3869, 2599, 1495, 310, 1307, 1611, 1390, 234, 2423, 1912, 3334, 1361, 4074, 45, + 2171, 1891, 1726, 804, 1506, 3422, 1678, 1720, 1909, 1779, 3583, 718, 1551, 1800, + 1662, 1733, 1571, 1631, 1175, 1492, 1760, 2327, 2943, 1721, 3728, 1597, 1034, 991, + 153, 3318, 238, 807, 1270, 2344, 1846, 3776, 781, 332, 1622, 344, 3123, 246, 2578, + 185, 35, 1953, 1727, 2469, 2365, 757, 1960, 1257, 4041, 2354, 944, 362, 1848, 160, + 2763, 2290, 2289, 1866, 1805, 1615, 1929, 3689, 1658, 3432, 399, 3833, 246, 2218, + 1694, 3069, 1447, 2312, 1997, 1423, 1484, 1795, 2003, 303, 1688, 452, 631, 1006, + 3922, 3495, 3408, 3505, 1469, 3679, 1390, 185, 1848, 1999, 3947, 835, 1703, 1068, + 2140, 2319, 13, 3447, 3743, 3946, 345, 237, 1944, 929, 1616, 3037, 1789, 1760, + 2035, 2712, 3411, 1658, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 1; + let segment_n = 2; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 1513, 2045, 1126, 245, 3943, 2044, 513, 1861, 1947, 1963, 2051, 1644, 1154, 1569, + 1920, 1869, 2013, 1470, 2044, 406, 1797, 2023, 2060, 1863, 1686, 3886, 1888, 1995, + 428, 940, 3907, 2071, 1058, 3602, 349, 863, 1968, 1221, 557, 1582, 944, 1583, 2088, + 1142, 853, 107, 2057, 1991, 3307, 1401, 451, 1164, 889, 1924, 1462, 2081, 3412, + 2103, 506, 803, 1961, 4062, 185, 1296, 1534, 16, 2100, 1768, 1941, 277, 3997, 1009, + 387, 1785, 1396, 1628, 2113, 3422, 2122, 1218, 615, 2127, 1743, 2095, 948, 3469, + 647, 543, 1923, 135, 1474, 1126, 2124, 924, 2140, 2115, 1123, 482, 1441, 1478, + 1546, 4076, 1818, 2020, 1720, 1933, 1841, 2153, 2154, 1811, 1370, 2066, 1362, 2031, + 2152, 2095, 1788, 2163, 2080, 2077, 2119, 1220, 859, 1921, 1940, 1830, 1761, 1347, + 645, 3286, 1196, 976, 1664, 2098, 1600, 1185, 1811, 1718, 1581, 1079, 2101, 1820, + 3408, 1714, 1979, 1774, 3723, 2180, 764, 706, 1467, 2003, 428, 575, 2070, 1566, + 1824, 295, 1333, 2185, 877, 527, 889, 1473, 3166, 2064, 2190, 1681, 1529, 3957, + 2214, 1479, 294, 4051, 1063, 1418, 2112, 3085, 768, 1651, 3359, 205, 1911, 2026, + 622, 325, 1877, 2172, 2195, 3758, 1028, 1937, 3589, 2088, 1583, 2172, 3657, 1075, + 2229, 701, 1655, 2247, 60, 3949, 1936, 1145, 380, 2082, 3785, 3366, 1363, 3509, + 989, 2185, 3289, 355, 2067, 2248, 3467, 2237, 1156, 2264, 2044, 2042, 2016, 362, + 1417, 2035, 2092, 1935, 572, 190, 3761, 1993, 1514, 315, 1406, 2282, 2216, 2192, + 3292, 1234, 442, 2257, 2219, 1875, 3399, 4092, 3126, 3610, 682, 1565, 2077, 2180, + 1416, 1854, 2293, 2288, 2232, 2001, 104, 2262, 887, 1954, 2281, 3947, 244, 1339, + 1474, 571, 2300, 287, 1803, 1868, 492, 1943, 4077, 3227, 511, 2206, 3179, 3779, + 3668, 1438, 1246, 1194, 1953, 1937, 2333, 1797, 2316, 2011, 1234, 3784, 2210, 1814, + 621, 1047, 3893, 2036, 345, 3745, 819, 2326, 2338, 889, 1928, 935, 1957, 3157, 569, + 728, 2219, 3279, 1156, 1934, 324, 635, 2131, 2314, 3740, 2348, 1824, 2008, 645, + 2269, 2354, 3085, 1653, 3497, 585, 701, 3604, 3798, 1801, 3229, 533, 4, 1676, 1781, + 4088, 1897, 1803, 2316, 2150, 2045, 1951, 1958, 2079, 849, 3774, 1300, 2354, 2134, + 1313, 2227, 2101, 2198, 116, 2400, 110, 2143, 764, 468, 2042, 3435, 2374, 2236, + 1323, 956, 1738, 1775, 10, 2064, 1931, 2267, 1383, 2291, 3468, 3500, 3210, 249, + 2428, 1463, 1847, 2426, 1754, 2324, 1683, 1462, 2361, 1508, 1870, 660, 2440, 2363, + 1455, 1002, 2430, 3263, 876, 3896, 3698, 1960, 2417, 178, 1203, 3587, 883, 2090, + 2430, 2252, 285, 3825, 2383, 2395, 2389, 3183, 2199, 381, 262, 2466, 2463, 167, + 1462, 1741, 1183, 1357, 837, 2458, 2267, 3893, 2453, 631, 2427, 1576, 1366, 1468, + 534, 1315, 2317, 3613, 2466, 3995, 1715, 3409, 1110, 3513, 2191, 2158, 1674, 2429, + 2007, 1926, 2421, 2490, 3496, 1740, 2483, 34, 1103, 1845, 1313, 3405, 3538, 2073, + 2512, 2299, 279, 2389, 2303, 1221, 352, 2519, 2454, 3118, 2479, 2519, 1664, 2205, + 524, 2009, 3915, 2485, 2332, 593, 2327, 2363, 3715, 2099, 2507, 1092, 1994, 2263, + 2341, 2370, 1126, 1628, 2535, 252, 773, 3748, 2547, 711, 2025, 1273, 323, 3187, + 1140, 620, 2517, 1221, 3710, 2058, 3398, 2357, 2119, 1424, 3824, 875, 741, 1247, + 3244, 2568, 568, 2277, 2570, 905, 2255, 124, 1658, 1680, 1060, 3235, 3820, 308, + 932, 1301, 343, 2554, 361, 2358, 2559, 2589, 2416, 2591, 2553, 1777, 2470, 1277, + 2589, 1888, 2531, 263, 1292, 1961, 2079, 2597, 1948, 2603, 3547, 193, 1748, 2545, + 2553, 1079, 1147, 3603, 592, 4033, 18, 2467, 1665, 2554, 2582, 2528, 1789, 1964, + 3765, 1414, 470, 2342, 1653, 2572, 1128, 3388, 2542, 723, 903, 2594, 2122, 2376, + 1666, 2566, 2637, 3476, 3818, 2618, 2188, 3996, 2600, 549, 1600, 960, 3146, 2377, + 2650, 1152, 3394, 2569, 1943, 2275, 2627, 3707, 2651, 3554, 972, 2556, 1917, 2636, + 1191, 3930, 1159, 1016, 765, 1944, 1424, 745, 3832, 2664, 102, 3179, 2608, 2469, + 1789, 3197, 3877, 1494, 2681, 2461, 805, 2594, 1643, 2397, 3751, 1717, 2515, 2550, + 2554, 1179, 2696, 2373, 1801, 3136, 2700, 87, 2606, 428, 34, 3817, 2311, 3668, + 2464, 2466, 2083, 1214, 2009, 849, 2714, 2119, 3726, 2703, 3685, 2599, 1364, 2509, + 2601, 2544, 3525, 1573, 1414, 1405, 2418, 2667, 1536, 64, 332, 1373, 930, 2067, + 2006, 304, 3259, 2227, 1821, 2718, 2649, 2721, 2026, 3998, 3552, 2021, 2742, 2236, + 2693, 3594, 2487, 1927, 2480, 563, 3090, 1812, 2548, 2061, 2758, 2490, 3, 455, + 1663, 3641, 1832, 2737, 945, 853, 2633, 1405, 1700, 2771, 2648, 2721, 2642, 3919, + 3837, 2488, 2669, 2393, 1373, 3511, 1129, 2602, 1648, 2595, 3852, 2679, 2281, 202, + 1579, 1665, 2643, 3787, 2739, 2746, 1737, 1736, 1922, 1602, 2303, 2151, 2722, 871, + 2767, 1178, 648, 1010, 2300, 2742, 2540, 779, 2703, 2805, 2589, 3297, 729, 731, + 1806, 2406, 3255, 2607, 614, 650, 844, 479, 3314, 3703, 3179, 609, 2776, 3164, + 1720, 634, 519, 2475, 1053, 1684, 589, 2564, 3687, 2836, 1908, 2330, 1396, 2840, + 2640, 2757, 1896, 19, 3919, 2825, 864, 48, 1554, 2345, 2474, 1751, 3898, 2391, + 2354, 775, 351, 2863, 2866, 682, 2160, 144, 2824, 2871, 2865, 1096, 1667, 469, + 2869, 306, 2642, 125, 2556, 2873, 2829, 2236, 683, 579, 1650, 2797, 2038, 2144, + 3779, 1848, 2398, 2719, 1223, 2751, 2848, 2529, 3195, 1749, 2407, 604, 2898, 1417, + 2804, 1883, 2735, 2774, 2781, 2869, 2632, 2595, 2912, 3259, 3475, 895, 531, 290, + 2918, 2710, 2655, 1254, 2407, 1924, 2746, 2560, 2903, 2729, 2837, 523, 1711, 2919, + 2887, 2627, 2867, 3430, 2089, 1121, 2391, 2408, 2850, 2349, 2863, 1617, 1443, 2931, + 4093, 1911, 3908, 2746, 1924, 1760, 3498, 1425, 3294, 1569, 1981, 792, 2313, 2954, + 2904, 396, 2850, 1309, 2060, 542, 703, 2184, 2259, 2629, 1993, 1000, 1350, 802, + 2896, 2746, 2778, 2729, 285, 155, 2676, 2685, 2960, 2941, 1617, 2585, 2833, 2913, + 2594, 1245, 3191, 674, 62, 355, 2382, 2194, 2441, 2077, 2907, 2418, 283, 2897, + 1010, 2228, 2991, 2811, 2852, 2468, 2998, 2942, 2627, 2164, 2111, 1737, 1688, 243, + 1557, 2043, 446, 3135, 381, 522, 2915, 2963, 2838, 2344, 2480, 3992, 1206, 3020, + 2327, 1195, 3303, 2923, 206, 3141, 2669, 2737, 1129, 2236, 2928, 2756, 1524, 3040, + 452, 1215, 2897, 2571, 2526, 2908, 2195, 2753, 3526, 2665, 2252, 2706, 1081, 3026, + 2313, 430, 3057, 2824, 2721, 1792, 1944, 3026, 3907, 2236, 978, 2126, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 1; + let segment_n = 3; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 1181, 2089, 2541, 1308, 2531, 2731, 2917, 2994, 551, 2431, 3080, 1148, 2328, 3025, + 2798, 2494, 2871, 1571, 1922, 2617, 910, 1553, 1431, 1393, 262, 2679, 3042, 2806, + 1705, 883, 137, 1122, 3070, 2945, 2562, 3096, 2991, 2326, 786, 3079, 2432, 562, + 2555, 2598, 1499, 1226, 3065, 841, 319, 3048, 371, 3094, 3100, 2390, 370, 2207, + 2965, 1263, 2574, 1478, 2992, 1602, 2749, 2480, 3108, 3135, 190, 1490, 1794, 502, + 2878, 3054, 469, 2726, 3111, 3135, 2708, 2488, 2699, 2598, 2097, 2631, 2615, 1710, + 1203, 148, 2686, 1136, 2669, 1542, 1789, 2185, 788, 1608, 2759, 3035, 2721, 1592, + 2730, 2750, 2154, 525, 3020, 2224, 3028, 1104, 3095, 1903, 438, 3179, 2980, 1683, + 820, 2727, 3166, 1192, 811, 703, 796, 3178, 334, 3191, 3138, 3117, 2580, 2410, + 1334, 2033, 1872, 1028, 435, 2089, 96, 3135, 2081, 377, 3126, 3204, 1227, 1744, + 3134, 2021, 2370, 470, 1334, 2745, 1739, 1230, 136, 60, 3099, 2613, 3172, 352, + 1599, 2530, 2382, 3198, 1797, 2601, 3083, 2540, 1784, 3233, 1357, 1989, 3234, 3044, + 2849, 2980, 3238, 2663, 3225, 3060, 610, 2598, 2001, 2497, 2161, 771, 1965, 2229, + 681, 3135, 2302, 3245, 2783, 2965, 449, 3125, 3199, 1739, 2975, 2936, 848, 3263, + 3256, 1013, 2491, 2039, 3238, 3220, 3117, 2029, 3265, 3192, 2672, 3168, 2584, 2329, + 626, 2430, 1362, 3281, 1974, 3048, 1302, 1880, 2898, 2833, 1854, 3277, 2615, 1466, + 1451, 1874, 2165, 3032, 2678, 3186, 2011, 3111, 1031, 571, 1823, 1165, 1568, 2635, + 3079, 1125, 2177, 3219, 426, 2283, 3296, 2767, 1524, 3267, 2556, 2508, 1996, 1072, + 1692, 3231, 3266, 3117, 2604, 3327, 2315, 3024, 1584, 1357, 3312, 1939, 2889, 1216, + 1509, 3152, 1231, 1371, 1710, 1549, 3253, 3268, 2725, 578, 3263, 3132, 50, 2647, + 1680, 3265, 2508, 2445, 3325, 2833, 1517, 3192, 1894, 3305, 1371, 1918, 2651, 1630, + 70, 934, 1676, 2348, 2400, 3218, 3278, 2631, 550, 94, 3014, 924, 3376, 1301, 2836, + 1051, 1477, 2888, 2105, 2346, 1844, 2052, 2859, 1735, 1429, 2436, 3047, 2856, 806, + 670, 3225, 624, 174, 1525, 3384, 3285, 2973, 2959, 3072, 2783, 3402, 3269, 1589, + 42, 2713, 856, 1843, 2265, 3379, 3079, 3004, 3405, 2636, 1782, 3416, 937, 3009, + 895, 2832, 1730, 3264, 3364, 3049, 3420, 1286, 2841, 3110, 2738, 3227, 3260, 3431, + 713, 3360, 2348, 1585, 3428, 3202, 1035, 3186, 2066, 145, 2814, 2684, 2836, 625, + 3420, 3371, 3418, 2395, 2766, 3454, 1403, 2782, 2102, 3430, 3339, 3436, 373, 54, + 2941, 3381, 916, 3453, 283, 3466, 2354, 773, 1781, 3452, 3333, 180, 368, 2057, + 1840, 3123, 3410, 3468, 2963, 3122, 3470, 1137, 1103, 391, 924, 2782, 1611, 2943, + 3489, 1682, 2538, 963, 2769, 1713, 2728, 2258, 3411, 2651, 317, 2177, 2610, 3005, + 3470, 3475, 3419, 3040, 2506, 3378, 1722, 623, 2415, 303, 165, 3516, 946, 1442, + 527, 3319, 3472, 2312, 3506, 1820, 3095, 3522, 3516, 2522, 2477, 577, 1423, 1026, + 758, 3299, 3154, 2818, 2883, 2111, 3209, 2107, 637, 2202, 3542, 1875, 1116, 3416, + 1776, 2639, 3452, 2668, 1398, 913, 2671, 3455, 1941, 3553, 386, 3521, 2064, 1071, + 1107, 3539, 236, 3253, 3511, 42, 3156, 784, 2774, 2412, 3014, 585, 600, 325, 1593, + 3561, 2343, 3430, 2507, 3509, 2925, 3438, 2295, 3361, 3583, 2748, 1078, 3580, 1964, + 2685, 206, 2447, 1745, 1724, 3257, 1607, 3506, 534, 1324, 1215, 2820, 2281, 1874, + 1541, 3600, 2775, 2684, 2275, 463, 975, 2529, 496, 792, 3295, 3237, 2407, 2908, + 1090, 1246, 821, 955, 1722, 1455, 3618, 3624, 680, 2559, 2107, 2102, 2024, 2809, + 2216, 3458, 3620, 3229, 3427, 3446, 3537, 2424, 2833, 3438, 3629, 1625, 1486, 58, + 3577, 3444, 1249, 2847, 3103, 1864, 2747, 1459, 2584, 3613, 1045, 157, 3606, 569, + 3558, 3602, 2711, 595, 1869, 3663, 3598, 3592, 3666, 3550, 2688, 1483, 3088, 3325, + 400, 2790, 1412, 850, 726, 3072, 277, 21, 1876, 3520, 2697, 2752, 2014, 3639, 1604, + 3506, 3643, 3691, 1467, 3264, 1647, 3625, 1591, 3195, 1432, 1967, 3700, 3563, 924, + 3342, 3566, 1130, 3502, 3689, 2209, 3524, 3308, 3488, 259, 72, 2418, 2630, 3478, + 3707, 1736, 1926, 305, 2759, 1670, 2385, 3250, 2974, 3094, 2653, 1620, 1873, 1678, + 2093, 3164, 2832, 2034, 1924, 3593, 1840, 558, 2318, 2625, 1398, 3095, 1939, 3741, + 1890, 2959, 3508, 3394, 2992, 3651, 2954, 3728, 3492, 1744, 3476, 3570, 1030, 2148, + 3351, 2202, 2292, 12, 129, 3629, 3613, 1707, 3754, 3098, 3769, 3672, 3763, 781, + 1649, 416, 1027, 706, 2595, 3773, 1971, 1696, 3278, 3490, 3783, 3766, 3698, 3179, + 1650, 2181, 2424, 260, 3755, 2190, 1406, 3177, 3105, 2323, 3124, 2046, 3209, 3571, + 3702, 1657, 3011, 1578, 3252, 3456, 3194, 1251, 1973, 636, 1139, 1412, 3483, 336, + 3623, 3764, 1665, 3814, 3610, 631, 186, 2617, 2523, 2590, 3587, 3724, 2904, 3802, + 3627, 2680, 3562, 3403, 138, 1720, 1655, 3342, 1439, 989, 3376, 3836, 3839, 3544, + 2461, 3800, 3711, 3456, 1270, 966, 3726, 3659, 3773, 3852, 3515, 3755, 293, 2235, + 1752, 458, 3666, 2797, 3005, 2463, 3093, 3161, 2767, 3477, 3162, 3406, 3485, 2151, + 2023, 3032, 1855, 3464, 2437, 2708, 3813, 1847, 3339, 1085, 36, 3159, 1401, 736, + 3872, 1044, 3150, 3745, 3603, 1918, 2150, 3888, 2250, 2914, 1597, 33, 3725, 3676, + 3837, 3758, 3834, 513, 3262, 1524, 3884, 3289, 3876, 3877, 18, 56, 3293, 1101, + 1249, 3090, 3837, 1468, 3652, 120, 3537, 2991, 1170, 883, 538, 3885, 2368, 2954, + 810, 3369, 3221, 1751, 3914, 3049, 1760, 3919, 3190, 3868, 3935, 3767, 2025, 3902, + 3131, 3857, 3902, 567, 690, 2499, 637, 1555, 230, 3335, 1355, 3937, 1240, 2733, + 1503, 1042, 1877, 2097, 3959, 1686, 3863, 3838, 854, 1131, 371, 3635, 1791, 709, + 1472, 2959, 2226, 482, 1152, 1024, 3169, 3677, 3788, 1907, 3059, 1606, 3809, 2782, + 3983, 2563, 3578, 222, 3363, 1830, 861, 1397, 3990, 720, 2864, 3973, 3848, 620, + 884, 3872, 3999, 2968, 3515, 3894, 3887, 3883, 2778, 1957, 2448, 2833, 3832, 3884, + 3951, 1638, 176, 3370, 3871, 3812, 516, 2483, 133, 384, 3978, 1783, 2078, 3731, + 3199, 4026, 3973, 2744, 4018, 2598, 3490, 2800, 3695, 3891, 57, 2391, 3983, 812, + 3863, 4031, 2483, 1602, 3667, 3503, 1166, 2270, 3603, 3459, 3921, 4049, 2050, 368, + 28, 4008, 1554, 3169, 3978, 3996, 2652, 2447, 4042, 764, 1182, 732, 3878, 4026, + 1383, 3860, 4061, 1363, 3942, 2083, 2428, 934, 3865, 3540, 3307, 2368, 2666, 3483, + 4010, 2728, 1454, 2225, 3648, 3476, 3811, 3592, 3339, 3548, 3973, 3979, 4061, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 2; + let segment_n = 0; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 3058, 3853, 3996, 3939, 1160, 3958, 2783, 2771, 3906, 3745, 2022, 3383, 2446, 4078, + 2898, 4091, 2517, 1562, 1308, 3, 4075, 3762, 3623, 1619, 4055, 4090, 4071, 2811, + 2414, 3947, 3842, 26, 3829, 3742, 2823, 2080, 2772, 2814, 36, 3879, 38, 3755, 3824, + 3921, 3814, 4049, 1744, 2237, 44, 47, 3914, 4071, 3283, 41, 3991, 9, 1207, 2279, + 2572, 1204, 3898, 42, 1276, 4002, 3593, 3311, 1593, 12, 3465, 30, 1965, 27, 3744, + 1184, 1793, 1827, 3963, 1574, 2898, 2945, 75, 2729, 3982, 3178, 46, 3227, 67, 1565, + 3377, 1784, 2444, 2511, 3723, 3486, 3934, 4087, 2188, 3037, 3263, 2394, 2859, 2784, + 3427, 11, 4057, 3699, 2739, 3690, 10, 1193, 85, 3513, 3712, 3198, 3759, 36, 3752, + 4076, 2378, 22, 101, 1829, 48, 3728, 1, 2863, 55, 1750, 4017, 3894, 55, 1958, 2442, + 3950, 4024, 1767, 4082, 74, 3754, 1149, 138, 1982, 4076, 1571, 3017, 143, 3387, + 2051, 2260, 1249, 3789, 1259, 2502, 1948, 100, 3732, 3070, 155, 2362, 4, 157, 2516, + 1208, 2827, 3377, 15, 147, 1296, 2341, 3295, 1810, 167, 3910, 141, 4062, 3032, + 3592, 163, 1808, 141, 3443, 97, 3562, 58, 1525, 1235, 3953, 17, 1783, 3650, 3495, + 147, 1407, 151, 3702, 182, 3493, 3562, 80, 1394, 2525, 4020, 2600, 3701, 140, 2550, + 3581, 1661, 1216, 2051, 2569, 2315, 132, 3036, 3927, 148, 3011, 1180, 98, 211, + 1561, 3798, 3754, 3622, 1663, 179, 1493, 3975, 3365, 3797, 4039, 2775, 222, 3191, + 1207, 1989, 2631, 3464, 191, 155, 1299, 3724, 3415, 3052, 2193, 3950, 1239, 3797, + 3613, 2637, 1454, 149, 3735, 1994, 2608, 3662, 157, 51, 2561, 3755, 3913, 2933, + 4042, 4067, 2901, 3462, 3961, 2567, 2862, 169, 121, 4, 3912, 1349, 4051, 1699, 273, + 1727, 276, 1867, 3118, 2998, 172, 1680, 2118, 25, 1980, 3089, 2164, 3828, 226, 273, + 3623, 4019, 2632, 280, 2009, 141, 2790, 2501, 1636, 4062, 299, 301, 3874, 3785, + 2463, 1664, 3601, 2163, 128, 177, 3014, 1738, 4031, 2055, 3119, 220, 283, 3179, + 3725, 3460, 2479, 4055, 1767, 2109, 3384, 3505, 4016, 3655, 2880, 3629, 3110, 1403, + 1393, 1523, 1533, 3925, 2515, 281, 1705, 221, 3406, 167, 3397, 1082, 326, 248, + 4061, 3268, 348, 1919, 1040, 291, 3994, 1733, 159, 281, 3482, 3840, 3908, 322, 242, + 2896, 323, 2115, 3049, 2547, 159, 355, 1522, 2090, 3999, 3526, 269, 3234, 3864, + 2647, 4055, 353, 1348, 3530, 359, 3262, 162, 3802, 381, 3706, 289, 3107, 3746, + 3662, 3109, 371, 2049, 3413, 3415, 2118, 2636, 163, 2396, 3566, 343, 396, 1447, + 2139, 404, 1552, 2442, 1648, 1522, 259, 218, 385, 3020, 1915, 406, 2360, 1970, + 2470, 3671, 2444, 2730, 3714, 2803, 1732, 3859, 227, 4017, 420, 2443, 200, 1966, + 296, 2940, 416, 243, 138, 418, 99, 406, 148, 3108, 3354, 3598, 368, 2626, 330, + 1780, 2970, 2752, 2354, 2360, 2648, 422, 1870, 3581, 446, 1355, 2820, 1972, 277, + 1396, 3807, 3626, 323, 196, 1903, 2051, 3693, 3041, 343, 2071, 3462, 1052, 2422, + 471, 2634, 1060, 1801, 3204, 3006, 1722, 1618, 1294, 1729, 304, 1881, 3998, 2183, + 2038, 3005, 218, 99, 491, 3470, 1964, 420, 2160, 2000, 2562, 1849, 91, 148, 242, + 3840, 2665, 2523, 253, 37, 1503, 2947, 3106, 507, 1399, 1546, 3281, 1906, 241, + 1254, 166, 330, 3267, 2960, 3322, 3658, 4014, 3336, 449, 3025, 225, 2588, 3133, + 140, 2394, 2280, 30, 196, 2418, 3234, 3362, 371, 2280, 317, 2089, 2096, 3265, 135, + 2328, 1093, 1863, 3852, 3920, 3155, 462, 1941, 3730, 514, 1965, 425, 2262, 3648, + 377, 2598, 2204, 281, 467, 326, 3136, 3698, 2074, 2290, 417, 1445, 3815, 392, 531, + 2503, 3932, 360, 494, 3464, 1906, 3779, 2719, 1366, 255, 577, 2807, 3710, 1962, + 2136, 310, 333, 3721, 2256, 1954, 2880, 43, 467, 1394, 2442, 207, 3683, 372, 596, + 1258, 3204, 2256, 3816, 608, 528, 2415, 3834, 235, 3352, 2616, 3447, 3751, 3372, + 3086, 284, 573, 3016, 533, 1150, 9, 3793, 2824, 554, 1599, 2002, 588, 3541, 513, + 612, 601, 424, 4094, 3084, 34, 3438, 1802, 1325, 641, 2232, 3743, 279, 346, 570, + 1494, 1140, 291, 4041, 646, 3652, 157, 566, 2510, 2160, 3838, 1545, 1048, 566, + 3305, 584, 2678, 634, 179, 1724, 2191, 669, 3655, 595, 3555, 402, 129, 1663, 2849, + 2758, 2725, 565, 3331, 681, 279, 2112, 505, 3634, 1954, 3995, 3253, 3566, 361, 688, + 192, 570, 3814, 2131, 1564, 2718, 308, 2879, 1247, 1933, 648, 2675, 1107, 2115, + 2766, 2083, 1666, 2952, 710, 3295, 1642, 1752, 498, 130, 3500, 2187, 717, 64, 3504, + 2226, 4026, 719, 3656, 603, 176, 3668, 2410, 1248, 2643, 1850, 196, 2817, 728, 612, + 3225, 245, 648, 238, 726, 3525, 2901, 1525, 230, 540, 3479, 735, 3108, 459, 377, + 735, 1396, 3137, 3899, 669, 2199, 3822, 527, 3470, 54, 754, 3000, 2673, 2338, 600, + 3911, 1807, 4079, 3644, 413, 4078, 609, 592, 748, 2437, 1585, 2297, 334, 1828, 33, + 1760, 3046, 760, 2789, 476, 779, 353, 1736, 3326, 625, 1826, 637, 747, 568, 640, + 2909, 512, 4062, 492, 3001, 765, 194, 1369, 637, 196, 806, 3050, 586, 2583, 728, + 2073, 533, 796, 812, 2494, 3823, 729, 55, 516, 3129, 2735, 793, 6, 719, 554, 768, + 690, 3235, 818, 2714, 2818, 4050, 829, 2117, 1983, 3143, 2707, 587, 783, 1684, 460, + 2502, 278, 828, 3609, 568, 1822, 1584, 211, 604, 3429, 624, 212, 3131, 3708, 2853, + 1662, 124, 455, 1054, 433, 1751, 3257, 283, 308, 456, 1745, 1341, 183, 1182, 655, + 687, 4047, 508, 3890, 3948, 1273, 498, 1037, 774, 821, 537, 373, 586, 1924, 638, + 2047, 2263, 1582, 2722, 824, 856, 3894, 739, 477, 409, 567, 3199, 852, 359, 2558, + 445, 2936, 3781, 2128, 617, 558, 1694, 3969, 4013, 3970, 2373, 3606, 426, 3545, + 1838, 1454, 1956, 915, 1227, 3411, 863, 339, 1703, 874, 1506, 2982, 77, 3407, 4015, + 697, 192, 4054, 3737, 3634, 186, 747, 1230, 20, 938, 1206, 801, 3529, 375, 487, + 2595, 925, 2865, 1230, 2263, 948, 3773, 87, 2227, 1037, 2741, 3943, 3986, 265, 905, + 3295, 917, 497, 292, 3292, 1688, 749, 3908, 66, 2665, 469, 1291, 3939, 1166, 436, + 2058, 2046, 930, 3745, 3928, 929, 707, 3462, 3951, 3657, 308, 164, 4046, 3826, + 2108, 2555, 3633, 721, 208, 2778, 3144, 1349, 957, 2632, 256, 992, 3949, 3977, 772, + 1484, 731, 1500, 346, 1490, 353, 3955, 2943, 362, 1964, 468, 81, 549, 59, 377, 351, + 402, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 2; + let segment_n = 1; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 245, 305, 3307, 2804, 3940, 209, 4024, 3645, 611, 3393, 168, 3928, 855, 917, 956, + 2253, 2645, 956, 3289, 927, 259, 2123, 3793, 470, 886, 392, 1024, 4063, 697, 1047, + 2858, 315, 2069, 2236, 2652, 871, 682, 996, 2717, 4003, 888, 2851, 948, 563, 3795, + 749, 846, 3966, 3264, 980, 427, 2546, 527, 108, 243, 666, 2669, 3825, 3882, 374, + 1056, 2290, 1077, 162, 3737, 3960, 3477, 316, 1042, 3933, 3274, 4014, 1079, 1092, + 3586, 4061, 566, 4000, 4061, 2186, 3418, 3459, 463, 3180, 3358, 785, 439, 3488, + 910, 1093, 2716, 3731, 1061, 939, 3079, 1097, 2319, 613, 3867, 448, 1074, 176, 528, + 2936, 516, 3759, 463, 99, 3882, 22, 1031, 549, 1047, 623, 710, 1093, 2886, 584, + 1140, 3834, 3626, 1101, 2137, 2626, 590, 846, 3532, 1149, 1119, 1115, 3105, 1130, + 246, 3008, 17, 2494, 705, 199, 2383, 1105, 18, 2638, 1124, 1075, 3896, 1083, 1102, + 4040, 788, 3048, 1157, 3309, 515, 3434, 798, 83, 3501, 1179, 304, 2806, 888, 1107, + 2551, 754, 3122, 2054, 877, 3501, 1185, 3636, 1192, 3037, 623, 286, 575, 2929, 420, + 1196, 2193, 980, 1105, 2523, 432, 3219, 869, 4049, 240, 1055, 1133, 1192, 739, 863, + 2193, 2298, 925, 2196, 1148, 1030, 732, 870, 1084, 2500, 349, 1225, 1121, 4017, + 4054, 611, 623, 1022, 1195, 562, 3575, 1233, 2288, 2974, 1020, 3573, 461, 589, 417, + 47, 303, 1074, 648, 1231, 1248, 3161, 838, 688, 2296, 3065, 126, 691, 980, 2487, + 1129, 229, 3139, 151, 1136, 427, 47, 2320, 958, 239, 1268, 47, 308, 751, 1150, + 2048, 1263, 3980, 998, 1189, 2903, 3569, 1086, 1016, 3418, 2253, 3556, 4005, 1180, + 1207, 965, 923, 86, 3912, 1241, 443, 3707, 2237, 644, 1065, 3369, 2427, 1132, 44, + 1237, 3181, 1249, 1276, 1097, 1260, 174, 1300, 3652, 1297, 3291, 654, 1289, 960, + 830, 994, 2510, 1318, 499, 3525, 1265, 1141, 3494, 897, 1288, 33, 2643, 10, 2199, + 3067, 3918, 972, 1232, 645, 430, 516, 2337, 2623, 1039, 25, 725, 777, 3300, 1123, + 2342, 4025, 993, 324, 3892, 1338, 1027, 1238, 4002, 968, 1331, 37, 3431, 2581, + 3063, 3096, 1328, 2057, 1009, 563, 769, 362, 3542, 475, 965, 873, 2513, 2631, 1359, + 314, 1285, 1294, 235, 1119, 3155, 2273, 2207, 1018, 22, 590, 2601, 1139, 3405, + 1382, 168, 3976, 2053, 2563, 1034, 2179, 1325, 828, 1396, 1301, 694, 738, 890, 898, + 1389, 408, 3932, 1363, 176, 2630, 445, 3327, 3657, 1412, 3156, 831, 443, 2650, 606, + 78, 2486, 1308, 517, 974, 2541, 443, 1359, 2459, 490, 962, 4073, 950, 827, 1248, + 700, 815, 3514, 1409, 1417, 1381, 2050, 2938, 1350, 332, 3609, 2575, 1441, 4055, + 634, 2524, 4074, 1358, 1444, 1432, 1077, 590, 1424, 2313, 897, 886, 1166, 1364, + 3767, 2997, 898, 1431, 539, 1141, 3348, 2795, 868, 1232, 1269, 909, 1347, 3624, + 3245, 1044, 334, 179, 892, 109, 1470, 773, 765, 291, 2756, 2283, 913, 846, 2810, + 871, 2756, 1234, 1245, 179, 204, 2128, 1160, 3116, 1477, 2512, 390, 767, 1074, + 2523, 262, 1502, 1312, 2838, 2184, 768, 1487, 1405, 991, 1096, 2645, 2984, 3528, + 769, 3452, 2780, 1450, 3164, 1433, 4, 490, 383, 2964, 2970, 3040, 1405, 38, 552, + 1534, 758, 3430, 329, 1497, 1232, 3708, 1128, 1083, 3133, 1467, 1545, 1466, 508, + 2348, 870, 1004, 1476, 1552, 534, 3803, 1405, 3588, 3265, 3260, 674, 1439, 711, + 1137, 970, 1045, 1539, 545, 1550, 1568, 1177, 3757, 125, 1567, 895, 377, 28, 2674, + 2505, 748, 2593, 3465, 1029, 1183, 804, 3142, 1053, 1544, 1116, 494, 2078, 1120, + 1549, 2581, 2397, 3376, 2176, 636, 1093, 2823, 1503, 937, 3053, 571, 1581, 299, + 1333, 1486, 1598, 1606, 3975, 911, 1583, 1587, 3454, 1152, 1558, 970, 1550, 3723, + 2663, 1476, 1128, 1489, 1589, 1433, 3506, 2497, 1552, 1287, 1387, 55, 1434, 4005, + 3971, 3550, 1611, 459, 995, 3143, 1637, 1619, 1390, 513, 1411, 502, 3425, 1597, + 1535, 2383, 109, 1436, 3216, 1346, 1421, 3212, 1601, 616, 1443, 1648, 2301, 1645, + 1328, 1650, 82, 226, 307, 1666, 861, 414, 3262, 1014, 1003, 1473, 4035, 4079, 984, + 374, 3761, 4021, 1150, 2932, 1196, 1455, 1681, 1625, 1132, 405, 1273, 1552, 2947, + 3987, 2071, 3912, 3980, 1694, 1589, 1112, 55, 1493, 1461, 1465, 1557, 934, 1687, + 1044, 1317, 3014, 1466, 3961, 3956, 3299, 214, 2427, 3963, 3145, 676, 491, 3522, + 2841, 1588, 1706, 1694, 35, 1719, 1188, 1707, 1304, 3273, 1564, 1690, 2256, 1471, + 100, 607, 940, 2693, 1334, 1640, 852, 1738, 588, 1733, 3203, 1517, 1677, 2448, + 1742, 3501, 1714, 444, 1673, 1416, 4011, 2284, 1753, 3912, 1231, 2921, 2947, 861, + 3720, 1496, 1263, 1384, 2112, 1281, 1480, 3686, 3955, 3989, 1290, 3489, 1124, 1770, + 2283, 618, 2836, 1413, 1503, 2257, 802, 1026, 2386, 516, 1412, 2787, 502, 1167, + 3816, 1701, 1603, 2125, 316, 3629, 1473, 1426, 1452, 1478, 1747, 3456, 1184, 2563, + 1681, 3136, 1617, 1406, 3562, 866, 153, 1621, 21, 1660, 1765, 1054, 1642, 335, + 1792, 730, 3773, 2429, 1732, 1378, 1746, 947, 1431, 3103, 945, 1698, 1488, 3433, 4, + 1831, 736, 469, 3095, 303, 376, 2759, 2259, 202, 854, 1348, 2437, 2954, 1828, 1206, + 1344, 1837, 3780, 1794, 747, 3928, 1817, 929, 1662, 1772, 1199, 3262, 1297, 1191, + 1773, 2908, 1122, 1860, 1214, 2310, 2918, 1829, 3371, 24, 1868, 1777, 536, 732, + 2223, 3738, 783, 1594, 1555, 703, 1131, 1257, 2780, 1666, 1671, 2690, 1181, 1769, + 23, 1016, 1551, 3351, 1660, 1493, 106, 765, 1598, 1043, 1699, 1889, 1057, 1901, + 2070, 3725, 857, 739, 2359, 49, 1486, 293, 3395, 3384, 2468, 1867, 1631, 3838, + 1432, 1450, 822, 2971, 1303, 1847, 3460, 1621, 772, 1924, 1287, 1926, 1654, 303, + 2395, 3994, 172, 3677, 1920, 1755, 2844, 1651, 1922, 1513, 4058, 854, 3068, 2, 741, + 1739, 1296, 1794, 3229, 1872, 1924, 2254, 730, 1023, 3703, 1349, 1692, 1406, 3815, + 1492, 40, 3040, 334, 1727, 591, 1503, 1733, 2691, 3063, 1204, 2722, 1665, 3895, + 1924, 3565, 3795, 1905, 3843, 3897, 1817, 1888, 1538, 224, 1476, 2081, 1280, 3972, + 2200, 1105, 3469, 3685, 1909, 974, 2516, 382, 1389, 2366, 2750, 136, 2174, 1915, + 1622, 2519, 3999, 3674, 1649, 1706, 757, 3776, 2898, 1614, 158, 450, 15, 2013, + 1852, 1930, 438, 1851, 775, 1746, 1870, 1021, 997, 1742, 1756, 2767, 1982, 1882, + 72, 1961, 2296, 2940, 2570, 2569, 2878, 3652, 3774, 2212, 2599, 30, 2183, 248, 508, + 1543, 2967, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 2; + let segment_n = 2; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 2035, 1527, 238, 1387, 1882, 1932, 772, 2047, 1735, 918, 619, 450, 1107, 783, 1692, + 181, 1995, 1895, 1973, 2045, 630, 1584, 1150, 3855, 2055, 2068, 3155, 1949, 3884, + 2064, 1369, 3749, 1961, 1505, 2058, 3972, 1019, 3196, 1559, 334, 1041, 3306, 2080, + 3189, 1988, 770, 805, 3651, 990, 486, 981, 237, 1270, 4085, 161, 18, 1821, 1330, + 1615, 3327, 2074, 487, 1759, 1580, 241, 4002, 1819, 1152, 2113, 3801, 1057, 1844, + 3249, 214, 719, 3415, 1180, 1917, 1546, 2085, 65, 1568, 2108, 960, 1645, 1794, + 2095, 686, 1295, 1334, 1685, 1041, 1959, 594, 1253, 1184, 1896, 529, 960, 2037, + 1027, 1117, 2085, 4042, 353, 3849, 3936, 69, 1912, 2006, 3887, 502, 655, 2139, 330, + 1314, 3872, 3847, 937, 390, 1126, 2129, 252, 2078, 1960, 2134, 891, 329, 1555, + 3363, 1063, 119, 1836, 978, 1896, 2072, 1517, 1828, 3173, 2159, 1246, 987, 1801, + 1742, 1975, 1655, 539, 1680, 457, 541, 2159, 1433, 447, 484, 8, 3129, 221, 2050, + 2185, 1393, 3686, 1950, 1543, 2121, 1955, 46, 2102, 1364, 2179, 2208, 2207, 2201, + 1802, 2142, 3986, 1943, 1240, 2197, 1812, 1624, 1790, 2023, 3355, 856, 1343, 1692, + 2079, 941, 3515, 2049, 2223, 1295, 1925, 2203, 3630, 696, 1966, 1206, 598, 3214, + 1658, 723, 2223, 1747, 236, 3337, 1868, 1392, 1965, 2110, 1514, 495, 1677, 2082, + 4052, 1710, 2110, 1918, 1324, 209, 3166, 1858, 1639, 1968, 1222, 1703, 1312, 2086, + 3249, 1263, 585, 245, 1354, 1907, 1829, 1338, 2280, 2189, 2246, 2170, 4095, 2103, + 2016, 612, 1554, 408, 1300, 1448, 4004, 2247, 2196, 1611, 248, 2242, 1056, 435, + 1470, 3828, 3731, 2302, 1054, 620, 3198, 1184, 2231, 3501, 1603, 2298, 73, 1048, + 2033, 1083, 1408, 1217, 274, 685, 3146, 2276, 2318, 2267, 1215, 2226, 2318, 3464, + 2328, 83, 2117, 2084, 2224, 3485, 209, 1872, 3575, 1809, 360, 1550, 3677, 3607, + 4004, 2343, 2043, 2341, 1501, 1661, 2218, 3854, 3250, 2098, 2036, 2355, 61, 419, + 2313, 1461, 2009, 2045, 2362, 344, 1799, 1832, 1092, 3573, 984, 3644, 1308, 2253, + 3240, 2332, 1096, 1225, 1769, 2226, 2284, 1260, 2214, 1693, 1240, 1936, 1619, 2240, + 2198, 2371, 3453, 3394, 1961, 2140, 304, 728, 1263, 2390, 1404, 2280, 2397, 3945, + 2101, 2381, 309, 435, 2160, 505, 1047, 1230, 1615, 2326, 2268, 2403, 1512, 3918, + 778, 3112, 1563, 2126, 88, 1048, 2418, 1924, 2419, 3248, 2377, 2307, 2098, 2062, + 1810, 1076, 2404, 2179, 2243, 1305, 2400, 1665, 1967, 2268, 4034, 3705, 2234, 1704, + 1814, 2414, 879, 1628, 902, 835, 337, 1010, 2332, 1992, 1013, 2407, 639, 454, 2333, + 1974, 720, 2448, 894, 1865, 3662, 1562, 611, 318, 1662, 670, 2364, 1748, 2280, + 1884, 1018, 2170, 2428, 881, 422, 2403, 2181, 2003, 3416, 3384, 2156, 3463, 3256, + 951, 1222, 1660, 1499, 1802, 644, 1432, 1886, 1431, 2398, 1953, 1760, 601, 469, + 2429, 1034, 3421, 1881, 1723, 1311, 509, 1924, 147, 2266, 1958, 2473, 3313, 1730, + 2371, 1176, 2475, 1968, 4088, 1736, 2057, 2143, 3518, 1874, 2011, 1144, 912, 2251, + 1926, 3997, 232, 50, 1051, 98, 2315, 2366, 1077, 894, 3202, 1669, 547, 3956, 1429, + 2193, 1156, 330, 1568, 143, 1071, 933, 1409, 2245, 2493, 153, 3256, 3702, 2374, + 2544, 3867, 2362, 1948, 1353, 2404, 1787, 1249, 2101, 555, 3478, 572, 2413, 838, + 2510, 690, 2018, 1479, 748, 2513, 2506, 808, 2557, 1671, 2504, 2581, 2077, 1660, + 826, 636, 2531, 3192, 3694, 18, 1865, 2295, 2523, 1469, 3764, 2454, 2020, 3543, + 2202, 1490, 2596, 2474, 3426, 2381, 474, 2384, 1500, 2137, 2467, 2545, 2496, 1799, + 2462, 2319, 2497, 3108, 1526, 1792, 1522, 2558, 2217, 2621, 3115, 2406, 1886, 2361, + 1047, 117, 331, 2257, 1277, 628, 2460, 2515, 2399, 1183, 2489, 769, 3784, 2633, + 3490, 1349, 1715, 2043, 1635, 2217, 3882, 2617, 2172, 2278, 1986, 2399, 2545, 2114, + 2205, 1769, 2618, 916, 3855, 85, 4010, 2365, 688, 882, 2401, 1883, 2401, 1728, + 2630, 1495, 3974, 1969, 2163, 2672, 2407, 2293, 2398, 3379, 1552, 3484, 2286, 2467, + 127, 614, 1644, 1243, 2570, 1436, 949, 1805, 1497, 1025, 3865, 1522, 3241, 2611, + 2178, 2684, 330, 1889, 3549, 535, 2252, 3890, 2408, 1604, 2337, 450, 4062, 1590, + 2709, 2206, 2313, 270, 1379, 1281, 719, 2622, 2457, 2482, 3595, 1234, 1854, 2718, + 2147, 2719, 2164, 1910, 289, 2516, 3366, 1953, 1347, 1671, 2059, 2650, 2686, 1720, + 2714, 2138, 2636, 1658, 1030, 1365, 634, 1873, 993, 2541, 543, 2707, 590, 2636, + 647, 3604, 3337, 3418, 3921, 2480, 3347, 2422, 1033, 3756, 2354, 2732, 1562, 2687, + 3757, 3553, 1962, 2069, 2696, 1944, 94, 54, 2710, 1133, 2341, 1271, 88, 3516, 734, + 3683, 1328, 3323, 2770, 2785, 1317, 2352, 2738, 1973, 2065, 2160, 2685, 2275, 3378, + 902, 3209, 441, 2228, 1205, 368, 2566, 2136, 2800, 2804, 2551, 2196, 2354, 2559, + 2655, 2659, 1754, 1629, 1334, 2743, 2778, 1044, 766, 3295, 1868, 3241, 116, 2818, + 2670, 3890, 332, 2734, 1835, 406, 683, 2625, 2383, 2251, 3388, 3615, 2659, 680, + 3667, 1525, 567, 1707, 2400, 2095, 2261, 2368, 2584, 2111, 2580, 1070, 1023, 2796, + 2492, 1196, 1106, 615, 1094, 2030, 2219, 2254, 2830, 1822, 2229, 2851, 1889, 2753, + 1157, 2388, 1418, 367, 2816, 1513, 2865, 763, 2161, 2793, 2680, 2634, 2598, 2188, + 3423, 2871, 2839, 2427, 933, 710, 1300, 2825, 2536, 2578, 789, 1387, 2245, 2708, + 2543, 49, 2702, 1166, 862, 2898, 2203, 1409, 3405, 2297, 2572, 375, 2755, 3209, + 489, 2904, 2865, 2545, 3812, 822, 473, 3135, 2915, 342, 3509, 2847, 2793, 1574, + 2456, 2517, 1100, 1330, 892, 2836, 3089, 3665, 1413, 2887, 1303, 218, 3378, 1333, + 1253, 2290, 2727, 132, 2805, 2424, 912, 551, 298, 1178, 2084, 2862, 3739, 2637, + 1538, 2780, 818, 2950, 2909, 367, 759, 2510, 1192, 4048, 152, 964, 2960, 1699, + 2898, 2865, 2283, 2568, 1537, 3974, 3300, 2138, 2083, 890, 2798, 1547, 65, 2815, + 2105, 920, 245, 1841, 2298, 1993, 2117, 2088, 2691, 1495, 940, 2704, 338, 3322, + 3797, 562, 97, 885, 2154, 1804, 209, 2267, 1714, 2252, 977, 3183, 2821, 2269, 2493, + 2440, 1060, 2817, 323, 1022, 1216, 2998, 2843, 3595, 3945, 1765, 2894, 3175, 1471, + 3668, 1426, 2172, 1766, 2821, 1321, 1238, 2172, 2586, 82, 2691, 2744, 1962, 670, + 1797, 1041, 2964, 3210, 4078, 2710, 2481, 710, 1630, 3461, 560, 106, 2097, 2669, + 2057, 2404, 2172, 2158, 3484, 3043, 3983, 3055, 3944, 3050, 3979, 3551, 1834, 2393, + 3171, 349, 1296, 1876, 2988, 1789, 3580, 1396, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + + let offset = 0; + let pass_n = 2; + let segment_n = 3; + + gidx.init(pass_n, segment_n, offset, &mut tmp_block); + + let expected_ref_idx: [u32; 1024] = [ + 1775, 3037, 2997, 1590, 386, 2714, 2140, 2437, 2432, 3079, 3079, 3044, 2964, 1089, + 398, 1962, 147, 2898, 764, 2571, 3066, 2723, 2452, 3078, 1811, 3091, 2278, 2171, + 2975, 2769, 3075, 3059, 2287, 2984, 1204, 1290, 1391, 2408, 1067, 2605, 2863, 1713, + 2479, 2690, 2005, 2619, 3116, 2718, 2142, 2468, 3112, 2400, 2547, 3093, 159, 1185, + 3126, 3080, 1683, 853, 838, 3044, 3101, 2693, 190, 2946, 1083, 1338, 470, 2521, + 2953, 2942, 2715, 3061, 3134, 3144, 832, 2058, 2223, 2002, 2015, 1887, 1236, 1367, + 3094, 3155, 419, 3084, 2718, 1890, 3158, 1458, 2741, 515, 1658, 1262, 1105, 2431, + 3166, 938, 800, 3160, 489, 3005, 3155, 2399, 3110, 1567, 3028, 2841, 3129, 841, + 3110, 3154, 3183, 2132, 37, 3127, 2310, 746, 3111, 1939, 1903, 1636, 3153, 1758, + 929, 2829, 988, 1130, 3148, 2130, 439, 1507, 1142, 1977, 784, 1131, 1494, 2481, + 1606, 1878, 2940, 955, 3087, 957, 1532, 2962, 397, 3204, 3158, 2651, 3204, 3192, + 157, 1903, 1050, 2595, 2738, 3129, 3229, 1987, 547, 2474, 3092, 3125, 3165, 3075, + 945, 2061, 2555, 1040, 2797, 823, 406, 3173, 3246, 3208, 2812, 2411, 1723, 1508, + 3145, 2944, 1977, 3208, 1036, 2971, 3093, 923, 2206, 204, 3257, 1160, 1484, 1694, + 3249, 1132, 1774, 2050, 2430, 1477, 2204, 1408, 2149, 3186, 3217, 3160, 660, 1875, + 1157, 617, 2748, 1973, 1881, 753, 314, 3030, 1987, 2162, 2711, 876, 3021, 2418, + 2394, 1813, 569, 2873, 210, 2765, 3287, 2916, 120, 3123, 3303, 2921, 1881, 751, + 1292, 1914, 3307, 1939, 2144, 1985, 3250, 645, 1324, 1965, 1193, 1043, 1264, 3143, + 310, 3247, 3085, 2670, 140, 1466, 3125, 2527, 3227, 2486, 1920, 3230, 3334, 1211, + 3241, 2593, 1396, 3339, 3116, 3164, 197, 2695, 1489, 1769, 1342, 1941, 2711, 311, + 3127, 2464, 921, 2398, 3218, 2936, 2620, 533, 2460, 1775, 1306, 1965, 3168, 1882, + 3292, 3303, 3219, 3251, 2616, 1435, 2191, 2254, 387, 3254, 2921, 3308, 2759, 851, + 1799, 3367, 3224, 1728, 2347, 1832, 3016, 3350, 2206, 3340, 1146, 3389, 3256, 1231, + 1966, 3122, 2962, 3087, 3375, 2410, 3117, 2759, 3073, 3304, 3402, 3159, 3369, 3403, + 1138, 1542, 2281, 3297, 2358, 724, 3072, 2656, 1737, 1099, 3326, 1334, 3366, 202, + 2835, 1833, 498, 3141, 226, 3077, 3125, 3364, 3287, 3214, 1519, 1703, 1715, 2652, + 1504, 3339, 2566, 3259, 1462, 2979, 3349, 1955, 1343, 3431, 1414, 3143, 3279, 583, + 1337, 2458, 3210, 1467, 3450, 1628, 3030, 2147, 1686, 3391, 1352, 1815, 792, 1189, + 3168, 3041, 3028, 202, 2700, 3414, 2447, 3422, 1267, 3200, 785, 3402, 1081, 3339, + 553, 2950, 2959, 2628, 2252, 2829, 3068, 2489, 3151, 2854, 3271, 1167, 536, 3355, + 3483, 2289, 3167, 2527, 1175, 2643, 3452, 3369, 1212, 163, 3134, 2528, 2769, 3481, + 2495, 138, 28, 3012, 2431, 610, 2772, 1706, 3507, 1733, 2968, 3495, 3309, 1860, + 225, 1301, 814, 1040, 3408, 253, 69, 3364, 2958, 666, 3395, 2936, 499, 3531, 2939, + 811, 3080, 3535, 2026, 3270, 127, 924, 3427, 1110, 3517, 3323, 3510, 1569, 3173, + 3542, 1016, 432, 3153, 2738, 798, 2811, 1199, 146, 234, 1277, 3397, 592, 3035, + 3043, 2841, 860, 2509, 2832, 3517, 3516, 2302, 3325, 1223, 3141, 3394, 1958, 257, + 2196, 958, 2652, 3416, 3265, 3446, 3546, 3335, 2798, 570, 3240, 2617, 1031, 3578, + 1768, 2767, 3587, 3588, 592, 865, 3530, 2813, 1742, 3177, 1400, 1233, 3236, 3368, + 702, 1087, 2580, 2976, 2733, 109, 3378, 3432, 2733, 3578, 3457, 537, 2644, 3587, + 1484, 3610, 3589, 3570, 1212, 2479, 1890, 2806, 490, 3274, 2528, 3628, 2317, 3618, + 3140, 2599, 3514, 3614, 3152, 353, 3186, 335, 3338, 3436, 3640, 174, 2053, 2313, + 149, 3561, 3637, 2245, 2978, 978, 3267, 818, 2328, 2442, 3164, 2826, 958, 3108, + 3639, 1313, 2787, 2502, 785, 3563, 3337, 1928, 3039, 3275, 3175, 654, 3641, 1238, + 3267, 961, 3344, 3610, 2188, 3568, 3645, 3650, 2739, 2453, 3329, 2250, 3366, 1835, + 877, 2135, 2479, 3425, 1106, 3458, 1106, 2769, 711, 2068, 3665, 560, 2255, 237, + 3356, 1259, 2466, 1327, 1673, 3654, 3275, 1711, 2422, 2215, 1551, 2230, 3142, 2559, + 1328, 2091, 2896, 768, 3705, 1066, 3184, 227, 2174, 3315, 3444, 168, 3012, 2873, + 2287, 2585, 3439, 3720, 2461, 815, 850, 3035, 802, 3348, 3699, 2864, 1015, 3178, + 1144, 3621, 2045, 3356, 1793, 1870, 2745, 550, 3654, 1373, 3653, 3687, 3236, 2760, + 1995, 1337, 3703, 3375, 2925, 3570, 3239, 3563, 3068, 3765, 3415, 1394, 2905, 3190, + 1223, 2870, 3197, 3366, 3368, 1648, 194, 2142, 2568, 3390, 2240, 2029, 3639, 3241, + 1822, 3025, 1630, 2413, 3643, 2724, 558, 2752, 2251, 2979, 3070, 2997, 3017, 2105, + 3190, 3559, 3762, 3337, 3087, 3494, 2808, 3126, 1800, 3109, 3804, 1040, 2418, 3233, + 2616, 1783, 3155, 1950, 2406, 3678, 3714, 3237, 3641, 3216, 2601, 1685, 3300, 3681, + 3030, 36, 1241, 1864, 520, 3305, 1622, 2582, 1333, 3176, 775, 3835, 2397, 2493, + 2466, 3471, 1337, 3827, 2298, 3846, 3444, 3818, 1586, 2712, 402, 2765, 2259, 1639, + 1066, 3787, 2836, 758, 3608, 2271, 399, 2943, 1712, 2736, 3656, 1345, 3594, 1382, + 624, 349, 2777, 71, 3740, 501, 1989, 1402, 2028, 449, 1984, 3091, 437, 2927, 3124, + 2515, 3856, 2, 3386, 3570, 1938, 2889, 2161, 3839, 575, 1536, 3080, 1811, 3895, + 2440, 3829, 1049, 887, 3431, 3824, 3878, 3864, 662, 3808, 3489, 3785, 2410, 3899, + 2697, 1389, 2079, 2094, 1599, 3766, 3819, 3919, 3873, 2841, 3921, 3868, 2214, 3371, + 2377, 2872, 1957, 3858, 1804, 3479, 1230, 3383, 456, 3572, 3918, 2468, 1153, 1961, + 3286, 3734, 2458, 2457, 3944, 3668, 3205, 1744, 9, 3899, 923, 890, 2710, 1022, + 3574, 3562, 1232, 1601, 2337, 3406, 3473, 3601, 1580, 3462, 0, 3268, 3290, 2073, + 2763, 1929, 3747, 659, 3692, 3555, 3758, 3605, 3220, 3153, 1570, 3333, 1437, 2664, + 2211, 1621, 3835, 2955, 3565, 3917, 1179, 3880, 1682, 439, 2077, 2153, 3976, 3962, + 62, 370, 2051, 3135, 3724, 2903, 3768, 3535, 2539, 3504, 1636, 3941, 422, 66, 2069, + 3188, 1213, 302, 2374, 3028, 3050, 3948, 4018, 1230, 3408, 3512, 2028, 2495, 4011, + 890, 1845, 3940, 3627, 1185, 1782, 3486, 3807, 1924, 854, 3047, 3497, 3875, 1892, + 687, 4027, 2591, 3639, 4032, 3278, 3094, 502, 3705, 1079, 3161, 4047, 3553, 2777, + 3264, 1325, 3845, 851, 3968, 3631, 3301, 2450, 519, 2243, 3689, 1142, 694, 3529, + 2267, 2057, 4037, 807, 3706, 2217, 2864, 3956, 3116, 1907, 4064, 3217, 3775, 3238, + 688, 1143, 599, 3050, 3856, 3094, 2281, 356, 3916, 3918, 3209, 349, 1317, + ]; + + let mut idx = offset; + for expected in expected_ref_idx.iter() { + // Mimic offset..segment_length runs with idx + assert_eq!( + *expected, + gidx.get_next(idx, &mut tmp_block), + "Invalid at {}", + idx + ); + idx += 1; + } + } + } + + mod test_g { + use super::*; + + #[test] + fn g_test() { + let mut w0: u64 = 15555726891008754466; + let mut w1: u64 = 5510367530937399982; + let mut w2: u64 = 11481008432838211339; + let mut w3: u64 = 8667059981748828325; + + let r0: u64 = 12666226408741176632; + let r1: u64 = 839899491230516963; + let r2: u64 = 17298398443694995777; + let r3: u64 = 10383764314571024184; + + g(&mut w0, &mut w1, &mut w2, &mut w3); + + assert_eq!(w0, r0); + assert_eq!(w1, r1); + assert_eq!(w2, r2); + assert_eq!(w3, r3); + } + } + + mod test_p { + use super::*; + + #[test] + fn p_test() { + let mut v0: u64 = 862185360016812330; + let mut v1: u64 = 9264562855185177247; + let mut v2: u64 = 17733520444968542606; + let mut v3: u64 = 13219822890422175473; + let mut v4: u64 = 6801067205434763034; + let mut v5: u64 = 10578543507696639262; + let mut v6: u64 = 10108704228654865903; + let mut v7: u64 = 2299791359568756431; + let mut v8: u64 = 15201093463674093404; + let mut v9: u64 = 13723714563716750079; + let mut v10: u64 = 9719717710557384967; + let mut v11: u64 = 1845563056782807427; + let mut v12: u64 = 1829242492466781631; + let mut v13: u64 = 17659944659119723559; + let mut v14: u64 = 14852831888916040100; + let mut v15: u64 = 12286853237524317048; + + let r0: u64 = 560590257705063197; + let r1: u64 = 9520578903939690713; + let r2: u64 = 3436672759520932446; + let r3: u64 = 14405027955696943046; + let r4: u64 = 17277966793721620420; + let r5: u64 = 3246848157586690114; + let r6: u64 = 13237761561989265024; + let r7: u64 = 9829692378347117758; + let r8: u64 = 1155007077473720963; + let r9: u64 = 10252695060491707233; + let r10: u64 = 10189249967016125740; + let r11: u64 = 14693238843422479195; + let r12: u64 = 13413025648622208818; + let r13: u64 = 16791374424966705294; + let r14: u64 = 11596653054387906253; + let r15: u64 = 12616166200637387407; + + permutation_p( + &mut v0, &mut v1, &mut v2, &mut v3, &mut v4, &mut v5, &mut v6, &mut v7, &mut v8, + &mut v9, &mut v10, &mut v11, &mut v12, &mut v13, &mut v14, &mut v15, + ); + + assert_eq!(v0, r0); + assert_eq!(v1, r1); + assert_eq!(v2, r2); + assert_eq!(v3, r3); + assert_eq!(v4, r4); + assert_eq!(v5, r5); + assert_eq!(v6, r6); + assert_eq!(v7, r7); + assert_eq!(v8, r8); + assert_eq!(v9, r9); + assert_eq!(v10, r10); + assert_eq!(v11, r11); + assert_eq!(v12, r12); + assert_eq!(v13, r13); + assert_eq!(v14, r14); + assert_eq!(v15, r15); + } + } +} diff --git a/vendor/orion/src/hazardous/kdf/hkdf.rs b/vendor/orion/src/hazardous/kdf/hkdf.rs new file mode 100644 index 0000000..371566a --- /dev/null +++ b/vendor/orion/src/hazardous/kdf/hkdf.rs @@ -0,0 +1,522 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `salt`: Salt value. +//! - `ikm`: Input keying material. +//! - `info`: Optional context and application-specific information. If [`None`] +//! then it's an empty string. +//! - `dst_out`: Destination buffer for the derived key. The length of the +//! derived key is implied by the length of `okm_out`. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than 1. +//! - The length of `dst_out` is greater than 255 * SHA(256/384/512)_OUTSIZE. +//! +//! # Security: +//! - Salts should always be generated using a CSPRNG. +//! [`secure_rand_bytes()`] can be used for this. +//! - The recommended length for a salt is 64 bytes. +//! - Even though a salt value is optional, it is strongly recommended to use one. +//! - HKDF is not suitable for password storage. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::{hazardous::kdf::hkdf, util}; +//! +//! let mut salt = [0u8; 64]; +//! util::secure_rand_bytes(&mut salt)?; +//! let mut okm_out = [0u8; 32]; +//! +//! hkdf::sha512::derive_key(&salt, "IKM".as_bytes(), None, &mut okm_out)?; +//! +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`secure_rand_bytes()`]: crate::util::secure_rand_bytes + +use crate::errors::UnknownCryptoError; +use crate::hazardous::mac::hmac; +use zeroize::Zeroize; + +/// The HKDF extract step. +/// +/// NOTE: Hmac has the output size of the hash function defined, +/// but the array initialization with the size cannot depend on a generic parameter, +/// because we don't have full support for const generics yet. +fn _extract( + salt: &[u8], + ikm: &[u8], +) -> Result<[u8; OUTSIZE], UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + debug_assert_eq!(OUTSIZE, Hmac::HASH_FUNC_OUTSIZE); + let mut dest = [0u8; OUTSIZE]; + + let mut ctx = Hmac::_new(salt)?; + ctx._update(ikm)?; + ctx._finalize(&mut dest)?; + + Ok(dest) +} + +/// The HKDF expand step. +fn _expand( + prk: &[u8], + info: Option<&[u8]>, + dest: &mut [u8], +) -> Result<(), UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + debug_assert_eq!(OUTSIZE, Hmac::HASH_FUNC_OUTSIZE); + debug_assert_eq!(prk.len(), Hmac::HASH_FUNC_OUTSIZE); + if dest.is_empty() || dest.len() > 255 * Hmac::HASH_FUNC_OUTSIZE { + return Err(UnknownCryptoError); + } + + let optional_info = info.unwrap_or(&[0u8; 0]); + let mut ctx = Hmac::_new(prk)?; + + // We require a temporary buffer in case the requested bytes + // to derive are lower than the HMAC functions output size. + let mut tmp = [0u8; OUTSIZE]; + let mut idx: u8 = 1; + for hlen_block in dest.chunks_mut(Hmac::HASH_FUNC_OUTSIZE) { + ctx._update(optional_info)?; + ctx._update(&[idx])?; + debug_assert!(!hlen_block.is_empty() && hlen_block.len() <= Hmac::HASH_FUNC_OUTSIZE); + ctx._finalize(&mut tmp)?; + hlen_block.copy_from_slice(&tmp[..hlen_block.len()]); + + if hlen_block.len() < Hmac::HASH_FUNC_OUTSIZE { + break; + } + match idx.checked_add(1) { + Some(next) => { + idx = next; + ctx._reset(); + ctx._update(hlen_block)?; + } + // If `idx` reaches 255, the maximum (255 * Hmac::HASH_FUNC_OUTSIZE) + // amount of blocks have been processed. + None => break, + }; + } + + tmp.iter_mut().zeroize(); + + Ok(()) +} + +/// Combine `extract` and `expand` to return a derived key. +/// +/// NOTE: See comment about const param at _extract function. +fn _derive_key( + salt: &[u8], + ikm: &[u8], + info: Option<&[u8]>, + dest: &mut [u8], +) -> Result<(), UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + _expand::(&_extract::(salt, ikm)?, info, dest) +} + +/// HKDF-HMAC-SHA256 (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the [RFC 5869](https://tools.ietf.org/html/rfc5869). +pub mod sha256 { + use super::*; + use crate::hazardous::hash::sha2::sha256::SHA256_OUTSIZE; + pub use crate::hazardous::mac::hmac::sha256::Tag; + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF extract step. + pub fn extract(salt: &[u8], ikm: &[u8]) -> Result { + Ok(Tag::from(_extract::< + hmac::sha256::HmacSha256, + { SHA256_OUTSIZE }, + >(salt, ikm)?)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF expand step. + pub fn expand( + prk: &Tag, + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _expand::( + prk.unprotected_as_bytes(), + info, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Combine `extract` and `expand` to return a derived key. + pub fn derive_key( + salt: &[u8], + ikm: &[u8], + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::(salt, ikm, info, dst_out) + } + + #[cfg(test)] + #[cfg(feature = "safe_api")] + // Mark safe_api because currently it only contains proptests. + mod test_derive_key { + use crate::hazardous::hash::sha2::sha256::SHA256_OUTSIZE; + + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Using derive_key() should always yield the same result + /// as using extract and expand separately. + fn prop_test_derive_key_same_separate( + salt: Vec, + ikm: Vec, + info: Vec, + outsize: usize, + ) -> bool { + let outsize_checked = if outsize == 0 || outsize > 255 * SHA256_OUTSIZE { + 64 + } else { + outsize + }; + + let prk = extract(&salt[..], &ikm[..]).unwrap(); + let mut out = vec![0u8; outsize_checked]; + expand(&prk, Some(&info[..]), &mut out).unwrap(); + + let mut out_one_shot = vec![0u8; outsize_checked]; + derive_key(&salt[..], &ikm[..], Some(&info[..]), &mut out_one_shot).unwrap(); + + out == out_one_shot + } + } +} + +/// HKDF-HMAC-SHA384 (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the [RFC 5869](https://tools.ietf.org/html/rfc5869). +pub mod sha384 { + use super::*; + use crate::hazardous::hash::sha2::sha384::SHA384_OUTSIZE; + pub use crate::hazardous::mac::hmac::sha384::Tag; + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF extract step. + pub fn extract(salt: &[u8], ikm: &[u8]) -> Result { + Ok(Tag::from(_extract::< + hmac::sha384::HmacSha384, + { SHA384_OUTSIZE }, + >(salt, ikm)?)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF expand step. + pub fn expand( + prk: &Tag, + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _expand::( + prk.unprotected_as_bytes(), + info, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Combine `extract` and `expand` to return a derived key. + pub fn derive_key( + salt: &[u8], + ikm: &[u8], + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::(salt, ikm, info, dst_out) + } + + #[cfg(test)] + #[cfg(feature = "safe_api")] + // Mark safe_api because currently it only contains proptests. + mod test_derive_key { + use crate::hazardous::hash::sha2::sha384::SHA384_OUTSIZE; + + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Using derive_key() should always yield the same result + /// as using extract and expand separately. + fn prop_test_derive_key_same_separate( + salt: Vec, + ikm: Vec, + info: Vec, + outsize: usize, + ) -> bool { + let outsize_checked = if outsize == 0 || outsize > 255 * SHA384_OUTSIZE { + 64 + } else { + outsize + }; + + let prk = extract(&salt[..], &ikm[..]).unwrap(); + let mut out = vec![0u8; outsize_checked]; + expand(&prk, Some(&info[..]), &mut out).unwrap(); + + let mut out_one_shot = vec![0u8; outsize_checked]; + derive_key(&salt[..], &ikm[..], Some(&info[..]), &mut out_one_shot).unwrap(); + + out == out_one_shot + } + } +} + +/// HKDF-HMAC-SHA512 (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the [RFC 5869](https://tools.ietf.org/html/rfc5869). +pub mod sha512 { + use super::*; + use crate::hazardous::hash::sha2::sha512::SHA512_OUTSIZE; + pub use crate::hazardous::mac::hmac::sha512::Tag; + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF extract step. + pub fn extract(salt: &[u8], ikm: &[u8]) -> Result { + Ok(Tag::from(_extract::< + hmac::sha512::HmacSha512, + { SHA512_OUTSIZE }, + >(salt, ikm)?)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// The HKDF expand step. + pub fn expand( + prk: &Tag, + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _expand::( + prk.unprotected_as_bytes(), + info, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Combine `extract` and `expand` to return a derived key. + pub fn derive_key( + salt: &[u8], + ikm: &[u8], + info: Option<&[u8]>, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::(salt, ikm, info, dst_out) + } + + #[cfg(test)] + #[cfg(feature = "safe_api")] + // Mark safe_api because currently it only contains proptests. + mod test_derive_key { + use crate::hazardous::hash::sha2::sha512::SHA512_OUTSIZE; + + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Using derive_key() should always yield the same result + /// as using extract and expand separately. + fn prop_test_derive_key_same_separate( + salt: Vec, + ikm: Vec, + info: Vec, + outsize: usize, + ) -> bool { + let outsize_checked = if outsize == 0 || outsize > 255 * SHA512_OUTSIZE { + 64 + } else { + outsize + }; + + let prk = extract(&salt[..], &ikm[..]).unwrap(); + let mut out = vec![0u8; outsize_checked]; + expand(&prk, Some(&info[..]), &mut out).unwrap(); + + let mut out_one_shot = vec![0u8; outsize_checked]; + derive_key(&salt[..], &ikm[..], Some(&info[..]), &mut out_one_shot).unwrap(); + + out == out_one_shot + } + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + use crate::hazardous::hash::sha2::{ + sha256::SHA256_OUTSIZE, sha384::SHA384_OUTSIZE, sha512::SHA512_OUTSIZE, + }; + + #[test] + fn hkdf_above_maximum_length_err() { + let mut okm_out = [0u8; 255 * SHA256_OUTSIZE + 1]; + let prk = sha256::extract(b"", b"").unwrap(); + assert!(sha256::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha256::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + + let mut okm_out = [0u8; 255 * SHA384_OUTSIZE + 1]; + let prk = sha384::extract(b"", b"").unwrap(); + assert!(sha384::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha384::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + + let mut okm_out = [0u8; 255 * SHA512_OUTSIZE + 1]; + let prk = sha512::extract(b"", b"").unwrap(); + assert!(sha512::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha512::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + } + + #[test] + fn hkdf_exact_maximum_length_ok() { + let mut okm_out = [0u8; 255 * SHA256_OUTSIZE]; + let prk = sha256::extract(b"", b"").unwrap(); + assert!(sha256::expand(&prk, Some(b""), &mut okm_out).is_ok()); + assert!(sha256::derive_key(b"", b"", Some(b""), &mut okm_out).is_ok()); + + let mut okm_out = [0u8; 255 * SHA384_OUTSIZE]; + let prk = sha384::extract(b"", b"").unwrap(); + assert!(sha384::expand(&prk, Some(b""), &mut okm_out).is_ok()); + assert!(sha384::derive_key(b"", b"", Some(b""), &mut okm_out).is_ok()); + + let mut okm_out = [0u8; 255 * SHA512_OUTSIZE]; + let prk = sha512::extract(b"", b"").unwrap(); + assert!(sha512::expand(&prk, Some(b""), &mut okm_out).is_ok()); + assert!(sha512::derive_key(b"", b"", Some(b""), &mut okm_out).is_ok()); + } + + #[test] + fn hkdf_zero_length_err() { + let mut okm_out = [0u8; 0]; + + let prk = sha256::extract(b"", b"").unwrap(); + assert!(sha256::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha256::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + + let prk = sha384::extract(b"", b"").unwrap(); + assert!(sha384::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha384::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + + let prk = sha512::extract(b"", b"").unwrap(); + assert!(sha512::expand(&prk, Some(b""), &mut okm_out).is_err()); + assert!(sha512::derive_key(b"", b"", Some(b""), &mut okm_out).is_err()); + } + + #[test] + fn hkdf_info_param() { + // Test that using None or empty array as info is the same. + let mut okm_out = [0u8; 32]; + let mut okm_out_verify = [0u8; 32]; + + let prk = sha256::extract(b"", b"").unwrap(); + assert!(sha256::expand(&prk, Some(b""), &mut okm_out).is_ok()); // Use info Some + assert!(sha256::derive_key(b"", b"", None, &mut okm_out_verify).is_ok()); + assert_eq!(okm_out, okm_out_verify); + + let prk = sha384::extract(b"", b"").unwrap(); + assert!(sha384::expand(&prk, Some(b""), &mut okm_out).is_ok()); // Use info Some + assert!(sha384::derive_key(b"", b"", None, &mut okm_out_verify).is_ok()); + assert_eq!(okm_out, okm_out_verify); + + let prk = sha512::extract(b"", b"").unwrap(); + assert!(sha512::expand(&prk, Some(b""), &mut okm_out).is_ok()); // Use info Some + assert!(sha512::derive_key(b"", b"", None, &mut okm_out_verify).is_ok()); + assert_eq!(okm_out, okm_out_verify); + } + + #[test] + fn hkdf_wrong_salt() { + let ikm = b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + let salt = b"000102030405060708090a0b0c"; + let info = b"f0f1f2f3f4f5f6f7f8f9"; + let mut okm_out = [0u8; 42]; + let mut okm_out_verify = [0u8; 42]; + + sha256::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha256::derive_key(b"", ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + + sha384::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha384::derive_key(b"", ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + + sha512::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha512::derive_key(b"", ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + } + + #[test] + fn hkdf_verify_wrong_ikm() { + let ikm = b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + let salt = b"000102030405060708090a0b0c"; + let info = b"f0f1f2f3f4f5f6f7f8f9"; + let mut okm_out = [0u8; 42]; + let mut okm_out_verify = [0u8; 42]; + + sha256::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha256::derive_key(salt, b"", Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + + sha384::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha384::derive_key(salt, b"", Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + + sha512::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha512::derive_key(salt, b"", Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out, okm_out_verify); + } + + #[test] + fn verify_diff_length() { + let ikm = b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + let salt = b"000102030405060708090a0b0c"; + let info = b"f0f1f2f3f4f5f6f7f8f9"; + let mut okm_out = [0u8; 42]; + let mut okm_out_verify = [0u8; 43]; + + sha256::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha256::derive_key(salt, ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out[..], okm_out_verify[..]); + + sha384::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha384::derive_key(salt, ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out[..], okm_out_verify[..]); + + sha512::derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + sha512::derive_key(salt, ikm, Some(info), &mut okm_out_verify).unwrap(); + assert_ne!(okm_out[..], okm_out_verify[..]); + } +} diff --git a/vendor/orion/src/hazardous/kdf/mod.rs b/vendor/orion/src/hazardous/kdf/mod.rs new file mode 100644 index 0000000..2806876 --- /dev/null +++ b/vendor/orion/src/hazardous/kdf/mod.rs @@ -0,0 +1,31 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// HKDF (HMAC-based Extract-and-Expand Key Derivation Function) as specified in the [RFC 5869](https://tools.ietf.org/html/rfc5869). +pub mod hkdf; + +/// PBKDF2(Password-Based Key Derivation Function 2) as specified in the [RFC 8018](https://tools.ietf.org/html/rfc8018). +pub mod pbkdf2; + +#[cfg(any(feature = "safe_api", feature = "alloc"))] +/// Argon2i password hashing function as described in the [P-H-C specification](https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf). +pub mod argon2i; diff --git a/vendor/orion/src/hazardous/kdf/pbkdf2.rs b/vendor/orion/src/hazardous/kdf/pbkdf2.rs new file mode 100644 index 0000000..9ed23b0 --- /dev/null +++ b/vendor/orion/src/hazardous/kdf/pbkdf2.rs @@ -0,0 +1,579 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `password`: Password. +//! - `salt`: Salt value. +//! - `iterations`: Iteration count. +//! - `dst_out`: Destination buffer for the derived key. The length of the +//! derived key is implied by the length of `dst_out`. +//! - `expected`: The expected derived key. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than 1. +//! - The specified iteration count is less than 1. +//! - The hashed password does not match the expected when verifying. +//! +//! # Panics: +//! A panic will occur if: +//! - The length of `dst_out` is greater than (2^32 - 1) * SHA(256/384/512)_OUTSIZE. +//! +//! # Security: +//! - Use [`Password::generate()`] to randomly generate a password of the same length as +//! the underlying SHA2 hash functions blocksize. +//! - Salts should always be generated using a CSPRNG. +//! [`secure_rand_bytes()`] can be used for this. +//! - The recommended length for a salt is 64 bytes. +//! - The iteration count should be set as high as feasible. Please check [OWASP] for +//! the recommended minimum amount (600000 at the time of writing). +//! - Please note that when verifying, a copy of the computed password hash is placed into +//! `dst_out`. If the derived hash is considered sensitive and you want to provide defense +//! in depth against an attacker reading your application's private memory, then you as +//! the user are responsible for zeroing out this buffer (see the [`zeroize` crate]). +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::{hazardous::kdf::pbkdf2, util}; +//! +//! let mut salt = [0u8; 64]; +//! util::secure_rand_bytes(&mut salt)?; +//! let password = pbkdf2::sha512::Password::from_slice("Secret password".as_bytes())?; +//! let mut dst_out = [0u8; 64]; +//! +//! pbkdf2::sha512::derive_key(&password, &salt, 10000, &mut dst_out)?; +//! +//! let expected_dk = dst_out; +//! +//! assert!(pbkdf2::sha512::verify(&expected_dk, &password, &salt, 10000, &mut dst_out).is_ok()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`Password::generate()`]: pbkdf2::sha512::Password::generate +//! [`secure_rand_bytes()`]: crate::util::secure_rand_bytes +//! [`zeroize` crate]: https://crates.io/crates/zeroize +//! [OWASP]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html + +use crate::{errors::UnknownCryptoError, hazardous::mac::hmac}; + +/// The F function as described in the RFC. +fn _function_f( + salt: &[u8], + iterations: usize, + index: u32, + dk_block: &mut [u8], + block_len: usize, + u_step: &mut [u8], + hmac: &mut Hmac, +) -> Result<(), UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + debug_assert_eq!(u_step.len(), Hmac::HASH_FUNC_OUTSIZE); + hmac._update(salt)?; + hmac._update(&index.to_be_bytes())?; + hmac._finalize(u_step)?; + debug_assert!(block_len <= u_step.len()); + dk_block.copy_from_slice(&u_step[..block_len]); + + if iterations > 1 { + for _ in 1..iterations { + hmac._reset(); + hmac._update(u_step)?; + hmac._finalize(u_step)?; + xor_slices!(u_step, dk_block); + } + } + + Ok(()) +} + +/// +/// +/// NOTE: Hmac has the output size of the hash function defined, +/// but the array initialization with the size cannot depend on a generic parameter, +/// because we don't have full support for const generics yet. +fn _derive_key( + padded_password: &[u8], + salt: &[u8], + iterations: usize, + dest: &mut [u8], +) -> Result<(), UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + debug_assert_eq!(OUTSIZE, Hmac::HASH_FUNC_OUTSIZE); + if dest.is_empty() || iterations < 1 { + return Err(UnknownCryptoError); + } + + let mut u_step = [0u8; OUTSIZE]; + let mut hmac = Hmac::_new(padded_password)?; + for (idx, dk_block) in dest.chunks_mut(Hmac::HASH_FUNC_OUTSIZE).enumerate() { + // If this panics, then the size limit for PBKDF2 is reached. + let block_idx: u32 = 1u32.checked_add(idx as u32).unwrap(); + + _function_f( + salt, + iterations, + block_idx, + dk_block, + dk_block.len(), + &mut u_step, + &mut hmac, + )?; + + hmac._reset(); + } + + Ok(()) +} + +/// +/// +/// NOTE: Hmac has the output size of the hash function defined, +/// but the array initialization with the size cannot depend on a generic parameter, +/// because we don't have full support for const generics yet. +fn _verify( + expected: &[u8], + padded_password: &[u8], + salt: &[u8], + iterations: usize, + dest: &mut [u8], +) -> Result<(), UnknownCryptoError> +where + Hmac: hmac::HmacFunction, +{ + debug_assert_eq!(OUTSIZE, Hmac::HASH_FUNC_OUTSIZE); + _derive_key::(padded_password, salt, iterations, dest)?; + crate::util::secure_cmp(expected, dest) +} + +/// PBKDF2-HMAC-SHA256 (Password-Based Key Derivation Function 2) as specified in the [RFC 8018](https://tools.ietf.org/html/rfc8018). +pub mod sha256 { + use super::*; + use crate::hazardous::hash::sha2::sha256::{self, Sha256}; + + construct_hmac_key! { + /// A type to represent the `Password` that PBKDF2 hashes. + /// + /// # Note: + /// Because `Password` is used as a `SecretKey` for HMAC during hashing, `Password` already + /// pads the given password to a length of 64, for use in HMAC, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the password with padding. + /// + /// Using `get_length()` will return the length with padding (always 64). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Password, Sha256, sha256::SHA256_OUTSIZE, test_pbkdf2_password, sha256::SHA256_BLOCKSIZE) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Derive a key using PBKDF2-HMAC-SHA256. + pub fn derive_key( + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::( + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify PBKDF2-HMAC-SHA256 derived key in constant time. + pub fn verify( + expected: &[u8], + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _verify::( + expected, + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } +} + +/// PBKDF2-HMAC-SHA384 (Password-Based Key Derivation Function 2) as specified in the [RFC 8018](https://tools.ietf.org/html/rfc8018). +pub mod sha384 { + use super::*; + use crate::hazardous::hash::sha2::sha384::{self, Sha384}; + + construct_hmac_key! { + /// A type to represent the `Password` that PBKDF2 hashes. + /// + /// # Note: + /// Because `Password` is used as a `SecretKey` for HMAC during hashing, `Password` already + /// pads the given password to a length of 128, for use in HMAC, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the password with padding. + /// + /// Using `get_length()` will return the length with padding (always 128). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Password, Sha384, sha384::SHA384_OUTSIZE, test_pbkdf2_password, sha384::SHA384_BLOCKSIZE) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Derive a key using PBKDF2-HMAC-SHA384. + pub fn derive_key( + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::( + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify PBKDF2-HMAC-SHA384 derived key in constant time. + pub fn verify( + expected: &[u8], + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _verify::( + expected, + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } +} + +/// PBKDF2-HMAC-SHA512 (Password-Based Key Derivation Function 2) as specified in the [RFC 8018](https://tools.ietf.org/html/rfc8018). +pub mod sha512 { + use super::*; + use crate::hazardous::hash::sha2::sha512::{self, Sha512}; + + construct_hmac_key! { + /// A type to represent the `Password` that PBKDF2 hashes. + /// + /// # Note: + /// Because `Password` is used as a `SecretKey` for HMAC during hashing, `Password` already + /// pads the given password to a length of 128, for use in HMAC, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the password with padding. + /// + /// Using `get_length()` will return the length with padding (always 128). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Password, Sha512, sha512::SHA512_OUTSIZE, test_pbkdf2_password, sha512::SHA512_BLOCKSIZE) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Derive a key using PBKDF2-HMAC-SHA512. + pub fn derive_key( + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _derive_key::( + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify PBKDF2-HMAC-SHA512 derived key in constant time. + pub fn verify( + expected: &[u8], + password: &Password, + salt: &[u8], + iterations: usize, + dst_out: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + _verify::( + expected, + password.unprotected_as_bytes(), + salt, + iterations, + dst_out, + ) + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_verify { + use super::*; + + #[test] + fn verify_true() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 128; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; + + sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap(); + assert!(sha256::verify( + &okm_out, + &password_256, + salt, + iterations, + &mut okm_out_verify + ) + .is_ok()); + + sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap(); + assert!(sha384::verify( + &okm_out, + &password_384, + salt, + iterations, + &mut okm_out_verify + ) + .is_ok()); + + sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap(); + assert!(sha512::verify( + &okm_out, + &password_512, + salt, + iterations, + &mut okm_out_verify + ) + .is_ok()); + } + + #[test] + fn verify_false_wrong_salt() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 128; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; + + sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap(); + assert!(sha256::verify( + &okm_out, + &password_256, + b"", + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap(); + assert!(sha384::verify( + &okm_out, + &password_384, + b"", + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap(); + assert!(sha512::verify( + &okm_out, + &password_512, + b"", + iterations, + &mut okm_out_verify + ) + .is_err()); + } + #[test] + fn verify_false_wrong_password() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 128; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; + + sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap(); + assert!(sha256::verify( + &okm_out, + &sha256::Password::from_slice(b"pass").unwrap(), + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap(); + assert!(sha384::verify( + &okm_out, + &sha384::Password::from_slice(b"pass").unwrap(), + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap(); + assert!(sha512::verify( + &okm_out, + &sha512::Password::from_slice(b"pass").unwrap(), + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + } + + #[test] + fn verify_diff_dklen_error() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 128; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 32]; + + sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap(); + assert!(sha256::verify( + &okm_out, + &password_256, + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap(); + assert!(sha384::verify( + &okm_out, + &password_384, + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + + sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap(); + assert!(sha512::verify( + &okm_out, + &password_512, + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + } + + #[test] + fn verify_diff_iter_error() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 128; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; + + sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap(); + assert!( + sha256::verify(&okm_out, &password_256, salt, 127, &mut okm_out_verify).is_err() + ); + + sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap(); + assert!( + sha384::verify(&okm_out, &password_384, salt, 127, &mut okm_out_verify).is_err() + ); + + sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap(); + assert!( + sha512::verify(&okm_out, &password_512, salt, 127, &mut okm_out_verify).is_err() + ); + } + } + + mod test_derive_key { + use super::*; + + #[test] + fn zero_iterations_err() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "salt".as_bytes(); + let iterations: usize = 0; + let mut okm_out = [0u8; 15]; + + assert!(sha256::derive_key(&password_256, salt, iterations, &mut okm_out).is_err()); + assert!(sha384::derive_key(&password_384, salt, iterations, &mut okm_out).is_err()); + assert!(sha512::derive_key(&password_512, salt, iterations, &mut okm_out).is_err()); + } + + #[test] + fn zero_dklen_err() { + let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap(); + let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap(); + + let salt = "salt".as_bytes(); + let iterations: usize = 1; + let mut okm_out = [0u8; 0]; + + assert!(sha256::derive_key(&password_256, salt, iterations, &mut okm_out).is_err()); + assert!(sha384::derive_key(&password_384, salt, iterations, &mut okm_out).is_err()); + assert!(sha512::derive_key(&password_512, salt, iterations, &mut okm_out).is_err()); + } + } +} diff --git a/vendor/orion/src/hazardous/kem/mod.rs b/vendor/orion/src/hazardous/kem/mod.rs new file mode 100644 index 0000000..767b653 --- /dev/null +++ b/vendor/orion/src/hazardous/kem/mod.rs @@ -0,0 +1,25 @@ +// MIT License + +// Copyright (c) 2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#[cfg(feature = "safe_api")] +/// DHKEM(X25519, HKDF-SHA256) as specified in HPKE [RFC 9180](https://www.rfc-editor.org/rfc/rfc9180.html). +pub mod x25519_hkdf_sha256; diff --git a/vendor/orion/src/hazardous/kem/x25519_hkdf_sha256.rs b/vendor/orion/src/hazardous/kem/x25519_hkdf_sha256.rs new file mode 100644 index 0000000..0ea9609 --- /dev/null +++ b/vendor/orion/src/hazardous/kem/x25519_hkdf_sha256.rs @@ -0,0 +1,289 @@ +// MIT License + +// Copyright (c) 2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `public_recipient`: The public X25519 key of the recipient. +//! - `public_ephemeral`: The ephemeral X25519 key fro this KEM operation. +//! - `secret_recipient`: The private X25519 of the recipient. +//! - `secret_sender`: The private X25519 of the sender. +//! +//! # Errors: +//! An error will be returned if: +//! - If a shared X25519 secret is all-zero. +//! - If `ikm.len() < 32` when calling [`derive_keypair()`]. +//! +//! # Panics: +//! A panic will occur if: +//! - [`generate()`] panics during [`encap()`], [`auth_encap()`], [`decap()`] or [`auth_decap()`]. +//! +//! # Security: +//! - The `ikm` used as input for [`derive_keypair()`] must never be reused. +//! - This KEM is vulnerable to key-compromise impersonation attacks (KCI), meaning +//! that if the recipients private key `secret_recipient` is leaked at any point, sender authentication +//! no longer holds. See [KCI section](https://www.rfc-editor.org/rfc/rfc9180.html#section-9.1.1) of the RFC +//! on recommendations on how to mitigate this. +//! - Please refer to the RFC for a detailed description of all security properties provided: . +//! +//! # 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 + pub const KEM_ID: u16 = 0x0020; + + /// Version of HPKE implemented. See . + pub const HPKE_VERSION_ID: &'static str = "HPKE-v1"; + + /// Length of bytes of a shared secret produced by this KEM. See . + const N_SECRET: u16 = 32; + + fn labeled_extract( + salt: &[u8], + label: &[u8; 7], + ikm: &[u8], + ) -> Result { + let mut labeled_ikm = Vec::::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( + prk: &sha256::Tag, + label: &[u8], + info: &[u8], + ) -> Result, UnknownCryptoError> { + let mut labeled_info = Vec::::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, 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 { + 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 { + let mut dh = [0u8; 64]; + dh[..32].copy_from_slice( + x25519::key_agreement(secret_recipient, public_ephemeral)?.unprotected_as_bytes(), + ); + dh[32..64].copy_from_slice( + x25519::key_agreement(secret_recipient, public_sender)?.unprotected_as_bytes(), + ); + + let mut kem_context = [0u8; 32 * 3]; + kem_context[..32].copy_from_slice(&public_ephemeral.to_bytes()); + kem_context[32..64].copy_from_slice(&PublicKey::try_from(secret_recipient)?.to_bytes()); + kem_context[64..96].copy_from_slice(&public_sender.to_bytes()); + + let shared_secret = Self::extract_and_expand(&dh, &kem_context)?; + dh.iter_mut().zeroize(); + + SharedSecret::from_slice(&shared_secret) + } +} + +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use crate::hazardous::ecc::x25519::{PrivateKey, PublicKey}; + use crate::hazardous::kem::x25519_hkdf_sha256::*; + + #[test] + fn encap_decap_roundtrip() { + let recipient_secret = PrivateKey::generate(); + let recipient_public = PublicKey::try_from(&recipient_secret).unwrap(); + + let (shared_secret_1, public_eph) = DhKem::encap(&recipient_public).unwrap(); + let shared_secret_2 = DhKem::decap(&public_eph, &recipient_secret).unwrap(); + + assert_eq!(shared_secret_1, shared_secret_2); + } + + #[test] + fn auth_encap_decap_roundtrip() { + let sender_secret = PrivateKey::generate(); + let sender_public = PublicKey::try_from(&sender_secret).unwrap(); + + let recipient_secret = PrivateKey::generate(); + let recipient_public = PublicKey::try_from(&recipient_secret).unwrap(); + + let (shared_secret_1, public_eph) = + DhKem::auth_encap(&recipient_public, &sender_secret).unwrap(); + let shared_secret_2 = + DhKem::auth_decap(&public_eph, &recipient_secret, &sender_public).unwrap(); + + assert_eq!(shared_secret_1, shared_secret_2); + } +} diff --git a/vendor/orion/src/hazardous/mac/blake2b.rs b/vendor/orion/src/hazardous/mac/blake2b.rs new file mode 100644 index 0000000..9b9bfab --- /dev/null +++ b/vendor/orion/src/hazardous/mac/blake2b.rs @@ -0,0 +1,435 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `secret_key`: The authentication key. +//! - `size`: The desired output length for the authentication tag. +//! - `data`: Data to be authenticated. +//! - `expected`: The expected authentication tag. +//! +//! # Errors: +//! An error will be returned if: +//! - `size` is 0 or greater than 64. +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) bytes of data are hashed. +//! +//! # Security: +//! - The secret key should always be generated using a CSPRNG. +//! [`SecretKey::generate()`] can be used for this. It generates +//! a secret key of 32 bytes. +//! - The minimum recommended size for a secret key is 32 bytes. +//! - The recommended minimum output size is 32. +//! - This interface only allows creating authentication tag using BLAKE2b. If hash digests are needed, +//! please refer to the [`hash::blake2::blake2b`] module. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::mac::blake2b::{Blake2b, SecretKey}; +//! +//! let key = SecretKey::generate(); +//! +//! let mut state = Blake2b::new(&key, 64)?; +//! state.update(b"Some data")?; +//! let tag = state.finalize()?; +//! +//! assert!(Blake2b::verify(&tag, &key, 64, b"Some data").is_ok()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: blake2b::Blake2b::update +//! [`reset()`]: blake2b::Blake2b::reset +//! [`finalize()`]: blake2b::Blake2b::finalize +//! [`SecretKey::generate()`]: blake2b::SecretKey::generate +//! [`hash::blake2::blake2b`]: crate::hazardous::hash::blake2::blake2b + +use crate::errors::UnknownCryptoError; +use crate::hazardous::hash::blake2::blake2b_core::{self, BLAKE2B_KEYSIZE, BLAKE2B_OUTSIZE}; +use core::ops::DerefMut; +use zeroize::Zeroizing; + +construct_secret_key! { + /// A type to represent the secret key that BLAKE2b uses for keyed mode. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `slice` is greater than 64 bytes. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, test_secret_key, 1, BLAKE2B_KEYSIZE, 32) +} + +construct_tag! { + /// A type to represent the `Tag` that BLAKE2b returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `slice` is greater than 64 bytes. + (Tag, test_tag, 1, BLAKE2B_OUTSIZE) +} + +#[derive(Debug, Clone)] +/// BLAKE2b streaming state. +pub struct Blake2b { + _state: blake2b_core::State, +} + +impl Blake2b { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize a `Blake2b` struct with a given size (in bytes) and key. + pub fn new(secret_key: &SecretKey, size: usize) -> Result { + Ok(Self { + _state: blake2b_core::State::_new(secret_key.unprotected_as_bytes(), size)?, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Reset to `new()` state. + pub fn reset(&mut self, secret_key: &SecretKey) -> Result<(), UnknownCryptoError> { + self._state._reset(secret_key.unprotected_as_bytes()) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a BLAKE2b tag. + pub fn finalize(&mut self) -> Result { + let mut tmp: Zeroizing<[u8; BLAKE2B_OUTSIZE]> = Zeroizing::new([0u8; BLAKE2B_OUTSIZE]); + self._state._finalize(tmp.deref_mut())?; + + Tag::from_slice(&tmp[..self._state.size]) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify a BLAKE2b tag in constant time. + pub fn verify( + expected: &Tag, + secret_key: &SecretKey, + size: usize, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + let mut ctx = Self::new(secret_key, size)?; + ctx.update(data)?; + + if &ctx.finalize()? == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } +} + +#[cfg(test)] +mod public { + mod test_streaming_interface_no_key { + use crate::errors::UnknownCryptoError; + use crate::hazardous::hash::blake2::blake2b_core::{ + compare_blake2b_states, BLAKE2B_BLOCKSIZE, BLAKE2B_OUTSIZE, + }; + use crate::hazardous::mac::blake2b::{Blake2b, SecretKey, Tag}; + use crate::test_framework::incremental_interface::{ + StreamingContextConsistencyTester, TestableStreamingContext, + }; + + const KEY: [u8; 32] = [255u8; 32]; + + impl TestableStreamingContext for Blake2b { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + let key = SecretKey::from_slice(&KEY).unwrap(); + self.reset(&key) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + let key = SecretKey::from_slice(&KEY).unwrap(); + let mut ctx = Blake2b::new(&key, BLAKE2B_OUTSIZE)?; + ctx.update(input)?; + ctx.finalize() + } + + fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> { + let actual = Self::one_shot(input)?; + + if &actual == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + + fn compare_states(state_1: &Blake2b, state_2: &Blake2b) { + compare_blake2b_states(&state_1._state, &state_2._state) + } + } + + #[test] + fn default_consistency_tests() { + let key = SecretKey::from_slice(&KEY).unwrap(); + let initial_state: Blake2b = Blake2b::new(&key, BLAKE2B_OUTSIZE).unwrap(); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + BLAKE2B_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec) -> bool { + let key = SecretKey::from_slice(&KEY).unwrap(); + let initial_state: Blake2b = Blake2b::new(&key, BLAKE2B_OUTSIZE).unwrap(); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + BLAKE2B_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + + mod test_new { + use crate::hazardous::mac::blake2b::{Blake2b, SecretKey}; + + #[test] + fn test_init_size() { + let sk = SecretKey::from_slice(&[0u8; 32]).unwrap(); + assert!(Blake2b::new(&sk, 0).is_err()); + assert!(Blake2b::new(&sk, 65).is_err()); + assert!(Blake2b::new(&sk, 1).is_ok()); + assert!(Blake2b::new(&sk, 64).is_ok()); + } + } + + #[cfg(feature = "safe_api")] + mod test_verify { + use crate::hazardous::mac::blake2b::{Blake2b, SecretKey}; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different key, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_key_false(data: Vec) -> bool { + let sk = SecretKey::generate(); + let mut state = Blake2b::new(&sk, 64).unwrap(); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + let bad_sk = SecretKey::generate(); + + Blake2b::verify(&tag, &bad_sk, 64, &data[..]).is_err() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different size, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_size_false(data: Vec, size_one: usize, size_two: usize) -> bool { + let (size_one, size_two) = match (size_one, size_two) { + (1..=64, 1..=64) => (size_one, size_two), + (_, _) => (32, 64), + }; + + let sk = SecretKey::generate(); + let mut state = Blake2b::new(&sk, size_one).unwrap(); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + + if size_one != size_two { + Blake2b::verify(&tag, &sk, size_two, &data[..]).is_err() + } else { + Blake2b::verify(&tag, &sk, size_two, &data[..]).is_ok() + } + } + } + + mod test_streaming_interface { + use crate::hazardous::hash::blake2::blake2b_core::compare_blake2b_states; + use crate::hazardous::mac::blake2b::{Blake2b, SecretKey}; + + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Testing different usage combinations of new(), update(), + /// finalize() and reset() produce the same Digest/Tag. + fn produces_same_hash(sk: &SecretKey, size: usize, data: &[u8]) { + // new(), update(), finalize() + let mut state_1 = Blake2b::new(sk, size).unwrap(); + state_1.update(data).unwrap(); + let res_1 = state_1.finalize().unwrap(); + + // new(), reset(), update(), finalize() + let mut state_2 = Blake2b::new(sk, size).unwrap(); + state_2.reset(sk).unwrap(); + state_2.update(data).unwrap(); + let res_2 = state_2.finalize().unwrap(); + + // new(), update(), reset(), update(), finalize() + let mut state_3 = Blake2b::new(sk, size).unwrap(); + state_3.update(data).unwrap(); + state_3.reset(sk).unwrap(); + state_3.update(data).unwrap(); + let res_3 = state_3.finalize().unwrap(); + + // new(), update(), finalize(), reset(), update(), finalize() + let mut state_4 = Blake2b::new(sk, size).unwrap(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(sk).unwrap(); + state_4.update(data).unwrap(); + let res_4 = state_4.finalize().unwrap(); + + assert_eq!(res_1, res_2); + assert_eq!(res_2, res_3); + assert_eq!(res_3, res_4); + + // Tests for the assumption that returning Ok() on empty update() calls + // with streaming APIs, gives the correct result. This is done by testing + // the reasoning that if update() is empty, returns Ok(), it is the same as + // calling new() -> finalize(). i.e not calling update() at all. + if data.is_empty() { + // new(), finalize() + let mut state_5 = Blake2b::new(sk, size).unwrap(); + let res_5 = state_5.finalize().unwrap(); + + // new(), reset(), finalize() + let mut state_6 = Blake2b::new(sk, size).unwrap(); + state_6.reset(sk).unwrap(); + let res_6 = state_6.finalize().unwrap(); + + // new(), update(), reset(), finalize() + let mut state_7 = Blake2b::new(sk, size).unwrap(); + state_7.update(b"Wrong data").unwrap(); + state_7.reset(sk).unwrap(); + let res_7 = state_7.finalize().unwrap(); + + assert_eq!(res_4, res_5); + assert_eq!(res_5, res_6); + assert_eq!(res_6, res_7); + } + } + + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Testing different usage combinations of new(), update(), + /// finalize() and reset() produce the same Digest/Tag. + fn produces_same_state(sk: &SecretKey, size: usize, data: &[u8]) { + // new() + let state_1 = Blake2b::new(sk, size).unwrap(); + + // new(), reset() + let mut state_2 = Blake2b::new(sk, size).unwrap(); + state_2.reset(sk).unwrap(); + + // new(), update(), reset() + let mut state_3 = Blake2b::new(sk, size).unwrap(); + state_3.update(data).unwrap(); + state_3.reset(sk).unwrap(); + + // new(), update(), finalize(), reset() + let mut state_4 = Blake2b::new(sk, size).unwrap(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(sk).unwrap(); + + compare_blake2b_states(&state_1._state, &state_2._state); + compare_blake2b_states(&state_2._state, &state_3._state); + compare_blake2b_states(&state_3._state, &state_4._state); + } + + #[test] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + fn test_produce_same_state() { + let sk = SecretKey::from_slice(b"Testing").unwrap(); + produces_same_state(&sk, 1, b"Tests"); + produces_same_state(&sk, 32, b"Tests"); + produces_same_state(&sk, 64, b"Tests"); + produces_same_state(&sk, 28, b"Tests"); + } + + #[test] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + fn test_produce_same_hash() { + let sk = SecretKey::from_slice(b"Testing").unwrap(); + produces_same_hash(&sk, 1, b"Tests"); + produces_same_hash(&sk, 32, b"Tests"); + produces_same_hash(&sk, 64, b"Tests"); + produces_same_hash(&sk, 28, b"Tests"); + + produces_same_hash(&sk, 1, b""); + produces_same_hash(&sk, 32, b""); + produces_same_hash(&sk, 64, b""); + produces_same_hash(&sk, 28, b""); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_hash_different_usage(data: Vec, size: usize) -> bool { + use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE; + + if (1..=BLAKE2B_OUTSIZE).contains(&size) { + // Will panic on incorrect results. + let sk = SecretKey::generate(); + produces_same_hash(&sk, size, &data[..]); + } + + true + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_state_different_usage(data: Vec, size: usize) -> bool { + use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE; + + if (1..=BLAKE2B_OUTSIZE).contains(&size) { + // Will panic on incorrect results. + let sk = SecretKey::generate(); + produces_same_state(&sk, size, &data[..]); + } + + true + } + } +} diff --git a/vendor/orion/src/hazardous/mac/hmac.rs b/vendor/orion/src/hazardous/mac/hmac.rs new file mode 100644 index 0000000..f5fecf2 --- /dev/null +++ b/vendor/orion/src/hazardous/mac/hmac.rs @@ -0,0 +1,919 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `secret_key`: The authentication key. +//! - `data`: Data to be authenticated. +//! - `expected`: The expected authentication tag. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! - The HMAC does not match the expected when verifying. +//! +//! # Security: +//! - The secret key should always be generated using a CSPRNG. +//! [`SecretKey::generate()`] can be used for this. +//! - The minimum recommended size for a secret key is 64 bytes. +//! +//! # Recommendation: +//! - If you are unsure of whether to use HMAC or Poly1305, it is most often +//! easier to just use HMAC. See also [Cryptographic Right Answers]. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::mac::hmac::sha512::{HmacSha512, SecretKey}; +//! +//! let key = SecretKey::generate(); +//! +//! let mut state = HmacSha512::new(&key); +//! state.update(b"Some message.")?; +//! let tag = state.finalize()?; +//! +//! assert!(HmacSha512::verify(&tag, &key, b"Some message.").is_ok()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: hmac::sha512::HmacSha512::update +//! [`reset()`]: hmac::sha512::HmacSha512::reset +//! [`finalize()`]: hmac::sha512::HmacSha512::finalize +//! [`SecretKey::generate()`]: hmac::sha512::SecretKey::generate +//! [Cryptographic Right Answers]: https://latacora.micro.blog/2018/04/03/cryptographic-right-answers.html + +use crate::errors::UnknownCryptoError; +use zeroize::Zeroize; + +/// A trait used to define a cryptographic hash function used by HMAC. +pub(crate) trait HmacHashFunction: Clone { + /// The blocksize of the hash function. + const _BLOCKSIZE: usize; + + /// The output size of the hash function. + const _OUTSIZE: usize; + + /// Create a new instance of the hash function. + fn _new() -> Self; + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError>; + + /// Finalize the hash and put the final digest into `dest`. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError>; + + /// Compute a digest of `data` and copy it into `dest`. + fn _digest(data: &[u8], dest: &mut [u8]) -> Result<(), UnknownCryptoError>; + + #[cfg(test)] + /// Compare two Sha2 state objects to check if their fields + /// are the same. + fn compare_state_to_other(&self, other: &Self); +} + +/// A trait used to define a HMAC function. +pub(crate) trait HmacFunction { + // NOTE: Clippy complains this is not used, however it is used in both HKDF and PBKDF2. Perhaps a bug + // with min_const_generics? + #[allow(dead_code)] + /// The output size of the internal hash function used. + const HASH_FUNC_OUTSIZE: usize; + + /// Create a new instance of the HMAC function, using a `secret_key` that may or may not be padded. + fn _new(secret_key: &[u8]) -> Result + where + Self: Sized; + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError>; + + /// Finalize the MAC and put the final tag into `dest`. + /// + /// NOTE: `dest` may be less than the complete output size of the hash function + /// (Self::HASH_FUNC_OUTSIZE). If that is the case, `dest.len()` bytes will be copied, + /// but `dest` should NEVER be empty. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError>; + + /// Reset the state. + fn _reset(&mut self); +} + +const IPAD: u8 = 0x36; +const OPAD: u8 = 0x5C; + +#[derive(Clone)] +pub(crate) struct Hmac { + working_hasher: S, + opad_hasher: S, + ipad_hasher: S, + is_finalized: bool, +} + +impl core::fmt::Debug for Hmac { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Hmac {{ working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: {:?} }}", + self.is_finalized + ) + } +} + +impl Hmac { + // NOTE: Clippy complains this is not used, however it is used in both HKDF and PBKDF2. Perhaps a bug + // with min_const_generics? + #[allow(dead_code)] + const HASH_FUNC_OUTSIZE: usize = S::_OUTSIZE; + + /// Construct a state from a `secret_key`. The `secret_key` may be pre-padded or not. + /// + /// Ref: https://brycx.github.io/2018/08/06/hmac-and-precomputation-optimization.html + fn _new(secret_key: &[u8]) -> Result { + debug_assert_eq!(S::_BLOCKSIZE, BLOCKSIZE); + let mut ipad = [IPAD; BLOCKSIZE]; + + if secret_key.len() > BLOCKSIZE { + // SK is NOT pre-padded. + debug_assert!(BLOCKSIZE > S::_OUTSIZE); + S::_digest(secret_key, &mut ipad[..S::_OUTSIZE])?; + for elem in ipad.iter_mut().take(S::_OUTSIZE) { + *elem ^= IPAD; + } + } else { + // SK has been pre-padded or SK.len() <= BLOCKSIZE. + // Because 0x00 xor IPAD = IPAD, the existence of padding bytes (0x00) + // within SK, during this operation, is inconsequential. + xor_slices!(secret_key, &mut ipad); + } + + let mut ih = S::_new(); + ih._update(&ipad)?; + + // Transform ipad into OPAD xor SK + for elem in ipad.iter_mut() { + *elem ^= IPAD ^ OPAD; + } + + let mut oh = S::_new(); + oh._update(&ipad)?; + + ipad.iter_mut().zeroize(); + + Ok(Self { + working_hasher: ih.clone(), + opad_hasher: oh, + ipad_hasher: ih, + is_finalized: false, + }) + } + + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + if self.is_finalized { + Err(UnknownCryptoError) + } else { + self.working_hasher._update(data) + } + } + + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + debug_assert!(!dest.is_empty()); + if self.is_finalized { + return Err(UnknownCryptoError); + } + + self.is_finalized = true; + let mut outer_hasher = self.opad_hasher.clone(); + self.working_hasher._finalize(dest)?; + outer_hasher._update(dest)?; + outer_hasher._finalize(dest) + } + + fn _reset(&mut self) { + self.working_hasher = self.ipad_hasher.clone(); + self.is_finalized = false; + } + + #[cfg(test)] + /// Compare two Hmac state objects to check if their fields + /// are the same. + pub(crate) fn compare_state_to_other(&self, other: &Self) { + self.working_hasher + .compare_state_to_other(&other.working_hasher); + self.opad_hasher.compare_state_to_other(&other.opad_hasher); + self.ipad_hasher.compare_state_to_other(&other.ipad_hasher); + assert_eq!(self.is_finalized, other.is_finalized); + } +} + +/// HMAC-SHA256 (Hash-based Message Authentication Code) as specified in the [RFC 2104](https://tools.ietf.org/html/rfc2104). +pub mod sha256 { + use super::*; + use crate::hazardous::hash::sha2::sha256::{self, Sha256}; + + construct_hmac_key! { + /// A type to represent the `SecretKey` that HMAC uses for authentication. + /// + /// # Note: + /// `SecretKey` pads the secret key for use with HMAC to a length of 64, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the secret key with padding. + /// + /// `len()` will return the length with padding (always 64). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, Sha256, sha256::SHA256_OUTSIZE, test_hmac_key, sha256::SHA256_BLOCKSIZE) + } + + construct_tag! { + /// A type to represent the `Tag` that HMAC returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + (Tag, test_tag, sha256::SHA256_OUTSIZE, sha256::SHA256_OUTSIZE) + } + + impl_from_trait!(Tag, sha256::SHA256_OUTSIZE); + + use super::Hmac; + + #[derive(Clone, Debug)] + /// HMAC-SHA256 streaming state. + pub struct HmacSha256 { + _state: Hmac, + } + + impl HmacSha256 { + fn _new(secret_key: &[u8]) -> Result { + Ok(Self { + _state: Hmac::::_new(secret_key)?, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize `HmacSha256` struct with a given key. + pub fn new(secret_key: &SecretKey) -> Self { + // NOTE: `secret_key` has been pre-padded so .unwrap() is OK. + Self::_new(secret_key.unprotected_as_bytes()).unwrap() + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self._state._reset() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Return a HMAC-SHA256 tag. + pub(crate) fn _finalize_internal( + &mut self, + dest: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a HMAC-SHA256 tag. + pub fn finalize(&mut self) -> Result { + let mut dest = [0u8; sha256::SHA256_OUTSIZE]; + self._finalize_internal(&mut dest)?; + + Ok(Tag::from(dest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// One-shot function for generating an HMAC-SHA256 tag of `data`. + pub fn hmac(secret_key: &SecretKey, data: &[u8]) -> Result { + let mut ctx = Self::new(secret_key); + ctx.update(data)?; + ctx.finalize() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify a HMAC-SHA256 tag in constant time. + pub fn verify( + expected: &Tag, + secret_key: &SecretKey, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + if &Self::hmac(secret_key, data)? == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + } + + impl HmacFunction for HmacSha256 { + /// The output size of the internal hash function used. + const HASH_FUNC_OUTSIZE: usize = sha256::SHA256_OUTSIZE; + + /// Create a new instance of the HMAC function, using a `secret_key` that may or may not be padded. + fn _new(secret_key: &[u8]) -> Result { + Self::_new(secret_key) + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Finalize the MAC and put the final tag into `dest`. + /// + /// NOTE: `dest` may be less than the complete output size of the hash function + /// (Self::HASH_FUNC_OUTSIZE). If that is the case, `dest.len()` bytes will be copied, + /// but `dest` should NEVER be empty. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + /// Reset the state. + fn _reset(&mut self) { + self._state._reset() + } + } + + #[cfg(test)] + mod public { + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let secret_key = SecretKey::generate(); + let initial_state = HmacSha256::new(&secret_key); + let debug = format!("{:?}", initial_state); + let expected = "HmacSha256 { _state: Hmac { working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "safe_api")] + mod test_verify { + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different key, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_key_false(data: Vec) -> bool { + let sk = SecretKey::generate(); + let mut state = HmacSha256::new(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + let bad_sk = SecretKey::generate(); + + HmacSha256::verify(&tag, &bad_sk, &data[..]).is_err() + } + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + const KEY: [u8; 32] = [0u8; 32]; + + impl TestableStreamingContext for HmacSha256 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + HmacSha256::hmac(&SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> { + // This will only run verification tests on differing input. They do not + // include tests for different secret keys. + HmacSha256::verify(expected, &SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn compare_states(state_1: &HmacSha256, state_2: &HmacSha256) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state = HmacSha256::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + sha256::SHA256_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec) -> bool { + let initial_state = HmacSha256::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + sha256::SHA256_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + } +} + +/// HMAC-SHA384 (Hash-based Message Authentication Code) as specified in the [RFC 2104](https://tools.ietf.org/html/rfc2104). +pub mod sha384 { + use super::*; + use crate::hazardous::hash::sha2::sha384::{self, Sha384}; + + construct_hmac_key! { + /// A type to represent the `SecretKey` that HMAC uses for authentication. + /// + /// # Note: + /// `SecretKey` pads the secret key for use with HMAC to a length of 128, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the secret key with padding. + /// + /// `len()` will return the length with padding (always 128). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, Sha384, sha384::SHA384_OUTSIZE, test_hmac_key, sha384::SHA384_BLOCKSIZE) + } + + construct_tag! { + /// A type to represent the `Tag` that HMAC returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 48 bytes. + (Tag, test_tag, sha384::SHA384_OUTSIZE, sha384::SHA384_OUTSIZE) + } + + impl_from_trait!(Tag, sha384::SHA384_OUTSIZE); + + use super::Hmac; + + #[derive(Clone, Debug)] + /// HMAC-SHA384 streaming state. + pub struct HmacSha384 { + _state: Hmac, + } + + impl HmacSha384 { + fn _new(secret_key: &[u8]) -> Result { + Ok(Self { + _state: Hmac::::_new(secret_key)?, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize `HmacSha384` struct with a given key. + pub fn new(secret_key: &SecretKey) -> Self { + // NOTE: `secret_key` has been pre-padded so .unwrap() is OK. + Self::_new(secret_key.unprotected_as_bytes()).unwrap() + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self._state._reset() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Return a HMAC-SHA384 tag. + pub(crate) fn _finalize_internal( + &mut self, + dest: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a HMAC-SHA384 tag. + pub fn finalize(&mut self) -> Result { + let mut dest = [0u8; sha384::SHA384_OUTSIZE]; + self._finalize_internal(&mut dest)?; + + Ok(Tag::from(dest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// One-shot function for generating an HMAC-SHA384 tag of `data`. + pub fn hmac(secret_key: &SecretKey, data: &[u8]) -> Result { + let mut ctx = Self::new(secret_key); + ctx.update(data)?; + ctx.finalize() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify a HMAC-SHA384 tag in constant time. + pub fn verify( + expected: &Tag, + secret_key: &SecretKey, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + if &Self::hmac(secret_key, data)? == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + } + + impl HmacFunction for HmacSha384 { + /// The output size of the internal hash function used. + const HASH_FUNC_OUTSIZE: usize = sha384::SHA384_OUTSIZE; + + /// Create a new instance of the HMAC function, using a `secret_key` that may or may not be padded. + fn _new(secret_key: &[u8]) -> Result { + Self::_new(secret_key) + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Finalize the MAC and put the final tag into `dest`. + /// + /// NOTE: `dest` may be less than the complete output size of the hash function + /// (Self::HASH_FUNC_OUTSIZE). If that is the case, `dest.len()` bytes will be copied, + /// but `dest` should NEVER be empty. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + /// Reset the state. + fn _reset(&mut self) { + self._state._reset() + } + } + + #[cfg(test)] + mod public { + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let secret_key = SecretKey::generate(); + let initial_state = HmacSha384::new(&secret_key); + let debug = format!("{:?}", initial_state); + let expected = "HmacSha384 { _state: Hmac { working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "safe_api")] + mod test_verify { + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different key, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_key_false(data: Vec) -> bool { + let sk = SecretKey::generate(); + let mut state = HmacSha384::new(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + let bad_sk = SecretKey::generate(); + + HmacSha384::verify(&tag, &bad_sk, &data[..]).is_err() + } + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + const KEY: [u8; 32] = [0u8; 32]; + + impl TestableStreamingContext for HmacSha384 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + HmacSha384::hmac(&SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> { + // This will only run verification tests on differing input. They do not + // include tests for different secret keys. + HmacSha384::verify(expected, &SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn compare_states(state_1: &HmacSha384, state_2: &HmacSha384) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state = HmacSha384::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + sha384::SHA384_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec) -> bool { + let initial_state = HmacSha384::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + sha384::SHA384_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + } +} + +/// HMAC-SHA512 (Hash-based Message Authentication Code) as specified in the [RFC 2104](https://tools.ietf.org/html/rfc2104). +pub mod sha512 { + use super::*; + use crate::hazardous::hash::sha2::sha512::{self, Sha512}; + + construct_hmac_key! { + /// A type to represent the `SecretKey` that HMAC uses for authentication. + /// + /// # Note: + /// `SecretKey` pads the secret key for use with HMAC to a length of 128, when initialized. + /// + /// Using `unprotected_as_bytes()` will return the secret key with padding. + /// + /// `len()` will return the length with padding (always 128). + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, Sha512, sha512::SHA512_OUTSIZE, test_hmac_key, sha512::SHA512_BLOCKSIZE) + } + + construct_tag! { + /// A type to represent the `Tag` that HMAC returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 64 bytes. + (Tag, test_tag, sha512::SHA512_OUTSIZE, sha512::SHA512_OUTSIZE) + } + + impl_from_trait!(Tag, sha512::SHA512_OUTSIZE); + + use super::Hmac; + + #[derive(Clone, Debug)] + /// HMAC-SHA512 streaming state. + pub struct HmacSha512 { + _state: Hmac, + } + + impl HmacSha512 { + fn _new(secret_key: &[u8]) -> Result { + Ok(Self { + _state: Hmac::::_new(secret_key)?, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize `HmacSha512` struct with a given key. + pub fn new(secret_key: &SecretKey) -> Self { + // NOTE: `secret_key` has been pre-padded so .unwrap() is OK. + Self::_new(secret_key.unprotected_as_bytes()).unwrap() + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self._state._reset() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Return a HMAC-SHA512 tag. + pub(crate) fn _finalize_internal( + &mut self, + dest: &mut [u8], + ) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a HMAC-SHA512 tag. + pub fn finalize(&mut self) -> Result { + let mut dest = [0u8; sha512::SHA512_OUTSIZE]; + self._finalize_internal(&mut dest)?; + + Ok(Tag::from(dest)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// One-shot function for generating an HMAC-SHA512 tag of `data`. + pub fn hmac(secret_key: &SecretKey, data: &[u8]) -> Result { + let mut ctx = Self::new(secret_key); + ctx.update(data)?; + ctx.finalize() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify a HMAC-SHA512 tag in constant time. + pub fn verify( + expected: &Tag, + secret_key: &SecretKey, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + if &Self::hmac(secret_key, data)? == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } + } + + impl HmacFunction for HmacSha512 { + /// The output size of the internal hash function used. + const HASH_FUNC_OUTSIZE: usize = sha512::SHA512_OUTSIZE; + + /// Create a new instance of the HMAC function, using a `secret_key` that may or may not be padded. + fn _new(secret_key: &[u8]) -> Result { + Self::_new(secret_key) + } + + /// Update the internal state with `data`. + fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + self._state._update(data) + } + + /// Finalize the MAC and put the final tag into `dest`. + /// + /// NOTE: `dest` may be less than the complete output size of the hash function + /// (Self::HASH_FUNC_OUTSIZE). If that is the case, `dest.len()` bytes will be copied, + /// but `dest` should NEVER be empty. + fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> { + self._state._finalize(dest) + } + + /// Reset the state. + fn _reset(&mut self) { + self._state._reset() + } + } + + #[cfg(test)] + mod public { + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let secret_key = SecretKey::generate(); + let initial_state = HmacSha512::new(&secret_key); + let debug = format!("{:?}", initial_state); + let expected = "HmacSha512 { _state: Hmac { working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: false } }"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "safe_api")] + mod test_verify { + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different key, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_key_false(data: Vec) -> bool { + let sk = SecretKey::generate(); + let mut state = HmacSha512::new(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + let bad_sk = SecretKey::generate(); + + HmacSha512::verify(&tag, &bad_sk, &data[..]).is_err() + } + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::*; + + const KEY: [u8; 32] = [0u8; 32]; + + impl TestableStreamingContext for HmacSha512 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + HmacSha512::hmac(&SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> { + // This will only run verification tests on differing input. They do not + // include tests for different secret keys. + HmacSha512::verify(expected, &SecretKey::from_slice(&KEY).unwrap(), input) + } + + fn compare_states(state_1: &HmacSha512, state_2: &HmacSha512) { + state_1._state.compare_state_to_other(&state_2._state); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state = HmacSha512::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + sha512::SHA512_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec) -> bool { + let initial_state = HmacSha512::new(&SecretKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + sha512::SHA512_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } + } +} diff --git a/vendor/orion/src/hazardous/mac/mod.rs b/vendor/orion/src/hazardous/mac/mod.rs new file mode 100644 index 0000000..114062b --- /dev/null +++ b/vendor/orion/src/hazardous/mac/mod.rs @@ -0,0 +1,30 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// HMAC (Hash-based Message Authentication Code) as specified in the [RFC 2104](https://tools.ietf.org/html/rfc2104). +pub mod hmac; + +/// Poly1305 as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub mod poly1305; + +/// BLAKE2b as specified in the [RFC 7693](https://tools.ietf.org/html/rfc7693). +pub mod blake2b; diff --git a/vendor/orion/src/hazardous/mac/poly1305.rs b/vendor/orion/src/hazardous/mac/poly1305.rs new file mode 100644 index 0000000..b9c974d --- /dev/null +++ b/vendor/orion/src/hazardous/mac/poly1305.rs @@ -0,0 +1,573 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers +// Based on the algorithm from https://github.com/floodyberry/poly1305-donna + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # About: +//! This implementation is based on [poly1305-donna] by Andrew Moon. +//! +//! # Parameters: +//! - `data`: Data to be authenticated. +//! - `one_time_key`: One-time key used to authenticate. +//! - `expected`: The expected tag that needs to be verified. +//! +//! # Errors: +//! An error will be returned if: +//! - [`finalize()`] is called twice without a [`reset()`] in between. +//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in +//! between. +//! - The calculated tag does not match the expected when verifying. +//! +//! # Security: +//! - A given key must never be used more than once. A unique [`OneTimeKey`], +//! for each message authenticated, is required. If a key is used more than once, +//! it reveals enough information for an attacker to forge future authentications with the same key. +//! - The one-time key should be generated using a CSPRNG. +//! [`OneTimeKey::generate()`] can be used for this. +//! +//! # Recommendation: +//! - If you are unsure of whether to use HMAC or Poly1305, it is most often +//! easier to just use HMAC. See also [Cryptographic Right Answers]. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::mac::poly1305::{OneTimeKey, Poly1305}; +//! +//! let one_time_key = OneTimeKey::generate(); +//! let msg = "Some message."; +//! +//! let mut poly1305_state = Poly1305::new(&one_time_key); +//! poly1305_state.update(msg.as_bytes())?; +//! let tag = poly1305_state.finalize()?; +//! +//! assert!(Poly1305::verify(&tag, &one_time_key, msg.as_bytes()).is_ok()); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`update()`]: poly1305::Poly1305::update +//! [`reset()`]: poly1305::Poly1305::reset +//! [`finalize()`]: poly1305::Poly1305::finalize +//! [`OneTimeKey::generate()`]: poly1305::OneTimeKey::generate +//! [`OneTimeKey`]: poly1305::OneTimeKey +//! [poly1305-donna]: https://github.com/floodyberry/poly1305-donna +//! [Cryptographic Right Answers]: https://latacora.micro.blog/2018/04/03/cryptographic-right-answers.html + +use crate::{ + errors::UnknownCryptoError, + util::endianness::{load_u32_le, store_u32_into_le}, +}; +use fiat_crypto::poly1305_32::{ + fiat_poly1305_add, fiat_poly1305_carry, fiat_poly1305_carry_mul, fiat_poly1305_from_bytes, + fiat_poly1305_loose_field_element, fiat_poly1305_relax, fiat_poly1305_selectznz, + fiat_poly1305_subborrowx_u26, fiat_poly1305_tight_field_element, fiat_poly1305_u1, +}; + +/// The blocksize which Poly1305 operates on. +const POLY1305_BLOCKSIZE: usize = 16; +/// The output size for Poly1305. +pub const POLY1305_OUTSIZE: usize = 16; +/// The key size for Poly1305. +pub const POLY1305_KEYSIZE: usize = 32; +/// Type for a Poly1305 tag. +type Poly1305Tag = [u8; POLY1305_OUTSIZE]; + +construct_secret_key! { + /// A type to represent the `OneTimeKey` that Poly1305 uses for authentication. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (OneTimeKey, test_one_time_key, POLY1305_KEYSIZE, POLY1305_KEYSIZE, POLY1305_KEYSIZE) +} + +impl_from_trait!(OneTimeKey, POLY1305_KEYSIZE); + +construct_tag! { + /// A type to represent the `Tag` that Poly1305 returns. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 16 bytes. + (Tag, test_tag, POLY1305_OUTSIZE, POLY1305_OUTSIZE) +} + +impl_from_trait!(Tag, POLY1305_OUTSIZE); + +#[derive(Clone)] +/// Poly1305 streaming state. +pub struct Poly1305 { + a: fiat_poly1305_tight_field_element, + r: fiat_poly1305_loose_field_element, + s: [u32; 4], + leftover: usize, + buffer: [u8; POLY1305_BLOCKSIZE], + is_finalized: bool, +} + +impl Drop for Poly1305 { + fn drop(&mut self) { + use zeroize::Zeroize; + self.a.0.zeroize(); + self.r.0.zeroize(); + self.s.zeroize(); + self.buffer.zeroize(); + } +} + +impl core::fmt::Debug for Poly1305 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Poly1305 {{ a: [***OMITTED***], r: [***OMITTED***], s: [***OMITTED***], leftover: [***OMITTED***], buffer: [***OMITTED***], is_finalized: {:?} }}", + self.is_finalized + ) + } +} + +impl Poly1305 { + /// Prime 2^130-5 in little-endian. + const PRIME: [u8; 17] = [ + 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3, + ]; + + /// Process a datablock of `POLY1305_BLOCKSIZE` length. + fn process_block(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + if data.len() != POLY1305_BLOCKSIZE { + return Err(UnknownCryptoError); + } + + let mut mb = [0u8; 17]; + mb[..16].copy_from_slice(data); + // One byte is appended to detect trailing zeroes if not last chunk. + // See https://cr.yp.to/mac/poly1305-20050329.pdf, Section 2 "Conversion and padding". + mb[16] = u8::from(!self.is_finalized); + let mut m = fiat_poly1305_tight_field_element([0u32; 5]); + fiat_poly1305_from_bytes(&mut m, &mb); + + // h += m + let mut h = fiat_poly1305_loose_field_element([0u32; 5]); + fiat_poly1305_add(&mut h, &self.a, &m); + // h *= r with partial reduction modulo p + fiat_poly1305_carry_mul(&mut self.a, &h, &self.r); + + Ok(()) + } + + #[rustfmt::skip] + #[allow(clippy::identity_op)] + /// Remaining processing after all data blocks have been processed. + fn process_end_of_stream(&mut self) { + // full carry h + let mut buf_h = fiat_poly1305_tight_field_element([0u32; 5]); + let mut a_relaxed = fiat_poly1305_loose_field_element([0u32; 5]); + fiat_poly1305_relax(&mut a_relaxed, &self.a); + fiat_poly1305_carry(&mut buf_h, &a_relaxed); + + // compute h + -p + let mut p = fiat_poly1305_tight_field_element([0u32; 5]); + fiat_poly1305_from_bytes(&mut p, &Self::PRIME); + + let mut carry: fiat_poly1305_u1 = 0; + let mut g0: u32 = 0; let c = carry; fiat_poly1305_subborrowx_u26(&mut g0, &mut carry, c, buf_h[0], p[0]); + let mut g1: u32 = 0; let c = carry; fiat_poly1305_subborrowx_u26(&mut g1, &mut carry, c, buf_h[1], p[1]); + let mut g2: u32 = 0; let c = carry; fiat_poly1305_subborrowx_u26(&mut g2, &mut carry, c, buf_h[2], p[2]); + let mut g3: u32 = 0; let c = carry; fiat_poly1305_subborrowx_u26(&mut g3, &mut carry, c, buf_h[3], p[3]); + let mut g4: u32 = 0; let c = carry; fiat_poly1305_subborrowx_u26(&mut g4, &mut carry, c, buf_h[4], p[4]); + + // select h if h < p, or h + -p if h >= p + let mut ret = [0u32; 5]; + fiat_poly1305_selectznz(&mut ret, carry,&[g0, g1, g2, g3, g4], &buf_h.0); + + let mut h0 = ret[0]; + let mut h1 = ret[1]; + let mut h2 = ret[2]; + let mut h3 = ret[3]; + let h4 = ret[4]; + + // h = h % (2^128) + h0 = ((h0) | (h1 << 26)) & 0xffffffff; + h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; + h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; + h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; + + // mac = (h + pad) % (2^128) + let mut f: u64 = (h0 as u64) + (self.s[0] as u64); h0 = f as u32; + f = (h1 as u64) + (self.s[1] as u64) + (f >> 32); h1 = f as u32; + f = (h2 as u64) + (self.s[2] as u64) + (f >> 32); h2 = f as u32; + f = (h3 as u64) + (self.s[3] as u64) + (f >> 32); h3 = f as u32; + + // Set self.a to MAC result + self.a[0] = h0; + self.a[1] = h1; + self.a[2] = h2; + self.a[3] = h3; + } + + #[allow(clippy::unreadable_literal)] + /// Initialize a `Poly1305` struct with a given one-time key. + pub fn new(one_time_key: &OneTimeKey) -> Self { + let mut state = Self { + a: fiat_poly1305_tight_field_element([0u32; 5]), + r: fiat_poly1305_loose_field_element([0u32; 5]), + s: [0u32; 4], + leftover: 0, + buffer: [0u8; POLY1305_BLOCKSIZE], + is_finalized: false, + }; + + state.r[0] = (load_u32_le(&one_time_key.unprotected_as_bytes()[0..4])) & 0x3ffffff; + state.r[1] = (load_u32_le(&one_time_key.unprotected_as_bytes()[3..7]) >> 2) & 0x3ffff03; + state.r[2] = (load_u32_le(&one_time_key.unprotected_as_bytes()[6..10]) >> 4) & 0x3ffc0ff; + state.r[3] = (load_u32_le(&one_time_key.unprotected_as_bytes()[9..13]) >> 6) & 0x3f03fff; + state.r[4] = (load_u32_le(&one_time_key.unprotected_as_bytes()[12..16]) >> 8) & 0x00fffff; + + state.s[0] = load_u32_le(&one_time_key.unprotected_as_bytes()[16..20]); + state.s[1] = load_u32_le(&one_time_key.unprotected_as_bytes()[20..24]); + state.s[2] = load_u32_le(&one_time_key.unprotected_as_bytes()[24..28]); + state.s[3] = load_u32_le(&one_time_key.unprotected_as_bytes()[28..32]); + + state + } + + /// Update state with a `data` and pad it to blocksize with 0, if not + /// evenly divisible by blocksize. + pub(crate) fn process_pad_to_blocksize( + &mut self, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + if self.is_finalized { + return Err(UnknownCryptoError); + } + if data.is_empty() { + return Ok(()); + } + + let mut blocksize_iter = data.chunks_exact(POLY1305_BLOCKSIZE); + for block in &mut blocksize_iter { + self.process_block(block).unwrap(); + } + + let remaining = blocksize_iter.remainder(); + if !remaining.is_empty() { + let mut pad = [0u8; POLY1305_BLOCKSIZE]; + pad[..remaining.len()].copy_from_slice(remaining); + self.process_block(&pad).unwrap(); + } + + Ok(()) + } + + /// Reset to `new()` state. + pub fn reset(&mut self) { + self.a = fiat_poly1305_tight_field_element([0u32; 5]); + self.leftover = 0; + self.is_finalized = false; + self.buffer = [0u8; POLY1305_BLOCKSIZE]; + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Update state with `data`. This can be called multiple times. + pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> { + if self.is_finalized { + return Err(UnknownCryptoError); + } + if data.is_empty() { + return Ok(()); + } + + let mut bytes = data; + + if self.leftover != 0 { + debug_assert!(self.leftover <= POLY1305_BLOCKSIZE); + + let mut want = POLY1305_BLOCKSIZE - self.leftover; + if want > bytes.len() { + want = bytes.len(); + } + + for (idx, itm) in bytes.iter().enumerate().take(want) { + self.buffer[self.leftover + idx] = *itm; + } + + bytes = &bytes[want..]; + self.leftover += want; + if self.leftover < POLY1305_BLOCKSIZE { + return Ok(()); + } + + let tmp = self.buffer; + self.process_block(&tmp)?; + self.leftover = 0; + } + + while bytes.len() >= POLY1305_BLOCKSIZE { + self.process_block(&bytes[0..POLY1305_BLOCKSIZE])?; + bytes = &bytes[POLY1305_BLOCKSIZE..]; + } + + self.buffer[..bytes.len()].copy_from_slice(bytes); + self.leftover = bytes.len(); + + Ok(()) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a Poly1305 tag. + pub fn finalize(&mut self) -> Result { + if self.is_finalized { + return Err(UnknownCryptoError); + } + + self.is_finalized = true; + + let mut local_buffer: Poly1305Tag = self.buffer; + + if self.leftover != 0 { + local_buffer[self.leftover] = 1; + // Pad the last block with zeroes before processing it + for buf_itm in local_buffer + .iter_mut() + .take(POLY1305_BLOCKSIZE) + .skip(self.leftover + 1) + { + *buf_itm = 0u8; + } + + self.process_block(&local_buffer)?; + } + + self.process_end_of_stream(); + store_u32_into_le(&self.a.0[0..4], &mut local_buffer); + + Ok(Tag::from(local_buffer)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// One-shot function for generating a Poly1305 tag of `data`. + pub fn poly1305(one_time_key: &OneTimeKey, data: &[u8]) -> Result { + let mut poly_1305_state = Self::new(one_time_key); + poly_1305_state.update(data)?; + poly_1305_state.finalize() + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Verify a Poly1305 tag in constant time. + pub fn verify( + expected: &Tag, + one_time_key: &OneTimeKey, + data: &[u8], + ) -> Result<(), UnknownCryptoError> { + if &Self::poly1305(one_time_key, data)? == expected { + Ok(()) + } else { + Err(UnknownCryptoError) + } + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let secret_key = OneTimeKey::generate(); + let initial_state = Poly1305::new(&secret_key); + let debug = format!("{:?}", initial_state); + let expected = "Poly1305 { a: [***OMITTED***], r: [***OMITTED***], s: [***OMITTED***], leftover: [***OMITTED***], buffer: [***OMITTED***], is_finalized: false }"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "safe_api")] + mod test_verify { + use super::*; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// When using a different key, verify() should always yield an error. + /// NOTE: Using different and same input data is tested with TestableStreamingContext. + fn prop_verify_diff_key_false(data: Vec) -> bool { + let sk = OneTimeKey::generate(); + let mut state = Poly1305::new(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + let bad_sk = OneTimeKey::generate(); + + Poly1305::verify(&tag, &bad_sk, &data[..]).is_err() + } + } + + mod test_streaming_interface { + use super::*; + use crate::test_framework::incremental_interface::{ + StreamingContextConsistencyTester, TestableStreamingContext, + }; + + // If a Poly1305 one-time key is all 0's then the tag will also be, regardless + // of which message data has been processed. + const KEY: [u8; 32] = [24u8; 32]; + + impl TestableStreamingContext for Poly1305 { + fn reset(&mut self) -> Result<(), UnknownCryptoError> { + self.reset(); + Ok(()) + } + + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { + self.update(input) + } + + fn finalize(&mut self) -> Result { + self.finalize() + } + + fn one_shot(input: &[u8]) -> Result { + Poly1305::poly1305(&OneTimeKey::from_slice(&KEY).unwrap(), input) + } + + fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> { + // This will only run verification tests on differing input. They do not + // include tests for different secret keys. + Poly1305::verify(expected, &OneTimeKey::from_slice(&KEY).unwrap(), input) + } + + fn compare_states(state_1: &Poly1305, state_2: &Poly1305) { + assert_eq!(state_1.a.0, state_2.a.0); + assert_eq!(state_1.r.0, state_2.r.0); + assert_eq!(state_1.s, state_2.s); + assert_eq!(state_1.leftover, state_2.leftover); + assert_eq!(state_1.buffer[..], state_2.buffer[..]); + assert_eq!(state_1.is_finalized, state_2.is_finalized); + } + } + + #[test] + fn default_consistency_tests() { + let initial_state: Poly1305 = Poly1305::new(&OneTimeKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + POLY1305_BLOCKSIZE, + ); + test_runner.run_all_tests(); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_input_to_consistency(data: Vec) -> bool { + let initial_state: Poly1305 = Poly1305::new(&OneTimeKey::from_slice(&KEY).unwrap()); + + let test_runner = StreamingContextConsistencyTester::::new( + initial_state, + POLY1305_BLOCKSIZE, + ); + test_runner.run_all_tests_property(&data); + true + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_process_pad_to_blocksize { + use super::*; + + #[test] + fn test_process_err_on_finalized() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state = Poly1305::new(&sk); + + state.process_pad_to_blocksize(&[0u8; 16]).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.process_pad_to_blocksize(&[0u8; 16]).is_err()); + } + + #[test] + fn test_process_pad_no_pad() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state_pad = Poly1305::new(&sk); + let mut state_no_pad = Poly1305::new(&sk); + + // 15 missing to be evenly divisible by 16. + state_pad.process_pad_to_blocksize(&[0u8; 17]).unwrap(); + state_no_pad.process_pad_to_blocksize(&[0u8; 32]).unwrap(); + + assert_eq!( + state_no_pad.finalize().unwrap(), + state_pad.finalize().unwrap() + ); + } + } + + mod test_process_block { + use super::*; + + #[test] + fn test_process_block_len() { + let block_0 = [0u8; 0]; + let block_1 = [0u8; 15]; + let block_2 = [0u8; 17]; + let block_3 = [0u8; 16]; + + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state = Poly1305::new(&sk); + + assert!(state.process_block(&block_0).is_err()); + assert!(state.process_block(&block_1).is_err()); + assert!(state.process_block(&block_2).is_err()); + assert!(state.process_block(&block_3).is_ok()); + } + } + + mod test_process_end_of_stream { + use super::*; + + #[test] + fn test_process_no_panic() { + let block = [0u8; 16]; + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state = Poly1305::new(&sk); + // Should not panic + state.process_end_of_stream(); + state.reset(); + state.process_end_of_stream(); + + let mut state = Poly1305::new(&sk); + state.process_block(&block).unwrap(); + // Should not panic + state.process_end_of_stream(); + state.reset(); + state.process_end_of_stream(); + } + } +} diff --git a/vendor/orion/src/hazardous/mod.rs b/vendor/orion/src/hazardous/mod.rs new file mode 100644 index 0000000..ff8c188 --- /dev/null +++ b/vendor/orion/src/hazardous/mod.rs @@ -0,0 +1,53 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! ### **Caution**: +//! Usage of the `hazardous` module is __**only intended for advanced users**__. +//! `hazardous` contains implementations with a much higher degree of control. +//! It is also much easier to misuse those implementations. Only use `hazardous` +//! if absolutely necessary. + +/// AEADs (Authenticated Encryption with Associated Data). +pub mod aead; + +/// Cryptographic hash functions. +pub mod hash; + +/// MACs (Message Authentication Code). +pub mod mac; + +/// KDFs (Key Derivation Function) and PBKDFs (Password-Based Key Derivation +/// Function). +pub mod kdf; + +/// Stream ciphers. +pub mod stream; + +/// Elliptic-Curve Cryptography. +pub mod ecc; + +#[cfg(feature = "experimental")] +/// Fully-committing Authenticated Encryption. __WARNING:__ Experimental feature. +pub mod cae; + +/// Key Encapsulation Mechanisms (KEMs). +pub mod kem; diff --git a/vendor/orion/src/hazardous/stream/chacha20.rs b/vendor/orion/src/hazardous/stream/chacha20.rs new file mode 100644 index 0000000..f5c9bcd --- /dev/null +++ b/vendor/orion/src/hazardous/stream/chacha20.rs @@ -0,0 +1,1331 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `secret_key`: The secret key. +//! - `nonce`: The nonce value. +//! - `initial_counter`: The initial counter value. In most cases, this is `0`. +//! - `ciphertext`: The encrypted data. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the ciphertext/plaintext after +//! encryption/decryption. +//! +//! `nonce`: "Counters and LFSRs are both acceptable ways of generating unique +//! nonces, as is encrypting a counter using a block cipher with a 64-bit block +//! size such as DES. Note that it is not acceptable to use a truncation of a +//! counter encrypted with block ciphers with 128-bit or 256-bit blocks, +//! because such a truncation may repeat after a short time." See [RFC] +//! for more information. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` or `ciphertext`. +//! - `plaintext` or `ciphertext` is empty. +//! - The `initial_counter` is high enough to cause a potential overflow. +//! +//! Even though `dst_out` is allowed to be of greater length than `plaintext`, +//! the `ciphertext` produced by `chacha20`/`xchacha20` will always be of the +//! same length as the `plaintext`. +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1` keystream blocks are processed or more than `2^32-1 * 64` +//! bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Functions herein do not provide any data integrity. If you need +//! data integrity, which is nearly ***always the case***, you should use an +//! AEAD construction instead. See the [`aead`](super::aead) module for this. +//! - Only a nonce for XChaCha20 is big enough to be randomly generated using a CSPRNG. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! +//! # Recommendation: +//! - It is recommended to use [`XChaCha20Poly1305`] when possible. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::stream::chacha20; +//! +//! let secret_key = chacha20::SecretKey::generate(); +//! +//! // WARNING: This nonce is only meant for demonstration and should not +//! // be repeated. Please read the security section. +//! let nonce = chacha20::Nonce::from([0u8; 12]); +//! let message = "Data to protect".as_bytes(); +//! +//! // The length of this message is 15. +//! +//! let mut dst_out_pt = [0u8; 15]; +//! let mut dst_out_ct = [0u8; 15]; +//! +//! chacha20::encrypt(&secret_key, &nonce, 0, message, &mut dst_out_ct)?; +//! +//! chacha20::decrypt(&secret_key, &nonce, 0, &dst_out_ct, &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt, message); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: chacha20::SecretKey::generate() +//! [`XChaCha20Poly1305`]: super::aead::xchacha20poly1305 +//! [RFC]: https://tools.ietf.org/html/rfc8439 +use crate::errors::UnknownCryptoError; +use crate::util::endianness::load_u32_le; +use crate::util::u32x4::U32x4; +use zeroize::{Zeroize, Zeroizing}; + +/// The key size for ChaCha20. +pub const CHACHA_KEYSIZE: usize = 32; +/// The nonce size for IETF ChaCha20. +pub const IETF_CHACHA_NONCESIZE: usize = 12; +/// The blocksize which ChaCha20 operates on. +pub(crate) const CHACHA_BLOCKSIZE: usize = 64; +/// The size of the subkey that HChaCha20 returns. +const HCHACHA_OUTSIZE: usize = 32; +/// The nonce size for HChaCha20. +pub(crate) const HCHACHA_NONCESIZE: usize = 16; + +construct_secret_key! { + /// A type to represent the `SecretKey` that Chacha20, XChaCha20, ChaCha20-Poly1305 and + /// XChaCha20-Poly1305 use. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 32 bytes. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, test_secret_key, CHACHA_KEYSIZE, CHACHA_KEYSIZE, CHACHA_KEYSIZE) +} + +impl_from_trait!(SecretKey, CHACHA_KEYSIZE); + +construct_public! { + /// A type that represents a `Nonce` that ChaCha20 and ChaCha20-Poly1305 use. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 12 bytes. + (Nonce, test_nonce, IETF_CHACHA_NONCESIZE, IETF_CHACHA_NONCESIZE) +} + +impl_from_trait!(Nonce, IETF_CHACHA_NONCESIZE); + +macro_rules! ROUND { + ($r0:expr, $r1:expr, $r2:expr, $r3:expr) => { + $r0 = $r0.wrapping_add($r1); + $r3 = ($r3 ^ $r0).rotate_left(16); + + $r2 = $r2.wrapping_add($r3); + $r1 = ($r1 ^ $r2).rotate_left(12); + + $r0 = $r0.wrapping_add($r1); + $r3 = ($r3 ^ $r0).rotate_left(8); + + $r2 = $r2.wrapping_add($r3); + $r1 = ($r1 ^ $r2).rotate_left(7); + }; +} + +macro_rules! DOUBLE_ROUND { + ($r0:expr, $r1:expr, $r2:expr, $r3:expr) => { + ROUND!($r0, $r1, $r2, $r3); + + // Shuffle + $r1 = $r1.shl_1(); + $r2 = $r2.shl_2(); + $r3 = $r3.shl_3(); + + ROUND!($r0, $r1, $r2, $r3); + + // Unshuffle + $r1 = $r1.shl_3(); + $r2 = $r2.shl_2(); + $r3 = $r3.shl_1(); + }; +} +pub(crate) struct ChaCha20 { + state: [U32x4; 4], + internal_counter: u32, + is_ietf: bool, +} + +impl Drop for ChaCha20 { + fn drop(&mut self) { + self.state.iter_mut().zeroize(); + } +} + +impl ChaCha20 { + #[allow(clippy::unreadable_literal)] + /// Initialize either a ChaCha or HChaCha state with a `secret_key` and + /// `nonce`. + pub(crate) fn new(sk: &[u8], n: &[u8], is_ietf: bool) -> Result { + debug_assert_eq!(sk.len(), CHACHA_KEYSIZE); + if (n.len() != IETF_CHACHA_NONCESIZE) && is_ietf { + return Err(UnknownCryptoError); + } + if (n.len() != HCHACHA_NONCESIZE) && !is_ietf { + return Err(UnknownCryptoError); + } + + // Row 0 with constants. + let r0 = U32x4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574); + + // Row 1 and 2 with secret key. + let r1 = U32x4( + load_u32_le(&sk[0..4]), + load_u32_le(&sk[4..8]), + load_u32_le(&sk[8..12]), + load_u32_le(&sk[12..16]), + ); + + let r2 = U32x4( + load_u32_le(&sk[16..20]), + load_u32_le(&sk[20..24]), + load_u32_le(&sk[24..28]), + load_u32_le(&sk[28..32]), + ); + + // Row 3 with counter and nonce if IETF, + // but only nonce if HChaCha20. + let r3 = if is_ietf { + U32x4( + 0, // Default counter + load_u32_le(&n[0..4]), + load_u32_le(&n[4..8]), + load_u32_le(&n[8..12]), + ) + } else { + U32x4( + load_u32_le(&n[0..4]), + load_u32_le(&n[4..8]), + load_u32_le(&n[8..12]), + load_u32_le(&n[12..16]), + ) + }; + + Ok(Self { + state: [r0, r1, r2, r3], + internal_counter: 0, + is_ietf, + }) + } + + /// Check that we can produce one more keystream block, given the current state. + /// + /// If the internal counter would overflow, we return an error. + pub(crate) fn next_produceable(&self) -> Result<(), UnknownCryptoError> { + if self.internal_counter.checked_add(1).is_none() { + Err(UnknownCryptoError) + } else { + Ok(()) + } + } + + /// Process the next keystream and copy into destination array. + pub(crate) fn keystream_block(&mut self, block_counter: u32, inplace: &mut [u8]) { + debug_assert!(if self.is_ietf { + inplace.len() == CHACHA_BLOCKSIZE + } else { + inplace.len() == HCHACHA_OUTSIZE + }); + + if self.is_ietf { + self.state[3].0 = block_counter; + } + + // If this panics, max amount of keystream blocks + // have been retrieved. + self.internal_counter = self.internal_counter.checked_add(1).unwrap(); + + let mut wr0 = self.state[0]; + let mut wr1 = self.state[1]; + let mut wr2 = self.state[2]; + let mut wr3 = self.state[3]; + + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + DOUBLE_ROUND!(wr0, wr1, wr2, wr3); + + let mut iter = inplace.chunks_exact_mut(16); + + if self.is_ietf { + wr0 = wr0.wrapping_add(self.state[0]); + wr1 = wr1.wrapping_add(self.state[1]); + wr2 = wr2.wrapping_add(self.state[2]); + wr3 = wr3.wrapping_add(self.state[3]); + + wr0.store_into_le(iter.next().unwrap()); + wr1.store_into_le(iter.next().unwrap()); + wr2.store_into_le(iter.next().unwrap()); + wr3.store_into_le(iter.next().unwrap()); + } else { + wr0.store_into_le(iter.next().unwrap()); + wr3.store_into_le(iter.next().unwrap()); + } + } +} + +/// XOR keystream into destination array using a temporary buffer for each keystream block. +pub(crate) fn xor_keystream( + ctx: &mut ChaCha20, + initial_counter: u32, + tmp_block: &mut [u8], + bytes: &mut [u8], +) -> Result<(), UnknownCryptoError> { + debug_assert_eq!(tmp_block.len(), CHACHA_BLOCKSIZE); + if bytes.is_empty() { + return Err(UnknownCryptoError); + } + + for (ctr, out_block) in bytes.chunks_mut(CHACHA_BLOCKSIZE).enumerate() { + match initial_counter.checked_add(ctr as u32) { + Some(counter) => { + ctx.keystream_block(counter, tmp_block); + xor_slices!(tmp_block, out_block); + } + None => return Err(UnknownCryptoError), + } + } + + Ok(()) +} + +/// In-place IETF ChaCha20 encryption as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub(crate) fn encrypt_in_place( + secret_key: &SecretKey, + nonce: &Nonce, + initial_counter: u32, + bytes: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if bytes.is_empty() { + return Err(UnknownCryptoError); + } + + let mut ctx = ChaCha20::new(secret_key.unprotected_as_bytes(), nonce.as_ref(), true)?; + let mut keystream_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + xor_keystream(&mut ctx, initial_counter, keystream_block.as_mut(), bytes) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// IETF ChaCha20 encryption as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub fn encrypt( + secret_key: &SecretKey, + nonce: &Nonce, + initial_counter: u32, + plaintext: &[u8], + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + if dst_out.len() < plaintext.len() { + return Err(UnknownCryptoError); + } + if plaintext.is_empty() { + return Err(UnknownCryptoError); + } + + let mut ctx = ChaCha20::new(secret_key.unprotected_as_bytes(), nonce.as_ref(), true)?; + let mut keystream_block = Zeroizing::new([0u8; CHACHA_BLOCKSIZE]); + + for (ctr, (p_block, c_block)) in plaintext + .chunks(CHACHA_BLOCKSIZE) + .zip(dst_out.chunks_mut(CHACHA_BLOCKSIZE)) + .enumerate() + { + match initial_counter.checked_add(ctr as u32) { + Some(counter) => { + // See https://github.com/orion-rs/orion/issues/308 + ctx.next_produceable()?; + + ctx.keystream_block(counter, keystream_block.as_mut()); + xor_slices!(p_block, keystream_block.as_mut()); + c_block[..p_block.len()] + .copy_from_slice(&keystream_block.as_ref()[..p_block.len()]); + } + None => return Err(UnknownCryptoError), + } + } + + Ok(()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// IETF ChaCha20 decryption as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub fn decrypt( + secret_key: &SecretKey, + nonce: &Nonce, + initial_counter: u32, + ciphertext: &[u8], + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + encrypt(secret_key, nonce, initial_counter, ciphertext, dst_out) +} + +/// HChaCha20 as specified in the [draft-RFC](https://github.com/bikeshedders/xchacha-rfc/blob/master). +pub(super) fn hchacha20( + secret_key: &SecretKey, + nonce: &[u8], +) -> Result<[u8; HCHACHA_OUTSIZE], UnknownCryptoError> { + let mut chacha_state = ChaCha20::new(secret_key.unprotected_as_bytes(), nonce, false)?; + let mut keystream_block = [0u8; HCHACHA_OUTSIZE]; + chacha_state.keystream_block(0, &mut keystream_block); + + Ok(keystream_block) +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[cfg(feature = "safe_api")] + #[test] + // See https://github.com/orion-rs/orion/issues/308 + fn test_plaintext_left_in_dst_out() { + let k = SecretKey::generate(); + let n = Nonce::from_slice(&[0u8; 12]).unwrap(); + let ic: u32 = u32::MAX - 1; + + let text = [b'x'; 128 + 4]; + let mut dst_out = [0u8; 128 + 4]; + + let _err = encrypt(&k, &n, ic, &text, &mut dst_out).unwrap_err(); + + assert_ne!(&[b'x'; 4], &dst_out[dst_out.len() - 4..]); + } + + #[cfg(feature = "safe_api")] + mod test_encrypt_decrypt { + use super::*; + use crate::test_framework::streamcipher_interface::*; + + impl TestingRandom for SecretKey { + fn gen() -> Self { + Self::generate() + } + } + + impl TestingRandom for Nonce { + fn gen() -> Self { + let mut n = [0u8; IETF_CHACHA_NONCESIZE]; + crate::util::secure_rand_bytes(&mut n).unwrap(); + Self::from_slice(&n).unwrap() + } + } + + #[quickcheck] + fn prop_streamcipher_interface(input: Vec, counter: u32) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::from_slice(&[0u8; IETF_CHACHA_NONCESIZE]).unwrap(); + StreamCipherTestRunner(encrypt, decrypt, secret_key, nonce, counter, &input, None); + test_diff_params_diff_output(&encrypt, &decrypt); + + true + } + } + + // hex crate uses Vec, so we need std. + mod test_hchacha20 { + use super::*; + + use hex::decode; + + #[test] + fn test_nonce_length() { + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16],).is_ok()); + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 17],).is_err()); + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 15],).is_err()); + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 0],).is_err()); + } + + #[test] + fn test_diff_keys_diff_output() { + let keystream1 = + hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).unwrap(); + + let keystream2 = + hchacha20(&SecretKey::from_slice(&[1u8; 32]).unwrap(), &[0u8; 16]).unwrap(); + + assert_ne!(keystream1, keystream2); + } + + #[test] + fn test_diff_nonce_diff_output() { + let keystream1 = + hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).unwrap(); + + let keystream2 = + hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[1u8; 16]).unwrap(); + + assert_ne!(keystream1, keystream2); + } + + pub fn hchacha_test_runner(key: &str, nonce: &str, output_expected: &str) { + let actual: [u8; 32] = hchacha20( + &SecretKey::from_slice(&decode(key).unwrap()).unwrap(), + &decode(nonce).unwrap(), + ) + .unwrap(); + + assert_eq!(&actual, &decode(output_expected).unwrap()[..]); + } + + // Testing against Monocypher-generated test vectors + // https://github.com/LoupVaillant/Monocypher/tree/master/tests/gen + // Pulled at commit: https://github.com/LoupVaillant/Monocypher/commit/39b164a5bf715d1a62689203b059144df76d98e2 + + #[test] + fn test_case_0() { + let key = "e4e4c4054fe35a75d9c0f679ad8770d8227e68e4c1e68ce67ee88e6be251a207"; + let nonce = "48b3753cff3a6d990163e6b60da1e4e5"; + let expected_output = + "d805447c583fd97a07a2b7ab66be621ad0fa32d63d86ac20588da90b87c1907b"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_1() { + let key = "d6a2df78c16c96a52d4fb01ea4ecf70e81ac001b08d6577bd91ce991c4c45c46"; + let nonce = "bc84d5465fc9139bf17042ae7313181f"; + let expected_output = + "66d1fd5e89a564b55ccf0c339455449c20dfbc9d17081c85fbb430a157777be9"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_2() { + let key = "7afb217bd1eceeac1e133aaa9edb441fa88ea3ae0eaa06cb9911b6d218570f92"; + let nonce = "4a70a7e992b43e0b18578e892e954c40"; + let expected_output = + "41119e28a00a9d3f24b1910495f3058f9db83cbcf12889de84a2fcd7de8dc31b"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_3() { + let key = "a51abdb5a85d300c32f391c45d6ef4db043ddcf4214f24ea6ef6b181071f299a"; + let nonce = "a254a4606ab6a058e0c6fb5598218db7"; + let expected_output = + "04c2f31fdcc7013ac7d10ec82e8d3628c9ab23b08bbf95d6d77ad2dec7e865d6"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_4() { + let key = "1deb473f7d04c152e7e857736715dc7b788aca39a3c96a878019e8999c815c57"; + let nonce = "23dbfbde05e6c71f118afc0dedb5b9f8"; + let expected_output = + "75e9a94daf28b6b8634823325c61cdcb2beeb17a8f7554cc6d5b1b1d2e3592cf"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_5() { + let key = "dea398b2d764bca68dfc023a9821939d389e38a072cf1b413bb1517c3fe83abe"; + let nonce = "bb1cdf3a218abb1b0c01da64c24f59ee"; + let expected_output = + "65a20993e8e69de41d38e94c0796cb7baccd6d80a6e4084e65d0d574fbcb7311"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_6() { + let key = "d19cfb8cb3940aba546f0be57895e2cc869fe55aab069c5abcf9e7ba6444a846"; + let nonce = "e5d73f1c8c5376c1220ff3d9d53eeb65"; + let expected_output = + "a345f5f10ec20b4a744634fbb94e94c9425699b4d57ffeab5403b8fbfb85bae7"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_7() { + let key = "cc53599f40d6c8348c353b00172655236cddcd1879ca1f04b35f91adab70b81f"; + let nonce = "504035fc169964a5ae985e6c11b0b7bb"; + let expected_output = + "11dda56dce88c92641177e2a6e21b11c5ca794912b3bceb9ccb375c87bcc7968"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_8() { + let key = "18a51fd77fbffd722aa220efdd8947ca5a5c7fb1c2ebdb9ad1f603801ff22e80"; + let nonce = "314f716af9c22022fa159dbb4b4d3153"; + let expected_output = + "14759f0e978a9f45a4696739fecb590b4ba6f06536384225333cccba074c8a68"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_9() { + let key = "f999b20ab4769eb1d01c057c5295ed042b4536561dce32478b113adb5b605cac"; + let nonce = "75bcfcacb5e3e811b78e72e398fdd118"; + let expected_output = + "564eb6b2ac2b92270af7c0b054cc7a721313e4ed3651b0970db9dfcdfda27220"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_10() { + let key = "bf04c6a7ed0756a3533e3dca02109e1830b739210bd8bffe6a8a542980bd73e9"; + let nonce = "ca43cdd4eb7173476862df6d2458d6c7"; + let expected_output = + "4f8975d01fb3525a60de55c61190471e86b95cb3e835374d58b003f55eb9819a"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_11() { + let key = "4739a0ad2169b9c89edd74e16fbcecc748c25dc338041fc34af0f1bda20eaf3f"; + let nonce = "ff7b372aa801eb98a1298bc610280737"; + let expected_output = + "06ccde41d10d6466859927bfc9a476dbc84064838ec721261cb548c18bd14c67"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_12() { + let key = "50831c8cb43cd6822bf3f6fae0801cb6c843d8066b07346635365fb7d6ee54e5"; + let nonce = "c9cd6f05d76b2bd4caec8d80b58235cb"; + let expected_output = + "6ed040d7721395fb2c74c8afe252a169ded78e6f2f889e8fb0ec1490533a8154"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_13() { + let key = "4268543ab0eb865a948cc5b5f6e31f05f8146bd9495acc459d6d200005ee72c3"; + let nonce = "bc3e4ae3badfd79adfe46b2ae1045f78"; + let expected_output = + "19b839a6d3424cf2a52d301e70e76cb77368cf9f60945bf43ce4c657aeb1d157"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_14() { + let key = "382e04c969df1a2d6a963a79c58401770a383248b5d70bb4adedcbe520fed634"; + let nonce = "f513b8c2ea6ab37fe633ba7302a5db6c"; + let expected_output = + "fd0739819bae6c98cbde7cb50a80e8d0b359567c50cec1ca7e985745c1cedb3a"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_15() { + let key = "2aa209e24478fa1bd6f6ffabe98555e034342cbec07364c54d1e407e282ef08e"; + let nonce = "dbfdbde936c9d42df58ae15889f5c939"; + let expected_output = + "f5047baa0acf9a603415a09b64268d77712ae902c73490e9c53db593765726db"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_16() { + let key = "a3087eaeac1f2a58e2c2763d01b55744c4a65f4db93adff0078c63f090fb607a"; + let nonce = "90c87defd622e5f55977877cec9ed883"; + let expected_output = + "1d882fa80248882c6bc311a693ebd06b8c09aa2776e6e90df523d12bfeeed77a"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_17() { + let key = "12b0411228540cd6dde6e84cd2da59b1871db119e3298e3c12fe8200a47eddf0"; + let nonce = "49c971cd99f694e3b2a5e25fa37aedf0"; + let expected_output = + "69bb83ccb7bc4deaf60cfe168cb11fad4257222c3523c2d08922564ac0fb74d2"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_18() { + let key = "1bf32e7c679a3187e22a635d301ce98ad000ca301049f2e891e403250c3358fc"; + let nonce = "2030b227bb96e93b88f419afe9f9d660"; + let expected_output = + "d0ed414a875a81db1e4cff7609afdbb2ffcdd575ebc17543fb92de53c6487efb"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_19() { + let key = "e013761228051ec5a8f0c093b33fc60e2cd7a9c845434e95d4319d79d1bdaa8f"; + let nonce = "73853fbd9958e9ffc23a0ecbb7b48dbb"; + let expected_output = + "e3f6c6da6c0300103d665dd877a8b62e23b1361bf3af5bbc2310502131d69be8"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_20() { + let key = "a63672d582bb83d92249800324cbc9a6e5b37d36887e7c79093f58ef8f1a0015"; + let nonce = "85321bfee1714260dd6130cc768d20b1"; + let expected_output = + "97e05360aca70058389d93be38d49fa26df01a4d3b4c4f10c3ec31e0ed64f08e"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_21() { + let key = "4d3850f0eec0f8f349110e751c16cdb5ed05516df17479937d942c90eb1fb181"; + let nonce = "3062bd3f3f6b7668cd8fd3afce0cc752"; + let expected_output = + "77513195542b2ab157cb2e6870c5b1ba143a8423ad276a64152ab923c6f54c06"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_22() { + let key = "9b87dfc58eceb951e1e53d9e94793329199c42d004bc0f0dab3adf0cd702e99e"; + let nonce = "fa5ef6e59d3b201680f8e2d5a4ef7f23"; + let expected_output = + "56a208bd87c5b486b5de50fbe4c1c476532f874147eba529cbb0cbeae8f09b94"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_23() { + let key = "f1b6a8e102670a3829a995ae23fbc3a5639e028cd2b5f71bb90c7a1e4a8a0501"; + let nonce = "7d26e3afc3a88541f6c3f45d71f8a3cc"; + let expected_output = + "a02140057f889e7ab36b4a5066e376dff248d13bd8072c384e23bd8fe4bf7047"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_24() { + let key = "31a063ea4aad1b4d00db6f5228e9b9b1561a7f61812b8b79e6af4292580d02ea"; + let nonce = "4f6266d04244303304510272e383eaa5"; + let expected_output = + "d610d44b8b3c14c7d3782f73405637fd14b7fada717665a9acbd4df6daa89adc"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_25() { + let key = "1a8ea7099a74bafa3375b210653a0d2f40b15afd725cf5065066be1cb803dc15"; + let nonce = "8865ed8d7cca72dcf2b7c6b5d0d045bf"; + let expected_output = + "f10cce296197a056bedbee166183ad6aaa56bdb21c3459296ca54c0bb78317d1"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_26() { + let key = "32b063d3da484ba1843e071b61c49ce7f30ba18a4f7ef2730ecd785494839966"; + let nonce = "f593168e17311913753c59593fc66cb6"; + let expected_output = + "f18115a9568724c25184728f563b65b737219cb0df1b3ce19a8bdcbdf7b8b2be"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_27() { + let key = "64c1572251132fc28bf37fd8e96f2327cf7948a1126fd37175a91f483d6b3ad9"; + let nonce = "2308df7e6daa8bf3efde75f80ad72a49"; + let expected_output = + "06a24cb90abe94cf3ee8e429d8197bc42bc769fbe81119156274f9692aa017a2"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_28() { + let key = "ae0794009e21ad33fa4141fe5fa79fed12f6a20f51614dc130f45598e92549b1"; + let nonce = "13ed6185724507e7fa5a7e8a75b2c7a3"; + let expected_output = + "51d1aec8d64d20e448a377bfa83ccbf71a73a3ad00d062bf6b83c549a7296ef1"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_29() { + let key = "ad700919f36a46ea0ffa680857e30188f8a03c7c4b6c11bc39aececec2668723"; + let nonce = "3682d31887277028e2fd286f2654c681"; + let expected_output = + "a24610a94968df2dc9d197cd0bc55cab08c9dabd444c0efcd2a47fd37016382e"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_30() { + let key = "efd9e7ed6b340874e897337d4dcc672811a6cf4b69086e0a57c266424dc1d10e"; + let nonce = "cbaf0c822cce9e4f17b19e0ece39c180"; + let expected_output = + "6f94a0f8ed7f3fe5ebaa3b8caba016ab64373ffc3c7b1c86e6787f31b4a905ec"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_31() { + let key = "a4c756c03c19900280ff6cdebe5174d507c6e0860c38c3537176c58965b74a56"; + let nonce = "c52b3151bb8a149cf4f82158d57c823f"; + let expected_output = + "50ea3d4f6a45e4a062b2d966e63cac51e093dfb6ab9df6d16bb109bc177b0a38"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_32() { + let key = "3a90c6b427912226ff604d9abee1fb8c8d35530a0cd5808e53e308ac580f7318"; + let nonce = "fe2ab2a4933b5d90db718aa3440fbe9b"; + let expected_output = + "2b57adcc5d26060383c87ef7e055f9aca4addcb2646cbf2cff4edc3f17b72ad5"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_33() { + let key = "a17f09716219bdffc93a189e410a6a3e6477fbb05c7c35956c3c0c5f342355fa"; + let nonce = "0850307998642501c025e3873ebac3cc"; + let expected_output = + "d3a58c49e9fe1ecf2eca169f4d4131cde27279053d562d0429a08ec701aaa39e"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_34() { + let key = "d749d8379ae6d830f785ec104897bd723d34ad20c9d36bfe371df46aebc6d459"; + let nonce = "5d490a770bee4dd0be6a5a0b5e95645c"; + let expected_output = + "c278c0079bd656f1dadf3dec692f19f25339c6557542181716d2a41379740bf2"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_35() { + let key = "7dcbc03c27010df3320fe75b0a3ecc8983ad94217e80348fd0f3f54e54b95bb5"; + let nonce = "48dc2225a264443732b41b861590358d"; + let expected_output = + "b244c408c74f3dcb8bcb72f834a054c554edad0363d761847003dab003ac6848"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_36() { + let key = "543894006b73f3d70fc04b15d0c2a5dfa650be5044fb5061811b866be7f9d623"; + let nonce = "fcb077ee19421610aeb263c57faef006"; + let expected_output = + "fb20ea177cb7225c87122f285d92faf0c2033e2497575f74505255b6d3dfcb96"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_37() { + let key = "62d424c07a7aa5005068b262251c0667a4e2e4b12f5df7f509564517887e370b"; + let nonce = "425fabab1ce9e733ab2911b42074414e"; + let expected_output = + "3a5eb5552cdd267c05c1e4fe936ce8f0eaf7279ff328ed9a42d6d83f7b30416c"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_38() { + let key = "387d7247fa5055489bbd4b7d4de256de723566c1c2d3ecee8c10e7d98233dbef"; + let nonce = "90494951ec91a843f6701f8216a7326b"; + let expected_output = + "8c4bc60a1e05004ec93aef4ae162aeff43d679ea1ba048739c700d6a168bc6cc"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_39() { + let key = "241fd57f32e09976de4054797b9aee820e0de381d02852ac13f511918267b703"; + let nonce = "7330e60ba1c5875a0275f8ccc75cbe98"; + let expected_output = + "9e724c5b0321e2528278a501108f1ae8a14dffaea9b6b138eacef3bd8d4dda41"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_40() { + let key = "7c12457eb5614f87f1fdc40118906d02c602059d48ae05ae62d3d607d6bf63c6"; + let nonce = "760b802483b0e3aaa9dd4f79c6c5e93e"; + let expected_output = + "e5b86f76fbc1f488c44e4d7f304736b752ab6cfb99fcf6910668eeefa4b67c2a"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_41() { + let key = "6b51da45018c6bde108f81f9abfa23640b83cfe3fed34bcf6640bf0baf647daf"; + let nonce = "e9bc99acee972b5a152efa3e69e50f34"; + let expected_output = + "1032b5d539b1c8cd6e0be96db443a08fc759bea8988384435c03b5f00b6e485f"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_42() { + let key = "3bc12887fec8e70db73b4b48dce564d83786aca4c6b7e224163ea928771fde37"; + let nonce = "78c453b35d98deced812fc5685843565"; + let expected_output = + "2279b063dab4c73a96abe02175e694662c65d09eb5889234293c7a1f2911e13d"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_43() { + let key = "b73d097601d3558278bd9d7327de5fdaa2b842050b370e837ef811a496169d5f"; + let nonce = "f768878766c08c45561fdc2aad6469c1"; + let expected_output = + "a8e85a6ab627f08ad415649a9cf9998f4b1065030f3c844e31c8185036af7558"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_44() { + let key = "1380c3d3f873c7233c541ea4c43824ecd8bf7e11ac8486208fb685218d46736e"; + let nonce = "51103d1fae0e8e368f25480ee7328381"; + let expected_output = + "9b84e50804449b594a54240741e21d75d31050d2612f4cbc651fea2f25bd9c1f"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_45() { + let key = "c2f8b252a18a29c44dbfbb62cbe6c3dfd4db55378734d8110b8f20f1d1ada6dd"; + let nonce = "d4da48fb09c06580eb46bbc5ca62bfab"; + let expected_output = + "315c3fe1009e438762a72f27e7a68b8ccb2c0b60bf79cb6e48123db0c42d4aeb"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_46() { + let key = "40b184271b73b710d40cb63435042c9b526d1e5c3a77bfc516a2bcb4cc27ecae"; + let nonce = "b3451318590c84e311dd1e876f527d81"; + let expected_output = + "cbbde3a3412504c1f684aa273ee691159edc9f44e306360278d63d4ee2f1faa4"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_47() { + let key = "ec81df06c7e426b729aebb02be30c846eb228490df4a0e6c688aaaa6bf05d144"; + let nonce = "28335f2652926bfdfe32dfd789173ba8"; + let expected_output = + "522b522e4cf9aa1e80126a446ed7b9665af3e781a3d5afdce43a5fe0cdbd4351"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_48() { + let key = "60fa0114802ee333d7c49ccaad8108db470c882514716592e57aba26bb75049b"; + let nonce = "75db088bd1a89c6a67fb76b96c987478"; + let expected_output = + "e004cc12dfdb74268e59958385e2a1c6ff31e31664838971629f5bbf88f4ed51"; + hchacha_test_runner(key, nonce, expected_output); + } + #[test] + fn test_case_49() { + let key = "bfba2449a607f3cca1c911d3b7d9cb972bcd84b0246189c7820032e031949f1e"; + let nonce = "97e8ad5eb5a75cc805900850969de48e"; + let expected_output = + "19faebfbb954552fcfbf9b91f271c9397a15c641733c394a9cb731c286c68645"; + hchacha_test_runner(key, nonce, expected_output); + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + mod test_init_state { + use super::*; + + #[test] + fn test_nonce_length() { + assert!(ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; 15], true).is_err()); + assert!(ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; 10], true).is_err()); + assert!( + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE], true).is_ok() + ); + + assert!(ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; 15], false).is_err()); + assert!(ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; 17], false).is_err()); + assert!( + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; HCHACHA_NONCESIZE], false).is_ok() + ); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_test_nonce_length_ietf(nonce: Vec) -> bool { + if nonce.len() == IETF_CHACHA_NONCESIZE { + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &nonce[..], true).is_ok() + } else { + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &nonce[..], true).is_err() + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + // Always fail to initialize state while the nonce is not + // the correct length. If it is correct length, never panic. + fn prop_test_nonce_length_hchacha(nonce: Vec) -> bool { + if nonce.len() == HCHACHA_NONCESIZE { + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &nonce, false).is_ok() + } else { + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &nonce, false).is_err() + } + } + } + + mod test_encrypt_in_place { + use super::*; + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn test_xor_keystream_err_bad_tmp() { + let mut ctx = + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE], true).unwrap(); + let mut tmp = [0u8; CHACHA_BLOCKSIZE - 1]; + let mut out = [0u8; CHACHA_BLOCKSIZE]; + xor_keystream(&mut ctx, 0, &mut tmp, &mut out).unwrap(); + } + + #[test] + fn test_xor_keystream_err_empty_input() { + let mut ctx = + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE], true).unwrap(); + let mut tmp = [0u8; CHACHA_BLOCKSIZE]; + let mut out = [0u8; 0]; + assert!(xor_keystream(&mut ctx, 0, &mut tmp, &mut out).is_err()); + } + + #[test] + fn test_enc_in_place_err_empty_input() { + let n = Nonce::from([0u8; IETF_CHACHA_NONCESIZE]); + let sk = SecretKey::from([0u8; CHACHA_KEYSIZE]); + let mut out = [0u8; 0]; + assert!(encrypt_in_place(&sk, &n, 0, &mut out).is_err()); + } + } + + mod test_keystream_block { + use super::*; + + #[test] + fn test_xor_keystream_block_ignore_counter_when_hchacha() { + let mut chacha_state_hchacha = + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; HCHACHA_NONCESIZE], false).unwrap(); + + let mut hchacha_keystream_block_zero = [0u8; HCHACHA_OUTSIZE]; + let mut hchacha_keystream_block_max = [0u8; HCHACHA_OUTSIZE]; + + chacha_state_hchacha.keystream_block(0, &mut hchacha_keystream_block_zero); + chacha_state_hchacha.keystream_block(u32::MAX, &mut hchacha_keystream_block_max); + + assert_eq!(hchacha_keystream_block_zero, hchacha_keystream_block_max); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn test_xor_keystream_block_invalid_blocksize_ietf() { + let mut chacha_state_ietf = + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE], true).unwrap(); + + let mut ietf_keystream_block = [0u8; CHACHA_BLOCKSIZE]; + let mut hchacha_keystream_block = [0u8; HCHACHA_OUTSIZE]; + + chacha_state_ietf.keystream_block(0, &mut ietf_keystream_block); + chacha_state_ietf.keystream_block(0, &mut hchacha_keystream_block); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn test_xor_keystream_block_invalid_blocksize_hchacha() { + let mut chacha_state_hchacha = + ChaCha20::new(&[0u8; CHACHA_KEYSIZE], &[0u8; HCHACHA_NONCESIZE], false).unwrap(); + + let mut ietf_keystream_block = [0u8; CHACHA_BLOCKSIZE]; + let mut hchacha_keystream_block = [0u8; HCHACHA_OUTSIZE]; + + chacha_state_hchacha.keystream_block(0, &mut hchacha_keystream_block); + chacha_state_hchacha.keystream_block(0, &mut ietf_keystream_block); + } + + #[test] + #[should_panic] + fn test_xor_keystream_panic_on_too_much_keystream_data_ietf() { + let mut chacha_state_ietf = ChaCha20 { + state: [ + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + ], + internal_counter: (u32::MAX - 128), + is_ietf: true, + }; + + let mut keystream_block = [0u8; CHACHA_BLOCKSIZE]; + + for amount in 0..(128 + 1) { + chacha_state_ietf.keystream_block(amount, &mut keystream_block); + } + } + + #[test] + #[should_panic] + fn test_xor_keystream_panic_on_too_much_keystream_data_hchacha() { + let mut chacha_state_ietf = ChaCha20 { + state: [ + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + ], + internal_counter: (u32::MAX - 128), + is_ietf: false, + }; + + let mut keystream_block = [0u8; HCHACHA_OUTSIZE]; + + for _ in 0..(128 + 1) { + chacha_state_ietf.keystream_block(0, &mut keystream_block); + } + } + + #[test] + fn test_error_if_internal_counter_would_overflow() { + let mut chacha_state = ChaCha20 { + state: [ + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + U32x4(0, 0, 0, 0), + ], + internal_counter: (u32::MAX - 2), + is_ietf: false, + }; + + assert!(chacha_state.next_produceable().is_ok()); + chacha_state.internal_counter += 1; + assert!(chacha_state.next_produceable().is_ok()); + chacha_state.internal_counter += 1; + assert!(chacha_state.next_produceable().is_err()); + } + } +} + +// Testing any test vectors that aren't put into library's /tests folder. +#[cfg(test)] +mod test_vectors { + use super::*; + + // NOTE: These PartialEq implementation should only be available in testing. + #[cfg(test)] + impl PartialEq for U32x4 { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 && self.1 == other.1 && self.2 == other.2 && self.3 == other.3 + } + } + + // Convenience function for testing. + fn init(key: &[u8], nonce: &[u8]) -> Result { + ChaCha20::new(key, nonce, true) + } + #[test] + fn rfc8439_chacha20_block_results() { + let key = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, + 0x71, 0xc4, 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, + 0xc3, 0xd4, 0x6c, 0x4e, 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, + 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2, 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, + 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e, + ]; + + let expected_init = [ + U32x4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574), + U32x4(0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c), + U32x4(0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c), + U32x4(0x00000001, 0x09000000, 0x4a000000, 0x00000000), + ]; + // Test initial key-setup + let mut state = init(&key, &nonce).unwrap(); + // Set block counter + state.state[3].0 = 1; + assert!(state.state[..] == expected_init[..]); + + let mut kb = [0u8; 64]; + state.keystream_block(1, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_1() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, + 0xbd, 0x28, 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, 0xa8, 0x36, 0xef, 0xcc, + 0x8b, 0x77, 0x0d, 0xc7, 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, 0x77, 0x24, + 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, + 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut kb = [0u8; 64]; + state.keystream_block(0, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_2() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x9f, 0x07, 0xe7, 0xbe, 0x55, 0x51, 0x38, 0x7a, 0x98, 0xba, 0x97, 0x7c, 0x73, 0x2d, + 0x08, 0x0d, 0xcb, 0x0f, 0x29, 0xa0, 0x48, 0xe3, 0x65, 0x69, 0x12, 0xc6, 0x53, 0x3e, + 0x32, 0xee, 0x7a, 0xed, 0x29, 0xb7, 0x21, 0x76, 0x9c, 0xe6, 0x4e, 0x43, 0xd5, 0x71, + 0x33, 0xb0, 0x74, 0xd8, 0x39, 0xd5, 0x31, 0xed, 0x1f, 0x28, 0x51, 0x0a, 0xfb, 0x45, + 0xac, 0xe1, 0x0a, 0x1f, 0x4b, 0x79, 0x4d, 0x6f, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut kb = [0u8; 64]; + state.keystream_block(1, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_3() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x3a, 0xeb, 0x52, 0x24, 0xec, 0xf8, 0x49, 0x92, 0x9b, 0x9d, 0x82, 0x8d, 0xb1, 0xce, + 0xd4, 0xdd, 0x83, 0x20, 0x25, 0xe8, 0x01, 0x8b, 0x81, 0x60, 0xb8, 0x22, 0x84, 0xf3, + 0xc9, 0x49, 0xaa, 0x5a, 0x8e, 0xca, 0x00, 0xbb, 0xb4, 0xa7, 0x3b, 0xda, 0xd1, 0x92, + 0xb5, 0xc4, 0x2f, 0x73, 0xf2, 0xfd, 0x4e, 0x27, 0x36, 0x44, 0xc8, 0xb3, 0x61, 0x25, + 0xa6, 0x4a, 0xdd, 0xeb, 0x00, 0x6c, 0x13, 0xa0, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut kb = [0u8; 64]; + state.keystream_block(1, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_4() { + let key = [ + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x72, 0xd5, 0x4d, 0xfb, 0xf1, 0x2e, 0xc4, 0x4b, 0x36, 0x26, 0x92, 0xdf, 0x94, 0x13, + 0x7f, 0x32, 0x8f, 0xea, 0x8d, 0xa7, 0x39, 0x90, 0x26, 0x5e, 0xc1, 0xbb, 0xbe, 0xa1, + 0xae, 0x9a, 0xf0, 0xca, 0x13, 0xb2, 0x5a, 0xa2, 0x6c, 0xb4, 0xa6, 0x48, 0xcb, 0x9b, + 0x9d, 0x1b, 0xe6, 0x5b, 0x2c, 0x09, 0x24, 0xa6, 0x6c, 0x54, 0xd5, 0x45, 0xec, 0x1b, + 0x73, 0x74, 0xf4, 0x87, 0x2e, 0x99, 0xf0, 0x96, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut kb = [0u8; 64]; + state.keystream_block(2, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_5() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]; + let expected = [ + 0xc2, 0xc6, 0x4d, 0x37, 0x8c, 0xd5, 0x36, 0x37, 0x4a, 0xe2, 0x04, 0xb9, 0xef, 0x93, + 0x3f, 0xcd, 0x1a, 0x8b, 0x22, 0x88, 0xb3, 0xdf, 0xa4, 0x96, 0x72, 0xab, 0x76, 0x5b, + 0x54, 0xee, 0x27, 0xc7, 0x8a, 0x97, 0x0e, 0x0e, 0x95, 0x5c, 0x14, 0xf3, 0xa8, 0x8e, + 0x74, 0x1b, 0x97, 0xc2, 0x86, 0xf7, 0x5f, 0x8f, 0xc2, 0x99, 0xe8, 0x14, 0x83, 0x62, + 0xfa, 0x19, 0x8a, 0x39, 0x53, 0x1b, 0xed, 0x6d, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut kb = [0u8; 64]; + state.keystream_block(0, &mut kb); + + assert_eq!(kb[..], expected[..]); + } + + #[test] + fn rfc8439_key_schedule() { + let key = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, + ]; + // First block setup expected + let first_state = [ + U32x4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574), + U32x4(0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c), + U32x4(0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c), + U32x4(0x00000001, 0x00000000, 0x4a000000, 0x00000000), + ]; + // Second block setup expected + let second_state = [ + U32x4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574), + U32x4(0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c), + U32x4(0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c), + U32x4(0x00000002, 0x00000000, 0x4a000000, 0x00000000), + ]; + + // Expected keystream + let expected_keystream = [ + 0x22, 0x4f, 0x51, 0xf3, 0x40, 0x1b, 0xd9, 0xe1, 0x2f, 0xde, 0x27, 0x6f, 0xb8, 0x63, + 0x1d, 0xed, 0x8c, 0x13, 0x1f, 0x82, 0x3d, 0x2c, 0x06, 0xe2, 0x7e, 0x4f, 0xca, 0xec, + 0x9e, 0xf3, 0xcf, 0x78, 0x8a, 0x3b, 0x0a, 0xa3, 0x72, 0x60, 0x0a, 0x92, 0xb5, 0x79, + 0x74, 0xcd, 0xed, 0x2b, 0x93, 0x34, 0x79, 0x4c, 0xba, 0x40, 0xc6, 0x3e, 0x34, 0xcd, + 0xea, 0x21, 0x2c, 0x4c, 0xf0, 0x7d, 0x41, 0xb7, 0x69, 0xa6, 0x74, 0x9f, 0x3f, 0x63, + 0x0f, 0x41, 0x22, 0xca, 0xfe, 0x28, 0xec, 0x4d, 0xc4, 0x7e, 0x26, 0xd4, 0x34, 0x6d, + 0x70, 0xb9, 0x8c, 0x73, 0xf3, 0xe9, 0xc5, 0x3a, 0xc4, 0x0c, 0x59, 0x45, 0x39, 0x8b, + 0x6e, 0xda, 0x1a, 0x83, 0x2c, 0x89, 0xc1, 0x67, 0xea, 0xcd, 0x90, 0x1d, 0x7e, 0x2b, + 0xf3, 0x63, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let mut actual_keystream = [0u8; 128]; + + state.keystream_block(1, &mut actual_keystream[..64]); + assert!(first_state == state.state); + + state.keystream_block(2, &mut actual_keystream[64..]); + assert!(second_state == state.state); + + assert_eq!( + actual_keystream[..expected_keystream.len()].as_ref(), + expected_keystream.as_ref() + ); + } +} diff --git a/vendor/orion/src/hazardous/stream/mod.rs b/vendor/orion/src/hazardous/stream/mod.rs new file mode 100644 index 0000000..a211b7d --- /dev/null +++ b/vendor/orion/src/hazardous/stream/mod.rs @@ -0,0 +1,27 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// IETF ChaCha20 as specified in the [RFC 8439](https://tools.ietf.org/html/rfc8439). +pub mod chacha20; + +/// XChaCha20 as specified in the [draft-irtf-cfrg-xchacha-03](https://tools.ietf.org/html/draft-irtf-cfrg-xchacha-03). +pub mod xchacha20; diff --git a/vendor/orion/src/hazardous/stream/xchacha20.rs b/vendor/orion/src/hazardous/stream/xchacha20.rs new file mode 100644 index 0000000..b8940aa --- /dev/null +++ b/vendor/orion/src/hazardous/stream/xchacha20.rs @@ -0,0 +1,176 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! # Parameters: +//! - `secret_key`: The secret key. +//! - `nonce`: The nonce value. +//! - `initial_counter`: The initial counter value. In most cases, this is `0`. +//! - `ciphertext`: The encrypted data. +//! - `plaintext`: The data to be encrypted. +//! - `dst_out`: Destination array that will hold the ciphertext/plaintext after +//! encryption/decryption. +//! +//! `dst_out`: The output buffer may have a capacity greater than the input. If this is the case, +//! only the first input length amount of bytes in `dst_out` are modified, while the rest remain untouched. +//! +//! # Errors: +//! An error will be returned if: +//! - The length of `dst_out` is less than `plaintext` or `ciphertext`. +//! - `plaintext` or `ciphertext` is empty. +//! - The `initial_counter` is high enough to cause a potential overflow. +//! +//! Even though `dst_out` is allowed to be of greater length than `plaintext`, +//! the `ciphertext` produced by `chacha20`/`xchacha20` will always be of the +//! same length as the `plaintext`. +//! +//! # Panics: +//! A panic will occur if: +//! - More than `2^32-1 * 64` bytes of data are processed. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, the security of all data that has been encrypted +//! with that given key is compromised. +//! - Functions herein do not provide any data integrity. If you need +//! data integrity, which is nearly ***always the case***, you should use an +//! AEAD construction instead. See the [`aead`](super::aead) module for this. +//! - Only a nonce for XChaCha20 is big enough to be randomly generated using a +//! CSPRNG. [`Nonce::generate()`] can be used for this. +//! - To securely generate a strong key, use [`SecretKey::generate()`]. +//! +//! # Recommendation: +//! - It is recommended to use [`XChaCha20Poly1305`] when possible. +//! +//! # Example: +//! ```rust +//! # #[cfg(feature = "safe_api")] { +//! use orion::hazardous::stream::xchacha20; +//! +//! let secret_key = xchacha20::SecretKey::generate(); +//! let nonce = xchacha20::Nonce::generate(); +//! let message = "Data to protect".as_bytes(); +//! +//! // Length of this message is 15 +//! +//! let mut dst_out_pt = [0u8; 15]; +//! let mut dst_out_ct = [0u8; 15]; +//! +//! xchacha20::encrypt(&secret_key, &nonce, 0, message, &mut dst_out_ct)?; +//! +//! xchacha20::decrypt(&secret_key, &nonce, 0, &dst_out_ct, &mut dst_out_pt)?; +//! +//! assert_eq!(dst_out_pt, message); +//! # } +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [`SecretKey::generate()`]: xchacha20::SecretKey::generate() +//! [`Nonce::generate()`]: xchacha20::Nonce::generate() +//! [`XChaCha20Poly1305`]: super::aead::xchacha20poly1305 +pub use crate::hazardous::stream::chacha20::SecretKey; +use crate::{ + errors::UnknownCryptoError, + hazardous::stream::chacha20::{self, Nonce as IETFNonce, IETF_CHACHA_NONCESIZE}, +}; + +/// The nonce size for XChaCha20. +pub const XCHACHA_NONCESIZE: usize = 24; + +construct_public! { + /// A type that represents a `Nonce` that XChaCha20, XChaCha20-Poly1305 use. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is not 24 bytes. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Nonce, test_nonce, XCHACHA_NONCESIZE, XCHACHA_NONCESIZE, XCHACHA_NONCESIZE) +} + +impl_from_trait!(Nonce, XCHACHA_NONCESIZE); + +/// Generate a subkey using HChaCha20 for XChaCha20 and corresponding nonce. +pub(crate) fn subkey_and_nonce(secret_key: &SecretKey, nonce: &Nonce) -> (SecretKey, IETFNonce) { + // .unwrap() should not be able to panic because we pass a 16-byte nonce. + let subkey: SecretKey = + SecretKey::from(chacha20::hchacha20(secret_key, &nonce.as_ref()[0..16]).unwrap()); + let mut prefixed_nonce = [0u8; IETF_CHACHA_NONCESIZE]; + prefixed_nonce[4..IETF_CHACHA_NONCESIZE].copy_from_slice(&nonce.as_ref()[16..24]); + + (subkey, IETFNonce::from(prefixed_nonce)) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// XChaCha20 encryption as specified in the [draft RFC](https://tools.ietf.org/html/draft-irtf-cfrg-xchacha-03). +pub fn encrypt( + secret_key: &SecretKey, + nonce: &Nonce, + initial_counter: u32, + plaintext: &[u8], + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + let (subkey, ietf_nonce) = subkey_and_nonce(secret_key, nonce); + + chacha20::encrypt(&subkey, &ietf_nonce, initial_counter, plaintext, dst_out) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// XChaCha20 decryption as specified in the [draft RFC](https://tools.ietf.org/html/draft-irtf-cfrg-xchacha-03). +pub fn decrypt( + secret_key: &SecretKey, + nonce: &Nonce, + initial_counter: u32, + ciphertext: &[u8], + dst_out: &mut [u8], +) -> Result<(), UnknownCryptoError> { + encrypt(secret_key, nonce, initial_counter, ciphertext, dst_out) +} + +// Testing public functions in the module. +#[cfg(test)] +#[cfg(feature = "safe_api")] +mod public { + use super::*; + + mod test_encrypt_decrypt { + use super::*; + use crate::test_framework::streamcipher_interface::*; + + impl TestingRandom for Nonce { + fn gen() -> Self { + Self::generate() + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_streamcipher_interface(input: Vec, counter: u32) -> bool { + let secret_key = SecretKey::generate(); + let nonce = Nonce::generate(); + StreamCipherTestRunner(encrypt, decrypt, secret_key, nonce, counter, &input, None); + test_diff_params_diff_output(&encrypt, &decrypt); + + true + } + } +} diff --git a/vendor/orion/src/high_level/aead.rs b/vendor/orion/src/high_level/aead.rs new file mode 100644 index 0000000..4159ef8 --- /dev/null +++ b/vendor/orion/src/high_level/aead.rs @@ -0,0 +1,631 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! Authenticated secret-key encryption. +//! +//! # Use case: +//! `orion::aead` can be used to encrypt data in a way that detects if the +//! encrypted data has been tampered with before decrypting it. +//! +//! An example of this could be sending messages across networks, where +//! confidentiality and authenticity of these messages is required. +//! +//! # About: +//! - Both one-shot functions and a [`streaming`] API are provided. +//! - The nonce is automatically generated. +//! - Returns a vector where the first 24 bytes are the nonce and the rest is +//! the authenticated ciphertext with the last 16 bytes being the corresponding Poly1305 tag. +//! - Uses XChaCha20Poly1305 with no additional data. +//! - When using [`seal`] and [`open`] then the separation of tags, nonces and +//! ciphertext are automatically handled. +//! +//! # Parameters: +//! - `plaintext`: The data to be encrypted. +//! - `secret_key`: The secret key used to encrypt the `plaintext`. +//! - `ciphertext_with_tag_and_nonce`: The data to be decrypted with the first +//! 24 bytes being the nonce and the last 16 bytes being the corresponding Poly1305 tag. +//! +//! # Errors: +//! An error will be returned if: +//! - `secret_key` is not 32 bytes. +//! - The `plaintext` is empty. +//! - `ciphertext_with_tag_and_nonce` is less than 41 bytes +//! ([`XCHACHA_NONCESIZE`] + [`POLY1305_OUTSIZE`] + 1). +//! - The received tag does not match the calculated tag when calling [`open`]. +//! - `plaintext.len()` + [`XCHACHA_NONCESIZE`] + [`POLY1305_OUTSIZE`] overflows when calling [`seal`]. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2^32-1 * 64 bytes of data are processed. +//! - Failure to generate random bytes securely. +//! +//! # Security: +//! - It is critical for security that a given nonce is not re-used with a given +//! key. Should this happen, +//! the security of all data that has been encrypted with that given key is +//! compromised. +//! - To securely generate a strong key, use [`SecretKey::default()`]. +//! - The length of the `plaintext` is not hidden, only its contents. +//! +//! # Example: +//! ```rust +//! use orion::aead; +//! +//! let secret_key = aead::SecretKey::default(); +//! let ciphertext = aead::seal(&secret_key, b"Secret message")?; +//! let decrypted_data = aead::open(&secret_key, &ciphertext)?; +//! +//! assert_eq!(decrypted_data, b"Secret message"); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use super::hltypes::SecretKey; +use crate::{ + errors::UnknownCryptoError, + hazardous::{ + aead, + mac::poly1305::POLY1305_OUTSIZE, + stream::{ + chacha20, + xchacha20::{Nonce, XCHACHA_NONCESIZE}, + }, + }, +}; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Authenticated encryption using XChaCha20Poly1305. +pub fn seal(secret_key: &SecretKey, plaintext: &[u8]) -> Result, UnknownCryptoError> { + if plaintext.is_empty() { + return Err(UnknownCryptoError); + } + + let out_len = match plaintext + .len() + .checked_add(XCHACHA_NONCESIZE + POLY1305_OUTSIZE) + { + Some(min_out_len) => min_out_len, + None => return Err(UnknownCryptoError), + }; + + let mut dst_out = vec![0u8; out_len]; + let nonce = Nonce::generate(); + dst_out[..XCHACHA_NONCESIZE].copy_from_slice(nonce.as_ref()); + + aead::xchacha20poly1305::seal( + &chacha20::SecretKey::from_slice(secret_key.unprotected_as_bytes())?, + &nonce, + plaintext, + None, + &mut dst_out[XCHACHA_NONCESIZE..], + )?; + + Ok(dst_out) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Authenticated decryption using XChaCha20Poly1305. +pub fn open( + secret_key: &SecretKey, + ciphertext_with_tag_and_nonce: &[u8], +) -> Result, UnknownCryptoError> { + // Avoid empty ciphertexts + if ciphertext_with_tag_and_nonce.len() <= (XCHACHA_NONCESIZE + POLY1305_OUTSIZE) { + return Err(UnknownCryptoError); + } + + let mut dst_out = + vec![0u8; ciphertext_with_tag_and_nonce.len() - (XCHACHA_NONCESIZE + POLY1305_OUTSIZE)]; + + aead::xchacha20poly1305::open( + &chacha20::SecretKey::from_slice(secret_key.unprotected_as_bytes())?, + &Nonce::from_slice(&ciphertext_with_tag_and_nonce[..XCHACHA_NONCESIZE])?, + &ciphertext_with_tag_and_nonce[XCHACHA_NONCESIZE..], + None, + &mut dst_out, + )?; + + Ok(dst_out) +} + +pub mod streaming { + //! Streaming AEAD based on XChaCha20Poly1305. + //! + //! # Use case: + //! This can be used to encrypt and authenticate a stream of data. It prevents the + //! modification, reordering, dropping or duplication of messages. Nonce management is handled automatically. + //! + //! An example of this could be the encryption of files that are too large to encrypt in one piece. + //! + //! # About: + //! This implementation is based on and compatible with the ["secretstream" API] of libsodium. + //! + //! # Parameters: + //! - `secret_key`: The secret key. + //! - `nonce`: The nonce value. + //! - `plaintext`: The data to be encrypted. + //! - `ciphertext`: The encrypted data with a Poly1305 tag and a [`StreamTag`] indicating its function. + //! - `tag`: Indicates the type of message. The `tag` is a part of the output when encrypting. It + //! is encrypted and authenticated. + //! + //! # Errors: + //! An error will be returned if: + //! - `secret_key` is not 32 bytes. + //! - The length of `ciphertext` is not at least [`ABYTES`]. + //! - The received mac does not match the calculated mac when decrypting. This can indicate + //! a dropped or reordered message within the stream. + //! - More than 2^32-3 * 64 bytes of data are processed when encrypting/decrypting a single chunk. + //! - [`ABYTES`] + `plaintext.len()` overflows when encrypting. + //! + //! # Panics: + //! A panic will occur if: + //! - 64 + (`ciphertext.len()` - [`ABYTES`]) overflows when decrypting. + //! - Failure to generate random bytes securely. + //! + //! # Security: + //! - It is critical for security that a given nonce is not re-used with a given + //! key. + //! - To securely generate a strong key, use [`SecretKey::generate()`]. + //! - The length of the messages is leaked. + //! - It is recommended to use `StreamTag::Finish` as tag for the last message. This allows the + //! decrypting side to detect if messages at the end of the stream are lost. + //! + //! # Example: + //! ```rust + //! use orion::aead::streaming::*; + //! use orion::aead::SecretKey; + //! + //! let chunk_size: usize = 128; // The size of the chunks you wish to split the stream into. + //! let src = [255u8; 4096]; // Some example input stream. + //! let mut out: Vec> = Vec::with_capacity(4096 / 128); + //! + //! let secret_key = SecretKey::default(); + //! + //! // Encryption: + //! let (mut sealer, nonce) = StreamSealer::new(&secret_key)?; + //! + //! for (n_chunk, src_chunk) in src.chunks(chunk_size).enumerate() { + //! let encrypted_chunk = + //! if src_chunk.len() != chunk_size || n_chunk + 1 == src.len() / chunk_size { + //! // We've reached the end of the input source, + //! // so we mark it with the Finish tag. + //! sealer.seal_chunk(src_chunk, &StreamTag::Finish)? + //! } else { + //! // Just a normal chunk + //! sealer.seal_chunk(src_chunk, &StreamTag::Message)? + //! }; + //! // Save the encrypted chunk somewhere + //! out.push(encrypted_chunk); + //! } + //! + //! // Decryption: + //! let mut opener = StreamOpener::new(&secret_key, &nonce)?; + //! + //! for (n_chunk, src_chunk) in out.iter().enumerate() { + //! let (_decrypted_chunk, tag) = opener.open_chunk(src_chunk)?; + //! + //! if src_chunk.len() != chunk_size + ABYTES || n_chunk + 1 == out.len() { + //! // We've reached the end of the input source, + //! // so we check if the last chunk is also set as Finish. + //! assert_eq!(tag, StreamTag::Finish, "Stream has been truncated!"); + //! } + //! } + //! + //! # Ok::<(), orion::errors::UnknownCryptoError>(()) + //! ``` + //! [`ABYTES`]: crate::hazardous::aead::streaming::ABYTES + //! [`StreamTag`]: crate::hazardous::aead::streaming::StreamTag + //! [`SecretKey::generate()`]: super::SecretKey::generate + //! ["secretstream" API]: https://download.libsodium.org/doc/secret-key_cryptography/secretstream + + use super::*; + pub use crate::hazardous::aead::streaming::Nonce; + pub use crate::hazardous::aead::streaming::StreamTag; + pub use crate::hazardous::aead::streaming::ABYTES; + + #[derive(Debug)] + /// Streaming authenticated encryption. + pub struct StreamSealer { + internal_sealer: aead::streaming::StreamXChaCha20Poly1305, + } + + impl StreamSealer { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize a `StreamSealer` struct with a given key. + pub fn new(secret_key: &SecretKey) -> Result<(Self, Nonce), UnknownCryptoError> { + let nonce = Nonce::generate(); + let sk = &aead::streaming::SecretKey::from_slice(secret_key.unprotected_as_bytes())?; + + let sealer = Self { + internal_sealer: aead::streaming::StreamXChaCha20Poly1305::new(sk, &nonce), + }; + Ok((sealer, nonce)) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Encrypts `plaintext`. The `StreamTag` indicates the type of message. + pub fn seal_chunk( + &mut self, + plaintext: &[u8], + tag: &StreamTag, + ) -> Result, UnknownCryptoError> { + let sealed_chunk_len = plaintext.len().checked_add(ABYTES); + if sealed_chunk_len.is_none() { + return Err(UnknownCryptoError); + } + + let mut sealed_chunk = vec![0u8; sealed_chunk_len.unwrap()]; + self.internal_sealer + .seal_chunk(plaintext, None, &mut sealed_chunk, tag)?; + + Ok(sealed_chunk) + } + } + + #[derive(Debug)] + /// Streaming authenticated decryption. + pub struct StreamOpener { + internal_sealer: aead::streaming::StreamXChaCha20Poly1305, + } + + impl StreamOpener { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Initialize a `StreamOpener` struct with a given key and nonce. + pub fn new(secret_key: &SecretKey, nonce: &Nonce) -> Result { + let sk = &chacha20::SecretKey::from_slice(secret_key.unprotected_as_bytes())?; + + Ok(Self { + internal_sealer: aead::streaming::StreamXChaCha20Poly1305::new(sk, nonce), + }) + } + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Decrypts `ciphertext`. Returns the decrypted data and the `StreamTag` indicating the type of message. + pub fn open_chunk( + &mut self, + ciphertext: &[u8], + ) -> Result<(Vec, StreamTag), UnknownCryptoError> { + if ciphertext.len() < ABYTES { + return Err(UnknownCryptoError); + } + + let mut opened_chunk = vec![0u8; ciphertext.len() - ABYTES]; + let tag = self + .internal_sealer + .open_chunk(ciphertext, None, &mut opened_chunk)?; + + Ok((opened_chunk, tag)) + } + } +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_seal_open { + use super::*; + + #[test] + fn test_auth_enc_encryption_decryption() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + + let dst_ciphertext = seal(&key, plaintext).unwrap(); + assert_eq!(dst_ciphertext.len(), plaintext.len() + (24 + 16)); + let dst_plaintext = open(&key, &dst_ciphertext).unwrap(); + assert_eq!(plaintext, &dst_plaintext[..]); + } + + #[test] + fn test_auth_enc_plaintext_empty_err() { + let key = SecretKey::default(); + let plaintext = "".as_bytes(); + + assert!(seal(&key, plaintext).is_err()); + } + + #[test] + fn test_auth_enc_ciphertext_less_than_41_err() { + let key = SecretKey::default(); + let ciphertext = [0u8; XCHACHA_NONCESIZE + POLY1305_OUTSIZE]; + + assert!(open(&key, &ciphertext).is_err()); + } + + #[test] + fn test_modified_nonce_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = seal(&key, plaintext).unwrap(); + // Modify nonce + dst_ciphertext[10] ^= 1; + assert!(open(&key, &dst_ciphertext).is_err()); + } + + #[test] + fn test_modified_ciphertext_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = seal(&key, plaintext).unwrap(); + // Modify ciphertext + dst_ciphertext[25] ^= 1; + assert!(open(&key, &dst_ciphertext).is_err()); + } + + #[test] + fn test_modified_tag_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = seal(&key, plaintext).unwrap(); + let dst_ciphertext_len = dst_ciphertext.len(); + // Modify tag + dst_ciphertext[dst_ciphertext_len - 6] ^= 1; + assert!(open(&key, &dst_ciphertext).is_err()); + } + + #[test] + fn test_diff_secret_key_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + + let dst_ciphertext = seal(&key, plaintext).unwrap(); + let bad_key = SecretKey::default(); + assert!(open(&bad_key, &dst_ciphertext).is_err()); + } + + #[test] + fn test_secret_length_err() { + let key = SecretKey::generate(31).unwrap(); + let plaintext = "Secret message".as_bytes(); + + assert!(seal(&key, plaintext).is_err()); + assert!(open(&key, plaintext).is_err()); + } + } + + mod test_stream_seal_open { + use super::streaming::*; + use super::*; + + #[test] + fn test_auth_enc_encryption_decryption() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let plaintext = "Secret message".as_bytes(); + + let dst_ciphertext = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + assert_eq!(dst_ciphertext.len(), plaintext.len() + 17); + let (dst_plaintext, tag) = opener.open_chunk(&dst_ciphertext).unwrap(); + assert_eq!(plaintext, &dst_plaintext[..]); + assert_eq!(tag, StreamTag::Message); + } + + #[test] + fn test_seal_chunk_plaintext_empty_ok() { + let key = SecretKey::default(); + let (mut sealer, _) = StreamSealer::new(&key).unwrap(); + let plaintext = "".as_bytes(); + + assert!(sealer.seal_chunk(plaintext, &StreamTag::Message).is_ok()); + } + + #[test] + fn test_open_chunk_less_than_abytes_err() { + let key = SecretKey::default(); + let ciphertext = [0u8; ABYTES - 1]; + let (_, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + + assert!(opener.open_chunk(&ciphertext).is_err()); + } + + #[test] + fn test_open_chunk_abytes_exact_ok() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let ciphertext = sealer + .seal_chunk("".as_bytes(), &StreamTag::Message) + .unwrap(); + let (pt, tag) = opener.open_chunk(&ciphertext).unwrap(); + + assert!(pt.is_empty()); + assert_eq!(tag.as_byte(), 0u8); + } + + #[test] + fn test_modified_tag_err() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + // Modify tag + dst_ciphertext[0] ^= 1; + assert!(opener.open_chunk(&dst_ciphertext).is_err()); + } + + #[test] + fn test_modified_ciphertext_err() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + // Modify ciphertext + dst_ciphertext[1] ^= 1; + assert!(opener.open_chunk(&dst_ciphertext).is_err()); + } + + #[test] + fn test_modified_mac_err() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let plaintext = "Secret message".as_bytes(); + + let mut dst_ciphertext = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + // Modify mac + let macpos = dst_ciphertext.len() - 1; + dst_ciphertext[macpos] ^= 1; + assert!(opener.open_chunk(&dst_ciphertext).is_err()); + } + + #[test] + fn test_diff_secret_key_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let bad_key = SecretKey::default(); + let mut opener = StreamOpener::new(&bad_key, &nonce).unwrap(); + + let dst_ciphertext = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + + assert!(opener.open_chunk(&dst_ciphertext).is_err()); + } + + #[test] + fn test_secret_length_err() { + let key = SecretKey::generate(31).unwrap(); + assert!(StreamSealer::new(&key).is_err()); + assert!(StreamOpener::new(&key, &Nonce::generate()).is_err()); + } + + #[test] + fn same_input_generates_different_ciphertext() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let plaintext = "Secret message 1".as_bytes(); + let cipher1 = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + let cipher2 = sealer.seal_chunk(plaintext, &StreamTag::Message).unwrap(); + assert_ne!(cipher1, cipher2); + + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let (dec1, tag1) = opener.open_chunk(&cipher1).unwrap(); + let (dec2, tag2) = opener.open_chunk(&cipher2).unwrap(); + assert_eq!(plaintext, &dec1[..]); + assert_eq!(plaintext, &dec2[..]); + assert_eq!(tag1, StreamTag::Message); + assert_eq!(tag2, StreamTag::Message); + } + + #[test] + fn same_input_on_same_init_different_ct() { + // Two sealers initialized that encrypt the same plaintext + // should produce different ciphertexts because the nonce + // is randomly generated. + let key = SecretKey::default(); + let (mut sealer_first, _) = StreamSealer::new(&key).unwrap(); + let (mut sealer_second, _) = StreamSealer::new(&key).unwrap(); + let plaintext = "Secret message 1".as_bytes(); + + let cipher1 = sealer_first + .seal_chunk(plaintext, &StreamTag::Message) + .unwrap(); + let cipher2 = sealer_second + .seal_chunk(plaintext, &StreamTag::Message) + .unwrap(); + assert_ne!(cipher1, cipher2); + } + + #[test] + fn test_stream_seal_and_open() { + let key = SecretKey::default(); + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let plaintext1 = "Secret message 1".as_bytes(); + let plaintext2 = "Secret message 2".as_bytes(); + let plaintext3 = "Secret message 3".as_bytes(); + let cipher1 = sealer.seal_chunk(plaintext1, &StreamTag::Message).unwrap(); + let cipher2 = sealer.seal_chunk(plaintext2, &StreamTag::Finish).unwrap(); + let cipher3 = sealer.seal_chunk(plaintext3, &StreamTag::Message).unwrap(); + + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let (dec1, tag1) = opener.open_chunk(&cipher1).unwrap(); + let (dec2, tag2) = opener.open_chunk(&cipher2).unwrap(); + let (dec3, tag3) = opener.open_chunk(&cipher3).unwrap(); + assert_eq!(plaintext1, &dec1[..]); + assert_eq!(plaintext2, &dec2[..]); + assert_eq!(plaintext3, &dec3[..]); + assert_eq!(tag1, StreamTag::Message); + assert_eq!(tag2, StreamTag::Finish); + assert_eq!(tag3, StreamTag::Message); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_stream_seal_open_same_input(input: Vec) -> bool { + let key = SecretKey::default(); + + let (mut sealer, nonce) = StreamSealer::new(&key).unwrap(); + let ct = sealer.seal_chunk(&input[..], &StreamTag::Message).unwrap(); + + let mut opener = StreamOpener::new(&key, &nonce).unwrap(); + let (pt_decrypted, tag) = opener.open_chunk(&ct).unwrap(); + + input == pt_decrypted && tag == StreamTag::Message + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + // Sealing input, and then opening should always yield the same input. + fn prop_seal_open_same_input(input: Vec) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let sk = SecretKey::default(); + + let ct = seal(&sk, &pt).unwrap(); + let pt_decrypted = open(&sk, &ct).unwrap(); + + pt == pt_decrypted + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + // Sealing input, modifying the tag and then opening should + // always fail due to authentication. + fn prop_fail_on_diff_key(input: Vec) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let sk = SecretKey::default(); + let sk2 = SecretKey::default(); + let ct = seal(&sk, &pt).unwrap(); + + open(&sk2, &ct).is_err() + } + } +} diff --git a/vendor/orion/src/high_level/auth.rs b/vendor/orion/src/high_level/auth.rs new file mode 100644 index 0000000..7c61f9b --- /dev/null +++ b/vendor/orion/src/high_level/auth.rs @@ -0,0 +1,212 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! Message authentication. +//! +//! # Use case: +//! `orion::auth` can be used to ensure message integrity and authenticity by +//! using a secret key. +//! +//! An example of this could be securing APIs by having a user of a given API +//! sign their API request and having the API server verify these signed API +//! requests. +//! +//! # About: +//! - Uses BLAKE2b-256 in keyed mode. +//! +//! # Parameters: +//! - `secret_key`: Secret key used to authenticate `data`. +//! - `data`: Data to be authenticated. +//! - `expected`: The expected authentication [`Tag`]. +//! +//! # Errors: +//! An error will be returned if: +//! - The calculated [`Tag`] does not match the expected. +//! - The [`SecretKey`] supplied is less than 32 bytes or greater than 64 bytes. +//! - The expected [`Tag`] is not 32 bytes when verifying. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) bytes of data are authenticated. +//! +//! # Security: +//! - The secret key should always be generated using a CSPRNG. +//! [`SecretKey::default()`] can be used for +//! this; it will generate a [`SecretKey`] of 32 bytes. +//! - The required minimum length for a [`SecretKey`] is 32 bytes. +//! +//! # Example: +//! ```rust +//! use orion::auth; +//! +//! // There exists a shared key between the user and API server +//! let key = auth::SecretKey::default(); +//! +//! // User generates message and authentication tag +//! let msg = "Some message.".as_bytes(); +//! let expected_tag = auth::authenticate(&key, msg)?; +//! +//! // API server verifies the authenticity of the message with the tag +//! assert!(auth::authenticate_verify(&expected_tag, &key, &msg).is_ok()); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use super::hltypes::SecretKey; +pub use crate::hazardous::mac::blake2b::Tag; +use crate::{ + errors::UnknownCryptoError, + hazardous::mac::blake2b::{self, Blake2b}, +}; + +/// The Tag size (bytes) to be output by BLAKE2b in keyed mode. +const BLAKE2B_TAG_SIZE: usize = 32; +/// The minimum `SecretKey` size (bytes) to be used by BLAKE2b in keyed mode. +const BLAKE2B_MIN_KEY_SIZE: usize = 32; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Authenticate a message using BLAKE2b-256 in keyed mode. +pub fn authenticate(secret_key: &SecretKey, data: &[u8]) -> Result { + if secret_key.len() < BLAKE2B_MIN_KEY_SIZE { + return Err(UnknownCryptoError); + } + let blake2b_secret_key = blake2b::SecretKey::from_slice(secret_key.unprotected_as_bytes())?; + let mut state = Blake2b::new(&blake2b_secret_key, BLAKE2B_TAG_SIZE)?; + state.update(data)?; + state.finalize() +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Authenticate and verify a message using BLAKE2b-256 in keyed mode. +pub fn authenticate_verify( + expected: &Tag, + secret_key: &SecretKey, + data: &[u8], +) -> Result<(), UnknownCryptoError> { + if secret_key.len() < BLAKE2B_MIN_KEY_SIZE || expected.len() != BLAKE2B_TAG_SIZE { + return Err(UnknownCryptoError); + } + let key = blake2b::SecretKey::from_slice(secret_key.unprotected_as_bytes())?; + Blake2b::verify(expected, &key, BLAKE2B_TAG_SIZE, data) +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_auth_and_verify { + use super::*; + #[test] + fn test_authenticate_verify_bad_key() { + let sec_key_correct = SecretKey::generate(64).unwrap(); + let sec_key_false = SecretKey::default(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + let mac_bob = authenticate(&sec_key_correct, &msg).unwrap(); + + assert!(authenticate_verify(&mac_bob, &sec_key_correct, &msg).is_ok()); + assert!(authenticate_verify(&mac_bob, &sec_key_false, &msg).is_err()); + } + + #[test] + fn test_authenticate_verify_bad_msg() { + let sec_key = SecretKey::generate(64).unwrap(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + let mac_bob = authenticate(&sec_key, &msg).unwrap(); + + assert!(authenticate_verify(&mac_bob, &sec_key, &msg).is_ok()); + assert!(authenticate_verify(&mac_bob, &sec_key, b"bad msg").is_err()); + } + + #[test] + fn test_authenticate_key_too_small() { + let sec_key = SecretKey::generate(31).unwrap(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + + assert!(authenticate(&sec_key, &msg).is_err()); + } + + #[test] + fn test_authenticate_verify_key_too_small() { + let sec_key = SecretKey::generate(31).unwrap(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + let mac = Tag::from_slice(&[0u8; 32][..]).unwrap(); + + assert!(authenticate_verify(&mac, &sec_key, &msg).is_err()); + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Authentication and verifying that tag with the same parameters + /// should always be true. + fn prop_authenticate_verify(input: Vec) -> bool { + let sk = SecretKey::default(); + let tag = authenticate(&sk, &input[..]).unwrap(); + authenticate_verify(&tag, &sk, &input[..]).is_ok() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Authentication and verifying that tag with a different key should + /// never be true. + fn prop_verify_fail_diff_key(input: Vec) -> bool { + let sk = SecretKey::default(); + let sk2 = SecretKey::default(); + let tag = authenticate(&sk, &input[..]).unwrap(); + + authenticate_verify(&tag, &sk2, &input[..]).is_err() + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Authentication and verifying that tag with different input should + /// never be true. + fn prop_verify_fail_diff_input(input: Vec) -> bool { + let sk = SecretKey::default(); + let tag = authenticate(&sk, &input[..]).unwrap(); + + authenticate_verify(&tag, &sk, b"Completely wrong input").is_err() + } + + use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_KEYSIZE; + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Verify the bounds of 32..=64 (inclusive) for the `SecretKey` used + /// in `authenticate/authenticate_verify`. + fn prop_authenticate_key_size(input: Vec) -> bool { + let sec_key_res = SecretKey::from_slice(&input); + if input.is_empty() || input.len() >= u32::MAX as usize { + return sec_key_res.is_err(); + } + let sec_key = sec_key_res.unwrap(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + let auth_res = authenticate(&sec_key, &msg); + if input.len() >= BLAKE2B_MIN_KEY_SIZE && input.len() <= BLAKE2B_KEYSIZE { + auth_res.is_ok() + } else { + auth_res.is_err() + } + } +} diff --git a/vendor/orion/src/high_level/hash.rs b/vendor/orion/src/high_level/hash.rs new file mode 100644 index 0000000..78b4bda --- /dev/null +++ b/vendor/orion/src/high_level/hash.rs @@ -0,0 +1,134 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! Hashing. +//! +//! # Use case: +//! `orion::hash` can be used to hash some given data. +//! +//! An example of this could be using hashes of files to ensure integrity. +//! Meaning, checking if a file has been modified since the time the hash was +//! recorded. +//! +//! If you are looking for a keyed hash, please see the [`orion::auth`](super::auth) module. +//! +//! # About: +//! - Uses BLAKE2b with an output size of 32 bytes (i.e BLAKE2b-256). +//! +//! # Parameters: +//! - `data`: The data to be hashed. +//! +//! # Panics: +//! A panic will occur if: +//! - More than 2*(2^64-1) bytes of data are hashed. +//! +//! # Security: +//! - This interface does not support supplying BLAKE2b with a secret key, and +//! the hashes retrieved +//! from using `orion::hash` are therefore not suitable as MACs. +//! - BLAKE2b is not suitable for password hashing. See [`orion::pwhash`](super::pwhash) +//! instead. +//! +//! # Examples +//! +//! ## Hashing in-memory data +//! ```rust +//! use orion::hash::{digest, Digest}; +//! +//! let hash: Digest = digest(b"Some data")?; +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! +//! ## Hashing data from an arbitrary reader +//! ```rust +//! use orion::hash::{digest_from_reader, Digest}; +//! +//! // `reader` could instead be `File::open("file.txt")?` +//! let reader = std::io::Cursor::new(b"some data"); +//! let hash: Digest = digest_from_reader(reader)?; +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use crate::hazardous::hash::blake2::blake2b::Digest; +use crate::{errors::UnknownCryptoError, hazardous::hash::blake2::blake2b}; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Hashing using BLAKE2b-256. +pub fn digest(data: &[u8]) -> Result { + blake2b::Hasher::Blake2b256.digest(data) +} + +/// Hash data from a [`Read`](std::io::Read)` type using BLAKE2b-256. +/// +/// See the [module-level docs](crate::hash) for an example of how to use this function. +/// Internally calls [`std::io::copy`]() to move data from the reader into the Blake2b writer. +/// Note that the [`std::io::copy`]() function buffers reads, so passing in a +/// [`BufReader`](std::io::BufReader) may be unnecessary. +/// +/// For lower-level control over reads, writes, buffer sizes, *etc.*, consider using the +/// [`Blake2b`](crate::hazardous::hash::blake2::blake2b::Blake2b) type and its +/// [`Write`](std::io::Write) implementation directly. See `Blake2b`'s `Write` implementation +/// and/or its `Write` documentation for an example. +/// +/// ## Errors: +/// This function will only ever return the [`std::io::ErrorKind::Other`]() +/// variant when it returns an error. Additionally, this will always contain Orion's +/// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type. +/// +/// Note that if an error is returned, data may still have been consumed from the given reader. +#[cfg(feature = "safe_api")] +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +pub fn digest_from_reader(mut reader: impl std::io::Read) -> Result { + let mut hasher = blake2b::Blake2b::new(32)?; + std::io::copy(&mut reader, &mut hasher).map_err(|_| UnknownCryptoError)?; + hasher.finalize() +} + +// Testing public functions in the module. +#[cfg(feature = "safe_api")] +#[cfg(test)] +mod public { + use super::*; + + #[quickcheck] + /// Hashing twice with same input should always produce same output. + fn prop_digest_same_result(input: Vec) -> bool { + digest(&input[..]).unwrap() == digest(&input[..]).unwrap() + } + + #[quickcheck] + /// Hashing all input should be the same as wrapping it in a + /// cursor and using digest_from_reader. + fn prop_digest_same_as_digest_from_reader(input: Vec) -> bool { + let digest_a = digest_from_reader(std::io::Cursor::new(&input)).unwrap(); + let digest_b = digest(&input).unwrap(); + digest_a == digest_b + } + + #[quickcheck] + /// Hashing twice with different input should never produce same output. + fn prop_digest_diff_result(input: Vec) -> bool { + digest(&input[..]).unwrap() != digest(b"Completely wrong input").unwrap() + } +} diff --git a/vendor/orion/src/high_level/hltypes.rs b/vendor/orion/src/high_level/hltypes.rs new file mode 100644 index 0000000..419cb22 --- /dev/null +++ b/vendor/orion/src/high_level/hltypes.rs @@ -0,0 +1,76 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// These are the different types used by the high-level interface. They are not +/// used in `hazardous`. +use crate::errors::UnknownCryptoError; + +construct_secret_key_variable_size! { + /// A type to represent a secret key. + /// + /// As default it will randomly generate a `SecretKey` of 32 bytes. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `length` is 0. + /// - `length` is not less than [`isize::MAX`]. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (SecretKey, test_secret_key, 32) +} + +construct_salt_variable_size! { + /// A type to represent the `Salt` that Argon2i uses during key derivation. + /// + /// As default it will randomly generate a `Salt` of 16 bytes. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `length` is 0. + /// - `length` is not less than [`isize::MAX`]. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Salt, test_salt, 16) +} + +construct_secret_key_variable_size! { + /// A type to represent the `Password` that Argon2i hashes and uses for key derivation. + /// + /// As default it will randomly generate a `Password` of 32 bytes. + /// + /// # Errors: + /// An error will be returned if: + /// - `slice` is empty. + /// - `length` is 0. + /// - `length` is not less than [`isize::MAX`]. + /// + /// # Panics: + /// A panic will occur if: + /// - Failure to generate random bytes securely. + (Password, test_password, 32) +} diff --git a/vendor/orion/src/high_level/kdf.rs b/vendor/orion/src/high_level/kdf.rs new file mode 100644 index 0000000..87f0192 --- /dev/null +++ b/vendor/orion/src/high_level/kdf.rs @@ -0,0 +1,222 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! Key derivation. +//! +//! # Use case: +//! `orion::kdf` can be used to derive higher-entropy keys from low-entropy +//! keys. Also known as key stretching. +//! +//! An example of this could be deriving a key from a user-submitted password +//! and using this derived key in disk encryption. +//! +//! # About: +//! - Uses Argon2i. +//! +//! # Note: +//! This implementation only supports a single thread/lane. +//! +//! # Parameters: +//! - `password`: The low-entropy input key to be used in key derivation. +//! - `salt`: The salt used for the key derivation. +//! - `iterations`: Iterations cost parameter for Argon2i. +//! - `memory`: Memory (in kibibytes (KiB)) cost parameter for Argon2i. +//! - `length`: The desired length of the derived key. +//! +//! # Errors: +//! An error will be returned if: +//! - `iterations` is less than 3. +//! - `length` is less than 4. +//! - `memory` is less than 8. +//! - The length of the `password` is greater than [`isize::MAX`]. +//! - The length of the `salt` is greater than [`isize::MAX`] or less than `8`. +//! +//! # Security: +//! - Choosing the correct cost parameters is important for security. Please refer to +//! [libsodium's docs] for a description of how to do this. +//! - The salt should always be generated using a CSPRNG. [`Salt::default()`] +//! can be used for this, it will generate a [`Salt`] of 16 bytes. +//! - The recommended minimum size for a salt is 16 bytes. +//! - The recommended minimum size for a derived key is 16 bytes. +//! +//! If the concrete cost parameters needed are unclear, please refer to [OWASP] for recommended minimum values. +//! +//! # Example: +//! ```rust +//! use orion::kdf; +//! +//! let user_password = kdf::Password::from_slice(b"User password")?; +//! let salt = kdf::Salt::default(); +//! +//! let derived_key = kdf::derive_key(&user_password, &salt, 3, 1<<16, 32)?; +//! +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [libsodium's docs]: https://download.libsodium.org/doc/password_hashing/default_phf#guidelines-for-choosing-the-parameters +//! [OWASP]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use super::hltypes::{Password, Salt, SecretKey}; +use crate::{errors::UnknownCryptoError, hazardous::kdf::argon2i, pwhash::MIN_ITERATIONS}; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Derive a key using Argon2i. +pub fn derive_key( + password: &Password, + salt: &Salt, + iterations: u32, + memory: u32, + length: u32, +) -> Result { + if iterations < MIN_ITERATIONS { + return Err(UnknownCryptoError); + } + + let mut dk = SecretKey::from_slice(&vec![0u8; length as usize])?; + + argon2i::derive_key( + password.unprotected_as_bytes(), + salt.as_ref(), + iterations, + memory, + None, + None, + &mut dk.value, + )?; + + Ok(dk) +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_derive_key_and_verify { + use super::*; + + #[test] + fn test_derive_key() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 16]).unwrap(); + let dk_first = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_second = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + + assert_eq!(dk_first, dk_second); + } + + #[test] + fn test_derive_key_err_diff_iter() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let dk = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_diff_iter = derive_key(&password, &salt, 4, 1024, 32).unwrap(); + + assert_ne!(dk, dk_diff_iter); + } + + #[test] + fn test_derive_key_err_diff_mem() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let dk = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_diff_mem = derive_key(&password, &salt, 3, 512, 32).unwrap(); + + assert_ne!(dk, dk_diff_mem); + } + + #[test] + fn test_derive_key_err_diff_salt() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let dk = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_diff_salt = derive_key( + &password, + &Salt::from_slice(&[1u8; 64]).unwrap(), + 3, + 1024, + 32, + ) + .unwrap(); + + assert_ne!(dk, dk_diff_salt); + } + + #[test] + fn test_derive_key_err_diff_len() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let dk = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_diff_len = derive_key(&password, &salt, 3, 1024, 64).unwrap(); + + assert_ne!(dk, dk_diff_len); + } + + #[test] + fn test_derive_key_err_diff_pass() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let dk = derive_key(&password, &salt, 3, 1024, 32).unwrap(); + let dk_diff_pass = derive_key( + &Password::from_slice(&[1u8; 64]).unwrap(), + &salt, + 3, + 1024, + 32, + ) + .unwrap(); + + assert_ne!(dk, dk_diff_pass); + } + + #[test] + fn test_derive_key_bad_length() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + + assert!(derive_key(&password, &salt, 3, 1024, 3).is_err()); + assert!(derive_key(&password, &salt, 3, 1024, 4).is_ok()); + assert!(derive_key(&password, &salt, 3, 1024, 5).is_ok()); + } + + #[test] + fn test_derive_key_bad_iter() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 16]).unwrap(); + + assert!(derive_key(&password, &salt, 2, 1024, 32).is_err()); + assert!(derive_key(&password, &salt, 3, 1024, 32).is_ok()); + assert!(derive_key(&password, &salt, 4, 1024, 32).is_ok()); + } + + #[test] + fn test_derive_key_bad_mem() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 16]).unwrap(); + + assert!(derive_key(&password, &salt, 3, 7, 32).is_err()); + assert!(derive_key(&password, &salt, 3, 8, 32).is_ok()); + assert!(derive_key(&password, &salt, 3, 9, 32).is_ok()); + } + } +} diff --git a/vendor/orion/src/high_level/kex.rs b/vendor/orion/src/high_level/kex.rs new file mode 100644 index 0000000..787f514 --- /dev/null +++ b/vendor/orion/src/high_level/kex.rs @@ -0,0 +1,539 @@ +// MIT License + +// Copyright (c) 2021-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! Ephemeral key exchange. +//! +//! # Use case: +//! `orion::kex` can be used to establish a pair of shared keys between two parties. +//! +//! # About: +//! - Both [`EphemeralClientSession`] and [`EphemeralServerSession`] consume `self` when shared keys +//! are being established. You can therefore never use the same private key for more than a single +//! key exchange. +//! +//! This implementation is based on and compatible with the +//! [key exchange API](https://doc.libsodium.org/key_exchange) of libsodium. +//! +//! # Parameters: +//! - `server_public_key`: The server's public key used to establish the client's shared session keys. +//! - `client_public_key`: The client's public key used to establish the server's shared session keys. +//! +//! # Errors: +//! An error will be returned if: +//! - If the key exchange results in an all-zero output. +//! +//! # Panics: +//! A panic will occur if: +//! - Failure to generate random bytes securely. +//! +//! # Security: +//! - The API is designed to be ephemeral and a [`PrivateKey`] should not be used more than once. +//! +//! # Example: +//! ```rust +//! use orion::kex::*; +//! use orion::aead; +//! +//! /// The server initializes their ephemeral session keys +//! let session_server = EphemeralServerSession::new()?; +//! let server_public_key = session_server.public_key(); +//! +//! /// The client initializes their ephemeral session keys +//! let session_client = EphemeralClientSession::new()?; +//! let client_public_key = session_client.public_key().clone(); +//! +//! let client_keys: SessionKeys = session_client +//! .establish_with_server(server_public_key)?; +//! +//! let server_keys: SessionKeys = session_server +//! .establish_with_client(&client_public_key)?; +//! +//! assert_eq!(client_keys.receiving(), server_keys.transport()); +//! assert_eq!(client_keys.transport(), server_keys.receiving()); +//! +//! // The client can now "send" encrypted data to the server and vice versa +//! +//! // Client sends an encrypted message which the server decrypts: +//! let client_msg = aead::seal(client_keys.transport(), b"Hello, server!")?; +//! assert_eq!(aead::open(server_keys.receiving(), &client_msg)?, b"Hello, server!"); +//! +//! // Server responds and client decrypts the received message: +//! let server_msg = aead::seal(server_keys.transport(), b"Hello, client!")?; +//! assert_eq!(aead::open(client_keys.receiving(), &server_msg)?, b"Hello, client!"); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use super::hltypes::SecretKey; +pub use crate::hazardous::ecc::x25519::PrivateKey; +pub use crate::hazardous::ecc::x25519::PublicKey; + +use crate::errors::UnknownCryptoError; +use crate::hazardous::ecc::x25519; +use crate::hazardous::hash::blake2::blake2b::{Blake2b, Digest}; +use core::convert::TryFrom; + +#[derive(Debug, PartialEq)] +/// A key pair used to establish shared keys for a single session. +pub struct EphemeralClientSession { + private_key: PrivateKey, + public_key: PublicKey, +} + +impl EphemeralClientSession { + /// Generate a new random key pair. + pub fn new() -> Result { + let privkey = PrivateKey::generate(); + let pubkey: PublicKey = PublicKey::try_from(&privkey)?; + + Ok(Self { + private_key: privkey, + public_key: pubkey, + }) + } + + /// Get a reference to the [`PublicKey`]. + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } + + /// Get a reference to the [`PrivateKey`]. + pub fn private_key(&self) -> &PrivateKey { + &self.private_key + } + + /// Establish session keys with a server. This moves `self` to ensure that the keys + /// generated with [`Self::new()`] are only used for this key exchange, thus remaining ephemeral. + pub fn establish_with_server( + self, + server_public_key: &PublicKey, + ) -> Result { + let q = x25519::key_agreement(&self.private_key, server_public_key)?; + let keys = establish_session_keys(&q, &self.public_key, server_public_key)?; + + Ok(SessionKeys { + rx: SecretKey::from_slice(&keys.as_ref()[..32])?, + tx: SecretKey::from_slice(&keys.as_ref()[32..])?, + }) + } +} + +#[derive(Debug, PartialEq)] +/// A key pair used to establish shared keys for a single session. +pub struct EphemeralServerSession { + private_key: PrivateKey, + public_key: PublicKey, +} + +impl EphemeralServerSession { + /// Generate a new random key pair. + pub fn new() -> Result { + let privkey = PrivateKey::generate(); + let pubkey: PublicKey = PublicKey::try_from(&privkey)?; + + Ok(Self { + private_key: privkey, + public_key: pubkey, + }) + } + + /// Get a reference to the [`PublicKey`]. + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } + + /// Get a reference to the [`PrivateKey`]. + pub fn private_key(&self) -> &PrivateKey { + &self.private_key + } + + /// Establish session keys with a client. This moves `self` to ensure that the keys + /// generated with [`Self::new()`] are only used for this key exchange, thus remaining ephemeral. + pub fn establish_with_client( + self, + client_public_key: &PublicKey, + ) -> Result { + let q = x25519::key_agreement(&self.private_key, client_public_key)?; + let keys = establish_session_keys(&q, client_public_key, &self.public_key)?; + + Ok(SessionKeys { + rx: SecretKey::from_slice(&keys.as_ref()[32..])?, + tx: SecretKey::from_slice(&keys.as_ref()[..32])?, + }) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, PartialEq)] +/// A set of shared secrets for either transmitting to this entity or send to another party. +pub struct SessionKeys { + rx: SecretKey, + tx: SecretKey, +} + +impl SessionKeys { + /// Get the shared secret intended to be used for receiving data from the other party. + pub fn receiving(&self) -> &SecretKey { + &self.rx + } + + /// Get the shared secret intended to be used for transporting data to the other party. + pub fn transport(&self) -> &SecretKey { + &self.tx + } +} + +/// Using BLAKE2b, derive two shared secret from a scalarmult computation. +fn establish_session_keys( + shared_secret: &x25519::SharedKey, + client_pk: &PublicKey, + server_pk: &PublicKey, +) -> Result { + let mut ctx = Blake2b::new(64)?; + ctx.update(shared_secret.unprotected_as_bytes())?; + ctx.update(&client_pk.to_bytes())?; + ctx.update(&server_pk.to_bytes())?; + ctx.finalize() +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + fn test_basic_key_exchange() { + let session_server = EphemeralServerSession::new().unwrap(); + let server_public_key = session_server.public_key(); + + let session_client = EphemeralClientSession::new().unwrap(); + let client_public_key = session_client.public_key().clone(); + + assert_ne!(session_client.private_key(), session_server.private_key()); + + let client = session_client + .establish_with_server(server_public_key) + .unwrap(); + let server = session_server + .establish_with_client(&client_public_key) + .unwrap(); + + assert_eq!(client.receiving(), server.transport()); + assert_eq!(client.transport(), server.receiving()); + + assert_ne!(client.receiving(), server.receiving()); + assert_ne!(client.transport(), server.transport()); + } + + #[test] + fn test_error_on_low_order_public() { + // Taken from: https://github.com/jedisct1/libsodium/blob/master/test/default/kx.c + let low_order_public: [u8; 32] = [ + 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, + 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, + 0x5f, 0x49, 0xb8, 0x00, + ]; + let server_low_order_pk = PublicKey::from_slice(&low_order_public).unwrap(); + + let session_client = EphemeralClientSession::new().unwrap(); + assert!(session_client + .establish_with_server(&server_low_order_pk) + .is_err()); + } + + // The following are tests generated with sodiumoxide to test basic compatability with libsodium API. + #[test] + fn libsodium_compat_test_1() { + let client_pk = "299283d8713b7d430376cb257e13cd5ad1a6e5ebe6135417f4bb3b45bf42f31a"; + let client_sk = "e026533c3efa096ce9c4d77ad7c3d6948af2f9ef628b88430228ca0465ec35b9"; + let server_pk = "1716d0c006e5f3c2240b2ccec9357dbd04030f51d3e584923e70823cd6fcab1c"; + let server_sk = "55a94da5003d7807850938e84a5082d3deba8e5bbf5c50f814e8160270c165b4"; + let client_rx = "37830d33c5de06fbe246db5803ed70284fe9ab78bc6b896a3db3a9b8db50418b"; + let client_tx = "201d1bb45d4b9164f269d59cc00ba1a49c1924c27485bb6e5cc77ea4cc38ec7e"; + let server_rx = "201d1bb45d4b9164f269d59cc00ba1a49c1924c27485bb6e5cc77ea4cc38ec7e"; + let server_tx = "37830d33c5de06fbe246db5803ed70284fe9ab78bc6b896a3db3a9b8db50418b"; + + let client_public = PublicKey::from_slice(&hex::decode(client_pk).unwrap()).unwrap(); + let client_secret = PrivateKey::from_slice(&hex::decode(client_sk).unwrap()).unwrap(); + let server_public = PublicKey::from_slice(&hex::decode(server_pk).unwrap()).unwrap(); + let server_secret = PrivateKey::from_slice(&hex::decode(server_sk).unwrap()).unwrap(); + + let client_recv = SecretKey::from_slice(&hex::decode(client_rx).unwrap()).unwrap(); + let client_trans = SecretKey::from_slice(&hex::decode(client_tx).unwrap()).unwrap(); + let server_recv = SecretKey::from_slice(&hex::decode(server_rx).unwrap()).unwrap(); + let server_trans = SecretKey::from_slice(&hex::decode(server_tx).unwrap()).unwrap(); + + let session_client = EphemeralClientSession { + private_key: client_secret, + public_key: client_public.clone(), + }; + + let session_server = EphemeralServerSession { + private_key: server_secret, + public_key: server_public.clone(), + }; + + let expected_client_shared = SessionKeys { + rx: client_recv, + tx: client_trans, + }; + + let expected_server_shared = SessionKeys { + rx: server_recv, + tx: server_trans, + }; + + assert_eq!( + session_client + .establish_with_server(&server_public) + .unwrap(), + expected_client_shared + ); + assert_eq!( + session_server + .establish_with_client(&client_public) + .unwrap(), + expected_server_shared + ); + } + + #[test] + fn libsodium_compat_test_2() { + let client_pk = "31f8eed74832d31b106702ecdf464a54e9fb9514d241473c5b51deb0d126893a"; + let client_sk = "dcb760f46480ea5c2647e8dcbdc30fff8da6811712b4d5c7144aca1b72d6adb7"; + let server_pk = "ccf7a7d5d2973f517a2276f0ca6c15da3c90a85db12e3ec171c4441c2b48f15d"; + let server_sk = "e73ebfde3907296e5452e1f22ea1a85d4f3cdbf3ff9f099d45a0853d3b87d64f"; + let client_rx = "25789992d2eac8bc0e1c3322d9b8e26050064ea3cead77ca2cf36966dea54186"; + let client_tx = "f69cf60f763fb2a9c47dc1b3237983ef79cecd26205c68f9c16e91db6c8f3f18"; + let server_rx = "f69cf60f763fb2a9c47dc1b3237983ef79cecd26205c68f9c16e91db6c8f3f18"; + let server_tx = "25789992d2eac8bc0e1c3322d9b8e26050064ea3cead77ca2cf36966dea54186"; + + let client_public = PublicKey::from_slice(&hex::decode(client_pk).unwrap()).unwrap(); + let client_secret = PrivateKey::from_slice(&hex::decode(client_sk).unwrap()).unwrap(); + let server_public = PublicKey::from_slice(&hex::decode(server_pk).unwrap()).unwrap(); + let server_secret = PrivateKey::from_slice(&hex::decode(server_sk).unwrap()).unwrap(); + + let client_recv = SecretKey::from_slice(&hex::decode(client_rx).unwrap()).unwrap(); + let client_trans = SecretKey::from_slice(&hex::decode(client_tx).unwrap()).unwrap(); + let server_recv = SecretKey::from_slice(&hex::decode(server_rx).unwrap()).unwrap(); + let server_trans = SecretKey::from_slice(&hex::decode(server_tx).unwrap()).unwrap(); + + let session_client = EphemeralClientSession { + private_key: client_secret, + public_key: client_public.clone(), + }; + + let session_server = EphemeralServerSession { + private_key: server_secret, + public_key: server_public.clone(), + }; + + let expected_client_shared = SessionKeys { + rx: client_recv, + tx: client_trans, + }; + + let expected_server_shared = SessionKeys { + rx: server_recv, + tx: server_trans, + }; + + assert_eq!( + session_client + .establish_with_server(&server_public) + .unwrap(), + expected_client_shared + ); + assert_eq!( + session_server + .establish_with_client(&client_public) + .unwrap(), + expected_server_shared + ); + } + + #[test] + fn libsodium_compat_test_3() { + let client_pk = "57f57eda618289c8f55dee0a8069405d9874684282b8380878b180719055333a"; + let client_sk = "7d613b91a1cf6a34787362229cfaf6d50613e276a20ef59eea7f02d9236cd9f7"; + let server_pk = "997f12b2ef5ba2c639c4dc39f159ce5169b60b9a3b65365f958cfb822e37b513"; + let server_sk = "c3156f11e0cc31ffb92dfd5e780738011cfe80cc4184f5a3f190892528a9bac3"; + let client_rx = "89f90402d56d5e184b1682c21583e695560e0ab54459d09a51a596a8d33293da"; + let client_tx = "547c1f1be7abe8d10bf92fb19f79edd2139441b4faa54976b5db90a50b7244c4"; + let server_rx = "547c1f1be7abe8d10bf92fb19f79edd2139441b4faa54976b5db90a50b7244c4"; + let server_tx = "89f90402d56d5e184b1682c21583e695560e0ab54459d09a51a596a8d33293da"; + + let client_public = PublicKey::from_slice(&hex::decode(client_pk).unwrap()).unwrap(); + let client_secret = PrivateKey::from_slice(&hex::decode(client_sk).unwrap()).unwrap(); + let server_public = PublicKey::from_slice(&hex::decode(server_pk).unwrap()).unwrap(); + let server_secret = PrivateKey::from_slice(&hex::decode(server_sk).unwrap()).unwrap(); + + let client_recv = SecretKey::from_slice(&hex::decode(client_rx).unwrap()).unwrap(); + let client_trans = SecretKey::from_slice(&hex::decode(client_tx).unwrap()).unwrap(); + let server_recv = SecretKey::from_slice(&hex::decode(server_rx).unwrap()).unwrap(); + let server_trans = SecretKey::from_slice(&hex::decode(server_tx).unwrap()).unwrap(); + + let session_client = EphemeralClientSession { + private_key: client_secret, + public_key: client_public.clone(), + }; + + let session_server = EphemeralServerSession { + private_key: server_secret, + public_key: server_public.clone(), + }; + + let expected_client_shared = SessionKeys { + rx: client_recv, + tx: client_trans, + }; + + let expected_server_shared = SessionKeys { + rx: server_recv, + tx: server_trans, + }; + + assert_eq!( + session_client + .establish_with_server(&server_public) + .unwrap(), + expected_client_shared + ); + assert_eq!( + session_server + .establish_with_client(&client_public) + .unwrap(), + expected_server_shared + ); + } + + #[test] + fn libsodium_compat_test_4() { + let client_pk = "2df30ccfd5eb6cb1ae5428dd06129a22fe8eac2b8b0cfcc1876bbaeb2b515703"; + let client_sk = "52b73937f462130c82c427d68b26b689d3c020169909fea3043882654fa8e1a3"; + let server_pk = "ef09a7379139627b2a13d0376f7fea1e4e2c27859757b74282b4368d2701de1c"; + let server_sk = "384de0b04d358ef0a99cec457507b83a9fcff0c9a2875d1fc771c1b203eb90f5"; + let client_rx = "738d3ff37e8b5d58daf888111359693042508617ef088c2048c0d87bc002ca38"; + let client_tx = "fd1ab19e5c6ac0c5508ba129ded170a25c04f6f1ab9ccc3e66cd73988ade8471"; + let server_rx = "fd1ab19e5c6ac0c5508ba129ded170a25c04f6f1ab9ccc3e66cd73988ade8471"; + let server_tx = "738d3ff37e8b5d58daf888111359693042508617ef088c2048c0d87bc002ca38"; + + let client_public = PublicKey::from_slice(&hex::decode(client_pk).unwrap()).unwrap(); + let client_secret = PrivateKey::from_slice(&hex::decode(client_sk).unwrap()).unwrap(); + let server_public = PublicKey::from_slice(&hex::decode(server_pk).unwrap()).unwrap(); + let server_secret = PrivateKey::from_slice(&hex::decode(server_sk).unwrap()).unwrap(); + + let client_recv = SecretKey::from_slice(&hex::decode(client_rx).unwrap()).unwrap(); + let client_trans = SecretKey::from_slice(&hex::decode(client_tx).unwrap()).unwrap(); + let server_recv = SecretKey::from_slice(&hex::decode(server_rx).unwrap()).unwrap(); + let server_trans = SecretKey::from_slice(&hex::decode(server_tx).unwrap()).unwrap(); + + let session_client = EphemeralClientSession { + private_key: client_secret, + public_key: client_public.clone(), + }; + + let session_server = EphemeralServerSession { + private_key: server_secret, + public_key: server_public.clone(), + }; + + let expected_client_shared = SessionKeys { + rx: client_recv, + tx: client_trans, + }; + + let expected_server_shared = SessionKeys { + rx: server_recv, + tx: server_trans, + }; + + assert_eq!( + session_client + .establish_with_server(&server_public) + .unwrap(), + expected_client_shared + ); + assert_eq!( + session_server + .establish_with_client(&client_public) + .unwrap(), + expected_server_shared + ); + } + + #[test] + fn libsodium_compat_test_5() { + let client_pk = "c4be3dfb50430e57313b6eeafc40e9b432120c4dd3d34ca6dedae1391b898c43"; + let client_sk = "23702adbbab5918b682b2a1b2b27c634865b3dcf51ed81e287da30edd4f7ef39"; + let server_pk = "a241a5a8c16e3731ddc8d3e5e5890f85b9de2c87095be3239379b3c62f73f949"; + let server_sk = "530493dddf346371ce562557d5b0ff40ddbff038cf6a20187c16510ce57cc93f"; + let client_rx = "8fa8dfc483108262b058b60b11e2f9b5b47287061bde785827afafb102a09ec7"; + let client_tx = "a01332e4cb85b2bfac65f86936f27058b339889442c13eee06414bfb2d68c58b"; + let server_rx = "a01332e4cb85b2bfac65f86936f27058b339889442c13eee06414bfb2d68c58b"; + let server_tx = "8fa8dfc483108262b058b60b11e2f9b5b47287061bde785827afafb102a09ec7"; + + let client_public = PublicKey::from_slice(&hex::decode(client_pk).unwrap()).unwrap(); + let client_secret = PrivateKey::from_slice(&hex::decode(client_sk).unwrap()).unwrap(); + let server_public = PublicKey::from_slice(&hex::decode(server_pk).unwrap()).unwrap(); + let server_secret = PrivateKey::from_slice(&hex::decode(server_sk).unwrap()).unwrap(); + + let client_recv = SecretKey::from_slice(&hex::decode(client_rx).unwrap()).unwrap(); + let client_trans = SecretKey::from_slice(&hex::decode(client_tx).unwrap()).unwrap(); + let server_recv = SecretKey::from_slice(&hex::decode(server_rx).unwrap()).unwrap(); + let server_trans = SecretKey::from_slice(&hex::decode(server_tx).unwrap()).unwrap(); + + let session_client = EphemeralClientSession { + private_key: client_secret, + public_key: client_public.clone(), + }; + + let session_server = EphemeralServerSession { + private_key: server_secret, + public_key: server_public.clone(), + }; + + let expected_client_shared = SessionKeys { + rx: client_recv, + tx: client_trans, + }; + + let expected_server_shared = SessionKeys { + rx: server_recv, + tx: server_trans, + }; + + assert_eq!( + session_client + .establish_with_server(&server_public) + .unwrap(), + expected_client_shared + ); + assert_eq!( + session_server + .establish_with_client(&client_public) + .unwrap(), + expected_server_shared + ); + } +} diff --git a/vendor/orion/src/high_level/mod.rs b/vendor/orion/src/high_level/mod.rs new file mode 100644 index 0000000..25080e8 --- /dev/null +++ b/vendor/orion/src/high_level/mod.rs @@ -0,0 +1,29 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +pub mod aead; +pub mod auth; +pub mod hash; +mod hltypes; +pub mod kdf; +pub mod kex; +pub mod pwhash; diff --git a/vendor/orion/src/high_level/pwhash.rs b/vendor/orion/src/high_level/pwhash.rs new file mode 100644 index 0000000..3daee39 --- /dev/null +++ b/vendor/orion/src/high_level/pwhash.rs @@ -0,0 +1,990 @@ +// MIT License + +// Copyright (c) 2020-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! Password hashing and verification. +//! +//! # Use case: +//! `orion::pwhash` is suitable for securely storing passwords. +//! +//! An example of this would be needing to store user passwords (from a sign-up +//! at a webstore) in a server database, +//! where a potential disclosure of the data in this database should not result +//! in the user's actual passwords being disclosed as well. +//! +//! # About: +//! - Uses Argon2i. +//! - A salt of 16 bytes is automatically generated. +//! - The password hash length is set to 32. +//! +//! [`PasswordHash`] provides two ways of retrieving the hashed password: +//! - [`PasswordHash::unprotected_as_encoded()`] returns the hashed password in an encoded form. +//! The encoding specifies the settings used to hash the password. +//! - [`PasswordHash::unprotected_as_bytes()`] returns only the hashed password in raw bytes. +//! +//! The following is an example of how the encoded password hash might look: +//! ```text +//! $argon2i$v=19$m=8192,t=3,p=1$c21hbGxzYWx0$lmO1aPPy3x0CcvrKpFLi1TL/uSVJ/eO5hPHiWZFaWvY +//! ``` +//! +//! See a more detailed description of the [encoding format here]. +//! +//! # Note: +//! This implementation only supports a single thread/lane. +//! +//! # Parameters: +//! - `password`: The password to be hashed. +//! - `expected`: The expected password hash. +//! - `iterations`: Iterations cost parameter for Argon2i. +//! - `memory`: Memory (in kibibytes (KiB)) cost parameter for Argon2i. +//! +//! # Errors: +//! An error will be returned if: +//! - `memory` is less than 8. +//! - `iterations` is less than 3. +//! - The length of the `password` is greater than [`isize::MAX`]. +//! - The password hash does not match `expected`. +//! +//! # Panics: +//! A panic will occur if: +//! - Failure to generate random bytes securely. +//! +//! # Security: +//! - [`PasswordHash::unprotected_as_encoded()`] and [`PasswordHash::unprotected_as_bytes()`] should never +//! be used to compare password hashes, as these will not run in constant-time. +//! Either use [`hash_password_verify()`] or compare two [`PasswordHash`]es. +//! - Choosing the correct cost parameters is important for security. Please refer to [libsodium's docs] +//! for a description of how to do this. +//! +//! If the concrete cost parameters needed are unclear, please refer to [OWASP] for recommended minimum values. +//! +//! # Example: +//! ```rust +//! use orion::pwhash; +//! +//! let password = pwhash::Password::from_slice(b"Secret password")?; +//! +//! let hash = pwhash::hash_password(&password, 3, 1<<16)?; +//! assert!(pwhash::hash_password_verify(&hash, &password).is_ok()); +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` +//! [encoding format here]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md +//! [libsodium's docs]: https://download.libsodium.org/doc/password_hashing/default_phf#guidelines-for-choosing-the-parameters +//! [OWASP]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html + +#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] + +pub use super::hltypes::Password; +use super::hltypes::Salt; +use crate::{ + errors::UnknownCryptoError, + hazardous::kdf::argon2i::{self, LANES, MIN_MEMORY}, +}; +use ct_codecs::{Base64NoPadding, Decoder, Encoder}; +use zeroize::Zeroizing; + +#[cfg(feature = "serde")] +use serde::{ + de::{self, Deserialize, Deserializer}, + ser::{Serialize, Serializer}, +}; + +/// The length of the salt used for password hashing. +pub const SALT_LENGTH: usize = 16; + +/// The length of the hashed password. +pub const PWHASH_LENGTH: usize = 32; + +/// Minimum amount of iterations. +pub(crate) const MIN_ITERATIONS: u32 = 3; + +/// A type to represent the `PasswordHash` that Argon2i returns when used for password hashing. +/// +/// +/// # Errors: +/// An error will be returned if: +/// - The encoded password hash contains whitespace. +/// - The encoded password hash has a parallelism count other than 1. +/// - The encoded password contains any other fields than: The algorithm name, +/// version, m, t, p and the salt and password hash. +/// - The encoded password hash contains invalid Base64 encoding. +/// - Any decimal parameter value, such as m, contains leading zeroes and is longer +/// than a single character. +/// - `iterations` is less than 3. +/// - `memory` is less than 8. +/// - `password` is not 32 bytes. +/// - `salt` is not 16 bytes. +/// - The encoded password hash contains numerical values that cannot +/// be represented as a `u32`. +/// - The encoded password hash length is less than [`PasswordHash::MIN_ENCODED_LEN`] or greater than [`PasswordHash::MAX_ENCODED_LEN`]. +/// - The parameters in the encoded password hash are not correctly ordered. The ordering must be: +/// `$argon2i$v=19$m=,t=,p=$$` +/// # Panics: +/// A panic will occur if: +/// - Overflowing calculations happen on `usize` when decoding the password and salt from Base64. +/// +/// # Security: +/// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections +/// that the type implements. +/// - Never use `unprotected_as_bytes()` or `unprotected_as_encoded()` to compare password hashes, +/// as that will not run in constant-time. Compare `PasswordHash`es directly using `==` instead. +/// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted +/// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait +/// is implemented in such a way that the comparison happens in constant time. Thus, users should +/// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. +/// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. +/// ```rust +/// use orion::hazardous::mac::hmac::sha512::Tag; +/// # use orion::errors::UnknownCryptoError; +/// +/// # fn main() -> Result<(), Box> { +/// // Initialize an arbitrary, 64-byte tag. +/// let tag = Tag::from_slice(&[1; 64])?; +/// +/// // Secure, constant-time comparison with a byte slice +/// assert_eq!(tag, &[1; 64][..]); +/// +/// // Secure, constant-time comparison with another Tag +/// assert_eq!(tag, Tag::from_slice(&[1; 64])?); +/// # Ok(()) +/// # } +/// ``` +pub struct PasswordHash { + encoded_password_hash: String, + password_hash: Vec, + salt: Salt, + iterations: u32, + memory: u32, +} + +#[allow(clippy::len_without_is_empty)] +impl PasswordHash { + /// Given a 16-byte salt (22 characters encoded) and 32-byte password hash (43 characters encoded), + /// and parameters (m, t) in decimal representation of 1..10 in length, 92 is the minimum length for an encoded password hash. + pub const MIN_ENCODED_LEN: usize = 92; + + /// Given a 16-byte salt (22 characters encoded) and 32-byte password hash (43 characters encoded), + /// and parameters (m, t) in decimal representation of 1..10 in length, 110 is the maximum length for an encoded password hash. + pub const MAX_ENCODED_LEN: usize = 110; + + /// Parse a decimal parameter value to a u32. Returns an error on overflow + /// and if the value has leading zeroes. + fn parse_decimal_value(value: &str) -> Result { + // See: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#decimal-encoding + if value.len() > 1 && value.starts_with('0') { + return Err(UnknownCryptoError); + } + // .parse::() detects overflows (in debug and release builds) + // and rejects empty strings. If the value contains spaces, parsing + // also fails. + Ok(value.parse::()?) + } + + /// Encode password hash, salt and parameters for storage. + fn encode( + password_hash: &[u8], + salt: &[u8], + iterations: u32, + memory: u32, + ) -> Result { + Ok(format!( + "$argon2i$v=19$m={},t={},p=1${}${}", + memory, + iterations, + Base64NoPadding::encode_to_string(salt)?, + Base64NoPadding::encode_to_string(password_hash)?, + )) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from given byte slice and parameters. + pub fn from_slice( + password_hash: &[u8], + salt: &[u8], + iterations: u32, + memory: u32, + ) -> Result { + if password_hash.len() != PWHASH_LENGTH { + return Err(UnknownCryptoError); + } + if salt.len() != SALT_LENGTH { + return Err(UnknownCryptoError); + } + if iterations < MIN_ITERATIONS { + return Err(UnknownCryptoError); + } + if memory < MIN_MEMORY { + return Err(UnknownCryptoError); + } + + let encoded_password_hash = Self::encode(password_hash, salt, iterations, memory)?; + + Ok(Self { + encoded_password_hash, + password_hash: password_hash.into(), + salt: Salt::from_slice(salt)?, + iterations, + memory, + }) + } + + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from encoded password hash. + pub fn from_encoded(password_hash: &str) -> Result { + if password_hash.len() > Self::MAX_ENCODED_LEN + || password_hash.len() < Self::MIN_ENCODED_LEN + { + return Err(UnknownCryptoError); + } + + if password_hash.contains(' ') { + return Err(UnknownCryptoError); + } + + let parts_split = password_hash.split('$').collect::>(); + if parts_split.len() != 6 { + return Err(UnknownCryptoError); + } + let mut parts = parts_split.into_iter(); + if parts.next() != Some("") { + return Err(UnknownCryptoError); + } + if parts.next() != Some("argon2i") { + return Err(UnknownCryptoError); + } + if parts.next() != Some("v=19") { + return Err(UnknownCryptoError); + } + + // Splits as ["m", "X", "t", "Y", "p", "Z"] where m=X, t=Y and p=Z. + let param_parts_split = parts + .next() + .unwrap() + .split(|v| v == '=' || v == ',') + .collect::>(); + if param_parts_split.len() != 6 { + return Err(UnknownCryptoError); + } + let mut param_parts = param_parts_split.into_iter(); + + if param_parts.next() != Some("m") { + return Err(UnknownCryptoError); + } + + let memory = Self::parse_decimal_value(param_parts.next().unwrap())?; + if memory < MIN_MEMORY { + return Err(UnknownCryptoError); + } + + if param_parts.next() != Some("t") { + return Err(UnknownCryptoError); + } + let iterations = Self::parse_decimal_value(param_parts.next().unwrap())?; + if iterations < MIN_ITERATIONS { + return Err(UnknownCryptoError); + } + + if param_parts.next() != Some("p") { + return Err(UnknownCryptoError); + } + let lanes = Self::parse_decimal_value(param_parts.next().unwrap())?; + if lanes != LANES { + return Err(UnknownCryptoError); + } + + let salt = Base64NoPadding::decode_to_vec(parts.next().unwrap(), None)?; + if salt.len() != SALT_LENGTH { + return Err(UnknownCryptoError); + } + let password_hash_raw = Base64NoPadding::decode_to_vec(parts.next().unwrap(), None)?; + if password_hash_raw.len() != PWHASH_LENGTH { + return Err(UnknownCryptoError); + } + + Ok(Self { + encoded_password_hash: password_hash.into(), + password_hash: password_hash_raw, + salt: Salt::from_slice(&salt)?, + iterations, + memory, + }) + } + + #[inline] + /// Return encoded password hash. __**Warning**__: Should not be used to verify + /// password hashes. This __**breaks protections**__ that the type implements. + pub fn unprotected_as_encoded(&self) -> &str { + self.encoded_password_hash.as_ref() + } + + #[inline] + /// Return the password hash as byte slice. __**Warning**__: Should not be used unless strictly + /// needed. This __**breaks protections**__ that the type implements. + pub fn unprotected_as_bytes(&self) -> &[u8] { + self.password_hash.as_ref() + } + + #[inline] + /// Return the length of the password hash. + pub fn len(&self) -> usize { + self.password_hash.len() + } + + #[inline] + /// Return `true` if the password hash is empty, `false` otherwise. + /// + /// __NOTE__: This method should always return `false`, since there shouldn't be a way + /// to create an empty password hash. + pub fn is_empty(&self) -> bool { + debug_assert_eq!(self.encoded_password_hash.is_empty(), self.password_hash.is_empty(), + "Both the encoded password hash and the raw hash must be non-empty or empty at the same time."); + self.password_hash.is_empty() + } +} + +impl core::fmt::Debug for PasswordHash { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "PasswordHash {{ encoded_password_hash: [***OMITTED***], password_hash: [***OMITTED***], iterations: \ + {:?}, memory: {:?} }}", + self.iterations, self.memory + ) + } +} + +impl_ct_partialeq_trait!(PasswordHash, unprotected_as_bytes); + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +/// `PasswordHash` serializes as would a [`String`](std::string::String). Note that +/// the serialized type likely does not have the same protections that Orion +/// provides, such as constant-time operations. A good rule of thumb is to only +/// serialize these types for storage. Don't operate on the serialized types. +impl Serialize for PasswordHash { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let encoded_string = self.unprotected_as_encoded(); + serializer.serialize_str(encoded_string) + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +/// `PasswordHash` deserializes from a [`String`](std::string::String). +impl<'de> Deserialize<'de> for PasswordHash { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let encoded_str = String::deserialize(deserializer)?; + PasswordHash::from_encoded(&encoded_str).map_err(de::Error::custom) + } +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Hash a password using Argon2i. +pub fn hash_password( + password: &Password, + iterations: u32, + memory: u32, +) -> Result { + if iterations < MIN_ITERATIONS { + return Err(UnknownCryptoError); + } + + // Cannot panic as this is a valid size. + let salt = Salt::generate(SALT_LENGTH).unwrap(); + let mut buffer = Zeroizing::new([0u8; PWHASH_LENGTH]); + + argon2i::derive_key( + password.unprotected_as_bytes(), + salt.as_ref(), + iterations, + memory, + None, + None, + buffer.as_mut(), + )?; + + PasswordHash::from_slice(buffer.as_ref(), salt.as_ref(), iterations, memory) +} + +/// Hash and verify a password using Argon2i. The Argon2i parameters `iterations` +/// and `memory` will be pulled from the `expected: &PasswordHash` argument. If +/// you want to manually specify the iterations and memory for Argon2i to use in +/// hashing the `password` argument, see the +/// [`hazardous::kdf`](crate::hazardous::kdf::argon2i) module. +/// +/// # Example: +/// ```rust +/// use orion::pwhash; +/// +/// let password = pwhash::Password::from_slice(b"Secret password")?; +/// let wrong_password = pwhash::Password::from_slice(b"hunter2")?; +/// +/// // Pretend these are stored somewhere and out-of-mind, e.g. in a database. +/// let hash1 = pwhash::hash_password(&password, 3, 1<<15)?; +/// let hash2 = pwhash::hash_password(&password, 4, 2<<15)?; +/// +/// // We don't have to remember which password used what parameters when it's +/// // time to verify them. Both will correctly return `Ok(())`. +/// assert!(pwhash::hash_password_verify(&hash1, &password).is_ok()); +/// assert!(pwhash::hash_password_verify(&hash2, &password).is_ok()); +/// +/// // The only way to get a failing result is to use the wrong password. +/// assert!(pwhash::hash_password_verify(&hash1, &wrong_password).is_err()); +/// # Ok::<(), orion::errors::UnknownCryptoError>(()) +/// ``` +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +pub fn hash_password_verify( + expected: &PasswordHash, + password: &Password, +) -> Result<(), UnknownCryptoError> { + let mut buffer = Zeroizing::new([0u8; PWHASH_LENGTH]); + + argon2i::verify( + expected.unprotected_as_bytes(), + password.unprotected_as_bytes(), + expected.salt.as_ref(), + expected.iterations, + expected.memory, + None, + None, + buffer.as_mut(), + ) +} + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + #[test] + #[cfg(feature = "safe_api")] + fn test_debug_impl() { + let valid = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let password_hash = PasswordHash::from_encoded(valid).unwrap(); + let debug = format!("{:?}", password_hash); + let expected = "PasswordHash { encoded_password_hash: [***OMITTED***], password_hash: [***OMITTED***], iterations: 3, memory: 65536 }"; + assert_eq!(debug, expected); + } + + #[cfg(feature = "serde")] + mod test_serde_impls { + use super::*; + + #[test] + fn test_valid_deserialization() { + let encoded_hash = "$argon2i$v=19$m=65536,t=3,p=1$c29tZXNhbHRzb21lc2FsdA$fRsRY9PAt5H+qAKuXRzL0/6JbFShsCd62W5aHzESk/c"; + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + let deserialized: PasswordHash = + serde_json::from_str(format!("\"{}\"", encoded_hash).as_str()).unwrap(); + assert_eq!(deserialized, expected); + } + + #[test] + fn test_valid_serialization() { + let encoded_hash = "$argon2i$v=19$m=65536,t=3,p=1$c29tZXNhbHRzb21lc2FsdA$fRsRY9PAt5H+qAKuXRzL0/6JbFShsCd62W5aHzESk/c"; + let hash = PasswordHash::from_encoded(encoded_hash).unwrap(); + let serialized: String = serde_json::to_string(&hash).unwrap(); + assert_eq!(serialized, format!("\"{}\"", encoded_hash)); + } + } + + /// The tests herein were generated with the CLI tool from the reference implementation at: + /// https://github.com/P-H-C/phc-winner-argon2/commit/62358ba2123abd17fccf2a108a301d4b52c01a7c + mod test_encoding_from_ref { + use super::*; + use hex; + + #[test] + fn test_encoding_and_verify_1() { + let password = Password::from_slice(b"password").unwrap(); + let raw_hash = + hex::decode("7d1b1163d3c0b791fea802ae5d1ccbd3fe896c54a1b0277ad96e5a1f311293f7") + .unwrap(); + let encoded_hash = "$argon2i$v=19$m=65536,t=3,p=1$c29tZXNhbHRzb21lc2FsdA$fRsRY9PAt5H+qAKuXRzL0/6JbFShsCd62W5aHzESk/c"; + + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + assert_eq!(expected.unprotected_as_bytes(), &raw_hash[..]); + assert!(hash_password_verify(&expected, &password).is_ok()); + } + + #[test] + fn test_encoding_and_verify_2() { + let password = Password::from_slice(b"passwordPASSWORDPassword").unwrap(); + let raw_hash = + hex::decode("ed4b0fd657e165f9ffe90f66ff315fbec878e629f03b2d6468d4b17a50c796aa") + .unwrap(); + let encoded_hash = "$argon2i$v=19$m=65536,t=3,p=1$c29tZXNhbHRzb21lc2FsdA$7UsP1lfhZfn/6Q9m/zFfvsh45inwOy1kaNSxelDHlqo"; + + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + assert_eq!(expected.unprotected_as_bytes(), &raw_hash[..]); + assert!(hash_password_verify(&expected, &password).is_ok()); + } + + #[test] + fn test_encoding_and_verify_3() { + // Different salt from test 2 + let password = Password::from_slice(b"passwordPASSWORDPassword").unwrap(); + let raw_hash = + hex::decode("fa9ea96fecd0998251d698c1303edda4df3889a39bfa87cd5e7b8656ef61b510") + .unwrap(); + let encoded_hash = "$argon2i$v=19$m=65536,t=3,p=1$U29tZVNhbHRTb21lU2FsdA$+p6pb+zQmYJR1pjBMD7dpN84iaOb+ofNXnuGVu9htRA"; + + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + assert_eq!(expected.unprotected_as_bytes(), &raw_hash[..]); + assert!(hash_password_verify(&expected, &password).is_ok()); + } + + #[test] + fn test_encoding_and_verify_4() { + let password = Password::from_slice(b"passwordPASSWORDPassword").unwrap(); + let raw_hash = + hex::decode("fb3e0cdf7b10970bf6711c151861851566006f8986c9109ba2cdd5d98f9ca9d7") + .unwrap(); + let encoded_hash = "$argon2i$v=19$m=256,t=3,p=1$c29tZXNhbHRzb21lc2FsdA$+z4M33sQlwv2cRwVGGGFFWYAb4mGyRCbos3V2Y+cqdc"; + + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + assert_eq!(expected.unprotected_as_bytes(), &raw_hash[..]); + assert!(hash_password_verify(&expected, &password).is_ok()); + } + + #[test] + fn test_encoding_and_verify_5() { + let password = Password::from_slice(b"passwordPASSWORDPassword").unwrap(); + let raw_hash = + hex::decode("9b134c4c1c34e66170d1088c18be3a8e0f4a1837d4c069703ce62f85248b1e8f") + .unwrap(); + let encoded_hash = "$argon2i$v=19$m=256,t=4,p=1$c29tZXNhbHRzb21lc2FsdA$mxNMTBw05mFw0QiMGL46jg9KGDfUwGlwPOYvhSSLHo8"; + + let expected = PasswordHash::from_encoded(encoded_hash).unwrap(); + assert_eq!(expected.unprotected_as_bytes(), &raw_hash[..]); + assert!(hash_password_verify(&expected, &password).is_ok()); + } + } + + mod test_password_hash { + use super::*; + + #[test] + fn test_password_hash_eq() { + let password_hash = + PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 1 << 16).unwrap(); + assert_eq!(password_hash.len(), 32); + assert_eq!(password_hash.unprotected_as_bytes(), &[0u8; 32]); + + let password_hash_again = + PasswordHash::from_encoded(password_hash.unprotected_as_encoded()).unwrap(); + assert_eq!(password_hash, password_hash_again); + } + + #[test] + fn test_password_hash_ne() { + let password_hash = + PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 1 << 16).unwrap(); + assert_eq!(password_hash.len(), 32); + assert_eq!(password_hash.unprotected_as_bytes(), &[0u8; 32]); + + let password_hash_again = + PasswordHash::from_slice(&[1u8; 32], &[0u8; 16], 3, 1 << 16).unwrap(); + + assert_ne!(password_hash, password_hash_again); + } + + #[test] + fn test_valid_encoded_password() { + let valid = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + assert!(PasswordHash::from_encoded(valid).is_ok()); + } + + #[test] + fn test_bad_encoding_missing_dollar() { + let first_missing = "argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let second_missing = "$argon2iv=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let third_missing = "$argon2i$v=19m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let fourth_missing = "$argon2i$v=19$m=65536,t=3,p=1cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let fifth_missing = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(first_missing).is_err()); + assert!(PasswordHash::from_encoded(second_missing).is_err()); + assert!(PasswordHash::from_encoded(third_missing).is_err()); + assert!(PasswordHash::from_encoded(fourth_missing).is_err()); + assert!(PasswordHash::from_encoded(fifth_missing).is_err()); + } + + #[test] + fn test_bad_encoding_missing_comma() { + let first_missing = "$argon2i$v=19$m=65536t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let second_missing = "$argon2i$v=19$m=65536,t=3p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(first_missing).is_err()); + assert!(PasswordHash::from_encoded(second_missing).is_err()); + } + + #[test] + fn test_bad_encoding_missing_equals() { + let first_missing = "$argon2i$v19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let second_missing = "$argon2$iv=19$m65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let third_missing = "$argon2i$v=19$m=65536,t3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let fourth_missing = "$argon2i$v=19$m=65536,t=3,p1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(first_missing).is_err()); + assert!(PasswordHash::from_encoded(second_missing).is_err()); + assert!(PasswordHash::from_encoded(third_missing).is_err()); + assert!(PasswordHash::from_encoded(fourth_missing).is_err()); + } + + #[test] + fn test_bad_encoding_whitespace() { + let first = "$argon2i$v=19$m=65536,t=3, p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let second = " $argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let third = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA "; + + assert!(PasswordHash::from_encoded(first).is_err()); + assert!(PasswordHash::from_encoded(second).is_err()); + assert!(PasswordHash::from_encoded(third).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_threads() { + let one = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let zero = "$argon2i$v=19$m=65536,t=3,p=0$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let two = "$argon2i$v=19$m=65536,t=3,p=2$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(one).is_ok()); + assert!(PasswordHash::from_encoded(zero).is_err()); + assert!(PasswordHash::from_encoded(two).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_memory() { + let exact_min = "$argon2i$v=19$m=8,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let less = "$argon2i$v=19$m=7,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + // Throws error during parsing as u32 + let u32_overflow = format!("$argon2i$v=19$m={},t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA", u64::MAX); + + assert!(PasswordHash::from_encoded(exact_min).is_ok()); + assert!(PasswordHash::from_encoded(less).is_err()); + assert!(PasswordHash::from_encoded(&u32_overflow).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_iterations() { + let exact_min = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let less = "$argon2i$v=19$m=65536,t=2,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + // Throws error during parsing as u32 + let u32_overflow = format!("$argon2i$v=19$m=65536,t={},p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA", u64::MAX); + + assert!(PasswordHash::from_encoded(exact_min).is_ok()); + assert!(PasswordHash::from_encoded(less).is_err()); + assert!(PasswordHash::from_encoded(&u32_overflow).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_algo() { + let argon2id = "$argon2id$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let argon2d = "$argon2d$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let nothing = "$$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(argon2d).is_err()); + assert!(PasswordHash::from_encoded(argon2id).is_err()); + assert!(PasswordHash::from_encoded(nothing).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_version() { + let v13 = "$argon2i$v=13$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let v0 = "$argon2i$v=0$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let nothing = "$argon2i$v=$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(v13).is_err()); + assert!(PasswordHash::from_encoded(v0).is_err()); + assert!(PasswordHash::from_encoded(nothing).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_order() { + let version_first = "$v=19$argon2i$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let t_before_m = "$argon2i$v=19$t=3,m=65536,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let p_before_t = "$argon2i$v=19$m=65536,p=1,t=3$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let p_before_m = "$argon2i$v=19$p=1,m=65536,t=3$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let pass_before_salt = "$argon2i$v=19$m=65536,t=3,p=1$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA$cHBwcHBwcHBwcHBwcHBwcA"; + let salt_first = "$cHBwcHBwcHBwcHBwcHBwcA$argon2i$v=19$m=65536,t=3,p=1$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let pass_first = "$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA"; + + assert!(PasswordHash::from_encoded(version_first).is_err()); + assert!(PasswordHash::from_encoded(t_before_m).is_err()); + assert!(PasswordHash::from_encoded(p_before_t).is_err()); + assert!(PasswordHash::from_encoded(p_before_m).is_err()); + assert!(PasswordHash::from_encoded(pass_before_salt).is_err()); + assert!(PasswordHash::from_encoded(salt_first).is_err()); + assert!(PasswordHash::from_encoded(pass_first).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_salt() { + let exact = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let nothing = + "$argon2i$v=19$m=65536,t=3,p=1$$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let above = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcAA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(exact).is_ok()); + assert!(PasswordHash::from_encoded(nothing).is_err()); + assert!(PasswordHash::from_encoded(above).is_err()); + } + + #[test] + fn test_bad_encoding_invalid_password() { + let exact = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let nothing = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$"; + let above = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAA"; + + assert!(PasswordHash::from_encoded(exact).is_ok()); + assert!(PasswordHash::from_encoded(nothing).is_err()); + assert!(PasswordHash::from_encoded(above).is_err()); + } + + #[test] + fn test_bad_encoding_bad_parsing_integers() { + let j_instead_of_mem = "$argon2i$v=19$m=j,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(j_instead_of_mem).is_err()); + } + + #[test] + fn test_bad_encoding_first_not_empty() { + // Nothing should precede "$argon2i" + let non_empty_first = "apples$argon2i$v=19$m=4096,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(non_empty_first).is_err()); + } + + #[test] + fn test_bad_encoding_bad_p() { + let p_is_j = "$argon2i$v=19$m=4096,t=3,j=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let p_gone = "$argon2i$v=19$m=4096,t=3,=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(p_is_j).is_err()); + assert!(PasswordHash::from_encoded(p_gone).is_err()); + } + + #[test] + fn test_decimal_value_reject_leading_zeroes() { + // https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#decimal-encoding + // According to the specification, the decimal parameters may not start with 0, if there is more than + // one character in the string. .parse::() will ignore leading 0's, so it will parse "0032" -> 32u32. + // Test here that these cases are detected and rejected by returning an error. + let valid = "$argon2i$v=19$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let invalid0 = "$argon2i$v=019$m=65536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let invalid1 = "$argon2i$v=19$m=065536,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let invalid2 = "$argon2i$v=19$m=65536,t=03,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + let invalid3 = "$argon2i$v=19$m=65536,t=3,p=01$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + + assert!(PasswordHash::from_encoded(valid).is_ok()); + assert!(PasswordHash::from_encoded(invalid0).is_err()); + assert!(PasswordHash::from_encoded(invalid1).is_err()); + assert!(PasswordHash::from_encoded(invalid2).is_err()); + assert!(PasswordHash::from_encoded(invalid3).is_err()); + } + + #[test] + fn test_bounds_max_min_encoded_len() { + let minimum = "$argon2i$v=19$m=8,t=3,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + assert_eq!(minimum.len(), PasswordHash::MIN_ENCODED_LEN); + let maximum = "$argon2i$v=19$m=1111111111,t=1111111111,p=1$cHBwcHBwcHBwcHBwcHBwcA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + assert_eq!(maximum.len(), PasswordHash::MAX_ENCODED_LEN); + + // salt removed one char + let less = "$argon2i$v=19$m=8,t=3,p=1$cHBwcHBwcHBwcHBwcHBwc$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + assert_eq!(less.len(), PasswordHash::MIN_ENCODED_LEN - 1); + // salt added one char + let more = "$argon2i$v=19$m=1111111111,t=1111111111,p=1$cHBwcHBwcHBwcHBwcHBwcAA$MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA"; + assert_eq!(more.len(), PasswordHash::MAX_ENCODED_LEN + 1); + + assert!(PasswordHash::from_encoded(minimum).is_ok()); + assert!(PasswordHash::from_encoded(maximum).is_ok()); + assert!(PasswordHash::from_encoded(less).is_err()); + assert!(PasswordHash::from_encoded(more).is_err()); + } + + #[test] + fn test_from_slice_password() { + assert!(PasswordHash::from_slice(&[0u8; 31], &[0u8; 16], 3, 1 << 16).is_err()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 1 << 16).is_ok()); + assert!(PasswordHash::from_slice(&[0u8; 33], &[0u8; 16], 3, 1 << 16).is_err()); + } + + #[test] + fn test_from_slice_salt() { + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 15], 3, 1 << 16).is_err()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 1 << 16).is_ok()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 17], 3, 1 << 16).is_err()); + } + + #[test] + fn test_from_slice_mem() { + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 7).is_err()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 8).is_ok()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 9).is_ok()); + } + + #[test] + fn test_from_slice_bad_iter() { + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 2, 1 << 16).is_err()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 3, 1 << 16).is_ok()); + assert!(PasswordHash::from_slice(&[0u8; 32], &[0u8; 16], 4, 1 << 16).is_ok()); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// If valid params then it's always valid to encode/decode. + fn prop_always_produce_valid_encoding( + password: Vec, + salt: Vec, + iterations: u32, + memory: u32, + ) -> bool { + let res = PasswordHash::from_slice(&password[..], &salt[..], iterations, memory); + if res.is_ok() { + assert!(PasswordHash::from_encoded(res.unwrap().unprotected_as_encoded()).is_ok()); + } + + true + } + } + + mod test_pwhash_and_verify { + use super::*; + + #[test] + fn test_argon2i_verify() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let dk = hash_password(&password, 3, 4096).unwrap(); + + assert!(hash_password_verify(&dk, &password).is_ok()); + } + + #[test] + fn test_argon2i_verify_err_modified_password() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let mut pwd_mod = dk.unprotected_as_bytes().to_vec(); + pwd_mod[0..32].copy_from_slice(&[0u8; 32]); + let modified = PasswordHash::from_slice(&pwd_mod, dk.salt.as_ref(), 3, 4096).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_verify_err_modified_memory() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let encoded = dk.unprotected_as_encoded(); + + let mut modified = encoded.to_string(); + let memory_offset = modified.find("$m=4096").unwrap(); + modified.replace_range(memory_offset..memory_offset + 7, "$m=2048"); + + let modified = PasswordHash::from_encoded(&modified).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_verify_err_modified_iterations() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let encoded = dk.unprotected_as_encoded(); + + let mut modified = encoded.to_string(); + let iterations_offset = modified.find(",t=3").unwrap(); + modified.replace_range(iterations_offset..iterations_offset + 4, ",t=4"); + + let modified = PasswordHash::from_encoded(&modified).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_verify_err_modified_memory_and_iterations() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let encoded = dk.unprotected_as_encoded(); + + let mut modified = encoded.to_string(); + let memory_offset = modified.find("$m=4096").unwrap(); + let iterations_offset = modified.find(",t=3").unwrap(); + modified.replace_range(memory_offset..memory_offset + 7, "$m=2048"); + modified.replace_range(iterations_offset..iterations_offset + 4, ",t=4"); + + let modified = PasswordHash::from_encoded(&modified).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_verify_err_modified_salt() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let mut salt_mod = dk.salt.as_ref().to_vec(); + salt_mod[0..16].copy_from_slice(&[0u8; 16]); + let modified = + PasswordHash::from_slice(dk.unprotected_as_bytes(), &salt_mod, 3, 4096).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_verify_err_modified_salt_and_password() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let dk = hash_password(&password, 3, 4096).unwrap(); + let mut pwd_mod = dk.unprotected_as_bytes().to_vec(); + let mut salt_mod = dk.salt.as_ref().to_vec(); + pwd_mod[0..32].copy_from_slice(&[0u8; 32]); + salt_mod[0..16].copy_from_slice(&[0u8; 16]); + let modified = PasswordHash::from_slice(&pwd_mod, &salt_mod, 3, 4096).unwrap(); + + assert!(hash_password_verify(&modified, &password).is_err()); + } + + #[test] + fn test_argon2i_invalid_iterations() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + assert!(hash_password(&password, MIN_ITERATIONS - 1, 4096).is_err()); + } + + #[test] + fn test_argon2i_invalid_memory() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + assert!(hash_password(&password, MIN_ITERATIONS, MIN_MEMORY - 1).is_err()); + } + } +} diff --git a/vendor/orion/src/lib.rs b/vendor/orion/src/lib.rs new file mode 100644 index 0000000..5064526 --- /dev/null +++ b/vendor/orion/src/lib.rs @@ -0,0 +1,117 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! A usable pure-Rust cryptography library. +//! +//! ## Authenticated secret-key encryption +//! [`orion::aead`] offers authenticated secret-key encryption using +//! XChaCha20Poly1305. +//! +//! ## Password hashing and verification +//! [`orion::pwhash`] offers password hashing and verification using Argon2i. +//! +//! ## Key derivation +//! [`orion::kdf`] offers key derivation using Argon2i. +//! +//! ## Message authentication +//! [`orion::auth`] offers message authentication and verification using BLAKE2b. +//! +//! ## Hashing +//! [`orion::hash`] offers hashing using BLAKE2b. +//! +//! ## Key exchange +//! [`orion::kex`] offers ephemeral key exchange using X25519 and BLAKE2b. +//! +//! ### A note on `no_std`: +//! When Orion is used in a `no_std` context, the high-level API is not available, since it relies on access to the systems random number generator. +//! +//! More information about Orion is available in the [wiki]. +//! +//! [`orion::aead`]: crate::aead +//! [`orion::pwhash`]: crate::pwhash +//! [`orion::kdf`]: crate::kdf +//! [`orion::auth`]: crate::auth +//! [`orion::hash`]: crate::hash +//! [`orion::kex`]: crate::kex +//! [wiki]: https://github.com/orion-rs/orion/wiki + +#![cfg_attr(not(feature = "safe_api"), no_std)] +#![forbid(unsafe_code)] +#![deny(clippy::mem_forget)] +#![warn( + missing_docs, + rust_2018_idioms, + trivial_casts, + unused_qualifications, + overflowing_literals +)] +#![cfg_attr(docsrs, feature(doc_cfg))] + +#[cfg(test)] +#[cfg(feature = "safe_api")] +extern crate quickcheck; +#[cfg(test)] +#[cfg(feature = "safe_api")] +#[macro_use(quickcheck)] +extern crate quickcheck_macros; + +#[cfg(feature = "alloc")] +#[cfg_attr(feature = "alloc", macro_use)] +extern crate alloc; + +#[macro_use] +mod typedefs; + +#[macro_use] +/// Utilities such as constant-time comparison. +pub mod util; + +/// Errors for Orion's cryptographic operations. +pub mod errors; + +/// \[__**Caution**__\] Low-level API. +pub mod hazardous; + +#[cfg(feature = "safe_api")] +mod high_level; + +#[cfg(feature = "safe_api")] +pub use high_level::hash; + +#[cfg(feature = "safe_api")] +pub use high_level::aead; + +#[cfg(feature = "safe_api")] +pub use high_level::auth; + +#[cfg(feature = "safe_api")] +pub use high_level::pwhash; + +#[cfg(feature = "safe_api")] +pub use high_level::kdf; + +#[cfg(feature = "safe_api")] +pub use high_level::kex; + +#[doc(hidden)] +/// Testing framework. +pub mod test_framework; diff --git a/vendor/orion/src/test_framework/aead_interface.rs b/vendor/orion/src/test_framework/aead_interface.rs new file mode 100644 index 0000000..b19c44f --- /dev/null +++ b/vendor/orion/src/test_framework/aead_interface.rs @@ -0,0 +1,379 @@ +// MIT License + +// Copyright (c) 2019-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#![allow(non_snake_case)] +#[cfg(feature = "safe_api")] +use crate::errors::UnknownCryptoError; + +#[cfg(test)] +#[cfg(feature = "safe_api")] +use crate::test_framework::streamcipher_interface::TestingRandom; + +#[allow(clippy::too_many_arguments)] +#[cfg(feature = "safe_api")] +/// Test runner for AEADs. +pub fn AeadTestRunner( + sealer: Sealer, + opener: Opener, + key: Key, + nonce: Nonce, + input: &[u8], + expected_ct_with_tag: Option<&[u8]>, + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + seal_dst_out_length(&sealer, &key, &nonce, input, tag_size, aad); + open_dst_out_length(&sealer, &opener, &key, &nonce, input, tag_size, aad); + open_modified_tag_err(&sealer, &opener, &key, &nonce, input, tag_size, aad); + open_modified_ciphertext_err(&sealer, &opener, &key, &nonce, input, tag_size, aad); + open_modified_aad_err(&sealer, &opener, &key, &nonce, input, tag_size, aad); + none_or_empty_some_aad_same_result(&sealer, &opener, &key, &nonce, input, tag_size); + seal_open_equals_expected( + &sealer, + &opener, + &key, + &nonce, + input, + expected_ct_with_tag, + tag_size, + aad, + ); + seal_plaintext_length(&sealer, &key, &nonce, tag_size, aad); + open_ciphertext_with_tag_length(&sealer, &opener, &key, &nonce, tag_size, aad); +} + +#[cfg(feature = "safe_api")] +/// Related bug: +/// Test dst_out mutable array sizes when using seal(). +fn seal_dst_out_length( + sealer: &Sealer, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + assert!(sealer(key, nonce, input, default_aad, &mut dst_out_ct).is_ok()); + + let mut dst_out_ct_more = vec![0u8; input.len() + (tag_size + 1)]; + // Related bug: #52 + assert!(sealer(key, nonce, input, default_aad, &mut dst_out_ct_more).is_ok()); + + let mut dst_out_ct_more_double = vec![0u8; input.len() + (tag_size * 2)]; + // Related bug: #52 + assert!(sealer(key, nonce, input, default_aad, &mut dst_out_ct_more_double).is_ok()); + + let mut dst_out_ct_less = vec![0u8; input.len() + (tag_size - 1)]; + assert!(sealer(key, nonce, input, default_aad, &mut dst_out_ct_less).is_err()); +} + +#[cfg(feature = "safe_api")] +/// Related bug: +/// Test input sizes when using seal(). +fn seal_plaintext_length( + sealer: &Sealer, + key: &Key, + nonce: &Nonce, + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let input_0 = vec![0u8; 0]; + let mut dst_out_ct_0 = vec![0u8; input_0.len() + tag_size]; + assert!(sealer(key, nonce, &input_0, default_aad, &mut dst_out_ct_0).is_ok()); + + let input_1 = vec![0u8; 1]; + let mut dst_out_ct_1 = vec![0u8; input_1.len() + tag_size]; + assert!(sealer(key, nonce, &input_1, default_aad, &mut dst_out_ct_1).is_ok()); + + let input_128 = vec![0u8; 128]; + let mut dst_out_ct_128 = vec![0u8; input_128.len() + tag_size]; + assert!(sealer(key, nonce, &input_128, default_aad, &mut dst_out_ct_128).is_ok()); +} + +#[cfg(feature = "safe_api")] +/// Related bug: +/// Test dst_out mutable array sizes when using open(). +fn open_dst_out_length( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + sealer(key, nonce, input, default_aad, &mut dst_out_ct).unwrap(); + + let mut dst_out_pt = vec![0u8; input.len()]; + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt).is_ok()); + + let mut dst_out_pt_0 = [0u8; 0]; + let empty_out_res = opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt_0); + if input.is_empty() { + assert!(empty_out_res.is_ok()); + } else { + assert!(empty_out_res.is_err()); + } + + if !input.is_empty() { + let mut dst_out_pt_less = vec![0u8; input.len() - 1]; + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt_less).is_err()); + } + + let mut dst_out_pt_more = vec![0u8; input.len() + 1]; + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt_more).is_ok()); +} + +#[cfg(feature = "safe_api")] +/// Test input sizes when using open(). +fn open_ciphertext_with_tag_length( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + let mut dst_out_pt = vec![0u8; tag_size]; + + assert!(opener(key, nonce, &[0u8; 0], default_aad, &mut dst_out_pt).is_err()); + + assert!(opener( + key, + nonce, + &vec![0u8; tag_size - 1], + default_aad, + &mut dst_out_pt + ) + .is_err()); + + let mut dst_out_ct = vec![0u8; tag_size]; + sealer(key, nonce, &[0u8; 0], default_aad, &mut dst_out_ct).unwrap(); + + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt).is_ok()); +} + +#[allow(clippy::too_many_arguments)] +#[cfg(feature = "safe_api")] +/// Test that sealing and opening produces the expected ciphertext. +fn seal_open_equals_expected( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + expected_ct_with_tag: Option<&[u8]>, + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + sealer(key, nonce, input, default_aad, &mut dst_out_ct).unwrap(); + if let Some(expected) = expected_ct_with_tag { + assert_eq!(expected, &dst_out_ct[..]); + } + + let mut dst_out_pt = input.to_vec(); + opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt).unwrap(); + assert_eq!(input, &dst_out_pt[..]); + if let Some(expected) = expected_ct_with_tag { + opener(key, nonce, expected, default_aad, &mut dst_out_pt).unwrap(); + assert_eq!(input, &dst_out_pt[..]); + } +} + +#[cfg(feature = "safe_api")] +/// When opening sealed data with a modified tag, an error should be returned. +fn open_modified_tag_err( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + sealer(key, nonce, input, default_aad, &mut dst_out_ct).unwrap(); + // Modify the first byte of the authentication tag. + dst_out_ct[input.len() + 1] ^= 1; + + let mut dst_out_pt = input.to_vec(); + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt).is_err()); +} + +#[cfg(feature = "safe_api")] +/// When opening sealed data with a modified ciphertext, an error should be returned. +fn open_modified_ciphertext_err( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut input = input; + if input.is_empty() { + input = &[0u8; 1]; + } + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + sealer(key, nonce, input, default_aad, &mut dst_out_ct).unwrap(); + // Modify the first byte of the ciphertext. + dst_out_ct[0] ^= 1; + + let mut dst_out_pt = input.to_vec(); + assert!(opener(key, nonce, &dst_out_ct, default_aad, &mut dst_out_pt).is_err()); +} + +#[cfg(feature = "safe_api")] +/// When opening sealed data with modified aad, an error should be returned. +fn open_modified_aad_err( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, + aad: &[u8], +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let default_aad = if aad.is_empty() { None } else { Some(aad) }; + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + sealer(key, nonce, input, default_aad, &mut dst_out_ct).unwrap(); + + let mut dst_out_pt = input.to_vec(); + assert!(opener(key, nonce, &dst_out_ct, Some(b"BAD AAD"), &mut dst_out_pt).is_err()); +} + +#[cfg(feature = "safe_api")] +/// Using None or Some with empty slice should produce the exact same result. +fn none_or_empty_some_aad_same_result( + sealer: &Sealer, + opener: &Opener, + key: &Key, + nonce: &Nonce, + input: &[u8], + tag_size: usize, +) where + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut dst_out_ct_none = vec![0u8; input.len() + tag_size]; + let mut dst_out_ct_some_empty = vec![0u8; input.len() + tag_size]; + + sealer(key, nonce, input, None, &mut dst_out_ct_none).unwrap(); + sealer( + key, + nonce, + input, + Some(&[0u8; 0]), + &mut dst_out_ct_some_empty, + ) + .unwrap(); + + assert_eq!(dst_out_ct_none, dst_out_ct_some_empty); + + let mut dst_out_pt = vec![0u8; input.len()]; + assert!(opener( + key, + nonce, + &dst_out_ct_none, + Some(&[0u8; 0]), + &mut dst_out_pt + ) + .is_ok()); + assert!(opener(key, nonce, &dst_out_ct_some_empty, None, &mut dst_out_pt).is_ok()); +} + +#[cfg(test)] +#[cfg(feature = "safe_api")] +/// Test that sealing and opening with different secret-key/nonce yields an error. +pub fn test_diff_params_err( + sealer: &Sealer, + opener: &Opener, + input: &[u8], + tag_size: usize, +) where + Key: TestingRandom + PartialEq, + Nonce: TestingRandom + PartialEq, + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let sk1 = Key::gen(); + let sk2 = Key::gen(); + assert!(sk1 != sk2); + + let n1 = Nonce::gen(); + let n2 = Nonce::gen(); + assert!(n1 != n2); + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + let mut dst_out_pt = vec![0u8; input.len()]; + + // Different secret key + sealer(&sk1, &n1, input, None, &mut dst_out_ct).unwrap(); + assert!(opener(&sk2, &n1, &dst_out_ct, None, &mut dst_out_pt).is_err()); + + // Different nonce + sealer(&sk1, &n1, input, None, &mut dst_out_ct).unwrap(); + assert!(opener(&sk1, &n2, &dst_out_ct, None, &mut dst_out_pt).is_err()); +} diff --git a/vendor/orion/src/test_framework/incremental_interface.rs b/vendor/orion/src/test_framework/incremental_interface.rs new file mode 100644 index 0000000..6bb14b9 --- /dev/null +++ b/vendor/orion/src/test_framework/incremental_interface.rs @@ -0,0 +1,343 @@ +// MIT License + +// Copyright (c) 2019-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use crate::errors::UnknownCryptoError; +use core::marker::PhantomData; + +/// Trait to define default streaming contexts that can be tested. +pub trait TestableStreamingContext { + /// Streaming context function to reset the internal state. + fn reset(&mut self) -> Result<(), UnknownCryptoError>; + + /// Streaming context function to update the internal state. + fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError>; + + /// Streaming context function to finalize the internal state. + fn finalize(&mut self) -> Result; + + /// Streaming context function to combine new(), update() and finalize() from the internal state. + fn one_shot(input: &[u8]) -> Result; + + /// Streaming context function to verify pre-computed results. + fn verify_result(expected: &T, input: &[u8]) -> Result<(), UnknownCryptoError>; + + /// Testing utility-function that compares the internal state to another. + fn compare_states(state_1: &Self, state_2: &Self); +} + +#[allow(dead_code)] // Allow because blocksize field is only used with std. +/// A streaming context tester. +pub struct StreamingContextConsistencyTester { + _return_type: PhantomData, + // The initial context to base further calls upon. + _initial_context: T, + blocksize: usize, +} + +impl StreamingContextConsistencyTester +where + R: PartialEq + core::fmt::Debug, + T: TestableStreamingContext + Clone, +{ + /// The streaming interface tester is created utilizing an initialized + /// streaming state. + pub fn new(streaming_context: T, blocksize: usize) -> Self { + Self { + _return_type: PhantomData, + _initial_context: streaming_context, + blocksize, + } + } + + // Default input to process. + // The number 37 has no particular meaning. + const DEFAULT_INPUT: [u8; 37] = [255u8; 37]; + + #[cfg(feature = "safe_api")] + /// Run all consistency tests given some input data. + /// Usually used with quickcheck. + pub fn run_all_tests_property(&self, data: &[u8]) { + self.consistency(data); + self.consistency(&[0u8; 0]); + self.produces_same_state(data); + + // Following test requires std. + self.incremental_and_one_shot(data); + + self.double_finalize_with_reset_no_update_ok(data); + self.double_finalize_with_reset_ok(data); + self.double_finalize_err(data); + self.update_after_finalize_with_reset_ok(data); + self.update_after_finalize_err(data); + self.double_reset_ok(data); + self.immediate_finalize(); + Self::verify_same_input_ok(data); + Self::verify_diff_input_err(data); + } + + #[cfg(feature = "safe_api")] + /// Used when quickcheck is not available to generate input. + /// Default input data is used instead. Requires std. + pub fn run_all_tests(&self) { + self.consistency(&Self::DEFAULT_INPUT); + self.consistency(&[0u8; 0]); + self.produces_same_state(&Self::DEFAULT_INPUT); + + // Following test requires std. + self.incremental_processing_with_leftover(); + + self.incremental_and_one_shot(&Self::DEFAULT_INPUT); + self.double_finalize_with_reset_no_update_ok(&Self::DEFAULT_INPUT); + self.double_finalize_with_reset_ok(&Self::DEFAULT_INPUT); + self.double_finalize_err(&Self::DEFAULT_INPUT); + self.update_after_finalize_with_reset_ok(&Self::DEFAULT_INPUT); + self.update_after_finalize_err(&Self::DEFAULT_INPUT); + self.double_reset_ok(&Self::DEFAULT_INPUT); + self.immediate_finalize(); + Self::verify_same_input_ok(&Self::DEFAULT_INPUT); + Self::verify_diff_input_err(&Self::DEFAULT_INPUT); + } + + #[cfg(not(feature = "safe_api"))] + /// Used when quickcheck is not available to generate input. + /// Default input data is used instead. Without std. + pub fn run_all_tests(&self) { + self.consistency(&Self::DEFAULT_INPUT); + self.consistency(&[0u8; 0]); + self.produces_same_state(&Self::DEFAULT_INPUT); + self.incremental_and_one_shot(&Self::DEFAULT_INPUT); + self.double_finalize_with_reset_no_update_ok(&Self::DEFAULT_INPUT); + self.double_finalize_with_reset_ok(&Self::DEFAULT_INPUT); + self.double_finalize_err(&Self::DEFAULT_INPUT); + self.update_after_finalize_with_reset_ok(&Self::DEFAULT_INPUT); + self.update_after_finalize_err(&Self::DEFAULT_INPUT); + self.double_reset_ok(&Self::DEFAULT_INPUT); + self.immediate_finalize(); + Self::verify_same_input_ok(&Self::DEFAULT_INPUT); + Self::verify_diff_input_err(&Self::DEFAULT_INPUT); + } + + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Testing different usage combinations of new(), update(), + /// finalize() and reset() produce the same output. + /// + /// It is important to ensure this is also called with empty + /// `data`. + fn consistency(&self, data: &[u8]) { + // new(), update(), finalize() + let mut state_1 = self._initial_context.clone(); + state_1.update(data).unwrap(); + let res_1 = state_1.finalize().unwrap(); + + // new(), reset(), update(), finalize() + let mut state_2 = self._initial_context.clone(); + state_2.reset().unwrap(); + state_2.update(data).unwrap(); + let res_2 = state_2.finalize().unwrap(); + + // new(), update(), reset(), update(), finalize() + let mut state_3 = self._initial_context.clone(); + state_3.update(data).unwrap(); + state_3.reset().unwrap(); + state_3.update(data).unwrap(); + let res_3 = state_3.finalize().unwrap(); + + // new(), update(), finalize(), reset(), update(), finalize() + let mut state_4 = self._initial_context.clone(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset().unwrap(); + state_4.update(data).unwrap(); + let res_4 = state_4.finalize().unwrap(); + + assert_eq!(res_1, res_2); + assert_eq!(res_2, res_3); + assert_eq!(res_3, res_4); + + // Tests for the assumption that returning Ok() on empty update() calls + // with streaming APIs, gives the correct result. This is done by testing + // the reasoning that if update() is empty, returns Ok(), it is the same as + // calling new() -> finalize(). i.e not calling update() at all. + if data.is_empty() { + // new(), finalize() + let mut state_5 = self._initial_context.clone(); + let res_5 = state_5.finalize().unwrap(); + + // new(), reset(), finalize() + let mut state_6 = self._initial_context.clone(); + state_6.reset().unwrap(); + let res_6 = state_6.finalize().unwrap(); + + // new(), update(), reset(), finalize() + let mut state_7 = self._initial_context.clone(); + state_7.update(b"WRONG DATA").unwrap(); + state_7.reset().unwrap(); + let res_7 = state_7.finalize().unwrap(); + + assert_eq!(res_4, res_5); + assert_eq!(res_5, res_6); + assert_eq!(res_6, res_7); + } + } + + /// Related bug: https://github.com/orion-rs/orion/issues/46 + /// Testing different usage combinations of new(), update(), + /// finalize() and reset() produce the same output. + fn produces_same_state(&self, data: &[u8]) { + // new() + let state_1 = self._initial_context.clone(); + + // new(), reset() + let mut state_2 = self._initial_context.clone(); + state_2.reset().unwrap(); + + // new(), update(), reset() + let mut state_3 = self._initial_context.clone(); + state_3.update(data).unwrap(); + state_3.reset().unwrap(); + + // new(), update(), finalize(), reset() + let mut state_4 = self._initial_context.clone(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset().unwrap(); + + T::compare_states(&state_1, &state_2); + T::compare_states(&state_2, &state_3); + T::compare_states(&state_3, &state_4); + } + + #[cfg(feature = "safe_api")] + /// Test for issues when incrementally processing data + /// with leftover in the internal buffer. It should produce + /// the same results as processing the same data in a single pass. + fn incremental_processing_with_leftover(&self) { + for len in 0..self.blocksize * 4 { + let data = vec![0u8; len]; + let mut state = self._initial_context.clone(); + let mut other_data: Vec = Vec::new(); + + other_data.extend_from_slice(&data); + state.update(&data).unwrap(); + + if data.len() > self.blocksize { + other_data.extend_from_slice(b""); + state.update(b"").unwrap(); + } + if data.len() > self.blocksize * 2 { + other_data.extend_from_slice(b"Extra"); + state.update(b"Extra").unwrap(); + } + if data.len() > self.blocksize * 3 { + other_data.extend_from_slice(&[0u8; 256]); + state.update(&[0u8; 256]).unwrap(); + } + + let streaming_result = state.finalize().unwrap(); + let one_shot_result = T::one_shot(&other_data).unwrap(); + + assert_eq!(streaming_result, one_shot_result); + } + } + + /// new(), update(), finalize() == one_shot() + fn incremental_and_one_shot(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let streaming_result = state.finalize().unwrap(); + let one_shot_result = T::one_shot(data).unwrap(); + + assert_eq!(streaming_result, one_shot_result); + } + + /// new(), update(), finalize(), reset(), finalize(): OK + fn double_finalize_with_reset_no_update_ok(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset().unwrap(); + assert!(state.finalize().is_ok()); + } + + /// new(), update(), finalize(), reset(), update(), finalize(): OK + fn double_finalize_with_reset_ok(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset().unwrap(); + state.update(data).unwrap(); + assert!(state.finalize().is_ok()); + } + + /// new(), update(), finalize(), finalize(): ERR + fn double_finalize_err(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.finalize().is_err()); + } + + /// new(), update(), finalize(), reset(), update(): OK + fn update_after_finalize_with_reset_ok(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset().unwrap(); + assert!(state.update(data).is_ok()); + } + + /// Related bug: https://github.com/orion-rs/orion/issues/28 + /// new(), update(), finalize(), update(): ERR + fn update_after_finalize_err(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.update(data).is_err()); + } + + /// reset(), reset(): OK + fn double_reset_ok(&self, data: &[u8]) { + let mut state = self._initial_context.clone(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset().unwrap(); + assert!(state.reset().is_ok()); + } + + /// new(), finalize(): OK + fn immediate_finalize(&self) { + let mut state = self._initial_context.clone(); + assert!(state.finalize().is_ok()); + } + + /// Using the same input should always result in a successful verification. + pub fn verify_same_input_ok(data: &[u8]) { + let expected = T::one_shot(data).unwrap(); + assert!(T::verify_result(&expected, data).is_ok()); + } + + /// Using different input should result in a failed verification. + pub fn verify_diff_input_err(data: &[u8]) { + let expected = T::one_shot(data).unwrap(); + assert!(T::verify_result(&expected, b"Bad data").is_err()); + } +} diff --git a/vendor/orion/src/test_framework/mod.rs b/vendor/orion/src/test_framework/mod.rs new file mode 100644 index 0000000..afe9361 --- /dev/null +++ b/vendor/orion/src/test_framework/mod.rs @@ -0,0 +1,30 @@ +// MIT License + +// Copyright (c) 2019-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// Tests for a streaming context that offers incremental processing. +pub mod incremental_interface; + +/// Tests for AEAD interfaces such as `chacha20poly1305`. +pub mod aead_interface; + +/// Tests for stream ciphers such as `chacha20`. +pub mod streamcipher_interface; diff --git a/vendor/orion/src/test_framework/streamcipher_interface.rs b/vendor/orion/src/test_framework/streamcipher_interface.rs new file mode 100644 index 0000000..d46e202 --- /dev/null +++ b/vendor/orion/src/test_framework/streamcipher_interface.rs @@ -0,0 +1,305 @@ +// MIT License + +// Copyright (c) 2019-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#![allow(non_snake_case)] + +#[cfg(feature = "safe_api")] +use crate::errors::UnknownCryptoError; + +#[cfg(test)] +#[cfg(feature = "safe_api")] +pub trait TestingRandom { + /// Randomly generate self. + fn gen() -> Self; +} + +#[cfg(feature = "safe_api")] +/// Test runner for stream ciphers. +pub fn StreamCipherTestRunner( + encryptor: Encryptor, + decryptor: Decryptor, + key: Key, + nonce: Nonce, + counter: u32, + input: &[u8], + expected_ct: Option<&[u8]>, +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + if !input.is_empty() { + encrypt_decrypt_out_length(&encryptor, &decryptor, &key, &nonce, input); + encrypt_decrypt_equals_expected( + &encryptor, + &decryptor, + &key, + &nonce, + counter, + input, + expected_ct, + ); + } + + encrypt_decrypt_input_empty(&encryptor, &decryptor, &key, &nonce); + initial_counter_overflow_err(&encryptor, &decryptor, &key, &nonce); + initial_counter_max_ok(&encryptor, &decryptor, &key, &nonce); +} + +#[cfg(feature = "safe_api")] +/// Given a input length `a` find out how many times +/// the initial counter on encrypt()/decrypt() would +/// increase. +fn counter_increase_times(a: f32) -> u32 { + // Otherwise a overflowing subtraction would happen + if a <= 64f32 { + return 0; + } + + let check_with_floor = (a / 64f32).floor(); + let actual = a / 64f32; + + assert!(actual >= check_with_floor); + // Subtract one because the first 64 in length + // the counter does not increase + if actual > check_with_floor { + (actual.ceil() as u32) - 1 + } else { + (actual as u32) - 1 + } +} + +#[cfg(feature = "safe_api")] +fn return_if_counter_will_overflow( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, + counter: u32, + input: &[u8], +) -> bool +where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + assert!(!input.is_empty()); + let mut dst_out = vec![0u8; input.len()]; + + // Overflow will occur and the operation should fail + let enc_res = encryptor(key, nonce, counter, &[0u8; 0], &mut dst_out).is_err(); + let dec_res = decryptor(key, nonce, counter, &[0u8; 0], &mut dst_out).is_err(); + + enc_res && dec_res +} + +#[cfg(feature = "safe_api")] +fn encrypt_decrypt_input_empty( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut dst_out = [0u8; 64]; + assert!(encryptor(key, nonce, 0, &[0u8; 0], &mut dst_out).is_err()); + assert!(decryptor(key, nonce, 0, &[0u8; 0], &mut dst_out).is_err()); +} + +#[cfg(feature = "safe_api")] +fn encrypt_decrypt_out_length( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, + input: &[u8], +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + assert!(!input.is_empty()); + + let mut dst_out_empty = vec![0u8; 0]; + assert!(encryptor(key, nonce, 0, input, &mut dst_out_empty).is_err()); + assert!(decryptor(key, nonce, 0, input, &mut dst_out_empty).is_err()); + + let mut dst_out_less = vec![0u8; input.len() - 1]; + assert!(encryptor(key, nonce, 0, input, &mut dst_out_less).is_err()); + assert!(decryptor(key, nonce, 0, input, &mut dst_out_less).is_err()); + + let mut dst_out_exact = vec![0u8; input.len()]; + assert!(encryptor(key, nonce, 0, input, &mut dst_out_exact).is_ok()); + assert!(decryptor(key, nonce, 0, input, &mut dst_out_exact).is_ok()); + + let mut dst_out_greater = vec![0u8; input.len() + 1]; + assert!(encryptor(key, nonce, 0, input, &mut dst_out_greater).is_ok()); + assert!(decryptor(key, nonce, 0, input, &mut dst_out_greater).is_ok()); +} + +#[cfg(feature = "safe_api")] +/// Test that encrypting and decrypting produces expected plaintext/ciphertext. +fn encrypt_decrypt_equals_expected( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, + counter: u32, + input: &[u8], + expected_ct: Option<&[u8]>, +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + assert!(!input.is_empty()); + + // Check if the counter would overflow. If yes, ensure that both encryptor and + // decryptor returned errors. + if counter_increase_times(input.len() as f32) + .checked_add(counter) + .is_none() + { + assert!(return_if_counter_will_overflow( + encryptor, decryptor, key, nonce, counter, input + )); + + return; + } + + let mut dst_out_ct = vec![0u8; input.len()]; + encryptor(key, nonce, counter, input, &mut dst_out_ct).unwrap(); + if let Some(expected_result) = expected_ct { + assert_eq!(expected_result, &dst_out_ct[..]); + } + + let mut dst_out_pt = vec![0u8; input.len()]; + decryptor(key, nonce, counter, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_eq!(input, &dst_out_pt[..]); + if let Some(expected_result) = expected_ct { + decryptor(key, nonce, counter, expected_result, &mut dst_out_pt).unwrap(); + assert_eq!(input, &dst_out_pt[..]); + } +} + +#[cfg(feature = "safe_api")] +/// Test that a initial counter will not overflow the internal. +fn initial_counter_overflow_err( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut dst_out = [0u8; 128]; + assert!(encryptor( + key, + nonce, + u32::MAX, + &[0u8; 65], // CHACHA_BLOCKSIZE + 1 one to trigger internal block counter addition. + &mut dst_out + ) + .is_err()); + assert!(decryptor( + key, + nonce, + u32::MAX, + &[0u8; 65], // CHACHA_BLOCKSIZE + 1 one to trigger internal block counter addition. + &mut dst_out + ) + .is_err()); +} + +#[cfg(feature = "safe_api")] +/// Test that processing one block does not fail on the largest possible initial block counter. +fn initial_counter_max_ok( + encryptor: &Encryptor, + decryptor: &Decryptor, + key: &Key, + nonce: &Nonce, +) where + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut dst_out = [0u8; 64]; + assert!(encryptor( + key, + nonce, + u32::MAX, + &[0u8; 64], // Only needs to process one keystream + &mut dst_out + ) + .is_ok()); + assert!(decryptor( + key, + nonce, + u32::MAX, + &[0u8; 64], // Only needs to process one keystream + &mut dst_out + ) + .is_ok()); +} + +#[cfg(test)] +#[cfg(feature = "safe_api")] +/// Test that encrypting using different secret-key/nonce/initial-counter combinations yields different +/// ciphertexts. +pub fn test_diff_params_diff_output( + encryptor: &Encryptor, + decryptor: &Decryptor, +) where + Key: TestingRandom + PartialEq, + Nonce: TestingRandom + PartialEq, + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let input = &[0u8; 16]; + + let sk1 = Key::gen(); + let sk2 = Key::gen(); + assert!(sk1 != sk2); + + let n1 = Nonce::gen(); + let n2 = Nonce::gen(); + assert!(n1 != n2); + + let c1 = 0u32; + let c2 = 1u32; + + let mut dst_out_ct = vec![0u8; input.len()]; + let mut dst_out_pt = vec![0u8; input.len()]; + + // Different secret key + encryptor(&sk1, &n1, c1, input, &mut dst_out_ct).unwrap(); + decryptor(&sk2, &n1, c1, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_ne!(&dst_out_pt[..], input); + + // Different nonce + encryptor(&sk1, &n1, c1, input, &mut dst_out_ct).unwrap(); + decryptor(&sk1, &n2, c1, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_ne!(&dst_out_pt[..], input); + + // Different initial counter + encryptor(&sk1, &n1, c1, input, &mut dst_out_ct).unwrap(); + decryptor(&sk1, &n1, c2, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_ne!(&dst_out_pt[..], input); +} diff --git a/vendor/orion/src/typedefs.rs b/vendor/orion/src/typedefs.rs new file mode 100644 index 0000000..6471c30 --- /dev/null +++ b/vendor/orion/src/typedefs.rs @@ -0,0 +1,993 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// +/// Trait implementation macros + +#[cfg(feature = "safe_api")] +/// Macro that implements the `Default` trait using a CSPRNG. +macro_rules! impl_default_trait (($name:ident, $size:expr) => ( + impl Default for $name { + #[cfg(feature = "safe_api")] + /// Randomly generate using a CSPRNG with recommended size. Not available in `no_std` context. + fn default() -> $name { + let mut value = vec![0u8; $size]; + crate::util::secure_rand_bytes(&mut value).unwrap(); + + $name { value, original_length: $size } + } + } +)); + +/// Macro that implements the `PartialEq` trait on a object called `$name` that +/// provides a given $bytes_function to return a slice. This `PartialEq` will +/// execute in constant-time. +/// +/// This also provides an empty `Eq` implementation. +macro_rules! impl_ct_partialeq_trait (($name:ident, $bytes_function:ident) => ( + impl PartialEq<$name> for $name { + fn eq(&self, other: &$name) -> bool { + use subtle::ConstantTimeEq; + + (self.$bytes_function() + .ct_eq(other.$bytes_function())).into() + } + } + + impl Eq for $name {} + + impl PartialEq<&[u8]> for $name { + fn eq(&self, other: &&[u8]) -> bool { + use subtle::ConstantTimeEq; + + (self.$bytes_function() + .ct_eq(*other)).into() + } + } +)); + +/// Macro that implements the `Debug` trait on a object called `$name`. +/// This `Debug` will omit any fields of object `$name` to avoid them being +/// written to logs. +macro_rules! impl_omitted_debug_trait (($name:ident) => ( + impl core::fmt::Debug for $name { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} {{***OMITTED***}}", stringify!($name)) + } + } +)); + +/// Macro that implements the `Debug` trait on a object called `$name`. +macro_rules! impl_normal_debug_trait (($name:ident) => ( + impl core::fmt::Debug for $name { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} {:?}", stringify!($name), &self.value[..]) + } + } +)); + +/// Macro that implements the `serde::{Serialize, Deserialize}` traits. +#[cfg(feature = "serde")] +macro_rules! impl_serde_traits (($name:ident, $bytes_function:ident) => ( + + #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] + /// This type tries to serialize as a `&[u8]` would. Note that the serialized + /// type likely does not have the same protections that Orion provides, such + /// as constant-time operations. A good rule of thumb is to only serialize + /// these types for storage. Don't operate on the serialized types. + impl serde::Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let bytes: &[u8] = &self.$bytes_function(); + bytes.serialize(serializer) + } + } + + #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] + /// This type tries to deserialize as a `Vec` would. If it succeeds, the digest + /// will be built using `Self::from_slice`. + /// + /// Note that **this allocates** once to store the referenced bytes on the heap. + impl<'de> serde::Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let bytes = Vec::::deserialize(deserializer)?; + std::convert::TryFrom::try_from(bytes.as_slice()).map_err(serde::de::Error::custom) + } + } +)); + +/// Macro that implements the `Drop` trait on a object called `$name` which has +/// a field `value`. This `Drop` will zero out the field `value` when the +/// objects destructor is called. +macro_rules! impl_drop_trait (($name:ident) => ( + impl Drop for $name { + fn drop(&mut self) { + use zeroize::Zeroize; + self.value.iter_mut().zeroize(); + } + } +)); + +/// Macro that implements the `AsRef<[u8]>` trait on a object called `$name` +/// which has fields `value` and `original_length`. This will return the inner +/// `value` as a byte slice, and should only be implemented on public types +/// which don't have any special protections. +macro_rules! impl_asref_trait (($name:ident) => ( + impl AsRef<[u8]> for $name { + #[inline] + fn as_ref(&self) -> &[u8] { + self.value[..self.original_length].as_ref() + } + } +)); + +/// Macro that implements the `From<[T]>` trait on a object called `$name` +/// which has fields `value` and `original_length`. It implements From +/// based on `$size` and this macro should, in most cases, only be used for +/// types which have a fixed-length. +macro_rules! impl_from_trait (($name:ident, $size:expr) => ( + impl From<[u8; $size]> for $name { + #[inline] + /// Make an object from a byte array. + fn from(bytes: [u8; $size]) -> $name { + $name { + value: bytes, + original_length: $size + } + } + } +)); + +/// Macro that implements `TryFrom<&[u8]>` on an object called `$name` that +/// implements the method `from_slice`. +macro_rules! impl_try_from_trait (($name:ident) => ( + /// Delegates to `from_slice` implementation + impl TryFrom<&[u8]> for $name { + type Error = UnknownCryptoError; + fn try_from(slice: &[u8]) -> Result { + Self::from_slice(slice) + } + } +)); + +/// +/// Function implementation macros + +/// Macro to implement a `from_slice()` function. Returns `UnknownCryptoError` +/// if the slice length is not accepted. +/// $lower_bound and $upper_bound is the inclusive range of which a slice might +/// be acceptable in length. If a slice may only be a fixed size, $lower_bound +/// and $upper_bound should be the same. The `value` field will always be allocated with +/// a size of $upper_bound. +macro_rules! func_from_slice (($name:ident, $lower_bound:expr, $upper_bound:expr) => ( + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> { + + let slice_len = slice.len(); + + if !($lower_bound..=$upper_bound).contains(&slice_len) { + return Err(UnknownCryptoError); + } + + let mut value = [0u8; $upper_bound]; + value[..slice_len].copy_from_slice(slice); + + Ok($name { value, original_length: slice_len }) + } +)); + +#[cfg(feature = "safe_api")] +/// Macro to implement a `from_slice()` function. Returns `UnknownCryptoError` +/// if the slice is empty. +macro_rules! func_from_slice_variable_size (($name:ident) => ( + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + #[cfg(feature = "safe_api")] + /// Construct from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> { + // See issue on `isize` limit: https://github.com/orion-rs/orion/issues/130 + if slice.is_empty() || slice.len() > (isize::MAX as usize) { + return Err(UnknownCryptoError); + } + + Ok($name { value: Vec::from(slice), original_length: slice.len() }) + } +)); + +/// Macro to implement a `unprotected_as_bytes()` function for objects that +/// implement extra protections. Typically used on objects that implement +/// `Drop`. +macro_rules! func_unprotected_as_bytes (() => ( + #[inline] + /// Return the object as byte slice. __**Warning**__: Should not be used unless strictly + /// needed. This __**breaks protections**__ that the type implements. + pub fn unprotected_as_bytes(&self) -> &[u8] { + self.value[..self.original_length].as_ref() + } +)); + +/// Macro to implement a `len()` function which will return the original_length +/// field. Meaning the amount of bytes the newtype was created from. +macro_rules! func_len (() => ( + #[inline] + /// Return the length of the object. + pub fn len(&self) -> usize { + self.original_length + } +)); + +/// Macro to implement an `is_empty()` function which will return `true` if `self.len() == 0`. +macro_rules! func_is_empty (() => ( + #[inline] + /// Return `true` if this object does not hold any data, `false` otherwise. + /// + /// __NOTE__: This method should always return `false`, since there shouldn't be a way + /// to create an empty instance of this object. + pub fn is_empty(&self) -> bool { + self.original_length == 0 + } +)); + +/// Macro to implement a `generate()` function for objects that benefit from +/// having a CSPRNG available to generate data of a fixed length $gen_length. +macro_rules! func_generate (($name:ident, $upper_bound:expr, $gen_length:expr) => ( + #[cfg(feature = "safe_api")] + /// Randomly generate using a CSPRNG. Not available in `no_std` context. + pub fn generate() -> $name { + let mut value = [0u8; $upper_bound]; + // This will not panic on size, unless the newtype has been defined with $upper_bound + // or $gen_length equal to 0. + crate::util::secure_rand_bytes(&mut value[..$gen_length]).unwrap(); + + $name { value, original_length: $gen_length } + } +)); + +#[cfg(feature = "safe_api")] +/// Macro to implement a `generate()` function for objects that benefit from +/// having a CSPRNG available to generate data of a variable length. +macro_rules! func_generate_variable_size (($name:ident) => ( + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + #[cfg(feature = "safe_api")] + /// Randomly generate using a CSPRNG. Not available in `no_std` context. + pub fn generate(length: usize) -> Result<$name, UnknownCryptoError> { + // See issue on `isize` limit: https://github.com/orion-rs/orion/issues/130 + if length < 1 || length > (isize::MAX as usize) { + return Err(UnknownCryptoError); + } + + let mut value = vec![0u8; length]; + // This cannot panic on size input due to above length checks. + crate::util::secure_rand_bytes(&mut value).unwrap(); + + Ok($name { value, original_length: length }) + } +)); + +/// +/// Test implementation macros + +#[cfg(test)] +#[cfg(feature = "serde")] +macro_rules! test_serde_impls (($name:ident, $gen_length:expr) => ( + #[test] + fn test_serde_serialized_equivalence_to_bytes_fn() { + let bytes = &[38u8; $gen_length][..]; + let orion_type = $name::from_slice(bytes).unwrap(); + let serialized_from_bytes = serde_json::to_value(bytes).unwrap(); + let serialized_from_orion_type = serde_json::to_value(&orion_type).unwrap(); + assert_eq!(serialized_from_bytes, serialized_from_orion_type); + } + + #[test] + fn test_serde_deserialized_equivalence_to_bytes_fn() { + let bytes = &[38u8; $gen_length][..]; + let serialized_from_bytes = serde_json::to_value(bytes).unwrap(); + let orion_type: $name = serde_json::from_value(serialized_from_bytes).unwrap(); + assert_eq!(orion_type, bytes); + } +)); + +#[cfg(test)] +macro_rules! test_bound_parameters (($name:ident, $lower_bound:expr, $upper_bound:expr, $gen_length:expr) => ( + #[test] + fn test_bound_params() { + // $lower_bound: + assert!($lower_bound <= $upper_bound); + // $upper_bound: + // $gen_length: + assert!($gen_length <= $upper_bound); + assert!($gen_length >= $lower_bound); + } +)); + +#[cfg(test)] +macro_rules! test_partial_eq (($name:ident, $upper_bound:expr) => ( + #[test] + fn test_partial_eq() { + // PartialEq + assert_eq!($name::from_slice(&[0u8; $upper_bound]).unwrap(), $name::from_slice(&[0u8; $upper_bound]).unwrap()); + assert_ne!($name::from_slice(&[0u8; $upper_bound]).unwrap(), $name::from_slice(&[1u8; $upper_bound]).unwrap()); + // PartialEq<&[u8]> + assert_eq!($name::from_slice(&[0u8; $upper_bound]).unwrap(), [0u8; $upper_bound].as_ref()); + assert_ne!($name::from_slice(&[0u8; $upper_bound]).unwrap(), [1u8; $upper_bound].as_ref()); + } +)); + +#[cfg(test)] +macro_rules! test_from_slice (($name:ident, $lower_bound:expr, $upper_bound:expr) => ( + #[test] + fn test_from_slice() { + assert!($name::from_slice(&[0u8; $upper_bound]).is_ok()); + assert!($name::from_slice(&[0u8; $lower_bound]).is_ok()); + + assert!($name::from_slice(&[0u8; $upper_bound + 1]).is_err()); + assert!($name::from_slice(&[0u8; $lower_bound - 1]).is_err()); + assert!($name::from_slice(&[0u8; 0]).is_err()); + + // Test non-fixed-length definitions + if $upper_bound != $lower_bound { + assert!($name::from_slice(&[0u8; $upper_bound - 1]).is_ok()); + assert!($name::from_slice(&[0u8; $lower_bound + 1]).is_ok()); + } + } +)); + +#[cfg(test)] +macro_rules! test_as_bytes_and_get_length (($name:ident, $lower_bound:expr, $upper_bound:expr, $bytes_function:ident) => ( + #[test] + fn test_as_bytes() { + let test_upper = $name::from_slice(&[0u8; $upper_bound]).unwrap(); + let test_lower = $name::from_slice(&[0u8; $lower_bound]).unwrap(); + + assert_eq!(test_upper.$bytes_function().len(), test_upper.len()); + assert_eq!(test_upper.len(), $upper_bound); + + assert_eq!(test_lower.$bytes_function().len(), test_lower.len()); + assert_eq!(test_lower.len(), $lower_bound); + + assert_eq!(test_upper.is_empty(), false); + assert_eq!(test_lower.is_empty(), false); + + // Test non-fixed-length definitions + if $lower_bound != $upper_bound { + let test_upper = $name::from_slice(&[0u8; $upper_bound - 1]).unwrap(); + let test_lower = $name::from_slice(&[0u8; $lower_bound + 1]).unwrap(); + + assert_eq!(test_upper.$bytes_function().len(), test_upper.len()); + assert_eq!(test_upper.len(), $upper_bound - 1); + + assert_eq!(test_lower.$bytes_function().len(), test_lower.len()); + assert_eq!(test_lower.len(), $lower_bound + 1); + + assert_eq!(test_upper.is_empty(), false); + assert_eq!(test_lower.is_empty(), false); + } + } +)); + +#[cfg(test)] +#[cfg(feature = "safe_api")] +macro_rules! test_generate (($name:ident, $gen_length:expr) => ( + #[test] + #[cfg(feature = "safe_api")] + fn test_generate() { + let test_zero = $name::from_slice(&[0u8; $gen_length]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate(); + assert_ne!(test_zero, test_rand); + // A random generated one should always be $gen_length in length. + assert_eq!(test_rand.len(), $gen_length); + } +)); + +#[cfg(test)] +#[cfg(feature = "safe_api")] +macro_rules! test_omitted_debug (($name:ident, $upper_bound:expr) => ( + #[test] + #[cfg(feature = "safe_api")] + // format! is only available with std + fn test_omitted_debug() { + let secret = format!("{:?}", [0u8; $upper_bound].as_ref()); + let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $upper_bound]).unwrap()); + assert_eq!(test_debug_contents.contains(&secret), false); + } +)); + +#[cfg(test)] +#[cfg(feature = "safe_api")] +macro_rules! test_normal_debug (($name:ident, $upper_bound:expr) => ( + #[test] + #[cfg(feature = "safe_api")] + // format! is only available with std + fn test_normal_debug() { + let public = format!("{:?}", [0u8; $upper_bound].as_ref()); + let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $upper_bound]).unwrap()); + assert_eq!(test_debug_contents.contains(&public), true); + } +)); + +#[cfg(test)] +#[cfg(feature = "safe_api")] +macro_rules! test_from_slice_variable (($name:ident) => ( + #[test] + #[cfg(feature = "safe_api")] + fn test_from_slice_variable() { + assert!($name::from_slice(&[0u8; 512]).is_ok()); + assert!($name::from_slice(&[0u8; 256]).is_ok()); + assert!($name::from_slice(&[0u8; 1]).is_ok()); + assert!($name::from_slice(&[0u8; 0]).is_err()); + } +)); + +#[cfg(test)] +#[cfg(feature = "safe_api")] +macro_rules! test_generate_variable (($name:ident) => ( + #[test] + #[cfg(feature = "safe_api")] + fn test_generate_variable() { + assert!($name::generate(0).is_err()); + assert!($name::generate((isize::MAX as usize) + 1).is_err()); + assert!($name::generate(1).is_ok()); + assert!($name::generate(64).is_ok()); + + let test_zero = $name::from_slice(&[0u8; 128]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate(128).unwrap(); + assert_ne!(test_zero, test_rand); + assert_eq!(test_rand.len(), 128); + } +)); + +/// +/// Newtype implementation macros + +/// Macro to construct a type containing sensitive data, using a fixed-size +/// array. +/// +/// - $name: The name for the newtype. +/// +/// - $test_module_name: The name for the newtype's testing module (usually +/// "test_$name"). +/// +/// - $lower_bound/$upper_bound: An inclusive range that defines what length a +/// secret value might be. Used to validate length of `slice` in from_slice(). +/// $upper_bound also defines the `value` field array allocation size. +/// +/// - $gen_length: The amount of data to be randomly generated when using +/// generate(). +macro_rules! construct_secret_key { + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr)) => ( + $(#[$meta])* + /// + /// # Security: + /// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections + /// that the type implements. + /// + /// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted + /// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait + /// is implemented in such a way that the comparison happens in constant time. Thus, users should + /// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. + /// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. + /// ```rust + /// # #[cfg(feature = "safe_api")] { + /// use orion::hazardous::stream::chacha20::SecretKey; + /// + /// // Initialize a secret key with random bytes. + /// let secret_key = SecretKey::generate(); + /// + /// // Secure, constant-time comparison with a byte slice + /// assert_ne!(secret_key, &[0; 32][..]); + /// + /// // Secure, constant-time comparison with another SecretKey + /// assert_ne!(secret_key, SecretKey::generate()); + /// # } + /// # Ok::<(), orion::errors::UnknownCryptoError>(()) + /// ``` + pub struct $name { + value: [u8; $upper_bound], + original_length: usize, + } + + impl_omitted_debug_trait!($name); + impl_drop_trait!($name); + impl_ct_partialeq_trait!($name, unprotected_as_bytes); + + impl $name { + func_from_slice!($name, $lower_bound, $upper_bound); + func_unprotected_as_bytes!(); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + + test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound); + test_from_slice!($name, $lower_bound, $upper_bound); + test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, unprotected_as_bytes); + test_partial_eq!($name, $upper_bound); + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_omitted_debug!($name, $upper_bound); + } + } + ); + + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr, $gen_length:expr)) => ( + $(#[$meta])* + /// + /// # Security: + /// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections + /// that the type implements. + /// + /// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted + /// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait + /// is implemented in such a way that the comparison happens in constant time. Thus, users should + /// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. + /// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. + /// ```rust + /// # #[cfg(feature = "safe_api")] { + /// use orion::hazardous::stream::chacha20::SecretKey; + /// + /// // Initialize a secret key with random bytes. + /// let secret_key = SecretKey::generate(); + /// + /// // Secure, constant-time comparison with a byte slice + /// assert_ne!(secret_key, &[0; 32][..]); + /// + /// // Secure, constant-time comparison with another SecretKey + /// assert_ne!(secret_key, SecretKey::generate()); + /// # } + /// # Ok::<(), orion::errors::UnknownCryptoError>(()) + /// ``` + pub struct $name { + value: [u8; $upper_bound], + original_length: usize, + } + + impl_omitted_debug_trait!($name); + impl_drop_trait!($name); + impl_ct_partialeq_trait!($name, unprotected_as_bytes); + + impl $name { + func_from_slice!($name, $lower_bound, $upper_bound); + func_unprotected_as_bytes!(); + func_generate!($name, $upper_bound, $gen_length); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + + test_bound_parameters!($name, $lower_bound, $upper_bound, $gen_length); + test_from_slice!($name, $lower_bound, $upper_bound); + test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, unprotected_as_bytes); + test_partial_eq!($name, $upper_bound); + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_generate!($name, $gen_length); + test_omitted_debug!($name, $upper_bound); + } + } + ); +} + +/// Macro to construct a public type containing non-sensitive data, using a +/// fixed-size array. +/// +/// - $name: The name for the newtype. +/// +/// - $test_module_name: The name for the newtype's testing module (usually +/// "test_$name"). +/// +/// - $lower_bound/$upper_bound: An inclusive range that defines what length a +/// public value might be. Used to validate length of `slice` in from_slice(). +/// $upper_bound also defines the `value` field array allocation size. +/// +/// - $gen_length: The amount of data to be randomly generated when using +/// generate(). If not supplied, the public newtype will not have a +/// `generate()` function available. +macro_rules! construct_public { + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr)) => ( + #[derive(Clone, Copy)] + $(#[$meta])* + /// + pub struct $name { + pub(crate) value: [u8; $upper_bound], + original_length: usize, + } + + impl_ct_partialeq_trait!($name, as_ref); + impl_normal_debug_trait!($name); + impl_try_from_trait!($name); + impl_asref_trait!($name); + + #[cfg(feature = "serde")] + impl_serde_traits!($name, as_ref); + + impl $name { + func_from_slice!($name, $lower_bound, $upper_bound); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + // Replace $gen_length with $upper_bound since this doesn't have + // generate() function. + test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound); + test_from_slice!($name, $lower_bound, $upper_bound); + test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, as_ref); + test_partial_eq!($name, $upper_bound); + + #[cfg(feature = "serde")] + test_serde_impls!($name, $upper_bound); + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_normal_debug!($name, $upper_bound); + } + } + ); + + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr, $gen_length:expr)) => ( + #[derive(Clone, Copy)] + $(#[$meta])* + /// + pub struct $name { + pub(crate) value: [u8; $upper_bound], + original_length: usize, + } + + impl_ct_partialeq_trait!($name, as_ref); + impl_normal_debug_trait!($name); + impl_try_from_trait!($name); + impl_asref_trait!($name); + + #[cfg(feature = "serde")] + impl_serde_traits!($name, as_ref); + + impl $name { + func_from_slice!($name, $lower_bound, $upper_bound); + func_generate!($name, $upper_bound, $gen_length); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound); + test_from_slice!($name, $lower_bound, $upper_bound); + test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, as_ref); + test_partial_eq!($name, $upper_bound); + + #[cfg(feature = "serde")] + test_serde_impls!($name, $upper_bound); + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_normal_debug!($name, $upper_bound); + test_generate!($name, $gen_length); + } + } + ); +} + +/// Macro to construct a tag type that MACs return. +macro_rules! construct_tag { + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr)) => ( + #[derive(Clone)] + $(#[$meta])* + /// + /// # Security: + /// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections + /// that the type implements. + /// + /// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted + /// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait + /// is implemented in such a way that the comparison happens in constant time. Thus, users should + /// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. + /// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. + /// ```rust + /// use orion::hazardous::mac::hmac::sha512::Tag; + /// + /// // Initialize an arbitrary, 64-byte tag. + /// let tag = Tag::from_slice(&[1; 64])?; + /// + /// // Secure, constant-time comparison with a byte slice + /// assert_eq!(tag, &[1; 64][..]); + /// + /// // Secure, constant-time comparison with another Tag + /// assert_eq!(tag, Tag::from_slice(&[1; 64])?); + /// # Ok::<(), orion::errors::UnknownCryptoError>(()) + /// ``` + pub struct $name { + value: [u8; $upper_bound], + original_length: usize, + } + + impl_omitted_debug_trait!($name); + impl_drop_trait!($name); + impl_ct_partialeq_trait!($name, unprotected_as_bytes); + impl_try_from_trait!($name); + + #[cfg(feature = "serde")] + impl_serde_traits!($name, unprotected_as_bytes); + + impl $name { + func_from_slice!($name, $lower_bound, $upper_bound); + func_unprotected_as_bytes!(); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + // Replace $gen_length with $upper_bound since a tag doesn't have + // generate() function. + test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound); + test_from_slice!($name, $lower_bound, $upper_bound); + test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, unprotected_as_bytes); + test_partial_eq!($name, $upper_bound); + + #[cfg(feature = "serde")] + test_serde_impls!($name, $upper_bound); + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_omitted_debug!($name, $upper_bound); + } + } + ); +} + +/// Macro to construct a secret key used for HMAC. This pre-pads the given key +/// to the required length specified by the HMAC specifications. +macro_rules! construct_hmac_key { + ($(#[$meta:meta])* + ($name:ident, $sha2:ident, $sha2_outsize:expr, $test_module_name:ident, $size:expr)) => ( + $(#[$meta])* + /// + /// # Security: + /// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections + /// that the type implements. + /// + /// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted + /// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait + /// is implemented in such a way that the comparison happens in constant time. Thus, users should + /// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. + /// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. + /// ```rust + /// # #[cfg(feature = "safe_api")] { + /// use orion::hazardous::mac::hmac::sha512::SecretKey; + /// + /// // Initialize a secret key with random bytes. + /// let secret_key = SecretKey::generate(); + /// + /// // Secure, constant-time comparison with a byte slice + /// assert_ne!(secret_key, &[0; 32][..]); + /// + /// // Secure, constant-time comparison with another SecretKey + /// assert_ne!(secret_key, SecretKey::generate()); + /// # } + /// # Ok::<(), orion::errors::UnknownCryptoError>(()) + /// ``` + pub struct $name { + value: [u8; $size], + original_length: usize, + } + + impl_omitted_debug_trait!($name); + impl_drop_trait!($name); + impl_ct_partialeq_trait!($name, unprotected_as_bytes); + + impl $name { + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Construct from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> { + let mut secret_key = [0u8; $size]; + + let slice_len = slice.len(); + + if slice_len > $size { + secret_key[..$sha2_outsize].copy_from_slice(&$sha2::digest(slice)?.as_ref()); + } else { + secret_key[..slice_len].copy_from_slice(slice); + } + + Ok($name { value: secret_key, original_length: $size }) + } + + func_unprotected_as_bytes!(); + func_generate!($name, $size, $size); + func_len!(); + func_is_empty!(); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + test_as_bytes_and_get_length!($name, $size, $size, unprotected_as_bytes); + test_partial_eq!($name, $size); + + #[test] + fn test_key_size() { + assert!($name::from_slice(&[0u8; $size]).is_ok()); + assert!($name::from_slice(&[0u8; $size - $size]).is_ok()); + assert!($name::from_slice(&[0u8; $size + 1]).is_ok()); + } + + #[cfg(test)] + #[cfg(feature = "safe_api")] + mod tests_with_std { + use super::*; + + test_generate!($name, $size); + test_omitted_debug!($name, $size); + } + } + ); +} + +#[cfg(feature = "safe_api")] +/// Macro to construct a type containing sensitive data which is stored on the +/// heap. +macro_rules! construct_secret_key_variable_size { + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $default_size:expr)) => ( + #[cfg(feature = "safe_api")] + $(#[$meta])* + /// + /// # Security: + /// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections + /// that the type implements. + /// + /// - The trait `PartialEq<&'_ [u8]>` is implemented for this type so that users are not tempted + /// to call `unprotected_as_bytes` to compare this sensitive value to a byte slice. The trait + /// is implemented in such a way that the comparison happens in constant time. Thus, users should + /// prefer `SecretType == &[u8]` over `SecretType.unprotected_as_bytes() == &[u8]`. + /// Examples are shown below. The examples apply to any type that implements `PartialEq<&'_ [u8]>`. + /// ```rust + /// # #[cfg(feature = "safe_api")] { + /// use orion::pwhash::Password; + /// + /// // Initialize a password with 32 random bytes. + /// let password = Password::generate(32)?; + /// + /// // Secure, constant-time comparison with a byte slice + /// assert_ne!(password, &[0; 32][..]); + /// + /// // Secure, constant-time comparison with another Password + /// assert_ne!(password, Password::generate(32)?); + /// # } + /// # Ok::<(), orion::errors::UnknownCryptoError>(()) + /// ``` + pub struct $name { + pub(crate) value: Vec, + original_length: usize, + } + + impl_omitted_debug_trait!($name); + impl_drop_trait!($name); + impl_ct_partialeq_trait!($name, unprotected_as_bytes); + impl_default_trait!($name, $default_size); + + impl $name { + func_from_slice_variable_size!($name); + func_unprotected_as_bytes!(); + func_len!(); + func_is_empty!(); + func_generate_variable_size!($name); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + + test_from_slice_variable!($name); + test_as_bytes_and_get_length!($name, 1, $default_size + 1, unprotected_as_bytes); + test_generate_variable!($name); + test_omitted_debug!($name, $default_size); + test_partial_eq!($name, $default_size); + } + ); +} + +#[cfg(feature = "safe_api")] +/// Macro to construct a type containing non-sensitive which is stored on the +/// heap. +macro_rules! construct_salt_variable_size { + ($(#[$meta:meta])* + ($name:ident, $test_module_name:ident, $default_size:expr)) => ( + #[cfg(feature = "safe_api")] + $(#[$meta])* + /// + pub struct $name { + value: Vec, + original_length: usize, + } + + impl_normal_debug_trait!($name); + impl_default_trait!($name, $default_size); + impl_ct_partialeq_trait!($name, as_ref); + impl_asref_trait!($name); + impl_try_from_trait!($name); + + #[cfg(feature = "serde")] + impl_serde_traits!($name, as_ref); + + impl $name { + func_from_slice_variable_size!($name); + func_len!(); + func_is_empty!(); + func_generate_variable_size!($name); + } + + #[cfg(test)] + mod $test_module_name { + use super::*; + + test_from_slice_variable!($name); + test_as_bytes_and_get_length!($name, 1, $default_size + 1, as_ref); + test_generate_variable!($name); + test_partial_eq!($name, $default_size); + test_normal_debug!($name, $default_size); + + #[cfg(feature = "serde")] + test_serde_impls!($name, $default_size); + } + ); +} diff --git a/vendor/orion/src/util/endianness.rs b/vendor/orion/src/util/endianness.rs new file mode 100644 index 0000000..b8d0d92 --- /dev/null +++ b/vendor/orion/src/util/endianness.rs @@ -0,0 +1,371 @@ +// MIT License + +// Copyright (c) 2019-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use core::convert::TryInto; +use core::mem; + +macro_rules! impl_store_into { + ($type_alias:ty, $conv_function:ident, $func_name:ident) => { + /// Store bytes in `src` in `dst`. + pub fn $func_name(src: &[$type_alias], dst: &mut [u8]) { + let type_alias_len = mem::size_of::<$type_alias>(); + // The length of src must be evenly divisible with the length of dst, + // making sure .chunks_exact() leaves no remainder. + assert_eq!(mem::size_of_val(src), dst.len()); + + for (src_elem, dst_chunk) in src.iter().zip(dst.chunks_exact_mut(type_alias_len)) { + dst_chunk.copy_from_slice(&src_elem.$conv_function()); + } + } + }; +} + +macro_rules! impl_load_into { + ($type_alias:ty, $type_alias_expr:ident, $conv_function:ident, $func_name:ident) => { + /// Load bytes in `src` into `dst`. + pub fn $func_name(src: &[u8], dst: &mut [$type_alias]) { + let type_alias_len = mem::size_of::<$type_alias>(); + // The length of src must be evenly divisible with the length of dst, + // making sure .chunks_exact() leaves no remainder. + assert_eq!(mem::size_of_val(dst), src.len()); + + for (src_chunk, dst_elem) in src.chunks_exact(type_alias_len).zip(dst.iter_mut()) { + // The above assert and this debug assert should prove that .unwrap() + // cannot panic using TryInto. + debug_assert_eq!(src_chunk.len(), type_alias_len); + *dst_elem = $type_alias_expr::$conv_function(src_chunk.try_into().unwrap()); + } + } + }; +} + +macro_rules! impl_load { + ($type_alias:ty, $type_alias_expr:ident, $conv_function:ident, $func_name:ident) => { + /// Convert bytes in `src` to a given primitive. + pub fn $func_name(src: &[u8]) -> $type_alias { + // Satisfying this assert should prove that using TryInto + // cannot panic. + assert_eq!(mem::size_of::<$type_alias>(), src.len()); + $type_alias_expr::$conv_function(src.try_into().unwrap()) + } + }; +} + +impl_load!(u32, u32, from_le_bytes, load_u32_le); + +#[cfg(test)] +impl_load_into!(u32, u32, from_le_bytes, load_u32_into_le); + +impl_load_into!(u64, u64, from_le_bytes, load_u64_into_le); + +impl_store_into!(u32, to_le_bytes, store_u32_into_le); + +#[cfg(any(feature = "safe_api", feature = "alloc", test))] +impl_store_into!(u64, to_le_bytes, store_u64_into_le); + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + macro_rules! test_empty_src_panic { + ($test_name:ident, $src_val:expr, $dst_val:expr, $func_to_test:expr) => { + #[test] + #[should_panic] + fn $test_name() { + let mut dst_load = $dst_val; + $func_to_test($src_val, &mut dst_load); + } + }; + } + + macro_rules! test_dst_length_panic { + ($test_name:ident, $src_val:expr, $dst_val:expr, $func_to_test:expr) => { + #[test] + #[should_panic] + fn $test_name() { + let mut dst_load = $dst_val; + $func_to_test($src_val, &mut dst_load); + } + }; + } + + macro_rules! test_dst_length_ok { + ($test_name:ident, $src_val:expr, $dst_val:expr, $func_to_test:expr) => { + #[test] + fn $test_name() { + let mut dst_load = $dst_val; + $func_to_test($src_val, &mut dst_load); + } + }; + } + + test_empty_src_panic! {test_panic_empty_load_u32_le, &[0u8; 0], [0u32; 4], load_u32_into_le} + test_empty_src_panic! {test_panic_empty_load_u64_le, &[0u8; 0], [0u64; 4], load_u64_into_le} + + test_empty_src_panic! {test_panic_empty_store_u32_le, &[0u32; 0], [0u8; 24], store_u32_into_le} + test_empty_src_panic! {test_panic_empty_store_u64_le, &[0u64; 0], [0u8; 24], store_u64_into_le} + + // -1 too low + test_dst_length_panic! {test_dst_length_load_u32_le_low, &[0u8; 64], [0u32; 15], load_u32_into_le} + test_dst_length_panic! {test_dst_length_load_u64_le_low, &[0u8; 64], [0u64; 7], load_u64_into_le} + + test_dst_length_panic! {test_dst_length_store_u32_le_low, &[0u32; 15], [0u8; 64], store_u32_into_le} + test_dst_length_panic! {test_dst_length_store_u64_le_low, &[0u64; 7], [0u8; 64], store_u64_into_le} + + // +1 too high + test_dst_length_panic! {test_dst_length_load_u32_le_high, &[0u8; 64], [0u32; 17], load_u32_into_le} + test_dst_length_panic! {test_dst_length_load_u64_le_high, &[0u8; 64], [0u64; 9], load_u64_into_le} + + test_dst_length_panic! {test_dst_length_store_u32_le_high, &[0u32; 17], [0u8; 64], store_u32_into_le} + test_dst_length_panic! {test_dst_length_store_u64_le_high, &[0u64; 9], [0u8; 64], store_u64_into_le} + + // Ok + test_dst_length_ok! {test_dst_length_load_u32_le_ok, &[0u8; 64], [0u32; 16], load_u32_into_le} + test_dst_length_ok! {test_dst_length_load_u64_le_ok, &[0u8; 64], [0u64; 8], load_u64_into_le} + + test_dst_length_ok! {test_dst_length_store_u32_le_ok, &[0u32; 16], [0u8; 64], store_u32_into_le} + test_dst_length_ok! {test_dst_length_store_u64_le_ok, &[0u64; 8], [0u8; 64], store_u64_into_le} + + #[test] + #[should_panic] + fn test_load_single_src_high() { + load_u32_le(&[0u8; 5]); + } + + #[test] + #[should_panic] + fn test_load_single_src_low() { + load_u32_le(&[0u8; 3]); + } + + #[test] + fn test_load_single_src_ok() { + load_u32_le(&[0u8; 4]); + } + + #[test] + fn test_results_store_and_load_u32_into_le() { + let input_0: [u32; 2] = [777190791, 1465409568]; + let input_1: [u32; 4] = [3418616323, 2289579672, 172726903, 1048927929]; + let input_2: [u32; 6] = [ + 84693101, 443297962, 3962861724, 3081916164, 4167874952, 3982893227, + ]; + let input_3: [u32; 8] = [ + 2761719494, 242571916, 3097304063, 3924274282, 1553851098, 3673278295, 3531531406, + 2347852690, + ]; + + let expected_0: [u8; 8] = [135, 253, 82, 46, 32, 96, 88, 87]; + let expected_1: [u8; 16] = [ + 3, 242, 195, 203, 152, 54, 120, 136, 119, 154, 75, 10, 185, 94, 133, 62, + ]; + let expected_2: [u8; 24] = [ + 109, 80, 12, 5, 170, 48, 108, 26, 156, 120, 52, 236, 4, 79, 178, 183, 136, 185, 108, + 248, 171, 32, 102, 237, + ]; + let expected_3: [u8; 32] = [ + 198, 126, 156, 164, 140, 90, 117, 14, 255, 27, 157, 184, 106, 172, 231, 233, 218, 226, + 157, 92, 87, 199, 241, 218, 142, 228, 126, 210, 146, 99, 241, 139, + ]; + + let mut actual_bytes_0 = [0u8; 8]; + let mut actual_bytes_1 = [0u8; 16]; + let mut actual_bytes_2 = [0u8; 24]; + let mut actual_bytes_3 = [0u8; 32]; + + store_u32_into_le(&input_0, &mut actual_bytes_0); + store_u32_into_le(&input_1, &mut actual_bytes_1); + store_u32_into_le(&input_2, &mut actual_bytes_2); + store_u32_into_le(&input_3, &mut actual_bytes_3); + + assert_eq!(actual_bytes_0, expected_0); + assert_eq!(actual_bytes_1, expected_1); + assert_eq!(actual_bytes_2, expected_2); + assert_eq!(actual_bytes_3, expected_3); + + let mut actual_nums_0 = [0u32; 2]; + let mut actual_nums_1 = [0u32; 4]; + let mut actual_nums_2 = [0u32; 6]; + let mut actual_nums_3 = [0u32; 8]; + + load_u32_into_le(&actual_bytes_0, &mut actual_nums_0); + load_u32_into_le(&actual_bytes_1, &mut actual_nums_1); + load_u32_into_le(&actual_bytes_2, &mut actual_nums_2); + load_u32_into_le(&actual_bytes_3, &mut actual_nums_3); + + assert_eq!(actual_nums_0, input_0); + assert_eq!(actual_nums_1, input_1); + assert_eq!(actual_nums_2, input_2); + assert_eq!(actual_nums_3, input_3); + } + + #[test] + fn test_results_store_and_load_u64_into_le() { + let input_0: [u64; 2] = [3449173576222258260, 2574723713182514848]; + let input_1: [u64; 4] = [ + 18418572897904167042, + 8576666536239673655, + 11410394363908906546, + 7465319841649779999, + ]; + let input_2: [u64; 6] = [ + 9356732802025012686, + 185726711773006573, + 11478604380402216982, + 11229612629557120299, + 2892361689551487626, + 11014300370630005317, + ]; + let input_3: [u64; 8] = [ + 9519534723912119720, + 6001603601558183532, + 8164850737304360888, + 571607234094878696, + 4752095875230140457, + 13190954815003641110, + 16657196750477544576, + 10329042493888204415, + ]; + + let expected_0: [u8; 16] = [ + 84, 52, 100, 211, 23, 237, 221, 47, 160, 190, 4, 95, 147, 65, 187, 35, + ]; + let expected_1: [u8; 32] = [ + 130, 8, 55, 1, 119, 234, 155, 255, 55, 177, 139, 9, 198, 112, 6, 119, 50, 222, 232, 23, + 56, 221, 89, 158, 31, 229, 53, 208, 215, 36, 154, 103, + ]; + let expected_2: [u8; 48] = [ + 206, 61, 242, 202, 232, 202, 217, 129, 237, 10, 136, 216, 117, 213, 147, 2, 22, 240, + 29, 35, 222, 49, 76, 159, 43, 13, 254, 133, 48, 153, 215, 155, 138, 66, 170, 219, 161, + 187, 35, 40, 69, 210, 218, 176, 212, 167, 218, 152, + ]; + let expected_3: [u8; 64] = [ + 168, 53, 199, 13, 101, 46, 28, 132, 108, 158, 148, 129, 173, 250, 73, 83, 184, 215, 28, + 129, 124, 96, 79, 113, 232, 207, 68, 59, 192, 193, 238, 7, 41, 8, 177, 85, 5, 214, 242, + 65, 22, 69, 133, 252, 131, 175, 15, 183, 128, 76, 1, 226, 48, 64, 42, 231, 127, 14, 31, + 46, 108, 33, 88, 143, + ]; + + let mut actual_bytes_0 = [0u8; 16]; + let mut actual_bytes_1 = [0u8; 32]; + let mut actual_bytes_2 = [0u8; 48]; + let mut actual_bytes_3 = [0u8; 64]; + + store_u64_into_le(&input_0, &mut actual_bytes_0); + store_u64_into_le(&input_1, &mut actual_bytes_1); + store_u64_into_le(&input_2, &mut actual_bytes_2); + store_u64_into_le(&input_3, &mut actual_bytes_3); + + assert_eq!(actual_bytes_0, expected_0); + assert_eq!(actual_bytes_1, expected_1); + assert_eq!(actual_bytes_2.as_ref(), expected_2.as_ref()); + assert_eq!(actual_bytes_3.as_ref(), expected_3.as_ref()); + + let mut actual_nums_0 = [0u64; 2]; + let mut actual_nums_1 = [0u64; 4]; + let mut actual_nums_2 = [0u64; 6]; + let mut actual_nums_3 = [0u64; 8]; + + load_u64_into_le(&actual_bytes_0, &mut actual_nums_0); + load_u64_into_le(&actual_bytes_1, &mut actual_nums_1); + load_u64_into_le(&actual_bytes_2, &mut actual_nums_2); + load_u64_into_le(&actual_bytes_3, &mut actual_nums_3); + + assert_eq!(actual_nums_0, input_0); + assert_eq!(actual_nums_1, input_1); + assert_eq!(actual_nums_2, input_2); + assert_eq!(actual_nums_3, input_3); + } + + #[test] + fn test_results_load_u32() { + let input_0: [u8; 4] = [203, 12, 195, 63]; + let expected_0: u32 = 1069747403; + + assert_eq!(load_u32_le(&input_0), expected_0); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Load and store should not change the result. + fn prop_load_store_u32_le(src: Vec) -> bool { + if !src.is_empty() && src.len() % 4 == 0 { + let mut dst_load = vec![0u32; src.len() / 4]; + load_u32_into_le(&src[..], &mut dst_load); + // Test that loading a single also is working correctly + dst_load[0] = load_u32_le(&src[..4]); + let mut dst_store = src.clone(); + store_u32_into_le(&dst_load[..], &mut dst_store); + + dst_store == src + } else { + // Otherwise above functions panic. + true + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Load and store should not change the result. + fn prop_load_store_u64_le(src: Vec) -> bool { + if !src.is_empty() && src.len() % 8 == 0 { + let mut dst_load = vec![0u64; src.len() / 8]; + load_u64_into_le(&src[..], &mut dst_load); + let mut dst_store = src.clone(); + store_u64_into_le(&dst_load[..], &mut dst_store); + + dst_store == src + } else { + // Otherwise above functions panic. + true + } + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Store and load should not change the result. + fn prop_store_load_u32_le(src: Vec) -> bool { + let mut dst_store = vec![0u8; src.len() * 4]; + store_u32_into_le(&src[..], &mut dst_store); + let mut dst_load = src.clone(); + load_u32_into_le(&dst_store[..], &mut dst_load); + if dst_store.len() >= 4 { + // Test that loading a single also is working correctly + dst_load[0] = load_u32_le(&dst_store[..4]); + } + + dst_load == src + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + /// Store and load should not change the result. + fn prop_store_load_u64_le(src: Vec) -> bool { + let mut dst_store = vec![0u8; src.len() * 8]; + store_u64_into_le(&src[..], &mut dst_store); + let mut dst_load = src.clone(); + load_u64_into_le(&dst_store[..], &mut dst_load); + + dst_load == src + } +} diff --git a/vendor/orion/src/util/mod.rs b/vendor/orion/src/util/mod.rs new file mode 100644 index 0000000..27fc291 --- /dev/null +++ b/vendor/orion/src/util/mod.rs @@ -0,0 +1,186 @@ +// MIT License + +// Copyright (c) 2018-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use crate::errors; +use subtle::ConstantTimeEq; + +/// xor_slices!(src, destination): XOR $src into $destination slice. +/// Uses iter() and .zip(), so it short-circuits on the slice that has +/// the smallest length. +macro_rules! xor_slices { + ($src:expr, $destination:expr) => { + for (inplace, _src_elem) in $destination.iter_mut().zip($src.iter()) { + *inplace ^= _src_elem; + } + }; +} + +pub(crate) mod endianness; +pub(crate) mod u32x4; +pub(crate) mod u64x4; + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +#[cfg(feature = "safe_api")] +/// Generate random bytes using a CSPRNG. Not available in `no_std` context. +/// +/// # About: +/// This function can be used to generate cryptographic keys, salts or other +/// values that rely on strong randomness. Please note that most keys and other +/// types used throughout Orion, implement their own `generate()` function and +/// it is strongly preferred to use those, compared to [`secure_rand_bytes()`]. +/// +/// This uses [`getrandom`]. +/// +/// # Parameters: +/// - `dst`: Destination buffer for the randomly generated bytes. The amount of +/// bytes to be generated is +/// implied by the length of `dst`. +/// +/// # Errors: +/// An error will be returned if: +/// - `dst` is empty. +/// +/// # Panics: +/// A panic will occur if: +/// - Failure to generate random bytes securely. +/// - The platform is not supported by [`getrandom`]. +/// +/// # Example: +/// ```rust +/// use orion::util; +/// +/// let mut salt = [0u8; 64]; +/// util::secure_rand_bytes(&mut salt)?; +/// # Ok::<(), orion::errors::UnknownCryptoError>(()) +/// ``` +/// [`getrandom`]: https://github.com/rust-random/getrandom +pub fn secure_rand_bytes(dst: &mut [u8]) -> Result<(), errors::UnknownCryptoError> { + if dst.is_empty() { + return Err(errors::UnknownCryptoError); + } + + getrandom::getrandom(dst).unwrap(); + + Ok(()) +} + +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +/// Compare two equal length slices in constant time. +/// +/// # About: +/// Compare two equal length slices, in constant time, using the +/// [subtle](https://github.com/dalek-cryptography/subtle) crate. +/// +/// # Parameters: +/// - `a`: The first slice used in the comparison. +/// - `b`: The second slice used in the comparison. +/// +/// # Errors: +/// An error will be returned if: +/// - `a` and `b` do not have the same length. +/// - `a` is not equal to `b`. +/// +/// # Example: +/// ```rust +/// # #[cfg(feature = "safe_api")] { +/// use orion::util; +/// +/// let mut rnd_bytes = [0u8; 64]; +/// assert!(util::secure_cmp(&rnd_bytes, &[0u8; 64]).is_ok()); +/// +/// util::secure_rand_bytes(&mut rnd_bytes)?; +/// assert!(util::secure_cmp(&rnd_bytes, &[0u8; 64]).is_err()); +/// # } +/// # Ok::<(), orion::errors::UnknownCryptoError>(()) +/// ``` +pub fn secure_cmp(a: &[u8], b: &[u8]) -> Result<(), errors::UnknownCryptoError> { + if a.ct_eq(b).into() { + Ok(()) + } else { + Err(errors::UnknownCryptoError) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "safe_api")] + #[test] + fn rand_key_len_ok() { + let mut dst = [0u8; 64]; + secure_rand_bytes(&mut dst).unwrap(); + } + + #[cfg(feature = "safe_api")] + #[test] + fn rand_key_len_error() { + let mut dst = [0u8; 0]; + assert!(secure_rand_bytes(&mut dst).is_err()); + + let err = secure_rand_bytes(&mut dst).unwrap_err(); + assert_eq!(err, errors::UnknownCryptoError); + } + + #[test] + fn test_ct_eq_ok() { + let buf_1 = [0x06; 10]; + let buf_2 = [0x06; 10]; + + assert!(secure_cmp(&buf_1, &buf_2).is_ok()); + assert!(secure_cmp(&buf_2, &buf_1).is_ok()); + } + + #[test] + fn test_ct_eq_diff_len() { + let buf_1 = [0x06; 10]; + let buf_2 = [0x06; 5]; + + assert!(secure_cmp(&buf_1, &buf_2).is_err()); + assert!(secure_cmp(&buf_2, &buf_1).is_err()); + } + + #[test] + fn test_ct_ne() { + let buf_1 = [0x06; 10]; + let buf_2 = [0x76; 10]; + + assert!(secure_cmp(&buf_1, &buf_2).is_err()); + assert!(secure_cmp(&buf_2, &buf_1).is_err()); + } + + #[test] + fn test_ct_ne_reg() { + assert!(secure_cmp(&[0], &[0, 1]).is_err()); + assert!(secure_cmp(&[0, 1], &[0]).is_err()); + } + + #[quickcheck] + #[cfg(feature = "safe_api")] + fn prop_secure_cmp(a: Vec, b: Vec) -> bool { + if a == b { + secure_cmp(&a, &b).is_ok() + } else { + secure_cmp(&a, &b).is_err() + } + } +} diff --git a/vendor/orion/src/util/u32x4.rs b/vendor/orion/src/util/u32x4.rs new file mode 100644 index 0000000..c1ff1bc --- /dev/null +++ b/vendor/orion/src/util/u32x4.rs @@ -0,0 +1,98 @@ +// MIT License + +// Copyright (c) 2019-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#[derive(Clone, Copy)] +pub(crate) struct U32x4( + pub(crate) u32, + pub(crate) u32, + pub(crate) u32, + pub(crate) u32, +); + +impl core::ops::BitXor for U32x4 { + type Output = Self; + + #[must_use] + fn bitxor(self, _rhs: Self) -> Self::Output { + Self( + self.0 ^ _rhs.0, + self.1 ^ _rhs.1, + self.2 ^ _rhs.2, + self.3 ^ _rhs.3, + ) + } +} + +impl zeroize::Zeroize for U32x4 { + fn zeroize(&mut self) { + self.0.zeroize(); + self.1.zeroize(); + self.2.zeroize(); + self.3.zeroize(); + } +} + +impl U32x4 { + #[must_use] + pub(crate) const fn wrapping_add(self, _rhs: Self) -> Self { + Self( + self.0.wrapping_add(_rhs.0), + self.1.wrapping_add(_rhs.1), + self.2.wrapping_add(_rhs.2), + self.3.wrapping_add(_rhs.3), + ) + } + + #[must_use] + pub(crate) const fn shl_1(self) -> Self { + Self(self.1, self.2, self.3, self.0) + } + + #[must_use] + pub(crate) const fn shl_2(self) -> Self { + Self(self.2, self.3, self.0, self.1) + } + + #[must_use] + pub(crate) const fn shl_3(self) -> Self { + Self(self.3, self.0, self.1, self.2) + } + + #[must_use] + pub(crate) const fn rotate_left(self, n: u32) -> Self { + Self( + self.0.rotate_left(n), + self.1.rotate_left(n), + self.2.rotate_left(n), + self.3.rotate_left(n), + ) + } + + pub(crate) fn store_into_le(&self, slice_in: &mut [u8]) { + debug_assert_eq!(slice_in.len(), core::mem::size_of::() * 4); + let mut iter = slice_in.chunks_exact_mut(core::mem::size_of::()); + iter.next().unwrap().copy_from_slice(&self.0.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.1.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.2.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.3.to_le_bytes()); + } +} diff --git a/vendor/orion/src/util/u64x4.rs b/vendor/orion/src/util/u64x4.rs new file mode 100644 index 0000000..bc181c4 --- /dev/null +++ b/vendor/orion/src/util/u64x4.rs @@ -0,0 +1,114 @@ +// MIT License + +// Copyright (c) 2019-2023 The orion Developers + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#[derive(Clone, Copy, Default)] +pub(crate) struct U64x4( + pub(crate) u64, + pub(crate) u64, + pub(crate) u64, + pub(crate) u64, +); + +impl core::ops::BitXor for U64x4 { + type Output = Self; + + #[must_use] + fn bitxor(self, _rhs: Self) -> Self::Output { + Self( + self.0 ^ _rhs.0, + self.1 ^ _rhs.1, + self.2 ^ _rhs.2, + self.3 ^ _rhs.3, + ) + } +} + +impl core::ops::BitXorAssign for U64x4 { + fn bitxor_assign(&mut self, _rhs: Self) { + self.0 ^= _rhs.0; + self.1 ^= _rhs.1; + self.2 ^= _rhs.2; + self.3 ^= _rhs.3; + } +} + +impl zeroize::Zeroize for U64x4 { + fn zeroize(&mut self) { + self.0.zeroize(); + self.1.zeroize(); + self.2.zeroize(); + self.3.zeroize(); + } +} + +#[cfg(test)] +impl PartialEq for U64x4 { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 && self.1 == other.1 && self.2 == other.2 && self.3 == other.3 + } +} + +impl U64x4 { + #[must_use] + pub(crate) const fn wrapping_add(self, _rhs: Self) -> Self { + Self( + self.0.wrapping_add(_rhs.0), + self.1.wrapping_add(_rhs.1), + self.2.wrapping_add(_rhs.2), + self.3.wrapping_add(_rhs.3), + ) + } + + #[must_use] + pub(crate) const fn shl_1(self) -> Self { + Self(self.1, self.2, self.3, self.0) + } + + #[must_use] + pub(crate) const fn shl_2(self) -> Self { + Self(self.2, self.3, self.0, self.1) + } + + #[must_use] + pub(crate) const fn shl_3(self) -> Self { + Self(self.3, self.0, self.1, self.2) + } + + #[must_use] + pub(crate) const fn rotate_right(self, n: u32) -> Self { + Self( + self.0.rotate_right(n), + self.1.rotate_right(n), + self.2.rotate_right(n), + self.3.rotate_right(n), + ) + } + + pub(crate) fn store_into_le(self, slice_in: &mut [u8]) { + debug_assert_eq!(slice_in.len(), core::mem::size_of::() * 4); + let mut iter = slice_in.chunks_exact_mut(core::mem::size_of::()); + iter.next().unwrap().copy_from_slice(&self.0.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.1.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.2.to_le_bytes()); + iter.next().unwrap().copy_from_slice(&self.3.to_le_bytes()); + } +} -- cgit v1.2.3