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