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