openentropy_core/sources/
memory.rs1use std::ptr;
7use std::time::Instant;
8
9use crate::source::{EntropySource, 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::Hardware,
22 platform_requirements: &[],
23 entropy_rate_estimate: 1500.0,
24 composite: false,
25};
26
27pub struct MemoryTimingSource;
29
30impl EntropySource for MemoryTimingSource {
31 fn info(&self) -> &SourceInfo {
32 &MEMORY_TIMING_INFO
33 }
34
35 fn is_available(&self) -> bool {
36 cfg!(unix)
38 }
39
40 fn collect(&self, n_samples: usize) -> Vec<u8> {
41 let mut output = Vec::with_capacity(n_samples);
42 let mut prev_ns: u64 = 0;
43
44 let iterations = n_samples + 1;
47
48 for i in 0..iterations {
49 let t0 = Instant::now();
50
51 let addr = unsafe {
54 libc::mmap(
55 ptr::null_mut(),
56 PAGE_SIZE,
57 libc::PROT_READ | libc::PROT_WRITE,
58 libc::MAP_ANONYMOUS | libc::MAP_PRIVATE,
59 -1, 0, )
62 };
63
64 if addr == libc::MAP_FAILED {
65 continue;
66 }
67
68 unsafe {
72 let page = addr as *mut u8;
73 ptr::write_volatile(page, 0xAA);
75 ptr::write_volatile(page.add(PAGE_SIZE - 1), 0x55);
76
77 let _v1 = ptr::read_volatile(page);
79 let _v2 = ptr::read_volatile(page.add(PAGE_SIZE - 1));
80
81 libc::munmap(addr, PAGE_SIZE);
83 }
84
85 let elapsed_ns = t0.elapsed().as_nanos() as u64;
86
87 if i > 0 {
88 let delta = elapsed_ns.wrapping_sub(prev_ns);
90 let mixed = (delta as u8) ^ ((delta >> 8) as u8);
92 output.push(mixed);
93
94 if output.len() >= n_samples {
95 break;
96 }
97 }
98
99 prev_ns = elapsed_ns;
100 }
101
102 output.truncate(n_samples);
103 output
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 #[cfg(unix)]
113 #[ignore] fn memory_timing_collects_bytes() {
115 let src = MemoryTimingSource;
116 assert!(src.is_available());
117 let data = src.collect(128);
118 assert!(!data.is_empty());
119 assert!(data.len() <= 128);
120 }
121
122 #[test]
123 fn memory_timing_info() {
124 let src = MemoryTimingSource;
125 assert_eq!(src.name(), "memory_timing");
126 assert_eq!(src.info().category, SourceCategory::Hardware);
127 }
128}