use crate::Error;
pub trait Padding {
fn padded_buffer_length(data_length: usize, block_size: usize) -> usize;
fn pad(buffer: &mut [u8], data_length: usize, block_size: usize) -> Result<&[u8], Error>;
fn unpad(buffer: &[u8]) -> Result<&[u8], Error>;
}
pub enum ZeroPadding {}
impl Padding for ZeroPadding {
fn padded_buffer_length(data_length: usize, block_size: usize) -> usize {
if data_length % block_size == 0 {
data_length
} else {
block_size + block_size * (data_length / block_size)
}
}
fn pad(buffer: &mut [u8], data_length: usize, block_size: usize) -> Result<&[u8], Error> {
let padded_length = Self::padded_buffer_length(data_length, block_size);
if buffer.len() < padded_length {
return Err(Error::PaddedBufferTooSmall);
}
if data_length < padded_length {
buffer[data_length..padded_length].fill(0u8);
}
Ok(&buffer[0..padded_length])
}
fn unpad(buffer: &[u8]) -> Result<&[u8], Error> {
let mut n = buffer.len() - 1;
while n > 0 {
if buffer[n] != 0 {
break;
}
n -= 1;
}
Ok(&buffer[0..=n])
}
}
pub enum Pkcs7 {}
impl Padding for Pkcs7 {
fn padded_buffer_length(data_length: usize, block_size: usize) -> usize {
block_size + block_size * (data_length / block_size)
}
fn pad(buffer: &mut [u8], data_length: usize, block_size: usize) -> Result<&[u8], Error> {
let padded_length = Self::padded_buffer_length(data_length, block_size);
if buffer.len() < padded_length {
return Err(Error::PaddedBufferTooSmall);
}
let padding_length = padded_length - data_length;
buffer[data_length..padded_length].fill(padding_length as u8);
Ok(&buffer[0..padded_length])
}
fn unpad(buffer: &[u8]) -> Result<&[u8], Error> {
let padding_length = match buffer.last() {
None => return Err(Error::UnpadError("Buffer to unpad is empty".to_string())),
Some(l) => *l,
};
if buffer.len() < padding_length as usize {
return Err(Error::UnpadError(
"Buffer smaller than PKCS7 recorded padded length".to_string(),
));
}
if padding_length == 0 {
return Err(Error::UnpadError("PKCS7 padding length can't be zero".to_string()));
}
Ok(&buffer[0..buffer.len() - padding_length as usize])
}
}
#[cfg(test)]
mod test {
use crate::padding::{Padding, Pkcs7, ZeroPadding};
use crate::Error;
#[test]
fn test_zero_padding() {
const BLOCK_SIZE: usize = 16;
assert_eq!(ZeroPadding::padded_buffer_length(20, BLOCK_SIZE), 2 * BLOCK_SIZE);
assert_eq!(ZeroPadding::padded_buffer_length(2 * BLOCK_SIZE, BLOCK_SIZE), 2 * BLOCK_SIZE);
const DATA_LENGTH: usize = 22;
let mut buffer = [2u8; 2 * BLOCK_SIZE + 1];
buffer[0..2 * BLOCK_SIZE].fill(1u8);
let padded_buffer =
ZeroPadding::pad(buffer.as_mut_slice(), DATA_LENGTH, BLOCK_SIZE).unwrap();
assert_eq!(&padded_buffer[0..DATA_LENGTH], [1u8; DATA_LENGTH].as_slice());
assert_eq!(
&padded_buffer[DATA_LENGTH..],
vec![0u8; padded_buffer.len() - DATA_LENGTH].as_slice()
);
let unpadded_padded_buffer = ZeroPadding::unpad(padded_buffer).unwrap();
assert_eq!(unpadded_padded_buffer, [1u8; DATA_LENGTH].as_slice());
assert_eq!(buffer[2 * BLOCK_SIZE], 2u8);
assert!(matches!(
ZeroPadding::pad(buffer.as_mut_slice(), 2 * BLOCK_SIZE + 1, BLOCK_SIZE),
Err(Error::PaddedBufferTooSmall)
));
}
#[test]
fn test_pkcs7_padding() {
const BLOCK_SIZE: usize = 16;
assert_eq!(Pkcs7::padded_buffer_length(20, BLOCK_SIZE), 2 * BLOCK_SIZE);
assert_eq!(Pkcs7::padded_buffer_length(2 * BLOCK_SIZE, BLOCK_SIZE), 3 * BLOCK_SIZE);
const DATA_LENGTH: usize = 22;
let mut buffer = [2u8; 2 * BLOCK_SIZE + 1];
buffer[0..2 * BLOCK_SIZE].fill(1u8);
let padded_buffer = Pkcs7::pad(buffer.as_mut_slice(), DATA_LENGTH, BLOCK_SIZE).unwrap();
assert_eq!(&padded_buffer[0..DATA_LENGTH], [1u8; DATA_LENGTH].as_slice());
assert_eq!(
&padded_buffer[DATA_LENGTH..],
vec![10u8; padded_buffer.len() - DATA_LENGTH].as_slice()
);
let unpadded_padded_buffer = Pkcs7::unpad(padded_buffer).unwrap();
assert_eq!(unpadded_padded_buffer, [1u8; DATA_LENGTH].as_slice());
assert_eq!(buffer[2 * BLOCK_SIZE], 2u8);
assert!(matches!(
Pkcs7::pad(buffer.as_mut_slice(), 2 * BLOCK_SIZE + 1, BLOCK_SIZE),
Err(Error::PaddedBufferTooSmall)
));
}
}