diff --git a/Cargo.lock b/Cargo.lock index 97bdeafa..17a0e54a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,9 +121,9 @@ checksum = "bcaaec4551594c969335c98c903c1397853d4198408ea609190f420500f6be71" [[package]] name = "hybrid-array" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7c10d9cd8b8e0733111482917f4f7e188cf6f57fc8eb0ff9b26a51db9fbd3c" +checksum = "2bad028b20a90afcdb5e28a53392562f1db2bdfa238aa1a978b911461bfffb92" dependencies = [ "typenum", ] @@ -159,9 +159,9 @@ checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "opaque-debug" @@ -193,9 +193,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" -version = "1.0.226" +version = "1.0.227" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +checksum = "80ece43fc6fbed4eb5392ab50c07334d3e577cbf40997ee896fe7af40bba4245" dependencies = [ "serde_core", "serde_derive", @@ -203,18 +203,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.226" +version = "1.0.227" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +checksum = "7a576275b607a2c86ea29e410193df32bc680303c82f31e275bbfcafe8b33be5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.226" +version = "1.0.227" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +checksum = "51e694923b8824cf0e9b382adf0f60d4e05f348f357b38833a3fa5ed7c2ede04" dependencies = [ "proc-macro2", "quote", diff --git a/block-padding/CHANGELOG.md b/block-padding/CHANGELOG.md index d624b500..4a21889e 100644 --- a/block-padding/CHANGELOG.md +++ b/block-padding/CHANGELOG.md @@ -5,17 +5,23 @@ 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.4.0 (unreleased) +### Added +- `Padding::pad_detached` method ([#1225]) + ### Changed - Migrated from `generic-array` to `hybrid-array` ([#944]) - Edition changed to 2024 and MSRV bumped to 1.85 ([#1149]) - Merged `RawPadding` and `Padding` traits ([#1217]) +- Renamed `UnpadError` to `Error` ([#1225]) ### Removed - `Block` type alias ([#1217]) +- `PadType` enum and associated type on the `Padding` trait ([#1225]) [#944]: https://github.com/RustCrypto/utils/pull/944 [#1149]: https://github.com/RustCrypto/utils/pull/1149 [#1217]: https://github.com/RustCrypto/utils/pull/1217 +[#1225]: https://github.com/RustCrypto/utils/pull/1225 ## 0.3.3 (2023-04-02) ### Added diff --git a/block-padding/README.md b/block-padding/README.md index d1a9dc75..3d594147 100644 --- a/block-padding/README.md +++ b/block-padding/README.md @@ -9,6 +9,9 @@ Padding and unpadding of messages divided into blocks. +This crate provides the `Padding` trait with padding and unpadding methods. +Additionally, several common padding schemes are available out of the box. + ## License Licensed under either of: diff --git a/block-padding/src/lib.rs b/block-padding/src/lib.rs index 3c6f3658..7fe9da7a 100644 --- a/block-padding/src/lib.rs +++ b/block-padding/src/lib.rs @@ -1,86 +1,82 @@ -//! Padding and unpadding of messages divided into blocks. -//! -//! This crate provides `Padding` trait which provides padding and unpadding -//! operations. Additionally several common padding schemes are available out -//! of the box. #![no_std] +#![doc = include_str!("../README.md")] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" )] #![deny(unsafe_code)] -#![warn(missing_docs)] +#![warn(missing_docs, missing_debug_implementations)] pub use hybrid_array as array; use core::fmt; use hybrid_array::{Array, ArraySize}; -/// Padding types -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum PadType { - /// Reversible padding - Reversible, - /// Ambiguous padding - Ambiguous, - /// No padding, message must be multiple of block size - NoPadding, -} - -/// Trait for messages padding algorithms. +/// Trait for message padding algorithms. pub trait Padding { - /// Padding type - const TYPE: PadType; - - /// Pads `block` filled with data up to `pos` (i.e length of a message - /// stored in the block is equal to `pos`). + /// Pads `block` filled with data up to `pos` (i.e the message length + /// stored in `block` is equal to `pos`). /// /// # Panics /// If `pos` is bigger than `block.len()`. Most padding algorithms also /// panic if they are equal. fn raw_pad(block: &mut [u8], pos: usize); - /// Unpad data in the `block`. + /// Unpad data in `block`. /// - /// Returns `Err(UnpadError)` if the block contains malformed padding. - fn raw_unpad(block: &[u8]) -> Result<&[u8], UnpadError>; + /// Returns error if the block contains malformed padding. + fn raw_unpad(block: &[u8]) -> Result<&[u8], Error>; - /// Pads `block` filled with data up to `pos` (i.e length of a message - /// stored in the block is equal to `pos`). + /// Pads `block` filled with data up to `pos` (i.e the message length + /// stored in `block` is equal to `pos`). /// /// # Panics /// If `pos` is bigger than `BlockSize`. Most padding algorithms also /// panic if they are equal. + #[inline] fn pad(block: &mut Array, pos: usize) { Self::raw_pad(block.as_mut_slice(), pos); } - /// Unpad data in the `block`. + /// Unpad data in `block`. /// - /// Returns `Err(UnpadError)` if the block contains malformed padding. - fn unpad(block: &Array) -> Result<&[u8], UnpadError> { + /// Returns error if the block contains malformed padding. + #[inline] + fn unpad(block: &Array) -> Result<&[u8], Error> { Self::raw_unpad(block.as_slice()) } - /// Unpad data in the `blocks`. + /// Pad message and return padded tail block. /// - /// Returns `Err(UnpadError)` if the block contains malformed padding. - fn unpad_blocks( - blocks: &[Array], - ) -> Result<&[u8], UnpadError> { + /// `Err` is returned only by [`NoPadding`] if `data` length is not multiple of the block size. + /// [`NoPadding`] and [`ZeroPadding`] return `Ok((blocks, None))` if `data` length + /// is multiple of block size. All other padding implementations should always return + /// `Ok((blocks, Some(tail_block)))`. + #[allow(clippy::type_complexity)] + #[inline] + fn pad_detached( + data: &[u8], + ) -> Result<(&[Array], Option>), Error> { + let (blocks, tail) = Array::slice_as_chunks(data); + let mut tail_block = Array::default(); + let pos = tail.len(); + tail_block[..pos].copy_from_slice(tail); + Self::pad(&mut tail_block, pos); + Ok((blocks, Some(tail_block))) + } + + /// Unpad data in `blocks` and return unpadded byte slice. + /// + /// Returns error if `blocks` contain malformed padding. + #[inline] + fn unpad_blocks(blocks: &[Array]) -> Result<&[u8], Error> { let bs = BlockSize::USIZE; - let res_len = match (blocks.last(), Self::TYPE) { - (_, PadType::NoPadding) => bs * blocks.len(), - (Some(last_block), _) => { - let n = Self::unpad(last_block)?.len(); - assert!(n <= bs); - n + bs * (blocks.len() - 1) - } - (None, PadType::Ambiguous) => 0, - (None, PadType::Reversible) => return Err(UnpadError), - }; - let data = Array::slice_as_flattened(blocks); - Ok(&data[..res_len]) + let (last_block, full_blocks) = blocks.split_last().ok_or(Error)?; + let unpad_len = Self::unpad(last_block)?.len(); + assert!(unpad_len <= bs); + let buf = Array::slice_as_flattened(blocks); + let data_len = full_blocks.len() * bs + unpad_len; + Ok(&buf[..data_len]) } } @@ -106,8 +102,6 @@ pub trait Padding { pub struct ZeroPadding; impl Padding for ZeroPadding { - const TYPE: PadType = PadType::Ambiguous; - #[inline] fn raw_pad(block: &mut [u8], pos: usize) { if pos > block.len() { @@ -117,7 +111,7 @@ impl Padding for ZeroPadding { } #[inline] - fn raw_unpad(block: &[u8]) -> Result<&[u8], UnpadError> { + fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> { for i in (0..block.len()).rev() { if block[i] != 0 { return Ok(&block[..i + 1]); @@ -125,6 +119,17 @@ impl Padding for ZeroPadding { } Ok(&block[..0]) } + + #[inline] + fn unpad_blocks(blocks: &[Array]) -> Result<&[u8], Error> { + let buf = Array::slice_as_flattened(blocks); + for i in (0..buf.len()).rev() { + if buf[i] != 0 { + return Ok(&buf[..i + 1]); + } + } + Ok(&buf[..0]) + } } /// Pad block with bytes with value equal to the number of bytes added. @@ -149,30 +154,26 @@ pub struct Pkcs7; impl Pkcs7 { #[inline] - fn unpad(block: &[u8], strict: bool) -> Result<&[u8], UnpadError> { - // TODO: use bounds to check it at compile time + fn unpad(block: &[u8], strict: bool) -> Result<&[u8], Error> { if block.len() > 255 { panic!("block size is too big for PKCS#7"); } let bs = block.len(); let n = block[bs - 1]; if n == 0 || n as usize > bs { - return Err(UnpadError); + return Err(Error); } let s = bs - n as usize; if strict && block[s..bs - 1].iter().any(|&v| v != n) { - return Err(UnpadError); + return Err(Error); } Ok(&block[..s]) } } impl Padding for Pkcs7 { - const TYPE: PadType = PadType::Reversible; - #[inline] fn raw_pad(block: &mut [u8], pos: usize) { - // TODO: use bounds to check it at compile time for Padding if block.len() > 255 { panic!("block size is too big for PKCS#7"); } @@ -184,7 +185,7 @@ impl Padding for Pkcs7 { } #[inline] - fn raw_unpad(block: &[u8]) -> Result<&[u8], UnpadError> { + fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> { Pkcs7::unpad(block, true) } } @@ -210,8 +211,6 @@ impl Padding for Pkcs7 { pub struct Iso10126; impl Padding for Iso10126 { - const TYPE: PadType = PadType::Reversible; - #[inline] fn raw_pad(block: &mut [u8], pos: usize) { // Instead of generating random bytes as specified by Iso10126 we @@ -220,7 +219,7 @@ impl Padding for Iso10126 { } #[inline] - fn raw_unpad(block: &[u8]) -> Result<&[u8], UnpadError> { + fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> { Pkcs7::unpad(block, false) } } @@ -245,11 +244,8 @@ impl Padding for Iso10126 { pub struct AnsiX923; impl Padding for AnsiX923 { - const TYPE: PadType = PadType::Reversible; - #[inline] fn raw_pad(block: &mut [u8], pos: usize) { - // TODO: use bounds to check it at compile time if block.len() > 255 { panic!("block size is too big for ANSI X9.23"); } @@ -262,19 +258,18 @@ impl Padding for AnsiX923 { } #[inline] - fn raw_unpad(block: &[u8]) -> Result<&[u8], UnpadError> { - // TODO: use bounds to check it at compile time + fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> { if block.len() > 255 { panic!("block size is too big for ANSI X9.23"); } let bs = block.len(); let n = block[bs - 1] as usize; if n == 0 || n > bs { - return Err(UnpadError); + return Err(Error); } let s = bs - n; if block[s..bs - 1].iter().any(|&v| v != 0) { - return Err(UnpadError); + return Err(Error); } Ok(&block[..s]) } @@ -299,8 +294,6 @@ impl Padding for AnsiX923 { pub struct Iso7816; impl Padding for Iso7816 { - const TYPE: PadType = PadType::Reversible; - #[inline] fn raw_pad(block: &mut [u8], pos: usize) { if pos >= block.len() { @@ -311,15 +304,15 @@ impl Padding for Iso7816 { } #[inline] - fn raw_unpad(block: &[u8]) -> Result<&[u8], UnpadError> { + fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> { for i in (0..block.len()).rev() { match block[i] { 0x80 => return Ok(&block[..i]), 0x00 => continue, - _ => return Err(UnpadError), + _ => return Err(Error), } } - Err(UnpadError) + Err(Error) } } @@ -348,8 +341,6 @@ impl Padding for Iso7816 { pub struct NoPadding; impl Padding for NoPadding { - const TYPE: PadType = PadType::NoPadding; - #[inline] fn raw_pad(block: &mut [u8], pos: usize) { if pos > block.len() { @@ -358,19 +349,24 @@ impl Padding for NoPadding { } #[inline] - fn raw_unpad(block: &[u8]) -> Result<&[u8], UnpadError> { + fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> { Ok(block) } + + #[inline] + fn unpad_blocks(blocks: &[Array]) -> Result<&[u8], Error> { + Ok(Array::slice_as_flattened(blocks)) + } } -/// Failed unpadding operation error. +/// Error returned by the [`Padding`] trait methods. #[derive(Clone, Copy, Debug)] -pub struct UnpadError; +pub struct Error; -impl fmt::Display for UnpadError { +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("Unpad Error") + f.write_str("Padding error") } } -impl core::error::Error for UnpadError {} +impl core::error::Error for Error {} diff --git a/inout/src/reserved.rs b/inout/src/reserved.rs index 701f8b50..ab143dfe 100644 --- a/inout/src/reserved.rs +++ b/inout/src/reserved.rs @@ -4,7 +4,7 @@ use core::{marker::PhantomData, slice}; #[cfg(feature = "block-padding")] use { crate::{InOut, errors::PadError}, - block_padding::{PadType, Padding}, + block_padding::Padding, hybrid_array::{Array, ArraySize}, }; @@ -160,55 +160,49 @@ impl<'inp, 'out> InOutBufReserved<'inp, 'out, u8> { { let bs = BS::USIZE; let blocks_len = self.in_len / bs; - let tail_len = self.in_len - bs * blocks_len; + let (blocks, tail_block) = P::pad_detached(self.get_in()).map_err(|_| PadError)?; + + assert_eq!(blocks.len(), blocks_len); + + let out_len = self.out_len; + let (in_ptr, out_ptr) = self.into_raw(); + let blocks = unsafe { InOutBuf::from_raw( - self.in_ptr as *const Array, - self.out_ptr as *mut Array, + in_ptr.cast::>(), + out_ptr.cast::>(), blocks_len, ) }; - let mut tail_in = Array::::default(); - let tail_out = match P::TYPE { - PadType::NoPadding | PadType::Ambiguous if tail_len == 0 => None, - PadType::NoPadding => return Err(PadError), - PadType::Reversible | PadType::Ambiguous => { - let blen = bs * blocks_len; - let res_len = blen + bs; - if res_len > self.out_len { - return Err(PadError); - } - // SAFETY: `in_ptr + blen..in_ptr + blen + tail_len` - // is valid region for reads and `tail_len` is smaller than `BS`. - // we have verified that `blen + bs <= out_len`, in other words, - // `out_ptr + blen..out_ptr + blen + bs` is valid region - // for writes. - let out_block = unsafe { - core::ptr::copy_nonoverlapping( - self.in_ptr.add(blen), - tail_in.as_mut_ptr(), - tail_len, - ); - &mut *(self.out_ptr.add(blen) as *mut Array) - }; - P::pad(&mut tail_in, tail_len); - Some(out_block) - } + + let Some(tail_block) = tail_block else { + let tail_inout = None; + return Ok(PaddedInOutBuf { blocks, tail_inout }); }; - Ok(PaddedInOutBuf { - blocks, - tail_in, - tail_out, - }) + + let blocks_byte_len = blocks_len * bs; + let reserve_len = out_len - blocks_byte_len; + if reserve_len < tail_block.len() { + return Err(PadError); + } + // SAFETY: we checked that the out buffer has enough bytes in reserve + let tail_out: &mut Array = unsafe { + let tail_out_ptr = out_ptr.add(blocks_byte_len); + &mut *(tail_out_ptr.cast()) + }; + + let tail_inout = Some((tail_block, tail_out)); + + Ok(PaddedInOutBuf { blocks, tail_inout }) } } /// Variant of [`InOutBuf`] with optional padded tail block. #[cfg(feature = "block-padding")] +#[allow(clippy::type_complexity)] pub struct PaddedInOutBuf<'inp, 'out, BS: ArraySize> { blocks: InOutBuf<'inp, 'out, Array>, - tail_in: Array, - tail_out: Option<&'out mut Array>, + tail_inout: Option<(Array, &'out mut Array)>, } #[cfg(feature = "block-padding")] @@ -221,20 +215,20 @@ impl<'out, BS: ArraySize> PaddedInOutBuf<'_, 'out, BS> { /// Get padded tail block. /// - /// For paddings with `P::TYPE = PadType::Reversible` it always returns `Some`. + /// Most padding implementations always return `Some`. #[inline(always)] - #[allow(clippy::needless_option_as_deref)] pub fn get_tail_block(&mut self) -> Option>> { - match self.tail_out.as_deref_mut() { - Some(out_block) => Some((&self.tail_in, out_block).into()), - None => None, - } + self.tail_inout.as_mut().map(|(in_block, out_block)| { + let in_block = &*in_block; + let out_block = &mut **out_block; + InOut::from((in_block, out_block)) + }) } /// Convert buffer into output slice. #[inline(always)] pub fn into_out(self) -> &'out [u8] { - let total_blocks = if self.tail_out.is_some() { + let total_blocks = if self.tail_inout.is_some() { self.blocks.len() + 1 } else { self.blocks.len()