#![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::Padding;
use core::{convert::TryInto, slice};
use generic_array::{typenum::U1, ArrayLength, GenericArray};
pub type Block<BlockSize> = GenericArray<u8, BlockSize>;
pub type ParBlock<BlockSize, ParBlocks> = GenericArray<Block<BlockSize>, ParBlocks>;
#[derive(Clone, Default)]
pub struct BlockBuffer<BlockSize: ArrayLength<u8>> {
buffer: Block<BlockSize>,
pos: usize,
}
impl<BlockSize: ArrayLength<u8>> BlockBuffer<BlockSize> {
#[inline]
pub fn digest_block(&mut self, mut input: &[u8], mut compress: impl FnMut(&Block<BlockSize>)) {
let pos = self.get_pos();
let r = self.remaining();
let n = input.len();
if n < r {
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(&self.buffer);
}
let mut chunks_iter = input.chunks_exact(self.size());
for chunk in &mut chunks_iter {
compress(chunk.try_into().unwrap());
}
let rem = chunks_iter.remainder();
self.buffer[..rem.len()].copy_from_slice(rem);
self.set_pos_unchecked(rem.len());
}
#[inline]
pub fn digest_blocks(
&mut self,
mut input: &[u8],
mut compress: impl FnMut(&[Block<BlockSize>]),
) {
let pos = self.get_pos();
let r = self.remaining();
let n = input.len();
if n < r {
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);
}
fn process_data<S, N: ArrayLength<Block<BlockSize>>>(
&mut self,
mut data: &mut [u8],
state: &mut S,
mut process: impl FnMut(&mut [u8], &[u8]),
mut gen_block: impl FnMut(&mut S) -> Block<BlockSize>,
mut gen_blocks: impl FnMut(&mut S) -> ParBlock<BlockSize, N>,
) {
let pos = self.get_pos();
let r = self.remaining();
let n = data.len();
if pos != 0 {
if n < r {
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::<BlockSize, N>(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);
}
#[inline]
pub fn par_xor_data<S, N: ArrayLength<Block<BlockSize>>>(
&mut self,
data: &mut [u8],
state: &mut S,
gen_block: impl FnMut(&mut S) -> Block<BlockSize>,
gen_blocks: impl FnMut(&mut S) -> ParBlock<BlockSize, N>,
) {
self.process_data(data, state, xor, gen_block, gen_blocks);
}
#[inline]
pub fn xor_data(&mut self, data: &mut [u8], mut gen_block: impl FnMut() -> Block<BlockSize>) {
self.process_data(data, &mut gen_block, xor, |f| f(), unreachable);
}
#[inline]
pub fn set_data(&mut self, data: &mut [u8], mut gen_block: impl FnMut() -> Block<BlockSize>) {
self.process_data(data, &mut gen_block, set, |f| f(), unreachable);
}
#[inline(always)]
fn digest_pad(&mut self, suffix: &[u8], mut compress: impl FnMut(&Block<BlockSize>)) {
let pos = self.get_pos();
self.buffer[pos] = 0x80;
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<BlockSize> = 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)
}
#[inline]
pub fn len64_padding_be(&mut self, data_len: u64, compress: impl FnMut(&Block<BlockSize>)) {
self.digest_pad(&data_len.to_be_bytes(), compress);
}
#[inline]
pub fn len64_padding_le(&mut self, data_len: u64, compress: impl FnMut(&Block<BlockSize>)) {
self.digest_pad(&data_len.to_le_bytes(), compress);
}
#[inline]
pub fn len128_padding_be(&mut self, data_len: u128, compress: impl FnMut(&Block<BlockSize>)) {
self.digest_pad(&data_len.to_be_bytes(), compress);
}
#[cfg(feature = "block-padding")]
#[inline]
pub fn pad_with<P: Padding<BlockSize>>(&mut self) -> &mut Block<BlockSize> {
let pos = self.get_pos();
P::pad(&mut self.buffer, pos);
self.set_pos_unchecked(0);
&mut self.buffer
}
#[inline]
pub fn size(&self) -> usize {
BlockSize::USIZE
}
#[inline]
pub fn remaining(&self) -> usize {
self.size() - self.get_pos()
}
#[inline]
pub fn reset(&mut self) {
self.pos = 0
}
#[inline]
pub fn get_pos(&self) -> usize {
debug_assert!(self.pos < BlockSize::USIZE);
if self.pos >= BlockSize::USIZE {
unsafe { core::hint::unreachable_unchecked() }
}
self.pos
}
pub fn set(&mut self, buf: Block<BlockSize>, 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);
}
#[inline(always)]
fn to_blocks<N: ArrayLength<u8>>(data: &[u8]) -> (&[Block<N>], &[u8]) {
let nb = data.len() / N::USIZE;
let (left, right) = data.split_at(nb * N::USIZE);
let p = left.as_ptr() as *const Block<N>;
let blocks = unsafe { slice::from_raw_parts(p, nb) };
(blocks, right)
}
#[allow(clippy::type_complexity)]
#[inline(always)]
fn to_blocks_mut<N: ArrayLength<u8>, M: ArrayLength<Block<N>>>(
data: &mut [u8],
) -> (&mut [ParBlock<N, M>], &mut [Block<N>], &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<N, M>;
let b_ptr = b_slice.as_mut_ptr() as *mut Block<N>;
unsafe {
(
slice::from_raw_parts_mut(pb_ptr, npb),
slice::from_raw_parts_mut(b_ptr, nb),
data,
)
}
}
fn unreachable<S, B: ArrayLength<u8>>(_: &mut S) -> ParBlock<B, U1> {
unreachable!();
}