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}