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