- 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 85b066d65901010fa06467ccd50f301c938593ee..82f2a8295e68929f833ce9c25b02b05a0eedacb7 100644 (file)
@@ -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)
 
index ff034dcf8084f89fbfca5bb9e8ab048ddcb72f6e..cee1c687624049abc1d412a4905086bd509f1e02 100644 (file)
  * 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 <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 790f24798849bbe32742692d4a6ac050fbc76436..cb9604b4a32c243bb4b14df7a5f4e8add077c013 100644 (file)
@@ -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 <postgres.h>
@@ -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 d7a15ffaa35836da137bcbd1ad7c22088ea41f1c..b584ae6b3477839b46ac9b0fee1cd621a0d3b38f 100644 (file)
@@ -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);
 
index 7f2f5f49258da2088690442ac3af8e473ee9ebce..0aa4aa2836c15d631fa8310b8fb9dc31004dcf10 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.
@@ -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 $
  */
 
 
 
 #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