cipher/stream/
core_api.rs1use super::StreamCipherError;
2use crate::{
3 array::{Array, ArraySize},
4 typenum::Unsigned,
5};
6use common::{Block, BlockSizeUser, ParBlocks, ParBlocksSizeUser};
7use inout::{InOut, InOutBuf};
8
9pub trait StreamCipherBackend: ParBlocksSizeUser {
11 fn gen_ks_block(&mut self, block: &mut Block<Self>);
13
14 #[inline(always)]
16 fn gen_par_ks_blocks(&mut self, blocks: &mut ParBlocks<Self>) {
17 for block in blocks {
18 self.gen_ks_block(block);
19 }
20 }
21
22 #[inline(always)]
25 fn gen_tail_blocks(&mut self, blocks: &mut [Block<Self>]) {
26 assert!(blocks.len() < Self::ParBlocksSize::USIZE);
27 for block in blocks {
28 self.gen_ks_block(block);
29 }
30 }
31}
32
33pub trait StreamCipherClosure: BlockSizeUser {
37 fn call<B: StreamCipherBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B);
39}
40
41pub trait StreamCipherCore: BlockSizeUser + Sized {
43 fn remaining_blocks(&self) -> Option<usize>;
48
49 fn process_with_backend(&mut self, f: impl StreamCipherClosure<BlockSize = Self::BlockSize>);
51
52 #[inline]
56 fn write_keystream_block(&mut self, block: &mut Block<Self>) {
57 self.process_with_backend(WriteBlockCtx { block });
58 }
59
60 #[inline]
64 fn write_keystream_blocks(&mut self, blocks: &mut [Block<Self>]) {
65 self.process_with_backend(WriteBlocksCtx { blocks });
66 }
67
68 #[inline]
72 fn apply_keystream_block_inout(&mut self, block: InOut<'_, '_, Block<Self>>) {
73 self.process_with_backend(ApplyBlockCtx { block });
74 }
75
76 #[inline]
80 fn apply_keystream_blocks(&mut self, blocks: &mut [Block<Self>]) {
81 self.process_with_backend(ApplyBlocksCtx {
82 blocks: blocks.into(),
83 });
84 }
85
86 #[inline]
90 fn apply_keystream_blocks_inout(&mut self, blocks: InOutBuf<'_, '_, Block<Self>>) {
91 self.process_with_backend(ApplyBlocksCtx { blocks });
92 }
93
94 #[inline]
102 fn try_apply_keystream_partial(
103 mut self,
104 mut buf: InOutBuf<'_, '_, u8>,
105 ) -> Result<(), StreamCipherError> {
106 if let Some(rem_blocks) = self.remaining_blocks() {
107 let req_blocks = buf.len() / Self::BlockSize::USIZE;
113 if req_blocks > rem_blocks {
114 return Err(StreamCipherError);
115 }
116 }
117
118 if buf.len() > Self::BlockSize::USIZE {
119 let (blocks, tail) = buf.into_chunks();
120 self.apply_keystream_blocks_inout(blocks);
121 buf = tail;
122 }
123 let n = buf.len();
124 if n == 0 {
125 return Ok(());
126 }
127 let mut block = Block::<Self>::default();
128 block[..n].copy_from_slice(buf.get_in());
129 let t = InOutBuf::from_mut(&mut block);
130 self.apply_keystream_blocks_inout(t);
131 buf.get_out().copy_from_slice(&block[..n]);
132 Ok(())
133 }
134
135 #[inline]
144 fn apply_keystream_partial(self, buf: InOutBuf<'_, '_, u8>) {
145 self.try_apply_keystream_partial(buf)
146 .expect("number of remaining blocks insufficient");
147 }
148}
149
150pub trait StreamCipherCounter:
161 TryFrom<i32>
162 + TryFrom<u32>
163 + TryFrom<u64>
164 + TryFrom<u128>
165 + TryFrom<usize>
166 + TryInto<i32>
167 + TryInto<u32>
168 + TryInto<u64>
169 + TryInto<u128>
170 + TryInto<usize>
171 + Copy
172{
173 fn is_max(&self) -> bool;
175}
176
177pub trait StreamCipherSeekCore: StreamCipherCore {
179 type Counter: StreamCipherCounter;
181
182 fn get_block_pos(&self) -> Self::Counter;
184
185 fn set_block_pos(&mut self, pos: Self::Counter);
187}
188
189macro_rules! impl_counter {
190 {$($t:ty )*} => {
191 $(
192 impl StreamCipherCounter for $t {
193 fn is_max(&self) -> bool {
194 *self == <$t>::MAX
195 }
196 }
197 )*
198 };
199}
200
201impl_counter! { u32 u64 u128 }
202
203struct WriteBlockCtx<'a, BS: ArraySize> {
204 block: &'a mut Block<Self>,
205}
206impl<BS: ArraySize> BlockSizeUser for WriteBlockCtx<'_, BS> {
207 type BlockSize = BS;
208}
209impl<BS: ArraySize> StreamCipherClosure for WriteBlockCtx<'_, BS> {
210 #[inline(always)]
211 fn call<B: StreamCipherBackend<BlockSize = BS>>(self, backend: &mut B) {
212 backend.gen_ks_block(self.block);
213 }
214}
215
216struct WriteBlocksCtx<'a, BS: ArraySize> {
217 blocks: &'a mut [Block<Self>],
218}
219
220impl<BS: ArraySize> BlockSizeUser for WriteBlocksCtx<'_, BS> {
221 type BlockSize = BS;
222}
223
224impl<BS: ArraySize> StreamCipherClosure for WriteBlocksCtx<'_, BS> {
225 #[inline(always)]
226 fn call<B: StreamCipherBackend<BlockSize = BS>>(self, backend: &mut B) {
227 if B::ParBlocksSize::USIZE > 1 {
228 let (chunks, tail) = Array::slice_as_chunks_mut(self.blocks);
229 for chunk in chunks {
230 backend.gen_par_ks_blocks(chunk);
231 }
232 backend.gen_tail_blocks(tail);
233 } else {
234 for block in self.blocks {
235 backend.gen_ks_block(block);
236 }
237 }
238 }
239}
240
241struct ApplyBlockCtx<'inp, 'out, BS: ArraySize> {
242 block: InOut<'inp, 'out, Block<Self>>,
243}
244
245impl<BS: ArraySize> BlockSizeUser for ApplyBlockCtx<'_, '_, BS> {
246 type BlockSize = BS;
247}
248
249impl<BS: ArraySize> StreamCipherClosure for ApplyBlockCtx<'_, '_, BS> {
250 #[inline(always)]
251 fn call<B: StreamCipherBackend<BlockSize = BS>>(mut self, backend: &mut B) {
252 let mut t = Default::default();
253 backend.gen_ks_block(&mut t);
254 self.block.xor_in2out(&t);
255 }
256}
257
258struct ApplyBlocksCtx<'inp, 'out, BS: ArraySize> {
259 blocks: InOutBuf<'inp, 'out, Block<Self>>,
260}
261
262impl<BS: ArraySize> BlockSizeUser for ApplyBlocksCtx<'_, '_, BS> {
263 type BlockSize = BS;
264}
265
266impl<BS: ArraySize> StreamCipherClosure for ApplyBlocksCtx<'_, '_, BS> {
267 #[inline(always)]
268 #[allow(clippy::needless_range_loop)]
269 fn call<B: StreamCipherBackend<BlockSize = BS>>(self, backend: &mut B) {
270 if B::ParBlocksSize::USIZE > 1 {
271 let (chunks, mut tail) = self.blocks.into_chunks::<B::ParBlocksSize>();
272 for mut chunk in chunks {
273 let mut tmp = Default::default();
274 backend.gen_par_ks_blocks(&mut tmp);
275 chunk.xor_in2out(&tmp);
276 }
277 let n = tail.len();
278 let mut buf = Array::<_, B::ParBlocksSize>::default();
279 let ks = &mut buf[..n];
280 backend.gen_tail_blocks(ks);
281 for i in 0..n {
282 tail.get(i).xor_in2out(&ks[i]);
283 }
284 } else {
285 for mut block in self.blocks {
286 let mut t = Default::default();
287 backend.gen_ks_block(&mut t);
288 block.xor_in2out(&t);
289 }
290 }
291 }
292}