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