blob: 3ac454e6dab485bfc98e88e0a4a7b2ea77d90858 [file] [log] [blame]
//! Traits which define functionality of stream ciphers.
//!
//! See [RustCrypto/stream-ciphers](https://github.com/RustCrypto/stream-ciphers)
//! for ciphers implementation.
use crate::errors::{OverflowError, StreamCipherError};
use crate::stream_core::Counter;
use crate::{Block, BlockDecryptMut, BlockEncryptMut};
use inout::{InOutBuf, NotEqualError};
/// Marker trait for block-level asynchronous stream ciphers
pub trait AsyncStreamCipher: Sized {
/// Encrypt data using `InOutBuf`.
fn encrypt_inout(mut self, data: InOutBuf<'_, '_, u8>)
where
Self: BlockEncryptMut,
{
let (blocks, mut tail) = data.into_chunks();
self.encrypt_blocks_inout_mut(blocks);
let n = tail.len();
if n != 0 {
let mut block = Block::<Self>::default();
block[..n].copy_from_slice(tail.get_in());
self.encrypt_block_mut(&mut block);
tail.get_out().copy_from_slice(&block[..n]);
}
}
/// Decrypt data using `InOutBuf`.
fn decrypt_inout(mut self, data: InOutBuf<'_, '_, u8>)
where
Self: BlockDecryptMut,
{
let (blocks, mut tail) = data.into_chunks();
self.decrypt_blocks_inout_mut(blocks);
let n = tail.len();
if n != 0 {
let mut block = Block::<Self>::default();
block[..n].copy_from_slice(tail.get_in());
self.decrypt_block_mut(&mut block);
tail.get_out().copy_from_slice(&block[..n]);
}
}
/// Encrypt data in place.
fn encrypt(self, buf: &mut [u8])
where
Self: BlockEncryptMut,
{
self.encrypt_inout(buf.into());
}
/// Decrypt data in place.
fn decrypt(self, buf: &mut [u8])
where
Self: BlockDecryptMut,
{
self.decrypt_inout(buf.into());
}
/// Encrypt data from buffer to buffer.
fn encrypt_b2b(self, in_buf: &[u8], out_buf: &mut [u8]) -> Result<(), NotEqualError>
where
Self: BlockEncryptMut,
{
InOutBuf::new(in_buf, out_buf).map(|b| self.encrypt_inout(b))
}
/// Decrypt data from buffer to buffer.
fn decrypt_b2b(self, in_buf: &[u8], out_buf: &mut [u8]) -> Result<(), NotEqualError>
where
Self: BlockDecryptMut,
{
InOutBuf::new(in_buf, out_buf).map(|b| self.decrypt_inout(b))
}
}
/// Synchronous stream cipher core trait.
pub trait StreamCipher {
/// Apply keystream to `inout` data.
///
/// If end of the keystream will be achieved with the given data length,
/// method will return [`StreamCipherError`] without modifying provided `data`.
fn try_apply_keystream_inout(
&mut self,
buf: InOutBuf<'_, '_, u8>,
) -> Result<(), StreamCipherError>;
/// Apply keystream to data behind `buf`.
///
/// If end of the keystream will be achieved with the given data length,
/// method will return [`StreamCipherError`] without modifying provided `data`.
#[inline]
fn try_apply_keystream(&mut self, buf: &mut [u8]) -> Result<(), StreamCipherError> {
self.try_apply_keystream_inout(buf.into())
}
/// Apply keystream to `inout` data.
///
/// It will XOR generated keystream with the data behind `in` pointer
/// and will write result to `out` pointer.
///
/// # Panics
/// If end of the keystream will be reached with the given data length,
/// method will panic without modifying the provided `data`.
#[inline]
fn apply_keystream_inout(&mut self, buf: InOutBuf<'_, '_, u8>) {
self.try_apply_keystream_inout(buf).unwrap();
}
/// Apply keystream to data in-place.
///
/// It will XOR generated keystream with `data` and will write result
/// to the same buffer.
///
/// # Panics
/// If end of the keystream will be reached with the given data length,
/// method will panic without modifying the provided `data`.
#[inline]
fn apply_keystream(&mut self, buf: &mut [u8]) {
self.try_apply_keystream(buf).unwrap();
}
/// Apply keystream to data buffer-to-buffer.
///
/// It will XOR generated keystream with data from the `input` buffer
/// and will write result to the `output` buffer.
///
/// Returns [`StreamCipherError`] if provided `in_blocks` and `out_blocks`
/// have different lengths or if end of the keystream will be reached with
/// the given input data length.
#[inline]
fn apply_keystream_b2b(
&mut self,
input: &[u8],
output: &mut [u8],
) -> Result<(), StreamCipherError> {
InOutBuf::new(input, output)
.map_err(|_| StreamCipherError)
.and_then(|buf| self.try_apply_keystream_inout(buf))
}
}
/// Trait for seekable stream ciphers.
///
/// Methods of this trait are generic over the [`SeekNum`] trait, which is
/// implemented for primitive numeric types, i.e.: `i32`, `u32`, `u64`,
/// `u128`, and `usize`.
pub trait StreamCipherSeek {
/// Try to get current keystream position
///
/// Returns [`OverflowError`] if position can not be represented by type `T`
fn try_current_pos<T: SeekNum>(&self) -> Result<T, OverflowError>;
/// Try to seek to the given position
///
/// Returns [`StreamCipherError`] if provided position value is bigger than
/// keystream length.
fn try_seek<T: SeekNum>(&mut self, pos: T) -> Result<(), StreamCipherError>;
/// Get current keystream position
///
/// # Panics
/// If position can not be represented by type `T`
fn current_pos<T: SeekNum>(&self) -> T {
self.try_current_pos().unwrap()
}
/// Seek to the given position
///
/// # Panics
/// If provided position value is bigger than keystream length
fn seek<T: SeekNum>(&mut self, pos: T) {
self.try_seek(pos).unwrap()
}
}
impl<C: StreamCipher> StreamCipher for &mut C {
#[inline]
fn try_apply_keystream_inout(
&mut self,
buf: InOutBuf<'_, '_, u8>,
) -> Result<(), StreamCipherError> {
C::try_apply_keystream_inout(self, buf)
}
}
/// Trait implemented for numeric types which can be used with the
/// [`StreamCipherSeek`] trait.
///
/// This trait is implemented for `i32`, `u32`, `u64`, `u128`, and `usize`.
/// It is not intended to be implemented in third-party crates.
pub trait SeekNum: Sized {
/// Try to get position for block number `block`, byte position inside
/// block `byte`, and block size `bs`.
fn from_block_byte<T: Counter>(block: T, byte: u8, bs: u8) -> Result<Self, OverflowError>;
/// Try to get block number and bytes position for given block size `bs`.
fn into_block_byte<T: Counter>(self, bs: u8) -> Result<(T, u8), OverflowError>;
}
macro_rules! impl_seek_num {
{$($t:ty )*} => {
$(
impl SeekNum for $t {
fn from_block_byte<T: Counter>(block: T, byte: u8, bs: u8) -> Result<Self, OverflowError> {
debug_assert!(byte < bs);
let mut block: Self = block.try_into().map_err(|_| OverflowError)?;
if byte != 0 {
block -= 1;
}
let pos = block.checked_mul(bs as Self).ok_or(OverflowError)? + (byte as Self);
Ok(pos)
}
fn into_block_byte<T: Counter>(self, bs: u8) -> Result<(T, u8), OverflowError> {
let bs = bs as Self;
let byte = self % bs;
let block = T::try_from(self/bs).map_err(|_| OverflowError)?;
Ok((block, byte as u8))
}
}
)*
};
}
impl_seek_num! { i32 u32 u64 u128 usize }