use dcrypt_api::Result;
#[cfg(feature = "std")]
use std::{boxed::Box, vec::Vec};
#[cfg(all(not(feature = "std"), feature = "alloc"))]
extern crate alloc;
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::{boxed::Box, vec::Vec};
#[cfg(any(feature = "std", feature = "alloc"))]
pub type CleanupFn<T> = Box<dyn FnOnce(&mut T)>;
pub trait SecureOperation<T> {
fn execute_secure(self) -> Result<T>;
fn clear_sensitive_data(&mut self);
}
pub trait SecureOperationExt: Sized {
type Output;
fn execute_with_cleanup<F>(self, cleanup: F) -> Result<Self::Output>
where
F: FnOnce();
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub struct SecureOperationBuilder<T> {
state: T,
cleanup_fns: Vec<CleanupFn<T>>,
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T> SecureOperationBuilder<T> {
pub fn new(initial_state: T) -> Self {
Self {
state: initial_state,
cleanup_fns: Vec::new(),
}
}
pub fn with_cleanup<F>(mut self, cleanup: F) -> Self
where
F: FnOnce(&mut T) + 'static,
{
self.cleanup_fns.push(Box::new(cleanup));
self
}
pub fn transform<U, F>(self, f: F) -> SecureOperationBuilder<U>
where
F: FnOnce(T) -> U,
{
SecureOperationBuilder {
state: f(self.state),
cleanup_fns: Vec::new(), }
}
pub fn build<O, F>(self, operation: F) -> Result<O>
where
F: FnOnce(&mut T) -> Result<O>,
{
let mut state = self.state;
let result = operation(&mut state);
for cleanup in self.cleanup_fns.into_iter().rev() {
cleanup(&mut state);
}
result
}
}
pub trait SecureCompare: Sized {
fn secure_eq(&self, other: &Self) -> bool;
fn secure_cmp(&self, other: &Self) -> subtle::Choice;
}
impl<const N: usize> SecureCompare for [u8; N] {
fn secure_eq(&self, other: &Self) -> bool {
use subtle::ConstantTimeEq;
bool::from(self.ct_eq(other))
}
fn secure_cmp(&self, other: &Self) -> subtle::Choice {
use subtle::ConstantTimeEq;
self.ct_eq(other)
}
}
impl SecureCompare for &[u8] {
fn secure_eq(&self, other: &Self) -> bool {
use subtle::ConstantTimeEq;
bool::from(self.ct_eq(other))
}
fn secure_cmp(&self, other: &Self) -> subtle::Choice {
use subtle::ConstantTimeEq;
self.ct_eq(other)
}
}
pub mod barrier {
use core::sync::atomic::{compiler_fence, fence, Ordering};
#[inline(always)]
pub fn compiler_fence_seq_cst() {
compiler_fence(Ordering::SeqCst);
}
#[inline(always)]
pub fn memory_fence_seq_cst() {
fence(Ordering::SeqCst);
}
#[inline(always)]
pub fn with_barriers<T, F: FnOnce() -> T>(f: F) -> T {
compiler_fence_seq_cst();
let result = f();
compiler_fence_seq_cst();
result
}
}
#[cfg(feature = "alloc")]
pub mod alloc {
use super::*;
use zeroize::Zeroize;
pub fn secure_alloc<T: Default + Zeroize + Clone>(size: usize) -> Result<Vec<T>> {
Ok(vec![T::default(); size])
}
pub fn secure_free<T: Zeroize>(mut data: Vec<T>) {
for item in data.iter_mut() {
item.zeroize();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use zeroize::Zeroize;
#[cfg(any(feature = "std", feature = "alloc"))]
struct TestOperation {
secret: Vec<u8>,
result: Option<Vec<u8>>,
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl SecureOperation<Vec<u8>> for TestOperation {
fn execute_secure(mut self) -> Result<Vec<u8>> {
self.result = Some(self.secret.iter().map(|&b| b ^ 0xFF).collect());
let result = self.result.clone().unwrap();
self.clear_sensitive_data();
Ok(result)
}
fn clear_sensitive_data(&mut self) {
self.secret.zeroize();
if let Some(ref mut result) = self.result {
result.zeroize();
}
self.result = None;
}
}
#[test]
#[cfg(any(feature = "std", feature = "alloc"))]
fn test_secure_operation() {
let op = TestOperation {
secret: vec![1, 2, 3, 4],
result: None,
};
let result = op.execute_secure().unwrap();
assert_eq!(result, vec![254, 253, 252, 251]);
}
#[test]
fn test_secure_compare() {
let a = [1u8, 2, 3, 4];
let b = [1u8, 2, 3, 4];
let c = [1u8, 2, 3, 5];
assert!(a.secure_eq(&b));
assert!(!a.secure_eq(&c));
}
#[test]
fn test_memory_barriers() {
use barrier::*;
let result = with_barriers(|| {
let mut x = 42;
x += 1;
x
});
assert_eq!(result, 43);
}
}