Skip to content

Commit 35c675a

Browse files
committed
Fortuna fixes. Marko Kreen
1 parent 2787db9 commit 35c675a

File tree

5 files changed

+193
-88
lines changed

5 files changed

+193
-88
lines changed

contrib/pgcrypto/fortuna.c

Lines changed: 108 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2727
* SUCH DAMAGE.
2828
*
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 $
3030
*/
3131

3232
#include "postgres.h"
@@ -94,14 +94,16 @@
9494
/* for one big request, reseed after this many bytes */
9595
#define RESEED_BYTES (1024*1024)
9696

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)
97102

98103
/*
99104
* Algorithm constants
100105
*/
101106

102-
/* max sources */
103-
#define MAX_SOURCES 8
104-
105107
/* Both cipher key size and hash result size */
106108
#define BLOCK 32
107109

@@ -118,9 +120,11 @@ struct fortuna_state {
118120
uint8 key[BLOCK];
119121
MD_CTX pool[NUM_POOLS];
120122
CIPH_CTX ciph;
121-
unsigned source_pos[MAX_SOURCES];
122123
unsigned reseed_count;
123124
struct timeval last_reseed_time;
125+
unsigned pool0_bytes;
126+
unsigned rnd_pos;
127+
int counter_init;
124128
};
125129
typedef struct fortuna_state FState;
126130

@@ -161,7 +165,6 @@ static void md_result(MD_CTX *ctx, uint8 *dst)
161165
memset(&tmp, 0, sizeof(tmp));
162166
}
163167

164-
165168
/*
166169
* initialize state
167170
*/
@@ -173,6 +176,32 @@ static void init_state(FState *st)
173176
md_init(&st->pool[i]);
174177
}
175178

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+
176205
/*
177206
* The time between reseed must be at least RESEED_INTERVAL
178207
* microseconds.
@@ -207,9 +236,8 @@ static void reseed(FState *st)
207236
MD_CTX key_md;
208237
uint8 buf[BLOCK];
209238

210-
/* check frequency */
211-
if (too_often(st))
212-
return;
239+
/* set pool as empty */
240+
st->pool0_bytes = 0;
213241

214242
/*
215243
* Both #0 and #1 reseed would use only pool 0.
@@ -243,82 +271,99 @@ static void reseed(FState *st)
243271
memset(buf, 0, BLOCK);
244272
}
245273

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+
246293
/*
247294
* update pools
248295
*/
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)
250297
{
251298
unsigned pos;
252299
uint8 hash[BLOCK];
253300
MD_CTX md;
254301

255-
/* just in case there's a bug somewhere */
256-
if (src_id >= MAX_SOURCES)
257-
src_id = USER_ENTROPY;
258-
259302
/* hash given data */
260303
md_init(&md);
261304
md_update(&md, data, len);
262305
md_result(&md, hash);
263306

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);
266315
md_update( &st->pool[pos], hash, BLOCK);
267316

268-
if (++pos >= NUM_POOLS)
269-
pos = 0;
270-
st->source_pos[src_id] = pos;
317+
if (pos == 0)
318+
st->pool0_bytes += len;
271319

272320
memset(hash, 0, BLOCK);
273321
memset(&md, 0, sizeof(md));
274322
}
275323

276324
/*
277-
* Endianess does not matter.
278-
* It just needs to change without repeating.
325+
* Just take 2 next blocks as new key
279326
*/
280-
static void inc_counter(FState *st)
327+
static void rekey(FState *st)
281328
{
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;
290349
}
291350

292351
static void extract_data(FState *st, unsigned count, uint8 *dst)
293352
{
294353
unsigned n;
295354
unsigned block_nr = 0;
296355

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);
302359

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);
307363

308364
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-
316365
/* 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);
322367

323368
/* copy result */
324369
if (count > CIPH_BLOCK)
@@ -328,7 +373,17 @@ static void extract_data(FState *st, unsigned count, uint8 *dst)
328373
memcpy(dst, st->result, n);
329374
dst += n;
330375
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+
}
331384
}
385+
/* Set new key for next request. */
386+
rekey(st);
332387
}
333388

334389
/*
@@ -338,7 +393,7 @@ static void extract_data(FState *st, unsigned count, uint8 *dst)
338393
static FState main_state;
339394
static int init_done = 0;
340395

341-
void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len)
396+
void fortuna_add_entropy(const uint8 *data, unsigned len)
342397
{
343398
if (!init_done)
344399
{
@@ -347,7 +402,7 @@ void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len)
347402
}
348403
if (!data || !len)
349404
return;
350-
add_entropy(&main_state, src_id, data, len);
405+
add_entropy(&main_state, data, len);
351406
}
352407

353408
void fortuna_get_bytes(unsigned len, uint8 *dst)

contrib/pgcrypto/fortuna.h

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,14 @@
2626
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2727
* SUCH DAMAGE.
2828
*
29-
* $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.h,v 1.1 2005/07/10 13:46:28 momjian Exp $
29+
* $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.h,v 1.2 2005/07/18 17:12:54 tgl Exp $
3030
*/
3131

3232
#ifndef __FORTUNA_H
3333
#define __FORTUNA_H
3434

35-
/*
36-
* Event source ID's
37-
*/
38-
#define SYSTEM_ENTROPY 0
39-
#define USER_ENTROPY 1
40-
4135
void fortuna_get_bytes(unsigned len, uint8 *dst);
42-
void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len);
36+
void fortuna_add_entropy(const uint8 *data, unsigned len);
4337

4438
#endif
4539

contrib/pgcrypto/internal.c

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2727
* SUCH DAMAGE.
2828
*
29-
* $PostgreSQL: pgsql/contrib/pgcrypto/internal.c,v 1.21 2005/07/18 17:09:01 tgl Exp $
29+
* $PostgreSQL: pgsql/contrib/pgcrypto/internal.c,v 1.22 2005/07/18 17:12:54 tgl Exp $
3030
*/
3131

3232
#include "postgres.h"
@@ -42,9 +42,22 @@
4242
#include "fortuna.h"
4343

4444
/*
45-
* How often to try to acquire system entropy. (In seconds)
45+
* System reseeds should be separated at least this much.
4646
*/
47-
#define SYSTEM_RESEED_FREQ (3*60*60)
47+
#define SYSTEM_RESEED_MIN (20*60) /* 20 min */
48+
/*
49+
* How often to roll dice.
50+
*/
51+
#define SYSTEM_RESEED_CHECK_TIME (10*60) /* 10 min */
52+
/*
53+
* The chance is x/256 that the reseed happens.
54+
*/
55+
#define SYSTEM_RESEED_CHANCE (4) /* 256/4 * 10min ~ 10h */
56+
57+
/*
58+
* If this much time has passed, force reseed.
59+
*/
60+
#define SYSTEM_RESEED_MAX (12*60*60) /* 12h */
4861

4962

5063
#ifndef MD5_DIGEST_LENGTH
@@ -823,20 +836,40 @@ px_get_pseudo_random_bytes(uint8 *dst, unsigned count)
823836
}
824837

825838
static time_t seed_time = 0;
839+
static time_t check_time = 0;
826840

827841
static void system_reseed(void)
828842
{
829843
uint8 buf[1024];
830844
int n;
831845
time_t t;
846+
int skip = 1;
832847

833848
t = time(NULL);
834-
if (seed_time && (t - seed_time) < SYSTEM_RESEED_FREQ)
849+
850+
if (seed_time == 0)
851+
skip = 0;
852+
else if ((t - seed_time) < SYSTEM_RESEED_MIN)
853+
skip = 1;
854+
else if ((t - seed_time) > SYSTEM_RESEED_MAX)
855+
skip = 0;
856+
else if (!check_time || (t - check_time) > SYSTEM_RESEED_CHECK_TIME)
857+
{
858+
check_time = t;
859+
860+
/* roll dice */
861+
px_get_random_bytes(buf, 1);
862+
skip = buf[0] >= SYSTEM_RESEED_CHANCE;
863+
}
864+
/* clear 1 byte */
865+
memset(buf, 0, sizeof(buf));
866+
867+
if (skip)
835868
return;
836869

837870
n = px_acquire_system_randomness(buf);
838871
if (n > 0)
839-
fortuna_add_entropy(SYSTEM_ENTROPY, buf, n);
872+
fortuna_add_entropy(buf, n);
840873

841874
seed_time = t;
842875
memset(buf, 0, sizeof(buf));
@@ -854,7 +887,7 @@ int
854887
px_add_entropy(const uint8 *data, unsigned count)
855888
{
856889
system_reseed();
857-
fortuna_add_entropy(USER_ENTROPY, data, count);
890+
fortuna_add_entropy(data, count);
858891
return 0;
859892
}
860893

0 commit comments

Comments
 (0)