Skip to main content

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 common::{Block, BlockSizeUser};
16use inout::{InOut, InOutBuf, NotEqualError};
17#[cfg(feature = "block-padding")]
18use inout::{
19    InOutBufReserved, PadError,
20    block_padding::{self, Padding},
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
86/// Decrypt-only functionality for block ciphers.
87pub trait BlockCipherDecrypt: BlockSizeUser {
88    /// Decrypt data using backend provided to the rank-2 closure.
89    fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = Self::BlockSize>);
90
91    /// Decrypt single `inout` block.
92    #[inline]
93    fn decrypt_block_inout(&self, block: InOut<'_, '_, Block<Self>>) {
94        self.decrypt_with_backend(BlockCtx { block });
95    }
96
97    /// Decrypt `inout` blocks.
98    #[inline]
99    fn decrypt_blocks_inout(&self, blocks: InOutBuf<'_, '_, Block<Self>>) {
100        self.decrypt_with_backend(BlocksCtx { blocks });
101    }
102
103    /// Decrypt single block in-place.
104    #[inline]
105    fn decrypt_block(&self, block: &mut Block<Self>) {
106        let block = block.into();
107        self.decrypt_with_backend(BlockCtx { block });
108    }
109
110    /// Decrypt `in_block` and write result to `out_block`.
111    #[inline]
112    fn decrypt_block_b2b(&self, in_block: &Block<Self>, out_block: &mut Block<Self>) {
113        let block = (in_block, out_block).into();
114        self.decrypt_with_backend(BlockCtx { block });
115    }
116
117    /// Decrypt blocks in-place.
118    #[inline]
119    fn decrypt_blocks(&self, blocks: &mut [Block<Self>]) {
120        let blocks = blocks.into();
121        self.decrypt_with_backend(BlocksCtx { blocks });
122    }
123
124    /// Decrypt blocks buffer-to-buffer.
125    ///
126    /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks`
127    /// have different lengths.
128    #[inline]
129    fn decrypt_blocks_b2b(
130        &self,
131        in_blocks: &[Block<Self>],
132        out_blocks: &mut [Block<Self>],
133    ) -> Result<(), NotEqualError> {
134        InOutBuf::new(in_blocks, out_blocks)
135            .map(|blocks| self.decrypt_with_backend(BlocksCtx { blocks }))
136    }
137}
138
139impl<Alg: BlockCipherEncrypt> BlockCipherEncrypt for &Alg {
140    fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = Self::BlockSize>) {
141        Alg::encrypt_with_backend(self, f);
142    }
143}
144
145impl<Alg: BlockCipherDecrypt> BlockCipherDecrypt for &Alg {
146    fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = Self::BlockSize>) {
147        Alg::decrypt_with_backend(self, f);
148    }
149}
150
151/// Encrypt-only functionality for block ciphers and modes with mutable access to `self`.
152///
153/// The main use case for this trait is blocks modes, but it also can be used
154/// for hardware cryptographic engines which require `&mut self` access to an
155/// underlying hardware peripheral.
156pub trait BlockModeEncrypt: BlockSizeUser + Sized {
157    /// Encrypt data using backend provided to the rank-2 closure.
158    fn encrypt_with_backend(&mut self, f: impl BlockModeEncClosure<BlockSize = Self::BlockSize>);
159
160    /// Encrypt single `inout` block.
161    #[inline]
162    fn encrypt_block_inout(&mut self, block: InOut<'_, '_, Block<Self>>) {
163        self.encrypt_with_backend(BlockCtx { block });
164    }
165
166    /// Encrypt `inout` blocks.
167    #[inline]
168    fn encrypt_blocks_inout(&mut self, blocks: InOutBuf<'_, '_, Block<Self>>) {
169        self.encrypt_with_backend(BlocksCtx { blocks });
170    }
171
172    /// Encrypt single block in-place.
173    #[inline]
174    fn encrypt_block(&mut self, block: &mut Block<Self>) {
175        let block = block.into();
176        self.encrypt_with_backend(BlockCtx { block });
177    }
178
179    /// Encrypt `in_block` and write result to `out_block`.
180    #[inline]
181    fn encrypt_block_b2b(&mut self, in_block: &Block<Self>, out_block: &mut Block<Self>) {
182        let block = (in_block, out_block).into();
183        self.encrypt_with_backend(BlockCtx { block });
184    }
185
186    /// Encrypt blocks in-place.
187    #[inline]
188    fn encrypt_blocks(&mut self, blocks: &mut [Block<Self>]) {
189        let blocks = blocks.into();
190        self.encrypt_with_backend(BlocksCtx { blocks });
191    }
192
193    /// Encrypt blocks buffer-to-buffer.
194    ///
195    /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks`
196    /// have different lengths.
197    #[inline]
198    fn encrypt_blocks_b2b(
199        &mut self,
200        in_blocks: &[Block<Self>],
201        out_blocks: &mut [Block<Self>],
202    ) -> Result<(), NotEqualError> {
203        InOutBuf::new(in_blocks, out_blocks)
204            .map(|blocks| self.encrypt_with_backend(BlocksCtx { blocks }))
205    }
206
207    /// Pad input and encrypt. Returns resulting ciphertext slice.
208    ///
209    /// Returns [`PadError`] if length of output buffer is not sufficient.
210    #[cfg(feature = "block-padding")]
211    #[inline]
212    fn encrypt_padded_inout<'out, P: Padding>(
213        mut self,
214        data: InOutBufReserved<'_, 'out, u8>,
215    ) -> Result<&'out [u8], PadError> {
216        let mut buf = data.into_padded_blocks::<P, Self::BlockSize>()?;
217        self.encrypt_blocks_inout(buf.get_blocks());
218        if let Some(block) = buf.get_tail_block() {
219            self.encrypt_block_inout(block);
220        }
221        Ok(buf.into_out())
222    }
223
224    /// Pad input and encrypt in-place. Returns resulting ciphertext slice.
225    ///
226    /// Returns [`PadError`] if length of output buffer is not sufficient.
227    #[cfg(feature = "block-padding")]
228    #[inline]
229    fn encrypt_padded<P: Padding>(self, buf: &mut [u8], msg_len: usize) -> Result<&[u8], PadError> {
230        let buf = InOutBufReserved::from_mut_slice(buf, msg_len).map_err(|_| PadError)?;
231        self.encrypt_padded_inout::<P>(buf)
232    }
233
234    /// Pad input and encrypt buffer-to-buffer. Returns resulting ciphertext slice.
235    ///
236    /// Returns [`PadError`] if length of output buffer is not sufficient.
237    #[cfg(feature = "block-padding")]
238    #[inline]
239    fn encrypt_padded_b2b<'a, P: Padding>(
240        self,
241        msg: &[u8],
242        out_buf: &'a mut [u8],
243    ) -> Result<&'a [u8], PadError> {
244        let buf = InOutBufReserved::from_slices(msg, out_buf).map_err(|_| PadError)?;
245        self.encrypt_padded_inout::<P>(buf)
246    }
247
248    /// Pad `msg` with padding algorithm `P`, encrypt it into a newly allocated `Vec`,
249    /// and return the resulting ciphertext vector.
250    ///
251    /// # Panics
252    /// If `NoPadding` is used with a message size that is not a multiple of the cipher block size.
253    #[cfg(all(feature = "block-padding", feature = "alloc"))]
254    #[inline]
255    fn encrypt_padded_vec<P: Padding>(self, msg: &[u8]) -> Vec<u8> {
256        use block_padding::{NoPadding, ZeroPadding};
257        use common::typenum::Unsigned;
258        use core::any::TypeId;
259
260        let bs = Self::BlockSize::USIZE;
261        let msg_len = msg.len();
262
263        let pad_type_id = TypeId::of::<P>();
264        let buf_blocks_len = if pad_type_id == TypeId::of::<NoPadding>() {
265            if msg_len % bs != 0 {
266                panic!(
267                    "NoPadding is used with a {msg_len}‑byte message,
268                    which is not a multiple of the {bs}‑byte cipher block size"
269                );
270            }
271            msg_len / bs
272        } else if pad_type_id == TypeId::of::<ZeroPadding>() {
273            msg_len.div_ceil(bs)
274        } else {
275            1 + msg_len / bs
276        };
277
278        let mut buf = vec![0; bs * buf_blocks_len];
279        let res_len = self
280            .encrypt_padded_b2b::<P>(msg, &mut buf)
281            .expect("`buf` has enough space for encryption")
282            .len();
283        buf.truncate(res_len);
284        buf
285    }
286}
287
288/// Decrypt-only functionality for block ciphers and modes with mutable access to `self`.
289///
290/// The main use case for this trait is blocks modes, but it also can be used
291/// for hardware cryptographic engines which require `&mut self` access to an
292/// underlying hardware peripheral.
293pub trait BlockModeDecrypt: BlockSizeUser + Sized {
294    /// Decrypt data using backend provided to the rank-2 closure.
295    fn decrypt_with_backend(&mut self, f: impl BlockModeDecClosure<BlockSize = Self::BlockSize>);
296
297    /// Decrypt single `inout` block.
298    #[inline]
299    fn decrypt_block_inout(&mut self, block: InOut<'_, '_, Block<Self>>) {
300        self.decrypt_with_backend(BlockCtx { block });
301    }
302
303    /// Decrypt `inout` blocks.
304    #[inline]
305    fn decrypt_blocks_inout(&mut self, blocks: InOutBuf<'_, '_, Block<Self>>) {
306        self.decrypt_with_backend(BlocksCtx { blocks });
307    }
308
309    /// Decrypt single block in-place.
310    #[inline]
311    fn decrypt_block(&mut self, block: &mut Block<Self>) {
312        let block = block.into();
313        self.decrypt_with_backend(BlockCtx { block });
314    }
315
316    /// Decrypt `in_block` and write result to `out_block`.
317    #[inline]
318    fn decrypt_block_b2b(&mut self, in_block: &Block<Self>, out_block: &mut Block<Self>) {
319        let block = (in_block, out_block).into();
320        self.decrypt_with_backend(BlockCtx { block });
321    }
322
323    /// Decrypt blocks in-place.
324    #[inline]
325    fn decrypt_blocks(&mut self, blocks: &mut [Block<Self>]) {
326        let blocks = blocks.into();
327        self.decrypt_with_backend(BlocksCtx { blocks });
328    }
329
330    /// Decrypt blocks buffer-to-buffer.
331    ///
332    /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks`
333    /// have different lengths.
334    #[inline]
335    fn decrypt_blocks_b2b(
336        &mut self,
337        in_blocks: &[Block<Self>],
338        out_blocks: &mut [Block<Self>],
339    ) -> Result<(), NotEqualError> {
340        InOutBuf::new(in_blocks, out_blocks)
341            .map(|blocks| self.decrypt_with_backend(BlocksCtx { blocks }))
342    }
343
344    /// Decrypt input and unpad it. Returns resulting plaintext slice.
345    ///
346    /// Returns [`block_padding::Error`] if padding is malformed or if input length is
347    /// not multiple of `Self::BlockSize`.
348    #[cfg(feature = "block-padding")]
349    #[inline]
350    fn decrypt_padded_inout<'out, P: Padding>(
351        mut self,
352        data: InOutBuf<'_, 'out, u8>,
353    ) -> Result<&'out [u8], block_padding::Error> {
354        let (mut blocks, tail) = data.into_chunks();
355        if !tail.is_empty() {
356            return Err(block_padding::Error);
357        }
358        self.decrypt_blocks_inout(blocks.reborrow());
359        P::unpad_blocks::<Self::BlockSize>(blocks.into_out())
360    }
361
362    /// Decrypt input and unpad it in-place. Returns resulting plaintext slice.
363    ///
364    /// Returns [`block_padding::Error`] if padding is malformed or if input length is
365    /// not multiple of `Self::BlockSize`.
366    #[cfg(feature = "block-padding")]
367    #[inline]
368    fn decrypt_padded<P: Padding>(self, buf: &mut [u8]) -> Result<&[u8], block_padding::Error> {
369        self.decrypt_padded_inout::<P>(buf.into())
370    }
371
372    /// Decrypt input and unpad it buffer-to-buffer. Returns resulting
373    /// plaintext slice.
374    ///
375    /// Returns [`block_padding::Error`] if padding is malformed or if input length is
376    /// not multiple of `Self::BlockSize`.
377    #[cfg(feature = "block-padding")]
378    #[inline]
379    fn decrypt_padded_b2b<'a, P: Padding>(
380        self,
381        in_buf: &[u8],
382        out_buf: &'a mut [u8],
383    ) -> Result<&'a [u8], block_padding::Error> {
384        if out_buf.len() < in_buf.len() {
385            return Err(block_padding::Error);
386        }
387        let n = in_buf.len();
388        // note: `new` always returns `Ok` here
389        let buf = InOutBuf::new(in_buf, &mut out_buf[..n]).map_err(|_| block_padding::Error)?;
390        self.decrypt_padded_inout::<P>(buf)
391    }
392
393    /// Decrypt input and unpad it in a newly allocated Vec. Returns resulting
394    /// plaintext `Vec`.
395    ///
396    /// Returns [`block_padding::Error`] if padding is malformed or if input length is
397    /// not multiple of `Self::BlockSize`.
398    #[cfg(all(feature = "block-padding", feature = "alloc"))]
399    #[inline]
400    fn decrypt_padded_vec<P: Padding>(self, buf: &[u8]) -> Result<Vec<u8>, block_padding::Error> {
401        let mut out = vec![0; buf.len()];
402        let len = self.decrypt_padded_b2b::<P>(buf, &mut out)?.len();
403        out.truncate(len);
404        Ok(out)
405    }
406}