use std::time::Instant;
use sha2::{Digest, Sha256};
use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
use crate::sources::helpers::{extract_timing_entropy, mach_time};
static HASH_TIMING_INFO: SourceInfo = SourceInfo {
name: "hash_timing",
description: "SHA-256 hashing timing jitter from micro-architectural side effects",
physics: "SHA-256 hashes data of varying sizes and measures timing. While SHA-256 is \
algorithmically constant-time, the actual execution time varies due to: \
memory access patterns for the message schedule, cache line alignment, TLB \
state, and CPU frequency scaling. The timing also captures micro-architectural \
side effects from other processes.",
category: SourceCategory::Signal,
platform: Platform::Any,
requirements: &[],
entropy_rate_estimate: 2.5,
composite: false,
is_fast: true,
};
pub struct HashTimingSource;
impl EntropySource for HashTimingSource {
fn info(&self) -> &SourceInfo {
&HASH_TIMING_INFO
}
fn is_available(&self) -> bool {
true
}
fn collect(&self, n_samples: usize) -> Vec<u8> {
let raw_count = n_samples * 4 + 64;
let mut timings: Vec<u64> = Vec::with_capacity(raw_count);
let mut lcg: u64 = mach_time() | 1;
for i in 0..raw_count {
let size = 32 + (lcg as usize % 2017);
let mut data = Vec::with_capacity(size);
for _ in 0..size {
lcg = lcg.wrapping_mul(6364136223846793005).wrapping_add(1);
data.push((lcg >> 32) as u8);
}
let rounds = if size < 256 { 3 } else { 1 };
let t0 = Instant::now();
for _ in 0..rounds {
let mut hasher = Sha256::new();
hasher.update(&data);
let digest = hasher.finalize();
std::hint::black_box(&digest);
if let Some(b) = data.last_mut() {
*b ^= digest[i % 32];
}
}
let elapsed_ns = t0.elapsed().as_nanos() as u64;
timings.push(elapsed_ns);
}
extract_timing_entropy(&timings, n_samples)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hash_timing_info() {
let src = HashTimingSource;
assert_eq!(src.name(), "hash_timing");
assert_eq!(src.info().category, SourceCategory::Signal);
assert!(!src.info().composite);
}
#[test]
#[ignore] fn hash_timing_collects_bytes() {
let src = HashTimingSource;
assert!(src.is_available());
let data = src.collect(64);
assert!(!data.is_empty());
}
}