use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
#[cfg(target_os = "macos")]
use crate::sources::helpers::extract_timing_entropy;
#[cfg(target_os = "macos")]
use crate::sources::helpers::mach_time;
static GETENTROPY_TIMING_INFO: SourceInfo = SourceInfo {
name: "getentropy_timing",
description: "getentropy() SEP TRNG reseed timing — CV=267% bimodal distribution",
physics: "Times getentropy(256 bytes) system calls. Small reads (≤32 bytes) served from \
DRBG state (mean=1143 ticks, CV=27.2%). Large reads (≥256 bytes) may trigger \
SEP TRNG reseed: bimodal distribution with fast DRBG path (~900 ticks) vs slow \
TRNG wait path (~100,000 ticks). Overall: mean=1012 ticks, CV=267.2%, LSB=0.455 \
(uniform). TRNG timing captures: SEP thermal state (ring oscillator frequency), \
SEP workload (other entropy consumers deplete pool), von Neumann corrector \
rejection rate, SEP-to-kernel IPC latency. Genuine cross-process covert channel.",
category: SourceCategory::System,
platform: Platform::MacOS,
requirements: &[],
entropy_rate_estimate: 1.0,
composite: false,
is_fast: false,
};
pub struct GetentropyTimingSource;
#[cfg(target_os = "macos")]
impl EntropySource for GetentropyTimingSource {
fn info(&self) -> &SourceInfo {
&GETENTROPY_TIMING_INFO
}
fn is_available(&self) -> bool {
true
}
fn collect(&self, n_samples: usize) -> Vec<u8> {
let raw = n_samples * 2 + 32;
let mut timings = Vec::with_capacity(raw);
let mut buf = [0u8; 256];
for _ in 0..4 {
unsafe { libc::getentropy(buf.as_mut_ptr() as *mut core::ffi::c_void, 256) };
}
for _ in 0..raw {
let t0 = mach_time();
let ret = unsafe { libc::getentropy(buf.as_mut_ptr() as *mut core::ffi::c_void, 256) };
let elapsed = mach_time().wrapping_sub(t0);
if ret == 0 && elapsed < 2_400_000 {
timings.push(elapsed);
}
}
extract_timing_entropy(&timings, n_samples)
}
}
#[cfg(not(target_os = "macos"))]
impl EntropySource for GetentropyTimingSource {
fn info(&self) -> &SourceInfo {
&GETENTROPY_TIMING_INFO
}
fn is_available(&self) -> bool {
false
}
fn collect(&self, _: usize) -> Vec<u8> {
Vec::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn info() {
let src = GetentropyTimingSource;
assert_eq!(src.info().name, "getentropy_timing");
assert!(matches!(src.info().category, SourceCategory::System));
assert_eq!(src.info().platform, Platform::MacOS);
}
#[test]
#[cfg(target_os = "macos")]
fn is_available_on_macos() {
assert!(GetentropyTimingSource.is_available());
}
#[test]
#[ignore]
fn collects_bimodal_trng_timing() {
let data = GetentropyTimingSource.collect(32);
assert!(!data.is_empty());
}
}