diff --git a/contrib/pgcrypto/Makefile b/contrib/pgcrypto/Makefile index 85b066d659..82f2a8295e 100644 --- a/contrib/pgcrypto/Makefile +++ b/contrib/pgcrypto/Makefile @@ -1,25 +1,21 @@ # -# $PostgreSQL: pgsql/contrib/pgcrypto/Makefile,v 1.18 2005/07/10 03:52:56 momjian Exp $ +# $PostgreSQL: pgsql/contrib/pgcrypto/Makefile,v 1.19 2005/07/10 03:55:28 momjian 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_SRCS = md5.c sha1.c sha2.c internal.c blf.c rijndael.c \ + fortuna.c random.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)) +CF_CFLAGS = PG_CPPFLAGS = $(CF_CFLAGS) -SRCS = pgcrypto.c px.c px-hmac.c px-crypt.c misc.c random.c \ +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) diff --git a/contrib/pgcrypto/internal.c b/contrib/pgcrypto/internal.c index ff034dcf80..cee1c68762 100644 --- a/contrib/pgcrypto/internal.c +++ b/contrib/pgcrypto/internal.c @@ -26,11 +26,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $PostgreSQL: pgsql/contrib/pgcrypto/internal.c,v 1.17 2005/07/10 03:52:56 momjian Exp $ + * $PostgreSQL: pgsql/contrib/pgcrypto/internal.c,v 1.18 2005/07/10 03:55:28 momjian Exp $ */ #include +#include #include "px.h" @@ -39,6 +40,13 @@ #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 @@ -784,3 +792,58 @@ px_find_cipher(const char *name, PX_Cipher ** res) *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; +} + diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c index 790f247988..cb9604b4a3 100644 --- a/contrib/pgcrypto/openssl.c +++ b/contrib/pgcrypto/openssl.c @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $PostgreSQL: pgsql/contrib/pgcrypto/openssl.c,v 1.20 2005/07/05 18:15:36 tgl Exp $ + * $PostgreSQL: pgsql/contrib/pgcrypto/openssl.c,v 1.21 2005/07/10 03:55:28 momjian Exp $ */ #include @@ -37,6 +37,9 @@ #include #include #include +#include +#include + /* * Does OpenSSL support AES? @@ -759,3 +762,58 @@ px_find_cipher(const char *name, PX_Cipher ** res) *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; +} + diff --git a/contrib/pgcrypto/px.h b/contrib/pgcrypto/px.h index d7a15ffaa3..b584ae6b34 100644 --- a/contrib/pgcrypto/px.h +++ b/contrib/pgcrypto/px.h @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $PostgreSQL: pgsql/contrib/pgcrypto/px.h,v 1.12 2005/03/21 05:22:14 neilc Exp $ + * $PostgreSQL: pgsql/contrib/pgcrypto/px.h,v 1.13 2005/07/10 03:55:28 momjian Exp $ */ #ifndef __PX_H @@ -170,6 +170,9 @@ int px_find_combo(const char *name, PX_Combo ** res); 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); diff --git a/contrib/pgcrypto/random.c b/contrib/pgcrypto/random.c index 7f2f5f4925..0aa4aa2836 100644 --- a/contrib/pgcrypto/random.c +++ b/contrib/pgcrypto/random.c @@ -1,6 +1,6 @@ /* * random.c - * Random functions. + * Acquire randomness from system. For seeding RNG. * * Copyright (c) 2001 Marko Kreen * All rights reserved. @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $PostgreSQL: pgsql/contrib/pgcrypto/random.c,v 1.10 2005/03/21 05:22:14 neilc Exp $ + * $PostgreSQL: pgsql/contrib/pgcrypto/random.c,v 1.11 2005/07/10 03:55:28 momjian Exp $ */ @@ -34,8 +34,20 @@ #include "px.h" +/* how many bytes to ask from system random provider */ +#define RND_BYTES 32 -#if defined(RAND_DEV) +/* + * 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 #include @@ -64,94 +76,169 @@ safe_read(int fd, void *buf, size_t count) return done; } -int -px_get_random_bytes(uint8 *dst, unsigned count) +static uint8 * +try_dev_random(uint8 *dst) { int fd; int res; - fd = open(RAND_DEV, O_RDONLY); + fd = open("/dev/urandom", O_RDONLY); if (fd == -1) - return PXE_DEV_READ_ERROR; - res = safe_read(fd, dst, count); + { + fd = open("/dev/random", O_RDONLY); + if (fd == -1) + return dst; + } + res = safe_read(fd, dst, RND_BYTES); close(fd); - return res; + if (res > 0) + dst += res; + return dst; } -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 -#include -#include -#include - -static int openssl_random_init = 0; +#endif /* - * OpenSSL random should re-feeded occasionally. From /dev/urandom - * preferably. + * Try to find randomness on Windows */ -static void init_openssl() +#ifdef WIN32 + +#define TRY_WIN32_GENRAND +#define TRY_WIN32_PERFC + +#define _WIN32_WINNT 0x0400 +#include +#include + +/* + * this function is from libtomcrypt + * + * try to use Microsoft crypto API + */ +static uint8 * try_win32_genrand(uint8 *dst) { - if (RAND_get_rand_method() == NULL) - RAND_set_rand_method(RAND_SSLeay()); - openssl_random_init = 1; + 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; } -int -px_get_random_bytes(uint8 *dst, unsigned count) +static uint8 * try_win32_perfc(uint8 *dst) { - int res; + int res; + LARGE_INTEGER time; - if (!openssl_random_init) - init_openssl(); + res = QueryPerformanceCounter(&time); + if (!res) + return dst; - res = RAND_bytes(dst, count); - if (res == 1) - return count; - - return PXE_OSSL_RAND_ERROR; + memcpy(dst, &time, sizeof(time)); + return dst + sizeof(time); } -int -px_get_pseudo_random_bytes(uint8 *dst, unsigned count) +#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 +#include +#include +#include + +/* + * 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) { - int res; + pid_t pid; + int x; + PX_MD *md; + struct timeval tv; + int res; - if (!openssl_random_init) - init_openssl(); + /* process id */ + pid = getpid(); + memcpy(dst, (uint8*)&pid, sizeof(pid)); + dst += sizeof(pid); - res = RAND_pseudo_bytes(dst, count); - if (res == 0 || res == 1) - return count; + /* time */ + gettimeofday(&tv, NULL); + memcpy(dst, (uint8*)&tv, sizeof(tv)); + dst += sizeof(tv); - return PXE_OSSL_RAND_ERROR; + /* 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; } -#else -#error "Invalid random source" #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; +} +