diff --git a/Cargo.lock b/Cargo.lock index 388b5e31..30f8a0bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.9.0" +version = "0.10.0-pre" dependencies = [ "block-padding", "generic-array", @@ -17,7 +17,10 @@ dependencies = [ [[package]] name = "block-padding" -version = "0.2.1" +version = "0.3.0-pre" +dependencies = [ + "generic-array", +] [[package]] name = "collectable" diff --git a/block-buffer/CHANGELOG.md b/block-buffer/CHANGELOG.md new file mode 100644 index 00000000..0e0f2560 --- /dev/null +++ b/block-buffer/CHANGELOG.md @@ -0,0 +1,18 @@ +# 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.10.0 (2020-12-08) +### Changed +- Rename `input_block(s)` methods to `digest_block(s)`. ([#113]) +- Upgrade the `block-padding` dependency to v0.3. ([#113]) + +### Added +- The `xor_data` method. ([#113]) + +### Removed +- The `input_lazy` method. ([#113]) + +[#113]: https://github.com/RustCrypto/utils/pull/113 diff --git a/block-buffer/Cargo.toml b/block-buffer/Cargo.toml index 67668561..9e50eb5b 100644 --- a/block-buffer/Cargo.toml +++ b/block-buffer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "block-buffer" -version = "0.9.0" +version = "0.10.0-pre" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" description = "Fixed size buffer for block processing of data" @@ -11,5 +11,5 @@ categories = ["cryptography", "no-std"] edition = "2018" [dependencies] -block-padding = { version = "0.2.0", path = "../block-padding", optional = true } +block-padding = { version = "0.3.0-pre", path = "../block-padding", optional = true } generic-array = "0.14" diff --git a/block-buffer/src/lib.rs b/block-buffer/src/lib.rs index 0c598481..3b267589 100644 --- a/block-buffer/src/lib.rs +++ b/block-buffer/src/lib.rs @@ -1,241 +1,323 @@ +//! Fixed size buffer for block processing of data. #![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg" +)] +#![warn(missing_docs, rust_2018_idioms)] + #[cfg(feature = "block-padding")] pub use block_padding; pub use generic_array; #[cfg(feature = "block-padding")] -use block_padding::{PadError, Padding}; +use block_padding::Padding; use core::{convert::TryInto, slice}; -use generic_array::{ArrayLength, GenericArray}; +use generic_array::{typenum::U1, ArrayLength, GenericArray}; + +/// Block on which a `BlockBuffer` operates. +pub type Block = GenericArray; +/// Blocks being acted over in parallel. +pub type ParBlock = GenericArray, ParBlocks>; -/// Buffer for block processing of data +/// Buffer for block processing of data. #[derive(Clone, Default)] pub struct BlockBuffer> { - buffer: GenericArray, + buffer: Block, pos: usize, } impl> BlockBuffer { - /// Process data in `input` in blocks of size `BlockSize` using function `f`. + /// Digest data in `input` in blocks of size `BlockSize` using + /// the `compress` function, which accepts a block reference. #[inline] - pub fn input_block( - &mut self, - mut input: &[u8], - mut f: impl FnMut(&GenericArray), - ) { + pub fn digest_block(&mut self, mut input: &[u8], mut compress: impl FnMut(&Block)) { + let pos = self.get_pos(); let r = self.remaining(); - if input.len() < r { - let n = input.len(); - self.buffer[self.pos..self.pos + n].copy_from_slice(input); - self.pos += n; + let n = input.len(); + if n < r { + // double slicing allows to remove panic branches + self.buffer[pos..][..n].copy_from_slice(input); + self.set_pos_unchecked(pos + n); return; } - if self.pos != 0 && input.len() >= r { - let (l, r) = input.split_at(r); - input = r; - self.buffer[self.pos..].copy_from_slice(l); - f(&self.buffer); + if pos != 0 { + let (left, right) = input.split_at(r); + input = right; + self.buffer[pos..].copy_from_slice(left); + compress(&self.buffer); } let mut chunks_iter = input.chunks_exact(self.size()); for chunk in &mut chunks_iter { - f(chunk.try_into().unwrap()); + compress(chunk.try_into().unwrap()); } let rem = chunks_iter.remainder(); // Copy any remaining data into the buffer. self.buffer[..rem.len()].copy_from_slice(rem); - self.pos = rem.len(); + self.set_pos_unchecked(rem.len()); } - /// Process data in `input` in blocks of size `BlockSize` using function `f`, which accepts - /// slice of blocks. + /// Digest data in `input` in blocks of size `BlockSize` using + /// the `compress` function, which accepts slice of blocks. #[inline] - pub fn input_blocks( + pub fn digest_blocks( &mut self, mut input: &[u8], - mut f: impl FnMut(&[GenericArray]), + mut compress: impl FnMut(&[Block]), ) { + let pos = self.get_pos(); let r = self.remaining(); - if input.len() < r { - let n = input.len(); - self.buffer[self.pos..self.pos + n].copy_from_slice(input); - self.pos += n; + let n = input.len(); + if n < r { + // double slicing allows to remove panic branches + self.buffer[pos..][..n].copy_from_slice(input); + self.set_pos_unchecked(pos + n); return; } - if self.pos != 0 && input.len() >= r { - let (l, r) = input.split_at(r); - input = r; - self.buffer[self.pos..].copy_from_slice(l); - self.pos = 0; - f(slice::from_ref(&self.buffer)); + if pos != 0 { + let (left, right) = input.split_at(r); + input = right; + self.buffer[pos..].copy_from_slice(left); + compress(slice::from_ref(&self.buffer)); } - // While we have at least a full buffer size chunks's worth of data, - // process its data without copying into the buffer - let n_blocks = input.len() / self.size(); - let (left, right) = input.split_at(n_blocks * self.size()); - // SAFETY: we guarantee that `blocks` does not point outside of `input` - let blocks = unsafe { - slice::from_raw_parts( - left.as_ptr() as *const GenericArray, - n_blocks, - ) - }; - f(blocks); - - // Copy remaining data into the buffer. - let n = right.len(); - self.buffer[..n].copy_from_slice(right); - self.pos = n; - } - - /// Variant that doesn't flush the buffer until there's additional - /// data to be processed. Suitable for tweakable block ciphers - /// like Threefish that need to know whether a block is the *last* - /// data block before processing it. - #[inline] - pub fn input_lazy( + let (blocks, leftover) = to_blocks(input); + compress(blocks); + + let n = leftover.len(); + self.buffer[..n].copy_from_slice(leftover); + self.set_pos_unchecked(n); + } + + /// Core method for `xor_data` and `set_data` methods. + /// + /// If `N` is equal to 1, the `gen_blocks` function is not used. + fn process_data>>( &mut self, - mut input: &[u8], - mut f: impl FnMut(&GenericArray), + mut data: &mut [u8], + state: &mut S, + mut process: impl FnMut(&mut [u8], &[u8]), + mut gen_block: impl FnMut(&mut S) -> Block, + mut gen_blocks: impl FnMut(&mut S) -> ParBlock, ) { + let pos = self.get_pos(); let r = self.remaining(); - if input.len() <= r { - let n = input.len(); - self.buffer[self.pos..self.pos + n].copy_from_slice(input); - self.pos += n; - return; + let n = data.len(); + if pos != 0 { + if n < r { + // double slicing allows to remove panic branches + process(data, &self.buffer[pos..][..n]); + self.set_pos_unchecked(pos + n); + return; + } + let (left, right) = data.split_at_mut(r); + data = right; + process(left, &self.buffer[pos..]); } - if self.pos != 0 && input.len() > r { - let (l, r) = input.split_at(r); - input = r; - self.buffer[self.pos..].copy_from_slice(l); - f(&self.buffer); + + let (par_blocks, blocks, leftover) = to_blocks_mut::(data); + for pb in par_blocks { + let blocks = gen_blocks(state); + for i in 0..N::USIZE { + process(&mut pb[i], &blocks[i]); + } + } + + for block in blocks { + process(block, &gen_block(state)); } - while input.len() > self.size() { - let (block, r) = input.split_at(self.size()); - input = r; - f(block.try_into().unwrap()); + let n = leftover.len(); + if n != 0 { + let block = gen_block(state); + process(leftover, &block[..n]); + self.buffer = block; } + self.set_pos_unchecked(n); + } - self.buffer[..input.len()].copy_from_slice(input); - self.pos = input.len(); + /// XORs `data` using the provided state and block generation functions. + /// + /// This method is intended for stream cipher implementations. If `N` is + /// equal to 1, the `gen_blocks` function is not used. + #[inline] + pub fn par_xor_data>>( + &mut self, + data: &mut [u8], + state: &mut S, + gen_block: impl FnMut(&mut S) -> Block, + gen_blocks: impl FnMut(&mut S) -> ParBlock, + ) { + self.process_data(data, state, xor, gen_block, gen_blocks); } - /// Pad buffer with `prefix` and make sure that internall buffer - /// has at least `up_to` free bytes. All remaining bytes get - /// zeroed-out. + /// Simplified version of the [`par_xor_data`] method, with `N = 1`. #[inline] - fn digest_pad(&mut self, up_to: usize, mut f: impl FnMut(&GenericArray)) { - if self.pos == self.size() { - f(&self.buffer); - self.pos = 0; - } - self.buffer[self.pos] = 0x80; - self.pos += 1; + pub fn xor_data(&mut self, data: &mut [u8], mut gen_block: impl FnMut() -> Block) { + // note: the unrachable panic should be removed by compiler since + // with `N = 1` the second closure is not used + self.process_data(data, &mut gen_block, xor, |f| f(), unreachable); + } - set_zero(&mut self.buffer[self.pos..]); + /// Set `data` to generated blocks. + #[inline] + pub fn set_data(&mut self, data: &mut [u8], mut gen_block: impl FnMut() -> Block) { + // note: the unrachable panic should be removed by compiler since + // with `N = 1` the second closure is not used + self.process_data(data, &mut gen_block, set, |f| f(), unreachable); + } + + /// Compress remaining data after padding it with `0x80`, zeros and + /// the `suffix` bytes. If there is not enough unused space, `compress` + /// will be called twice. + #[inline(always)] + fn digest_pad(&mut self, suffix: &[u8], mut compress: impl FnMut(&Block)) { + let pos = self.get_pos(); + self.buffer[pos] = 0x80; + for b in &mut self.buffer[pos + 1..] { + *b = 0; + } - if self.remaining() < up_to { - f(&self.buffer); - set_zero(&mut self.buffer[..self.pos]); + let n = self.size() - suffix.len(); + if self.size() - pos - 1 < suffix.len() { + compress(&self.buffer); + let mut block: Block = Default::default(); + block[n..].copy_from_slice(suffix); + compress(&block); + } else { + self.buffer[n..].copy_from_slice(suffix); + compress(&self.buffer); } + self.set_pos_unchecked(0) } - /// Pad message with 0x80, zeros and 64-bit message length - /// using big-endian byte order + /// Pad message with 0x80, zeros and 64-bit message length using + /// big-endian byte order. #[inline] - pub fn len64_padding_be( - &mut self, - data_len: u64, - mut f: impl FnMut(&GenericArray), - ) { - self.digest_pad(8, &mut f); - let b = data_len.to_be_bytes(); - let n = self.buffer.len() - b.len(); - self.buffer[n..].copy_from_slice(&b); - f(&self.buffer); - self.pos = 0; + pub fn len64_padding_be(&mut self, data_len: u64, compress: impl FnMut(&Block)) { + self.digest_pad(&data_len.to_be_bytes(), compress); } - /// Pad message with 0x80, zeros and 64-bit message length - /// using little-endian byte order + /// Pad message with 0x80, zeros and 64-bit message length using + /// little-endian byte order. #[inline] - pub fn len64_padding_le( - &mut self, - data_len: u64, - mut f: impl FnMut(&GenericArray), - ) { - self.digest_pad(8, &mut f); - let b = data_len.to_le_bytes(); - let n = self.buffer.len() - b.len(); - self.buffer[n..].copy_from_slice(&b); - f(&self.buffer); - self.pos = 0; + pub fn len64_padding_le(&mut self, data_len: u64, compress: impl FnMut(&Block)) { + self.digest_pad(&data_len.to_le_bytes(), compress); } - /// Pad message with 0x80, zeros and 128-bit message length - /// using big-endian byte order + /// Pad message with 0x80, zeros and 128-bit message length using + /// big-endian byte order. #[inline] - pub fn len128_padding_be( - &mut self, - data_len: u128, - mut f: impl FnMut(&GenericArray), - ) { - self.digest_pad(16, &mut f); - let b = data_len.to_be_bytes(); - let n = self.buffer.len() - b.len(); - self.buffer[n..].copy_from_slice(&b); - f(&self.buffer); - self.pos = 0; + pub fn len128_padding_be(&mut self, data_len: u128, compress: impl FnMut(&Block)) { + self.digest_pad(&data_len.to_be_bytes(), compress); } - /// Pad message with a given padding `P` - /// - /// Returns `PadError` if internall buffer is full, which can only happen if - /// `input_lazy` was used. + /// Pad message with a given padding `P`. #[cfg(feature = "block-padding")] #[inline] - pub fn pad_with(&mut self) -> Result<&mut GenericArray, PadError> { - P::pad_block(&mut self.buffer[..], self.pos)?; - self.pos = 0; - Ok(&mut self.buffer) + pub fn pad_with>(&mut self) -> &mut Block { + let pos = self.get_pos(); + P::pad(&mut self.buffer, pos); + self.set_pos_unchecked(0); + &mut self.buffer } - /// Return size of the internall buffer in bytes + /// Return size of the internall buffer in bytes. #[inline] pub fn size(&self) -> usize { - BlockSize::to_usize() - } - - /// Return current cursor position - #[inline] - pub fn position(&self) -> usize { - self.pos + BlockSize::USIZE } - /// Return number of remaining bytes in the internall buffer + /// Return number of remaining bytes in the internall buffer. #[inline] pub fn remaining(&self) -> usize { - self.size() - self.pos + self.size() - self.get_pos() } - /// Reset buffer by setting cursor position to zero + /// Reset buffer by setting cursor position to zero. #[inline] pub fn reset(&mut self) { self.pos = 0 } + + /// Return current cursor position. + #[inline] + pub fn get_pos(&self) -> usize { + debug_assert!(self.pos < BlockSize::USIZE); + if self.pos >= BlockSize::USIZE { + // SAFETY: `pos` is set only to values smaller than block size + unsafe { core::hint::unreachable_unchecked() } + } + self.pos + } + + /// Set buffer content and cursor position. + /// + /// # Panics + /// If `pos` is bigger or equal to block size. + pub fn set(&mut self, buf: Block, pos: usize) { + assert!(pos < BlockSize::USIZE); + self.buffer = buf; + self.pos = pos; + } + + #[inline] + fn set_pos_unchecked(&mut self, pos: usize) { + debug_assert!(pos < BlockSize::USIZE); + self.pos = pos; + } +} + +#[inline(always)] +fn xor(a: &mut [u8], b: &[u8]) { + debug_assert_eq!(a.len(), b.len()); + a.iter_mut().zip(b.iter()).for_each(|(a, &b)| *a ^= b); +} + +#[inline(always)] +fn set(a: &mut [u8], b: &[u8]) { + a.copy_from_slice(b); } -/// Sets all bytes in `dst` to zero #[inline(always)] -fn set_zero(dst: &mut [u8]) { - // SAFETY: we overwrite valid memory behind `dst` - // note: loop is not used here because it produces - // unnecessary branch which tests for zero-length slices +fn to_blocks>(data: &[u8]) -> (&[Block], &[u8]) { + let nb = data.len() / N::USIZE; + let (left, right) = data.split_at(nb * N::USIZE); + let p = left.as_ptr() as *const Block; + // SAFETY: we guarantee that `blocks` does not point outside of `data` + let blocks = unsafe { slice::from_raw_parts(p, nb) }; + (blocks, right) +} + +#[allow(clippy::type_complexity)] +#[inline(always)] +fn to_blocks_mut, M: ArrayLength>>( + data: &mut [u8], +) -> (&mut [ParBlock], &mut [Block], &mut [u8]) { + let b_size = N::USIZE; + let pb_size = N::USIZE * M::USIZE; + let npb = match M::USIZE { + 1 => 0, + _ => data.len() / pb_size, + }; + let (pb_slice, data) = data.split_at_mut(npb * pb_size); + let nb = data.len() / b_size; + let (b_slice, data) = data.split_at_mut(nb * b_size); + let pb_ptr = pb_slice.as_mut_ptr() as *mut ParBlock; + let b_ptr = b_slice.as_mut_ptr() as *mut Block; + // SAFETY: we guarantee that the resulting values do not overlap and do not + // point outside of the input slice unsafe { - core::ptr::write_bytes(dst.as_mut_ptr(), 0, dst.len()); + ( + slice::from_raw_parts_mut(pb_ptr, npb), + slice::from_raw_parts_mut(b_ptr, nb), + data, + ) } } + +fn unreachable>(_: &mut S) -> ParBlock { + unreachable!(); +} diff --git a/block-padding/CHANGELOG.md b/block-padding/CHANGELOG.md index da648be0..6d3295a7 100644 --- a/block-padding/CHANGELOG.md +++ b/block-padding/CHANGELOG.md @@ -4,9 +4,15 @@ 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.3.0 (2020-12-08) +### Changed +- The `Padding` trait methods now work with blocks instead of byte slices. ([#113]) + +[#113]: https://github.com/RustCrypto/utils/pull/113 + ## 0.2.1 (2020-08-14) ### Added -- `Copy`, `Clone`, and `Debug` trait implementations for padding types ([#78]) +- `Copy`, `Clone`, and `Debug` trait implementations for padding types. ([#78]) [#78]: https://github.com/RustCrypto/utils/pull/78 diff --git a/block-padding/Cargo.toml b/block-padding/Cargo.toml index 64f99e31..bb8b77ab 100644 --- a/block-padding/Cargo.toml +++ b/block-padding/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "block-padding" -version = "0.2.1" +version = "0.3.0-pre" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" description = "Padding and unpadding of messages divided into blocks." @@ -9,3 +9,9 @@ repository = "https://github.com/RustCrypto/utils" keywords = ["padding", "pkcs7", "ansix923", "iso7816"] categories = ["cryptography", "no-std"] edition = "2018" + +[dependencies] +generic-array = "0.14" + +[features] +std = [] diff --git a/block-padding/src/lib.rs b/block-padding/src/lib.rs index 29487fec..ff70ed83 100644 --- a/block-padding/src/lib.rs +++ b/block-padding/src/lib.rs @@ -4,108 +4,79 @@ //! operations. Additionally several common padding schemes are available out //! of the box. #![no_std] -#![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg" +)] +#![forbid(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] -/// Error for indicating failed padding operation -#[derive(Clone, Copy, Debug)] -pub struct PadError; +#[cfg(feature = "std")] +extern crate std; -/// Error for indicating failed unpadding operation -#[derive(Clone, Copy, Debug)] -pub struct UnpadError; +use core::fmt; +pub use generic_array; +use generic_array::{ArrayLength, GenericArray}; + +/// Block size. +pub type Block = GenericArray; /// Trait for padding messages divided into blocks -pub trait Padding { - /// Pads `block` filled with data up to `pos`. +pub trait Padding> { + /// Pads `block` filled with data up to `pos` (i.e length of a message + /// stored in the block is equal to `pos`). /// - /// `pos` should be inside of the block and block must not be full, i.e. - /// `pos < block.len()` must be true. Otherwise method will return - /// `PadError`. Some potentially irreversible padding schemes can allow - /// padding of the full block, in this case aforementioned condition is - /// relaxed to `pos <= block.len()`. - fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError>; + /// # Panics + /// If `pos` is bigger than `BlockSize`. Most paddin algorithms also + /// panic if they are equal. + fn pad(block: &mut Block, pos: usize); - /// Pads message with length `pos` in the provided buffer. + /// Unpad data in the `block`. /// - /// `&buf[..pos]` is perceived as the message, the buffer must contain - /// enough leftover space for padding: `block_size - (pos % block_size)` - /// extra bytes must be available. Otherwise method will return - /// `PadError`. - fn pad(buf: &mut [u8], pos: usize, block_size: usize) -> Result<&mut [u8], PadError> { - let bs = block_size * (pos / block_size); - if buf.len() < bs || buf.len() - bs < block_size { - Err(PadError)? - } - Self::pad_block(&mut buf[bs..bs + block_size], pos - bs)?; - Ok(&mut buf[..bs + block_size]) - } - - /// Unpad given `data` by truncating it according to the used padding. - /// In case of the malformed padding will return `UnpadError` - fn unpad(data: &[u8]) -> Result<&[u8], UnpadError>; + /// Returns `Err(UnpadError)` if the block containts malformed padding. + fn unpad(block: &Block) -> Result<&[u8], UnpadError>; } /// Pad block with zeros. /// /// ``` /// use block_padding::{ZeroPadding, Padding}; +/// use generic_array::{GenericArray, typenum::U8}; /// /// let msg = b"test"; -/// let n = msg.len(); -/// let mut buffer = [0xff; 16]; -/// buffer[..n].copy_from_slice(msg); -/// let padded_msg = ZeroPadding::pad(&mut buffer, n, 8).unwrap(); -/// assert_eq!(padded_msg, b"test\x00\x00\x00\x00"); -/// assert_eq!(ZeroPadding::unpad(&padded_msg).unwrap(), msg); -/// ``` -/// ``` -/// # use block_padding::{ZeroPadding, Padding}; -/// # let msg = b"test"; -/// # let n = msg.len(); -/// # let mut buffer = [0xff; 16]; -/// # buffer[..n].copy_from_slice(msg); -/// let padded_msg = ZeroPadding::pad(&mut buffer, n, 2).unwrap(); -/// assert_eq!(padded_msg, b"test"); -/// assert_eq!(ZeroPadding::unpad(&padded_msg).unwrap(), msg); +/// let pos = msg.len(); +/// let mut block: GenericArray:: = [0xff; 8].into(); +/// block[..pos].copy_from_slice(msg); +/// ZeroPadding::pad(&mut block, pos); +/// assert_eq!(&block[..], b"test\x00\x00\x00\x00"); +/// let res = ZeroPadding::unpad(&mut block).unwrap(); +/// assert_eq!(res, msg); /// ``` /// -/// Note that zero padding may not be reversible if the original message ends +/// Note that zero padding is not reversible for messages which end /// with one or more zero bytes. #[derive(Clone, Copy, Debug)] -pub enum ZeroPadding {} +pub struct ZeroPadding; -impl Padding for ZeroPadding { - fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> { - if pos > block.len() { - Err(PadError)? +impl> Padding for ZeroPadding { + #[inline] + fn pad(block: &mut Block, pos: usize) { + if pos > B::USIZE { + panic!("`pos` is bigger than block size"); } - set(&mut block[pos..], 0); - Ok(()) - } - - fn pad(buf: &mut [u8], pos: usize, block_size: usize) -> Result<&mut [u8], PadError> { - if pos % block_size == 0 { - Ok(&mut buf[..pos]) - } else { - let bs = block_size * (pos / block_size); - let be = bs + block_size; - if buf.len() < be { - Err(PadError)? - } - Self::pad_block(&mut buf[bs..be], pos - bs)?; - Ok(&mut buf[..be]) + for b in &mut block[pos..] { + *b = 0; } } - fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> { - let mut n = data.len() - 1; - while n != 0 { - if data[n] != 0 { - break; + #[inline] + fn unpad(block: &Block) -> Result<&[u8], UnpadError> { + for i in (0..B::USIZE).rev() { + if block[i] != 0 { + return Ok(&block[..i + 1]); } - n -= 1; } - Ok(&data[..n + 1]) + Ok(&block[..0]) } } @@ -115,70 +86,52 @@ impl Padding for ZeroPadding { /// /// ``` /// use block_padding::{Pkcs7, Padding}; +/// use generic_array::{GenericArray, typenum::U8}; /// /// let msg = b"test"; -/// let n = msg.len(); -/// let mut buffer = [0xff; 8]; -/// buffer[..n].copy_from_slice(msg); -/// let padded_msg = Pkcs7::pad(&mut buffer, n, 8).unwrap(); -/// assert_eq!(padded_msg, b"test\x04\x04\x04\x04"); -/// assert_eq!(Pkcs7::unpad(&padded_msg).unwrap(), msg); +/// let pos = msg.len(); +/// let mut block: GenericArray:: = [0xff; 8].into(); +/// block[..pos].copy_from_slice(msg); +/// Pkcs7::pad(&mut block, pos); +/// assert_eq!(&block[..], b"test\x04\x04\x04\x04"); +/// let res = Pkcs7::unpad(&block).unwrap(); +/// assert_eq!(res, msg); /// ``` -/// ``` -/// # use block_padding::{Pkcs7, Padding}; -/// # let msg = b"test"; -/// # let n = msg.len(); -/// # let mut buffer = [0xff; 8]; -/// # buffer[..n].copy_from_slice(msg); -/// let padded_msg = Pkcs7::pad(&mut buffer, n, 2).unwrap(); -/// assert_eq!(padded_msg, b"test\x02\x02"); -/// assert_eq!(Pkcs7::unpad(&padded_msg).unwrap(), msg); -/// ``` -/// ``` -/// # use block_padding::{Pkcs7, Padding}; -/// let mut buffer = [0xff; 5]; -/// assert!(Pkcs7::pad(&mut buffer, 4, 2).is_err()); -/// ``` -/// ``` -/// # use block_padding::{Pkcs7, Padding}; -/// # let buffer = [0xff; 16]; -/// assert!(Pkcs7::unpad(&buffer).is_err()); -/// ``` -/// -/// In addition to conditions stated in the `Padding` trait documentation, -/// `pad_block` will return `PadError` if `block.len() > 255`, and in case of -/// `pad` if `block_size > 255`. #[derive(Clone, Copy, Debug)] -pub enum Pkcs7 {} +pub struct Pkcs7; -impl Padding for Pkcs7 { - fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> { - if block.len() > 255 { - Err(PadError)? +impl> Padding for Pkcs7 { + #[inline] + fn pad(block: &mut Block, pos: usize) { + // TODO: use bounds to check it at compile time + if B::USIZE > 255 { + panic!("block size is too big for PKCS#7"); + } + if pos >= B::USIZE { + panic!("`pos` is bigger or equal to block size"); } - if pos >= block.len() { - Err(PadError)? + let n = (B::USIZE - pos) as u8; + for b in &mut block[pos..] { + *b = n; } - let n = block.len() - pos; - set(&mut block[pos..], n as u8); - Ok(()) } - fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> { - if data.is_empty() { - Err(UnpadError)? + #[inline] + fn unpad(block: &Block) -> Result<&[u8], UnpadError> { + // TODO: use bounds to check it at compile time + if B::USIZE > 255 { + panic!("block size is too big for PKCS#7"); } - let l = data.len(); - let n = data[l - 1]; - if n == 0 || n as usize > l { - Err(UnpadError)? + let bs = B::USIZE; + let n = block[bs - 1]; + if n == 0 || n as usize > bs { + return Err(UnpadError); } - for v in &data[l - n as usize..l - 1] { - if *v != n { - Err(UnpadError)? - } + let s = bs - n as usize; + if block[s..bs - 1].iter().any(|&v| v != n) { + return Err(UnpadError); } - Ok(&data[..l - n as usize]) + Ok(&block[..s]) } } @@ -187,66 +140,53 @@ impl Padding for Pkcs7 { /// /// ``` /// use block_padding::{AnsiX923, Padding}; +/// use generic_array::{GenericArray, typenum::U8}; /// /// let msg = b"test"; -/// let n = msg.len(); -/// let mut buffer = [0xff; 16]; -/// buffer[..n].copy_from_slice(msg); -/// let padded_msg = AnsiX923::pad(&mut buffer, n, 8).unwrap(); -/// assert_eq!(padded_msg, b"test\x00\x00\x00\x04"); -/// assert_eq!(AnsiX923::unpad(&padded_msg).unwrap(), msg); +/// let pos = msg.len(); +/// let mut block: GenericArray:: = [0xff; 8].into(); +/// block[..pos].copy_from_slice(msg); +/// AnsiX923::pad(&mut block, pos); +/// assert_eq!(&block[..], b"test\x00\x00\x00\x04"); +/// let res = AnsiX923::unpad(&block).unwrap(); +/// assert_eq!(res, msg); /// ``` -/// ``` -/// # use block_padding::{AnsiX923, Padding}; -/// # let msg = b"test"; -/// # let n = msg.len(); -/// # let mut buffer = [0xff; 16]; -/// # buffer[..n].copy_from_slice(msg); -/// let padded_msg = AnsiX923::pad(&mut buffer, n, 2).unwrap(); -/// assert_eq!(padded_msg, b"test\x00\x02"); -/// assert_eq!(AnsiX923::unpad(&padded_msg).unwrap(), msg); -/// ``` -/// ``` -/// # use block_padding::{AnsiX923, Padding}; -/// # let buffer = [0xff; 16]; -/// assert!(AnsiX923::unpad(&buffer).is_err()); -/// ``` -/// -/// In addition to conditions stated in the `Padding` trait documentation, -/// `pad_block` will return `PadError` if `block.len() > 255`, and in case of -/// `pad` if `block_size > 255`. #[derive(Clone, Copy, Debug)] -pub enum AnsiX923 {} +pub struct AnsiX923; -impl Padding for AnsiX923 { - fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> { - if block.len() > 255 { - Err(PadError)? +impl> Padding for AnsiX923 { + #[inline] + fn pad(block: &mut Block, pos: usize) { + // TODO: use bounds to check it at compile time + if B::USIZE > 255 { + panic!("block size is too big for PKCS#7"); + } + if pos >= B::USIZE { + panic!("`pos` is bigger or equal to block size"); } - if pos >= block.len() { - Err(PadError)? + let bs = B::USIZE; + for b in &mut block[pos..bs - 1] { + *b = 0; } - let bs = block.len(); - set(&mut block[pos..bs - 1], 0); block[bs - 1] = (bs - pos) as u8; - Ok(()) } - fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> { - if data.is_empty() { - Err(UnpadError)? + #[inline] + fn unpad(block: &Block) -> Result<&[u8], UnpadError> { + // TODO: use bounds to check it at compile time + if B::USIZE > 255 { + panic!("block size is too big for PKCS#7"); } - let l = data.len(); - let n = data[l - 1] as usize; - if n == 0 || n > l { + let bs = B::USIZE; + let n = block[bs - 1] as usize; + if n == 0 || n > bs { return Err(UnpadError); } - for v in &data[l - n..l - 1] { - if *v != 0 { - Err(UnpadError)? - } + let s = bs - n; + if block[s..bs - 1].iter().any(|&v| v != 0) { + return Err(UnpadError); } - Ok(&data[..l - n]) + Ok(&block[..s]) } } @@ -254,110 +194,92 @@ impl Padding for AnsiX923 { /// /// ``` /// use block_padding::{Iso7816, Padding}; +/// use generic_array::{GenericArray, typenum::U8}; /// /// let msg = b"test"; -/// let n = msg.len(); -/// let mut buffer = [0xff; 16]; -/// buffer[..n].copy_from_slice(msg); -/// let padded_msg = Iso7816::pad(&mut buffer, n, 8).unwrap(); -/// assert_eq!(padded_msg, b"test\x80\x00\x00\x00"); -/// assert_eq!(Iso7816::unpad(&padded_msg).unwrap(), msg); -/// ``` -/// ``` -/// # use block_padding::{Iso7816, Padding}; -/// # let msg = b"test"; -/// # let n = msg.len(); -/// # let mut buffer = [0xff; 16]; -/// # buffer[..n].copy_from_slice(msg); -/// let padded_msg = Iso7816::pad(&mut buffer, n, 2).unwrap(); -/// assert_eq!(padded_msg, b"test\x80\x00"); -/// assert_eq!(Iso7816::unpad(&padded_msg).unwrap(), msg); +/// let pos = msg.len(); +/// let mut block: GenericArray:: = [0xff; 8].into(); +/// block[..pos].copy_from_slice(msg); +/// Iso7816::pad(&mut block, pos); +/// assert_eq!(&block[..], b"test\x80\x00\x00\x00"); +/// let res = Iso7816::unpad(&block).unwrap(); +/// assert_eq!(res, msg); /// ``` #[derive(Clone, Copy, Debug)] -pub enum Iso7816 {} +pub struct Iso7816; -impl Padding for Iso7816 { - fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> { - if pos >= block.len() { - Err(PadError)? +impl> Padding for Iso7816 { + #[inline] + fn pad(block: &mut Block, pos: usize) { + if pos >= B::USIZE { + panic!("`pos` is bigger or equal to block size"); } block[pos] = 0x80; - set(&mut block[pos + 1..], 0); - Ok(()) + for b in &mut block[pos + 1..] { + *b = 0; + } } - fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> { - if data.is_empty() { - Err(UnpadError)? - } - let mut n = data.len() - 1; - while n != 0 { - if data[n] != 0 { - break; + #[inline] + fn unpad(block: &Block) -> Result<&[u8], UnpadError> { + for i in (0..B::USIZE).rev() { + match block[i] { + 0x80 => return Ok(&block[..i]), + 0x00 => continue, + _ => return Err(UnpadError), } - n -= 1; } - if data[n] != 0x80 { - Err(UnpadError)? - } - Ok(&data[..n]) + Err(UnpadError) } } -/// Don't pad the data. Useful for key wrapping. Padding will fail if the data cannot be -/// fitted into blocks without padding. +/// Don't pad the data. Useful for key wrapping. /// /// ``` /// use block_padding::{NoPadding, Padding}; +/// use generic_array::{GenericArray, typenum::U8}; /// /// let msg = b"test"; -/// let n = msg.len(); -/// let mut buffer = [0xff; 16]; -/// buffer[..n].copy_from_slice(msg); -/// let padded_msg = NoPadding::pad(&mut buffer, n, 4).unwrap(); -/// assert_eq!(padded_msg, b"test"); -/// assert_eq!(NoPadding::unpad(&padded_msg).unwrap(), msg); -/// ``` -/// ``` -/// # use block_padding::{NoPadding, Padding}; -/// # let msg = b"test"; -/// # let n = msg.len(); -/// # let mut buffer = [0xff; 16]; -/// # buffer[..n].copy_from_slice(msg); -/// let padded_msg = NoPadding::pad(&mut buffer, n, 2).unwrap(); -/// assert_eq!(padded_msg, b"test"); -/// assert_eq!(NoPadding::unpad(&padded_msg).unwrap(), msg); +/// let pos = msg.len(); +/// let mut block: GenericArray:: = [0xff; 8].into(); +/// block[..pos].copy_from_slice(msg); +/// NoPadding::pad(&mut block, pos); +/// assert_eq!(&block[..], b"test\xff\xff\xff\xff"); +/// let res = NoPadding::unpad(&block).unwrap(); +/// assert_eq!(res, b"test\xff\xff\xff\xff"); /// ``` +/// +/// Note that even though the passed length of the message is equal to 4, +/// the size of unpadded message is equal to the block size of 8 bytes. +/// Also padded message contains "garbage" bytes stored in the block buffer. +/// Thus `NoPadding` generally should not be used with data length of which +/// is not multiple of block size. #[derive(Clone, Copy, Debug)] -pub enum NoPadding {} +pub struct NoPadding; -impl Padding for NoPadding { - fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> { - if pos % block.len() != 0 { - Err(PadError)? +impl> Padding for NoPadding { + #[inline] + fn pad(_block: &mut Block, pos: usize) { + if pos > B::USIZE { + panic!("`pos` is bigger than block size"); } - Ok(()) } - fn pad(buf: &mut [u8], pos: usize, block_size: usize) -> Result<&mut [u8], PadError> { - if pos % block_size != 0 { - Err(PadError)? - } - Ok(&mut buf[..pos]) - } - - fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> { - Ok(data) + #[inline] + fn unpad(block: &Block) -> Result<&[u8], UnpadError> { + Ok(block) } } -/// Sets all bytes in `dst` equal to `value` -#[inline(always)] -fn set(dst: &mut [u8], value: u8) { - // SAFETY: we overwrite valid memory behind `dst` - // note: loop is not used here because it produces - // unnecessary branch which tests for zero-length slices - unsafe { - core::ptr::write_bytes(dst.as_mut_ptr(), value, dst.len()); +/// Failed unpadding operation error. +#[derive(Clone, Copy, Debug)] +pub struct UnpadError; + +impl fmt::Display for UnpadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("Unpad Error") } } + +#[cfg(feature = "std")] +impl std::error::Error for UnpadError {}