Re: [patch 2/2] Fortuna PRNG - Mailing list pgsql-patches
From | Bruce Momjian |
---|---|
Subject | Re: [patch 2/2] Fortuna PRNG |
Date | |
Msg-id | 200507100355.j6A3tah14415@candle.pha.pa.us Whole thread Raw |
In response to | [patch 2/2] Fortuna PRNG (Marko Kreen <marko@l-t.ee>) |
List | pgsql-patches |
Patch applied. Thanks. --------------------------------------------------------------------------- Marko Kreen wrote: > - Add Fortuna PRNG to pgcrypto. > - Move openssl random provider to openssl.c and builtin provider > to internal.c > - Make px_random_bytes use Fortuna, instead of giving error. > - Retarget random.c to aquiring system randomness, for initial seeding > of Fortuna. There is ATM 2 functions for Windows, > reader from /dev/urandom and the regular time()/getpid() silliness. > > > Index: pgsql/contrib/pgcrypto/fortuna.c > =================================================================== > *** /dev/null > --- pgsql/contrib/pgcrypto/fortuna.c > *************** > *** 0 **** > --- 1,365 ---- > + /* > + * fortuna.c > + * Fortuna-like PRNG. > + * > + * Copyright (c) 2005 Marko Kreen > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + * > + * $PostgreSQL$ > + */ > + > + #include <postgres.h> > + #include <sys/time.h> > + #include <time.h> > + > + #include "rijndael.h" > + #include "sha2.h" > + > + #include "fortuna.h" > + > + > + /* > + * Why Fortuna-like: There does not seem to be any definitive reference > + * on Fortuna in the net. Instead this implementation is based on > + * following references: > + * > + * http://en.wikipedia.org/wiki/Fortuna_(PRNG) > + * - Wikipedia article > + * http://jlcooke.ca/random/ > + * - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux. > + */ > + > + /* > + * There is some confusion about whether and how to carry forward > + * the state of the pools. Seems like original Fortuna does not > + * do it, resetting hash after each request. I guess expecting > + * feeding to happen more often that requesting. This is absolutely > + * unsuitable for pgcrypto, as nothing asynchronous happens here. > + * > + * J.L. Cooke fixed this by feeding previous hash to new re-initialized > + * hash context. > + * > + * Fortuna predecessor Yarrow requires ability to query intermediate > + * 'final result' from hash, without affecting it. > + * > + * This implementation uses the Yarrow method - asking intermediate > + * results, but continuing with old state. > + */ > + > + > + /* > + * Algorithm parameters > + */ > + > + /* > + * How many pools. > + * > + * Original Fortuna uses 32 pools, that means 32'th pool is > + * used not earlier than in 13th year. This is a waste in > + * pgcrypto, as we have very low-frequancy seeding. Here > + * is preferable to have all entropy usable in reasonable time. > + * > + * With 23 pools, 23th pool is used after 9 days which seems > + * more sane. > + * > + * In our case the minimal cycle time would be bit longer > + * than the system-randomness feeding frequency. > + */ > + #define NUM_POOLS 23 > + > + /* in microseconds */ > + #define RESEED_INTERVAL 100000 /* 0.1 sec */ > + > + /* 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 > + > + /* cipher block size */ > + #define CIPH_BLOCK 16 > + > + /* for internal wrappers */ > + #define MD_CTX SHA256_CTX > + #define CIPH_CTX rijndael_ctx > + > + struct fortuna_state { > + uint8 counter[CIPH_BLOCK]; > + uint8 result[CIPH_BLOCK]; > + uint8 key[BLOCK]; > + MD_CTX pool[NUM_POOLS]; > + CIPH_CTX ciph; > + unsigned source_pos[MAX_SOURCES]; > + unsigned reseed_count; > + struct timeval last_reseed_time; > + }; > + typedef struct fortuna_state FState; > + > + > + /* > + * Use our own wrappers here. > + * - Need to get intermediate result from digest, without affecting it. > + * - Need re-set key on a cipher context. > + * - Algorithms are guaranteed to exist. > + * - No memory allocations. > + */ > + > + static void ciph_init(CIPH_CTX *ctx, const uint8 *key, int klen) > + { > + rijndael_set_key(ctx, (const uint32 *)key, klen, 1); > + } > + > + static void ciph_encrypt(CIPH_CTX *ctx, const uint8 *in, uint8 *out) > + { > + rijndael_encrypt(ctx, (const uint32 *)in, (uint32 *)out); > + } > + > + static void md_init(MD_CTX *ctx) > + { > + SHA256_Init(ctx); > + } > + > + static void md_update(MD_CTX *ctx, const uint8 *data, int len) > + { > + SHA256_Update(ctx, data, len); > + } > + > + static void md_result(MD_CTX *ctx, uint8 *dst) > + { > + SHA256_CTX tmp; > + memcpy(&tmp, ctx, sizeof(*ctx)); > + SHA256_Final(dst, &tmp); > + memset(&tmp, 0, sizeof(tmp)); > + } > + > + > + /* > + * initialize state > + */ > + static void init_state(FState *st) > + { > + int i; > + memset(st, 0, sizeof(*st)); > + for (i = 0; i < NUM_POOLS; i++) > + md_init(&st->pool[i]); > + } > + > + /* > + * Must not reseed more ofter than RESEED_PER_SEC > + * times per second. > + */ > + static int too_often(FState *st) > + { > + int ok; > + struct timeval tv; > + struct timeval *last = &st->last_reseed_time; > + > + gettimeofday(&tv, NULL); > + > + ok = 0; > + if (tv.tv_sec != last->tv_sec) > + ok = 1; > + else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL) > + ok = 1; > + > + memcpy(last, &tv, sizeof(tv)); > + memset(&tv, 0, sizeof(tv)); > + > + return ok; > + } > + > + /* > + * generate new key from all the pools > + */ > + static void reseed(FState *st) > + { > + unsigned k; > + unsigned n; > + MD_CTX key_md; > + uint8 buf[BLOCK]; > + > + /* check frequency */ > + if (too_often(st)) > + return; > + > + /* > + * Both #0 and #1 reseed would use only pool 0. > + * Just skip #0 then. > + */ > + n = ++st->reseed_count; > + > + /* > + * The goal: use k-th pool only 1/(2^k) of the time. > + */ > + md_init(&key_md); > + for (k = 0; k < NUM_POOLS; k++) { > + md_result(&st->pool[k], buf); > + md_update(&key_md, buf, BLOCK); > + > + if (n & 1 || !n) > + break; > + n >>= 1; > + } > + > + /* add old key into mix too */ > + md_update(&key_md, st->key, BLOCK); > + > + /* now we have new key */ > + md_result(&key_md, st->key); > + > + /* use new key */ > + ciph_init(&st->ciph, st->key, BLOCK); > + > + memset(&key_md, 0, sizeof(key_md)); > + memset(buf, 0, BLOCK); > + n = k = 0; > + } > + > + /* > + * 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); > + memset(&md, 0, sizeof(md)); > + } > + > + /* > + * 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]; > + } > + > + 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); > + > + /* > + * If the reseed didn't happen, don't use the old data > + * rather encrypt again. > + */ > + > + 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); > + > + /* copy result */ > + if (count > CIPH_BLOCK) > + n = CIPH_BLOCK; > + else > + n = count; > + memcpy(dst, st->result, n); > + dst += n; > + count -= n; > + } > + } > + > + /* > + * public interface > + */ > + > + static FState main_state; > + static int init_done = 0; > + > + void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len) > + { > + if (!init_done) > + { > + init_state(&main_state); > + init_done = 1; > + } > + if (!data || !len) > + return; > + add_entropy(&main_state, src_id, data, len); > + } > + > + void fortuna_get_bytes(unsigned len, uint8 *dst) > + { > + if (!init_done) > + { > + init_state(&main_state); > + init_done = 1; > + } > + if (!dst || !len) > + return; > + extract_data(&main_state, len, dst); > + } > + > Index: pgsql/contrib/pgcrypto/fortuna.h > =================================================================== > *** /dev/null > --- pgsql/contrib/pgcrypto/fortuna.h > *************** > *** 0 **** > --- 1,45 ---- > + /* > + * fortuna.c > + * Fortuna PRNG. > + * > + * Copyright (c) 2005 Marko Kreen > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + * > + * $PostgreSQL$ > + */ > + > + #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); > + > + #endif > + > Index: pgsql/contrib/pgcrypto/Makefile > =================================================================== > *** pgsql.orig/contrib/pgcrypto/Makefile > --- pgsql/contrib/pgcrypto/Makefile > *************** > *** 2,25 **** > # $PostgreSQL: pgsql/contrib/pgcrypto/Makefile,v 1.16 2005/07/05 23:18:44 tgl Exp $ > # > > ! # if you don't have OpenSSL, you can use libc random() or /dev/urandom > ! INT_CFLAGS = -DRAND_SILLY > ! #INT_CFLAGS = -DRAND_DEV=\"/dev/urandom\" > ! > ! INT_SRCS = md5.c sha1.c sha2.c internal.c blf.c rijndael.c > INT_TESTS = sha2 > > - OSSL_CFLAGS = -DRAND_OPENSSL > OSSL_SRCS = openssl.c > OSSL_TESTS = des 3des cast5 > > CF_SRCS = $(if $(subst no,,$(with_openssl)), $(OSSL_SRCS), $(INT_SRCS)) > CF_TESTS = $(if $(subst no,,$(with_openssl)), $(OSSL_TESTS), $(INT_TESTS)) > ! CF_CFLAGS = $(if $(subst no,,$(with_openssl)), $(OSSL_CFLAGS), $(INT_CFLAGS)) > > PG_CPPFLAGS = $(CF_CFLAGS) > > ! SRCS = pgcrypto.c px.c px-hmac.c px-crypt.c misc.c random.c \ > crypt-gensalt.c crypt-blowfish.c crypt-des.c \ > crypt-md5.c $(CF_SRCS) > > --- 2,21 ---- > # $PostgreSQL: pgsql/contrib/pgcrypto/Makefile,v 1.16 2005/07/05 23:18:44 tgl Exp $ > # > > ! INT_SRCS = md5.c sha1.c sha2.c internal.c blf.c rijndael.c \ > ! fortuna.c random.c > INT_TESTS = sha2 > > OSSL_SRCS = openssl.c > OSSL_TESTS = des 3des cast5 > > CF_SRCS = $(if $(subst no,,$(with_openssl)), $(OSSL_SRCS), $(INT_SRCS)) > CF_TESTS = $(if $(subst no,,$(with_openssl)), $(OSSL_TESTS), $(INT_TESTS)) > ! CF_CFLAGS = > > PG_CPPFLAGS = $(CF_CFLAGS) > > ! SRCS = pgcrypto.c px.c px-hmac.c px-crypt.c misc.c \ > crypt-gensalt.c crypt-blowfish.c crypt-des.c \ > crypt-md5.c $(CF_SRCS) > > Index: pgsql/contrib/pgcrypto/internal.c > =================================================================== > *** pgsql.orig/contrib/pgcrypto/internal.c > --- pgsql/contrib/pgcrypto/internal.c > *************** > *** 31,36 **** > --- 31,37 ---- > > > #include <postgres.h> > + #include <time.h> > > #include "px.h" > > *************** > *** 39,44 **** > --- 40,52 ---- > #include "sha2.h" > #include "blf.h" > #include "rijndael.h" > + #include "fortuna.h" > + > + /* > + * How often to try to acquire system entropy. (In seconds) > + */ > + #define SYSTEM_RESEED_FREQ (3*60*60) > + > > #ifndef MD5_DIGEST_LENGTH > #define MD5_DIGEST_LENGTH 16 > *************** px_find_cipher(const char *name, PX_Ciph > *** 784,786 **** > --- 792,849 ---- > *res = c; > return 0; > } > + > + /* > + * Randomness provider > + */ > + > + /* > + * Use libc for all 'public' bytes. > + * > + * That way we don't expose bytes from Fortuna > + * to the public, in case it has some bugs. > + */ > + int > + px_get_pseudo_random_bytes(uint8 *dst, unsigned count) > + { > + int i; > + > + for (i = 0; i < count; i++) > + *dst++ = random(); > + return i; > + } > + > + static time_t seed_time = 0; > + static void system_reseed() > + { > + uint8 buf[1024]; > + int n; > + time_t t; > + > + t = time(NULL); > + if (seed_time && (t - seed_time) < SYSTEM_RESEED_FREQ) > + return; > + > + n = px_acquire_system_randomness(buf); > + if (n > 0) > + fortuna_add_entropy(SYSTEM_ENTROPY, buf, n); > + > + seed_time = t; > + } > + > + int > + px_get_random_bytes(uint8 *dst, unsigned count) > + { > + system_reseed(); > + fortuna_get_bytes(count, dst); > + return 0; > + } > + > + int > + px_add_entropy(const uint8 *data, unsigned count) > + { > + system_reseed(); > + fortuna_add_entropy(USER_ENTROPY, data, count); > + return 0; > + } > + > Index: pgsql/contrib/pgcrypto/openssl.c > =================================================================== > *** pgsql.orig/contrib/pgcrypto/openssl.c > --- pgsql/contrib/pgcrypto/openssl.c > *************** > *** 37,42 **** > --- 37,45 ---- > #include <openssl/blowfish.h> > #include <openssl/cast.h> > #include <openssl/des.h> > + #include <openssl/rand.h> > + #include <openssl/err.h> > + > > /* > * Does OpenSSL support AES? > *************** px_find_cipher(const char *name, PX_Ciph > *** 759,761 **** > --- 762,819 ---- > *res = c; > return 0; > } > + > + > + static int openssl_random_init = 0; > + > + /* > + * OpenSSL random should re-feeded occasionally. From /dev/urandom > + * preferably. > + */ > + static void init_openssl_rand() > + { > + if (RAND_get_rand_method() == NULL) > + RAND_set_rand_method(RAND_SSLeay()); > + openssl_random_init = 1; > + } > + > + int > + px_get_random_bytes(uint8 *dst, unsigned count) > + { > + int res; > + > + if (!openssl_random_init) > + init_openssl_rand(); > + > + res = RAND_bytes(dst, count); > + if (res == 1) > + return count; > + > + return PXE_OSSL_RAND_ERROR; > + } > + > + int > + px_get_pseudo_random_bytes(uint8 *dst, unsigned count) > + { > + int res; > + > + if (!openssl_random_init) > + init_openssl_rand(); > + > + res = RAND_pseudo_bytes(dst, count); > + if (res == 0 || res == 1) > + return count; > + > + return PXE_OSSL_RAND_ERROR; > + } > + > + int > + px_add_entropy(const uint8 *data, unsigned count) > + { > + /* > + * estimate 0 bits > + */ > + RAND_add(data, count, 0); > + return 0; > + } > + > Index: pgsql/contrib/pgcrypto/px.h > =================================================================== > *** pgsql.orig/contrib/pgcrypto/px.h > --- pgsql/contrib/pgcrypto/px.h > *************** int px_find_combo(const char *name, PX > *** 170,175 **** > --- 170,178 ---- > > int px_get_random_bytes(uint8 *dst, unsigned count); > int px_get_pseudo_random_bytes(uint8 *dst, unsigned count); > + int px_add_entropy(const uint8 *data, unsigned count); > + > + unsigned px_acquire_system_randomness(uint8 *dst); > > const char *px_strerror(int err); > > Index: pgsql/contrib/pgcrypto/random.c > =================================================================== > *** pgsql.orig/contrib/pgcrypto/random.c > --- pgsql/contrib/pgcrypto/random.c > *************** > *** 1,6 **** > /* > * random.c > ! * Random functions. > * > * Copyright (c) 2001 Marko Kreen > * All rights reserved. > --- 1,6 ---- > /* > * random.c > ! * Acquire randomness from system. For seeding RNG. > * > * Copyright (c) 2001 Marko Kreen > * All rights reserved. > *************** > *** 34,41 **** > > #include "px.h" > > > ! #if defined(RAND_DEV) > > #include <errno.h> > #include <fcntl.h> > --- 34,53 ---- > > #include "px.h" > > + /* how many bytes to ask from system random provider */ > + #define RND_BYTES 32 > > ! /* > ! * Try to read from /dev/urandom or /dev/random on these OS'es. > ! * > ! * The list can be pretty liberal, as the device not existing > ! * is expected event. > ! */ > ! #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \ > ! || defined(__NetBSD__) || defined(__DragonFly__) \ > ! || defined(__darwin__) || defined(__SOLARIS__) > ! > ! #define TRY_DEV_RANDOM > > #include <errno.h> > #include <fcntl.h> > *************** safe_read(int fd, void *buf, size_t coun > *** 64,157 **** > return done; > } > > ! int > ! px_get_random_bytes(uint8 *dst, unsigned count) > { > int fd; > int res; > > ! fd = open(RAND_DEV, O_RDONLY); > if (fd == -1) > ! return PXE_DEV_READ_ERROR; > ! res = safe_read(fd, dst, count); > close(fd); > ! return res; > } > > ! int > ! px_get_pseudo_random_bytes(uint8 *dst, unsigned count) > ! { > ! return px_get_random_bytes(dst, count); > ! } > > ! #elif defined(RAND_SILLY) > > ! int > ! px_get_pseudo_random_bytes(uint8 *dst, unsigned count) > { > ! int i; > > ! for (i = 0; i < count; i++) > ! *dst++ = random(); > ! return i; > } > > ! int > ! px_get_random_bytes(uint8 *dst, unsigned count) > { > ! return PXE_NO_RANDOM; > ! } > > ! #elif defined(RAND_OPENSSL) > > ! #include <openssl/evp.h> > ! #include <openssl/blowfish.h> > ! #include <openssl/rand.h> > ! #include <openssl/err.h> > > - static int openssl_random_init = 0; > > /* > ! * OpenSSL random should re-feeded occasionally. From /dev/urandom > ! * preferably. > */ > ! static void init_openssl() > ! { > ! if (RAND_get_rand_method() == NULL) > ! RAND_set_rand_method(RAND_SSLeay()); > ! openssl_random_init = 1; > ! } > > ! int > ! px_get_random_bytes(uint8 *dst, unsigned count) > ! { > ! int res; > ! > ! if (!openssl_random_init) > ! init_openssl(); > ! > ! res = RAND_bytes(dst, count); > ! if (res == 1) > ! return count; > > ! return PXE_OSSL_RAND_ERROR; > ! } > > ! int > ! px_get_pseudo_random_bytes(uint8 *dst, unsigned count) > { > ! int res; > > ! if (!openssl_random_init) > ! init_openssl(); > > ! res = RAND_pseudo_bytes(dst, count); > ! if (res == 0 || res == 1) > ! return count; > > ! return PXE_OSSL_RAND_ERROR; > } > > - #else > - #error "Invalid random source" > #endif > --- 76,244 ---- > return done; > } > > ! static uint8 * > ! try_dev_random(uint8 *dst) > { > int fd; > int res; > > ! fd = open("/dev/urandom", O_RDONLY); > if (fd == -1) > ! { > ! fd = open("/dev/random", O_RDONLY); > ! if (fd == -1) > ! return dst; > ! } > ! res = safe_read(fd, dst, RND_BYTES); > close(fd); > ! if (res > 0) > ! dst += res; > ! return dst; > } > > ! #endif > ! > ! /* > ! * Try to find randomness on Windows > ! */ > ! #ifdef WIN32 > ! > ! #define TRY_WIN32_GENRAND > ! #define TRY_WIN32_PERFC > > ! #define _WIN32_WINNT 0x0400 > ! #include <windows.h> > ! #include <wincrypt.h> > > ! /* > ! * this function is from libtomcrypt > ! * > ! * try to use Microsoft crypto API > ! */ > ! static uint8 * try_win32_genrand(uint8 *dst) > { > ! int res; > ! HCRYPTPROV h = 0; > ! > ! res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL, > ! (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)); > ! if (!res) > ! res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL, > ! CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET); > ! if (!res) > ! return dst; > ! > ! res = CryptGenRandom(h, NUM_BYTES, dst); > ! if (res == TRUE) > ! dst += len; > > ! CryptReleaseContext(h, 0); > ! return dst; > } > > ! static uint8 * try_win32_perfc(uint8 *dst) > { > ! int res; > ! LARGE_INTEGER time; > > ! res = QueryPerformanceCounter(&time); > ! if (!res) > ! return dst; > > ! memcpy(dst, &time, sizeof(time)); > ! return dst + sizeof(time); > ! } > ! > ! #endif /* WIN32 */ > > > /* > ! * If we are not on Windows, then hopefully we are > ! * on a unix-like system. Use the usual suspects > ! * for randomness. > */ > ! #ifndef WIN32 > > ! #define TRY_UNIXSTD > > ! #include <sys/types.h> > ! #include <sys/time.h> > ! #include <time.h> > ! #include <unistd.h> > > ! /* > ! * Everything here is predictible, only needs some patience. > ! * > ! * But there is a chance that the system-specific functions > ! * did not work. So keep faith and try to slow the attacker down. > ! */ > ! static uint8 * > ! try_unix_std(uint8 *dst) > { > ! pid_t pid; > ! int x; > ! PX_MD *md; > ! struct timeval tv; > ! int res; > ! > ! /* process id */ > ! pid = getpid(); > ! memcpy(dst, (uint8*)&pid, sizeof(pid)); > ! dst += sizeof(pid); > ! > ! /* time */ > ! gettimeofday(&tv, NULL); > ! memcpy(dst, (uint8*)&tv, sizeof(tv)); > ! dst += sizeof(tv); > ! > ! /* pointless, but should not hurt */ > ! x = random(); > ! memcpy(dst, (uint8*)&x, sizeof(x)); > ! dst += sizeof(x); > ! > ! /* let's be desperate */ > ! res = px_find_digest("sha1", &md); > ! if (res >= 0) { > ! uint8 *ptr; > ! uint8 stack[8192]; > ! int alloc = 32*1024; > ! > ! px_md_update(md, stack, sizeof(stack)); > ! ptr = px_alloc(alloc); > ! px_md_update(md, ptr, alloc); > ! px_free(ptr); > > ! px_md_finish(md, dst); > ! px_md_free(md); > > ! dst += 20; > ! } > > ! return dst; > } > > #endif > + > + /* > + * try to extract some randomness for initial seeding > + * > + * dst should have room for 1024 bytes. > + */ > + unsigned px_acquire_system_randomness(uint8 *dst) > + { > + uint8 *p = dst; > + #ifdef TRY_DEV_RANDOM > + p = try_dev_random(p); > + #endif > + #ifdef TRY_WIN32_GENRAND > + p = try_win32_genrand(p); > + #endif > + #ifdef TRY_WIN32_PERFC > + p = try_win32_perfc(p); > + #endif > + #ifdef TRY_UNIXSTD > + p = try_unix_std(p); > + #endif > + return p - dst; > + } > + > > -- > > ---------------------------(end of broadcast)--------------------------- > TIP 5: don't forget to increase your free space map settings > -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
pgsql-patches by date: