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(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")]
8
9/// Error for indicating failed padding operation
10#[derive(Clone, Copy, Debug)]
11pub struct PadError;
12
13/// Error for indicating failed unpadding operation
14#[derive(Clone, Copy, Debug)]
15pub struct UnpadError;
16
17/// Trait for padding messages divided into blocks
18pub trait Padding {
19    /// Pads `block` filled with data up to `pos`.
20    ///
21    /// `pos` should be inside of the block and block must not be full, i.e.
22    /// `pos < block.len()` must be true. Otherwise method will return
23    /// `PadError`. Some potentially irreversible padding schemes can allow
24    /// padding of the full block, in this case aforementioned condition is
25    /// relaxed to `pos <= block.len()`.
26    fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError>;
27
28    /// Pads message with length `pos` in the provided buffer.
29    ///
30    /// `&buf[..pos]` is perceived as the message, the buffer must contain
31    /// enough leftover space for padding: `block_size - (pos % block_size)`
32    /// extra bytes must be available. Otherwise method will return
33    /// `PadError`.
34    fn pad(buf: &mut [u8], pos: usize, block_size: usize) -> Result<&mut [u8], PadError> {
35        let bs = block_size * (pos / block_size);
36        if buf.len() < bs || buf.len() - bs < block_size {
37            Err(PadError)?
38        }
39        Self::pad_block(&mut buf[bs..bs + block_size], pos - bs)?;
40        Ok(&mut buf[..bs + block_size])
41    }
42
43    /// Unpad given `data` by truncating it according to the used padding.
44    /// In case of the malformed padding will return `UnpadError`
45    fn unpad(data: &[u8]) -> Result<&[u8], UnpadError>;
46}
47
48/// Pad block with zeros.
49///
50/// ```
51/// use block_padding::{ZeroPadding, Padding};
52///
53/// let msg = b"test";
54/// let n = msg.len();
55/// let mut buffer = [0xff; 16];
56/// buffer[..n].copy_from_slice(msg);
57/// let padded_msg = ZeroPadding::pad(&mut buffer, n, 8).unwrap();
58/// assert_eq!(padded_msg, b"test\x00\x00\x00\x00");
59/// assert_eq!(ZeroPadding::unpad(&padded_msg).unwrap(), msg);
60/// ```
61/// ```
62/// # use block_padding::{ZeroPadding, Padding};
63/// # let msg = b"test";
64/// # let n = msg.len();
65/// # let mut buffer = [0xff; 16];
66/// # buffer[..n].copy_from_slice(msg);
67/// let padded_msg = ZeroPadding::pad(&mut buffer, n, 2).unwrap();
68/// assert_eq!(padded_msg, b"test");
69/// assert_eq!(ZeroPadding::unpad(&padded_msg).unwrap(), msg);
70/// ```
71///
72/// Note that zero padding may not be reversible if the original message ends
73/// with one or more zero bytes.
74#[derive(Clone, Copy, Debug)]
75pub enum ZeroPadding {}
76
77impl Padding for ZeroPadding {
78    fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> {
79        if pos > block.len() {
80            Err(PadError)?
81        }
82        set(&mut block[pos..], 0);
83        Ok(())
84    }
85
86    fn pad(buf: &mut [u8], pos: usize, block_size: usize) -> Result<&mut [u8], PadError> {
87        if pos % block_size == 0 {
88            Ok(&mut buf[..pos])
89        } else {
90            let bs = block_size * (pos / block_size);
91            let be = bs + block_size;
92            if buf.len() < be {
93                Err(PadError)?
94            }
95            Self::pad_block(&mut buf[bs..be], pos - bs)?;
96            Ok(&mut buf[..be])
97        }
98    }
99
100    fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> {
101        let mut n = data.len() - 1;
102        while n != 0 {
103            if data[n] != 0 {
104                break;
105            }
106            n -= 1;
107        }
108        Ok(&data[..n + 1])
109    }
110}
111
112/// Pad block with bytes with value equal to the number of bytes added.
113///
114/// PKCS#7 described in the [RFC 5652](https://tools.ietf.org/html/rfc5652#section-6.3).
115///
116/// ```
117/// use block_padding::{Pkcs7, Padding};
118///
119/// let msg = b"test";
120/// let n = msg.len();
121/// let mut buffer = [0xff; 8];
122/// buffer[..n].copy_from_slice(msg);
123/// let padded_msg = Pkcs7::pad(&mut buffer, n, 8).unwrap();
124/// assert_eq!(padded_msg, b"test\x04\x04\x04\x04");
125/// assert_eq!(Pkcs7::unpad(&padded_msg).unwrap(), msg);
126/// ```
127/// ```
128/// # use block_padding::{Pkcs7, Padding};
129/// # let msg = b"test";
130/// # let n = msg.len();
131/// # let mut buffer = [0xff; 8];
132/// # buffer[..n].copy_from_slice(msg);
133/// let padded_msg = Pkcs7::pad(&mut buffer, n, 2).unwrap();
134/// assert_eq!(padded_msg, b"test\x02\x02");
135/// assert_eq!(Pkcs7::unpad(&padded_msg).unwrap(), msg);
136/// ```
137/// ```
138/// # use block_padding::{Pkcs7, Padding};
139/// let mut buffer = [0xff; 5];
140/// assert!(Pkcs7::pad(&mut buffer, 4, 2).is_err());
141/// ```
142/// ```
143/// # use block_padding::{Pkcs7, Padding};
144/// # let buffer = [0xff; 16];
145/// assert!(Pkcs7::unpad(&buffer).is_err());
146/// ```
147///
148/// In addition to conditions stated in the `Padding` trait documentation,
149/// `pad_block` will return `PadError` if `block.len() > 255`, and in case of
150/// `pad` if `block_size > 255`.
151#[derive(Clone, Copy, Debug)]
152pub enum Pkcs7 {}
153
154impl Padding for Pkcs7 {
155    fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> {
156        if block.len() > 255 {
157            Err(PadError)?
158        }
159        if pos >= block.len() {
160            Err(PadError)?
161        }
162        let n = block.len() - pos;
163        set(&mut block[pos..], n as u8);
164        Ok(())
165    }
166
167    fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> {
168        if data.is_empty() {
169            Err(UnpadError)?
170        }
171        let l = data.len();
172        let n = data[l - 1];
173        if n == 0 || n as usize > l {
174            Err(UnpadError)?
175        }
176        for v in &data[l - n as usize..l - 1] {
177            if *v != n {
178                Err(UnpadError)?
179            }
180        }
181        Ok(&data[..l - n as usize])
182    }
183}
184
185/// Pad block with zeros except the last byte which will be set to the number
186/// bytes.
187///
188/// ```
189/// use block_padding::{AnsiX923, Padding};
190///
191/// let msg = b"test";
192/// let n = msg.len();
193/// let mut buffer = [0xff; 16];
194/// buffer[..n].copy_from_slice(msg);
195/// let padded_msg = AnsiX923::pad(&mut buffer, n, 8).unwrap();
196/// assert_eq!(padded_msg, b"test\x00\x00\x00\x04");
197/// assert_eq!(AnsiX923::unpad(&padded_msg).unwrap(), msg);
198/// ```
199/// ```
200/// # use block_padding::{AnsiX923, Padding};
201/// # let msg = b"test";
202/// # let n = msg.len();
203/// # let mut buffer = [0xff; 16];
204/// # buffer[..n].copy_from_slice(msg);
205/// let padded_msg = AnsiX923::pad(&mut buffer, n, 2).unwrap();
206/// assert_eq!(padded_msg, b"test\x00\x02");
207/// assert_eq!(AnsiX923::unpad(&padded_msg).unwrap(), msg);
208/// ```
209/// ```
210/// # use block_padding::{AnsiX923, Padding};
211/// # let buffer = [0xff; 16];
212/// assert!(AnsiX923::unpad(&buffer).is_err());
213/// ```
214///
215/// In addition to conditions stated in the `Padding` trait documentation,
216/// `pad_block` will return `PadError` if `block.len() > 255`, and in case of
217/// `pad` if `block_size > 255`.
218#[derive(Clone, Copy, Debug)]
219pub enum AnsiX923 {}
220
221impl Padding for AnsiX923 {
222    fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> {
223        if block.len() > 255 {
224            Err(PadError)?
225        }
226        if pos >= block.len() {
227            Err(PadError)?
228        }
229        let bs = block.len();
230        set(&mut block[pos..bs - 1], 0);
231        block[bs - 1] = (bs - pos) as u8;
232        Ok(())
233    }
234
235    fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> {
236        if data.is_empty() {
237            Err(UnpadError)?
238        }
239        let l = data.len();
240        let n = data[l - 1] as usize;
241        if n == 0 || n > l {
242            return Err(UnpadError);
243        }
244        for v in &data[l - n..l - 1] {
245            if *v != 0 {
246                Err(UnpadError)?
247            }
248        }
249        Ok(&data[..l - n])
250    }
251}
252
253/// Pad block with byte sequence `\x80 00...00 00`.
254///
255/// ```
256/// use block_padding::{Iso7816, Padding};
257///
258/// let msg = b"test";
259/// let n = msg.len();
260/// let mut buffer = [0xff; 16];
261/// buffer[..n].copy_from_slice(msg);
262/// let padded_msg = Iso7816::pad(&mut buffer, n, 8).unwrap();
263/// assert_eq!(padded_msg, b"test\x80\x00\x00\x00");
264/// assert_eq!(Iso7816::unpad(&padded_msg).unwrap(), msg);
265/// ```
266/// ```
267/// # use block_padding::{Iso7816, Padding};
268/// # let msg = b"test";
269/// # let n = msg.len();
270/// # let mut buffer = [0xff; 16];
271/// # buffer[..n].copy_from_slice(msg);
272/// let padded_msg = Iso7816::pad(&mut buffer, n, 2).unwrap();
273/// assert_eq!(padded_msg, b"test\x80\x00");
274/// assert_eq!(Iso7816::unpad(&padded_msg).unwrap(), msg);
275/// ```
276#[derive(Clone, Copy, Debug)]
277pub enum Iso7816 {}
278
279impl Padding for Iso7816 {
280    fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> {
281        if pos >= block.len() {
282            Err(PadError)?
283        }
284        block[pos] = 0x80;
285        set(&mut block[pos + 1..], 0);
286        Ok(())
287    }
288
289    fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> {
290        if data.is_empty() {
291            Err(UnpadError)?
292        }
293        let mut n = data.len() - 1;
294        while n != 0 {
295            if data[n] != 0 {
296                break;
297            }
298            n -= 1;
299        }
300        if data[n] != 0x80 {
301            Err(UnpadError)?
302        }
303        Ok(&data[..n])
304    }
305}
306
307/// Don't pad the data. Useful for key wrapping. Padding will fail if the data cannot be
308/// fitted into blocks without padding.
309///
310/// ```
311/// use block_padding::{NoPadding, Padding};
312///
313/// let msg = b"test";
314/// let n = msg.len();
315/// let mut buffer = [0xff; 16];
316/// buffer[..n].copy_from_slice(msg);
317/// let padded_msg = NoPadding::pad(&mut buffer, n, 4).unwrap();
318/// assert_eq!(padded_msg, b"test");
319/// assert_eq!(NoPadding::unpad(&padded_msg).unwrap(), msg);
320/// ```
321/// ```
322/// # use block_padding::{NoPadding, Padding};
323/// # let msg = b"test";
324/// # let n = msg.len();
325/// # let mut buffer = [0xff; 16];
326/// # buffer[..n].copy_from_slice(msg);
327/// let padded_msg = NoPadding::pad(&mut buffer, n, 2).unwrap();
328/// assert_eq!(padded_msg, b"test");
329/// assert_eq!(NoPadding::unpad(&padded_msg).unwrap(), msg);
330/// ```
331#[derive(Clone, Copy, Debug)]
332pub enum NoPadding {}
333
334impl Padding for NoPadding {
335    fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> {
336        if pos % block.len() != 0 {
337            Err(PadError)?
338        }
339        Ok(())
340    }
341
342    fn pad(buf: &mut [u8], pos: usize, block_size: usize) -> Result<&mut [u8], PadError> {
343        if pos % block_size != 0 {
344            Err(PadError)?
345        }
346        Ok(&mut buf[..pos])
347    }
348
349    fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> {
350        Ok(data)
351    }
352}
353
354/// Sets all bytes in `dst` equal to `value`
355#[inline(always)]
356fn set(dst: &mut [u8], value: u8) {
357    // SAFETY: we overwrite valid memory behind `dst`
358    // note: loop is not used here because it produces
359    // unnecessary branch which tests for zero-length slices
360    unsafe {
361        core::ptr::write_bytes(dst.as_mut_ptr(), value, dst.len());
362    }
363}