diff --git a/Cargo.lock b/Cargo.lock index de2cf1a0b..91d3a73b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,7 +75,7 @@ source = "git+https://github.com/RustCrypto/utils#f5ac85f6aa02aa39614f62a6a1add9 [[package]] name = "block-buffer" version = "0.11.0-rc.4" -source = "git+https://github.com/RustCrypto/utils#f5ac85f6aa02aa39614f62a6a1add9468be67892" +source = "git+https://github.com/RustCrypto/utils?branch=block-buffer%2Fread-buf#79e12dfd745e732d24ba1566f705f35ba409edc6" dependencies = [ "hybrid-array", "zeroize", @@ -113,6 +113,7 @@ name = "cipher" version = "0.5.0-rc.0" dependencies = [ "blobby", + "block-buffer", "crypto-common", "inout", "zeroize", @@ -429,9 +430,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beef09f85ae72cea1ef96ba6870c51e6382ebfa4f0e85b643459331f3daa5be0" +checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" dependencies = [ "unicode-ident", ] diff --git a/Cargo.toml b/Cargo.toml index 7f57fc98b..63c918333 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,5 @@ signature = { path = "signature" } blobby = { git = "https://github.com/RustCrypto/utils" } # https://github.com/RustCrypto/utils/pull/1192 # https://github.com/RustCrypto/utils/pull/1200 -block-buffer = { git = "https://github.com/RustCrypto/utils" } +# https://github.com/RustCrypto/utils/pull/1201 +block-buffer = { git = "https://github.com/RustCrypto/utils", branch = "block-buffer/read-buf" } diff --git a/cipher/Cargo.toml b/cipher/Cargo.toml index 393703371..ac7cedecb 100644 --- a/cipher/Cargo.toml +++ b/cipher/Cargo.toml @@ -18,16 +18,18 @@ inout = "0.2.0-rc.4" # optional dependencies blobby = { version = "0.4.0-pre.0", optional = true } +block-buffer = { version = "0.11.0-rc.4", optional = true} zeroize = { version = "1.8", optional = true, default-features = false } [features] alloc = [] block-padding = ["inout/block-padding"] +stream-wrapper = ["block-buffer"] # Enable random key and IV generation methods rand_core = ["crypto-common/rand_core"] os_rng = ["crypto-common/os_rng", "rand_core"] dev = ["blobby"] -zeroize = ["dep:zeroize", "crypto-common/zeroize"] +zeroize = ["dep:zeroize", "crypto-common/zeroize", "block-buffer?/zeroize"] [package.metadata.docs.rs] all-features = true diff --git a/cipher/src/dev/stream.rs b/cipher/src/dev/stream.rs index 00dc14f54..50e13b28f 100644 --- a/cipher/src/dev/stream.rs +++ b/cipher/src/dev/stream.rs @@ -57,8 +57,8 @@ macro_rules! stream_cipher_test { ); for (i, tv) in TEST_VECTORS.iter().enumerate() { - let res = $crate::dev::stream::stream_cipher_test(tv); - if Err(reason) = res { + let res = $crate::dev::stream::stream_cipher_test::<$cipher>(tv); + if let Err(reason) = res { panic!( "\n\ Failed test #{i}\n\ diff --git a/cipher/src/lib.rs b/cipher/src/lib.rs index 009a8b8a9..af95d9fc7 100644 --- a/cipher/src/lib.rs +++ b/cipher/src/lib.rs @@ -17,6 +17,7 @@ unused_lifetimes, missing_debug_implementations )] +#![forbid(unsafe_code)] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/cipher/src/stream.rs b/cipher/src/stream.rs index a1ba34347..f6169595d 100644 --- a/cipher/src/stream.rs +++ b/cipher/src/stream.rs @@ -9,6 +9,7 @@ use inout::{InOutBuf, NotEqualError}; mod core_api; mod errors; +#[cfg(feature = "stream-wrapper")] mod wrapper; pub use core_api::{ @@ -16,6 +17,7 @@ pub use core_api::{ StreamCipherSeekCore, }; pub use errors::{OverflowError, StreamCipherError}; +#[cfg(feature = "stream-wrapper")] pub use wrapper::StreamCipherCoreWrapper; /// Marker trait for block-level asynchronous stream ciphers diff --git a/cipher/src/stream/wrapper.rs b/cipher/src/stream/wrapper.rs index 3dbc7e1b5..5789cdddd 100644 --- a/cipher/src/stream/wrapper.rs +++ b/cipher/src/stream/wrapper.rs @@ -1,29 +1,22 @@ use super::{ - Block, OverflowError, SeekNum, StreamCipher, StreamCipherCore, StreamCipherSeek, - StreamCipherSeekCore, errors::StreamCipherError, + OverflowError, SeekNum, StreamCipher, StreamCipherCore, StreamCipherSeek, StreamCipherSeekCore, + errors::StreamCipherError, }; +use block_buffer::ReadBuffer; use core::fmt; use crypto_common::{ Iv, IvSizeUser, Key, KeyInit, KeyIvInit, KeySizeUser, array::Array, typenum::Unsigned, }; use inout::InOutBuf; #[cfg(feature = "zeroize")] -use zeroize::{Zeroize, ZeroizeOnDrop}; +use zeroize::ZeroizeOnDrop; /// Buffering wrapper around a [`StreamCipherCore`] implementation. /// /// It handles data buffering and implements the slice-based traits. pub struct StreamCipherCoreWrapper { core: T, - // First byte is used as position - buffer: Block, -} - -impl Default for StreamCipherCoreWrapper { - #[inline] - fn default() -> Self { - Self::from_core(T::default()) - } + buffer: ReadBuffer, } impl Clone for StreamCipherCoreWrapper { @@ -38,70 +31,19 @@ impl Clone for StreamCipherCoreWrapper { impl fmt::Debug for StreamCipherCoreWrapper { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let pos = self.get_pos().into(); - let buf_data = &self.buffer[pos..]; f.debug_struct("StreamCipherCoreWrapper") - .field("core", &self.core) - .field("buffer_data", &buf_data) - .finish() + .finish_non_exhaustive() } } impl StreamCipherCoreWrapper { - /// Return reference to the core type. - pub fn get_core(&self) -> &T { - &self.core - } - - /// Return reference to the core type. - pub fn from_core(core: T) -> Self { - let mut buffer: Block = Default::default(); - buffer[0] = T::BlockSize::U8; - Self { core, buffer } - } - - /// Return current cursor position. - #[inline] - fn get_pos(&self) -> u8 { - let pos = self.buffer[0]; - if pos == 0 || pos > T::BlockSize::U8 { - debug_assert!(false); - // SAFETY: `pos` never breaks the invariant - unsafe { - core::hint::unreachable_unchecked(); - } - } - pos - } - - /// Set buffer position without checking that it's smaller - /// than buffer size. - /// - /// # Safety - /// `pos` MUST be bigger than zero and smaller or equal to `T::BlockSize::USIZE`. - #[inline] - unsafe fn set_pos_unchecked(&mut self, pos: usize) { - debug_assert!(pos != 0 && pos <= T::BlockSize::USIZE); - // Block size is always smaller than 256 because of the `BlockSizes` bound, - // so if the safety condition is satisfied, the `as` cast does not truncate - // any non-zero bits. - self.buffer[0] = pos as u8; - } - - /// Return number of remaining bytes in the internal buffer. - #[inline] - fn remaining(&self) -> u8 { - // This never underflows because of the safety invariant - T::BlockSize::U8 - self.get_pos() - } - fn check_remaining(&self, data_len: usize) -> Result<(), StreamCipherError> { let rem_blocks = match self.core.remaining_blocks() { Some(v) => v, None => return Ok(()), }; - let buf_rem = usize::from(self.remaining()); + let buf_rem = self.buffer.remaining(); let data_len = match data_len.checked_sub(buf_rem) { Some(0) | None => return Ok(()), Some(res) => res, @@ -121,106 +63,46 @@ impl StreamCipher for StreamCipherCoreWrapper { #[inline] fn try_apply_keystream_inout( &mut self, - mut data: InOutBuf<'_, '_, u8>, + data: InOutBuf<'_, '_, u8>, ) -> Result<(), StreamCipherError> { self.check_remaining(data.len())?; - let pos = usize::from(self.get_pos()); - let rem = usize::from(self.remaining()); - let data_len = data.len(); - - if rem != 0 { - if data_len <= rem { - data.xor_in2out(&self.buffer[pos..][..data_len]); - // SAFETY: we have checked that `data_len` is less or equal to length - // of remaining keystream data, thus `pos + data_len` can not be bigger - // than block size. Since `pos` is never zero, `pos + data_len` can not - // be zero. Thus `pos + data_len` satisfies the safety invariant required - // by `set_pos_unchecked`. - unsafe { - self.set_pos_unchecked(pos + data_len); - } - return Ok(()); - } - let (mut left, right) = data.split_at(rem); - data = right; - left.xor_in2out(&self.buffer[pos..]); - } + let head_ks = self.buffer.read_cached(data.len()); + let (mut head, data) = data.split_at(head_ks.len()); let (blocks, mut tail) = data.into_chunks(); - self.core.apply_keystream_blocks_inout(blocks); - let new_pos = if tail.is_empty() { - T::BlockSize::USIZE - } else { - // Note that we temporarily write a pseudo-random byte into - // the first byte of `self.buffer`. It may break the safety invariant, - // but after XORing keystream block with `tail`, we immediately - // overwrite the first byte with a correct value. - self.core.write_keystream_block(&mut self.buffer); - tail.xor_in2out(&self.buffer[..tail.len()]); - tail.len() - }; + head.xor_in2out(head_ks); + self.core.apply_keystream_blocks_inout(blocks); - // SAFETY: `into_chunks` always returns tail with size - // less than block size. If `tail.len()` is zero, we replace - // it with block size. Thus the invariant required by - // `set_pos_unchecked` is satisfied. - unsafe { - self.set_pos_unchecked(new_pos); - } + self.buffer.write_block( + tail.len(), + |b| self.core.write_keystream_block(b), + |tail_ks| { + tail.xor_in2out(tail_ks); + }, + ); Ok(()) } #[inline] - fn try_write_keystream(&mut self, mut data: &mut [u8]) -> Result<(), StreamCipherError> { + fn try_write_keystream(&mut self, data: &mut [u8]) -> Result<(), StreamCipherError> { self.check_remaining(data.len())?; - let pos = usize::from(self.get_pos()); - let rem = usize::from(self.remaining()); - let data_len = data.len(); - - if rem != 0 { - if data_len <= rem { - data.copy_from_slice(&self.buffer[pos..][..data_len]); - // SAFETY: we have checked that `data_len` is less or equal to length - // of remaining keystream data, thus `pos + data_len` can not be bigger - // than block size. Since `pos` is never zero, `pos + data_len` can not - // be zero. Thus `pos + data_len` satisfies the safety invariant required - // by `set_pos_unchecked`. - unsafe { - self.set_pos_unchecked(pos + data_len); - } - return Ok(()); - } - let (left, right) = data.split_at_mut(rem); - data = right; - left.copy_from_slice(&self.buffer[pos..]); - } + let head_ks = self.buffer.read_cached(data.len()); + let (head, data) = data.split_at_mut(head_ks.len()); let (blocks, tail) = Array::slice_as_chunks_mut(data); - self.core.write_keystream_blocks(blocks); - let new_pos = if tail.is_empty() { - T::BlockSize::USIZE - } else { - // Note that we temporarily write a pseudo-random byte into - // the first byte of `self.buffer`. It may break the safety invariant, - // but after writing keystream block with `tail`, we immediately - // overwrite the first byte with a correct value. - self.core.write_keystream_block(&mut self.buffer); - tail.copy_from_slice(&self.buffer[..tail.len()]); - tail.len() - }; + head.copy_from_slice(head_ks); + self.core.write_keystream_blocks(blocks); - // SAFETY: `into_chunks` always returns tail with size - // less than block size. If `tail.len()` is zero, we replace - // it with block size. Thus the invariant required by - // `set_pos_unchecked` is satisfied. - unsafe { - self.set_pos_unchecked(new_pos); - } + self.buffer.write_block( + tail.len(), + |b| self.core.write_keystream_block(b), + |tail_ks| tail.copy_from_slice(tail_ks), + ); Ok(()) } @@ -228,7 +110,8 @@ impl StreamCipher for StreamCipherCoreWrapper { impl StreamCipherSeek for StreamCipherCoreWrapper { fn try_current_pos(&self) -> Result { - let pos = self.get_pos(); + let pos = u8::try_from(self.buffer.get_pos()) + .expect("buffer position is always smaller than 256"); SN::from_block_byte(self.core.get_block_pos(), pos, T::BlockSize::U8) } @@ -239,19 +122,14 @@ impl StreamCipherSeek for StreamCipherCoreWrapper { assert!(byte_pos < T::BlockSize::U8); self.core.set_block_pos(block_pos); - let new_pos = if byte_pos != 0 { - // See comment in `try_apply_keystream_inout` for use of `write_keystream_block` - self.core.write_keystream_block(&mut self.buffer); - byte_pos.into() - } else { - T::BlockSize::USIZE - }; - // SAFETY: we assert that `byte_pos` is always smaller than block size. - // If `byte_pos` is zero, we replace it with block size. Thus the invariant - // required by `set_pos_unchecked` is satisfied. - unsafe { - self.set_pos_unchecked(new_pos); - } + + self.buffer.reset(); + + self.buffer.write_block( + usize::from(byte_pos), + |b| self.core.write_keystream_block(b), + |_| {}, + ); Ok(()) } } @@ -272,11 +150,9 @@ impl IvSizeUser for StreamCipherCoreWrapper impl KeyIvInit for StreamCipherCoreWrapper { #[inline] fn new(key: &Key, iv: &Iv) -> Self { - let mut buffer = Block::::default(); - buffer[0] = T::BlockSize::U8; Self { core: T::new(key, iv), - buffer, + buffer: Default::default(), } } } @@ -284,22 +160,21 @@ impl KeyIvInit for StreamCipherCoreWrapper { impl KeyInit for StreamCipherCoreWrapper { #[inline] fn new(key: &Key) -> Self { - let mut buffer = Block::::default(); - buffer[0] = T::BlockSize::U8; Self { core: T::new(key), - buffer, + buffer: Default::default(), } } } #[cfg(feature = "zeroize")] -impl Drop for StreamCipherCoreWrapper { - fn drop(&mut self) { - // If present, `core` will be zeroized by its own `Drop`. - self.buffer.zeroize(); - } -} +impl ZeroizeOnDrop for StreamCipherCoreWrapper {} +// Assert that `ReadBuffer` implements `ZeroizeOnDrop` #[cfg(feature = "zeroize")] -impl ZeroizeOnDrop for StreamCipherCoreWrapper {} +const _: () = { + #[allow(dead_code)] + fn check_buffer(v: &ReadBuffer) { + let _ = v as &dyn crate::zeroize::ZeroizeOnDrop; + } +};