1 /* $NetBSD: ldseed.c,v 1.2 2022/10/08 16:12:50 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* ldseed 3
6 /* SUMMARY
7 /* seed for non-cryptographic applications
8 /* SYNOPSIS
9 /* #include <ldseed.h>
10 /*
11 /* void ldseed(
12 /* void *dst,
13 /* size_t len)
14 /* DESCRIPTION
15 /* ldseed() preferably extracts pseudo-random bits from
16 /* /dev/urandom, a non-blocking device that is available on
17 /* modern systems.
18 /*
19 /* On systems where /dev/urandom is unavailable or does not
20 /* immediately return the requested amount of randomness,
21 /* ldseed() falls back to a combination of wallclock time,
22 /* the time since boot, and the process ID.
23 /* BUGS
24 /* With Linux "the O_NONBLOCK flag has no effect when opening
25 /* /dev/urandom", but reads "can incur an appreciable delay
26 /* when requesting large amounts of data". Apparently, "large"
27 /* means more than 256 bytes.
28 /* LICENSE
29 /* .ad
30 /* .fi
31 /* The Secure Mailer license must be distributed with this software.
32 /* AUTHOR(S)
33 /* Wietse Venema
34 /* Google, Inc.
35 /* 111 8th Avenue
36 /* New York, NY 10011, USA
37 /*--*/
38
39 /*
40 * System library
41 */
42 #include <sys_defs.h>
43 #include <string.h>
44 #include <sys/time.h>
45 #include <time.h>
46 #include <stdlib.h>
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include <limits.h> /* CHAR_BIT */
50
51 /*
52 * Utility library.
53 */
54 #include <iostuff.h>
55 #include <msg.h>
56 #include <ldseed.h>
57
58 /*
59 * Different systems have different names for non-wallclock time.
60 */
61 #ifdef CLOCK_UPTIME
62 #define NON_WALLTIME_CLOCK CLOCK_UPTIME
63 #elif defined(CLOCK_BOOTTIME)
64 #define NON_WALLTIME_CLOCK CLOCK_BOOTTIME
65 #elif defined(CLOCK_MONOTONIC)
66 #define NON_WALLTIME_CLOCK CLOCK_MONOTONIC
67 #elif defined(CLOCK_HIGHRES)
68 #define NON_WALLTIME_CLOCK CLOCK_HIGHRES
69 #endif
70
71 /* ldseed - best-effort, low-dependency seed */
72
ldseed(void * dst,size_t len)73 void ldseed(void *dst, size_t len)
74 {
75 int count;
76 int fd;
77 int n;
78 time_t fallback = 0;
79
80 /*
81 * Medium-quality seed.
82 */
83 if ((fd = open("/dev/urandom", O_RDONLY)) > 0) {
84 non_blocking(fd, NON_BLOCKING);
85 count = read(fd, dst, len);
86 (void) close(fd);
87 if (count == len)
88 return;
89 }
90
91 /*
92 * Low-quality seed. Based on 1) the time since boot (good when an
93 * attacker knows the program start time but not the system boot time),
94 * and 2) absolute time (good when an attacker does not know the program
95 * start time). Assumes a system with better than microsecond resolution,
96 * and a network stack that does not leak the time since boot, for
97 * example, through TCP or ICMP timestamps. With those caveats, this seed
98 * is good for 20-30 bits of randomness.
99 */
100 #ifdef NON_WALLTIME_CLOCK
101 {
102 struct timespec ts;
103
104 if (clock_gettime(NON_WALLTIME_CLOCK, &ts) != 0)
105 msg_fatal("clock_gettime() failed: %m");
106 fallback += ts.tv_sec ^ ts.tv_nsec;
107 }
108 #elif defined(USE_GETHRTIME)
109 fallback += gethrtime();
110 #endif
111
112 #ifdef CLOCK_REALTIME
113 {
114 struct timespec ts;
115
116 if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
117 msg_fatal("clock_gettime() failed: %m");
118 fallback += ts.tv_sec ^ ts.tv_nsec;
119 }
120 #else
121 {
122 struct timeval tv;
123
124 if (GETTIMEOFDAY(&tv) != 0)
125 msg_fatal("gettimeofday() failed: %m");
126 fallback += tv.tv_sec + tv.tv_usec;
127 }
128 #endif
129 fallback += getpid();
130
131 /*
132 * Copy the least significant bytes first, because those are the most
133 * volatile.
134 */
135 for (n = 0; n < sizeof(fallback) && n < len; n++) {
136 *(char *) dst++ ^= (fallback & 0xff);
137 fallback >>= CHAR_BIT;
138 }
139 return;
140 }
141