#![no_std] pub use generic_array; #[cfg(feature = "block-padding")] pub use block_padding; use core::{slice, convert::TryInto}; use generic_array::{GenericArray, ArrayLength}; #[cfg(feature = "block-padding")] use block_padding::{Padding, PadError}; /// Buffer for block processing of data #[derive(Clone, Default)] pub struct BlockBuffer> { buffer: GenericArray, pos: usize, } impl> BlockBuffer { /// Process data in `input` in blocks of size `BlockSize` using function `f`. #[inline] pub fn input_block( &mut self, mut input: &[u8], mut f: impl FnMut(&GenericArray), ) { 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; } 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 mut chunks_iter = input.chunks_exact(self.size()); for chunk in &mut chunks_iter { f(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(); } /// Process data in `input` in blocks of size `BlockSize` using function `f`, which accepts /// slice of blocks. #[inline] pub fn input_blocks( &mut self, mut input: &[u8], mut f: impl FnMut(&[GenericArray]), ) { 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; } 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)); } // 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( &mut self, mut input: &[u8], mut f: impl FnMut(&GenericArray), ) { 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; } 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); } while input.len() > self.size() { let (block, r) = input.split_at(self.size()); input = r; f(block.try_into().unwrap()); } self.buffer[..input.len()].copy_from_slice(input); self.pos = input.len(); } /// Pad buffer with `prefix` and make sure that internall buffer /// has at least `up_to` free bytes. All remaining bytes get /// zeroed-out. #[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; set_zero(&mut self.buffer[self.pos..]); if self.remaining() < up_to { f(&self.buffer); set_zero(&mut self.buffer[..self.pos]); } } /// 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; } /// 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; } /// 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; } /// Pad message with a given padding `P` /// /// Returns `PadError` if internall buffer is full, which can only happen if /// `input_lazy` was used. #[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) } /// 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 } /// Return number of remaining bytes in the internall buffer #[inline] pub fn remaining(&self) -> usize { self.size() - self.pos } /// Reset buffer by setting cursor position to zero #[inline] pub fn reset(&mut self) { self.pos = 0 } } /// 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 unsafe { core::ptr::write_bytes(dst.as_mut_ptr(), 0, dst.len()); } }