Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
entropy.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "entropy.hh"
3#include "randomhash.hh"
4#include "strings.hh"
5#include "platform.hh"
6#include <random>
7#include <setjmp.h>
8#include <signal.h>
9#include <string.h>
10#include <stdio.h>
11#include <unistd.h>
12#include <time.h>
13#include <glob.h>
14#include <sys/types.h>
15#include <sys/socket.h>
16#include <netinet/in.h>
17#include <net/if.h>
18#include <sys/ioctl.h>
19#include <sys/stat.h>
20#include <sys/times.h>
21#include <sys/resource.h>
22#include <sys/time.h> // gettimeofday
23#include <linux/random.h> // GRND_NONBLOCK
24#include <sys/syscall.h> // __NR_getrandom
25#include <sys/utsname.h> // uname
26#if defined (__i386__) || defined (__x86_64__)
27# include <x86intrin.h> // __rdtsc
28#endif
29
30namespace Ase {
31
32static int
33getrandom (void *buffer, size_t count, unsigned flags)
34{
35#ifdef __NR_getrandom
36 const long ret = syscall (__NR_getrandom, buffer, count, flags);
37 if (ret > 0)
38 return ret;
39#endif
40 FILE *file = fopen ("/dev/urandom", "r"); // fallback
41 if (file)
42 {
43 const size_t l = fread (buffer, 1, count, file);
44 fclose (file);
45 if (l > 0)
46 return 0;
47 }
48 errno = ENOSYS;
49 return 0;
50}
51
52static bool
53hash_getrandom (KeccakRng &pool)
54{
55 uint64_t buffer[25];
56 int flags = 0;
57#ifdef GRND_NONBLOCK
58 flags |= GRND_NONBLOCK;
59#endif
60 int l = getrandom (buffer, sizeof (buffer), flags);
61 if (l > 0)
62 {
63 pool.xor_seed (buffer, l / sizeof (buffer[0]));
64 return true;
65 }
66 return false;
67}
68
69template<class Data> static void
70hash_anything (KeccakRng &pool, const Data &data)
71{
72 const uint64_t *d64 = (const uint64_t*) &data;
73 uint len = sizeof (data);
74 uint64_t dummy;
75 if (sizeof (Data) < sizeof (uint64_t))
76 {
77 dummy = 0;
78 memcpy (&dummy, &data, sizeof (Data));
79 d64 = &dummy;
80 len = 1;
81 }
82 pool.xor_seed (d64, len / sizeof (d64[0]));
83}
84
85static bool
86hash_macs (KeccakRng &pool)
87{
88 // query devices for the AF_INET family which might be the only one supported
89 int sockfd = socket (AF_INET, SOCK_DGRAM, 0); // open IPv4 UDP socket
90 if (sockfd < 0)
91 return false;
92 // discover devices by index, might include devices that are 'down'
93 String devices;
94 int ret = 0;
95 for (size_t j = 0; j <= 1 || ret >= 0; j++) // try [0] and [1]
96 {
97 struct ifreq iftmp = { { { 0 }, }, };
98 iftmp.ifr_ifindex = j;
99 ret = ioctl (sockfd, SIOCGIFNAME, &iftmp);
100 if (ret < 0)
101 continue;
102 if (!devices.empty())
103 devices += ",";
104 devices += iftmp.ifr_name; // found name
105 devices += "//"; // no inet address
106 if (ioctl (sockfd, SIOCGIFHWADDR, &iftmp) >= 0) // query MAC
107 {
108 const uint8_t *mac = (const uint8_t*) iftmp.ifr_hwaddr.sa_data;
109 devices += string_format ("%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
110 }
111 }
112 // discover devices that are 'up'
113 char ifcbuffer[8192] = { 0, }; // buffer space for returning interfaces
114 struct ifconf ifc;
115 ifc.ifc_len = sizeof (ifcbuffer);
116 ifc.ifc_buf = ifcbuffer;
117 ret = ioctl (sockfd, SIOCGIFCONF, &ifc);
118 for (size_t i = 0; ret >= 0 && i < ifc.ifc_len / sizeof (struct ifreq); i++)
119 {
120 const struct ifreq *iface = &ifc.ifc_req[i];
121 if (!devices.empty())
122 devices += ",";
123 devices += iface->ifr_name; // found name
124 devices += "/";
125 const uint8_t *addr = (const uint8_t*) &((struct sockaddr_in*) &iface->ifr_addr)->sin_addr;
126 devices += string_format ("%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]);
127 devices += "/"; // added inet address
128 if (ioctl (sockfd, SIOCGIFHWADDR, iface) >= 0) // query MAC
129 {
130 const uint8_t *mac = (const uint8_t*) iface->ifr_hwaddr.sa_data;
131 devices += string_format ("%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
132 }
133 }
134 close (sockfd);
135 devices.resize (((devices.size() + 7) / 8) * 8); // align to uint64_t
136 pool.xor_seed ((const uint64_t*) devices.data(), devices.size() / 8);
137 // printerr ("SEED(MACs): %s\n", devices.c_str());
138 return !devices.empty();
139}
140
141static bool
142hash_stat (KeccakRng &pool, const char *filename)
143{
144 struct {
145 struct stat stat;
146 uint64_t padding;
147 } s = { { 0 }, };
148 if (stat (filename, &s.stat) == 0)
149 {
150 pool.xor_seed ((const uint64_t*) &s.stat, sizeof (s.stat) / sizeof (uint64_t));
151 // printerr ("SEED(%s): atime=%u mtime=%u ctime=%u size=%u...\n", filename, s.stat.st_atime, s.stat.st_atime, s.stat.st_atime, s.stat.st_size);
152 return true;
153 }
154 return false;
155}
156
157static bool
158hash_file (KeccakRng &pool, const char *filename, const size_t maxbytes = 16384)
159{
160 FILE *file = fopen (filename, "r");
161 if (file)
162 {
163 uint64_t buffer[maxbytes / 8];
164 const size_t l = fread (buffer, sizeof (buffer[0]), sizeof (buffer) / sizeof (buffer[0]), file);
165 fclose (file);
166 if (l > 0)
167 {
168 pool.xor_seed (buffer, l);
169 // printerr ("SEED(%s): %s\n", filename, String ((const char*) buffer, std::min (l * 8, size_t (48))));
170 return true;
171 }
172 }
173 return false;
174}
175
176static bool __attribute__ ((__unused__))
177hash_glob (KeccakRng &pool, const char *fileglob, const size_t maxbytes = 16384)
178{
179 glob_t globbuf = { 0, };
180 glob (fileglob, GLOB_NOSORT, NULL, &globbuf);
181 bool success = false;
182 for (size_t i = globbuf.gl_offs; i < globbuf.gl_pathc; i++)
183 success |= hash_file (pool, globbuf.gl_pathv[i], maxbytes);
184 globfree (&globbuf);
185 return success;
186}
187
188struct HashStamp {
189 uint64_t bstamp;
190#ifdef CLOCK_PROCESS_CPUTIME_ID
191 uint64_t tcpu;
192#endif
193#if defined (__i386__) || defined (__x86_64__)
194 uint64_t tsc;
195#endif
196};
197
198static void __attribute__ ((__noinline__))
199hash_time (HashStamp *hstamp)
200{
201 asm (""); // enfore __noinline__
202 hstamp->bstamp = timestamp_benchmark();
203#ifdef CLOCK_PROCESS_CPUTIME_ID
204 {
205 struct timespec ts;
206 clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &ts);
207 hstamp->tcpu = ts.tv_sec * uint64_t (1000000000) + ts.tv_nsec;
208 }
209#endif
210#if defined (__i386__) || defined (__x86_64__)
211 hstamp->tsc = __rdtsc();
212#endif
213}
214
215static void
216hash_cpu_usage (KeccakRng &pool)
217{
218 union {
219 uint64_t ui64[24];
220 struct {
221 struct rusage rusage; // 144 bytes
222 struct tms tms; // 32 bytes
223 clock_t clk; // 8 bytes
224 };
225 } u = { { 0 }, };
226 getrusage (RUSAGE_SELF, &u.rusage);
227 u.clk = times (&u.tms);
228 pool.xor_seed (u.ui64, sizeof (u.ui64) / sizeof (u.ui64[0]));
229}
230
231static void
232hash_sys_structs (KeccakRng &pool)
233{
234 struct SysStructs {
235 uint64 alignment_dummy1;
236 struct timezone tz;
237 struct timeval tv;
238 struct utsname uts;
239 uint64 alignment_dummy2;
240 };
241 SysStructs sst = { 0, };
242 gettimeofday (&sst.tv, &sst.tz);
243 uname (&sst.uts);
244 pool.xor_seed ((uint64*) &sst, sizeof (sst) / sizeof (uint64));
245}
246
247static bool
248get_rdrand (uint64 *u, uint count)
249{
250#if defined (__i386__) || defined (__x86_64__)
251 if (strstr (cpu_info().c_str(), " rdrand"))
252 for (uint i = 0; i < count; i++)
253 __asm__ __volatile__ ("rdrand %0" : "=r" (u[i]));
254 else
255 for (uint i = 0; i < count; i++)
256 {
257 uint64_t d = __rdtsc(); // fallback
258 u[i] = pcg_hash64 ((const uint8*) &d, sizeof (d), 0xeaeaeaea113377ccULL);
259 }
260 return true;
261#endif
262 return false;
263}
264
265static void
266get_arc4random (uint64 *u, uint count)
267{
268#ifdef __OpenBSD__
269 for (uint i = 0; i < count; i++)
270 u[i] = uint64_t (arc4random()) << 32;
271 arc4random_stir();
272 for (uint i = 0; i < count; i++)
273 u[i] |= arc4random();
274#endif
275}
276
277static void
278runtime_entropy (KeccakRng &pool)
279{
280 HashStamp hash_stamps[64] = { { 0 }, };
281 HashStamp *stamp = &hash_stamps[0];
282 hash_time (stamp++);
283 uint64_t uint_array[64] = { 0, };
284 uint64_t *uintp = &uint_array[0];
285 hash_time (stamp++); *uintp++ = timestamp_realtime();
286 hash_time (stamp++); hash_cpu_usage (pool);
287 hash_time (stamp++); *uintp++ = timestamp_benchmark();
288 hash_time (stamp++); get_rdrand (uintp, 8); uintp += 8;
289 hash_time (stamp++); get_arc4random (uintp, 8); uintp += 8;
290 hash_time (stamp++); *uintp++ = this_thread_gettid();
291 hash_time (stamp++); *uintp++ = getuid();
292 hash_time (stamp++); *uintp++ = geteuid();
293 hash_time (stamp++); *uintp++ = getgid();
294 hash_time (stamp++); *uintp++ = getegid();
295 hash_time (stamp++); *uintp++ = getpid();
296 hash_time (stamp++); *uintp++ = getsid (0);
297 int ppid;
298 hash_time (stamp++); *uintp++ = ppid = getppid();
299 hash_time (stamp++); *uintp++ = getsid (ppid);
300 hash_time (stamp++); *uintp++ = getpgrp();
301 hash_time (stamp++); *uintp++ = tcgetpgrp (0);
302 hash_time (stamp++); hash_getrandom (pool);
303 hash_time (stamp++); { *uintp++ = std::random_device()(); } // may open devices, so destroy early on
304 hash_time (stamp++); hash_anything (pool, std::chrono::high_resolution_clock::now().time_since_epoch().count());
305 hash_time (stamp++); hash_anything (pool, std::chrono::steady_clock::now().time_since_epoch().count());
306 hash_time (stamp++); hash_anything (pool, std::chrono::system_clock::now().time_since_epoch().count());
307 hash_time (stamp++); hash_anything (pool, std::this_thread::get_id());
308 String compiletime = __DATE__ __TIME__ __FILE__ __TIMESTAMP__;
309 hash_time (stamp++); *uintp++ = fnv1a_consthash64 (compiletime.c_str()); // compilation entropy
310 hash_time (stamp++); *uintp++ = size_t (compiletime.data()); // heap address
311 static int dummy;
312 hash_time (stamp++); *uintp++ = size_t (&dummy); // data segment
313 hash_time (stamp++); *uintp++ = size_t ("PATH"); // const data segment
314 hash_time (stamp++); *uintp++ = size_t (getenv ("PATH")); // a.out address
315 hash_time (stamp++); *uintp++ = size_t (&stamp); // stack segment
316 hash_time (stamp++); *uintp++ = size_t (&runtime_entropy); // code segment
317 hash_time (stamp++); *uintp++ = size_t (&::fopen); // libc code segment
318 hash_time (stamp++); *uintp++ = size_t (&std::string::npos); // stl address
319 hash_time (stamp++); *uintp++ = fnv1a_consthash64 (cpu_info().c_str()); // CPU type influence
320 hash_time (stamp++); hash_sys_structs (pool);
321 hash_time (stamp++); *uintp++ = timestamp_benchmark();
322 hash_time (stamp++); hash_cpu_usage (pool);
323 hash_time (stamp++); *uintp++ = timestamp_realtime();
324 ASE_ASSERT_RETURN (uintp <= &uint_array[sizeof (uint_array) / sizeof (uint_array[0])]);
325 pool.xor_seed (&uint_array[0], uintp - &uint_array[0]);
326 hash_time (stamp++);
327 ASE_ASSERT_RETURN (stamp <= &hash_stamps[sizeof (hash_stamps) / sizeof (hash_stamps[0])]);
328 pool.xor_seed ((uint64_t*) &hash_stamps[0], (stamp - &hash_stamps[0]) * sizeof (hash_stamps[0]) / sizeof (uint64_t));
329 // printerr ("%s(): duration=%fµsec\n", __func__, (stamp[-1].bstamp - hash_stamps[0].bstamp) / 1000.0);
330}
331
332static void
333system_entropy (KeccakRng &pool)
334{
335 HashStamp hash_stamps[64] = { { 0 }, };
336 HashStamp *stamp = &hash_stamps[0];
337 hash_time (stamp++);
338 uint64_t uint_array[64] = { 0, };
339 uint64_t *uintp = &uint_array[0];
340 hash_time (stamp++); *uintp++ = timestamp_realtime();
341 hash_time (stamp++); *uintp++ = timestamp_benchmark();
342 hash_time (stamp++); *uintp++ = timestamp_startup();
343 hash_time (stamp++); hash_cpu_usage (pool);
344 hash_time (stamp++); hash_file (pool, "/proc/sys/kernel/random/boot_id");
345 hash_time (stamp++); hash_file (pool, "/proc/version");
346 hash_time (stamp++); hash_file (pool, "/proc/cpuinfo");
347 hash_time (stamp++); hash_file (pool, "/proc/devices");
348 hash_time (stamp++); hash_file (pool, "/proc/meminfo");
349 hash_time (stamp++); hash_file (pool, "/proc/buddyinfo");
350 hash_time (stamp++); hash_file (pool, "/proc/diskstats");
351 hash_time (stamp++); hash_file (pool, "/proc/1/stat");
352 hash_time (stamp++); hash_file (pool, "/proc/1/sched");
353 hash_time (stamp++); hash_file (pool, "/proc/1/schedstat");
354 hash_time (stamp++); hash_file (pool, "/proc/self/stat");
355 hash_time (stamp++); hash_file (pool, "/proc/self/sched");
356 hash_time (stamp++); hash_file (pool, "/proc/self/schedstat");
357 hash_time (stamp++); hash_macs (pool);
358 // hash_glob: "/sys/devices/**/net/*/address", "/sys/devices/*/*/*/ieee80211/phy*/*address*"
359 hash_time (stamp++); hash_file (pool, "/proc/uptime");
360 hash_time (stamp++); hash_file (pool, "/proc/user_beancounters");
361 hash_time (stamp++); hash_file (pool, "/proc/driver/rtc");
362 hash_time (stamp++); hash_stat (pool, "/var/log/syslog"); // for mtime
363 hash_time (stamp++); hash_stat (pool, "/var/log/auth.log"); // for mtime
364 hash_time (stamp++); hash_stat (pool, "/var/tmp"); // for mtime
365 hash_time (stamp++); hash_stat (pool, "/tmp"); // for mtime
366 hash_time (stamp++); hash_stat (pool, "/dev"); // for mtime
367 hash_time (stamp++); hash_stat (pool, "/var/lib/ntp/ntp.drift"); // for mtime
368 hash_time (stamp++); hash_stat (pool, "/var/run/utmp"); // for mtime & atime
369 hash_time (stamp++); hash_stat (pool, "/var/log/wtmp"); // for mtime & atime
370 hash_time (stamp++); hash_stat (pool, "/sbin/init"); // for atime
371 hash_time (stamp++); hash_stat (pool, "/var/spool"); // for atime
372 hash_time (stamp++); hash_stat (pool, "/var/spool/cron"); // for atime
373 hash_time (stamp++); hash_stat (pool, "/var/spool/anacron"); // for atime
374 hash_time (stamp++); hash_file (pool, "/dev/urandom", 400);
375 hash_time (stamp++); hash_file (pool, "/proc/sys/kernel/random/uuid");
376 hash_time (stamp++); hash_file (pool, "/proc/schedstat");
377 hash_time (stamp++); hash_file (pool, "/proc/sched_debug");
378 hash_time (stamp++); hash_file (pool, "/proc/fairsched");
379 hash_time (stamp++); hash_file (pool, "/proc/interrupts");
380 hash_time (stamp++); hash_file (pool, "/proc/loadavg");
381 hash_time (stamp++); hash_file (pool, "/proc/softirqs");
382 hash_time (stamp++); hash_file (pool, "/proc/stat");
383 hash_time (stamp++); hash_file (pool, "/proc/net/fib_triestat");
384 hash_time (stamp++); hash_file (pool, "/proc/net/netstat");
385 hash_time (stamp++); hash_file (pool, "/proc/net/dev");
386 hash_time (stamp++); hash_file (pool, "/proc/vz/vestat");
387 hash_time (stamp++); hash_cpu_usage (pool);
388 hash_time (stamp++); *uintp++ = timestamp_realtime();
389 ASE_ASSERT_RETURN (uintp <= &uint_array[sizeof (uint_array) / sizeof (uint_array[0])]);
390 pool.xor_seed (&uint_array[0], uintp - &uint_array[0]);
391 hash_time (stamp++);
392 ASE_ASSERT_RETURN (stamp <= &hash_stamps[sizeof (hash_stamps) / sizeof (hash_stamps[0])]);
393 pool.xor_seed ((uint64_t*) &hash_stamps[0], (stamp - &hash_stamps[0]) * sizeof (hash_stamps[0]) / sizeof (uint64_t));
394 // printerr ("%s(): duration=%fµsec\n", __func__, (stamp[-1].bstamp - hash_stamps[0].bstamp) / 1000.0);
395}
396
407void
409{
410 KeccakRng pool (128, 8);
411 runtime_entropy (pool);
412 for (size_t i = 0; i < n; i++)
413 data[i] = pool();
414}
415
425void
427{
428 KeccakRng pool (512, 24);
429 hash_cpu_usage (pool);
430 runtime_entropy (pool);
431 system_entropy (pool);
432 hash_cpu_usage (pool);
433 for (size_t i = 0; i < n; i++)
434 data[i] = pool();
435}
436
437} // Ase
#define ENOSYS
clock_gettime
close
T count(T... args)
#define ASE_ASSERT_RETURN(expr,...)
Return from the current function if expr evaluates to false and issue an assertion warning.
Definition cxxaux.hh:82
T data(T... args)
timezone
errno
fclose
fopen
fread
stat
T get_id(T... args)
getegid
geteuid
getgid
getpgrp
getpid
getppid
getrusage
getsid
gettimeofday
getuid
glob
ioctl
memcpy
The Anklang C++ API namespace.
Definition api.hh:9
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:25
String cpu_info()
Retrieve string identifying the runtime CPU type.
Definition platform.cc:480
void collect_runtime_entropy(uint64 *data, size_t n)
Collect entropy from the current process, usually quicker than collect_system_entropy().
Definition entropy.cc:408
uint8_t uint8
An 8-bit unsigned integer.
Definition cxxaux.hh:22
uint64 timestamp_benchmark()
Returns benchmark timestamp in nano-seconds, clock starts around program startup.
Definition platform.cc:601
void collect_system_entropy(uint64 *data, size_t n)
Collect entropy from system devices, like interrupt counters, clocks and random devices.
Definition entropy.cc:426
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:18
uint64 timestamp_startup()
Provides the timestamp_realtime() value from program startup.
Definition platform.cc:570
uint64 timestamp_realtime()
Return the current time as uint64 in µseconds.
Definition platform.cc:578
socket
typedef uint64_t
typedef clock_t
tcgetpgrp
times
T timespec(T... args)
uname