aboutsummaryrefslogtreecommitdiffstats
path: root/crypto/rand/rand_unix.c
diff options
context:
space:
mode:
authorDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>2017-08-31 23:16:22 +0200
committerBen Kaduk <kaduk@mit.edu>2017-10-18 08:39:20 -0500
commitc16de9d8329d41a2433d0f273c080d9d06ad7a87 (patch)
tree3bdcf5e354efd8991aa1f7d83921655e81db726b /crypto/rand/rand_unix.c
parentaf1d638730bdfad85a7fa8c3f157b2828eda7c1d (diff)
downloadopenssl-c16de9d8329d41a2433d0f273c080d9d06ad7a87.tar.gz
Fix reseeding issues of the public RAND_DRBG
Reseeding is handled very differently by the classic RAND_METHOD API and the new RAND_DRBG api. These differences led to some problems when the new RAND_DRBG was made the default OpenSSL RNG. In particular, RAND_add() did not work as expected anymore. These issues are discussed on the thread '[openssl-dev] Plea for a new public OpenSSL RNG API' and in Pull Request #4328. This commit fixes the mentioned issues, introducing the following changes: - Replace the fixed size RAND_BYTES_BUFFER by a new RAND_POOL API which facilitates collecting entropy by the get_entropy() callback. - Don't use RAND_poll()/RAND_add() for collecting entropy from the get_entropy() callback anymore. Instead, replace RAND_poll() by RAND_POOL_acquire_entropy(). - Add a new function rand_drbg_restart() which tries to get the DRBG in an instantiated state by all means, regardless of the current state (uninstantiated, error, ...) the DRBG is in. If the caller provides entropy or additional input, it will be used for reseeding. - Restore the original documented behaviour of RAND_add() and RAND_poll() (namely to reseed the DRBG immediately) by a new implementation based on rand_drbg_restart(). - Add automatic error recovery from temporary failures of the entropy source to RAND_DRBG_generate() using the rand_drbg_restart() function. Reviewed-by: Paul Dale <paul.dale@oracle.com> Reviewed-by: Kurt Roeckx <kurt@roeckx.be> Reviewed-by: Rich Salz <rsalz@openssl.org> Reviewed-by: Ben Kaduk <kaduk@mit.edu> (Merged from https://github.com/openssl/openssl/pull/4328)
Diffstat (limited to 'crypto/rand/rand_unix.c')
-rw-r--r--crypto/rand/rand_unix.c127
1 files changed, 84 insertions, 43 deletions
diff --git a/crypto/rand/rand_unix.c b/crypto/rand/rand_unix.c
index 08ea55fbb9..f5a59cb28a 100644
--- a/crypto/rand/rand_unix.c
+++ b/crypto/rand/rand_unix.c
@@ -14,12 +14,18 @@
#include "rand_lcl.h"
#include <stdio.h>
-#if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI))
+#ifdef OPENSSL_RAND_SEED_GETRANDOM
+# include <linux/random.h>
+#endif
-# if (defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI)) && \
+#if (defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI)) && \
!defined(OPENSSL_RAND_SEED_NONE)
-# error "UEFI and VXWorks only support seeding NONE"
-# endif
+# error "UEFI and VXWorks only support seeding NONE"
+#endif
+
+#if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) \
+ || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) \
+ || defined(OPENSSL_SYS_UEFI))
# if defined(OPENSSL_SYS_VOS)
@@ -46,16 +52,16 @@
* would be far more predictable. This should only be used for legacy
* platforms.
*
- * As a precaution, we generate four times the required amount of seed
- * data.
+ * As a precaution, we assume only 2 bits of entropy per byte.
*/
-int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
+size_t RAND_POOL_acquire_entropy(RAND_POOL *pool)
{
short int code;
gid_t curr_gid;
pid_t curr_pid;
uid_t curr_uid;
int i, k;
+ size_t bytes_needed;
struct timespec ts;
unsigned char v;
# ifdef OPENSSL_SYS_VOS_HPPA
@@ -71,13 +77,15 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
* different processes.
*/
curr_gid = getgid();
- rand_add(arg, &curr_gid, sizeof curr_gid, 0);
+ RAND_POOL_add(pool, &curr_gid, sizeof(curr_gid), 0);
curr_pid = getpid();
- rand_add(arg, &curr_pid, sizeof curr_pid, 0);
+ RAND_POOL_add(pool, &curr_pid, sizeof(curr_pid), 0);
curr_uid = getuid();
- rand_add(arg, &curr_uid, sizeof curr_uid, 0);
+ RAND_POOL_add(pool, &curr_uid, sizeof(curr_uid), 0);
- for (i = 0; i < (RANDOMNESS_NEEDED * 4); i++) {
+ bytes_needed = RAND_POOL_bytes_needed(pool, 2 /*entropy_per_byte*/);
+
+ for (i = 0; i < bytes_needed; i++) {
/*
* burn some cpu; hope for interrupts, cache collisions, bus
* interference, etc.
@@ -98,9 +106,9 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
/* Get wall clock time, take 8 bits. */
clock_gettime(CLOCK_REALTIME, &ts);
v = (unsigned char)(ts.tv_nsec & 0xFF);
- rand_add(arg, &v, sizeof v, 1);
+ RAND_POOL_add(pool, arg, &v, sizeof(v) , 2);
}
- return 1;
+ return RAND_POOL_entropy_available(pool);
}
# else
@@ -127,27 +135,44 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
# endif
/*
- * Try the various seeding methods in turn, exit when succesful.
+ * Try the various seeding methods in turn, exit when successful.
+ *
+ * TODO(DRBG): If more than one entropy source is available, is it
+ * preferable to stop as soon as enough entropy has been collected
+ * (as favored by @rsalz) or should one rather be defensive and add
+ * more entropy than requested and/or from different sources?
+ *
+ * Currently, the user can select multiple entropy sources in the
+ * configure step, yet in practice only the first available source
+ * will be used. A more flexible solution has been requested, but
+ * currently it is not clear how this can be achieved without
+ * overengineering the problem. There are many parameters which
+ * could be taken into account when selecting the order and amount
+ * of input from the different entropy sources (trust, quality,
+ * possibility of blocking).
*/
-int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
+size_t RAND_POOL_acquire_entropy(RAND_POOL *pool)
{
# ifdef OPENSSL_RAND_SEED_NONE
- return 0;
+ return RAND_POOL_entropy_available(pool);
# else
- int ok = 1;
- char temp[RANDOMNESS_NEEDED];
-# define TEMPSIZE (int)sizeof(temp)
+ size_t bytes_needed;
+ size_t entropy_available = 0;
+ unsigned char *buffer;
# ifdef OPENSSL_RAND_SEED_GETRANDOM
- {
- int i = getrandom(temp, TEMPSIZE, 0);
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ buffer = RAND_POOL_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
- if (i >= 0) {
- rand_add(arg, temp, i, i);
- if (i == TEMPSIZE)
- goto done;
- }
+ if (getrandom(buffer, bytes_needed, 0) == (int)bytes_needed)
+ bytes = bytes_needed;
+
+ entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
}
+ if (entropy_available > 0)
+ return entropy_available;
# endif
# if defined(OPENSSL_RAND_SEED_LIBRANDOM)
@@ -157,7 +182,8 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
# endif
# ifdef OPENSSL_RAND_SEED_DEVRANDOM
- {
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ if (bytes_needed > 0) {
static const char *paths[] = { DEVRANDOM, NULL };
FILE *fp;
int i;
@@ -166,44 +192,59 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
if ((fp = fopen(paths[i], "rb")) == NULL)
continue;
setbuf(fp, NULL);
- if (fread(temp, 1, TEMPSIZE, fp) == TEMPSIZE) {
- rand_add(arg, temp, TEMPSIZE, TEMPSIZE);
- fclose(fp);
- goto done;
+ buffer = RAND_POOL_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
+ if (fread(buffer, 1, bytes_needed, fp) == bytes_needed)
+ bytes = bytes_needed;
+
+ entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
}
fclose(fp);
+ if (entropy_available > 0)
+ return entropy_available;
+
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
}
}
# endif
# ifdef OPENSSL_RAND_SEED_RDTSC
- rand_read_tsc(rand_add, arg);
+ entropy_available = rand_acquire_entropy_from_tsc(pool);
+ if (entropy_available > 0)
+ return entropy_available;
# endif
# ifdef OPENSSL_RAND_SEED_RDCPU
- if (rand_read_cpu(rand_add, arg))
- goto done;
+ entropy_available = rand_acquire_entropy_from_cpu(pool);
+ if (entropy_available > 0)
+ return entropy_available;
# endif
# ifdef OPENSSL_RAND_SEED_EGD
- {
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ if (bytes_needed > 0) {
static const char *paths[] = { DEVRANDOM_EGD, NULL };
int i;
for (i = 0; paths[i] != NULL; i++) {
- if (RAND_query_egd_bytes(paths[i], temp, TEMPSIZE) == TEMPSIZE) {
- rand_add(arg, temp, TEMPSIZE, TEMPSIZE);
- goto done;
+ buffer = RAND_POOL_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
+ int num = RAND_query_egd_bytes(paths[i],
+ buffer, (int)bytes_needed);
+ if (num == (int)bytes_needed)
+ bytes = bytes_needed;
+
+ entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
}
+ if (entropy_available > 0)
+ return entropy_available;
}
}
# endif
- ok = 0;
-
-done:
- OPENSSL_cleanse(temp, TEMPSIZE);
- return ok;
+ return RAND_POOL_entropy_available(pool);
# endif
}
# endif