diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index c04f7ca..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.rs linguist-language=Rust diff --git a/.github/workflows/hkdf.yml b/.github/workflows/hkdf.yml index 19f46f5..d5d60fa 100644 --- a/.github/workflows/hkdf.yml +++ b/.github/workflows/hkdf.yml @@ -17,61 +17,41 @@ env: RUSTFLAGS: "-Dwarnings" jobs: - test: + build: runs-on: ubuntu-latest strategy: matrix: - include: - - target: i686-unknown-linux-gnu - toolchain: 1.41.0 # MSRV - use_cross: false - deps: sudo apt install gcc-multilib - - target: i686-unknown-linux-gnu - toolchain: stable - use_cross: false - deps: sudo apt install gcc-multilib - - target: x86_64-unknown-linux-gnu - toolchain: 1.41.0 # MSRV - use_cross: false - deps: true - - target: x86_64-unknown-linux-gnu - toolchain: stable - use_cross: false - deps: true - - target: powerpc-unknown-linux-gnu - toolchain: 1.41.0 # MSRV - use_cross: true - deps: true - - target: powerpc-unknown-linux-gnu - toolchain: stable - use_cross: true - deps: true - ## TODO: debug PPC64 linking errors. Example failure: - ## - #- target: powerpc64-unknown-linux-gnu - # toolchain: 1.41.0 # MSRV - # use_cross: true - # deps: true - #- target: powerpc64-unknown-linux-gnu - # toolchain: stable - # use_cross: true - # deps: true + rust: + - 1.41.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown steps: - uses: actions/checkout@v1 - - run: ${{ matrix.deps }} - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: ${{ matrix.toolchain }} + toolchain: ${{ matrix.rust }} target: ${{ matrix.target }} override: true - - uses: actions-rs/cargo@v1 - with: - command: test - use-cross: ${{ matrix.use_cross }} - args: --target ${{ matrix.target }} --release - - uses: actions-rs/cargo@v1 + - run: cargo build --no-default-features --target ${{ matrix.target }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.41.0 # MSRV + - stable + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 with: - command: test - use-cross: ${{ matrix.use_cross }} - args: --target ${{ matrix.target }} --release --all-features + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + - run: cargo check --all-features + - run: cargo test --no-default-features + - run: cargo test + - run: cargo test --all-features diff --git a/Cargo.lock b/Cargo.lock index 5225932..3906712 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,138 +1,93 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "bencher" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfdb4953a096c551ce9ace855a604d702e6e62d77fac690575ae347571717f5" +version = 3 [[package]] name = "blobby" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc52553543ecb104069b0ff9e0fcc5c739ad16202935528a112d974e8f1a4ee8" +checksum = "847495c209977a90e8aad588b959d0ca9f5dc228096d29a6bd3defd53f35eaec" [[package]] name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.4", -] - -[[package]] -name = "block-cipher-trait" -version = "0.4.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85cf11f176fc683b9d290b231200f501a938f36dd0b9516a5ae6278377a1583" +checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" dependencies = [ - "generic-array 0.8.3", + "generic-array", ] [[package]] name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "cpuid-bool" -version = "0.1.2" +name = "cpufeatures" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" - -[[package]] -name = "crypto-mac" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779015233ac67d65098614aec748ac1c756ab6677fa2e14cf8b37c08dfed1198" -dependencies = [ - "constant_time_eq", - "generic-array 0.8.3", -] - -[[package]] -name = "crypto-mac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" dependencies = [ - "generic-array 0.14.4", - "subtle", + "libc", ] [[package]] -name = "crypto-tests" -version = "0.5.5" +name = "crypto-common" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b8c30525659210fcced70ae9be2d0269ae4a417efc77b939884d2a40081d923" +checksum = "567569e659735adb39ff2d4c20600f7cd78be5471f8c58ab162bce3c03fdbc5f" dependencies = [ - "block-cipher-trait", - "crypto-mac 0.4.0", - "digest 0.6.2", - "generic-array 0.8.3", + "generic-array", ] [[package]] name = "digest" -version = "0.6.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" +checksum = "8549e6bfdecd113b7e221fe60b433087f6957387a20f8118ebca9b12af19143d" dependencies = [ - "generic-array 0.8.3", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.4", + "block-buffer", + "crypto-common", + "generic-array", + "subtle", ] [[package]] name = "generic-array" -version = "0.8.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fceb69994e330afed50c93524be68c42fa898c2d9fd4ee8da03bd7363acd26f2" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ - "nodrop", "typenum", + "version_check", ] [[package]] -name = "generic-array" -version = "0.14.4" +name = "hex-literal" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" dependencies = [ - "typenum", - "version_check", + "hex-literal-impl", + "proc-macro-hack", ] [[package]] -name = "hex" -version = "0.4.2" +name = "hex-literal-impl" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +checksum = "853f769599eb31de176303197b7ba4973299c38c7a7604a6bc88c3eef05b9b46" +dependencies = [ + "proc-macro-hack", +] [[package]] name = "hkdf" -version = "0.11.0" +version = "0.12.0" dependencies = [ - "bencher", "blobby", - "crypto-tests", - "digest 0.9.0", - "hex", + "hex-literal", "hmac", "sha-1", "sha2", @@ -140,66 +95,61 @@ dependencies = [ [[package]] name = "hmac" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +checksum = "ddca131f3e7f2ce2df364b57949a9d47915cfbd35e46cfee355ccebbf794d6a2" dependencies = [ - "crypto-mac 0.11.0", - "digest 0.9.0", + "digest", ] [[package]] -name = "nodrop" -version = "0.1.14" +name = "libc" +version = "0.2.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01" [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "proc-macro-hack" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "sha-1" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ - "block-buffer", "cfg-if", - "cpuid-bool", - "digest 0.9.0", - "opaque-debug", + "cpufeatures", + "digest", ] [[package]] name = "sha2" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" +checksum = "900d964dd36bb15bcf2f2b35694c072feab74969a54f2bbeec7a2d725d2bdcb6" dependencies = [ - "block-buffer", "cfg-if", - "cpuid-bool", - "digest 0.9.0", - "opaque-debug", + "cpufeatures", + "digest", ] [[package]] name = "subtle" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "typenum" -version = "1.12.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" diff --git a/README.md b/README.md index 7f2ea8c..980e6b6 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,21 @@ -# RustCrypto KDFs ![Rust Version][rustc-image] [![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link] +# RustCrypto: Key Derivation Functions -Collection of key derivation functions (KDFs) written in pure Rust. +[![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link] ![Apache2/MIT licensed][license-image] -## Crates +Collection of [Key Derivation Functions][KDF] (KDF) written in pure Rust. -| Name | Algorithm | Crates.io | Documentation | Build Status | -|--------|-----------|---------------|---------------|--------------| -| `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) | [![Build](https://github.com/RustCrypto/KDFs/workflows/hkdf/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/KDFs/actions?query=workflow:hkdf+branch:master) +## Supported Algorithms + +| Algorithm | Crate | Crates.io | Documentation | MSRV | +|-----------|--------|:-------------:|:-------------:|:----:| +| [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.41] | *NOTE: for password-based KDFs (e.g. Argon2, PBKDF2, scrypt), please see [RustCrypto/password-hashes]* +### Minimum Supported Rust Version (MSRV) Policy + +MSRV bumps are considered breaking changes and will be performed only with minor version bump. + ## License All crates licensed under either of @@ -21,19 +27,23 @@ 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. +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. [//]: # (badges) -[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260043-KDFs +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/KDFs/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/KDFs +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg + +[//]: # (crates) + +[`hkdf`]: ./hkdf [//]: # (algorithms) +[KDF]: https://en.wikipedia.org/wiki/Key_derivation_function [HKDF]: https://en.wikipedia.org/wiki/HKDF [RustCrypto/password-hashes]: https://github.com/RustCrypto/password-hashes diff --git a/hkdf/CHANGELOG.md b/hkdf/CHANGELOG.md index 8bbebc9..4f5bf17 100644 --- a/hkdf/CHANGELOG.md +++ b/hkdf/CHANGELOG.md @@ -4,6 +4,12 @@ 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.12.0 (2021-12-07) +### Changed +- Bump `hmac` crate dependency to v0.12 and `digest` to v0.10 ([#52]) + +[#52]: https://github.com/RustCrypto/KDFs/pull/52 + ## 0.11.0 (2021-04-29) ### Added - Wycheproof HKDF test vectors ([#49]) diff --git a/hkdf/Cargo.toml b/hkdf/Cargo.toml index 41f0646..5a864b1 100644 --- a/hkdf/Cargo.toml +++ b/hkdf/Cargo.toml @@ -1,34 +1,28 @@ [package] name = "hkdf" -version = "0.11.0" -authors = ["vladikoff", "warner", "RustCrypto Developers"] -license = "MIT/Apache-2.0" +version = "0.12.0" # Also update html_root_url in lib.rs when bumping this +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" homepage = "https://github.com/RustCrypto/KDFs/" repository = "https://github.com/RustCrypto/KDFs/" description = "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)" -keywords = [ "HKDF", "Crypto" ] +keywords = ["crypto", "HKDF", "KDF"] +categories = ["cryptography", "no-std"] readme = "README.md" edition = "2018" -[features] -std = [] - -[lib] -name = "hkdf" -path = "src/hkdf.rs" - [dependencies] -digest = "0.9" -hmac = "0.11" +hmac = "0.12" [dev-dependencies] blobby = "0.3" -crypto-tests = "0.5.*" -hex = "0.4" -sha-1 = "0.9" -sha2 = "0.9" -bencher = "0.1" +hex-literal = "0.2" +sha-1 = { version = "0.10", default-features = false } +sha2 = { version = "0.10", default-features = false } -[[bench]] -name = "hkdf" -harness = false +[features] +std = ["hmac/std"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/hkdf/LICENSE-MIT b/hkdf/LICENSE-MIT index d95fddc..c0d0781 100644 --- a/hkdf/LICENSE-MIT +++ b/hkdf/LICENSE-MIT @@ -1,5 +1,5 @@ Copyright (c) 2015-2018 Vlad Filippov -Copyright (c) 2018 RustCrypto Developers +Copyright (c) 2018-2021 RustCrypto Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/hkdf/README.md b/hkdf/README.md index 3115db3..76efd86 100644 --- a/hkdf/README.md +++ b/hkdf/README.md @@ -7,28 +7,69 @@ [![Project Chat][chat-image]][chat-link] [![Build Status][build-image]][build-link] -[HMAC-based Extract-and-Expand Key Derivation Function (HKDF)](https://tools.ietf.org/html/rfc5869) for [Rust](http://www.rust-lang.org/). +Pure Rust implementation of the [HMAC-based Extract-and-Expand Key Derivation Function (HKDF)](https://tools.ietf.org/html/rfc5869) generic over hash function. -Uses the Digest trait which specifies an interface common to digest functions, such as SHA-1, SHA-256, etc. +# Usage -## Installation +The most common way to use HKDF is as follows: you provide the Initial Key Material (IKM) and an optional salt, then you expand it (perhaps multiple times) into some Output Key Material (OKM) bound to an "info" context string. -From crates.io: +```rust +use sha2::Sha256; +use hkdf::Hkdf; +use hex_literal::hex; -```toml -[dependencies] -hkdf = "0.7" +let ikm = hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); +let salt = hex!("000102030405060708090a0b0c"); +let info = hex!("f0f1f2f3f4f5f6f7f8f9"); + +let hk = Hkdf::::new(Some(&salt[..]), &ikm); +let mut okm = [0u8; 42]; +hk.expand(&info, &mut okm) + .expect("42 is a valid length for Sha256 to output"); + +let expected = hex!(" + 3cb25f25faacd57a90434f64d0362f2a + 2d2d0a90cf1a5a4c5db02d56ecc4c5bf + 34007208d5b887185865 +"); +assert_eq!(okm, expected); +``` + +Normally the PRK (Pseudo-Random Key) remains hidden within the HKDF object, but if you need to access it, use `Hkdf::extract` instead of `Hkdf::new`. + +```rust +let (prk, hk) = Hkdf::::extract(Some(&salt[..]), &ikm); +let expected = hex!(" + 077709362c2e32df0ddc3f0dc47bba63 + 90b6c73bb50f9c3122ec844ad7c2b3e5 +"); +assert_eq!(prk[..], expected[..]); ``` -## Usage +If you already have a strong key to work from (uniformly-distributed and +long enough), you can save a tiny amount of time by skipping the extract +step. In this case, you pass a Pseudo-Random Key (PRK) into the +`Hkdf::from_prk` constructor, then use the resulting `Hkdf` object +as usual. -See the example [examples/main.rs](examples/main.rs) or run it with `cargo run --example main` +```rust +let prk = hex!(" + 077709362c2e32df0ddc3f0dc47bba63 + 90b6c73bb50f9c3122ec844ad7c2b3e5 +"); -## Authors +let hk = Hkdf::::from_prk(&prk).expect("PRK should be large enough"); +let mut okm = [0u8; 42]; +hk.expand(&info, &mut okm) + .expect("42 is a valid length for Sha256 to output"); -[![Vlad Filippov](https://avatars3.githubusercontent.com/u/128755?s=70)](http://vf.io/) | [![Brian Warner](https://avatars3.githubusercontent.com/u/27146?v=4&s=70)](http://www.lothar.com/blog/) ----|--- -[Vlad Filippov](http://vf.io/) | [Brian Warner](http://www.lothar.com/blog/) +let expected = hex!(" + 3cb25f25faacd57a90434f64d0362f2a + 2d2d0a90cf1a5a4c5db02d56ecc4c5bf + 34007208d5b887185865 +"); +assert_eq!(okm, expected); +``` [//]: # (badges) diff --git a/hkdf/benches/hkdf.rs b/hkdf/benches/hkdf.rs deleted file mode 100644 index 699f181..0000000 --- a/hkdf/benches/hkdf.rs +++ /dev/null @@ -1,29 +0,0 @@ -#[macro_use] -extern crate bencher; - -use bencher::Bencher; -use hkdf::Hkdf; -use sha2::Sha256; - -fn sha256_10(b: &mut Bencher) { - let mut okm = vec![0u8; 10]; - b.iter(|| Hkdf::::new(Some(&[]), &[]).expand(&[], &mut okm)); - b.bytes = 10u64; -} - -fn sha256_1k(b: &mut Bencher) { - let mut okm = vec![0u8; 1024]; - b.iter(|| Hkdf::::new(Some(&[]), &[]).expand(&[], &mut okm)); - b.bytes = 1024u64; -} - -fn sha256_8k(b: &mut Bencher) { - let mut okm = vec![0u8; 8000]; - b.iter(|| Hkdf::::new(Some(&[]), &[]).expand(&[], &mut okm)); - b.bytes = 8000u64; -} - -// note: SHA-256 output limit is 255*32=8160 bytes - -benchmark_group!(benches, sha256_10, sha256_1k, sha256_8k); -benchmark_main!(benches); diff --git a/hkdf/benches/mod.rs b/hkdf/benches/mod.rs new file mode 100644 index 0000000..4679b59 --- /dev/null +++ b/hkdf/benches/mod.rs @@ -0,0 +1,27 @@ +#![feature(test)] +extern crate test; + +use test::Bencher; + +type HkdfSha256 = hkdf::Hkdf; + +#[bench] +fn hkdf_sha256_10(b: &mut Bencher) { + let mut okm = vec![0u8; 10]; + b.iter(|| HkdfSha256::new(Some(&[]), &[]).expand(&[], &mut okm)); + b.bytes = okm.len() as u64; +} + +#[bench] +fn hkdf_sha256_1024(b: &mut Bencher) { + let mut okm = vec![0u8; 1024]; + b.iter(|| HkdfSha256::new(Some(&[]), &[]).expand(&[], &mut okm)); + b.bytes = okm.len() as u64; +} + +#[bench] +fn hkdf_sha256_8000(b: &mut Bencher) { + let mut okm = vec![0u8; 8000]; + b.iter(|| HkdfSha256::new(Some(&[]), &[]).expand(&[], &mut okm)); + b.bytes = okm.len() as u64; +} diff --git a/hkdf/examples/extract.rs b/hkdf/examples/extract.rs deleted file mode 100644 index 6797843..0000000 --- a/hkdf/examples/extract.rs +++ /dev/null @@ -1,25 +0,0 @@ -use hkdf::Hkdf; -use sha2::Sha256; - -// Normally the PRK (Pseudo-Random Key) remains hidden within the HKDF -// object, but if you need to access it, use `Hkdf::extract` instead of -// `Hkdf::new` - -fn main() { - let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); - let salt = hex::decode("000102030405060708090a0b0c").unwrap(); - let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); - - let (prk, hk) = Hkdf::::extract(Some(&salt[..]), &ikm); - let mut okm = [0u8; 42]; - hk.expand(&info, &mut okm) - .expect("42 is a valid length for Sha256 to output"); - - println!("Vector 1 PRK is {}", hex::encode(prk)); - println!("Vector 1 OKM is {}", hex::encode(&okm[..])); - println!("Matched with https://tools.ietf.org/html/rfc5869#appendix-A.1"); - - let expected = - "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"; - assert_eq!(hex::encode(&okm[..]), expected); -} diff --git a/hkdf/examples/from_prk.rs b/hkdf/examples/from_prk.rs deleted file mode 100644 index 7636da8..0000000 --- a/hkdf/examples/from_prk.rs +++ /dev/null @@ -1,26 +0,0 @@ -use hkdf::Hkdf; -use sha2::Sha256; - -// If you already have a strong key to work from (uniformly-distributed and -// long enough), you can save a tiny amount of time by skipping the extract -// step. In this case, you pass a Pseudo-Random Key (PRK) into the `from_prk` -// constructor, then use the resulting `Hkdf` object as usual. - -fn main() { - // data from RFC 5869, section A.1, Test Case 1 - let prk = - hex::decode("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5").unwrap(); - let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); - - let hk = Hkdf::::from_prk(&prk).expect("PRK should be large enough"); - let mut okm = [0u8; 42]; - hk.expand(&info, &mut okm) - .expect("42 is a valid length for Sha256 to output"); - - println!("Vector 1 OKM is {}", hex::encode(&okm[..])); - println!("Matched with https://tools.ietf.org/html/rfc5869#appendix-A.1"); - - let expected = - "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"; - assert_eq!(hex::encode(&okm[..]), expected); -} diff --git a/hkdf/examples/main.rs b/hkdf/examples/main.rs deleted file mode 100644 index fbb68bc..0000000 --- a/hkdf/examples/main.rs +++ /dev/null @@ -1,24 +0,0 @@ -use hkdf::Hkdf; -use sha2::Sha256; - -// this is the most common way to use HKDF: you provide the Initial Key -// Material and an optional salt, then you expand it (perhaps multiple times) -// into some Output Key Material bound to an "info" context string. - -fn main() { - let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); - let salt = hex::decode("000102030405060708090a0b0c").unwrap(); - let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); - - let hk = Hkdf::::new(Some(&salt[..]), &ikm); - let mut okm = [0u8; 42]; - hk.expand(&info, &mut okm) - .expect("42 is a valid length for Sha256 to output"); - - println!("Vector 1 OKM is {}", hex::encode(&okm[..])); - println!("Matched with https://tools.ietf.org/html/rfc5869#appendix-A.1"); - - let expected = - "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"; - assert_eq!(hex::encode(&okm[..]), expected); -} diff --git a/hkdf/src/hkdf.rs b/hkdf/src/hkdf.rs deleted file mode 100644 index 4d72733..0000000 --- a/hkdf/src/hkdf.rs +++ /dev/null @@ -1,203 +0,0 @@ -//! An implementation of HKDF, the [HMAC-based Extract-and-Expand Key Derivation Function][1]. -//! -//! # Usage -//! -//! ```rust -//! # use sha2::Sha256; -//! # use hkdf::Hkdf; -//! let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); -//! let salt = hex::decode("000102030405060708090a0b0c").unwrap(); -//! let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); -//! -//! let h = Hkdf::::new(Some(&salt[..]), &ikm); -//! let mut okm = [0u8; 42]; -//! h.expand(&info, &mut okm).unwrap(); -//! println!("OKM is {}", hex::encode(&okm[..])); -//! ``` -//! -//! [1]: https://tools.ietf.org/html/rfc5869 - -#![no_std] -#![warn(rust_2018_idioms)] - -#[cfg(feature = "std")] -extern crate std; - -use core::fmt; -use digest::generic_array::{self, ArrayLength, GenericArray}; -use digest::{BlockInput, FixedOutput, Reset, Update}; -use hmac::{Hmac, Mac, NewMac}; - -/// Error that is returned when supplied pseudorandom key (PRK) is not long enough. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub struct InvalidPrkLength; - -/// Structure for InvalidLength, used for output error handling. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub struct InvalidLength; - -/// Structure representing the streaming context of an HKDF-Extract operation -/// ```rust -/// # use hkdf::{Hkdf, HkdfExtract}; -/// # use sha2::Sha256; -/// let mut extract_ctx = HkdfExtract::::new(Some(b"mysalt")); -/// extract_ctx.input_ikm(b"hello"); -/// extract_ctx.input_ikm(b" world"); -/// let (streamed_res, _) = extract_ctx.finalize(); -/// -/// let (oneshot_res, _) = Hkdf::::extract(Some(b"mysalt"), b"hello world"); -/// assert_eq!(streamed_res, oneshot_res); -/// ``` -#[derive(Clone)] -pub struct HkdfExtract -where - D: Update + BlockInput + FixedOutput + Reset + Default + Clone, - D::BlockSize: ArrayLength, - D::OutputSize: ArrayLength, -{ - hmac: Hmac, -} - -impl HkdfExtract -where - D: Update + BlockInput + FixedOutput + Reset + Default + Clone, - D::BlockSize: ArrayLength, - D::OutputSize: ArrayLength, -{ - /// Initiates the HKDF-Extract context with the given optional salt - pub fn new(salt: Option<&[u8]>) -> HkdfExtract { - let default_salt = GenericArray::::default(); - let salt = salt.unwrap_or(&default_salt); - let hmac = Hmac::::new_from_slice(salt).expect("HMAC can take a key of any size"); - HkdfExtract { hmac } - } - - /// Feeds in additional input key material to the HKDF-Extract context - pub fn input_ikm(&mut self, ikm: &[u8]) { - self.hmac.update(ikm); - } - - /// Completes the HKDF-Extract operation, returning both the generated pseudorandom key and - /// `Hkdf` struct for expanding. - pub fn finalize(self) -> (GenericArray, Hkdf) { - let prk = self.hmac.finalize().into_bytes(); - let hkdf = Hkdf::from_prk(&prk).expect("PRK size is correct"); - (prk, hkdf) - } -} - -/// Structure representing the HKDF, capable of HKDF-Expand and HKDF-Extract operations. -#[derive(Clone)] -pub struct Hkdf -where - D: Update + BlockInput + FixedOutput + Reset + Default + Clone, - D::BlockSize: ArrayLength, - D::OutputSize: ArrayLength, -{ - hmac: Hmac, -} - -impl Hkdf -where - D: Update + BlockInput + FixedOutput + Reset + Default + Clone, - D::BlockSize: ArrayLength, - D::OutputSize: ArrayLength, -{ - /// Convenience method for [`extract`][Hkdf::extract] when the generated - /// pseudorandom key can be ignored and only HKDF-Expand operation is needed. This is the most - /// common constructor. - pub fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Hkdf { - let (_, hkdf) = Hkdf::extract(salt, ikm); - hkdf - } - - /// Create `Hkdf` from an already cryptographically strong pseudorandom key - /// as per section 3.3 from RFC5869. - pub fn from_prk(prk: &[u8]) -> Result, InvalidPrkLength> { - use crate::generic_array::typenum::Unsigned; - - // section 2.3 specifies that prk must be "at least HashLen octets" - if prk.len() < D::OutputSize::to_usize() { - return Err(InvalidPrkLength); - } - - Ok(Hkdf { - hmac: Hmac::new_from_slice(prk).expect("HMAC can take a key of any size"), - }) - } - - /// The RFC5869 HKDF-Extract operation returning both the generated - /// pseudorandom key and `Hkdf` struct for expanding. - pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> (GenericArray, Hkdf) { - let mut extract_ctx = HkdfExtract::new(salt); - extract_ctx.input_ikm(ikm); - extract_ctx.finalize() - } - - /// The RFC5869 HKDF-Expand operation. This is equivalent to calling - /// [`expand`][Hkdf::extract] with the `info` argument set equal to the - /// concatenation of all the elements of `info_components`. - pub fn expand_multi_info( - &self, - info_components: &[&[u8]], - okm: &mut [u8], - ) -> Result<(), InvalidLength> { - use crate::generic_array::typenum::Unsigned; - - let mut prev: Option::OutputSize>> = None; - - let hmac_output_bytes = D::OutputSize::to_usize(); - if okm.len() > hmac_output_bytes * 255 { - return Err(InvalidLength); - } - - let mut hmac = self.hmac.clone(); - for (blocknum, okm_block) in okm.chunks_mut(hmac_output_bytes).enumerate() { - let block_len = okm_block.len(); - - if let Some(ref prev) = prev { - hmac.update(prev) - }; - - // Feed in the info components in sequence. This is equivalent to feeding in the - // concatenation of all the info components - for info in info_components { - hmac.update(info); - } - - hmac.update(&[blocknum as u8 + 1]); - - let output = hmac.finalize_reset().into_bytes(); - okm_block.copy_from_slice(&output[..block_len]); - - prev = Some(output); - } - - Ok(()) - } - - /// The RFC5869 HKDF-Expand operation - /// - /// If you don't have any `info` to pass, use an empty slice. - pub fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> { - self.expand_multi_info(&[info], okm) - } -} - -impl fmt::Display for InvalidPrkLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("invalid pseudorandom key length, too short") - } -} - -#[cfg(feature = "std")] -impl ::std::error::Error for InvalidPrkLength {} - -impl fmt::Display for InvalidLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("invalid number of blocks, too large output") - } -} - -#[cfg(feature = "std")] -impl ::std::error::Error for InvalidLength {} diff --git a/hkdf/src/lib.rs b/hkdf/src/lib.rs new file mode 100644 index 0000000..b6737fd --- /dev/null +++ b/hkdf/src/lib.rs @@ -0,0 +1,342 @@ +//! An implementation of HKDF, the [HMAC-based Extract-and-Expand Key Derivation Function][1]. +//! +//! # Usage +//! +//! The most common way to use HKDF is as follows: you provide the Initial Key +//! Material (IKM) and an optional salt, then you expand it (perhaps multiple times) +//! into some Output Key Material (OKM) bound to an "info" context string. +//! +//! ```rust +//! use sha2::Sha256; +//! use hkdf::Hkdf; +//! use hex_literal::hex; +//! +//! let ikm = hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); +//! let salt = hex!("000102030405060708090a0b0c"); +//! let info = hex!("f0f1f2f3f4f5f6f7f8f9"); +//! +//! let hk = Hkdf::::new(Some(&salt[..]), &ikm); +//! let mut okm = [0u8; 42]; +//! hk.expand(&info, &mut okm) +//! .expect("42 is a valid length for Sha256 to output"); +//! +//! let expected = hex!(" +//! 3cb25f25faacd57a90434f64d0362f2a +//! 2d2d0a90cf1a5a4c5db02d56ecc4c5bf +//! 34007208d5b887185865 +//! "); +//! assert_eq!(okm[..], expected[..]); +//! ``` +//! +//! Normally the PRK (Pseudo-Random Key) remains hidden within the HKDF +//! object, but if you need to access it, use [`Hkdf::extract`] instead of +//! [`Hkdf::new`]. +//! +//! ```rust +//! # use sha2::Sha256; +//! # use hkdf::Hkdf; +//! # use hex_literal::hex; +//! # let ikm = hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); +//! # let salt = hex!("000102030405060708090a0b0c"); +//! +//! let (prk, hk) = Hkdf::::extract(Some(&salt[..]), &ikm); +//! let expected = hex!(" +//! 077709362c2e32df0ddc3f0dc47bba63 +//! 90b6c73bb50f9c3122ec844ad7c2b3e5 +//! "); +//! assert_eq!(prk[..], expected[..]); +//! ``` +//! +//! If you already have a strong key to work from (uniformly-distributed and +//! long enough), you can save a tiny amount of time by skipping the extract +//! step. In this case, you pass a Pseudo-Random Key (PRK) into the +//! [`Hkdf::from_prk`] constructor, then use the resulting [`Hkdf`] object +//! as usual. +//! +//! ```rust +//! # use sha2::Sha256; +//! # use hkdf::Hkdf; +//! # use hex_literal::hex; +//! # let salt = hex!("000102030405060708090a0b0c"); +//! # let info = hex!("f0f1f2f3f4f5f6f7f8f9"); +//! let prk = hex!(" +//! 077709362c2e32df0ddc3f0dc47bba63 +//! 90b6c73bb50f9c3122ec844ad7c2b3e5 +//! "); +//! +//! let hk = Hkdf::::from_prk(&prk).expect("PRK should be large enough"); +//! let mut okm = [0u8; 42]; +//! hk.expand(&info, &mut okm) +//! .expect("42 is a valid length for Sha256 to output"); +//! +//! let expected = hex!(" +//! 3cb25f25faacd57a90434f64d0362f2a +//! 2d2d0a90cf1a5a4c5db02d56ecc4c5bf +//! 34007208d5b887185865 +//! "); +//! assert_eq!(okm[..], expected[..]); +//! ``` +//! +//! [1]: https://tools.ietf.org/html/rfc5869 + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_root_url = "https://docs.rs/hkdf/0.12.0" +)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![forbid(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] + +#[cfg(feature = "std")] +extern crate std; + +use core::fmt; +use hmac::digest::{ + block_buffer::Eager, + core_api::{ + AlgorithmName, BlockSizeUser, BufferKindUser, CoreProxy, CoreWrapper, FixedOutputCore, + OutputSizeUser, UpdateCore, + }, + generic_array::typenum::{IsLess, Le, NonZero, Unsigned, U256}, + FixedOutput, HashMarker, KeyInit, Output, Update, +}; +use hmac::{Hmac, HmacCore}; + +/// Error that is returned when supplied pseudorandom key (PRK) is not long enough. +#[derive(Copy, Clone, Debug)] +pub struct InvalidPrkLength; + +/// Structure for InvalidLength, used for output error handling. +#[derive(Copy, Clone, Debug)] +pub struct InvalidLength; + +/// Structure representing the streaming context of an HKDF-Extract operation +/// ```rust +/// # use hkdf::{Hkdf, HkdfExtract}; +/// # use sha2::Sha256; +/// let mut extract_ctx = HkdfExtract::::new(Some(b"mysalt")); +/// extract_ctx.input_ikm(b"hello"); +/// extract_ctx.input_ikm(b" world"); +/// let (streamed_res, _) = extract_ctx.finalize(); +/// +/// let (oneshot_res, _) = Hkdf::::extract(Some(b"mysalt"), b"hello world"); +/// assert_eq!(streamed_res, oneshot_res); +/// ``` +#[derive(Clone)] +pub struct HkdfExtract +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + hmac: Hmac, +} + +impl HkdfExtract +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + /// Initiates the HKDF-Extract context with the given optional salt + pub fn new(salt: Option<&[u8]>) -> HkdfExtract { + let default_salt = Output::::default(); + let salt = salt.unwrap_or(&default_salt); + let hmac = Hmac::::new_from_slice(salt).expect("HMAC can take a key of any size"); + HkdfExtract { hmac } + } + + /// Feeds in additional input key material to the HKDF-Extract context + pub fn input_ikm(&mut self, ikm: &[u8]) { + self.hmac.update(ikm); + } + + /// Completes the HKDF-Extract operation, returning both the generated pseudorandom key and + /// `Hkdf` struct for expanding. + pub fn finalize(self) -> (Output, Hkdf) { + let prk = self.hmac.finalize_fixed(); + let hkdf = Hkdf::from_prk(&prk).expect("PRK size is correct"); + (prk, hkdf) + } +} + +impl fmt::Debug for HkdfExtract +where + D: CoreProxy, + D::Core: HashMarker + + AlgorithmName + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("HkdfExtract<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +/// Structure representing the HKDF, capable of HKDF-Expand and HKDF-Extract operations. +#[derive(Clone)] +pub struct Hkdf +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + hmac: HmacCore, +} + +impl Hkdf +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + /// Convenience method for [`extract`][Hkdf::extract] when the generated + /// pseudorandom key can be ignored and only HKDF-Expand operation is needed. This is the most + /// common constructor. + pub fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Hkdf { + let (_, hkdf) = Hkdf::extract(salt, ikm); + hkdf + } + + /// Create `Hkdf` from an already cryptographically strong pseudorandom key + /// as per section 3.3 from RFC5869. + pub fn from_prk(prk: &[u8]) -> Result, InvalidPrkLength> { + // section 2.3 specifies that prk must be "at least HashLen octets" + if prk.len() < ::OutputSize::to_usize() { + return Err(InvalidPrkLength); + } + + Ok(Hkdf { + hmac: HmacCore::new_from_slice(prk).expect("HMAC can take a key of any size"), + }) + } + + /// The RFC5869 HKDF-Extract operation returning both the generated + /// pseudorandom key and `Hkdf` struct for expanding. + pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> (Output, Hkdf) { + let mut extract_ctx = HkdfExtract::new(salt); + extract_ctx.input_ikm(ikm); + extract_ctx.finalize() + } + + /// The RFC5869 HKDF-Expand operation. This is equivalent to calling + /// [`expand`][Hkdf::extract] with the `info` argument set equal to the + /// concatenation of all the elements of `info_components`. + pub fn expand_multi_info( + &self, + info_components: &[&[u8]], + okm: &mut [u8], + ) -> Result<(), InvalidLength> { + let mut prev: Option> = None; + + let chunk_len = ::OutputSize::USIZE; + if okm.len() > chunk_len * 255 { + return Err(InvalidLength); + } + + for (block_n, block) in okm.chunks_mut(chunk_len).enumerate() { + let mut hmac = CoreWrapper::from_core(self.hmac.clone()); + + if let Some(ref prev) = prev { + hmac.update(prev) + }; + + // Feed in the info components in sequence. This is equivalent to feeding in the + // concatenation of all the info components + for info in info_components { + hmac.update(info); + } + + hmac.update(&[block_n as u8 + 1]); + + let output = hmac.finalize_fixed(); + + let block_len = block.len(); + block.copy_from_slice(&output[..block_len]); + + prev = Some(output); + } + + Ok(()) + } + + /// The RFC5869 HKDF-Expand operation + /// + /// If you don't have any `info` to pass, use an empty slice. + pub fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> { + self.expand_multi_info(&[info], okm) + } +} + +impl fmt::Debug for Hkdf +where + D: CoreProxy, + D::Core: HashMarker + + AlgorithmName + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Hkdf<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +impl fmt::Display for InvalidPrkLength { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("invalid pseudorandom key length, too short") + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl ::std::error::Error for InvalidPrkLength {} + +impl fmt::Display for InvalidLength { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("invalid number of blocks, too large output") + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl ::std::error::Error for InvalidLength {} diff --git a/hkdf/tests/tests.rs b/hkdf/tests/tests.rs index 00a18c1..765ab1d 100644 --- a/hkdf/tests/tests.rs +++ b/hkdf/tests/tests.rs @@ -1,87 +1,201 @@ use core::iter; -use hex; - +use hex_literal::hex; use hkdf::{Hkdf, HkdfExtract}; use sha1::Sha1; use sha2::{Sha256, Sha384, Sha512}; struct Test<'a> { - ikm: &'a str, - salt: &'a str, - info: &'a str, - length: usize, - prk: &'a str, - okm: &'a str, + ikm: &'a [u8], + salt: &'a [u8], + info: &'a [u8], + prk: &'a [u8], + okm: &'a [u8], } // Test Vectors from https://tools.ietf.org/html/rfc5869. -fn tests_sha256<'a>() -> Vec> { - vec![ +#[test] +#[rustfmt::skip] +fn test_rfc5869_sha256() { + let tests = [ Test { // Test Case 1 - ikm: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - salt: "000102030405060708090a0b0c", - info: "f0f1f2f3f4f5f6f7f8f9", - length: 42, - prk: "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", - okm: "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b8\ - 87185865", + ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), + salt: &hex!("000102030405060708090a0b0c"), + info: &hex!("f0f1f2f3f4f5f6f7f8f9"), + prk: &hex!(" + 077709362c2e32df0ddc3f0dc47bba63 + 90b6c73bb50f9c3122ec844ad7c2b3e5 + "), + okm: &hex!(" + 3cb25f25faacd57a90434f64d0362f2a + 2d2d0a90cf1a5a4c5db02d56ecc4c5bf + 34007208d5b887185865 + "), }, Test { // Test Case 2 - ikm: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425\ - 262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b\ - 4c4d4e4f", - salt: "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f80818283848\ - 5868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aa\ - abacadaeaf", - info: "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d\ - 5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fa\ - fbfcfdfeff", - length: 82, - prk: "06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244", - okm: "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7\ - 827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5\ - c1f3434f1d87", + ikm: &hex!(" + 000102030405060708090a0b0c0d0e0f + 101112131415161718191a1b1c1d1e1f + 202122232425262728292a2b2c2d2e2f + 303132333435363738393a3b3c3d3e3f + 404142434445464748494a4b4c4d4e4f + "), + salt: &hex!(" + 606162636465666768696a6b6c6d6e6f + 707172737475767778797a7b7c7d7e7f + 808182838485868788898a8b8c8d8e8f + 909192939495969798999a9b9c9d9e9f + a0a1a2a3a4a5a6a7a8a9aaabacadaeaf + "), + info: &hex!(" + b0b1b2b3b4b5b6b7b8b9babbbcbdbebf + c0c1c2c3c4c5c6c7c8c9cacbcccdcecf + d0d1d2d3d4d5d6d7d8d9dadbdcdddedf + e0e1e2e3e4e5e6e7e8e9eaebecedeeef + f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff + "), + prk: &hex!(" + 06a6b88c5853361a06104c9ceb35b45c + ef760014904671014a193f40c15fc244 + "), + okm: &hex!(" + b11e398dc80327a1c8e7f78c596a4934 + 4f012eda2d4efad8a050cc4c19afa97c + 59045a99cac7827271cb41c65e590e09 + da3275600c2f09b8367793a9aca3db71 + cc30c58179ec3e87c14c01d5c1f3434f + 1d87 + "), }, Test { // Test Case 3 - ikm: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - salt: "", - info: "", - length: 42, - prk: "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04", - okm: "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4\ - b61a96c8", + ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), + salt: &hex!(""), + info: &hex!(""), + prk: &hex!(" + 19ef24a32c717b167f33a91d6f648bdf + 96596776afdb6377ac434c1c293ccb04 + "), + okm: &hex!(" + 8da4e775a563c18f715f802a063c5a31 + b8a11f5c5ee1879ec3454e5f3c738d2d + 9d201395faa4b61a96c8 + "), }, - ] + ]; + for Test { ikm, salt, info, prk, okm } in tests.iter() { + let salt = if salt.is_empty() { + None + } else { + Some(&salt[..]) + }; + let (prk2, hkdf) = Hkdf::::extract(salt, ikm); + let mut okm2 = vec![0u8; okm.len()]; + assert!(hkdf.expand(&info[..], &mut okm2).is_ok()); + + assert_eq!(prk2[..], prk[..]); + assert_eq!(okm2[..], okm[..]); + + okm2.iter_mut().for_each(|b| *b = 0); + let hkdf = Hkdf::::from_prk(prk).unwrap(); + assert!(hkdf.expand(&info[..], &mut okm2).is_ok()); + assert_eq!(okm2[..], okm[..]); + } } #[test] -fn test_derive_sha256() { - let tests = tests_sha256(); - for t in tests.iter() { - let ikm = hex::decode(&t.ikm).unwrap(); - let salt = hex::decode(&t.salt).unwrap(); - let info = hex::decode(&t.info).unwrap(); +#[rustfmt::skip] +fn test_rfc5869_sha1() { + let tests = [ + Test { + // Test Case 4 + ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b"), + salt: &hex!("000102030405060708090a0b0c"), + info: &hex!("f0f1f2f3f4f5f6f7f8f9"), + prk: &hex!("9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243"), + okm: &hex!(" + 085a01ea1b10f36933068b56efa5ad81 + a4f14b822f5b091568a9cdd4f155fda2 + c22e422478d305f3f896 + "), + }, + Test { + // Test Case 5 + ikm: &hex!(" + 000102030405060708090a0b0c0d0e0f + 101112131415161718191a1b1c1d1e1f + 202122232425262728292a2b2c2d2e2f + 303132333435363738393a3b3c3d3e3f + 404142434445464748494a4b4c4d4e4f + "), + salt: &hex!(" + 606162636465666768696a6b6c6d6e6f + 707172737475767778797a7b7c7d7e7f + 808182838485868788898a8b8c8d8e8f + 909192939495969798999a9b9c9d9e9f + a0a1a2a3a4a5a6a7a8a9aaabacadaeaf + "), + info: &hex!(" + b0b1b2b3b4b5b6b7b8b9babbbcbdbebf + c0c1c2c3c4c5c6c7c8c9cacbcccdcecf + d0d1d2d3d4d5d6d7d8d9dadbdcdddedf + e0e1e2e3e4e5e6e7e8e9eaebecedeeef + f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff + "), + prk: &hex!("8adae09a2a307059478d309b26c4115a224cfaf6"), + okm: &hex!(" + 0bd770a74d1160f7c9f12cd5912a06eb + ff6adcae899d92191fe4305673ba2ffe + 8fa3f1a4e5ad79f3f334b3b202b2173c + 486ea37ce3d397ed034c7f9dfeb15c5e + 927336d0441f4c4300e2cff0d0900b52 + d3b4 + "), + }, + Test { + // Test Case 6 + ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), + salt: &hex!(""), + info: &hex!(""), + prk: &hex!("da8c8a73c7fa77288ec6f5e7c297786aa0d32d01"), + okm: &hex!(" + 0ac1af7002b3d761d1e55298da9d0506 + b9ae52057220a306e07b6b87e8df21d0 + ea00033de03984d34918 + "), + }, + Test { + // Test Case 7 + ikm: &hex!("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"), + salt: &hex!(""), // "Not Provided" + info: &hex!(""), + prk: &hex!("2adccada18779e7c2077ad2eb19d3f3e731385dd"), + okm: &hex!(" + 2c91117204d745f3500d636a62f64f0a + b3bae548aa53d423b0d1f27ebba6f5e5 + 673a081d70cce7acfc48 + "), + }, + ]; + for Test { ikm, salt, info, prk, okm } in tests.iter() { let salt = if salt.is_empty() { None } else { Some(&salt[..]) }; - let (prk, hkdf) = Hkdf::::extract(salt, &ikm[..]); - let mut okm = vec![0u8; t.length]; - assert!(hkdf.expand(&info[..], &mut okm).is_ok()); + let (prk2, hkdf) = Hkdf::::extract(salt, ikm); + let mut okm2 = vec![0u8; okm.len()]; + assert!(hkdf.expand(&info[..], &mut okm2).is_ok()); - assert_eq!(hex::encode(prk), t.prk); - assert_eq!(hex::encode(&okm), t.okm); + assert_eq!(prk2[..], prk[..]); + assert_eq!(okm2[..], okm[..]); - let prk = &hex::decode(&t.prk).unwrap(); - let hkdf = Hkdf::::from_prk(prk).unwrap(); - assert!(hkdf.expand(&info[..], &mut okm).is_ok()); - - assert_eq!(hex::encode(&okm), t.okm); + okm2.iter_mut().for_each(|b| *b = 0); + let hkdf = Hkdf::::from_prk(prk).unwrap(); + assert!(hkdf.expand(&info[..], &mut okm2).is_ok()); + assert_eq!(okm2[..], okm[..]); } } @@ -102,7 +216,7 @@ fn test_lengths() { let mut okm = vec![0u8; length]; assert!(hkdf.expand(&[], &mut okm).is_ok()); assert_eq!(okm.len(), length); - assert_eq!(hex::encode(okm), hex::encode(longest[..length].iter())); + assert_eq!(okm[..], longest[..length]); } } @@ -129,121 +243,34 @@ fn test_unsupported_length() { #[test] fn test_prk_too_short() { - use sha2::digest::generic_array::typenum::Unsigned; use sha2::digest::Digest; - let output_len = ::OutputSize::to_usize(); + let output_len = Sha256::output_size(); let prk = vec![0; output_len - 1]; assert!(Hkdf::::from_prk(&prk).is_err()); } -// Test Vectors from https://tools.ietf.org/html/rfc5869. -fn tests_sha1<'a>() -> Vec> { - vec![ - Test { - // Test Case 4 - ikm: "0b0b0b0b0b0b0b0b0b0b0b", - salt: "000102030405060708090a0b0c", - info: "f0f1f2f3f4f5f6f7f8f9", - length: 42, - prk: "9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243", - okm: "085a01ea1b10f36933068b56efa5ad81\ - a4f14b822f5b091568a9cdd4f155fda2\ - c22e422478d305f3f896", - }, - Test { - // Test Case 5 - ikm: "000102030405060708090a0b0c0d0e0f\ - 101112131415161718191a1b1c1d1e1f\ - 202122232425262728292a2b2c2d2e2f\ - 303132333435363738393a3b3c3d3e3f\ - 404142434445464748494a4b4c4d4e4f", - salt: "606162636465666768696a6b6c6d6e6f\ - 707172737475767778797a7b7c7d7e7f\ - 808182838485868788898a8b8c8d8e8f\ - 909192939495969798999a9b9c9d9e9f\ - a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", - info: "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf\ - c0c1c2c3c4c5c6c7c8c9cacbcccdcecf\ - d0d1d2d3d4d5d6d7d8d9dadbdcdddedf\ - e0e1e2e3e4e5e6e7e8e9eaebecedeeef\ - f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", - length: 82, - prk: "8adae09a2a307059478d309b26c4115a224cfaf6", - okm: "0bd770a74d1160f7c9f12cd5912a06eb\ - ff6adcae899d92191fe4305673ba2ffe\ - 8fa3f1a4e5ad79f3f334b3b202b2173c\ - 486ea37ce3d397ed034c7f9dfeb15c5e\ - 927336d0441f4c4300e2cff0d0900b52\ - d3b4", - }, - Test { - // Test Case 6 - ikm: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - salt: "", - info: "", - length: 42, - prk: "da8c8a73c7fa77288ec6f5e7c297786aa0d32d01", - okm: "0ac1af7002b3d761d1e55298da9d0506\ - b9ae52057220a306e07b6b87e8df21d0\ - ea00033de03984d34918", - }, - Test { - // Test Case 7 - ikm: "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", - salt: "", // "Not Provided" - info: "", - length: 42, - prk: "2adccada18779e7c2077ad2eb19d3f3e731385dd", - okm: "2c91117204d745f3500d636a62f64f0a\ - b3bae548aa53d423b0d1f27ebba6f5e5\ - 673a081d70cce7acfc48", - }, - ] -} - -#[test] -fn test_derive_sha1() { - let tests = tests_sha1(); - for t in tests.iter() { - let ikm = hex::decode(&t.ikm).unwrap(); - let salt = hex::decode(&t.salt).unwrap(); - let info = hex::decode(&t.info).unwrap(); - let salt = if salt.is_empty() { - None - } else { - Some(&salt[..]) - }; - let (prk, hkdf) = Hkdf::::extract(salt, &ikm[..]); - let mut okm = vec![0u8; t.length]; - assert!(hkdf.expand(&info[..], &mut okm).is_ok()); - - assert_eq!(hex::encode(prk), t.prk); - assert_eq!(hex::encode(&okm), t.okm); - - let prk = &hex::decode(&t.prk).unwrap(); - let hkdf = Hkdf::::from_prk(&prk).unwrap(); - assert!(hkdf.expand(&info[..], &mut okm).is_ok()); - - assert_eq!(hex::encode(&okm), t.okm); - } -} - #[test] +#[rustfmt::skip] fn test_derive_sha1_with_none() { - let ikm = hex::decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c").unwrap(); + let ikm = hex!("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"); let salt = None; - let info = hex::decode("").unwrap(); + let info = hex!(""); let (prk, hkdf) = Hkdf::::extract(salt, &ikm[..]); - let mut okm = vec![0u8; 42]; + let mut okm = [0u8; 42]; assert!(hkdf.expand(&info[..], &mut okm).is_ok()); - assert_eq!(hex::encode(prk), "2adccada18779e7c2077ad2eb19d3f3e731385dd"); assert_eq!( - hex::encode(&okm), - "2c91117204d745f3500d636a62f64f0a\ - b3bae548aa53d423b0d1f27ebba6f5e5\ - 673a081d70cce7acfc48" + prk[..], + hex!("2adccada18779e7c2077ad2eb19d3f3e731385dd")[..] + ); + assert_eq!( + okm[..], + hex!(" + 2c91117204d745f3500d636a62f64f0a + b3bae548aa53d423b0d1f27ebba6f5e5 + 673a081d70cce7acfc48 + ")[..], ); }