diff --git a/.github/workflows/ansi-x963-kdf.yml b/.github/workflows/ansi-x963-kdf.yml new file mode 100644 index 0000000..4b7c0fc --- /dev/null +++ b/.github/workflows/ansi-x963-kdf.yml @@ -0,0 +1,53 @@ +name: ansi-x963-kdf + +on: + pull_request: + paths: + - "ansi-x963-kdf/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: ansi-x963-kdf + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.81.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + targets: ${{ matrix.target }} + - run: cargo build --no-default-features --target ${{ matrix.target }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.81.0 # MSRV + - stable + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + - run: cargo check --all-features + - run: cargo test --no-default-features + - run: cargo test + - run: cargo test --all-features diff --git a/.github/workflows/workspace.yml b/.github/workflows/workspace.yml index b2fb78f..5005c97 100644 --- a/.github/workflows/workspace.yml +++ b/.github/workflows/workspace.yml @@ -17,7 +17,7 @@ jobs: - uses: RustCrypto/actions/cargo-cache@master - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.75.0 + toolchain: 1.81.0 components: clippy - run: cargo clippy --all -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index 7c002ca..858fd1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ansi-x963-kdf" +version = "0.1.0" +dependencies = [ + "digest", + "hex-literal", + "sha2", +] + [[package]] name = "blobby" version = "0.3.1" @@ -82,7 +91,7 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hkdf" -version = "0.13.0-pre.3" +version = "0.13.0-pre.4" dependencies = [ "blobby", "hex-literal", diff --git a/Cargo.toml b/Cargo.toml index 119ab97..00e0ff2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ resolver = "2" members = [ "hkdf", - "concat-kdf", + "concat-kdf", "ansi-x963-kdf", ] [profile.dev] diff --git a/README.md b/README.md index 1df026d..321a7b1 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Collection of [Key Derivation Functions][KDF] (KDF) written in pure Rust. |--------------|----------------|:---------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------:|:-----------------------:| | [HKDF] | [`hkdf`] | [![crates.io](https://img.shields.io/crates/v/hkdf.svg)](https://crates.io/crates/hkdf) | [![Documentation](https://docs.rs/hkdf/badge.svg)](https://docs.rs/hkdf) | ![MSRV 1.41][msrv-1.72] | | [Concat-KDF] | [`concat-kdf`] | [![crates.io](https://img.shields.io/crates/v/concat-kdf.svg)](https://crates.io/crates/concat-kdf) | [![Documentation](https://docs.rs/concat-kdf/badge.svg)](https://docs.rs/concat-kdf) | ![MSRV 1.56][msrv-1.72] | +| [ANSI-X9.63-KDF] | [`ansi-x963-kdf`] | [![crates.io](https://img.shields.io/crates/v/ansi-x963-kdf.svg)](https://crates.io/crates/ansi-x963-kdf) | [![Documentation](https://docs.rs/ansi-x963-kdf/badge.svg)](https://docs.rs/ansi-x963-kdf) | ![MSRV 1.81][msrv-1.81] | *NOTE: for password-based KDFs (e.g. Argon2, PBKDF2, scrypt), please see [RustCrypto/password-hashes]* @@ -43,10 +44,12 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [`hkdf`]: ./hkdf [`concat-kdf`]: ./concat-kdf +[`ansi-x963-kdf`]: ./ansi-x963-kdf [//]: # (algorithms) [KDF]: https://en.wikipedia.org/wiki/Key_derivation_function [HKDF]: https://en.wikipedia.org/wiki/HKDF [Concat-KDF]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-56ar.pdf +[ANSI-X9.63-KDF]: https://www.secg.org/sec1-v2.pdf [RustCrypto/password-hashes]: https://github.com/RustCrypto/password-hashes diff --git a/ansi-x963-kdf/CHANGELOG.md b/ansi-x963-kdf/CHANGELOG.md new file mode 100644 index 0000000..2e1cc9c --- /dev/null +++ b/ansi-x963-kdf/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 (2024-04-10) +- Initial release diff --git a/ansi-x963-kdf/Cargo.toml b/ansi-x963-kdf/Cargo.toml new file mode 100644 index 0000000..739e91a --- /dev/null +++ b/ansi-x963-kdf/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "ansi-x963-kdf" +version = "0.1.0" +description = "ANSI X9.63 Key Derivation Function (ANSI-X9.63-KDF)" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +edition = "2021" +documentation = "https://docs.rs/ansi-x963-kdf" +repository = "https://github.com/RustCrypto/KDFs" +keywords = ["crypto", "ansi-x963-kdf", "KDF", "SEC1"] +categories = ["cryptography", "no-std"] +rust-version = "1.81" + +[dependencies] +digest = "=0.11.0-pre.9" + +[dev-dependencies] +hex-literal = "0.4" +sha2 = { version = "=0.11.0-pre.4", default-features = false } + +[features] +std = [] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + diff --git a/ansi-x963-kdf/README.md b/ansi-x963-kdf/README.md new file mode 100644 index 0000000..eda8bd3 --- /dev/null +++ b/ansi-x963-kdf/README.md @@ -0,0 +1,59 @@ +# RustCrypto: ANSI X9.63 Key Derivation Function (ANSI-X9.63-KDF) + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Pure Rust implementation of the ANSI X9.63 Key Derivation Function (ANSI-X9.63-KDF) generic over hash function. +This function is described in the section 3.6.1 of [SEC 1: Elliptic Curve Cryptography](http://www.secg.org/sec1-v2.pdf). + +# Usage + +The most common way to use ANSI-X9.63-KDF is as follows: you generate a shared secret with other party (e.g. via Diffie-Hellman algorithm) +and use key derivation function to derive a shared key. + +```rust +let mut key = [0u8; 32]; +ansi_x963_kdf::derive_key_into::(b"shared-secret", b"other-info", &mut key).unwrap(); +``` + +## Minimum Supported Rust Version + +Rust **1.72** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + +* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +* [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[crate-image]: https://img.shields.io/crates/v/ansi-x963-kdf.svg +[crate-link]: https://crates.io/crates/ansi-x963-kdf +[docs-image]: https://docs.rs/ansi-x963-kdf/badge.svg +[docs-link]: https://docs.rs/ansi-x963-kdf/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.72+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260043-KDFs +[build-image]: https://github.com/RustCrypto/KDFs/workflows/ansi-x963-kdf/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/KDFs/actions?query=workflow:ansi-x963-kdf diff --git a/ansi-x963-kdf/src/lib.rs b/ansi-x963-kdf/src/lib.rs new file mode 100644 index 0000000..1a80357 --- /dev/null +++ b/ansi-x963-kdf/src/lib.rs @@ -0,0 +1,123 @@ +//! An implementation of ANSI-X9.63 KDF Key Derivation Function. +//! +//! This function is described in the section 3.6.1 of [SEC 1: Elliptic Curve Cryptography][1]. +//! +//! # Usage +//! +//! The most common way to use ANSI-X9.63 KDF is as follows: you generate a shared secret +//! with other party (e.g. via Diffie-Hellman algorithm) and use key derivation function +//! to derive a shared key. +//! +//! ```rust +//! let mut key = [0u8; 32]; +//! ansi_x963_kdf::derive_key_into::(b"shared-secret", b"other-info", &mut key).unwrap(); +//! ``` +//! +//! [1]: https://www.secg.org/sec1-v2.pdf + +#![no_std] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + +use core::fmt; +use digest::{array::typenum::Unsigned, Digest, FixedOutputReset}; + +#[cfg(feature = "std")] +extern crate std; + +/// ANSI-X9.63 KDF errors. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Error { + /// The length of the secret is zero. + NoSecret, + /// The length of the output is zero. + NoOutput, + /// The length of the input is too big + InputOverflow, + /// The length of the output is too big. + CounterOverflow, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str(match self { + Error::NoSecret => "Buffer for secret has zero length.", + Error::NoOutput => "Buffer for key has zero length.", + Error::InputOverflow => "Input length is to big.", + Error::CounterOverflow => "Requested key length is to big.", + }) + } +} + +impl ::core::error::Error for Error {} + +/// Derives `key` in-place from `secret` and `shared_info`. +/// ```rust +/// use hex_literal::hex; +/// let mut key = [0u8; 42]; +/// ansi_x963_kdf::derive_key_into::(b"top-secret", b"info", &mut key).unwrap(); +/// assert_eq!(key, hex!("85397c03b3894cdc12e7e042698d040f449dbff97a86d0a4dd2d0a4409b8d969e01e57091cf170dfd977")); +/// ``` +pub fn derive_key_into(secret: &[u8], shared_info: &[u8], key: &mut [u8]) -> Result<(), Error> +where + D: Digest + FixedOutputReset, +{ + if secret.is_empty() { + return Err(Error::NoSecret); + } + + if key.is_empty() { + return Err(Error::NoOutput); + } + + // 1. Check that |Z| + |SharedInfo| + 4 < hashmaxlen + // where "hashmaxlen denote the maximum length in octets of messages that can be hashed using Hash". + // N.B.: `D::OutputSize::U64 * (u32::MAX as u64)`` is currently used as an approximation of hashmaxlen. + if secret.len() as u64 + shared_info.len() as u64 + 4 >= D::OutputSize::U64 * (u32::MAX as u64) + { + return Err(Error::InputOverflow); + } + + // 2. Check that keydatalen < hashlen × (2^32 − 1) + if key.len() as u64 >= D::OutputSize::U64 * (u32::MAX as u64) { + return Err(Error::CounterOverflow); + } + + let mut digest = D::new(); + + // 3. Initiate a 4 octet, big-endian octet string Counter as 00000001 + let mut counter: u32 = 1; + + // 4. For i = 1 to keydatalen/hashlen, + for chunk in key.chunks_mut(D::OutputSize::USIZE) { + // 4.1 Compute Ki = Hash(Z ‖ Counter ‖ [SharedInfo]) using the selected hash function + Digest::update(&mut digest, secret); + Digest::update(&mut digest, counter.to_be_bytes()); + Digest::update(&mut digest, shared_info); + chunk.copy_from_slice(&digest.finalize_reset()[..chunk.len()]); + // 4.2. Increment Counter + counter += 1; + } + + Ok(()) +} + +/// Derives and returns `length` bytes key from `secret` and `shared_info`. +/// ```rust +/// use hex_literal::hex; +/// let key = ansi_x963_kdf::derive_key::(b"top-secret", b"info", 42).unwrap(); +/// assert_eq!(key, hex!("85397c03b3894cdc12e7e042698d040f449dbff97a86d0a4dd2d0a4409b8d969e01e57091cf170dfd977")); +/// ``` +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub fn derive_key( + secret: &[u8], + shared_info: &[u8], + length: usize, +) -> Result, Error> +where + D: Digest + FixedOutputReset, +{ + let mut key = std::vec![0u8; length]; + derive_key_into::(secret, shared_info, &mut key)?; + Ok(key) +} diff --git a/ansi-x963-kdf/tests/tests.rs b/ansi-x963-kdf/tests/tests.rs new file mode 100644 index 0000000..3015d18 --- /dev/null +++ b/ansi-x963-kdf/tests/tests.rs @@ -0,0 +1,252 @@ +//! Tests for ansi-x963-kdf +//! +//! Test vectors have been generated using the java-based Bouncy-Castle +//! KDF2 implementation [KDF2BytesGenerator][1] +//! +//! [1]: https://downloads.bouncycastle.org/java/docs/bcprov-jdk18on-javadoc/ +use digest::{Digest, FixedOutputReset}; +use hex_literal::hex; +use sha2::{Sha224, Sha256, Sha512}; + +struct Fixture<'a> { + secret: &'a [u8], + shared_info: &'a [u8], + expected_key: &'a [u8], +} + +fn test_key_derivation(fixtures: &[Fixture]) +where + D: Digest + FixedOutputReset, +{ + for Fixture { + secret, + shared_info, + expected_key, + } in fixtures.iter() + { + for key_length in 1..expected_key.len() { + let mut key = vec![0u8; key_length]; + assert!(ansi_x963_kdf::derive_key_into::(secret, shared_info, &mut key).is_ok()); + assert_eq!(&expected_key[..key_length], &key); + } + } +} + +#[test] +fn test_input_output_sha224() { + let fixtures = [ + Fixture { + secret: &hex!("00"), + shared_info: &[], + expected_key: &hex!( + "4a6ebc83b8e2b19eea640500be6bcffdddaa07b8b2f81f2c533940e4e6ad6cfd + e680e5ba8eb25351402f0e75a6246cf006f6dd2187185af41d04abb648124e27 + 827cf4f2b871f9bc3fb2313c4f146b44faf3be170f2d87296c9b533c516b9a48 + dc73f73bafcc610bce18965566e3d0ca0f083c8a6a20b3b84457486e204a1014" + ), + }, + Fixture { + secret: &hex!("00"), + shared_info: &hex!("00"), + expected_key: &hex!( + "4bfb11552c4bf91bce4833aa06f854ceb8a3f7e435f42907e6d86e7597b20789 + aba17dccaf09d3e26bc3dd0ad6051f0e46b830cc57091bd0ba1da24a4ab96492 + 3b47b4b73ccb6cec6aa1e6339f4fa93995baef4a3ace3cadcf1ee63eaecb868f + 2f8ca06def29797d33673803a185574dec0c4bc0a5d0d0ffb4c527eb738d5bd2 + 4fcc424f46785f693f60ea2f00d3ff38f9f1e73847a50bf6ece7bda4abe3767f + 19f0a767f2ea69ed84f4f5837084edd2945c39d4b459b38fc2e83264ba47896a + a3e106058f1d13f2b1422c7ff33c279dfc7a42cc4f775babcae8122a4dbdf427 + a8634e9464607fe4a6f91fc59f07ab42f18dac313384b50d572cdff0b406cff2" + ), + }, + Fixture { + secret: &hex!("ba5eba11bedabb1ebe5077edb0a710adb01dfacecab005eca11ab1eca55e77e011"), + shared_info: &hex!("f005ba1100ddba11"), + expected_key: &hex!( + "20328557e258ecbe845fcde1002aa36dba5e29383d1b9813c2410819c09bd7d7 + 5b75f4d2ca71354080b64b3e8e3ef457f22517b074cbbbbf11d660b7b4706de1 + 5678893c6712e104b34fb776a90341c905a028bf1892aa4487899ef4436f4ac6 + d436db25763c7fa7d43fbedac386aa69f5b156d4a84ede0b4371d34eb083fce1 + 6cb6e051e846a923a82707925838371797b09fc94134d33b48e0ab9175fdbd90 + cd57b1570d55f5d4a391f5c15660757c447e0480bd6b6f0ca80a4e3ab5c40220 + 7d1edcc2210eb77aff4eda6e35afce2815d82ab242574b7b9d0e72d8daa1c853 + e0b3dad4cb384ce70c5a23afd4f1e35a01fdd14f78812a5a99a93f4d57877901" + ), + }, + ]; + + test_key_derivation::(&fixtures); +} + +#[test] +fn test_input_output_sha256() { + let fixtures = [ + Fixture { + secret: &hex!("00"), + shared_info: &[], + expected_key: &hex!( + "15f2f1a4339f5f2a313b95015cad8124d054a171ac2f31cf529dda7cfb6a38b4 + 89eefc18fa4b815bd1aded2f24eb28885993aa00b6d0171bf5005f9d39aaea10 + 016a682d1df4f869b32c48b0a9b442a1493949fb85d951d121c1143bd3d5c1af + b59024333110b3108625f25447665c1ebf10c6a6bbe9f018c421f4b0dcb5a993 + 42a5578600f1b0902c599a39268c12bdb1e820fd9a82212db588a71ae74cb6e4 + 1f8a792ae7c5800a0b0e3aea6ed808bedca2b0a3cc8f7b22c5effbd545f632c2 + 043a0631871a3f67ac03c5f8406b69a0dc14bd5b23e55f27a5d4462b0f0a2d23 + 18519afd330d3447bb196dd75ea7a7998db6f2fcb2a5dc134f35690a2dbcc072" + ), + }, + Fixture { + secret: &hex!("00"), + shared_info: &hex!("00"), + expected_key: &hex!( + "588611f65741c171a3d92c1d5343f5dd67f4fc472fc56f01c9bc568f5ac2a623 + 55af2e3db27cf364b9465ea89a489710da6c78ecc59ddf3ac6203261a6649d9e + 45673cfcd9849e761a24b07d99f5c35167c343244c160b973b55a29408d9d988 + 654670625fbd22634494df9f4f9a5328352eb92b4104612eef6dff382c119064 + 785b35d50e5df9eee4bb06e5b102b1088d149500e934c04eac6936a09e4b36d1 + 1e4f69ae41148ec0d7b5cca9bde9db8b850660e759c75f32154bb60357145ed3 + c0112a61a92f4eacd699c70a603df40f38babf6420587478c05ec70670e7221e + ce2081d38382369c0d2ec51f89db2e29146d555c7c2aa62518962824682553a7" + ), + }, + Fixture { + secret: &hex!("ba5eba11bedabb1ebe5077edb0a710adb01dfacecab005eca11ab1eca55e77e011"), + shared_info: &hex!("f005ba1100ddba11"), + expected_key: &hex!( + "41bf219e0dedf77305f1f79739fd917b3311e61dd504150d6f3c40195837c75a + 441fd05332d739a43fd70e11e4be66683eb05586c6c03bbf6d8030990e724a38 + c2ab1f5c22b0f47a84a2699d11701c6bfb3337e606130522f4f7a26df3b1cb95 + 28ca56781af9af361e7c2ac64d50f73d275d5a6c83fc67b2e05f20ab9b595cce + b8f205c57993647bf64c6f4ad8899eb5d0111efed1859006ec256b2e8cbb058b + b83a8d40fa7f435037acd155b27a87716fdd7619b900f051a2437539f830789b + f71080ff642285a01ff2db3e11ca5377c389be3f3851611cc8189728496fddca + cac6b89565fd78a1b8d4c8d407ff45e39610526668abacabede347d5c1e9fb69" + ), + }, + ]; + + test_key_derivation::(&fixtures); +} + +#[test] +fn test_input_output_sha512() { + let fixtures = [ + Fixture { + secret: &hex!("00"), + shared_info: &[], + expected_key: &hex!( + "b8eef223e484fe7a872e4db84711a01db365b205e477c3e3170f26623e2fa230 + 4d93f6c04337d0ea7454d1f2073f8eb8ee58b361438b61f363eb1037a77f716c + e89b92de1146cf3831eff44361d872f61dea1f05b3e08a9330c302949f6c93bd + 3e908f5ce5444e45a47bc0625600fff575472f04bcecc393387c244a93fbd4f4 + 26b22edbdaa5eef8565feb1d6a3c46dedb89c00efcaf3f5d95d53f936b570efb + 18db044083a075f3d1322378a07f00694e4e21a535d91e893cacac87d877b2ab + da0cff964fd1c291b759c38657bc7904be9f98cc8794099a6351b68f382e2df8 + 79cab5d5a1d7f5e9d6461f015b11c47fb14cf99e496905fa95e8d7d5ec59a493" + ), + }, + Fixture { + secret: &hex!("00"), + shared_info: &hex!("00"), + expected_key: &hex!( + "74cc6e00677ea1683c3c3fbc6337101db4e2ffdd0053a8783fd4c9f5b53117db + 9089ce3beef287cbe273a7c47ad1e88509842f9a70ff354280dc7a8e1c61214a + e698b4186af5628a28dad9ff4b25d0cfbceac9c9c522d496f8513338a9426991 + 2e0bbd2b2c500b303dae963b707ed4a05e9f57eb0c7de06da884669a93dbb29b + 3d262e7c98e24f8cd68d0ea44fe9d5e4e0b033b0c3f77193cdf2163dfac30da9 + eb39b147e2d9746dd1149ac512920d8e8316577e6713498beb7fa838a80b1736 + 383001d5151582a16bcf9fcc38edbafaf18ab976e01a0244b462c6b6f907ba14 + 32d14e641961c3d48e300ec5561424c4b8125cf172d06f9368bfdec0d5c57b8b" + ), + }, + Fixture { + secret: &hex!("ba5eba11bedabb1ebe5077edb0a710adb01dfacecab005eca11ab1eca55e77e011"), + shared_info: &hex!("f005ba1100ddba11"), + expected_key: &hex!( + "ae21b84e638fc7de4d838d2a7232655c39d2794116f00e43891170c0a16df11c + 15afbdb903c5722e22afc885c0f851c2ccacc2a0802437bc5bef6c18a0573246 + 65de72200dac5321ed92f530ed441bc194c402055419d73f52165a2bf9985fab + 756abce8e3b9c5e4a3d179b2eceaa6ef7b335245f480ed32a7f847921ab5e3c1 + a8867aff9802e6f8cec4d6a5fdf3cc0c2c1a14f08ec4df3654f2579164c6ed90 + a2262a8d492a0aa0942838952dc89f494018da5dd16c0b18ca6a9837685489bf + a55debb243045e83a730e5e08917836181693cb4ab1827e968e3bb0e8e3b9a0e + 7cdab180f59168211dad86eb88fc3b4bc1dbeb0c8a8c967c5e0d1b2a84bf215c" + ), + }, + ]; + + test_key_derivation::(&fixtures); +} + +#[test] +fn test_errors() { + // secret has zero length. + assert_eq!( + ansi_x963_kdf::derive_key_into::(&[], &[], &mut [0u8; 42]), + Err(ansi_x963_kdf::Error::NoSecret) + ); + + // key has zero length. + assert_eq!( + ansi_x963_kdf::derive_key_into::(&[0u8; 42], &[], &mut [0u8; 0]), + Err(ansi_x963_kdf::Error::NoOutput) + ); + + // shared_info has a length that causes input overflow. + #[cfg(target_pointer_width = "64")] + { + // Secret + let secret = [0u8; 42]; + + // Calculate the required length for shared_info to cause an input overflow: |Z| + |SharedInfo| + 4 >= hashmaxlen + let shared_info_len = Sha224::output_size() * (u32::MAX as usize) - secret.len() - 4; + + // Create a layout for allocation. + let layout = std::alloc::Layout::from_size_align(shared_info_len, 1).unwrap(); + unsafe { + // We assume that OS will not allocate physical memory for this buffer + let p = std::alloc::alloc_zeroed(layout); + if p.is_null() { + panic!("Failed to allocate memory"); + } + + // Wrap the allocated pointer in a struct that will deallocate it on drop. + struct AllocGuard { + ptr: *mut u8, + layout: std::alloc::Layout, + } + impl Drop for AllocGuard { + fn drop(&mut self) { + unsafe { + std::alloc::dealloc(self.ptr, self.layout); + } + } + } + let _guard = AllocGuard { ptr: p, layout }; + + // Create a slice from the allocated memory. + let shared_info = std::slice::from_raw_parts(p, shared_info_len); + assert_eq!( + ansi_x963_kdf::derive_key_into::(&secret, shared_info, &mut [0u8; 42]), + Err(ansi_x963_kdf::Error::InputOverflow) + ); + } + } + + // key has a length that causes counter overflow. + #[cfg(target_pointer_width = "64")] + { + let size = Sha224::output_size() * u32::MAX as usize; + let layout = std::alloc::Layout::from_size_align(size, 1).unwrap(); + unsafe { + // We assume that OS will not allocate physicall memory for this buffer + let p = std::alloc::alloc_zeroed(layout); + let buf = std::slice::from_raw_parts_mut(p, size); + assert_eq!( + ansi_x963_kdf::derive_key_into::(&[0u8; 42], &[], buf), + Err(ansi_x963_kdf::Error::CounterOverflow) + ); + std::alloc::dealloc(p, layout) + }; + } +}