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: 'static {
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    /// [`PaddedData::Error`] is returned only by [`NoPadding`] if `data` length is not multiple
52    /// of the block size. [`NoPadding`] and [`ZeroPadding`] return [`PaddedData::NoPad`]
53    /// if `data` length is multiple of block size. All other padding implementations
54    /// should always return [`PaddedData::Pad`].
55    #[inline]
56    fn pad_detached<BlockSize: ArraySize>(data: &[u8]) -> PaddedData<'_, BlockSize> {
57        let (blocks, tail) = Array::slice_as_chunks(data);
58        let mut tail_block = Array::default();
59        let pos = tail.len();
60        tail_block[..pos].copy_from_slice(tail);
61        Self::pad(&mut tail_block, pos);
62        PaddedData::Pad { blocks, tail_block }
63    }
64
65    /// Unpad data in `blocks` and return unpadded byte slice.
66    ///
67    /// Returns error if `blocks` contain malformed padding.
68    #[inline]
69    fn unpad_blocks<BlockSize: ArraySize>(blocks: &[Array<u8, BlockSize>]) -> Result<&[u8], Error> {
70        let bs = BlockSize::USIZE;
71        let (last_block, full_blocks) = blocks.split_last().ok_or(Error)?;
72        let unpad_len = Self::unpad(last_block)?.len();
73        assert!(unpad_len <= bs);
74        let buf = Array::slice_as_flattened(blocks);
75        let data_len = full_blocks.len() * bs + unpad_len;
76        Ok(&buf[..data_len])
77    }
78}
79
80/// Pad block with zeros.
81///
82/// ```
83/// use block_padding::{ZeroPadding, Padding};
84/// use block_padding::array::{Array, typenum::U8};
85///
86/// let msg = b"test";
87/// let pos = msg.len();
88/// let mut block: Array::<u8, U8> = [0xff; 8].into();
89/// block[..pos].copy_from_slice(msg);
90/// ZeroPadding::pad(&mut block, pos);
91/// assert_eq!(&block[..], b"test\x00\x00\x00\x00");
92/// let res = ZeroPadding::unpad(&mut block).unwrap();
93/// assert_eq!(res, msg);
94/// ```
95///
96/// Note that zero padding is not reversible for messages which end
97/// with one or more zero bytes.
98#[derive(Clone, Copy, Debug)]
99pub struct ZeroPadding;
100
101impl Padding for ZeroPadding {
102    #[inline]
103    fn raw_pad(block: &mut [u8], pos: usize) {
104        if pos > block.len() {
105            panic!("`pos` is bigger than block size");
106        }
107        block[pos..].fill(0);
108    }
109
110    #[inline]
111    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
112        for i in (0..block.len()).rev() {
113            if block[i] != 0 {
114                return Ok(&block[..i + 1]);
115            }
116        }
117        Ok(&block[..0])
118    }
119
120    #[inline]
121    fn pad_detached<BlockSize: ArraySize>(data: &[u8]) -> PaddedData<'_, BlockSize> {
122        let (blocks, tail) = Array::slice_as_chunks(data);
123        if tail.is_empty() {
124            return PaddedData::NoPad { blocks };
125        }
126        let mut tail_block = Array::default();
127        let pos = tail.len();
128        tail_block[..pos].copy_from_slice(tail);
129        Self::pad(&mut tail_block, pos);
130        PaddedData::Pad { blocks, tail_block }
131    }
132
133    #[inline]
134    fn unpad_blocks<BlockSize: ArraySize>(blocks: &[Array<u8, BlockSize>]) -> Result<&[u8], Error> {
135        let buf = Array::slice_as_flattened(blocks);
136        for i in (0..buf.len()).rev() {
137            if buf[i] != 0 {
138                return Ok(&buf[..i + 1]);
139            }
140        }
141        Ok(&buf[..0])
142    }
143}
144
145/// Pad block with bytes with value equal to the number of bytes added.
146///
147/// PKCS#7 described in the [RFC 5652](https://tools.ietf.org/html/rfc5652#section-6.3).
148///
149/// ```
150/// use block_padding::{Pkcs7, Padding};
151/// use block_padding::array::{Array, typenum::U8};
152///
153/// let msg = b"test";
154/// let pos = msg.len();
155/// let mut block: Array::<u8, U8> = [0xff; 8].into();
156/// block[..pos].copy_from_slice(msg);
157/// Pkcs7::pad(&mut block, pos);
158/// assert_eq!(&block[..], b"test\x04\x04\x04\x04");
159/// let res = Pkcs7::unpad(&block).unwrap();
160/// assert_eq!(res, msg);
161/// ```
162#[derive(Clone, Copy, Debug)]
163pub struct Pkcs7;
164
165impl Pkcs7 {
166    #[inline]
167    fn unpad(block: &[u8], strict: bool) -> Result<&[u8], Error> {
168        if block.len() > 255 {
169            panic!("block size is too big for PKCS#7");
170        }
171        let bs = block.len();
172        let n = block[bs - 1];
173        if n == 0 || n as usize > bs {
174            return Err(Error);
175        }
176        let s = bs - n as usize;
177        if strict && block[s..bs - 1].iter().any(|&v| v != n) {
178            return Err(Error);
179        }
180        Ok(&block[..s])
181    }
182}
183
184impl Padding for Pkcs7 {
185    #[inline]
186    fn raw_pad(block: &mut [u8], pos: usize) {
187        if block.len() > 255 {
188            panic!("block size is too big for PKCS#7");
189        }
190        if pos >= block.len() {
191            panic!("`pos` is bigger or equal to block size");
192        }
193        let n = (block.len() - pos) as u8;
194        block[pos..].fill(n);
195    }
196
197    #[inline]
198    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
199        Pkcs7::unpad(block, true)
200    }
201}
202
203/// Pad block with arbitrary bytes ending with value equal to the number of bytes added.
204///
205/// A variation of PKCS#7 that is less strict when decoding.
206///
207/// ```
208/// use block_padding::{Iso10126, Padding};
209/// use block_padding::array::{Array, typenum::U8};
210///
211/// let msg = b"test";
212/// let pos = msg.len();
213/// let mut block: Array::<u8, U8> = [0xff; 8].into();
214/// block[..pos].copy_from_slice(msg);
215/// Iso10126::pad(&mut block, pos);
216/// assert_eq!(&block[..], b"test\x04\x04\x04\x04");
217/// let res = Iso10126::unpad(&block).unwrap();
218/// assert_eq!(res, msg);
219/// ```
220#[derive(Clone, Copy, Debug)]
221pub struct Iso10126;
222
223impl Padding for Iso10126 {
224    #[inline]
225    fn raw_pad(block: &mut [u8], pos: usize) {
226        // Instead of generating random bytes as specified by Iso10126 we
227        // simply use Pkcs7 padding.
228        Pkcs7::raw_pad(block, pos)
229    }
230
231    #[inline]
232    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
233        Pkcs7::unpad(block, false)
234    }
235}
236
237/// Pad block with zeros except the last byte which will be set to the number
238/// bytes.
239///
240/// ```
241/// use block_padding::{AnsiX923, Padding};
242/// use block_padding::array::{Array, typenum::U8};
243///
244/// let msg = b"test";
245/// let pos = msg.len();
246/// let mut block: Array::<u8, U8> = [0xff; 8].into();
247/// block[..pos].copy_from_slice(msg);
248/// AnsiX923::pad(&mut block, pos);
249/// assert_eq!(&block[..], b"test\x00\x00\x00\x04");
250/// let res = AnsiX923::unpad(&block).unwrap();
251/// assert_eq!(res, msg);
252/// ```
253#[derive(Clone, Copy, Debug)]
254pub struct AnsiX923;
255
256impl Padding for AnsiX923 {
257    #[inline]
258    fn raw_pad(block: &mut [u8], pos: usize) {
259        if block.len() > 255 {
260            panic!("block size is too big for ANSI X9.23");
261        }
262        if pos >= block.len() {
263            panic!("`pos` is bigger or equal to block size");
264        }
265        let bs = block.len();
266        block[pos..bs - 1].fill(0);
267        block[bs - 1] = (bs - pos) as u8;
268    }
269
270    #[inline]
271    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
272        if block.len() > 255 {
273            panic!("block size is too big for ANSI X9.23");
274        }
275        let bs = block.len();
276        let n = block[bs - 1] as usize;
277        if n == 0 || n > bs {
278            return Err(Error);
279        }
280        let s = bs - n;
281        if block[s..bs - 1].iter().any(|&v| v != 0) {
282            return Err(Error);
283        }
284        Ok(&block[..s])
285    }
286}
287
288/// Pad block with byte sequence `\x80 00...00 00`.
289///
290/// ```
291/// use block_padding::{Iso7816, Padding};
292/// use block_padding::array::{Array, typenum::U8};
293///
294/// let msg = b"test";
295/// let pos = msg.len();
296/// let mut block: Array::<u8, U8> = [0xff; 8].into();
297/// block[..pos].copy_from_slice(msg);
298/// Iso7816::pad(&mut block, pos);
299/// assert_eq!(&block[..], b"test\x80\x00\x00\x00");
300/// let res = Iso7816::unpad(&block).unwrap();
301/// assert_eq!(res, msg);
302/// ```
303#[derive(Clone, Copy, Debug)]
304pub struct Iso7816;
305
306impl Padding for Iso7816 {
307    #[inline]
308    fn raw_pad(block: &mut [u8], pos: usize) {
309        if pos >= block.len() {
310            panic!("`pos` is bigger or equal to block size");
311        }
312        block[pos] = 0x80;
313        block[pos + 1..].fill(0);
314    }
315
316    #[inline]
317    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
318        for i in (0..block.len()).rev() {
319            match block[i] {
320                0x80 => return Ok(&block[..i]),
321                0x00 => continue,
322                _ => return Err(Error),
323            }
324        }
325        Err(Error)
326    }
327}
328
329/// Don't pad the data. Useful for key wrapping.
330///
331/// ```
332/// use block_padding::{NoPadding, Padding};
333/// use block_padding::array::{Array, typenum::U8};
334///
335/// let msg = b"test";
336/// let pos = msg.len();
337/// let mut block: Array::<u8, U8> = [0xff; 8].into();
338/// block[..pos].copy_from_slice(msg);
339/// NoPadding::pad(&mut block, pos);
340/// assert_eq!(&block[..], b"test\xff\xff\xff\xff");
341/// let res = NoPadding::unpad(&block).unwrap();
342/// assert_eq!(res, b"test\xff\xff\xff\xff");
343/// ```
344///
345/// Note that even though the passed length of the message is equal to 4,
346/// the size of unpadded message is equal to the block size of 8 bytes.
347/// Also padded message contains "garbage" bytes stored in the block buffer.
348/// Thus `NoPadding` generally should not be used with data length of which
349/// is not multiple of block size.
350#[derive(Clone, Copy, Debug)]
351pub struct NoPadding;
352
353impl Padding for NoPadding {
354    #[inline]
355    fn raw_pad(block: &mut [u8], pos: usize) {
356        if pos > block.len() {
357            panic!("`pos` is bigger than block size");
358        }
359    }
360
361    #[inline]
362    fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
363        Ok(block)
364    }
365
366    #[inline]
367    fn pad_detached<BlockSize: ArraySize>(data: &[u8]) -> PaddedData<'_, BlockSize> {
368        let (blocks, tail) = Array::slice_as_chunks(data);
369        if tail.is_empty() {
370            PaddedData::NoPad { blocks }
371        } else {
372            PaddedData::Error
373        }
374    }
375
376    #[inline]
377    fn unpad_blocks<BlockSize: ArraySize>(blocks: &[Array<u8, BlockSize>]) -> Result<&[u8], Error> {
378        Ok(Array::slice_as_flattened(blocks))
379    }
380}
381
382/// Error returned by the [`Padding`] trait methods.
383#[derive(Clone, Copy, Debug)]
384pub struct Error;
385
386impl fmt::Display for Error {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
388        f.write_str("Padding error")
389    }
390}
391
392impl core::error::Error for Error {}
393
394/// Padded data split into blocks with detached last block returned by [`Padding::pad_detached`].
395#[derive(Debug)]
396pub enum PaddedData<'a, BlockSize: ArraySize> {
397    /// Message split into blocks with detached and padded `tail_block`.
398    Pad {
399        /// Message blocks.
400        blocks: &'a [Array<u8, BlockSize>],
401        /// Last message block with padding.
402        tail_block: Array<u8, BlockSize>,
403    },
404    /// [`NoPadding`] or [`ZeroPadding`] were used on a message which does not require any padding.
405    NoPad {
406        /// Message blocks.
407        blocks: &'a [Array<u8, BlockSize>],
408    },
409    /// [`NoPadding`] was used on a message with size not multiple of the block size.
410    Error,
411}
412
413impl<'a, BlockSize: ArraySize> PaddedData<'a, BlockSize> {
414    /// Unwrap the `Pad` variant.
415    pub fn unwrap(self) -> (&'a [Array<u8, BlockSize>], Array<u8, BlockSize>) {
416        match self {
417            PaddedData::Pad { blocks, tail_block } => (blocks, tail_block),
418            PaddedData::NoPad { .. } => {
419                panic!("Expected `PaddedData::Pad`, but got `PaddedData::NoPad`");
420            }
421            PaddedData::Error => {
422                panic!("Expected `PaddedData::Pad`, but got `PaddedData::Error`");
423            }
424        }
425    }
426}