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"
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 {
118120uint8 key [BLOCK ];
119121MD_CTX pool [NUM_POOLS ];
120122CIPH_CTX ciph ;
121- unsigned source_pos [MAX_SOURCES ];
122123unsigned reseed_count ;
123124struct timeval last_reseed_time ;
125+ unsigned pool0_bytes ;
126+ unsigned rnd_pos ;
127+ int counter_init ;
124128};
125129typedef struct fortuna_state FState ;
126130
@@ -161,7 +165,6 @@ static void md_result(MD_CTX *ctx, uint8 *dst)
161165memset (& tmp ,0 ,sizeof (tmp ));
162166}
163167
164-
165168/*
166169 * initialize state
167170 */
@@ -173,6 +176,32 @@ static void init_state(FState *st)
173176md_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)
207236MD_CTX key_md ;
208237uint8 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)
243271memset (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{
251298unsigned pos ;
252299uint8 hash [BLOCK ];
253300MD_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 */
260303md_init (& md );
261304md_update (& md ,data ,len );
262305md_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 );
266315md_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
272320memset (hash ,0 ,BLOCK );
273321memset (& 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
292351static void extract_data (FState * st ,unsigned count ,uint8 * dst )
293352{
294353unsigned n ;
295354unsigned 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
308364while (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 */
324369if (count > CIPH_BLOCK )
@@ -328,7 +373,17 @@ static void extract_data(FState *st, unsigned count, uint8 *dst)
328373memcpy (dst ,st -> result ,n );
329374dst += n ;
330375count -= 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)
338393static FState main_state ;
339394static 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{
343398if (!init_done )
344399{
@@ -347,7 +402,7 @@ void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len)
347402}
348403if (!data || !len )
349404return ;
350- add_entropy (& main_state ,src_id , data ,len );
405+ add_entropy (& main_state ,data ,len );
351406}
352407
353408void fortuna_get_bytes (unsigned len ,uint8 * dst )