diff --git a/Cargo.lock b/Cargo.lock index 58632b46c..eb22b2868 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,12 @@ dependencies = [ "generic-array 0.14.4", ] +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "byteorder" version = "1.3.4" @@ -88,6 +94,7 @@ name = "cipher" version = "0.3.0-pre" dependencies = [ "blobby 0.3.0", + "block-padding", "generic-array 0.14.4", ] diff --git a/cipher/Cargo.toml b/cipher/Cargo.toml index 9c37e7aa6..483cef598 100644 --- a/cipher/Cargo.toml +++ b/cipher/Cargo.toml @@ -14,9 +14,12 @@ categories = ["cryptography", "no-std"] [dependencies] generic-array = "0.14" blobby = { version = "0.3", optional = true } +block-padding = { verstion = "0.2", optional = true } [features] +default = ["block-mode"] std = [] +block-mode = ["block-padding"] dev = ["blobby"] [package.metadata.docs.rs] diff --git a/cipher/src/block.rs b/cipher/src/block.rs index 16205ba86..4e08dc259 100644 --- a/cipher/src/block.rs +++ b/cipher/src/block.rs @@ -9,21 +9,17 @@ //! [1]: https://en.wikipedia.org/wiki/Block_cipher //! [2]: https://en.wikipedia.org/wiki/Symmetric-key_algorithm -#[cfg(feature = "dev")] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -pub mod dev; - use crate::errors::InvalidLength; use core::convert::TryInto; use generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; /// Key for an algorithm that implements [`NewBlockCipher`]. -pub type Key = GenericArray::KeySize>; +pub type BlockCipherKey = GenericArray::KeySize>; /// Block on which a [`BlockCipher`] operates. pub type Block = GenericArray::BlockSize>; -/// Blocks being acted over in parallel. +/// Block on which a [`BlockCipher`] operates in parallel. pub type ParBlocks = GenericArray, ::ParBlocks>; /// Instantiate a [`BlockCipher`] algorithm. @@ -32,13 +28,13 @@ pub trait NewBlockCipher: Sized { type KeySize: ArrayLength; /// Create new block cipher instance from key with fixed size. - fn new(key: &Key) -> Self; + fn new(key: &BlockCipherKey) -> Self; /// Create new block cipher instance from key with variable size. /// /// Default implementation will accept only keys with length equal to /// `KeySize`, but some ciphers can accept range of key lengths. - fn new_varkey(key: &[u8]) -> Result { + fn new_var(key: &[u8]) -> Result { if key.len() != Self::KeySize::to_usize() { Err(InvalidLength) } else { @@ -57,7 +53,7 @@ pub trait BlockCipher { type ParBlocks: ArrayLength>; } -/// Encrypt-only functionality for block ciphers +/// Encrypt-only functionality for block ciphers. pub trait BlockEncrypt: BlockCipher { /// Encrypt block in-place fn encrypt_block(&self, block: &mut Block); @@ -94,7 +90,7 @@ pub trait BlockEncrypt: BlockCipher { } } -/// Decrypt-only functionality for block ciphers +/// Decrypt-only functionality for block ciphers. pub trait BlockDecrypt: BlockCipher { /// Decrypt block in-place fn decrypt_block(&self, block: &mut Block); diff --git a/cipher/src/common.rs b/cipher/src/common.rs new file mode 100644 index 000000000..7416c956a --- /dev/null +++ b/cipher/src/common.rs @@ -0,0 +1,79 @@ +use generic_array::{ArrayLength, GenericArray, typenum::Unsigned}; +use crate::{BlockCipher, NewBlockCipher, errors::InvalidLength}; + +/// Key for an algorithm that implements [`NewCipher`]. +pub type CipherKey = GenericArray::KeySize>; + +/// Nonce for an algorithm that implements [`NewCipher`]. +pub type Nonce = GenericArray::NonceSize>; + +/// Cipher creation trait. +/// +/// It can be used for creation of block modes, synchronous and asynchronous stream ciphers. +pub trait NewCipher: Sized { + /// Key size in bytes + type KeySize: ArrayLength; + + /// Nonce size in bytes + type NonceSize: ArrayLength; + + /// Create new stream cipher instance from variable length key and nonce. + fn new(key: &CipherKey, nonce: &Nonce) -> Self; + + /// Create new stream cipher instance from variable length key and nonce. + #[inline] + fn new_var(key: &[u8], nonce: &[u8]) -> Result { + let kl = Self::KeySize::to_usize(); + let nl = Self::NonceSize::to_usize(); + if key.len() != kl || nonce.len() != nl { + Err(InvalidLength) + } else { + let key = GenericArray::from_slice(key); + let nonce = GenericArray::from_slice(nonce); + Ok(Self::new(key, nonce)) + } + } +} + +/// Trait for types which can be initialized from a block cipher and nonce. +pub trait FromBlockCipher { + /// Block cipher + type BlockCipher: BlockCipher; + /// Nonce size in bytes + type NonceSize: ArrayLength; + + /// Instantiate a stream cipher from a block cipher + fn from_block_cipher( + cipher: Self::BlockCipher, + nonce: &GenericArray, + ) -> Self; +} + +impl NewCipher for C +where + C: FromBlockCipher, + C::BlockCipher: NewBlockCipher, +{ + type KeySize = <::BlockCipher as NewBlockCipher>::KeySize; + type NonceSize = ::NonceSize; + + fn new(key: &CipherKey, nonce: &Nonce) -> C { + C::from_block_cipher( + <::BlockCipher as NewBlockCipher>::new(key), + nonce, + ) + } + + fn new_var(key: &[u8], nonce: &[u8]) -> Result { + if nonce.len() != Self::NonceSize::USIZE { + Err(InvalidLength) + } else { + C::BlockCipher::new_var(key) + .map_err(|_| InvalidLength) + .map(|cipher| { + let nonce = GenericArray::from_slice(nonce); + Self::from_block_cipher(cipher, nonce) + }) + } + } +} diff --git a/cipher/src/dev.rs b/cipher/src/dev.rs new file mode 100644 index 000000000..0737a04e1 --- /dev/null +++ b/cipher/src/dev.rs @@ -0,0 +1,2 @@ +mod stream; +mod block; diff --git a/cipher/src/block/dev.rs b/cipher/src/dev/block.rs similarity index 91% rename from cipher/src/block/dev.rs rename to cipher/src/dev/block.rs index 8c4f2fda9..f8de973b0 100644 --- a/cipher/src/block/dev.rs +++ b/cipher/src/dev/block.rs @@ -9,13 +9,13 @@ macro_rules! block_cipher_test { ($name:ident, $test_name:expr, $cipher:ty) => { #[test] fn $name() { - use cipher::block::{ - dev::blobby::Blob3Iterator, BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, + use cipher::{ + blobby::Blob3Iterator, BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, }; use cipher::generic_array::{typenum::Unsigned, GenericArray}; fn run_test(key: &[u8], pt: &[u8], ct: &[u8]) -> bool { - let state = <$cipher as NewBlockCipher>::new_varkey(key).unwrap(); + let state = <$cipher as NewBlockCipher>::new_var(key).unwrap(); let mut block = GenericArray::clone_from_slice(pt); state.encrypt_block(&mut block); @@ -37,7 +37,7 @@ macro_rules! block_cipher_test { type Block = GenericArray; type ParBlock = GenericArray; - let state = <$cipher as NewBlockCipher>::new_varkey(key).unwrap(); + let state = <$cipher as NewBlockCipher>::new_var(key).unwrap(); let block = Block::clone_from_slice(pt); let mut blocks1 = ParBlock::default(); @@ -113,12 +113,12 @@ macro_rules! block_cipher_bench { ($cipher:path, $key_len:expr) => { extern crate test; - use cipher::block::{BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher}; + use cipher::{BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher}; use test::Bencher; #[bench] pub fn encrypt(bh: &mut Bencher) { - let state = <$cipher>::new_varkey(&[1u8; $key_len]).unwrap(); + let state = <$cipher>::new_var(&[1u8; $key_len]).unwrap(); let mut block = Default::default(); bh.iter(|| { @@ -130,7 +130,7 @@ macro_rules! block_cipher_bench { #[bench] pub fn decrypt(bh: &mut Bencher) { - let state = <$cipher>::new_varkey(&[1u8; $key_len]).unwrap(); + let state = <$cipher>::new_var(&[1u8; $key_len]).unwrap(); let mut block = Default::default(); bh.iter(|| { diff --git a/cipher/src/stream/dev.rs b/cipher/src/dev/stream.rs similarity index 93% rename from cipher/src/stream/dev.rs rename to cipher/src/dev/stream.rs index 92e5c9331..4f922bcb6 100644 --- a/cipher/src/stream/dev.rs +++ b/cipher/src/dev/stream.rs @@ -3,12 +3,12 @@ /// Test core functionality of synchronous stream cipher #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! stream_cipher_sync_test { +macro_rules! stream_cipher_test { ($name:ident, $cipher:ty, $test_name:expr) => { #[test] fn $name() { use cipher::generic_array::GenericArray; - use cipher::stream::{blobby::Blob4Iterator, NewStreamCipher, SyncStreamCipher}; + use cipher::{blobby::Blob4Iterator, NewCipher, StreamCipher}; let data = include_bytes!(concat!("data/", $test_name, ".blb")); for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() { @@ -44,7 +44,7 @@ macro_rules! stream_cipher_seek_test { #[test] fn $name() { use cipher::generic_array::GenericArray; - use cipher::stream::{NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek}; + use cipher::{NewCipher, StreamCipher, StreamCipherSeek}; fn get_cipher() -> $cipher { <$cipher>::new(&Default::default(), &Default::default()) @@ -97,7 +97,7 @@ macro_rules! stream_cipher_async_test { #[test] fn $name() { use cipher::generic_array::GenericArray; - use cipher::stream::{blobby::Blob4Iterator, NewStreamCipher, StreamCipher}; + use cipher::{blobby::Blob4Iterator, NewCipher, AsyncStreamCipher}; fn run_test( key: &[u8], @@ -172,8 +172,7 @@ macro_rules! stream_cipher_sync_bench { ($cipher:path) => { extern crate test; - use cipher::generic_array::GenericArray; - use cipher::stream::{NewStreamCipher, SyncStreamCipher}; + use cipher::{NewCipher, StreamCipher, generic_array::GenericArray}; use test::Bencher; #[inline(never)] @@ -225,8 +224,7 @@ macro_rules! stream_cipher_async_bench { ($cipher:path) => { extern crate test; - use cipher::generic_array::GenericArray; - use cipher::stream::{NewStreamCipher, StreamCipher}; + use cipher::{NewCipher, AsyncStreamCipher, generic_array::GenericArray}; use test::Bencher; #[inline(never)] diff --git a/cipher/src/errors.rs b/cipher/src/errors.rs index 089a7a077..787834e52 100644 --- a/cipher/src/errors.rs +++ b/cipher/src/errors.rs @@ -1,4 +1,4 @@ -//! Error types +//! Error types. use core::fmt; @@ -48,3 +48,16 @@ impl From for LoopError { #[cfg(feature = "std")] impl std::error::Error for OverflowError {} + +/// Block mode error. +#[derive(Clone, Copy, Debug)] +pub struct BlockModeError; + +impl fmt::Display for BlockModeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("BlockModeError") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for BlockModeError {} diff --git a/cipher/src/lib.rs b/cipher/src/lib.rs index 5a9dcd182..875bab243 100644 --- a/cipher/src/lib.rs +++ b/cipher/src/lib.rs @@ -15,15 +15,25 @@ #[cfg(feature = "std")] extern crate std; +#[cfg(feature = "block-mode")] +pub extern crate block_padding; +#[cfg(feature = "dev")] +pub extern crate blobby; -pub mod block; pub mod errors; -pub mod stream; +#[cfg(feature = "dev")] +mod dev; +mod block; +mod common; +#[cfg(feature = "block-padding")] +mod mode; +mod stream; pub use crate::{ - block::{ - BlockCipher, BlockDecrypt, BlockDecryptMut, BlockEncrypt, BlockEncryptMut, NewBlockCipher, - }, - stream::{NewStreamCipher, StreamCipher, SyncStreamCipher, SyncStreamCipherSeek}, + block::*, + common::*, + stream::*, }; +#[cfg(feature = "block-mode")] +pub use crate::mode::BlockModeCore; pub use generic_array::{self, typenum::consts}; diff --git a/cipher/src/mode.rs b/cipher/src/mode.rs new file mode 100644 index 000000000..cf0f715da --- /dev/null +++ b/cipher/src/mode.rs @@ -0,0 +1,84 @@ +use core::marker::PhantomData; +use block_padding::Padding; +use generic_array::{ArrayLength, GenericArray}; + +/// Block on which a [`BlockModeCore`] operates. +pub type Block = GenericArray::BlockSize>; + +/// Core trait for a block cipher mode of operation. +pub trait BlockModeCore { + /// Block size in bytes + type BlockSize: ArrayLength; + + /// Encrypt blocks of data + fn encrypt_blocks(&mut self, blocks: &mut [Block]); + + /// Decrypt blocks of data + fn decrypt_blocks(&mut self, blocks: &mut [Block]); +} + +pub struct BlockModeEncryptor{ + inner: M, + p: PhantomData

, +} + +/* + /// Encrypt message in-place. + /// + /// `&buffer[..pos]` is used as a message and `&buffer[pos..]` as a reserved + /// space for padding. The padding space should be big enough for padding, + /// otherwise method will return `Err(BlockModeError)`. + fn encrypt(mut self, buffer: &mut [u8], pos: usize) -> Result<&[u8], BlockModeError> { + let bs = C::BlockSize::to_usize(); + let buf = P::pad(buffer, pos, bs).map_err(|_| BlockModeError)?; + self.encrypt_blocks(to_blocks(buf)); + Ok(buf) + } + + /// Decrypt message in-place. + /// + /// Returns an error if `buffer` length is not multiple of block size and + /// if after decoding message has malformed padding. + fn decrypt(mut self, buffer: &mut [u8]) -> Result<&[u8], BlockModeError> { + let bs = C::BlockSize::to_usize(); + if buffer.len() % bs != 0 { + return Err(BlockModeError); + } + self.decrypt_blocks(to_blocks(buffer)); + P::unpad(buffer).map_err(|_| BlockModeError) + } + + /// Encrypt message and store result in vector. + #[cfg(feature = "alloc")] + fn encrypt_to_vec(mut self, plaintext: &[u8]) -> Vec { + let bs = C::BlockSize::to_usize(); + let pos = plaintext.len(); + let n = pos + bs; + let mut buf = Vec::with_capacity(n); + buf.extend_from_slice(plaintext); + // prepare space for padding + let block: Block = Default::default(); + buf.extend_from_slice(&block[..n - pos]); + + let n = P::pad(&mut buf, pos, bs) + .expect("enough space for padding is allocated") + .len(); + buf.truncate(n); + self.encrypt_blocks(to_blocks(&mut buf)); + buf + } + + /// Encrypt message and store result in vector. + #[cfg(feature = "alloc")] + fn decrypt_to_vec(mut self, ciphertext: &[u8]) -> Result, BlockModeError> { + let bs = C::BlockSize::to_usize(); + if ciphertext.len() % bs != 0 { + return Err(BlockModeError); + } + let mut buf = ciphertext.to_vec(); + self.decrypt_blocks(to_blocks(&mut buf)); + let n = P::unpad(&buf).map_err(|_| BlockModeError)?.len(); + buf.truncate(n); + Ok(buf) + } +*/ \ No newline at end of file diff --git a/cipher/src/stream.rs b/cipher/src/stream.rs index f5daf01e3..843ff56eb 100644 --- a/cipher/src/stream.rs +++ b/cipher/src/stream.rs @@ -3,56 +3,11 @@ //! See [RustCrypto/stream-ciphers](https://github.com/RustCrypto/stream-ciphers) //! for ciphers implementation. -#[cfg(feature = "dev")] -mod dev; - -#[cfg(feature = "dev")] -pub use blobby; - -use crate::{ - block::{BlockCipher, NewBlockCipher}, - errors::{InvalidLength, LoopError, OverflowError}, -}; +use crate::errors::{LoopError, OverflowError}; use core::convert::{TryFrom, TryInto}; -use generic_array::typenum::Unsigned; -use generic_array::{ArrayLength, GenericArray}; - -/// Key for an algorithm that implements [`NewStreamCipher`]. -pub type Key = GenericArray::KeySize>; - -/// Nonce for an algorithm that implements [`NewStreamCipher`]. -pub type Nonce = GenericArray::NonceSize>; - -/// Stream cipher creation trait. -/// -/// It can be used for creation of synchronous and asynchronous ciphers. -pub trait NewStreamCipher: Sized { - /// Key size in bytes - type KeySize: ArrayLength; - - /// Nonce size in bytes - type NonceSize: ArrayLength; - - /// Create new stream cipher instance from variable length key and nonce. - fn new(key: &Key, nonce: &Nonce) -> Self; - - /// Create new stream cipher instance from variable length key and nonce. - #[inline] - fn new_var(key: &[u8], nonce: &[u8]) -> Result { - let kl = Self::KeySize::to_usize(); - let nl = Self::NonceSize::to_usize(); - if key.len() != kl || nonce.len() != nl { - Err(InvalidLength) - } else { - let key = GenericArray::from_slice(key); - let nonce = GenericArray::from_slice(nonce); - Ok(Self::new(key, nonce)) - } - } -} /// Synchronous stream cipher core trait. -pub trait SyncStreamCipher { +pub trait StreamCipher { /// Apply keystream to the data. /// /// It will XOR generated keystream with the data, which can be both @@ -63,10 +18,7 @@ pub trait SyncStreamCipher { /// method will panic without modifying the provided `data`. #[inline] fn apply_keystream(&mut self, data: &mut [u8]) { - let res = self.try_apply_keystream(data); - if res.is_err() { - panic!("stream cipher loop detected"); - } + self.try_apply_keystream(data).unwrap(); } /// Apply keystream to the data, but return an error if end of a keystream @@ -82,7 +34,7 @@ pub trait SyncStreamCipher { /// Methods of this trait are generic over the [`SeekNum`] trait, which is /// implemented for primitive numeric types, i.e.: `i/u8`, `i/u16`, `i/u32`, /// `i/u64`, `i/u128`, and `i/usize`. -pub trait SyncStreamCipherSeek { +pub trait StreamCipherSeek { /// Try to get current keystream position /// /// Returns [`LoopError`] if position can not be represented by type `T` @@ -91,7 +43,7 @@ pub trait SyncStreamCipherSeek { /// Try to seek to the given position /// /// Returns [`LoopError`] if provided position value is bigger than - /// keystream leangth + /// keystream length. fn try_seek(&mut self, pos: T) -> Result<(), LoopError>; /// Get current keystream position @@ -111,12 +63,8 @@ pub trait SyncStreamCipherSeek { } } -/// Stream cipher core trait which covers both synchronous and asynchronous -/// ciphers. -/// -/// Note that for synchronous ciphers `encrypt` and `decrypt` are equivalent to -/// each other. -pub trait StreamCipher { +/// Asynchronous stream cipher core trait. +pub trait AsyncStreamCipher { /// Encrypt data in place. fn encrypt(&mut self, data: &mut [u8]); @@ -124,19 +72,7 @@ pub trait StreamCipher { fn decrypt(&mut self, data: &mut [u8]); } -impl StreamCipher for C { - #[inline(always)] - fn encrypt(&mut self, data: &mut [u8]) { - SyncStreamCipher::apply_keystream(self, data); - } - - #[inline(always)] - fn decrypt(&mut self, data: &mut [u8]) { - SyncStreamCipher::apply_keystream(self, data); - } -} - -impl SyncStreamCipher for &mut C { +impl StreamCipher for &mut C { #[inline] fn apply_keystream(&mut self, data: &mut [u8]) { C::apply_keystream(self, data); @@ -148,54 +84,11 @@ impl SyncStreamCipher for &mut C { } } -/// Trait for initializing a stream cipher from a block cipher -pub trait FromBlockCipher { - /// Block cipher - type BlockCipher: BlockCipher; - /// Nonce size in bytes - type NonceSize: ArrayLength; - - /// Instantiate a stream cipher from a block cipher - fn from_block_cipher( - cipher: Self::BlockCipher, - nonce: &GenericArray, - ) -> Self; -} - -impl NewStreamCipher for C -where - C: FromBlockCipher, - C::BlockCipher: NewBlockCipher, -{ - type KeySize = <::BlockCipher as NewBlockCipher>::KeySize; - type NonceSize = ::NonceSize; - - fn new(key: &Key, nonce: &Nonce) -> C { - C::from_block_cipher( - <::BlockCipher as NewBlockCipher>::new(key), - nonce, - ) - } - - fn new_var(key: &[u8], nonce: &[u8]) -> Result { - if nonce.len() != Self::NonceSize::USIZE { - Err(InvalidLength) - } else { - C::BlockCipher::new_varkey(key) - .map_err(|_| InvalidLength) - .map(|cipher| { - let nonce = GenericArray::from_slice(nonce); - Self::from_block_cipher(cipher, nonce) - }) - } - } -} - /// Trait implemented for numeric types which can be used with the -/// [`SyncStreamCipherSeek`] trait. +/// [`StreamCipherSeek`] trait. /// /// This trait is implemented for primitive numeric types, i.e. `i/u8`, -/// `i/u16`, `i/u32`, `i/u64`, `i/u128`, and `i/usize`. It is not intended +/// `u16`, `u32`, `u64`, `u128`, `usize`, and `i32`. It is not intended /// to be implemented in third-party crates. #[rustfmt::skip] pub trait SeekNum: @@ -221,8 +114,17 @@ macro_rules! impl_seek_num { impl SeekNum for $t { fn from_block_byte>(block: T, byte: u8, bs: u8) -> Result { debug_assert!(byte < bs); - let block = block.try_into().map_err(|_| OverflowError)?; - let pos = block.checked_mul(bs as Self).ok_or(OverflowError)? + (byte as Self); + let pos = block + .try_into() + .ok() + .and_then(|v| match byte == 0 { + true => Some(v), + // if `byte` is not zero, then block counter was incremented + false => v.checked_sub(1), + }) + .and_then(|v| v.checked_mul(bs as Self)) + .and_then(|v| v.checked_add(byte as Self)) + .ok_or(OverflowError)?; Ok(pos) } @@ -237,4 +139,4 @@ macro_rules! impl_seek_num { }; } -impl_seek_num! { u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 isize usize } +impl_seek_num! { u8 u16 u32 u64 u128 usize i32 }