use crate::BlockCipher;
use crate::cipher::aes::Aes;
use crate::cipher::des::{Des, TripleDes};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Algorithm {
Aes128,
Aes192,
Aes256,
Des,
TripleDes,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Mode {
Ecb,
Cbc,
Ctr,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Padding {
None,
Pkcs7,
Iso9797M1,
Iso9797M2,
AnsiX923,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Direction {
Encrypt,
Decrypt,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
InvalidKeyLen,
InvalidIvLen,
InvalidPaddingForMode,
OutputBufferTooSmall {
needed: usize,
},
NotInitialized,
AlreadyFinalized,
UnpaddedInput,
BadPadding,
}
const MAX_BLOCK: usize = 16;
enum Key {
None,
Aes(Aes),
Des(Des),
TripleDes(TripleDes),
}
impl Key {
fn encrypt_block(&self, blk: &mut [u8]) {
match self {
Key::None => unreachable!("encrypt_block on uninitialised Cipher"),
Key::Aes(c) => c.encrypt_block(blk),
Key::Des(c) => c.encrypt_block(blk),
Key::TripleDes(c) => c.encrypt_block(blk),
}
}
fn decrypt_block(&self, blk: &mut [u8]) {
match self {
Key::None => unreachable!("decrypt_block on uninitialised Cipher"),
Key::Aes(c) => c.decrypt_block(blk),
Key::Des(c) => c.decrypt_block(blk),
Key::TripleDes(c) => c.decrypt_block(blk),
}
}
}
pub struct Cipher {
algo: Algorithm,
mode: Mode,
padding: Padding,
block_len: usize,
key: Key,
direction: Option<Direction>,
iv: [u8; MAX_BLOCK],
ctr_ks: [u8; MAX_BLOCK],
ctr_ks_pos: usize,
buf: [u8; 2 * MAX_BLOCK],
buf_len: usize,
finalized: bool,
}
impl Cipher {
pub fn new(algo: Algorithm, mode: Mode, padding: Padding) -> Result<Self, Error> {
let block_len = match algo {
Algorithm::Aes128 | Algorithm::Aes192 | Algorithm::Aes256 => 16,
Algorithm::Des | Algorithm::TripleDes => 8,
};
if matches!(mode, Mode::Ctr) && !matches!(padding, Padding::None) {
return Err(Error::InvalidPaddingForMode);
}
Ok(Self {
algo,
mode,
padding,
block_len,
key: Key::None,
direction: None,
iv: [0u8; MAX_BLOCK],
ctr_ks: [0u8; MAX_BLOCK],
ctr_ks_pos: MAX_BLOCK, buf: [0u8; 2 * MAX_BLOCK],
buf_len: 0,
finalized: false,
})
}
pub fn init(&mut self, direction: Direction, key: &[u8], iv: &[u8]) -> Result<(), Error> {
let expected_key_len = match self.algo {
Algorithm::Aes128 => 16,
Algorithm::Aes192 => 24,
Algorithm::Aes256 => 32,
Algorithm::Des => 8,
Algorithm::TripleDes => 24,
};
if key.len() != expected_key_len {
return Err(Error::InvalidKeyLen);
}
match self.mode {
Mode::Ecb => { }
Mode::Cbc | Mode::Ctr => {
if iv.len() != self.block_len {
return Err(Error::InvalidIvLen);
}
}
}
self.key = match self.algo {
Algorithm::Aes128 | Algorithm::Aes192 | Algorithm::Aes256 => Key::Aes(Aes::new(key)),
Algorithm::Des => Key::Des(Des::new(key)),
Algorithm::TripleDes => Key::TripleDes(TripleDes::new(key)),
};
self.iv = [0u8; MAX_BLOCK];
if matches!(self.mode, Mode::Cbc | Mode::Ctr) {
self.iv[..self.block_len].copy_from_slice(iv);
}
self.ctr_ks = [0u8; MAX_BLOCK];
self.ctr_ks_pos = self.block_len; self.buf = [0u8; 2 * MAX_BLOCK];
self.buf_len = 0;
self.direction = Some(direction);
self.finalized = false;
Ok(())
}
pub const fn block_len(&self) -> usize {
self.block_len
}
pub const fn iv_len(&self) -> usize {
match self.mode {
Mode::Ecb => 0,
Mode::Cbc | Mode::Ctr => self.block_len,
}
}
pub fn update_output_size(&self, input_len: usize) -> usize {
match self.mode {
Mode::Ctr => input_len,
Mode::Ecb | Mode::Cbc => {
let total = self.buf_len + input_len;
if total < self.block_len {
0
} else {
(total / self.block_len) * self.block_len
}
}
}
}
pub const fn finalize_output_size(&self) -> usize {
match self.mode {
Mode::Ctr => 0,
Mode::Ecb | Mode::Cbc => self.block_len,
}
}
pub fn update(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, Error> {
let dir = self.direction.ok_or(Error::NotInitialized)?;
if self.finalized {
return Err(Error::AlreadyFinalized);
}
match self.mode {
Mode::Ctr => self.update_ctr(input, output),
Mode::Ecb | Mode::Cbc => self.update_block(dir, input, output),
}
}
pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, Error> {
let dir = self.direction.ok_or(Error::NotInitialized)?;
if self.finalized {
return Err(Error::AlreadyFinalized);
}
let written = match self.mode {
Mode::Ctr => {
0
}
Mode::Ecb | Mode::Cbc => match dir {
Direction::Encrypt => self.finalize_block_encrypt(output)?,
Direction::Decrypt => self.finalize_block_decrypt(output)?,
},
};
self.finalized = true;
Ok(written)
}
fn update_ctr(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, Error> {
if output.len() < input.len() {
return Err(Error::OutputBufferTooSmall { needed: input.len() });
}
let bs = self.block_len;
for i in 0..input.len() {
if self.ctr_ks_pos == bs {
self.ctr_ks[..bs].copy_from_slice(&self.iv[..bs]);
self.key.encrypt_block(&mut self.ctr_ks[..bs]);
ctr_increment(&mut self.iv[..bs]);
self.ctr_ks_pos = 0;
}
output[i] = input[i] ^ self.ctr_ks[self.ctr_ks_pos];
self.ctr_ks_pos += 1;
}
Ok(input.len())
}
fn keep_back(&self, dir: Direction) -> usize {
if matches!(dir, Direction::Decrypt) && !matches!(self.padding, Padding::None) {
self.block_len
} else {
0
}
}
fn update_block(&mut self, dir: Direction, input: &[u8], output: &mut [u8]) -> Result<usize, Error> {
let bs = self.block_len;
let kb = self.keep_back(dir);
let mut written = 0usize;
let mut in_pos = 0usize;
let cap = 2 * bs;
while in_pos < input.len() {
let space = cap - self.buf_len;
if space > 0 {
let take = space.min(input.len() - in_pos);
self.buf[self.buf_len..self.buf_len + take].copy_from_slice(&input[in_pos..in_pos + take]);
self.buf_len += take;
in_pos += take;
}
while self.buf_len >= bs && self.buf_len - bs >= kb {
if output.len() - written < bs {
return Err(Error::OutputBufferTooSmall { needed: written + bs });
}
let mut blk = [0u8; MAX_BLOCK];
blk[..bs].copy_from_slice(&self.buf[..bs]);
self.process_one_block(dir, &mut blk[..bs]);
output[written..written + bs].copy_from_slice(&blk[..bs]);
written += bs;
self.buf.copy_within(bs..self.buf_len, 0);
self.buf_len -= bs;
}
}
Ok(written)
}
fn process_one_block(&mut self, dir: Direction, blk: &mut [u8]) {
let bs = self.block_len;
match (self.mode, dir) {
(Mode::Ecb, Direction::Encrypt) => {
self.key.encrypt_block(blk);
}
(Mode::Ecb, Direction::Decrypt) => {
self.key.decrypt_block(blk);
}
(Mode::Cbc, Direction::Encrypt) => {
for i in 0..bs {
blk[i] ^= self.iv[i];
}
self.key.encrypt_block(blk);
self.iv[..bs].copy_from_slice(&blk[..bs]);
}
(Mode::Cbc, Direction::Decrypt) => {
let ct_copy = {
let mut tmp = [0u8; MAX_BLOCK];
tmp[..bs].copy_from_slice(&blk[..bs]);
tmp
};
self.key.decrypt_block(blk);
for i in 0..bs {
blk[i] ^= self.iv[i];
}
self.iv[..bs].copy_from_slice(&ct_copy[..bs]);
}
(Mode::Ctr, _) => unreachable!("CTR uses update_ctr"),
}
}
fn finalize_block_encrypt(&mut self, output: &mut [u8]) -> Result<usize, Error> {
let bs = self.block_len;
match self.padding {
Padding::None => {
if self.buf_len != 0 {
return Err(Error::UnpaddedInput);
}
Ok(0)
}
Padding::Pkcs7 | Padding::Iso9797M2 | Padding::Iso9797M1 | Padding::AnsiX923 => {
if matches!(self.padding, Padding::Iso9797M1) && self.buf_len == 0 {
return Ok(0);
}
if output.len() < bs {
return Err(Error::OutputBufferTooSmall { needed: bs });
}
let pad_len = bs - self.buf_len; apply_padding(self.padding, &mut self.buf[..bs], self.buf_len, pad_len);
self.buf_len = bs;
let mut blk = [0u8; MAX_BLOCK];
blk[..bs].copy_from_slice(&self.buf[..bs]);
self.process_one_block(Direction::Encrypt, &mut blk[..bs]);
output[..bs].copy_from_slice(&blk[..bs]);
self.buf_len = 0;
Ok(bs)
}
}
}
fn finalize_block_decrypt(&mut self, output: &mut [u8]) -> Result<usize, Error> {
let bs = self.block_len;
match self.padding {
Padding::None => {
if self.buf_len != 0 {
return Err(Error::UnpaddedInput);
}
Ok(0)
}
_ => {
if self.buf_len != bs {
return Err(Error::UnpaddedInput);
}
let mut blk = [0u8; MAX_BLOCK];
blk[..bs].copy_from_slice(&self.buf[..bs]);
self.process_one_block(Direction::Decrypt, &mut blk[..bs]);
let unpadded = strip_padding(self.padding, &blk[..bs])?;
if output.len() < unpadded {
return Err(Error::OutputBufferTooSmall { needed: unpadded });
}
output[..unpadded].copy_from_slice(&blk[..unpadded]);
self.buf_len = 0;
Ok(unpadded)
}
}
}
pub fn update_to_vec(&mut self, input: &[u8]) -> Result<Vec<u8>, Error> {
let upper = self.update_output_size(input.len());
let mut out = vec![0u8; upper];
let n = self.update(input, &mut out)?;
out.truncate(n);
Ok(out)
}
pub fn finalize_to_vec(&mut self) -> Result<Vec<u8>, Error> {
let upper = self.finalize_output_size();
let mut out = vec![0u8; upper];
let n = self.finalize(&mut out)?;
out.truncate(n);
Ok(out)
}
}
fn apply_padding(padding: Padding, block: &mut [u8], data_len: usize, pad_len: usize) {
let bs = block.len();
debug_assert_eq!(data_len + pad_len, bs);
match padding {
Padding::None => {}
Padding::Pkcs7 => {
for b in &mut block[data_len..bs] {
*b = pad_len as u8;
}
}
Padding::Iso9797M1 => {
for b in &mut block[data_len..bs] {
*b = 0x00;
}
}
Padding::Iso9797M2 => {
block[data_len] = 0x80;
for b in &mut block[data_len + 1..bs] {
*b = 0x00;
}
}
Padding::AnsiX923 => {
for b in &mut block[data_len..bs - 1] {
*b = 0x00;
}
block[bs - 1] = pad_len as u8;
}
}
}
fn strip_padding(padding: Padding, block: &[u8]) -> Result<usize, Error> {
let bs = block.len();
match padding {
Padding::None => Ok(bs),
Padding::Pkcs7 => {
let pad_len = block[bs - 1] as usize;
if pad_len == 0 || pad_len > bs {
return Err(Error::BadPadding);
}
for &b in &block[bs - pad_len..bs] {
if b as usize != pad_len {
return Err(Error::BadPadding);
}
}
Ok(bs - pad_len)
}
Padding::Iso9797M1 => {
let mut len = bs;
while len > 0 && block[len - 1] == 0x00 {
len -= 1;
}
Ok(len)
}
Padding::Iso9797M2 => {
let mut i = bs;
while i > 0 {
i -= 1;
if block[i] == 0x80 {
return Ok(i);
}
if block[i] != 0x00 {
return Err(Error::BadPadding);
}
}
Err(Error::BadPadding)
}
Padding::AnsiX923 => {
let pad_len = block[bs - 1] as usize;
if pad_len == 0 || pad_len > bs {
return Err(Error::BadPadding);
}
for &b in &block[bs - pad_len..bs - 1] {
if b != 0x00 {
return Err(Error::BadPadding);
}
}
Ok(bs - pad_len)
}
}
}
fn ctr_increment(counter: &mut [u8]) {
let n = counter.len();
let lo = n.saturating_sub(8);
for i in (lo..n).rev() {
let (v, carry) = counter[i].overflowing_add(1);
counter[i] = v;
if !carry {
return;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::BlockCipher;
use crate::cipher::aes::Aes128;
use crate::cipher::modes::{cbc_decrypt, cbc_encrypt, ctr_encrypt, ecb_encrypt};
fn hex(s: &str) -> Vec<u8> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
.collect()
}
#[test]
fn ecb_aes128_no_padding_round_trip() {
let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
let pt = hex("6bc1bee22e409f96e93d7e117393172a");
let mut enc = Cipher::new(Algorithm::Aes128, Mode::Ecb, Padding::None).unwrap();
enc.init(Direction::Encrypt, &key, &[]).unwrap();
let ct = {
let mut out = vec![0u8; 32];
let n = enc.update(&pt, &mut out).unwrap();
let m = enc.finalize(&mut out[n..]).unwrap();
out.truncate(n + m);
out
};
let mut dec = Cipher::new(Algorithm::Aes128, Mode::Ecb, Padding::None).unwrap();
dec.init(Direction::Decrypt, &key, &[]).unwrap();
let mut got = vec![0u8; 32];
let n = dec.update(&ct, &mut got).unwrap();
let m = dec.finalize(&mut got[n..]).unwrap();
got.truncate(n + m);
assert_eq!(got, pt);
}
#[test]
fn ecb_aes128_pkcs7_unaligned() {
let key = [0x42u8; 16];
let pt: Vec<u8> = (0u8..30).collect();
let mut padded = pt.clone();
let pad = 16 - (padded.len() % 16);
padded.extend(std::iter::repeat(pad as u8).take(pad));
let cipher_ref = Aes128::new(&key);
ecb_encrypt(&cipher_ref, &mut padded);
let ct_ref = padded;
let mut enc = Cipher::new(Algorithm::Aes128, Mode::Ecb, Padding::Pkcs7).unwrap();
enc.init(Direction::Encrypt, &key, &[]).unwrap();
let ct = {
let mut out = vec![0u8; 64];
let n = enc.update(&pt, &mut out).unwrap();
let m = enc.finalize(&mut out[n..]).unwrap();
out.truncate(n + m);
out
};
assert_eq!(ct, ct_ref, "Cipher PKCS#7 ECB encrypt mismatch");
let mut dec = Cipher::new(Algorithm::Aes128, Mode::Ecb, Padding::Pkcs7).unwrap();
dec.init(Direction::Decrypt, &key, &[]).unwrap();
let pt_back = {
let mut out = vec![0u8; 64];
let n = dec.update(&ct, &mut out).unwrap();
let m = dec.finalize(&mut out[n..]).unwrap();
out.truncate(n + m);
out
};
assert_eq!(pt_back, pt);
}
#[test]
fn cbc_aes128_pkcs7_matches_modes_helper() {
let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
let iv = hex("000102030405060708090a0b0c0d0e0f");
let pt: Vec<u8> = (0u8..37).collect();
let mut padded = pt.clone();
let pad = 16 - (padded.len() % 16);
padded.extend(std::iter::repeat(pad as u8).take(pad));
let cipher_ref = Aes128::new(&key);
cbc_encrypt(&cipher_ref, &iv, &mut padded);
let ct_ref = padded;
let mut enc = Cipher::new(Algorithm::Aes128, Mode::Cbc, Padding::Pkcs7).unwrap();
enc.init(Direction::Encrypt, &key, &iv).unwrap();
let mut ct = vec![0u8; 64];
let mut w = enc.update(&pt[..10], &mut ct).unwrap();
w += enc.update(&pt[10..], &mut ct[w..]).unwrap();
w += enc.finalize(&mut ct[w..]).unwrap();
ct.truncate(w);
assert_eq!(ct, ct_ref, "Cipher CBC PKCS#7 encrypt mismatch");
let mut ref_pt = ct.clone();
cbc_decrypt(&cipher_ref, &iv, &mut ref_pt);
let pad = *ref_pt.last().unwrap() as usize;
ref_pt.truncate(ref_pt.len() - pad);
assert_eq!(ref_pt, pt);
let mut dec = Cipher::new(Algorithm::Aes128, Mode::Cbc, Padding::Pkcs7).unwrap();
dec.init(Direction::Decrypt, &key, &iv).unwrap();
let mut got = vec![0u8; 64];
let mut n = dec.update(&ct[..7], &mut got).unwrap();
n += dec.update(&ct[7..], &mut got[n..]).unwrap();
n += dec.finalize(&mut got[n..]).unwrap();
got.truncate(n);
assert_eq!(got, pt);
}
#[test]
fn cbc_aes128_none_padding_aligned() {
let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
let iv = hex("000102030405060708090a0b0c0d0e0f");
let pt = hex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51");
let mut enc = Cipher::new(Algorithm::Aes128, Mode::Cbc, Padding::None).unwrap();
enc.init(Direction::Encrypt, &key, &iv).unwrap();
let mut ct = vec![0u8; 64];
let mut n = enc.update(&pt, &mut ct).unwrap();
n += enc.finalize(&mut ct[n..]).unwrap();
ct.truncate(n);
let cipher_ref = Aes128::new(&key);
let mut ct_ref = pt.clone();
cbc_encrypt(&cipher_ref, &iv, &mut ct_ref);
assert_eq!(ct, ct_ref);
}
#[test]
fn cbc_aes128_none_padding_unaligned_errors() {
let key = [0u8; 16];
let iv = [0u8; 16];
let mut enc = Cipher::new(Algorithm::Aes128, Mode::Cbc, Padding::None).unwrap();
enc.init(Direction::Encrypt, &key, &iv).unwrap();
let mut out = vec![0u8; 64];
enc.update(&[0u8; 13], &mut out).unwrap();
assert_eq!(enc.finalize(&mut out), Err(Error::UnpaddedInput));
}
#[test]
fn ctr_aes128_streaming_matches_one_shot() {
let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
let nonce = hex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"); let pt = hex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51\
30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710");
let cipher_ref = Aes128::new(&key);
let mut ct_ref = pt.clone();
let nonce8 = &nonce[..8];
ctr_encrypt(&cipher_ref, nonce8, &mut ct_ref);
let mut iv = [0u8; 16];
iv[..8].copy_from_slice(nonce8);
iv[15] = 1;
let mut enc = Cipher::new(Algorithm::Aes128, Mode::Ctr, Padding::None).unwrap();
enc.init(Direction::Encrypt, &key, &iv).unwrap();
let mut ct = vec![0u8; pt.len()];
let mut w = 0;
for chunk in pt.chunks(7) {
w += enc.update(chunk, &mut ct[w..]).unwrap();
}
w += enc.finalize(&mut ct[w..]).unwrap();
assert_eq!(w, pt.len());
assert_eq!(ct, ct_ref, "Cipher CTR streaming mismatch");
let mut dec = Cipher::new(Algorithm::Aes128, Mode::Ctr, Padding::None).unwrap();
dec.init(Direction::Decrypt, &key, &iv).unwrap();
let mut pt_back = vec![0u8; ct.len()];
let mut w = 0;
for chunk in ct.chunks(11) {
w += dec.update(chunk, &mut pt_back[w..]).unwrap();
}
w += dec.finalize(&mut pt_back[w..]).unwrap();
assert_eq!(w, ct.len());
assert_eq!(pt_back, pt);
}
fn pad_round_trip(padding: Padding, msg_len: usize) {
let key = [0x33u8; 16];
let iv = [0x77u8; 16];
let pt: Vec<u8> = (0..msg_len).map(|i| (i as u8).wrapping_mul(7)).collect();
let mut enc = Cipher::new(Algorithm::Aes128, Mode::Cbc, padding).unwrap();
enc.init(Direction::Encrypt, &key, &iv).unwrap();
let mut ct = vec![0u8; pt.len() + 16];
let mut n = enc.update(&pt, &mut ct).unwrap();
n += enc.finalize(&mut ct[n..]).unwrap();
ct.truncate(n);
let mut dec = Cipher::new(Algorithm::Aes128, Mode::Cbc, padding).unwrap();
dec.init(Direction::Decrypt, &key, &iv).unwrap();
let mut got = vec![0u8; ct.len() + 16];
let mut n = dec.update(&ct, &mut got).unwrap();
n += dec.finalize(&mut got[n..]).unwrap();
got.truncate(n);
assert_eq!(got, pt, "round-trip {:?} len={}", padding, msg_len);
}
#[test]
fn pkcs7_round_trips() {
for len in &[0, 1, 15, 16, 17, 31, 32, 33, 100] {
pad_round_trip(Padding::Pkcs7, *len);
}
}
#[test]
fn iso9797_m2_round_trips() {
for len in &[0, 1, 15, 16, 17, 31, 32, 33, 100] {
pad_round_trip(Padding::Iso9797M2, *len);
}
}
#[test]
fn ansix923_round_trips() {
for len in &[1, 15, 16, 17, 31, 32, 33, 100] {
pad_round_trip(Padding::AnsiX923, *len);
}
}
#[test]
fn iso9797_m1_round_trips_when_no_trailing_zero() {
let padding = Padding::Iso9797M1;
let key = [0u8; 16];
let iv = [0u8; 16];
for len in &[1usize, 5, 15, 17, 30] {
let pt: Vec<u8> = (0..*len).map(|i| (i as u8) | 1).collect();
let mut enc = Cipher::new(Algorithm::Aes128, Mode::Cbc, padding).unwrap();
enc.init(Direction::Encrypt, &key, &iv).unwrap();
let mut ct = vec![0u8; pt.len() + 16];
let mut n = enc.update(&pt, &mut ct).unwrap();
n += enc.finalize(&mut ct[n..]).unwrap();
ct.truncate(n);
let mut dec = Cipher::new(Algorithm::Aes128, Mode::Cbc, padding).unwrap();
dec.init(Direction::Decrypt, &key, &iv).unwrap();
let mut got = vec![0u8; ct.len() + 16];
let mut n = dec.update(&ct, &mut got).unwrap();
n += dec.finalize(&mut got[n..]).unwrap();
got.truncate(n);
assert_eq!(got, pt);
}
}
#[test]
fn invalid_key_len_rejected() {
let mut c = Cipher::new(Algorithm::Aes128, Mode::Cbc, Padding::Pkcs7).unwrap();
assert_eq!(
c.init(Direction::Encrypt, &[0u8; 17], &[0u8; 16]),
Err(Error::InvalidKeyLen)
);
}
#[test]
fn invalid_iv_len_rejected() {
let mut c = Cipher::new(Algorithm::Aes128, Mode::Cbc, Padding::Pkcs7).unwrap();
assert_eq!(
c.init(Direction::Encrypt, &[0u8; 16], &[0u8; 8]),
Err(Error::InvalidIvLen)
);
}
#[test]
fn ctr_with_padding_rejected() {
assert_eq!(
Cipher::new(Algorithm::Aes128, Mode::Ctr, Padding::Pkcs7).err(),
Some(Error::InvalidPaddingForMode)
);
}
#[test]
fn output_too_small_reported() {
let key = [0u8; 16];
let iv = [0u8; 16];
let mut enc = Cipher::new(Algorithm::Aes128, Mode::Cbc, Padding::Pkcs7).unwrap();
enc.init(Direction::Encrypt, &key, &iv).unwrap();
let mut tiny = [0u8; 4];
let err = enc.update(&[0u8; 32], &mut tiny);
assert!(matches!(err, Err(Error::OutputBufferTooSmall { .. })));
}
#[test]
fn bad_padding_detected() {
let key = [1u8; 16];
let iv = [2u8; 16];
let pt = b"hello, world! Some content here.";
let mut enc = Cipher::new(Algorithm::Aes128, Mode::Cbc, Padding::Pkcs7).unwrap();
enc.init(Direction::Encrypt, &key, &iv).unwrap();
let mut ct = vec![0u8; 64];
let mut n = enc.update(pt, &mut ct).unwrap();
n += enc.finalize(&mut ct[n..]).unwrap();
ct.truncate(n);
let last = ct.len() - 1;
ct[last] ^= 0xFF;
let mut dec = Cipher::new(Algorithm::Aes128, Mode::Cbc, Padding::Pkcs7).unwrap();
dec.init(Direction::Decrypt, &key, &iv).unwrap();
let mut out = vec![0u8; 64];
let n = dec.update(&ct, &mut out).unwrap();
let r = dec.finalize(&mut out[n..]);
assert_eq!(r, Err(Error::BadPadding));
}
#[test]
fn tripledes_cbc_pkcs7_round_trip() {
let key = [0x11u8; 24];
let iv = [0x22u8; 8];
let pt: Vec<u8> = (0u8..23).collect();
let mut enc = Cipher::new(Algorithm::TripleDes, Mode::Cbc, Padding::Pkcs7).unwrap();
enc.init(Direction::Encrypt, &key, &iv).unwrap();
let mut ct = vec![0u8; 64];
let mut n = enc.update(&pt, &mut ct).unwrap();
n += enc.finalize(&mut ct[n..]).unwrap();
ct.truncate(n);
assert_eq!(ct.len() % 8, 0);
let mut dec = Cipher::new(Algorithm::TripleDes, Mode::Cbc, Padding::Pkcs7).unwrap();
dec.init(Direction::Decrypt, &key, &iv).unwrap();
let mut got = vec![0u8; 64];
let mut n = dec.update(&ct, &mut got).unwrap();
n += dec.finalize(&mut got[n..]).unwrap();
got.truncate(n);
assert_eq!(got, pt);
}
#[test]
fn reuse_after_init() {
let key = [9u8; 16];
let iv = [8u8; 16];
let mut c = Cipher::new(Algorithm::Aes128, Mode::Cbc, Padding::Pkcs7).unwrap();
for &msg in &[b"first message".as_slice(), b"second", b"a third one!"] {
c.init(Direction::Encrypt, &key, &iv).unwrap();
let ct = {
let mut out = vec![0u8; 64];
let mut n = c.update(msg, &mut out).unwrap();
n += c.finalize(&mut out[n..]).unwrap();
out.truncate(n);
out
};
c.init(Direction::Decrypt, &key, &iv).unwrap();
let pt = {
let mut out = vec![0u8; 64];
let mut n = c.update(&ct, &mut out).unwrap();
n += c.finalize(&mut out[n..]).unwrap();
out.truncate(n);
out
};
assert_eq!(pt, msg);
}
}
}