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 }