26
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
27
* SUCH DAMAGE.
28
28
*
29
- * $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.c,v 1.3 2005/07/18 17:09:01 tgl Exp $
29
+ * $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.c,v 1.4 2005/07/18 17:12:54 tgl Exp $
30
30
*/
31
31
32
32
#include "postgres.h"
94
94
/* for one big request, reseed after this many bytes */
95
95
#define RESEED_BYTES (1024*1024)
96
96
97
+ /*
98
+ * Skip reseed if pool 0 has less than this many
99
+ * bytes added since last reseed.
100
+ */
101
+ #define POOL0_FILL (256/8)
97
102
98
103
/*
99
104
* Algorithm constants
100
105
*/
101
106
102
- /* max sources */
103
- #define MAX_SOURCES 8
104
-
105
107
/* Both cipher key size and hash result size */
106
108
#define BLOCK 32
107
109
@@ -118,9 +120,11 @@ struct fortuna_state {
118
120
uint8 key [BLOCK ];
119
121
MD_CTX pool [NUM_POOLS ];
120
122
CIPH_CTX ciph ;
121
- unsigned source_pos [MAX_SOURCES ];
122
123
unsigned reseed_count ;
123
124
struct timeval last_reseed_time ;
125
+ unsigned pool0_bytes ;
126
+ unsigned rnd_pos ;
127
+ int counter_init ;
124
128
};
125
129
typedef struct fortuna_state FState ;
126
130
@@ -161,7 +165,6 @@ static void md_result(MD_CTX *ctx, uint8 *dst)
161
165
memset (& tmp , 0 , sizeof (tmp ));
162
166
}
163
167
164
-
165
168
/*
166
169
* initialize state
167
170
*/
@@ -173,6 +176,32 @@ static void init_state(FState *st)
173
176
md_init (& st -> pool [i ]);
174
177
}
175
178
179
+ /*
180
+ * Endianess does not matter.
181
+ * It just needs to change without repeating.
182
+ */
183
+ static void inc_counter (FState * st )
184
+ {
185
+ uint32 * val = (uint32 * )st -> counter ;
186
+ if (++ val [0 ])
187
+ return ;
188
+ if (++ val [1 ])
189
+ return ;
190
+ if (++ val [2 ])
191
+ return ;
192
+ ++ val [3 ];
193
+ }
194
+
195
+ /*
196
+ * This is called 'cipher in counter mode'.
197
+ */
198
+ static void encrypt_counter (FState * st , uint8 * dst )
199
+ {
200
+ ciph_encrypt (& st -> ciph , st -> counter , dst );
201
+ inc_counter (st );
202
+ }
203
+
204
+
176
205
/*
177
206
* The time between reseed must be at least RESEED_INTERVAL
178
207
* microseconds.
@@ -207,9 +236,8 @@ static void reseed(FState *st)
207
236
MD_CTX key_md ;
208
237
uint8 buf [BLOCK ];
209
238
210
- /* check frequency */
211
- if (too_often (st ))
212
- return ;
239
+ /* set pool as empty */
240
+ st -> pool0_bytes = 0 ;
213
241
214
242
/*
215
243
* Both #0 and #1 reseed would use only pool 0.
@@ -243,82 +271,99 @@ static void reseed(FState *st)
243
271
memset (buf , 0 , BLOCK );
244
272
}
245
273
274
+ /*
275
+ * Pick a random pool. This uses key bytes as random source.
276
+ */
277
+ static unsigned get_rand_pool (FState * st )
278
+ {
279
+ unsigned rnd ;
280
+
281
+ /*
282
+ * This slightly prefers lower pools - thats OK.
283
+ */
284
+ rnd = st -> key [st -> rnd_pos ] % NUM_POOLS ;
285
+
286
+ st -> rnd_pos ++ ;
287
+ if (st -> rnd_pos >= BLOCK )
288
+ st -> rnd_pos = 0 ;
289
+
290
+ return rnd ;
291
+ }
292
+
246
293
/*
247
294
* update pools
248
295
*/
249
- static void add_entropy (FState * st , unsigned src_id , const uint8 * data , unsigned len )
296
+ static void add_entropy (FState * st , const uint8 * data , unsigned len )
250
297
{
251
298
unsigned pos ;
252
299
uint8 hash [BLOCK ];
253
300
MD_CTX md ;
254
301
255
- /* just in case there's a bug somewhere */
256
- if (src_id >= MAX_SOURCES )
257
- src_id = USER_ENTROPY ;
258
-
259
302
/* hash given data */
260
303
md_init (& md );
261
304
md_update (& md , data , len );
262
305
md_result (& md , hash );
263
306
264
- /* update pools round-robin manner */
265
- pos = st -> source_pos [src_id ];
307
+ /*
308
+ * Make sure the pool 0 is initialized,
309
+ * then update randomly.
310
+ */
311
+ if (st -> reseed_count == 0 && st -> pool0_bytes < POOL0_FILL )
312
+ pos = 0 ;
313
+ else
314
+ pos = get_rand_pool (st );
266
315
md_update ( & st -> pool [pos ], hash , BLOCK );
267
316
268
- if (++ pos >= NUM_POOLS )
269
- pos = 0 ;
270
- st -> source_pos [src_id ] = pos ;
317
+ if (pos == 0 )
318
+ st -> pool0_bytes += len ;
271
319
272
320
memset (hash , 0 , BLOCK );
273
321
memset (& md , 0 , sizeof (md ));
274
322
}
275
323
276
324
/*
277
- * Endianess does not matter.
278
- * It just needs to change without repeating.
325
+ * Just take 2 next blocks as new key
279
326
*/
280
- static void inc_counter (FState * st )
327
+ static void rekey (FState * st )
281
328
{
282
- uint32 * val = (uint32 * )st -> counter ;
283
- if (++ val [0 ])
284
- return ;
285
- if (++ val [1 ])
286
- return ;
287
- if (++ val [2 ])
288
- return ;
289
- ++ val [3 ];
329
+ encrypt_counter (st , st -> key );
330
+ encrypt_counter (st , st -> key + CIPH_BLOCK );
331
+ ciph_init (& st -> ciph , st -> key , BLOCK );
332
+ }
333
+
334
+ /*
335
+ * Fortuna relies on AES standing known-plaintext attack.
336
+ * In case it does not, slow down the attacker by initialising
337
+ * the couter to random value.
338
+ */
339
+ static void init_counter (FState * st )
340
+ {
341
+ /* Use next block as counter. */
342
+ encrypt_counter (st , st -> counter );
343
+
344
+ /* Hide the key. */
345
+ rekey (st );
346
+
347
+ /* The counter can be shuffled only once. */
348
+ st -> counter_init = 1 ;
290
349
}
291
350
292
351
static void extract_data (FState * st , unsigned count , uint8 * dst )
293
352
{
294
353
unsigned n ;
295
354
unsigned block_nr = 0 ;
296
355
297
- /*
298
- * Every request should be with different key,
299
- * if possible.
300
- */
301
- reseed (st );
356
+ /* Can we reseed? */
357
+ if (st -> pool0_bytes >= POOL0_FILL && !too_often (st ))
358
+ reseed (st );
302
359
303
- /*
304
- * If the reseed didn't happen, don't use the old data
305
- * rather encrypt again.
306
- */
360
+ /* Is counter initialized? */
361
+ if (!st -> counter_init )
362
+ init_counter (st );
307
363
308
364
while (count > 0 ) {
309
- /* must not give out too many bytes with one key */
310
- if (block_nr > (RESEED_BYTES / CIPH_BLOCK ))
311
- {
312
- reseed (st );
313
- block_nr = 0 ;
314
- }
315
-
316
365
/* produce bytes */
317
- ciph_encrypt (& st -> ciph , st -> counter , st -> result );
318
- block_nr ++ ;
319
-
320
- /* prepare for next time */
321
- inc_counter (st );
366
+ encrypt_counter (st , st -> result );
322
367
323
368
/* copy result */
324
369
if (count > CIPH_BLOCK )
@@ -328,7 +373,17 @@ static void extract_data(FState *st, unsigned count, uint8 *dst)
328
373
memcpy (dst , st -> result , n );
329
374
dst += n ;
330
375
count -= n ;
376
+
377
+ /* must not give out too many bytes with one key */
378
+ block_nr ++ ;
379
+ if (block_nr > (RESEED_BYTES / CIPH_BLOCK ))
380
+ {
381
+ rekey (st );
382
+ block_nr = 0 ;
383
+ }
331
384
}
385
+ /* Set new key for next request. */
386
+ rekey (st );
332
387
}
333
388
334
389
/*
@@ -338,7 +393,7 @@ static void extract_data(FState *st, unsigned count, uint8 *dst)
338
393
static FState main_state ;
339
394
static int init_done = 0 ;
340
395
341
- void fortuna_add_entropy (unsigned src_id , const uint8 * data , unsigned len )
396
+ void fortuna_add_entropy (const uint8 * data , unsigned len )
342
397
{
343
398
if (!init_done )
344
399
{
@@ -347,7 +402,7 @@ void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len)
347
402
}
348
403
if (!data || !len )
349
404
return ;
350
- add_entropy (& main_state , src_id , data , len );
405
+ add_entropy (& main_state , data , len );
351
406
}
352
407
353
408
void fortuna_get_bytes (unsigned len , uint8 * dst )
0 commit comments