openentropy_core/sources/
memory.rs1use std::ptr;
7use std::time::Instant;
8
9use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
10
11const PAGE_SIZE: usize = 4096;
13
14static MEMORY_TIMING_INFO: SourceInfo = SourceInfo {
15 name: "memory_timing",
16 description: "DRAM allocation and access timing jitter via mmap",
17 physics: "Times memory allocation (malloc/mmap) and access patterns. Allocation jitter \
18 comes from heap fragmentation, page fault handling, and kernel memory pressure. \
19 Access timing varies with: DRAM refresh interference (~64ms cycle), cache \
20 hierarchy state (L1/L2/L3 hits vs misses), and memory controller scheduling.",
21 category: SourceCategory::Timing,
22 platform: Platform::Any,
23 requirements: &[],
24 entropy_rate_estimate: 1500.0,
25 composite: false,
26};
27
28pub struct MemoryTimingSource;
30
31impl EntropySource for MemoryTimingSource {
32 fn info(&self) -> &SourceInfo {
33 &MEMORY_TIMING_INFO
34 }
35
36 fn is_available(&self) -> bool {
37 cfg!(unix)
39 }
40
41 fn collect(&self, n_samples: usize) -> Vec<u8> {
42 let mut output = Vec::with_capacity(n_samples);
43 let mut prev_ns: u64 = 0;
44
45 let iterations = n_samples + 1;
48
49 for i in 0..iterations {
50 let t0 = Instant::now();
51
52 let addr = unsafe {
55 libc::mmap(
56 ptr::null_mut(),
57 PAGE_SIZE,
58 libc::PROT_READ | libc::PROT_WRITE,
59 libc::MAP_ANONYMOUS | libc::MAP_PRIVATE,
60 -1, 0, )
63 };
64
65 if addr == libc::MAP_FAILED {
66 continue;
67 }
68
69 unsafe {
73 let page = addr as *mut u8;
74 ptr::write_volatile(page, 0xAA);
76 ptr::write_volatile(page.add(PAGE_SIZE - 1), 0x55);
77
78 let _v1 = ptr::read_volatile(page);
80 let _v2 = ptr::read_volatile(page.add(PAGE_SIZE - 1));
81
82 libc::munmap(addr, PAGE_SIZE);
84 }
85
86 let elapsed_ns = t0.elapsed().as_nanos() as u64;
87
88 if i > 0 {
89 let delta = elapsed_ns.wrapping_sub(prev_ns);
91 let mixed = (delta as u8) ^ ((delta >> 8) as u8);
93 output.push(mixed);
94
95 if output.len() >= n_samples {
96 break;
97 }
98 }
99
100 prev_ns = elapsed_ns;
101 }
102
103 output.truncate(n_samples);
104 output
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 #[cfg(unix)]
114 #[ignore] fn memory_timing_collects_bytes() {
116 let src = MemoryTimingSource;
117 assert!(src.is_available());
118 let data = src.collect(128);
119 assert!(!data.is_empty());
120 assert!(data.len() <= 128);
121 }
122
123 #[test]
124 fn memory_timing_info() {
125 let src = MemoryTimingSource;
126 assert_eq!(src.name(), "memory_timing");
127 assert_eq!(src.info().category, SourceCategory::Timing);
128 }
129}