blob: 29e2a510b53beb90fa78d6880e30bfa6c4d9289e [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// TODO - Optimize the XORs
// TODO - Maybe use macros to specialize BlockEngine for encryption or decryption?
// TODO - I think padding could be done better. Maybe macros for BlockEngine would help this too.
use std::prelude::v1::*;
use std::cmp;
use std::iter::repeat;
use buffer::{ReadBuffer, WriteBuffer, OwnedReadBuffer, OwnedWriteBuffer, BufferResult,
RefReadBuffer, RefWriteBuffer};
use buffer::BufferResult::{BufferUnderflow, BufferOverflow};
use cryptoutil::{self, symm_enc_or_dec};
use symmetriccipher::{BlockEncryptor, BlockEncryptorX8, Encryptor, BlockDecryptor, Decryptor,
SynchronousStreamCipher, SymmetricCipherError};
use symmetriccipher::SymmetricCipherError::{InvalidPadding, InvalidLength};
/// The BlockProcessor trait is used to implement modes that require processing complete blocks of
/// data. The methods of this trait are called by the BlockEngine which is in charge of properly
/// buffering input data.
trait BlockProcessor {
/// Process a block of data. The in_hist and out_hist parameters represent the input and output
/// when the last block was processed. These values are necessary for certain modes.
fn process_block(&mut self, in_hist: &[u8], out_hist: &[u8], input: &[u8], output: &mut [u8]);
}
/// A PaddingProcessor handles adding or removing padding
pub trait PaddingProcessor {
/// Add padding to the last block of input data
/// If the mode can't handle a non-full block, it signals that error by simply leaving the block
/// as it is which will be detected as an InvalidLength error.
fn pad_input<W: WriteBuffer>(&mut self, input_buffer: &mut W);
/// Remove padding from the last block of output data
/// If false is returned, the processing fails
fn strip_output<R: ReadBuffer>(&mut self, output_buffer: &mut R) -> bool;
}
/// The BlockEngine is implemented as a state machine with the following states. See comments in the
/// BlockEngine code for more information on the states.
#[derive(Clone, Copy)]
enum BlockEngineState {
FastMode,
NeedInput,
NeedOutput,
LastInput,
LastInput2,
Finished,
Error(SymmetricCipherError)
}
/// BlockEngine buffers input and output data and handles sending complete block of data to the
/// Processor object. Additionally, BlockEngine handles logic necessary to add or remove padding by
/// calling the appropriate methods on the Processor object.
struct BlockEngine<P, X> {
/// The block sized expected by the Processor
block_size: usize,
/// in_hist and out_hist keep track of data that was input to and output from the last
/// invocation of the process_block() method of the Processor. Depending on the mode, these may
/// be empty vectors if history is not needed.
in_hist: Vec<u8>,
out_hist: Vec<u8>,
/// If some input data is supplied, but not a complete blocks worth, it is stored in this buffer
/// until enough arrives that it can be passed to the process_block() method of the Processor.
in_scratch: OwnedWriteBuffer,
/// If input data is processed but there isn't enough space in the output buffer to store it,
/// it is written into out_write_scratch. OwnedWriteBuffer's may be converted into
/// OwnedReaderBuffers without re-allocating, so, after being written, out_write_scratch is
/// turned into out_read_scratch. After that, if is written to the output as more output becomes
/// available. The main point is - only out_write_scratch or out_read_scratch contains a value
/// at any given time; never both.
out_write_scratch: Option<OwnedWriteBuffer>,
out_read_scratch: Option<OwnedReadBuffer>,
/// The processor that implements the particular block mode.
processor: P,
/// The padding processor
padding: X,
/// The current state of the operation.
state: BlockEngineState
}
fn update_history(in_hist: &mut [u8], out_hist: &mut [u8], last_in: &[u8], last_out: &[u8]) {
let in_hist_len = in_hist.len();
if in_hist_len > 0 {
cryptoutil::copy_memory(
&last_in[last_in.len() - in_hist_len..],
in_hist);
}
let out_hist_len = out_hist.len();
if out_hist_len > 0 {
cryptoutil::copy_memory(
&last_out[last_out.len() - out_hist_len..],
out_hist);
}
}
impl <P: BlockProcessor, X: PaddingProcessor> BlockEngine<P, X> {
/// Create a new BlockProcessor instance with the given processor and block_size. No history
/// will be saved.
fn new(processor: P, padding: X, block_size: usize) -> BlockEngine<P, X> {
BlockEngine {
block_size: block_size,
in_hist: Vec::new(),
out_hist: Vec::new(),
in_scratch: OwnedWriteBuffer::new(repeat(0).take(block_size).collect()),
out_write_scratch: Some(OwnedWriteBuffer::new(repeat(0).take(block_size).collect())),
out_read_scratch: None,
processor: processor,
padding: padding,
state: BlockEngineState::FastMode
}
}
/// Create a new BlockProcessor instance with the given processor, block_size, and initial input
/// and output history.
fn new_with_history(
processor: P,
padding: X,
block_size: usize,
in_hist: Vec<u8>,
out_hist: Vec<u8>) -> BlockEngine<P, X> {
BlockEngine {
in_hist: in_hist,
out_hist: out_hist,
..BlockEngine::new(processor, padding, block_size)
}
}
/// This implements the FastMode state. Ideally, the encryption or decryption operation should
/// do the bulk of its work in FastMode. Significantly, FastMode avoids doing copies as much as
/// possible. The FastMode state does not handle the final block of data.
fn fast_mode<R: ReadBuffer, W: WriteBuffer>(
&mut self,
input: &mut R,
output: &mut W) -> BlockEngineState {
fn has_next<R: ReadBuffer, W: WriteBuffer>(
input: &mut R,
output: &mut W,
block_size: usize) -> bool {
// Not the greater than - very important since this method must never process the last
// block.
let enough_input = input.remaining() > block_size;
let enough_output = output.remaining() >= block_size;
enough_input && enough_output
};
fn split_at<'a>(vec: &'a [u8], at: usize) -> (&'a [u8], &'a [u8]) {
(&vec[..at], &vec[at..])
}
// First block processing. We have to retrieve the history information from self.in_hist and
// self.out_hist.
if !has_next(input, output, self.block_size) {
if input.is_empty() {
return BlockEngineState::FastMode;
} else {
return BlockEngineState::NeedInput;
}
} else {
let next_in = input.take_next(self.block_size);
let next_out = output.take_next(self.block_size);
self.processor.process_block(
&self.in_hist[..],
&self.out_hist[..],
next_in,
next_out);
}
// Process all remaing blocks. We can pull the history out of the buffers without having to
// do any copies
let next_in_size = self.in_hist.len() + self.block_size;
let next_out_size = self.out_hist.len() + self.block_size;
while has_next(input, output, self.block_size) {
input.rewind(self.in_hist.len());
let (in_hist, next_in) = split_at(input.take_next(next_in_size), self.in_hist.len());
output.rewind(self.out_hist.len());
let (out_hist, next_out) = output.take_next(next_out_size).split_at_mut(
self.out_hist.len());
self.processor.process_block(
in_hist,
out_hist,
next_in,
next_out);
}
// Save the history and then transition to the next state
{
input.rewind(self.in_hist.len());
let last_in = input.take_next(self.in_hist.len());
output.rewind(self.out_hist.len());
let last_out = output.take_next(self.out_hist.len());
update_history(
&mut self.in_hist,
&mut self.out_hist,
last_in,
last_out);
}
if input.is_empty() {
BlockEngineState::FastMode
} else {
BlockEngineState::NeedInput
}
}
/// This method implements the BlockEngine state machine.
fn process<R: ReadBuffer, W: WriteBuffer>(
&mut self,
input: &mut R,
output: &mut W,
eof: bool) -> Result<BufferResult, SymmetricCipherError> {
// Process a block of data from in_scratch and write the result to out_write_scratch.
// Finally, convert out_write_scratch into out_read_scratch.
fn process_scratch<P: BlockProcessor, X: PaddingProcessor>(me: &mut BlockEngine<P, X>) {
let mut rin = me.in_scratch.take_read_buffer();
let mut wout = me.out_write_scratch.take().unwrap();
{
let next_in = rin.take_remaining();
let next_out = wout.take_remaining();
me.processor.process_block(
&me.in_hist[..],
&me.out_hist[..],
next_in,
next_out);
update_history(
&mut me.in_hist,
&mut me.out_hist,
next_in,
next_out);
}
let rb = wout.into_read_buffer();
me.out_read_scratch = Some(rb);
};
loop {
match self.state {
// FastMode tries to process as much data as possible while minimizing copies.
// FastMode doesn't make use of the scratch buffers and only updates the history
// just before exiting.
BlockEngineState::FastMode => {
self.state = self.fast_mode(input, output);
match self.state {
BlockEngineState::FastMode => {
// If FastMode completes but stays in the FastMode state, it means that
// we've run out of input data.
return Ok(BufferUnderflow);
}
_ => {}
}
}
// The NeedInput mode is entered when there isn't enough data to run in FastMode
// anymore. Input data is buffered in in_scratch until there is a full block or eof
// occurs. IF eof doesn't occur, the data is processed and then we go to the
// NeedOutput state. Otherwise, we go to the LastInput state. This state always
// writes all available data into in_scratch before transitioning to the next state.
BlockEngineState::NeedInput => {
input.push_to(&mut self.in_scratch);
if !input.is_empty() {
// !is_empty() guarantees two things - in_scratch is full and its not the
// last block. This state must never process the last block.
process_scratch(self);
self.state = BlockEngineState::NeedOutput;
} else {
if eof {
self.state = BlockEngineState::LastInput;
} else {
return Ok(BufferUnderflow);
}
}
}
// The NeedOutput state just writes buffered processed data to the output stream
// until all of it has been written.
BlockEngineState::NeedOutput => {
let mut rout = self.out_read_scratch.take().unwrap();
rout.push_to(output);
if rout.is_empty() {
self.out_write_scratch = Some(rout.into_write_buffer());
self.state = BlockEngineState::FastMode;
} else {
self.out_read_scratch = Some(rout);
return Ok(BufferOverflow);
}
}
// None of the other states are allowed to process the last block of data since
// last block handling is a little tricky due to modes have special needs regarding
// padding. When the last block of data is detected, this state is transitioned to
// for handling.
BlockEngineState::LastInput => {
// We we arrive in this state, we know that all input data that is going to be
// supplied has been suplied and that that data has been written to in_scratch
// by the NeedInput state. Furthermore, we know that one of three things must be
// true about in_scratch:
// 1) It is empty. This only occurs if the input is zero length. We can do last
// block processing by executing the pad_input() method of the processor
// which may either pad out to a full block or leave it empty, process the
// data if it was padded out to a full block, and then pass it to
// strip_output().
// 2) It is partially filled. This will occur if the input data was not a
// multiple of the block size. Processing proceeds identically to case #1.
// 3) It is full. This case occurs when the input data was a multiple of the
// block size. This case is a little trickier, since, depending on the mode,
// we might actually have 2 blocks worth of data to process - the last user
// supplied block (currently in in_scratch) and then another block that could
// be added as padding. Processing proceeds by first processing the data in
// in_scratch and writing it to out_scratch. Then, the now-empty in_scratch
// buffer is passed to pad_input() which may leave it empty or write a block
// of padding to it. If no padding is added, processing proceeds as in cases
// #1 and #2. However, if padding is added, now have data in in_scratch and
// also in out_scratch meaning that we can't immediately process the padding
// data since we have nowhere to put it. So, we transition to the LastInput2
// state which will first write out the last non-padding block, then process
// the padding block (in in_scratch) and write it to the now-empty
// out_scratch.
if !self.in_scratch.is_full() {
self.padding.pad_input(&mut self.in_scratch);
if self.in_scratch.is_full() {
process_scratch(self);
if self.padding.strip_output(self.out_read_scratch.as_mut().unwrap()) {
self.state = BlockEngineState::Finished;
} else {
self.state = BlockEngineState::Error(InvalidPadding);
}
} else if self.in_scratch.is_empty() {
self.state = BlockEngineState::Finished;
} else {
self.state = BlockEngineState::Error(InvalidLength);
}
} else {
process_scratch(self);
self.padding.pad_input(&mut self.in_scratch);
if self.in_scratch.is_full() {
self.state = BlockEngineState::LastInput2;
} else if self.in_scratch.is_empty() {
if self.padding.strip_output(self.out_read_scratch.as_mut().unwrap()) {
self.state = BlockEngineState::Finished;
} else {
self.state = BlockEngineState::Error(InvalidPadding);
}
} else {
self.state = BlockEngineState::Error(InvalidLength);
}
}
}
// See the comments on LastInput for more details. This state handles final blocks
// of data in the case that the input was a multiple of the block size and the mode
// decided to add a full extra block of padding.
BlockEngineState::LastInput2 => {
let mut rout = self.out_read_scratch.take().unwrap();
rout.push_to(output);
if rout.is_empty() {
self.out_write_scratch = Some(rout.into_write_buffer());
process_scratch(self);
if self.padding.strip_output(self.out_read_scratch.as_mut().unwrap()) {
self.state = BlockEngineState::Finished;
} else {
self.state = BlockEngineState::Error(InvalidPadding);
}
} else {
self.out_read_scratch = Some(rout);
return Ok(BufferOverflow);
}
}
// The Finished mode just writes the data in out_scratch to the output until there
// is no more data left.
BlockEngineState::Finished => {
match self.out_read_scratch {
Some(ref mut rout) => {
rout.push_to(output);
if rout.is_empty() {
return Ok(BufferUnderflow);
} else {
return Ok(BufferOverflow);
}
}
None => { return Ok(BufferUnderflow); }
}
}
// The Error state is used to store error information.
BlockEngineState::Error(err) => {
return Err(err);
}
}
}
}
fn reset(&mut self) {
self.state = BlockEngineState::FastMode;
self.in_scratch.reset();
if self.out_read_scratch.is_some() {
let ors = self.out_read_scratch.take().unwrap();
let ows = ors.into_write_buffer();
self.out_write_scratch = Some(ows);
} else {
self.out_write_scratch.as_mut().unwrap().reset();
}
}
fn reset_with_history(&mut self, in_hist: &[u8], out_hist: &[u8]) {
self.reset();
cryptoutil::copy_memory(in_hist, &mut self.in_hist);
cryptoutil::copy_memory(out_hist, &mut self.out_hist);
}
}
/// No padding mode for ECB and CBC encryption
#[derive(Clone, Copy)]
pub struct NoPadding;
impl PaddingProcessor for NoPadding {
fn pad_input<W: WriteBuffer>(&mut self, _: &mut W) { }
fn strip_output<R: ReadBuffer>(&mut self, _: &mut R) -> bool { true }
}
/// PKCS padding mode for ECB and CBC encryption
#[derive(Clone, Copy)]
pub struct PkcsPadding;
// This class implements both encryption padding, where padding is added, and decryption padding,
// where padding is stripped. Since BlockEngine doesn't know if its an Encryption or Decryption
// operation, it will call both methods if given a chance. So, this class can't be passed directly
// to BlockEngine. Instead, it must be wrapped with EncPadding or DecPadding which will ensure that
// only the propper methods are called. The client of the library, however, doesn't have to
// distinguish encryption padding handling from decryption padding handline, which is the whole
// point.
impl PaddingProcessor for PkcsPadding {
fn pad_input<W: WriteBuffer>(&mut self, input_buffer: &mut W) {
let rem = input_buffer.remaining();
assert!(rem != 0 && rem <= 255);
for v in input_buffer.take_remaining().iter_mut() {
*v = rem as u8;
}
}
fn strip_output<R: ReadBuffer>(&mut self, output_buffer: &mut R) -> bool {
let last_byte: u8;
{
let data = output_buffer.peek_remaining();
last_byte = *data.last().unwrap();
for &x in data.iter().rev().take(last_byte as usize) {
if x != last_byte {
return false;
}
}
}
output_buffer.truncate(last_byte as usize);
true
}
}
/// Wraps a PaddingProcessor so that only pad_input() will actually be called.
pub struct EncPadding<X> {
padding: X
}
impl <X: PaddingProcessor> EncPadding<X> {
fn wrap(p: X) -> EncPadding<X> { EncPadding { padding: p } }
}
impl <X: PaddingProcessor> PaddingProcessor for EncPadding<X> {
fn pad_input<W: WriteBuffer>(&mut self, a: &mut W) { self.padding.pad_input(a); }
fn strip_output<R: ReadBuffer>(&mut self, _: &mut R) -> bool { true }
}
/// Wraps a PaddingProcessor so that only strip_output() will actually be called.
pub struct DecPadding<X> {
padding: X
}
impl <X: PaddingProcessor> DecPadding<X> {
fn wrap(p: X) -> DecPadding<X> { DecPadding { padding: p } }
}
impl <X: PaddingProcessor> PaddingProcessor for DecPadding<X> {
fn pad_input<W: WriteBuffer>(&mut self, _: &mut W) { }
fn strip_output<R: ReadBuffer>(&mut self, a: &mut R) -> bool { self.padding.strip_output(a) }
}
struct EcbEncryptorProcessor<T> {
algo: T
}
impl <T: BlockEncryptor> BlockProcessor for EcbEncryptorProcessor<T> {
fn process_block(&mut self, _: &[u8], _: &[u8], input: &[u8], output: &mut [u8]) {
self.algo.encrypt_block(input, output);
}
}
/// ECB Encryption mode
pub struct EcbEncryptor<T, X> {
block_engine: BlockEngine<EcbEncryptorProcessor<T>, X>
}
impl <T: BlockEncryptor, X: PaddingProcessor> EcbEncryptor<T, X> {
/// Create a new ECB encryption mode object
pub fn new(algo: T, padding: X) -> EcbEncryptor<T, EncPadding<X>> {
let block_size = algo.block_size();
let processor = EcbEncryptorProcessor {
algo: algo
};
EcbEncryptor {
block_engine: BlockEngine::new(processor, EncPadding::wrap(padding), block_size)
}
}
pub fn reset(&mut self) {
self.block_engine.reset();
}
}
impl <T: BlockEncryptor, X: PaddingProcessor> Encryptor for EcbEncryptor<T, X> {
fn encrypt(&mut self, input: &mut RefReadBuffer, output: &mut RefWriteBuffer, eof: bool)
-> Result<BufferResult, SymmetricCipherError> {
self.block_engine.process(input, output, eof)
}
}
struct EcbDecryptorProcessor<T> {
algo: T
}
impl <T: BlockDecryptor> BlockProcessor for EcbDecryptorProcessor<T> {
fn process_block(&mut self, _: &[u8], _: &[u8], input: &[u8], output: &mut [u8]) {
self.algo.decrypt_block(input, output);
}
}
/// ECB Decryption mode
pub struct EcbDecryptor<T, X> {
block_engine: BlockEngine<EcbDecryptorProcessor<T>, X>
}
impl <T: BlockDecryptor, X: PaddingProcessor> EcbDecryptor<T, X> {
/// Create a new ECB decryption mode object
pub fn new(algo: T, padding: X) -> EcbDecryptor<T, DecPadding<X>> {
let block_size = algo.block_size();
let processor = EcbDecryptorProcessor {
algo: algo
};
EcbDecryptor {
block_engine: BlockEngine::new(processor, DecPadding::wrap(padding), block_size)
}
}
pub fn reset(&mut self) {
self.block_engine.reset();
}
}
impl <T: BlockDecryptor, X: PaddingProcessor> Decryptor for EcbDecryptor<T, X> {
fn decrypt(&mut self, input: &mut RefReadBuffer, output: &mut RefWriteBuffer, eof: bool)
-> Result<BufferResult, SymmetricCipherError> {
self.block_engine.process(input, output, eof)
}
}
struct CbcEncryptorProcessor<T> {
algo: T,
temp: Vec<u8>
}
impl <T: BlockEncryptor> BlockProcessor for CbcEncryptorProcessor<T> {
fn process_block(&mut self, _: &[u8], out_hist: &[u8], input: &[u8], output: &mut [u8]) {
for ((&x, &y), o) in input.iter().zip(out_hist.iter()).zip(self.temp.iter_mut()) {
*o = x ^ y;
}
self.algo.encrypt_block(&self.temp[..], output);
}
}
/// CBC encryption mode
pub struct CbcEncryptor<T, X> {
block_engine: BlockEngine<CbcEncryptorProcessor<T>, X>
}
impl <T: BlockEncryptor, X: PaddingProcessor> CbcEncryptor<T, X> {
/// Create a new CBC encryption mode object
pub fn new(algo: T, padding: X, iv: Vec<u8>) -> CbcEncryptor<T, EncPadding<X>> {
let block_size = algo.block_size();
let processor = CbcEncryptorProcessor {
algo: algo,
temp: repeat(0).take(block_size).collect()
};
CbcEncryptor {
block_engine: BlockEngine::new_with_history(
processor,
EncPadding::wrap(padding),
block_size,
Vec::new(),
iv)
}
}
pub fn reset(&mut self, iv: &[u8]) {
self.block_engine.reset_with_history(&[], iv);
}
}
impl <T: BlockEncryptor, X: PaddingProcessor> Encryptor for CbcEncryptor<T, X> {
fn encrypt(&mut self, input: &mut RefReadBuffer, output: &mut RefWriteBuffer, eof: bool)
-> Result<BufferResult, SymmetricCipherError> {
self.block_engine.process(input, output, eof)
}
}
struct CbcDecryptorProcessor<T> {
algo: T,
temp: Vec<u8>
}
impl <T: BlockDecryptor> BlockProcessor for CbcDecryptorProcessor<T> {
fn process_block(&mut self, in_hist: &[u8], _: &[u8], input: &[u8], output: &mut [u8]) {
self.algo.decrypt_block(input, &mut self.temp);
for ((&x, &y), o) in self.temp.iter().zip(in_hist.iter()).zip(output.iter_mut()) {
*o = x ^ y;
}
}
}
/// CBC decryption mode
pub struct CbcDecryptor<T, X> {
block_engine: BlockEngine<CbcDecryptorProcessor<T>, X>
}
impl <T: BlockDecryptor, X: PaddingProcessor> CbcDecryptor<T, X> {
/// Create a new CBC decryption mode object
pub fn new(algo: T, padding: X, iv: Vec<u8>) -> CbcDecryptor<T, DecPadding<X>> {
let block_size = algo.block_size();
let processor = CbcDecryptorProcessor {
algo: algo,
temp: repeat(0).take(block_size).collect()
};
CbcDecryptor {
block_engine: BlockEngine::new_with_history(
processor,
DecPadding::wrap(padding),
block_size,
iv,
Vec::new())
}
}
pub fn reset(&mut self, iv: &[u8]) {
self.block_engine.reset_with_history(iv, &[]);
}
}
impl <T: BlockDecryptor, X: PaddingProcessor> Decryptor for CbcDecryptor<T, X> {
fn decrypt(&mut self, input: &mut RefReadBuffer, output: &mut RefWriteBuffer, eof: bool)
-> Result<BufferResult, SymmetricCipherError> {
self.block_engine.process(input, output, eof)
}
}
fn add_ctr(ctr: &mut [u8], mut ammount: u8) {
for i in ctr.iter_mut().rev() {
let prev = *i;
*i = i.wrapping_add(ammount);
if *i >= prev {
break;
}
ammount = 1;
}
}
/// CTR Mode
pub struct CtrMode<A> {
algo: A,
ctr: Vec<u8>,
bytes: OwnedReadBuffer
}
impl <A: BlockEncryptor> CtrMode<A> {
/// Create a new CTR object
pub fn new(algo: A, ctr: Vec<u8>) -> CtrMode<A> {
let block_size = algo.block_size();
CtrMode {
algo: algo,
ctr: ctr,
bytes: OwnedReadBuffer::new_with_len(repeat(0).take(block_size).collect(), 0)
}
}
pub fn reset(&mut self, ctr: &[u8]) {
cryptoutil::copy_memory(ctr, &mut self.ctr);
self.bytes.reset();
}
fn process(&mut self, input: &[u8], output: &mut [u8]) {
assert!(input.len() == output.len());
let len = input.len();
let mut i = 0;
while i < len {
if self.bytes.is_empty() {
let mut wb = self.bytes.borrow_write_buffer();
self.algo.encrypt_block(&self.ctr[..], wb.take_remaining());
add_ctr(&mut self.ctr, 1);
}
let count = cmp::min(self.bytes.remaining(), len - i);
let bytes_it = self.bytes.take_next(count).iter();
let in_it = input[i..].iter();
let out_it = output[i..].iter_mut();
for ((&x, &y), o) in bytes_it.zip(in_it).zip(out_it) {
*o = x ^ y;
}
i += count;
}
}
}
impl <A: BlockEncryptor> SynchronousStreamCipher for CtrMode<A> {
fn process(&mut self, input: &[u8], output: &mut [u8]) {
self.process(input, output);
}
}
impl <A: BlockEncryptor> Encryptor for CtrMode<A> {
fn encrypt(&mut self, input: &mut RefReadBuffer, output: &mut RefWriteBuffer, _: bool)
-> Result<BufferResult, SymmetricCipherError> {
symm_enc_or_dec(self, input, output)
}
}
impl <A: BlockEncryptor> Decryptor for CtrMode<A> {
fn decrypt(&mut self, input: &mut RefReadBuffer, output: &mut RefWriteBuffer, _: bool)
-> Result<BufferResult, SymmetricCipherError> {
symm_enc_or_dec(self, input, output)
}
}
/// CTR Mode that operates on 8 blocks at a time
pub struct CtrModeX8<A> {
algo: A,
ctr_x8: Vec<u8>,
bytes: OwnedReadBuffer
}
fn construct_ctr_x8(in_ctr: &[u8], out_ctr_x8: &mut [u8]) {
for (i, ctr_i) in out_ctr_x8.chunks_mut(in_ctr.len()).enumerate() {
cryptoutil::copy_memory(in_ctr, ctr_i);
add_ctr(ctr_i, i as u8);
}
}
impl <A: BlockEncryptorX8> CtrModeX8<A> {
/// Create a new CTR object that operates on 8 blocks at a time
pub fn new(algo: A, ctr: &[u8]) -> CtrModeX8<A> {
let block_size = algo.block_size();
let mut ctr_x8: Vec<u8> = repeat(0).take(block_size * 8).collect();
construct_ctr_x8(ctr, &mut ctr_x8);
CtrModeX8 {
algo: algo,
ctr_x8: ctr_x8,
bytes: OwnedReadBuffer::new_with_len(repeat(0).take(block_size * 8).collect(), 0)
}
}
pub fn reset(&mut self, ctr: &[u8]) {
construct_ctr_x8(ctr, &mut self.ctr_x8);
self.bytes.reset();
}
fn process(&mut self, input: &[u8], output: &mut [u8]) {
// TODO - Can some of this be combined with regular CtrMode?
assert!(input.len() == output.len());
let len = input.len();
let mut i = 0;
while i < len {
if self.bytes.is_empty() {
let mut wb = self.bytes.borrow_write_buffer();
self.algo.encrypt_block_x8(&self.ctr_x8[..], wb.take_remaining());
for ctr_i in &mut self.ctr_x8.chunks_mut(self.algo.block_size()) {
add_ctr(ctr_i, 8);
}
}
let count = cmp::min(self.bytes.remaining(), len - i);
let bytes_it = self.bytes.take_next(count).iter();
let in_it = input[i..].iter();
let out_it = &mut output[i..];
for ((&x, &y), o) in bytes_it.zip(in_it).zip(out_it.iter_mut()) {
*o = x ^ y;
}
i += count;
}
}
}
impl <A: BlockEncryptorX8> SynchronousStreamCipher for CtrModeX8<A> {
fn process(&mut self, input: &[u8], output: &mut [u8]) {
self.process(input, output);
}
}
impl <A: BlockEncryptorX8> Encryptor for CtrModeX8<A> {
fn encrypt(&mut self, input: &mut RefReadBuffer, output: &mut RefWriteBuffer, _: bool)
-> Result<BufferResult, SymmetricCipherError> {
symm_enc_or_dec(self, input, output)
}
}
impl <A: BlockEncryptorX8> Decryptor for CtrModeX8<A> {
fn decrypt(&mut self, input: &mut RefReadBuffer, output: &mut RefWriteBuffer, _: bool)
-> Result<BufferResult, SymmetricCipherError> {
symm_enc_or_dec(self, input, output)
}
}
#[cfg(test)]
mod test {
use std::iter::repeat;
use aessafe;
use blockmodes::{EcbEncryptor, EcbDecryptor, CbcEncryptor, CbcDecryptor, CtrMode, CtrModeX8,
NoPadding, PkcsPadding};
use buffer::{ReadBuffer, WriteBuffer, RefReadBuffer, RefWriteBuffer, BufferResult};
use buffer::BufferResult::{BufferUnderflow, BufferOverflow};
use symmetriccipher::{Encryptor, Decryptor};
use symmetriccipher::SymmetricCipherError::{self, InvalidLength, InvalidPadding};
use std::cmp;
trait CipherTest {
fn get_plain<'a>(&'a self) -> &'a [u8];
fn get_cipher<'a>(&'a self) -> &'a [u8];
}
struct EcbTest {
key: Vec<u8>,
plain: Vec<u8>,
cipher: Vec<u8>
}
impl CipherTest for EcbTest {
fn get_plain<'a>(&'a self) -> &'a [u8] {
&self.plain[..]
}
fn get_cipher<'a>(&'a self) -> &'a [u8] {
&self.cipher[..]
}
}
struct CbcTest {
key: Vec<u8>,
iv: Vec<u8>,
plain: Vec<u8>,
cipher: Vec<u8>
}
impl CipherTest for CbcTest {
fn get_plain<'a>(&'a self) -> &'a [u8] {
&self.plain[..]
}
fn get_cipher<'a>(&'a self) -> &'a [u8] {
&self.cipher[..]
}
}
struct CtrTest {
key: Vec<u8>,
ctr: Vec<u8>,
plain: Vec<u8>,
cipher: Vec<u8>
}
impl CipherTest for CtrTest {
fn get_plain<'a>(&'a self) -> &'a [u8] {
&self.plain[..]
}
fn get_cipher<'a>(&'a self) -> &'a [u8] {
&self.cipher[..]
}
}
fn aes_ecb_no_padding_tests() -> Vec<EcbTest> {
vec![
EcbTest {
key: repeat(0).take(16).collect(),
plain: repeat(0).take(32).collect(),
cipher: vec![
0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b,
0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34, 0x2b, 0x2e,
0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b,
0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34, 0x2b, 0x2e ]
}
]
}
fn aes_ecb_pkcs_padding_tests() -> Vec<EcbTest> {
vec![
EcbTest {
key: repeat(0).take(16).collect(),
plain: repeat(0).take(32).collect(),
cipher: vec![
0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b,
0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34, 0x2b, 0x2e,
0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b,
0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34, 0x2b, 0x2e,
0x01, 0x43, 0xdb, 0x63, 0xee, 0x66, 0xb0, 0xcd,
0xff, 0x9f, 0x69, 0x91, 0x76, 0x80, 0x15, 0x1e ]
},
EcbTest {
key: repeat(0).take(16).collect(),
plain: repeat(0).take(33).collect(),
cipher: vec![
0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b,
0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34, 0x2b, 0x2e,
0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b,
0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34, 0x2b, 0x2e,
0x7a, 0xdc, 0x99, 0xb2, 0x9e, 0x82, 0xb1, 0xb2,
0xb0, 0xa6, 0x5a, 0x38, 0xbc, 0x57, 0x8a, 0x01 ]
}
]
}
fn aes_cbc_no_padding_tests() -> Vec<CbcTest> {
vec![
CbcTest {
key: repeat(1).take(16).collect(),
iv: repeat(3).take(16).collect(),
plain: repeat(2).take(32).collect(),
cipher: vec![
0x5e, 0x77, 0xe5, 0x9f, 0x8f, 0x85, 0x94, 0x34,
0x89, 0xa2, 0x41, 0x49, 0xc7, 0x5f, 0x4e, 0xc9,
0xe0, 0x9a, 0x77, 0x36, 0xfb, 0xc8, 0xb2, 0xdc,
0xb3, 0xfb, 0x9f, 0xc0, 0x31, 0x4c, 0xb0, 0xb1 ]
}
]
}
fn aes_cbc_pkcs_padding_tests() -> Vec<CbcTest> {
vec![
CbcTest {
key: repeat(1).take(16).collect(),
iv: repeat(3).take(16).collect(),
plain: repeat(2).take(32).collect(),
cipher: vec![
0x5e, 0x77, 0xe5, 0x9f, 0x8f, 0x85, 0x94, 0x34,
0x89, 0xa2, 0x41, 0x49, 0xc7, 0x5f, 0x4e, 0xc9,
0xe0, 0x9a, 0x77, 0x36, 0xfb, 0xc8, 0xb2, 0xdc,
0xb3, 0xfb, 0x9f, 0xc0, 0x31, 0x4c, 0xb0, 0xb1,
0xa4, 0xc2, 0xe4, 0x62, 0xef, 0x7a, 0xe3, 0x7e,
0xef, 0x88, 0xf3, 0x27, 0xbd, 0x9c, 0xc8, 0x4d ]
},
CbcTest {
key: repeat(1).take(16).collect(),
iv: repeat(3).take(16).collect(),
plain: repeat(2).take(33).collect(),
cipher: vec![
0x5e, 0x77, 0xe5, 0x9f, 0x8f, 0x85, 0x94, 0x34,
0x89, 0xa2, 0x41, 0x49, 0xc7, 0x5f, 0x4e, 0xc9,
0xe0, 0x9a, 0x77, 0x36, 0xfb, 0xc8, 0xb2, 0xdc,
0xb3, 0xfb, 0x9f, 0xc0, 0x31, 0x4c, 0xb0, 0xb1,
0x6c, 0x47, 0xcd, 0xec, 0xae, 0xbb, 0x1a, 0x65,
0x04, 0xd2, 0x32, 0x23, 0xa6, 0x8d, 0x4a, 0x65 ]
}
]
}
fn aes_ctr_tests() -> Vec<CtrTest> {
vec![
CtrTest {
key: repeat(1).take(16).collect(),
ctr: repeat(3).take(16).collect(),
plain: repeat(2).take(33).collect(),
cipher: vec![
0x64, 0x3e, 0x05, 0x19, 0x79, 0x78, 0xd7, 0x45,
0xa9, 0x10, 0x5f, 0xd8, 0x4c, 0xd7, 0xe6, 0xb1,
0x5f, 0x66, 0xc6, 0x17, 0x4b, 0x25, 0xea, 0x24,
0xe6, 0xf9, 0x19, 0x09, 0xb7, 0xdd, 0x84, 0xfb,
0x86 ]
}
]
}
// Test the mode by encrypting all of the data at once
fn run_full_test<T: CipherTest, E: Encryptor, D: Decryptor>(
test: &T,
enc: &mut E,
dec: &mut D) {
let mut cipher_out: Vec<u8> = repeat(0).take(test.get_cipher().len()).collect();
{
let mut buff_in = RefReadBuffer::new(test.get_plain());
let mut buff_out = RefWriteBuffer::new(&mut cipher_out);
match enc.encrypt(&mut buff_in, &mut buff_out, true) {
Ok(BufferUnderflow) => {}
Ok(BufferOverflow) => panic!("Encryption not completed"),
Err(_) => panic!("Error"),
}
}
assert!(test.get_cipher() == &cipher_out[..]);
let mut plain_out: Vec<u8> = repeat(0).take(test.get_plain().len()).collect();
{
let mut buff_in = RefReadBuffer::new(test.get_cipher());
let mut buff_out = RefWriteBuffer::new(&mut plain_out);
match dec.decrypt(&mut buff_in, &mut buff_out, true) {
Ok(BufferUnderflow) => {}
Ok(BufferOverflow) => panic!("Decryption not completed"),
Err(_) => panic!("Error"),
}
}
assert!(test.get_plain() == &plain_out[..]);
}
/// Run and encryption or decryption operation, passing in variable sized input and output
/// buffers.
///
/// # Arguments
/// * input - The complete input vector
/// * output - The complete output vector
/// * op - A closure that runs either an encryption or decryption operation
/// * next_in_len - A closure that returns the length to use for the next input buffer; If the
/// returned value is not in a valid range, it will be fixed up by this
/// function.
/// * next_out_len - A closure that returns the length to use for the next output buffer; If the
/// returned value is not in a valid range, it will be fixed up by this
/// function.
/// * immediate_eof - Whether eof should be set immediately upon running out of input or if eof
/// should be passed only after all input has been consumed.
fn run_inc<OpFunc, NextInFunc, NextOutFunc>(
input: &[u8],
output: &mut [u8],
mut op: OpFunc,
mut next_in_len: NextInFunc,
mut next_out_len: NextOutFunc,
immediate_eof: bool)
where
OpFunc: FnMut(&mut RefReadBuffer, &mut RefWriteBuffer, bool) ->
Result<BufferResult, SymmetricCipherError>,
NextInFunc: FnMut() -> usize,
NextOutFunc: FnMut() -> usize {
use std::cell::Cell;
let in_len = input.len();
let out_len = output.len();
let mut state: Result<BufferResult, SymmetricCipherError> = Ok(BufferUnderflow);
let mut in_pos: usize = 0;
let mut out_pos: usize = 0;
let eof = Cell::new(false);
let mut in_end = |in_pos: usize, primary: bool| {
if eof.get() {
return in_len;
}
let x = next_in_len();
if x >= in_len && immediate_eof {
eof.set(true);
}
cmp::min(in_len, in_pos + cmp::max(x, if primary { 1 } else { 0 }))
};
let mut out_end = |out_pos: usize| {
let x = next_out_len();
cmp::min(out_len, out_pos + cmp::max(x, 1))
};
loop {
match state {
Ok(BufferUnderflow) => {
if in_pos == input.len() {
break;
}
let mut tmp_in = RefReadBuffer::new(&input[in_pos..in_end(in_pos, true)]);
let out_end = out_end(out_pos);
let mut tmp_out = RefWriteBuffer::new(&mut output[out_pos..out_end]);
state = op(&mut tmp_in, &mut tmp_out, eof.get());
match state {
Ok(BufferUnderflow) => assert!(tmp_in.is_empty()),
_ => {}
}
in_pos += tmp_in.position();
out_pos += tmp_out.position();
}
Ok(BufferOverflow) => {
let mut tmp_in = RefReadBuffer::new(&input[in_pos..in_end(in_pos, false)]);
let out_end = out_end(out_pos);
let mut tmp_out = RefWriteBuffer::new(&mut output[out_pos..out_end]);
state = op(&mut tmp_in, &mut tmp_out, eof.get());
match state {
Ok(BufferOverflow) => assert!(tmp_out.is_full()),
_ => {}
}
in_pos += tmp_in.position();
out_pos += tmp_out.position();
}
Err(InvalidPadding) => panic!("Invalid Padding"),
Err(InvalidLength) => panic!("Invalid Length")
}
}
if !eof.get() {
eof.set(true);
let mut tmp_out = RefWriteBuffer::new(&mut output[out_pos..out_end(out_pos)]);
state = op(&mut RefReadBuffer::new(&[]), &mut tmp_out, eof.get());
out_pos += tmp_out.position();
}
loop {
match state {
Ok(BufferUnderflow) => {
break;
}
Ok(BufferOverflow) => {
let out_end = out_end(out_pos);
let mut tmp_out = RefWriteBuffer::new(&mut output[out_pos..out_end]);
state = op(&mut RefReadBuffer::new(&[]), &mut tmp_out, eof.get());
assert!(tmp_out.is_full());
out_pos += tmp_out.position();
}
Err(InvalidPadding) => panic!("Invalid Padding"),
Err(InvalidLength) => panic!("Invalid Length")
}
}
}
fn run_inc1_test<T: CipherTest, E: Encryptor, D: Decryptor>(
test: &T,
enc: &mut E,
dec: &mut D) {
let mut cipher_out: Vec<u8> = repeat(0).take(test.get_cipher().len()).collect();
run_inc(
test.get_plain(),
&mut cipher_out,
|in_buff: &mut RefReadBuffer, out_buff: &mut RefWriteBuffer, eof: bool| {
enc.encrypt(in_buff, out_buff, eof)
},
|| { 0 },
|| { 1 },
false);
assert!(test.get_cipher() == &cipher_out[..]);
let mut plain_out: Vec<u8> = repeat(0).take(test.get_plain().len()).collect();
run_inc(
test.get_cipher(),
&mut plain_out,
|in_buff: &mut RefReadBuffer, out_buff: &mut RefWriteBuffer, eof: bool| {
dec.decrypt(in_buff, out_buff, eof)
},
|| { 0 },
|| { 1 },
false);
assert!(test.get_plain() == &plain_out[..]);
}
fn run_rand_test<T, E, D, NewEncFunc, NewDecFunc>(
test: &T,
mut new_enc: NewEncFunc,
mut new_dec: NewDecFunc)
where
T: CipherTest,
E: Encryptor,
D: Decryptor,
NewEncFunc: FnMut() -> E,
NewDecFunc: FnMut() -> D{
use rand;
use rand::Rng;
let tmp : &[_] = &[1, 2, 3, 4];
let mut rng1: rand::StdRng = rand::SeedableRng::from_seed(tmp);
let mut rng2: rand::StdRng = rand::SeedableRng::from_seed(tmp);
let mut rng3: rand::StdRng = rand::SeedableRng::from_seed(tmp);
let max_size = cmp::max(test.get_plain().len(), test.get_cipher().len());
let mut r1 = || {
rng1.gen_range(0, max_size)
};
let mut r2 = || {
rng2.gen_range(0, max_size)
};
for _ in 0..1000 {
let mut enc = new_enc();
let mut dec = new_dec();
let mut cipher_out: Vec<u8> = repeat(0).take(test.get_cipher().len()).collect();
run_inc(
test.get_plain(),
&mut cipher_out,
|in_buff: &mut RefReadBuffer, out_buff: &mut RefWriteBuffer, eof: bool| {
enc.encrypt(in_buff, out_buff, eof)
},
|| { r1() },
|| { r2() },
rng3.gen());
assert!(test.get_cipher() == &cipher_out[..]);
let mut plain_out: Vec<u8> = repeat(0).take(test.get_plain().len()).collect();
run_inc(
test.get_cipher(),
&mut plain_out,
|in_buff: &mut RefReadBuffer, out_buff: &mut RefWriteBuffer, eof: bool| {
dec.decrypt(in_buff, out_buff, eof)
},
|| { r1() },
|| { r2() },
rng3.gen());
assert!(test.get_plain() == &plain_out[..]);
}
}
fn run_test<T, E, D, NewEncFunc, NewDecFunc>(
test: &T,
mut new_enc: NewEncFunc,
mut new_dec: NewDecFunc)
where
T: CipherTest,
E: Encryptor,
D: Decryptor,
NewEncFunc: FnMut() -> E,
NewDecFunc: FnMut() -> D{
run_full_test(test, &mut new_enc(), &mut new_dec());
run_inc1_test(test, &mut new_enc(), &mut new_dec());
run_rand_test(test, new_enc, new_dec);
}
#[test]
fn aes_ecb_no_padding() {
let tests = aes_ecb_no_padding_tests();
for test in tests.iter() {
run_test(
test,
|| {
let aes_enc = aessafe::AesSafe128Encryptor::new(&test.key[..]);
EcbEncryptor::new(aes_enc, NoPadding)
},
|| {
let aes_dec = aessafe::AesSafe128Decryptor::new(&test.key[..]);
EcbDecryptor::new(aes_dec, NoPadding)
});
}
}
#[test]
fn aes_ecb_pkcs_padding() {
let tests = aes_ecb_pkcs_padding_tests();
for test in tests.iter() {
run_test(
test,
|| {
let aes_enc = aessafe::AesSafe128Encryptor::new(&test.key[..]);
EcbEncryptor::new(aes_enc, PkcsPadding)
},
|| {
let aes_dec = aessafe::AesSafe128Decryptor::new(&test.key[..]);
EcbDecryptor::new(aes_dec, PkcsPadding)
});
}
}
#[test]
fn aes_cbc_no_padding() {
let tests = aes_cbc_no_padding_tests();
for test in tests.iter() {
run_test(
test,
|| {
let aes_enc = aessafe::AesSafe128Encryptor::new(&test.key[..]);
CbcEncryptor::new(aes_enc, NoPadding, test.iv.clone())
},
|| {
let aes_dec = aessafe::AesSafe128Decryptor::new(&test.key[..]);
CbcDecryptor::new(aes_dec, NoPadding, test.iv.clone())
});
}
}
#[test]
fn aes_cbc_pkcs_padding() {
let tests = aes_cbc_pkcs_padding_tests();
for test in tests.iter() {
run_test(
test,
|| {
let aes_enc = aessafe::AesSafe128Encryptor::new(&test.key[..]);
CbcEncryptor::new(aes_enc, PkcsPadding, test.iv.clone())
},
|| {
let aes_dec = aessafe::AesSafe128Decryptor::new(&test.key[..]);
CbcDecryptor::new(aes_dec, PkcsPadding, test.iv.clone())
});
}
}
#[test]
fn aes_ctr() {
let tests = aes_ctr_tests();
for test in tests.iter() {
run_test(
test,
|| {
let aes_enc = aessafe::AesSafe128Encryptor::new(&test.key[..]);
CtrMode::new(aes_enc, test.ctr.clone())
},
|| {
let aes_enc = aessafe::AesSafe128Encryptor::new(&test.key[..]);
CtrMode::new(aes_enc, test.ctr.clone())
});
}
}
#[test]
fn aes_ctr_x8() {
let tests = aes_ctr_tests();
for test in tests.iter() {
run_test(
test,
|| {
let aes_enc = aessafe::AesSafe128EncryptorX8::new(&test.key[..]);
CtrModeX8::new(aes_enc, &test.ctr[..])
},
|| {
let aes_enc = aessafe::AesSafe128EncryptorX8::new(&test.key[..]);
CtrModeX8::new(aes_enc, &test.ctr[..])
});
}
}
}
#[cfg(all(test, feature = "with-bench"))]
mod bench {
use aessafe;
use blockmodes::{EcbEncryptor, CbcEncryptor, CtrMode, CtrModeX8,
NoPadding, PkcsPadding};
use buffer::{ReadBuffer, WriteBuffer, RefReadBuffer, RefWriteBuffer};
use buffer::BufferResult::{BufferUnderflow, BufferOverflow};
use symmetriccipher::{Encryptor};
use test::Bencher;
#[bench]
pub fn aes_ecb_no_padding_bench(bh: &mut Bencher) {
let key = [1u8; 16];
let plain = [3u8; 512];
let mut cipher = [3u8; 528];
let aes_enc = aessafe::AesSafe128Encryptor::new(&key);
let mut enc = EcbEncryptor::new(aes_enc, NoPadding);
bh.iter( || {
enc.reset();
let mut buff_in = RefReadBuffer::new(&plain);
let mut buff_out = RefWriteBuffer::new(&mut cipher);
match enc.encrypt(&mut buff_in, &mut buff_out, true) {
Ok(BufferUnderflow) => {}
Ok(BufferOverflow) => panic!("Encryption not completed"),
Err(_) => panic!("Error"),
}
});
bh.bytes = (plain.len()) as u64;
}
#[bench]
pub fn aes_cbc_pkcs_padding_bench(bh: &mut Bencher) {
let key = [1u8; 16];
let iv = [2u8; 16];
let plain = [3u8; 512];
let mut cipher = [3u8; 528];
let aes_enc = aessafe::AesSafe128Encryptor::new(&key);
let mut enc = CbcEncryptor::new(aes_enc, PkcsPadding, iv.to_vec());
bh.iter( || {
enc.reset(&iv);
let mut buff_in = RefReadBuffer::new(&plain);
let mut buff_out = RefWriteBuffer::new(&mut cipher);
match enc.encrypt(&mut buff_in, &mut buff_out, true) {
Ok(BufferUnderflow) => {}
Ok(BufferOverflow) => panic!("Encryption not completed"),
Err(_) => panic!("Error"),
}
});
bh.bytes = (plain.len()) as u64;
}
#[bench]
pub fn aes_ctr_bench(bh: &mut Bencher) {
let key = [1u8; 16];
let ctr = [2u8; 16];
let plain = [3u8; 512];
let mut cipher = [3u8; 528];
let aes_enc = aessafe::AesSafe128Encryptor::new(&key);
let mut enc = CtrMode::new(aes_enc, ctr.to_vec());
bh.iter( || {
enc.reset(&ctr);
let mut buff_in = RefReadBuffer::new(&plain);
let mut buff_out = RefWriteBuffer::new(&mut cipher);
match enc.encrypt(&mut buff_in, &mut buff_out, true) {
Ok(BufferUnderflow) => {}
Ok(BufferOverflow) => panic!("Encryption not completed"),
Err(_) => panic!("Error"),
}
});
bh.bytes = (plain.len()) as u64;
}
#[bench]
pub fn aes_ctr_x8_bench(bh: &mut Bencher) {
let key = [1u8; 16];
let ctr = [2u8; 16];
let plain = [3u8; 512];
let mut cipher = [3u8; 528];
let aes_enc = aessafe::AesSafe128EncryptorX8::new(&key);
let mut enc = CtrModeX8::new(aes_enc, &ctr);
bh.iter( || {
enc.reset(&ctr);
let mut buff_in = RefReadBuffer::new(&plain);
let mut buff_out = RefWriteBuffer::new(&mut cipher);
match enc.encrypt(&mut buff_in, &mut buff_out, true) {
Ok(BufferUnderflow) => {}
Ok(BufferOverflow) => panic!("Encryption not completed"),
Err(_) => panic!("Error"),
}
});
bh.bytes = (plain.len()) as u64;
}
}