diff --git a/.github/workflows/block-buffer.yml b/.github/workflows/block-buffer.yml index c3065bbc..11f5d2f3 100644 --- a/.github/workflows/block-buffer.yml +++ b/.github/workflows/block-buffer.yml @@ -36,7 +36,6 @@ jobs: target: ${{ matrix.target }} override: true - run: cargo build --release --target ${{ matrix.target }} - - run: cargo build --features block-padding --release --target ${{ matrix.target }} test: runs-on: ubuntu-latest strategy: @@ -52,5 +51,4 @@ jobs: toolchain: ${{ matrix.rust }} override: true - run: cargo test --release - - run: cargo test --features block-padding --release - run: cargo test --all-features --release diff --git a/Cargo.lock b/Cargo.lock index edd9cf52..edf29492 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,9 +11,8 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.0-pre.4" +version = "0.10.0" dependencies = [ - "block-padding", "generic-array", ] @@ -70,9 +69,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "libc" -version = "0.2.107" +version = "0.2.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" +checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01" [[package]] name = "opaque-debug" @@ -80,9 +79,9 @@ version = "0.3.0" [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a" dependencies = [ "unicode-xid", ] @@ -98,9 +97,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568" [[package]] name = "serde" @@ -124,9 +123,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.69" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" +checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527" dependencies = [ "itoa", "ryu", @@ -135,9 +134,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index 46bb9147..0a63b533 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# RustCrypto: Utilities [![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link] +# RustCrypto: Utilities + +[![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link] ![Apache2/MIT licensed][license-image] This repository contains various utility crates used in the RustCrypto project. @@ -6,15 +8,16 @@ This repository contains various utility crates used in the RustCrypto project. | Name | crates.io | Docs | Description | |------|-----------|------|--------------| -| `blobby` | [![crates.io](https://img.shields.io/crates/v/blobby.svg)](https://crates.io/crates/blobby) | [![Documentation](https://docs.rs/blobby/badge.svg)](https://docs.rs/blobby) | Decoder of the simple de-duplicated binary blob storage format | -| `block-buffer` | [![crates.io](https://img.shields.io/crates/v/block-buffer.svg)](https://crates.io/crates/block-buffer) | [![Documentation](https://docs.rs/block-buffer/badge.svg)](https://docs.rs/block-buffer) | Fixed size buffer for block processing of data | -| `block‑padding` | [![crates.io](https://img.shields.io/crates/v/block-padding.svg)](https://crates.io/crates/block-padding) | [![Documentation](https://docs.rs/block-padding/badge.svg)](https://docs.rs/block-padding) | Padding and unpadding of messages divided into blocks | -| `collectable` | [![crates.io](https://img.shields.io/crates/v/collectable.svg)](https://crates.io/crates/collectable) | [![Documentation](https://docs.rs/collectable/badge.svg)](https://docs.rs/collectable) | Fallible, `no_std`-friendly collection traits | -| `cpufeatures` | [![crates.io](https://img.shields.io/crates/v/cpufeatures.svg)](https://crates.io/crates/cpufeatures) | [![Documentation](https://docs.rs/cpufeatures/badge.svg)](https://docs.rs/cpufeatures) | Lightweight and efficient alternative to the `is_x86_feature_detected!` macro | -| `dbl` | [![crates.io](https://img.shields.io/crates/v/dbl.svg)](https://crates.io/crates/dbl) | [![Documentation](https://docs.rs/dbl/badge.svg)](https://docs.rs/dbl) | Double operation in Galois Field (GF) | -| `hex-literal` | [![crates.io](https://img.shields.io/crates/v/hex-literal.svg)](https://crates.io/crates/hex-literal) | [![Documentation](https://docs.rs/hex-literal/badge.svg)](https://docs.rs/hex-literal) | Procedural macro for converting hexadecimal string to byte array at compile time | -| `opaque-debug` | [![crates.io](https://img.shields.io/crates/v/opaque-debug.svg)](https://crates.io/crates/opaque-debug) | [![Documentation](https://docs.rs/opaque-debug/badge.svg)](https://docs.rs/opaque-debug) | Macro for opaque `Debug` trait implementation | -| `zeroize` | [![crates.io](https://img.shields.io/crates/v/zeroize.svg)](https://crates.io/crates/zeroize) | [![Documentation](https://docs.rs/zeroize/badge.svg)](https://docs.rs/zeroize) | Securely zero memory while avoiding compiler optimizations | +| [`blobby`] | [![crates.io](https://img.shields.io/crates/v/blobby.svg)](https://crates.io/crates/blobby) | [![Documentation](https://docs.rs/blobby/badge.svg)](https://docs.rs/blobby) | Decoder of the simple de-duplicated binary blob storage format | +| [`block-buffer`] | [![crates.io](https://img.shields.io/crates/v/block-buffer.svg)](https://crates.io/crates/block-buffer) | [![Documentation](https://docs.rs/block-buffer/badge.svg)](https://docs.rs/block-buffer) | Fixed size buffer for block processing of data | +| [`block‑padding`] | [![crates.io](https://img.shields.io/crates/v/block-padding.svg)](https://crates.io/crates/block-padding) | [![Documentation](https://docs.rs/block-padding/badge.svg)](https://docs.rs/block-padding) | Padding and unpadding of messages divided into blocks | +| [`collectable`] | [![crates.io](https://img.shields.io/crates/v/collectable.svg)](https://crates.io/crates/collectable) | [![Documentation](https://docs.rs/collectable/badge.svg)](https://docs.rs/collectable) | Fallible, `no_std`-friendly collection traits | +| [`cpufeatures`] | [![crates.io](https://img.shields.io/crates/v/cpufeatures.svg)](https://crates.io/crates/cpufeatures) | [![Documentation](https://docs.rs/cpufeatures/badge.svg)](https://docs.rs/cpufeatures) | Lightweight and efficient alternative to the `is_x86_feature_detected!` macro | +| [`dbl`] | [![crates.io](https://img.shields.io/crates/v/dbl.svg)](https://crates.io/crates/dbl) | [![Documentation](https://docs.rs/dbl/badge.svg)](https://docs.rs/dbl) | Double operation in Galois Field (GF) | +| [`hex-literal`] | [![crates.io](https://img.shields.io/crates/v/hex-literal.svg)](https://crates.io/crates/hex-literal) | [![Documentation](https://docs.rs/hex-literal/badge.svg)](https://docs.rs/hex-literal) | Procedural macro for converting hexadecimal string to byte array at compile time | +| [`opaque-debug`] | [![crates.io](https://img.shields.io/crates/v/opaque-debug.svg)](https://crates.io/crates/opaque-debug) | [![Documentation](https://docs.rs/opaque-debug/badge.svg)](https://docs.rs/opaque-debug) | Macro for opaque `Debug` trait implementation | +| [`wycheproof2blb`] | [![crates.io](https://img.shields.io/crates/v/wycheproof2blb.svg)](https://crates.io/crates/wycheproof2blb) | [![Documentation](https://docs.rs/wycheproof2blb/badge.svg)](https://docs.rs/wycheproof2blb) | Utility for converting [Wycheproof] test vectors to the blobby format | +| [`zeroize`] | [![crates.io](https://img.shields.io/crates/v/zeroize.svg)](https://crates.io/crates/zeroize) | [![Documentation](https://docs.rs/zeroize/badge.svg)](https://docs.rs/zeroize) | Securely zero memory while avoiding compiler optimizations | ## License @@ -27,13 +30,29 @@ at your option. ### Contribution -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. [//]: # (badges) [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/utils + +[//]: # (crates) + +[`blobby`]: ./blobby +[`block-buffer`]: ./block-buffer +[`block‑padding`]: ./block‑padding +[`collectable`]: ./collectable +[`cpufeatures`]: ./cpufeatures +[`dbl`]: ./dbl +[`hex-literal`]: ./hex-literal +[`opaque-debug`]: ./opaque-debug +[`wycheproof2blb`]: ./wycheproof2blb +[`zeroize`]: ./zeroize + +[//]: # (misc) + +[Wycheproof]: https://github.com/google/wycheproof diff --git a/block-buffer/CHANGELOG.md b/block-buffer/CHANGELOG.md index 73d7db09..b7c27fce 100644 --- a/block-buffer/CHANGELOG.md +++ b/block-buffer/CHANGELOG.md @@ -4,6 +4,17 @@ 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-07) +### Changed +- Significant reduction of number of unreachable panics. ([#671]) +- Added buffer kind type parameter to `BlockBuffer`, respective marker types, and type aliases. ([#671]) +- Various `BlockBuffer` method changes. ([#671]) + +### Removed +- `pad_with` method and dependency on `block-padding`. ([#671]) + +[#671]: https://github.com/RustCrypto/utils/pull/671 + ## 0.10.0 (2020-12-08) ### Changed - Rename `input_block(s)` methods to `digest_block(s)`. ([#113]) diff --git a/block-buffer/Cargo.toml b/block-buffer/Cargo.toml index 124e5a7a..62ca06bd 100644 --- a/block-buffer/Cargo.toml +++ b/block-buffer/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "block-buffer" -version = "0.10.0-pre.4" # Also update html_root_url in lib.rs when bumping this +version = "0.10.0" # Also update html_root_url in lib.rs when bumping this authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" -description = "Fixed size buffer for block processing of data" +description = "Buffer type for block processing of data" documentation = "https://docs.rs/block-buffer" repository = "https://github.com/RustCrypto/utils" keywords = ["block", "buffer"] categories = ["cryptography", "no-std"] edition = "2018" +readme = "README.md" [dependencies] -block-padding = { version = "0.3.0-pre", path = "../block-padding", optional = true } generic-array = "0.14" diff --git a/block-buffer/README.md b/block-buffer/README.md new file mode 100644 index 00000000..cb134cc9 --- /dev/null +++ b/block-buffer/README.md @@ -0,0 +1,40 @@ +# [RustCrypto]: Block Buffer + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Buffer type for block processing of data with minimized amount of unreachable panics. + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/block-buffer.svg +[crate-link]: https://crates.io/crates/block-buffer +[docs-image]: https://docs.rs/block-buffer/badge.svg +[docs-link]: https://docs.rs/block-buffer/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils +[build-image]: https://github.com/RustCrypto/utils/workflows/block-buffer/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/utils/actions/workflows/block-buffer.yml + +[//]: # (general links) + +[RustCrypto]: https://github.com/rustcrypto diff --git a/block-buffer/src/buffer.rs b/block-buffer/src/buffer.rs deleted file mode 100644 index fd0a0806..00000000 --- a/block-buffer/src/buffer.rs +++ /dev/null @@ -1,308 +0,0 @@ -#[cfg(feature = "block-padding")] -use block_padding::Padding; - -use crate::{ - utils::{to_blocks, to_blocks_mut}, - Block, DigestBuffer, InvalidLength, ParBlock, -}; -use core::slice; -use generic_array::{typenum::U1, ArrayLength}; - -/// Buffer for block processing of data. -#[derive(Clone, Default)] -pub struct BlockBuffer> { - buffer: Block, - pos: usize, -} - -impl> BlockBuffer { - /// 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 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(); - 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..]); - } - - 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)); - } - - let n = leftover.len(); - if n != 0 { - let block = gen_block(state); - process(leftover, &block[..n]); - self.buffer = block; - } - self.set_pos_unchecked(n); - } - - /// 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); - } - - /// Simplified version of the [`par_xor_data`][BlockBuffer::par_xor_data] method, with `N = 1`. - #[inline] - 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 `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); - } - - /// Process `data` in blocks and write result to `out_buf`, storing - /// leftovers for future use. - #[inline] - pub fn block_mode_processing<'a>( - &mut self, - mut data: &[u8], - buf: &'a mut [u8], - mut process: impl FnMut(&mut [Block]), - ) -> Result<&'a [u8], InvalidLength> { - let pos = self.get_pos(); - let rem = self.remaining(); - let mut blocks_processed = 0; - let (_, mut buf_blocks, _) = to_blocks_mut::(buf); - if pos != 0 { - let n = data.len(); - if n < rem { - // double slicing allows to remove panic branches - self.buffer[pos..][..n].copy_from_slice(data); - self.set_pos_unchecked(pos + n); - return Ok(&buf[..0]); - } - if buf_blocks.is_empty() { - return Err(InvalidLength); - } - - let (l, r) = buf_blocks.split_at_mut(1); - let buf_block = &mut l[0]; - buf_blocks = r; - let (l, r) = data.split_at(rem); - data = r; - - buf_block[..pos].copy_from_slice(&self.buffer[..pos]); - buf_block[pos..].copy_from_slice(l); - - process(slice::from_mut(buf_block)); - blocks_processed += 1; - } - - let (data_blocks, leftover) = to_blocks(data); - let buf_blocks = buf_blocks - .get_mut(..data_blocks.len()) - .ok_or(InvalidLength)?; - buf_blocks.clone_from_slice(data_blocks); - process(buf_blocks); - blocks_processed += buf_blocks.len(); - - let n = leftover.len(); - self.buffer[..n].copy_from_slice(leftover); - self.set_pos_unchecked(n); - - let res = unsafe { - let res_len = BlockSize::USIZE * blocks_processed; - // SAFETY: number of processed blocks never exceeds capacity of `buf` - debug_assert!(buf.len() >= res_len); - buf.get_unchecked(..res_len) - }; - Ok(res) - } - - /// 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. - #[inline(always)] - pub fn digest_pad( - &mut self, - delim: u8, - suffix: &[u8], - mut compress: impl FnMut(&Block), - ) { - let pos = self.get_pos(); - self.buffer[pos] = delim; - for b in &mut self.buffer[pos + 1..] { - *b = 0; - } - - 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. - #[inline] - pub fn len64_padding_be(&mut self, data_len: u64, compress: impl FnMut(&Block)) { - 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.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.digest_pad(0x80, &data_len.to_be_bytes(), compress); - } - - /// Pad message with a given padding `P`. - #[cfg(feature = "block-padding")] - #[inline] - 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. - #[inline] - pub fn size(&self) -> usize { - BlockSize::USIZE - } - - /// Return number of remaining bytes in the internall buffer. - #[inline] - pub fn remaining(&self) -> usize { - self.size() - self.get_pos() - } - - /// 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; - } -} - -impl> DigestBuffer for BlockBuffer { - #[inline] - fn digest_blocks(&mut self, mut input: &[u8], mut compress: impl FnMut(&[Block])) { - let pos = self.get_pos(); - let r = self.remaining(); - 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 pos != 0 { - let (left, right) = input.split_at(r); - input = right; - self.buffer[pos..].copy_from_slice(left); - compress(slice::from_ref(&self.buffer)); - } - - let (blocks, leftover) = to_blocks(input); - compress(blocks); - - let n = leftover.len(); - self.buffer[..n].copy_from_slice(leftover); - self.set_pos_unchecked(n); - } - - #[inline] - fn reset(&mut self) { - self.pos = 0; - } -} - -#[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); -} - -fn unreachable>(_: &mut S) -> ParBlock { - unreachable!(); -} diff --git a/block-buffer/src/lazy.rs b/block-buffer/src/lazy.rs deleted file mode 100644 index 2a705ebe..00000000 --- a/block-buffer/src/lazy.rs +++ /dev/null @@ -1,184 +0,0 @@ -use crate::{ - utils::{to_blocks, to_blocks_mut}, - Block, DigestBuffer, InvalidLength, -}; -use core::slice; -use generic_array::{typenum::U1, ArrayLength}; - -/// Buffer for lazy block processing of data. -#[derive(Clone, Default)] -pub struct LazyBlockBuffer> { - buffer: Block, - pos: usize, -} - -impl> LazyBlockBuffer { - /// Process `data` in blocks and write result to `out_buf`, storing - /// leftovers for future use. - #[inline] - pub fn block_mode_processing<'a>( - &mut self, - mut data: &[u8], - buf: &'a mut [u8], - mut process: impl FnMut(&mut [Block]), - ) -> Result<&'a [u8], InvalidLength> { - let pos = self.get_pos(); - let rem = self.remaining(); - let mut blocks_processed = 0; - let (_, mut buf_blocks, _) = to_blocks_mut::(buf); - if pos != 0 { - let n = data.len(); - if n <= rem { - // double slicing allows to remove panic branches - self.buffer[pos..][..n].copy_from_slice(data); - self.set_pos_unchecked(pos + n); - return Ok(&buf[..0]); - } - if buf_blocks.is_empty() { - return Err(InvalidLength); - } - - let (l, r) = buf_blocks.split_at_mut(1); - let buf_block = &mut l[0]; - buf_blocks = r; - let (l, r) = data.split_at(rem); - data = r; - - buf_block[..pos].copy_from_slice(&self.buffer[..pos]); - buf_block[pos..].copy_from_slice(l); - - process(slice::from_mut(buf_block)); - blocks_processed += 1; - } - - let (data_blocks, leftover) = to_blocks_lazy(data); - let buf_blocks = buf_blocks - .get_mut(..data_blocks.len()) - .ok_or(InvalidLength)?; - buf_blocks.clone_from_slice(data_blocks); - process(buf_blocks); - blocks_processed += buf_blocks.len(); - - let n = leftover.len(); - self.buffer[..n].copy_from_slice(leftover); - self.set_pos_unchecked(n); - - let res = unsafe { - let res_len = BlockSize::USIZE * blocks_processed; - // SAFETY: number of processed blocks never exceeds capacity of `buf` - debug_assert!(buf.len() >= res_len); - buf.get_unchecked(..res_len) - }; - Ok(res) - } - - /// Pad remaining data with zeros and call `compress` with resulting block. - pub fn pad_zeros(&mut self) -> &mut Block { - let pos = self.get_pos(); - self.buffer[pos..].iter_mut().for_each(|b| *b = 0); - self.set_pos_unchecked(0); - &mut self.buffer - } - - /// Return block if buffer is full, otherwise returns `None`. - #[inline] - pub fn get_full_block(&mut self) -> Option<&mut Block> { - match self.remaining() { - 0 => Some(&mut self.buffer), - _ => None, - } - } - - /// Return size of the internall buffer in bytes. - #[inline] - pub fn size(&self) -> usize { - BlockSize::USIZE - } - - /// Return number of remaining bytes in the internall buffer. - #[inline] - pub fn remaining(&self) -> usize { - self.size() - self.get_pos() - } - - /// 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 or equal to 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; - } -} - -impl> DigestBuffer for LazyBlockBuffer { - #[inline] - fn digest_blocks(&mut self, mut input: &[u8], mut compress: impl FnMut(&[Block])) { - let pos = self.get_pos(); - let r = self.remaining(); - 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 pos != 0 { - let (left, right) = input.split_at(r); - input = right; - self.buffer[pos..].copy_from_slice(left); - compress(core::slice::from_ref(&self.buffer)); - } - - let (blocks, leftover) = to_blocks_lazy(input); - compress(blocks); - - let n = leftover.len(); - self.buffer[..n].copy_from_slice(leftover); - self.set_pos_unchecked(n); - } - - #[inline] - fn reset(&mut self) { - self.pos = 0; - } -} - -fn to_blocks_lazy>(data: &[u8]) -> (&[Block], &[u8]) { - let (mut blocks, mut leftover) = to_blocks(data); - if leftover.is_empty() { - debug_assert!(!blocks.is_empty()); - let m = blocks.len() - 1; - // SAFETY: at this stage `input` always contains at least one byte, - // so either `leftover` is not empty or we have at least one block - unsafe { - leftover = blocks.get_unchecked(m); - blocks = blocks.get_unchecked(..m); - } - } - (blocks, leftover) -} diff --git a/block-buffer/src/lib.rs b/block-buffer/src/lib.rs index 76274efb..52e63bf6 100644 --- a/block-buffer/src/lib.rs +++ b/block-buffer/src/lib.rs @@ -1,48 +1,324 @@ //! 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", - html_root_url = "https://docs.rs/block-buffer/0.10.0-pre.4" + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_root_url = "https://docs.rs/block-buffer/0.10.0" )] #![warn(missing_docs, rust_2018_idioms)] -#[cfg(feature = "block-padding")] -pub use block_padding; pub use generic_array; -use core::fmt; -use generic_array::{ArrayLength, GenericArray}; +use core::{marker::PhantomData, slice}; +use generic_array::{ + typenum::{IsLess, Le, NonZero, U256}, + ArrayLength, GenericArray, +}; -mod buffer; -mod lazy; -mod utils; +mod sealed; -pub use buffer::BlockBuffer; -pub use lazy::LazyBlockBuffer; - -/// Block on which a `BlockBuffer` operates. +/// Block on which `BlockBuffer` operates. pub type Block = GenericArray; -/// Blocks being acted over in parallel. -pub type ParBlock = GenericArray, ParBlocks>; -/// Trait which generalizes digest functionality of buffers. -pub trait DigestBuffer>: Default { +/// Trait for buffer kinds. +pub trait BufferKind: sealed::Sealed {} + +/// Eager block buffer kind, which guarantees that buffer position +/// always lies in the range of `0..BlockSize`. +#[derive(Copy, Clone, Debug, Default)] +pub struct Eager {} + +/// Lazy block buffer kind, which guarantees that buffer position +/// always lies in the range of `0..=BlockSize`. +#[derive(Copy, Clone, Debug, Default)] +pub struct Lazy {} + +impl BufferKind for Eager {} +impl BufferKind for Lazy {} + +/// Eager block buffer. +pub type EagerBuffer = BlockBuffer; +/// Lazy block buffer. +pub type LazyBuffer = BlockBuffer; + +/// Buffer for block processing of data. +#[derive(Debug)] +pub struct BlockBuffer +where + BlockSize: ArrayLength + IsLess, + Le: NonZero, + Kind: BufferKind, +{ + buffer: Block, + pos: u8, + _pd: PhantomData, +} + +impl Default for BlockBuffer +where + BlockSize: ArrayLength + IsLess, + Le: NonZero, + Kind: BufferKind, +{ + fn default() -> Self { + Self { + buffer: Default::default(), + pos: 0, + _pd: PhantomData, + } + } +} + +impl Clone for BlockBuffer +where + BlockSize: ArrayLength + IsLess, + Le: NonZero, + Kind: BufferKind, +{ + fn clone(&self) -> Self { + Self { + buffer: self.buffer.clone(), + pos: self.pos, + _pd: PhantomData, + } + } +} + +impl BlockBuffer +where + BlockSize: ArrayLength + IsLess, + Le: NonZero, + Kind: BufferKind, +{ + /// Create new buffer from slice. + /// + /// # Panics + /// If slice length is not valid for used buffer kind. + #[inline(always)] + pub fn new(buf: &[u8]) -> Self { + let pos = buf.len(); + assert!(Kind::invariant(pos, BlockSize::USIZE)); + let mut buffer = Block::::default(); + buffer[..pos].copy_from_slice(buf); + Self { + buffer, + pos: pos as u8, + _pd: PhantomData, + } + } + /// Digest data in `input` in blocks of size `BlockSize` using /// the `compress` function, which accepts slice of blocks. - fn digest_blocks(&mut self, input: &[u8], compress: impl FnMut(&[Block])); + #[inline] + pub fn digest_blocks( + &mut self, + mut input: &[u8], + mut compress: impl FnMut(&[Block]), + ) { + let pos = self.get_pos(); + // using `self.remaining()` for some reason + // prevents panic elimination + let rem = self.size() - pos; + let n = input.len(); + // Note that checking condition `pos + n < BlockSize` is + // equivalent to checking `n < rem`, where `rem` is equal + // to `BlockSize - pos`. Using the latter allows us to work + // around compiler accounting for possible overflow of + // `pos + n` which results in it inserting unreachable + // panic branches. Using `unreachable_unchecked` in `get_pos` + // we convince compiler that `BlockSize - pos` never underflows. + if Kind::invariant(n, rem) { + // double slicing allows to remove panic branches + self.buffer[pos..][..n].copy_from_slice(input); + self.set_pos_unchecked(pos + n); + return; + } + if pos != 0 { + let (left, right) = input.split_at(rem); + input = right; + self.buffer[pos..].copy_from_slice(left); + compress(slice::from_ref(&self.buffer)); + } + + let (blocks, leftover) = Kind::split_blocks(input); + if !blocks.is_empty() { + compress(blocks); + } + + let n = leftover.len(); + self.buffer[..n].copy_from_slice(leftover); + self.set_pos_unchecked(n); + } /// Reset buffer by setting cursor position to zero. - fn reset(&mut self); + #[inline(always)] + pub fn reset(&mut self) { + self.set_pos_unchecked(0); + } + + /// Pad remaining data with zeros and return resulting block. + #[inline(always)] + pub fn pad_with_zeros(&mut self) -> &mut Block { + let pos = self.get_pos(); + self.buffer[pos..].iter_mut().for_each(|b| *b = 0); + self.set_pos_unchecked(0); + &mut self.buffer + } + + /// Return current cursor position. + #[inline(always)] + pub fn get_pos(&self) -> usize { + let pos = self.pos as usize; + if !Kind::invariant(pos, BlockSize::USIZE) { + debug_assert!(false); + // SAFETY: `pos` never breaks the invariant + unsafe { + core::hint::unreachable_unchecked(); + } + } + pos + } + + /// Return slice of data stored inside the buffer. + #[inline(always)] + pub fn get_data(&self) -> &[u8] { + &self.buffer[..self.get_pos()] + } + + /// Set buffer content and cursor position. + /// + /// # Panics + /// If `pos` is bigger or equal to block size. + #[inline] + pub fn set(&mut self, buf: Block, pos: usize) { + assert!(Kind::invariant(pos, BlockSize::USIZE)); + self.buffer = buf; + self.set_pos_unchecked(pos); + } + + /// Return size of the internall buffer in bytes. + #[inline(always)] + pub fn size(&self) -> usize { + BlockSize::USIZE + } + + /// Return number of remaining bytes in the internall buffer. + #[inline(always)] + pub fn remaining(&self) -> usize { + self.size() - self.get_pos() + } + + #[inline(always)] + fn set_pos_unchecked(&mut self, pos: usize) { + debug_assert!(Kind::invariant(pos, BlockSize::USIZE)); + self.pos = pos as u8; + } } -/// Error type used by the [`BlockBuffer::block_mode_processing`] and -/// [`LazyBlockBuffer::block_mode_processing`] methods. -#[derive(Copy, Clone, Debug)] -pub struct InvalidLength; +impl BlockBuffer +where + BlockSize: ArrayLength + IsLess, + Le: NonZero, +{ + /// Set `data` to generated blocks. + #[inline] + pub fn set_data( + &mut self, + mut data: &mut [u8], + mut process_blocks: impl FnMut(&mut [Block]), + ) { + let pos = self.get_pos(); + let r = self.remaining(); + let n = data.len(); + if pos != 0 { + if n < r { + // double slicing allows to remove panic branches + data.copy_from_slice(&self.buffer[pos..][..n]); + self.set_pos_unchecked(pos + n); + return; + } + let (left, right) = data.split_at_mut(r); + data = right; + left.copy_from_slice(&self.buffer[pos..]); + } + + let (blocks, leftover) = to_blocks_mut(data); + process_blocks(blocks); -impl fmt::Display for InvalidLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("Invalid Length") + let n = leftover.len(); + if n != 0 { + let mut block = Default::default(); + process_blocks(slice::from_mut(&mut block)); + leftover.copy_from_slice(&block[..n]); + self.buffer = block; + } + self.set_pos_unchecked(n); } + + /// 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), + ) { + if suffix.len() > BlockSize::USIZE { + panic!("suffix is too long"); + } + let pos = self.get_pos(); + self.buffer[pos] = delim; + for b in &mut self.buffer[pos + 1..] { + *b = 0; + } + + let n = self.size() - suffix.len(); + if self.size() - pos - 1 < suffix.len() { + compress(&self.buffer); + let mut block = Block::::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. + #[inline] + pub fn len64_padding_be(&mut self, data_len: u64, compress: impl FnMut(&Block)) { + 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.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.digest_pad(0x80, &data_len.to_be_bytes(), compress); + } +} + +/// Split message into mutable slice of parallel blocks, blocks, and leftover bytes. +#[inline(always)] +fn to_blocks_mut>(data: &mut [u8]) -> (&mut [Block], &mut [u8]) { + let nb = data.len() / N::USIZE; + let (left, right) = data.split_at_mut(nb * N::USIZE); + let p = left.as_ptr() as *mut Block; + // SAFETY: we guarantee that `blocks` does not point outside of `data` + let blocks = unsafe { slice::from_raw_parts_mut(p, nb) }; + (blocks, right) } diff --git a/block-buffer/src/sealed.rs b/block-buffer/src/sealed.rs new file mode 100644 index 00000000..256525ee --- /dev/null +++ b/block-buffer/src/sealed.rs @@ -0,0 +1,64 @@ +use super::{ArrayLength, Block}; +use core::slice; + +/// Sealed trait for buffer kinds. +pub trait Sealed { + /// Invariant guaranteed by a buffer kind, i.e. with correct + /// buffer code this function always returns true. + fn invariant(pos: usize, block_size: usize) -> bool; + + /// Split input data into slice fo blocks and tail. + fn split_blocks>(data: &[u8]) -> (&[Block], &[u8]); +} + +impl Sealed for super::Eager { + #[inline(always)] + fn invariant(pos: usize, block_size: usize) -> bool { + pos < block_size + } + + #[inline(always)] + fn split_blocks>(data: &[u8]) -> (&[Block], &[u8]) { + let nb = data.len() / N::USIZE; + let blocks_len = nb * N::USIZE; + let tail_len = data.len() - blocks_len; + // SAFETY: we guarantee that created slices do not point + // outside of `data` + unsafe { + let blocks_ptr = data.as_ptr() as *const Block; + let tail_ptr = data.as_ptr().add(blocks_len); + ( + slice::from_raw_parts(blocks_ptr, nb), + slice::from_raw_parts(tail_ptr, tail_len), + ) + } + } +} + +impl Sealed for super::Lazy { + #[inline(always)] + fn invariant(pos: usize, block_size: usize) -> bool { + pos <= block_size + } + + #[inline(always)] + fn split_blocks>(data: &[u8]) -> (&[Block], &[u8]) { + let nb = if data.is_empty() || data.len() % N::USIZE != 0 { + data.len() / N::USIZE + } else { + data.len() / N::USIZE - 1 + }; + let blocks_len = nb * N::USIZE; + let tail_len = data.len() - blocks_len; + // SAFETY: we guarantee that created slices do not point + // outside of `data` + unsafe { + let blocks_ptr = data.as_ptr() as *const Block; + let tail_ptr = data.as_ptr().add(blocks_len); + ( + slice::from_raw_parts(blocks_ptr, nb), + slice::from_raw_parts(tail_ptr, tail_len), + ) + } + } +} diff --git a/block-buffer/src/utils.rs b/block-buffer/src/utils.rs deleted file mode 100644 index 707e2c5a..00000000 --- a/block-buffer/src/utils.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::{Block, ParBlock}; -use core::slice; -use generic_array::ArrayLength; - -/// Split message into slice of blocks and leftover bytes. -#[inline(always)] -pub(crate) 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) -} - -/// Split message into mutable slice of parallel blocks, blocks, and leftover bytes. -#[allow(clippy::type_complexity)] -#[inline(always)] -pub(crate) 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 { - ( - slice::from_raw_parts_mut(pb_ptr, npb), - slice::from_raw_parts_mut(b_ptr, nb), - data, - ) - } -} diff --git a/block-buffer/tests/mod.rs b/block-buffer/tests/mod.rs new file mode 100644 index 00000000..6dfa1ae1 --- /dev/null +++ b/block-buffer/tests/mod.rs @@ -0,0 +1,188 @@ +use block_buffer::{ + generic_array::typenum::{U10, U16, U24, U4, U8}, + Block, EagerBuffer, LazyBuffer, +}; + +#[test] +fn test_eager_digest_pad() { + let mut buf = EagerBuffer::::default(); + let inputs = [ + &b"01234567"[..], + &b"89"[..], + &b"abcdefghij"[..], + &b"klmnopqrs"[..], + &b"tuv"[..], + &b"wx"[..], + ]; + let exp_blocks = [ + (0, &[b"0123", b"4567"][..]), + (2, &[b"89ab"][..]), + (2, &[b"cdef", b"ghij"][..]), + (3, &[b"klmn", b"opqr"][..]), + (4, &[b"stuv"][..]), + ]; + let exp_poses = [0, 2, 0, 1, 0, 2]; + + let mut n = 0; + for (i, input) in inputs.iter().enumerate() { + buf.digest_blocks(input, |b| { + let (j, exp) = exp_blocks[n]; + n += 1; + assert_eq!(i, j); + assert_eq!(b.len(), exp.len()); + assert!(b.iter().zip(exp.iter()).all(|v| v.0[..] == v.1[..])); + }); + assert_eq!(exp_poses[i], buf.get_pos()); + } + assert_eq!(buf.pad_with_zeros()[..], b"wx\0\0"[..]); + assert_eq!(buf.get_pos(), 0); +} + +#[test] +fn test_lazy_digest_pad() { + let mut buf = LazyBuffer::::default(); + let inputs = [ + &b"01234567"[..], + &b"89"[..], + &b"abcdefghij"[..], + &b"klmnopqrs"[..], + ]; + let expected = [ + (0, &[b"0123"][..]), + (1, &[b"4567"][..]), + (2, &[b"89ab"][..]), + (2, &[b"cdef"][..]), + (3, &[b"ghij"][..]), + (3, &[b"klmn", b"opqr"][..]), + ]; + let exp_poses = [4, 2, 4, 1]; + + let mut n = 0; + for (i, input) in inputs.iter().enumerate() { + buf.digest_blocks(input, |b| { + let (j, exp) = expected[n]; + n += 1; + assert_eq!(i, j); + assert_eq!(b.len(), exp.len()); + assert!(b.iter().zip(exp.iter()).all(|v| v.0[..] == v.1[..])); + }); + assert_eq!(exp_poses[i], buf.get_pos()); + } + assert_eq!(buf.pad_with_zeros()[..], b"s\0\0\0"[..]); + assert_eq!(buf.get_pos(), 0); +} + +#[test] +fn test_eager_set_data() { + let mut buf = EagerBuffer::::default(); + + let mut n = 0u8; + let mut gen = |blocks: &mut [Block]| { + for block in blocks { + block.iter_mut().for_each(|b| *b = n); + n += 1; + } + }; + + let mut out = [0u8; 6]; + buf.set_data(&mut out, &mut gen); + assert_eq!(out, [0, 0, 0, 0, 1, 1]); + assert_eq!(buf.get_pos(), 2); + + let mut out = [0u8; 3]; + buf.set_data(&mut out, &mut gen); + assert_eq!(out, [1, 1, 2]); + assert_eq!(buf.get_pos(), 1); + + let mut out = [0u8; 3]; + buf.set_data(&mut out, &mut gen); + assert_eq!(out, [2, 2, 2]); + assert_eq!(n, 3); + assert_eq!(buf.get_pos(), 0); +} + +#[test] +#[rustfmt::skip] +fn test_eager_paddings() { + let mut buf_be = EagerBuffer::::new(&[0x42]); + let mut buf_le = buf_be.clone(); + let mut out_be = Vec::::new(); + let mut out_le = Vec::::new(); + let len = 0x0001_0203_0405_0607; + buf_be.len64_padding_be(len, |block| out_be.extend(block)); + buf_le.len64_padding_le(len, |block| out_le.extend(block)); + + assert_eq!( + out_be, + [ + 0x42, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + ], + ); + assert_eq!( + out_le, + [ + 0x42, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + ], + ); + + let mut buf_be = EagerBuffer::::new(&[0x42]); + let mut buf_le = buf_be.clone(); + let mut out_be = Vec::::new(); + let mut out_le = Vec::::new(); + buf_be.len64_padding_be(len, |block| out_be.extend(block)); + buf_le.len64_padding_le(len, |block| out_le.extend(block)); + + assert_eq!( + out_be, + [0x42, 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07], + ); + assert_eq!( + out_le, + [0x42, 0x80, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00], + ); + + let mut buf = EagerBuffer::::new(&[0x42]); + let mut out = Vec::::new(); + let len = 0x0001_0203_0405_0607_0809_0a0b_0c0d_0e0f; + buf.len128_padding_be(len, |block| out.extend(block)); + assert_eq!( + out, + [ + 0x42, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + ], + ); + + let mut buf = EagerBuffer::::new(&[0x42]); + let mut out = Vec::::new(); + let len = 0x0001_0203_0405_0607_0809_0a0b_0c0d_0e0f; + buf.len128_padding_be(len, |block| out.extend(block)); + assert_eq!( + out, + [ + 0x42, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + ], + ); + + let mut buf = EagerBuffer::::new(&[0x42]); + let mut out = Vec::::new(); + buf.digest_pad(0xff, &[0x10, 0x11, 0x12], |block| out.extend(block)); + assert_eq!( + out, + [0x42, 0xff, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12], + ); + + let mut buf = EagerBuffer::::new(&[0x42]); + let mut out = Vec::::new(); + buf.digest_pad(0xff, &[0x10, 0x11], |block| out.extend(block)); + assert_eq!( + out, + [0x42, 0xff, 0x10, 0x11], + ); +}