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 {}