* 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"
/* 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
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;
memset(&tmp, 0, sizeof(tmp));
}
-
/*
* initialize state
*/
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.
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.
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)
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)
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);
}
/*
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)
{
}
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)
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* 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
#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_add_entropy(unsigned src_id, const uint8 *data, unsigned len);
+void fortuna_add_entropy(const uint8 *data, unsigned len);
#endif
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* 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 "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
}
static time_t seed_time = 0;
+static time_t check_time = 0;
static void system_reseed(void)
{
uint8 buf[1024];
int n;
time_t t;
+ int skip = 1;
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;
n = px_acquire_system_randomness(buf);
if (n > 0)
- fortuna_add_entropy(SYSTEM_ENTROPY, buf, n);
+ fortuna_add_entropy(buf, n);
seed_time = t;
memset(buf, 0, sizeof(buf));
px_add_entropy(const uint8 *data, unsigned count)
{
system_reseed();
- fortuna_add_entropy(USER_ENTROPY, data, count);
+ fortuna_add_entropy(data, count);
return 0;
}
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* 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"
} \
} 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
* RNG state shuffled.
static void add_entropy(text *data1, text *data2, text *data3)
{
PX_MD *md;
- uint8 sha1[20];
- int res;
+ uint8 rnd[3];
if (!data1 && !data2 && !data3)
return;
- res = px_find_digest("sha1", &md);
- if (res < 0)
+ if (px_get_random_bytes(rnd, 3) < 0)
return;
- if (data1)
- px_md_update(md, VARDATA(data1), VARSIZE(data1) - VARHDRSZ);
- if (data2)
- px_md_update(md, VARDATA(data2), VARSIZE(data2) - VARHDRSZ);
- if (data3)
- px_md_update(md, VARDATA(data3), VARSIZE(data3) - VARHDRSZ);
+ if (px_find_digest("sha1", &md) < 0)
+ return;
- 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);
- memset(sha1, 0, 20);
+ /* chance: 7/8 */
+ 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)
- ereport(NOTICE, (errmsg("add_entropy: %s", px_strerror(res))));
+ /* chance: 5/8 */
+ if (data3 && rnd[2] >= 160)
+ add_block_entropy(md, data3);
+
+ px_md_free(md);
+ memset(rnd, 0, sizeof(rnd));
}
/*
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* 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"
case 0:
break;
case 1:
- res = px_get_random_bytes(s2k->salt, PGP_S2K_SALT);
+ res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT);
break;
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)
break;
- res = px_get_random_bytes(&tmp, 1);
+ res = px_get_pseudo_random_bytes(&tmp, 1);
if (res < 0)
break;
s2k->iter = decide_count(tmp);