block_padding/
lib.rs

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