xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/ldseed.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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