Repository: RustCrypto/block-modes (language: rust)

Issue:
block-modes: add opaque Debug implementations
Depends on RustCrypto/utils#79


Discussion / hints:
@newpavlov is this done?
Not yet, I have replaced unpopulated enums with ZST structs in `block-padding`. I think it should be a backwards-compatible change, but to be extra-safe I wanted to cut a breaking release.

Fix patch:
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.
+
+<img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cbc_enc.svg" width="50%"><img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cbc_dec.svg" width="50%">
+
+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<Aes128>,
+    cbc_aes128_encrypt_block,
+    cbc_aes128_encrypt_blocks,
+);
+
+cipher::block_decryptor_bench!(
+    KeyIv: cbc::Decryptor<Aes128>,
+    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<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    cipher: C,
+    iv: Block<C>,
+}
+
+impl<C> BlockSizeUser for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    type BlockSize = C::BlockSize;
+}
+
+impl<C> BlockDecryptMut for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.decrypt_with_backend_mut(Closure { iv, f })
+    }
+}
+
+impl<C> InnerUser for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    type IvSize = C::BlockSize;
+}
+
+impl<C> InnerIvInit for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    #[inline]
+    fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> Self {
+        Self {
+            cipher,
+            iv: iv.clone(),
+        }
+    }
+}
+
+impl<C> IvState for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        self.iv.clone()
+    }
+}
+
+impl<C> AlgorithmName for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher + AlgorithmName,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cbc::Decryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher + AlgorithmName,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cbc::Decryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockDecryptMut + BlockCipher> Drop for Decryptor<C> {
+    fn drop(&mut self) {
+        self.iv.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockDecryptMut + BlockCipher + ZeroizeOnDrop> ZeroizeOnDrop for Decryptor<C> {}
+
+struct Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
+        let Self { iv, f } = self;
+        f.call(&mut Backend { iv, backend });
+    }
+}
+
+struct Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = BK::ParBlocksSize;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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<Self>>) {
+        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<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    cipher: C,
+    iv: Block<C>,
+}
+
+impl<C> BlockSizeUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type BlockSize = C::BlockSize;
+}
+
+impl<C> BlockEncryptMut for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.encrypt_with_backend_mut(Closure { iv, f })
+    }
+}
+
+impl<C> InnerUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type IvSize = C::BlockSize;
+}
+
+impl<C> InnerIvInit for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> Self {
+        Self {
+            cipher,
+            iv: iv.clone(),
+        }
+    }
+}
+
+impl<C> IvState for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        self.iv.clone()
+    }
+}
+
+impl<C> AlgorithmName for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cbc::Encryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cbc::Encryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher> Drop for Encryptor<C> {
+    fn drop(&mut self) {
+        self.iv.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop> ZeroizeOnDrop for Encryptor<C> {}
+
+struct Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
+        let Self { iv, f } = self;
+        f.call(&mut Backend { iv, backend });
+    }
+}
+
+struct Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = U1;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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.
+//!
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cbc_enc.svg" width="49%" />
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cbc_dec.svg" width="49%"/>
+//!
+//! 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<aes::Aes128>;
+//! type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
+//!
+//! 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::<Pkcs7>(&mut buf, pt_len)
+//!     .unwrap();
+//! assert_eq!(ct, &ciphertext[..]);
+//!
+//! let pt = Aes128CbcDec::new(&key.into(), &iv.into())
+//!     .decrypt_padded_mut::<Pkcs7>(&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::<Pkcs7>(&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::<Pkcs7>(&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<N: ArrayLength<u8>>(out: &mut GenericArray<u8, N>, buf: &GenericArray<u8, N>) {
+    for (a, b) in out.iter_mut().zip(buf) {
+        *a ^= *b;
+    }
+}
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.
+
+<img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cfb_enc.svg" width="50%"><img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cfb_dec.svg" width="50%">
+
+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<Aes128>,
+    cfb_aes128_encrypt_block,
+    cfb_aes128_encrypt_blocks,
+);
+
+cipher::block_decryptor_bench!(
+    KeyIv: cfb_mode::Decryptor<Aes128>,
+    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<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    cipher: C,
+    iv: Block<C>,
+}
+
+impl<C> BlockSizeUser for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type BlockSize = C::BlockSize;
+}
+
+impl<C> BlockDecryptMut for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.encrypt_with_backend_mut(Closure { iv, f })
+    }
+}
+
+impl<C> AsyncStreamCipher for Decryptor<C> where C: BlockEncryptMut + BlockCipher {}
+
+impl<C> InnerUser for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type IvSize = C::BlockSize;
+}
+
+impl<C> InnerIvInit for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn inner_iv_init(mut cipher: C, iv: &Iv<Self>) -> Self {
+        let mut iv = iv.clone();
+        cipher.encrypt_block_mut(&mut iv);
+        Self { cipher, iv }
+    }
+}
+
+impl<C> IvState for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockDecrypt + BlockCipher,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        let mut res = self.iv.clone();
+        self.cipher.decrypt_block(&mut res);
+        res
+    }
+}
+
+impl<C> AlgorithmName for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cfb::Decryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cfb::Decryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher> Drop for Decryptor<C> {
+    fn drop(&mut self) {
+        self.iv.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop> ZeroizeOnDrop for Decryptor<C> {}
+
+struct Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
+        let Self { iv, f } = self;
+        f.call(&mut Backend { iv, backend });
+    }
+}
+
+struct Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = BK::ParBlocksSize;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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<Self>>) {
+        let mut t = ParBlocks::<Self>::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<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    cipher: C,
+    iv: Block<C>,
+}
+
+impl<C> BlockSizeUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type BlockSize = C::BlockSize;
+}
+
+impl<C> BlockEncryptMut for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.encrypt_with_backend_mut(Closure { iv, f })
+    }
+}
+
+impl<C> AsyncStreamCipher for Encryptor<C> where C: BlockEncryptMut + BlockCipher {}
+
+impl<C> InnerUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type IvSize = C::BlockSize;
+}
+
+impl<C> InnerIvInit for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn inner_iv_init(mut cipher: C, iv: &Iv<Self>) -> Self {
+        let mut iv = iv.clone();
+        cipher.encrypt_block_mut(&mut iv);
+        Self { cipher, iv }
+    }
+}
+
+impl<C> IvState for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockDecrypt + BlockCipher,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        let mut res = self.iv.clone();
+        self.cipher.decrypt_block(&mut res);
+        res
+    }
+}
+
+impl<C> AlgorithmName for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cfb::Encryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cfb::Encryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher> Drop for Encryptor<C> {
+    fn drop(&mut self) {
+        self.iv.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop> ZeroizeOnDrop for Encryptor<C> {}
+
+struct Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
+        let Self { iv, f } = self;
+        f.call(&mut Backend { iv, backend });
+    }
+}
+
+struct Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = U1;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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.
+//!
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cfb_enc.svg" width="49%" />
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cfb_dec.svg" width="49%"/>
+//!
+//! 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<aes::Aes128>;
+//! type Aes128CfbDec = cfb_mode::Decryptor<aes::Aes128>;
+//!
+//! 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/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.
+
+<img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cfb_enc.svg" width="50%"><img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cfb_dec.svg" width="50%">
+
+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<Aes128>,
+    cfb8_aes128_encrypt_block,
+    cfb8_aes128_encrypt_blocks,
+);
+
+cipher::block_decryptor_bench!(
+    KeyIv: cfb8::Decryptor<Aes128>,
+    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<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    cipher: C,
+    iv: Block<C>,
+}
+
+impl<C> BlockSizeUser for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type BlockSize = U1;
+}
+
+impl<C> BlockDecryptMut for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.encrypt_with_backend_mut(Closure { iv, f })
+    }
+}
+
+impl<C: BlockEncryptMut + BlockCipher> AsyncStreamCipher for Decryptor<C> {}
+
+impl<C> InnerUser for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type IvSize = C::BlockSize;
+}
+
+impl<C> InnerIvInit for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> Self {
+        let iv = iv.clone();
+        Self { cipher, iv }
+    }
+}
+
+impl<C> IvState for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockDecryptMut + BlockCipher,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        self.iv.clone()
+    }
+}
+
+impl<C> AlgorithmName for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cfb8::Decryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for Decryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cfb8::Decryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher> Drop for Decryptor<C> {
+    fn drop(&mut self) {
+        self.iv.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop> ZeroizeOnDrop for Decryptor<C> {}
+
+struct Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = U1>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = U1>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = U1>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
+        let Self { iv, f } = self;
+        f.call(&mut Backend { iv, backend });
+    }
+}
+
+struct Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = U1;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = U1;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    cipher: C,
+    iv: Block<C>,
+}
+
+impl<C> BlockSizeUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type BlockSize = U1;
+}
+
+impl<C> BlockEncryptMut for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.encrypt_with_backend_mut(Closure { iv, f })
+    }
+}
+
+impl<C: BlockEncryptMut + BlockCipher> AsyncStreamCipher for Encryptor<C> {}
+
+impl<C> InnerUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type IvSize = C::BlockSize;
+}
+
+impl<C> InnerIvInit for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> Self {
+        let iv = iv.clone();
+        Self { cipher, iv }
+    }
+}
+
+impl<C> IvState for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        self.iv.clone()
+    }
+}
+
+impl<C> AlgorithmName for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cfb8::Encryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("cfb8::Encryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher> Drop for Encryptor<C> {
+    fn drop(&mut self) {
+        self.iv.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop> ZeroizeOnDrop for Encryptor<C> {}
+
+struct Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = U1>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = U1>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = U1>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
+        let Self { iv, f } = self;
+        f.call(&mut Backend { iv, backend });
+    }
+}
+
+struct Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = U1;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = U1;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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.
+//!
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cfb_enc.svg" width="49%" />
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/cfb_dec.svg" width="49%"/>
+//!
+//! 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<aes::Aes128>;
+//! type Aes128Cfb8Dec = cfb8::Decryptor<aes::Aes128>;
+//!
+//! 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/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.
+
+<img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ctr_enc.svg" width="50%"><img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ctr_dec.svg" width="50%">
+
+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<aes::Aes128>;
+    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<aes::Aes128>;
+    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<aes::Aes128>;
+    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::BlockSize>,
+    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::BlockSize>,
+    B: BlockBackend,
+{
+    type BlockSize = B::BlockSize;
+}
+
+impl<'a, F, B> ParBlocksSizeUser for Backend<'a, F, B>
+where
+    F: CtrFlavor<B::BlockSize>,
+    B: BlockBackend,
+{
+    type ParBlocksSize = B::ParBlocksSize;
+}
+
+impl<'a, F, B> StreamBackend for Backend<'a, F, B>
+where
+    F: CtrFlavor<B::BlockSize>,
+    B: BlockBackend,
+{
+    #[inline(always)]
+    fn gen_ks_block(&mut self, block: &mut Block<Self>) {
+        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<Self>) {
+        let mut tmp = ParBlocks::<Self>::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>,
+    BS: ArrayLength<u8>,
+    SC: StreamClosure<BlockSize = BS>,
+{
+    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>,
+    BS: ArrayLength<u8>,
+    SC: StreamClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, F, BS, SC> BlockClosure for Closure<'a, F, BS, SC>
+where
+    F: CtrFlavor<BS>,
+    BS: ArrayLength<u8>,
+    SC: StreamClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = BS>>(self, backend: &mut B) {
+        let Self { ctr_nonce, f } = self;
+        f.call(&mut Backend::<F, B> { 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<C, F>
+where
+    C: BlockEncryptMut + BlockCipher,
+    F: CtrFlavor<C::BlockSize>,
+{
+    cipher: C,
+    ctr_nonce: F::CtrNonce,
+}
+
+impl<C, F> BlockSizeUser for CtrCore<C, F>
+where
+    C: BlockEncryptMut + BlockCipher,
+    F: CtrFlavor<C::BlockSize>,
+{
+    type BlockSize = C::BlockSize;
+}
+
+impl<C, F> StreamCipherCore for CtrCore<C, F>
+where
+    C: BlockEncryptMut + BlockCipher,
+    F: CtrFlavor<C::BlockSize>,
+{
+    #[inline]
+    fn remaining_blocks(&self) -> Option<usize> {
+        F::remaining(&self.ctr_nonce)
+    }
+
+    #[inline]
+    fn process_with_backend(&mut self, f: impl StreamClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, ctr_nonce } = self;
+        cipher.encrypt_with_backend_mut(Closure::<F, _, _> { ctr_nonce, f });
+    }
+}
+
+impl<C, F> StreamCipherSeekCore for CtrCore<C, F>
+where
+    C: BlockEncryptMut + BlockCipher,
+    F: CtrFlavor<C::BlockSize>,
+{
+    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<C, F> InnerUser for CtrCore<C, F>
+where
+    C: BlockEncryptMut + BlockCipher,
+    F: CtrFlavor<C::BlockSize>,
+{
+    type Inner = C;
+}
+
+impl<C, F> IvSizeUser for CtrCore<C, F>
+where
+    C: BlockEncryptMut + BlockCipher,
+    F: CtrFlavor<C::BlockSize>,
+{
+    type IvSize = C::BlockSize;
+}
+
+impl<C, F> InnerIvInit for CtrCore<C, F>
+where
+    C: BlockEncryptMut + BlockCipher,
+    F: CtrFlavor<C::BlockSize>,
+{
+    #[inline]
+    fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> Self {
+        Self {
+            cipher,
+            ctr_nonce: F::from_nonce(iv),
+        }
+    }
+}
+
+impl<C, F> IvState for CtrCore<C, F>
+where
+    C: BlockEncryptMut + BlockCipher,
+    F: CtrFlavor<C::BlockSize>,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        F::current_block(&self.ctr_nonce)
+    }
+}
+
+impl<C, F> AlgorithmName for CtrCore<C, F>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+    F: CtrFlavor<C::BlockSize>,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Ctr")?;
+        f.write_str(F::NAME)?;
+        f.write_str("<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C, F> fmt::Debug for CtrCore<C, F>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+    F: CtrFlavor<C::BlockSize>,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Ctr")?;
+        f.write_str(F::NAME)?;
+        f.write_str("<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C, F> ZeroizeOnDrop for CtrCore<C, F>
+where
+    C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop,
+    F: CtrFlavor<C::BlockSize>,
+    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<B: ArrayLength<u8>> {
+    /// 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<usize>;
+
+    /// Generate block for given `nonce` and current counter value.
+    fn next_block(cn: &mut Self::CtrNonce) -> GenericArray<u8, B>;
+
+    /// Generate block for given `nonce` and current counter value.
+    fn current_block(cn: &Self::CtrNonce) -> GenericArray<u8, B>;
+
+    /// Initialize from bytes.
+    fn from_nonce(block: &GenericArray<u8, B>) -> 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<B> = PartialQuot<B, ChunkSize>;
+const CS: usize = ChunkSize::USIZE;
+
+#[derive(Clone)]
+pub struct CtrNonce128<N: ArrayLength<u128>> {
+    ctr: u128,
+    nonce: GenericArray<u128, N>,
+}
+
+#[cfg(feature = "zeroize")]
+impl<N: ArrayLength<u128>> Drop for CtrNonce128<N> {
+    fn drop(&mut self) {
+        self.ctr.zeroize();
+        self.nonce.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl<N: ArrayLength<u128>> ZeroizeOnDrop for CtrNonce128<N> {}
+
+/// 128-bit big endian counter flavor.
+pub enum Ctr128BE {}
+
+impl<B> CtrFlavor<B> for Ctr128BE
+where
+    B: ArrayLength<u8> + PartialDiv<ChunkSize>,
+    Chunks<B>: ArrayLength<u128>,
+{
+    type CtrNonce = CtrNonce128<Chunks<B>>;
+    type Backend = u128;
+    const NAME: &'static str = "128BE";
+
+    #[inline]
+    fn remaining(cn: &Self::CtrNonce) -> Option<usize> {
+        (core::u128::MAX - cn.ctr).try_into().ok()
+    }
+
+    #[inline(always)]
+    fn current_block(cn: &Self::CtrNonce) -> GenericArray<u8, B> {
+        let mut block = GenericArray::<u8, B>::default();
+        for i in 0..Chunks::<B>::USIZE {
+            let t = if i == Chunks::<B>::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<u8, B> {
+        let block = Self::current_block(cn);
+        cn.ctr = cn.ctr.wrapping_add(1);
+        block
+    }
+
+    #[inline]
+    fn from_nonce(block: &GenericArray<u8, B>) -> Self::CtrNonce {
+        let mut nonce = GenericArray::<u128, Chunks<B>>::default();
+        for i in 0..Chunks::<B>::USIZE {
+            let chunk = block[CS * i..][..CS].try_into().unwrap();
+            nonce[i] = if i == Chunks::<B>::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<B> CtrFlavor<B> for Ctr128LE
+where
+    B: ArrayLength<u8> + PartialDiv<ChunkSize>,
+    Chunks<B>: ArrayLength<u128>,
+{
+    type CtrNonce = CtrNonce128<Chunks<B>>;
+    type Backend = u128;
+    const NAME: &'static str = "128LE";
+
+    #[inline]
+    fn remaining(cn: &Self::CtrNonce) -> Option<usize> {
+        (core::u128::MAX - cn.ctr).try_into().ok()
+    }
+
+    #[inline(always)]
+    fn current_block(cn: &Self::CtrNonce) -> GenericArray<u8, B> {
+        let mut block = GenericArray::<u8, B>::default();
+        for i in 0..Chunks::<B>::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<u8, B> {
+        let block = Self::current_block(cn);
+        cn.ctr = cn.ctr.wrapping_add(1);
+        block
+    }
+
+    #[inline]
+    fn from_nonce(block: &GenericArray<u8, B>) -> Self::CtrNonce {
+        let mut nonce = GenericArray::<u128, Chunks<B>>::default();
+        for i in 0..Chunks::<B>::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<B> = PartialQuot<B, ChunkSize>;
+const CS: usize = ChunkSize::USIZE;
+
+#[derive(Clone)]
+pub struct CtrNonce32<N: ArrayLength<u32>> {
+    ctr: u32,
+    nonce: GenericArray<u32, N>,
+}
+
+#[cfg(feature = "zeroize")]
+impl<N: ArrayLength<u32>> Drop for CtrNonce32<N> {
+    fn drop(&mut self) {
+        self.ctr.zeroize();
+        self.nonce.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl<N: ArrayLength<u32>> ZeroizeOnDrop for CtrNonce32<N> {}
+
+/// 32-bit big endian counter flavor.
+pub enum Ctr32BE {}
+
+impl<B> CtrFlavor<B> for Ctr32BE
+where
+    B: ArrayLength<u8> + PartialDiv<ChunkSize>,
+    Chunks<B>: ArrayLength<u32>,
+{
+    type CtrNonce = CtrNonce32<Chunks<B>>;
+    type Backend = u32;
+    const NAME: &'static str = "32BE";
+
+    #[inline]
+    fn remaining(cn: &Self::CtrNonce) -> Option<usize> {
+        (core::u32::MAX - cn.ctr).try_into().ok()
+    }
+
+    #[inline(always)]
+    fn current_block(cn: &Self::CtrNonce) -> GenericArray<u8, B> {
+        let mut block = GenericArray::<u8, B>::default();
+        for i in 0..Chunks::<B>::USIZE {
+            let t = if i == Chunks::<B>::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<u8, B> {
+        let block = Self::current_block(cn);
+        cn.ctr = cn.ctr.wrapping_add(1);
+        block
+    }
+
+    #[inline]
+    fn from_nonce(block: &GenericArray<u8, B>) -> Self::CtrNonce {
+        let mut nonce = GenericArray::<u32, Chunks<B>>::default();
+        for i in 0..Chunks::<B>::USIZE {
+            let chunk = block[CS * i..][..CS].try_into().unwrap();
+            nonce[i] = if i == Chunks::<B>::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<B> CtrFlavor<B> for Ctr32LE
+where
+    B: ArrayLength<u8> + PartialDiv<ChunkSize>,
+    Chunks<B>: ArrayLength<u32>,
+{
+    type CtrNonce = CtrNonce32<Chunks<B>>;
+    type Backend = u32;
+    const NAME: &'static str = "32LE";
+
+    #[inline]
+    fn remaining(cn: &Self::CtrNonce) -> Option<usize> {
+        (core::u32::MAX - cn.ctr).try_into().ok()
+    }
+
+    #[inline(always)]
+    fn current_block(cn: &Self::CtrNonce) -> GenericArray<u8, B> {
+        let mut block = GenericArray::<u8, B>::default();
+        for i in 0..Chunks::<B>::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<u8, B> {
+        let block = Self::current_block(cn);
+        cn.ctr = cn.ctr.wrapping_add(1);
+        block
+    }
+
+    #[inline]
+    fn from_nonce(block: &GenericArray<u8, B>) -> Self::CtrNonce {
+        let mut nonce = GenericArray::<u32, Chunks<B>>::default();
+        for i in 0..Chunks::<B>::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<B> = PartialQuot<B, ChunkSize>;
+const CS: usize = ChunkSize::USIZE;
+
+#[derive(Clone)]
+pub struct CtrNonce64<N: ArrayLength<u64>> {
+    ctr: u64,
+    nonce: GenericArray<u64, N>,
+}
+
+#[cfg(feature = "zeroize")]
+impl<N: ArrayLength<u64>> Drop for CtrNonce64<N> {
+    fn drop(&mut self) {
+        self.ctr.zeroize();
+        self.nonce.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl<N: ArrayLength<u64>> ZeroizeOnDrop for CtrNonce64<N> {}
+
+/// 64-bit big endian counter flavor.
+pub enum Ctr64BE {}
+
+impl<B> CtrFlavor<B> for Ctr64BE
+where
+    B: ArrayLength<u8> + PartialDiv<ChunkSize>,
+    Chunks<B>: ArrayLength<u64>,
+{
+    type CtrNonce = CtrNonce64<Chunks<B>>;
+    type Backend = u64;
+    const NAME: &'static str = "64BE";
+
+    #[inline]
+    fn remaining(cn: &Self::CtrNonce) -> Option<usize> {
+        (core::u64::MAX - cn.ctr).try_into().ok()
+    }
+
+    #[inline(always)]
+    fn current_block(cn: &Self::CtrNonce) -> GenericArray<u8, B> {
+        let mut block = GenericArray::<u8, B>::default();
+        for i in 0..Chunks::<B>::USIZE {
+            let t = if i == Chunks::<B>::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<u8, B> {
+        let block = Self::current_block(cn);
+        cn.ctr = cn.ctr.wrapping_add(1);
+        block
+    }
+
+    #[inline]
+    fn from_nonce(block: &GenericArray<u8, B>) -> Self::CtrNonce {
+        let mut nonce = GenericArray::<u64, Chunks<B>>::default();
+        for i in 0..Chunks::<B>::USIZE {
+            let chunk = block[CS * i..][..CS].try_into().unwrap();
+            nonce[i] = if i == Chunks::<B>::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<B> CtrFlavor<B> for Ctr64LE
+where
+    B: ArrayLength<u8> + PartialDiv<ChunkSize>,
+    Chunks<B>: ArrayLength<u64>,
+{
+    type CtrNonce = CtrNonce64<Chunks<B>>;
+    type Backend = u64;
+    const NAME: &'static str = "64LE";
+
+    #[inline]
+    fn remaining(cn: &Self::CtrNonce) -> Option<usize> {
+        (core::u64::MAX - cn.ctr).try_into().ok()
+    }
+
+    #[inline(always)]
+    fn current_block(cn: &Self::CtrNonce) -> GenericArray<u8, B> {
+        let mut block = GenericArray::<u8, B>::default();
+        for i in 0..Chunks::<B>::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<u8, B> {
+        let block = Self::current_block(cn);
+        cn.ctr = cn.ctr.wrapping_add(1);
+        block
+    }
+
+    #[inline]
+    fn from_nonce(block: &GenericArray<u8, B>) -> Self::CtrNonce {
+        let mut nonce = GenericArray::<u64, Chunks<B>>::default();
+        for i in 0..Chunks::<B>::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.
+//!
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ctr_enc.svg" width="49%" />
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ctr_dec.svg" width="49%"/>
+//!
+//! 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<aes::Aes128>;
+//!
+//! 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<B> = StreamCipherCoreWrapper<CtrCore<B, flavors::Ctr128BE>>;
+/// CTR mode with 128-bit little endian counter.
+pub type Ctr128LE<B> = StreamCipherCoreWrapper<CtrCore<B, flavors::Ctr128LE>>;
+/// CTR mode with 64-bit big endian counter.
+pub type Ctr64BE<B> = StreamCipherCoreWrapper<CtrCore<B, flavors::Ctr64BE>>;
+/// CTR mode with 64-bit little endian counter.
+pub type Ctr64LE<B> = StreamCipherCoreWrapper<CtrCore<B, flavors::Ctr64LE>>;
+/// CTR mode with 32-bit big endian counter.
+pub type Ctr32BE<B> = StreamCipherCoreWrapper<CtrCore<B, flavors::Ctr32BE>>;
+/// CTR mode with 32-bit little endian counter.
+pub type Ctr32LE<B> = StreamCipherCoreWrapper<CtrCore<B, flavors::Ctr32LE>>;
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.
+
+<img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ige_enc.svg" width="50%"><img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ige_dec.svg" width="50%">
+
+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<Aes128>,
+    ige_aes128_encrypt_block,
+    ige_aes128_encrypt_blocks,
+);
+
+cipher::block_decryptor_bench!(
+    KeyIv: ige::Decryptor<Aes128>,
+    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<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    cipher: C,
+    x: Block<C>,
+    y: Block<C>,
+}
+
+impl<C> BlockDecryptMut for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, x, y } = self;
+        cipher.decrypt_with_backend_mut(Closure { x, y, f })
+    }
+}
+
+impl<C> BlockSizeUser for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    type BlockSize = C::BlockSize;
+}
+
+impl<C> InnerUser for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    type IvSize = IgeIvSize<C>;
+}
+
+impl<C> InnerIvInit for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    #[inline]
+    fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> 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<C> IvState for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        self.y.clone().concat(self.x.clone())
+    }
+}
+
+impl<C> AlgorithmName for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher + AlgorithmName,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("ige::Decryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher + AlgorithmName,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("ige::Decryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C> Drop for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    fn drop(&mut self) {
+        self.x.zeroize();
+        self.y.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C> ZeroizeOnDrop for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher + ZeroizeOnDrop,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+}
+
+struct Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    x: &'a mut GenericArray<u8, BS>,
+    y: &'a mut GenericArray<u8, BS>,
+    f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(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<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    x: &'a mut GenericArray<u8, BS>,
+    y: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = U1;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    cipher: C,
+    x: Block<C>,
+    y: Block<C>,
+}
+
+impl<C> BlockEncryptMut for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, x, y } = self;
+        cipher.encrypt_with_backend_mut(Closure { x, y, f })
+    }
+}
+
+impl<C> BlockSizeUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    type BlockSize = C::BlockSize;
+}
+
+impl<C> InnerUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    type IvSize = IgeIvSize<C>;
+}
+
+impl<C> InnerIvInit for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    #[inline]
+    fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> 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<C> IvState for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        self.y.clone().concat(self.x.clone())
+    }
+}
+
+impl<C> AlgorithmName for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("ige::Encryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("ige::Encryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C> Drop for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+    fn drop(&mut self) {
+        self.x.zeroize();
+        self.y.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C> ZeroizeOnDrop for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop,
+    C::BlockSize: Add,
+    IgeIvSize<C>: ArrayLength<u8>,
+{
+}
+
+struct Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    x: &'a mut GenericArray<u8, BS>,
+    y: &'a mut GenericArray<u8, BS>,
+    f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(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<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    x: &'a mut GenericArray<u8, BS>,
+    y: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = U1;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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.
+//!
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ige_enc.svg" width="49%" />
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ige_dec.svg" width="49%"/>
+//!
+//! 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<aes::Aes128>;
+//! type Aes128IgeDec = ige::Decryptor<aes::Aes128>;
+//!
+//! 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::<Pkcs7>(&mut buf, pt_len)
+//!     .unwrap();
+//! assert_eq!(ct, &ciphertext[..]);
+//!
+//! let pt = Aes128IgeDec::new(&key.into(), &iv.into())
+//!     .decrypt_padded_mut::<Pkcs7>(&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::<Pkcs7>(&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::<Pkcs7>(&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<C> = <C as BlockSizeUser>::BlockSize;
+type IgeIvSize<C> = Sum<BlockSize<C>, BlockSize<C>>;
+
+#[inline(always)]
+fn xor<N: ArrayLength<u8>>(out: &mut GenericArray<u8, N>, buf: &GenericArray<u8, N>) {
+    for (a, b) in out.iter_mut().zip(buf) {
+        *a ^= *b;
+    }
+}
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.
+
+<img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ofb_enc.svg" width="50%"><img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ofb_dec.svg" width="50%">
+
+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<aes::Aes128>;
+    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<Aes128>,
+    ofb_aes128_encrypt_block,
+    ofb_aes128_encrypt_blocks,
+);
+
+cipher::block_decryptor_bench!(
+    KeyIv: ofb::OfbCore<Aes128>,
+    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<u8>,
+    SC: StreamClosure<BlockSize = BS>,
+{
+    pub(crate) iv: &'a mut GenericArray<u8, BS>,
+    pub(crate) f: SC,
+}
+
+impl<'a, BS, SC> BlockSizeUser for Closure1<'a, BS, SC>
+where
+    BS: ArrayLength<u8>,
+    SC: StreamClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, SC> BlockClosure for Closure1<'a, BS, SC>
+where
+    BS: ArrayLength<u8>,
+    SC: StreamClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(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<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    pub(crate) iv: &'a mut GenericArray<u8, BS>,
+    pub(crate) f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure2<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure2<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
+        let Self { iv, f } = self;
+        f.call(&mut Backend { iv, backend });
+    }
+}
+
+struct Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = U1;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn gen_ks_block(&mut self, block: &mut Block<Self>) {
+        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.
+//!
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ofb_enc.svg" width="49%" />
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/ofb_dec.svg" width="49%"/>
+//!
+//! 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<aes::Aes128>;
+//!
+//! 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<C> = StreamCipherCoreWrapper<OfbCore<C>>;
+
+/// Output feedback (OFB) mode.
+#[derive(Clone)]
+pub struct OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    cipher: C,
+    iv: Block<C>,
+}
+
+impl<C> BlockSizeUser for OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type BlockSize = C::BlockSize;
+}
+
+impl<C> InnerUser for OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type IvSize = C::BlockSize;
+}
+
+impl<C> InnerIvInit for OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> Self {
+        Self {
+            cipher,
+            iv: iv.clone(),
+        }
+    }
+}
+
+impl<C> IvState for OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        self.iv.clone()
+    }
+}
+
+impl<C> StreamCipherCore for OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    fn remaining_blocks(&self) -> Option<usize> {
+        None
+    }
+
+    fn process_with_backend(&mut self, f: impl StreamClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.encrypt_with_backend_mut(backend::Closure1 { iv, f });
+    }
+}
+
+impl<C> BlockEncryptMut for OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.encrypt_with_backend_mut(backend::Closure2 { iv, f })
+    }
+}
+
+impl<C> BlockDecryptMut for OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.encrypt_with_backend_mut(backend::Closure2 { iv, f })
+    }
+}
+
+impl<C> AlgorithmName for OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Ofb<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for OfbCore<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("OfbCore<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher> Drop for OfbCore<C> {
+    fn drop(&mut self) {
+        self.iv.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop> ZeroizeOnDrop for OfbCore<C> {}
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.
+
+<img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/pcbc_enc.svg" width="50%"><img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/pcbc_dec.svg" width="50%">
+
+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<Aes128>,
+    pcbc_aes128_encrypt_block,
+    pcbc_aes128_encrypt_blocks,
+);
+
+cipher::block_decryptor_bench!(
+    KeyIv: pcbc::Decryptor<Aes128>,
+    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<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    cipher: C,
+    iv: Block<C>,
+}
+
+impl<C> BlockSizeUser for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    type BlockSize = C::BlockSize;
+}
+
+impl<C> BlockDecryptMut for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.decrypt_with_backend_mut(Closure { iv, f })
+    }
+}
+
+impl<C> InnerUser for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    type IvSize = C::BlockSize;
+}
+
+impl<C> InnerIvInit for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    #[inline]
+    fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> Self {
+        Self {
+            cipher,
+            iv: iv.clone(),
+        }
+    }
+}
+
+impl<C> IvState for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        self.iv.clone()
+    }
+}
+
+impl<C> AlgorithmName for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher + AlgorithmName,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("pcbc::Decryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for Decryptor<C>
+where
+    C: BlockDecryptMut + BlockCipher + AlgorithmName,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("pcbc::Decryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockDecryptMut + BlockCipher> Drop for Decryptor<C> {
+    fn drop(&mut self) {
+        self.iv.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockDecryptMut + BlockCipher + ZeroizeOnDrop> ZeroizeOnDrop for Decryptor<C> {}
+
+struct Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
+        let Self { iv, f } = self;
+        f.call(&mut Backend { iv, backend });
+    }
+}
+
+struct Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = U1;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    cipher: C,
+    iv: Block<C>,
+}
+
+impl<C> BlockSizeUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type BlockSize = C::BlockSize;
+}
+
+impl<C> BlockEncryptMut for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure<BlockSize = Self::BlockSize>) {
+        let Self { cipher, iv } = self;
+        cipher.encrypt_with_backend_mut(Closure { iv, f })
+    }
+}
+
+impl<C> InnerUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type Inner = C;
+}
+
+impl<C> IvSizeUser for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    type IvSize = C::BlockSize;
+}
+
+impl<C> InnerIvInit for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> Self {
+        Self {
+            cipher,
+            iv: iv.clone(),
+        }
+    }
+}
+
+impl<C> IvState for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher,
+{
+    #[inline]
+    fn iv_state(&self) -> Iv<Self> {
+        self.iv.clone()
+    }
+}
+
+impl<C> AlgorithmName for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("pcbc::Encryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str(">")
+    }
+}
+
+impl<C> fmt::Debug for Encryptor<C>
+where
+    C: BlockEncryptMut + BlockCipher + AlgorithmName,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("pcbc::Encryptor<")?;
+        <C as AlgorithmName>::write_alg_name(f)?;
+        f.write_str("> { ... }")
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher> Drop for Encryptor<C> {
+    fn drop(&mut self) {
+        self.iv.zeroize();
+    }
+}
+
+#[cfg(feature = "zeroize")]
+#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
+impl<C: BlockEncryptMut + BlockCipher + ZeroizeOnDrop> ZeroizeOnDrop for Encryptor<C> {}
+
+struct Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    f: BC,
+}
+
+impl<'a, BS, BC> BlockSizeUser for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BC> BlockClosure for Closure<'a, BS, BC>
+where
+    BS: ArrayLength<u8>,
+    BC: BlockClosure<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
+        let Self { iv, f } = self;
+        f.call(&mut Backend { iv, backend });
+    }
+}
+
+struct Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    iv: &'a mut GenericArray<u8, BS>,
+    backend: &'a mut BK,
+}
+
+impl<'a, BS, BK> BlockSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type BlockSize = BS;
+}
+
+impl<'a, BS, BK> ParBlocksSizeUser for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    type ParBlocksSize = U1;
+}
+
+impl<'a, BS, BK> BlockBackend for Backend<'a, BS, BK>
+where
+    BS: ArrayLength<u8>,
+    BK: BlockBackend<BlockSize = BS>,
+{
+    #[inline(always)]
+    fn proc_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
+        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.
+//!
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/pcbc_enc.svg" width="49%" />
+//! <img src="https://raw.githubusercontent.com/RustCrypto/media/26acc39f/img/block-modes/pcbc_dec.svg" width="49%"/>
+//!
+//! 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<aes::Aes128>;
+//! type Aes128PcbcDec = pcbc::Decryptor<aes::Aes128>;
+//!
+//! 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::<Pkcs7>(&mut buf, pt_len)
+//!     .unwrap();
+//! assert_eq!(ct, &ciphertext[..]);
+//!
+//! let pt = Aes128PcbcDec::new(&key.into(), &iv.into())
+//!     .decrypt_padded_mut::<Pkcs7>(&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::<Pkcs7>(&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::<Pkcs7>(&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<N: ArrayLength<u8>>(out: &mut GenericArray<u8, N>, buf: &GenericArray<u8, N>) {
+    for (a, b) in out.iter_mut().zip(buf) {
+        *a ^= *b;
+    }
+}


Test patch:
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<Aes128>, encrypt);
+iv_state_test!(aes128_cbc_dec_iv_state, Decryptor<Aes128>, decrypt);
+iv_state_test!(aes192_cbc_enc_iv_state, Encryptor<Aes192>, encrypt);
+iv_state_test!(aes192_cbc_dec_iv_state, Decryptor<Aes192>, decrypt);
+iv_state_test!(aes256_cbc_enc_iv_state, Encryptor<Aes256>, encrypt);
+iv_state_test!(aes256_cbc_dec_iv_state, Decryptor<Aes256>, decrypt);
+
+// Test vectors from CVAP "AES Multiblock Message Test (MMT) Sample Vectors":
+// <https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Block-Ciphers>
+block_mode_enc_test!(aes128_cbc_enc_test, "aes128", Encryptor<Aes128>);
+block_mode_dec_test!(aes128_cbc_dec_test, "aes128", Decryptor<Aes128>);
+block_mode_enc_test!(aes128enc_cbc_enc_test, "aes128", Encryptor<Aes128Enc>);
+block_mode_dec_test!(aes128dec_cbc_dec_test, "aes128", Decryptor<Aes128Dec>);
+block_mode_enc_test!(aes192_cbc_enc_test, "aes192", Encryptor<Aes192>);
+block_mode_dec_test!(aes192_cbc_dec_test, "aes192", Decryptor<Aes192>);
+block_mode_enc_test!(aes192enc_cbc_enc_test, "aes192", Encryptor<Aes192Enc>);
+block_mode_dec_test!(aes192dec_cbc_dec_test, "aes192", Decryptor<Aes192Dec>);
+block_mode_enc_test!(aes256_cbc_enc_test, "aes256", Encryptor<Aes256>);
+block_mode_dec_test!(aes256_cbc_dec_test, "aes256", Decryptor<Aes256>);
+block_mode_enc_test!(aes256enc_cbc_enc_test, "aes256", Encryptor<Aes256Enc>);
+block_mode_dec_test!(aes256dec_cbc_dec_test, "aes256", Decryptor<Aes256Dec>);
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/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<Aes128>, encrypt);
+iv_state_test!(aes128_cfb_dec_iv_state, Decryptor<Aes128>, decrypt);
+iv_state_test!(aes192_cfb_enc_iv_state, Encryptor<Aes192>, encrypt);
+iv_state_test!(aes192_cfb_dec_iv_state, Decryptor<Aes192>, decrypt);
+iv_state_test!(aes256_cfb_enc_iv_state, Encryptor<Aes256>, encrypt);
+iv_state_test!(aes256_cfb_dec_iv_state, Decryptor<Aes256>, decrypt);
+
+// Test vectors from CVAP "AES Multiblock Message Test (MMT) Sample Vectors":
+// <https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Block-Ciphers>
+block_mode_enc_test!(aes128_cfb_enc_test, "aes128", Encryptor<Aes128>);
+block_mode_dec_test!(aes128_cfb_dec_test, "aes128", Decryptor<Aes128>);
+block_mode_enc_test!(aes128enc_cfb_enc_test, "aes128", Encryptor<Aes128Enc>);
+block_mode_dec_test!(aes128enc_cfb_dec_test, "aes128", Decryptor<Aes128Enc>);
+block_mode_enc_test!(aes192_cfb_enc_test, "aes192", Encryptor<Aes192>);
+block_mode_dec_test!(aes192_cfb_dec_test, "aes192", Decryptor<Aes192>);
+block_mode_enc_test!(aes192enc_cfb_enc_test, "aes192", Encryptor<Aes192Enc>);
+block_mode_dec_test!(aes192dec_cfb_dec_test, "aes192", Decryptor<Aes192Enc>);
+block_mode_enc_test!(aes256_cfb_enc_test, "aes256", Encryptor<Aes256>);
+block_mode_dec_test!(aes256_cfb_dec_test, "aes256", Decryptor<Aes256>);
+block_mode_enc_test!(aes256enc_cfb_enc_test, "aes256", Encryptor<Aes256Enc>);
+block_mode_dec_test!(aes256dec_cfb_dec_test, "aes256", Decryptor<Aes256Enc>);
+
+/// Test methods from the `AsyncStreamCipher` trait.
+#[test]
+fn aes128_cfb_async_test() {
+    use cipher::{AsyncStreamCipher, KeyIvInit};
+
+    type Enc = Encryptor<Aes128>;
+    type Dec = Decryptor<Aes128>;
+
+    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/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<Aes128>, encrypt);
+iv_state_test!(aes128_cfb8_dec_iv_state, Decryptor<Aes128>, decrypt);
+iv_state_test!(aes192_cfb8_enc_iv_state, Encryptor<Aes192>, encrypt);
+iv_state_test!(aes192_cfb8_dec_iv_state, Decryptor<Aes192>, decrypt);
+iv_state_test!(aes256_cfb8_enc_iv_state, Encryptor<Aes256>, encrypt);
+iv_state_test!(aes256_cfb8_dec_iv_state, Decryptor<Aes256>, decrypt);
+
+// Test vectors from CVAP "AES Multiblock Message Test (MMT) Sample Vectors":
+// <https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Block-Ciphers>
+block_mode_enc_test!(aes128_cfb8_enc_test, "aes128", Encryptor<Aes128>);
+block_mode_dec_test!(aes128_cfb8_dec_test, "aes128", Decryptor<Aes128>);
+block_mode_enc_test!(aes128enc_cfb8_enc_test, "aes128", Encryptor<Aes128Enc>);
+block_mode_dec_test!(aes128enc_cfb8_dec_test, "aes128", Decryptor<Aes128Enc>);
+block_mode_enc_test!(aes192_cfb8_enc_test, "aes192", Encryptor<Aes192>);
+block_mode_dec_test!(aes192_cfb8_dec_test, "aes192", Decryptor<Aes192>);
+block_mode_enc_test!(aes192enc_cfb8_enc_test, "aes192", Encryptor<Aes192Enc>);
+block_mode_dec_test!(aes192dec_cfb8_dec_test, "aes192", Decryptor<Aes192Enc>);
+block_mode_enc_test!(aes256_cfb8_enc_test, "aes256", Encryptor<Aes256>);
+block_mode_dec_test!(aes256_cfb8_dec_test, "aes256", Decryptor<Aes256>);
+block_mode_enc_test!(aes256enc_cfb8_enc_test, "aes256", Encryptor<Aes256Enc>);
+block_mode_dec_test!(aes256dec_cfb8_dec_test, "aes256", Decryptor<Aes256Enc>);
+
+/// Test methods from the `AsyncStreamCipher` trait.
+#[test]
+fn aes128_cfb8_async_test() {
+    use cipher::{AsyncStreamCipher, KeyIvInit};
+
+    type Enc = Encryptor<Aes128>;
+    type Dec = Decryptor<Aes128>;
+
+    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/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<Aes128>);
+cipher::stream_cipher_test!(aes256_ctr_core, "aes256-ctr", Ctr128BE<Aes256>);
+cipher::stream_cipher_seek_test!(aes128_ctr_seek, Ctr128BE<Aes128>);
+cipher::stream_cipher_seek_test!(aes256_ctr_seek, Ctr128BE<Aes256>);
+cipher::iv_state_test!(
+    aes128_ctr_iv_state,
+    CtrCore<Aes128, flavors::Ctr128BE>,
+    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<aes::Aes128>;
+
+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<aes::Aes128, ctr::flavors::Ctr32BE>,
+    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<aes::Aes128>;
+
+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<u8, U16> {
+    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<aes::Aes128, ctr::flavors::Ctr32LE>,
+    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<magma::Magma>;
+type KuznyechikCtr = ctr::Ctr64BE<kuznyechik::Kuznyechik>;
+
+/// 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/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<Aes128>, encrypt);
+iv_state_test!(aes128_ige_dec_iv_state, Decryptor<Aes128>, decrypt);
+
+// Test vectors from: <https://mgp25.com/AESIGE/>
+block_mode_enc_test!(aes128_cbc_enc_test, "aes128", Encryptor<Aes128>);
+block_mode_dec_test!(aes128_cbc_dec_test, "aes128", Decryptor<Aes128>);
+block_mode_enc_test!(aes128enc_cbc_enc_test, "aes128", Encryptor<Aes128Enc>);
+block_mode_dec_test!(aes128dec_cbc_dec_test, "aes128", Decryptor<Aes128Dec>);
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/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<Aes128>, encrypt);
+iv_state_test!(aes128_ofb_dec_iv_state, OfbCore<Aes128>, decrypt);
+iv_state_test!(aes128_ofb_apply_ks_iv_state, OfbCore<Aes128>, apply_ks);
+iv_state_test!(aes192_ofb_enc_iv_state, OfbCore<Aes192>, encrypt);
+iv_state_test!(aes192_ofb_dec_iv_state, OfbCore<Aes192>, decrypt);
+iv_state_test!(aes192_ofb_apply_ks_iv_state, OfbCore<Aes192>, apply_ks);
+iv_state_test!(aes256_ofb_enc_iv_state, OfbCore<Aes256>, encrypt);
+iv_state_test!(aes256_ofb_dec_iv_state, OfbCore<Aes256>, decrypt);
+iv_state_test!(aes256_ofb_apply_ks_iv_state, OfbCore<Aes256>, apply_ks);
+
+// Test vectors from CVAP "AES Multiblock Message Test (MMT) Sample Vectors":
+// <https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Block-Ciphers>
+block_mode_enc_test!(aes128_ofb_enc_test, "aes128", OfbCore<Aes128>);
+block_mode_dec_test!(aes128_ofb_dec_test, "aes128", OfbCore<Aes128>);
+block_mode_enc_test!(aes128enc_ofb_enc_test, "aes128", OfbCore<Aes128Enc>);
+block_mode_dec_test!(aes128dec_ofb_dec_test, "aes128", OfbCore<Aes128Enc>);
+stream_cipher_test!(aes128_ofb_stream_test, "aes128", Ofb<Aes128>);
+stream_cipher_test!(aes128enc_ofb_stream_test, "aes128", Ofb<Aes128Enc>);
+block_mode_enc_test!(aes192_ofb_enc_test, "aes192", OfbCore<Aes192>);
+block_mode_dec_test!(aes192_ofb_dec_test, "aes192", OfbCore<Aes192>);
+block_mode_enc_test!(aes192enc_ofb_enc_test, "aes192", OfbCore<Aes192Enc>);
+block_mode_dec_test!(aes192dec_ofb_dec_test, "aes192", OfbCore<Aes192Enc>);
+stream_cipher_test!(aes192_ofb_stream_test, "aes192", Ofb<Aes192>);
+stream_cipher_test!(aes192enc_ofb_stream_test, "aes192", Ofb<Aes192Enc>);
+block_mode_enc_test!(aes256_ofb_enc_test, "aes256", OfbCore<Aes256>);
+block_mode_dec_test!(aes256_ofb_dec_test, "aes256", OfbCore<Aes256>);
+block_mode_enc_test!(aes256enc_ofb_enc_test, "aes256", OfbCore<Aes256Enc>);
+block_mode_dec_test!(aes256dec_ofb_dec_test, "aes256", OfbCore<Aes256Enc>);
+stream_cipher_test!(aes256_ofb_stream_test, "aes256", Ofb<Aes256>);
+stream_cipher_test!(aes256enc_ofb_stream_test, "aes256", Ofb<Aes256Enc>);
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/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<Aes128>, encrypt);
+iv_state_test!(aes128_pcbc_dec_iv_state, Decryptor<Aes128>, decrypt);
+
+// The test vectors are generated using this implementation.
+block_mode_enc_test!(aes128_pcbc_enc_test, "aes128", Encryptor<Aes128>);
+block_mode_dec_test!(aes128_pcbc_dec_test, "aes128", Decryptor<Aes128>);
+block_mode_enc_test!(aes128enc_pcbc_enc_test, "aes128", Encryptor<Aes128Enc>);
+block_mode_dec_test!(aes128dec_pcbc_dec_test, "aes128", Decryptor<Aes128Dec>);
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