diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5cde165 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/.github/workflows/cbc.yaml b/.github/workflows/cbc.yaml new file mode 100644 index 0000000..41a3fc4 --- /dev/null +++ b/.github/workflows/cbc.yaml @@ -0,0 +1,55 @@ +name: cbc + +on: + pull_request: + paths: + - "cbc/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: cbc + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + profile: minimal + - run: cargo build --no-default-features --release --target ${{ matrix.target }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + profile: minimal + - run: cargo test + - run: cargo test --all-features diff --git a/.github/workflows/cfb-mode.yaml b/.github/workflows/cfb-mode.yaml new file mode 100644 index 0000000..41880b5 --- /dev/null +++ b/.github/workflows/cfb-mode.yaml @@ -0,0 +1,55 @@ +name: cfb-mode + +on: + pull_request: + paths: + - "cfb-mode/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: cfb-mode + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + profile: minimal + - run: cargo build --no-default-features --release --target ${{ matrix.target }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + profile: minimal + - run: cargo test + - run: cargo test --all-features diff --git a/.github/workflows/cfb8.yaml b/.github/workflows/cfb8.yaml new file mode 100644 index 0000000..53ea01e --- /dev/null +++ b/.github/workflows/cfb8.yaml @@ -0,0 +1,55 @@ +name: cfb8 + +on: + pull_request: + paths: + - "cfb8/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: cfb8 + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + profile: minimal + - run: cargo build --no-default-features --release --target ${{ matrix.target }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + profile: minimal + - run: cargo test + - run: cargo test --all-features diff --git a/.github/workflows/ctr.yaml b/.github/workflows/ctr.yaml new file mode 100644 index 0000000..4baac54 --- /dev/null +++ b/.github/workflows/ctr.yaml @@ -0,0 +1,55 @@ +name: ctr + +on: + pull_request: + paths: + - "ctr/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: ctr + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + profile: minimal + - run: cargo build --no-default-features --release --target ${{ matrix.target }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + profile: minimal + - run: cargo test + - run: cargo test --all-features diff --git a/.github/workflows/ige.yaml b/.github/workflows/ige.yaml new file mode 100644 index 0000000..6792c36 --- /dev/null +++ b/.github/workflows/ige.yaml @@ -0,0 +1,55 @@ +name: ige + +on: + pull_request: + paths: + - "ige/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: ige + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + profile: minimal + - run: cargo build --no-default-features --release --target ${{ matrix.target }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + profile: minimal + - run: cargo test + - run: cargo test --all-features diff --git a/.github/workflows/ofb.yaml b/.github/workflows/ofb.yaml new file mode 100644 index 0000000..a2b3e0e --- /dev/null +++ b/.github/workflows/ofb.yaml @@ -0,0 +1,55 @@ +name: ofb + +on: + pull_request: + paths: + - "ofb/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: ofb + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + profile: minimal + - run: cargo build --no-default-features --release --target ${{ matrix.target }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + profile: minimal + - run: cargo test + - run: cargo test --all-features diff --git a/.github/workflows/pcbc.yaml b/.github/workflows/pcbc.yaml new file mode 100644 index 0000000..7f26013 --- /dev/null +++ b/.github/workflows/pcbc.yaml @@ -0,0 +1,55 @@ +name: pcbc + +on: + pull_request: + paths: + - "pcbc/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: pcbc + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + profile: minimal + - run: cargo build --no-default-features --release --target ${{ matrix.target }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + profile: minimal + - run: cargo test + - run: cargo test --all-features diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml new file mode 100644 index 0000000..0d82d54 --- /dev/null +++ b/.github/workflows/security-audit.yml @@ -0,0 +1,24 @@ +name: Security Audit +on: + pull_request: + paths: Cargo.lock + push: + branches: master + paths: Cargo.lock + schedule: + - cron: "0 0 * * *" + +jobs: + security_audit: + name: Security Audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Cache cargo bin + uses: actions/cache@v1 + with: + path: ~/.cargo/bin + key: ${{ runner.os }}-cargo-audit-v0.12.0 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/workspace.yml b/.github/workflows/workspace.yml new file mode 100644 index 0000000..599f211 --- /dev/null +++ b/.github/workflows/workspace.yml @@ -0,0 +1,53 @@ +name: Workspace + +on: + pull_request: + paths-ignore: + - README.md + push: + branches: master + paths-ignore: + - README.md + +jobs: + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.56.0 # MSRV + components: clippy + override: true + profile: minimal + - run: cargo clippy --all --all-features -- -D warnings + + rustfmt: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v1 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: rustfmt + override: true + profile: minimal + - name: Run cargo fmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + benches: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + profile: minimal + override: true + - run: cargo build --all --benches diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..53ecea4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,198 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aes" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f6c3373fb58bb23c6ed0f191f915f0e9459c6929fc430c0d74b8237c521953a" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "blobby" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "847495c209977a90e8aad588b959d0ca9f5dc228096d29a6bd3defd53f35eaec" + +[[package]] +name = "block-padding" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5808df4b2412175c4db3afb115c83d8d0cd26ca4f30a042026cddef8580e526a" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cbc" +version = "0.1.0" +dependencies = [ + "aes", + "cipher", + "hex-literal", +] + +[[package]] +name = "cfb-mode" +version = "0.8.0" +dependencies = [ + "aes", + "cipher", + "hex-literal", +] + +[[package]] +name = "cfb8" +version = "0.8.0" +dependencies = [ + "aes", + "cipher", + "hex-literal", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f3e8c9be82c31c331bc9db0fd70a1068f8a288d980b2414dcaa25ab17ac1e0" +dependencies = [ + "blobby", + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ctr" +version = "0.9.0" +dependencies = [ + "aes", + "cipher", + "hex-literal", + "kuznyechik", + "magma", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "ige" +version = "0.1.0" +dependencies = [ + "aes", + "cipher", + "hex-literal", +] + +[[package]] +name = "inout" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1d8734d7f28aaff861d726dc3bc8003e2987d2fc26add21f5dab0c35d5c348a" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "kuznyechik" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b8a760d5bebee051e542981fd2a562a6e8265c3262bfec1fd0ee3a0ea37f65" +dependencies = [ + "cipher", +] + +[[package]] +name = "libc" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" + +[[package]] +name = "magma" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6a003780e63ac83d9e21e3ee2ec69e9ca860b220ea97476bde316ad1a5bb071" +dependencies = [ + "cipher", +] + +[[package]] +name = "ofb" +version = "0.6.0" +dependencies = [ + "aes", + "cipher", + "hex-literal", +] + +[[package]] +name = "pcbc" +version = "0.1.0" +dependencies = [ + "aes", + "cipher", + "hex-literal", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c88870063c39ee00ec285a2f8d6a966e5b6fb2becc4e8dac77ed0d370ed6006" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e82a512 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[workspace] +members = [ + "cbc", + "cfb8", + "cfb-mode", + "ctr", + "ige", + "ofb", + "pcbc", +] diff --git a/README.md b/README.md new file mode 100644 index 0000000..5a3e49d --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# RustCrypto: block modes + +[![Project Chat][chat-image]][chat-link] +[![dependency status][deps-image]][deps-link] +![Apache2/MIT licensed][license-image] +[![HAZMAT][hazmat-image]][hazmat-link] + +Collection of [block modes] written in pure Rust generic over +block ciphers. + +## ⚠️ Security Warning: [Hazmat!][hazmat-link] + +Crates in this repository do not ensure ciphertexts are authentic +(i.e. by using a MAC to verify ciphertext integrity), which can lead to +serious vulnerabilities if used incorrectly! + +**USE AT YOUR OWN RISK!** + +## Supported algorithms + +| Name | Crate name | crates.io | Docs | MSRV | +|------|------------|-----------|--------|------| +| [Cipher Block Chaining][CBC] | [`cbc`] | [![crates.io](https://img.shields.io/crates/v/cbc.svg)](https://crates.io/crates/cbc) | [![Documentation](https://docs.rs/cbc/badge.svg)](https://docs.rs/cbc) | ![MSRV 1.56][msrv-1.56] | +| [8-bit Cipher Feedback][CFB-8] | [`cfb8`] | [![crates.io](https://img.shields.io/crates/v/cfb8.svg)](https://crates.io/crates/cfb8) | [![Documentation](https://docs.rs/cfb8/badge.svg)](https://docs.rs/cfb8) | ![MSRV 1.56][msrv-1.56] | +| [Full-block Cipher Feedback][CFB] | [`cfb-mode`] | [![crates.io](https://img.shields.io/crates/v/cfb-mode.svg)](https://crates.io/crates/cfb-mode) | [![Documentation](https://docs.rs/cfb-mode/badge.svg)](https://docs.rs/cfb-mode) | ![MSRV 1.56][msrv-1.56] | +| [Counter][CTR] | [`ctr`] | [![crates.io](https://img.shields.io/crates/v/ctr.svg)](https://crates.io/crates/ctr) | [![Documentation](https://docs.rs/ctr/badge.svg)](https://docs.rs/ctr) | ![MSRV 1.56][msrv-1.56] | +| [GOST R 34.13-2015] | [`gost-modes`] | [![crates.io](https://img.shields.io/crates/v/gost-modes.svg)](https://crates.io/crates/gost-modes) | [![Documentation](https://docs.rs/gost-modes/badge.svg)](https://docs.rs/gost-modes) | ![MSRV 1.56][msrv-1.56] | +| [Infinite Garble Extension][IGE] | [`ige`] | [![crates.io](https://img.shields.io/crates/v/ige.svg)](https://crates.io/crates/ige) | [![Documentation](https://docs.rs/ige/badge.svg)](https://docs.rs/ige) | ![MSRV 1.56][msrv-1.56] | +| [Output Feedback][OFB] | [`ofb`] | [![crates.io](https://img.shields.io/crates/v/ofb.svg)](https://crates.io/crates/ofb) | [![Documentation](https://docs.rs/ofb/badge.svg)](https://docs.rs/ofb) | ![MSRV 1.56][msrv-1.56] | +| [Propagating Cipher Block Chaining][PCBC] | [`pcbc`] | [![crates.io](https://img.shields.io/crates/v/pcbc.svg)](https://crates.io/crates/pcbc) | [![Documentation](https://docs.rs/pcbc/badge.svg)](https://docs.rs/pcbc) | ![MSRV 1.56][msrv-1.56] | + +### Minimum Supported Rust Version (MSRV) Policy + +MSRV bumps are considered breaking changes and will be performed only with minor version bump. + +## License + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/308460-block-modes +[deps-image]: https://deps.rs/repo/github/RustCrypto/block-modes/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/block-modes +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[hazmat-image]: https://img.shields.io/badge/crypto-hazmat%E2%9A%A0-red.svg +[hazmat-link]: https://github.com/RustCrypto/meta/blob/master/HAZMAT.md +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg + +[//]: # (crates) + +[`cbc`]: ./cbc +[`cfb8`]: ./cfb8 +[`cfb-mode`]: ./cfb-mode +[`ctr`]: ./ctr +[`gost-modes`]: ./gost-modes +[`ige`]: ./ige +[`ofb`]: ./ofb +[`pcbc`]: ./pcbc + +[//]: # (links) + +[block modes]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation +[CBC]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_block_chaining_(CBC) +[CFB-8]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CFB-1,_CFB-8,_CFB-64,_CFB-128,_etc. +[CFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Full-block_CFB +[CTR]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR) +[GOST R 34.13-2015]: https://tc26.ru/standard/gost/GOST_R_3413-2015.pdf +[IGE]: https://www.links.org/files/openssl-ige.pdf +[OFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB) +[PCBC]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Propagating_cipher_block_chaining_(PCBC) diff --git a/cbc/CHANGELOG.md b/cbc/CHANGELOG.md new file mode 100644 index 0000000..b6059ab --- /dev/null +++ b/cbc/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 (2022-02-10) +- Initial release ([#2]) + +[#2]: https://github.com/RustCrypto/block-modes/pull/2 diff --git a/cbc/Cargo.toml b/cbc/Cargo.toml new file mode 100644 index 0000000..541af0e --- /dev/null +++ b/cbc/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "cbc" +version = "0.1.0" # Also update html_root_url in lib.rs when bumping this +description = "Cipher Block Chaining (CBC) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.56" +readme = "README.md" +documentation = "https://docs.rs/cbc" +repository = "https://github.com/RustCrypto/block-modes" +keywords = ["crypto", "block-mode", "ciphers"] +categories = ["cryptography", "no-std"] + +[dependencies] +cipher = "0.4" + +[dev-dependencies] +aes = "0.8" +cipher = { version = "0.4", features = ["dev"] } +hex-literal = "0.3.3" + +[features] +default = ["block-padding"] +block-padding = ["cipher/block-padding"] +zeroize = ["cipher/zeroize"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/cbc/LICENSE-APACHE b/cbc/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/cbc/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cbc/LICENSE-MIT b/cbc/LICENSE-MIT new file mode 100644 index 0000000..d19d409 --- /dev/null +++ b/cbc/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2018-2022 RustCrypto Developers +Copyright (c) 2018 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/cbc/README.md b/cbc/README.md new file mode 100644 index 0000000..f99efea --- /dev/null +++ b/cbc/README.md @@ -0,0 +1,60 @@ +# RustCrypto: CBC + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Cipher Block Chaining][CBC] (CBC) block cipher +mode of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.56** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/cbc.svg +[crate-link]: https://crates.io/crates/cbc +[docs-image]: https://docs.rs/cbc/badge.svg +[docs-link]: https://docs.rs/cbc/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/308460-block-modes +[build-image]: https://github.com/RustCrypto/block-modes/workflows/cbc/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-modes/actions?query=workflow%3Acbc+branch%3Amaster + +[//]: # (general links) + +[CBC]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC +[cipher-doc]: https://docs.rs/cipher/ diff --git a/cbc/benches/aes128.rs b/cbc/benches/aes128.rs new file mode 100644 index 0000000..b478146 --- /dev/null +++ b/cbc/benches/aes128.rs @@ -0,0 +1,16 @@ +#![feature(test)] +extern crate test; + +use aes::Aes128; + +cipher::block_encryptor_bench!( + KeyIv: cbc::Encryptor, + cbc_aes128_encrypt_block, + cbc_aes128_encrypt_blocks, +); + +cipher::block_decryptor_bench!( + KeyIv: cbc::Decryptor, + cbc_aes128_decrypt_block, + cbc_aes128_decrypt_blocks, +); diff --git a/cbc/src/decrypt.rs b/cbc/src/decrypt.rs new file mode 100644 index 0000000..b3fe6a5 --- /dev/null +++ b/cbc/src/decrypt.rs @@ -0,0 +1,191 @@ +use crate::xor; +use cipher::{ + crypto_common::{InnerUser, IvSizeUser}, + generic_array::{ArrayLength, GenericArray}, + inout::InOut, + AlgorithmName, Block, BlockBackend, BlockCipher, BlockClosure, BlockDecryptMut, BlockSizeUser, + InnerIvInit, Iv, IvState, ParBlocks, ParBlocksSizeUser, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// CBC mode decryptor. +#[derive(Clone)] +pub struct Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + cipher: C, + iv: Block, +} + +impl BlockSizeUser for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + type BlockSize = C::BlockSize; +} + +impl BlockDecryptMut for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, iv } = self; + cipher.decrypt_with_backend_mut(Closure { iv, f }) + } +} + +impl InnerUser for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + type Inner = C; +} + +impl IvSizeUser for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.iv.clone() + } +} + +impl AlgorithmName for Decryptor +where + C: BlockDecryptMut + BlockCipher + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cbc::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Decryptor +where + C: BlockDecryptMut + BlockCipher + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cbc::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for Decryptor { + fn drop(&mut self) { + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for Decryptor {} + +struct Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + iv: &'a mut GenericArray, + f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + iv: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = BS; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = BK::ParBlocksSize; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let enc_block = block.clone_in(); + self.backend.proc_block(block.reborrow()); + xor(block.get_out(), self.iv); + *self.iv = enc_block; + } + + #[inline(always)] + fn proc_par_blocks(&mut self, mut blocks: InOut<'_, '_, ParBlocks>) { + let t = blocks.clone_in(); + self.backend.proc_par_blocks(blocks.reborrow()); + let out = blocks.get_out(); + let n = out.len(); + xor(&mut out[0], self.iv); + for i in 1..n { + xor(&mut out[i], &t[i - 1]) + } + *self.iv = t[n - 1].clone(); + } +} diff --git a/cbc/src/encrypt.rs b/cbc/src/encrypt.rs new file mode 100644 index 0000000..cb19070 --- /dev/null +++ b/cbc/src/encrypt.rs @@ -0,0 +1,179 @@ +use crate::xor; +use cipher::{ + consts::U1, + crypto_common::{InnerUser, IvSizeUser}, + generic_array::{ArrayLength, GenericArray}, + inout::InOut, + AlgorithmName, Block, BlockBackend, BlockCipher, BlockClosure, BlockEncryptMut, BlockSizeUser, + InnerIvInit, Iv, IvState, ParBlocksSizeUser, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// CBC mode encryptor. +#[derive(Clone)] +pub struct Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + cipher: C, + iv: Block, +} + +impl BlockSizeUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type BlockSize = C::BlockSize; +} + +impl BlockEncryptMut for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, iv } = self; + cipher.encrypt_with_backend_mut(Closure { iv, f }) + } +} + +impl InnerUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type Inner = C; +} + +impl IvSizeUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.iv.clone() + } +} + +impl AlgorithmName for Encryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cbc::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Encryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cbc::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for Encryptor { + fn drop(&mut self) { + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for Encryptor {} + +struct Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + iv: &'a mut GenericArray, + f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + iv: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = BS; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = U1; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let mut t = block.clone_in(); + xor(&mut t, self.iv); + self.backend.proc_block((&t, block.get_out()).into()); + *self.iv = block.get_out().clone(); + } +} diff --git a/cbc/src/lib.rs b/cbc/src/lib.rs new file mode 100644 index 0000000..4929ad0 --- /dev/null +++ b/cbc/src/lib.rs @@ -0,0 +1,85 @@ +//! [Cipher Block Chaining][1] (CBC) mode. +//! +//! +//! +//! +//! Mode functionality is accessed using traits from re-exported [`cipher`] crate. +//! +//! # ⚠️ Security Warning: Hazmat! +//! +//! This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity +//! is not verified, which can lead to serious vulnerabilities! +//! +//! # Example +//! ``` +//! use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; +//! use hex_literal::hex; +//! +//! type Aes128CbcEnc = cbc::Encryptor; +//! type Aes128CbcDec = cbc::Decryptor; +//! +//! let key = [0x42; 16]; +//! let iv = [0x24; 16]; +//! let plaintext = b"hello world! this is my plaintext."; +//! let ciphertext = hex!( +//! "c7fe247ef97b21f07cbdd26cb5d346bf" +//! "d27867cb00d9486723e159978fb9a5f9" +//! "14cfb228a710de4171e396e7b6cf859e" +//! ); +//! +//! // encrypt/decrypt in-place +//! // buffer must be big enough for padded plaintext +//! let mut buf = vec![0u8; 48]; +//! let pt_len = plaintext.len(); +//! buf[..pt_len].copy_from_slice(&plaintext[..]); +//! let ct = Aes128CbcEnc::new(&key.into(), &iv.into()) +//! .encrypt_padded_mut::(&mut buf, pt_len) +//! .unwrap(); +//! assert_eq!(ct, &ciphertext[..]); +//! +//! let pt = Aes128CbcDec::new(&key.into(), &iv.into()) +//! .decrypt_padded_mut::(&mut buf) +//! .unwrap(); +//! assert_eq!(pt, &plaintext[..]); +//! +//! // encrypt/decrypt from buffer to buffer +//! let mut buf = vec![0u8; 48]; +//! let ct = Aes128CbcEnc::new(&key.into(), &iv.into()) +//! .encrypt_padded_b2b_mut::(&plaintext[..], &mut buf) +//! .unwrap(); +//! assert_eq!(ct, &ciphertext[..]); +//! +//! let mut buf = vec![0u8; 48]; +//! let pt = Aes128CbcDec::new(&key.into(), &iv.into()) +//! .decrypt_padded_b2b_mut::(&ct, &mut buf) +//! .unwrap(); +//! assert_eq!(pt, &plaintext[..]); +//! ``` +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_root_url = "https://docs.rs/cbc/0.1.0" +)] +#![forbid(unsafe_code)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs, rust_2018_idioms)] + +mod decrypt; +mod encrypt; + +pub use cipher; +pub use decrypt::Decryptor; +pub use encrypt::Encryptor; + +use cipher::generic_array::{ArrayLength, GenericArray}; + +#[inline(always)] +fn xor>(out: &mut GenericArray, buf: &GenericArray) { + for (a, b) in out.iter_mut().zip(buf) { + *a ^= *b; + } +} diff --git a/cbc/tests/aes.rs b/cbc/tests/aes.rs new file mode 100644 index 0000000..5513bf1 --- /dev/null +++ b/cbc/tests/aes.rs @@ -0,0 +1,25 @@ +use aes::*; +use cbc::{Decryptor, Encryptor}; +use cipher::{block_mode_dec_test, block_mode_enc_test, iv_state_test}; + +iv_state_test!(aes128_cbc_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes128_cbc_dec_iv_state, Decryptor, decrypt); +iv_state_test!(aes192_cbc_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes192_cbc_dec_iv_state, Decryptor, decrypt); +iv_state_test!(aes256_cbc_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes256_cbc_dec_iv_state, Decryptor, decrypt); + +// Test vectors from CVAP "AES Multiblock Message Test (MMT) Sample Vectors": +// +block_mode_enc_test!(aes128_cbc_enc_test, "aes128", Encryptor); +block_mode_dec_test!(aes128_cbc_dec_test, "aes128", Decryptor); +block_mode_enc_test!(aes128enc_cbc_enc_test, "aes128", Encryptor); +block_mode_dec_test!(aes128dec_cbc_dec_test, "aes128", Decryptor); +block_mode_enc_test!(aes192_cbc_enc_test, "aes192", Encryptor); +block_mode_dec_test!(aes192_cbc_dec_test, "aes192", Decryptor); +block_mode_enc_test!(aes192enc_cbc_enc_test, "aes192", Encryptor); +block_mode_dec_test!(aes192dec_cbc_dec_test, "aes192", Decryptor); +block_mode_enc_test!(aes256_cbc_enc_test, "aes256", Encryptor); +block_mode_dec_test!(aes256_cbc_dec_test, "aes256", Decryptor); +block_mode_enc_test!(aes256enc_cbc_enc_test, "aes256", Encryptor); +block_mode_dec_test!(aes256dec_cbc_dec_test, "aes256", Decryptor); diff --git a/cbc/tests/data/aes128.blb b/cbc/tests/data/aes128.blb new file mode 100644 index 0000000..b74b505 Binary files /dev/null and b/cbc/tests/data/aes128.blb differ diff --git a/cbc/tests/data/aes192.blb b/cbc/tests/data/aes192.blb new file mode 100644 index 0000000..f2fccb1 Binary files /dev/null and b/cbc/tests/data/aes192.blb differ diff --git a/cbc/tests/data/aes256.blb b/cbc/tests/data/aes256.blb new file mode 100644 index 0000000..c4a7002 Binary files /dev/null and b/cbc/tests/data/aes256.blb differ diff --git a/cfb-mode/CHANGELOG.md b/cfb-mode/CHANGELOG.md new file mode 100644 index 0000000..2d49421 --- /dev/null +++ b/cfb-mode/CHANGELOG.md @@ -0,0 +1,58 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.8.0 (2022-02-10) +### Changed +- Update `cipher` dependency to v0.4 and move crate +to the [RustCrypto/block-modes] repository ([#2]) + +[#2]: https://github.com/RustCrypto/block-modes/pull/2 +[RustCrypto/block-modes]: https://github.com/RustCrypto/block-modes + +## 0.7.1 (2021-04-30) +### Changed +- Removed redundant `NewBlockCipher` bound from `FromBlockCipher` implementation ([#236]) + +[#236]: https://github.com/RustCrypto/stream-ciphers/pull/236 + +## 0.7.0 (2021-04-29) +### Changed +- Bump `cipher` dependency to v0.3 release ([#226]) +- Bump `aes` dev dependency to v0.7 release ([#232]) + +[#226]: https://github.com/RustCrypto/stream-ciphers/pull/226 +[#232]: https://github.com/RustCrypto/stream-ciphers/pull/232 + +## 0.6.0 (2020-10-16) +### Changed +- Replace `block-cipher`/`stream-cipher` with `cipher` crate ([#177]) + +[#177]: https://github.com/RustCrypto/stream-ciphers/pull/177 + +## 0.5.0 (2020-08-25) +### Changed +- Bump `stream-cipher` dependency to v0.7, implement the `FromBlockCipher` trait ([#161], [#164]) + +[#161]: https://github.com/RustCrypto/stream-ciphers/pull/161 +[#164]: https://github.com/RustCrypto/stream-ciphers/pull/164 + +## 0.4.0 (2020-06-08) +### Changed +- Bump `stream-cipher` dependency to v0.4 ([#119]) +- Upgrade to Rust 2018 edition ([#119]) + +[#119]: https://github.com/RustCrypto/stream-ciphers/pull/119 + +## 0.3.2 (2019-03-11) + +## 0.3.1 (2018-11-01) + +## 0.3.0 (2018-11-01) + +## 0.2.0 (2018-10-13) + +## 0.1.0 (2018-10-01) diff --git a/cfb-mode/Cargo.toml b/cfb-mode/Cargo.toml new file mode 100644 index 0000000..90f773a --- /dev/null +++ b/cfb-mode/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "cfb-mode" +version = "0.8.0" # Also update html_root_url in lib.rs when bumping this +description = "Cipher Feedback (CFB) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.56" +readme = "README.md" +documentation = "https://docs.rs/cfb-mode" +repository = "https://github.com/RustCrypto/block-modes" +keywords = ["crypto", "block-mode", "stream-cipher", "ciphers"] +categories = ["cryptography", "no-std"] + +[dependencies] +cipher = "0.4" + +[dev-dependencies] +aes = "0.8" +cipher = { version = "0.4", features = ["dev"] } +hex-literal = "0.3" + +[features] +block-padding = ["cipher/block-padding"] +zeroize = ["cipher/zeroize"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/cfb-mode/LICENSE-APACHE b/cfb-mode/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/cfb-mode/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cfb-mode/LICENSE-MIT b/cfb-mode/LICENSE-MIT new file mode 100644 index 0000000..d19d409 --- /dev/null +++ b/cfb-mode/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2018-2022 RustCrypto Developers +Copyright (c) 2018 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/cfb-mode/README.md b/cfb-mode/README.md new file mode 100644 index 0000000..4015f24 --- /dev/null +++ b/cfb-mode/README.md @@ -0,0 +1,60 @@ +# RustCrypto: CFB + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Cipher Feedback][CFB] (CFB) block cipher mode +of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.56** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/cfb-mode.svg +[crate-link]: https://crates.io/crates/cfb-mode +[docs-image]: https://docs.rs/cfb-mode/badge.svg +[docs-link]: https://docs.rs/cfb-mode/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/308460-block-modes +[build-image]: https://github.com/RustCrypto/block-modes/workflows/cfb-mode/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-modes/actions?query=workflow%3Acfb-mode+branch%3Amaster + +[//]: # (general links) + +[CFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB) +[cipher-doc]: https://docs.rs/cipher/ diff --git a/cfb-mode/benches/aes128.rs b/cfb-mode/benches/aes128.rs new file mode 100644 index 0000000..1d5c9df --- /dev/null +++ b/cfb-mode/benches/aes128.rs @@ -0,0 +1,16 @@ +#![feature(test)] +extern crate test; + +use aes::Aes128; + +cipher::block_encryptor_bench!( + KeyIv: cfb_mode::Encryptor, + cfb_aes128_encrypt_block, + cfb_aes128_encrypt_blocks, +); + +cipher::block_decryptor_bench!( + KeyIv: cfb_mode::Decryptor, + cfb_aes128_decrypt_block, + cfb_aes128_decrypt_blocks, +); diff --git a/cfb-mode/src/decrypt.rs b/cfb-mode/src/decrypt.rs new file mode 100644 index 0000000..b188a89 --- /dev/null +++ b/cfb-mode/src/decrypt.rs @@ -0,0 +1,195 @@ +use cipher::{ + crypto_common::{InnerUser, IvSizeUser}, + generic_array::{ArrayLength, GenericArray}, + inout::InOut, + AlgorithmName, AsyncStreamCipher, Block, BlockBackend, BlockCipher, BlockClosure, BlockDecrypt, + BlockDecryptMut, BlockEncryptMut, BlockSizeUser, InnerIvInit, Iv, IvState, ParBlocks, + ParBlocksSizeUser, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// CFB mode decryptor. +#[derive(Clone)] +pub struct Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + cipher: C, + iv: Block, +} + +impl BlockSizeUser for Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type BlockSize = C::BlockSize; +} + +impl BlockDecryptMut for Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, iv } = self; + cipher.encrypt_with_backend_mut(Closure { iv, f }) + } +} + +impl AsyncStreamCipher for Decryptor where C: BlockEncryptMut + BlockCipher {} + +impl InnerUser for Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type Inner = C; +} + +impl IvSizeUser for Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn inner_iv_init(mut cipher: C, iv: &Iv) -> Self { + let mut iv = iv.clone(); + cipher.encrypt_block_mut(&mut iv); + Self { cipher, iv } + } +} + +impl IvState for Decryptor +where + C: BlockEncryptMut + BlockDecrypt + BlockCipher, +{ + #[inline] + fn iv_state(&self) -> Iv { + let mut res = self.iv.clone(); + self.cipher.decrypt_block(&mut res); + res + } +} + +impl AlgorithmName for Decryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cfb::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Decryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cfb::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for Decryptor { + fn drop(&mut self) { + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for Decryptor {} + +struct Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + iv: &'a mut GenericArray, + f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + iv: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = BS; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = BK::ParBlocksSize; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let mut t = block.clone_in(); + block.xor_in2out(self.iv); + self.backend.proc_block((&mut t).into()); + *self.iv = t; + } + + #[inline(always)] + fn proc_par_blocks(&mut self, mut blocks: InOut<'_, '_, ParBlocks>) { + let mut t = ParBlocks::::default(); + let b = (blocks.get_in(), &mut t).into(); + self.backend.proc_par_blocks(b); + + let n = t.len(); + blocks.get(0).xor_in2out(self.iv); + for i in 1..n { + blocks.get(i).xor_in2out(&t[i - 1]) + } + *self.iv = t[n - 1].clone(); + } +} diff --git a/cfb-mode/src/encrypt.rs b/cfb-mode/src/encrypt.rs new file mode 100644 index 0000000..42a7fd6 --- /dev/null +++ b/cfb-mode/src/encrypt.rs @@ -0,0 +1,181 @@ +use cipher::{ + consts::U1, + crypto_common::{InnerUser, IvSizeUser}, + generic_array::{ArrayLength, GenericArray}, + inout::InOut, + AlgorithmName, AsyncStreamCipher, Block, BlockBackend, BlockCipher, BlockClosure, BlockDecrypt, + BlockEncryptMut, BlockSizeUser, InnerIvInit, Iv, IvState, ParBlocksSizeUser, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// CFB mode encryptor. +#[derive(Clone)] +pub struct Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + cipher: C, + iv: Block, +} + +impl BlockSizeUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type BlockSize = C::BlockSize; +} + +impl BlockEncryptMut for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, iv } = self; + cipher.encrypt_with_backend_mut(Closure { iv, f }) + } +} + +impl AsyncStreamCipher for Encryptor where C: BlockEncryptMut + BlockCipher {} + +impl InnerUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type Inner = C; +} + +impl IvSizeUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn inner_iv_init(mut cipher: C, iv: &Iv) -> Self { + let mut iv = iv.clone(); + cipher.encrypt_block_mut(&mut iv); + Self { cipher, iv } + } +} + +impl IvState for Encryptor +where + C: BlockEncryptMut + BlockDecrypt + BlockCipher, +{ + #[inline] + fn iv_state(&self) -> Iv { + let mut res = self.iv.clone(); + self.cipher.decrypt_block(&mut res); + res + } +} + +impl AlgorithmName for Encryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cfb::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Encryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cfb::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for Encryptor { + fn drop(&mut self) { + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for Encryptor {} + +struct Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + iv: &'a mut GenericArray, + f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + iv: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = BS; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = U1; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + block.xor_in2out(self.iv); + let mut t = block.get_out().clone(); + self.backend.proc_block((&mut t).into()); + *self.iv = t; + } +} diff --git a/cfb-mode/src/lib.rs b/cfb-mode/src/lib.rs new file mode 100644 index 0000000..de7cabc --- /dev/null +++ b/cfb-mode/src/lib.rs @@ -0,0 +1,68 @@ +//! [Cipher feedback][1] (CFB) mode with full block feedback. +//! +//! +//! +//! +//! Mode functionality is accessed using traits from re-exported [`cipher`] crate. +//! +//! # ⚠️ Security Warning: Hazmat! +//! +//! This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity +//! is not verified, which can lead to serious vulnerabilities! +//! +//! # Example +//! ``` +//! use aes::cipher::{AsyncStreamCipher, KeyIvInit}; +//! use hex_literal::hex; +//! +//! type Aes128CfbEnc = cfb_mode::Encryptor; +//! type Aes128CfbDec = cfb_mode::Decryptor; +//! +//! let key = [0x42; 16]; +//! let iv = [0x24; 16]; +//! let plaintext = b"hello world! this is my plaintext."; +//! let ciphertext = hex!( +//! "3357121ebb5a29468bd861467596ce3d6f99e251cc2d9f0a598032ae386d0ab995b3" +//! ); +//! +//! // encrypt/decrypt in-place +//! let mut buf = plaintext.to_vec(); +//! Aes128CfbEnc::new(&key.into(), &iv.into()).encrypt(&mut buf); +//! assert_eq!(buf, &ciphertext[..]); +//! +//! Aes128CfbDec::new(&key.into(), &iv.into()).decrypt(&mut buf); +//! assert_eq!(buf, &plaintext[..]); +//! +//! // encrypt/decrypt from buffer to buffer +//! // buffer length must be equal to input length +//! let mut buf1 = vec![0u8; 34]; +//! Aes128CfbEnc::new(&key.into(), &iv.into()) +//! .encrypt_b2b(&plaintext[..], &mut buf1) +//! .unwrap(); +//! assert_eq!(buf1, &ciphertext[..]); +//! +//! let mut buf2 = vec![0u8; 34]; +//! Aes128CfbDec::new(&key.into(), &iv.into()) +//! .decrypt_b2b(&buf1, &mut buf2) +//! .unwrap(); +//! assert_eq!(buf2, &plaintext[..]); +//! ``` +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB) + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_root_url = "https://docs.rs/cfb-mode/0.8.0" +)] +#![forbid(unsafe_code)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs, rust_2018_idioms)] + +mod decrypt; +mod encrypt; + +pub use cipher; +pub use decrypt::Decryptor; +pub use encrypt::Encryptor; diff --git a/cfb-mode/tests/aes.rs b/cfb-mode/tests/aes.rs new file mode 100644 index 0000000..2f8bf98 --- /dev/null +++ b/cfb-mode/tests/aes.rs @@ -0,0 +1,55 @@ +use aes::*; +use cfb_mode::{Decryptor, Encryptor}; +use cipher::{block_mode_dec_test, block_mode_enc_test, iv_state_test}; + +iv_state_test!(aes128_cfb_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes128_cfb_dec_iv_state, Decryptor, decrypt); +iv_state_test!(aes192_cfb_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes192_cfb_dec_iv_state, Decryptor, decrypt); +iv_state_test!(aes256_cfb_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes256_cfb_dec_iv_state, Decryptor, decrypt); + +// Test vectors from CVAP "AES Multiblock Message Test (MMT) Sample Vectors": +// +block_mode_enc_test!(aes128_cfb_enc_test, "aes128", Encryptor); +block_mode_dec_test!(aes128_cfb_dec_test, "aes128", Decryptor); +block_mode_enc_test!(aes128enc_cfb_enc_test, "aes128", Encryptor); +block_mode_dec_test!(aes128enc_cfb_dec_test, "aes128", Decryptor); +block_mode_enc_test!(aes192_cfb_enc_test, "aes192", Encryptor); +block_mode_dec_test!(aes192_cfb_dec_test, "aes192", Decryptor); +block_mode_enc_test!(aes192enc_cfb_enc_test, "aes192", Encryptor); +block_mode_dec_test!(aes192dec_cfb_dec_test, "aes192", Decryptor); +block_mode_enc_test!(aes256_cfb_enc_test, "aes256", Encryptor); +block_mode_dec_test!(aes256_cfb_dec_test, "aes256", Decryptor); +block_mode_enc_test!(aes256enc_cfb_enc_test, "aes256", Encryptor); +block_mode_dec_test!(aes256dec_cfb_dec_test, "aes256", Decryptor); + +/// Test methods from the `AsyncStreamCipher` trait. +#[test] +fn aes128_cfb_async_test() { + use cipher::{AsyncStreamCipher, KeyIvInit}; + + type Enc = Encryptor; + type Dec = Decryptor; + + let key = [42; 16]; + let iv = [24; 16]; + let mut pt = [0u8; 101]; + for (i, b) in pt.iter_mut().enumerate() { + *b = (i % 11) as u8; + } + let enc = Enc::new_from_slices(&key, &iv).unwrap(); + let mut ct = pt.clone(); + enc.encrypt(&mut ct); + for i in 1..100 { + let enc = Enc::new_from_slices(&key, &iv).unwrap(); + let mut t = pt.clone(); + let t = &mut t[..i]; + enc.encrypt(t); + assert_eq!(t, &ct[..i]); + + let dec = Dec::new_from_slices(&key, &iv).unwrap(); + dec.decrypt(t); + assert_eq!(t, &pt[..i]); + } +} diff --git a/cfb-mode/tests/data/aes128.blb b/cfb-mode/tests/data/aes128.blb new file mode 100644 index 0000000..6e2c0c1 Binary files /dev/null and b/cfb-mode/tests/data/aes128.blb differ diff --git a/cfb-mode/tests/data/aes192.blb b/cfb-mode/tests/data/aes192.blb new file mode 100644 index 0000000..d3267a1 Binary files /dev/null and b/cfb-mode/tests/data/aes192.blb differ diff --git a/cfb-mode/tests/data/aes256.blb b/cfb-mode/tests/data/aes256.blb new file mode 100644 index 0000000..8479e7f Binary files /dev/null and b/cfb-mode/tests/data/aes256.blb differ diff --git a/cfb8/CHANGELOG.md b/cfb8/CHANGELOG.md new file mode 100644 index 0000000..6b2e363 --- /dev/null +++ b/cfb8/CHANGELOG.md @@ -0,0 +1,56 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.8.0 (2022-02-10) +### Changed +- Update `cipher` dependency to v0.4 and move crate +to the [RustCrypto/block-modes] repository ([#2]) + +[#2]: https://github.com/RustCrypto/block-modes/pull/2 +[RustCrypto/block-modes]: https://github.com/RustCrypto/block-modes + +## 0.7.1 (2021-04-30) +### Changed +- Removed redundant `NewBlockCipher` boundfrom `FromBlockCipher` implementation ([#236]) + +[#236]: https://github.com/RustCrypto/stream-ciphers/pull/236 + +## 0.7.0 (2021-04-29) +### Changed +- Bump `cipher` dependency to v0.3 release ([#226]) +- Bump `aes` dev dependency to v0.7 release ([#232]) + +[#226]: https://github.com/RustCrypto/stream-ciphers/pull/226 +[#232]: https://github.com/RustCrypto/stream-ciphers/pull/232 + +## 0.6.0 (2020-10-16) +### Changed +- Replace `block-cipher`/`stream-cipher` with `cipher` crate ([#177]) + +[#177]: https://github.com/RustCrypto/stream-ciphers/pull/177 + +## 0.5.0 (2020-08-25) +### Changed +- Bump `stream-cipher` dependency to v0.7, implement the `FromBlockCipher` trait ([#161], [#164]) + +[#161]: https://github.com/RustCrypto/stream-ciphers/pull/161 +[#164]: https://github.com/RustCrypto/stream-ciphers/pull/164 + +## 0.4.0 (2020-06-08) +### Changed +- Bump `stream-cipher` dependency to v0.4 ([#120]) +- Upgrade to Rust 2018 edition ([#120]) + +[#120]: https://github.com/RustCrypto/stream-ciphers/pull/120 + +## 0.3.2 (2019-03-11) + +## 0.3.1 (2018-11-01) + +## 0.3.0 (2018-11-01) + +## 0.1.0 (2018-11-01) diff --git a/cfb8/Cargo.toml b/cfb8/Cargo.toml new file mode 100644 index 0000000..ea16b02 --- /dev/null +++ b/cfb8/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "cfb8" +version = "0.8.0" # Also update html_root_url in lib.rs when bumping this +description = "Cipher Feedback with eight bit feedback (CFB-8) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.56" +readme = "README.md" +documentation = "https://docs.rs/cfb8" +repository = "https://github.com/RustCrypto/block-modes" +keywords = ["crypto", "block-mode", "stream-cipher", "ciphers"] +categories = ["cryptography", "no-std"] + +[dependencies] +cipher = "0.4" + +[dev-dependencies] +aes = "0.8" +cipher = { version = "0.4", features = ["dev"] } +hex-literal = "0.3" + +[features] +block-padding = ["cipher/block-padding"] +zeroize = ["cipher/zeroize"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/cfb8/LICENSE-APACHE b/cfb8/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/cfb8/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cfb8/LICENSE-MIT b/cfb8/LICENSE-MIT new file mode 100644 index 0000000..d19d409 --- /dev/null +++ b/cfb8/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2018-2022 RustCrypto Developers +Copyright (c) 2018 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/cfb8/README.md b/cfb8/README.md new file mode 100644 index 0000000..612de72 --- /dev/null +++ b/cfb8/README.md @@ -0,0 +1,60 @@ +# RustCrypto: CFB-8 + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Cipher Feedback][CFB-8] with eight bit +feedback (CFB-8) block cipher mode of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.56** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/cfb.svg +[crate-link]: https://crates.io/crates/cfb +[docs-image]: https://docs.rs/cfb/badge.svg +[docs-link]: https://docs.rs/cfb/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/308460-block-modes +[build-image]: https://github.com/RustCrypto/block-modes/workflows/cfb/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-modes/actions?query=workflow%3Acfb+branch%3Amaster + +[//]: # (general links) + +[CFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CFB-1,_CFB-8,_CFB-64,_CFB-128,_etc. +[cipher-doc]: https://docs.rs/cipher/ diff --git a/cfb8/benches/aes128.rs b/cfb8/benches/aes128.rs new file mode 100644 index 0000000..aa6dd6b --- /dev/null +++ b/cfb8/benches/aes128.rs @@ -0,0 +1,16 @@ +#![feature(test)] +extern crate test; + +use aes::Aes128; + +cipher::block_encryptor_bench!( + KeyIv: cfb8::Encryptor, + cfb8_aes128_encrypt_block, + cfb8_aes128_encrypt_blocks, +); + +cipher::block_decryptor_bench!( + KeyIv: cfb8::Decryptor, + cfb8_aes128_decrypt_block, + cfb8_aes128_decrypt_blocks, +); diff --git a/cfb8/src/decrypt.rs b/cfb8/src/decrypt.rs new file mode 100644 index 0000000..31362fc --- /dev/null +++ b/cfb8/src/decrypt.rs @@ -0,0 +1,183 @@ +use cipher::{ + consts::U1, + crypto_common::{InnerUser, IvSizeUser}, + generic_array::{ArrayLength, GenericArray}, + inout::InOut, + AlgorithmName, AsyncStreamCipher, Block, BlockBackend, BlockCipher, BlockClosure, + BlockDecryptMut, BlockEncryptMut, BlockSizeUser, InnerIvInit, Iv, IvState, ParBlocksSizeUser, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// CFB-8 mode decryptor. +#[derive(Clone)] +pub struct Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + cipher: C, + iv: Block, +} + +impl BlockSizeUser for Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type BlockSize = U1; +} + +impl BlockDecryptMut for Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, iv } = self; + cipher.encrypt_with_backend_mut(Closure { iv, f }) + } +} + +impl AsyncStreamCipher for Decryptor {} + +impl InnerUser for Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type Inner = C; +} + +impl IvSizeUser for Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for Decryptor +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + let iv = iv.clone(); + Self { cipher, iv } + } +} + +impl IvState for Decryptor +where + C: BlockEncryptMut + BlockDecryptMut + BlockCipher, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.iv.clone() + } +} + +impl AlgorithmName for Decryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cfb8::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Decryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cfb8::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for Decryptor { + fn drop(&mut self) { + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for Decryptor {} + +struct Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + iv: &'a mut GenericArray, + f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + iv: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = U1; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = U1; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let mut t = self.iv.clone(); + self.backend.proc_block((&mut t).into()); + let r = block.get(0).clone_in(); + block.xor_in2out(GenericArray::from_slice(&t[..1])); + let n = self.iv.len(); + for i in 0..n - 1 { + self.iv[i] = self.iv[i + 1]; + } + self.iv[n - 1] = r; + } +} diff --git a/cfb8/src/encrypt.rs b/cfb8/src/encrypt.rs new file mode 100644 index 0000000..54c4c2a --- /dev/null +++ b/cfb8/src/encrypt.rs @@ -0,0 +1,183 @@ +use cipher::{ + consts::U1, + crypto_common::{InnerUser, IvSizeUser}, + generic_array::{ArrayLength, GenericArray}, + inout::InOut, + AlgorithmName, AsyncStreamCipher, Block, BlockBackend, BlockCipher, BlockClosure, + BlockEncryptMut, BlockSizeUser, InnerIvInit, Iv, IvState, ParBlocksSizeUser, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// CFB-8 mode encryptor. +#[derive(Clone)] +pub struct Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + cipher: C, + iv: Block, +} + +impl BlockSizeUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type BlockSize = U1; +} + +impl BlockEncryptMut for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, iv } = self; + cipher.encrypt_with_backend_mut(Closure { iv, f }) + } +} + +impl AsyncStreamCipher for Encryptor {} + +impl InnerUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type Inner = C; +} + +impl IvSizeUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + let iv = iv.clone(); + Self { cipher, iv } + } +} + +impl IvState for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.iv.clone() + } +} + +impl AlgorithmName for Encryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cfb8::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Encryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cfb8::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for Encryptor { + fn drop(&mut self) { + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for Encryptor {} + +struct Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + iv: &'a mut GenericArray, + f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + iv: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = U1; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = U1; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let mut t = self.iv.clone(); + self.backend.proc_block((&mut t).into()); + block.xor_in2out(GenericArray::from_slice(&t[..1])); + let r = block.get_out()[0]; + let n = self.iv.len(); + for i in 0..n - 1 { + self.iv[i] = self.iv[i + 1]; + } + self.iv[n - 1] = r; + } +} diff --git a/cfb8/src/lib.rs b/cfb8/src/lib.rs new file mode 100644 index 0000000..5bc6726 --- /dev/null +++ b/cfb8/src/lib.rs @@ -0,0 +1,68 @@ +//! [Cipher Feedback with eight bit feedback][1] (CFB-8) mode. +//! +//! +//! +//! +//! Mode functionality is accessed using traits from re-exported [`cipher`] crate. +//! +//! # ⚠️ Security Warning: Hazmat! +//! +//! This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity +//! is not verified, which can lead to serious vulnerabilities! +//! +//! # Example +//! ``` +//! use aes::cipher::{AsyncStreamCipher, KeyIvInit}; +//! use hex_literal::hex; +//! +//! type Aes128Cfb8Enc = cfb8::Encryptor; +//! type Aes128Cfb8Dec = cfb8::Decryptor; +//! +//! let key = [0x42; 16]; +//! let iv = [0x24; 16]; +//! let plaintext = b"hello world! this is my plaintext."; +//! let ciphertext = hex!( +//! "33b356ce9184290c4c8facc1c0b1f918d5475aeb75b88c161ca65bdf05c7137ff4b0" +//! ); +//! +//! // encrypt/decrypt in-place +//! let mut buf = plaintext.to_vec(); +//! Aes128Cfb8Enc::new(&key.into(), &iv.into()).encrypt(&mut buf); +//! assert_eq!(buf, &ciphertext[..]); +//! +//! Aes128Cfb8Dec::new(&key.into(), &iv.into()).decrypt(&mut buf); +//! assert_eq!(buf, &plaintext[..]); +//! +//! // encrypt/decrypt from buffer to buffer +//! // buffer length must be equal to input length +//! let mut buf1 = vec![0u8; 34]; +//! Aes128Cfb8Enc::new(&key.into(), &iv.into()) +//! .encrypt_b2b(&plaintext[..], &mut buf1) +//! .unwrap(); +//! assert_eq!(buf1, &ciphertext[..]); +//! +//! let mut buf2 = vec![0u8; 34]; +//! Aes128Cfb8Dec::new(&key.into(), &iv.into()) +//! .decrypt_b2b(&buf1, &mut buf2) +//! .unwrap(); +//! assert_eq!(buf2, &plaintext[..]); +//! ``` +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CFB-1,_CFB-8,_CFB-64,_CFB-128,_etc. + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_root_url = "https://docs.rs/cfb8/0.8.0" +)] +#![forbid(unsafe_code)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs, rust_2018_idioms)] + +mod decrypt; +mod encrypt; + +pub use cipher; +pub use decrypt::Decryptor; +pub use encrypt::Encryptor; diff --git a/cfb8/tests/aes.rs b/cfb8/tests/aes.rs new file mode 100644 index 0000000..f1f7ab9 --- /dev/null +++ b/cfb8/tests/aes.rs @@ -0,0 +1,55 @@ +use aes::*; +use cfb8::{Decryptor, Encryptor}; +use cipher::{block_mode_dec_test, block_mode_enc_test, iv_state_test}; + +iv_state_test!(aes128_cfb8_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes128_cfb8_dec_iv_state, Decryptor, decrypt); +iv_state_test!(aes192_cfb8_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes192_cfb8_dec_iv_state, Decryptor, decrypt); +iv_state_test!(aes256_cfb8_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes256_cfb8_dec_iv_state, Decryptor, decrypt); + +// Test vectors from CVAP "AES Multiblock Message Test (MMT) Sample Vectors": +// +block_mode_enc_test!(aes128_cfb8_enc_test, "aes128", Encryptor); +block_mode_dec_test!(aes128_cfb8_dec_test, "aes128", Decryptor); +block_mode_enc_test!(aes128enc_cfb8_enc_test, "aes128", Encryptor); +block_mode_dec_test!(aes128enc_cfb8_dec_test, "aes128", Decryptor); +block_mode_enc_test!(aes192_cfb8_enc_test, "aes192", Encryptor); +block_mode_dec_test!(aes192_cfb8_dec_test, "aes192", Decryptor); +block_mode_enc_test!(aes192enc_cfb8_enc_test, "aes192", Encryptor); +block_mode_dec_test!(aes192dec_cfb8_dec_test, "aes192", Decryptor); +block_mode_enc_test!(aes256_cfb8_enc_test, "aes256", Encryptor); +block_mode_dec_test!(aes256_cfb8_dec_test, "aes256", Decryptor); +block_mode_enc_test!(aes256enc_cfb8_enc_test, "aes256", Encryptor); +block_mode_dec_test!(aes256dec_cfb8_dec_test, "aes256", Decryptor); + +/// Test methods from the `AsyncStreamCipher` trait. +#[test] +fn aes128_cfb8_async_test() { + use cipher::{AsyncStreamCipher, KeyIvInit}; + + type Enc = Encryptor; + type Dec = Decryptor; + + let key = [42; 16]; + let iv = [24; 16]; + let mut pt = [0u8; 101]; + for (i, b) in pt.iter_mut().enumerate() { + *b = (i % 11) as u8; + } + let enc = Enc::new_from_slices(&key, &iv).unwrap(); + let mut ct = pt.clone(); + enc.encrypt(&mut ct); + for i in 1..100 { + let enc = Enc::new_from_slices(&key, &iv).unwrap(); + let mut t = pt.clone(); + let t = &mut t[..i]; + enc.encrypt(t); + assert_eq!(t, &ct[..i]); + + let dec = Dec::new_from_slices(&key, &iv).unwrap(); + dec.decrypt(t); + assert_eq!(t, &pt[..i]); + } +} diff --git a/cfb8/tests/data/aes128.blb b/cfb8/tests/data/aes128.blb new file mode 100644 index 0000000..81b6caf Binary files /dev/null and b/cfb8/tests/data/aes128.blb differ diff --git a/cfb8/tests/data/aes192.blb b/cfb8/tests/data/aes192.blb new file mode 100644 index 0000000..f7eb86d Binary files /dev/null and b/cfb8/tests/data/aes192.blb differ diff --git a/cfb8/tests/data/aes256.blb b/cfb8/tests/data/aes256.blb new file mode 100644 index 0000000..9a03c66 Binary files /dev/null and b/cfb8/tests/data/aes256.blb differ diff --git a/ctr/CHANGELOG.md b/ctr/CHANGELOG.md new file mode 100644 index 0000000..37149d1 --- /dev/null +++ b/ctr/CHANGELOG.md @@ -0,0 +1,67 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.9.0 (2022-02-10) +### Changed +- Update `cipher` dependency to v0.4 and move crate +to the [RustCrypto/block-modes] repository ([#2]) + +[#2]: https://github.com/RustCrypto/block-modes/pull/2 +[RustCrypto/block-modes]: https://github.com/RustCrypto/block-modes + +## 0.8.0 (2021-07-08) +### Changed +- Make implementation generic over block size (previously it +was generic only over 128-bit block ciphers). Breaking changes +in the `CtrFlavor` API. ([#252]). + +[#252]: https://github.com/RustCrypto/stream-ciphers/pull/252 + +## 0.7.0 (2020-04-29) +### Changed +- Generic implementation of CTR ([#195]) +- Removed `Ctr32LE` mask bit ([#197]) +- Bump `cipher` dependency to v0.3 ([#226]) + +[#195]: https://github.com/RustCrypto/stream-ciphers/pull/195 +[#197]: https://github.com/RustCrypto/stream-ciphers/pull/197 +[#226]: https://github.com/RustCrypto/stream-ciphers/pull/226 + +## 0.6.0 (2020-10-16) +### Added +- `Ctr32BE` and `Ctr32LE` ([#170]) + +### Changed +- Replace `block-cipher`/`stream-cipher` with `cipher` crate ([#177]) + +[#177]: https://github.com/RustCrypto/stream-ciphers/pull/177 +[#170]: https://github.com/RustCrypto/stream-ciphers/pull/170 + +## 0.5.0 (2020-08-26) +### Changed +- Bump `stream-cipher` dependency to v0.7, implement the `FromBlockCipher` trait ([#161], [#164]) + +[#161]: https://github.com/RustCrypto/stream-ciphers/pull/161 +[#164]: https://github.com/RustCrypto/stream-ciphers/pull/164 + +## 0.4.0 (2020-06-06) +### Changed +- Upgrade to the `stream-cipher` v0.4 crate ([#116], [#138]) +- Upgrade to Rust 2018 edition ([#116]) + +[#138]: https://github.com/RustCrypto/stream-ciphers/pull/138 +[#116]: https://github.com/RustCrypto/stream-ciphers/pull/121 + +## 0.3.2 (2019-03-11) + +## 0.3.0 (2018-11-01) + +## 0.2.0 (2018-10-13) + +## 0.1.1 (2018-10-13) + +## 0.1.0 (2018-07-30) diff --git a/ctr/Cargo.toml b/ctr/Cargo.toml new file mode 100644 index 0000000..9f984ce --- /dev/null +++ b/ctr/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "ctr" +version = "0.9.0" # Also update html_root_url in lib.rs when bumping this +description = "CTR block modes of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.56" +readme = "README.md" +documentation = "https://docs.rs/ctr" +repository = "https://github.com/RustCrypto/block-modes" +keywords = ["crypto", "block-mode", "stream-cipher", "ciphers"] +categories = ["cryptography", "no-std"] + +[dependencies] +cipher = "0.4" + +[dev-dependencies] +aes = "0.8" +magma = "0.8" +kuznyechik = "0.8" +cipher = { version = "0.4", features = ["dev"] } +hex-literal = "0.3" + +[features] +block-padding = ["cipher/block-padding"] +zeroize = ["cipher/zeroize"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/ctr/LICENSE-APACHE b/ctr/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/ctr/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/ctr/LICENSE-MIT b/ctr/LICENSE-MIT new file mode 100644 index 0000000..d19d409 --- /dev/null +++ b/ctr/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2018-2022 RustCrypto Developers +Copyright (c) 2018 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/ctr/README.md b/ctr/README.md new file mode 100644 index 0000000..a2a3043 --- /dev/null +++ b/ctr/README.md @@ -0,0 +1,59 @@ +# RustCrypto: CTR + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Counter][CTR] (CTR) block cipher mode of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.56** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/ctr.svg +[crate-link]: https://crates.io/crates/ctr +[docs-image]: https://docs.rs/ctr/badge.svg +[docs-link]: https://docs.rs/ctr/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/308460-block-modes +[build-image]: https://github.com/RustCrypto/block-modes/workflows/ctr/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-modes/actions?query=workflow%3Actr+branch%3Amaster + +[//]: # (general links) + +[CTR]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR) +[cipher-doc]: https://docs.rs/cipher/ diff --git a/ctr/benches/aes128.rs b/ctr/benches/aes128.rs new file mode 100644 index 0000000..ad30e34 --- /dev/null +++ b/ctr/benches/aes128.rs @@ -0,0 +1,26 @@ +#![feature(test)] +extern crate test; + +cipher::stream_cipher_bench!( + ctr::Ctr32LE; + ctr_32le_aes128_stream_bench1_16b 16; + ctr_32le_aes128_stream_bench2_256b 256; + ctr_32le_aes128_stream_bench3_1kib 1024; + ctr_32le_aes128_stream_bench4_16kib 16384; +); + +cipher::stream_cipher_bench!( + ctr::Ctr64LE; + ctr_64le_aes128_stream_bench1_16b 16; + ctr_64le_aes128_stream_bench2_256b 256; + ctr_64le_aes128_stream_bench3_1kib 1024; + ctr_64le_aes128_stream_bench4_16kib 16384; +); + +cipher::stream_cipher_bench!( + ctr::Ctr128BE; + ctr_128be_aes128_stream_bench1_16b 16; + ctr_128be_aes128_stream_bench2_256b 256; + ctr_128be_aes128_stream_bench3_1kib 1024; + ctr_128be_aes128_stream_bench4_16kib 16384; +); diff --git a/ctr/src/backend.rs b/ctr/src/backend.rs new file mode 100644 index 0000000..a94fd43 --- /dev/null +++ b/ctr/src/backend.rs @@ -0,0 +1,83 @@ +use crate::CtrFlavor; +use cipher::{ + generic_array::ArrayLength, Block, BlockBackend, BlockClosure, BlockSizeUser, ParBlocks, + ParBlocksSizeUser, StreamBackend, StreamClosure, +}; + +struct Backend<'a, F, B> +where + F: CtrFlavor, + B: BlockBackend, +{ + ctr_nonce: &'a mut F::CtrNonce, + backend: &'a mut B, +} + +impl<'a, F, B> BlockSizeUser for Backend<'a, F, B> +where + F: CtrFlavor, + B: BlockBackend, +{ + type BlockSize = B::BlockSize; +} + +impl<'a, F, B> ParBlocksSizeUser for Backend<'a, F, B> +where + F: CtrFlavor, + B: BlockBackend, +{ + type ParBlocksSize = B::ParBlocksSize; +} + +impl<'a, F, B> StreamBackend for Backend<'a, F, B> +where + F: CtrFlavor, + B: BlockBackend, +{ + #[inline(always)] + fn gen_ks_block(&mut self, block: &mut Block) { + let tmp = F::next_block(self.ctr_nonce); + self.backend.proc_block((&tmp, block).into()); + } + + #[inline(always)] + fn gen_par_ks_blocks(&mut self, blocks: &mut ParBlocks) { + let mut tmp = ParBlocks::::default(); + for block in tmp.iter_mut() { + *block = F::next_block(self.ctr_nonce); + } + self.backend.proc_par_blocks((&tmp, blocks).into()); + } +} + +pub(crate) struct Closure<'a, F, BS, SC> +where + F: CtrFlavor, + BS: ArrayLength, + SC: StreamClosure, +{ + pub(crate) ctr_nonce: &'a mut F::CtrNonce, + pub(crate) f: SC, +} + +impl<'a, F, BS, SC> BlockSizeUser for Closure<'a, F, BS, SC> +where + F: CtrFlavor, + BS: ArrayLength, + SC: StreamClosure, +{ + type BlockSize = BS; +} + +impl<'a, F, BS, SC> BlockClosure for Closure<'a, F, BS, SC> +where + F: CtrFlavor, + BS: ArrayLength, + SC: StreamClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { ctr_nonce, f } = self; + f.call(&mut Backend:: { ctr_nonce, backend }) + } +} diff --git a/ctr/src/ctr_core.rs b/ctr/src/ctr_core.rs new file mode 100644 index 0000000..7868f5b --- /dev/null +++ b/ctr/src/ctr_core.rs @@ -0,0 +1,143 @@ +use crate::{backend::Closure, CtrFlavor}; +use cipher::{ + crypto_common::{InnerUser, IvSizeUser}, + AlgorithmName, BlockCipher, BlockEncryptMut, BlockSizeUser, InnerIvInit, Iv, IvState, + StreamCipherCore, StreamCipherSeekCore, StreamClosure, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::ZeroizeOnDrop; + +/// Generic CTR block mode isntance. +#[derive(Clone)] +pub struct CtrCore +where + C: BlockEncryptMut + BlockCipher, + F: CtrFlavor, +{ + cipher: C, + ctr_nonce: F::CtrNonce, +} + +impl BlockSizeUser for CtrCore +where + C: BlockEncryptMut + BlockCipher, + F: CtrFlavor, +{ + type BlockSize = C::BlockSize; +} + +impl StreamCipherCore for CtrCore +where + C: BlockEncryptMut + BlockCipher, + F: CtrFlavor, +{ + #[inline] + fn remaining_blocks(&self) -> Option { + F::remaining(&self.ctr_nonce) + } + + #[inline] + fn process_with_backend(&mut self, f: impl StreamClosure) { + let Self { cipher, ctr_nonce } = self; + cipher.encrypt_with_backend_mut(Closure:: { ctr_nonce, f }); + } +} + +impl StreamCipherSeekCore for CtrCore +where + C: BlockEncryptMut + BlockCipher, + F: CtrFlavor, +{ + type Counter = F::Backend; + + #[inline] + fn get_block_pos(&self) -> Self::Counter { + F::as_backend(&self.ctr_nonce) + } + + #[inline] + fn set_block_pos(&mut self, pos: Self::Counter) { + F::set_from_backend(&mut self.ctr_nonce, pos); + } +} + +impl InnerUser for CtrCore +where + C: BlockEncryptMut + BlockCipher, + F: CtrFlavor, +{ + type Inner = C; +} + +impl IvSizeUser for CtrCore +where + C: BlockEncryptMut + BlockCipher, + F: CtrFlavor, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for CtrCore +where + C: BlockEncryptMut + BlockCipher, + F: CtrFlavor, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + Self { + cipher, + ctr_nonce: F::from_nonce(iv), + } + } +} + +impl IvState for CtrCore +where + C: BlockEncryptMut + BlockCipher, + F: CtrFlavor, +{ + #[inline] + fn iv_state(&self) -> Iv { + F::current_block(&self.ctr_nonce) + } +} + +impl AlgorithmName for CtrCore +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, + F: CtrFlavor, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Ctr")?; + f.write_str(F::NAME)?; + f.write_str("<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for CtrCore +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, + F: CtrFlavor, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Ctr")?; + f.write_str(F::NAME)?; + f.write_str("<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for CtrCore +where + C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop, + F: CtrFlavor, + F::CtrNonce: ZeroizeOnDrop, +{ +} diff --git a/ctr/src/flavors.rs b/ctr/src/flavors.rs new file mode 100644 index 0000000..30361c8 --- /dev/null +++ b/ctr/src/flavors.rs @@ -0,0 +1,44 @@ +//! CTR mode flavors + +use cipher::{ + generic_array::{ArrayLength, GenericArray}, + Counter, +}; + +mod ctr128; +mod ctr32; +mod ctr64; + +pub use ctr128::{Ctr128BE, Ctr128LE}; +pub use ctr32::{Ctr32BE, Ctr32LE}; +pub use ctr64::{Ctr64BE, Ctr64LE}; + +/// Trait implemented by different CTR flavors. +pub trait CtrFlavor> { + /// Inner representation of nonce. + type CtrNonce: Clone; + /// Backend numeric type + type Backend: Counter; + /// Flavor name + const NAME: &'static str; + + /// Return number of remaining blocks. + /// + /// If result does not fit into `usize`, returns `None`. + fn remaining(cn: &Self::CtrNonce) -> Option; + + /// Generate block for given `nonce` and current counter value. + fn next_block(cn: &mut Self::CtrNonce) -> GenericArray; + + /// Generate block for given `nonce` and current counter value. + fn current_block(cn: &Self::CtrNonce) -> GenericArray; + + /// Initialize from bytes. + fn from_nonce(block: &GenericArray) -> Self::CtrNonce; + + /// Convert from a backend value + fn set_from_backend(cn: &mut Self::CtrNonce, v: Self::Backend); + + /// Convert to a backend value + fn as_backend(cn: &Self::CtrNonce) -> Self::Backend; +} diff --git a/ctr/src/flavors/ctr128.rs b/ctr/src/flavors/ctr128.rs new file mode 100644 index 0000000..9d10385 --- /dev/null +++ b/ctr/src/flavors/ctr128.rs @@ -0,0 +1,158 @@ +//! 128-bit counter falvors. +use super::CtrFlavor; +use cipher::{ + generic_array::{ArrayLength, GenericArray}, + typenum::{PartialDiv, PartialQuot, Unsigned, U16}, +}; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +type ChunkSize = U16; +type Chunks = PartialQuot; +const CS: usize = ChunkSize::USIZE; + +#[derive(Clone)] +pub struct CtrNonce128> { + ctr: u128, + nonce: GenericArray, +} + +#[cfg(feature = "zeroize")] +impl> Drop for CtrNonce128 { + fn drop(&mut self) { + self.ctr.zeroize(); + self.nonce.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl> ZeroizeOnDrop for CtrNonce128 {} + +/// 128-bit big endian counter flavor. +pub enum Ctr128BE {} + +impl CtrFlavor for Ctr128BE +where + B: ArrayLength + PartialDiv, + Chunks: ArrayLength, +{ + type CtrNonce = CtrNonce128>; + type Backend = u128; + const NAME: &'static str = "128BE"; + + #[inline] + fn remaining(cn: &Self::CtrNonce) -> Option { + (core::u128::MAX - cn.ctr).try_into().ok() + } + + #[inline(always)] + fn current_block(cn: &Self::CtrNonce) -> GenericArray { + let mut block = GenericArray::::default(); + for i in 0..Chunks::::USIZE { + let t = if i == Chunks::::USIZE - 1 { + cn.ctr.wrapping_add(cn.nonce[i]).to_be_bytes() + } else { + cn.nonce[i].to_ne_bytes() + }; + block[CS * i..][..CS].copy_from_slice(&t); + } + block + } + + #[inline] + fn next_block(cn: &mut Self::CtrNonce) -> GenericArray { + let block = Self::current_block(cn); + cn.ctr = cn.ctr.wrapping_add(1); + block + } + + #[inline] + fn from_nonce(block: &GenericArray) -> Self::CtrNonce { + let mut nonce = GenericArray::>::default(); + for i in 0..Chunks::::USIZE { + let chunk = block[CS * i..][..CS].try_into().unwrap(); + nonce[i] = if i == Chunks::::USIZE - 1 { + u128::from_be_bytes(chunk) + } else { + u128::from_ne_bytes(chunk) + } + } + let ctr = 0; + Self::CtrNonce { ctr, nonce } + } + + #[inline] + fn as_backend(cn: &Self::CtrNonce) -> Self::Backend { + cn.ctr + } + + #[inline] + fn set_from_backend(cn: &mut Self::CtrNonce, v: Self::Backend) { + cn.ctr = v; + } +} + +/// 128-bit big endian counter flavor. +pub enum Ctr128LE {} + +impl CtrFlavor for Ctr128LE +where + B: ArrayLength + PartialDiv, + Chunks: ArrayLength, +{ + type CtrNonce = CtrNonce128>; + type Backend = u128; + const NAME: &'static str = "128LE"; + + #[inline] + fn remaining(cn: &Self::CtrNonce) -> Option { + (core::u128::MAX - cn.ctr).try_into().ok() + } + + #[inline(always)] + fn current_block(cn: &Self::CtrNonce) -> GenericArray { + let mut block = GenericArray::::default(); + for i in 0..Chunks::::USIZE { + let t = if i == 0 { + cn.ctr.wrapping_add(cn.nonce[i]).to_le_bytes() + } else { + cn.nonce[i].to_ne_bytes() + }; + block[CS * i..][..CS].copy_from_slice(&t); + } + block + } + + #[inline] + fn next_block(cn: &mut Self::CtrNonce) -> GenericArray { + let block = Self::current_block(cn); + cn.ctr = cn.ctr.wrapping_add(1); + block + } + + #[inline] + fn from_nonce(block: &GenericArray) -> Self::CtrNonce { + let mut nonce = GenericArray::>::default(); + for i in 0..Chunks::::USIZE { + let chunk = block[CS * i..][..CS].try_into().unwrap(); + nonce[i] = if i == 0 { + u128::from_le_bytes(chunk) + } else { + u128::from_ne_bytes(chunk) + } + } + let ctr = 0; + Self::CtrNonce { ctr, nonce } + } + + #[inline] + fn as_backend(cn: &Self::CtrNonce) -> Self::Backend { + cn.ctr + } + + #[inline] + fn set_from_backend(cn: &mut Self::CtrNonce, v: Self::Backend) { + cn.ctr = v; + } +} diff --git a/ctr/src/flavors/ctr32.rs b/ctr/src/flavors/ctr32.rs new file mode 100644 index 0000000..17cb011 --- /dev/null +++ b/ctr/src/flavors/ctr32.rs @@ -0,0 +1,158 @@ +//! 32-bit counter falvors. +use super::CtrFlavor; +use cipher::{ + generic_array::{ArrayLength, GenericArray}, + typenum::{PartialDiv, PartialQuot, Unsigned, U4}, +}; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +type ChunkSize = U4; +type Chunks = PartialQuot; +const CS: usize = ChunkSize::USIZE; + +#[derive(Clone)] +pub struct CtrNonce32> { + ctr: u32, + nonce: GenericArray, +} + +#[cfg(feature = "zeroize")] +impl> Drop for CtrNonce32 { + fn drop(&mut self) { + self.ctr.zeroize(); + self.nonce.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl> ZeroizeOnDrop for CtrNonce32 {} + +/// 32-bit big endian counter flavor. +pub enum Ctr32BE {} + +impl CtrFlavor for Ctr32BE +where + B: ArrayLength + PartialDiv, + Chunks: ArrayLength, +{ + type CtrNonce = CtrNonce32>; + type Backend = u32; + const NAME: &'static str = "32BE"; + + #[inline] + fn remaining(cn: &Self::CtrNonce) -> Option { + (core::u32::MAX - cn.ctr).try_into().ok() + } + + #[inline(always)] + fn current_block(cn: &Self::CtrNonce) -> GenericArray { + let mut block = GenericArray::::default(); + for i in 0..Chunks::::USIZE { + let t = if i == Chunks::::USIZE - 1 { + cn.ctr.wrapping_add(cn.nonce[i]).to_be_bytes() + } else { + cn.nonce[i].to_ne_bytes() + }; + block[CS * i..][..CS].copy_from_slice(&t); + } + block + } + + #[inline] + fn next_block(cn: &mut Self::CtrNonce) -> GenericArray { + let block = Self::current_block(cn); + cn.ctr = cn.ctr.wrapping_add(1); + block + } + + #[inline] + fn from_nonce(block: &GenericArray) -> Self::CtrNonce { + let mut nonce = GenericArray::>::default(); + for i in 0..Chunks::::USIZE { + let chunk = block[CS * i..][..CS].try_into().unwrap(); + nonce[i] = if i == Chunks::::USIZE - 1 { + u32::from_be_bytes(chunk) + } else { + u32::from_ne_bytes(chunk) + } + } + let ctr = 0; + Self::CtrNonce { ctr, nonce } + } + + #[inline] + fn as_backend(cn: &Self::CtrNonce) -> Self::Backend { + cn.ctr + } + + #[inline] + fn set_from_backend(cn: &mut Self::CtrNonce, v: Self::Backend) { + cn.ctr = v; + } +} + +/// 32-bit big endian counter flavor. +pub enum Ctr32LE {} + +impl CtrFlavor for Ctr32LE +where + B: ArrayLength + PartialDiv, + Chunks: ArrayLength, +{ + type CtrNonce = CtrNonce32>; + type Backend = u32; + const NAME: &'static str = "32LE"; + + #[inline] + fn remaining(cn: &Self::CtrNonce) -> Option { + (core::u32::MAX - cn.ctr).try_into().ok() + } + + #[inline(always)] + fn current_block(cn: &Self::CtrNonce) -> GenericArray { + let mut block = GenericArray::::default(); + for i in 0..Chunks::::USIZE { + let t = if i == 0 { + cn.ctr.wrapping_add(cn.nonce[i]).to_le_bytes() + } else { + cn.nonce[i].to_ne_bytes() + }; + block[CS * i..][..CS].copy_from_slice(&t); + } + block + } + + #[inline] + fn next_block(cn: &mut Self::CtrNonce) -> GenericArray { + let block = Self::current_block(cn); + cn.ctr = cn.ctr.wrapping_add(1); + block + } + + #[inline] + fn from_nonce(block: &GenericArray) -> Self::CtrNonce { + let mut nonce = GenericArray::>::default(); + for i in 0..Chunks::::USIZE { + let chunk = block[CS * i..][..CS].try_into().unwrap(); + nonce[i] = if i == 0 { + u32::from_le_bytes(chunk) + } else { + u32::from_ne_bytes(chunk) + } + } + let ctr = 0; + Self::CtrNonce { ctr, nonce } + } + + #[inline] + fn as_backend(cn: &Self::CtrNonce) -> Self::Backend { + cn.ctr + } + + #[inline] + fn set_from_backend(cn: &mut Self::CtrNonce, v: Self::Backend) { + cn.ctr = v; + } +} diff --git a/ctr/src/flavors/ctr64.rs b/ctr/src/flavors/ctr64.rs new file mode 100644 index 0000000..3e63abe --- /dev/null +++ b/ctr/src/flavors/ctr64.rs @@ -0,0 +1,158 @@ +//! 64-bit counter falvors. +use super::CtrFlavor; +use cipher::{ + generic_array::{ArrayLength, GenericArray}, + typenum::{PartialDiv, PartialQuot, Unsigned, U8}, +}; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +type ChunkSize = U8; +type Chunks = PartialQuot; +const CS: usize = ChunkSize::USIZE; + +#[derive(Clone)] +pub struct CtrNonce64> { + ctr: u64, + nonce: GenericArray, +} + +#[cfg(feature = "zeroize")] +impl> Drop for CtrNonce64 { + fn drop(&mut self) { + self.ctr.zeroize(); + self.nonce.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl> ZeroizeOnDrop for CtrNonce64 {} + +/// 64-bit big endian counter flavor. +pub enum Ctr64BE {} + +impl CtrFlavor for Ctr64BE +where + B: ArrayLength + PartialDiv, + Chunks: ArrayLength, +{ + type CtrNonce = CtrNonce64>; + type Backend = u64; + const NAME: &'static str = "64BE"; + + #[inline] + fn remaining(cn: &Self::CtrNonce) -> Option { + (core::u64::MAX - cn.ctr).try_into().ok() + } + + #[inline(always)] + fn current_block(cn: &Self::CtrNonce) -> GenericArray { + let mut block = GenericArray::::default(); + for i in 0..Chunks::::USIZE { + let t = if i == Chunks::::USIZE - 1 { + cn.ctr.wrapping_add(cn.nonce[i]).to_be_bytes() + } else { + cn.nonce[i].to_ne_bytes() + }; + block[CS * i..][..CS].copy_from_slice(&t); + } + block + } + + #[inline] + fn next_block(cn: &mut Self::CtrNonce) -> GenericArray { + let block = Self::current_block(cn); + cn.ctr = cn.ctr.wrapping_add(1); + block + } + + #[inline] + fn from_nonce(block: &GenericArray) -> Self::CtrNonce { + let mut nonce = GenericArray::>::default(); + for i in 0..Chunks::::USIZE { + let chunk = block[CS * i..][..CS].try_into().unwrap(); + nonce[i] = if i == Chunks::::USIZE - 1 { + u64::from_be_bytes(chunk) + } else { + u64::from_ne_bytes(chunk) + } + } + let ctr = 0; + Self::CtrNonce { ctr, nonce } + } + + #[inline] + fn as_backend(cn: &Self::CtrNonce) -> Self::Backend { + cn.ctr + } + + #[inline] + fn set_from_backend(cn: &mut Self::CtrNonce, v: Self::Backend) { + cn.ctr = v; + } +} + +/// 64-bit big endian counter flavor. +pub enum Ctr64LE {} + +impl CtrFlavor for Ctr64LE +where + B: ArrayLength + PartialDiv, + Chunks: ArrayLength, +{ + type CtrNonce = CtrNonce64>; + type Backend = u64; + const NAME: &'static str = "64LE"; + + #[inline] + fn remaining(cn: &Self::CtrNonce) -> Option { + (core::u64::MAX - cn.ctr).try_into().ok() + } + + #[inline(always)] + fn current_block(cn: &Self::CtrNonce) -> GenericArray { + let mut block = GenericArray::::default(); + for i in 0..Chunks::::USIZE { + let t = if i == 0 { + cn.ctr.wrapping_add(cn.nonce[i]).to_le_bytes() + } else { + cn.nonce[i].to_ne_bytes() + }; + block[CS * i..][..CS].copy_from_slice(&t); + } + block + } + + #[inline] + fn next_block(cn: &mut Self::CtrNonce) -> GenericArray { + let block = Self::current_block(cn); + cn.ctr = cn.ctr.wrapping_add(1); + block + } + + #[inline] + fn from_nonce(block: &GenericArray) -> Self::CtrNonce { + let mut nonce = GenericArray::>::default(); + for i in 0..Chunks::::USIZE { + let chunk = block[CS * i..][..CS].try_into().unwrap(); + nonce[i] = if i == 0 { + u64::from_le_bytes(chunk) + } else { + u64::from_ne_bytes(chunk) + } + } + let ctr = 0; + Self::CtrNonce { ctr, nonce } + } + + #[inline] + fn as_backend(cn: &Self::CtrNonce) -> Self::Backend { + cn.ctr + } + + #[inline] + fn set_from_backend(cn: &mut Self::CtrNonce, v: Self::Backend) { + cn.ctr = v; + } +} diff --git a/ctr/src/lib.rs b/ctr/src/lib.rs new file mode 100644 index 0000000..bafb064 --- /dev/null +++ b/ctr/src/lib.rs @@ -0,0 +1,91 @@ +//! Generic implementations of [CTR mode][1] for block ciphers. +//! +//! +//! +//! +//! Mode functionality is accessed using traits from re-exported [`cipher`] crate. +//! +//! # ⚠️ Security Warning: Hazmat! +//! +//! This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity +//! is not verified, which can lead to serious vulnerabilities! +//! +//! # Example +//! ``` +//! use aes::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek}; +//! use hex_literal::hex; +//! +//! type Aes128Ctr64LE = ctr::Ctr64LE; +//! +//! let key = [0x42; 16]; +//! let iv = [0x24; 16]; +//! let plaintext = b"hello world! this is my plaintext."; +//! let ciphertext = hex!( +//! "3357121ebb5a29468bd861467596ce3da59bdee42dcc0614dea955368d8a5dc0cad4" +//! ); +//! +//! // encrypt in-place +//! let mut buf = plaintext.to_vec(); +//! let mut cipher = Aes128Ctr64LE::new(&key.into(), &iv.into()); +//! cipher.apply_keystream(&mut buf); +//! assert_eq!(buf, &ciphertext[..]); +//! +//! // CTR mode can be used with streaming messages +//! let mut cipher = Aes128Ctr64LE::new(&key.into(), &iv.into()); +//! for chunk in buf.chunks_mut(3) { +//! cipher.apply_keystream(chunk); +//! } +//! assert_eq!(buf, &plaintext[..]); +//! +//! // CTR mode supports seeking +//! cipher.seek(0u32); +//! +//! // encrypt/decrypt from buffer to buffer +//! // buffer length must be equal to input length +//! let mut buf1 = vec![0u8; 34]; +//! cipher +//! .apply_keystream_b2b(&plaintext[..], &mut buf1) +//! .unwrap(); +//! assert_eq!(buf1, &ciphertext[..]); +//! +//! let mut buf2 = vec![0u8; 34]; +//! cipher.seek(0u32); +//! cipher.apply_keystream_b2b(&buf1, &mut buf2).unwrap(); +//! assert_eq!(buf2, &plaintext[..]); +//! ``` +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CTR + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_root_url = "https://docs.rs/ctr/0.9.0" +)] +#![forbid(unsafe_code)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs, rust_2018_idioms)] + +pub mod flavors; + +mod backend; +mod ctr_core; + +pub use cipher; +pub use flavors::CtrFlavor; + +use cipher::StreamCipherCoreWrapper; +pub use ctr_core::CtrCore; + +/// CTR mode with 128-bit big endian counter. +pub type Ctr128BE = StreamCipherCoreWrapper>; +/// CTR mode with 128-bit little endian counter. +pub type Ctr128LE = StreamCipherCoreWrapper>; +/// CTR mode with 64-bit big endian counter. +pub type Ctr64BE = StreamCipherCoreWrapper>; +/// CTR mode with 64-bit little endian counter. +pub type Ctr64LE = StreamCipherCoreWrapper>; +/// CTR mode with 32-bit big endian counter. +pub type Ctr32BE = StreamCipherCoreWrapper>; +/// CTR mode with 32-bit little endian counter. +pub type Ctr32LE = StreamCipherCoreWrapper>; diff --git a/ctr/tests/ctr128/data/aes128-ctr.blb b/ctr/tests/ctr128/data/aes128-ctr.blb new file mode 100644 index 0000000..d721e4e Binary files /dev/null and b/ctr/tests/ctr128/data/aes128-ctr.blb differ diff --git a/ctr/tests/ctr128/data/aes256-ctr.blb b/ctr/tests/ctr128/data/aes256-ctr.blb new file mode 100644 index 0000000..47daaf8 Binary files /dev/null and b/ctr/tests/ctr128/data/aes256-ctr.blb differ diff --git a/ctr/tests/ctr128/mod.rs b/ctr/tests/ctr128/mod.rs new file mode 100644 index 0000000..c5e0206 --- /dev/null +++ b/ctr/tests/ctr128/mod.rs @@ -0,0 +1,12 @@ +use aes::{Aes128, Aes256}; +use ctr::{flavors, Ctr128BE, CtrCore}; + +cipher::stream_cipher_test!(aes128_ctr_core, "aes128-ctr", Ctr128BE); +cipher::stream_cipher_test!(aes256_ctr_core, "aes256-ctr", Ctr128BE); +cipher::stream_cipher_seek_test!(aes128_ctr_seek, Ctr128BE); +cipher::stream_cipher_seek_test!(aes256_ctr_seek, Ctr128BE); +cipher::iv_state_test!( + aes128_ctr_iv_state, + CtrCore, + apply_ks, +); diff --git a/ctr/tests/ctr32/be.rs b/ctr/tests/ctr32/be.rs new file mode 100644 index 0000000..98b1302 --- /dev/null +++ b/ctr/tests/ctr32/be.rs @@ -0,0 +1,86 @@ +//! Counter Mode with a 32-bit big endian counter + +use cipher::{KeyIvInit, StreamCipher, StreamCipherSeek, StreamCipherSeekCore}; +use hex_literal::hex; + +type Aes128Ctr = ctr::Ctr32BE; + +const KEY: &[u8; 16] = &hex!("000102030405060708090A0B0C0D0E0F"); +const NONCE1: &[u8; 16] = &hex!("11111111111111111111111111111111"); +const NONCE2: &[u8; 16] = &hex!("222222222222222222222222FFFFFFFE"); + +#[test] +fn counter_incr() { + let mut ctr = Aes128Ctr::new(KEY.into(), NONCE1.into()); + assert_eq!(ctr.get_core().get_block_pos(), 0); + + let mut buffer = [0u8; 64]; + ctr.apply_keystream(&mut buffer); + + assert_eq!(ctr.get_core().get_block_pos(), 4); + assert_eq!( + &buffer[..], + &hex!( + "35D14E6D3E3A279CF01E343E34E7DED36EEADB04F42E2251AB4377F257856DBA" + "0AB37657B9C2AA09762E518FC9395D5304E96C34CCD2F0A95CDE7321852D90C0" + )[..] + ); +} + +#[test] +fn counter_seek() { + let mut ctr = Aes128Ctr::new(KEY.into(), NONCE1.into()); + ctr.seek(16); + assert_eq!(ctr.get_core().get_block_pos(), 1); + + let mut buffer = [0u8; 64]; + ctr.apply_keystream(&mut buffer); + + assert_eq!(ctr.get_core().get_block_pos(), 5); + assert_eq!( + &buffer[..], + &hex!( + "6EEADB04F42E2251AB4377F257856DBA0AB37657B9C2AA09762E518FC9395D53" + "04E96C34CCD2F0A95CDE7321852D90C0F7441EAB3811A03FDBD162AEC402F5AA" + )[..] + ); +} + +#[test] +fn keystream_xor() { + let mut ctr = Aes128Ctr::new(KEY.into(), NONCE1.into()); + let mut buffer = [1u8; 64]; + + ctr.apply_keystream(&mut buffer); + assert_eq!( + &buffer[..], + &hex!( + "34D04F6C3F3B269DF11F353F35E6DFD26FEBDA05F52F2350AA4276F356846CBB" + "0BB27756B8C3AB08772F508EC8385C5205E86D35CDD3F1A85DDF7220842C91C1" + )[..] + ); +} + +#[test] +fn counter_wrap() { + let mut ctr = Aes128Ctr::new(KEY.into(), NONCE2.into()); + assert_eq!(ctr.get_core().get_block_pos(), 0); + + let mut buffer = [0u8; 64]; + ctr.apply_keystream(&mut buffer); + + assert_eq!(ctr.get_core().get_block_pos(), 4); + assert_eq!( + &buffer[..], + &hex!( + "58FC849D1CF53C54C63E1B1D15CB3C8AAA335F72135585E9FF943F4DAC77CB63" + "BD1AE8716BE69C3B4D886B222B9B4E1E67548EF896A96E2746D8CA6476D8B183" + )[..] + ); +} + +cipher::iv_state_test!( + iv_state, + ctr::CtrCore, + apply_ks, +); diff --git a/ctr/tests/ctr32/le.rs b/ctr/tests/ctr32/le.rs new file mode 100644 index 0000000..21bf358 --- /dev/null +++ b/ctr/tests/ctr32/le.rs @@ -0,0 +1,96 @@ +//! Counter Mode with a 32-bit little endian counter + +use cipher::{ + consts::U16, generic_array::GenericArray, KeyIvInit, StreamCipher, StreamCipherSeek, + StreamCipherSeekCore, +}; +use hex_literal::hex; + +type Aes128Ctr = ctr::Ctr32LE; + +const KEY: &[u8; 16] = &hex!("000102030405060708090A0B0C0D0E0F"); +const NONCE1: &[u8; 16] = &hex!("11111111111111111111111111111111"); +const NONCE2: &[u8; 16] = &hex!("FEFFFFFF222222222222222222222222"); + +/// Compute nonce as used by AES-GCM-SIV +fn nonce(bytes: &[u8; 16]) -> GenericArray { + let mut n = *bytes; + n[15] |= 0x80; + n.into() +} + +#[test] +fn counter_incr() { + let mut ctr = Aes128Ctr::new(KEY.into(), &nonce(NONCE1)); + assert_eq!(ctr.get_core().get_block_pos(), 0); + + let mut buffer = [0u8; 64]; + ctr.apply_keystream(&mut buffer); + + // assert_eq!(ctr.current_ctr(), 4); + assert_eq!( + &buffer[..], + &hex!( + "2A0680B210CAD45E886D7EF6DAB357C9F18B39AFF6930FDB2D9FCE34261FF699" + "EB96774669D24B560C9AD028C5C39C4580775A82065256B4787DC91C6942B700" + )[..] + ); +} + +#[test] +fn counter_seek() { + let mut ctr = Aes128Ctr::new(KEY.into(), &nonce(NONCE1)); + ctr.seek(16); + assert_eq!(ctr.get_core().get_block_pos(), 1); + + let mut buffer = [0u8; 64]; + ctr.apply_keystream(&mut buffer); + + assert_eq!(ctr.get_core().get_block_pos(), 5); + assert_eq!( + &buffer[..], + &hex!( + "F18B39AFF6930FDB2D9FCE34261FF699EB96774669D24B560C9AD028C5C39C45" + "80775A82065256B4787DC91C6942B7001564DDA1B07DCED9201AB71BAF06905B" + )[..] + ); +} + +#[test] +fn keystream_xor() { + let mut ctr = Aes128Ctr::new(KEY.into(), &nonce(NONCE1)); + let mut buffer = [1u8; 64]; + + ctr.apply_keystream(&mut buffer); + assert_eq!( + &buffer[..], + &hex!( + "2B0781B311CBD55F896C7FF7DBB256C8F08A38AEF7920EDA2C9ECF35271EF798" + "EA97764768D34A570D9BD129C4C29D4481765B83075357B5797CC81D6843B601" + )[..] + ); +} + +#[test] +fn counter_wrap() { + let mut ctr = Aes128Ctr::new(KEY.into(), &nonce(NONCE2)); + assert_eq!(ctr.get_core().get_block_pos(), 0); + + let mut buffer = [0u8; 64]; + ctr.apply_keystream(&mut buffer); + + assert_eq!(ctr.get_core().get_block_pos(), 4); + assert_eq!( + &buffer[..], + &hex!( + "A1E649D8B382293DC28375C42443BB6A226BAADC9E9CCA8214F56E07A4024E06" + "6355A0DA2E08FB00112FFA38C26189EE55DD5B0B130ED87096FE01B59A665A60" + )[..] + ); +} + +cipher::iv_state_test!( + iv_state, + ctr::CtrCore, + apply_ks, +); diff --git a/ctr/tests/ctr32/mod.rs b/ctr/tests/ctr32/mod.rs new file mode 100644 index 0000000..f9c17d4 --- /dev/null +++ b/ctr/tests/ctr32/mod.rs @@ -0,0 +1,9 @@ +//! Counter Mode with a 32-bit counter. +//! +//! NOTE: AES-128-CTR test vectors used by these tests were generated by first +//! integration testing the implementation in the contexts of AES-GCM and +//! AES-GCM-SIV, with the former tested against the NIST CAVS vectors, and the +//! latter against the RFC8452 test vectors. + +mod be; +mod le; diff --git a/ctr/tests/gost/mod.rs b/ctr/tests/gost/mod.rs new file mode 100644 index 0000000..31e5cec --- /dev/null +++ b/ctr/tests/gost/mod.rs @@ -0,0 +1,55 @@ +use cipher::{KeyIvInit, StreamCipher}; +use hex_literal::hex; + +type MagmaCtr = ctr::Ctr32BE; +type KuznyechikCtr = ctr::Ctr64BE; + +/// Test vectors from GOST R 34.13-2015 (Section A.1.2) +#[test] +fn kuznyechik() { + let key = hex!( + "8899aabbccddeeff0011223344556677" + "fedcba98765432100123456789abcdef" + ); + let nonce = hex!("1234567890abcef00000000000000000"); + let mut pt = hex!( + "1122334455667700ffeeddccbbaa9988" + "00112233445566778899aabbcceeff0a" + "112233445566778899aabbcceeff0a00" + "2233445566778899aabbcceeff0a0011" + ); + let ct = hex!( + "f195d8bec10ed1dbd57b5fa240bda1b8" + "85eee733f6a13e5df33ce4b33c45dee4" + "a5eae88be6356ed3d5e877f13564a3a5" + "cb91fab1f20cbab6d1c6d15820bdba73" + ); + let mut cipher = KuznyechikCtr::new(&key.into(), &nonce.into()); + cipher.apply_keystream(&mut pt); + assert_eq!(pt[..], ct[..]); +} + +/// Test vectors from GOST R 34.13-2015 (Section A.2.2) +#[test] +fn magma() { + let key = hex!( + "ffeeddccbbaa99887766554433221100" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" + ); + let nonce = hex!("1234567800000000"); + let mut pt = hex!( + "92def06b3c130a59" + "db54c704f8189d20" + "4a98fb2e67a8024c" + "8912409b17b57e41" + ); + let ct = hex!( + "4e98110c97b7b93c" + "3e250d93d6e85d69" + "136d868807b2dbef" + "568eb680ab52a12d" + ); + let mut cipher = MagmaCtr::new(&key.into(), &nonce.into()); + cipher.apply_keystream(&mut pt); + assert_eq!(pt[..], ct[..]); +} diff --git a/ctr/tests/mod.rs b/ctr/tests/mod.rs new file mode 100644 index 0000000..0f91835 --- /dev/null +++ b/ctr/tests/mod.rs @@ -0,0 +1,5 @@ +//! Counter Mode Tests + +mod ctr128; +mod ctr32; +mod gost; diff --git a/ige/CHANGELOG.md b/ige/CHANGELOG.md new file mode 100644 index 0000000..b6059ab --- /dev/null +++ b/ige/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 (2022-02-10) +- Initial release ([#2]) + +[#2]: https://github.com/RustCrypto/block-modes/pull/2 diff --git a/ige/Cargo.toml b/ige/Cargo.toml new file mode 100644 index 0000000..121a3bb --- /dev/null +++ b/ige/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "ige" +version = "0.1.0" # Also update html_root_url in lib.rs when bumping this +description = "Infinite Garble Extension (IGE) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.56" +readme = "README.md" +documentation = "https://docs.rs/ige" +repository = "https://github.com/RustCrypto/block-modes" +keywords = ["crypto", "block-mode", "ciphers"] +categories = ["cryptography", "no-std"] + +[dependencies] +cipher = "0.4" + +[dev-dependencies] +aes = "0.8" +cipher = { version = "0.4", features = ["dev"] } +hex-literal = "0.3.3" + +[features] +default = ["block-padding"] +block-padding = ["cipher/block-padding"] +zeroize = ["cipher/zeroize"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/ige/LICENSE-APACHE b/ige/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/ige/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/ige/LICENSE-MIT b/ige/LICENSE-MIT new file mode 100644 index 0000000..d19d409 --- /dev/null +++ b/ige/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2018-2022 RustCrypto Developers +Copyright (c) 2018 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/ige/README.md b/ige/README.md new file mode 100644 index 0000000..04f4281 --- /dev/null +++ b/ige/README.md @@ -0,0 +1,60 @@ +# RustCrypto: IGE + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Infinite Garble Extension][IGE] (IGE) +block cipher mode of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.56** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/ige.svg +[crate-link]: https://crates.io/crates/ige +[docs-image]: https://docs.rs/ige/badge.svg +[docs-link]: https://docs.rs/ige/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/308460-block-modes +[build-image]: https://github.com/RustCrypto/block-modes/workflows/ige/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-modes/actions?query=workflow%3Aige+branch%3Amaster + +[//]: # (general links) + +[CBC]: https://www.links.org/files/openssl-ige.pdf +[cipher-doc]: https://docs.rs/cipher/ diff --git a/ige/benches/aes128.rs b/ige/benches/aes128.rs new file mode 100644 index 0000000..af67406 --- /dev/null +++ b/ige/benches/aes128.rs @@ -0,0 +1,16 @@ +#![feature(test)] +extern crate test; + +use aes::Aes128; + +cipher::block_encryptor_bench!( + KeyIv: ige::Encryptor, + ige_aes128_encrypt_block, + ige_aes128_encrypt_blocks, +); + +cipher::block_decryptor_bench!( + KeyIv: ige::Decryptor, + ige_aes128_decrypt_block, + ige_aes128_decrypt_blocks, +); diff --git a/ige/src/decrypt.rs b/ige/src/decrypt.rs new file mode 100644 index 0000000..4060cf4 --- /dev/null +++ b/ige/src/decrypt.rs @@ -0,0 +1,218 @@ +use crate::{xor, IgeIvSize}; +use cipher::{ + crypto_common::{InnerUser, IvSizeUser}, + generic_array::{sequence::Concat, ArrayLength, GenericArray}, + inout::InOut, + typenum::{Unsigned, U1}, + AlgorithmName, Block, BlockBackend, BlockCipher, BlockClosure, BlockDecryptMut, BlockSizeUser, + InnerIvInit, Iv, IvState, ParBlocksSizeUser, +}; +use core::{fmt, ops::Add}; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// IGE mode decryptor. +#[derive(Clone)] +pub struct Decryptor +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + cipher: C, + x: Block, + y: Block, +} + +impl BlockDecryptMut for Decryptor +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, x, y } = self; + cipher.decrypt_with_backend_mut(Closure { x, y, f }) + } +} + +impl BlockSizeUser for Decryptor +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type BlockSize = C::BlockSize; +} + +impl InnerUser for Decryptor +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type Inner = C; +} + +impl IvSizeUser for Decryptor +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type IvSize = IgeIvSize; +} + +impl InnerIvInit for Decryptor +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + let (y, x) = iv.split_at(C::BlockSize::to_usize()); + Self { + cipher, + x: GenericArray::clone_from_slice(x), + y: GenericArray::clone_from_slice(y), + } + } +} + +impl IvState for Decryptor +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.y.clone().concat(self.x.clone()) + } +} + +impl AlgorithmName for Decryptor +where + C: BlockDecryptMut + BlockCipher + AlgorithmName, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("ige::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Decryptor +where + C: BlockDecryptMut + BlockCipher + AlgorithmName, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("ige::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for Decryptor +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn drop(&mut self) { + self.x.zeroize(); + self.y.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for Decryptor +where + C: BlockDecryptMut + BlockCipher + ZeroizeOnDrop, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ +} + +struct Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + x: &'a mut GenericArray, + y: &'a mut GenericArray, + f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { x, y, f } = self; + f.call(&mut Backend { x, y, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + x: &'a mut GenericArray, + y: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = BS; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = U1; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let new_y = block.clone_in(); + let mut t = new_y.clone(); + xor(&mut t, self.x); + self.backend.proc_block((&mut t).into()); + xor(&mut t, self.y); + *block.get_out() = t.clone(); + *self.x = t; + *self.y = new_y; + } +} diff --git a/ige/src/encrypt.rs b/ige/src/encrypt.rs new file mode 100644 index 0000000..c8fd965 --- /dev/null +++ b/ige/src/encrypt.rs @@ -0,0 +1,218 @@ +use crate::{xor, IgeIvSize}; +use cipher::{ + crypto_common::{InnerUser, IvSizeUser}, + generic_array::{sequence::Concat, ArrayLength, GenericArray}, + inout::InOut, + typenum::{Unsigned, U1}, + AlgorithmName, Block, BlockBackend, BlockCipher, BlockClosure, BlockEncryptMut, BlockSizeUser, + InnerIvInit, Iv, IvState, ParBlocksSizeUser, +}; +use core::{fmt, ops::Add}; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// IGE mode encryptor. +#[derive(Clone)] +pub struct Encryptor +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + cipher: C, + x: Block, + y: Block, +} + +impl BlockEncryptMut for Encryptor +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, x, y } = self; + cipher.encrypt_with_backend_mut(Closure { x, y, f }) + } +} + +impl BlockSizeUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type BlockSize = C::BlockSize; +} + +impl InnerUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type Inner = C; +} + +impl IvSizeUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type IvSize = IgeIvSize; +} + +impl InnerIvInit for Encryptor +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + let (y, x) = iv.split_at(C::BlockSize::to_usize()); + Self { + cipher, + x: GenericArray::clone_from_slice(x), + y: GenericArray::clone_from_slice(y), + } + } +} + +impl IvState for Encryptor +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.y.clone().concat(self.x.clone()) + } +} + +impl AlgorithmName for Encryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("ige::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Encryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("ige::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for Encryptor +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn drop(&mut self) { + self.x.zeroize(); + self.y.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for Encryptor +where + C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ +} + +struct Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + x: &'a mut GenericArray, + y: &'a mut GenericArray, + f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { x, y, f } = self; + f.call(&mut Backend { x, y, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + x: &'a mut GenericArray, + y: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = BS; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = U1; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let new_x = block.clone_in(); + let mut t = new_x.clone(); + xor(&mut t, self.y); + self.backend.proc_block((&mut t).into()); + xor(&mut t, self.x); + *block.get_out() = t.clone(); + *self.x = new_x; + *self.y = t; + } +} diff --git a/ige/src/lib.rs b/ige/src/lib.rs new file mode 100644 index 0000000..b145a05 --- /dev/null +++ b/ige/src/lib.rs @@ -0,0 +1,92 @@ +//! [Infinite Garble Extension][1] (IGE) block cipher mode of operation. +//! +//! +//! +//! +//! Mode functionality is accessed using traits from re-exported [`cipher`] crate. +//! +//! # ⚠️ Security Warning: Hazmat! +//! +//! This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity +//! is not verified, which can lead to serious vulnerabilities! +//! +//! # Example +//! ``` +//! use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; +//! use hex_literal::hex; +//! +//! type Aes128IgeEnc = ige::Encryptor; +//! type Aes128IgeDec = ige::Decryptor; +//! +//! let key = [0x42; 16]; +//! let iv = [0x24; 32]; +//! let plaintext = b"hello world! this is my plaintext."; +//! let ciphertext = hex!( +//! "e3da005add5f05d45899f64891f7629b" +//! "6fcff7d21537fcd3569373d25701a5d1" +//! "d9e586e4c5b8ac09f2190485a76873c2" +//! ); +//! +//! // encrypt/decrypt in-place +//! // buffer must be big enough for padded plaintext +//! let mut buf = vec![0u8; 48]; +//! let pt_len = plaintext.len(); +//! buf[..pt_len].copy_from_slice(&plaintext[..]); +//! let ct = Aes128IgeEnc::new(&key.into(), &iv.into()) +//! .encrypt_padded_mut::(&mut buf, pt_len) +//! .unwrap(); +//! assert_eq!(ct, &ciphertext[..]); +//! +//! let pt = Aes128IgeDec::new(&key.into(), &iv.into()) +//! .decrypt_padded_mut::(&mut buf) +//! .unwrap(); +//! assert_eq!(pt, &plaintext[..]); +//! +//! // encrypt/decrypt from buffer to buffer +//! let mut buf = vec![0u8; 48]; +//! let ct = Aes128IgeEnc::new(&key.into(), &iv.into()) +//! .encrypt_padded_b2b_mut::(&plaintext[..], &mut buf) +//! .unwrap(); +//! assert_eq!(ct, &ciphertext[..]); +//! +//! let mut buf = vec![0u8; 48]; +//! let pt = Aes128IgeDec::new(&key.into(), &iv.into()) +//! .decrypt_padded_b2b_mut::(&ct, &mut buf) +//! .unwrap(); +//! assert_eq!(pt, &plaintext[..]); +//! ``` +//! +//! [1]: https://www.links.org/files/openssl-ige.pdf + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_root_url = "https://docs.rs/ige/0.1.0" +)] +#![forbid(unsafe_code)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs, rust_2018_idioms)] + +mod decrypt; +mod encrypt; + +pub use cipher; +pub use decrypt::Decryptor; +pub use encrypt::Encryptor; + +use cipher::{ + generic_array::{ArrayLength, GenericArray}, + typenum::Sum, + BlockSizeUser, +}; + +type BlockSize = ::BlockSize; +type IgeIvSize = Sum, BlockSize>; + +#[inline(always)] +fn xor>(out: &mut GenericArray, buf: &GenericArray) { + for (a, b) in out.iter_mut().zip(buf) { + *a ^= *b; + } +} diff --git a/ige/tests/aes.rs b/ige/tests/aes.rs new file mode 100644 index 0000000..813617f --- /dev/null +++ b/ige/tests/aes.rs @@ -0,0 +1,12 @@ +use aes::{Aes128, Aes128Dec, Aes128Enc}; +use cipher::{block_mode_dec_test, block_mode_enc_test, iv_state_test}; +use ige::{Decryptor, Encryptor}; + +iv_state_test!(aes128_ige_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes128_ige_dec_iv_state, Decryptor, decrypt); + +// Test vectors from: +block_mode_enc_test!(aes128_cbc_enc_test, "aes128", Encryptor); +block_mode_dec_test!(aes128_cbc_dec_test, "aes128", Decryptor); +block_mode_enc_test!(aes128enc_cbc_enc_test, "aes128", Encryptor); +block_mode_dec_test!(aes128dec_cbc_dec_test, "aes128", Decryptor); diff --git a/ige/tests/data/aes128.blb b/ige/tests/data/aes128.blb new file mode 100644 index 0000000..f5a89c7 Binary files /dev/null and b/ige/tests/data/aes128.blb differ diff --git a/ofb/CHANGELOG.md b/ofb/CHANGELOG.md new file mode 100644 index 0000000..d534cfa --- /dev/null +++ b/ofb/CHANGELOG.md @@ -0,0 +1,52 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.6.0 (2022-02-10) +### Changed +- Update `cipher` dependency to v0.4 and move crate +to the [RustCrypto/block-modes] repository ([#2]) + +[#2]: https://github.com/RustCrypto/block-modes/pull/2 +[RustCrypto/block-modes]: https://github.com/RustCrypto/block-modes + +## 0.5.1 (2021-04-30) +### Changed +- Removed redundant `NewBlockCipher` bound from `FromBlockCipher` implementation ([#236]) + +[#236]: https://github.com/RustCrypto/stream-ciphers/pull/236 + +## 0.5.0 (2021-04-29) +### Changed +- Bump `cipher` dependency to v0.3 release ([#226]) +- Bump `aes` dev dependency to v0.7 release ([#232]) + +[#226]: https://github.com/RustCrypto/stream-ciphers/pull/226 +[#232]: https://github.com/RustCrypto/stream-ciphers/pull/232 + +## 0.4.0 (2020-10-16) +### Changed +- Replace `block-cipher`/`stream-cipher` with `cipher` crate ([#177]) + +[#177]: https://github.com/RustCrypto/stream-ciphers/pull/177 + +## 0.3.0 (2020-08-25) +### Changed +- Bump `stream-cipher` dependency to v0.7, implement the `FromBlockCipher` trait ([#161], [#164]) + +[#161]: https://github.com/RustCrypto/stream-ciphers/pull/161 +[#164]: https://github.com/RustCrypto/stream-ciphers/pull/164 + +## 0.2.0 (2020-06-08) +### Changed +- Bump `stream-cipher` dependency to v0.4 ([#123]) +- Upgrade to Rust 2018 edition ([#123]) + +[#123]: https://github.com/RustCrypto/stream-ciphers/pull/123 + +## 0.1.1 (2019-03-11) + +## 0.1.0 (2018-12-26) diff --git a/ofb/Cargo.toml b/ofb/Cargo.toml new file mode 100644 index 0000000..a7ee724 --- /dev/null +++ b/ofb/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "ofb" +version = "0.6.0" # Also update html_root_url in lib.rs when bumping this +description = "Output Feedback][OFB] (OFB) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.56" +readme = "README.md" +documentation = "https://docs.rs/ofb" +repository = "https://github.com/RustCrypto/block-modes" +keywords = ["crypto", "block-mode", "stream-cipher", "ciphers"] +categories = ["cryptography", "no-std"] + +[dependencies] +cipher = "0.4" + +[dev-dependencies] +aes = "0.8" +cipher = { version = "0.4", features = ["dev"] } +hex-literal = "0.3" + +[features] +block-padding = ["cipher/block-padding"] +zeroize = ["cipher/zeroize"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/ofb/LICENSE-APACHE b/ofb/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/ofb/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/ofb/LICENSE-MIT b/ofb/LICENSE-MIT new file mode 100644 index 0000000..d19d409 --- /dev/null +++ b/ofb/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2018-2022 RustCrypto Developers +Copyright (c) 2018 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/ofb/README.md b/ofb/README.md new file mode 100644 index 0000000..44fa9b7 --- /dev/null +++ b/ofb/README.md @@ -0,0 +1,60 @@ +# RustCrypto: OFB + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Output Feedback][OFB] (OFB) block cipher mode +of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.56** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/ofb.svg +[crate-link]: https://crates.io/crates/ofb +[docs-image]: https://docs.rs/ofb/badge.svg +[docs-link]: https://docs.rs/ofb/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/308460-block-modes +[build-image]: https://github.com/RustCrypto/block-modes/workflows/ofb/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-modes/actions?query=workflow%3Aofb+branch%3Amaster + +[//]: # (general links) + +[OFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB) +[cipher-doc]: https://docs.rs/cipher/ diff --git a/ofb/benches/aes128.rs b/ofb/benches/aes128.rs new file mode 100644 index 0000000..d3f7c70 --- /dev/null +++ b/ofb/benches/aes128.rs @@ -0,0 +1,24 @@ +#![feature(test)] +extern crate test; + +use aes::Aes128; + +cipher::stream_cipher_bench!( + ofb::Ofb; + ofb_aes128_stream_bench1_16b 16; + ofb_aes128_stream_bench2_256b 256; + ofb_aes128_stream_bench3_1kib 1024; + ofb_aes128_stream_bench4_16kib 16384; +); + +cipher::block_encryptor_bench!( + KeyIv: ofb::OfbCore, + ofb_aes128_encrypt_block, + ofb_aes128_encrypt_blocks, +); + +cipher::block_decryptor_bench!( + KeyIv: ofb::OfbCore, + ofb_aes128_decrypt_block, + ofb_aes128_decrypt_blocks, +); diff --git a/ofb/src/backend.rs b/ofb/src/backend.rs new file mode 100644 index 0000000..b5f9566 --- /dev/null +++ b/ofb/src/backend.rs @@ -0,0 +1,114 @@ +use cipher::{ + consts::U1, + generic_array::{ArrayLength, GenericArray}, + inout::InOut, + Block, BlockBackend, BlockClosure, BlockSizeUser, ParBlocksSizeUser, StreamBackend, + StreamClosure, +}; + +pub(crate) struct Closure1<'a, BS, SC> +where + BS: ArrayLength, + SC: StreamClosure, +{ + pub(crate) iv: &'a mut GenericArray, + pub(crate) f: SC, +} + +impl<'a, BS, SC> BlockSizeUser for Closure1<'a, BS, SC> +where + BS: ArrayLength, + SC: StreamClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, SC> BlockClosure for Closure1<'a, BS, SC> +where + BS: ArrayLength, + SC: StreamClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, backend }); + } +} + +pub(crate) struct Closure2<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + pub(crate) iv: &'a mut GenericArray, + pub(crate) f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure2<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure2<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + iv: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = BS; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = U1; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + self.backend.proc_block(self.iv.into()); + block.xor_in2out(self.iv); + } +} + +impl<'a, BS, BK> StreamBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn gen_ks_block(&mut self, block: &mut Block) { + self.backend.proc_block(self.iv.into()); + *block = self.iv.clone(); + } +} diff --git a/ofb/src/lib.rs b/ofb/src/lib.rs new file mode 100644 index 0000000..fe9a6e2 --- /dev/null +++ b/ofb/src/lib.rs @@ -0,0 +1,207 @@ +//! [Output feedback][1] (OFB) mode. +//! +//! +//! +//! +//! Mode functionality is accessed using traits from re-exported [`cipher`] crate. +//! +//! # ⚠️ Security Warning: Hazmat! +//! +//! This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity +//! is not verified, which can lead to serious vulnerabilities! +//! +//! # Example +//! ``` +//! use aes::cipher::{KeyIvInit, StreamCipher}; +//! use hex_literal::hex; +//! +//! type Aes128Ofb = ofb::Ofb; +//! +//! let key = [0x42; 16]; +//! let iv = [0x24; 16]; +//! let plaintext = b"hello world! this is my plaintext."; +//! let ciphertext = hex!( +//! "3357121ebb5a29468bd861467596ce3dc6ba5df50e536a2443b8ee16c2f7cd0869c9" +//! ); +//! +//! // encrypt in-place +//! let mut buf = plaintext.to_vec(); +//! let mut cipher = Aes128Ofb::new(&key.into(), &iv.into()); +//! cipher.apply_keystream(&mut buf); +//! assert_eq!(buf, &ciphertext[..]); +//! +//! // OFB mode can be used with streaming messages +//! let mut cipher = Aes128Ofb::new(&key.into(), &iv.into()); +//! for chunk in buf.chunks_mut(3) { +//! cipher.apply_keystream(chunk); +//! } +//! assert_eq!(buf, &plaintext[..]); +//! +//! // encrypt/decrypt from buffer to buffer +//! // buffer length must be equal to input length +//! let mut buf1 = vec![0u8; 34]; +//! let mut cipher = Aes128Ofb::new(&key.into(), &iv.into()); +//! cipher +//! .apply_keystream_b2b(&plaintext[..], &mut buf1) +//! .unwrap(); +//! assert_eq!(buf1, &ciphertext[..]); +//! +//! let mut buf2 = vec![0u8; 34]; +//! let mut cipher = Aes128Ofb::new(&key.into(), &iv.into()); +//! cipher.apply_keystream_b2b(&buf1, &mut buf2).unwrap(); +//! assert_eq!(buf2, &plaintext[..]); +//! ``` +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB) + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_root_url = "https://docs.rs/ofb/0.6.0" +)] +#![forbid(unsafe_code)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs, rust_2018_idioms)] + +pub use cipher; + +mod backend; + +use cipher::{ + crypto_common::{InnerUser, IvSizeUser}, + AlgorithmName, Block, BlockCipher, BlockClosure, BlockDecryptMut, BlockEncryptMut, + BlockSizeUser, InnerIvInit, Iv, IvState, StreamCipherCore, StreamCipherCoreWrapper, + StreamClosure, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Buffered Output feedback (OFB) mode. +pub type Ofb = StreamCipherCoreWrapper>; + +/// Output feedback (OFB) mode. +#[derive(Clone)] +pub struct OfbCore +where + C: BlockEncryptMut + BlockCipher, +{ + cipher: C, + iv: Block, +} + +impl BlockSizeUser for OfbCore +where + C: BlockEncryptMut + BlockCipher, +{ + type BlockSize = C::BlockSize; +} + +impl InnerUser for OfbCore +where + C: BlockEncryptMut + BlockCipher, +{ + type Inner = C; +} + +impl IvSizeUser for OfbCore +where + C: BlockEncryptMut + BlockCipher, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for OfbCore +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for OfbCore +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.iv.clone() + } +} + +impl StreamCipherCore for OfbCore +where + C: BlockEncryptMut + BlockCipher, +{ + fn remaining_blocks(&self) -> Option { + None + } + + fn process_with_backend(&mut self, f: impl StreamClosure) { + let Self { cipher, iv } = self; + cipher.encrypt_with_backend_mut(backend::Closure1 { iv, f }); + } +} + +impl BlockEncryptMut for OfbCore +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, iv } = self; + cipher.encrypt_with_backend_mut(backend::Closure2 { iv, f }) + } +} + +impl BlockDecryptMut for OfbCore +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, iv } = self; + cipher.encrypt_with_backend_mut(backend::Closure2 { iv, f }) + } +} + +impl AlgorithmName for OfbCore +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Ofb<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for OfbCore +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("OfbCore<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for OfbCore { + fn drop(&mut self) { + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for OfbCore {} diff --git a/ofb/tests/aes.rs b/ofb/tests/aes.rs new file mode 100644 index 0000000..74fd5ce --- /dev/null +++ b/ofb/tests/aes.rs @@ -0,0 +1,34 @@ +use aes::*; +use cipher::{block_mode_dec_test, block_mode_enc_test, iv_state_test, stream_cipher_test}; +use ofb::{Ofb, OfbCore}; + +iv_state_test!(aes128_ofb_enc_iv_state, OfbCore, encrypt); +iv_state_test!(aes128_ofb_dec_iv_state, OfbCore, decrypt); +iv_state_test!(aes128_ofb_apply_ks_iv_state, OfbCore, apply_ks); +iv_state_test!(aes192_ofb_enc_iv_state, OfbCore, encrypt); +iv_state_test!(aes192_ofb_dec_iv_state, OfbCore, decrypt); +iv_state_test!(aes192_ofb_apply_ks_iv_state, OfbCore, apply_ks); +iv_state_test!(aes256_ofb_enc_iv_state, OfbCore, encrypt); +iv_state_test!(aes256_ofb_dec_iv_state, OfbCore, decrypt); +iv_state_test!(aes256_ofb_apply_ks_iv_state, OfbCore, apply_ks); + +// Test vectors from CVAP "AES Multiblock Message Test (MMT) Sample Vectors": +// +block_mode_enc_test!(aes128_ofb_enc_test, "aes128", OfbCore); +block_mode_dec_test!(aes128_ofb_dec_test, "aes128", OfbCore); +block_mode_enc_test!(aes128enc_ofb_enc_test, "aes128", OfbCore); +block_mode_dec_test!(aes128dec_ofb_dec_test, "aes128", OfbCore); +stream_cipher_test!(aes128_ofb_stream_test, "aes128", Ofb); +stream_cipher_test!(aes128enc_ofb_stream_test, "aes128", Ofb); +block_mode_enc_test!(aes192_ofb_enc_test, "aes192", OfbCore); +block_mode_dec_test!(aes192_ofb_dec_test, "aes192", OfbCore); +block_mode_enc_test!(aes192enc_ofb_enc_test, "aes192", OfbCore); +block_mode_dec_test!(aes192dec_ofb_dec_test, "aes192", OfbCore); +stream_cipher_test!(aes192_ofb_stream_test, "aes192", Ofb); +stream_cipher_test!(aes192enc_ofb_stream_test, "aes192", Ofb); +block_mode_enc_test!(aes256_ofb_enc_test, "aes256", OfbCore); +block_mode_dec_test!(aes256_ofb_dec_test, "aes256", OfbCore); +block_mode_enc_test!(aes256enc_ofb_enc_test, "aes256", OfbCore); +block_mode_dec_test!(aes256dec_ofb_dec_test, "aes256", OfbCore); +stream_cipher_test!(aes256_ofb_stream_test, "aes256", Ofb); +stream_cipher_test!(aes256enc_ofb_stream_test, "aes256", Ofb); diff --git a/ofb/tests/data/aes128.blb b/ofb/tests/data/aes128.blb new file mode 100644 index 0000000..c5d1f41 Binary files /dev/null and b/ofb/tests/data/aes128.blb differ diff --git a/ofb/tests/data/aes192.blb b/ofb/tests/data/aes192.blb new file mode 100644 index 0000000..24fb0de Binary files /dev/null and b/ofb/tests/data/aes192.blb differ diff --git a/ofb/tests/data/aes256.blb b/ofb/tests/data/aes256.blb new file mode 100644 index 0000000..8940b5f Binary files /dev/null and b/ofb/tests/data/aes256.blb differ diff --git a/pcbc/CHANGELOG.md b/pcbc/CHANGELOG.md new file mode 100644 index 0000000..b6059ab --- /dev/null +++ b/pcbc/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 (2022-02-10) +- Initial release ([#2]) + +[#2]: https://github.com/RustCrypto/block-modes/pull/2 diff --git a/pcbc/Cargo.toml b/pcbc/Cargo.toml new file mode 100644 index 0000000..35161c1 --- /dev/null +++ b/pcbc/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "pcbc" +version = "0.1.0" # Also update html_root_url in lib.rs when bumping this +description = "Propagating Cipher Block Chaining (PCBC) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.56" +readme = "README.md" +documentation = "https://docs.rs/pcbc" +repository = "https://github.com/RustCrypto/block-ciphers" +keywords = ["crypto", "block-mode", "ciphers"] +categories = ["cryptography", "no-std"] + +[dependencies] +cipher = "0.4" + +[dev-dependencies] +aes = "0.8" +cipher = { version = "0.4", features = ["dev"] } +hex-literal = "0.3.3" + +[features] +default = ["block-padding"] +block-padding = ["cipher/block-padding"] +zeroize = ["cipher/zeroize"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/pcbc/LICENSE-APACHE b/pcbc/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/pcbc/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/pcbc/LICENSE-MIT b/pcbc/LICENSE-MIT new file mode 100644 index 0000000..d19d409 --- /dev/null +++ b/pcbc/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2018-2022 RustCrypto Developers +Copyright (c) 2018 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/pcbc/README.md b/pcbc/README.md new file mode 100644 index 0000000..ab64cf1 --- /dev/null +++ b/pcbc/README.md @@ -0,0 +1,60 @@ +# RustCrypto: PCBC + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Propagating Cipher Block Chaining][PCBC] (PCBC) +block cipher mode of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.56** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/pcbc.svg +[crate-link]: https://crates.io/crates/pcbc +[docs-image]: https://docs.rs/pcbc/badge.svg +[docs-link]: https://docs.rs/pcbc/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/308460-block-modes +[build-image]: https://github.com/RustCrypto/block-modes/workflows/pcbc/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-modes/actions?query=workflow%3Apcbc+branch%3Amaster + +[//]: # (general links) + +[PCBC]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Propagating_cipher_block_chaining_(PCBC) +[cipher-doc]: https://docs.rs/cipher/ diff --git a/pcbc/benches/aes128.rs b/pcbc/benches/aes128.rs new file mode 100644 index 0000000..5abeae5 --- /dev/null +++ b/pcbc/benches/aes128.rs @@ -0,0 +1,16 @@ +#![feature(test)] +extern crate test; + +use aes::Aes128; + +cipher::block_encryptor_bench!( + KeyIv: pcbc::Encryptor, + pcbc_aes128_encrypt_block, + pcbc_aes128_encrypt_blocks, +); + +cipher::block_decryptor_bench!( + KeyIv: pcbc::Decryptor, + pcbc_aes128_decrypt_block, + pcbc_aes128_decrypt_blocks, +); diff --git a/pcbc/src/decrypt.rs b/pcbc/src/decrypt.rs new file mode 100644 index 0000000..0abc781 --- /dev/null +++ b/pcbc/src/decrypt.rs @@ -0,0 +1,181 @@ +use crate::xor; +use cipher::{ + consts::U1, + crypto_common::{InnerUser, IvSizeUser}, + generic_array::{ArrayLength, GenericArray}, + inout::InOut, + AlgorithmName, Block, BlockBackend, BlockCipher, BlockClosure, BlockDecryptMut, BlockSizeUser, + InnerIvInit, Iv, IvState, ParBlocksSizeUser, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// PCBC mode decryptor. +#[derive(Clone)] +pub struct Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + cipher: C, + iv: Block, +} + +impl BlockSizeUser for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + type BlockSize = C::BlockSize; +} + +impl BlockDecryptMut for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, iv } = self; + cipher.decrypt_with_backend_mut(Closure { iv, f }) + } +} + +impl InnerUser for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + type Inner = C; +} + +impl IvSizeUser for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Decryptor +where + C: BlockDecryptMut + BlockCipher, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.iv.clone() + } +} + +impl AlgorithmName for Decryptor +where + C: BlockDecryptMut + BlockCipher + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("pcbc::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Decryptor +where + C: BlockDecryptMut + BlockCipher + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("pcbc::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for Decryptor { + fn drop(&mut self) { + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for Decryptor {} + +struct Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + iv: &'a mut GenericArray, + f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + iv: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = BS; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = U1; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let t = self.iv.clone(); + *self.iv = block.clone_in(); + self.backend.proc_block(block.reborrow()); + let res = block.get_out(); + xor(res, &t); + xor(self.iv, res); + } +} diff --git a/pcbc/src/encrypt.rs b/pcbc/src/encrypt.rs new file mode 100644 index 0000000..c54dc4c --- /dev/null +++ b/pcbc/src/encrypt.rs @@ -0,0 +1,181 @@ +use crate::xor; +use cipher::{ + consts::U1, + crypto_common::{InnerUser, IvSizeUser}, + generic_array::{ArrayLength, GenericArray}, + inout::InOut, + AlgorithmName, Block, BlockBackend, BlockCipher, BlockClosure, BlockEncryptMut, BlockSizeUser, + InnerIvInit, Iv, IvState, ParBlocksSizeUser, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// PCBC mode encryptor. +#[derive(Clone)] +pub struct Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + cipher: C, + iv: Block, +} + +impl BlockSizeUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type BlockSize = C::BlockSize; +} + +impl BlockEncryptMut for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + let Self { cipher, iv } = self; + cipher.encrypt_with_backend_mut(Closure { iv, f }) + } +} + +impl InnerUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type Inner = C; +} + +impl IvSizeUser for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Encryptor +where + C: BlockEncryptMut + BlockCipher, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.iv.clone() + } +} + +impl AlgorithmName for Encryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("pcbc::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Encryptor +where + C: BlockEncryptMut + BlockCipher + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("pcbc::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl Drop for Encryptor { + fn drop(&mut self) { + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] +impl ZeroizeOnDrop for Encryptor {} + +struct Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + iv: &'a mut GenericArray, + f: BC, +} + +impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + type BlockSize = BS; +} + +impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC> +where + BS: ArrayLength, + BC: BlockClosure, +{ + #[inline(always)] + fn call>(self, backend: &mut B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, backend }); + } +} + +struct Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + iv: &'a mut GenericArray, + backend: &'a mut BK, +} + +impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type BlockSize = BS; +} + +impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + type ParBlocksSize = U1; +} + +impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK> +where + BS: ArrayLength, + BK: BlockBackend, +{ + #[inline(always)] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let mut t = block.clone_in(); + xor(&mut t, self.iv); + *self.iv = block.clone_in(); + let b = (&t, block.get_out()).into(); + self.backend.proc_block(b); + xor(self.iv, block.get_out()); + } +} diff --git a/pcbc/src/lib.rs b/pcbc/src/lib.rs new file mode 100644 index 0000000..de6829d --- /dev/null +++ b/pcbc/src/lib.rs @@ -0,0 +1,85 @@ +//! [Propagating Cipher Block Chaining][1] (PCBC) mode. +//! +//! +//! +//! +//! Mode functionality is accessed using traits from re-exported [`cipher`] crate. +//! +//! # ⚠️ Security Warning: Hazmat! +//! +//! This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity +//! is not verified, which can lead to serious vulnerabilities! +//! +//! # Example +//! ``` +//! use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; +//! use hex_literal::hex; +//! +//! type Aes128PcbcEnc = pcbc::Encryptor; +//! type Aes128PcbcDec = pcbc::Decryptor; +//! +//! let key = [0x42; 16]; +//! let iv = [0x24; 16]; +//! let plaintext = b"hello world! this is my plaintext."; +//! let ciphertext = hex!( +//! "c7fe247ef97b21f07cbdd26cb5d346bf" +//! "ab13156d0b2f05f91c4837db5157bad5" +//! "62cb0b6fa7816e254a2fc8d852fb4315" +//! ); +//! +//! // encrypt/decrypt in-place +//! // buffer must be big enough for padded plaintext +//! let mut buf = vec![0u8; 48]; +//! let pt_len = plaintext.len(); +//! buf[..pt_len].copy_from_slice(&plaintext[..]); +//! let ct = Aes128PcbcEnc::new(&key.into(), &iv.into()) +//! .encrypt_padded_mut::(&mut buf, pt_len) +//! .unwrap(); +//! assert_eq!(ct, &ciphertext[..]); +//! +//! let pt = Aes128PcbcDec::new(&key.into(), &iv.into()) +//! .decrypt_padded_mut::(&mut buf) +//! .unwrap(); +//! assert_eq!(pt, &plaintext[..]); +//! +//! // encrypt/decrypt from buffer to buffer +//! let mut buf = vec![0u8; 48]; +//! let ct = Aes128PcbcEnc::new(&key.into(), &iv.into()) +//! .encrypt_padded_b2b_mut::(&plaintext[..], &mut buf) +//! .unwrap(); +//! assert_eq!(ct, &ciphertext[..]); +//! +//! let mut buf = vec![0u8; 48]; +//! let pt = Aes128PcbcDec::new(&key.into(), &iv.into()) +//! .decrypt_padded_b2b_mut::(&ct, &mut buf) +//! .unwrap(); +//! assert_eq!(pt, &plaintext[..]); +//! ``` +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Propagating_cipher_block_chaining_(PCBC) + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", + html_root_url = "https://docs.rs/pcbc/0.1.0" +)] +#![forbid(unsafe_code)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs, rust_2018_idioms)] + +mod decrypt; +mod encrypt; + +pub use cipher; +pub use decrypt::Decryptor; +pub use encrypt::Encryptor; + +use cipher::generic_array::{ArrayLength, GenericArray}; + +#[inline(always)] +fn xor>(out: &mut GenericArray, buf: &GenericArray) { + for (a, b) in out.iter_mut().zip(buf) { + *a ^= *b; + } +} diff --git a/pcbc/tests/aes.rs b/pcbc/tests/aes.rs new file mode 100644 index 0000000..5a18387 --- /dev/null +++ b/pcbc/tests/aes.rs @@ -0,0 +1,12 @@ +use aes::{Aes128, Aes128Dec, Aes128Enc}; +use cipher::{block_mode_dec_test, block_mode_enc_test, iv_state_test}; +use pcbc::{Decryptor, Encryptor}; + +iv_state_test!(aes128_pcbc_enc_iv_state, Encryptor, encrypt); +iv_state_test!(aes128_pcbc_dec_iv_state, Decryptor, decrypt); + +// The test vectors are generated using this implementation. +block_mode_enc_test!(aes128_pcbc_enc_test, "aes128", Encryptor); +block_mode_dec_test!(aes128_pcbc_dec_test, "aes128", Decryptor); +block_mode_enc_test!(aes128enc_pcbc_enc_test, "aes128", Encryptor); +block_mode_dec_test!(aes128dec_pcbc_dec_test, "aes128", Decryptor); diff --git a/pcbc/tests/data/aes128.blb b/pcbc/tests/data/aes128.blb new file mode 100644 index 0000000..4eedb92 Binary files /dev/null and b/pcbc/tests/data/aes128.blb differ