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 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