Skip to main content

cipher/
stream.rs

1//! Traits which define functionality of stream ciphers.
2//!
3//! See the [RustCrypto/stream-ciphers](https://github.com/RustCrypto/stream-ciphers) repository
4//! for ciphers implementation.
5
6use inout::{InOutBuf, NotEqualError};
7
8mod core_api;
9mod errors;
10#[cfg(feature = "stream-wrapper")]
11mod wrapper;
12
13pub use core_api::{
14    StreamCipherBackend, StreamCipherClosure, StreamCipherCore, StreamCipherCounter,
15    StreamCipherSeekCore,
16};
17pub use errors::{OverflowError, StreamCipherError};
18#[cfg(feature = "stream-wrapper")]
19pub use wrapper::StreamCipherCoreWrapper;
20
21/// Stream cipher trait.
22///
23/// This trait applies only to synchronous stream ciphers, which generate a keystream and
24/// XOR data with it during both encryption and decryption. Therefore, instead of separate methods
25/// for encryption and decryption, this trait provides methods for keystream application.
26///
27/// # Notes on Keystream Repetition
28/// All stream ciphers have a finite state, so the generated keystream inevitably repeats itself,
29/// making the cipher vulnerable to chosen plaintext attack. Typically, the repetition period is
30/// astronomically large, rendering keystream repetition impossible to encounter in practice.
31///
32/// However, counter-based stream ciphers allow seeking across the keystream, and some also use
33/// small counters (e.g. 32 bits). This can result in triggering keystream repetition in practice.
34///
35/// To guard against this, methods either panic (e.g. [`StreamCipher::apply_keystream`]) or
36/// return [`StreamCipherError`] (e.g. [`StreamCipher::try_apply_keystream`]) when
37/// keystream repetition occurs. We also provide a number of "unchecked" methods
38/// (e.g. [`StreamCipher::unchecked_apply_keystream`]), but they should be used with extreme care.
39///
40/// For efficiency reasons, the check for keystream repetition is typically implemented by
41/// forbidding the generation of the last keystream block in both the keystream application methods
42/// and the seeking methods defined in the [`StreamCipherSeek`] trait.
43pub trait StreamCipher {
44    /// Check that the cipher can generate a keystream with a length of `data_len` bytes.
45    ///
46    /// # Errors
47    /// Returns [`StreamCipherError`] in the event it cannot.
48    fn check_remaining(&self, data_len: usize) -> Result<(), StreamCipherError>;
49
50    /// Apply keystream to `inout` without checking for keystream repetition.
51    ///
52    /// <div><class = "warning">
53    /// <b>WARNING<b>
54    ///
55    /// This method should be used with extreme caution! Triggering keystream repetition can expose
56    /// the stream cipher to chosen plaintext attacks.
57    /// </div>
58    fn unchecked_apply_keystream_inout(&mut self, buf: InOutBuf<'_, '_, u8>);
59
60    /// Apply keystream to `buf` without checking for keystream repetition.
61    ///
62    /// <div><class = "warning">
63    /// <b>WARNING<b>
64    ///
65    /// This method should be used with extreme caution! Triggering keystream repetition can expose
66    /// the stream cipher to chosen plaintext attacks.
67    /// </div>
68    fn unchecked_write_keystream(&mut self, buf: &mut [u8]);
69
70    /// Apply keystream to data behind `buf` without checking for keystream repetition.
71    ///
72    /// <div><class = "warning">
73    /// <b>WARNING<b>
74    ///
75    /// This method should be used with extreme caution! Triggering keystream repetition can expose
76    /// the stream cipher to chosen plaintext attacks.
77    /// </div>
78    #[inline]
79    fn unchecked_apply_keystream(&mut self, buf: &mut [u8]) {
80        self.unchecked_apply_keystream_inout(buf.into());
81    }
82
83    /// Apply keystream to data buffer-to-buffer without checking for keystream repetition.
84    ///
85    /// It will XOR generated keystream with data from the `input` buffer
86    /// and will write result to the `output` buffer.
87    ///
88    /// # Errors
89    /// Returns [`NotEqualError`] if the `input` and `output` buffers have different lengths.
90    ///
91    /// <div><class = "warning">
92    /// <b>WARNING<b>
93    ///
94    /// This method should be used with extreme caution! Triggering keystream repetition can expose
95    /// the stream cipher to chosen plaintext attacks.
96    /// </div>
97    #[inline]
98    fn unchecked_apply_keystream_b2b(
99        &mut self,
100        input: &[u8],
101        output: &mut [u8],
102    ) -> Result<(), NotEqualError> {
103        let buf = InOutBuf::new(input, output)?;
104        self.unchecked_apply_keystream_inout(buf);
105        Ok(())
106    }
107
108    /// Apply keystream to `inout` data.
109    ///
110    /// # Errors
111    /// If the end of the keystream is reached with the given buffer length,
112    /// the method will return [`StreamCipherError`] without modifying `buf`.
113    fn try_apply_keystream_inout(
114        &mut self,
115        buf: InOutBuf<'_, '_, u8>,
116    ) -> Result<(), StreamCipherError> {
117        self.check_remaining(buf.len())?;
118        self.unchecked_apply_keystream_inout(buf);
119        Ok(())
120    }
121
122    /// Apply keystream to data behind `buf`.
123    ///
124    /// # Errors
125    /// If the end of the keystream is reached with the given buffer length,
126    /// the method will return [`StreamCipherError`] without modifying `buf`.
127    #[inline]
128    fn try_apply_keystream(&mut self, buf: &mut [u8]) -> Result<(), StreamCipherError> {
129        self.try_apply_keystream_inout(buf.into())
130    }
131
132    /// Apply keystream to data buffer-to-buffer.
133    ///
134    /// It will XOR generated keystream with data from the `input` buffer
135    /// and will write result to the `output` buffer.
136    ///
137    /// # Errors
138    /// Returns [`StreamCipherError`] without modifying the buffers if the `input` and `output`
139    /// buffers have different lengths, or if the end of the keystream is reached with
140    /// the given data length.
141    #[inline]
142    fn try_apply_keystream_b2b(
143        &mut self,
144        input: &[u8],
145        output: &mut [u8],
146    ) -> Result<(), StreamCipherError> {
147        InOutBuf::new(input, output)
148            .map_err(|_| StreamCipherError)
149            .and_then(|buf| self.try_apply_keystream_inout(buf))
150    }
151
152    /// Write keystream to `buf`.
153    ///
154    /// # Errors
155    /// If the end of the keystream is reached with the given buffer length,
156    /// the method will return [`StreamCipherError`] without modifying `buf`.
157    #[inline]
158    fn try_write_keystream(&mut self, buf: &mut [u8]) -> Result<(), StreamCipherError> {
159        self.check_remaining(buf.len())?;
160        self.unchecked_write_keystream(buf);
161        Ok(())
162    }
163
164    /// Apply keystream to `inout` data.
165    ///
166    /// It will XOR generated keystream with the data behind `in` pointer
167    /// and will write result to `out` pointer.
168    ///
169    /// # Panics
170    /// If the end of the keystream is reached with the given buffer length.
171    #[inline]
172    fn apply_keystream_inout(&mut self, buf: InOutBuf<'_, '_, u8>) {
173        self.try_apply_keystream_inout(buf)
174            .expect("end of keystream reached");
175    }
176
177    /// Apply keystream to data in-place.
178    ///
179    /// It will XOR generated keystream with `data` and will write result
180    /// to the same buffer.
181    ///
182    /// # Panics
183    /// If the end of the keystream is reached with the given buffer length.
184    #[inline]
185    fn apply_keystream(&mut self, buf: &mut [u8]) {
186        self.try_apply_keystream(buf)
187            .expect("end of keystream reached");
188    }
189
190    /// Apply keystream to data buffer-to-buffer.
191    ///
192    /// It will XOR generated keystream with data from the `input` buffer
193    /// and will write result to the `output` buffer.
194    ///
195    /// # Panics
196    /// If the end of the keystream is reached with the given buffer length,
197    /// of if the `input` and `output` buffers have different lengths.
198    #[inline]
199    fn apply_keystream_b2b(&mut self, input: &[u8], output: &mut [u8]) {
200        let Ok(buf) = InOutBuf::new(input, output) else {
201            panic!("Lengths of input and output buffers are not equal to each other!");
202        };
203        self.apply_keystream_inout(buf);
204    }
205
206    /// Write keystream to `buf`.
207    ///
208    /// # Panics
209    /// If the end of the keystream is reached with the given buffer length.
210    #[inline]
211    fn write_keystream(&mut self, buf: &mut [u8]) {
212        self.try_write_keystream(buf)
213            .expect("end of keystream reached");
214    }
215}
216
217/// Trait for seekable stream ciphers.
218///
219/// Methods of this trait are generic over the [`SeekNum`] trait,
220/// i.e. they can be used with `i32`, `u32`, `u64`, `u128`, and `usize`.
221pub trait StreamCipherSeek {
222    /// Try to get current keystream position in bytes.
223    ///
224    /// # Errors
225    /// Returns [`OverflowError`] if the position value can not be represented by type `T`.
226    fn try_current_pos<T: SeekNum>(&self) -> Result<T, OverflowError>;
227
228    /// Try to seek to the provided position in bytes.
229    ///
230    /// # Errors
231    /// Returns [`StreamCipherError`] if the position value is bigger than keystream length.
232    fn try_seek<T: SeekNum>(&mut self, pos: T) -> Result<(), StreamCipherError>;
233
234    /// Get current keystream position in bytes.
235    ///
236    /// # Panics
237    /// If the position value can not be represented by type `T`.
238    fn current_pos<T: SeekNum>(&self) -> T {
239        self.try_current_pos()
240            .expect("position cannot be represented by `T`")
241    }
242
243    /// Seek to the provided keystream position in bytes.
244    ///
245    /// # Panics
246    /// If the position value is bigger than keystream length.
247    fn seek<T: SeekNum>(&mut self, pos: T) {
248        self.try_seek(pos)
249            .expect("position value bigger than keystream length");
250    }
251}
252
253impl<C: StreamCipher> StreamCipher for &mut C {
254    #[inline]
255    fn check_remaining(&self, data_len: usize) -> Result<(), StreamCipherError> {
256        C::check_remaining(self, data_len)
257    }
258
259    #[inline]
260    fn unchecked_apply_keystream_inout(&mut self, buf: InOutBuf<'_, '_, u8>) {
261        C::unchecked_apply_keystream_inout(self, buf);
262    }
263
264    #[inline]
265    fn unchecked_write_keystream(&mut self, buf: &mut [u8]) {
266        C::unchecked_write_keystream(self, buf);
267    }
268}
269
270/// Trait implemented for numeric types which can be used with the
271/// [`StreamCipherSeek`] trait.
272///
273/// This trait is implemented for `i32`, `u32`, `u64`, `u128`, and `usize`.
274/// It is not intended to be implemented in third-party crates.
275pub trait SeekNum: Sized {
276    /// Try to get position for block number `block`, byte position inside
277    /// block `byte`, and block size `bs`.
278    ///
279    /// # Errors
280    /// Returns [`OverflowError`] in the event of a counter overflow.
281    fn from_block_byte<T: StreamCipherCounter>(
282        block: T,
283        byte: u8,
284        bs: u8,
285    ) -> Result<Self, OverflowError>;
286
287    /// Try to get block number and bytes position for given block size `bs`.
288    ///
289    /// # Errors
290    /// Returns [`OverflowError`] in the event of a counter overflow.
291    fn into_block_byte<T: StreamCipherCounter>(self, bs: u8) -> Result<(T, u8), OverflowError>;
292}
293
294macro_rules! impl_seek_num {
295    {$($t:ty )*} => {
296        $(
297            impl SeekNum for $t {
298                fn from_block_byte<T: StreamCipherCounter>(block: T, byte: u8, block_size: u8) -> Result<Self, OverflowError> {
299                    debug_assert!(byte != 0);
300                    let rem = block_size.checked_sub(byte).ok_or(OverflowError)?;
301                    let block: Self = block.try_into().map_err(|_| OverflowError)?;
302                    block
303                        .checked_mul(block_size.into())
304                        .and_then(|v| v.checked_sub(rem.into()))
305                        .ok_or(OverflowError)
306                }
307
308                fn into_block_byte<T: StreamCipherCounter>(self, block_size: u8) -> Result<(T, u8), OverflowError> {
309                    let bs = Self::from(block_size);
310                    let byte = u8::try_from(self % bs).expect("bs fits into u8");
311                    let block = T::try_from(self / bs).map_err(|_| OverflowError)?;
312                    Ok((block, byte))
313                }
314            }
315        )*
316    };
317}
318
319impl_seek_num! { i32 u32 u64 u128 usize }