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