block_padding/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![doc(
4    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
5    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
6)]
7#![deny(unsafe_code)]
8#![warn(missing_docs, missing_debug_implementations)]
9
10pub use hybrid_array as array;
11
12use core::fmt;
13use hybrid_array::{Array, ArraySize};
14
15/// Trait for message padding algorithms.
16pub trait Padding {
17    /// Pads `block` filled with data up to `pos` (i.e the message length
18    /// stored in `block` is equal to `pos`).
19    ///
20    /// # Panics
21    /// If `pos` is bigger than `block.len()`. Most padding algorithms also
22    /// panic if they are equal.
23    fn raw_pad(block: &mut [u8], pos: usize);
24
25    /// Unpad data in `block`.
26    ///
27    /// Returns error if the block contains malformed padding.
28    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error>;
29
30    /// Pads `block` filled with data up to `pos` (i.e the message length
31    /// stored in `block` is equal to `pos`).
32    ///
33    /// # Panics
34    /// If `pos` is bigger than `BlockSize`. Most padding algorithms also
35    /// panic if they are equal.
36    #[inline]
37    fn pad<BlockSize: ArraySize>(block: &mut Array<u8, BlockSize>, pos: usize) {
38        Self::raw_pad(block.as_mut_slice(), pos);
39    }
40
41    /// Unpad data in `block`.
42    ///
43    /// Returns error if the block contains malformed padding.
44    #[inline]
45    fn unpad<BlockSize: ArraySize>(block: &Array<u8, BlockSize>) -> Result<&[u8], Error> {
46        Self::raw_unpad(block.as_slice())
47    }
48
49    /// Pad message and return padded tail block.
50    ///
51    /// `Err` is returned only by [`NoPadding`] if `data` length is not multiple of the block size.
52    /// [`NoPadding`] and [`ZeroPadding`] return `Ok((blocks, None))` if `data` length
53    /// is multiple of block size. All other padding implementations should always return
54    /// `Ok((blocks, Some(tail_block)))`.
55    #[allow(clippy::type_complexity)]
56    #[inline]
57    fn pad_detached<BlockSize: ArraySize>(
58        data: &[u8],
59    ) -> Result<(&[Array<u8, BlockSize>], Option<Array<u8, BlockSize>>), Error> {
60        let (blocks, tail) = Array::slice_as_chunks(data);
61        let mut tail_block = Array::default();
62        let pos = tail.len();
63        tail_block[..pos].copy_from_slice(tail);
64        Self::pad(&mut tail_block, pos);
65        Ok((blocks, Some(tail_block)))
66    }
67
68    /// Unpad data in `blocks` and return unpadded byte slice.
69    ///
70    /// Returns error if `blocks` contain malformed padding.
71    #[inline]
72    fn unpad_blocks<BlockSize: ArraySize>(blocks: &[Array<u8, BlockSize>]) -> Result<&[u8], Error> {
73        let bs = BlockSize::USIZE;
74        let (last_block, full_blocks) = blocks.split_last().ok_or(Error)?;
75        let unpad_len = Self::unpad(last_block)?.len();
76        assert!(unpad_len <= bs);
77        let buf = Array::slice_as_flattened(blocks);
78        let data_len = full_blocks.len() * bs + unpad_len;
79        Ok(&buf[..data_len])
80    }
81}
82
83/// Pad block with zeros.
84///
85/// ```
86/// use block_padding::{ZeroPadding, Padding};
87/// use block_padding::array::{Array, typenum::U8};
88///
89/// let msg = b"test";
90/// let pos = msg.len();
91/// let mut block: Array::<u8, U8> = [0xff; 8].into();
92/// block[..pos].copy_from_slice(msg);
93/// ZeroPadding::pad(&mut block, pos);
94/// assert_eq!(&block[..], b"test\x00\x00\x00\x00");
95/// let res = ZeroPadding::unpad(&mut block).unwrap();
96/// assert_eq!(res, msg);
97/// ```
98///
99/// Note that zero padding is not reversible for messages which end
100/// with one or more zero bytes.
101#[derive(Clone, Copy, Debug)]
102pub struct ZeroPadding;
103
104impl Padding for ZeroPadding {
105    #[inline]
106    fn raw_pad(block: &mut [u8], pos: usize) {
107        if pos > block.len() {
108            panic!("`pos` is bigger than block size");
109        }
110        block[pos..].fill(0);
111    }
112
113    #[inline]
114    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
115        for i in (0..block.len()).rev() {
116            if block[i] != 0 {
117                return Ok(&block[..i + 1]);
118            }
119        }
120        Ok(&block[..0])
121    }
122
123    #[inline]
124    fn unpad_blocks<BlockSize: ArraySize>(blocks: &[Array<u8, BlockSize>]) -> Result<&[u8], Error> {
125        let buf = Array::slice_as_flattened(blocks);
126        for i in (0..buf.len()).rev() {
127            if buf[i] != 0 {
128                return Ok(&buf[..i + 1]);
129            }
130        }
131        Ok(&buf[..0])
132    }
133}
134
135/// Pad block with bytes with value equal to the number of bytes added.
136///
137/// PKCS#7 described in the [RFC 5652](https://tools.ietf.org/html/rfc5652#section-6.3).
138///
139/// ```
140/// use block_padding::{Pkcs7, Padding};
141/// use block_padding::array::{Array, typenum::U8};
142///
143/// let msg = b"test";
144/// let pos = msg.len();
145/// let mut block: Array::<u8, U8> = [0xff; 8].into();
146/// block[..pos].copy_from_slice(msg);
147/// Pkcs7::pad(&mut block, pos);
148/// assert_eq!(&block[..], b"test\x04\x04\x04\x04");
149/// let res = Pkcs7::unpad(&block).unwrap();
150/// assert_eq!(res, msg);
151/// ```
152#[derive(Clone, Copy, Debug)]
153pub struct Pkcs7;
154
155impl Pkcs7 {
156    #[inline]
157    fn unpad(block: &[u8], strict: bool) -> Result<&[u8], Error> {
158        if block.len() > 255 {
159            panic!("block size is too big for PKCS#7");
160        }
161        let bs = block.len();
162        let n = block[bs - 1];
163        if n == 0 || n as usize > bs {
164            return Err(Error);
165        }
166        let s = bs - n as usize;
167        if strict && block[s..bs - 1].iter().any(|&v| v != n) {
168            return Err(Error);
169        }
170        Ok(&block[..s])
171    }
172}
173
174impl Padding for Pkcs7 {
175    #[inline]
176    fn raw_pad(block: &mut [u8], pos: usize) {
177        if block.len() > 255 {
178            panic!("block size is too big for PKCS#7");
179        }
180        if pos >= block.len() {
181            panic!("`pos` is bigger or equal to block size");
182        }
183        let n = (block.len() - pos) as u8;
184        block[pos..].fill(n);
185    }
186
187    #[inline]
188    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
189        Pkcs7::unpad(block, true)
190    }
191}
192
193/// Pad block with arbitrary bytes ending with value equal to the number of bytes added.
194///
195/// A variation of PKCS#7 that is less strict when decoding.
196///
197/// ```
198/// use block_padding::{Iso10126, Padding};
199/// use block_padding::array::{Array, typenum::U8};
200///
201/// let msg = b"test";
202/// let pos = msg.len();
203/// let mut block: Array::<u8, U8> = [0xff; 8].into();
204/// block[..pos].copy_from_slice(msg);
205/// Iso10126::pad(&mut block, pos);
206/// assert_eq!(&block[..], b"test\x04\x04\x04\x04");
207/// let res = Iso10126::unpad(&block).unwrap();
208/// assert_eq!(res, msg);
209/// ```
210#[derive(Clone, Copy, Debug)]
211pub struct Iso10126;
212
213impl Padding for Iso10126 {
214    #[inline]
215    fn raw_pad(block: &mut [u8], pos: usize) {
216        // Instead of generating random bytes as specified by Iso10126 we
217        // simply use Pkcs7 padding.
218        Pkcs7::raw_pad(block, pos)
219    }
220
221    #[inline]
222    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
223        Pkcs7::unpad(block, false)
224    }
225}
226
227/// Pad block with zeros except the last byte which will be set to the number
228/// bytes.
229///
230/// ```
231/// use block_padding::{AnsiX923, Padding};
232/// use block_padding::array::{Array, typenum::U8};
233///
234/// let msg = b"test";
235/// let pos = msg.len();
236/// let mut block: Array::<u8, U8> = [0xff; 8].into();
237/// block[..pos].copy_from_slice(msg);
238/// AnsiX923::pad(&mut block, pos);
239/// assert_eq!(&block[..], b"test\x00\x00\x00\x04");
240/// let res = AnsiX923::unpad(&block).unwrap();
241/// assert_eq!(res, msg);
242/// ```
243#[derive(Clone, Copy, Debug)]
244pub struct AnsiX923;
245
246impl Padding for AnsiX923 {
247    #[inline]
248    fn raw_pad(block: &mut [u8], pos: usize) {
249        if block.len() > 255 {
250            panic!("block size is too big for ANSI X9.23");
251        }
252        if pos >= block.len() {
253            panic!("`pos` is bigger or equal to block size");
254        }
255        let bs = block.len();
256        block[pos..bs - 1].fill(0);
257        block[bs - 1] = (bs - pos) as u8;
258    }
259
260    #[inline]
261    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
262        if block.len() > 255 {
263            panic!("block size is too big for ANSI X9.23");
264        }
265        let bs = block.len();
266        let n = block[bs - 1] as usize;
267        if n == 0 || n > bs {
268            return Err(Error);
269        }
270        let s = bs - n;
271        if block[s..bs - 1].iter().any(|&v| v != 0) {
272            return Err(Error);
273        }
274        Ok(&block[..s])
275    }
276}
277
278/// Pad block with byte sequence `\x80 00...00 00`.
279///
280/// ```
281/// use block_padding::{Iso7816, Padding};
282/// use block_padding::array::{Array, typenum::U8};
283///
284/// let msg = b"test";
285/// let pos = msg.len();
286/// let mut block: Array::<u8, U8> = [0xff; 8].into();
287/// block[..pos].copy_from_slice(msg);
288/// Iso7816::pad(&mut block, pos);
289/// assert_eq!(&block[..], b"test\x80\x00\x00\x00");
290/// let res = Iso7816::unpad(&block).unwrap();
291/// assert_eq!(res, msg);
292/// ```
293#[derive(Clone, Copy, Debug)]
294pub struct Iso7816;
295
296impl Padding for Iso7816 {
297    #[inline]
298    fn raw_pad(block: &mut [u8], pos: usize) {
299        if pos >= block.len() {
300            panic!("`pos` is bigger or equal to block size");
301        }
302        block[pos] = 0x80;
303        block[pos + 1..].fill(0);
304    }
305
306    #[inline]
307    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
308        for i in (0..block.len()).rev() {
309            match block[i] {
310                0x80 => return Ok(&block[..i]),
311                0x00 => continue,
312                _ => return Err(Error),
313            }
314        }
315        Err(Error)
316    }
317}
318
319/// Don't pad the data. Useful for key wrapping.
320///
321/// ```
322/// use block_padding::{NoPadding, Padding};
323/// use block_padding::array::{Array, typenum::U8};
324///
325/// let msg = b"test";
326/// let pos = msg.len();
327/// let mut block: Array::<u8, U8> = [0xff; 8].into();
328/// block[..pos].copy_from_slice(msg);
329/// NoPadding::pad(&mut block, pos);
330/// assert_eq!(&block[..], b"test\xff\xff\xff\xff");
331/// let res = NoPadding::unpad(&block).unwrap();
332/// assert_eq!(res, b"test\xff\xff\xff\xff");
333/// ```
334///
335/// Note that even though the passed length of the message is equal to 4,
336/// the size of unpadded message is equal to the block size of 8 bytes.
337/// Also padded message contains "garbage" bytes stored in the block buffer.
338/// Thus `NoPadding` generally should not be used with data length of which
339/// is not multiple of block size.
340#[derive(Clone, Copy, Debug)]
341pub struct NoPadding;
342
343impl Padding for NoPadding {
344    #[inline]
345    fn raw_pad(block: &mut [u8], pos: usize) {
346        if pos > block.len() {
347            panic!("`pos` is bigger than block size");
348        }
349    }
350
351    #[inline]
352    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
353        Ok(block)
354    }
355
356    #[inline]
357    fn unpad_blocks<BlockSize: ArraySize>(blocks: &[Array<u8, BlockSize>]) -> Result<&[u8], Error> {
358        Ok(Array::slice_as_flattened(blocks))
359    }
360}
361
362/// Error returned by the [`Padding`] trait methods.
363#[derive(Clone, Copy, Debug)]
364pub struct Error;
365
366impl fmt::Display for Error {
367    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
368        f.write_str("Padding error")
369    }
370}
371
372impl core::error::Error for Error {}