use crate::errors::UnknownCryptoError;
use core::marker::PhantomData;
pub trait TestableStreamingContext<T: PartialEq> {
fn reset(&mut self) -> Result<(), UnknownCryptoError>;
fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError>;
fn finalize(&mut self) -> Result<T, UnknownCryptoError>;
fn one_shot(input: &[u8]) -> Result<T, UnknownCryptoError>;
fn verify_result(expected: &T, input: &[u8]) -> Result<(), UnknownCryptoError>;
fn compare_states(state_1: &Self, state_2: &Self);
}
#[allow(dead_code)] pub struct StreamingContextConsistencyTester<R, T> {
_return_type: PhantomData<R>,
_initial_context: T,
blocksize: usize,
}
impl<R, T> StreamingContextConsistencyTester<R, T>
where
R: PartialEq + core::fmt::Debug,
T: TestableStreamingContext<R> + Clone,
{
pub fn new(streaming_context: T, blocksize: usize) -> Self {
Self {
_return_type: PhantomData,
_initial_context: streaming_context,
blocksize,
}
}
const DEFAULT_INPUT: [u8; 37] = [255u8; 37];
#[cfg(feature = "safe_api")]
pub fn run_all_tests_property(&self, data: &[u8]) {
self.consistency(data);
self.consistency(&[0u8; 0]);
self.produces_same_state(data);
self.incremental_and_one_shot(data);
self.double_finalize_with_reset_no_update_ok(data);
self.double_finalize_with_reset_ok(data);
self.double_finalize_err(data);
self.update_after_finalize_with_reset_ok(data);
self.update_after_finalize_err(data);
self.double_reset_ok(data);
self.immediate_finalize();
Self::verify_same_input_ok(data);
Self::verify_diff_input_err(data);
}
#[cfg(feature = "safe_api")]
pub fn run_all_tests(&self) {
self.consistency(&Self::DEFAULT_INPUT);
self.consistency(&[0u8; 0]);
self.produces_same_state(&Self::DEFAULT_INPUT);
self.incremental_processing_with_leftover();
self.incremental_and_one_shot(&Self::DEFAULT_INPUT);
self.double_finalize_with_reset_no_update_ok(&Self::DEFAULT_INPUT);
self.double_finalize_with_reset_ok(&Self::DEFAULT_INPUT);
self.double_finalize_err(&Self::DEFAULT_INPUT);
self.update_after_finalize_with_reset_ok(&Self::DEFAULT_INPUT);
self.update_after_finalize_err(&Self::DEFAULT_INPUT);
self.double_reset_ok(&Self::DEFAULT_INPUT);
self.immediate_finalize();
Self::verify_same_input_ok(&Self::DEFAULT_INPUT);
Self::verify_diff_input_err(&Self::DEFAULT_INPUT);
}
#[cfg(not(feature = "safe_api"))]
pub fn run_all_tests(&self) {
self.consistency(&Self::DEFAULT_INPUT);
self.consistency(&[0u8; 0]);
self.produces_same_state(&Self::DEFAULT_INPUT);
self.incremental_and_one_shot(&Self::DEFAULT_INPUT);
self.double_finalize_with_reset_no_update_ok(&Self::DEFAULT_INPUT);
self.double_finalize_with_reset_ok(&Self::DEFAULT_INPUT);
self.double_finalize_err(&Self::DEFAULT_INPUT);
self.update_after_finalize_with_reset_ok(&Self::DEFAULT_INPUT);
self.update_after_finalize_err(&Self::DEFAULT_INPUT);
self.double_reset_ok(&Self::DEFAULT_INPUT);
self.immediate_finalize();
Self::verify_same_input_ok(&Self::DEFAULT_INPUT);
Self::verify_diff_input_err(&Self::DEFAULT_INPUT);
}
fn consistency(&self, data: &[u8]) {
let mut state_1 = self._initial_context.clone();
state_1.update(data).unwrap();
let res_1 = state_1.finalize().unwrap();
let mut state_2 = self._initial_context.clone();
state_2.reset().unwrap();
state_2.update(data).unwrap();
let res_2 = state_2.finalize().unwrap();
let mut state_3 = self._initial_context.clone();
state_3.update(data).unwrap();
state_3.reset().unwrap();
state_3.update(data).unwrap();
let res_3 = state_3.finalize().unwrap();
let mut state_4 = self._initial_context.clone();
state_4.update(data).unwrap();
let _ = state_4.finalize().unwrap();
state_4.reset().unwrap();
state_4.update(data).unwrap();
let res_4 = state_4.finalize().unwrap();
assert_eq!(res_1, res_2);
assert_eq!(res_2, res_3);
assert_eq!(res_3, res_4);
if data.is_empty() {
let mut state_5 = self._initial_context.clone();
let res_5 = state_5.finalize().unwrap();
let mut state_6 = self._initial_context.clone();
state_6.reset().unwrap();
let res_6 = state_6.finalize().unwrap();
let mut state_7 = self._initial_context.clone();
state_7.update(b"WRONG DATA").unwrap();
state_7.reset().unwrap();
let res_7 = state_7.finalize().unwrap();
assert_eq!(res_4, res_5);
assert_eq!(res_5, res_6);
assert_eq!(res_6, res_7);
}
}
fn produces_same_state(&self, data: &[u8]) {
let state_1 = self._initial_context.clone();
let mut state_2 = self._initial_context.clone();
state_2.reset().unwrap();
let mut state_3 = self._initial_context.clone();
state_3.update(data).unwrap();
state_3.reset().unwrap();
let mut state_4 = self._initial_context.clone();
state_4.update(data).unwrap();
let _ = state_4.finalize().unwrap();
state_4.reset().unwrap();
T::compare_states(&state_1, &state_2);
T::compare_states(&state_2, &state_3);
T::compare_states(&state_3, &state_4);
}
#[cfg(feature = "safe_api")]
fn incremental_processing_with_leftover(&self) {
for len in 0..self.blocksize * 4 {
let data = vec![0u8; len];
let mut state = self._initial_context.clone();
let mut other_data: Vec<u8> = Vec::new();
other_data.extend_from_slice(&data);
state.update(&data).unwrap();
if data.len() > self.blocksize {
other_data.extend_from_slice(b"");
state.update(b"").unwrap();
}
if data.len() > self.blocksize * 2 {
other_data.extend_from_slice(b"Extra");
state.update(b"Extra").unwrap();
}
if data.len() > self.blocksize * 3 {
other_data.extend_from_slice(&[0u8; 256]);
state.update(&[0u8; 256]).unwrap();
}
let streaming_result = state.finalize().unwrap();
let one_shot_result = T::one_shot(&other_data).unwrap();
assert_eq!(streaming_result, one_shot_result);
}
}
fn incremental_and_one_shot(&self, data: &[u8]) {
let mut state = self._initial_context.clone();
state.update(data).unwrap();
let streaming_result = state.finalize().unwrap();
let one_shot_result = T::one_shot(data).unwrap();
assert_eq!(streaming_result, one_shot_result);
}
fn double_finalize_with_reset_no_update_ok(&self, data: &[u8]) {
let mut state = self._initial_context.clone();
state.update(data).unwrap();
let _ = state.finalize().unwrap();
state.reset().unwrap();
assert!(state.finalize().is_ok());
}
fn double_finalize_with_reset_ok(&self, data: &[u8]) {
let mut state = self._initial_context.clone();
state.update(data).unwrap();
let _ = state.finalize().unwrap();
state.reset().unwrap();
state.update(data).unwrap();
assert!(state.finalize().is_ok());
}
fn double_finalize_err(&self, data: &[u8]) {
let mut state = self._initial_context.clone();
state.update(data).unwrap();
let _ = state.finalize().unwrap();
assert!(state.finalize().is_err());
}
fn update_after_finalize_with_reset_ok(&self, data: &[u8]) {
let mut state = self._initial_context.clone();
state.update(data).unwrap();
let _ = state.finalize().unwrap();
state.reset().unwrap();
assert!(state.update(data).is_ok());
}
fn update_after_finalize_err(&self, data: &[u8]) {
let mut state = self._initial_context.clone();
state.update(data).unwrap();
let _ = state.finalize().unwrap();
assert!(state.update(data).is_err());
}
fn double_reset_ok(&self, data: &[u8]) {
let mut state = self._initial_context.clone();
state.update(data).unwrap();
let _ = state.finalize().unwrap();
state.reset().unwrap();
assert!(state.reset().is_ok());
}
fn immediate_finalize(&self) {
let mut state = self._initial_context.clone();
assert!(state.finalize().is_ok());
}
pub fn verify_same_input_ok(data: &[u8]) {
let expected = T::one_shot(data).unwrap();
assert!(T::verify_result(&expected, data).is_ok());
}
pub fn verify_diff_input_err(data: &[u8]) {
let expected = T::one_shot(data).unwrap();
assert!(T::verify_result(&expected, b"Bad data").is_err());
}
}