Commit 35c675a7 authored by Tom Lane's avatar Tom Lane

Fortuna fixes. Marko Kreen

parent 2787db9b
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * 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" #include "postgres.h"
...@@ -94,14 +94,16 @@ ...@@ -94,14 +94,16 @@
/* for one big request, reseed after this many bytes */ /* for one big request, reseed after this many bytes */
#define RESEED_BYTES (1024*1024) #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 * Algorithm constants
*/ */
/* max sources */
#define MAX_SOURCES 8
/* Both cipher key size and hash result size */ /* Both cipher key size and hash result size */
#define BLOCK 32 #define BLOCK 32
...@@ -118,9 +120,11 @@ struct fortuna_state { ...@@ -118,9 +120,11 @@ struct fortuna_state {
uint8 key[BLOCK]; uint8 key[BLOCK];
MD_CTX pool[NUM_POOLS]; MD_CTX pool[NUM_POOLS];
CIPH_CTX ciph; CIPH_CTX ciph;
unsigned source_pos[MAX_SOURCES];
unsigned reseed_count; unsigned reseed_count;
struct timeval last_reseed_time; struct timeval last_reseed_time;
unsigned pool0_bytes;
unsigned rnd_pos;
int counter_init;
}; };
typedef struct fortuna_state FState; typedef struct fortuna_state FState;
...@@ -161,7 +165,6 @@ static void md_result(MD_CTX *ctx, uint8 *dst) ...@@ -161,7 +165,6 @@ static void md_result(MD_CTX *ctx, uint8 *dst)
memset(&tmp, 0, sizeof(tmp)); memset(&tmp, 0, sizeof(tmp));
} }
/* /*
* initialize state * initialize state
*/ */
...@@ -173,6 +176,32 @@ static void init_state(FState *st) ...@@ -173,6 +176,32 @@ static void init_state(FState *st)
md_init(&st->pool[i]); 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 * The time between reseed must be at least RESEED_INTERVAL
* microseconds. * microseconds.
...@@ -207,9 +236,8 @@ static void reseed(FState *st) ...@@ -207,9 +236,8 @@ static void reseed(FState *st)
MD_CTX key_md; MD_CTX key_md;
uint8 buf[BLOCK]; uint8 buf[BLOCK];
/* check frequency */ /* set pool as empty */
if (too_often(st)) st->pool0_bytes = 0;
return;
/* /*
* Both #0 and #1 reseed would use only pool 0. * Both #0 and #1 reseed would use only pool 0.
...@@ -243,50 +271,81 @@ static void reseed(FState *st) ...@@ -243,50 +271,81 @@ static void reseed(FState *st)
memset(buf, 0, BLOCK); 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 * 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; unsigned pos;
uint8 hash[BLOCK]; uint8 hash[BLOCK];
MD_CTX md; MD_CTX md;
/* just in case there's a bug somewhere */
if (src_id >= MAX_SOURCES)
src_id = USER_ENTROPY;
/* hash given data */ /* hash given data */
md_init(&md); md_init(&md);
md_update(&md, data, len); md_update(&md, data, len);
md_result(&md, hash); 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); md_update( &st->pool[pos], hash, BLOCK);
if (++pos >= NUM_POOLS) if (pos == 0)
pos = 0; st->pool0_bytes += len;
st->source_pos[src_id] = pos;
memset(hash, 0, BLOCK); memset(hash, 0, BLOCK);
memset(&md, 0, sizeof(md)); memset(&md, 0, sizeof(md));
} }
/* /*
* Endianess does not matter. * Just take 2 next blocks as new key
* It just needs to change without repeating.
*/ */
static void inc_counter(FState *st) static void rekey(FState *st)
{ {
uint32 *val = (uint32*)st->counter; encrypt_counter(st, st->key);
if (++val[0]) encrypt_counter(st, st->key + CIPH_BLOCK);
return; ciph_init(&st->ciph, st->key, BLOCK);
if (++val[1]) }
return;
if (++val[2]) /*
return; * Fortuna relies on AES standing known-plaintext attack.
++val[3]; * 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) static void extract_data(FState *st, unsigned count, uint8 *dst)
...@@ -294,31 +353,17 @@ 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 n;
unsigned block_nr = 0; unsigned block_nr = 0;
/* /* Can we reseed? */
* Every request should be with different key, if (st->pool0_bytes >= POOL0_FILL && !too_often(st))
* if possible. reseed(st);
*/
reseed(st);
/* /* Is counter initialized? */
* If the reseed didn't happen, don't use the old data if (!st->counter_init)
* rather encrypt again. init_counter(st);
*/
while (count > 0) { 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 */ /* produce bytes */
ciph_encrypt(&st->ciph, st->counter, st->result); encrypt_counter(st, st->result);
block_nr++;
/* prepare for next time */
inc_counter(st);
/* copy result */ /* copy result */
if (count > CIPH_BLOCK) if (count > CIPH_BLOCK)
...@@ -328,7 +373,17 @@ static void extract_data(FState *st, unsigned count, uint8 *dst) ...@@ -328,7 +373,17 @@ static void extract_data(FState *st, unsigned count, uint8 *dst)
memcpy(dst, st->result, n); memcpy(dst, st->result, n);
dst += n; dst += n;
count -= 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) ...@@ -338,7 +393,7 @@ static void extract_data(FState *st, unsigned count, uint8 *dst)
static FState main_state; static FState main_state;
static int init_done = 0; 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) if (!init_done)
{ {
...@@ -347,7 +402,7 @@ void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len) ...@@ -347,7 +402,7 @@ void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len)
} }
if (!data || !len) if (!data || !len)
return; return;
add_entropy(&main_state, src_id, data, len); add_entropy(&main_state, data, len);
} }
void fortuna_get_bytes(unsigned len, uint8 *dst) void fortuna_get_bytes(unsigned len, uint8 *dst)
......
...@@ -26,20 +26,14 @@ ...@@ -26,20 +26,14 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.h,v 1.1 2005/07/10 13:46:28 momjian Exp $ * $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.h,v 1.2 2005/07/18 17:12:54 tgl Exp $
*/ */
#ifndef __FORTUNA_H #ifndef __FORTUNA_H
#define __FORTUNA_H #define __FORTUNA_H
/*
* Event source ID's
*/
#define SYSTEM_ENTROPY 0
#define USER_ENTROPY 1
void fortuna_get_bytes(unsigned len, uint8 *dst); void fortuna_get_bytes(unsigned len, uint8 *dst);
void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len); void fortuna_add_entropy(const uint8 *data, unsigned len);
#endif #endif
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* $PostgreSQL: pgsql/contrib/pgcrypto/internal.c,v 1.21 2005/07/18 17:09:01 tgl Exp $ * $PostgreSQL: pgsql/contrib/pgcrypto/internal.c,v 1.22 2005/07/18 17:12:54 tgl Exp $
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -42,9 +42,22 @@ ...@@ -42,9 +42,22 @@
#include "fortuna.h" #include "fortuna.h"
/* /*
* How often to try to acquire system entropy. (In seconds) * System reseeds should be separated at least this much.
*/ */
#define SYSTEM_RESEED_FREQ (3*60*60) #define SYSTEM_RESEED_MIN (20*60) /* 20 min */
/*
* How often to roll dice.
*/
#define SYSTEM_RESEED_CHECK_TIME (10*60) /* 10 min */
/*
* The chance is x/256 that the reseed happens.
*/
#define SYSTEM_RESEED_CHANCE (4) /* 256/4 * 10min ~ 10h */
/*
* If this much time has passed, force reseed.
*/
#define SYSTEM_RESEED_MAX (12*60*60) /* 12h */
#ifndef MD5_DIGEST_LENGTH #ifndef MD5_DIGEST_LENGTH
...@@ -823,20 +836,40 @@ px_get_pseudo_random_bytes(uint8 *dst, unsigned count) ...@@ -823,20 +836,40 @@ px_get_pseudo_random_bytes(uint8 *dst, unsigned count)
} }
static time_t seed_time = 0; static time_t seed_time = 0;
static time_t check_time = 0;
static void system_reseed(void) static void system_reseed(void)
{ {
uint8 buf[1024]; uint8 buf[1024];
int n; int n;
time_t t; time_t t;
int skip = 1;
t = time(NULL); t = time(NULL);
if (seed_time && (t - seed_time) < SYSTEM_RESEED_FREQ)
if (seed_time == 0)
skip = 0;
else if ((t - seed_time) < SYSTEM_RESEED_MIN)
skip = 1;
else if ((t - seed_time) > SYSTEM_RESEED_MAX)
skip = 0;
else if (!check_time || (t - check_time) > SYSTEM_RESEED_CHECK_TIME)
{
check_time = t;
/* roll dice */
px_get_random_bytes(buf, 1);
skip = buf[0] >= SYSTEM_RESEED_CHANCE;
}
/* clear 1 byte */
memset(buf, 0, sizeof(buf));
if (skip)
return; return;
n = px_acquire_system_randomness(buf); n = px_acquire_system_randomness(buf);
if (n > 0) if (n > 0)
fortuna_add_entropy(SYSTEM_ENTROPY, buf, n); fortuna_add_entropy(buf, n);
seed_time = t; seed_time = t;
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
...@@ -854,7 +887,7 @@ int ...@@ -854,7 +887,7 @@ int
px_add_entropy(const uint8 *data, unsigned count) px_add_entropy(const uint8 *data, unsigned count)
{ {
system_reseed(); system_reseed();
fortuna_add_entropy(USER_ENTROPY, data, count); fortuna_add_entropy(data, count);
return 0; return 0;
} }
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* $PostgreSQL: pgsql/contrib/pgcrypto/pgp-pgsql.c,v 1.2 2005/07/11 15:07:59 tgl Exp $ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-pgsql.c,v 1.3 2005/07/18 17:12:54 tgl Exp $
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -86,6 +86,22 @@ PG_FUNCTION_INFO_V1(pg_dearmor); ...@@ -86,6 +86,22 @@ PG_FUNCTION_INFO_V1(pg_dearmor);
} \ } \
} while (0) } while (0)
/*
* Mix a block of data into RNG.
*/
static void add_block_entropy(PX_MD *md, text *data)
{
uint8 sha1[20];
px_md_reset(md);
px_md_update(md, VARDATA(data), VARSIZE(data) - VARHDRSZ);
px_md_finish(md, sha1);
px_add_entropy(sha1, 20);
memset(sha1, 0, 20);
}
/* /*
* Mix user data into RNG. It is for user own interests to have * Mix user data into RNG. It is for user own interests to have
* RNG state shuffled. * RNG state shuffled.
...@@ -93,31 +109,38 @@ PG_FUNCTION_INFO_V1(pg_dearmor); ...@@ -93,31 +109,38 @@ PG_FUNCTION_INFO_V1(pg_dearmor);
static void add_entropy(text *data1, text *data2, text *data3) static void add_entropy(text *data1, text *data2, text *data3)
{ {
PX_MD *md; PX_MD *md;
uint8 sha1[20]; uint8 rnd[3];
int res;
if (!data1 && !data2 && !data3) if (!data1 && !data2 && !data3)
return; return;
res = px_find_digest("sha1", &md); if (px_get_random_bytes(rnd, 3) < 0)
if (res < 0)
return; return;
if (data1) if (px_find_digest("sha1", &md) < 0)
px_md_update(md, VARDATA(data1), VARSIZE(data1) - VARHDRSZ); return;
if (data2)
px_md_update(md, VARDATA(data2), VARSIZE(data2) - VARHDRSZ);
if (data3)
px_md_update(md, VARDATA(data3), VARSIZE(data3) - VARHDRSZ);
px_md_finish(md, sha1); /*
px_md_free(md); * Try to make the feeding unpredictable.
*
* Prefer data over keys, as it's rather likely
* that key is same in several calls.
*/
res = px_add_entropy(sha1, 20); /* chance: 7/8 */
memset(sha1, 0, 20); if (data1 && rnd[0] >= 32)
add_block_entropy(md, data1);
/* chance: 5/8 */
if (data2 && rnd[1] >= 160)
add_block_entropy(md, data2);
if (res < 0) /* chance: 5/8 */
ereport(NOTICE, (errmsg("add_entropy: %s", px_strerror(res)))); if (data3 && rnd[2] >= 160)
add_block_entropy(md, data3);
px_md_free(md);
memset(rnd, 0, sizeof(rnd));
} }
/* /*
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* $PostgreSQL: pgsql/contrib/pgcrypto/pgp-s2k.c,v 1.2 2005/07/11 15:07:59 tgl Exp $ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-s2k.c,v 1.3 2005/07/18 17:12:54 tgl Exp $
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -225,13 +225,13 @@ pgp_s2k_fill(PGP_S2K *s2k, int mode,int digest_algo) ...@@ -225,13 +225,13 @@ pgp_s2k_fill(PGP_S2K *s2k, int mode,int digest_algo)
case 0: case 0:
break; break;
case 1: case 1:
res = px_get_random_bytes(s2k->salt, PGP_S2K_SALT); res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT);
break; break;
case 3: case 3:
res = px_get_random_bytes(s2k->salt, PGP_S2K_SALT); res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT);
if (res < 0) if (res < 0)
break; break;
res = px_get_random_bytes(&tmp, 1); res = px_get_pseudo_random_bytes(&tmp, 1);
if (res < 0) if (res < 0)
break; break;
s2k->iter = decide_count(tmp); s2k->iter = decide_count(tmp);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment