├── .github ├── dependabot.yml └── workflows │ ├── ansi-x963-kdf.yml │ ├── bake-kdf.yml │ ├── concat-kdf.yml │ ├── hkdf.yml │ ├── kbkdf.yml │ └── workspace.yml ├── .gitignore ├── .typos.toml ├── Cargo.lock ├── Cargo.toml ├── README.md ├── ansi-x963-kdf ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ └── lib.rs └── tests │ └── tests.rs ├── bake-kdf ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches │ └── benchmark.rs ├── src │ └── lib.rs └── tests │ └── tests.rs ├── concat-kdf ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ └── lib.rs └── tests │ └── tests.rs ├── hkdf ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches │ └── mod.rs ├── src │ ├── errors.rs │ ├── lib.rs │ └── sealed.rs └── tests │ ├── data │ ├── wycheproof-sha1.blb │ ├── wycheproof-sha256.blb │ ├── wycheproof-sha384.blb │ └── wycheproof-sha512.blb │ ├── rfc5869.rs │ ├── tests.rs │ └── wycheproof.rs └── kbkdf ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── src ├── lib.rs ├── sealed.rs └── tests.rs └── tests ├── data ├── CounterMode │ └── KDFCTR_gen.rsp ├── FeedbackModeNOzeroiv │ └── KDFFeedback_gen.rsp ├── FeedbackModenocounter │ └── KDFFeedback_gen.rsp ├── FeedbackModewzeroiv │ └── KDFFeedback_gen.rsp ├── PipelineModeWOCounterr │ └── KDFDblPipeline_gen.rsp └── PipelineModewithCounter │ └── KDFDblPipeline_gen.rsp └── kbkdf ├── main.rs └── parser.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | versioning-strategy: lockfile-only 5 | directory: "/" 6 | allow: 7 | - dependency-type: "all" 8 | groups: 9 | all-deps: 10 | patterns: 11 | - "*" 12 | schedule: 13 | interval: weekly 14 | open-pull-requests-limit: 10 15 | - package-ecosystem: github-actions 16 | directory: "/" 17 | schedule: 18 | interval: weekly 19 | open-pull-requests-limit: 10 20 | -------------------------------------------------------------------------------- /.github/workflows/ansi-x963-kdf.yml: -------------------------------------------------------------------------------- 1 | name: ansi-x963-kdf 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - ".github/workflows/ansi-x963-kdf.yml" 7 | - "ansi-x963-kdf/**" 8 | - "Cargo.*" 9 | push: 10 | branches: master 11 | 12 | defaults: 13 | run: 14 | working-directory: ansi-x963-kdf 15 | 16 | env: 17 | CARGO_INCREMENTAL: 0 18 | RUSTFLAGS: "-Dwarnings" 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | strategy: 24 | matrix: 25 | rust: 26 | - 1.85.0 # MSRV 27 | - stable 28 | target: 29 | - thumbv7em-none-eabi 30 | - wasm32-unknown-unknown 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: dtolnay/rust-toolchain@master 34 | with: 35 | toolchain: ${{ matrix.rust }} 36 | targets: ${{ matrix.target }} 37 | - run: cargo build --no-default-features --target ${{ matrix.target }} 38 | 39 | test: 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | rust: 44 | - 1.85.0 # MSRV 45 | - stable 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: dtolnay/rust-toolchain@master 49 | with: 50 | toolchain: ${{ matrix.rust }} 51 | - uses: RustCrypto/actions/cargo-hack-install@master 52 | - run: cargo hack test --feature-powerset 53 | - run: cargo test --release --all-features 54 | -------------------------------------------------------------------------------- /.github/workflows/bake-kdf.yml: -------------------------------------------------------------------------------- 1 | name: bake-kdf 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - ".github/workflows/bake-kdf.yml" 7 | - "bake-kdf/**" 8 | - "Cargo.*" 9 | push: 10 | branches: master 11 | 12 | defaults: 13 | run: 14 | working-directory: bake-kdf 15 | 16 | env: 17 | CARGO_INCREMENTAL: 0 18 | RUSTFLAGS: "-Dwarnings" 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | strategy: 24 | matrix: 25 | rust: 26 | - 1.85.0 # MSRV 27 | - stable 28 | target: 29 | - thumbv7em-none-eabi 30 | - wasm32-unknown-unknown 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: dtolnay/rust-toolchain@master 34 | with: 35 | toolchain: ${{ matrix.rust }} 36 | targets: ${{ matrix.target }} 37 | - run: cargo build --no-default-features --target ${{ matrix.target }} 38 | 39 | test: 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | rust: 44 | - 1.85.0 # MSRV 45 | - stable 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: dtolnay/rust-toolchain@master 49 | with: 50 | toolchain: ${{ matrix.rust }} 51 | - uses: RustCrypto/actions/cargo-hack-install@master 52 | - run: cargo hack test --feature-powerset 53 | - run: cargo test --release --all-features 54 | -------------------------------------------------------------------------------- /.github/workflows/concat-kdf.yml: -------------------------------------------------------------------------------- 1 | name: concat-kdf 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - ".github/workflows/concat-kdf.yml" 7 | - "concat-kdf/**" 8 | - "Cargo.*" 9 | push: 10 | branches: master 11 | 12 | defaults: 13 | run: 14 | working-directory: concat-kdf 15 | 16 | env: 17 | CARGO_INCREMENTAL: 0 18 | RUSTFLAGS: "-Dwarnings" 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | strategy: 24 | matrix: 25 | rust: 26 | - 1.85.0 # MSRV 27 | - stable 28 | target: 29 | - thumbv7em-none-eabi 30 | - wasm32-unknown-unknown 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: dtolnay/rust-toolchain@master 34 | with: 35 | toolchain: ${{ matrix.rust }} 36 | targets: ${{ matrix.target }} 37 | - run: cargo build --no-default-features --target ${{ matrix.target }} 38 | 39 | test: 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | rust: 44 | - 1.85.0 # MSRV 45 | - stable 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: dtolnay/rust-toolchain@master 49 | with: 50 | toolchain: ${{ matrix.rust }} 51 | - uses: RustCrypto/actions/cargo-hack-install@master 52 | - run: cargo hack test --feature-powerset 53 | - run: cargo test --release --all-features 54 | -------------------------------------------------------------------------------- /.github/workflows/hkdf.yml: -------------------------------------------------------------------------------- 1 | name: hkdf 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - ".github/workflows/hkdf.yml" 7 | - "hkdf/**" 8 | - "Cargo.*" 9 | push: 10 | branches: master 11 | 12 | defaults: 13 | run: 14 | working-directory: hkdf 15 | 16 | env: 17 | CARGO_INCREMENTAL: 0 18 | RUSTFLAGS: "-Dwarnings" 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | strategy: 24 | matrix: 25 | rust: 26 | - 1.85.0 # MSRV 27 | - stable 28 | target: 29 | - thumbv7em-none-eabi 30 | - wasm32-unknown-unknown 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: RustCrypto/actions/cargo-cache@master 34 | - uses: dtolnay/rust-toolchain@master 35 | with: 36 | toolchain: ${{ matrix.rust }} 37 | targets: ${{ matrix.target }} 38 | - run: cargo build --no-default-features --target ${{ matrix.target }} 39 | 40 | minimal-versions: 41 | # Temporarily disabled until hkdf 0.13.0-pre.0 gets published 42 | if: false 43 | uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master 44 | with: 45 | working-directory: ${{ github.workflow }} 46 | 47 | test: 48 | runs-on: ubuntu-latest 49 | strategy: 50 | matrix: 51 | rust: 52 | - 1.85.0 # MSRV 53 | - stable 54 | steps: 55 | - uses: actions/checkout@v4 56 | - uses: RustCrypto/actions/cargo-cache@master 57 | - uses: dtolnay/rust-toolchain@master 58 | with: 59 | toolchain: ${{ matrix.rust }} 60 | - uses: RustCrypto/actions/cargo-hack-install@master 61 | - run: cargo hack test --feature-powerset 62 | - run: cargo test --release --all-features 63 | -------------------------------------------------------------------------------- /.github/workflows/kbkdf.yml: -------------------------------------------------------------------------------- 1 | name: kbkdf 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - ".github/workflows/kbkdf.yml" 7 | - "kbkdf/**" 8 | - "Cargo.*" 9 | push: 10 | branches: master 11 | 12 | defaults: 13 | run: 14 | working-directory: kbkdf 15 | 16 | env: 17 | CARGO_INCREMENTAL: 0 18 | RUSTFLAGS: "-Dwarnings" 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | strategy: 24 | matrix: 25 | rust: 26 | - 1.85.0 # MSRV 27 | - stable 28 | target: 29 | - thumbv7em-none-eabi 30 | - wasm32-unknown-unknown 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: RustCrypto/actions/cargo-cache@master 34 | - uses: dtolnay/rust-toolchain@master 35 | with: 36 | toolchain: ${{ matrix.rust }} 37 | targets: ${{ matrix.target }} 38 | - run: cargo build --no-default-features --target ${{ matrix.target }} 39 | 40 | minimal-versions: 41 | if: false # Disabled because it can't handle git dependencies use to upgrade ecosystem 42 | uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master 43 | with: 44 | working-directory: ${{ github.workflow }} 45 | 46 | test: 47 | runs-on: ubuntu-latest 48 | strategy: 49 | matrix: 50 | rust: 51 | - 1.85.0 # MSRV 52 | - stable 53 | steps: 54 | - uses: actions/checkout@v4 55 | - uses: RustCrypto/actions/cargo-cache@master 56 | - uses: dtolnay/rust-toolchain@master 57 | with: 58 | toolchain: ${{ matrix.rust }} 59 | - uses: RustCrypto/actions/cargo-hack-install@master 60 | - run: cargo hack test --feature-powerset 61 | - run: cargo test --release --all-features 62 | -------------------------------------------------------------------------------- /.github/workflows/workspace.yml: -------------------------------------------------------------------------------- 1 | name: Workspace 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - README.md 7 | push: 8 | branches: master 9 | paths-ignore: 10 | - README.md 11 | 12 | jobs: 13 | clippy: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: RustCrypto/actions/cargo-cache@master 18 | - uses: dtolnay/rust-toolchain@master 19 | with: 20 | toolchain: 1.87.0 21 | components: clippy 22 | - run: cargo clippy --all -- -D warnings 23 | 24 | rustfmt: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout sources 28 | uses: actions/checkout@v4 29 | 30 | - name: Install stable toolchain 31 | uses: dtolnay/rust-toolchain@master 32 | with: 33 | toolchain: stable 34 | components: rustfmt 35 | - name: Run cargo fmt 36 | run: cargo fmt --all -- --check 37 | 38 | typos: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v4 42 | - uses: crate-ci/typos@v1 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | */target/ 3 | */*/target/ 4 | -------------------------------------------------------------------------------- /.typos.toml: -------------------------------------------------------------------------------- 1 | [files] 2 | extend-exclude = [ 3 | ".git/", 4 | "kbkdf/tests/data/" 5 | ] 6 | 7 | [default] 8 | extend-ignore-re = [ 9 | # Patterns which appear to be 16 or more characters of Base16 10 | '"[0-9A-Fa-f +]{16,}"', 11 | ] 12 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aes" 7 | version = "0.9.0-pre.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "38e4da00d9978020ddaa556c1747cfcafc3f375cfadb109acfe8b752cfc373bf" 10 | dependencies = [ 11 | "cfg-if", 12 | "cipher", 13 | "cpufeatures", 14 | ] 15 | 16 | [[package]] 17 | name = "ansi-x963-kdf" 18 | version = "0.1.0-rc.0" 19 | dependencies = [ 20 | "digest", 21 | "hex-literal", 22 | "sha2", 23 | ] 24 | 25 | [[package]] 26 | name = "bake-kdf" 27 | version = "0.0.1" 28 | dependencies = [ 29 | "belt-hash", 30 | "hex-literal", 31 | ] 32 | 33 | [[package]] 34 | name = "belt-block" 35 | version = "0.1.2" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "d9aa1eef3994e2ccd304a78fe3fea4a73e5792007f85f09b79bb82143ca5f82b" 38 | 39 | [[package]] 40 | name = "belt-hash" 41 | version = "0.2.0-rc.0" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "bb51c5f2d38f4751a11964ac3988a05812fdec736b15bc0424f2cec697a85728" 44 | dependencies = [ 45 | "belt-block", 46 | "digest", 47 | ] 48 | 49 | [[package]] 50 | name = "blobby" 51 | version = "0.4.0-pre.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "4a859067dcb257cb2ae028cb821399b55140b76fb8b2a360e052fe109019db43" 54 | 55 | [[package]] 56 | name = "block-buffer" 57 | version = "0.11.0-rc.4" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "a229bfd78e4827c91b9b95784f69492c1b77c1ab75a45a8a037b139215086f94" 60 | dependencies = [ 61 | "hybrid-array", 62 | ] 63 | 64 | [[package]] 65 | name = "cfg-if" 66 | version = "1.0.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 69 | 70 | [[package]] 71 | name = "cipher" 72 | version = "0.5.0-pre.8" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "276974d2acb7cf592603150941fc1ff6442acdeb1dc653ac2825928f4703c131" 75 | dependencies = [ 76 | "crypto-common", 77 | "inout", 78 | ] 79 | 80 | [[package]] 81 | name = "cmac" 82 | version = "0.8.0-pre.4" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "f1dc01cf22561395b0f7859e50943c03be79d20353592a3366f8aa7fd01037fe" 85 | dependencies = [ 86 | "cipher", 87 | "dbl", 88 | "digest", 89 | ] 90 | 91 | [[package]] 92 | name = "concat-kdf" 93 | version = "0.2.0-pre" 94 | dependencies = [ 95 | "digest", 96 | "hex-literal", 97 | "sha2", 98 | ] 99 | 100 | [[package]] 101 | name = "cpufeatures" 102 | version = "0.2.17" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 105 | dependencies = [ 106 | "libc", 107 | ] 108 | 109 | [[package]] 110 | name = "crypto-common" 111 | version = "0.2.0-rc.3" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "8a23fa214dea9efd4dacee5a5614646b30216ae0f05d4bb51bafb50e9da1c5be" 114 | dependencies = [ 115 | "hybrid-array", 116 | ] 117 | 118 | [[package]] 119 | name = "dbl" 120 | version = "0.4.0-rc.2" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "cb24c766034b76390c67f3d9c44e63019febeb4cc39e4ba40b5fc79e20c898e1" 123 | dependencies = [ 124 | "hybrid-array", 125 | ] 126 | 127 | [[package]] 128 | name = "digest" 129 | version = "0.11.0-rc.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "460dd7f37e4950526b54a5a6b1f41b6c8e763c58eb9a8fc8fc05ba5c2f44ca7b" 132 | dependencies = [ 133 | "block-buffer", 134 | "crypto-common", 135 | "subtle", 136 | ] 137 | 138 | [[package]] 139 | name = "hex" 140 | version = "0.4.3" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 143 | 144 | [[package]] 145 | name = "hex-literal" 146 | version = "1.0.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "bcaaec4551594c969335c98c903c1397853d4198408ea609190f420500f6be71" 149 | 150 | [[package]] 151 | name = "hkdf" 152 | version = "0.13.0-rc.0" 153 | dependencies = [ 154 | "blobby", 155 | "hex-literal", 156 | "hmac", 157 | "sha1", 158 | "sha2", 159 | ] 160 | 161 | [[package]] 162 | name = "hmac" 163 | version = "0.13.0-rc.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "8dc6a2fcc35ab09136c6df2cdf9ca49790701420a3a6b5db0987dddbabc79b21" 166 | dependencies = [ 167 | "digest", 168 | ] 169 | 170 | [[package]] 171 | name = "hybrid-array" 172 | version = "0.3.1" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "891d15931895091dea5c47afa5b3c9a01ba634b311919fd4d41388fa0e3d76af" 175 | dependencies = [ 176 | "typenum", 177 | ] 178 | 179 | [[package]] 180 | name = "inout" 181 | version = "0.2.0-rc.5" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "c774c86bce20ea04abe1c37cf0051c5690079a3a28ef5fdac2a5a0412b3d7d74" 184 | dependencies = [ 185 | "hybrid-array", 186 | ] 187 | 188 | [[package]] 189 | name = "kbkdf" 190 | version = "0.1.0-pre.0" 191 | dependencies = [ 192 | "aes", 193 | "cmac", 194 | "digest", 195 | "hex", 196 | "hex-literal", 197 | "hmac", 198 | "sha1", 199 | "sha2", 200 | ] 201 | 202 | [[package]] 203 | name = "libc" 204 | version = "0.2.172" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 207 | 208 | [[package]] 209 | name = "sha1" 210 | version = "0.11.0-rc.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "6f9318facddf9ac32a33527066936837e189b3f23ced6edc1603720ead5e2b3d" 213 | dependencies = [ 214 | "cfg-if", 215 | "cpufeatures", 216 | "digest", 217 | ] 218 | 219 | [[package]] 220 | name = "sha2" 221 | version = "0.11.0-rc.0" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "aa1d2e6b3cc4e43a8258a9a3b17aa5dfd2cc5186c7024bba8a64aa65b2c71a59" 224 | dependencies = [ 225 | "cfg-if", 226 | "cpufeatures", 227 | "digest", 228 | ] 229 | 230 | [[package]] 231 | name = "subtle" 232 | version = "2.6.1" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 235 | 236 | [[package]] 237 | name = "typenum" 238 | version = "1.18.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 241 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "bake-kdf", 5 | "hkdf", 6 | "concat-kdf", 7 | "ansi-x963-kdf", 8 | "kbkdf", 9 | ] 10 | 11 | [profile.dev] 12 | opt-level = 2 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RustCrypto: Key Derivation Functions 2 | 3 | [![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link] ![Apache2/MIT licensed][license-image] 4 | 5 | Collection of [Key Derivation Functions][KDF] (KDF) written in pure Rust. 6 | 7 | ## Supported Algorithms 8 | 9 | | Algorithm | Crate | Crates.io | Documentation | MSRV | 10 | |--------------|-------|:---------:|:-------------:|:----:| 11 | | [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] | 12 | | [bake-kdf] | [`bake-kdf`] | [![crates.io](https://img.shields.io/crates/v/bake-kdf.svg)](https://crates.io/crates/bake-kdf) | [![Documentation](https://docs.rs/bake-kdf/badge.svg)](https://docs.rs/bake-kdf) | ![MSRV 1.81][msrv-1.81] | 13 | | [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.81][msrv-1.81] | 14 | | [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.81][msrv-1.81] | 15 | | [KBKDF] | [`kbkdf`] | [![crates.io](https://img.shields.io/crates/v/kbkdf.svg)](https://crates.io/crates/kbkdf) | [![Documentation](https://docs.rs/kbkdf/badge.svg)](https://docs.rs/kbkdf) | ![MSRV 1.81][msrv-1.81] | 16 | 17 | *NOTE: for password-based KDFs (e.g. Argon2, PBKDF2, scrypt), please see [RustCrypto/password-hashes]* 18 | 19 | ### Minimum Supported Rust Version (MSRV) Policy 20 | 21 | MSRV bumps are considered breaking changes and will be performed only with minor version bump. 22 | 23 | ## License 24 | 25 | All crates licensed under either of 26 | 27 | * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 28 | * [MIT license](http://opensource.org/licenses/MIT) 29 | 30 | at your option. 31 | 32 | ### Contribution 33 | 34 | 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. 35 | 36 | [//]: # (badges) 37 | 38 | [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg 39 | [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260043-KDFs 40 | [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg 41 | [deps-image]: https://deps.rs/repo/github/RustCrypto/KDFs/status.svg 42 | [deps-link]: https://deps.rs/repo/github/RustCrypto/KDFs 43 | [msrv-1.81]: https://img.shields.io/badge/rustc-1.81+-blue.svg 44 | 45 | [//]: # (crates) 46 | 47 | [`ansi-x963-kdf`]: ./ansi-x963-kdf 48 | [`bake-kdf`]: ./bake-kdf 49 | [`concat-kdf`]: ./concat-kdf 50 | [`hkdf`]: ./hkdf 51 | [`kbkdf`]: ./kbkdf 52 | 53 | [//]: # (algorithms) 54 | 55 | [KDF]: https://en.wikipedia.org/wiki/Key_derivation_function 56 | [HKDF]: https://en.wikipedia.org/wiki/HKDF 57 | [ANSI-X9.63-KDF]: https://www.secg.org/sec1-v2.pdf 58 | [bake-kdf]: https://apmi.bsu.by/assets/files/std/bake-spec19.pdf 59 | [Concat-KDF]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-56ar.pdf 60 | [KBKDF]: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/key-derivation 61 | [RustCrypto/password-hashes]: https://github.com/RustCrypto/password-hashes 62 | -------------------------------------------------------------------------------- /ansi-x963-kdf/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## 0.0.1 (2025-02-13) 8 | - Initial release 9 | 10 | -------------------------------------------------------------------------------- /ansi-x963-kdf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ansi-x963-kdf" 3 | version = "0.1.0-rc.0" 4 | description = "ANSI X9.63 Key Derivation Function" 5 | authors = ["RustCrypto Developers"] 6 | license = "MIT OR Apache-2.0" 7 | readme = "README.md" 8 | edition = "2024" 9 | documentation = "https://docs.rs/ansi-x963-kdf" 10 | repository = "https://github.com/RustCrypto/KDFs" 11 | keywords = ["crypto", "ansi-x963-kdf", "KDF", "SEC1"] 12 | categories = ["cryptography", "no-std"] 13 | rust-version = "1.85" 14 | 15 | [dependencies] 16 | digest = "0.11.0-rc.0" 17 | 18 | [dev-dependencies] 19 | hex-literal = "1" 20 | sha2 = { version = "0.11.0-rc.0", default-features = false } 21 | -------------------------------------------------------------------------------- /ansi-x963-kdf/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /ansi-x963-kdf/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024-2025 RustCrypto Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /ansi-x963-kdf/README.md: -------------------------------------------------------------------------------- 1 | # RustCrypto: ANSI X9.63 Key Derivation Function 2 | 3 | [![crate][crate-image]][crate-link] 4 | [![Docs][docs-image]][docs-link] 5 | [![Build Status][build-image]][build-link] 6 | ![Apache2/MIT licensed][license-image] 7 | ![Rust Version][rustc-image] 8 | [![Project Chat][chat-image]][chat-link] 9 | 10 | Pure Rust implementation of the ANSI X9.63 Key Derivation Function (ANSI-X9.63-KDF) generic over hash function. 11 | This function is described in the section 3.6.1 of [SEC 1: Elliptic Curve Cryptography](http://www.secg.org/sec1-v2.pdf). 12 | 13 | # Usage 14 | 15 | The most common way to use ANSI-X9.63-KDF is as follows: you generate a shared secret with other 16 | party (e.g. via Diffie-Hellman algorithm) and use key derivation function to derive a shared key. 17 | 18 | ```rust 19 | use hex_literal::hex; 20 | use sha2::Sha256; 21 | 22 | let mut key = [0u8; 16]; 23 | ansi_x963_kdf::derive_key_into::(b"secret", b"shared-info", &mut key).unwrap(); 24 | assert_eq!(key, hex!("8dbb1d50bcc7fc782abc9db5c64a2826")); 25 | ``` 26 | 27 | ## License 28 | 29 | Licensed under either of: 30 | 31 | * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 32 | * [MIT license](http://opensource.org/licenses/MIT) 33 | 34 | at your option. 35 | 36 | ### Contribution 37 | 38 | Unless you explicitly state otherwise, any contribution intentionally submitted 39 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 40 | dual licensed as above, without any additional terms or conditions. 41 | 42 | [crate-image]: https://img.shields.io/crates/v/ansi-x963-kdf.svg?logo=rust 43 | [crate-link]: https://crates.io/crates/ansi-x963-kdf 44 | [docs-image]: https://docs.rs/ansi-x963-kdf/badge.svg 45 | [docs-link]: https://docs.rs/ansi-x963-kdf/ 46 | [build-image]: https://github.com/RustCrypto/KDFs/actions/workflows/ansi-x963-kdf.yml/badge.svg 47 | [build-link]: https://github.com/RustCrypto/KDFs/actions/workflows/ansi-x963-kdf.yml 48 | [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg 49 | [rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg 50 | [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg 51 | [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260043-KDFs 52 | -------------------------------------------------------------------------------- /ansi-x963-kdf/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![doc = include_str!("../README.md")] 3 | #![doc( 4 | html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", 5 | html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" 6 | )] 7 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 8 | #![forbid(unsafe_code)] 9 | #![warn(missing_docs)] 10 | 11 | use core::fmt; 12 | use digest::{Digest, FixedOutputReset, array::typenum::Unsigned}; 13 | 14 | /// Derives `key` in-place from `secret` and `shared_info`. 15 | /// 16 | /// # Example 17 | /// ``` 18 | /// use hex_literal::hex; 19 | /// use sha2::Sha256; 20 | /// 21 | /// let mut key = [0u8; 16]; 22 | /// ansi_x963_kdf::derive_key_into::(b"secret", b"shared-info", &mut key).unwrap(); 23 | /// assert_eq!(key, hex!("8dbb1d50bcc7fc782abc9db5c64a2826")); 24 | /// ``` 25 | #[inline] 26 | pub fn derive_key_into(secret: &[u8], shared_info: &[u8], key: &mut [u8]) -> Result<(), Error> 27 | where 28 | D: Digest + FixedOutputReset, 29 | { 30 | if secret.is_empty() { 31 | return Err(Error::NoSecret); 32 | } 33 | 34 | if key.is_empty() { 35 | return Err(Error::NoOutput); 36 | } 37 | 38 | // 1. Check that |Z| + |SharedInfo| + 4 < hashmaxlen 39 | // where "hashmaxlen denote the maximum length in octets of messages that can be hashed using Hash". 40 | // N.B.: `D::OutputSize::U64 * (u32::MAX as u64)`` is currently used as an approximation of hashmaxlen. 41 | if secret.len() as u64 + shared_info.len() as u64 + 4 >= D::OutputSize::U64 * (u32::MAX as u64) 42 | { 43 | return Err(Error::InputOverflow); 44 | } 45 | 46 | // 2. Check that keydatalen < hashlen × (2^32 − 1) 47 | if key.len() as u64 >= D::OutputSize::U64 * (u32::MAX as u64) { 48 | return Err(Error::CounterOverflow); 49 | } 50 | 51 | let mut digest = D::new(); 52 | 53 | // 3. Initiate a 4 octet, big-endian octet string Counter as 00000001 54 | let mut counter: u32 = 1; 55 | 56 | // 4. For i = 1 to keydatalen/hashlen, 57 | for chunk in key.chunks_mut(D::OutputSize::USIZE) { 58 | // 4.1 Compute Ki = Hash(Z ‖ Counter ‖ [SharedInfo]) using the selected hash function 59 | Digest::update(&mut digest, secret); 60 | Digest::update(&mut digest, counter.to_be_bytes()); 61 | Digest::update(&mut digest, shared_info); 62 | chunk.copy_from_slice(&digest.finalize_reset()[..chunk.len()]); 63 | // 4.2. Increment Counter 64 | counter += 1; 65 | } 66 | 67 | Ok(()) 68 | } 69 | 70 | /// ANSI-X9.63 KDF errors. 71 | #[derive(Clone, Copy, Debug, PartialEq)] 72 | pub enum Error { 73 | /// The length of the secret is zero. 74 | NoSecret, 75 | /// The length of the output is zero. 76 | NoOutput, 77 | /// The length of the input is too big 78 | InputOverflow, 79 | /// The length of the output is too big. 80 | CounterOverflow, 81 | } 82 | 83 | impl fmt::Display for Error { 84 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 85 | f.write_str(match self { 86 | Error::NoSecret => "Buffer for secret has zero length.", 87 | Error::NoOutput => "Buffer for key has zero length.", 88 | Error::InputOverflow => "Input length is to big.", 89 | Error::CounterOverflow => "Requested key length is to big.", 90 | }) 91 | } 92 | } 93 | 94 | impl core::error::Error for Error {} 95 | -------------------------------------------------------------------------------- /ansi-x963-kdf/tests/tests.rs: -------------------------------------------------------------------------------- 1 | //! Tests for ansi-x963-kdf 2 | //! 3 | //! Test vectors have been generated using the java-based Bouncy-Castle 4 | //! KDF2 implementation [KDF2BytesGenerator][1] 5 | //! 6 | //! [1]: https://downloads.bouncycastle.org/java/docs/bcprov-jdk18on-javadoc/ 7 | use digest::{Digest, FixedOutputReset}; 8 | use hex_literal::hex; 9 | use sha2::{Sha224, Sha256, Sha512}; 10 | 11 | struct Fixture<'a> { 12 | secret: &'a [u8], 13 | shared_info: &'a [u8], 14 | expected_key: &'a [u8], 15 | } 16 | 17 | fn test_key_derivation(fixtures: &[Fixture]) 18 | where 19 | D: Digest + FixedOutputReset, 20 | { 21 | for f in fixtures.iter() { 22 | let mut buf = [0; 256]; 23 | for key_length in 1..f.expected_key.len() { 24 | let key = &mut buf[..key_length]; 25 | ansi_x963_kdf::derive_key_into::(f.secret, f.shared_info, key).unwrap(); 26 | assert_eq!(&f.expected_key[..key_length], key); 27 | } 28 | } 29 | } 30 | 31 | #[test] 32 | fn test_input_output_sha224() { 33 | let fixtures = [ 34 | Fixture { 35 | secret: &hex!("00"), 36 | shared_info: &[], 37 | expected_key: &hex!( 38 | "4a6ebc83b8e2b19eea640500be6bcffdddaa07b8b2f81f2c533940e4e6ad6cfd" 39 | "e680e5ba8eb25351402f0e75a6246cf006f6dd2187185af41d04abb648124e27" 40 | "827cf4f2b871f9bc3fb2313c4f146b44faf3be170f2d87296c9b533c516b9a48" 41 | "dc73f73bafcc610bce18965566e3d0ca0f083c8a6a20b3b84457486e204a1014" 42 | ), 43 | }, 44 | Fixture { 45 | secret: &hex!("00"), 46 | shared_info: &hex!("00"), 47 | expected_key: &hex!( 48 | "4bfb11552c4bf91bce4833aa06f854ceb8a3f7e435f42907e6d86e7597b20789" 49 | "aba17dccaf09d3e26bc3dd0ad6051f0e46b830cc57091bd0ba1da24a4ab96492" 50 | "3b47b4b73ccb6cec6aa1e6339f4fa93995baef4a3ace3cadcf1ee63eaecb868f" 51 | "2f8ca06def29797d33673803a185574dec0c4bc0a5d0d0ffb4c527eb738d5bd2" 52 | "4fcc424f46785f693f60ea2f00d3ff38f9f1e73847a50bf6ece7bda4abe3767f" 53 | "19f0a767f2ea69ed84f4f5837084edd2945c39d4b459b38fc2e83264ba47896a" 54 | "a3e106058f1d13f2b1422c7ff33c279dfc7a42cc4f775babcae8122a4dbdf427" 55 | "a8634e9464607fe4a6f91fc59f07ab42f18dac313384b50d572cdff0b406cff2" 56 | ), 57 | }, 58 | Fixture { 59 | secret: &hex!("ba5eba11bedabb1ebe5077edb0a710adb01dfacecab005eca11ab1eca55e77e011"), 60 | shared_info: &hex!("f005ba1100ddba11"), 61 | expected_key: &hex!( 62 | "20328557e258ecbe845fcde1002aa36dba5e29383d1b9813c2410819c09bd7d7" 63 | "5b75f4d2ca71354080b64b3e8e3ef457f22517b074cbbbbf11d660b7b4706de1" 64 | "5678893c6712e104b34fb776a90341c905a028bf1892aa4487899ef4436f4ac6" 65 | "d436db25763c7fa7d43fbedac386aa69f5b156d4a84ede0b4371d34eb083fce1" 66 | "6cb6e051e846a923a82707925838371797b09fc94134d33b48e0ab9175fdbd90" 67 | "cd57b1570d55f5d4a391f5c15660757c447e0480bd6b6f0ca80a4e3ab5c40220" 68 | "7d1edcc2210eb77aff4eda6e35afce2815d82ab242574b7b9d0e72d8daa1c853" 69 | "e0b3dad4cb384ce70c5a23afd4f1e35a01fdd14f78812a5a99a93f4d57877901" 70 | ), 71 | }, 72 | ]; 73 | 74 | test_key_derivation::(&fixtures); 75 | } 76 | 77 | #[test] 78 | fn test_input_output_sha256() { 79 | let fixtures = [ 80 | Fixture { 81 | secret: &hex!("00"), 82 | shared_info: &[], 83 | expected_key: &hex!( 84 | "15f2f1a4339f5f2a313b95015cad8124d054a171ac2f31cf529dda7cfb6a38b4" 85 | "89eefc18fa4b815bd1aded2f24eb28885993aa00b6d0171bf5005f9d39aaea10" 86 | "016a682d1df4f869b32c48b0a9b442a1493949fb85d951d121c1143bd3d5c1af" 87 | "b59024333110b3108625f25447665c1ebf10c6a6bbe9f018c421f4b0dcb5a993" 88 | "42a5578600f1b0902c599a39268c12bdb1e820fd9a82212db588a71ae74cb6e4" 89 | "1f8a792ae7c5800a0b0e3aea6ed808bedca2b0a3cc8f7b22c5effbd545f632c2" 90 | "043a0631871a3f67ac03c5f8406b69a0dc14bd5b23e55f27a5d4462b0f0a2d23" 91 | "18519afd330d3447bb196dd75ea7a7998db6f2fcb2a5dc134f35690a2dbcc072" 92 | ), 93 | }, 94 | Fixture { 95 | secret: &hex!("00"), 96 | shared_info: &hex!("00"), 97 | expected_key: &hex!( 98 | "588611f65741c171a3d92c1d5343f5dd67f4fc472fc56f01c9bc568f5ac2a623" 99 | "55af2e3db27cf364b9465ea89a489710da6c78ecc59ddf3ac6203261a6649d9e" 100 | "45673cfcd9849e761a24b07d99f5c35167c343244c160b973b55a29408d9d988" 101 | "654670625fbd22634494df9f4f9a5328352eb92b4104612eef6dff382c119064" 102 | "785b35d50e5df9eee4bb06e5b102b1088d149500e934c04eac6936a09e4b36d1" 103 | "1e4f69ae41148ec0d7b5cca9bde9db8b850660e759c75f32154bb60357145ed3" 104 | "c0112a61a92f4eacd699c70a603df40f38babf6420587478c05ec70670e7221e" 105 | "ce2081d38382369c0d2ec51f89db2e29146d555c7c2aa62518962824682553a7" 106 | ), 107 | }, 108 | Fixture { 109 | secret: &hex!("ba5eba11bedabb1ebe5077edb0a710adb01dfacecab005eca11ab1eca55e77e011"), 110 | shared_info: &hex!("f005ba1100ddba11"), 111 | expected_key: &hex!( 112 | "41bf219e0dedf77305f1f79739fd917b3311e61dd504150d6f3c40195837c75a" 113 | "441fd05332d739a43fd70e11e4be66683eb05586c6c03bbf6d8030990e724a38" 114 | "c2ab1f5c22b0f47a84a2699d11701c6bfb3337e606130522f4f7a26df3b1cb95" 115 | "28ca56781af9af361e7c2ac64d50f73d275d5a6c83fc67b2e05f20ab9b595cce" 116 | "b8f205c57993647bf64c6f4ad8899eb5d0111efed1859006ec256b2e8cbb058b" 117 | "b83a8d40fa7f435037acd155b27a87716fdd7619b900f051a2437539f830789b" 118 | "f71080ff642285a01ff2db3e11ca5377c389be3f3851611cc8189728496fddca" 119 | "cac6b89565fd78a1b8d4c8d407ff45e39610526668abacabede347d5c1e9fb69" 120 | ), 121 | }, 122 | ]; 123 | 124 | test_key_derivation::(&fixtures); 125 | } 126 | 127 | #[test] 128 | fn test_input_output_sha512() { 129 | let fixtures = [ 130 | Fixture { 131 | secret: &hex!("00"), 132 | shared_info: &[], 133 | expected_key: &hex!( 134 | "b8eef223e484fe7a872e4db84711a01db365b205e477c3e3170f26623e2fa230" 135 | "4d93f6c04337d0ea7454d1f2073f8eb8ee58b361438b61f363eb1037a77f716c" 136 | "e89b92de1146cf3831eff44361d872f61dea1f05b3e08a9330c302949f6c93bd" 137 | "3e908f5ce5444e45a47bc0625600fff575472f04bcecc393387c244a93fbd4f4" 138 | "26b22edbdaa5eef8565feb1d6a3c46dedb89c00efcaf3f5d95d53f936b570efb" 139 | "18db044083a075f3d1322378a07f00694e4e21a535d91e893cacac87d877b2ab" 140 | "da0cff964fd1c291b759c38657bc7904be9f98cc8794099a6351b68f382e2df8" 141 | "79cab5d5a1d7f5e9d6461f015b11c47fb14cf99e496905fa95e8d7d5ec59a493" 142 | ), 143 | }, 144 | Fixture { 145 | secret: &hex!("00"), 146 | shared_info: &hex!("00"), 147 | expected_key: &hex!( 148 | "74cc6e00677ea1683c3c3fbc6337101db4e2ffdd0053a8783fd4c9f5b53117db" 149 | "9089ce3beef287cbe273a7c47ad1e88509842f9a70ff354280dc7a8e1c61214a" 150 | "e698b4186af5628a28dad9ff4b25d0cfbceac9c9c522d496f8513338a9426991" 151 | "2e0bbd2b2c500b303dae963b707ed4a05e9f57eb0c7de06da884669a93dbb29b" 152 | "3d262e7c98e24f8cd68d0ea44fe9d5e4e0b033b0c3f77193cdf2163dfac30da9" 153 | "eb39b147e2d9746dd1149ac512920d8e8316577e6713498beb7fa838a80b1736" 154 | "383001d5151582a16bcf9fcc38edbafaf18ab976e01a0244b462c6b6f907ba14" 155 | "32d14e641961c3d48e300ec5561424c4b8125cf172d06f9368bfdec0d5c57b8b" 156 | ), 157 | }, 158 | Fixture { 159 | secret: &hex!("ba5eba11bedabb1ebe5077edb0a710adb01dfacecab005eca11ab1eca55e77e011"), 160 | shared_info: &hex!("f005ba1100ddba11"), 161 | expected_key: &hex!( 162 | "ae21b84e638fc7de4d838d2a7232655c39d2794116f00e43891170c0a16df11c" 163 | "15afbdb903c5722e22afc885c0f851c2ccacc2a0802437bc5bef6c18a0573246" 164 | "65de72200dac5321ed92f530ed441bc194c402055419d73f52165a2bf9985fab" 165 | "756abce8e3b9c5e4a3d179b2eceaa6ef7b335245f480ed32a7f847921ab5e3c1" 166 | "a8867aff9802e6f8cec4d6a5fdf3cc0c2c1a14f08ec4df3654f2579164c6ed90" 167 | "a2262a8d492a0aa0942838952dc89f494018da5dd16c0b18ca6a9837685489bf" 168 | "a55debb243045e83a730e5e08917836181693cb4ab1827e968e3bb0e8e3b9a0e" 169 | "7cdab180f59168211dad86eb88fc3b4bc1dbeb0c8a8c967c5e0d1b2a84bf215c" 170 | ), 171 | }, 172 | ]; 173 | 174 | test_key_derivation::(&fixtures); 175 | } 176 | 177 | #[test] 178 | fn test_no_secret() { 179 | assert_eq!( 180 | ansi_x963_kdf::derive_key_into::(&[], &[], &mut [0u8; 42]), 181 | Err(ansi_x963_kdf::Error::NoSecret) 182 | ); 183 | } 184 | 185 | #[test] 186 | fn test_no_output() { 187 | assert_eq!( 188 | ansi_x963_kdf::derive_key_into::(&[0u8; 42], &[], &mut [0u8; 0]), 189 | Err(ansi_x963_kdf::Error::NoOutput) 190 | ); 191 | } 192 | -------------------------------------------------------------------------------- /bake-kdf/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## 0.0.1 (2025-02-13) 8 | - Initial release 9 | -------------------------------------------------------------------------------- /bake-kdf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bake-kdf" 3 | version = "0.0.1" 4 | description = "bake-kdf function (STB 34.101.66-2014)" 5 | authors = ["RustCrypto Developers"] 6 | license = "MIT OR Apache-2.0" 7 | readme = "README.md" 8 | edition = "2024" 9 | rust-version = "1.85" 10 | documentation = "https://docs.rs/bake-kdf" 11 | repository = "https://github.com/RustCrypto/KDFs" 12 | keywords = ["crypto", "bake", "stb", "kdf"] 13 | categories = ["cryptography", "no-std"] 14 | 15 | [dependencies] 16 | belt-hash = { version = "0.2.0-rc.0", default-features = false } 17 | 18 | [dev-dependencies] 19 | hex-literal = "1" 20 | 21 | [package.metadata.docs.rs] 22 | all-features = true 23 | -------------------------------------------------------------------------------- /bake-kdf/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /bake-kdf/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024-2025 RustCrypto Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /bake-kdf/README.md: -------------------------------------------------------------------------------- 1 | # RustCrypto: bake-kdf 2 | 3 | [![crate][crate-image]][crate-link] 4 | [![Docs][docs-image]][docs-link] 5 | [![Build Status][build-image]][build-link] 6 | ![Apache2/MIT licensed][license-image] 7 | ![Rust Version][rustc-image] 8 | [![Project Chat][chat-image]][chat-link] 9 | 10 | 11 | Pure Rust implementation of the [bake-kdf][1] function. 12 | 13 | [1]: https://apmi.bsu.by/assets/files/std/bake-spec19.pdf 14 | 15 | # Examples 16 | 17 | ```rust 18 | use bake_kdf::bake_kdf; 19 | use hex_literal::hex; 20 | let x = [0x42; 32]; 21 | let s = [0x24; 8]; 22 | let c = 0x00; 23 | let key = bake_kdf(&x, &s, c); 24 | 25 | assert_eq!(key, hex!("bbd7ece0080bee33c776a140f8d807a113a119a4e4d4270f9f2018fbd5e6292e")); 26 | ``` 27 | 28 | ## License 29 | 30 | Licensed under either of: 31 | 32 | * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 33 | * [MIT license](http://opensource.org/licenses/MIT) 34 | 35 | at your option. 36 | 37 | ### Contribution 38 | 39 | Unless you explicitly state otherwise, any contribution intentionally submitted 40 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 41 | dual licensed as above, without any additional terms or conditions. 42 | 43 | 44 | [//]: # (badges) 45 | 46 | [crate-image]: https://img.shields.io/crates/v/bake-kdf.svg?logo=rust 47 | [crate-link]: https://crates.io/crates/bake-kdf 48 | [docs-image]: https://docs.rs/bake-kdf/badge.svg 49 | [docs-link]: https://docs.rs/bake-kdf/ 50 | [build-image]: https://github.com/RustCrypto/KDFs/actions/workflows/bake-kdf.yml/badge.svg 51 | [build-link]: https://github.com/RustCrypto/KDFs/actions/workflows/bake-kdf.yml 52 | [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg 53 | [rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg 54 | [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg 55 | [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260043-KDFs 56 | -------------------------------------------------------------------------------- /bake-kdf/benches/benchmark.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | 4 | use bake_kdf::bake_kdf; 5 | use test::Bencher; 6 | 7 | #[bench] 8 | fn bake_kdf_benchmark(b: &mut Bencher) { 9 | let secret = [0u8; 32]; 10 | let iv = [0u8; 64]; 11 | let c = 0u128; 12 | b.iter(|| { 13 | let (secret, iv, c) = test::black_box((&secret, &iv, c)); 14 | test::black_box(bake_kdf(secret, iv, c)); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /bake-kdf/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![doc = include_str!("../README.md")] 3 | #![doc( 4 | html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", 5 | html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" 6 | )] 7 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 8 | #![forbid(unsafe_code)] 9 | #![warn(missing_docs)] 10 | 11 | use belt_hash::digest::FixedOutput; 12 | use belt_hash::{BeltHash, Digest, block_api::belt_compress}; 13 | 14 | /// `belt-keyexpand` key expansion algorithm described in STB 34.101.34-2020 8.1.2. 15 | /// 16 | /// # Panics 17 | /// If `N` is not equal to 16, 24, or 32. 18 | // TODO: use compile-time checks for `N` 19 | #[inline] 20 | pub fn belt_keyexpand(k: &[u8; N]) -> [u32; 8] { 21 | let mut t = [0u32; 8]; 22 | // TODO: move this conversion into `belt_keyrep` when we will be able 23 | // to use generic parameters as `[u32; N / 4]`. 24 | for (src, dst) in k.chunks_exact(4).zip(t.iter_mut()) { 25 | *dst = u32::from_le_bytes(src.try_into().unwrap()); 26 | } 27 | match N { 28 | 16 => { 29 | t[4] = t[0]; 30 | t[5] = t[1]; 31 | t[6] = t[2]; 32 | t[7] = t[3]; 33 | } 34 | 24 => { 35 | t[6] = t[0] ^ t[1] ^ t[2]; 36 | t[7] = t[3] ^ t[4] ^ t[5]; 37 | } 38 | 32 => {} 39 | _ => panic!("Invalid key size n={N}. Expected 16, 24, or 32."), 40 | } 41 | t 42 | } 43 | 44 | /// `belt-keyrep` key repetition algorithm described in STB 34.101.34-2020 8.1.3. 45 | /// 46 | /// # Panics 47 | /// If `(N, M)` is not equal to `(16, 16)`, `(24, 16)`, `(24, 24)`, 48 | /// `(32, 16)`, `(32, 24)`, or `(32, 32)`. 49 | // TODO: use compile-time check for `N` and `M` 50 | #[inline] 51 | pub fn belt_keyrep( 52 | x: &[u8; N], 53 | d: &[u8; 12], 54 | i: &[u8; 16], 55 | ) -> [u8; M] { 56 | let r: u32 = match (N, M) { 57 | (16, 16) => 0xC8BA94B1, 58 | (24, 16) => 0x12D6E35B, 59 | (24, 24) => 0xFFC0B05C, 60 | (32, 16) => 0x1ADC2BE1, 61 | (32, 24) => 0x3876ABC1, 62 | (32, 32) => 0x7B653CF3, 63 | _ => panic!("belt-keyrep: invalid combination of N ({N}) and M ({M})"), 64 | }; 65 | 66 | let s = belt_keyexpand(x); 67 | 68 | let d = [ 69 | u32::from_le_bytes(d[..4].try_into().unwrap()), 70 | u32::from_le_bytes(d[4..][..4].try_into().unwrap()), 71 | u32::from_le_bytes(d[8..][..4].try_into().unwrap()), 72 | ]; 73 | 74 | let i = [ 75 | u32::from_le_bytes(i[0..][..4].try_into().unwrap()), 76 | u32::from_le_bytes(i[4..][..4].try_into().unwrap()), 77 | u32::from_le_bytes(i[8..][..4].try_into().unwrap()), 78 | u32::from_le_bytes(i[12..][..4].try_into().unwrap()), 79 | ]; 80 | 81 | let (_, s) = belt_compress([r, d[0], d[1], d[2]], i, s); 82 | 83 | let mut y = [0u8; M]; 84 | for (src, dst) in s.iter().zip(y.chunks_exact_mut(4)) { 85 | dst.copy_from_slice(&src.to_le_bytes()); 86 | } 87 | y 88 | } 89 | 90 | /// `bake-kdf` key derivation algorithm described in STB 34.101.66-2014 8.1.4. 91 | #[inline] 92 | pub fn bake_kdf(x: &[u8], s: &[u8], c: u128) -> [u8; 32] { 93 | let mut hasher = BeltHash::default(); 94 | hasher.update(x); 95 | hasher.update(s); 96 | let y = hasher.finalize_fixed().0; 97 | 98 | belt_keyrep(&y, &[0xFF; 12], &c.to_le_bytes()) 99 | } 100 | -------------------------------------------------------------------------------- /bake-kdf/tests/tests.rs: -------------------------------------------------------------------------------- 1 | use bake_kdf::{bake_kdf, belt_keyexpand, belt_keyrep}; 2 | use hex_literal::hex; 3 | 4 | #[test] 5 | fn test_keyexpand() { 6 | let k_u32: [u32; 6] = [ 7 | 0xE9DEE72C, 0x8F0C0FA6, 0x2DDB49F4, 0x6F739647, 0x06075316, 0xED247A37, 8 | ]; 9 | let mut k = [0u8; 24]; 10 | for (src, dst) in k_u32.iter().zip(k.chunks_exact_mut(4)) { 11 | dst.copy_from_slice(&src.to_le_bytes()); 12 | } 13 | 14 | let k1 = belt_keyexpand::<16>(&k[..16].try_into().unwrap()); 15 | let k2 = belt_keyexpand(&k); 16 | 17 | assert_eq!( 18 | k1, 19 | [ 20 | 0xE9DEE72C, 0x8F0C0FA6, 0x2DDB49F4, 0x6F739647, 0xE9DEE72C, 0x8F0C0FA6, 0x2DDB49F4, 21 | 0x6F739647 22 | ] 23 | ); 24 | assert_eq!( 25 | k2, 26 | [ 27 | 0xE9DEE72C, 0x8F0C0FA6, 0x2DDB49F4, 0x6F739647, 0x06075316, 0xED247A37, 0x4B09A17E, 28 | 0x8450BF66 29 | ] 30 | ); 31 | } 32 | 33 | #[test] 34 | fn test_keyrep() { 35 | let x: [u8; 32] = 36 | hex!("E9DEE72C 8F0C0FA6 2DDB49F4 6F739647 06075316 ED247A37 39CBA383 03A98BF6"); 37 | let d: [u8; 12] = hex!("01000000 00000000 00000000"); 38 | let i: [u8; 16] = hex!("5BE3D612 17B96181 FE6786AD 716B890B"); 39 | 40 | let out: [u8; 16] = belt_keyrep(&x, &d, &i); 41 | assert_eq!(out, hex!("6BBBC233 6670D31A B83DAA90 D52C0541")); 42 | 43 | let out: [u8; 24] = belt_keyrep(&x, &d, &i); 44 | assert_eq!( 45 | out, 46 | hex!("9A2532A1 8CBAF145 398D5A95 FEEA6C82 5B9C1971 56A00275"), 47 | ); 48 | 49 | let out: [u8; 32] = belt_keyrep(&x, &d, &i); 50 | assert_eq!( 51 | out, 52 | hex!("76E166E6 AB21256B 6739397B 672B8796 14B81CF0 5955FC3A B09343A7 45C48F77"), 53 | ); 54 | } 55 | 56 | #[test] 57 | fn test_kdf() { 58 | let secret = hex!("723356E3 35ED7062 0FFB1842 752092C3 2603EB66 60409205 87D80057 5BECFC42"); 59 | let iv = hex!( 60 | "6B13ACBB 086FB876 18BCC2EF 20A3FA89 475654CB 367E670A 2441730B 24B8AB31" 61 | "CD3D6487 DC4EEB23 45697818 6A069C71 375D75C2 DF198BAD 1E61EEA0 DBBFF737" 62 | ); 63 | let key = bake_kdf(&secret, &iv, 0); 64 | assert_eq!( 65 | key, 66 | hex!("DAC4D8F4 11F9C523 D28BBAAB 32A5270E 4DFA1F0F 757EF8E0 F30AF08F BDE1E7F4"), 67 | ); 68 | 69 | let key = bake_kdf(&secret, &iv, 1); 70 | assert_eq!( 71 | key, 72 | hex!("54AC0582 84D679CF 4C47D3D7 2651F3E4 EF0D61D1 D0ED5BAF 8FF30B89 24E599D8"), 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /concat-kdf/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## 0.1.0 (2022-03-27) 8 | - Initial release 9 | -------------------------------------------------------------------------------- /concat-kdf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "concat-kdf" 3 | version = "0.2.0-pre" 4 | description = "Concatenation Key Derivation Function (Concat KDF)" 5 | authors = ["RustCrypto Developers"] 6 | license = "MIT OR Apache-2.0" 7 | readme = "README.md" 8 | edition = "2024" 9 | documentation = "https://docs.rs/concat-kdf" 10 | repository = "https://github.com/RustCrypto/KDFs" 11 | keywords = ["crypto", "concat-kdf", "KDF", "NIST"] 12 | categories = ["cryptography", "no-std"] 13 | rust-version = "1.85" 14 | 15 | [dependencies] 16 | digest = "0.11.0-rc.0" 17 | 18 | [dev-dependencies] 19 | hex-literal = "1" 20 | sha2 = { version = "0.11.0-rc.0", default-features = false } 21 | -------------------------------------------------------------------------------- /concat-kdf/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /concat-kdf/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-2024 RustCrypto Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /concat-kdf/README.md: -------------------------------------------------------------------------------- 1 | # RustCrypto: Concat KDF 2 | 3 | [![crate][crate-image]][crate-link] 4 | [![Docs][docs-image]][docs-link] 5 | ![Apache2/MIT licensed][license-image] 6 | ![Rust Version][rustc-image] 7 | [![Project Chat][chat-image]][chat-link] 8 | [![Build Status][build-image]][build-link] 9 | 10 | Pure Rust implementation of the Concatenation Key Derivation Function (Concat KDF) generic over hash function. 11 | This function is described in the section 5.8.1 of [NIST SP 800-56A, Recommendation for Pair-Wise Key Establishment 12 | Schemes Using Discrete Logarithm Cryptography](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-56ar.pdf). 13 | 14 | # Usage 15 | 16 | The most common way to use Concat KDF is as follows: you generate a shared secret with other party 17 | (e.g. via Diffie-Hellman algorithm) and use key derivation function to derive a shared key. 18 | 19 | ```rust 20 | use hex_literal::hex; 21 | use sha2::Sha256; 22 | 23 | let mut key = [0u8; 16]; 24 | concat_kdf::derive_key_into::(b"secret", b"shared-info", &mut key).unwrap(); 25 | assert_eq!(key, hex!("960db2c549ab16d71a7b008e005c2bdc")); 26 | ``` 27 | 28 | ## License 29 | 30 | Licensed under either of: 31 | 32 | * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 33 | * [MIT license](http://opensource.org/licenses/MIT) 34 | 35 | at your option. 36 | 37 | ### Contribution 38 | 39 | Unless you explicitly state otherwise, any contribution intentionally submitted 40 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 41 | dual licensed as above, without any additional terms or conditions. 42 | 43 | [crate-image]: https://img.shields.io/crates/v/concat-kdf.svg 44 | [crate-link]: https://crates.io/crates/concat-kdf 45 | [docs-image]: https://docs.rs/concat-kdf/badge.svg 46 | [docs-link]: https://docs.rs/concat-kdf/ 47 | [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg 48 | [rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg 49 | [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg 50 | [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260043-KDFs 51 | [build-image]: https://github.com/RustCrypto/KDFs/workflows/concat-kdf/badge.svg?branch=master&event=push 52 | [build-link]: https://github.com/RustCrypto/KDFs/actions?query=workflow:concat-kdf 53 | -------------------------------------------------------------------------------- /concat-kdf/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![doc = include_str!("../README.md")] 3 | #![doc( 4 | html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", 5 | html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" 6 | )] 7 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 8 | #![forbid(unsafe_code)] 9 | #![warn(missing_docs)] 10 | 11 | use core::fmt; 12 | use digest::{Digest, FixedOutputReset, Update, array::typenum::Unsigned}; 13 | 14 | /// Derives `key` in-place from `secret` and `other_info`. 15 | /// 16 | /// # Example 17 | /// ```rust 18 | /// use hex_literal::hex; 19 | /// use sha2::Sha256; 20 | /// 21 | /// let mut key = [0u8; 16]; 22 | /// concat_kdf::derive_key_into::(b"secret", b"shared-info", &mut key).unwrap(); 23 | /// assert_eq!(key, hex!("960db2c549ab16d71a7b008e005c2bdc")); 24 | /// ``` 25 | pub fn derive_key_into(secret: &[u8], other_info: &[u8], key: &mut [u8]) -> Result<(), Error> 26 | where 27 | D: Digest + FixedOutputReset, 28 | { 29 | if secret.is_empty() { 30 | return Err(Error::NoSecret); 31 | } 32 | 33 | if key.is_empty() { 34 | return Err(Error::NoOutput); 35 | } 36 | 37 | // Key length shall be less than or equal to hash output length * (2^32 - 1). 38 | if (key.len() as u64) >= D::OutputSize::U64 * (u32::MAX as u64) { 39 | return Err(Error::CounterOverflow); 40 | } 41 | 42 | let mut digest = D::new(); 43 | let mut counter: u32 = 1; 44 | 45 | for chunk in key.chunks_mut(D::OutputSize::USIZE) { 46 | Update::update(&mut digest, &counter.to_be_bytes()); 47 | Update::update(&mut digest, secret); 48 | Update::update(&mut digest, other_info); 49 | chunk.copy_from_slice(&digest.finalize_reset()[..chunk.len()]); 50 | counter += 1; 51 | } 52 | 53 | Ok(()) 54 | } 55 | 56 | /// Concat KDF errors. 57 | #[derive(Clone, Copy, Debug, PartialEq)] 58 | pub enum Error { 59 | /// The length of the secret is zero. 60 | NoSecret, 61 | /// The length of the output is zero. 62 | NoOutput, 63 | /// The length of the output is too big. 64 | CounterOverflow, 65 | } 66 | 67 | impl fmt::Display for Error { 68 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 69 | f.write_str(match self { 70 | Error::NoSecret => "Buffer for secret has zero length.", 71 | Error::NoOutput => "Buffer for key has zero length.", 72 | Error::CounterOverflow => "Requested key length is to big.", 73 | }) 74 | } 75 | } 76 | 77 | impl core::error::Error for Error {} 78 | -------------------------------------------------------------------------------- /concat-kdf/tests/tests.rs: -------------------------------------------------------------------------------- 1 | use digest::{Digest, FixedOutputReset}; 2 | use hex_literal::hex; 3 | use sha2::{Sha224, Sha256, Sha512}; 4 | 5 | struct Fixture<'a> { 6 | secret: &'a [u8], 7 | other_info: &'a [u8], 8 | expected_key: &'a [u8], 9 | } 10 | 11 | fn test_key_derivation(fixtures: &[Fixture]) 12 | where 13 | D: Digest + FixedOutputReset, 14 | { 15 | for f in fixtures.iter() { 16 | let mut buf = [0u8; 256]; 17 | for key_length in 1..f.expected_key.len() { 18 | let key = &mut buf[..key_length]; 19 | concat_kdf::derive_key_into::(f.secret, f.other_info, key).unwrap(); 20 | assert_eq!(&f.expected_key[..key_length], key); 21 | } 22 | } 23 | } 24 | 25 | #[test] 26 | fn test_input_output_sha224() { 27 | let fixtures = [ 28 | Fixture { 29 | secret: &hex!("00"), 30 | other_info: &[], 31 | expected_key: &hex!( 32 | "5a5f55dc7112b236b7b9e4734bfa2276a565c802b0e704e84d6f3afc19364a9b" 33 | "a2e6fdfe0d05c792b6ccc1c694efc1d253cc44975d5f7a1dac05745422639850" 34 | "0679a1a65f0586d655ed6e5d62a46d05741ae7133edc866f863fdeeb3b181a5b" 35 | "b7539309b809fba02c5f036926bcb25cb966683664634de66f4d72b3f1d671c0" 36 | "acb28bf4618c5faecc07ecb4bb60cdba51d3c902637fecb0173ab8185bc2939f" 37 | "d66d35a76ac3644e79166c4445123dfb91aa787e76b91b917e74eefb211a1264" 38 | "f0b493a980533252be954681094081628ad7ec9d8c77f3b05254b326e45cfeb2" 39 | "3b5dd5697ae7fe11e44af84c5254ae6d32e8079c442e19a4e0bdad348a8c7d73" 40 | ), 41 | }, 42 | Fixture { 43 | secret: &hex!("00"), 44 | other_info: &hex!("00"), 45 | expected_key: &hex!( 46 | "8741be040b4a815d358adf598bdabac4293a7e1353967aedbb80bcebfbd11dc0" 47 | "7c520882f90500ac6d9fe6a078e3213d056e9ca7ed7535f11a6417a843a56465" 48 | "cf7d67775d3392758d71c233c6611e45a96e2bfdb81ea1d3ccdfed90b768b1b1" 49 | "c3518e4c30cf9224af2a55e68f35b496a1239148732ec15e2419b1da97ace8c9" 50 | "fb7de3fe03656b7978a5286a1a0e212a65c9fb9b5c8d33cc1995497903492a27" 51 | "3e5be119898695e1872a43f2d32f95ac688816b3d4b344645d525f49859e8a70" 52 | "9d03d0d7ac947057f3eb5c3a830c510db96856fa7206d8a0f784223acf9cd211" 53 | "4d4a9d38c463ebede8ae758d577bf5704d30c5b0bcd225c007c8092bbaa26909" 54 | ), 55 | }, 56 | Fixture { 57 | secret: &hex!("ba5eba11bedabb1ebe5077edb0a710adb01dfacecab005eca11ab1eca55e77e011"), 58 | other_info: &hex!("f005ba1100ddba11"), 59 | expected_key: &hex!( 60 | "3467360b50fee27cfb8e6bdc28ef5252ac0938a2987693a23478bcdbe43a0fe6" 61 | "d2de581e4858c544722caf6776d423da3b73b623b4e39dcdd6d2b51685399e99" 62 | "1d53c53afd44cd294b992dccfb37a9cdaf9dee05bd2053fa9f1e0e3b3719e3c4" 63 | "3b0c7ad2aefb0bfb9b69e32c61bf3690d60c74cd7e37b0bf043e873028828a4a" 64 | "a6efbd8f9aedb1ab858616fe93878c5d815b6fa7cf13a205cced53a6fd8d7685" 65 | "339bb4a0be0f9ccb68419b1e0814acfcf67d2d06a492d429d2e8740ecbd94ceb" 66 | "cef9696bbf26867a7b192780deb59c7ae7ed97844a359a790a00aaa79f6aadae" 67 | "8bfc62ab653a8375de876cb5865e8a60d92b403be34050ac74e5da99787b3357" 68 | ), 69 | }, 70 | ]; 71 | 72 | test_key_derivation::(&fixtures); 73 | } 74 | 75 | #[test] 76 | fn test_input_output_sha256() { 77 | let fixtures = [ 78 | Fixture { 79 | secret: &hex!("00"), 80 | other_info: &[], 81 | expected_key: &hex!( 82 | "060dc63e5595dffbd161c9ec98bc06fcf67cb22e2e75ecdf0003821388aeee4d" 83 | "182cf10a4a3cc9c7ed07a46bfc0327a406e14b2e892b62471a523ceea8cb7664" 84 | "598cd1428ca03f178cc23c367994cd739eb3e029f63b3e7079e4df62717f2dd0" 85 | "d110457e8900a7bff0e9474ecd94fb6cd001d6928d7e018132678ac22013bdd0" 86 | "5f8b7ad1b5241a8326638b7f596fcc965cc2c81665ad275d0110a9af8fe1d8fb" 87 | "69af0678d2e4cd5b3a9dfeee6343496ee4ec37b3d4240954b64364acb9aa47f8" 88 | "49befd6c253f0eb97f3fb0c118542c39519746da27b55f32cca541f9a1a16091" 89 | "6a8814853bd214d9f0faf8d19724d53383fd0084a9471f67b989d47e225aa1a8" 90 | ), 91 | }, 92 | Fixture { 93 | secret: &hex!("00"), 94 | other_info: &hex!("00"), 95 | expected_key: &hex!( 96 | "10487d86ce3584e156874ed9b2650e8d772a8d1fdbb1c9111bf7e2fbcab18ccb" 97 | "e44728408fa247053c017f791d5d2fe87752119c5010006ffc4e098efbaea679" 98 | "52a554d18aa44185bbd82ea8882354ddc5286b9fd1af206bb9c88dbf424e4b1b" 99 | "5f54b6ff037cd93f528964739d54d41a837e86c0baa777865a2a48bb15c910a9" 100 | "4e01ccc1186d19c2db4e65bd81dd29c492d88668f6fc70c5fa20855ed535d20e" 101 | "5acee3f2b6a0568f4d1048d1ca04e85606e14d0bc48ebb7cf063a780f0096129" 102 | "52da97e695e38843ae3fcc649f301915fc8e7675d0065aabb2c6698daaf494a4" 103 | "df6d80b66a8b32c5bdd8ecdb650ae4fc2b47e6f50711eedd42dcbf2864e089cd" 104 | ), 105 | }, 106 | Fixture { 107 | secret: &hex!("ba5eba11bedabb1ebe5077edb0a710adb01dfacecab005eca11ab1eca55e77e011"), 108 | other_info: &hex!("f005ba1100ddba11"), 109 | expected_key: &hex!( 110 | "a400be9935d0c843a1504aa64f6078195b6e20ea3fe64bb8d7f29aaea6a351bf" 111 | "7e40cafe54e86c4d502f82a390ab77098e8cccf905b5826d475e8316583eb53f" 112 | "fc6afda60479492c5142db5896cbd0438e583d64162e448e68a3725944866f3e" 113 | "55ea6d4e3eea479b7d0a5c7a78d4425bc3c118a564f078f6dc41439c255e87c9" 114 | "be4e3e4a80509f84727ee661b9f04a8da4ece7214328f5180e96a9d641136ae7" 115 | "e2c83707106623dd890cc0c8a04a4af628b530da938753e07c4891204014b675" 116 | "1beb080ee8c3391607652411423567b60a56ca9efe5bc858c9a23f87b13cffa6" 117 | "bcaf5fab9ad33ff2a284c15c8af0195674b46be2f98fad136eb327a8a60a8f4e" 118 | ), 119 | }, 120 | ]; 121 | 122 | test_key_derivation::(&fixtures); 123 | } 124 | 125 | #[test] 126 | fn test_input_output_sha512() { 127 | let fixtures = [ 128 | Fixture { 129 | secret: &hex!("00"), 130 | other_info: &[], 131 | expected_key: &hex!( 132 | "63e1c62226df5825c32eceb32e9f318316f54d8b56a12f764c88ec4f4b4ed80c 133 | b4fd20c2b9fd522a9439f067dfabc8cc2da781dd7b0abc0821909dff25ff33db 134 | bac4619b1569765a23fd8fb91fbad73903d48a1088ae1b710437f2fc08815625 135 | 31248ae2f6c12a7156eb27e881ed186f4de45cf22a97cca0d67a8dc5dc7acf8c 136 | 6dc447966ff13da6f4d347c7421208f8553a9aeda6f66bcbfc556f47a402f20e 137 | b6f1a0c21bd375fa2246f7faad19a05c22536770ffb3466e544b0723005cea89 138 | 40706c1e16e81000d2829e7ab2e1a13f278396da38bd17414cf14d5dfe33212f 139 | c2ca53bb60df0e36fc76d4861e540da48db328b56e9bf5561a30ecc15184e70b" 140 | ), 141 | }, 142 | Fixture { 143 | secret: &hex!("00"), 144 | other_info: &hex!("00"), 145 | expected_key: &hex!( 146 | "5a91d2ac619076c5eb6aae55512841d76b6442f98e23372660e854dc5b327844 147 | 77e504f2fd620c9328711b682b8a21a0173cfd0c42a96263006e992d8483193f 148 | 03347c4cd479f8d0797fe6613fa999cd40bac7fad61715210c044dc722e8db8c 149 | 8b4a35076d17b0eb6170429ba2fb51371ddfc6189cb90dc27ae701523a709f67 150 | 08c5d1d4140e1434aeb8b593e41f21616543c7727dffe14f4c424dd487d01a92 151 | 1370993c359c2a45c9a6584df5a09e36c14453df46a9f9fa8b65ebd790eec122 152 | a0789cabb04854e392656eade6e40d821826708ba1fb078fd1d15565902c68c5 153 | 531c0be6415f9b24721fdc118fafaef6f1b5db03d895e7ff503ddbd73d111f38" 154 | ), 155 | }, 156 | Fixture { 157 | secret: &hex!("ba5eba11bedabb1ebe5077edb0a710adb01dfacecab005eca11ab1eca55e77e011"), 158 | other_info: &hex!("f005ba1100ddba11"), 159 | expected_key: &hex!( 160 | "247e5d73b8f45ff573294437d6e4fddecf95dee5f54b9ad8db11aa3f856e02b2 161 | 5da6159680c87f629c0ef6fbad79ef6499561d444106a765a7c7a96055ee684e 162 | 37e406b52ca3c6ca6690b5b93f8b1a5609a4b159a90a8ce9391f6e2738447034 163 | 8c2dc71f559cd2a9f471063a056ab138634679587dd51ac6d4729b7f94b44343 164 | 00dee4eb7c43c11919f83ee52aa11150c38476b8e675e3d2a20dac03b7153d05 165 | 92041e8324b7509f64a098060e74eb7d51ccfd9231154ef0fc0ee9de46252794 166 | 9f149b14636338fb9fef85c56f8a5d6f95e9268ed312053abe7cb5f8e6e405a4 167 | 058d5ef840e5ec6218ec2dfc4acdff1bea1a5341b9ea47e61642c7214b305083" 168 | ), 169 | }, 170 | ]; 171 | 172 | test_key_derivation::(&fixtures); 173 | } 174 | 175 | #[test] 176 | fn test_no_secret() { 177 | assert_eq!( 178 | concat_kdf::derive_key_into::(&[], &[], &mut [0u8; 42]), 179 | Err(concat_kdf::Error::NoSecret) 180 | ); 181 | } 182 | 183 | #[test] 184 | fn test_no_output() { 185 | assert_eq!( 186 | concat_kdf::derive_key_into::(&[0u8; 42], &[], &mut [0u8; 0]), 187 | Err(concat_kdf::Error::NoOutput) 188 | ); 189 | } 190 | -------------------------------------------------------------------------------- /hkdf/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## UNRELEASED 8 | ### Breaking changes 9 | - Removed `std` crate feature ([#105]) 10 | - Bump MSRV to 1.81 ([#105]) 11 | - Bump `hmac` dependency to v0.13 12 | 13 | [#105]: https://github.com/RustCrypto/KDFs/pull/105 14 | 15 | ## 0.12.3 (2022-02-17) 16 | ### Fixed 17 | - Minimal versions build ([#63]) 18 | 19 | [#63]: https://github.com/RustCrypto/KDFs/pull/63 20 | 21 | ## 0.12.2 (2022-01-27) 22 | ### Fixed 23 | - Re-export `InvalidLength` and `InvalidPrkLength` ([#59]) 24 | 25 | [#59]: https://github.com/RustCrypto/KDFs/pull/59 26 | 27 | ## 0.12.1 (2022-01-27) [YANKED] 28 | ### Added 29 | - Ability to switch HMAC implementation to `SimpleHmac` with respective `SimpleHkdfExtract` and `SimpleHkdf` aliases ([#57]) 30 | 31 | [#57]: https://github.com/RustCrypto/KDFs/pull/55 32 | 33 | ## 0.12.0 (2021-12-07) 34 | ### Changed 35 | - Bump `hmac` crate dependency to v0.12 and `digest` to v0.10 ([#52]) 36 | 37 | [#52]: https://github.com/RustCrypto/KDFs/pull/52 38 | 39 | ## 0.11.0 (2021-04-29) 40 | ### Added 41 | - Wycheproof HKDF test vectors ([#49]) 42 | 43 | ### Changed 44 | - Bump `hmac` crate dependency to v0.11 ([#50]) 45 | 46 | ### Fixed 47 | - HKDF-Extract with empty salt ([#46]) 48 | 49 | [#46]: https://github.com/RustCrypto/KDFs/pull/46 50 | [#49]: https://github.com/RustCrypto/KDFs/pull/49 51 | [#50]: https://github.com/RustCrypto/KDFs/pull/50 52 | 53 | ## 0.10.0 (2020-10-26) 54 | ### Changed 55 | - Bump `hmac` dependency to v0.10 ([#40]) 56 | 57 | [#40]: https://github.com/RustCrypto/KDFs/pull/40 58 | 59 | ## 0.9.0 (2020-06-22) 60 | ### Added 61 | - Multipart features for HKDF-Extract and HKDF-Expand ([#34]) 62 | 63 | ### Changed 64 | - Bump `digest` v0.9; `hmac` v0.9 ([#35]) 65 | 66 | [#34]: https://github.com/RustCrypto/KDFs/pull/34 67 | [#35]: https://github.com/RustCrypto/KDFs/pull/35 68 | 69 | ## 0.8.0 (2019-07-26) 70 | ### Added 71 | - `Hkdf::from_prk()`, `Hkdf::extract()` 72 | 73 | ## 0.7.1 (2019-07-15) 74 | 75 | ## 0.7.0 (2018-10-16) 76 | ### Changed 77 | - Update digest to 0.8 78 | - Refactor for API changes 79 | 80 | ### Removed 81 | - Redundant `generic-array` crate. 82 | 83 | ## 0.6.0 (2018-08-20) 84 | ### Changed 85 | - The `expand` signature has changed. 86 | 87 | ### Removed 88 | - `std` requirement 89 | 90 | ## 0.5.0 (2018-05-20) 91 | ### Fixed 92 | - Omitting HKDF salt. 93 | 94 | ### Removed 95 | - Deprecated interface 96 | 97 | ## 0.4.0 (2018-03-20 98 | ### Added 99 | - Benchmarks 100 | - derive `Clone` 101 | 102 | ### Changed 103 | - RFC-inspired interface 104 | - Reduce heap allocation 105 | - Bump deps: hex-0.3 106 | 107 | ### Removed 108 | - Unnecessary mut 109 | 110 | ## 0.3.0 (2017-11-29) 111 | ### Changed 112 | - update dependencies: digest-0.7, hmac-0.5 113 | 114 | ## 0.2.0 (2017-09-21) 115 | ### Fixed 116 | - Support for rustc 1.20.0 117 | 118 | ## 0.1.2 (2017-09-21) 119 | ### Fixed 120 | - Support for rustc 1.5.0 121 | 122 | ## 0.1.0 (2017-09-21) 123 | - Initial release 124 | -------------------------------------------------------------------------------- /hkdf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hkdf" 3 | version = "0.13.0-rc.0" 4 | authors = ["RustCrypto Developers"] 5 | license = "MIT OR Apache-2.0" 6 | homepage = "https://github.com/RustCrypto/KDFs/" 7 | repository = "https://github.com/RustCrypto/KDFs/" 8 | description = "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)" 9 | keywords = ["crypto", "HKDF", "KDF"] 10 | categories = ["cryptography", "no-std"] 11 | readme = "README.md" 12 | edition = "2024" 13 | rust-version = "1.85" 14 | 15 | [dependencies] 16 | hmac = "0.13.0-pre.5" 17 | 18 | [dev-dependencies] 19 | blobby = "=0.4.0-pre.0" 20 | hex-literal = "1" 21 | sha1 = { version = "0.11.0-rc.0", default-features = false } 22 | sha2 = { version = "0.11.0-rc.0", default-features = false } 23 | 24 | [package.metadata.docs.rs] 25 | all-features = true 26 | -------------------------------------------------------------------------------- /hkdf/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /hkdf/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2018 Vlad Filippov 2 | Copyright (c) 2018-2021 RustCrypto Developers 3 | 4 | Permission is hereby granted, free of charge, to any 5 | person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the 7 | Software without restriction, including without 8 | limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software 11 | is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice 15 | shall be included in all copies or substantial portions 16 | of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 19 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 20 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 21 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 22 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 25 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /hkdf/README.md: -------------------------------------------------------------------------------- 1 | # RustCrypto: HKDF 2 | 3 | [![crate][crate-image]][crate-link] 4 | [![Docs][docs-image]][docs-link] 5 | ![Apache2/MIT licensed][license-image] 6 | ![Rust Version][rustc-image] 7 | [![Project Chat][chat-image]][chat-link] 8 | [![Build Status][build-image]][build-link] 9 | 10 | Pure Rust implementation of the [HMAC-based Extract-and-Expand Key Derivation Function (HKDF)](https://tools.ietf.org/html/rfc5869) generic over hash function. 11 | 12 | # Usage 13 | 14 | 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. 15 | 16 | ```rust 17 | use sha2::Sha256; 18 | use hkdf::Hkdf; 19 | use hex_literal::hex; 20 | 21 | let ikm = hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); 22 | let salt = hex!("000102030405060708090a0b0c"); 23 | let info = hex!("f0f1f2f3f4f5f6f7f8f9"); 24 | 25 | let hk = Hkdf::::new(Some(&salt[..]), &ikm); 26 | let mut okm = [0u8; 42]; 27 | hk.expand(&info, &mut okm) 28 | .expect("42 is a valid length for Sha256 to output"); 29 | 30 | let expected = hex!(" 31 | 3cb25f25faacd57a90434f64d0362f2a 32 | 2d2d0a90cf1a5a4c5db02d56ecc4c5bf 33 | 34007208d5b887185865 34 | "); 35 | assert_eq!(okm, expected); 36 | ``` 37 | 38 | 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`. 39 | 40 | ```rust 41 | use sha2::Sha256; 42 | use hkdf::Hkdf; 43 | use hex_literal::hex; 44 | 45 | let ikm = hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); 46 | let salt = hex!("000102030405060708090a0b0c"); 47 | 48 | let (prk, hk) = Hkdf::::extract(Some(&salt[..]), &ikm); 49 | let expected = hex!(" 50 | 077709362c2e32df0ddc3f0dc47bba63 51 | 90b6c73bb50f9c3122ec844ad7c2b3e5 52 | "); 53 | assert_eq!(prk[..], expected[..]); 54 | ``` 55 | 56 | If you already have a strong key to work from (uniformly-distributed and 57 | long enough), you can save a tiny amount of time by skipping the extract 58 | step. In this case, you pass a Pseudo-Random Key (PRK) into the 59 | `Hkdf::from_prk` constructor, then use the resulting `Hkdf` object 60 | as usual. 61 | 62 | ```rust 63 | use sha2::Sha256; 64 | use hkdf::Hkdf; 65 | use hex_literal::hex; 66 | 67 | let salt = hex!("000102030405060708090a0b0c"); 68 | let info = hex!("f0f1f2f3f4f5f6f7f8f9"); 69 | 70 | let prk = hex!(" 71 | 077709362c2e32df0ddc3f0dc47bba63 72 | 90b6c73bb50f9c3122ec844ad7c2b3e5 73 | "); 74 | 75 | let hk = Hkdf::::from_prk(&prk).expect("PRK should be large enough"); 76 | let mut okm = [0u8; 42]; 77 | hk.expand(&info, &mut okm) 78 | .expect("42 is a valid length for Sha256 to output"); 79 | 80 | let expected = hex!(" 81 | 3cb25f25faacd57a90434f64d0362f2a 82 | 2d2d0a90cf1a5a4c5db02d56ecc4c5bf 83 | 34007208d5b887185865 84 | "); 85 | assert_eq!(okm, expected); 86 | ``` 87 | 88 | ## License 89 | 90 | Licensed under either of: 91 | 92 | * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 93 | * [MIT license](http://opensource.org/licenses/MIT) 94 | 95 | at your option. 96 | 97 | ### Contribution 98 | 99 | Unless you explicitly state otherwise, any contribution intentionally submitted 100 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 101 | dual licensed as above, without any additional terms or conditions. 102 | 103 | [//]: # (badges) 104 | 105 | [crate-image]: https://img.shields.io/crates/v/hkdf.svg 106 | [crate-link]: https://crates.io/crates/hkdf 107 | [docs-image]: https://docs.rs/hkdf/badge.svg 108 | [docs-link]: https://docs.rs/hkdf/ 109 | [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg 110 | [rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg 111 | [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg 112 | [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260043-KDFs 113 | [build-image]: https://github.com/RustCrypto/KDFs/workflows/hkdf/badge.svg?branch=master&event=push 114 | [build-link]: https://github.com/RustCrypto/KDFs/actions?query=workflow:hkdf 115 | -------------------------------------------------------------------------------- /hkdf/benches/mod.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | 4 | use test::Bencher; 5 | 6 | type HkdfSha256 = hkdf::Hkdf; 7 | 8 | #[bench] 9 | fn hkdf_sha256_10(b: &mut Bencher) { 10 | let mut okm = vec![0u8; 10]; 11 | b.iter(|| HkdfSha256::new(Some(&[]), &[]).expand(&[], &mut okm)); 12 | b.bytes = okm.len() as u64; 13 | } 14 | 15 | #[bench] 16 | fn hkdf_sha256_1024(b: &mut Bencher) { 17 | let mut okm = vec![0u8; 1024]; 18 | b.iter(|| HkdfSha256::new(Some(&[]), &[]).expand(&[], &mut okm)); 19 | b.bytes = okm.len() as u64; 20 | } 21 | 22 | #[bench] 23 | fn hkdf_sha256_8000(b: &mut Bencher) { 24 | let mut okm = vec![0u8; 8000]; 25 | b.iter(|| HkdfSha256::new(Some(&[]), &[]).expand(&[], &mut okm)); 26 | b.bytes = okm.len() as u64; 27 | } 28 | -------------------------------------------------------------------------------- /hkdf/src/errors.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | /// Error that is returned when supplied pseudorandom key (PRK) is not long enough. 4 | #[derive(Copy, Clone, Debug)] 5 | pub struct InvalidPrkLength; 6 | 7 | impl fmt::Display for InvalidPrkLength { 8 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 9 | f.write_str("invalid pseudorandom key length, too short") 10 | } 11 | } 12 | 13 | impl core::error::Error for InvalidPrkLength {} 14 | 15 | /// Structure for InvalidLength, used for output error handling. 16 | #[derive(Copy, Clone, Debug)] 17 | pub struct InvalidLength; 18 | 19 | impl fmt::Display for InvalidLength { 20 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 21 | f.write_str("invalid number of blocks, too large output") 22 | } 23 | } 24 | 25 | impl core::error::Error for InvalidLength {} 26 | -------------------------------------------------------------------------------- /hkdf/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![doc = include_str!("../README.md")] 3 | #![doc( 4 | html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", 5 | html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" 6 | )] 7 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 8 | #![forbid(unsafe_code)] 9 | #![warn(missing_docs)] 10 | 11 | pub use hmac; 12 | 13 | use core::fmt; 14 | use core::marker::PhantomData; 15 | use hmac::digest::{ 16 | Output, OutputSizeUser, array::typenum::Unsigned, crypto_common::AlgorithmName, 17 | }; 18 | use hmac::{Hmac, SimpleHmac}; 19 | 20 | mod errors; 21 | mod sealed; 22 | 23 | pub use errors::{InvalidLength, InvalidPrkLength}; 24 | 25 | /// [`HkdfExtract`] variant which uses [`SimpleHmac`] for underlying HMAC 26 | /// implementation. 27 | pub type SimpleHkdfExtract = HkdfExtract>; 28 | /// [`Hkdf`] variant which uses [`SimpleHmac`] for underlying HMAC 29 | /// implementation. 30 | pub type SimpleHkdf = Hkdf>; 31 | 32 | /// Structure representing the streaming context of an HKDF-Extract operation 33 | /// ```rust 34 | /// # use hkdf::{Hkdf, HkdfExtract}; 35 | /// # use sha2::Sha256; 36 | /// let mut extract_ctx = HkdfExtract::::new(Some(b"mysalt")); 37 | /// extract_ctx.input_ikm(b"hello"); 38 | /// extract_ctx.input_ikm(b" world"); 39 | /// let (streamed_res, _) = extract_ctx.finalize(); 40 | /// 41 | /// let (oneshot_res, _) = Hkdf::::extract(Some(b"mysalt"), b"hello world"); 42 | /// assert_eq!(streamed_res, oneshot_res); 43 | /// ``` 44 | #[derive(Clone)] 45 | pub struct HkdfExtract> 46 | where 47 | H: OutputSizeUser, 48 | I: HmacImpl, 49 | { 50 | hmac: I, 51 | _pd: PhantomData, 52 | } 53 | 54 | impl HkdfExtract 55 | where 56 | H: OutputSizeUser, 57 | I: HmacImpl, 58 | { 59 | /// Initiates the HKDF-Extract context with the given optional salt 60 | pub fn new(salt: Option<&[u8]>) -> Self { 61 | let default_salt = Output::::default(); 62 | let salt = salt.unwrap_or(&default_salt); 63 | Self { 64 | hmac: I::new_from_slice(salt), 65 | _pd: PhantomData, 66 | } 67 | } 68 | 69 | /// Feeds in additional input key material to the HKDF-Extract context 70 | pub fn input_ikm(&mut self, ikm: &[u8]) { 71 | self.hmac.update(ikm); 72 | } 73 | 74 | /// Completes the HKDF-Extract operation, returning both the generated pseudorandom key and 75 | /// `Hkdf` struct for expanding. 76 | pub fn finalize(self) -> (Output, Hkdf) { 77 | let prk = self.hmac.finalize(); 78 | let hkdf = Hkdf::from_prk(&prk).expect("PRK size is correct"); 79 | (prk, hkdf) 80 | } 81 | } 82 | 83 | impl fmt::Debug for HkdfExtract 84 | where 85 | H: OutputSizeUser, 86 | I: HmacImpl + AlgorithmName, 87 | { 88 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 89 | f.write_str("HkdfExtract<")?; 90 | ::write_alg_name(f)?; 91 | f.write_str("> { ... }") 92 | } 93 | } 94 | 95 | /// Structure representing the HKDF, capable of HKDF-Expand and HKDF-Extract operations. 96 | /// Recommendations for the correct usage of the parameters can be found in the 97 | /// [crate root](index.html#usage). 98 | #[derive(Clone)] 99 | pub struct Hkdf = Hmac> { 100 | hmac: I, 101 | _pd: PhantomData, 102 | } 103 | 104 | impl> Hkdf { 105 | /// Convenience method for [`extract`][Hkdf::extract] when the generated 106 | /// pseudorandom key can be ignored and only HKDF-Expand operation is needed. This is the most 107 | /// common constructor. 108 | pub fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Self { 109 | let (_, hkdf) = Self::extract(salt, ikm); 110 | hkdf 111 | } 112 | 113 | /// Create `Hkdf` from an already cryptographically strong pseudorandom key 114 | /// as per section 3.3 from RFC5869. 115 | pub fn from_prk(prk: &[u8]) -> Result { 116 | // section 2.3 specifies that prk must be "at least HashLen octets" 117 | if prk.len() < ::OutputSize::to_usize() { 118 | return Err(InvalidPrkLength); 119 | } 120 | Ok(Self { 121 | hmac: I::new_from_slice(prk), 122 | _pd: PhantomData, 123 | }) 124 | } 125 | 126 | /// The RFC5869 HKDF-Extract operation returning both the generated 127 | /// pseudorandom key and `Hkdf` struct for expanding. 128 | pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> (Output, Self) { 129 | let mut extract_ctx = HkdfExtract::new(salt); 130 | extract_ctx.input_ikm(ikm); 131 | extract_ctx.finalize() 132 | } 133 | 134 | /// The RFC5869 HKDF-Expand operation. This is equivalent to calling 135 | /// [`expand`][Hkdf::extract] with the `info` argument set equal to the 136 | /// concatenation of all the elements of `info_components`. 137 | pub fn expand_multi_info( 138 | &self, 139 | info_components: &[&[u8]], 140 | okm: &mut [u8], 141 | ) -> Result<(), InvalidLength> { 142 | let mut prev: Option> = None; 143 | 144 | let chunk_len = ::OutputSize::USIZE; 145 | if okm.len() > chunk_len * 255 { 146 | return Err(InvalidLength); 147 | } 148 | 149 | for (block_n, block) in okm.chunks_mut(chunk_len).enumerate() { 150 | let mut hmac = self.hmac.clone(); 151 | 152 | if let Some(ref prev) = prev { 153 | hmac.update(prev) 154 | }; 155 | 156 | // Feed in the info components in sequence. This is equivalent to feeding in the 157 | // concatenation of all the info components 158 | for info in info_components { 159 | hmac.update(info); 160 | } 161 | 162 | hmac.update(&[block_n as u8 + 1]); 163 | 164 | let output = hmac.finalize(); 165 | 166 | let block_len = block.len(); 167 | block.copy_from_slice(&output[..block_len]); 168 | 169 | prev = Some(output); 170 | } 171 | 172 | Ok(()) 173 | } 174 | 175 | /// The RFC5869 HKDF-Expand operation 176 | /// 177 | /// If you don't have any `info` to pass, use an empty slice. 178 | pub fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> { 179 | self.expand_multi_info(&[info], okm) 180 | } 181 | } 182 | 183 | impl fmt::Debug for Hkdf 184 | where 185 | H: OutputSizeUser, 186 | I: HmacImpl, 187 | I: AlgorithmName, 188 | { 189 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 190 | f.write_str("Hkdf<")?; 191 | ::write_alg_name(f)?; 192 | f.write_str("> { ... }") 193 | } 194 | } 195 | 196 | /// Sealed trait implemented for [`Hmac`] and [`SimpleHmac`]. 197 | pub trait HmacImpl: sealed::Sealed + Clone {} 198 | 199 | impl + Clone> HmacImpl for T {} 200 | -------------------------------------------------------------------------------- /hkdf/src/sealed.rs: -------------------------------------------------------------------------------- 1 | use hmac::digest::{ 2 | Digest, FixedOutput, KeyInit, Output, Update, 3 | block_api::{BlockSizeUser, OutputSizeUser}, 4 | }; 5 | use hmac::{EagerHash, Hmac, SimpleHmac}; 6 | 7 | pub trait Sealed { 8 | fn new_from_slice(key: &[u8]) -> Self; 9 | 10 | fn update(&mut self, data: &[u8]); 11 | 12 | fn finalize(self) -> Output; 13 | } 14 | 15 | impl Sealed for Hmac { 16 | #[inline(always)] 17 | fn new_from_slice(key: &[u8]) -> Self { 18 | KeyInit::new_from_slice(key).expect("HMAC can take a key of any size") 19 | } 20 | 21 | #[inline(always)] 22 | fn update(&mut self, data: &[u8]) { 23 | Update::update(self, data); 24 | } 25 | 26 | #[inline(always)] 27 | #[allow(deprecated)] // clone_from_slice 28 | fn finalize(self) -> Output { 29 | // Output and Output are always equal to each other, 30 | // but we can not prove it at type level 31 | Output::::clone_from_slice(&self.finalize_fixed()) 32 | } 33 | } 34 | 35 | impl Sealed for SimpleHmac { 36 | #[inline(always)] 37 | fn new_from_slice(key: &[u8]) -> Self { 38 | KeyInit::new_from_slice(key).expect("HMAC can take a key of any size") 39 | } 40 | 41 | #[inline(always)] 42 | fn update(&mut self, data: &[u8]) { 43 | Update::update(self, data); 44 | } 45 | 46 | #[inline(always)] 47 | #[allow(deprecated)] // clone_from_slice 48 | fn finalize(self) -> Output { 49 | // Output and Output are always equal to each other, 50 | // but we can not prove it at type level 51 | Output::::clone_from_slice(&self.finalize_fixed()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /hkdf/tests/data/wycheproof-sha1.blb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustCrypto/KDFs/24e9844f4dd42d27debf0a85c8134e33457be247/hkdf/tests/data/wycheproof-sha1.blb -------------------------------------------------------------------------------- /hkdf/tests/data/wycheproof-sha256.blb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustCrypto/KDFs/24e9844f4dd42d27debf0a85c8134e33457be247/hkdf/tests/data/wycheproof-sha256.blb -------------------------------------------------------------------------------- /hkdf/tests/data/wycheproof-sha384.blb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustCrypto/KDFs/24e9844f4dd42d27debf0a85c8134e33457be247/hkdf/tests/data/wycheproof-sha384.blb -------------------------------------------------------------------------------- /hkdf/tests/data/wycheproof-sha512.blb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustCrypto/KDFs/24e9844f4dd42d27debf0a85c8134e33457be247/hkdf/tests/data/wycheproof-sha512.blb -------------------------------------------------------------------------------- /hkdf/tests/rfc5869.rs: -------------------------------------------------------------------------------- 1 | use hex_literal::hex; 2 | use hkdf::{Hkdf, hmac::EagerHash}; 3 | use sha1::Sha1; 4 | use sha2::Sha256; 5 | 6 | struct Test<'a> { 7 | ikm: &'a [u8], 8 | salt: &'a [u8], 9 | info: &'a [u8], 10 | prk: &'a [u8], 11 | okm: &'a [u8], 12 | } 13 | 14 | fn rfc_test(tests: &[Test]) { 15 | let mut buf = [0u8; 128]; 16 | for test in tests.iter() { 17 | let salt = if test.salt.is_empty() { 18 | None 19 | } else { 20 | Some(test.salt) 21 | }; 22 | let (prk2, hkdf) = Hkdf::::extract(salt, test.ikm); 23 | let okm = &mut buf[..test.okm.len()]; 24 | assert!(hkdf.expand(test.info, okm).is_ok()); 25 | 26 | assert_eq!(prk2[..], test.prk[..]); 27 | assert_eq!(okm, test.okm); 28 | 29 | okm.fill(0); 30 | let hkdf = Hkdf::::from_prk(test.prk).unwrap(); 31 | assert!(hkdf.expand(test.info, okm).is_ok()); 32 | assert_eq!(okm, test.okm); 33 | } 34 | } 35 | 36 | // Test Vectors from https://tools.ietf.org/html/rfc5869. 37 | #[test] 38 | fn test_rfc5869_sha256() { 39 | rfc_test::(&[ 40 | // Test Case 1 41 | Test { 42 | ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), 43 | salt: &hex!("000102030405060708090a0b0c"), 44 | info: &hex!("f0f1f2f3f4f5f6f7f8f9"), 45 | prk: &hex!( 46 | "077709362c2e32df0ddc3f0dc47bba63" 47 | "90b6c73bb50f9c3122ec844ad7c2b3e5" 48 | ), 49 | okm: &hex!( 50 | "3cb25f25faacd57a90434f64d0362f2a" 51 | "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" 52 | "34007208d5b887185865" 53 | ), 54 | }, 55 | // Test Case 2 56 | Test { 57 | ikm: &hex!( 58 | "000102030405060708090a0b0c0d0e0f" 59 | "101112131415161718191a1b1c1d1e1f" 60 | "202122232425262728292a2b2c2d2e2f" 61 | "303132333435363738393a3b3c3d3e3f" 62 | "404142434445464748494a4b4c4d4e4f" 63 | ), 64 | salt: &hex!( 65 | "606162636465666768696a6b6c6d6e6f" 66 | "707172737475767778797a7b7c7d7e7f" 67 | "808182838485868788898a8b8c8d8e8f" 68 | "909192939495969798999a9b9c9d9e9f" 69 | "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" 70 | ), 71 | info: &hex!( 72 | "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" 73 | "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" 74 | "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" 75 | "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" 76 | "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 77 | ), 78 | prk: &hex!( 79 | "06a6b88c5853361a06104c9ceb35b45c" 80 | "ef760014904671014a193f40c15fc244" 81 | ), 82 | okm: &hex!( 83 | "b11e398dc80327a1c8e7f78c596a4934" 84 | "4f012eda2d4efad8a050cc4c19afa97c" 85 | "59045a99cac7827271cb41c65e590e09" 86 | "da3275600c2f09b8367793a9aca3db71" 87 | "cc30c58179ec3e87c14c01d5c1f3434f" 88 | "1d87" 89 | ), 90 | }, 91 | // Test Case 3 92 | Test { 93 | ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), 94 | salt: &hex!(""), 95 | info: &hex!(""), 96 | prk: &hex!( 97 | "19ef24a32c717b167f33a91d6f648bdf" 98 | "96596776afdb6377ac434c1c293ccb04" 99 | ), 100 | okm: &hex!( 101 | "8da4e775a563c18f715f802a063c5a31" 102 | "b8a11f5c5ee1879ec3454e5f3c738d2d" 103 | "9d201395faa4b61a96c8" 104 | ), 105 | }, 106 | ]); 107 | } 108 | 109 | #[test] 110 | fn test_rfc5869_sha1() { 111 | rfc_test::(&[ 112 | // Test Case 4 113 | Test { 114 | ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b"), 115 | salt: &hex!("000102030405060708090a0b0c"), 116 | info: &hex!("f0f1f2f3f4f5f6f7f8f9"), 117 | prk: &hex!("9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243"), 118 | okm: &hex!( 119 | "085a01ea1b10f36933068b56efa5ad81" 120 | "a4f14b822f5b091568a9cdd4f155fda2" 121 | "c22e422478d305f3f896" 122 | ), 123 | }, 124 | // Test Case 5 125 | Test { 126 | ikm: &hex!( 127 | "000102030405060708090a0b0c0d0e0f" 128 | "101112131415161718191a1b1c1d1e1f" 129 | "202122232425262728292a2b2c2d2e2f" 130 | "303132333435363738393a3b3c3d3e3f" 131 | "404142434445464748494a4b4c4d4e4f" 132 | ), 133 | salt: &hex!( 134 | "606162636465666768696a6b6c6d6e6f" 135 | "707172737475767778797a7b7c7d7e7f" 136 | "808182838485868788898a8b8c8d8e8f" 137 | "909192939495969798999a9b9c9d9e9f" 138 | "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" 139 | ), 140 | info: &hex!( 141 | "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" 142 | "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" 143 | "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" 144 | "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" 145 | "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 146 | ), 147 | prk: &hex!("8adae09a2a307059478d309b26c4115a224cfaf6"), 148 | okm: &hex!( 149 | "0bd770a74d1160f7c9f12cd5912a06eb" 150 | "ff6adcae899d92191fe4305673ba2ffe" 151 | "8fa3f1a4e5ad79f3f334b3b202b2173c" 152 | "486ea37ce3d397ed034c7f9dfeb15c5e" 153 | "927336d0441f4c4300e2cff0d0900b52" 154 | "d3b4" 155 | ), 156 | }, 157 | // Test Case 6 158 | Test { 159 | ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), 160 | salt: &hex!(""), 161 | info: &hex!(""), 162 | prk: &hex!("da8c8a73c7fa77288ec6f5e7c297786aa0d32d01"), 163 | okm: &hex!( 164 | "0ac1af7002b3d761d1e55298da9d0506" 165 | "b9ae52057220a306e07b6b87e8df21d0" 166 | "ea00033de03984d34918" 167 | ), 168 | }, 169 | // Test Case 7 170 | Test { 171 | ikm: &hex!("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"), 172 | salt: &hex!(""), // "Not Provided" 173 | info: &hex!(""), 174 | prk: &hex!("2adccada18779e7c2077ad2eb19d3f3e731385dd"), 175 | okm: &hex!( 176 | "2c91117204d745f3500d636a62f64f0a" 177 | "b3bae548aa53d423b0d1f27ebba6f5e5" 178 | "673a081d70cce7acfc48" 179 | ), 180 | }, 181 | ]); 182 | } 183 | -------------------------------------------------------------------------------- /hkdf/tests/tests.rs: -------------------------------------------------------------------------------- 1 | use core::iter; 2 | 3 | use hex_literal::hex; 4 | use hkdf::{Hkdf, HkdfExtract, SimpleHkdfExtract}; 5 | use sha1::Sha1; 6 | use sha2::Sha256; 7 | 8 | const MAX_SHA256_LENGTH: usize = 255 * (256 / 8); // =8160 9 | static COMPONENTS: &[&[u8]] = &[ 10 | b"09090909090909090909090909090909090909090909", 11 | b"8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a", 12 | b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0", 13 | b"4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4", 14 | b"1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d", 15 | ]; 16 | 17 | #[test] 18 | fn test_lengths() { 19 | let hkdf = Hkdf::::new(None, &[]); 20 | let mut longest = vec![0u8; MAX_SHA256_LENGTH]; 21 | assert!(hkdf.expand(&[], &mut longest).is_ok()); 22 | // Runtime is O(length), so exhaustively testing all legal lengths 23 | // would take too long (at least without --release). Only test a 24 | // subset: the first 500, the last 10, and every 100th in between. 25 | let range = 500..MAX_SHA256_LENGTH - 10; 26 | let lengths = (0..MAX_SHA256_LENGTH + 1).filter(|len| !range.contains(len) || *len % 100 == 0); 27 | 28 | for length in lengths { 29 | let mut okm = vec![0u8; length]; 30 | assert!(hkdf.expand(&[], &mut okm).is_ok()); 31 | assert_eq!(okm.len(), length); 32 | assert_eq!(okm[..], longest[..length]); 33 | } 34 | } 35 | 36 | #[test] 37 | fn test_max_length() { 38 | let hkdf = Hkdf::::new(Some(&[]), &[]); 39 | let mut okm = vec![0u8; MAX_SHA256_LENGTH]; 40 | assert!(hkdf.expand(&[], &mut okm).is_ok()); 41 | } 42 | 43 | #[test] 44 | fn test_max_length_exceeded() { 45 | let hkdf = Hkdf::::new(Some(&[]), &[]); 46 | let mut okm = vec![0u8; MAX_SHA256_LENGTH + 1]; 47 | assert!(hkdf.expand(&[], &mut okm).is_err()); 48 | } 49 | 50 | #[test] 51 | fn test_unsupported_length() { 52 | let hkdf = Hkdf::::new(Some(&[]), &[]); 53 | let mut okm = vec![0u8; 90000]; 54 | assert!(hkdf.expand(&[], &mut okm).is_err()); 55 | } 56 | 57 | #[test] 58 | fn test_prk_too_short() { 59 | use sha2::digest::Digest; 60 | 61 | let output_len = Sha256::output_size(); 62 | let prk = vec![0; output_len - 1]; 63 | assert!(Hkdf::::from_prk(&prk).is_err()); 64 | } 65 | 66 | #[test] 67 | #[rustfmt::skip] 68 | fn test_derive_sha1_with_none() { 69 | let ikm = hex!("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"); 70 | let salt = None; 71 | let info = hex!(""); 72 | let (prk, hkdf) = Hkdf::::extract(salt, &ikm[..]); 73 | let mut okm = [0u8; 42]; 74 | assert!(hkdf.expand(&info[..], &mut okm).is_ok()); 75 | 76 | assert_eq!( 77 | prk[..], 78 | hex!("2adccada18779e7c2077ad2eb19d3f3e731385dd")[..] 79 | ); 80 | assert_eq!( 81 | okm[..], 82 | hex!(" 83 | 2c91117204d745f3500d636a62f64f0a 84 | b3bae548aa53d423b0d1f27ebba6f5e5 85 | 673a081d70cce7acfc48 86 | ")[..], 87 | ); 88 | } 89 | 90 | #[test] 91 | fn test_expand_multi_info() { 92 | let info_components = COMPONENTS; 93 | 94 | let (_, hkdf_ctx) = Hkdf::::extract(None, b"some ikm here"); 95 | 96 | // Compute HKDF-Expand on the concatenation of all the info components 97 | let mut oneshot_res = [0u8; 16]; 98 | hkdf_ctx 99 | .expand(&info_components.concat(), &mut oneshot_res) 100 | .unwrap(); 101 | 102 | // Now iteratively join the components of info_components until it's all 1 component. The value 103 | // of HKDF-Expand should be the same throughout 104 | let mut num_concatted = 0; 105 | let mut info_head = Vec::new(); 106 | 107 | while num_concatted < info_components.len() { 108 | info_head.extend(info_components[num_concatted]); 109 | 110 | // Build the new input to be the info head followed by the remaining components 111 | let input: Vec<&[u8]> = iter::once(info_head.as_slice()) 112 | .chain(info_components.iter().cloned().skip(num_concatted + 1)) 113 | .collect(); 114 | 115 | // Compute and compare to the one-shot answer 116 | let mut multipart_res = [0u8; 16]; 117 | hkdf_ctx 118 | .expand_multi_info(&input, &mut multipart_res) 119 | .unwrap(); 120 | assert_eq!(multipart_res, oneshot_res); 121 | 122 | num_concatted += 1; 123 | } 124 | } 125 | 126 | #[test] 127 | fn test_extract_streaming() { 128 | let ikm_components = COMPONENTS; 129 | let salt = b"mysalt"; 130 | 131 | // Compute HKDF-Extract on the concatenation of all the IKM components 132 | let (oneshot_res, _) = Hkdf::::extract(Some(&salt[..]), &ikm_components.concat()); 133 | 134 | // Now iteratively join the components of ikm_components until it's all 1 component. The value 135 | // of HKDF-Extract should be the same throughout 136 | let mut num_concatted = 0; 137 | let mut ikm_head = Vec::new(); 138 | 139 | while num_concatted < ikm_components.len() { 140 | ikm_head.extend(ikm_components[num_concatted]); 141 | 142 | // Make a new extraction context and build the new input to be the IKM head followed by the 143 | // remaining components 144 | let mut extract_ctx = HkdfExtract::::new(Some(&salt[..])); 145 | let input = iter::once(ikm_head.as_slice()) 146 | .chain(ikm_components.iter().cloned().skip(num_concatted + 1)); 147 | 148 | // Stream in the IKM input in the chunks specified 149 | for ikm in input { 150 | extract_ctx.input_ikm(ikm); 151 | } 152 | 153 | // Finalize and compare to the one-shot answer 154 | let (multipart_res, _) = extract_ctx.finalize(); 155 | assert_eq!(multipart_res, oneshot_res); 156 | 157 | num_concatted += 1; 158 | } 159 | 160 | let mut num_concatted = 0; 161 | let mut ikm_head = Vec::new(); 162 | 163 | while num_concatted < ikm_components.len() { 164 | ikm_head.extend(ikm_components[num_concatted]); 165 | 166 | // Make a new extraction context and build the new input to be the IKM head followed by the 167 | // remaining components 168 | let mut extract_ctx = SimpleHkdfExtract::::new(Some(&salt[..])); 169 | let input = iter::once(ikm_head.as_slice()) 170 | .chain(ikm_components.iter().cloned().skip(num_concatted + 1)); 171 | 172 | // Stream in the IKM input in the chunks specified 173 | for ikm in input { 174 | extract_ctx.input_ikm(ikm); 175 | } 176 | 177 | // Finalize and compare to the one-shot answer 178 | let (multipart_res, _) = extract_ctx.finalize(); 179 | assert_eq!(multipart_res, oneshot_res); 180 | 181 | num_concatted += 1; 182 | } 183 | } 184 | 185 | #[test] 186 | fn test_debug_impls() { 187 | fn needs_debug() {} 188 | needs_debug::>(); 189 | needs_debug::>(); 190 | } 191 | -------------------------------------------------------------------------------- /hkdf/tests/wycheproof.rs: -------------------------------------------------------------------------------- 1 | use blobby::Blob4Iterator; 2 | use hkdf::{Hkdf, HmacImpl}; 3 | use hmac::{Hmac, SimpleHmac}; 4 | use sha1::Sha1; 5 | use sha2::{Sha256, Sha384, Sha512, digest::OutputSizeUser}; 6 | 7 | fn test>(data: &[u8]) { 8 | for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() { 9 | let [ikm, salt, info, okm] = row.unwrap(); 10 | 11 | let prk = Hkdf::::new(Some(salt), ikm); 12 | let mut got_okm = vec![0; okm.len()]; 13 | 14 | let mut err = None; 15 | if prk.expand(info, &mut got_okm).is_err() { 16 | err = Some("prk expand"); 17 | } 18 | if got_okm != okm { 19 | err = Some("mismatch in okm"); 20 | } 21 | 22 | if let Some(err_desc) = err { 23 | panic!( 24 | "\n\ 25 | Failed test №{i}: {err_desc}\n\ 26 | ikm:\t{ikm:?}\n\ 27 | salt:\t{salt:?}\n\ 28 | info:\t{info:?}\n\ 29 | okm:\t{okm:?}\n" 30 | ); 31 | } 32 | } 33 | } 34 | 35 | macro_rules! new_test { 36 | ($name:ident, $test_name:expr, $hash:ty) => { 37 | #[test] 38 | fn $name() { 39 | let data = include_bytes!(concat!("data/", $test_name, ".blb")); 40 | test::<$hash, Hmac<$hash>>(data); 41 | test::<$hash, SimpleHmac<$hash>>(data); 42 | } 43 | }; 44 | } 45 | 46 | new_test!(wycheproof_sha1, "wycheproof-sha1", Sha1); 47 | new_test!(wycheproof_sha256, "wycheproof-sha256", Sha256); 48 | new_test!(wycheproof_sha384, "wycheproof-sha384", Sha384); 49 | new_test!(wycheproof_sha512, "wycheproof-sha512", Sha512); 50 | -------------------------------------------------------------------------------- /kbkdf/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## 0.0.1 (2025-02-04) 8 | - Initial release 9 | -------------------------------------------------------------------------------- /kbkdf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kbkdf" 3 | version = "0.1.0-pre.0" 4 | edition = "2024" 5 | authors = ["RustCrypto Developers"] 6 | license = "MIT OR Apache-2.0" 7 | homepage = "https://github.com/RustCrypto/KDFs/tree/master/kbkdf" 8 | repository = "https://github.com/RustCrypto/KDFs/" 9 | description = "Key Derivation Using Pseudorandom Function (KBKDF)" 10 | keywords = ["crypto", "KBKDF", "KDF"] 11 | categories = ["cryptography", "no-std"] 12 | readme = "README.md" 13 | rust-version = "1.85" 14 | exclude = ["/tests/*"] 15 | 16 | [dependencies] 17 | digest = { version = "0.11.0-rc.0", default-features = false, features = ["mac"] } 18 | 19 | [dev-dependencies] 20 | hex-literal = "1" 21 | hex = "0.4" 22 | hmac = { version = "0.13.0-rc.0", default-features = false } 23 | sha2 = { version = "0.11.0-rc.0", default-features = false } 24 | sha1 = { version = "0.11.0-rc.0", default-features = false } 25 | cmac = "0.8.0-pre.4" 26 | aes = "0.9.0-pre.3" 27 | 28 | [package.metadata.docs.rs] 29 | all-features = true 30 | -------------------------------------------------------------------------------- /kbkdf/README.md: -------------------------------------------------------------------------------- 1 | # RustCrypto: KBKDF 2 | 3 | [![crate][crate-image]][crate-link] 4 | [![Docs][docs-image]][docs-link] 5 | [![Build Status][build-image]][build-link] 6 | ![Apache2/MIT licensed][license-image] 7 | ![Rust Version][rustc-image] 8 | [![Project Chat][chat-image]][chat-link] 9 | 10 | Pure Rust implementation of the Key Based Key Derivation Function (KBKDF). 11 | This function is described in section 4 of [NIST SP 800-108r1, Recommendation 12 | for Key Derivation Using Pseudorandom Functions](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-108r1.pdf). 13 | 14 | # Usage 15 | 16 | The most common way to use KBKDF is as follows: you generate a shared secret with other party 17 | (e.g. via Diffie-Hellman algorithm) and use key derivation function to derive a shared key. 18 | 19 | ```rust 20 | use hex_literal::hex; 21 | use hmac::Hmac; 22 | use kbkdf::{Counter, Kbkdf, Params}; 23 | use sha2::Sha256; 24 | 25 | type HmacSha256 = Hmac; 26 | let counter = Counter::::default(); 27 | let key = counter 28 | .derive(Params::builder(b"secret").with_label(b"label").build()) 29 | .unwrap(); 30 | assert_eq!( 31 | key, 32 | hex!( 33 | "ff6a1e505e0f2546eae8f1e11ab95ff6" 34 | "47b78bb2182a835c7c1f8054ae7cfea5" 35 | "8182da6b978c411fa840326ebbe07bfc" 36 | "aaef01c090bb6f8e9c1da9dedf40bc3e" 37 | ) 38 | ); 39 | ``` 40 | 41 | ## License 42 | 43 | Licensed under either of: 44 | 45 | * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 46 | * [MIT license](http://opensource.org/licenses/MIT) 47 | 48 | at your option. 49 | 50 | ### Contribution 51 | 52 | Unless you explicitly state otherwise, any contribution intentionally submitted 53 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 54 | dual licensed as above, without any additional terms or conditions. 55 | 56 | [crate-image]: https://img.shields.io/crates/v/kbkdf.svg 57 | [crate-link]: https://crates.io/crates/kbkdf 58 | [docs-image]: https://docs.rs/kbkdf/badge.svg 59 | [docs-link]: https://docs.rs/kbkdf/ 60 | [build-image]: https://github.com/RustCrypto/KDFs/actions/workflows/kbkdf.yml/badge.svg 61 | [build-link]: https://github.com/RustCrypto/KDFs/actions/workflows/kbkdf.yml 62 | [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg 63 | [rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg 64 | [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg 65 | [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260043-KDFs 66 | 67 | -------------------------------------------------------------------------------- /kbkdf/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![doc = include_str!("../README.md")] 3 | #![doc( 4 | html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", 5 | html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" 6 | )] 7 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 8 | #![forbid(unsafe_code)] 9 | #![warn(missing_docs)] 10 | 11 | use core::{fmt, marker::PhantomData, ops::Mul}; 12 | use digest::{ 13 | KeyInit, Mac, 14 | array::{Array, ArraySize, typenum::Unsigned}, 15 | consts::{U8, U32}, 16 | crypto_common::KeySizeUser, 17 | typenum::op, 18 | }; 19 | 20 | pub mod sealed; 21 | 22 | /// KBKDF error type. 23 | #[derive(Debug, PartialEq)] 24 | pub enum Error { 25 | /// Indicates that the requested length of the derived key is too large for the value of R specified. 26 | InvalidRequestSize, 27 | } 28 | 29 | impl fmt::Display for Error { 30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 | match self { 32 | Error::InvalidRequestSize => write!( 33 | f, 34 | "Request output size is too large for the value of R specified" 35 | ), 36 | } 37 | } 38 | } 39 | 40 | impl core::error::Error for Error {} 41 | 42 | /// Parameters used for KBKDF. 43 | /// 44 | /// For more details, read the official specification: [NIST SP 800-108r1](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-108r1.pdf). 45 | pub struct Params<'k, 'l, 'c> { 46 | /// Key-derivation key. 47 | /// 48 | /// key that is used as an input to a key-derivation function (along with other input data) to derive keying material. 49 | pub kin: &'k [u8], 50 | /// A string that identifies the purpose for the derived keying material, which is encoded as a bit string. 51 | /// 52 | /// The encoding method for the Label is defined in a larger context, for example, in the protocol that uses a KDF. 53 | pub label: &'l [u8], 54 | /// A bit string containing the information related to the derived keying material. 55 | /// 56 | /// It may include the identities of the parties who are deriving and/or using the derived keying material and, 57 | /// optionally, a nonce known by the parties who derive the keys. 58 | pub context: &'c [u8], 59 | /// A flag indicating whether to update the Prf with the requested key length. 60 | pub use_l: bool, 61 | /// A flag indicating whether to separate the label from the context with a NULL byte. 62 | pub use_separator: bool, 63 | /// A flag indicating whether to update the Prf with the iteration counter. 64 | pub use_counter: bool, 65 | } 66 | 67 | impl<'k, 'l, 'c> Params<'k, 'l, 'c> { 68 | /// Create a new builder for [`Params`] 69 | pub fn builder(kin: &'k [u8]) -> ParamsBuilder<'k, 'l, 'c> { 70 | let params = Params { 71 | kin, 72 | label: &[], 73 | context: &[], 74 | use_l: true, 75 | use_separator: true, 76 | use_counter: true, 77 | }; 78 | ParamsBuilder(params) 79 | } 80 | } 81 | 82 | /// Parameters builders for [`Params`]. 83 | pub struct ParamsBuilder<'k, 'l, 'c>(Params<'k, 'l, 'c>); 84 | 85 | impl<'k, 'l, 'c> ParamsBuilder<'k, 'l, 'c> { 86 | /// Return the built [`Params`] 87 | pub fn build(self) -> Params<'k, 'l, 'c> { 88 | self.0 89 | } 90 | 91 | /// Set the label for the parameters 92 | pub fn with_label(mut self, label: &'l [u8]) -> Self { 93 | self.0.label = label; 94 | self 95 | } 96 | 97 | /// Set the context for the parameters 98 | pub fn with_context(mut self, context: &'c [u8]) -> Self { 99 | self.0.context = context; 100 | self 101 | } 102 | 103 | /// During the iterations, append the length of the Prf 104 | pub fn use_l(mut self, use_l: bool) -> Self { 105 | self.0.use_l = use_l; 106 | self 107 | } 108 | 109 | /// During the iterations, separate the label from the context with a NULL byte 110 | pub fn use_separator(mut self, use_separator: bool) -> Self { 111 | self.0.use_separator = use_separator; 112 | self 113 | } 114 | 115 | /// During the iterations, update the Prf with the iteration counter 116 | pub fn use_counter(mut self, use_counter: bool) -> Self { 117 | self.0.use_counter = use_counter; 118 | self 119 | } 120 | } 121 | 122 | // Helper structure along with [`KbkdfUser`] to compute values of L and H. 123 | struct KbkdfCore { 124 | _marker: PhantomData<(OutputLen, PrfOutputLen)>, 125 | } 126 | 127 | trait KbkdfUser { 128 | // L - An integer specifying the requested length (in bits) of the derived keying material 129 | // KOUT. L is represented as a bit string when it is an input to a key-derivation function. The 130 | // length of the bit string is specified by the encoding method for the input data. 131 | type L; 132 | 133 | // h - An integer that indicates the length (in bits) of the output of a single invocation of the 134 | // PRF. 135 | type H; 136 | } 137 | 138 | impl KbkdfUser for KbkdfCore 139 | where 140 | OutputLen: ArraySize + Mul, 141 | >::Output: Unsigned, 142 | PrfOutputLen: ArraySize + Mul, 143 | >::Output: Unsigned, 144 | { 145 | type L = op!(OutputLen * U8); 146 | type H = op!(PrfOutputLen * U8); 147 | } 148 | 149 | /// [`Kbkdf`] is a trait representing a mode of KBKDF. 150 | /// It takes multiple arguments: 151 | /// - Prf - the Pseudorandom Function to derive keys from 152 | /// - K - the expected output length of the newly derived key 153 | /// - R - An integer (1 <= r <= 32) that indicates the length of the binary encoding of the counter i 154 | /// as an integer in the interval [1, 2r − 1]. 155 | pub trait Kbkdf 156 | where 157 | Prf: Mac + KeyInit, 158 | K: KeySizeUser, 159 | K::KeySize: ArraySize + Mul, 160 | >::Output: Unsigned, 161 | Prf::OutputSize: ArraySize + Mul, 162 | >::Output: Unsigned, 163 | { 164 | /// Derives `key` from `kin` and other parameters. 165 | fn derive(&self, params: Params<'_, '_, '_>) -> Result, Error> { 166 | // n - An integer whose value is the number of iterations of the PRF needed to generate L 167 | // bits of keying material 168 | let n: u32 = as KbkdfUser>::L::U32 169 | .div_ceil( as KbkdfUser>::H::U32); 170 | 171 | if n as usize > 2usize.pow(R::U32) - 1 { 172 | return Err(Error::InvalidRequestSize); 173 | } 174 | 175 | let mut output = Array::::default(); 176 | let mut builder = output.as_mut_slice(); 177 | 178 | let mut ki = None; 179 | self.input_iv(&mut ki); 180 | let mut a = { 181 | let mut h = Prf::new_from_slice(params.kin).unwrap(); 182 | h.update(params.label); 183 | if params.use_separator { 184 | h.update(&[0]); 185 | } 186 | h.update(params.context); 187 | h.finalize().into_bytes() 188 | }; 189 | 190 | for counter in 1..=n { 191 | if counter > 1 { 192 | a = { 193 | let mut h = Prf::new_from_slice(params.kin).unwrap(); 194 | h.update(a.as_slice()); 195 | h.finalize().into_bytes() 196 | }; 197 | } 198 | 199 | let mut h = Prf::new_from_slice(params.kin).unwrap(); 200 | 201 | if Self::FEEDBACK_KI { 202 | if let Some(ki) = ki { 203 | h.update(ki.as_slice()); 204 | } 205 | } 206 | 207 | if Self::DOUBLE_PIPELINE { 208 | h.update(a.as_slice()); 209 | } 210 | if params.use_counter { 211 | // counter encoded as big endian u32 212 | // Type parameter R encodes how large the value is to be (either U8, U16, U24, or U32) 213 | // 214 | // counter = 1u32 ([0, 0, 0, 1]) 215 | // \-------/ 216 | // R = u24 217 | h.update(&counter.to_be_bytes()[(4 - R::USIZE / 8)..]); 218 | } 219 | 220 | // Fixed input data 221 | h.update(params.label); 222 | if params.use_separator { 223 | h.update(&[0]); 224 | } 225 | h.update(params.context); 226 | if params.use_l { 227 | h.update( 228 | &( as KbkdfUser>::L::U32).to_be_bytes() 229 | [..], 230 | ); 231 | } 232 | 233 | let buf = h.finalize().into_bytes(); 234 | ki = Some(buf.clone()); 235 | 236 | let remaining = usize::min(buf.len(), builder.len()); 237 | 238 | builder[..remaining].copy_from_slice(&buf[..remaining]); 239 | builder = &mut builder[remaining..]; 240 | } 241 | 242 | assert_eq!(builder.len(), 0, "output has uninitialized bytes"); 243 | 244 | Ok(output) 245 | } 246 | 247 | /// Input the IV in the PRF. 248 | fn input_iv(&self, _ki: &mut Option>) {} 249 | 250 | /// Whether the KI should be reinjected every round. 251 | /// 252 | /// Or, in other words, whether the KBKDF is in Feedback Mode. 253 | const FEEDBACK_KI: bool = false; 254 | 255 | /// Whether the KBKDF is in Double-Pipeline Mode. 256 | const DOUBLE_PIPELINE: bool = false; 257 | } 258 | 259 | /// KBKDF in Counter Mode. 260 | pub struct Counter { 261 | _marker: PhantomData<(Prf, K, R)>, 262 | } 263 | 264 | impl Default for Counter { 265 | fn default() -> Self { 266 | Self { 267 | _marker: PhantomData, 268 | } 269 | } 270 | } 271 | 272 | impl Kbkdf for Counter 273 | where 274 | Prf: Mac + KeyInit, 275 | K: KeySizeUser, 276 | K::KeySize: ArraySize + Mul, 277 | >::Output: Unsigned, 278 | Prf::OutputSize: ArraySize + Mul, 279 | >::Output: Unsigned, 280 | R: sealed::R, 281 | { 282 | } 283 | 284 | /// KBKDF in Feedback Mode. 285 | pub struct Feedback<'a, Prf, K, R = U32> 286 | where 287 | Prf: Mac, 288 | { 289 | iv: Option<&'a Array>, 290 | _marker: PhantomData<(Prf, K, R)>, 291 | } 292 | 293 | impl<'a, Prf, K, R> Feedback<'a, Prf, K, R> 294 | where 295 | Prf: Mac, 296 | { 297 | /// Creates a new [`Feedback`] instance with an optional IV. 298 | pub fn new(iv: Option<&'a Array>) -> Self { 299 | Self { 300 | iv, 301 | _marker: PhantomData, 302 | } 303 | } 304 | } 305 | 306 | impl Kbkdf for Feedback<'_, Prf, K, R> 307 | where 308 | Prf: Mac + KeyInit, 309 | K: KeySizeUser, 310 | K::KeySize: ArraySize + Mul, 311 | >::Output: Unsigned, 312 | Prf::OutputSize: ArraySize + Mul, 313 | >::Output: Unsigned, 314 | R: sealed::R, 315 | { 316 | fn input_iv(&self, ki: &mut Option>) { 317 | if let Some(iv) = self.iv { 318 | *ki = Some(iv.clone()) 319 | } 320 | } 321 | 322 | const FEEDBACK_KI: bool = true; 323 | } 324 | 325 | /// KBKDF in Double-Pipeline Mode. 326 | pub struct DoublePipeline 327 | where 328 | Prf: Mac, 329 | { 330 | _marker: PhantomData<(Prf, K, R)>, 331 | } 332 | 333 | impl Default for DoublePipeline 334 | where 335 | Prf: Mac, 336 | { 337 | fn default() -> Self { 338 | Self { 339 | _marker: PhantomData, 340 | } 341 | } 342 | } 343 | 344 | impl Kbkdf for DoublePipeline 345 | where 346 | Prf: Mac + KeyInit, 347 | K: KeySizeUser, 348 | K::KeySize: ArraySize + Mul, 349 | >::Output: Unsigned, 350 | Prf::OutputSize: ArraySize + Mul, 351 | >::Output: Unsigned, 352 | R: sealed::R, 353 | { 354 | const DOUBLE_PIPELINE: bool = true; 355 | } 356 | 357 | #[cfg(test)] 358 | mod tests; 359 | -------------------------------------------------------------------------------- /kbkdf/src/sealed.rs: -------------------------------------------------------------------------------- 1 | //! The module provides the sealed trait [R]. 2 | 3 | use digest::{ 4 | array::typenum::Unsigned, 5 | consts::{U8, U16, U24, U32}, 6 | }; 7 | 8 | mod private { 9 | use digest::consts::{U8, U16, U24, U32}; 10 | 11 | pub trait Sealed {} 12 | 13 | impl Sealed for U8 {} 14 | impl Sealed for U16 {} 15 | impl Sealed for U24 {} 16 | impl Sealed for U32 {} 17 | } 18 | 19 | /// Marker used to register valid values for R in the KBKDF. 20 | pub trait R: Unsigned + private::Sealed {} 21 | 22 | impl R for U8 {} 23 | impl R for U16 {} 24 | impl R for U24 {} 25 | impl R for U32 {} 26 | -------------------------------------------------------------------------------- /kbkdf/src/tests.rs: -------------------------------------------------------------------------------- 1 | use super::{Array, Counter, DoublePipeline, Feedback, Kbkdf, Params}; 2 | use core::convert::TryFrom; 3 | use digest::{consts::*, crypto_common::KeySizeUser}; 4 | use hex_literal::hex; 5 | 6 | #[derive(Debug)] 7 | struct KnownValue { 8 | key: &'static [u8], 9 | iv: Option<&'static [u8]>, 10 | use_l: bool, 11 | label: &'static [u8], 12 | context: &'static [u8], 13 | use_separator: bool, 14 | expected: &'static [u8], 15 | } 16 | 17 | static KNOWN_VALUES_COUNTER_HMAC_SHA256: &[KnownValue] = &[ 18 | KnownValue { 19 | iv: None, 20 | use_l: false, 21 | use_separator: false, 22 | label: &[], 23 | context: &[], 24 | key: &hex!( 25 | "241C3FBAABEDE87601B1C778B24F9A32" 26 | "742A14FE34DA61D77E8352EF9D6C7FC8" 27 | "E335E32344E21D7DC0CD627D7E2FF973" 28 | "992611F372C5D3DD91C100F2C6DB2CAF" 29 | ), 30 | expected: &hex!( 31 | "0FBF4313B2F1AF1F98C9763FE7F816CD" 32 | "6464234F7C524F0C4ACDF66F287B01EB" 33 | "82D3A90CEF26EE996EE4F0295FA7FA36" 34 | "1E2E85DC710A236974E1ABBC342F4E23" 35 | "D9A8F6B1ADC4C48332C5ED88C42FDCFB" 36 | "BA34CF70F1EA599908FBE35E2C121E0D" 37 | "BFD94D45C70FC9D9CCB899E439D21F88" 38 | "D3924EF5EC8613E5C386DE7B22427FC4" 39 | ), 40 | }, 41 | KnownValue { 42 | iv: None, 43 | use_l: true, 44 | use_separator: true, 45 | label: &[0x22, 0x33], 46 | context: &[0x0, 0x11], 47 | key: &hex!( 48 | "241C3FBAABEDE87601B1C778B24F9A32" 49 | "742A14FE34DA61D77E8352EF9D6C7FC8" 50 | "E335E32344E21D7DC0CD627D7E2FF973" 51 | "992611F372C5D3DD91C100F2C6DB2CAF" 52 | ), 53 | expected: &hex!( 54 | "7F21415C8ED32102BE3C284E970B3DF4" 55 | "5FCE9F7464FC6616ED59AC1F1ECA0565" 56 | "8E2868C57974293A79D49B576C4083C3" 57 | "48AD07508E8A673D0F6B496ED444E0DE" 58 | "80AE1F146F8C2CBFE09F1D04516338DE" 59 | "9E5284236FE29CB2D71A183B7573DFE7" 60 | "0A8321ADAAF6FC2EDC73C228289948DD" 61 | "3230D56E7A9103E2736957B326ACE921" 62 | ), 63 | }, 64 | ]; 65 | 66 | #[test] 67 | fn test_static_values_counter() { 68 | type HmacSha256 = hmac::Hmac; 69 | type HmacSha512 = hmac::Hmac; 70 | 71 | let counter = Counter::::default(); 72 | for (v, i) in KNOWN_VALUES_COUNTER_HMAC_SHA256.iter().zip(0..) { 73 | assert_eq!( 74 | counter.derive( 75 | Params::builder(v.key) 76 | .use_l(v.use_l) 77 | .use_separator(v.use_separator) 78 | .with_label(v.label) 79 | .with_context(v.context) 80 | .build() 81 | ), 82 | Ok(Array::<_, _>::try_from(v.expected).unwrap()), 83 | "key derivation failed for (index: {i}):\n{v:x?}" 84 | ); 85 | } 86 | } 87 | 88 | #[test] 89 | fn test_counter_kbkdfvs() { 90 | type HmacSha256 = hmac::Hmac; 91 | struct MockOutput; 92 | 93 | impl KeySizeUser for MockOutput { 94 | type KeySize = U32; 95 | } 96 | 97 | let counter = Counter::::default(); 98 | // KDFCTR_gen.txt count 15 99 | assert_eq!( 100 | counter.derive( 101 | Params::builder(&hex!( 102 | "43eef6d824fd820405626ab9b6d79f1fd04e126ab8e17729e3afc7cb5af794f8" 103 | )) 104 | .use_l(false) 105 | .use_separator(false) 106 | .with_label(&hex!( 107 | "5e269b5a7bdedcc3e875e2725693a257fc60011af7dcd68a3358507fe29b0659" 108 | "ca66951daa05a15032033650bc58a27840f8fbe9f4088b9030738f68" 109 | )) 110 | .build() 111 | ), 112 | Ok(Array::::from(hex!( 113 | "f0a339ecbcae6add1afb27da3ba40a1320c6427a58afb9dc366b219b7eb29ecf" 114 | ))), 115 | ); 116 | } 117 | 118 | static KNOWN_VALUES_FEEDBACK_HMAC_SHA256: &[KnownValue] = &[ 119 | KnownValue { 120 | iv: Some(b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 121 | use_l: true, 122 | use_separator: true, 123 | label: &[0x22, 0x33], 124 | context: &[0x0, 0x11], 125 | key: &hex!( 126 | "241C3FBAABEDE87601B1C778B24F9A32" 127 | "742A14FE34DA61D77E8352EF9D6C7FC8" 128 | "E335E32344E21D7DC0CD627D7E2FF973" 129 | "992611F372C5D3DD91C100F2C6DB2CAF" 130 | ), 131 | expected: &hex!( 132 | "C8C8E79188DB5732B52F81111E2982BD" 133 | "479865EF98E90A823926BC0EA1EB173B" 134 | "21CA03B80228A6A1E27BE64DA382F1B7" 135 | "ADFF97CF43598AF2435827B2F6E78DD5" 136 | "F9CBCC4948775451AD2BD44A9DBE2EE4" 137 | "6FA5A73463E10142A3A1228183B45BC8" 138 | "D3831AA13EED6F94E8F221FACBC80F8B" 139 | "D19BDF06EC82DE7AE0FE0EE37CA51FF2" 140 | ), 141 | }, 142 | KnownValue { 143 | iv: None, 144 | use_l: true, 145 | use_separator: true, 146 | label: &[0x22, 0x33], 147 | context: &[0x0, 0x11], 148 | key: &hex!( 149 | "241C3FBAABEDE87601B1C778B24F9A32" 150 | "742A14FE34DA61D77E8352EF9D6C7FC8" 151 | "E335E32344E21D7DC0CD627D7E2FF973" 152 | "992611F372C5D3DD91C100F2C6DB2CAF" 153 | ), 154 | expected: &hex!( 155 | "7F21415C8ED32102BE3C284E970B3DF4" 156 | "5FCE9F7464FC6616ED59AC1F1ECA0565" 157 | "B0BDCC163BF8119490B0B82715FF3EF1" 158 | "B52DF9A81BC836BA6FC5168C08CE837B" 159 | "0CB7C18D1C47459DF6A05C16C140109E" 160 | "8FC15D0EC9541FC41E127EBBDBC48CDE" 161 | "93E8909855F9070E9B709A497D31A825" 162 | "3E3CB4EEB1C18586277B2F76E4BF9FF0" 163 | ), 164 | }, 165 | ]; 166 | 167 | #[test] 168 | fn test_static_values_feedback() { 169 | type HmacSha256 = hmac::Hmac; 170 | type HmacSha512 = hmac::Hmac; 171 | 172 | for (v, i) in KNOWN_VALUES_FEEDBACK_HMAC_SHA256.iter().zip(0..) { 173 | let iv = v.iv.map(|iv| Array::try_from(iv).unwrap()); 174 | let feedback = Feedback::::new(iv.as_ref()); 175 | assert_eq!( 176 | feedback.derive( 177 | Params::builder(v.key) 178 | .use_l(v.use_l) 179 | .use_separator(v.use_separator) 180 | .with_label(v.label) 181 | .with_context(v.context) 182 | .build() 183 | ), 184 | Ok(Array::<_, _>::try_from(v.expected).unwrap()), 185 | "key derivation failed for (index: {i}):\n{v:x?}" 186 | ); 187 | } 188 | } 189 | 190 | static KNOWN_VALUES_DOUBLE_PIPELINE_HMAC_SHA256: &[KnownValue] = &[KnownValue { 191 | iv: None, 192 | use_l: false, //true, 193 | use_separator: true, 194 | label: &hex!("921ab061920b191de12f746ac9de08"), 195 | context: &hex!("4f2c20f01775e27bcacdc21ee4a5ff0387758f36d8ec71c7a8c8208284f650b611837e"), 196 | key: &hex!("7d4f86fdfd1c4ba04c674a68d60316d12c99c1b1f44f0a8e02bd2601377ebcd9"), 197 | expected: &hex!( 198 | "506bc2ba51410b2a6e7c05d33891520d" 199 | "dd5f702ad3d6203d76d8dae1216d0783" 200 | "d8c59fae2e821d8eff2d8ddd93a6741c" 201 | "8f144fb96e9ca7d7c532468f213f5efe" 202 | ), 203 | }]; 204 | 205 | #[test] 206 | fn test_static_values_double_pipeline() { 207 | type HmacSha256 = hmac::Hmac; 208 | 209 | struct MockOutput; 210 | 211 | impl KeySizeUser for MockOutput { 212 | type KeySize = U64; 213 | } 214 | 215 | for (v, i) in KNOWN_VALUES_DOUBLE_PIPELINE_HMAC_SHA256.iter().zip(0..) { 216 | let dbl_pipeline = DoublePipeline::::default(); 217 | assert_eq!( 218 | dbl_pipeline.derive( 219 | Params::builder(v.key) 220 | .use_l(v.use_l) 221 | .use_separator(v.use_separator) 222 | .use_counter(false) 223 | .with_label(v.label) 224 | .with_context(v.context) 225 | .build(), 226 | ), 227 | Ok(Array::<_, _>::try_from(v.expected).unwrap()), 228 | "key derivation failed for (index: {i}):\n{v:x?}" 229 | ); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /kbkdf/tests/kbkdf/main.rs: -------------------------------------------------------------------------------- 1 | mod parser; 2 | 3 | type HmacSha1 = hmac::Hmac; 4 | type HmacSha224 = hmac::Hmac; 5 | type HmacSha256 = hmac::Hmac; 6 | type HmacSha384 = hmac::Hmac; 7 | type HmacSha512 = hmac::Hmac; 8 | 9 | type CmacAes128 = cmac::Cmac; 10 | type CmacAes192 = cmac::Cmac; 11 | type CmacAes256 = cmac::Cmac; 12 | 13 | macro_rules! mock_output { 14 | ($name:ident, $size:ident) => { 15 | struct $name; 16 | 17 | impl digest::crypto_common::KeySizeUser for $name { 18 | type KeySize = digest::consts::$size; 19 | } 20 | }; 21 | } 22 | 23 | mock_output!(MockOutputU128, U16); 24 | mock_output!(MockOutputU160, U20); 25 | mock_output!(MockOutputU256, U32); 26 | mock_output!(MockOutputU320, U40); 27 | mock_output!(MockOutputU480, U60); 28 | mock_output!(MockOutputU512, U64); 29 | mock_output!(MockOutputU528, U66); 30 | mock_output!(MockOutputU560, U70); 31 | mock_output!(MockOutputU1024, U128); 32 | mock_output!(MockOutputU1040, U130); 33 | mock_output!(MockOutputU1600, U200); 34 | mock_output!(MockOutputU2048, U256); 35 | mock_output!(MockOutputU2064, U258); 36 | mock_output!(MockOutputU2400, U300); 37 | -------------------------------------------------------------------------------- /kbkdf/tests/kbkdf/parser.rs: -------------------------------------------------------------------------------- 1 | use digest::consts::*; 2 | use kbkdf::{Kbkdf, Params}; 3 | 4 | use core::{convert::TryInto, ops::Mul}; 5 | use digest::{ 6 | KeyInit, Mac, 7 | array::{ArraySize, typenum::Unsigned}, 8 | crypto_common::KeySizeUser, 9 | }; 10 | 11 | use crate::*; 12 | 13 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 14 | enum Prf { 15 | CmacAes128, 16 | CmacAes192, 17 | CmacAes256, 18 | CmacTdes2, 19 | CmacTdes3, 20 | HmacSha1, 21 | HmacSha224, 22 | HmacSha256, 23 | HmacSha384, 24 | HmacSha512, 25 | } 26 | 27 | impl Prf { 28 | fn from_str(s: &str) -> Self { 29 | match s { 30 | "[PRF=CMAC_AES128]" => Self::CmacAes128, 31 | "[PRF=CMAC_AES192]" => Self::CmacAes192, 32 | "[PRF=CMAC_AES256]" => Self::CmacAes256, 33 | "[PRF=CMAC_TDES2]" => Self::CmacTdes2, 34 | "[PRF=CMAC_TDES3]" => Self::CmacTdes3, 35 | "[PRF=HMAC_SHA1]" => Self::HmacSha1, 36 | "[PRF=HMAC_SHA224]" => Self::HmacSha224, 37 | "[PRF=HMAC_SHA256]" => Self::HmacSha256, 38 | "[PRF=HMAC_SHA384]" => Self::HmacSha384, 39 | "[PRF=HMAC_SHA512]" => Self::HmacSha512, 40 | _ => panic!("Invalid prf: {s}"), 41 | } 42 | } 43 | 44 | fn is_supported(&self) -> bool { 45 | let not_supported = matches!(self, Self::CmacTdes2 | Self::CmacTdes3); 46 | !not_supported 47 | } 48 | } 49 | 50 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 51 | enum CounterLocation { 52 | Before, 53 | Middle, 54 | After, 55 | BeforeIter, 56 | AfterIter, 57 | } 58 | 59 | impl CounterLocation { 60 | fn from_str(s: &str) -> Self { 61 | match s { 62 | "[CTRLOCATION=BEFORE_FIXED]" => Self::Before, 63 | "[CTRLOCATION=MIDDLE_FIXED]" => Self::Middle, 64 | "[CTRLOCATION=AFTER_FIXED]" => Self::After, 65 | "[CTRLOCATION=AFTER_ITER]" => Self::AfterIter, 66 | "[CTRLOCATION=BEFORE_ITER]" => Self::BeforeIter, 67 | _ => panic!("Invalid counter_location: {s}"), 68 | } 69 | } 70 | 71 | fn is_supported(&self) -> bool { 72 | matches!(self, Self::Before | Self::AfterIter) 73 | } 74 | } 75 | 76 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 77 | enum Rlen { 78 | Bits8, 79 | Bits16, 80 | Bits24, 81 | Bits32, 82 | } 83 | 84 | impl Rlen { 85 | fn from_str(s: &str) -> Self { 86 | match s { 87 | "[RLEN=8_BITS]" => Self::Bits8, 88 | "[RLEN=16_BITS]" => Self::Bits16, 89 | "[RLEN=24_BITS]" => Self::Bits24, 90 | "[RLEN=32_BITS]" => Self::Bits32, 91 | _ => panic!("Invalid r_len: {s}"), 92 | } 93 | } 94 | } 95 | 96 | /// Each trait implementation represents one KBKDF mode and its data. 97 | trait TestData { 98 | /// Returns the expected output length of the output key. 99 | fn l(&self) -> usize; 100 | 101 | /// Reads test data from the iterator over test data file. 102 | fn read_test_data<'a>(lines: impl Iterator, ctx: CounterLocation) -> Self; 103 | 104 | /// Executes KBKDF key derivation. 105 | fn test_kbkdf(&self, use_counter: bool) 106 | where 107 | Prf: Mac + KeyInit, 108 | K: KeySizeUser, 109 | K::KeySize: ArraySize + Mul, 110 | >::Output: Unsigned, 111 | Prf::OutputSize: ArraySize + Mul, 112 | >::Output: Unsigned, 113 | R: kbkdf::sealed::R; 114 | } 115 | 116 | struct CounterTestData { 117 | l: usize, 118 | ki: Vec, 119 | fixed_data: (Vec, Vec), 120 | ko: Vec, 121 | } 122 | 123 | impl TestData for CounterTestData { 124 | fn l(&self) -> usize { 125 | self.l 126 | } 127 | 128 | fn read_test_data<'a>( 129 | mut data: impl Iterator, 130 | counter_location: CounterLocation, 131 | ) -> Self { 132 | // L = ... 133 | let l = data.next().unwrap()[4..].parse().unwrap(); 134 | // KI = ... 135 | let ki = hex::decode(&data.next().unwrap()[5..]).unwrap(); 136 | 137 | let fixed_data = if let CounterLocation::Middle = counter_location { 138 | // Skip "DataBeforeCtrLen" 139 | data.next(); 140 | let before_counter = hex::decode(&data.next().unwrap()[19..]).unwrap(); 141 | 142 | // Skip "DataAfterCtrLen" 143 | data.next(); 144 | let after_counter = hex::decode(&data.next().unwrap()[18..]).unwrap(); 145 | 146 | (before_counter, after_counter) 147 | } else { 148 | // Skip "FixedInputDataByteLen". 149 | data.next(); 150 | let fixed_input_data = hex::decode(&data.next().unwrap()[17..]).unwrap(); 151 | 152 | (fixed_input_data, Vec::new()) 153 | }; 154 | let ko = hex::decode(&data.next().unwrap()[5..]).unwrap(); 155 | 156 | Self { 157 | l, 158 | ki, 159 | fixed_data, 160 | ko, 161 | } 162 | } 163 | 164 | fn test_kbkdf(&self, use_counter: bool) 165 | where 166 | Prf: Mac + KeyInit, 167 | K: KeySizeUser, 168 | K::KeySize: ArraySize + Mul, 169 | >::Output: Unsigned, 170 | Prf::OutputSize: ArraySize + Mul, 171 | >::Output: Unsigned, 172 | R: kbkdf::sealed::R, 173 | { 174 | let counter = kbkdf::Counter::::default(); 175 | 176 | let (label, context) = &self.fixed_data; 177 | 178 | let key = counter 179 | .derive( 180 | Params::builder(self.ki.as_slice()) 181 | .use_l(false) 182 | .use_separator(false) 183 | .use_counter(use_counter) 184 | .with_label(label.as_slice()) 185 | .with_context(context.as_slice()) 186 | .build(), 187 | ) 188 | .unwrap(); 189 | 190 | assert_eq!(self.ko[..], key[..]); 191 | } 192 | } 193 | 194 | struct DoublePipelineTestData { 195 | l: usize, 196 | ki: Vec, 197 | fixed_data: Vec, 198 | ko: Vec, 199 | } 200 | 201 | impl TestData for DoublePipelineTestData { 202 | fn read_test_data<'a>(mut data: impl Iterator, _: CounterLocation) -> Self { 203 | // L = ... 204 | let l = data.next().unwrap()[4..].parse().unwrap(); 205 | // KI = ... 206 | let ki = hex::decode(&data.next().unwrap()[5..]).unwrap(); 207 | 208 | // Skip "FixedInputDataByteLen". 209 | data.next(); 210 | let fixed_data = hex::decode(&data.next().unwrap()[17..]).unwrap(); 211 | 212 | let ko = hex::decode(&data.next().unwrap()[5..]).unwrap(); 213 | 214 | Self { 215 | l, 216 | ki, 217 | fixed_data, 218 | ko, 219 | } 220 | } 221 | 222 | fn test_kbkdf(&self, use_counter: bool) 223 | where 224 | Prf: Mac + KeyInit, 225 | K: KeySizeUser, 226 | K::KeySize: ArraySize + Mul, 227 | >::Output: Unsigned, 228 | Prf::OutputSize: ArraySize + Mul, 229 | >::Output: Unsigned, 230 | R: kbkdf::sealed::R, 231 | { 232 | let double_pipeline = kbkdf::DoublePipeline::::default(); 233 | 234 | let key = double_pipeline 235 | .derive( 236 | Params::builder(self.ki.as_slice()) 237 | .use_l(false) 238 | .use_separator(false) 239 | .use_counter(use_counter) 240 | .with_label(self.fixed_data.as_slice()) 241 | .build(), 242 | ) 243 | .unwrap(); 244 | 245 | assert_eq!(self.ko[..], key[..]); 246 | } 247 | 248 | fn l(&self) -> usize { 249 | self.l 250 | } 251 | } 252 | 253 | struct FeedbackTestData { 254 | l: usize, 255 | ki: Vec, 256 | iv: Vec, 257 | fixed_data: Vec, 258 | ko: Vec, 259 | } 260 | 261 | impl TestData for FeedbackTestData { 262 | fn read_test_data<'a>(mut data: impl Iterator, _: CounterLocation) -> Self { 263 | // L = ... 264 | let l = data.next().unwrap()[4..].parse().unwrap(); 265 | // KI = ... 266 | let ki = hex::decode(&data.next().unwrap()[5..]).unwrap(); 267 | 268 | // Skip "IVlen" 269 | data.next(); 270 | // IV = ... 271 | let iv = hex::decode(&data.next().unwrap()[5..]).unwrap(); 272 | 273 | // Skip "FixedInputDataByteLen". 274 | data.next(); 275 | let fixed_data = hex::decode(&data.next().unwrap()[17..]).unwrap(); 276 | 277 | let ko = hex::decode(&data.next().unwrap()[5..]).unwrap(); 278 | 279 | Self { 280 | l, 281 | ki, 282 | iv, 283 | fixed_data, 284 | ko, 285 | } 286 | } 287 | 288 | fn test_kbkdf(&self, use_counter: bool) 289 | where 290 | Prf: Mac + KeyInit, 291 | K: KeySizeUser, 292 | K::KeySize: ArraySize + Mul, 293 | >::Output: Unsigned, 294 | Prf::OutputSize: ArraySize + Mul, 295 | >::Output: Unsigned, 296 | R: kbkdf::sealed::R, 297 | { 298 | let iv = if !self.iv.is_empty() { 299 | Some(self.iv.as_slice().try_into().unwrap()) 300 | } else { 301 | None 302 | }; 303 | let feedback = kbkdf::Feedback::::new(iv); 304 | 305 | let key = feedback 306 | .derive( 307 | Params::builder(self.ki.as_slice()) 308 | .use_l(false) 309 | .use_separator(false) 310 | .use_counter(use_counter) 311 | .with_label(self.fixed_data.as_slice()) 312 | .build(), 313 | ) 314 | .unwrap(); 315 | 316 | assert_eq!(self.ko[..], key[..]); 317 | } 318 | 319 | fn l(&self) -> usize { 320 | self.l 321 | } 322 | } 323 | 324 | // This function tests KBKDF implementation against test vectors parsed from the file. 325 | // 326 | // All KDF parameters are obtained in runtime, but the implementation relies on generic type parameters which are compile-time. 327 | // Macros inside this function generate all possible KDF parameter configurations. A suitable KDF configuration will be selected during runtime. 328 | fn test_kbkdf(test_data: T, prf: Prf, r_len: Rlen, use_counter: bool) { 329 | macro_rules! gen_inner { 330 | ($prf_ty:ident, { $($l_value:expr => $l_ty:ident,)* }, { $($r_value:expr => $r_ty:ident,)* }) => { 331 | gen_inner!(@inner $prf_ty : $($l_value => $l_ty,)* ; $($r_value => $r_ty,)*); 332 | }; 333 | (@inner $prf_ty:ident : $next_l_value:expr => $next_l_ty:ident, $($l_value:expr => $l_ty:ident,)* ; $($r_value:expr => $r_ty:ident,)*) => { 334 | if test_data.l() == $next_l_value { 335 | $( 336 | if r_len == $r_value { 337 | test_data.test_kbkdf::<$prf_ty, $next_l_ty, $r_ty>(use_counter); 338 | return; 339 | } 340 | )* 341 | } 342 | gen_inner!(@inner $prf_ty : $($l_value => $l_ty,)* ; $($r_value => $r_ty,)*); 343 | }; 344 | (@inner $prf_ty:ident : ; $($r_value:expr => $r_ty:ident,)*) => {}; 345 | } 346 | 347 | macro_rules! gen_test { 348 | ({ $($prf_value:expr => $prf_ty:ident,)* }, { $($l_value:expr => $l_ty:ident,)* }, { $($r_value:expr => $r_ty:ident,)* }) => { 349 | gen_test!(@inner $($prf_value => $prf_ty,)* ; $($l_value => $l_ty,)* ; $($r_value => $r_ty,)*); 350 | }; 351 | (@inner $next_prf_value:expr => $next_prf_ty:ident, $($prf_value:expr => $prf_ty:ident,)* ; $($l_value:expr => $l_ty:ident,)* ; $($r_value:expr => $r_ty:ident,)*) => { 352 | if prf == $next_prf_value { 353 | gen_inner!($next_prf_ty, { $($l_value => $l_ty,)* }, { $($r_value => $r_ty,)* }); 354 | } 355 | gen_test!(@inner $($prf_value => $prf_ty,)* ; $($l_value => $l_ty,)* ; $($r_value => $r_ty,)*); 356 | }; 357 | (@inner ; $($l_value:expr => $l_ty:ident,)* ; $($r_value:expr => $r_ty:ident,)*) => {}; 358 | } 359 | 360 | gen_test!({ 361 | Prf::CmacAes128 => CmacAes128, 362 | Prf::CmacAes192 => CmacAes192, 363 | Prf::CmacAes256 => CmacAes256, 364 | Prf::HmacSha1 => HmacSha1, 365 | Prf::HmacSha224 => HmacSha224, 366 | Prf::HmacSha256 => HmacSha256, 367 | Prf::HmacSha384 => HmacSha384, 368 | Prf::HmacSha512 => HmacSha512, 369 | }, { 370 | 128 => MockOutputU128, 371 | 160 => MockOutputU160, 372 | 256 => MockOutputU256, 373 | 320 => MockOutputU320, 374 | 480 => MockOutputU480, 375 | 512 => MockOutputU512, 376 | 528 => MockOutputU528, 377 | 560 => MockOutputU560, 378 | 1024 => MockOutputU1024, 379 | 1040 => MockOutputU1040, 380 | 1600 => MockOutputU1600, 381 | 2048 => MockOutputU2048, 382 | 2064 => MockOutputU2064, 383 | 2400 => MockOutputU2400, 384 | }, { 385 | Rlen::Bits8 => U8, 386 | Rlen::Bits16 => U16, 387 | Rlen::Bits24 => U24, 388 | Rlen::Bits32 => U32, 389 | }); 390 | 391 | panic!( 392 | "unhandled KBKDF parameters: {:?} {} {:?}", 393 | prf, 394 | test_data.l(), 395 | r_len 396 | ); 397 | } 398 | 399 | fn next_line<'a>(mut data: impl Iterator) -> Option<&'a str> { 400 | Some(loop { 401 | if let Some(l) = data.next() { 402 | if !l.is_empty() { 403 | break l; 404 | } 405 | } else { 406 | return None; 407 | } 408 | }) 409 | } 410 | 411 | /// Parses NIST test vectors and test the KBKDF implementation against them. 412 | /// 413 | /// The return value should be ignored. It uses the `Option`type only to 414 | /// get out of the function conveniently when the end of the file is reached. 415 | fn eval_test_vectors(data: &str, use_counter: bool) -> Option<()> { 416 | let mut data = data.split("\r\n"); 417 | 418 | let mut line = data.next(); 419 | 420 | while let Some(l) = line { 421 | // Skip comments and empty lines. 422 | if l.starts_with("#") || l.is_empty() { 423 | line = data.next(); 424 | continue; 425 | } 426 | 427 | // Read and parse KBKDF configuration. 428 | let prf = Prf::from_str(l); 429 | let (counter_location, r_len) = if use_counter { 430 | ( 431 | CounterLocation::from_str(data.next().unwrap()), 432 | Rlen::from_str(data.next().unwrap()), 433 | ) 434 | } else { 435 | // Counter location and r-len are not needed and do not present in a test file. 436 | // We any use any values here, because they are ignored anyway 437 | (CounterLocation::Before, Rlen::Bits8) 438 | }; 439 | 440 | if !prf.is_supported() || !counter_location.is_supported() { 441 | // Skip unsupported configuration. 442 | 443 | line = loop { 444 | let next_line = next_line(&mut data)?; 445 | if next_line.starts_with("[PRF=") { 446 | // Reached next KBKDF configuration. 447 | break Some(next_line); 448 | } 449 | }; 450 | } else { 451 | let mut count_data = next_line(&mut data); 452 | while let Some(count) = count_data { 453 | if count.is_empty() { 454 | break; 455 | } 456 | 457 | // Read test cases data. 458 | let test_data = T::read_test_data(&mut data, counter_location); 459 | 460 | // Test KBKDF. 461 | test_kbkdf(test_data, prf, r_len, use_counter); 462 | 463 | let next_line = next_line(&mut data)?; 464 | 465 | if next_line.starts_with("[PRF=") { 466 | line = Some(next_line); 467 | // Reached the next KBKDF configuration. 468 | break; 469 | } else { 470 | // Continue reading test cases. 471 | count_data = Some(next_line); 472 | } 473 | } 474 | } 475 | } 476 | 477 | Some(()) 478 | } 479 | 480 | #[test] 481 | fn counter_mode() { 482 | let data = include_str!("../data/CounterMode/KDFCTR_gen.rsp"); 483 | 484 | eval_test_vectors::(data, true); 485 | } 486 | 487 | #[test] 488 | fn feedback_mode_no_zero_iv() { 489 | let data = include_str!("../data/FeedbackModeNOzeroiv/KDFFeedback_gen.rsp"); 490 | 491 | eval_test_vectors::(data, true); 492 | } 493 | 494 | #[test] 495 | fn feedback_mode_with_zero_iv() { 496 | let data = include_str!("../data/FeedbackModewzeroiv/KDFFeedback_gen.rsp"); 497 | 498 | eval_test_vectors::(data, true); 499 | } 500 | 501 | #[test] 502 | fn feedback_mode_without_counter() { 503 | let data = include_str!("../data/FeedbackModenocounter/KDFFeedback_gen.rsp"); 504 | 505 | eval_test_vectors::(data, false); 506 | } 507 | 508 | #[test] 509 | fn pipeline_mode_without_counter() { 510 | let data = include_str!("../data/PipelineModeWOCounterr/KDFDblPipeline_gen.rsp"); 511 | 512 | eval_test_vectors::(data, false); 513 | } 514 | 515 | #[test] 516 | fn pipeline_mode_with_counter() { 517 | let data = include_str!("../data/PipelineModewithCounter/KDFDblPipeline_gen.rsp"); 518 | 519 | eval_test_vectors::(data, true); 520 | } 521 | --------------------------------------------------------------------------------