use crate::aes::BLOCK_SIZE;
use crate::Error;
pub fn pkcs7_padding_len(len: usize) -> usize {
BLOCK_SIZE.saturating_sub(len % BLOCK_SIZE)
}
pub fn pkcs7_pad(data: &[u8]) -> Vec<u8> {
let mut buf = Vec::with_capacity(data.len().saturating_add(BLOCK_SIZE));
buf.extend_from_slice(data);
pkcs7_pad_in_place(&mut buf);
buf
}
pub fn pkcs7_pad_in_place(buf: &mut Vec<u8>) {
let n = pkcs7_padding_len(buf.len());
buf.resize(buf.len().saturating_add(n), n as u8);
}
pub fn pkcs7_unpad(data: &[u8]) -> Result<&[u8], Error> {
let len = pkcs7_unpadded_len(data)?;
data.get(..len).ok_or(Error::InvalidPadding)
}
pub fn pkcs7_unpad_in_place(buf: &mut Vec<u8>) -> Result<(), Error> {
let len = pkcs7_unpadded_len(buf)?;
buf.truncate(len);
Ok(())
}
fn pkcs7_unpadded_len(data: &[u8]) -> Result<usize, Error> {
if data.is_empty() || data.len() % BLOCK_SIZE != 0 {
return Err(Error::InvalidPadding);
}
let last = data.rchunks_exact(BLOCK_SIZE).next().ok_or(Error::InvalidPadding)?;
let n = *last.last().ok_or(Error::InvalidPadding)?;
let mut bad = ((n as u32).wrapping_sub(1) >> 31) | (16u32.wrapping_sub(n as u32) >> 31);
for (i, &b) in last.iter().enumerate() {
let distance = BLOCK_SIZE.saturating_sub(i) as u32; let outside = (n as u32).wrapping_sub(distance) >> 31; let in_padding = outside.wrapping_sub(1);
bad |= in_padding & (b ^ n) as u32;
}
if bad != 0 {
return Err(Error::InvalidPadding);
}
Ok(data.len().saturating_sub(n as usize))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pads_every_length() {
for len in 0..=48 {
let data = vec![0xAAu8; len];
let padded = pkcs7_pad(&data);
let n = pkcs7_padding_len(len);
assert!((1..=16).contains(&n));
assert_eq!(padded.len(), len + n);
assert_eq!(padded.len() % 16, 0);
assert!(padded[len..].iter().all(|&b| b as usize == n));
assert_eq!(pkcs7_unpad(&padded).unwrap(), &data[..]);
let mut buf = padded.clone();
pkcs7_unpad_in_place(&mut buf).unwrap();
assert_eq!(buf, data);
}
}
#[test]
fn rejects_bad_padding() {
let mut block = [16u8; 16];
assert_eq!(pkcs7_unpad(&block).unwrap(), b"");
block[15] = 0;
assert!(matches!(pkcs7_unpad(&block), Err(Error::InvalidPadding)));
block[15] = 17;
assert!(matches!(pkcs7_unpad(&block), Err(Error::InvalidPadding)));
let mut block = [0u8; 16];
block[13] = 2;
block[14] = 3;
block[15] = 3;
assert!(matches!(pkcs7_unpad(&block), Err(Error::InvalidPadding)));
block[13] = 3;
assert_eq!(pkcs7_unpad(&block).unwrap(), &[0u8; 13][..]);
assert!(matches!(pkcs7_unpad(&[]), Err(Error::InvalidPadding)));
assert!(matches!(pkcs7_unpad(&[1u8; 15]), Err(Error::InvalidPadding)));
assert!(matches!(pkcs7_unpad(&[1u8; 17]), Err(Error::InvalidPadding)));
}
}