#![forbid(unsafe_code)]
use crate::Error;
use aes::Aes128;
use aes::cipher::{BlockCipherEncrypt, KeyInit};
type Block16 = aes::cipher::Block<Aes128>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Scheme {
Cenc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Pattern {
pub crypt_blocks: u8,
pub skip_blocks: u8,
}
impl Pattern {
pub const NONE: Self = Self {
crypt_blocks: 0,
skip_blocks: 0,
};
#[must_use]
pub const fn is_none(self) -> bool {
self.crypt_blocks == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Subsample {
pub clear_bytes: u16,
pub protected_bytes: u32,
}
pub fn decrypt_cenc(
key: &[u8; 16],
iv: &[u8; 16],
pattern: Pattern,
data: &mut [u8],
subsamples: &[Subsample],
) -> Result<(), Error> {
apply_cenc(key, iv, pattern, data, subsamples)
}
pub fn encrypt_cenc(
key: &[u8; 16],
iv: &[u8; 16],
pattern: Pattern,
data: &mut [u8],
subsamples: &[Subsample],
) -> Result<(), Error> {
apply_cenc(key, iv, pattern, data, subsamples)
}
fn apply_cenc(
key: &[u8; 16],
iv: &[u8; 16],
pattern: Pattern,
data: &mut [u8],
subsamples: &[Subsample],
) -> Result<(), Error> {
if !pattern.is_none() {
return Err(Error::InvalidPattern);
}
let cipher = Aes128::new(&(*key).into());
let mut counter = *iv;
if subsamples.is_empty() {
xor_ctr(&cipher, &mut counter, data);
return Ok(());
}
let mut pos = 0usize;
for sub in subsamples {
let clear = usize::from(sub.clear_bytes);
let protected = sub.protected_bytes as usize;
let end_clear = pos.checked_add(clear).ok_or(Error::SubsampleOverflow)?;
let end_prot = end_clear
.checked_add(protected)
.ok_or(Error::SubsampleOverflow)?;
if end_prot > data.len() {
return Err(Error::SubsampleOverflow);
}
pos = end_clear;
if protected > 0 {
xor_ctr(&cipher, &mut counter, &mut data[pos..end_prot]);
}
pos = end_prot;
}
Ok(())
}
fn xor_ctr(cipher: &Aes128, counter: &mut [u8; 16], data: &mut [u8]) {
let mut offset = 0;
while offset < data.len() {
let mut block: Block16 = (*counter).into();
cipher.encrypt_block(&mut block);
let n = (data.len() - offset).min(16);
for i in 0..n {
data[offset + i] ^= block[i];
}
offset += n;
inc_be128(counter);
}
}
fn inc_be128(block: &mut [u8; 16]) {
for i in (0..16).rev() {
let (v, overflow) = block[i].overflowing_add(1);
block[i] = v;
if !overflow {
break;
}
}
}
#[must_use]
pub fn iv_from_8(iv8: &[u8; 8]) -> [u8; 16] {
let mut out = [0u8; 16];
out[..8].copy_from_slice(iv8);
out
}
pub fn iv_from_constant(constant_iv: &[u8]) -> Result<[u8; 16], Error> {
match constant_iv.len() {
8 => {
let mut a = [0u8; 8];
a.copy_from_slice(constant_iv);
Ok(iv_from_8(&a))
}
16 => {
let mut a = [0u8; 16];
a.copy_from_slice(constant_iv);
Ok(a)
}
_ => Err(Error::InvalidKeyMaterial),
}
}
#[cfg(test)]
#[path = "cenc_tests.rs"]
mod tests;