Skip to content

Commit

Permalink
block-buffer: remove dependency on crypto-common (#1115)
Browse files Browse the repository at this point in the history
Dependency on `crypto-common` causes some annoyances as mentioned in
RustCrypto/traits#1662.

Instead of using the sealed `BlockSizes` trait, the new code uses
monomorphization errors to enforce block size correctness. This is
somewhat non-idiomatic, but should be fine in practice since block
buffers are usually used with fixed block sizes. After this change
`BlockSizes` probably can be removed from `crypto-common`.

I decided against vendoring `block-buffer` code into `digest` as was
proposed in the linked issue because `block-buffer` has third-party
users and having a separate crate should make it a bit easier for
reviewers.
  • Loading branch information
newpavlov authored Sep 27, 2024
1 parent b5d80a2 commit 021e014
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 72 deletions.
9 changes: 4 additions & 5 deletions .github/workflows/block-buffer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,10 @@ jobs:
targets: ${{ matrix.target }}
- run: cargo build --target ${{ matrix.target }}

# TODO(tarcieri): re-enable after next `crypto-common` release
#minimal-versions:
# uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
# with:
# working-directory: ${{ github.workflow }}
minimal-versions:
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
with:
working-directory: ${{ github.workflow }}

test:
runs-on: ubuntu-latest
Expand Down
11 changes: 1 addition & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions block-buffer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Optional implementation of the `Zeroize` trait ([#963])

### Changed
- Supported block sizes are now bounded by the `crypto_common::BlockSizes` trait,
which is implemented for types from `U1` to `U255` ([#823])
- Block sizes must be bigger than 0 and smaller than 256.
This is enforced using compile-time monomorphization errors. ([#1115])
- Size of `EagerBuffer` is equal to buffer size, while previously it was equal
to buffer size plus one byte ([#823])
- Edition changed to 2021 and MSRV bumped to 1.56 ([#823])
- Edition changed to 2021 and MSRV bumped to 1.81 ([#823], [#1116])

### Removed
- `EagerBuffer::set_data` method. Use the `ReadBuffer` type instead. ([#823])

[#823]: https://github.com/RustCrypto/utils/pull/823
[#963]: https://github.com/RustCrypto/utils/pull/963
[#1115]: https://github.com/RustCrypto/utils/pull/1115
[#1115]: https://github.com/RustCrypto/utils/pull/1116

## 0.10.3 (2022-09-04)
### Added
Expand Down
2 changes: 1 addition & 1 deletion block-buffer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ readme = "README.md"
rust-version = "1.81"

[dependencies]
crypto-common = "0.2.0-rc.1"
hybrid-array = "0.2.0-rc.10"
zeroize = { version = "1.4", optional = true, default-features = false }

[dev-dependencies]
Expand Down
106 changes: 76 additions & 30 deletions block-buffer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
//! Fixed size buffer for block processing of data.
//!
//! # Examples
//! ```
//! use block_buffer::{EagerBuffer, array::typenum::U4};
//!
//! let mut buf = EagerBuffer::<U4>::default();
//!
//! let mut accum = Vec::new();
//! let msg1: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
//! let msg2: &[u8] = &[10, 11, 12];
//!
//! buf.digest_blocks(msg1, |blocks| accum.extend_from_slice(blocks));
//! buf.digest_blocks(msg2, |blocks| accum.extend_from_slice(blocks));
//!
//! assert_eq!(accum.len(), 3);
//! assert_eq!(accum[0], [0, 1, 2, 3]);
//! assert_eq!(accum[1], [4, 5, 6, 7]);
//! assert_eq!(accum[2], [8, 9, 10, 11]);
//!
//! let padded_block = buf.pad_with_zeros();
//! assert_eq!(padded_block, [12, 0, 0, 0]);
//! ```
//!
//! Note that block size used with buffers MUST be bigger than zero and smaller than 256.
//! You will get a compilation error with an invalid block size:
//!
//! ```compile_fail
//! use block_buffer::{EagerBuffer, array::typenum::U0};
//! let buf = EagerBuffer::<U0>::default();
//! ```
//! ```compile_fail
//! use block_buffer::{EagerBuffer, array::typenum::U256};
//! let buf = EagerBuffer::<U256>::default();
//! ```
#![no_std]
#![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"
)]
#![warn(missing_docs, rust_2018_idioms)]

pub use crypto_common::{array, Block};
pub use hybrid_array as array;

use array::{
typenum::{Add1, B1},
Array, ArraySize,
};
use core::{fmt, mem::MaybeUninit, ops::Add, ptr, slice};
use crypto_common::{BlockSizeUser, BlockSizes};

#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
Expand All @@ -23,9 +56,6 @@ mod sealed;

pub use read::ReadBuffer;

/// Block with additional one byte
type BlockP1<BlockSize> = Array<u8, Add1<BlockSize>>;

/// Trait for buffer kinds.
pub trait BufferKind: sealed::Sealed {}

Expand Down Expand Up @@ -59,26 +89,36 @@ impl fmt::Display for Error {
}

/// Buffer for block processing of data.
pub struct BlockBuffer<BS: BlockSizes, K: BufferKind> {
buffer: MaybeUninit<Block<Self>>,
pub struct BlockBuffer<BS: ArraySize, K: BufferKind> {
buffer: MaybeUninit<Array<u8, BS>>,
pos: K::Pos,
}

impl<BS: BlockSizes, K: BufferKind> BlockSizeUser for BlockBuffer<BS, K> {
type BlockSize = BS;
impl<BS: ArraySize, K: BufferKind> BlockBuffer<BS, K> {
/// This associated constant is used to assert block size correctness at compile time.
const BLOCK_SIZE_ASSERT: bool = {
if BS::USIZE == 0 {
panic!("Block size can not be equal to zero!");
}
if BS::USIZE > 255 {
panic!("Block size can not be bigger than 255!");
}
true
};
}

impl<BS: BlockSizes, K: BufferKind> Default for BlockBuffer<BS, K> {
impl<BS: ArraySize, K: BufferKind> Default for BlockBuffer<BS, K> {
#[inline]
fn default() -> Self {
assert!(Self::BLOCK_SIZE_ASSERT);
let mut buffer = MaybeUninit::uninit();
let mut pos = Default::default();
K::set_pos(&mut buffer, &mut pos, 0);
Self { buffer, pos }
}
}

impl<BS: BlockSizes, K: BufferKind> Clone for BlockBuffer<BS, K> {
impl<BS: ArraySize, K: BufferKind> Clone for BlockBuffer<BS, K> {
#[inline]
fn clone(&self) -> Self {
// SAFETY: `BlockBuffer` does not implement `Drop` (i.e. it could be a `Copy` type),
Expand All @@ -87,7 +127,7 @@ impl<BS: BlockSizes, K: BufferKind> Clone for BlockBuffer<BS, K> {
}
}

impl<BS: BlockSizes, K: BufferKind> fmt::Debug for BlockBuffer<BS, K> {
impl<BS: ArraySize, K: BufferKind> fmt::Debug for BlockBuffer<BS, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct(K::NAME)
.field("pos", &self.get_pos())
Expand All @@ -97,7 +137,7 @@ impl<BS: BlockSizes, K: BufferKind> fmt::Debug for BlockBuffer<BS, K> {
}
}

impl<BS: BlockSizes, K: BufferKind> BlockBuffer<BS, K> {
impl<BS: ArraySize, K: BufferKind> BlockBuffer<BS, K> {
/// Create new buffer from slice.
///
/// # Panics
Expand All @@ -112,6 +152,7 @@ impl<BS: BlockSizes, K: BufferKind> BlockBuffer<BS, K> {
/// Returns an error if slice length is not valid for used buffer kind.
#[inline(always)]
pub fn try_new(buf: &[u8]) -> Result<Self, Error> {
assert!(Self::BLOCK_SIZE_ASSERT);
if !K::invariant(buf.len(), BS::USIZE) {
return Err(Error);
}
Expand All @@ -126,7 +167,7 @@ impl<BS: BlockSizes, K: BufferKind> BlockBuffer<BS, K> {
/// Digest data in `input` in blocks of size `BlockSize` using
/// the `compress` function, which accepts slice of blocks.
#[inline]
pub fn digest_blocks(&mut self, mut input: &[u8], mut compress: impl FnMut(&[Block<Self>])) {
pub fn digest_blocks(&mut self, mut input: &[u8], mut compress: impl FnMut(&[Array<u8, BS>])) {
let pos = self.get_pos();
// using `self.remaining()` for some reason
// prevents panic elimination
Expand Down Expand Up @@ -186,8 +227,8 @@ impl<BS: BlockSizes, K: BufferKind> BlockBuffer<BS, K> {

/// Pad remaining data with zeros and return resulting block.
#[inline(always)]
pub fn pad_with_zeros(&mut self) -> Block<Self> {
let mut res = Block::<Self>::default();
pub fn pad_with_zeros(&mut self) -> Array<u8, BS> {
let mut res = Array::<u8, BS>::default();
let data = self.get_data();
res[..data.len()].copy_from_slice(data);
self.reset();
Expand Down Expand Up @@ -221,7 +262,7 @@ impl<BS: BlockSizes, K: BufferKind> BlockBuffer<BS, K> {
/// # Panics
/// If `pos` is bigger or equal to block size.
#[inline]
pub fn set(&mut self, buf: Block<Self>, pos: usize) {
pub fn set(&mut self, buf: Array<u8, BS>, pos: usize) {
assert!(K::invariant(pos, BS::USIZE));
self.buffer = MaybeUninit::new(buf);
// SAFETY: we have asserted that `pos` satisfies the invariant and
Expand Down Expand Up @@ -271,15 +312,20 @@ impl<BS: BlockSizes, K: BufferKind> BlockBuffer<BS, K> {
}
}

impl<BS: BlockSizes> BlockBuffer<BS, Eager> {
impl<BS: ArraySize> BlockBuffer<BS, Eager> {
/// Compress remaining data after padding it with `delim`, zeros and
/// the `suffix` bytes. If there is not enough unused space, `compress`
/// will be called twice.
///
/// # Panics
/// If suffix length is bigger than block size.
#[inline(always)]
pub fn digest_pad(&mut self, delim: u8, suffix: &[u8], mut compress: impl FnMut(&Block<Self>)) {
pub fn digest_pad(
&mut self,
delim: u8,
suffix: &[u8],
mut compress: impl FnMut(&Array<u8, BS>),
) {
if suffix.len() > BS::USIZE {
panic!("suffix is too long");
}
Expand All @@ -303,28 +349,28 @@ impl<BS: BlockSizes> BlockBuffer<BS, Eager> {
/// 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, compress: impl FnMut(&Block<Self>)) {
pub fn len64_padding_be(&mut self, data_len: u64, compress: impl FnMut(&Array<u8, BS>)) {
self.digest_pad(0x80, &data_len.to_be_bytes(), compress);
}

/// 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, compress: impl FnMut(&Block<Self>)) {
pub fn len64_padding_le(&mut self, data_len: u64, compress: impl FnMut(&Array<u8, BS>)) {
self.digest_pad(0x80, &data_len.to_le_bytes(), compress);
}

/// 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, compress: impl FnMut(&Block<Self>)) {
pub fn len128_padding_be(&mut self, data_len: u128, compress: impl FnMut(&Array<u8, BS>)) {
self.digest_pad(0x80, &data_len.to_be_bytes(), compress);
}

/// Serialize buffer into a byte array.
#[inline]
pub fn serialize(&self) -> Block<Self> {
let mut res = Block::<Self>::default();
pub fn serialize(&self) -> Array<u8, BS> {
let mut res = Array::<u8, BS>::default();
let data = self.get_data();
res[..data.len()].copy_from_slice(data);
res[BS::USIZE - 1] = data.len() as u8;
Expand All @@ -333,7 +379,7 @@ impl<BS: BlockSizes> BlockBuffer<BS, Eager> {

/// Deserialize buffer from a byte array.
#[inline]
pub fn deserialize(buffer: &Block<Self>) -> Result<Self, Error> {
pub fn deserialize(buffer: &Array<u8, BS>) -> Result<Self, Error> {
let pos = buffer[BS::USIZE - 1] as usize;
if !<Eager as sealed::Sealed>::invariant(pos, BS::USIZE) {
return Err(Error);
Expand All @@ -348,15 +394,15 @@ impl<BS: BlockSizes> BlockBuffer<BS, Eager> {
}
}

impl<BS: BlockSizes> BlockBuffer<BS, Lazy> {
impl<BS: ArraySize> BlockBuffer<BS, Lazy> {
/// Serialize buffer into a byte array.
#[inline]
pub fn serialize(&self) -> BlockP1<BS>
pub fn serialize(&self) -> Array<u8, Add1<BS>>
where
BS: Add<B1>,
Add1<BS>: ArraySize,
{
let mut res = BlockP1::<BS>::default();
let mut res = Array::<u8, Add1<BS>>::default();
res[0] = self.pos;
let data = self.get_data();
res[1..][..data.len()].copy_from_slice(data);
Expand All @@ -365,7 +411,7 @@ impl<BS: BlockSizes> BlockBuffer<BS, Lazy> {

/// Deserialize buffer from a byte array.
#[inline]
pub fn deserialize(buffer: &BlockP1<BS>) -> Result<Self, Error>
pub fn deserialize(buffer: &Array<u8, Add1<BS>>) -> Result<Self, Error>
where
BS: Add<B1>,
Add1<BS>: ArraySize,
Expand All @@ -386,7 +432,7 @@ impl<BS: BlockSizes> BlockBuffer<BS, Lazy> {
}

#[cfg(feature = "zeroize")]
impl<BS: BlockSizes, K: BufferKind> Zeroize for BlockBuffer<BS, K> {
impl<BS: ArraySize, K: BufferKind> Zeroize for BlockBuffer<BS, K> {
#[inline]
fn zeroize(&mut self) {
self.buffer.zeroize();
Expand Down
Loading

0 comments on commit 021e014

Please sign in to comment.