use crate::{backends, Ysc1Variant};
use cipher::{
BlockSizeUser, Iv, IvSizeUser, Key, KeyIvInit, KeySizeUser, StreamCipherCore,
StreamCipherSeekCore,
};
use core::marker::PhantomData;
pub struct Ysc1Core<V: Ysc1Variant> {
pub(crate) state: [u64; 16],
pub(crate) counter: u64,
pub(crate) _variant: PhantomData<V>,
}
impl<V: Ysc1Variant> KeySizeUser for Ysc1Core<V> {
type KeySize = V::KeySize;
}
impl<V: Ysc1Variant> IvSizeUser for Ysc1Core<V> {
type IvSize = V::NonceSize;
}
impl<V: Ysc1Variant> BlockSizeUser for Ysc1Core<V> {
type BlockSize = cipher::consts::U64; }
impl<V: Ysc1Variant> KeyIvInit for Ysc1Core<V> {
fn new(key: &Key<Self>, iv: &Iv<Self>) -> Self {
let mut state = [0u64; 16];
let (s_l, s_r) = state.split_at_mut(8);
let key_len = V::KEY_SIZE;
let key_half_len = key_len / 2;
let (key_l_bytes, key_r_bytes) = key.split_at(key_half_len);
for (i, chunk) in iv.chunks_exact(8).enumerate() {
s_l[i] = u64::from_le_bytes(chunk.try_into().unwrap());
}
for (i, chunk) in key_r_bytes.chunks_exact(8).enumerate() {
s_r[i] = u64::from_le_bytes(chunk.try_into().unwrap());
}
for _ in 0..V::INIT_ROUNDS {
backends::soft::permutation(&mut state);
}
let (_, s_r) = state.split_at_mut(8);
for (i, chunk) in key_l_bytes.chunks_exact(8).enumerate() {
s_r[i] ^= u64::from_le_bytes(chunk.try_into().unwrap());
}
for _ in 0..V::INIT_ROUNDS {
backends::soft::permutation(&mut state);
}
Self {
state,
counter: 0,
_variant: PhantomData,
}
}
}
impl<V: Ysc1Variant> StreamCipherCore for Ysc1Core<V> {
fn remaining_blocks(&self) -> Option<usize> {
None
}
fn process_with_backend(&mut self, f: impl cipher::StreamClosure<BlockSize = Self::BlockSize>) {
cfg_if::cfg_if! {
if #[cfg(feature = "ysc1_simd")] {
f.call(&mut backends::simd::Backend(self));
} else {
f.call(&mut backends::soft::Backend(self));
}
}
}
}
impl<V: Ysc1Variant> StreamCipherSeekCore for Ysc1Core<V> {
type Counter = u64;
fn get_block_pos(&self) -> Self::Counter {
self.counter
}
fn set_block_pos(&mut self, pos: Self::Counter) {
self.counter = pos;
}
}