#![no_std]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![deny(unsafe_code)]
#![warn(missing_docs, missing_debug_implementations)]
pub use hybrid_array as array;
use core::fmt;
use hybrid_array::{Array, ArraySize};
pub trait Padding: 'static {
fn raw_pad(block: &mut [u8], pos: usize);
fn raw_unpad(block: &[u8]) -> Result<&[u8], Error>;
#[inline]
fn pad<BlockSize: ArraySize>(block: &mut Array<u8, BlockSize>, pos: usize) {
Self::raw_pad(block.as_mut_slice(), pos);
}
#[inline]
fn unpad<BlockSize: ArraySize>(block: &Array<u8, BlockSize>) -> Result<&[u8], Error> {
Self::raw_unpad(block.as_slice())
}
#[inline]
fn pad_detached<BlockSize: ArraySize>(data: &[u8]) -> PaddedData<'_, BlockSize> {
let (blocks, tail) = Array::slice_as_chunks(data);
let mut tail_block = Array::default();
let pos = tail.len();
tail_block[..pos].copy_from_slice(tail);
Self::pad(&mut tail_block, pos);
PaddedData::Pad { blocks, tail_block }
}
#[inline]
fn unpad_blocks<BlockSize: ArraySize>(blocks: &[Array<u8, BlockSize>]) -> Result<&[u8], Error> {
let bs = BlockSize::USIZE;
let (last_block, full_blocks) = blocks.split_last().ok_or(Error)?;
let unpad_len = Self::unpad(last_block)?.len();
assert!(unpad_len <= bs);
let buf = Array::slice_as_flattened(blocks);
let data_len = full_blocks.len() * bs + unpad_len;
Ok(&buf[..data_len])
}
}
#[derive(Clone, Copy, Debug)]
pub struct ZeroPadding;
impl Padding for ZeroPadding {
#[inline]
fn raw_pad(block: &mut [u8], pos: usize) {
if pos > block.len() {
panic!("`pos` is bigger than block size");
}
block[pos..].fill(0);
}
#[inline]
fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
for i in (0..block.len()).rev() {
if block[i] != 0 {
return Ok(&block[..i + 1]);
}
}
Ok(&block[..0])
}
#[inline]
fn pad_detached<BlockSize: ArraySize>(data: &[u8]) -> PaddedData<'_, BlockSize> {
let (blocks, tail) = Array::slice_as_chunks(data);
if tail.is_empty() {
return PaddedData::NoPad { blocks };
}
let mut tail_block = Array::default();
let pos = tail.len();
tail_block[..pos].copy_from_slice(tail);
Self::pad(&mut tail_block, pos);
PaddedData::Pad { blocks, tail_block }
}
#[inline]
fn unpad_blocks<BlockSize: ArraySize>(blocks: &[Array<u8, BlockSize>]) -> Result<&[u8], Error> {
let buf = Array::slice_as_flattened(blocks);
for i in (0..buf.len()).rev() {
if buf[i] != 0 {
return Ok(&buf[..i + 1]);
}
}
Ok(&buf[..0])
}
}
#[derive(Clone, Copy, Debug)]
pub struct Pkcs7;
impl Pkcs7 {
#[inline]
fn unpad(block: &[u8], strict: bool) -> Result<&[u8], Error> {
if block.len() > 255 {
panic!("block size is too big for PKCS#7");
}
let bs = block.len();
let n = block[bs - 1];
if n == 0 || n as usize > bs {
return Err(Error);
}
let s = bs - n as usize;
if strict && block[s..bs - 1].iter().any(|&v| v != n) {
return Err(Error);
}
Ok(&block[..s])
}
}
impl Padding for Pkcs7 {
#[inline]
fn raw_pad(block: &mut [u8], pos: usize) {
if block.len() > 255 {
panic!("block size is too big for PKCS#7");
}
if pos >= block.len() {
panic!("`pos` is bigger or equal to block size");
}
let n = (block.len() - pos) as u8;
block[pos..].fill(n);
}
#[inline]
fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
Pkcs7::unpad(block, true)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Iso10126;
impl Padding for Iso10126 {
#[inline]
fn raw_pad(block: &mut [u8], pos: usize) {
Pkcs7::raw_pad(block, pos)
}
#[inline]
fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
Pkcs7::unpad(block, false)
}
}
#[derive(Clone, Copy, Debug)]
pub struct AnsiX923;
impl Padding for AnsiX923 {
#[inline]
fn raw_pad(block: &mut [u8], pos: usize) {
if block.len() > 255 {
panic!("block size is too big for ANSI X9.23");
}
if pos >= block.len() {
panic!("`pos` is bigger or equal to block size");
}
let bs = block.len();
block[pos..bs - 1].fill(0);
block[bs - 1] = (bs - pos) as u8;
}
#[inline]
fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
if block.len() > 255 {
panic!("block size is too big for ANSI X9.23");
}
let bs = block.len();
let n = block[bs - 1] as usize;
if n == 0 || n > bs {
return Err(Error);
}
let s = bs - n;
if block[s..bs - 1].iter().any(|&v| v != 0) {
return Err(Error);
}
Ok(&block[..s])
}
}
#[derive(Clone, Copy, Debug)]
pub struct Iso7816;
impl Padding for Iso7816 {
#[inline]
fn raw_pad(block: &mut [u8], pos: usize) {
if pos >= block.len() {
panic!("`pos` is bigger or equal to block size");
}
block[pos] = 0x80;
block[pos + 1..].fill(0);
}
#[inline]
fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
for i in (0..block.len()).rev() {
match block[i] {
0x80 => return Ok(&block[..i]),
0x00 => continue,
_ => return Err(Error),
}
}
Err(Error)
}
}
#[derive(Clone, Copy, Debug)]
pub struct NoPadding;
impl Padding for NoPadding {
#[inline]
fn raw_pad(block: &mut [u8], pos: usize) {
if pos > block.len() {
panic!("`pos` is bigger than block size");
}
}
#[inline]
fn raw_unpad(block: &[u8]) -> Result<&[u8], Error> {
Ok(block)
}
#[inline]
fn pad_detached<BlockSize: ArraySize>(data: &[u8]) -> PaddedData<'_, BlockSize> {
let (blocks, tail) = Array::slice_as_chunks(data);
if tail.is_empty() {
PaddedData::NoPad { blocks }
} else {
PaddedData::Error
}
}
#[inline]
fn unpad_blocks<BlockSize: ArraySize>(blocks: &[Array<u8, BlockSize>]) -> Result<&[u8], Error> {
Ok(Array::slice_as_flattened(blocks))
}
}
#[derive(Clone, Copy, Debug)]
pub struct Error;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("Padding error")
}
}
impl core::error::Error for Error {}
#[derive(Debug)]
pub enum PaddedData<'a, BlockSize: ArraySize> {
Pad {
blocks: &'a [Array<u8, BlockSize>],
tail_block: Array<u8, BlockSize>,
},
NoPad {
blocks: &'a [Array<u8, BlockSize>],
},
Error,
}
impl<'a, BlockSize: ArraySize> PaddedData<'a, BlockSize> {
pub fn unwrap(self) -> (&'a [Array<u8, BlockSize>], Array<u8, BlockSize>) {
match self {
PaddedData::Pad { blocks, tail_block } => (blocks, tail_block),
PaddedData::NoPad { .. } => {
panic!("Expected `PaddedData::Pad`, but got `PaddedData::NoPad`");
}
PaddedData::Error => {
panic!("Expected `PaddedData::Pad`, but got `PaddedData::Error`");
}
}
}
}