Home | History | Annotate | Line # | Download | only in util
      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