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}