- Add Fortuna PRNG to pgcrypto.
authorBruce Momjian
Sun, 10 Jul 2005 03:55:28 +0000 (03:55 +0000)
committerBruce Momjian
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 
+#include 
 
 #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 
@@ -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;
+}
+
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 
 #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);
-}
+#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 
+#include 
 
-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 
-#include 
-#include 
-#include 
+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 
+#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;
+
+   /* 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