openentropy_core/sources/timing/
mach_timing.rs1use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
7use crate::sources::helpers::{extract_timing_entropy, mach_time};
8
9pub struct MachTimingSource;
13
14static MACH_TIMING_INFO: SourceInfo = SourceInfo {
15 name: "mach_timing",
16 description: "mach_absolute_time() with micro-workload jitter (raw LSBs)",
17 physics: "Reads the ARM system counter (mach_absolute_time) at sub-nanosecond \
18 resolution with variable micro-workloads between samples. The timing \
19 jitter comes from CPU pipeline state: instruction reordering, branch \
20 prediction, cache state, interrupt coalescing, and power-state \
21 transitions.",
22 category: SourceCategory::Timing,
23 platform: Platform::MacOS,
24 requirements: &[],
25 entropy_rate_estimate: 0.3,
26 composite: false,
27 is_fast: true,
28};
29
30impl EntropySource for MachTimingSource {
31 fn info(&self) -> &SourceInfo {
32 &MACH_TIMING_INFO
33 }
34
35 fn is_available(&self) -> bool {
36 cfg!(target_os = "macos")
37 }
38
39 fn collect(&self, n_samples: usize) -> Vec<u8> {
40 let raw_count = n_samples + 64;
41 let mut timings = Vec::with_capacity(raw_count);
42
43 let mut lcg: u64 = mach_time() | 1;
45
46 for _ in 0..raw_count {
47 let t0 = mach_time();
48
49 lcg = lcg.wrapping_mul(6364136223846793005).wrapping_add(1);
52 let iterations = ((lcg >> 32) & 7) + 1;
53 let mut sink: u64 = t0;
54 for _ in 0..iterations {
55 sink = sink.wrapping_mul(6364136223846793005).wrapping_add(1);
56 }
57 std::hint::black_box(sink);
58
59 let t1 = mach_time();
60 timings.push(t1.wrapping_sub(t0));
61 }
62
63 extract_timing_entropy(&timings, n_samples)
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 #[cfg(target_os = "macos")]
73 #[ignore] fn mach_timing_collects_bytes() {
75 let src = MachTimingSource;
76 assert!(src.is_available());
77 let data = src.collect(64);
78 assert!(!data.is_empty());
79 assert!(data.len() <= 64);
80 }
81
82 #[test]
83 fn source_info_name() {
84 assert_eq!(MachTimingSource.name(), "mach_timing");
85 }
86
87 #[test]
88 fn source_info_category() {
89 assert_eq!(MachTimingSource.info().category, SourceCategory::Timing);
90 assert!(!MachTimingSource.info().composite);
91 }
92}