use crate::{BlockCipherPadding, PaddingError};
use core::fmt::{Display, Formatter};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Iso7816d4Padding;
impl Iso7816d4Padding {
pub const fn new() -> Self {
Self
}
}
impl BlockCipherPadding for Iso7816d4Padding {
type Error = PaddingError;
fn add_padding(&mut self, block: &mut [u8], position: usize) -> Result<usize, Self::Error> {
let tail = block
.get_mut(position..)
.ok_or(PaddingError::PositionOutOfRange)?;
let count = tail.len();
let (marker, rest) = tail.split_first_mut().ok_or(PaddingError::BlockFull)?;
*marker = 0x80;
rest.fill(0x00);
Ok(count)
}
fn pad_count(&self, block: &[u8]) -> Result<usize, Self::Error> {
let mut position: isize = -1;
let mut still_zero: isize = -1;
for (index, &byte) in block.iter().enumerate().rev() {
let value = byte as isize;
let matches_00 = (value - 1) >> (isize::BITS - 1);
let matches_80 = ((value ^ 0x80) - 1) >> (isize::BITS - 1);
position ^= (index as isize ^ position) & still_zero & matches_80;
still_zero &= matches_00;
}
if position < 0 {
return Err(PaddingError::CorruptPadding);
}
Ok(block.len() - position as usize)
}
}
impl Display for Iso7816d4Padding {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str("ISO7816-4")
}
}
#[cfg(test)]
mod tests {
extern crate std;
use std::string::ToString;
use super::Iso7816d4Padding;
use crate::{BlockCipherPadding, PaddingError};
#[test]
fn writes_the_marker_then_zeros() {
let mut padding = Iso7816d4Padding::new();
let mut block = [0xff_u8; 8];
assert_eq!(padding.add_padding(&mut block, 3), Ok(5));
assert_eq!(block, [0xff, 0xff, 0xff, 0x80, 0, 0, 0, 0]);
assert_eq!(padding.pad_count(&block), Ok(5));
}
#[test]
fn a_single_padding_byte_is_only_the_marker() {
let mut padding = Iso7816d4Padding::new();
let mut block = [0xff_u8; 8];
assert_eq!(padding.add_padding(&mut block, 7), Ok(1));
assert_eq!(block, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80]);
assert_eq!(padding.pad_count(&block), Ok(1));
}
#[test]
fn an_empty_block_pads_to_its_full_length() {
let mut padding = Iso7816d4Padding::new();
let mut block = [0xff_u8; 8];
assert_eq!(padding.add_padding(&mut block, 0), Ok(8));
assert_eq!(block, [0x80, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(padding.pad_count(&block), Ok(8));
}
#[test]
fn a_full_block_has_no_room_for_padding() {
let mut padding = Iso7816d4Padding::new();
assert_eq!(
padding.add_padding(&mut [0xff_u8; 8], 8),
Err(PaddingError::BlockFull)
);
}
#[test]
fn rejects_a_position_past_the_end_of_the_block() {
let mut padding = Iso7816d4Padding::new();
assert_eq!(
padding.add_padding(&mut [0xff_u8; 8], 9),
Err(PaddingError::PositionOutOfRange)
);
}
#[test]
fn stays_unambiguous_for_messages_ending_in_zero() {
let mut padding = Iso7816d4Padding::new();
let mut block = [0x00_u8; 8];
block[0] = 0x01;
assert_eq!(padding.add_padding(&mut block, 3), Ok(5));
assert_eq!(block, [0x01, 0, 0, 0x80, 0, 0, 0, 0]);
assert_eq!(padding.pad_count(&block), Ok(5));
}
#[test]
fn takes_the_last_marker_when_the_message_contains_one() {
let padding = Iso7816d4Padding::new();
assert_eq!(padding.pad_count(&[0x80, 0x01, 0x80, 0x00]), Ok(2));
}
#[test]
fn rejects_a_block_without_a_marker() {
let padding = Iso7816d4Padding::new();
assert_eq!(
padding.pad_count(&[1, 2, 3, 4]),
Err(PaddingError::CorruptPadding)
);
assert_eq!(
padding.pad_count(&[0, 0, 0, 0]),
Err(PaddingError::CorruptPadding)
);
assert_eq!(padding.pad_count(&[]), Err(PaddingError::CorruptPadding));
}
#[test]
fn padding_round_trips_for_every_message_length() {
let mut padding = Iso7816d4Padding::new();
for used in 0..8 {
let mut block = [0xa5_u8; 8];
let added = padding.add_padding(&mut block, used).unwrap();
assert_eq!(added, 8 - used);
assert_eq!(padding.pad_count(&block), Ok(8 - used));
}
}
#[test]
fn reports_its_algorithm_name() {
assert_eq!(Iso7816d4Padding::new().to_string(), "ISO7816-4");
}
}