diff --git a/Cargo.lock b/Cargo.lock
index e6939e82..ff9b3e83 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5,7 +5,7 @@ name = "aes"
 version = "0.7.0-pre"
 dependencies = [
  "cfg-if",
- "cipher",
+ "cipher 0.3.0-pre",
  "ctr",
  "opaque-debug",
 ]
@@ -18,11 +18,11 @@ checksum = "fc52553543ecb104069b0ff9e0fcc5c739ad16202935528a112d974e8f1a4ee8"
 
 [[package]]
 name = "block-modes"
-version = "0.7.0"
+version = "0.8.0-pre"
 dependencies = [
  "aes",
  "block-padding",
- "cipher",
+ "cipher 0.3.0-pre",
  "hex-literal",
 ]
 
@@ -34,10 +34,10 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
 
 [[package]]
 name = "blowfish"
-version = "0.7.0"
+version = "0.8.0-pre"
 dependencies = [
  "byteorder",
- "cipher",
+ "cipher 0.3.0-pre",
  "opaque-debug",
 ]
 
@@ -49,10 +49,10 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
 
 [[package]]
 name = "cast5"
-version = "0.9.0"
+version = "0.10.0-pre"
 dependencies = [
  "byteorder",
- "cipher",
+ "cipher 0.3.0-pre",
  "hex-literal",
  "opaque-debug",
 ]
@@ -68,6 +68,15 @@ name = "cipher"
 version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "cipher"
+version = "0.3.0-pre"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42848bd4f28c6e74641082fc3ca870cb5381ce08d8715045cc96ff4ca20c0bbe"
 dependencies = [
  "blobby",
  "generic-array",
@@ -79,15 +88,15 @@ version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f"
 dependencies = [
- "cipher",
+ "cipher 0.2.5",
 ]
 
 [[package]]
 name = "des"
-version = "0.6.0"
+version = "0.7.0-pre"
 dependencies = [
  "byteorder",
- "cipher",
+ "cipher 0.3.0-pre",
  "opaque-debug",
 ]
 
@@ -103,10 +112,10 @@ dependencies = [
 
 [[package]]
 name = "gost-modes"
-version = "0.4.0"
+version = "0.5.0-pre"
 dependencies = [
  "block-modes",
- "cipher",
+ "cipher 0.3.0-pre",
  "generic-array",
  "hex-literal",
  "kuznyechik",
@@ -134,26 +143,26 @@ dependencies = [
 
 [[package]]
 name = "idea"
-version = "0.3.0"
+version = "0.4.0-pre"
 dependencies = [
- "cipher",
+ "cipher 0.3.0-pre",
  "opaque-debug",
 ]
 
 [[package]]
 name = "kuznyechik"
-version = "0.6.0"
+version = "0.7.0-pre"
 dependencies = [
- "cipher",
+ "cipher 0.3.0-pre",
  "hex-literal",
  "opaque-debug",
 ]
 
 [[package]]
 name = "magma"
-version = "0.6.0"
+version = "0.7.0-pre"
 dependencies = [
- "cipher",
+ "cipher 0.3.0-pre",
  "hex-literal",
  "opaque-debug",
 ]
@@ -172,44 +181,44 @@ checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
 
 [[package]]
 name = "rc2"
-version = "0.6.0"
+version = "0.7.0-pre"
 dependencies = [
- "cipher",
+ "cipher 0.3.0-pre",
  "opaque-debug",
 ]
 
 [[package]]
 name = "serpent"
-version = "0.3.0"
+version = "0.4.0-pre"
 dependencies = [
  "byteorder",
- "cipher",
+ "cipher 0.3.0-pre",
  "opaque-debug",
 ]
 
 [[package]]
 name = "sm4"
-version = "0.3.0"
+version = "0.4.0-pre"
 dependencies = [
  "byteorder",
- "cipher",
+ "cipher 0.3.0-pre",
  "hex-literal",
  "opaque-debug",
 ]
 
 [[package]]
 name = "threefish"
-version = "0.3.0"
+version = "0.4.0-pre"
 dependencies = [
- "cipher",
+ "cipher 0.3.0-pre",
 ]
 
 [[package]]
 name = "twofish"
-version = "0.5.0"
+version = "0.6.0-pre"
 dependencies = [
  "byteorder",
- "cipher",
+ "cipher 0.3.0-pre",
  "hex-literal",
  "opaque-debug",
 ]
diff --git a/aes/Cargo.toml b/aes/Cargo.toml
index 6bfd7f70..dca75b85 100644
--- a/aes/Cargo.toml
+++ b/aes/Cargo.toml
@@ -16,12 +16,12 @@ categories = ["cryptography", "no-std"]
 
 [dependencies]
 cfg-if = "1"
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 ctr = { version = "0.6", optional = true }
 opaque-debug = "0.3"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
 
 [features]
 semi_fixslice = []
diff --git a/aes/src/lib.rs b/aes/src/lib.rs
index bf5fb6c7..d894fed9 100644
--- a/aes/src/lib.rs
+++ b/aes/src/lib.rs
@@ -13,9 +13,11 @@
 //!
 //! # Usage example
 //! ```
-//! use aes::cipher::generic_array::GenericArray;
-//! use aes::cipher::{BlockCipher, NewBlockCipher};
 //! use aes::Aes128;
+//! use aes::cipher::{
+//!     BlockCipher, BlockEncrypt, BlockDecrypt, NewBlockCipher,
+//!     generic_array::GenericArray,
+//! };
 //!
 //! let key = GenericArray::from_slice(&[0u8; 16]);
 //! let mut block = GenericArray::clone_from_slice(&[0u8; 16]);
@@ -33,8 +35,8 @@
 //! // We can encrypt 8 blocks simultaneously using
 //! // instruction-level parallelism
 //! let block8_copy = block8.clone();
-//! cipher.encrypt_blocks(&mut block8);
-//! cipher.decrypt_blocks(&mut block8);
+//! cipher.encrypt_par_blocks(&mut block8);
+//! cipher.decrypt_par_blocks(&mut block8);
 //! assert_eq!(block8, block8_copy);
 //! ```
 //!
diff --git a/aes/src/ni/aes128.rs b/aes/src/ni/aes128.rs
index 85db802a..6f37cd53 100644
--- a/aes/src/ni/aes128.rs
+++ b/aes/src/ni/aes128.rs
@@ -2,7 +2,7 @@ use super::arch::*;
 use cipher::{
     consts::{U16, U8},
     generic_array::GenericArray,
-    BlockCipher, NewBlockCipher,
+    BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher,
 };
 
 use super::utils::{Block128, Block128x8};
@@ -80,7 +80,9 @@ impl NewBlockCipher for Aes128 {
 impl BlockCipher for Aes128 {
     type BlockSize = U16;
     type ParBlocks = U8;
+}
 
+impl BlockEncrypt for Aes128 {
     #[inline]
     fn encrypt_block(&self, block: &mut Block128) {
         // Safety: `loadu` and `storeu` support unaligned access
@@ -92,6 +94,16 @@ impl BlockCipher for Aes128 {
         }
     }
 
+    #[inline]
+    fn encrypt_par_blocks(&self, blocks: &mut Block128x8) {
+        unsafe {
+            let b = self.encrypt8(load8!(blocks));
+            store8!(blocks, b);
+        }
+    }
+}
+
+impl BlockDecrypt for Aes128 {
     #[inline]
     fn decrypt_block(&self, block: &mut Block128) {
         #[inline]
@@ -118,15 +130,7 @@ impl BlockCipher for Aes128 {
     }
 
     #[inline]
-    fn encrypt_blocks(&self, blocks: &mut Block128x8) {
-        unsafe {
-            let b = self.encrypt8(load8!(blocks));
-            store8!(blocks, b);
-        }
-    }
-
-    #[inline]
-    fn decrypt_blocks(&self, blocks: &mut Block128x8) {
+    fn decrypt_par_blocks(&self, blocks: &mut Block128x8) {
         #[inline]
         #[target_feature(enable = "aes")]
         unsafe fn aes128_decrypt8(blocks: &mut Block128x8, keys: &[__m128i; 11]) {
diff --git a/aes/src/ni/aes192.rs b/aes/src/ni/aes192.rs
index b7b27c32..9cd4c345 100644
--- a/aes/src/ni/aes192.rs
+++ b/aes/src/ni/aes192.rs
@@ -2,7 +2,7 @@ use super::arch::*;
 use cipher::{
     consts::{U16, U24, U8},
     generic_array::GenericArray,
-    BlockCipher, NewBlockCipher,
+    BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher,
 };
 
 use super::utils::{Block128, Block128x8};
@@ -82,7 +82,9 @@ impl NewBlockCipher for Aes192 {
 impl BlockCipher for Aes192 {
     type BlockSize = U16;
     type ParBlocks = U8;
+}
 
+impl BlockEncrypt for Aes192 {
     #[inline]
     fn encrypt_block(&self, block: &mut Block128) {
         // Safety: `loadu` and `storeu` support unaligned access
@@ -94,6 +96,16 @@ impl BlockCipher for Aes192 {
         }
     }
 
+    #[inline]
+    fn encrypt_par_blocks(&self, blocks: &mut Block128x8) {
+        unsafe {
+            let b = self.encrypt8(load8!(blocks));
+            store8!(blocks, b);
+        }
+    }
+}
+
+impl BlockDecrypt for Aes192 {
     #[inline]
     fn decrypt_block(&self, block: &mut Block128) {
         #[inline]
@@ -122,15 +134,7 @@ impl BlockCipher for Aes192 {
     }
 
     #[inline]
-    fn encrypt_blocks(&self, blocks: &mut Block128x8) {
-        unsafe {
-            let b = self.encrypt8(load8!(blocks));
-            store8!(blocks, b);
-        }
-    }
-
-    #[inline]
-    fn decrypt_blocks(&self, blocks: &mut Block128x8) {
+    fn decrypt_par_blocks(&self, blocks: &mut Block128x8) {
         #[inline]
         #[target_feature(enable = "aes")]
         unsafe fn aes192_decrypt8(blocks: &mut Block128x8, keys: &[__m128i; 13]) {
diff --git a/aes/src/ni/aes256.rs b/aes/src/ni/aes256.rs
index 19dc769f..17d47a46 100644
--- a/aes/src/ni/aes256.rs
+++ b/aes/src/ni/aes256.rs
@@ -2,7 +2,7 @@ use super::arch::*;
 use cipher::{
     consts::{U16, U32, U8},
     generic_array::GenericArray,
-    BlockCipher, NewBlockCipher,
+    BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher,
 };
 
 use super::utils::{Block128, Block128x8};
@@ -86,7 +86,9 @@ impl NewBlockCipher for Aes256 {
 impl BlockCipher for Aes256 {
     type BlockSize = U16;
     type ParBlocks = U8;
+}
 
+impl BlockEncrypt for Aes256 {
     #[inline]
     fn encrypt_block(&self, block: &mut Block128) {
         // Safety: `loadu` and `storeu` support unaligned access
@@ -98,6 +100,16 @@ impl BlockCipher for Aes256 {
         }
     }
 
+    #[inline]
+    fn encrypt_par_blocks(&self, blocks: &mut Block128x8) {
+        unsafe {
+            let b = self.encrypt8(load8!(blocks));
+            store8!(blocks, b);
+        }
+    }
+}
+
+impl BlockDecrypt for Aes256 {
     #[inline]
     fn decrypt_block(&self, block: &mut Block128) {
         #[inline]
@@ -128,15 +140,7 @@ impl BlockCipher for Aes256 {
     }
 
     #[inline]
-    fn encrypt_blocks(&self, blocks: &mut Block128x8) {
-        unsafe {
-            let b = self.encrypt8(load8!(blocks));
-            store8!(blocks, b);
-        }
-    }
-
-    #[inline]
-    fn decrypt_blocks(&self, blocks: &mut Block128x8) {
+    fn decrypt_par_blocks(&self, blocks: &mut Block128x8) {
         #[inline]
         #[target_feature(enable = "aes")]
         unsafe fn aes256_decrypt8(blocks: &mut Block128x8, keys: &[__m128i; 15]) {
diff --git a/aes/src/soft/fixslice32.rs b/aes/src/soft/fixslice32.rs
index fda0543c..10001250 100644
--- a/aes/src/soft/fixslice32.rs
+++ b/aes/src/soft/fixslice32.rs
@@ -37,7 +37,6 @@ type State = [u32; 8];
 
 /// Fully bitsliced AES-128 key schedule to match the fully-fixsliced representation.
 pub(crate) fn aes128_key_schedule(key: &GenericArray<u8, U16>) -> FixsliceKeys128 {
-    // TODO(tarcieri): use `::default()` after MSRV 1.47+
     let mut rkeys = [0u32; 88];
 
     bitslice(&mut rkeys[..8], key, key);
@@ -89,7 +88,6 @@ pub(crate) fn aes128_key_schedule(key: &GenericArray<u8, U16>) -> FixsliceKeys12
 
 /// Fully bitsliced AES-192 key schedule to match the fully-fixsliced representation.
 pub(crate) fn aes192_key_schedule(key: &GenericArray<u8, U24>) -> FixsliceKeys192 {
-    // TODO(tarcieri): use `::default()` after MSRV 1.47+
     let mut rkeys = [0u32; 104];
     let mut tmp = [0u32; 8];
 
@@ -184,7 +182,6 @@ pub(crate) fn aes192_key_schedule(key: &GenericArray<u8, U24>) -> FixsliceKeys19
 
 /// Fully bitsliced AES-256 key schedule to match the fully-fixsliced representation.
 pub(crate) fn aes256_key_schedule(key: &GenericArray<u8, U32>) -> FixsliceKeys256 {
-    // TODO(tarcieri): use `::default()` after MSRV 1.47+
     let mut rkeys = [0u32; 120];
 
     bitslice(&mut rkeys[..8], &key[..16], &key[..16]);
diff --git a/aes/src/soft/fixslice64.rs b/aes/src/soft/fixslice64.rs
index 0acb9d9a..aee3a557 100644
--- a/aes/src/soft/fixslice64.rs
+++ b/aes/src/soft/fixslice64.rs
@@ -36,7 +36,6 @@ type State = [u64; 8];
 
 /// Fully bitsliced AES-128 key schedule to match the fully-fixsliced representation.
 pub(crate) fn aes128_key_schedule(key: &GenericArray<u8, U16>) -> FixsliceKeys128 {
-    // TODO(tarcieri): use `::default()` after MSRV 1.47+
     let mut rkeys = [0u64; 88];
 
     bitslice(&mut rkeys[..8], key, key, key, key);
@@ -88,7 +87,6 @@ pub(crate) fn aes128_key_schedule(key: &GenericArray<u8, U16>) -> FixsliceKeys12
 
 /// Fully bitsliced AES-192 key schedule to match the fully-fixsliced representation.
 pub(crate) fn aes192_key_schedule(key: &GenericArray<u8, U24>) -> FixsliceKeys192 {
-    // TODO(tarcieri): use `::default()` after MSRV 1.47+
     let mut rkeys = [0u64; 104];
     let mut tmp = [0u64; 8];
 
@@ -194,7 +192,6 @@ pub(crate) fn aes192_key_schedule(key: &GenericArray<u8, U24>) -> FixsliceKeys19
 
 /// Fully bitsliced AES-256 key schedule to match the fully-fixsliced representation.
 pub(crate) fn aes256_key_schedule(key: &GenericArray<u8, U32>) -> FixsliceKeys256 {
-    // TODO(tarcieri): use `::default()` after MSRV 1.47+
     let mut rkeys = [0u64; 120];
 
     bitslice(
diff --git a/aes/src/soft/impls.rs b/aes/src/soft/impls.rs
index 33952b1d..5b76926c 100644
--- a/aes/src/soft/impls.rs
+++ b/aes/src/soft/impls.rs
@@ -3,7 +3,7 @@
 use cipher::{
     consts::{U16, U24, U32, U8},
     generic_array::GenericArray,
-    BlockCipher, NewBlockCipher,
+    BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher,
 };
 
 use super::fixslice::{self, FixsliceKeys128, FixsliceKeys192, FixsliceKeys256, FIXSLICE_BLOCKS};
@@ -37,7 +37,9 @@ macro_rules! define_aes_impl {
         impl BlockCipher for $name {
             type BlockSize = U16;
             type ParBlocks = U8;
+        }
 
+        impl BlockEncrypt for $name {
             #[inline]
             fn encrypt_block(&self, block: &mut Block) {
                 let mut blocks = [Block::default(); FIXSLICE_BLOCKS];
@@ -46,6 +48,15 @@ macro_rules! define_aes_impl {
                 block.copy_from_slice(&blocks[0]);
             }
 
+            #[inline]
+            fn encrypt_par_blocks(&self, blocks: &mut ParBlocks) {
+                for chunk in blocks.chunks_mut(FIXSLICE_BLOCKS) {
+                    $fixslice_encrypt(&self.keys, chunk);
+                }
+            }
+        }
+
+        impl BlockDecrypt for $name {
             #[inline]
             fn decrypt_block(&self, block: &mut Block) {
                 let mut blocks = [Block::default(); FIXSLICE_BLOCKS];
@@ -55,14 +66,7 @@ macro_rules! define_aes_impl {
             }
 
             #[inline]
-            fn encrypt_blocks(&self, blocks: &mut ParBlocks) {
-                for chunk in blocks.chunks_mut(FIXSLICE_BLOCKS) {
-                    $fixslice_encrypt(&self.keys, chunk);
-                }
-            }
-
-            #[inline]
-            fn decrypt_blocks(&self, blocks: &mut ParBlocks) {
+            fn decrypt_par_blocks(&self, blocks: &mut ParBlocks) {
                 for chunk in blocks.chunks_mut(FIXSLICE_BLOCKS) {
                     $fixslice_decrypt(&self.keys, chunk);
                 }
diff --git a/block-modes/Cargo.toml b/block-modes/Cargo.toml
index 5e05ba36..b9604db4 100644
--- a/block-modes/Cargo.toml
+++ b/block-modes/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "block-modes"
-version = "0.7.0"
+version = "0.8.0-pre"
 description = "Block cipher modes of operation"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,7 +12,7 @@ keywords = ["crypto", "block-cipher", "ciphers"]
 
 [dependencies]
 block-padding = "0.2"
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 
 [dev-dependencies]
 aes = { version = "=0.7.0-pre", path = "../aes" }
diff --git a/block-modes/src/cbc.rs b/block-modes/src/cbc.rs
index 5dbaa0be..e2bab1fd 100644
--- a/block-modes/src/cbc.rs
+++ b/block-modes/src/cbc.rs
@@ -1,16 +1,15 @@
 use crate::traits::BlockMode;
 use crate::utils::{get_par_blocks, xor, Block, ParBlocks};
 use block_padding::Padding;
-use cipher::block::{BlockCipher, NewBlockCipher};
-use cipher::generic_array::typenum::Unsigned;
-use cipher::generic_array::GenericArray;
+use cipher::generic_array::{typenum::Unsigned, GenericArray};
+use cipher::{BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher};
 use core::marker::PhantomData;
 
 /// [Cipher Block Chaining][1] (CBC) block cipher mode instance.
 ///
 /// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC
 #[derive(Clone)]
-pub struct Cbc<C: BlockCipher + NewBlockCipher, P: Padding> {
+pub struct Cbc<C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher, P: Padding> {
     cipher: C,
     iv: GenericArray<u8, C::BlockSize>,
     _p: PhantomData<P>,
@@ -18,7 +17,7 @@ pub struct Cbc<C: BlockCipher + NewBlockCipher, P: Padding> {
 
 impl<C, P> Cbc<C, P>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher,
     P: Padding,
 {
     #[inline(always)]
@@ -36,7 +35,7 @@ where
 
 impl<C, P> BlockMode<C, P> for Cbc<C, P>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher,
     P: Padding,
 {
     type IvSize = C::BlockSize;
diff --git a/block-modes/src/cfb.rs b/block-modes/src/cfb.rs
index 76c7e7b3..7d79506f 100644
--- a/block-modes/src/cfb.rs
+++ b/block-modes/src/cfb.rs
@@ -1,9 +1,8 @@
 use crate::traits::BlockMode;
 use crate::utils::{xor, Block, ParBlocks};
 use block_padding::Padding;
-use cipher::block::{BlockCipher, NewBlockCipher};
-use cipher::generic_array::typenum::Unsigned;
-use cipher::generic_array::GenericArray;
+use cipher::block::{BlockCipher, BlockEncrypt, NewBlockCipher};
+use cipher::generic_array::{typenum::Unsigned, GenericArray};
 use core::marker::PhantomData;
 use core::ptr;
 
@@ -11,7 +10,7 @@ use core::ptr;
 ///
 /// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB)
 #[derive(Clone)]
-pub struct Cfb<C: BlockCipher + BlockCipher, P: Padding> {
+pub struct Cfb<C: BlockCipher + BlockEncrypt + NewBlockCipher, P: Padding> {
     cipher: C,
     iv: GenericArray<u8, C::BlockSize>,
     _p: PhantomData<P>,
@@ -19,7 +18,7 @@ pub struct Cfb<C: BlockCipher + BlockCipher, P: Padding> {
 
 impl<C, P> BlockMode<C, P> for Cfb<C, P>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + NewBlockCipher,
     P: Padding,
 {
     type IvSize = C::BlockSize;
diff --git a/block-modes/src/cfb8.rs b/block-modes/src/cfb8.rs
index 021f3db2..559a66f0 100644
--- a/block-modes/src/cfb8.rs
+++ b/block-modes/src/cfb8.rs
@@ -1,7 +1,7 @@
 use crate::traits::BlockMode;
 use crate::utils::Block;
 use block_padding::Padding;
-use cipher::block::{BlockCipher, NewBlockCipher};
+use cipher::block::{BlockCipher, BlockEncrypt, NewBlockCipher};
 use cipher::generic_array::GenericArray;
 use core::marker::PhantomData;
 
@@ -9,7 +9,7 @@ use core::marker::PhantomData;
 ///
 /// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB)
 #[derive(Clone)]
-pub struct Cfb8<C: BlockCipher + NewBlockCipher, P: Padding> {
+pub struct Cfb8<C: BlockCipher + BlockEncrypt + NewBlockCipher, P: Padding> {
     cipher: C,
     iv: GenericArray<u8, C::BlockSize>,
     _p: PhantomData<P>,
@@ -17,7 +17,7 @@ pub struct Cfb8<C: BlockCipher + NewBlockCipher, P: Padding> {
 
 impl<C, P> BlockMode<C, P> for Cfb8<C, P>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + NewBlockCipher,
     P: Padding,
 {
     type IvSize = C::BlockSize;
diff --git a/block-modes/src/ecb.rs b/block-modes/src/ecb.rs
index b7993234..5e9d111a 100644
--- a/block-modes/src/ecb.rs
+++ b/block-modes/src/ecb.rs
@@ -2,7 +2,7 @@ use crate::errors::InvalidKeyIvLength;
 use crate::traits::BlockMode;
 use crate::utils::{get_par_blocks, Block};
 use block_padding::Padding;
-use cipher::block::{BlockCipher, NewBlockCipher};
+use cipher::block::{BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher};
 use cipher::generic_array::typenum::{Unsigned, U0};
 use cipher::generic_array::GenericArray;
 use core::marker::PhantomData;
@@ -14,14 +14,14 @@ use core::marker::PhantomData;
 ///
 /// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#ECB
 #[derive(Clone)]
-pub struct Ecb<C: BlockCipher + BlockCipher, P: Padding> {
+pub struct Ecb<C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher, P: Padding> {
     cipher: C,
     _p: PhantomData<P>,
 }
 
 impl<C, P> BlockMode<C, P> for Ecb<C, P>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher,
     P: Padding,
 {
     type IvSize = U0;
diff --git a/block-modes/src/ofb.rs b/block-modes/src/ofb.rs
index bff7872c..029c369c 100644
--- a/block-modes/src/ofb.rs
+++ b/block-modes/src/ofb.rs
@@ -1,7 +1,7 @@
 use crate::traits::BlockMode;
 use crate::utils::{xor, Block};
 use block_padding::Padding;
-use cipher::block::{BlockCipher, NewBlockCipher};
+use cipher::block::{BlockCipher, BlockEncrypt, NewBlockCipher};
 use cipher::generic_array::GenericArray;
 use core::marker::PhantomData;
 
@@ -9,7 +9,7 @@ use core::marker::PhantomData;
 ///
 /// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB)
 #[derive(Clone)]
-pub struct Ofb<C: BlockCipher + NewBlockCipher, P: Padding> {
+pub struct Ofb<C: BlockCipher + BlockEncrypt + NewBlockCipher, P: Padding> {
     cipher: C,
     iv: GenericArray<u8, C::BlockSize>,
     _p: PhantomData<P>,
@@ -17,7 +17,7 @@ pub struct Ofb<C: BlockCipher + NewBlockCipher, P: Padding> {
 
 impl<C, P> BlockMode<C, P> for Ofb<C, P>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + NewBlockCipher,
     P: Padding,
 {
     type IvSize = C::BlockSize;
diff --git a/block-modes/src/pcbc.rs b/block-modes/src/pcbc.rs
index 3516fa9b..8d4360ce 100644
--- a/block-modes/src/pcbc.rs
+++ b/block-modes/src/pcbc.rs
@@ -1,7 +1,7 @@
 use crate::traits::BlockMode;
 use crate::utils::{xor, Block};
 use block_padding::Padding;
-use cipher::block::{BlockCipher, NewBlockCipher};
+use cipher::block::{BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher};
 use cipher::generic_array::GenericArray;
 use core::marker::PhantomData;
 
@@ -9,7 +9,7 @@ use core::marker::PhantomData;
 ///
 /// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#PCBC
 #[derive(Clone)]
-pub struct Pcbc<C: BlockCipher + NewBlockCipher, P: Padding> {
+pub struct Pcbc<C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher, P: Padding> {
     cipher: C,
     iv: GenericArray<u8, C::BlockSize>,
     _p: PhantomData<P>,
@@ -17,7 +17,7 @@ pub struct Pcbc<C: BlockCipher + NewBlockCipher, P: Padding> {
 
 impl<C, P> Pcbc<C, P>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher,
     P: Padding,
 {
     /// Initialize PCBC
@@ -32,7 +32,7 @@ where
 
 impl<C, P> BlockMode<C, P> for Pcbc<C, P>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher,
     P: Padding,
 {
     type IvSize = C::BlockSize;
diff --git a/blowfish/Cargo.toml b/blowfish/Cargo.toml
index 4e362159..cd214fcd 100644
--- a/blowfish/Cargo.toml
+++ b/blowfish/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "blowfish"
-version = "0.7.0"
+version = "0.8.0-pre"
 description = "Blowfish block cipher"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,12 +12,12 @@ keywords = ["crypto", "blowfish", "block-cipher"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 byteorder = { version = "1", default-features = false }
 opaque-debug = "0.3"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
 
 [features]
 bcrypt = []
diff --git a/blowfish/src/lib.rs b/blowfish/src/lib.rs
index 1b7d58d6..f5ccfeab 100644
--- a/blowfish/src/lib.rs
+++ b/blowfish/src/lib.rs
@@ -10,11 +10,9 @@
 
 pub use cipher;
 
-use byteorder::ByteOrder;
-use byteorder::{BE, LE};
-use cipher::block::consts::{U1, U56, U8};
-use cipher::block::InvalidKeyLength;
-use cipher::block::{BlockCipher, NewBlockCipher};
+use byteorder::{ByteOrder, BE, LE};
+use cipher::block::{BlockCipher, BlockDecrypt, BlockEncrypt, InvalidKeyLength, NewBlockCipher};
+use cipher::consts::{U1, U56, U8};
 use cipher::generic_array::GenericArray;
 use core::marker::PhantomData;
 
@@ -128,7 +126,9 @@ impl<T: ByteOrder> NewBlockCipher for Blowfish<T> {
 impl<T: ByteOrder> BlockCipher for Blowfish<T> {
     type BlockSize = U8;
     type ParBlocks = U1;
+}
 
+impl<T: ByteOrder> BlockEncrypt for Blowfish<T> {
     #[inline]
     fn encrypt_block(&self, block: &mut Block) {
         let l = T::read_u32(&block[..4]);
@@ -137,7 +137,9 @@ impl<T: ByteOrder> BlockCipher for Blowfish<T> {
         T::write_u32(&mut block[..4], l);
         T::write_u32(&mut block[4..], r);
     }
+}
 
+impl<T: ByteOrder> BlockDecrypt for Blowfish<T> {
     #[inline]
     fn decrypt_block(&self, block: &mut Block) {
         let l = T::read_u32(&block[..4]);
diff --git a/cast5/Cargo.toml b/cast5/Cargo.toml
index 1af085db..3ac006a2 100644
--- a/cast5/Cargo.toml
+++ b/cast5/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cast5"
-version = "0.9.0"
+version = "0.10.0-pre"
 description = "CAST5 block cipher"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,12 +12,12 @@ keywords = ["crypto", "cast5", "block-cipher"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 opaque-debug = "0.3"
 byteorder = { version = "1", default-features = false }
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/cast5/src/cast5.rs b/cast5/src/cast5.rs
index 525b9871..4bac043d 100644
--- a/cast5/src/cast5.rs
+++ b/cast5/src/cast5.rs
@@ -4,7 +4,7 @@ use cipher::{
 };
 
 use byteorder::{BigEndian, ByteOrder};
-use cipher::block::{BlockCipher, InvalidKeyLength, NewBlockCipher};
+use cipher::block::{BlockCipher, BlockDecrypt, BlockEncrypt, InvalidKeyLength, NewBlockCipher};
 
 use crate::{
     consts::{S1, S2, S3, S4},
@@ -111,7 +111,9 @@ impl NewBlockCipher for Cast5 {
 impl BlockCipher for Cast5 {
     type BlockSize = U8;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for Cast5 {
     #[inline]
     fn encrypt_block(&self, block: &mut Block) {
         let masking = self.masking;
@@ -158,7 +160,9 @@ impl BlockCipher for Cast5 {
         BigEndian::write_u32(&mut block[0..4], r);
         BigEndian::write_u32(&mut block[4..8], l);
     }
+}
 
+impl BlockDecrypt for Cast5 {
     #[inline]
     fn decrypt_block(&self, block: &mut Block) {
         let masking = self.masking;
diff --git a/cast5/src/lib.rs b/cast5/src/lib.rs
index beb78880..a96b9740 100644
--- a/cast5/src/lib.rs
+++ b/cast5/src/lib.rs
@@ -2,11 +2,10 @@
 //!
 //! Implementation according to [RFC 2144](https://tools.ietf.org/html/rfc2144).
 //!
-//!
 //! # Usage example
 //! ```
 //! use cast5::cipher::generic_array::GenericArray;
-//! use cast5::cipher::{BlockCipher, NewBlockCipher};
+//! use cast5::cipher::{BlockCipher, BlockEncrypt, BlockDecrypt, NewBlockCipher};
 //! use cast5::Cast5;
 //!
 //! let key = GenericArray::from_slice(&[0u8; 16]);
diff --git a/cast5/tests/lib.rs b/cast5/tests/lib.rs
index eeb41011..27fa7fed 100644
--- a/cast5/tests/lib.rs
+++ b/cast5/tests/lib.rs
@@ -1,5 +1,5 @@
 use cast5::Cast5;
-use cipher::block::{BlockCipher, NewBlockCipher};
+use cipher::block::{BlockDecrypt, BlockEncrypt, NewBlockCipher};
 use cipher::generic_array::GenericArray;
 use hex_literal::hex;
 
diff --git a/des/Cargo.toml b/des/Cargo.toml
index 9e08cbed..4c9a824d 100644
--- a/des/Cargo.toml
+++ b/des/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "des"
-version = "0.6.0"
+version = "0.7.0-pre"
 description = "DES and Triple DES (3DES, TDES) block ciphers implementation"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,9 +12,9 @@ keywords = ["crypto", "des", "tdes", "block-cipher"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 byteorder = { version = "1", default-features = false }
 opaque-debug = "0.3"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
diff --git a/des/src/des.rs b/des/src/des.rs
index c001b0f6..0620839f 100644
--- a/des/src/des.rs
+++ b/des/src/des.rs
@@ -6,7 +6,7 @@ use byteorder::{ByteOrder, BE};
 use cipher::{
     consts::{U1, U8},
     generic_array::GenericArray,
-    BlockCipher, NewBlockCipher,
+    BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher,
 };
 
 use crate::consts::{SBOXES, SHIFTS};
@@ -199,12 +199,16 @@ impl NewBlockCipher for Des {
 impl BlockCipher for Des {
     type BlockSize = U8;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for Des {
     fn encrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let data = BE::read_u64(block);
         BE::write_u64(block, self.encrypt(data));
     }
+}
 
+impl BlockDecrypt for Des {
     fn decrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let data = BE::read_u64(block);
         BE::write_u64(block, self.decrypt(data));
diff --git a/des/src/tdes.rs b/des/src/tdes.rs
index a7070da8..578e11ff 100644
--- a/des/src/tdes.rs
+++ b/des/src/tdes.rs
@@ -5,7 +5,7 @@ use byteorder::{ByteOrder, BE};
 use cipher::{
     consts::{U1, U16, U24, U8},
     generic_array::GenericArray,
-    BlockCipher, NewBlockCipher,
+    BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher,
 };
 
 /// Triple DES (3DES) block cipher.
@@ -58,7 +58,9 @@ impl NewBlockCipher for TdesEde3 {
 impl BlockCipher for TdesEde3 {
     type BlockSize = U8;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for TdesEde3 {
     fn encrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let mut data = BE::read_u64(block);
 
@@ -68,7 +70,9 @@ impl BlockCipher for TdesEde3 {
 
         BE::write_u64(block, data);
     }
+}
 
+impl BlockDecrypt for TdesEde3 {
     fn decrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let mut data = BE::read_u64(block);
 
@@ -100,7 +104,9 @@ impl NewBlockCipher for TdesEee3 {
 impl BlockCipher for TdesEee3 {
     type BlockSize = U8;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for TdesEee3 {
     fn encrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let mut data = BE::read_u64(block);
 
@@ -110,7 +116,9 @@ impl BlockCipher for TdesEee3 {
 
         BE::write_u64(block, data);
     }
+}
 
+impl BlockDecrypt for TdesEee3 {
     fn decrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let mut data = BE::read_u64(block);
 
@@ -139,7 +147,9 @@ impl NewBlockCipher for TdesEde2 {
 impl BlockCipher for TdesEde2 {
     type BlockSize = U8;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for TdesEde2 {
     fn encrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let mut data = BE::read_u64(block);
 
@@ -149,7 +159,9 @@ impl BlockCipher for TdesEde2 {
 
         BE::write_u64(block, data);
     }
+}
 
+impl BlockDecrypt for TdesEde2 {
     fn decrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let mut data = BE::read_u64(block);
 
@@ -178,7 +190,9 @@ impl NewBlockCipher for TdesEee2 {
 impl BlockCipher for TdesEee2 {
     type BlockSize = U8;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for TdesEee2 {
     fn encrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let mut data = BE::read_u64(block);
 
@@ -188,7 +202,9 @@ impl BlockCipher for TdesEee2 {
 
         BE::write_u64(block, data);
     }
+}
 
+impl BlockDecrypt for TdesEee2 {
     fn decrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let mut data = BE::read_u64(block);
 
diff --git a/gost-modes/Cargo.toml b/gost-modes/Cargo.toml
index 01660ba7..730b7165 100644
--- a/gost-modes/Cargo.toml
+++ b/gost-modes/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "gost-modes"
-version = "0.4.0"
+version = "0.5.0-pre"
 description = "Block cipher modes of operation"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -11,14 +11,14 @@ repository = "https://github.com/RustCrypto/block-ciphers"
 keywords = ["crypto", "block-cipher", "ciphers"]
 
 [dependencies]
-block-modes = { version = "0.7", path = "../block-modes", default-features = false }
-cipher = { version = "0.2", default-features = false }
+block-modes = { version = "=0.8.0-pre", path = "../block-modes", default-features = false }
+cipher = { version = "=0.3.0-pre", default-features = false }
 generic-array = "0.14"
 
 [dev-dependencies]
-kuznyechik = { version = "0.6", path = "../kuznyechik" }
-magma = { version = "0.6", path = "../magma" }
-cipher = { version = "0.2", features = ["dev"] }
+kuznyechik = { version = "=0.7.0-pre", path = "../kuznyechik" }
+magma = { version = "=0.7.0-pre", path = "../magma" }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/gost-modes/src/cbc.rs b/gost-modes/src/cbc.rs
index 02b330b2..04605d55 100644
--- a/gost-modes/src/cbc.rs
+++ b/gost-modes/src/cbc.rs
@@ -1,11 +1,11 @@
 use crate::{utils::xor, GostPadding};
-use block_modes::block_padding::Padding;
-use block_modes::BlockMode;
-use cipher::block::{Block, BlockCipher, NewBlockCipher};
-use core::marker::PhantomData;
-use core::ops::Mul;
-use generic_array::typenum::type_operators::{IsGreater, IsLessOrEqual};
-use generic_array::typenum::{Prod, Unsigned, U0, U1, U255};
+use block_modes::{block_padding::Padding, BlockMode};
+use cipher::block::{Block, BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher};
+use core::{marker::PhantomData, ops::Mul};
+use generic_array::typenum::{
+    type_operators::{IsGreater, IsLessOrEqual},
+    Prod, Unsigned, U0, U1, U255,
+};
 use generic_array::{ArrayLength, GenericArray};
 
 /// Cipher Block Chaining (CBC) mode of operation as defined in GOST R 34.13-2015
@@ -34,7 +34,7 @@ where
 
 impl<C, P, Z> BlockMode<C, P> for GostCbc<C, P, Z>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher,
     C::BlockSize: IsLessOrEqual<U255>,
     Z: ArrayLength<Block<C>> + Unsigned + Mul<C::BlockSize> + IsGreater<U0> + IsLessOrEqual<U255>,
     Prod<Z, C::BlockSize>: ArrayLength<u8>,
diff --git a/gost-modes/src/cfb.rs b/gost-modes/src/cfb.rs
index 8da26cb7..7e30da20 100644
--- a/gost-modes/src/cfb.rs
+++ b/gost-modes/src/cfb.rs
@@ -1,6 +1,6 @@
 use crate::utils::{xor_set1, xor_set2};
 use cipher::{
-    block::{Block, BlockCipher, NewBlockCipher},
+    block::{Block, BlockCipher, BlockEncrypt, NewBlockCipher},
     stream::{FromBlockCipher, StreamCipher},
 };
 use core::ops::Sub;
@@ -24,7 +24,7 @@ type Tail<C, M> = GenericArray<u8, Diff<M, <C as BlockCipher>::BlockSize>>;
 #[derive(Clone)]
 pub struct GostCfb<C, M = BlockSize<C>, S = BlockSize<C>>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + NewBlockCipher,
     C::BlockSize: IsLessOrEqual<U255>,
     M: Unsigned + ArrayLength<u8> + IsGreaterOrEqual<C::BlockSize> + Sub<C::BlockSize>,
     S: Unsigned + ArrayLength<u8> + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
@@ -38,7 +38,7 @@ where
 
 impl<C, M, S> GostCfb<C, M, S>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + NewBlockCipher,
     C::BlockSize: IsLessOrEqual<U255>,
     M: Unsigned + ArrayLength<u8> + IsGreaterOrEqual<C::BlockSize> + Sub<C::BlockSize>,
     S: Unsigned + ArrayLength<u8> + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
@@ -68,7 +68,7 @@ where
 
 impl<C, M, S> FromBlockCipher for GostCfb<C, M, S>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + NewBlockCipher,
     C::BlockSize: IsLessOrEqual<U255>,
     M: Unsigned + ArrayLength<u8> + IsGreaterOrEqual<C::BlockSize> + Sub<C::BlockSize>,
     S: Unsigned + ArrayLength<u8> + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
@@ -92,7 +92,7 @@ where
 
 impl<C, M, S> StreamCipher for GostCfb<C, M, S>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + NewBlockCipher,
     C::BlockSize: IsLessOrEqual<U255>,
     M: Unsigned + ArrayLength<u8> + IsGreaterOrEqual<C::BlockSize> + Sub<C::BlockSize>,
     S: Unsigned + ArrayLength<u8> + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
diff --git a/gost-modes/src/ctr128.rs b/gost-modes/src/ctr128.rs
index c8e3acb7..f346a8d3 100644
--- a/gost-modes/src/ctr128.rs
+++ b/gost-modes/src/ctr128.rs
@@ -1,12 +1,14 @@
 use crate::utils::xor;
 use cipher::{
-    block::{Block, BlockCipher, NewBlockCipher},
+    block::{Block, BlockCipher, BlockEncrypt, NewBlockCipher},
     stream::{
         FromBlockCipher, LoopError, OverflowError, SeekNum, SyncStreamCipher, SyncStreamCipherSeek,
     },
 };
-use generic_array::typenum::type_operators::{IsGreater, IsLessOrEqual};
-use generic_array::typenum::{Unsigned, U0, U16, U8};
+use generic_array::typenum::{
+    type_operators::{IsGreater, IsLessOrEqual},
+    Unsigned, U0, U16, U8,
+};
 use generic_array::{ArrayLength, GenericArray};
 
 /// Counter (CTR) mode of operation for 128-bit block ciphers as defined in
@@ -18,7 +20,7 @@ use generic_array::{ArrayLength, GenericArray};
 #[derive(Clone)]
 pub struct GostCtr128<C, S = <C as BlockCipher>::BlockSize>
 where
-    C: BlockCipher<BlockSize = U16> + NewBlockCipher,
+    C: BlockCipher<BlockSize = U16> + BlockEncrypt + NewBlockCipher,
     C::ParBlocks: ArrayLength<GenericArray<u8, U16>>,
     S: ArrayLength<u8> + Unsigned + IsGreater<U0> + IsLessOrEqual<U16>,
 {
@@ -31,7 +33,7 @@ where
 
 impl<C, S> GostCtr128<C, S>
 where
-    C: BlockCipher<BlockSize = U16> + NewBlockCipher,
+    C: BlockCipher<BlockSize = U16> + BlockEncrypt + NewBlockCipher,
     C::ParBlocks: ArrayLength<GenericArray<u8, U16>>,
     S: ArrayLength<u8> + Unsigned + IsGreater<U0> + IsLessOrEqual<U16>,
 {
@@ -48,7 +50,7 @@ where
 
 impl<C, S> FromBlockCipher for GostCtr128<C, S>
 where
-    C: BlockCipher<BlockSize = U16> + NewBlockCipher,
+    C: BlockCipher<BlockSize = U16> + BlockEncrypt + NewBlockCipher,
     C::ParBlocks: ArrayLength<GenericArray<u8, U16>>,
     S: ArrayLength<u8> + Unsigned + IsGreater<U0> + IsLessOrEqual<U16>,
 {
@@ -68,7 +70,7 @@ where
 
 impl<C, S> SyncStreamCipher for GostCtr128<C, S>
 where
-    C: BlockCipher<BlockSize = U16> + NewBlockCipher,
+    C: BlockCipher<BlockSize = U16> + BlockEncrypt + NewBlockCipher,
     C::ParBlocks: ArrayLength<GenericArray<u8, U16>>,
     S: ArrayLength<u8> + Unsigned + IsGreater<U0> + IsLessOrEqual<U16>,
 {
@@ -110,7 +112,7 @@ where
 
 impl<C, S> SyncStreamCipherSeek for GostCtr128<C, S>
 where
-    C: BlockCipher<BlockSize = U16> + NewBlockCipher,
+    C: BlockCipher<BlockSize = U16> + BlockEncrypt + NewBlockCipher,
     C::ParBlocks: ArrayLength<GenericArray<u8, U16>>,
     S: ArrayLength<u8> + Unsigned + IsGreater<U0> + IsLessOrEqual<U16>,
 {
diff --git a/gost-modes/src/ctr64.rs b/gost-modes/src/ctr64.rs
index e173f6f9..e1034e67 100644
--- a/gost-modes/src/ctr64.rs
+++ b/gost-modes/src/ctr64.rs
@@ -1,12 +1,14 @@
 use crate::utils::xor;
 use cipher::{
-    block::{Block, BlockCipher, NewBlockCipher},
+    block::{Block, BlockCipher, BlockEncrypt, NewBlockCipher},
     stream::{
         FromBlockCipher, LoopError, OverflowError, SeekNum, SyncStreamCipher, SyncStreamCipherSeek,
     },
 };
-use generic_array::typenum::type_operators::{IsGreater, IsLessOrEqual};
-use generic_array::typenum::{Unsigned, U0, U4, U8};
+use generic_array::typenum::{
+    type_operators::{IsGreater, IsLessOrEqual},
+    Unsigned, U0, U4, U8,
+};
 use generic_array::{ArrayLength, GenericArray};
 
 /// Counter (CTR) mode of operation for 64-bit block ciphers as defined in
@@ -18,7 +20,7 @@ use generic_array::{ArrayLength, GenericArray};
 #[derive(Clone)]
 pub struct GostCtr64<C, S = <C as BlockCipher>::BlockSize>
 where
-    C: BlockCipher<BlockSize = U8> + NewBlockCipher,
+    C: BlockCipher<BlockSize = U8> + BlockEncrypt + NewBlockCipher,
     C::ParBlocks: ArrayLength<GenericArray<u8, U8>>,
     S: ArrayLength<u8> + Unsigned + IsGreater<U0> + IsLessOrEqual<U8>,
 {
@@ -31,7 +33,7 @@ where
 
 impl<C, S> GostCtr64<C, S>
 where
-    C: BlockCipher<BlockSize = U8> + NewBlockCipher,
+    C: BlockCipher<BlockSize = U8> + BlockEncrypt + NewBlockCipher,
     C::ParBlocks: ArrayLength<GenericArray<u8, U8>>,
     S: ArrayLength<u8> + Unsigned + IsGreater<U0> + IsLessOrEqual<U8>,
 {
@@ -48,7 +50,7 @@ where
 
 impl<C, S> FromBlockCipher for GostCtr64<C, S>
 where
-    C: BlockCipher<BlockSize = U8> + NewBlockCipher,
+    C: BlockCipher<BlockSize = U8> + BlockEncrypt + NewBlockCipher,
     C::ParBlocks: ArrayLength<GenericArray<u8, U8>>,
     S: ArrayLength<u8> + Unsigned + IsGreater<U0> + IsLessOrEqual<U8>,
 {
@@ -68,7 +70,7 @@ where
 
 impl<C, S> SyncStreamCipher for GostCtr64<C, S>
 where
-    C: BlockCipher<BlockSize = U8> + NewBlockCipher,
+    C: BlockCipher<BlockSize = U8> + BlockEncrypt + NewBlockCipher,
     C::ParBlocks: ArrayLength<GenericArray<u8, U8>>,
     S: ArrayLength<u8> + Unsigned + IsGreater<U0> + IsLessOrEqual<U8>,
 {
@@ -110,7 +112,7 @@ where
 
 impl<C, S> SyncStreamCipherSeek for GostCtr64<C, S>
 where
-    C: BlockCipher<BlockSize = U8> + NewBlockCipher,
+    C: BlockCipher<BlockSize = U8> + BlockEncrypt + NewBlockCipher,
     C::ParBlocks: ArrayLength<GenericArray<u8, U8>>,
     S: ArrayLength<u8> + Unsigned + IsGreater<U0> + IsLessOrEqual<U8>,
 {
diff --git a/gost-modes/src/ofb.rs b/gost-modes/src/ofb.rs
index 2f1d80b5..739d2565 100644
--- a/gost-modes/src/ofb.rs
+++ b/gost-modes/src/ofb.rs
@@ -1,12 +1,13 @@
 use crate::utils::xor;
 use cipher::{
-    block::{Block, BlockCipher, NewBlockCipher},
+    block::{Block, BlockCipher, BlockEncrypt, NewBlockCipher},
     stream::{FromBlockCipher, LoopError, SyncStreamCipher},
 };
-use core::marker::PhantomData;
-use core::ops::Mul;
-use generic_array::typenum::type_operators::{IsGreater, IsLessOrEqual};
-use generic_array::typenum::{Prod, Unsigned, U0, U1, U255};
+use core::{marker::PhantomData, ops::Mul};
+use generic_array::typenum::{
+    type_operators::{IsGreater, IsLessOrEqual},
+    Prod, Unsigned, U0, U1, U255,
+};
 use generic_array::{ArrayLength, GenericArray};
 
 /// Output feedback (OFB) mode of operation as defined in GOST R 34.13-2015
@@ -21,7 +22,7 @@ use generic_array::{ArrayLength, GenericArray};
 #[derive(Clone)]
 pub struct GostOfb<C, Z = U1, S = <C as BlockCipher>::BlockSize>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + NewBlockCipher,
     C::BlockSize: IsLessOrEqual<U255>,
     S: Unsigned + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
     Z: ArrayLength<Block<C>> + Unsigned + Mul<C::BlockSize> + IsGreater<U0> + IsLessOrEqual<U255>,
@@ -36,7 +37,7 @@ where
 
 impl<C, Z, S> FromBlockCipher for GostOfb<C, Z, S>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + NewBlockCipher,
     C::BlockSize: IsLessOrEqual<U255>,
     S: Unsigned + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
     Z: ArrayLength<Block<C>> + Unsigned + Mul<C::BlockSize> + IsGreater<U0> + IsLessOrEqual<U255>,
@@ -66,7 +67,7 @@ where
 
 impl<C, Z, S> SyncStreamCipher for GostOfb<C, Z, S>
 where
-    C: BlockCipher + NewBlockCipher,
+    C: BlockCipher + BlockEncrypt + NewBlockCipher,
     C::BlockSize: IsLessOrEqual<U255>,
     S: Unsigned + IsGreater<U0> + IsLessOrEqual<C::BlockSize>,
     Z: ArrayLength<Block<C>> + Unsigned + Mul<C::BlockSize> + IsGreater<U0> + IsLessOrEqual<U255>,
diff --git a/idea/Cargo.toml b/idea/Cargo.toml
index 0b7e2fb4..2163ea98 100644
--- a/idea/Cargo.toml
+++ b/idea/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "idea"
-version = "0.3.0"
+version = "0.4.0-pre"
 description = "IDEA block cipher"
 authors = ["RustCrypto Developers"]
 license = "Apache-2.0 AND MIT"
@@ -12,8 +12,8 @@ keywords = ["crypto", "idea", "block-cipher"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 opaque-debug = "0.3"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
diff --git a/idea/src/lib.rs b/idea/src/lib.rs
index 6e271913..4fd6b4be 100644
--- a/idea/src/lib.rs
+++ b/idea/src/lib.rs
@@ -11,7 +11,7 @@
 #![warn(missing_docs, rust_2018_idioms)]
 #![allow(clippy::many_single_char_names)]
 
-pub use cipher::{self, BlockCipher, NewBlockCipher};
+pub use cipher::{self, BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher};
 
 use cipher::{
     consts::{U1, U16, U8},
@@ -185,11 +185,15 @@ impl NewBlockCipher for Idea {
 impl BlockCipher for Idea {
     type BlockSize = U8;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for Idea {
     fn encrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         self.crypt(block, &self.encryption_sub_keys);
     }
+}
 
+impl BlockDecrypt for Idea {
     fn decrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         self.crypt(block, &self.decryption_sub_keys);
     }
diff --git a/kuznyechik/Cargo.toml b/kuznyechik/Cargo.toml
index 77b3326e..013461f4 100644
--- a/kuznyechik/Cargo.toml
+++ b/kuznyechik/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "kuznyechik"
-version = "0.6.0"
+version = "0.7.0-pre"
 description = "Kuznyechik (GOST R 34.12-2015) block cipher"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,11 +12,11 @@ keywords = ["crypto", "kuznyechik", "gost", "block-cipher"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 opaque-debug = "0.3"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/kuznyechik/src/lib.rs b/kuznyechik/src/lib.rs
index 4033e9d7..1393e113 100644
--- a/kuznyechik/src/lib.rs
+++ b/kuznyechik/src/lib.rs
@@ -15,7 +15,7 @@ pub use cipher;
 use cipher::{
     consts::{U1, U16, U32},
     generic_array::GenericArray,
-    BlockCipher, NewBlockCipher,
+    BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher,
 };
 
 mod consts;
@@ -153,14 +153,18 @@ impl NewBlockCipher for Kuznyechik {
 impl BlockCipher for Kuznyechik {
     type BlockSize = U16;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for Kuznyechik {
     #[inline]
     fn encrypt_block(&self, block: &mut Block) {
         #[allow(unsafe_code)]
         let block: &mut [u8; 16] = unsafe { core::mem::transmute(block) };
         self.encrypt(block);
     }
+}
 
+impl BlockDecrypt for Kuznyechik {
     #[inline]
     fn decrypt_block(&self, block: &mut Block) {
         #[allow(unsafe_code)]
diff --git a/kuznyechik/tests/lib.rs b/kuznyechik/tests/lib.rs
index 8824785d..44c0963c 100644
--- a/kuznyechik/tests/lib.rs
+++ b/kuznyechik/tests/lib.rs
@@ -1,7 +1,7 @@
 #![cfg_attr(rustfmt, rustfmt_skip)]
 
 use cipher::generic_array::GenericArray;
-use cipher::block::{BlockCipher, NewBlockCipher};
+use cipher::block::{BlockEncrypt, BlockDecrypt, NewBlockCipher};
 use hex_literal::hex;
 
 /// Example vectors from GOST 34.12-2018
diff --git a/magma/Cargo.toml b/magma/Cargo.toml
index e3e04b0a..0e51dd51 100644
--- a/magma/Cargo.toml
+++ b/magma/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "magma"
-version = "0.6.0"
+version = "0.7.0-pre"
 description = "Magma (GOST 28147-89 and GOST R 34.12-2015) block cipher"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,9 +12,9 @@ keywords = ["crypto", "magma", "gost", "block-cipher"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 opaque-debug = "0.3"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
 hex-literal = "0.2"
diff --git a/magma/src/lib.rs b/magma/src/lib.rs
index 19f9b6b5..f10ce379 100644
--- a/magma/src/lib.rs
+++ b/magma/src/lib.rs
@@ -3,7 +3,7 @@
 //!
 //! # Examples
 //! ```
-//! use magma::{Magma, BlockCipher, NewBlockCipher};
+//! use magma::{Magma, BlockCipher, BlockEncrypt, BlockDecrypt, NewBlockCipher};
 //! use magma::cipher::generic_array::GenericArray;
 //! use hex_literal::hex;
 //!
@@ -34,7 +34,7 @@
 #![deny(unsafe_code)]
 #![warn(rust_2018_idioms)]
 
-pub use cipher::{self, BlockCipher, NewBlockCipher};
+pub use cipher::{self, BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher};
 
 use cipher::{
     consts::{U1, U32, U8},
@@ -71,7 +71,9 @@ impl<S: Sbox> NewBlockCipher for Gost89<S> {
 impl<S: Sbox> BlockCipher for Gost89<S> {
     type BlockSize = U8;
     type ParBlocks = U1;
+}
 
+impl<S: Sbox> BlockEncrypt for Gost89<S> {
     #[inline]
     fn encrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let mut v = (to_u32(&block[0..4]), to_u32(&block[4..8]));
@@ -86,7 +88,9 @@ impl<S: Sbox> BlockCipher for Gost89<S> {
         block[0..4].copy_from_slice(&v.1.to_be_bytes());
         block[4..8].copy_from_slice(&v.0.to_be_bytes());
     }
+}
 
+impl<S: Sbox> BlockDecrypt for Gost89<S> {
     #[inline]
     fn decrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         let mut v = (to_u32(&block[0..4]), to_u32(&block[4..8]));
diff --git a/rc2/Cargo.toml b/rc2/Cargo.toml
index 341c5d5c..912929b4 100644
--- a/rc2/Cargo.toml
+++ b/rc2/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "rc2"
-version = "0.6.0"
+version = "0.7.0-pre"
 description = "RC2 block cipher"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,8 +12,8 @@ keywords = ["crypto", "rc2", "block-cipher"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 opaque-debug = "0.3"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
diff --git a/rc2/src/lib.rs b/rc2/src/lib.rs
index 76a7c107..8c40f9ad 100644
--- a/rc2/src/lib.rs
+++ b/rc2/src/lib.rs
@@ -13,7 +13,7 @@
 pub use cipher;
 
 use cipher::{
-    block::{BlockCipher, InvalidKeyLength, NewBlockCipher},
+    block::{BlockCipher, BlockDecrypt, BlockEncrypt, InvalidKeyLength, NewBlockCipher},
     consts::{U1, U32, U8},
     generic_array::GenericArray,
 };
@@ -212,11 +212,15 @@ impl NewBlockCipher for Rc2 {
 impl BlockCipher for Rc2 {
     type BlockSize = U8;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for Rc2 {
     fn encrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         self.encrypt(block);
     }
+}
 
+impl BlockDecrypt for Rc2 {
     fn decrypt_block(&self, block: &mut GenericArray<u8, U8>) {
         self.decrypt(block);
     }
diff --git a/rc2/tests/lib.rs b/rc2/tests/lib.rs
index f80eb7e3..d950ea70 100644
--- a/rc2/tests/lib.rs
+++ b/rc2/tests/lib.rs
@@ -1,4 +1,4 @@
-use cipher::block::{BlockCipher, NewBlockCipher};
+use cipher::block::{BlockDecrypt, BlockEncrypt, NewBlockCipher};
 use cipher::generic_array::GenericArray;
 
 struct Test {
diff --git a/serpent/Cargo.toml b/serpent/Cargo.toml
index 94626171..37f727ef 100644
--- a/serpent/Cargo.toml
+++ b/serpent/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "serpent"
-version = "0.3.0"
+version = "0.4.0-pre"
 description = "Serpent block cipher"
 authors = ["RustCrypto Developers"]
 license = "Apache-2.0 OR MIT"
@@ -13,8 +13,8 @@ categories = ["cryptography", "no-std"]
 
 [dependencies]
 byteorder = { version = "1", default-features = false }
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 opaque-debug = "0.3"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
diff --git a/serpent/src/lib.rs b/serpent/src/lib.rs
index 4f2b3529..16116cfa 100644
--- a/serpent/src/lib.rs
+++ b/serpent/src/lib.rs
@@ -17,7 +17,7 @@ pub use cipher;
 
 use byteorder::{ByteOrder, LE};
 use cipher::{
-    block::{BlockCipher, InvalidKeyLength, NewBlockCipher},
+    block::{BlockCipher, BlockDecrypt, BlockEncrypt, InvalidKeyLength, NewBlockCipher},
     consts::{U1, U16},
     generic_array::GenericArray,
 };
@@ -254,7 +254,9 @@ impl NewBlockCipher for Serpent {
 impl BlockCipher for Serpent {
     type BlockSize = U16;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for Serpent {
     fn encrypt_block(&self, block: &mut GenericArray<u8, Self::BlockSize>) {
         let mut b = [0u8; 16];
 
@@ -267,7 +269,9 @@ impl BlockCipher for Serpent {
         }
         *block = *GenericArray::from_slice(&b);
     }
+}
 
+impl BlockDecrypt for Serpent {
     fn decrypt_block(&self, block: &mut GenericArray<u8, Self::BlockSize>) {
         let mut b = [0u8; 16];
 
diff --git a/sm4/Cargo.toml b/sm4/Cargo.toml
index 6e2c67f9..83c7e51d 100644
--- a/sm4/Cargo.toml
+++ b/sm4/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "sm4"
-version = "0.3.0"
+version = "0.4.0-pre"
 authors = ["andelf <andelf@gmail.com>"]
 license = "Apache-2.0 OR MIT"
 description = "SM4 block cipher algorithm"
@@ -12,10 +12,10 @@ keywords = ["crypto", "sm4", "block-cipher"]
 categories = ["cryptography"]
 
 [dependencies]
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 byteorder = { version = "1", default-features = false }
 opaque-debug = "0.3"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
 hex-literal = "0.2"
diff --git a/sm4/src/lib.rs b/sm4/src/lib.rs
index f2bc5af0..21420d6f 100644
--- a/sm4/src/lib.rs
+++ b/sm4/src/lib.rs
@@ -17,7 +17,7 @@ use byteorder::{ByteOrder, BE};
 use cipher::{
     consts::{U1, U16},
     generic_array::GenericArray,
-    BlockCipher, NewBlockCipher,
+    BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher,
 };
 
 pub const SBOX: [u8; 256] = [
@@ -114,7 +114,9 @@ impl NewBlockCipher for Sm4 {
 impl BlockCipher for Sm4 {
     type BlockSize = U16;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for Sm4 {
     fn encrypt_block(&self, block: &mut GenericArray<u8, U16>) {
         let mut x = [0u32; 4];
         BE::read_u32_into(block, &mut x);
@@ -128,7 +130,9 @@ impl BlockCipher for Sm4 {
         x = [x[3], x[2], x[1], x[0]];
         BE::write_u32_into(&x, block);
     }
+}
 
+impl BlockDecrypt for Sm4 {
     fn decrypt_block(&self, block: &mut GenericArray<u8, U16>) {
         let mut x = [0u32; 4];
         BE::read_u32_into(block, &mut x);
diff --git a/sm4/tests/lib.rs b/sm4/tests/lib.rs
index b0008463..1ea03a66 100644
--- a/sm4/tests/lib.rs
+++ b/sm4/tests/lib.rs
@@ -1,6 +1,6 @@
 //! Test vectors are from GM/T 0002-2012
 
-use cipher::block::{BlockCipher, NewBlockCipher};
+use cipher::block::{BlockDecrypt, BlockEncrypt, NewBlockCipher};
 use hex_literal::hex;
 use sm4::Sm4;
 
diff --git a/threefish/Cargo.toml b/threefish/Cargo.toml
index 6d8849f7..f7ed4415 100644
--- a/threefish/Cargo.toml
+++ b/threefish/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "threefish"
-version = "0.3.0"
+version = "0.4.0-pre"
 authors = ["The Rust-Crypto Project Developers"]
 license = "MIT OR Apache-2.0"
 description = "Threefish block cipher"
@@ -11,7 +11,7 @@ repository = "https://github.com/RustCrypto/block-ciphers"
 keywords = ["crypto", "threefish", "gost", "block-cipher"]
 
 [dependencies]
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
diff --git a/threefish/src/lib.rs b/threefish/src/lib.rs
index 241c225e..bbc9613a 100644
--- a/threefish/src/lib.rs
+++ b/threefish/src/lib.rs
@@ -14,7 +14,7 @@ use crate::consts::{C240, P_1024, P_256, P_512, R_1024, R_256, R_512};
 use cipher::{
     consts::{U1, U128, U32, U64},
     generic_array::GenericArray,
-    BlockCipher, NewBlockCipher,
+    BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher,
 };
 use core::{convert::TryInto, ops::BitXor};
 
@@ -84,7 +84,9 @@ macro_rules! impl_threefish(
         impl BlockCipher for $name {
             type BlockSize = $block_size;
             type ParBlocks = U1;
+        }
 
+        impl BlockEncrypt for $name {
             fn encrypt_block(&self, block: &mut GenericArray<u8, Self::BlockSize>) {
                 let mut v = [0u64; $n_w];
                 for (vv, chunk) in v.iter_mut().zip(block.chunks_exact(8)) {
@@ -119,7 +121,9 @@ macro_rules! impl_threefish(
                     chunk.copy_from_slice(&vv.to_le_bytes());
                 }
             }
+        }
 
+        impl BlockDecrypt for $name {
             fn decrypt_block(&self, block: &mut GenericArray<u8, Self::BlockSize>) {
                 let mut v = [0u64; $n_w];
                 for (vv, chunk) in v.iter_mut().zip(block.chunks_exact(8)) {
diff --git a/twofish/Cargo.toml b/twofish/Cargo.toml
index bb452746..beb2f07f 100644
--- a/twofish/Cargo.toml
+++ b/twofish/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "twofish"
-version = "0.5.0"
+version = "0.6.0-pre"
 description = "Twofish block cipher"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -13,9 +13,9 @@ categories = ["cryptography", "no-std"]
 
 [dependencies]
 byteorder = { version = "1", default-features = false }
-cipher = "0.2"
+cipher = "=0.3.0-pre"
 opaque-debug = "0.3"
 
 [dev-dependencies]
-cipher = { version = "0.2", features = ["dev"] }
+cipher = { version = "=0.3.0-pre", features = ["dev"] }
 hex-literal = "0.2"
diff --git a/twofish/src/lib.rs b/twofish/src/lib.rs
index e6324e1c..c9aaa8bd 100644
--- a/twofish/src/lib.rs
+++ b/twofish/src/lib.rs
@@ -13,7 +13,7 @@ pub use cipher;
 
 use byteorder::{ByteOrder, LE};
 use cipher::{
-    block::{BlockCipher, InvalidKeyLength, NewBlockCipher},
+    block::{BlockCipher, BlockDecrypt, BlockEncrypt, InvalidKeyLength, NewBlockCipher},
     consts::{U1, U16, U32},
     generic_array::GenericArray,
 };
@@ -185,7 +185,9 @@ impl NewBlockCipher for Twofish {
 impl BlockCipher for Twofish {
     type BlockSize = U16;
     type ParBlocks = U1;
+}
 
+impl BlockEncrypt for Twofish {
     fn encrypt_block(&self, block: &mut Block) {
         let mut p = [0u32; 4];
         LE::read_u32_into(block, &mut p);
@@ -217,7 +219,9 @@ impl BlockCipher for Twofish {
         LE::write_u32(&mut block[8..12], p[0] ^ self.k[6]);
         LE::write_u32(&mut block[12..16], p[1] ^ self.k[7]);
     }
+}
 
+impl BlockDecrypt for Twofish {
     fn decrypt_block(&self, block: &mut Block) {
         let mut c = [0u32; 4];
 
diff --git a/twofish/tests/lib.rs b/twofish/tests/lib.rs
index 8e1fc1cc..89f5a0f8 100644
--- a/twofish/tests/lib.rs
+++ b/twofish/tests/lib.rs
@@ -1,4 +1,4 @@
-use cipher::block::{BlockCipher, NewBlockCipher};
+use cipher::block::{BlockDecrypt, BlockEncrypt, NewBlockCipher};
 use cipher::generic_array::GenericArray;
 use hex_literal::hex;
 use twofish::Twofish;
