summaryrefslogtreecommitdiffstats
path: root/src/util/ldseed.c
blob: c23115272ecafb6c58d90fd1141cd82fc133b851 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*++
/* NAME
/*	ldseed 3
/* SUMMARY
/*	seed for non-cryptographic applications
/* SYNOPSIS
/*	#include <ldseed.h>
/*
/*	void	ldseed(
/*	void	*dst,
/*	size_t	len)
/* DESCRIPTION
/*	ldseed() preferably extracts pseudo-random bits from
/*	/dev/urandom, a non-blocking device that is available on
/*	modern systems.
/*
/*	On systems where /dev/urandom is unavailable or does not
/*	immediately return the requested amount of randomness,
/*	ldseed() falls back to a combination of wallclock time,
/*	the time since boot, and the process ID.
/* BUGS
/*	With Linux "the O_NONBLOCK flag has no effect when opening
/*	/dev/urandom", but reads "can incur an appreciable delay
/*	when requesting large amounts of data". Apparently, "large"
/*	means more than 256 bytes.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

 /*
  * System library
  */
#include <sys_defs.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>			/* CHAR_BIT */

 /*
  * Utility library.
  */
#include <iostuff.h>
#include <msg.h>
#include <ldseed.h>

 /*
  * Different systems have different names for non-wallclock time.
  */
#ifdef CLOCK_UPTIME
#define NON_WALLTIME_CLOCK      CLOCK_UPTIME
#elif defined(CLOCK_BOOTTIME)
#define NON_WALLTIME_CLOCK      CLOCK_BOOTTIME
#elif defined(CLOCK_MONOTONIC)
#define NON_WALLTIME_CLOCK      CLOCK_MONOTONIC
#elif defined(CLOCK_HIGHRES)
#define NON_WALLTIME_CLOCK      CLOCK_HIGHRES
#endif

/* ldseed - best-effort, low-dependency seed */

void    ldseed(void *dst, size_t len)
{
    int     count;
    int     fd;
    int     n;
    time_t  fallback = 0;

    /*
     * Medium-quality seed.
     */
    if ((fd = open("/dev/urandom", O_RDONLY)) > 0) {
	non_blocking(fd, NON_BLOCKING);
	count = read(fd, dst, len);
	(void) close(fd);
	if (count == len)
	    return;
    }

    /*
     * Low-quality seed. Based on 1) the time since boot (good when an
     * attacker knows the program start time but not the system boot time),
     * and 2) absolute time (good when an attacker does not know the program
     * start time). Assumes a system with better than microsecond resolution,
     * and a network stack that does not leak the time since boot, for
     * example, through TCP or ICMP timestamps. With those caveats, this seed
     * is good for 20-30 bits of randomness.
     */
#ifdef NON_WALLTIME_CLOCK
    {
	struct timespec ts;

	if (clock_gettime(NON_WALLTIME_CLOCK, &ts) != 0)
	    msg_fatal("clock_gettime() failed: %m");
	fallback += ts.tv_sec ^ ts.tv_nsec;
    }
#elif defined(USE_GETHRTIME)
    fallback += gethrtime();
#endif

#ifdef CLOCK_REALTIME
    {
	struct timespec ts;

	if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
	    msg_fatal("clock_gettime() failed: %m");
	fallback += ts.tv_sec ^ ts.tv_nsec;
    }
#else
    {
	struct timeval tv;

	if (GETTIMEOFDAY(&tv) != 0)
	    msg_fatal("gettimeofday() failed: %m");
	fallback += tv.tv_sec + tv.tv_usec;
    }
#endif
    fallback += getpid();

    /*
     * Copy the least significant bytes first, because those are the most
     * volatile.
     */
    for (n = 0; n < sizeof(fallback) && n < len; n++) {
	*(char *) dst++ ^= (fallback & 0xff);
	fallback >>= CHAR_BIT;
    }
    return;
}