Re: [PATCHES] [patch 2/3] Fortuna fixes

2005-07-18 Thread Tom Lane
Marko Kreen marko@l-t.ee writes:
 After studying Fortuna more, I found out that I missed
 some of the details.

 - reseeding should happen only if pool #0 has aquired additional
   entropy.

 - a 'rekeying' operation should happend after each request and
   also after 1M of extracted data.  That means taking next two
   blocks and using it as new key.

 - Fortuna _really_ wants entropy sources to be somewhat unpredictible.

   So roll dice when adding it and also add them to pools randomly,
   not sequentially.

   This hopefully makes harder for someone to doctor with the
   internal state (as in our case user can directly control
   what goes into it).

   That also drops the idea of several sources - which really
   fits more to hardware backed event sources.
  
 - add a really obvious obfuscation: take the absolutely first
   block be initial counter value.  If Fortuna (AES to be exact)
   is secure with known counter value, then it should be also
   secure with unknown counter value.  This does not go against
   the important property of counter - that the bit-pattern repeat
   period should be as long as possible.

 - S2K functions should use px_get_pseudo_random_bytes not
   px_get_random_bytes.

Applied.

regards, tom lane

---(end of broadcast)---
TIP 3: Have you checked our extensive FAQ?

   http://www.postgresql.org/docs/faq


[PATCHES] [patch 2/3] Fortuna fixes

2005-07-15 Thread Marko Kreen
After studying Fortuna more, I found out that I missed
some of the details.

- reseeding should happen only if pool #0 has aquired additional
  entropy.

- a 'rekeying' operation should happend after each request and
  also after 1M of extracted data.  That means taking next two
  blocks and using it as new key.

- Fortuna _really_ wants entropy sources to be somewhat unpredictible.

  So roll dice when adding it and also add them to pools randomly,
  not sequentially.

  This hopefully makes harder for someone to doctor with the
  internal state (as in our case user can directly control
  what goes into it).

  That also drops the idea of several sources - which really
  fits more to hardware backed event sources.
  
- add a really obvious obfuscation: take the absolutely first
  block be initial counter value.  If Fortuna (AES to be exact)
  is secure with known counter value, then it should be also
  secure with unknown counter value.  This does not go against
  the important property of counter - that the bit-pattern repeat
  period should be as long as possible.

- S2K functions should use px_get_pseudo_random_bytes not
  px_get_random_bytes.

Index: pgsql/contrib/pgcrypto/fortuna.c
===
*** pgsql.orig/contrib/pgcrypto/fortuna.c
--- pgsql/contrib/pgcrypto/fortuna.c
***
*** 94,107 
  /* for one big request, reseed after this many bytes */
  #define RESEED_BYTES  (1024*1024)
  
  
  /*
   * Algorithm constants
   */
  
- /* max sources */
- #define MAX_SOURCES   8
- 
  /* Both cipher key size and hash result size */
  #define BLOCK 32
  
--- 94,109 
  /* for one big request, reseed after this many bytes */
  #define RESEED_BYTES  (1024*1024)
  
+ /* 
+  * Skip reseed if pool 0 has less than this many
+  * bytes added since last reseed.
+  */
+ #define POOL0_FILL(256/8)
  
  /*
   * Algorithm constants
   */
  
  /* Both cipher key size and hash result size */
  #define BLOCK 32
  
*** struct fortuna_state {
*** 118,126 
uint8   key[BLOCK];
MD_CTX  pool[NUM_POOLS];
CIPH_CTXciph;
-   unsignedsource_pos[MAX_SOURCES];
unsignedreseed_count;
struct timeval  last_reseed_time;
  };
  typedef struct fortuna_state FState;
  
--- 120,130 
uint8   key[BLOCK];
MD_CTX  pool[NUM_POOLS];
CIPH_CTXciph;
unsignedreseed_count;
struct timeval  last_reseed_time;
+   unsignedpool0_bytes;
+   unsignedrnd_pos;
+   int counter_init;
  };
  typedef struct fortuna_state FState;
  
*** static void md_result(MD_CTX *ctx, uint8
*** 161,167 
memset(tmp, 0, sizeof(tmp));
  }
  
- 
  /*
   * initialize state
   */
--- 165,170 
*** static void init_state(FState *st)
*** 174,179 
--- 177,208 
  }
  
  /*
+  * Endianess does not matter.
+  * It just needs to change without repeating.
+  */
+ static void inc_counter(FState *st)
+ {
+   uint32 *val = (uint32*)st-counter;
+   if (++val[0])
+   return;
+   if (++val[1])
+   return;
+   if (++val[2])
+   return;
+   ++val[3];
+ }
+ 
+ /*
+  * This is called 'cipher in counter mode'.
+  */
+ static void encrypt_counter(FState *st, uint8 *dst)
+ {
+   ciph_encrypt(st-ciph, st-counter, dst);
+   inc_counter(st);
+ }
+ 
+ 
+ /*
   * The time between reseed must be at least RESEED_INTERVAL
   * microseconds.
   */
*** static void reseed(FState *st)
*** 207,215 
MD_CTX key_md;
uint8 buf[BLOCK];
  
!   /* check frequency */
!   if (too_often(st))
!   return;
  
/*
 * Both #0 and #1 reseed would use only pool 0.
--- 236,243 
MD_CTX key_md;
uint8 buf[BLOCK];
  
!   /* set pool as empty */
!   st-pool0_bytes = 0;
  
/*
 * Both #0 and #1 reseed would use only pool 0.
*** static void reseed(FState *st)
*** 244,292 
  }
  
  /*
   * update pools
   */
! static void add_entropy(FState *st, unsigned src_id, const uint8 *data, 
unsigned len)
  {
unsigned pos;
uint8 hash[BLOCK];
MD_CTX md;
  
-   /* just in case there's a bug somewhere */
-   if (src_id = MAX_SOURCES)
-   src_id = USER_ENTROPY;
- 
/* hash given data */
md_init(md);
md_update(md, data, len);
md_result(md, hash);
  
!   /* update pools round-robin manner */
!   pos = st-source_pos[src_id];
md_update( st-pool[pos], hash, BLOCK);
  
!   if (++pos = NUM_POOLS)
!   pos = 0;
!   st-source_pos[src_id] = pos;
  
memset(hash, 0, BLOCK);