Coverage Report

Created: 2025-08-27 07:03

/src/dropbear/src/dbrandom.c
Line
Count
Source (jump to first uncovered line)
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
#include "dbrandom.h"
30
#include "runopts.h"
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
61.5k
#define MAX_COUNTER (1<<30)
36
37
static unsigned char hashpool[SHA256_HASH_SIZE] = {0};
38
static int donerandinit = 0;
39
40
0
#define INIT_SEED_SIZE 32 /* 256 bits */
41
42
/* The basic setup is we read some data from /dev/(u)random or prngd and hash it
43
 * into hashpool. To read data, we hash together current hashpool contents,
44
 * and a counter. We feed more data in by hashing the current pool and new
45
 * data into the pool.
46
 *
47
 * It is important to ensure that counter doesn't wrap around before we
48
 * feed in new entropy.
49
 *
50
 */
51
52
/* Pass wantlen=0 to hash an entire file */
53
static int
54
process_file(hash_state *hs, const char *filename,
55
0
    unsigned int wantlen, int prngd) {
56
0
  int readfd = -1;
57
0
  unsigned int readcount;
58
0
  int ret = DROPBEAR_FAILURE;
59
60
0
  if (prngd) {
61
#if DROPBEAR_USE_PRNGD
62
    readfd = connect_unix(filename);
63
#endif
64
0
  } else {
65
0
    readfd = open(filename, O_RDONLY);
66
0
  }
67
68
0
  if (readfd < 0) {
69
0
    goto out;
70
0
  }
71
72
0
  readcount = 0;
73
0
  while (wantlen == 0 || readcount < wantlen) {
74
0
    int readlen, wantread;
75
0
    unsigned char readbuf[4096];
76
0
    if (wantlen == 0) {
77
0
      wantread = sizeof(readbuf);
78
0
    } else {
79
0
      wantread = MIN(sizeof(readbuf), wantlen-readcount);
80
0
    }
81
82
#if DROPBEAR_USE_PRNGD
83
    if (prngd) {
84
      char egdcmd[2];
85
      egdcmd[0] = 0x02; /* blocking read */
86
      egdcmd[1] = (unsigned char)wantread;
87
      if (write(readfd, egdcmd, 2) < 0) {
88
        dropbear_exit("Can't send command to egd");
89
      }
90
    }
91
#endif
92
0
    readlen = read(readfd, readbuf, wantread);
93
0
    if (readlen <= 0) {
94
0
      if (readlen < 0 && errno == EINTR) {
95
0
        continue;
96
0
      }
97
0
      if (readlen == 0 && wantlen == 0) {
98
        /* whole file was read as requested */
99
0
        break;
100
0
      }
101
0
      goto out;
102
0
    }
103
0
    sha256_process(hs, readbuf, readlen);
104
0
    readcount += readlen;
105
0
  }
106
0
  ret = DROPBEAR_SUCCESS;
107
0
out:
108
0
  close(readfd);
109
0
  return ret;
110
0
}
111
112
void addrandom(const unsigned char * buf, unsigned int len)
113
0
{
114
0
  hash_state hs;
115
116
0
#if DROPBEAR_FUZZ
117
0
  if (fuzz.fuzzing) {
118
0
    return;
119
0
  }
120
0
#endif
121
122
  /* hash in the new seed data */
123
0
  sha256_init(&hs);
124
  /* existing state (zeroes on startup) */
125
0
  sha256_process(&hs, (void*)hashpool, sizeof(hashpool));
126
127
  /* new */
128
0
  sha256_process(&hs, buf, len);
129
0
  sha256_done(&hs, hashpool);
130
0
}
131
132
static void write_urandom()
133
0
{
134
0
#if DROPBEAR_FUZZ
135
0
  if (fuzz.fuzzing) {
136
0
    return;
137
0
  }
138
0
#endif
139
0
#if !DROPBEAR_USE_PRNGD
140
  /* This is opportunistic, don't worry about failure */
141
0
  unsigned char buf[INIT_SEED_SIZE];
142
0
  FILE *f = fopen(DROPBEAR_URANDOM_DEV, "w");
143
0
  if (!f) {
144
0
    return;
145
0
  }
146
0
  genrandom(buf, sizeof(buf));
147
0
  fwrite(buf, sizeof(buf), 1, f);
148
0
  fclose(f);
149
0
#endif
150
0
}
151
152
#if DROPBEAR_FUZZ
153
2.99k
void fuzz_seed(const unsigned char* dat, unsigned int len) {
154
2.99k
  hash_state hs;
155
2.99k
  sha256_init(&hs);
156
2.99k
  sha256_process(&hs, "fuzzfuzzfuzz", strlen("fuzzfuzzfuzz"));
157
2.99k
  sha256_process(&hs, dat, len);
158
2.99k
  sha256_done(&hs, hashpool);
159
2.99k
  counter = 0;
160
2.99k
  donerandinit = 1;
161
2.99k
}
162
#endif
163
164
165
#ifdef HAVE_GETRANDOM
166
/* Reads entropy seed with getrandom(). 
167
 * May block if the kernel isn't ready.
168
 * Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
169
0
static int process_getrandom(hash_state *hs) {
170
0
  char buf[INIT_SEED_SIZE];
171
0
  ssize_t ret;
172
173
  /* First try non-blocking so that we can warn about waiting */
174
0
  ret = getrandom(buf, sizeof(buf), GRND_NONBLOCK);
175
0
  if (ret == -1) {
176
0
    if (errno == ENOSYS) {
177
      /* Old kernel */
178
0
      return DROPBEAR_FAILURE;
179
0
    }
180
    /* Other errors fall through to blocking getrandom() */
181
0
    TRACE(("first getrandom() failed: %d %s", errno, strerror(errno)))
182
0
    if (errno == EAGAIN) {
183
0
      dropbear_log(LOG_WARNING, "Waiting for kernel randomness to be initialised...");
184
0
    }
185
0
  }
186
187
  /* Wait blocking if needed. Loop in case we get EINTR */
188
0
  while (ret != sizeof(buf)) {
189
0
    ret = getrandom(buf, sizeof(buf), 0);
190
191
0
    if (ret == sizeof(buf)) {
192
      /* Success */
193
0
      break;
194
0
    }
195
0
    if (ret == -1 && errno == EINTR) {
196
      /* Try again. */
197
0
      continue;
198
0
    }
199
0
    if (ret >= 0) {
200
0
      TRACE(("Short read %zd from getrandom() shouldn't happen", ret))
201
      /* Try again? */
202
0
      continue;
203
0
    }
204
205
    /* Unexpected problem, fall back to /dev/urandom */
206
0
    TRACE(("2nd getrandom() failed: %d %s", errno, strerror(errno)))
207
0
    break;
208
0
  }
209
210
0
  if (ret == sizeof(buf)) {
211
    /* Success, stir in the entropy */
212
0
    sha256_process(hs, (void*)buf, sizeof(buf));
213
0
    return DROPBEAR_SUCCESS;
214
0
  }
215
216
0
  return DROPBEAR_FAILURE;
217
218
0
}
219
#endif /* HAVE_GETRANDOM */
220
221
/* Initialise the prng from /dev/urandom or prngd. This function can
222
 * be called multiple times */
223
0
void seedrandom() {
224
0
  hash_state hs;
225
226
0
  pid_t pid;
227
0
  struct timeval tv;
228
0
  clock_t clockval;
229
0
  int urandom_seeded = 0;
230
231
0
#if DROPBEAR_FUZZ
232
0
  if (fuzz.fuzzing) {
233
0
    return;
234
0
  }
235
0
#endif
236
237
  /* hash in the new seed data */
238
0
  sha256_init(&hs);
239
240
  /* existing state */
241
0
  sha256_process(&hs, (void*)hashpool, sizeof(hashpool));
242
243
0
#ifdef HAVE_GETRANDOM
244
0
  if (process_getrandom(&hs) == DROPBEAR_SUCCESS) {
245
0
    urandom_seeded = 1;
246
0
  }
247
0
#endif
248
249
0
  if (!urandom_seeded) {
250
#if DROPBEAR_USE_PRNGD
251
    if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1) 
252
        != DROPBEAR_SUCCESS) {
253
      dropbear_exit("Failure reading random device %s", 
254
          DROPBEAR_PRNGD_SOCKET);
255
      urandom_seeded = 1;
256
    }
257
#else
258
    /* non-blocking random source (probably /dev/urandom) */
259
0
    if (process_file(&hs, DROPBEAR_URANDOM_DEV, INIT_SEED_SIZE, 0) 
260
0
        != DROPBEAR_SUCCESS) {
261
0
      dropbear_exit("Failure reading random device %s", 
262
0
          DROPBEAR_URANDOM_DEV);
263
0
      urandom_seeded = 1;
264
0
    }
265
0
#endif
266
0
  } /* urandom_seeded */
267
268
  /* A few other sources to fall back on. 
269
   * Add more here for other platforms */
270
0
#ifdef __linux__
271
  /* Might help on systems with wireless */
272
0
  process_file(&hs, "/proc/interrupts", 0, 0);
273
274
0
  process_file(&hs, "/proc/loadavg", 0, 0);
275
0
  process_file(&hs, "/proc/sys/kernel/random/entropy_avail", 0, 0);
276
277
  /* Mostly network visible but useful in some situations.
278
   * Limit size to avoid slowdowns on systems with lots of routes */
279
0
  process_file(&hs, "/proc/net/netstat", 4096, 0);
280
0
  process_file(&hs, "/proc/net/dev", 4096, 0);
281
0
  process_file(&hs, "/proc/net/tcp", 4096, 0);
282
  /* Also includes interface lo */
283
0
  process_file(&hs, "/proc/net/rt_cache", 4096, 0);
284
0
  process_file(&hs, "/proc/vmstat", 0, 0);
285
0
#endif
286
287
0
  pid = getpid();
288
0
  sha256_process(&hs, (void*)&pid, sizeof(pid));
289
290
  /* gettimeofday() doesn't completely fill out struct timeval on 
291
     OS X (10.8.3), avoid valgrind warnings by clearing it first */
292
0
  memset(&tv, 0x0, sizeof(tv));
293
0
  gettimeofday(&tv, NULL);
294
0
  sha256_process(&hs, (void*)&tv, sizeof(tv));
295
296
0
  clockval = clock();
297
0
  sha256_process(&hs, (void*)&clockval, sizeof(clockval));
298
299
  /* When a private key is read by the client or server it will
300
   * be added to the hashpool - see runopts.c */
301
302
0
  sha256_done(&hs, hashpool);
303
304
0
  counter = 0;
305
0
  donerandinit = 1;
306
307
  /* Feed it all back into /dev/urandom - this might help if Dropbear
308
   * is running from inetd and gets new state each time */
309
0
  write_urandom();
310
0
}
311
312
/* return len bytes of pseudo-random data */
313
61.5k
void genrandom(unsigned char* buf, unsigned int len) {
314
315
61.5k
  hash_state hs;
316
61.5k
  unsigned char hash[SHA256_HASH_SIZE];
317
61.5k
  unsigned int copylen;
318
319
61.5k
  if (!donerandinit) {
320
0
    dropbear_exit("seedrandom not done");
321
0
  }
322
323
123k
  while (len > 0) {
324
61.5k
    sha256_init(&hs);
325
61.5k
    sha256_process(&hs, (void*)hashpool, sizeof(hashpool));
326
61.5k
    sha256_process(&hs, (void*)&counter, sizeof(counter));
327
61.5k
    sha256_done(&hs, hash);
328
329
61.5k
    counter++;
330
61.5k
    if (counter > MAX_COUNTER) {
331
0
      seedrandom();
332
0
    }
333
334
61.5k
    copylen = MIN(len, SHA256_HASH_SIZE);
335
61.5k
    memcpy(buf, hash, copylen);
336
61.5k
    len -= copylen;
337
61.5k
    buf += copylen;
338
61.5k
  }
339
61.5k
  m_burn(hash, sizeof(hash));
340
61.5k
}
341
342
/* Generates a random mp_int. 
343
 * max is a *mp_int specifying an upper bound.
344
 * rand must be an initialised *mp_int for the result.
345
 * the result rand satisfies:  0 < rand < max 
346
 * */
347
0
void gen_random_mpint(const mp_int *max, mp_int *rand) {
348
349
0
  unsigned char *randbuf = NULL;
350
0
  unsigned int len = 0;
351
0
  const unsigned char masks[] = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
352
353
0
  const int size_bits = mp_count_bits(max);
354
355
0
  len = size_bits / 8;
356
0
  if ((size_bits % 8) != 0) {
357
0
    len += 1;
358
0
  }
359
360
0
  randbuf = (unsigned char*)m_malloc(len);
361
0
  do {
362
0
    genrandom(randbuf, len);
363
    /* Mask out the unrequired bits - mp_read_unsigned_bin expects
364
     * MSB first.*/
365
0
    randbuf[0] &= masks[size_bits % 8];
366
367
0
    bytes_to_mp(rand, randbuf, len);
368
369
    /* keep regenerating until we get one satisfying
370
     * 0 < rand < max    */
371
0
  } while (!(mp_cmp(rand, max) == MP_LT && mp_cmp_d(rand, 0) == MP_GT));
372
0
  m_burn(randbuf, len);
373
0
  m_free(randbuf);
374
0
}