use crate::error::{CryptoError, Result};
#[cfg(feature = "i18n")]
use crate::i18n::translate;
use rand::SeedableRng;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
pub mod cache_protection;
pub mod constant_time;
pub mod embedded_power;
pub mod error_injection;
pub mod masking;
pub mod power_analysis;
#[cfg(test)]
mod stats_tests;
pub use error_injection::*;
pub use masking::*;
pub use power_analysis::*;
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct SideChannelConfig {
pub constant_time_enabled: bool,
pub power_analysis_protection: bool,
pub error_injection_protection: bool,
pub cache_protection: bool,
pub timing_noise_enabled: bool,
pub masking_operations_enabled: bool,
pub redundancy_checks_enabled: bool,
pub cache_flush_enabled: bool,
pub timing_noise_level: f32,
pub max_timing_deviation: Duration,
pub operation_timeout: Duration,
}
impl Default for SideChannelConfig {
fn default() -> Self {
Self {
constant_time_enabled: true,
power_analysis_protection: cfg!(target_arch = "arm")
|| cfg!(target_arch = "riscv32")
|| cfg!(target_arch = "riscv64"),
error_injection_protection: true,
cache_protection: true,
timing_noise_enabled: true,
masking_operations_enabled: true,
redundancy_checks_enabled: true,
cache_flush_enabled: true,
timing_noise_level: 0.1,
max_timing_deviation: Duration::from_micros(50),
operation_timeout: Duration::from_secs(5),
}
}
}
#[derive(Debug, Clone)]
pub struct SideChannelContext {
pub config: SideChannelConfig,
pub countermeasure_stats: CountermeasureStats,
}
#[derive(Debug, Default, Clone)]
pub struct CountermeasureStats {
pub timing_protections: u64,
pub masking_operations: u64,
pub error_detection_triggers: u64,
pub cache_flush_operations: u64,
}
impl SideChannelContext {
pub fn new(config: SideChannelConfig) -> Self {
Self {
config,
countermeasure_stats: CountermeasureStats::default(),
}
}
#[allow(dead_code)]
pub fn config(&self) -> &SideChannelConfig {
&self.config
}
#[allow(dead_code)]
pub fn reset(&mut self) {
self.countermeasure_stats = CountermeasureStats::default();
}
pub fn increment_cache_flush(&mut self) {
self.countermeasure_stats.cache_flush_operations += 1;
}
pub fn increment_masking_operations(&mut self) {
self.countermeasure_stats.masking_operations += 1;
}
pub fn increment_timing_protections(&mut self) {
self.countermeasure_stats.timing_protections += 1;
}
pub fn increment_error_detection_triggers(&mut self) {
self.countermeasure_stats.error_detection_triggers += 1;
}
#[allow(dead_code)]
pub fn protect_timing<F, R>(&mut self, operation: F) -> Result<R>
where
F: FnOnce() -> Result<R>,
{
if !self.config.constant_time_enabled {
return operation();
}
let start = Instant::now();
if self.config.timing_noise_level > 0.0 {
add_timing_noise(self.config.timing_noise_level);
}
let result = operation();
let elapsed = start.elapsed();
let min_time = self.config.max_timing_deviation;
if elapsed < min_time {
std::thread::sleep(min_time - elapsed);
}
self.countermeasure_stats.timing_protections += 1;
result
}
#[allow(dead_code)]
pub fn protect_power_analysis<F, R>(&mut self, operation: F) -> Result<R>
where
F: FnOnce() -> Result<R>,
{
if !self.config.power_analysis_protection {
return operation();
}
let _guard = PowerAnalysisGuard::new()?;
self.countermeasure_stats.masking_operations += 1;
operation()
}
#[allow(dead_code)]
pub fn protect_error_injection<F, R>(&mut self, operation: F) -> Result<R>
where
F: FnOnce() -> Result<R>,
{
if !self.config.error_injection_protection {
return operation();
}
let detector = ErrorInjectionDetector::new();
let result = operation();
if detector.detect_fault() {
self.countermeasure_stats.error_detection_triggers += 1;
return Err(CryptoError::SideChannelError(
"Fault injection detected".into(),
));
}
result
}
#[allow(dead_code)]
pub fn protect_cache_access<F, R>(&mut self, operation: F) -> Result<R>
where
F: FnOnce() -> Result<R>,
{
if !self.config.cache_protection {
return operation();
}
flush_cpu_cache();
let result = operation();
flush_cpu_cache();
self.countermeasure_stats.cache_flush_operations += 1;
result
}
pub fn get_stats(&self) -> SideChannelStats {
SideChannelStats {
timing_protections: self.countermeasure_stats.timing_protections,
masking_operations: self.countermeasure_stats.masking_operations,
error_detection_triggers: self.countermeasure_stats.error_detection_triggers,
cache_flush_operations: self.countermeasure_stats.cache_flush_operations,
}
}
}
#[derive(Debug, Clone, Default)]
#[allow(dead_code)]
pub struct SideChannelStats {
#[allow(dead_code)]
pub timing_protections: u64,
#[allow(dead_code)]
pub masking_operations: u64,
#[allow(dead_code)]
pub error_detection_triggers: u64,
#[allow(dead_code)]
pub cache_flush_operations: u64,
}
pub fn protect_critical_operation<F, R>(context: &mut SideChannelContext, operation: F) -> Result<R>
where
F: FnOnce() -> Result<R>,
{
let config = context.config.clone();
if config.cache_protection {
flush_cpu_cache();
context.increment_cache_flush();
}
if config.power_analysis_protection && PowerAnalysisGuard::new().is_ok() {
context.increment_masking_operations();
}
if config.constant_time_enabled {
let start = Instant::now();
if config.timing_noise_level > 0.0 {
add_timing_noise(config.timing_noise_level);
}
let result = operation();
let elapsed = start.elapsed();
if elapsed > config.operation_timeout {
return Err(CryptoError::SideChannelError(format!(
"操作超时: {:?} > {:?}",
elapsed, config.operation_timeout
)));
}
let min_time = config.max_timing_deviation;
if elapsed < min_time {
std::thread::sleep(min_time - elapsed);
}
context.increment_timing_protections();
result
} else if config.error_injection_protection {
let detector = ErrorInjectionDetector::new();
let result = operation();
if detector.detect_fault() {
context.increment_error_detection_triggers();
return Err(CryptoError::SideChannelError(translate(
"error.fault_injection_detected",
)));
}
result
} else {
operation()
}
}
#[allow(dead_code)]
pub fn protect_critical_operation_with_context<F, R>(
context_arc: Arc<Mutex<SideChannelContext>>,
operation: F,
) -> Result<R>
where
F: FnOnce() -> Result<R>,
{
let mut context_guard = context_arc
.lock()
.map_err(|_| CryptoError::SideChannelError("Side channel context lock poisoned".into()))?;
let config = context_guard.config.clone();
if config.cache_protection {
flush_cpu_cache();
context_guard.countermeasure_stats.cache_flush_operations += 1;
}
if config.power_analysis_protection && PowerAnalysisGuard::new().is_ok() {
context_guard.countermeasure_stats.masking_operations += 1;
}
if config.constant_time_enabled {
let start = Instant::now();
if config.timing_noise_level > 0.0 {
add_timing_noise(config.timing_noise_level);
}
let result = operation();
let elapsed = start.elapsed();
let min_time = config.max_timing_deviation;
if elapsed < min_time {
std::thread::sleep(min_time - elapsed);
}
context_guard.countermeasure_stats.timing_protections += 1;
result
} else if config.error_injection_protection {
let detector = ErrorInjectionDetector::new();
let result = operation();
if detector.detect_fault() {
context_guard.countermeasure_stats.error_detection_triggers += 1;
return Err(CryptoError::SideChannelError(
"Fault injection detected".into(),
));
}
result
} else {
operation()
}
}
pub fn add_timing_noise(level: f32) {
use rand::RngCore;
use std::hint::black_box;
thread_local! {
static THREAD_RNG: std::cell::RefCell<rand::rngs::SmallRng> = std::cell::RefCell::new(
rand::rngs::SmallRng::from_entropy()
);
}
let iterations = ((level * 100.0) as u32).min(50); let mut dummy = 0u64;
THREAD_RNG.with(|rng| {
let mut rng = rng.borrow_mut();
let extra_delay = rng.next_u32() % 10; let total_iterations = iterations + extra_delay;
for _ in 0..total_iterations {
dummy = dummy.wrapping_add(black_box(1));
dummy = black_box(dummy.rotate_left(13));
}
});
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn flush_cpu_cache() {
use std::arch::asm;
unsafe {
asm!("clflush [{0}]", in(reg) &0u8, options(nostack));
}
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
pub fn flush_cpu_cache() {
std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
}