comparison random.c @ 687:167fdc091c05

Improve RNG seeding. Try to read from /dev/urandom multiple times, take input from extra sources, and use /dev/random when generating private keys
author Matt Johnston <matt@ucc.asn.au>
date Fri, 29 Jun 2012 23:19:43 +0800
parents 2d896267f16d
children 650c41a4909a
comparison
equal deleted inserted replaced
683:63f8d6c469cf 687:167fdc091c05
25 #include "includes.h" 25 #include "includes.h"
26 #include "buffer.h" 26 #include "buffer.h"
27 #include "dbutil.h" 27 #include "dbutil.h"
28 #include "bignum.h" 28 #include "bignum.h"
29 29
30 static int donerandinit = 0;
31
32 /* this is used to generate unique output from the same hashpool */ 30 /* this is used to generate unique output from the same hashpool */
33 static uint32_t counter = 0; 31 static uint32_t counter = 0;
34 /* the max value for the counter, so it won't integer overflow */ 32 /* the max value for the counter, so it won't integer overflow */
35 #define MAX_COUNTER 1<<30 33 #define MAX_COUNTER 1<<30
36 34
37 static unsigned char hashpool[SHA1_HASH_SIZE]; 35 static unsigned char hashpool[SHA1_HASH_SIZE] = {0};
36 static int donerandinit = 0;
38 37
39 #define INIT_SEED_SIZE 32 /* 256 bits */ 38 #define INIT_SEED_SIZE 32 /* 256 bits */
40
41 static void readrand(unsigned char* buf, unsigned int buflen);
42 39
43 /* The basic setup is we read some data from /dev/(u)random or prngd and hash it 40 /* The basic setup is we read some data from /dev/(u)random or prngd and hash it
44 * into hashpool. To read data, we hash together current hashpool contents, 41 * into hashpool. To read data, we hash together current hashpool contents,
45 * and a counter. We feed more data in by hashing the current pool and new 42 * and a counter. We feed more data in by hashing the current pool and new
46 * data into the pool. 43 * data into the pool.
48 * It is important to ensure that counter doesn't wrap around before we 45 * It is important to ensure that counter doesn't wrap around before we
49 * feed in new entropy. 46 * feed in new entropy.
50 * 47 *
51 */ 48 */
52 49
53 static void readrand(unsigned char* buf, unsigned int buflen) { 50 /* Pass len=0 to hash an entire file */
54 51 static int
52 process_file(hash_state *hs, const char *filename,
53 unsigned int len, int prngd)
54 {
55 static int already_blocked = 0; 55 static int already_blocked = 0;
56 int readfd; 56 int readfd;
57 unsigned int readpos; 57 unsigned int readcount;
58 int readlen; 58 int ret = DROPBEAR_FAILURE;
59
59 #ifdef DROPBEAR_PRNGD_SOCKET 60 #ifdef DROPBEAR_PRNGD_SOCKET
60 struct sockaddr_un egdsock; 61 if (prngd)
61 char egdcmd[2]; 62 {
62 #endif 63 readfd = connect_unix(filename);
63 64 }
64 #ifdef DROPBEAR_RANDOM_DEV 65 else
65 readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY); 66 #endif
67 {
68 readfd = open(filename, O_RDONLY);
69 }
70
66 if (readfd < 0) { 71 if (readfd < 0) {
67 dropbear_exit("Couldn't open random device"); 72 goto out;
68 } 73 }
69 #endif 74
70 75 readcount = 0;
71 #ifdef DROPBEAR_PRNGD_SOCKET 76 while (readcount < len)
72 readfd = connect_unix(DROPBEAR_PRNGD_SOCKET); 77 {
73 78 int readlen, wantread;
74 if (readfd < 0) { 79 unsigned char readbuf[128];
75 dropbear_exit("Couldn't open random device");
76 }
77
78 if (buflen > 255)
79 dropbear_exit("Can't request more than 255 bytes from egd");
80 egdcmd[0] = 0x02; /* blocking read */
81 egdcmd[1] = (unsigned char)buflen;
82 if (write(readfd, egdcmd, 2) < 0)
83 dropbear_exit("Can't send command to egd");
84 #endif
85
86 /* read the actual random data */
87 readpos = 0;
88 do {
89 if (!already_blocked) 80 if (!already_blocked)
90 { 81 {
91 int ret; 82 int ret;
92 struct timeval timeout; 83 struct timeval timeout = { .tv_sec = 2, .tv_usec = 0};
93 fd_set read_fds; 84 fd_set read_fds;
94
95 timeout.tv_sec = 2; /* two seconds should be enough */
96 timeout.tv_usec = 0;
97 85
98 FD_ZERO(&read_fds); 86 FD_ZERO(&read_fds);
99 FD_SET(readfd, &read_fds); 87 FD_SET(readfd, &read_fds);
100 ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout); 88 ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout);
101 if (ret == 0) 89 if (ret == 0)
102 { 90 {
103 dropbear_log(LOG_INFO, "Warning: Reading the random source seems to have blocked.\nIf you experience problems, you probably need to find a better entropy source."); 91 dropbear_log(LOG_WARNING, "Warning: Reading the randomness source '%s' seems to have blocked.\nYou may need to find a better entropy source.", filename);
104 already_blocked = 1; 92 already_blocked = 1;
105 } 93 }
106 } 94 }
107 readlen = read(readfd, &buf[readpos], buflen - readpos); 95
96 wantread = MIN(sizeof(readbuf), len-readcount);
97
98 #ifdef DROPBEAR_PRNGD_SOCKET
99 if (prngd)
100 {
101 char egdcmd[2];
102 egdcmd[0] = 0x02; /* blocking read */
103 egdcmd[1] = (unsigned char)wantread;
104 if (write(readfd, egdcmd, 2) < 0)
105 {
106 dropbear_exit("Can't send command to egd");
107 }
108 }
109 #endif
110
111 readlen = read(readfd, readbuf, wantread);
108 if (readlen <= 0) { 112 if (readlen <= 0) {
109 if (readlen < 0 && errno == EINTR) { 113 if (readlen < 0 && errno == EINTR) {
110 continue; 114 continue;
111 } 115 }
112 dropbear_exit("Error reading random source"); 116 if (readlen == 0 && len == 0)
113 } 117 {
114 readpos += readlen; 118 /* whole file was read as requested */
115 } while (readpos < buflen); 119 break;
116 120 }
117 close (readfd); 121 goto out;
118 } 122 }
119 123 sha1_process(hs, readbuf, readlen);
120 /* initialise the prng from /dev/(u)random or prngd */ 124 readcount += readlen;
125 }
126 ret = DROPBEAR_SUCCESS;
127 out:
128 close(readfd);
129 return ret;
130 }
131
132 void addrandom(char * buf, int len)
133 {
134 hash_state hs;
135
136 /* hash in the new seed data */
137 sha1_init(&hs);
138 /* existing state (zeroes on startup) */
139 sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
140
141 /* new */
142 sha1_process(&hs, buf, len);
143 sha1_done(&hs, hashpool);
144 }
145
146 static void write_urandom()
147 {
148 #ifndef DROPBEAR_PRNGD_SOCKET
149 /* This is opportunistic, don't worry about failure */
150 unsigned char buf[INIT_SEED_SIZE];
151 FILE *f = fopen(DROPBEAR_URANDOM_DEV, "w");
152 genrandom(buf, sizeof(buf));
153 fwrite(buf, sizeof(buf), 1, f);
154 fclose(f);
155 #endif
156 }
157
158 /* add entropy from the stronger, blocking source /dev/random. Only used
159 * for generating persistent private keys (RSA and DSS) */
160 void seedstrongrandom()
161 {
162 /* We assume that PRNGD is a strong source, so don't need to do anything here */
163 #ifndef DROPBEAR_PRNGD_SOCKET
164 hash_state hs;
165
166 sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
167 if (process_file(&hs, "/dev/random", INIT_SEED_SIZE, 0)
168 != DROPBEAR_SUCCESS) {
169 dropbear_exit("Failure reading random device %s", "/dev/random");
170 }
171
172 sha1_done(&hs, hashpool);
173 #endif
174 }
175
176 /* Initialise the prng from /dev/urandom or prngd. This function can
177 * be called multiple times */
121 void seedrandom() { 178 void seedrandom() {
122 179
123 unsigned char readbuf[INIT_SEED_SIZE]; 180 hash_state hs;
124 181
125 hash_state hs; 182 pid_t pid;
126 183 struct timeval tv;
127 /* initialise so that things won't warn about 184 clock_t clockval;
128 * hashing an undefined buffer */
129 if (!donerandinit) {
130 m_burn(hashpool, sizeof(hashpool));
131 }
132
133 /* get the seed data */
134 readrand(readbuf, sizeof(readbuf));
135 185
136 /* hash in the new seed data */ 186 /* hash in the new seed data */
137 sha1_init(&hs); 187 sha1_init(&hs);
188 /* existing state */
138 sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); 189 sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
139 sha1_process(&hs, (void*)readbuf, sizeof(readbuf)); 190
191 #ifdef DROPBEAR_PRNGD_SOCKET
192 if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1)
193 != DROPBEAR_SUCCESS) {
194 dropbear_exit("Failure reading random device %s",
195 DROPBEAR_PRNGD_SOCKET);
196 }
197 #else
198 /* non-blocking random source (probably /dev/urandom) */
199 if (process_file(&hs, DROPBEAR_URANDOM_DEV, INIT_SEED_SIZE, 0)
200 != DROPBEAR_SUCCESS) {
201 dropbear_exit("Failure reading random device %s",
202 DROPBEAR_URANDOM_DEV);
203 }
204 #endif
205
206 /* A few other sources to fall back on. Add more here for other platforms */
207 #ifdef __linux__
208 /* Seems to be a reasonable source of entropy from timers */
209 process_file(&hs, "/proc/timer_list", 0, 0);
210 /* Might help on systems with wireless */
211 process_file(&hs, "/proc/interrupts", 0, 0);
212 #endif
213
214 pid = getpid();
215 sha1_process(&hs, (void*)&pid, sizeof(pid));
216
217 gettimeofday(&tv, NULL);
218 sha1_process(&hs, (void*)&tv, sizeof(tv));
219
220 clockval = clock();
221 sha1_process(&hs, (void*)&clockval, sizeof(clockval));
222
223 /* When a private key is read by the client or server it will
224 * be added to the hashpool - see runopts.c */
225
140 sha1_done(&hs, hashpool); 226 sha1_done(&hs, hashpool);
141 227
142 counter = 0; 228 counter = 0;
143 donerandinit = 1; 229 donerandinit = 1;
144 } 230
145 231 /* Feed it all back into /dev/urandom - this might help if Dropbear
146 /* hash the current random pool with some unique identifiers 232 * is running from inetd and gets new state each time */
147 * for this process and point-in-time. this is used to separate 233 write_urandom();
148 * the random pools for fork()ed processes. */
149 void reseedrandom() {
150
151 pid_t pid;
152 hash_state hs;
153 struct timeval tv;
154
155 if (!donerandinit) {
156 dropbear_exit("seedrandom not done");
157 }
158
159 pid = getpid();
160 gettimeofday(&tv, NULL);
161
162 sha1_init(&hs);
163 sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
164 sha1_process(&hs, (void*)&pid, sizeof(pid));
165 sha1_process(&hs, (void*)&tv, sizeof(tv));
166 sha1_done(&hs, hashpool);
167 } 234 }
168 235
169 /* return len bytes of pseudo-random data */ 236 /* return len bytes of pseudo-random data */
170 void genrandom(unsigned char* buf, unsigned int len) { 237 void genrandom(unsigned char* buf, unsigned int len) {
171 238