• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Dropbear - a SSH2 server
3  *
4  * Copyright (c) 2002,2003 Matt Johnston
5  * All rights reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE. */
24 
25 #include "includes.h"
26 #include "buffer.h"
27 #include "dbutil.h"
28 #include "bignum.h"
29 
30 static int donerandinit = 0;
31 
32 /* this is used to generate unique output from the same hashpool */
33 static uint32_t counter = 0;
34 /* the max value for the counter, so it won't integer overflow */
35 #define MAX_COUNTER 1<<30
36 
37 static unsigned char hashpool[SHA1_HASH_SIZE];
38 
39 #define INIT_SEED_SIZE 32 /* 256 bits */
40 
41 static void readrand(unsigned char* buf, unsigned int buflen);
42 
43 /* 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,
45  * and a counter. We feed more data in by hashing the current pool and new
46  * data into the pool.
47  *
48  * It is important to ensure that counter doesn't wrap around before we
49  * feed in new entropy.
50  *
51  */
52 
readrand(unsigned char * buf,unsigned int buflen)53 static void readrand(unsigned char* buf, unsigned int buflen) {
54 
55 	static int already_blocked = 0;
56 	int readfd;
57 	unsigned int readpos;
58 	int readlen;
59 #ifdef DROPBEAR_PRNGD_SOCKET
60 	struct sockaddr_un egdsock;
61 	char egdcmd[2];
62 #endif
63 
64 #ifdef DROPBEAR_RANDOM_DEV
65 	readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY);
66 	if (readfd < 0) {
67 		dropbear_exit("couldn't open random device");
68 	}
69 #endif
70 
71 #ifdef DROPBEAR_PRNGD_SOCKET
72 	memset((void*)&egdsock, 0x0, sizeof(egdsock));
73 	egdsock.sun_family = AF_UNIX;
74 	strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET,
75 			sizeof(egdsock.sun_path));
76 
77 	readfd = socket(PF_UNIX, SOCK_STREAM, 0);
78 	if (readfd < 0) {
79 		dropbear_exit("couldn't open random device");
80 	}
81 	/* todo - try various common locations */
82 	if (connect(readfd, (struct sockaddr*)&egdsock,
83 			sizeof(struct sockaddr_un)) < 0) {
84 		dropbear_exit("couldn't open random device");
85 	}
86 
87 	if (buflen > 255)
88 		dropbear_exit("can't request more than 255 bytes from egd");
89 	egdcmd[0] = 0x02;	/* blocking read */
90 	egdcmd[1] = (unsigned char)buflen;
91 	if (write(readfd, egdcmd, 2) < 0)
92 		dropbear_exit("can't send command to egd");
93 #endif
94 
95 	/* read the actual random data */
96 	readpos = 0;
97 	do {
98 		if (!already_blocked)
99 		{
100 			int ret;
101 			struct timeval timeout;
102 			fd_set read_fds;
103 
104 			timeout.tv_sec = 2; /* two seconds should be enough */
105 			timeout.tv_usec = 0;
106 
107 			FD_ZERO(&read_fds);
108 			FD_SET(readfd, &read_fds);
109 			ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout);
110 			if (ret == 0)
111 			{
112 				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.");
113 				already_blocked = 1;
114 			}
115 		}
116 		readlen = read(readfd, &buf[readpos], buflen - readpos);
117 		if (readlen <= 0) {
118 			if (readlen < 0 && errno == EINTR) {
119 				continue;
120 			}
121 			dropbear_exit("error reading random source");
122 		}
123 		readpos += readlen;
124 	} while (readpos < buflen);
125 
126 	close (readfd);
127 }
128 
129 /* initialise the prng from /dev/(u)random or prngd */
seedrandom()130 void seedrandom() {
131 
132 	unsigned char readbuf[INIT_SEED_SIZE];
133 
134 	hash_state hs;
135 
136 	/* initialise so that things won't warn about
137 	 * hashing an undefined buffer */
138 	if (!donerandinit) {
139 		m_burn(hashpool, sizeof(hashpool));
140 	}
141 
142 	/* get the seed data */
143 	readrand(readbuf, sizeof(readbuf));
144 
145 	/* hash in the new seed data */
146 	sha1_init(&hs);
147 	sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
148 	sha1_process(&hs, (void*)readbuf, sizeof(readbuf));
149 	sha1_done(&hs, hashpool);
150 
151 	counter = 0;
152 	donerandinit = 1;
153 }
154 
155 /* hash the current random pool with some unique identifiers
156  * for this process and point-in-time. this is used to separate
157  * the random pools for fork()ed processes. */
reseedrandom()158 void reseedrandom() {
159 
160 	pid_t pid;
161 	hash_state hs;
162 	struct timeval tv;
163 
164 	if (!donerandinit) {
165 		dropbear_exit("seedrandom not done");
166 	}
167 
168 	pid = getpid();
169 	gettimeofday(&tv, NULL);
170 
171 	sha1_init(&hs);
172 	sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
173 	sha1_process(&hs, (void*)&pid, sizeof(pid));
174 	sha1_process(&hs, (void*)&tv, sizeof(tv));
175 	sha1_done(&hs, hashpool);
176 }
177 
178 /* return len bytes of pseudo-random data */
genrandom(unsigned char * buf,unsigned int len)179 void genrandom(unsigned char* buf, unsigned int len) {
180 
181 	hash_state hs;
182 	unsigned char hash[SHA1_HASH_SIZE];
183 	unsigned int copylen;
184 
185 	if (!donerandinit) {
186 		dropbear_exit("seedrandom not done");
187 	}
188 
189 	while (len > 0) {
190 		sha1_init(&hs);
191 		sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
192 		sha1_process(&hs, (void*)&counter, sizeof(counter));
193 		sha1_done(&hs, hash);
194 
195 		counter++;
196 		if (counter > MAX_COUNTER) {
197 			seedrandom();
198 		}
199 
200 		copylen = MIN(len, SHA1_HASH_SIZE);
201 		memcpy(buf, hash, copylen);
202 		len -= copylen;
203 		buf += copylen;
204 	}
205 	m_burn(hash, sizeof(hash));
206 }
207 
208 /* Generates a random mp_int.
209  * max is a *mp_int specifying an upper bound.
210  * rand must be an initialised *mp_int for the result.
211  * the result rand satisfies:  0 < rand < max
212  * */
gen_random_mpint(mp_int * max,mp_int * rand)213 void gen_random_mpint(mp_int *max, mp_int *rand) {
214 
215 	unsigned char *randbuf = NULL;
216 	unsigned int len = 0;
217 	const unsigned char masks[] = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
218 
219 	const int size_bits = mp_count_bits(max);
220 
221 	len = size_bits / 8;
222 	if ((size_bits % 8) != 0) {
223 		len += 1;
224 	}
225 
226 	randbuf = (unsigned char*)m_malloc(len);
227 	do {
228 		genrandom(randbuf, len);
229 		/* Mask out the unrequired bits - mp_read_unsigned_bin expects
230 		 * MSB first.*/
231 		randbuf[0] &= masks[size_bits % 8];
232 
233 		bytes_to_mp(rand, randbuf, len);
234 
235 		/* keep regenerating until we get one satisfying
236 		 * 0 < rand < max    */
237 	} while (mp_cmp(rand, max) != MP_LT);
238 	m_burn(randbuf, len);
239 	m_free(randbuf);
240 }
241