X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/ba044e65f37effe25ed5a6fa86d7c250b8065f60..025c5f4aa5ffbf8948482a4233318db81c2df5d2:/rand.c diff --git a/rand.c b/rand.c index 9aea479..c50e10a 100644 --- a/rand.c +++ b/rand.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: rand.c,v 1.2 1999/10/12 21:00:15 mdw Exp $ + * $Id$ * * Secure random number generator * @@ -27,24 +27,16 @@ * MA 02111-1307, USA. */ -/*----- Revision history --------------------------------------------------* - * - * $Log: rand.c,v $ - * Revision 1.2 1999/10/12 21:00:15 mdw - * Make pool and buffer sizes more sensible. - * - * Revision 1.1 1999/09/03 08:41:12 mdw - * Initial import. - * - */ - /*----- Header files ------------------------------------------------------*/ +#include #include #include #include +#include +#include "arena.h" #include "blowfish-cbc.h" #include "paranoia.h" #include "rand.h" @@ -53,13 +45,23 @@ /*----- Static variables --------------------------------------------------*/ -static rand_pool pool; /* Default random pool */ +static const grand_ops gops; + +typedef struct gctx { + grand r; + rand_pool p; +} gctx; + +static gctx *pool = 0; /* Default random pool */ /*----- Macros ------------------------------------------------------------*/ #define RAND_RESOLVE(r) do { \ - if ((r) == RAND_GLOBAL) \ - (r) = &pool; \ + if ((r) == RAND_GLOBAL) { \ + if (!pool) \ + pool = (gctx *)rand_create(); \ + (r) = &pool->p; \ + } \ } while (0) #define TIMER(r) do { \ @@ -76,7 +78,9 @@ static rand_pool pool; /* Default random pool */ * Returns: --- * * Use: Initializes a randomness pool. The pool doesn't start out - * very random: that's your job to sort out. + * very random: that's your job to sort out. A good suggestion + * would be to attach an appropriate noise source and call + * @rand_seed@. */ void rand_init(rand_pool *r) @@ -89,7 +93,7 @@ void rand_init(rand_pool *r) r->ibits = r->obits = 0; r->o = RAND_SECSZ; r->s = 0; - rmd160_hmac(&r->k, 0, 0); + rmd160_hmacinit(&r->k, 0, 0); rand_gate(r); } @@ -116,6 +120,31 @@ void rand_noisesrc(rand_pool *r, const rand_source *s) r->s = s; } +/* --- @rand_seed@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @unsigned bits@ = number of bits to ensure + * + * Returns: --- + * + * Use: Ensures that there are at least @bits@ good bits of entropy + * in the pool. It is recommended that you call this after + * initializing a new pool. Requesting @bits > RAND_IBITS@ is + * doomed to failure (and is an error). + */ + +void rand_seed(rand_pool *r, unsigned bits) +{ + RAND_RESOLVE(r); + + assert(((void)"bits pointlessly large in rand_seed", bits <= RAND_IBITS)); + assert(((void)"no noise source in rand_seed", r->s)); + + while (r->ibits < bits) + r->s->getnoise(r); + rand_gate(r); +} + /* --- @rand_key@ --- * * * Arguments: @rand_pool *r@ = pointer to a randomness pool @@ -131,7 +160,7 @@ void rand_noisesrc(rand_pool *r, const rand_source *s) void rand_key(rand_pool *r, const void *k, size_t sz) { RAND_RESOLVE(r); - rmd160_hmac(&r->k, k, sz); + rmd160_hmacinit(&r->k, k, sz); } /* --- @rand_add@ --- * @@ -218,8 +247,8 @@ void rand_gate(rand_pool *r) rmd160_macctx mc; rmd160_macinit(&mc, &r->k); - rmd160_mac(&mc, r->pool, sizeof(r->pool)); - rmd160_mac(&mc, r->buf, sizeof(r->buf)); + rmd160_machash(&mc, r->pool, sizeof(r->pool)); + rmd160_machash(&mc, r->buf, sizeof(r->buf)); rmd160_macdone(&mc, mac); BURN(mc); } @@ -272,8 +301,8 @@ void rand_stretch(rand_pool *r) rmd160_macctx mc; rmd160_macinit(&mc, &r->k); - rmd160_mac(&mc, r->pool, sizeof(r->pool)); - rmd160_mac(&mc, r->buf, sizeof(r->buf)); + rmd160_machash(&mc, r->pool, sizeof(r->pool)); + rmd160_machash(&mc, r->buf, sizeof(r->buf)); rmd160_macdone(&mc, mac); BURN(mc); } @@ -349,13 +378,11 @@ void rand_get(rand_pool *r, void *p, size_t sz) * * Returns: --- * - * Use: Gets random data from the pool. The pool's contents can't be - * determined from the output of this function; nor can the - * output data be determined from a knowledge of the data input - * to the pool wihtout also having knowledge of the secret key. - * If a noise source is attached to the pool in question, it is - * called to replenish the supply of good bits in the pool; - * otherwise this call is equivalent to @rand_get@. + * Use: Gets random data from the pool, ensuring that there are + * enough good bits. This interface isn't recommended: it makes + * the generator slow, and doesn't provide much more security + * than @rand_get@, assuming you've previously done a + * @rand_seed@. */ void rand_getgood(rand_pool *r, void *p, size_t sz) @@ -377,7 +404,7 @@ void rand_getgood(rand_pool *r, void *p, size_t sz) if (chunk * 8 > r->obits) { if (chunk * 8 > r->ibits + r->obits) - do r->s->getnoise(r); while (r->ibits + r->obits < 128); + do r->s->getnoise(r); while (r->ibits + r->obits < 256); rand_gate(r); if (chunk * 8 > r->obits) chunk = r->obits / 8; @@ -387,10 +414,166 @@ void rand_getgood(rand_pool *r, void *p, size_t sz) chunk = RAND_BUFSZ - r->o; memcpy(o, r->buf + r->o, chunk); + r->o += chunk; r->obits -= chunk * 8; o += chunk; sz -= chunk; } } +/*----- Generic random number generator interface -------------------------*/ + +#define GRESOLVE(g, r) do { \ + if (r != &rand_global) \ + g = (gctx *)r; \ + else { \ + if (!pool) \ + pool = (gctx *)rand_create(); \ + g = pool; \ + } \ +} while (0) + +static void gdestroy(grand *r) +{ + gctx *g; + GRESOLVE(g, r); + if (g != pool) { + BURN(*g); + S_DESTROY(g); + } +} + +static int gmisc(grand *r, unsigned op, ...) +{ + gctx *g; + va_list ap; + int rc = 0; + va_start(ap, op); + + GRESOLVE(g, r); + switch (op) { + case GRAND_CHECK: + switch (va_arg(ap, unsigned)) { + case GRAND_CHECK: + case GRAND_SEEDINT: + case GRAND_SEEDUINT32: + case GRAND_SEEDBLOCK: + case GRAND_SEEDRAND: + case RAND_GATE: + case RAND_STRETCH: + case RAND_KEY: + case RAND_NOISESRC: + case RAND_SEED: + case RAND_TIMER: + case RAND_GOODBITS: + rc = 1; + break; + default: + rc = 0; + break; + } + break; + case GRAND_SEEDINT: { + unsigned u = va_arg(ap, unsigned); + rand_add(&g->p, &u, sizeof(u), sizeof(u)); + } break; + case GRAND_SEEDUINT32: { + uint32 i = va_arg(ap, uint32); + rand_add(&g->p, &i, sizeof(i), 4); + } break; + case GRAND_SEEDBLOCK: { + const void *p = va_arg(ap, const void *); + size_t sz = va_arg(ap, size_t); + rand_add(&g->p, p, sz, sz); + } break; + case GRAND_SEEDRAND: { + grand *rr = va_arg(ap, grand *); + octet buf[16]; + rr->ops->fill(rr, buf, sizeof(buf)); + rand_add(&g->p, buf, sizeof(buf), 8); + } break; + case RAND_GATE: + rand_gate(&g->p); + break; + case RAND_STRETCH: + rand_stretch(&g->p); + break; + case RAND_KEY: { + const void *k = va_arg(ap, const void *); + size_t sz = va_arg(ap, size_t); + rand_key(&g->p, k, sz); + } break; + case RAND_NOISESRC: + rand_noisesrc(&g->p, va_arg(ap, const rand_source *)); + break; + case RAND_SEED: + rand_seed(&g->p, va_arg(ap, unsigned)); + break; + case RAND_TIMER: + TIMER(&g->p); + break; + case RAND_GOODBITS: + rc = rand_goodbits(&g->p); + break; + default: + GRAND_BADOP; + break; + } + + va_end(ap); + return (rc); +} + +static octet gbyte(grand *r) +{ + gctx *g; + octet o; + GRESOLVE(g, r); + rand_getgood(&g->p, &o, 1); + return (o); +} + +static uint32 gword(grand *r) +{ + gctx *g; + octet b[4]; + GRESOLVE(g, r); + rand_getgood(&g->p, &b, sizeof(b)); + return (LOAD32(b)); +} + +static void gfill(grand *r, void *p, size_t sz) +{ + gctx *g; + GRESOLVE(g, r); + rand_get(&g->p, p, sz); +} + +static const grand_ops gops = { + "rand", + GRAND_CRYPTO, 0, + gmisc, gdestroy, + gword, gbyte, gword, grand_range, gfill +}; + +grand rand_global = { &gops }; + +/* --- @rand_create@ --- * + * + * Arguments: --- + * + * Returns: Pointer to a generic generator. + * + * Use: Constructs a generic generator interface over a Catacomb + * entropy pool generator. + */ + +grand *rand_create(void) +{ + gctx *g = S_CREATE(gctx); + g->r.ops = &gops; + rand_init(&g->p); + return (&g->r); +} + /*----- That's all, folks -------------------------------------------------*/