Add an internal-representation no-op function.
[u/mdw/catacomb] / rand.c
CommitLineData
d03ab969 1/* -*-c-*-
2 *
ac2fd5cd 3 * $Id: rand.c,v 1.5 2000/06/17 11:53:55 mdw Exp $
d03ab969 4 *
5 * Secure random number generator
6 *
7 * (c) 1998 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Catacomb.
13 *
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30/*----- Revision history --------------------------------------------------*
31 *
32 * $Log: rand.c,v $
ac2fd5cd 33 * Revision 1.5 2000/06/17 11:53:55 mdw
34 * Deprecate `rand_getgood'. Provide a new interface to ensure that a pool
35 * is well seeded. Use secure arena for memory allocation.
36 *
b056b793 37 * Revision 1.4 1999/12/13 15:34:28 mdw
38 * Increase the entropy threshhold in rand_getgood.
39 *
aacbc1c6 40 * Revision 1.3 1999/12/10 23:28:07 mdw
41 * Bug fix: rand_getgood didn't update buffer pointer.
42 *
ba044e65 43 * Revision 1.2 1999/10/12 21:00:15 mdw
44 * Make pool and buffer sizes more sensible.
45 *
d03ab969 46 * Revision 1.1 1999/09/03 08:41:12 mdw
47 * Initial import.
48 *
49 */
50
51/*----- Header files ------------------------------------------------------*/
52
aacbc1c6 53#include <stdarg.h>
d03ab969 54#include <stdio.h>
55#include <string.h>
56
57#include <mLib/bits.h>
aacbc1c6 58#include <mLib/sub.h>
d03ab969 59
ac2fd5cd 60#include "arena.h"
d03ab969 61#include "blowfish-cbc.h"
62#include "paranoia.h"
63#include "rand.h"
64#include "rmd160.h"
65#include "rmd160-hmac.h"
66
67/*----- Static variables --------------------------------------------------*/
68
aacbc1c6 69static const grand_ops gops;
70
71typedef struct gctx {
72 grand r;
73 rand_pool p;
74} gctx;
75
ac2fd5cd 76static gctx *pool = 0; /* Default random pool */
d03ab969 77
78/*----- Macros ------------------------------------------------------------*/
79
80#define RAND_RESOLVE(r) do { \
ac2fd5cd 81 if ((r) == RAND_GLOBAL) { \
82 if (!pool) \
83 pool = (gctx *)rand_create(); \
84 (r) = &pool->p; \
85 } \
d03ab969 86} while (0)
87
88#define TIMER(r) do { \
89 if ((r)->s && (r)->s->timer) \
90 (r)->s->timer(r); \
91} while (0)
92
93/*----- Main code ---------------------------------------------------------*/
94
95/* --- @rand_init@ --- *
96 *
97 * Arguments: @rand_pool *r@ = pointer to a randomness pool
98 *
99 * Returns: ---
100 *
101 * Use: Initializes a randomness pool. The pool doesn't start out
ac2fd5cd 102 * very random: that's your job to sort out. A good suggestion
103 * would be to attach an appropriate noise source and call
104 * @rand_seed@.
d03ab969 105 */
106
107void rand_init(rand_pool *r)
108{
109 RAND_RESOLVE(r);
110 memset(r->pool, 0, sizeof(r->pool));
111 memset(r->buf, 0, sizeof(r->buf));
112 r->i = 0;
113 r->irot = 0;
114 r->ibits = r->obits = 0;
115 r->o = RAND_SECSZ;
116 r->s = 0;
aacbc1c6 117 rmd160_hmacinit(&r->k, 0, 0);
d03ab969 118 rand_gate(r);
119}
120
121/* --- @rand_noisesrc@ --- *
122 *
123 * Arguments: @rand_pool *r@ = pointer to a randomness pool
124 * @const rand_source *s@ = pointer to source definition
125 *
126 * Returns: ---
127 *
128 * Use: Sets a noise source for a randomness pool. When the pool's
129 * estimate of good random bits falls to zero, the @getnoise@
130 * function is called, passing the pool handle as an argument.
131 * It is expected to increase the number of good bits by at
132 * least one, because it'll be called over and over again until
133 * there are enough bits to satisfy the caller. The @timer@
134 * function is called frequently throughout the generator's
135 * operation.
136 */
137
138void rand_noisesrc(rand_pool *r, const rand_source *s)
139{
140 RAND_RESOLVE(r);
141 r->s = s;
142}
143
ac2fd5cd 144/* --- @rand_seed@ --- *
145 *
146 * Arguments: @rand_pool *r@ = pointer to a randomness pool
147 * @unsigned bits@ = number of bits to ensure
148 *
149 * Returns: ---
150 *
151 * Use: Ensures that there are at least @bits@ good bits of entropy
152 * in the pool. It is recommended that you call this after
153 * initializing a new pool. Requesting @bits > RAND_IBITS@ is
154 * doomed to failure (and is an error).
155 */
156
157void rand_seed(rand_pool *r, unsigned bits)
158{
159 RAND_RESOLVE(r);
160
161 assert(((void)"bits pointlessly large in rand_seed", bits <= RAND_IBITS));
162 assert(((void)"no noise source in rand_seed", r->s));
163
164 while (r->ibits < bits)
165 r->s->getnoise(r);
166 rand_gate(r);
167}
168
d03ab969 169/* --- @rand_key@ --- *
170 *
171 * Arguments: @rand_pool *r@ = pointer to a randomness pool
172 * @const void *k@ = pointer to key data
173 * @size_t sz@ = size of key data
174 *
175 * Returns: ---
176 *
177 * Use: Sets the secret key for a randomness pool. The key is used
178 * when mixing in new random bits.
179 */
180
181void rand_key(rand_pool *r, const void *k, size_t sz)
182{
183 RAND_RESOLVE(r);
aacbc1c6 184 rmd160_hmacinit(&r->k, k, sz);
d03ab969 185}
186
187/* --- @rand_add@ --- *
188 *
189 * Arguments: @rand_pool *r@ = pointer to a randomness pool
190 * @const void *p@ = pointer a buffer of data to add
191 * @size_t sz@ = size of the data buffer
192 * @unsigned goodbits@ = number of good bits estimated in buffer
193 *
194 * Returns: ---
195 *
196 * Use: Mixes the data in the buffer with the contents of the
197 * pool. The estimate of the number of good bits is added to
198 * the pool's own count. The mixing operation is not
199 * cryptographically strong. However, data in the input pool
200 * isn't output directly, only through the one-way gating
201 * operation, so that shouldn't matter.
202 */
203
204void rand_add(rand_pool *r, const void *p, size_t sz, unsigned goodbits)
205{
206 const octet *c = p;
ba044e65 207 int i, rot;
d03ab969 208
ba044e65 209#if RAND_POOLSZ != 128
d03ab969 210# error Polynomial in rand_add is out of date. Fix it.
211#endif
212
213 RAND_RESOLVE(r);
214
ba044e65 215 i = r->i; rot = r->irot;
d03ab969 216
217 while (sz) {
218 octet o = *c++;
ba044e65 219 r->pool[i] ^= (ROL8(o, rot) ^
220 r->pool[(i + 1) % RAND_POOLSZ] ^
221 r->pool[(i + 2) % RAND_POOLSZ] ^
222 r->pool[(i + 7) % RAND_POOLSZ]);
d03ab969 223 rot = (rot + 5) & 7;
224 i++; if (i >= RAND_POOLSZ) i -= RAND_POOLSZ;
d03ab969 225 sz--;
226 }
227
228 r->i = i;
229 r->irot = rot;
230 r->ibits += goodbits;
231 if (r->ibits > RAND_IBITS)
232 r->ibits = RAND_IBITS;
233}
234
235/* --- @rand_goodbits@ --- *
236 *
237 * Arguments: @rand_pool *r@ = pointer to a randomness pool
238 *
239 * Returns: Estimate of the number of good bits remaining in the pool.
240 */
241
242unsigned rand_goodbits(rand_pool *r)
243{
244 RAND_RESOLVE(r);
245 return (r->ibits + r->obits);
246}
247
248/* --- @rand_gate@ --- *
249 *
250 * Arguments: @rand_pool *r@ = pointer to a randomness pool
251 *
252 * Returns: ---
253 *
254 * Use: Mixes up the entire state of the generator in a nonreversible
255 * way.
256 */
257
258void rand_gate(rand_pool *r)
259{
260 octet mac[RMD160_HASHSZ];
261
262 RAND_RESOLVE(r);
263 TIMER(r);
264
265 /* --- Hash up all the data in the pool --- */
266
267 {
268 rmd160_macctx mc;
269
270 rmd160_macinit(&mc, &r->k);
aacbc1c6 271 rmd160_machash(&mc, r->pool, sizeof(r->pool));
272 rmd160_machash(&mc, r->buf, sizeof(r->buf));
d03ab969 273 rmd160_macdone(&mc, mac);
274 BURN(mc);
275 }
276
277 /* --- Now mangle all of the data based on the hash --- */
278
279 {
280 blowfish_cbcctx bc;
281
282 blowfish_cbcinit(&bc, mac, sizeof(mac), 0);
283 blowfish_cbcencrypt(&bc, r->pool, r->pool, sizeof(r->pool));
284 blowfish_cbcencrypt(&bc, r->buf, r->buf, sizeof(r->buf));
285 BURN(bc);
286 }
287
288 /* --- Reset the various state variables --- */
289
290 r->o = RAND_SECSZ;
291 r->obits += r->ibits;
292 if (r->obits > RAND_OBITS) {
293 r->ibits = r->obits - r->ibits;
294 r->obits = RAND_OBITS;
295 } else
296 r->ibits = 0;
297 TIMER(r);
298}
299
300/* --- @rand_stretch@ --- *
301 *
302 * Arguments: @rand_pool *r@ = pointer to a randomness pool
303 *
304 * Returns: ---
305 *
306 * Use: Stretches the contents of the output buffer by transforming
307 * it in a nonreversible way. This doesn't add any entropy
308 * worth speaking about, but it works well enough when the
309 * caller doesn't care about that sort of thing.
310 */
311
312void rand_stretch(rand_pool *r)
313{
314 octet mac[RMD160_HASHSZ];
315
316 RAND_RESOLVE(r);
317 TIMER(r);
318
319 /* --- Hash up all the data in the buffer --- */
320
321 {
322 rmd160_macctx mc;
323
324 rmd160_macinit(&mc, &r->k);
aacbc1c6 325 rmd160_machash(&mc, r->pool, sizeof(r->pool));
326 rmd160_machash(&mc, r->buf, sizeof(r->buf));
d03ab969 327 rmd160_macdone(&mc, mac);
328 BURN(mc);
329 }
330
331 /* --- Now mangle the buffer based on that hash --- */
332
333 {
334 blowfish_cbcctx bc;
335
336 blowfish_cbcinit(&bc, mac, sizeof(mac), 0);
337 blowfish_cbcencrypt(&bc, r->buf, r->buf, sizeof(r->buf));
338 BURN(bc);
339 }
340
341 /* --- Reset the various state variables --- */
342
343 r->o = RAND_SECSZ;
344 TIMER(r);
345}
346
347/* --- @rand_get@ --- *
348 *
349 * Arguments: @rand_pool *r@ = pointer to a randomness pool
350 * @void *p@ = pointer to output buffer
351 * @size_t sz@ = size of output buffer
352 *
353 * Returns: ---
354 *
355 * Use: Gets random data from the pool. The pool's contents can't be
356 * determined from the output of this function; nor can the
357 * output data be determined from a knowledge of the data input
358 * to the pool wihtout also having knowledge of the secret key.
359 * The good bits counter is decremented, although no special
360 * action is taken if it reaches zero.
361 */
362
363void rand_get(rand_pool *r, void *p, size_t sz)
364{
365 octet *o = p;
366
367 RAND_RESOLVE(r);
368 TIMER(r);
369
370 if (!sz)
371 return;
372 for (;;) {
373 if (r->o + sz <= RAND_BUFSZ) {
374 memcpy(o, r->buf + r->o, sz);
375 r->o += sz;
376 break;
377 } else {
378 size_t chunk = RAND_BUFSZ - r->o;
379 if (chunk) {
380 memcpy(o, r->buf + r->o, chunk);
381 sz -= chunk;
382 o += chunk;
383 }
384 rand_stretch(r);
385 }
386 }
387
388 if (r->obits > sz * 8)
389 r->obits -= sz * 8;
390 else
391 r->obits = 0;
392}
393
394/* --- @rand_getgood@ --- *
395 *
396 * Arguments: @rand_pool *r@ = pointer to a randomness pool
397 * @void *p@ = pointer to output buffer
398 * @size_t sz@ = size of output buffer
399 *
400 * Returns: ---
401 *
ac2fd5cd 402 * Use: Gets random data from the pool, ensuring that there are
403 * enough good bits. This interface isn't recommended: it makes
404 * the generator slow, and doesn't provide much more security
405 * than @rand_get@, assuming you've previously done a
406 * @rand_seed@.
d03ab969 407 */
408
409void rand_getgood(rand_pool *r, void *p, size_t sz)
410{
411 octet *o = p;
412
413 RAND_RESOLVE(r);
414
415 if (!sz)
416 return;
417 if (!r->s || !r->s->getnoise) {
418 rand_get(r, p, sz);
419 return;
420 }
421 TIMER(r);
422
423 while (sz) {
424 size_t chunk = sz;
425
426 if (chunk * 8 > r->obits) {
427 if (chunk * 8 > r->ibits + r->obits)
b056b793 428 do r->s->getnoise(r); while (r->ibits + r->obits < 256);
d03ab969 429 rand_gate(r);
430 if (chunk * 8 > r->obits)
431 chunk = r->obits / 8;
432 }
433
434 if (chunk + r->o > RAND_BUFSZ)
435 chunk = RAND_BUFSZ - r->o;
436
437 memcpy(o, r->buf + r->o, chunk);
aacbc1c6 438 r->o += chunk;
d03ab969 439 r->obits -= chunk * 8;
440 o += chunk;
441 sz -= chunk;
442 }
443}
444
aacbc1c6 445/*----- Generic random number generator interface -------------------------*/
446
ac2fd5cd 447#define GRESOLVE(g, r) do { \
448 if (r != &rand_global) \
449 g = (gctx *)r; \
450 else { \
451 if (!pool) \
452 pool = (gctx *)rand_create(); \
453 g = pool; \
454 } \
455} while (0)
456
aacbc1c6 457static void gdestroy(grand *r)
458{
ac2fd5cd 459 gctx *g;
460 GRESOLVE(g, r);
461 if (g != pool) {
462 BURN(*g);
463 S_DESTROY(g);
464 }
aacbc1c6 465}
466
467static int gmisc(grand *r, unsigned op, ...)
468{
ac2fd5cd 469 gctx *g;
aacbc1c6 470 va_list ap;
471 int rc = 0;
472 va_start(ap, op);
473
ac2fd5cd 474 GRESOLVE(g, r);
aacbc1c6 475 switch (op) {
476 case GRAND_CHECK:
477 switch (va_arg(ap, unsigned)) {
478 case GRAND_CHECK:
479 case GRAND_SEEDINT:
480 case GRAND_SEEDUINT32:
481 case GRAND_SEEDBLOCK:
b056b793 482 case GRAND_SEEDRAND:
aacbc1c6 483 case RAND_GATE:
484 case RAND_STRETCH:
485 case RAND_KEY:
486 case RAND_NOISESRC:
ac2fd5cd 487 case RAND_SEED:
aacbc1c6 488 rc = 1;
489 break;
490 default:
491 rc = 0;
492 break;
493 }
494 break;
495 case GRAND_SEEDINT: {
496 unsigned u = va_arg(ap, unsigned);
497 rand_add(&g->p, &u, sizeof(u), sizeof(u));
498 } break;
499 case GRAND_SEEDUINT32: {
500 uint32 i = va_arg(ap, uint32);
501 rand_add(&g->p, &i, sizeof(i), 4);
502 } break;
503 case GRAND_SEEDBLOCK: {
504 const void *p = va_arg(ap, const void *);
505 size_t sz = va_arg(ap, size_t);
506 rand_add(&g->p, p, sz, sz);
507 } break;
b056b793 508 case GRAND_SEEDRAND: {
509 grand *rr = va_arg(ap, grand *);
510 octet buf[16];
511 rr->ops->fill(rr, buf, sizeof(buf));
512 rand_add(&g->p, buf, sizeof(buf), 8);
513 } break;
aacbc1c6 514 case RAND_GATE:
515 rand_gate(&g->p);
516 break;
517 case RAND_STRETCH:
518 rand_stretch(&g->p);
519 break;
520 case RAND_KEY: {
521 const void *k = va_arg(ap, const void *);
522 size_t sz = va_arg(ap, size_t);
523 rand_key(&g->p, k, sz);
524 } break;
525 case RAND_NOISESRC:
526 rand_noisesrc(&g->p, va_arg(ap, const rand_source *));
527 break;
ac2fd5cd 528 case RAND_SEED:
529 rand_seed(&g->p, va_arg(ap, unsigned));
530 break;
aacbc1c6 531 default:
532 GRAND_BADOP;
533 break;
534 }
535
536 va_end(ap);
537 return (rc);
538}
539
540static octet gbyte(grand *r)
541{
ac2fd5cd 542 gctx *g;
aacbc1c6 543 octet o;
ac2fd5cd 544 GRESOLVE(g, r);
aacbc1c6 545 rand_getgood(&g->p, &o, 1);
546 return (o);
547}
548
549static uint32 gword(grand *r)
550{
ac2fd5cd 551 gctx *g;
aacbc1c6 552 octet b[4];
ac2fd5cd 553 GRESOLVE(g, r);
aacbc1c6 554 rand_getgood(&g->p, &b, sizeof(b));
555 return (LOAD32(b));
556}
557
558static void gfill(grand *r, void *p, size_t sz)
559{
ac2fd5cd 560 gctx *g;
561 GRESOLVE(g, r);
562 rand_get(&g->p, p, sz);
aacbc1c6 563}
564
565static const grand_ops gops = {
566 "rand",
ac2fd5cd 567 GRAND_CRYPTO, 0,
aacbc1c6 568 gmisc, gdestroy,
569 gword, gbyte, gword, grand_range, gfill
570};
571
572grand rand_global = { &gops };
573
574/* --- @rand_create@ --- *
575 *
576 * Arguments: ---
577 *
578 * Returns: Pointer to a generic generator.
579 *
580 * Use: Constructs a generic generator interface over a Catacomb
581 * entropy pool generator.
582 */
583
584grand *rand_create(void)
585{
ac2fd5cd 586 gctx *g = S_CREATE(gctx);
aacbc1c6 587 g->r.ops = &gops;
588 rand_init(&g->p);
589 return (&g->r);
590}
591
d03ab969 592/*----- That's all, folks -------------------------------------------------*/