DRBG: make locking api truly private
[openssl.git] / crypto / rand / rand_lib.c
index e82a63e5998d6878565924424572c9ab2713456d..7b8b8fcb48a564aea82591313d351aa7c8e197bd 100644 (file)
 #endif
 #include "e_os.h"
 
+/* Macro to convert two thirty two bit values into a sixty four bit one */
+#define TWO32TO64(a, b) ((((uint64_t)(a)) << 32) + (b))
+
+/*
+ * Check for the existence and support of POSIX timers.  The standard
+ * says that the _POSIX_TIMERS macro will have a positive value if they
+ * are available.
+ *
+ * However, we want an additional constraint: that the timer support does
+ * not require an extra library dependency.  Early versions of glibc
+ * require -lrt to be specified on the link line to access the timers,
+ * so this needs to be checked for.
+ *
+ * It is worse because some libraries define __GLIBC__ but don't
+ * support the version testing macro (e.g. uClibc).  This means
+ * an extra check is needed.
+ *
+ * The final condition is:
+ *      "have posix timers and either not glibc or glibc without -lrt"
+ *
+ * The nested #if sequences are required to avoid using a parameterised
+ * macro that might be undefined.
+ */
+#undef OSSL_POSIX_TIMER_OKAY
+#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0
+# if defined(__GLIBC__)
+#  if defined(__GLIBC_PREREQ)
+#   if __GLIBC_PREREQ(2, 17)
+#    define OSSL_POSIX_TIMER_OKAY
+#   endif
+#  endif
+# else
+#  define OSSL_POSIX_TIMER_OKAY
+# endif
+#endif
+
 #ifndef OPENSSL_NO_ENGINE
 /* non-NULL if default_RAND_meth is ENGINE-provided */
 static ENGINE *funct_ref;
@@ -164,17 +200,16 @@ size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
             /*
              * Get random from parent, include our state as additional input.
              * Our lock is already held, but we need to lock our parent before
-             * generating bits from it.
+             * generating bits from it. (Note: taking the lock will be a no-op
+             * if locking if drbg->parent->lock == NULL.)
              */
-            if (drbg->parent->lock)
-                CRYPTO_THREAD_write_lock(drbg->parent->lock);
+            rand_drbg_lock(drbg->parent);
             if (RAND_DRBG_generate(drbg->parent,
                                    buffer, bytes_needed,
                                    0,
                                    (unsigned char *)drbg, sizeof(*drbg)) != 0)
                 bytes = bytes_needed;
-            if (drbg->parent->lock)
-                CRYPTO_THREAD_unlock(drbg->parent->lock);
+            rand_drbg_unlock(drbg->parent);
 
             entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
         }
@@ -193,6 +228,68 @@ size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
     return ret;
 }
 
+/*
+ * Find a suitable system time.  Start with the highest resolution source
+ * and work down to the slower ones.  This is added as additional data and
+ * isn't counted as randomness, so any result is acceptable.
+ */
+static uint64_t get_timer_bits(void)
+{
+    uint64_t res = OPENSSL_rdtsc();
+
+    if (res != 0)
+        return res;
+#if defined(_WIN32)
+    {
+        LARGE_INTEGER t;
+        FILETIME ft;
+
+        if (QueryPerformanceCounter(&t) != 0)
+            return t.QuadPart;
+        GetSystemTimeAsFileTime(&ft);
+        return TWO32TO64(ft.dwHighDateTime, ft.dwLowDateTime);
+    }
+#elif defined(__sun) || defined(__hpux)
+    return gethrtime();
+#elif defined(_AIX)
+    {
+        timebasestruct_t t;
+
+        read_wall_time(&t, TIMEBASE_SZ);
+        return TWO32TO64(t.tb_high, t.tb_low);
+    }
+#else
+
+#if defined(OSSL_POSIX_TIMER_OKAY)
+    {
+        struct timespec ts;
+        clockid_t cid;
+
+#  ifdef CLOCK_BOOTTIME
+        cid = CLOCK_BOOTTIME;
+#  elif defined(_POSIX_MONOTONIC_CLOCK)
+        cid = CLOCK_MONOTONIC;
+#  else
+        cid = CLOCK_REALTIME;
+#  endif
+
+        if (clock_gettime(cid, &ts) == 0)
+            return TWO32TO64(ts.tv_sec, ts.tv_nsec);
+    }
+# endif
+# if defined(__unix__) \
+     || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
+    {
+        struct timeval tv;
+
+        if (gettimeofday(&tv, NULL) == 0)
+            return TWO32TO64(tv.tv_sec, tv.tv_usec);
+    }
+# endif
+    return time(NULL);
+#endif
+}
+
 /*
  * Generate additional data that can be used for the drbg. The data does
  * not need to contain entropy, but it's useful if it contains at least
@@ -210,15 +307,10 @@ size_t rand_drbg_get_additional_data(unsigned char **pout, size_t max_len)
     size_t len;
 #ifdef OPENSSL_SYS_UNIX
     pid_t pid;
-    struct timeval tv;
 #elif defined(OPENSSL_SYS_WIN32)
     DWORD pid;
-    FILETIME ft;
-    LARGE_INTEGER pc;
-#endif
-#ifdef OPENSSL_CPUID_OBJ
-    uint32_t tsc = 0;
 #endif
+    uint64_t tbits;
 
     pool = RAND_POOL_new(0, 0, max_len);
     if (pool == NULL)
@@ -236,21 +328,8 @@ size_t rand_drbg_get_additional_data(unsigned char **pout, size_t max_len)
     if (thread_id != 0)
         RAND_POOL_add(pool, (unsigned char *)&thread_id, sizeof(thread_id), 0);
 
-#ifdef OPENSSL_CPUID_OBJ
-    tsc = OPENSSL_rdtsc();
-    if (tsc != 0)
-        RAND_POOL_add(pool, (unsigned char *)&tsc, sizeof(tsc), 0);
-#endif
-
-#ifdef OPENSSL_SYS_UNIX
-    if (gettimeofday(&tv, NULL) == 0)
-        RAND_POOL_add(pool, (unsigned char *)&tv, sizeof(tv), 0);
-#elif defined(OPENSSL_SYS_WIN32)
-    if (QueryPerformanceCounter(&pc) != 0)
-        RAND_POOL_add(pool, (unsigned char *)&pc, sizeof(pc), 0);
-    GetSystemTimeAsFileTime(&ft);
-    RAND_POOL_add(pool, (unsigned char *)&ft, sizeof(ft), 0);
-#endif
+    tbits = get_timer_bits();
+    RAND_POOL_add(pool, (unsigned char *)&tbits, sizeof(tbits), 0);
 
     /* TODO: Use RDSEED? */
 
@@ -326,9 +405,9 @@ int RAND_poll(void)
         if (drbg == NULL)
             return 0;
 
-        CRYPTO_THREAD_write_lock(drbg->lock);
+        rand_drbg_lock(drbg);
         ret = rand_drbg_restart(drbg, NULL, 0, 0);
-        CRYPTO_THREAD_unlock(drbg->lock);
+        rand_drbg_unlock(drbg);
 
         return ret;
 
@@ -718,9 +797,9 @@ int RAND_priv_bytes(unsigned char *buf, int num)
         return 0;
 
     /* We have to lock the DRBG before generating bits from it. */
-    CRYPTO_THREAD_write_lock(drbg->lock);
+    rand_drbg_lock(drbg);
     ret = RAND_DRBG_bytes(drbg, buf, num);
-    CRYPTO_THREAD_unlock(drbg->lock);
+    rand_drbg_unlock(drbg);
     return ret;
 }