chiark / gitweb /
New source file added to maintain a randomness pool.
[become] / src / rand.c
1 /* -*-c-*-
2  *
3  * $Id: rand.c,v 1.1 1997/08/07 09:46:05 mdw Exp $
4  *
5  * Random number generation
6  *
7  * (c) 1997 EBI
8  */
9
10 /*----- Licencing notice --------------------------------------------------*
11  *
12  * This file is part of Become.
13  *
14  * Become is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * Become 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 General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with Become; if not, write to the Free Software
26  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------*
30  *
31  * $Log: rand.c,v $
32  * Revision 1.1  1997/08/07 09:46:05  mdw
33  * New source file added to maintain a randomness pool.
34  *
35  */
36
37 /*----- Header files ------------------------------------------------------*/
38
39 /* --- ANSI headers --- */
40
41 #include <ctype.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <time.h>
47
48 /* --- Local headers --- */
49
50 #include "become.h"
51 #include "config.h"
52 #include "icrypt.h"
53 #include "md5.h"
54 #include "rand.h"
55 #include "tx.h"
56 #include "utils.h"
57
58 /*----- Magic numbers -----------------------------------------------------*/
59
60 #define rand__seedBits 512              /* Number of random seed bits */
61
62 /*----- Persistant state --------------------------------------------------*/
63
64 static unsigned char rand__pool[rand__seedBits / 8]; /* Entropy pool */
65 static size_t rand__i = 0;              /* Index into entropy pool */
66 static size_t rand__r = 0;              /* Rotation to apply to next byte */
67
68 /*----- Main code ---------------------------------------------------------*/
69
70 /* --- @rand_read@ --- *
71  *
72  * Arguments:   @FILE *fp@ = pointer to file to read from
73  *
74  * Returns:     ---
75  *
76  * Use:         Reads a random number seed from the stream.
77  */
78
79 void rand_read(FILE *fp)
80 {
81   tx_getBits(rand__pool, rand__seedBits, fp);
82   rand__i = 0;
83   rand__r = 0;
84   T( traceblk(TRACE_RAND, "rand: loaded randomness pool",
85               rand__pool, sizeof(rand__pool)); )
86 }
87
88 /* --- @rand_write@ --- *
89  *
90  * Arguments:   @FILE *fp@ = pointer to file to write on
91  *
92  * Returns:     ---
93  *
94  * Use:         Writes a random number seed back to the stream.
95  */
96
97 void rand_write(FILE *fp)
98 {
99   rand_churn();
100   tx_putBits(rand__pool, rand__seedBits, fp);
101 }
102
103 /* --- @rand_clear@ --- *
104  *
105  * Arguments:   ---
106  *
107  * Returns:     ---
108  *
109  * Use:         Clears the random number pool.
110  */
111
112 void rand_clear(void)
113 {
114   memset(rand__pool, 0, sizeof(rand__pool));
115   T( trace(TRACE_RAND, "rand: cleared randomness pool"); )
116   rand__i = 0;
117   rand__r = 0;
118 }
119
120 /* --- @rand_encrypt@ --- *
121  *
122  * Arguments:   @icrypt_job *j@ = pointer to an encryption job to apply
123  *
124  * Returns:     ---
125  *
126  * Use:         Encrypts the randomness pool with a given key.  This should
127  *              be done before use, in case the pool gets compromised.
128  */
129
130 void rand_encrypt(icrypt_job *j)
131 {
132   icrypt_encrypt(j, rand__pool, rand__pool, sizeof(rand__pool));
133   rand__i = 0;
134   rand__r = 0;
135   T( traceblk(TRACE_RAND, "encrypted randomness pool",
136               rand__pool, sizeof(rand__pool)); )
137 }  
138
139 /* --- @rand_add@ --- *
140  *
141  * Arguments:   @const void *p@ = pointer to some data
142  *              @size_t sz@ = size of the data in bytes
143  *
144  * Returns:     ---
145  *
146  * Use:         Adds entropy to the pool.
147  */
148
149 void rand_add(const void *p, size_t sz)
150 {
151   /* --- Method --- *
152    *
153    * Entropy is inserted byte-by-byte.  The method used is derived from
154    * Linux's `drivers/char/random.c', which is in turn appears to be inspired
155    * by SHA-1.
156    *
157    * Imagine the randomness pool as 8 parallel shift registers.  To insert
158    * a byte into the pool, XOR it with bytes picked according to a primitive
159    * polynomial mod 2, and rotate one place to the left.  (The rotation is
160    * from SHA-1; it introduces some interaction between the registers.)
161    */
162
163   size_t i = rand__i;
164   const unsigned char *cp = p;
165   size_t mask = (rand__seedBits / 8) - 1;
166   size_t r = rand__r;
167   unsigned int x;
168
169   /* --- Ensure the polynomial is valid --- *
170    *
171    * The current polynomal is %$x^{64} + x^4 + x^3 + x + 1$% (picked from
172    * Schneier's excellent book).  If the pool size changes, though, I'll
173    * need another polynomial.
174    */
175
176 #if rand__seedBits / 8 != 64
177 #  error Change the polynomal in rand_add
178 #endif
179
180   T( traceblk(TRACE_RAND, "rand: incoming entropy", p, sz); )
181
182   /* --- Add values to the entropy pool --- */
183
184   while (sz) {
185     x = *cp++ & 0xffu;
186     if (r)
187       x = ((x << r) | (x >> (8 - r)));
188     x ^= (rand__pool[i] ^
189           rand__pool[(i + 1) & mask] ^
190           rand__pool[(i + 3) & mask] ^
191           rand__pool[(i + 4) & mask]);
192     rand__pool[i] = ((x << 1) | (x >> 7)) & 0xffu;
193     sz--;
194     i = (i + 1) & mask;
195     r = (r + 3) & 7;
196   }
197
198   /* --- Update the global counters --- */
199
200   rand__r = r;
201   rand__i = i;
202
203   T( traceblk(TRACE_RAND, "rand: added some entropy",
204               rand__pool, sizeof(rand__pool)); )
205 }
206
207 /* --- @rand_extract@ --- *
208  *
209  * Arguments:   @unsigned char *b@ = pointer to output buffer
210  *              @size_t sz@ = number of bytes wanted
211  *
212  * Returns:     ---
213  *
214  * Use:         Produces a number of random bytes.
215  */
216
217 void rand_extract(unsigned char *b, size_t sz)
218 {
219   /* --- Method --- *
220    *
221    * Hash the buffer with a secure hash (or, in this case, with MD5 and hope
222    * for the best...).  If this gives us enough bytes, fill the output
223    * buffer; otherwise copy the whole hash out.  The contribute the hash back
224    * into the randomness pool with @rand_add@ to provide some feedback.
225    *
226    * The secure hash gives us good mixing over the whole of the randomness
227    * pool, and attempts to ensure that an attacker receiving our random
228    * numbers can't predict any numbers we don't actually give him.
229    */
230
231   size_t c;
232   unsigned char mdbuf[MD5_HASHSIZE];
233   md5 md;
234
235   T( trace(TRACE_RAND, "rand: extracting entropy"); )
236
237   while (sz) {
238     c = (sz >= sizeof(mdbuf)) ? sizeof(mdbuf) : sz;
239     md5_init(&md);
240     md5_buffer(&md, rand__pool, sizeof(rand__pool));
241     md5_final(&md, mdbuf);
242     memcpy(b, mdbuf, c);
243     rand_add(mdbuf, c);
244     b += c; sz -= c;
245   }
246   burn(mdbuf); burn(md);
247
248   T( trace(TRACE_RAND, "rand: finished extracting entropy"); )
249 }
250
251 /* --- @rand_churn@ --- *
252  *
253  * Arguments:   ---
254  *
255  * Returns:     ---
256  *
257  * Use:         Churns the randomness pool completely.  The pool is replaced
258  *              by repeated MD5-ings of itself.
259  */
260
261 void rand_churn(void)
262 {
263   md5 md;
264   unsigned char mdbuf[MD5_HASHSIZE];
265   size_t sz = sizeof(rand__pool);
266   size_t c;
267
268   T( trace(TRACE_RAND, "rand: churning pool"); )
269
270   rand__i = 0;
271   rand__r = 0;
272
273   while (sz) {
274     c = (sz >= sizeof(mdbuf)) ? sizeof(mdbuf) : sz;
275     md5_init(&md);
276     md5_buffer(&md, rand__pool, sizeof(rand__pool));
277     md5_final(&md, mdbuf);
278     rand_add(mdbuf, c);
279     sz -= c;
280   }
281
282   rand__i = 0;
283   rand__r = 0;
284
285   burn(mdbuf); burn(md);
286
287   T( trace(TRACE_RAND, "rand: finished churning pool"); )
288 }
289
290 /*----- That's all, folks -------------------------------------------------*/