1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
// ---------------- [ File: bitcoin-random/src/rand_add_dynamic_env.rs ]
crate::ix!();
/**
| Gather non-cryptographic environment
| data that changes over time.
|
*/
pub fn rand_add_dynamic_env(hasher: &mut Sha512) {
// --- Windows: perfmon + FILETIME ---------------------------------------
#[cfg(WIN32)]
{
// Matches: RandAddSeedPerfmon(hasher);
rand_add_seed_perfmon(hasher);
// Matches: FILETIME ftime; GetSystemTimeAsFileTime(&ftime); hasher << ftime;
unsafe {
use winapi::um::{minwinbase::FILETIME, sysinfoapi::GetSystemTimeAsFileTime};
let mut ftime: FILETIME = core::mem::zeroed();
GetSystemTimeAsFileTime(&mut ftime);
hasher.write(
&ftime as *const _ as *const u8,
core::mem::size_of::<FILETIME>(),
);
}
}
// --- POSIX: various clocks (timespecs) + gettimeofday -------------------
#[cfg(not(WIN32))]
unsafe {
// struct timespec ts = {};
let mut ts: libc::timespec = core::mem::zeroed();
// #ifdef CLOCK_MONOTONIC
#[cfg(any(
target_os = "linux", target_os = "android",
target_os = "freebsd", target_os = "netbsd",
target_os = "openbsd", target_os = "dragonfly", target_os = "macos"
))]
if libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) == 0 {
hasher.write(
&ts as *const _ as *const u8,
core::mem::size_of::<libc::timespec>(),
);
}
// #ifdef CLOCK_REALTIME
if libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts) == 0 {
hasher.write(
&ts as *const _ as *const u8,
core::mem::size_of::<libc::timespec>(),
);
}
// #ifdef CLOCK_BOOTTIME (Linux/Android)
#[cfg(any(target_os = "linux", target_os = "android"))]
if libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut ts) == 0 {
hasher.write(
&ts as *const _ as *const u8,
core::mem::size_of::<libc::timespec>(),
);
}
// // gettimeofday is available on all UNIX systems (microsecond precision).
let mut tv: libc::timeval = core::mem::zeroed();
libc::gettimeofday(&mut tv, core::ptr::null_mut());
hasher.write(
&tv as *const _ as *const u8,
core::mem::size_of::<libc::timeval>(),
);
}
// --- C++11 clocks: write three counts in this exact order ---------------
// system_clock::now().time_since_epoch().count();
// steady_clock::now().time_since_epoch().count();
// high_resolution_clock::now().time_since_epoch().count();
//
// We serialize them as 64-bit integers, matching the common libstdc++
// representation (nanoseconds) on POSIX. On Windows, we derive a
// monotonic-ish counter similarly to Core’s intent (value content is
// entropy; exact epoch isn’t relied upon).
{
// system_clock count
let sys_ns: i64 = {
#[cfg(not(WIN32))]
unsafe {
let mut ts: libc::timespec = core::mem::zeroed();
if libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts) == 0 {
let ns = (ts.tv_sec as i128) * 1_000_000_000i128 + (ts.tv_nsec as i128);
ns as i64
} else {
0
}
}
#[cfg(WIN32)]
{
// FILETIME is in 100ns ticks since 1601-01-01; multiply to ns.
use winapi::um::{minwinbase::FILETIME, sysinfoapi::GetSystemTimeAsFileTime};
let mut ft: FILETIME = unsafe { core::mem::zeroed() };
unsafe { GetSystemTimeAsFileTime(&mut ft) };
let v100ns = ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64);
(v100ns.saturating_mul(100) as i64)
}
};
hasher.write(&sys_ns as *const _ as *const u8, core::mem::size_of_val(&sys_ns));
// steady_clock count
#[cfg(not(WIN32))]
let steady_ns: i64 = unsafe {
let mut ts: libc::timespec = core::mem::zeroed();
if libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) == 0 {
let ns = (ts.tv_sec as i128) * 1_000_000_000i128 + (ts.tv_nsec as i128);
ns as i64
} else {
0
}
};
#[cfg(WIN32)]
let steady_ns: i64 = {
// Use our high‑res counter fallback; Core treats this as entropy.
get_performance_counter() as i64
};
hasher.write(
&steady_ns as *const _ as *const u8,
core::mem::size_of_val(&steady_ns),
);
// high_resolution_clock count (libstdc++ typically aliases steady_clock)
let high_ns: i64 = steady_ns;
hasher.write(
&high_ns as *const _ as *const u8,
core::mem::size_of_val(&high_ns),
);
}
// --- POSIX: current resource usage --------------------------------------
#[cfg(not(WIN32))]
unsafe {
let mut usage: libc::rusage = core::mem::zeroed();
if libc::getrusage(libc::RUSAGE_SELF, &mut usage) == 0 {
hasher.write(
&usage as *const _ as *const u8,
core::mem::size_of::<libc::rusage>(),
);
}
}
// --- Linux: a handful of small /proc snapshots --------------------------
#[cfg(all(not(WIN32), target_os = "linux"))]
{
use std::ffi::CString;
for p in [
"/proc/diskstats",
"/proc/vmstat",
"/proc/schedstat",
"/proc/zoneinfo",
"/proc/meminfo",
"/proc/softirqs",
"/proc/stat",
"/proc/self/schedstat",
"/proc/self/status",
] {
if let Ok(cstr) = CString::new(p) {
add_file(hasher, cstr.as_ptr());
}
}
}
// --- Stack and heap location (exact pointer bytes, order preserved) -----
unsafe {
// c_void* addr = malloc(4097);
let addr = libc::malloc(4097);
// hasher << &addr
let pptr = &addr as *const *mut libc::c_void;
hasher.write(pptr as *const u8, core::mem::size_of::<*mut libc::c_void>());
// hasher << addr
hasher.write(
&addr as *const _ as *const u8,
core::mem::size_of::<*mut libc::c_void>(),
);
// free(addr);
libc::free(addr);
}
}
#[cfg(test)]
mod rand_add_dynamic_env_spec {
use super::*;
#[traced_test]
fn increases_hasher_size_at_all() {
let mut h = Sha512::default();
let before = h.size();
rand_add_dynamic_env(&mut h);
let after = h.size();
assert!(after > before, "rand_add_dynamic_env did not add any bytes");
}
// Windows: we know we always write FILETIME (16 bytes) and
// 3 chrono-like counters (3 * i64), and two pointer-sized values (&addr and addr).
#[traced_test]
#[cfg(WIN32)]
fn includes_minimum_windows_material() {
let mut h = Sha512::default();
let before = h.size();
rand_add_dynamic_env(&mut h);
let after = h.size();
let ptr_sz = core::mem::size_of::<*mut core::ffi::c_void>();
let filetime_sz = core::mem::size_of::<winapi::um::minwinbase::FILETIME>();
let chrono_counts = 3 * core::mem::size_of::<i64>();
let heap_stack = 2 * ptr_sz;
let min_expected: u64 = (filetime_sz + chrono_counts + heap_stack) as u64;
assert!(
after >= before + min_expected,
"expected at least {} bytes, got {}",
min_expected,
after - before
);
}
// POSIX: we *always* write a timeval, three 64-bit counts (system/steady/high),
// and two pointer-sized values (&addr and addr). Other pieces (timespecs/rusage)
// are best-effort; we do not count them in the minimum.
#[traced_test]
#[cfg(not(WIN32))]
fn includes_minimum_posix_material() {
let mut h = Sha512::default();
let before = h.size();
rand_add_dynamic_env(&mut h);
let after = h.size();
let ptr_sz = core::mem::size_of::<*mut core::ffi::c_void>();
let tv_sz = core::mem::size_of::<libc::timeval>();
let chrono_counts = 3 * core::mem::size_of::<i64>();
let heap_stack = 2 * ptr_sz;
let min_expected: u64 = (tv_sz + chrono_counts + heap_stack) as u64;
assert!(
after >= before + min_expected,
"expected at least {} bytes, got {}",
min_expected,
after - before
);
}
// Calling it multiple times should keep increasing the hasher's byte count.
#[traced_test]
fn multiple_calls_accumulate() {
let mut h = Sha512::default();
let b0 = h.size();
rand_add_dynamic_env(&mut h);
let b1 = h.size();
rand_add_dynamic_env(&mut h);
let b2 = h.size();
assert!(b1 > b0, "first call did not add bytes");
assert!(b2 > b1, "second call did not add bytes");
}
// Linux: make sure /proc reads don't panic and at least contribute something
// beyond the strictly minimal pieces we already counted.
// (We don't assume any particular file size; we just expect *some* growth.)
#[traced_test]
#[cfg(all(not(WIN32), target_os = "linux"))]
fn linux_proc_is_best_effort_but_nonzero() {
let mut h = Sha512::default();
let before = h.size();
rand_add_dynamic_env(&mut h);
let after = h.size();
// Very weak assertion that catches no-op implementations on Linux.
assert!(after > before, "/proc snapshot did not add any bytes");
}
}