Skip to main content

cipher/stream/
core_api.rs

1use super::StreamCipherError;
2use crate::{
3    array::{Array, ArraySize},
4    typenum::Unsigned,
5};
6use common::{Block, BlockSizeUser, ParBlocks, ParBlocksSizeUser};
7use inout::{InOut, InOutBuf};
8
9/// Trait implemented by stream cipher backends.
10pub trait StreamCipherBackend: ParBlocksSizeUser {
11    /// Generate keystream block.
12    fn gen_ks_block(&mut self, block: &mut Block<Self>);
13
14    /// Generate keystream blocks in parallel.
15    #[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    /// Generate keystream blocks. Length of the buffer MUST be smaller
23    /// than `Self::ParBlocksSize`.
24    #[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
33/// Trait for [`StreamCipherBackend`] users.
34///
35/// This trait is used to define rank-2 closures.
36pub trait StreamCipherClosure: BlockSizeUser {
37    /// Execute closure with the provided stream cipher backend.
38    fn call<B: StreamCipherBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B);
39}
40
41/// Block-level synchronous stream ciphers.
42pub trait StreamCipherCore: BlockSizeUser + Sized {
43    /// Return number of remaining blocks before the cipher wraps around.
44    ///
45    /// Returns `None` if number of remaining blocks can not be computed
46    /// (e.g. in the case of sponge-based stream ciphers) or it’s too big to fit into `usize`.
47    fn remaining_blocks(&self) -> Option<usize>;
48
49    /// Process data using backend provided to the rank-2 closure.
50    fn process_with_backend(&mut self, f: impl StreamCipherClosure<BlockSize = Self::BlockSize>);
51
52    /// Write keystream block.
53    ///
54    /// WARNING: this method does not check number of remaining blocks!
55    #[inline]
56    fn write_keystream_block(&mut self, block: &mut Block<Self>) {
57        self.process_with_backend(WriteBlockCtx { block });
58    }
59
60    /// Write keystream blocks.
61    ///
62    /// WARNING: this method does not check number of remaining blocks!
63    #[inline]
64    fn write_keystream_blocks(&mut self, blocks: &mut [Block<Self>]) {
65        self.process_with_backend(WriteBlocksCtx { blocks });
66    }
67
68    /// Apply keystream block.
69    ///
70    /// WARNING: this method does not check number of remaining blocks!
71    #[inline]
72    fn apply_keystream_block_inout(&mut self, block: InOut<'_, '_, Block<Self>>) {
73        self.process_with_backend(ApplyBlockCtx { block });
74    }
75
76    /// Apply keystream blocks.
77    ///
78    /// WARNING: this method does not check number of remaining blocks!
79    #[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    /// Apply keystream blocks.
87    ///
88    /// WARNING: this method does not check number of remaining blocks!
89    #[inline]
90    fn apply_keystream_blocks_inout(&mut self, blocks: InOutBuf<'_, '_, Block<Self>>) {
91        self.process_with_backend(ApplyBlocksCtx { blocks });
92    }
93
94    /// Try to apply keystream to data not divided into blocks.
95    ///
96    /// Consumes cipher since it may consume the final keystream block only partially.
97    ///
98    /// # Errors
99    /// Returns [`StreamCipherError`] if the number of remaining blocks is not sufficient
100    /// for processing of the input data.
101    #[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            // Note that if `rem_blocks` is equal to zero, it means that
108            // the next generated block will be the last in the keystream and
109            // the cipher core will wrap to its initial state.
110            // Since we consume `self`, it's fine to generate the last keystream block,
111            // so we can use division instead of `div_ceil` to compute `req_blocks`.
112            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    /// Try to apply keystream to data not divided into blocks.
136    ///
137    /// Consumes cipher since it may consume final keystream block only
138    /// partially.
139    ///
140    /// # Panics
141    /// If number of remaining blocks is not sufficient for processing the
142    /// input data.
143    #[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
150// note: unfortunately, currently we can not write blanket impls of
151// `BlockEncryptMut` and `BlockDecryptMut` for `T: StreamCipherCore`
152// since it requires mutually exclusive traits, see:
153// https://github.com/rust-lang/rfcs/issues/1053
154
155/// Counter type usable with [`StreamCipherCore`].
156///
157/// This trait is implemented for `i32`, `u32`, `u64`, `u128`, and `usize`.
158/// It's not intended to be implemented in third-party crates, but doing so
159/// is not forbidden.
160pub 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    /// Returns `true` if `self` is equal to the max counter value.
174    fn is_max(&self) -> bool;
175}
176
177/// Block-level seeking trait for stream ciphers.
178pub trait StreamCipherSeekCore: StreamCipherCore {
179    /// Counter type used inside stream cipher.
180    type Counter: StreamCipherCounter;
181
182    /// Get current block position.
183    fn get_block_pos(&self) -> Self::Counter;
184
185    /// Set block position.
186    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}