openentropy_core/sources/timing/
clock_jitter.rs1use std::time::{Instant, SystemTime};
7
8use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
9use crate::sources::helpers::extract_timing_entropy;
10
11pub struct ClockJitterSource;
16
17static CLOCK_JITTER_INFO: SourceInfo = SourceInfo {
18 name: "clock_jitter",
19 description: "Timing jitter between Instant and SystemTime readout paths",
20 physics: "Reads both Instant (monotonic) and SystemTime (wall clock) in \
21 rapid succession and measures the time for the pair of reads. \
22 Jitter comes from variable OS clock-readout overhead, cache state, \
23 and interrupt timing between the two clock API calls.",
24 category: SourceCategory::Timing,
25 platform: Platform::Any,
26 requirements: &[],
27 entropy_rate_estimate: 0.5,
28 composite: false,
29 is_fast: true,
30};
31
32impl EntropySource for ClockJitterSource {
33 fn info(&self) -> &SourceInfo {
34 &CLOCK_JITTER_INFO
35 }
36
37 fn is_available(&self) -> bool {
38 true
39 }
40
41 fn collect(&self, n_samples: usize) -> Vec<u8> {
42 let raw_count = n_samples * 4 + 64;
43 let mut timings = Vec::with_capacity(raw_count);
44
45 for _ in 0..raw_count {
46 let t0 = Instant::now();
50
51 let _wall = SystemTime::now()
52 .duration_since(SystemTime::UNIX_EPOCH)
53 .unwrap_or_default();
54 std::hint::black_box(&_wall);
55
56 let elapsed = t0.elapsed().as_nanos() as u64;
57 timings.push(elapsed);
58 }
59
60 extract_timing_entropy(&timings, n_samples)
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 #[ignore] fn clock_jitter_collects_bytes() {
71 let src = ClockJitterSource;
72 assert!(src.is_available());
73 let data = src.collect(128);
74 assert!(!data.is_empty());
75 assert!(data.len() <= 128);
76 let first = data[0];
77 assert!(data.iter().any(|&b| b != first), "all bytes were identical");
78 }
79
80 #[test]
81 fn source_info_name() {
82 assert_eq!(ClockJitterSource.name(), "clock_jitter");
83 }
84
85 #[test]
86 fn source_info_category() {
87 assert_eq!(ClockJitterSource.info().category, SourceCategory::Timing);
88 assert!(!ClockJitterSource.info().composite);
89 }
90}