1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Fortuna fixes. Marko Kreen

This commit is contained in:
Tom Lane
2005-07-18 17:12:54 +00:00
parent 2787db9b1d
commit 35c675a7fd
5 changed files with 194 additions and 89 deletions

View File

@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.c,v 1.3 2005/07/18 17:09:01 tgl Exp $
* $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.c,v 1.4 2005/07/18 17:12:54 tgl Exp $
*/
#include "postgres.h"
@ -94,14 +94,16 @@
/* 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
*/
/* max sources */
#define MAX_SOURCES 8
/* Both cipher key size and hash result size */
#define BLOCK 32
@ -118,9 +120,11 @@ struct fortuna_state {
uint8 key[BLOCK];
MD_CTX pool[NUM_POOLS];
CIPH_CTX ciph;
unsigned source_pos[MAX_SOURCES];
unsigned reseed_count;
struct timeval last_reseed_time;
unsigned pool0_bytes;
unsigned rnd_pos;
int counter_init;
};
typedef struct fortuna_state FState;
@ -161,7 +165,6 @@ static void md_result(MD_CTX *ctx, uint8 *dst)
memset(&tmp, 0, sizeof(tmp));
}
/*
* initialize state
*/
@ -173,6 +176,32 @@ static void init_state(FState *st)
md_init(&st->pool[i]);
}
/*
* 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.
@ -207,9 +236,8 @@ static void reseed(FState *st)
MD_CTX key_md;
uint8 buf[BLOCK];
/* check frequency */
if (too_often(st))
return;
/* set pool as empty */
st->pool0_bytes = 0;
/*
* Both #0 and #1 reseed would use only pool 0.
@ -243,50 +271,81 @@ static void reseed(FState *st)
memset(buf, 0, BLOCK);
}
/*
* Pick a random pool. This uses key bytes as random source.
*/
static unsigned get_rand_pool(FState *st)
{
unsigned rnd;
/*
* This slightly prefers lower pools - thats OK.
*/
rnd = st->key[st->rnd_pos] % NUM_POOLS;
st->rnd_pos++;
if (st->rnd_pos >= BLOCK)
st->rnd_pos = 0;
return rnd;
}
/*
* update pools
*/
static void add_entropy(FState *st, unsigned src_id, const uint8 *data, unsigned len)
static void add_entropy(FState *st, 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];
/*
* Make sure the pool 0 is initialized,
* then update randomly.
*/
if (st->reseed_count == 0 && st->pool0_bytes < POOL0_FILL)
pos = 0;
else
pos = get_rand_pool(st);
md_update( &st->pool[pos], hash, BLOCK);
if (++pos >= NUM_POOLS)
pos = 0;
st->source_pos[src_id] = pos;
if (pos == 0)
st->pool0_bytes += len;
memset(hash, 0, BLOCK);
memset(&md, 0, sizeof(md));
}
/*
* Endianess does not matter.
* It just needs to change without repeating.
* Just take 2 next blocks as new key
*/
static void inc_counter(FState *st)
static void rekey(FState *st)
{
uint32 *val = (uint32*)st->counter;
if (++val[0])
return;
if (++val[1])
return;
if (++val[2])
return;
++val[3];
encrypt_counter(st, st->key);
encrypt_counter(st, st->key + CIPH_BLOCK);
ciph_init(&st->ciph, st->key, BLOCK);
}
/*
* Fortuna relies on AES standing known-plaintext attack.
* In case it does not, slow down the attacker by initialising
* the couter to random value.
*/
static void init_counter(FState *st)
{
/* Use next block as counter. */
encrypt_counter(st, st->counter);
/* Hide the key. */
rekey(st);
/* The counter can be shuffled only once. */
st->counter_init = 1;
}
static void extract_data(FState *st, unsigned count, uint8 *dst)
@ -294,31 +353,17 @@ static void extract_data(FState *st, unsigned count, uint8 *dst)
unsigned n;
unsigned block_nr = 0;
/*
* Every request should be with different key,
* if possible.
*/
reseed(st);
/* Can we reseed? */
if (st->pool0_bytes >= POOL0_FILL && !too_often(st))
reseed(st);
/*
* If the reseed didn't happen, don't use the old data
* rather encrypt again.
*/
/* Is counter initialized? */
if (!st->counter_init)
init_counter(st);
while (count > 0) {
/* must not give out too many bytes with one key */
if (block_nr > (RESEED_BYTES / CIPH_BLOCK))
{
reseed(st);
block_nr = 0;
}
/* produce bytes */
ciph_encrypt(&st->ciph, st->counter, st->result);
block_nr++;
/* prepare for next time */
inc_counter(st);
encrypt_counter(st, st->result);
/* copy result */
if (count > CIPH_BLOCK)
@ -328,7 +373,17 @@ static void extract_data(FState *st, unsigned count, uint8 *dst)
memcpy(dst, st->result, n);
dst += n;
count -= n;
/* must not give out too many bytes with one key */
block_nr++;
if (block_nr > (RESEED_BYTES / CIPH_BLOCK))
{
rekey(st);
block_nr = 0;
}
}
/* Set new key for next request. */
rekey(st);
}
/*
@ -338,7 +393,7 @@ static void extract_data(FState *st, unsigned count, uint8 *dst)
static FState main_state;
static int init_done = 0;
void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len)
void fortuna_add_entropy(const uint8 *data, unsigned len)
{
if (!init_done)
{
@ -347,7 +402,7 @@ void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len)
}
if (!data || !len)
return;
add_entropy(&main_state, src_id, data, len);
add_entropy(&main_state, data, len);
}
void fortuna_get_bytes(unsigned len, uint8 *dst)