Commit ef6d85a1a6977b3c367112117ebcd01ac50aa744

Steffen Jaeckel 2018-04-14T14:33:36

don't use rand() in mp_rand() This fixes #103

diff --git a/bn_mp_rand.c b/bn_mp_rand.c
index 5e0c1b3..6330e7f 100644
--- a/bn_mp_rand.c
+++ b/bn_mp_rand.c
@@ -13,34 +13,171 @@
  * guarantee it works.
  */
 
-#if defined(MP_8BIT) || defined(MP_16BIT)
-#define MP_GEN_RANDOM_SHIFT  DIGIT_BIT
-#else
-#if MP_GEN_RANDOM_MAX == 0xffffffffu
-#define MP_GEN_RANDOM_SHIFT  32
-#elif MP_GEN_RANDOM_MAX == 32767
-/* SHRT_MAX */
-#define MP_GEN_RANDOM_SHIFT  15
-#elif MP_GEN_RANDOM_MAX == 2147483647
-/* INT_MAX */
-#define MP_GEN_RANDOM_SHIFT  31
-#elif !defined(MP_GEN_RANDOM_SHIFT)
-#error Thou shalt define their own valid MP_GEN_RANDOM_SHIFT
-#endif
-#endif
+/* First the OS-specific special cases
+ * - *BSD
+ * - Windows
+ */
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
+#define MP_ARC4RANDOM
+#define MP_GEN_RANDOM_MAX     0xffffffffu
+#define MP_GEN_RANDOM_SHIFT   32
 
-/* makes a pseudo-random int of a given size */
-static mp_digit s_gen_random(void)
+static int s_read_arc4random(mp_digit* p)
 {
    mp_digit d = 0, msk = 0;
    do {
       d <<= MP_GEN_RANDOM_SHIFT;
-      d |= ((mp_digit) MP_GEN_RANDOM());
+      d |= ((mp_digit) arc4random());
       msk <<= MP_GEN_RANDOM_SHIFT;
       msk |= (MP_MASK & MP_GEN_RANDOM_MAX);
    } while ((MP_MASK & msk) != MP_MASK);
-   d &= MP_MASK;
-   return d;
+   *p = d;
+   return MP_OKAY;
+}
+#endif
+
+#if defined(_WIN32) || defined(_WIN32_WCE)
+#define MP_WIN_CSP
+
+#ifndef _WIN32_WINNT
+   #define _WIN32_WINNT 0x0400
+#endif
+#ifdef _WIN32_WCE
+   #define UNDER_CE
+   #define ARM
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <wincrypt.h>
+
+static HCRYPTPROV hProv = 0;
+
+static void s_cleanup_win_csp(void)
+{
+   CryptReleaseContext(hProv, 0);
+   hProv = 0;
+}
+
+static int s_read_win_csp(mp_digit* p)
+{
+   int ret = -1;
+   if (hProv == 0) {
+      if (!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+                               (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) &&
+          !CryptAcquireContext (&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+                               CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET)) {
+         hProv = 0;
+         return ret;
+      }
+      atexit(s_cleanup_win_csp);
+   }
+   if (CryptGenRandom(hProv, sizeof(*p), (void*)p) == TRUE) {
+      ret = MP_OKAY;
+   }
+   return ret;
+}
+#endif /* WIN32 */
+
+#if !defined(MP_WIN_CSP) && defined(__linux__) && defined(__GLIBC_PREREQ)
+#if __GLIBC_PREREQ(2, 25)
+#define MP_GETRANDOM
+#include <sys/random.h>
+#include <errno.h>
+
+static int s_read_getrandom(mp_digit* p)
+{
+   int ret;
+   do {
+      ret = getrandom(p, sizeof(*p), 0);
+   } while((ret == -1) && (errno == EINTR));
+   if (ret == sizeof(*p)) return MP_OKAY;
+   return -1;
+}
+#endif
+#endif
+
+/* We assume all platforms besides windows provide "/dev/urandom".
+ * In case yours doesn't, define MP_NO_DEV_URANDOM at compile-time.
+ */
+#if !defined(MP_WIN_CSP) && !defined(MP_NO_DEV_URANDOM)
+#ifndef MP_DEV_URANDOM
+#define MP_DEV_URANDOM "/dev/urandom"
+#endif
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+static int s_read_dev_urandom(mp_digit* p)
+{
+   ssize_t r;
+   int fd;
+   do {
+      fd = open(MP_DEV_URANDOM, O_RDONLY);
+   } while((fd == -1) && (errno == EINTR));
+   if (fd == -1) return -1;
+   do {
+      r = read(fd, p, sizeof(*p));
+   } while((r == -1) && (errno == EINTR));
+   close(fd);
+   if (r != sizeof(*p)) return -1;
+   return MP_OKAY;
+}
+#endif
+
+#if defined(MP_PRNG_ENABLE_LTM_RNG)
+unsigned long (*ltm_rng)(unsigned char *out, unsigned long outlen, void (*callback)(void));
+void (*ltm_rng_callback)(void);
+
+static int s_read_ltm_rng(mp_digit* p)
+{
+   unsigned long ret;
+   if (ltm_rng == NULL) return -1;
+   ret = ltm_rng((void*)p, sizeof(*p), ltm_rng_callback);
+   if (ret != sizeof(*p)) return -1;
+   return MP_OKAY;
+}
+#endif
+
+static int s_rand_digit(mp_digit* p)
+{
+   int ret = -1;
+
+#if defined(MP_ARC4RANDOM)
+   ret = s_read_arc4random(p);
+   if (ret == MP_OKAY) return ret;
+#endif
+
+#if defined(MP_WIN_CSP)
+   ret = s_read_win_csp(p);
+   if (ret == MP_OKAY) return ret;
+#else
+
+#if defined(MP_GETRANDOM)
+   ret = s_read_getrandom(p);
+   if (ret == MP_OKAY) return ret;
+#endif
+#if defined(MP_DEV_URANDOM)
+   ret = s_read_dev_urandom(p);
+   if (ret == MP_OKAY) return ret;
+#endif
+
+#endif /* MP_WIN_CSP */
+
+#if defined(MP_PRNG_ENABLE_LTM_RNG)
+   ret = s_read_ltm_rng(p);
+   if (ret == MP_OKAY) return ret;
+#endif
+
+   return ret;
+}
+
+/* makes a pseudo-random int of a given size */
+static int s_gen_random(mp_digit *r)
+{
+   int ret = s_rand_digit(r);
+   *r &= MP_MASK;
+   return ret;
 }
 
 int mp_rand(mp_int *a, int digits)
@@ -55,7 +192,9 @@ int mp_rand(mp_int *a, int digits)
 
    /* first place a random non-zero digit */
    do {
-      d = s_gen_random();
+      if (s_gen_random(&d) != MP_OKAY) {
+         return MP_VAL;
+      }
    } while (d == 0u);
 
    if ((res = mp_add_d(a, d, a)) != MP_OKAY) {
@@ -67,7 +206,10 @@ int mp_rand(mp_int *a, int digits)
          return res;
       }
 
-      if ((res = mp_add_d(a, s_gen_random(), a)) != MP_OKAY) {
+      if (s_gen_random(&d) != MP_OKAY) {
+         return MP_VAL;
+      }
+      if ((res = mp_add_d(a, d, a)) != MP_OKAY) {
          return res;
       }
    }
diff --git a/tommath.h b/tommath.h
index e26e1b9..b6b945a 100644
--- a/tommath.h
+++ b/tommath.h
@@ -100,18 +100,6 @@ typedef uint_least32_t mp_min_u32;
 typedef mp_digit mp_min_u32;
 #endif
 
-/* use arc4random on platforms that support it */
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
-#   define MP_GEN_RANDOM()    arc4random()
-#   define MP_GEN_RANDOM_MAX  0xffffffffu
-#endif
-
-/* use rand() as fall-back if there's no better rand function */
-#ifndef MP_GEN_RANDOM
-#   define MP_GEN_RANDOM()    rand()
-#   define MP_GEN_RANDOM_MAX  RAND_MAX
-#endif
-
 #define MP_DIGIT_BIT     DIGIT_BIT
 #define MP_MASK          ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1))
 #define MP_DIGIT_MAX     MP_MASK
@@ -287,6 +275,14 @@ int mp_cnt_lsb(const mp_int *a);
 /* makes a pseudo-random int of a given size */
 int mp_rand(mp_int *a, int digits);
 
+#ifdef MP_PRNG_ENABLE_LTM_RNG
+/* as last resort we will fall back to libtomcrypt's rng_get_bytes()
+ * in case you don't use libtomcrypt or use it w/o rng_get_bytes()
+ * you have to implement it somewhere else, as it's required */
+extern unsigned long (*ltm_rng)(unsigned char *out, unsigned long outlen, void (*callback)(void));
+extern void (*ltm_rng_callback)(void);
+#endif
+
 /* ---> binary operations <--- */
 /* c = a XOR b  */
 int mp_xor(const mp_int *a, const mp_int *b, mp_int *c);