tx_padding/lib.rs
1//! Padding and unpadding of messages with random prepended bytes and trailing zeros
2//!
3//! This crate provides a padding scheme compatible with `block-padding` crate
4//! in which random bytes are prepended and zeros are appended.
5//!
6//! For a message of length `size`, a buffer of length
7//! `block_size * ((size + 1) / block_size) + 2 * block_size` is required for padding. Let `pad_len` be
8//! `(-size - 2) % block_size + 2`. Apparently `pad_len` is the number of bytes to pad the message into
9//! multiple of `block_size`. The padding scheme appends `pad_len + 1` bytes at the front of the
10//! message, where the lower `log(block_size)` bits of the first byte stores `pad_len - 2` and the rest
11//! of the bits in the padding are random. At this point the message needs `block_size - 1` more
12//! bytes to form multiple of `block_size` and we will just pad `\0` at the end.
13//!
14//! So `TxPadding<N>` comes with a type parameter `N` which specify the block size to use which is
15//! essential for unpadding. `N` must be a power of 2.
16//!
17//! ```
18//! use tx_padding::{TxPadding, Padding};
19//! use tx_padding::consts::{U8};
20//!
21//! let msg = b"test";
22//! let n = msg.len();
23//! let mut buffer = [0xff; 16];
24//! buffer[..n].copy_from_slice(msg);
25//! let padded_msg = TxPadding::<U8>::pad(&mut buffer, n, 8).unwrap();
26//! assert_eq!(&padded_msg[5..], b"test\x00\x00\x00\x00\x00\x00\x00");
27//! assert_eq!((padded_msg[0] & 0x7) + 2, 4);
28//! assert_eq!(TxPadding::<U8>::unpad(&padded_msg).unwrap(), msg);
29//! ```
30//! ```
31//! use tx_padding::{TxPadding, Padding};
32//! use tx_padding::consts::{U8};
33//! let mut buffer = [0xff; 8];
34//! assert!(TxPadding::<U8>::pad(&mut buffer, 5, 8).is_err());
35//! ```
36//!
37//! `pad_block` will always return `PadError` since it is not intended to be called. `pad` will
38//! return `PadError` if `block_size > 511`, `block_size` mismatch type parameter `N` or buffer
39//! is not sufficiently large, which is stricter than the requirement of the `Padding` trait.
40#![no_std]
41
42pub use block_padding::{PadError, Padding, UnpadError};
43
44use core::convert::Infallible;
45use core::marker::PhantomData;
46
47use consts::{U1, U256};
48pub use typenum::consts;
49
50use typenum::marker_traits::{NonZero, PowerOfTwo, Unsigned};
51use typenum::operator_aliases::{Gr, LeEq};
52use typenum::type_operators::{IsGreater, IsLessOrEqual};
53
54use rand::RngCore;
55
56#[cfg(not(features = "thread_rng"))]
57type DefaultRng = rand::rngs::OsRng;
58#[cfg(features = "thread_rng")]
59type DefaultRng = rand::ThreadRng;
60
61#[derive(Clone, Copy, Debug)]
62pub enum TxPadding<N> {
63 _Phantom(Infallible, PhantomData<N>),
64}
65
66impl<N> Padding for TxPadding<N>
67where
68 N: PowerOfTwo + Unsigned + IsLessOrEqual<U256> + IsGreater<U1>,
69 LeEq<N, U256>: NonZero,
70 Gr<N, U1>: NonZero,
71{
72 fn pad_block(_block: &mut [u8], _pos: usize) -> Result<(), PadError> {
73 Err(PadError)
74 }
75
76 fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> {
77 if data.is_empty() {
78 Err(UnpadError)?
79 }
80 let l = data.len();
81 let block_size = N::to_usize();
82 let pad_zero = block_size - 1;
83 let pad_len = (data[0] & (pad_zero as u8)) as usize + 2;
84 if l < pad_len + block_size {
85 Err(UnpadError)?
86 }
87 if data[l - pad_zero..l].iter().any(|&v| v != 0) {
88 Err(UnpadError)?
89 }
90
91 Ok(&data[1 + pad_len..l - pad_zero])
92 }
93
94 fn pad(buf: &mut [u8], pos: usize, block_size: usize) -> Result<&mut [u8], PadError> {
95 if block_size != N::to_usize() {
96 Err(PadError)?
97 }
98 let block_size = N::to_usize();
99 let be = block_size * ((pos + 1) / block_size + 2);
100 if buf.len() < be {
101 Err(PadError)?
102 }
103
104 let pad_zero = block_size - 1;
105 let pad_len = ((-(pos as isize) - 2).rem_euclid(block_size as isize)) as usize + 2;
106 buf.copy_within(..pos, 1 + pad_len);
107 DefaultRng::default()
108 .try_fill_bytes(&mut buf[1..1 + pad_len])
109 .map_err(|_| PadError)?;
110 buf[0] = !((block_size - 1) as u8) | (pad_len - 2) as u8;
111
112 // SAFETY: will use slice::fill after it stabilizes
113 unsafe {
114 core::ptr::write_bytes(buf[be - pad_zero..be].as_mut_ptr(), 0, pad_zero);
115 }
116
117 Ok(&mut buf[..be])
118 }
119}