New multiprecision integer arithmetic suite.
[u/mdw/catacomb] / rand.c
CommitLineData
d03ab969 1/* -*-c-*-
2 *
ba044e65 3 * $Id: rand.c,v 1.2 1999/10/12 21:00:15 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 $
ba044e65 33 * Revision 1.2 1999/10/12 21:00:15 mdw
34 * Make pool and buffer sizes more sensible.
35 *
d03ab969 36 * Revision 1.1 1999/09/03 08:41:12 mdw
37 * Initial import.
38 *
39 */
40
41/*----- Header files ------------------------------------------------------*/
42
43#include <stdio.h>
44#include <string.h>
45
46#include <mLib/bits.h>
47
48#include "blowfish-cbc.h"
49#include "paranoia.h"
50#include "rand.h"
51#include "rmd160.h"
52#include "rmd160-hmac.h"
53
54/*----- Static variables --------------------------------------------------*/
55
56static rand_pool pool; /* Default random pool */
57
58/*----- Macros ------------------------------------------------------------*/
59
60#define RAND_RESOLVE(r) do { \
61 if ((r) == RAND_GLOBAL) \
62 (r) = &pool; \
63} while (0)
64
65#define TIMER(r) do { \
66 if ((r)->s && (r)->s->timer) \
67 (r)->s->timer(r); \
68} while (0)
69
70/*----- Main code ---------------------------------------------------------*/
71
72/* --- @rand_init@ --- *
73 *
74 * Arguments: @rand_pool *r@ = pointer to a randomness pool
75 *
76 * Returns: ---
77 *
78 * Use: Initializes a randomness pool. The pool doesn't start out
79 * very random: that's your job to sort out.
80 */
81
82void rand_init(rand_pool *r)
83{
84 RAND_RESOLVE(r);
85 memset(r->pool, 0, sizeof(r->pool));
86 memset(r->buf, 0, sizeof(r->buf));
87 r->i = 0;
88 r->irot = 0;
89 r->ibits = r->obits = 0;
90 r->o = RAND_SECSZ;
91 r->s = 0;
92 rmd160_hmac(&r->k, 0, 0);
93 rand_gate(r);
94}
95
96/* --- @rand_noisesrc@ --- *
97 *
98 * Arguments: @rand_pool *r@ = pointer to a randomness pool
99 * @const rand_source *s@ = pointer to source definition
100 *
101 * Returns: ---
102 *
103 * Use: Sets a noise source for a randomness pool. When the pool's
104 * estimate of good random bits falls to zero, the @getnoise@
105 * function is called, passing the pool handle as an argument.
106 * It is expected to increase the number of good bits by at
107 * least one, because it'll be called over and over again until
108 * there are enough bits to satisfy the caller. The @timer@
109 * function is called frequently throughout the generator's
110 * operation.
111 */
112
113void rand_noisesrc(rand_pool *r, const rand_source *s)
114{
115 RAND_RESOLVE(r);
116 r->s = s;
117}
118
119/* --- @rand_key@ --- *
120 *
121 * Arguments: @rand_pool *r@ = pointer to a randomness pool
122 * @const void *k@ = pointer to key data
123 * @size_t sz@ = size of key data
124 *
125 * Returns: ---
126 *
127 * Use: Sets the secret key for a randomness pool. The key is used
128 * when mixing in new random bits.
129 */
130
131void rand_key(rand_pool *r, const void *k, size_t sz)
132{
133 RAND_RESOLVE(r);
134 rmd160_hmac(&r->k, k, sz);
135}
136
137/* --- @rand_add@ --- *
138 *
139 * Arguments: @rand_pool *r@ = pointer to a randomness pool
140 * @const void *p@ = pointer a buffer of data to add
141 * @size_t sz@ = size of the data buffer
142 * @unsigned goodbits@ = number of good bits estimated in buffer
143 *
144 * Returns: ---
145 *
146 * Use: Mixes the data in the buffer with the contents of the
147 * pool. The estimate of the number of good bits is added to
148 * the pool's own count. The mixing operation is not
149 * cryptographically strong. However, data in the input pool
150 * isn't output directly, only through the one-way gating
151 * operation, so that shouldn't matter.
152 */
153
154void rand_add(rand_pool *r, const void *p, size_t sz, unsigned goodbits)
155{
156 const octet *c = p;
ba044e65 157 int i, rot;
d03ab969 158
ba044e65 159#if RAND_POOLSZ != 128
d03ab969 160# error Polynomial in rand_add is out of date. Fix it.
161#endif
162
163 RAND_RESOLVE(r);
164
ba044e65 165 i = r->i; rot = r->irot;
d03ab969 166
167 while (sz) {
168 octet o = *c++;
ba044e65 169 r->pool[i] ^= (ROL8(o, rot) ^
170 r->pool[(i + 1) % RAND_POOLSZ] ^
171 r->pool[(i + 2) % RAND_POOLSZ] ^
172 r->pool[(i + 7) % RAND_POOLSZ]);
d03ab969 173 rot = (rot + 5) & 7;
174 i++; if (i >= RAND_POOLSZ) i -= RAND_POOLSZ;
d03ab969 175 sz--;
176 }
177
178 r->i = i;
179 r->irot = rot;
180 r->ibits += goodbits;
181 if (r->ibits > RAND_IBITS)
182 r->ibits = RAND_IBITS;
183}
184
185/* --- @rand_goodbits@ --- *
186 *
187 * Arguments: @rand_pool *r@ = pointer to a randomness pool
188 *
189 * Returns: Estimate of the number of good bits remaining in the pool.
190 */
191
192unsigned rand_goodbits(rand_pool *r)
193{
194 RAND_RESOLVE(r);
195 return (r->ibits + r->obits);
196}
197
198/* --- @rand_gate@ --- *
199 *
200 * Arguments: @rand_pool *r@ = pointer to a randomness pool
201 *
202 * Returns: ---
203 *
204 * Use: Mixes up the entire state of the generator in a nonreversible
205 * way.
206 */
207
208void rand_gate(rand_pool *r)
209{
210 octet mac[RMD160_HASHSZ];
211
212 RAND_RESOLVE(r);
213 TIMER(r);
214
215 /* --- Hash up all the data in the pool --- */
216
217 {
218 rmd160_macctx mc;
219
220 rmd160_macinit(&mc, &r->k);
221 rmd160_mac(&mc, r->pool, sizeof(r->pool));
222 rmd160_mac(&mc, r->buf, sizeof(r->buf));
223 rmd160_macdone(&mc, mac);
224 BURN(mc);
225 }
226
227 /* --- Now mangle all of the data based on the hash --- */
228
229 {
230 blowfish_cbcctx bc;
231
232 blowfish_cbcinit(&bc, mac, sizeof(mac), 0);
233 blowfish_cbcencrypt(&bc, r->pool, r->pool, sizeof(r->pool));
234 blowfish_cbcencrypt(&bc, r->buf, r->buf, sizeof(r->buf));
235 BURN(bc);
236 }
237
238 /* --- Reset the various state variables --- */
239
240 r->o = RAND_SECSZ;
241 r->obits += r->ibits;
242 if (r->obits > RAND_OBITS) {
243 r->ibits = r->obits - r->ibits;
244 r->obits = RAND_OBITS;
245 } else
246 r->ibits = 0;
247 TIMER(r);
248}
249
250/* --- @rand_stretch@ --- *
251 *
252 * Arguments: @rand_pool *r@ = pointer to a randomness pool
253 *
254 * Returns: ---
255 *
256 * Use: Stretches the contents of the output buffer by transforming
257 * it in a nonreversible way. This doesn't add any entropy
258 * worth speaking about, but it works well enough when the
259 * caller doesn't care about that sort of thing.
260 */
261
262void rand_stretch(rand_pool *r)
263{
264 octet mac[RMD160_HASHSZ];
265
266 RAND_RESOLVE(r);
267 TIMER(r);
268
269 /* --- Hash up all the data in the buffer --- */
270
271 {
272 rmd160_macctx mc;
273
274 rmd160_macinit(&mc, &r->k);
275 rmd160_mac(&mc, r->pool, sizeof(r->pool));
276 rmd160_mac(&mc, r->buf, sizeof(r->buf));
277 rmd160_macdone(&mc, mac);
278 BURN(mc);
279 }
280
281 /* --- Now mangle the buffer based on that hash --- */
282
283 {
284 blowfish_cbcctx bc;
285
286 blowfish_cbcinit(&bc, mac, sizeof(mac), 0);
287 blowfish_cbcencrypt(&bc, r->buf, r->buf, sizeof(r->buf));
288 BURN(bc);
289 }
290
291 /* --- Reset the various state variables --- */
292
293 r->o = RAND_SECSZ;
294 TIMER(r);
295}
296
297/* --- @rand_get@ --- *
298 *
299 * Arguments: @rand_pool *r@ = pointer to a randomness pool
300 * @void *p@ = pointer to output buffer
301 * @size_t sz@ = size of output buffer
302 *
303 * Returns: ---
304 *
305 * Use: Gets random data from the pool. The pool's contents can't be
306 * determined from the output of this function; nor can the
307 * output data be determined from a knowledge of the data input
308 * to the pool wihtout also having knowledge of the secret key.
309 * The good bits counter is decremented, although no special
310 * action is taken if it reaches zero.
311 */
312
313void rand_get(rand_pool *r, void *p, size_t sz)
314{
315 octet *o = p;
316
317 RAND_RESOLVE(r);
318 TIMER(r);
319
320 if (!sz)
321 return;
322 for (;;) {
323 if (r->o + sz <= RAND_BUFSZ) {
324 memcpy(o, r->buf + r->o, sz);
325 r->o += sz;
326 break;
327 } else {
328 size_t chunk = RAND_BUFSZ - r->o;
329 if (chunk) {
330 memcpy(o, r->buf + r->o, chunk);
331 sz -= chunk;
332 o += chunk;
333 }
334 rand_stretch(r);
335 }
336 }
337
338 if (r->obits > sz * 8)
339 r->obits -= sz * 8;
340 else
341 r->obits = 0;
342}
343
344/* --- @rand_getgood@ --- *
345 *
346 * Arguments: @rand_pool *r@ = pointer to a randomness pool
347 * @void *p@ = pointer to output buffer
348 * @size_t sz@ = size of output buffer
349 *
350 * Returns: ---
351 *
352 * Use: Gets random data from the pool. The pool's contents can't be
353 * determined from the output of this function; nor can the
354 * output data be determined from a knowledge of the data input
355 * to the pool wihtout also having knowledge of the secret key.
356 * If a noise source is attached to the pool in question, it is
357 * called to replenish the supply of good bits in the pool;
358 * otherwise this call is equivalent to @rand_get@.
359 */
360
361void rand_getgood(rand_pool *r, void *p, size_t sz)
362{
363 octet *o = p;
364
365 RAND_RESOLVE(r);
366
367 if (!sz)
368 return;
369 if (!r->s || !r->s->getnoise) {
370 rand_get(r, p, sz);
371 return;
372 }
373 TIMER(r);
374
375 while (sz) {
376 size_t chunk = sz;
377
378 if (chunk * 8 > r->obits) {
379 if (chunk * 8 > r->ibits + r->obits)
380 do r->s->getnoise(r); while (r->ibits + r->obits < 128);
381 rand_gate(r);
382 if (chunk * 8 > r->obits)
383 chunk = r->obits / 8;
384 }
385
386 if (chunk + r->o > RAND_BUFSZ)
387 chunk = RAND_BUFSZ - r->o;
388
389 memcpy(o, r->buf + r->o, chunk);
390 r->obits -= chunk * 8;
391 o += chunk;
392 sz -= chunk;
393 }
394}
395
396/*----- That's all, folks -------------------------------------------------*/