use cipher::{
Block, BlockCipherEncrypt, BlockSizeUser, InnerIvInit, Iv,
IvSizeUser, typenum::{U16, U32, U64}, common::InnerUser,
};
use core::fmt;
#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
ZeroIv,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ZeroIv => write!(f, "IV cannot be all zeroes in ELK mode"),
}
}
}
impl core::error::Error for Error {}
pub trait ElkPolynomial {
fn apply_mask(block: &mut [u8]);
}
impl ElkPolynomial for U16 {
#[inline(always)]
fn apply_mask(block: &mut [u8]) {
block[1] ^= 1 << 1; block[3] ^= 1 << 1; block[4] ^= 1 << 1; block[6] ^= 1 << 7; block[8] ^= 1 << 3; block[11] ^= 1 << 5; block[13] ^= 1 << 7; block[15] ^= 1 << 0; }
}
impl ElkPolynomial for U32 {
#[inline(always)]
fn apply_mask(block: &mut [u8]) {
block[3] ^= 1 << 5; block[7] ^= 1 << 1; block[11] ^= 1 << 7; block[14] ^= 1 << 3; block[18] ^= 1 << 5; block[23] ^= 1 << 7; block[27] ^= 1 << 5; block[31] ^= 1 << 0; }
}
impl ElkPolynomial for U64 {
#[inline(always)]
fn apply_mask(block: &mut [u8]) {
block[6] ^= 1 << 5; block[14] ^= 1 << 5; block[22] ^= 1 << 3; block[28] ^= 1 << 1; block[37] ^= 1 << 3; block[46] ^= 1 << 3; block[55] ^= 1 << 7; block[63] ^= 1 << 0; }
}
#[inline]
fn step_lfsr<C>(state: &mut Block<C>)
where
C: BlockSizeUser,
C::BlockSize: ElkPolynomial,
{
let overflow = state[0] >> 7;
let len = state.len();
for i in 0..(len - 1) {
state[i] = (state[i] << 1) | (state[i + 1] >> 7);
}
state[len - 1] <<= 1;
if overflow != 0 {
C::BlockSize::apply_mask(state.as_mut_slice());
}
}
#[derive(Clone)]
#[cfg_attr(feature = "zeroize", derive(ZeroizeOnDrop))]
pub struct Elk<C>
where
C: BlockCipherEncrypt + BlockSizeUser,
C::BlockSize: ElkPolynomial,
{
#[cfg_attr(feature = "zeroize", zeroize(skip))]
cipher: C,
state: Block<C>,
}
impl<C> BlockSizeUser for Elk<C>
where
C: BlockCipherEncrypt + BlockSizeUser,
C::BlockSize: ElkPolynomial,
{
type BlockSize = C::BlockSize;
}
impl<C> InnerUser for Elk<C>
where
C: BlockCipherEncrypt + BlockSizeUser,
C::BlockSize: ElkPolynomial,
{
type Inner = C;
}
impl<C> IvSizeUser for Elk<C>
where
C: BlockCipherEncrypt + BlockSizeUser,
C::BlockSize: ElkPolynomial,
{
type IvSize = C::BlockSize;
}
impl<C> InnerIvInit for Elk<C>
where
C: BlockCipherEncrypt + BlockSizeUser,
C::BlockSize: ElkPolynomial,
{
#[inline]
fn inner_iv_init(cipher: C, iv: &Iv<Self>) -> Self {
Self {
cipher,
state: iv.clone(),
}
}
}
impl<C> cipher::AlgorithmName for Elk<C>
where
C: BlockCipherEncrypt + BlockSizeUser + cipher::AlgorithmName,
C::BlockSize: ElkPolynomial,
{
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Elk<")?;
<C as cipher::AlgorithmName>::write_alg_name(f)?;
f.write_str(">")
}
}
impl<C> fmt::Debug for Elk<C>
where
C: BlockCipherEncrypt + BlockSizeUser + cipher::AlgorithmName,
C::BlockSize: ElkPolynomial,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Elk<")?;
<C as cipher::AlgorithmName>::write_alg_name(f)?;
f.write_str("> { ... }")
}
}
impl<C> Elk<C>
where
C: BlockCipherEncrypt + BlockSizeUser,
C::BlockSize: ElkPolynomial,
{
pub fn apply_keystream(&mut self, data: &mut [u8]) -> Result<(), Error> {
if self.state.iter().all(|&b| b == 0) {
return Err(Error::ZeroIv);
}
let block_size = self.state.len();
let mut keystream = self.state.clone();
for chunk in data.chunks_mut(block_size) {
keystream.clone_from(&self.state);
self.cipher.encrypt_block(&mut keystream);
for (byte, key_byte) in chunk.iter_mut().zip(keystream.iter()) {
*byte ^= key_byte;
}
step_lfsr::<C>(&mut self.state);
}
Ok(())
}
}
#[cfg(kani)]
mod verification {
use super::*;
use cipher::{Block, BlockCipherEncrypt, BlockSizeUser};
use cipher::typenum::U16;
#[derive(Clone)]
struct MockCipher<Size> {
_marker: core::marker::PhantomData<Size>,
}
impl<Size> BlockSizeUser for MockCipher<Size>
where
Size: hybrid_array::ArraySize,
{
type BlockSize = Size;
}
impl<Size> BlockCipherEncrypt for MockCipher<Size>
where
Size: hybrid_array::ArraySize,
{
fn encrypt_block(&self, _block: &mut Block<Self>) {
}
fn encrypt_with_backend(&self, _backend: impl cipher::BlockCipherEncClosure<BlockSize = Self::BlockSize>) {
}
}
#[kani::proof]
#[kani::unwind(22)]
fn verify_elk_16_multi_block() {
let state_bytes: [u8; 16] = kani::any();
let state: Block<MockCipher<U16>> = hybrid_array::Array::from(state_bytes);
let cipher = MockCipher { _marker: core::marker::PhantomData };
let mut elk = Elk { cipher, state };
let mut buffer = [0u8; 48];
let data_len: usize = kani::any();
kani::assume(data_len <= 48);
let data = &mut buffer[..data_len];
let _ = elk.apply_keystream(data);
}
}