cipher/
block.rs

1//! Traits used to define functionality of [block ciphers][1] and [modes of operation][2].
2//!
3//! # About block ciphers
4//!
5//! Block ciphers are keyed, deterministic permutations of a fixed-sized input
6//! "block" providing a reversible transformation to/from an encrypted output.
7//! They are one of the fundamental structural components of [symmetric cryptography][3].
8//!
9//! [1]: https://en.wikipedia.org/wiki/Block_cipher
10//! [2]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
11//! [3]: https://en.wikipedia.org/wiki/Symmetric-key_algorithm
12
13#[cfg(all(feature = "block-padding", feature = "alloc"))]
14use alloc::{vec, vec::Vec};
15use crypto_common::{Block, BlockSizeUser};
16use inout::{InOut, InOutBuf, NotEqualError};
17#[cfg(feature = "block-padding")]
18use inout::{
19    InOutBufReserved, PadError,
20    block_padding::{Padding, UnpadError},
21};
22
23mod backends;
24mod ctx;
25
26use ctx::{BlockCtx, BlocksCtx};
27
28pub use backends::{
29    BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherEncBackend, BlockCipherEncClosure,
30    BlockModeDecBackend, BlockModeDecClosure, BlockModeEncBackend, BlockModeEncClosure,
31};
32
33/// Encrypt-only functionality for block ciphers.
34pub trait BlockCipherEncrypt: BlockSizeUser + Sized {
35    /// Encrypt data using backend provided to the rank-2 closure.
36    fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = Self::BlockSize>);
37
38    /// Encrypt single `inout` block.
39    #[inline]
40    fn encrypt_block_inout(&self, block: InOut<'_, '_, Block<Self>>) {
41        self.encrypt_with_backend(BlockCtx { block });
42    }
43
44    /// Encrypt `inout` blocks.
45    #[inline]
46    fn encrypt_blocks_inout(&self, blocks: InOutBuf<'_, '_, Block<Self>>) {
47        self.encrypt_with_backend(BlocksCtx { blocks });
48    }
49
50    /// Encrypt single block in-place.
51    #[inline]
52    fn encrypt_block(&self, block: &mut Block<Self>) {
53        let block = block.into();
54        self.encrypt_with_backend(BlockCtx { block });
55    }
56
57    /// Encrypt `in_block` and write result to `out_block`.
58    #[inline]
59    fn encrypt_block_b2b(&self, in_block: &Block<Self>, out_block: &mut Block<Self>) {
60        let block = (in_block, out_block).into();
61        self.encrypt_with_backend(BlockCtx { block });
62    }
63
64    /// Encrypt blocks in-place.
65    #[inline]
66    fn encrypt_blocks(&self, blocks: &mut [Block<Self>]) {
67        let blocks = blocks.into();
68        self.encrypt_with_backend(BlocksCtx { blocks });
69    }
70
71    /// Encrypt blocks buffer-to-buffer.
72    ///
73    /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks`
74    /// have different lengths.
75    #[inline]
76    fn encrypt_blocks_b2b(
77        &self,
78        in_blocks: &[Block<Self>],
79        out_blocks: &mut [Block<Self>],
80    ) -> Result<(), NotEqualError> {
81        InOutBuf::new(in_blocks, out_blocks)
82            .map(|blocks| self.encrypt_with_backend(BlocksCtx { blocks }))
83    }
84
85    /// Pad input and encrypt. Returns resulting ciphertext slice.
86    ///
87    /// Returns [`PadError`] if length of output buffer is not sufficient.
88    #[cfg(feature = "block-padding")]
89    #[inline]
90    fn encrypt_padded_inout<'out, P: Padding<Self::BlockSize>>(
91        &self,
92        data: InOutBufReserved<'_, 'out, u8>,
93    ) -> Result<&'out [u8], PadError> {
94        let mut buf = data.into_padded_blocks::<P, Self::BlockSize>()?;
95        self.encrypt_blocks_inout(buf.get_blocks());
96        if let Some(block) = buf.get_tail_block() {
97            self.encrypt_block_inout(block);
98        }
99        Ok(buf.into_out())
100    }
101
102    /// Pad input and encrypt in-place. Returns resulting ciphertext slice.
103    ///
104    /// Returns [`PadError`] if length of output buffer is not sufficient.
105    #[cfg(feature = "block-padding")]
106    #[inline]
107    fn encrypt_padded<'a, P: Padding<Self::BlockSize>>(
108        &self,
109        buf: &'a mut [u8],
110        msg_len: usize,
111    ) -> Result<&'a [u8], PadError> {
112        let buf = InOutBufReserved::from_mut_slice(buf, msg_len).map_err(|_| PadError)?;
113        self.encrypt_padded_inout::<P>(buf)
114    }
115
116    /// Pad input and encrypt buffer-to-buffer. Returns resulting ciphertext slice.
117    ///
118    /// Returns [`PadError`] if length of output buffer is not sufficient.
119    #[cfg(feature = "block-padding")]
120    #[inline]
121    fn encrypt_padded_b2b<'a, P: Padding<Self::BlockSize>>(
122        &self,
123        msg: &[u8],
124        out_buf: &'a mut [u8],
125    ) -> Result<&'a [u8], PadError> {
126        let buf = InOutBufReserved::from_slices(msg, out_buf).map_err(|_| PadError)?;
127        self.encrypt_padded_inout::<P>(buf)
128    }
129
130    /// Pad input and encrypt into a newly allocated Vec. Returns resulting ciphertext Vec.
131    #[cfg(all(feature = "block-padding", feature = "alloc"))]
132    #[inline]
133    fn encrypt_padded_vec<P: Padding<Self::BlockSize>>(&self, msg: &[u8]) -> Vec<u8> {
134        use crypto_common::typenum::Unsigned;
135        let bs = Self::BlockSize::USIZE;
136        let mut out = vec![0; bs * (msg.len() / bs + 1)];
137        let len = self
138            .encrypt_padded_b2b::<P>(msg, &mut out)
139            .expect("enough space for encrypting is allocated")
140            .len();
141        out.truncate(len);
142        out
143    }
144}
145
146/// Decrypt-only functionality for block ciphers.
147pub trait BlockCipherDecrypt: BlockSizeUser {
148    /// Decrypt data using backend provided to the rank-2 closure.
149    fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = Self::BlockSize>);
150
151    /// Decrypt single `inout` block.
152    #[inline]
153    fn decrypt_block_inout(&self, block: InOut<'_, '_, Block<Self>>) {
154        self.decrypt_with_backend(BlockCtx { block });
155    }
156
157    /// Decrypt `inout` blocks.
158    #[inline]
159    fn decrypt_blocks_inout(&self, blocks: InOutBuf<'_, '_, Block<Self>>) {
160        self.decrypt_with_backend(BlocksCtx { blocks });
161    }
162
163    /// Decrypt single block in-place.
164    #[inline]
165    fn decrypt_block(&self, block: &mut Block<Self>) {
166        let block = block.into();
167        self.decrypt_with_backend(BlockCtx { block });
168    }
169
170    /// Decrypt `in_block` and write result to `out_block`.
171    #[inline]
172    fn decrypt_block_b2b(&self, in_block: &Block<Self>, out_block: &mut Block<Self>) {
173        let block = (in_block, out_block).into();
174        self.decrypt_with_backend(BlockCtx { block });
175    }
176
177    /// Decrypt blocks in-place.
178    #[inline]
179    fn decrypt_blocks(&self, blocks: &mut [Block<Self>]) {
180        let blocks = blocks.into();
181        self.decrypt_with_backend(BlocksCtx { blocks });
182    }
183
184    /// Decrypt blocks buffer-to-buffer.
185    ///
186    /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks`
187    /// have different lengths.
188    #[inline]
189    fn decrypt_blocks_b2b(
190        &self,
191        in_blocks: &[Block<Self>],
192        out_blocks: &mut [Block<Self>],
193    ) -> Result<(), NotEqualError> {
194        InOutBuf::new(in_blocks, out_blocks)
195            .map(|blocks| self.decrypt_with_backend(BlocksCtx { blocks }))
196    }
197
198    /// Decrypt input and unpad it. Returns resulting plaintext slice.
199    ///
200    /// Returns [`UnpadError`] if padding is malformed or if input length is
201    /// not multiple of `Self::BlockSize`.
202    #[cfg(feature = "block-padding")]
203    #[inline]
204    fn decrypt_padded_inout<'out, P: Padding<Self::BlockSize>>(
205        &self,
206        data: InOutBuf<'_, 'out, u8>,
207    ) -> Result<&'out [u8], UnpadError> {
208        let (mut blocks, tail) = data.into_chunks();
209        if !tail.is_empty() {
210            return Err(UnpadError);
211        }
212        self.decrypt_blocks_inout(blocks.reborrow());
213        P::unpad_blocks(blocks.into_out())
214    }
215
216    /// Decrypt input and unpad it in-place. Returns resulting plaintext slice.
217    ///
218    /// Returns [`UnpadError`] if padding is malformed or if input length is
219    /// not multiple of `Self::BlockSize`.
220    #[cfg(feature = "block-padding")]
221    #[inline]
222    fn decrypt_padded<'a, P: Padding<Self::BlockSize>>(
223        &self,
224        buf: &'a mut [u8],
225    ) -> Result<&'a [u8], UnpadError> {
226        self.decrypt_padded_inout::<P>(buf.into())
227    }
228
229    /// Decrypt input and unpad it buffer-to-buffer. Returns resulting
230    /// plaintext slice.
231    ///
232    /// Returns [`UnpadError`] if padding is malformed or if input length is
233    /// not multiple of `Self::BlockSize`.
234    #[cfg(feature = "block-padding")]
235    #[inline]
236    fn decrypt_padded_b2b<'a, P: Padding<Self::BlockSize>>(
237        &self,
238        in_buf: &[u8],
239        out_buf: &'a mut [u8],
240    ) -> Result<&'a [u8], UnpadError> {
241        if out_buf.len() < in_buf.len() {
242            return Err(UnpadError);
243        }
244        let n = in_buf.len();
245        // note: `new` always returns `Ok` here
246        let buf = InOutBuf::new(in_buf, &mut out_buf[..n]).map_err(|_| UnpadError)?;
247        self.decrypt_padded_inout::<P>(buf)
248    }
249
250    /// Decrypt input and unpad it in a newly allocated Vec. Returns resulting
251    /// plaintext `Vec`.
252    ///
253    /// Returns [`UnpadError`] if padding is malformed or if input length is
254    /// not multiple of `Self::BlockSize`.
255    #[cfg(all(feature = "block-padding", feature = "alloc"))]
256    #[inline]
257    fn decrypt_padded_vec<P: Padding<Self::BlockSize>>(
258        &self,
259        buf: &[u8],
260    ) -> Result<Vec<u8>, UnpadError> {
261        let mut out = vec![0; buf.len()];
262        let len = self.decrypt_padded_b2b::<P>(buf, &mut out)?.len();
263        out.truncate(len);
264        Ok(out)
265    }
266}
267
268impl<Alg: BlockCipherEncrypt> BlockCipherEncrypt for &Alg {
269    fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = Self::BlockSize>) {
270        Alg::encrypt_with_backend(self, f);
271    }
272}
273
274impl<Alg: BlockCipherDecrypt> BlockCipherDecrypt for &Alg {
275    fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = Self::BlockSize>) {
276        Alg::decrypt_with_backend(self, f);
277    }
278}
279
280/// Encrypt-only functionality for block ciphers and modes with mutable access to `self`.
281///
282/// The main use case for this trait is blocks modes, but it also can be used
283/// for hardware cryptographic engines which require `&mut self` access to an
284/// underlying hardware peripheral.
285pub trait BlockModeEncrypt: BlockSizeUser + Sized {
286    /// Encrypt data using backend provided to the rank-2 closure.
287    fn encrypt_with_backend(&mut self, f: impl BlockModeEncClosure<BlockSize = Self::BlockSize>);
288
289    /// Encrypt single `inout` block.
290    #[inline]
291    fn encrypt_block_inout(&mut self, block: InOut<'_, '_, Block<Self>>) {
292        self.encrypt_with_backend(BlockCtx { block });
293    }
294
295    /// Encrypt `inout` blocks.
296    #[inline]
297    fn encrypt_blocks_inout(&mut self, blocks: InOutBuf<'_, '_, Block<Self>>) {
298        self.encrypt_with_backend(BlocksCtx { blocks });
299    }
300
301    /// Encrypt single block in-place.
302    #[inline]
303    fn encrypt_block(&mut self, block: &mut Block<Self>) {
304        let block = block.into();
305        self.encrypt_with_backend(BlockCtx { block });
306    }
307
308    /// Encrypt `in_block` and write result to `out_block`.
309    #[inline]
310    fn encrypt_block_b2b(&mut self, in_block: &Block<Self>, out_block: &mut Block<Self>) {
311        let block = (in_block, out_block).into();
312        self.encrypt_with_backend(BlockCtx { block });
313    }
314
315    /// Encrypt blocks in-place.
316    #[inline]
317    fn encrypt_blocks(&mut self, blocks: &mut [Block<Self>]) {
318        let blocks = blocks.into();
319        self.encrypt_with_backend(BlocksCtx { blocks });
320    }
321
322    /// Encrypt blocks buffer-to-buffer.
323    ///
324    /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks`
325    /// have different lengths.
326    #[inline]
327    fn encrypt_blocks_b2b(
328        &mut self,
329        in_blocks: &[Block<Self>],
330        out_blocks: &mut [Block<Self>],
331    ) -> Result<(), NotEqualError> {
332        InOutBuf::new(in_blocks, out_blocks)
333            .map(|blocks| self.encrypt_with_backend(BlocksCtx { blocks }))
334    }
335
336    /// Pad input and encrypt. Returns resulting ciphertext slice.
337    ///
338    /// Returns [`PadError`] if length of output buffer is not sufficient.
339    #[cfg(feature = "block-padding")]
340    #[inline]
341    fn encrypt_padded_inout<'out, P: Padding<Self::BlockSize>>(
342        mut self,
343        data: InOutBufReserved<'_, 'out, u8>,
344    ) -> Result<&'out [u8], PadError> {
345        let mut buf = data.into_padded_blocks::<P, Self::BlockSize>()?;
346        self.encrypt_blocks_inout(buf.get_blocks());
347        if let Some(block) = buf.get_tail_block() {
348            self.encrypt_block_inout(block);
349        }
350        Ok(buf.into_out())
351    }
352
353    /// Pad input and encrypt in-place. Returns resulting ciphertext slice.
354    ///
355    /// Returns [`PadError`] if length of output buffer is not sufficient.
356    #[cfg(feature = "block-padding")]
357    #[inline]
358    fn encrypt_padded<P: Padding<Self::BlockSize>>(
359        self,
360        buf: &mut [u8],
361        msg_len: usize,
362    ) -> Result<&[u8], PadError> {
363        let buf = InOutBufReserved::from_mut_slice(buf, msg_len).map_err(|_| PadError)?;
364        self.encrypt_padded_inout::<P>(buf)
365    }
366
367    /// Pad input and encrypt buffer-to-buffer. Returns resulting ciphertext slice.
368    ///
369    /// Returns [`PadError`] if length of output buffer is not sufficient.
370    #[cfg(feature = "block-padding")]
371    #[inline]
372    fn encrypt_padded_b2b<'a, P: Padding<Self::BlockSize>>(
373        self,
374        msg: &[u8],
375        out_buf: &'a mut [u8],
376    ) -> Result<&'a [u8], PadError> {
377        let buf = InOutBufReserved::from_slices(msg, out_buf).map_err(|_| PadError)?;
378        self.encrypt_padded_inout::<P>(buf)
379    }
380
381    /// Pad input and encrypt into a newly allocated Vec. Returns resulting ciphertext Vec.
382    #[cfg(all(feature = "block-padding", feature = "alloc"))]
383    #[inline]
384    fn encrypt_padded_vec<P: Padding<Self::BlockSize>>(self, msg: &[u8]) -> Vec<u8> {
385        use crypto_common::typenum::Unsigned;
386        let bs = Self::BlockSize::USIZE;
387        let mut out = vec![0; bs * (msg.len() / bs + 1)];
388        let len = self
389            .encrypt_padded_b2b::<P>(msg, &mut out)
390            .expect("enough space for encrypting is allocated")
391            .len();
392        out.truncate(len);
393        out
394    }
395}
396
397/// Decrypt-only functionality for block ciphers and modes with mutable access to `self`.
398///
399/// The main use case for this trait is blocks modes, but it also can be used
400/// for hardware cryptographic engines which require `&mut self` access to an
401/// underlying hardware peripheral.
402pub trait BlockModeDecrypt: BlockSizeUser + Sized {
403    /// Decrypt data using backend provided to the rank-2 closure.
404    fn decrypt_with_backend(&mut self, f: impl BlockModeDecClosure<BlockSize = Self::BlockSize>);
405
406    /// Decrypt single `inout` block.
407    #[inline]
408    fn decrypt_block_inout(&mut self, block: InOut<'_, '_, Block<Self>>) {
409        self.decrypt_with_backend(BlockCtx { block });
410    }
411
412    /// Decrypt `inout` blocks.
413    #[inline]
414    fn decrypt_blocks_inout(&mut self, blocks: InOutBuf<'_, '_, Block<Self>>) {
415        self.decrypt_with_backend(BlocksCtx { blocks });
416    }
417
418    /// Decrypt single block in-place.
419    #[inline]
420    fn decrypt_block(&mut self, block: &mut Block<Self>) {
421        let block = block.into();
422        self.decrypt_with_backend(BlockCtx { block });
423    }
424
425    /// Decrypt `in_block` and write result to `out_block`.
426    #[inline]
427    fn decrypt_block_b2b(&mut self, in_block: &Block<Self>, out_block: &mut Block<Self>) {
428        let block = (in_block, out_block).into();
429        self.decrypt_with_backend(BlockCtx { block });
430    }
431
432    /// Decrypt blocks in-place.
433    #[inline]
434    fn decrypt_blocks(&mut self, blocks: &mut [Block<Self>]) {
435        let blocks = blocks.into();
436        self.decrypt_with_backend(BlocksCtx { blocks });
437    }
438
439    /// Decrypt blocks buffer-to-buffer.
440    ///
441    /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks`
442    /// have different lengths.
443    #[inline]
444    fn decrypt_blocks_b2b(
445        &mut self,
446        in_blocks: &[Block<Self>],
447        out_blocks: &mut [Block<Self>],
448    ) -> Result<(), NotEqualError> {
449        InOutBuf::new(in_blocks, out_blocks)
450            .map(|blocks| self.decrypt_with_backend(BlocksCtx { blocks }))
451    }
452
453    /// Decrypt input and unpad it. Returns resulting plaintext slice.
454    ///
455    /// Returns [`UnpadError`] if padding is malformed or if input length is
456    /// not multiple of `Self::BlockSize`.
457    #[cfg(feature = "block-padding")]
458    #[inline]
459    fn decrypt_padded_inout<'out, P: Padding<Self::BlockSize>>(
460        mut self,
461        data: InOutBuf<'_, 'out, u8>,
462    ) -> Result<&'out [u8], UnpadError> {
463        let (mut blocks, tail) = data.into_chunks();
464        if !tail.is_empty() {
465            return Err(UnpadError);
466        }
467        self.decrypt_blocks_inout(blocks.reborrow());
468        P::unpad_blocks(blocks.into_out())
469    }
470
471    /// Decrypt input and unpad it in-place. Returns resulting plaintext slice.
472    ///
473    /// Returns [`UnpadError`] if padding is malformed or if input length is
474    /// not multiple of `Self::BlockSize`.
475    #[cfg(feature = "block-padding")]
476    #[inline]
477    fn decrypt_padded<P: Padding<Self::BlockSize>>(
478        self,
479        buf: &mut [u8],
480    ) -> Result<&[u8], UnpadError> {
481        self.decrypt_padded_inout::<P>(buf.into())
482    }
483
484    /// Decrypt input and unpad it buffer-to-buffer. Returns resulting
485    /// plaintext slice.
486    ///
487    /// Returns [`UnpadError`] if padding is malformed or if input length is
488    /// not multiple of `Self::BlockSize`.
489    #[cfg(feature = "block-padding")]
490    #[inline]
491    fn decrypt_padded_b2b<'a, P: Padding<Self::BlockSize>>(
492        self,
493        in_buf: &[u8],
494        out_buf: &'a mut [u8],
495    ) -> Result<&'a [u8], UnpadError> {
496        if out_buf.len() < in_buf.len() {
497            return Err(UnpadError);
498        }
499        let n = in_buf.len();
500        // note: `new` always returns `Ok` here
501        let buf = InOutBuf::new(in_buf, &mut out_buf[..n]).map_err(|_| UnpadError)?;
502        self.decrypt_padded_inout::<P>(buf)
503    }
504
505    /// Decrypt input and unpad it in a newly allocated Vec. Returns resulting
506    /// plaintext `Vec`.
507    ///
508    /// Returns [`UnpadError`] if padding is malformed or if input length is
509    /// not multiple of `Self::BlockSize`.
510    #[cfg(all(feature = "block-padding", feature = "alloc"))]
511    #[inline]
512    fn decrypt_padded_vec<P: Padding<Self::BlockSize>>(
513        self,
514        buf: &[u8],
515    ) -> Result<Vec<u8>, UnpadError> {
516        let mut out = vec![0; buf.len()];
517        let len = self.decrypt_padded_b2b::<P>(buf, &mut out)?.len();
518        out.truncate(len);
519        Ok(out)
520    }
521}