- Add Fortuna PRNG to pgcrypto.
authorBruce Momjian <bruce@momjian.us>
Sun, 10 Jul 2005 03:55:28 +0000 (03:55 +0000)
committerBruce Momjian <bruce@momjian.us>
Sun, 10 Jul 2005 03:55:28 +0000 (03:55 +0000)
- 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.

Marko Kreen

contrib/pgcrypto/Makefile
contrib/pgcrypto/internal.c
contrib/pgcrypto/openssl.c
contrib/pgcrypto/px.h
contrib/pgcrypto/random.c

index efdf03f02f6fbd0d8a32609cf594d2a8afff0db5..55a4b5d87662d8a08b154edc8be04c5f7ae6788d 100644 (file)
@@ -2,24 +2,20 @@
 # $PostgreSQL$
 #
 
-# 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)
 
index 8f538a61d2c4d37d49cb6d6677a8df9501fe5c07..dbc56bb5e2552b45fc3001df2d78ca2d0201c402 100644 (file)
@@ -31,6 +31,7 @@
 
 
 #include <postgres.h>
+#include <time.h>
 
 #include "px.h"
 
 #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;
+}
+
index 1f7f23535f752474ca67045f5314b8a7f4fef2aa..9f335b7919e212dcb6e48330cf47b8d384758475 100644 (file)
@@ -37,6 +37,9 @@
 #include <openssl/blowfish.h>
 #include <openssl/cast.h>
 #include <openssl/des.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+
 
 /*
  * 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;
+}
+
index d1625487fb2032c34eaca82ef72ea2b61fc12a41..88f0409e59c3c8909cddb1453dc0452b09974d5d 100644 (file)
@@ -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);
 
index dcf54fb148c7f040589d4858067a8a9fc22e6d81..bdc39965a54c529a869c183c12018001c3822175 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * random.c
- *             Random functions.
+ *             Acquire randomness from system.  For seeding RNG.
  *
  * Copyright (c) 2001 Marko Kreen
  * All rights reserved.
 
 #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 <errno.h>
 #include <fcntl.h>
@@ -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);
-}
+#endif
 
-#elif defined(RAND_SILLY)
+/*
+ * Try to find randomness on Windows
+ */
+#ifdef WIN32
 
-int
-px_get_pseudo_random_bytes(uint8 *dst, unsigned count)
-{
-       int                     i;
+#define TRY_WIN32_GENRAND
+#define TRY_WIN32_PERFC
 
-       for (i = 0; i < count; i++)
-               *dst++ = random();
-       return i;
-}
+#define _WIN32_WINNT 0x0400
+#include <windows.h>
+#include <wincrypt.h>
 
-int
-px_get_random_bytes(uint8 *dst, unsigned count)
+/*
+ * this function is from libtomcrypt
+ * 
+ * try to use Microsoft crypto API
+ */
+static uint8 * try_win32_genrand(uint8 *dst)
 {
-       return PXE_NO_RANDOM;
+       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;
 }
 
-#elif defined(RAND_OPENSSL)
-
-#include <openssl/evp.h>
-#include <openssl/blowfish.h>
-#include <openssl/rand.h>
-#include <openssl/err.h>
+static uint8 * try_win32_perfc(uint8 *dst)
+{
+       int res;
+       LARGE_INTEGER time;
 
-static int     openssl_random_init = 0;
+       res = QueryPerformanceCounter(&time);
+       if (!res)
+               return dst;
 
-/*
- * 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;
+       memcpy(dst, &time, sizeof(time));
+       return dst + sizeof(time);
 }
 
-int
-px_get_random_bytes(uint8 *dst, unsigned count)
-{
-       int                     res;
+#endif /* WIN32 */
 
-       if (!openssl_random_init)
-               init_openssl();
 
-       res = RAND_bytes(dst, count);
-       if (res == 1)
-               return count;
+/*
+ * If we are not on Windows, then hopefully we are
+ * on a unix-like system.  Use the usual suspects
+ * for randomness.
+ */
+#ifndef WIN32
 
-       return PXE_OSSL_RAND_ERROR;
-}
+#define TRY_UNIXSTD
 
-int
-px_get_pseudo_random_bytes(uint8 *dst, unsigned count)
+#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)
 {
-       int                     res;
+       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;
+       }
 
-       if (!openssl_random_init)
-               init_openssl();
+       return dst;
+}
 
-       res = RAND_pseudo_bytes(dst, count);
-       if (res == 0 || res == 1)
-               return count;
+#endif
 
-       return PXE_OSSL_RAND_ERROR;
+/*
+ * 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;
 }
 
-#else
-#error "Invalid random source"
-#endif