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