From 8389ec4b4950b9474e72a959eb0b0a6ce77ac1e8 Mon Sep 17 00:00:00 2001 From: Rich Salz Date: Tue, 18 Jul 2017 09:39:21 -0400 Subject: [PATCH] Add --with-rand-seed Add a new config param to specify how the CSPRNG should be seeded. Illegal values or nonsensical combinations (e.g., anything other than "os" on VMS or HP VOS etc) result in build failures. Add RDSEED support. Add RDTSC but leave it disabled for now pending more investigation. Refactor and reorganization all seeding files (rand_unix/win/vms) so that they are simpler. Only require 128 bits of seeding material. Many document improvements, including why to not use RAND_add() and the limitations around using load_file/write_file. Document RAND_poll(). Cleanup Windows RAND_poll and return correct status More completely initialize the default DRBG. Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/3965) --- Configure | 23 +++ apps/version.c | 38 ++++- crypto/rand/drbg_rand.c | 6 +- crypto/rand/rand_lcl.h | 28 ++-- crypto/rand/rand_lib.c | 64 ++++++++ crypto/rand/rand_unix.c | 292 ++++++++++++------------------------ crypto/rand/rand_vms.c | 6 +- crypto/rand/rand_win.c | 98 ++++-------- doc/man3/RAND_add.pod | 65 ++++---- doc/man3/RAND_load_file.pod | 10 +- 10 files changed, 324 insertions(+), 306 deletions(-) diff --git a/Configure b/Configure index 9612976cfa..ebfe01d290 100755 --- a/Configure +++ b/Configure @@ -561,6 +561,9 @@ $config{build_type} = "release"; my %unsupported_options = (); my %deprecated_options = (); +# If you change this, update apps/version.c +my @known_seed_sources = qw(getrandom devrandom os egd none rdcpu librandom); +my @seed_sources = (); while (@argvcopy) { $_ = shift @argvcopy; @@ -729,6 +732,15 @@ while (@argvcopy) { $withargs{fuzzer_include}=$1; } + elsif (/^--with-rand-seed=(.*)$/) + { + foreach my $x (split(m|,|, $1)) + { + die "Unknown --with-rand-seed choice $x\n" + if ! grep { $x eq $_ } @known_seed_sources; + push @seed_sources, $x; + } + } elsif (/^--cross-compile-prefix=(.*)$/) { $config{cross_compile_prefix}=$1; @@ -812,6 +824,17 @@ if ($libs =~ /(^|\s)-Wl,-rpath,/ "***** any of asan, msan or ubsan\n"; } +if (scalar(@seed_sources) == 0) { + print "Using implicit seed configuration\n"; + push @seed_sources, 'os'; +} +die "Cannot seed with none and anything else" + if scalar(grep { $_ eq 'none' } @seed_sources) > 0 + && scalar(@seed_sources) > 1; +push @{$config{openssl_other_defines}}, + map { (my $x = $_) =~ tr|[\-a-z]|[_A-Z]|; "OPENSSL_RAND_SEED_$x" } + @seed_sources; + my @tocheckfor = (keys %disabled); while (@tocheckfor) { my %new_tocheckfor = (); diff --git a/apps/version.c b/apps/version.c index 24f1a80fdf..298d837f5a 100644 --- a/apps/version.c +++ b/apps/version.c @@ -32,7 +32,7 @@ typedef enum OPTION_choice { OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, - OPT_B, OPT_D, OPT_E, OPT_F, OPT_O, OPT_P, OPT_V, OPT_A + OPT_B, OPT_D, OPT_E, OPT_F, OPT_O, OPT_P, OPT_V, OPT_A, OPT_R } OPTION_CHOICE; const OPTIONS version_options[] = { @@ -44,13 +44,14 @@ const OPTIONS version_options[] = { {"f", OPT_F, '-', "Show compiler flags used"}, {"o", OPT_O, '-', "Show some internal datatype options"}, {"p", OPT_P, '-', "Show target build platform"}, + {"r", OPT_R, '-', "Show random seeding options"}, {"v", OPT_V, '-', "Show library version"}, {NULL} }; int version_main(int argc, char **argv) { - int ret = 1, dirty = 0; + int ret = 1, dirty = 0, seed = 0; int cflags = 0, version = 0, date = 0, options = 0, platform = 0, dir = 0; int engdir = 0; char *prog; @@ -85,11 +86,14 @@ int version_main(int argc, char **argv) case OPT_P: dirty = platform = 1; break; + case OPT_R: + dirty = seed = 1; + break; case OPT_V: dirty = version = 1; break; case OPT_A: - cflags = version = date = platform = dir = engdir = 1; + seed = cflags = version = date = platform = dir = engdir = 1; break; } } @@ -133,6 +137,34 @@ int version_main(int argc, char **argv) printf("%s\n", OpenSSL_version(OPENSSL_DIR)); if (engdir) printf("%s\n", OpenSSL_version(OPENSSL_ENGINES_DIR)); + if (seed) { + printf("Seeding source:"); +#ifdef OPENSSL_RAND_SEED_RTDSC + printf(" rtdsc"); +#endif +#ifdef OPENSSL_RAND_SEED_RDCPU + printf(" rdrand-hardware"); +#endif +#ifdef OPENSSL_RAND_SEED_LIBRANDOM + printf(" C-library-random"); +#endif +#ifdef OPENSSL_RAND_SEED_GETRANDOM + printf(" getrandom-syscall"); +#endif +#ifdef OPENSSL_RAND_SEED_DEVRANDOM + printf(" random-device"); +#endif +#ifdef OPENSSL_RAND_SEED_EGD + printf(" EGD"); +#endif +#ifdef OPENSSL_RAND_SEED_NONE + printf(" none"); +#endif +#ifdef OPENSSL_RAND_SEED_OS + printf(" os-specific"); +#endif + printf("\n"); + } ret = 0; end: return (ret); diff --git a/crypto/rand/drbg_rand.c b/crypto/rand/drbg_rand.c index 77d59ec813..8b30cd8a0d 100644 --- a/crypto/rand/drbg_rand.c +++ b/crypto/rand/drbg_rand.c @@ -29,8 +29,12 @@ static CRYPTO_ONCE ossl_drbg_init = CRYPTO_ONCE_STATIC_INIT; DEFINE_RUN_ONCE_STATIC(do_ossl_drbg_init) { + int st = 1; + ossl_drbg.lock = CRYPTO_THREAD_lock_new(); - return ossl_drbg.lock != NULL; + st &= ossl_drbg.lock != NULL; + st &= RAND_DRBG_set(&ossl_drbg, NID_aes_128_ctr, 0) == 1; + return st; } void rand_drbg_cleanup(void) diff --git a/crypto/rand/rand_lcl.h b/crypto/rand/rand_lcl.h index de1f2cd4e6..b9df5d18e2 100644 --- a/crypto/rand/rand_lcl.h +++ b/crypto/rand/rand_lcl.h @@ -17,21 +17,24 @@ # include # include "internal/rand.h" -/* we require 256 bits of randomness */ -# define RANDOMNESS_NEEDED (256 / 8) +/* Amount of randomness (in bytes) we want for initial seeding. */ +# define RANDOMNESS_NEEDED (128 / 8) /* Maximum count allowed in reseeding */ #define MAX_RESEED (1 << 24) /* DRBG status values */ -#define DRBG_STATUS_UNINITIALISED 0 -#define DRBG_STATUS_READY 1 -#define DRBG_STATUS_RESEED 2 -#define DRBG_STATUS_ERROR 3 +# define DRBG_STATUS_UNINITIALISED 0 +# define DRBG_STATUS_READY 1 +# define DRBG_STATUS_RESEED 2 +# define DRBG_STATUS_ERROR 3 /* A default maximum length: larger than any reasonable value used in pratice */ -#define DRBG_MAX_LENGTH 0x7ffffff0 +# define DRBG_MAX_LENGTH 0x7ffffff0 +/* + * The context for DRBG AES-CTR + */ typedef struct drbg_ctr_ctx_st { AES_KEY ks; size_t keylen; @@ -46,6 +49,10 @@ typedef struct drbg_ctr_ctx_st { unsigned char KX[48]; } DRBG_CTR_CTX; + +/* + * The context for all DRBG's + */ struct drbg_ctx_st { CRYPTO_RWLOCK *lock; DRBG_CTX *parent; @@ -84,9 +91,12 @@ struct drbg_ctx_st { extern RAND_METHOD openssl_rand_meth; void rand_drbg_cleanup(void); +/* Hardware-based seeding functions. */ +void rand_rdtsc(void); +int rand_rdcpu(void); + +/* DRBG functions implementing AES-CTR */ int ctr_init(DRBG_CTX *dctx); -int drbg_hash_init(DRBG_CTX *dctx); -int drbg_hmac_init(DRBG_CTX *dctx); int ctr_uninstantiate(DRBG_CTX *dctx); int ctr_instantiate(DRBG_CTX *dctx, const unsigned char *ent, size_t entlen, diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c index 3168d84b47..05aa45cd64 100644 --- a/crypto/rand/rand_lib.c +++ b/crypto/rand/rand_lib.c @@ -25,6 +25,70 @@ static CRYPTO_RWLOCK *rand_meth_lock; static const RAND_METHOD *default_RAND_meth; static CRYPTO_ONCE rand_init = CRYPTO_ONCE_STATIC_INIT; +#ifdef OPENSSL_RAND_SEED_RDTSC +/* + * IMPORTANT NOTE: It is not currently possible to use this code + * because we are not sure about the amount of randomness. Some + * SP900 tests have been run, but there is internal skepticism. + * So for now this code is not used. + */ +# error "RDTSC enabled? Should not be possible!" + +/* + * Since we get some randomness from the low-order bits of the + * high-speec clock, it can help. But don't return a status since + * it's not sufficient to indicate whether or not the seeding was + * done. + */ +void rand_rdtsc(void) +{ + unsigned char c; + int i; + + for (i = 0; i < 10; i++) { + c = (unsigned char)(OPENSSL_rdtsc() & 0xFF); + RAND_add(&c, 1, 0.5); + } +} +#endif + +#ifdef OPENSSL_RAND_SEED_RDCPU +size_t OPENSSL_ia32_rdseed(void); +size_t OPENSSL_ia32_rdrand(void); + +extern unsigned int OPENSSL_ia32cap_P[]; + +int rand_rdcpu(void) +{ + size_t i, s; + + /* If RDSEED is available, use that. */ + if ((OPENSSL_ia32cap_P[1] & (1 << 18)) != 0) { + for (i = 0; i < RANDOMNESS_NEEDED; i += sizeof(s)) { + s = OPENSSL_ia32_rdseed(); + if (s == 0) + break; + RAND_add(&s, (int)sizeof(s), sizeof(s)); + } + if (i >= RANDOMNESS_NEEDED) + return 1; + } + + /* Second choice is RDRAND. */ + if ((OPENSSL_ia32cap_P[1] & (1 << (62 - 32))) != 0) { + for (i = 0; i < RANDOMNESS_NEEDED; i += sizeof(s)) { + s = OPENSSL_ia32_rdrand(); + if (s == 0) + break; + RAND_add(&s, (int)sizeof(s), sizeof(s)); + } + if (i >= RANDOMNESS_NEEDED) + return 1; + } + + return 0; +} +#endif DEFINE_RUN_ONCE_STATIC(do_rand_init) { diff --git a/crypto/rand/rand_unix.c b/crypto/rand/rand_unix.c index bfdd3e6efa..2cfd20dd50 100644 --- a/crypto/rand/rand_unix.c +++ b/crypto/rand/rand_unix.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -9,32 +9,32 @@ #include -#define USE_SOCKETS #include "e_os.h" #include "internal/cryptlib.h" #include #include "rand_lcl.h" +#include #if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI)) -# include -# include -# include -# include -# include -# include -# include -# if defined(OPENSSL_SYS_LINUX) /* should actually be available virtually - * everywhere */ -# include -# endif -# include -# ifndef FD_SETSIZE -# define FD_SETSIZE (8*sizeof(fd_set)) +# if (defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI)) && \ + !defined(OPENSSL_RAND_SEED_NONE) +# error "UEFI and VXWorks only support seeding NONE" # endif # if defined(OPENSSL_SYS_VOS) +# ifndef OPENSSL_RAND_SEED_OS +# error "Unsupported seeding method configured; must be os" +# endif + +# if defined(OPENSSL_SYS_VOS_HPPA) && defined(OPENSSL_SYS_VOS_IA32) +# error "Unsupported HP-PA and IA32 at the same time." +# endif +# if !defined(OPENSSL_SYS_VOS_HPPA) && !defined(OPENSSL_SYS_VOS_IA32) +# error "Must have one of HP-PA or IA32" +# endif + /* * The following algorithm repeatedly samples the real-time clock (RTC) to * generate a sequence of unpredictable data. The algorithm relies upon the @@ -51,7 +51,6 @@ * As a precaution, we generate 4 times the minimum required amount of seed * data. */ - int RAND_poll(void) { short int code; @@ -61,35 +60,24 @@ int RAND_poll(void) int i, k; struct timespec ts; unsigned char v; - # ifdef OPENSSL_SYS_VOS_HPPA long duration; extern void s$sleep(long *_duration, short int *_code); # else -# ifdef OPENSSL_SYS_VOS_IA32 long long duration; extern void s$sleep2(long long *_duration, short int *_code); -# else -# error "Unsupported Platform." -# endif /* OPENSSL_SYS_VOS_IA32 */ -# endif /* OPENSSL_SYS_VOS_HPPA */ +# endif /* * Seed with the gid, pid, and uid, to ensure *some* variation between * different processes. */ - curr_gid = getgid(); - RAND_add(&curr_gid, sizeof curr_gid, 1); - curr_gid = 0; - + RAND_add(&curr_gid, sizeof curr_gid, 0); curr_pid = getpid(); - RAND_add(&curr_pid, sizeof curr_pid, 1); - curr_pid = 0; - + RAND_add(&curr_pid, sizeof curr_pid, 0); curr_uid = getuid(); - RAND_add(&curr_uid, sizeof curr_uid, 1); - curr_uid = 0; + RAND_add(&curr_uid, sizeof curr_uid, 0); for (i = 0; i < (RANDOMNESS_NEEDED * 4); i++) { /* @@ -104,203 +92,111 @@ int RAND_poll(void) duration = 1; s$sleep(&duration, &code); # else -# ifdef OPENSSL_SYS_VOS_IA32 /* sleep for 1/65536 of a second (15 us). */ duration = 1; s$sleep2(&duration, &code); -# endif /* OPENSSL_SYS_VOS_IA32 */ -# endif /* OPENSSL_SYS_VOS_HPPA */ +# endif - /* get wall clock time. */ + /* Get wall clock time, take 8 bits. */ clock_gettime(CLOCK_REALTIME, &ts); - - /* take 8 bits */ - v = (unsigned char)(ts.tv_nsec % 256); + v = (unsigned char)(ts.tv_nsec & 0xFF); RAND_add(&v, sizeof v, 1); - v = 0; } return 1; } + # else -int RAND_poll(void) -{ - unsigned long l; - pid_t curr_pid = getpid(); -# if defined(DEVRANDOM) || (!defined(OPENSS_NO_EGD) && defined(DEVRANDOM_EGD)) - unsigned char tmpbuf[RANDOMNESS_NEEDED]; - int n = 0; -# endif -# ifdef DEVRANDOM - static const char *randomfiles[] = { DEVRANDOM }; - struct stat randomstats[OSSL_NELEM(randomfiles)]; - int fd; - unsigned int i; -# endif -# if !defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD) - static const char *egdsockets[] = { DEVRANDOM_EGD, NULL }; - const char **egdsocket = NULL; + +# if defined(OPENSSL_RAND_SEED_EGD) && \ + (defined(OPENSSL_NO_EGD) || !defined(DEVRANDOM_EGD)) +# error "Seeding uses EGD but EGD is turned off or no device given" # endif -# ifdef DEVRANDOM - memset(randomstats, 0, sizeof(randomstats)); - /* - * Use a randomness device. Linux, FreeBSD and OpenBSD have - * this. Use /dev/urandom if you can as /dev/random may block if it runs - * out of random entries. - */ +# if defined(OPENSSL_RAND_SEED_DEVRANDOM) && !defined(DEVRANDOM) +# error "Seeding uses urandom but DEVRANDOM is not configured" +# endif - for (i = 0; (i < OSSL_NELEM(randomfiles)) && (n < RANDOMNESS_NEEDED); i++) { - if ((fd = open(randomfiles[i], O_RDONLY -# ifdef O_NONBLOCK - | O_NONBLOCK -# endif -# ifdef O_BINARY - | O_BINARY -# endif -# ifdef O_NOCTTY /* If it happens to be a TTY (god forbid), do - * not make it our controlling tty */ - | O_NOCTTY +# if defined(OPENSSL_RAND_SEED_OS) +# if defined(DEVRANDOM) +# define OPENSSL_RAND_SEED_DEVRANDOM +# else +# error "OS seeding requires DEVRANDOM to be configured" # endif - )) >= 0) { - int usec = 10 * 1000; /* spend 10ms on each file */ - int r; - unsigned int j; - struct stat *st = &randomstats[i]; - - /* - * Avoid using same input... Used to be O_NOFOLLOW above, but - * it's not universally appropriate... - */ - if (fstat(fd, st) != 0) { - close(fd); - continue; - } - for (j = 0; j < i; j++) { - if (randomstats[j].st_ino == st->st_ino && - randomstats[j].st_dev == st->st_dev) - break; - } - if (j < i) { - close(fd); - continue; - } - - do { - int try_read = 0; - -# if defined(OPENSSL_SYS_LINUX) - /* use poll() */ - struct pollfd pset; - - pset.fd = fd; - pset.events = POLLIN; - pset.revents = 0; - - if (poll(&pset, 1, usec / 1000) < 0) - usec = 0; - else - try_read = (pset.revents & POLLIN) != 0; +# endif -# else - /* use select() */ - fd_set fset; - struct timeval t; +# if defined(OPENSSL_RAND_SEED_LIBRANDOM) +# error "librandom not (yet) supported" +# endif - t.tv_sec = 0; - t.tv_usec = usec; +int RAND_poll(void) +{ +# ifdef OPENSSL_RAND_SEED_NONE + return 0; +# else + int ok = 0; + char temp[RANDOMNESS_NEEDED]; +# define TEMPSIZE (int)sizeof(temp) - if (FD_SETSIZE > 0 && (unsigned)fd >= FD_SETSIZE) { - /* - * can't use select, so just try to read once anyway - */ - try_read = 1; - } else { - FD_ZERO(&fset); - FD_SET(fd, &fset); +# ifdef OPENSSL_RAND_SEED_RDTSC + rand_rdtsc(); +# endif - if (select(fd + 1, &fset, NULL, NULL, &t) >= 0) { - usec = t.tv_usec; - if (FD_ISSET(fd, &fset)) - try_read = 1; - } else - usec = 0; - } +# ifdef OPENSSL_RAND_SEED_RDCPU + if (rand_rdcpu()) + ok++; # endif - if (try_read) { - r = read(fd, (unsigned char *)tmpbuf + n, - RANDOMNESS_NEEDED - n); - if (r > 0) - n += r; - } else - r = -1; +# ifdef OPENSSL_RAND_SEED_EGD + { + static const char *paths[] = { DEVRANDOM_EGD, NULL }; + int i; - /* - * Some Unixen will update t in select(), some won't. For - * those who won't, or if we didn't use select() in the first - * place, give up here, otherwise, we will do this once again - * for the remaining time. - */ - if (usec == 10 * 1000) - usec = 0; + for (i = 0; paths[i] != NULL; i++) { + if (RAND_query_egd_bytes(paths[i], temp, TEMPSIZE) == TEMPSIZE) { + RAND_add(temp, TEMPSIZE, TEMPSIZE); + ok++; + break; } - while ((r > 0 || - (errno == EINTR || errno == EAGAIN)) && usec != 0 - && n < RANDOMNESS_NEEDED); - - close(fd); } } -# endif /* defined(DEVRANDOM) */ - -# if !defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD) - /* - * Use an EGD socket to read randomness from the daemon. - */ - - for (egdsocket = egdsockets; *egdsocket && n < RANDOMNESS_NEEDED; - egdsocket++) { - int r; +# endif - r = RAND_query_egd_bytes(*egdsocket, (unsigned char *)tmpbuf + n, - RANDOMNESS_NEEDED - n); - if (r > 0) - n += r; - } -# endif /* defined(DEVRANDOM_EGD) */ +# ifdef OPENSSL_RAND_SEED_DEVRANDOM + { + static const char *paths[] = { DEVRANDOM, NULL }; + FILE *fp; + int i; -# if defined(DEVRANDOM) || (!defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD)) - if (n > 0) { - RAND_add(tmpbuf, sizeof tmpbuf, (double)n); - OPENSSL_cleanse(tmpbuf, n); + for (i = 0; paths[i] != NULL; i++) { + if ((fp = fopen(paths[i], "rb")) == NULL) + continue; + setbuf(fp, NULL); + if (fread(temp, 1, TEMPSIZE, fp) == TEMPSIZE) { + RAND_add(temp, TEMPSIZE, TEMPSIZE); + ok++; + fclose(fp); + break; + } + } } -# endif +# endif - /* put in some default random data, we need more than just this */ - l = curr_pid; - RAND_add(&l, sizeof(l), 0.0); - l = getuid(); - RAND_add(&l, sizeof(l), 0.0); +# ifdef OPENSSL_RAND_SEED_GETRANDOM + { + int i = getrandom(temp, TEMPSIZE, 0); - l = time(NULL); - RAND_add(&l, sizeof(l), 0.0); + if (i >= 0) { + RAND_add(temp, i, i); + if (i == TEMPSIZE) + ok++; + } + } +# endif -# if defined(DEVRANDOM) || (!defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD)) - return 1; -# else - return 0; + OPENSSL_cleanse(temp, TEMPSIZE); + return ok > 0 ? 1 : 0; # endif } +# endif -# endif /* defined(__OpenBSD__) */ -#endif /* !(defined(OPENSSL_SYS_WINDOWS) || - * defined(OPENSSL_SYS_WIN32) || - * defined(OPENSSL_SYS_VMS) || - * defined(OPENSSL_SYS_VXWORKS) */ - -#if defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI) -int RAND_poll(void) -{ - return 0; -} #endif diff --git a/crypto/rand/rand_vms.c b/crypto/rand/rand_vms.c index ec99dbfe26..16afae7a2e 100644 --- a/crypto/rand/rand_vms.c +++ b/crypto/rand/rand_vms.c @@ -1,5 +1,5 @@ /* - * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2001-2017 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -21,6 +21,10 @@ # pragma message disable DOLLARID # endif +# ifndef OPENSSL_RAND_SEED_OS +# error "Unsupported seeding method configured; must be os" +# endif + /* * Use 32-bit pointers almost everywhere. Define the type to which to cast a * pointer passed to an external function. diff --git a/crypto/rand/rand_win.c b/crypto/rand/rand_win.c index 1be0ed3c9a..acc428640e 100644 --- a/crypto/rand/rand_win.c +++ b/crypto/rand/rand_win.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -12,13 +12,18 @@ #include "rand_lcl.h" #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) + +# ifndef OPENSSL_RAND_SEED_OS +# error "Unsupported seeding method configured; must be os" +# endif + # include /* On Windows 7 or higher use BCrypt instead of the legacy CryptoAPI */ -# if defined(_MSC_VER) && defined(_WIN32_WINNT) && _WIN32_WINNT>=0x0601 -# define RAND_WINDOWS_USE_BCRYPT +# if defined(_MSC_VER) && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0601 +# define USE_BCRYPTGENRANDOM # endif -# ifdef RAND_WINDOWS_USE_BCRYPT +# ifdef USE_BCRYPTGENRANDOM # include # pragma comment(lib, "bcrypt.lib") # ifndef STATUS_SUCCESS @@ -34,52 +39,52 @@ # define INTEL_DEF_PROV L"Intel Hardware Cryptographic Service Provider" # endif -static void readtimer(void); - int RAND_poll(void) { - MEMORYSTATUS mst; -# ifndef RAND_WINDOWS_USE_BCRYPT +# ifndef USE_BCRYPTGENRANDOM HCRYPTPROV hProvider; # endif DWORD w; - BYTE buf[64]; + BYTE buf[RANDOMNESS_NEEDED]; + int ok = 0; -# ifdef RAND_WINDOWS_USE_BCRYPT - if (BCryptGenRandom(NULL, buf, (ULONG)sizeof(buf), BCRYPT_USE_SYSTEM_PREFERRED_RNG) == STATUS_SUCCESS) { - RAND_add(buf, sizeof(buf), sizeof(buf)); - } +# ifdef OPENSSL_RAND_SEED_RDTSC + rand_rdtsc(); +# endif +# ifdef OPENSSL_RAND_SEED_RDCPU + if (rand_rdcpu()) + ok++; +# endif + +# ifdef USE_BCRYPTGENRANDOM + if (BCryptGenRandom(NULL, buf, (ULONG)sizeof(buf), + BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS) + return 0; + RAND_add(buf, sizeof(buf), sizeof(buf)); + return 1; # else /* poll the CryptoAPI PRNG */ - /* The CryptoAPI returns sizeof(buf) bytes of randomness */ - if (CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + if (CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != 0) { if (CryptGenRandom(hProvider, (DWORD)sizeof(buf), buf) != 0) { RAND_add(buf, sizeof(buf), sizeof(buf)); + ok++; } CryptReleaseContext(hProvider, 0); } /* poll the Pentium PRG with CryptoAPI */ - if (CryptAcquireContextW(&hProvider, NULL, INTEL_DEF_PROV, PROV_INTEL_SEC, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + if (CryptAcquireContextW(&hProvider, NULL, INTEL_DEF_PROV, PROV_INTEL_SEC, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != 0) { if (CryptGenRandom(hProvider, (DWORD)sizeof(buf), buf) != 0) { RAND_add(buf, sizeof(buf), sizeof(buf)); + ok++; } CryptReleaseContext(hProvider, 0); } # endif - /* timer data */ - readtimer(); - - /* memory usage statistics */ - GlobalMemoryStatus(&mst); - RAND_add(&mst, sizeof(mst), 1); - - /* process ID */ - w = GetCurrentProcessId(); - RAND_add(&w, sizeof(w), 1); - - return (1); + return ok ? 1 : 0; } #if OPENSSL_API_COMPAT < 0x10100000L @@ -95,41 +100,4 @@ void RAND_screen(void) } #endif -/* feed timing information to the PRNG */ -static void readtimer(void) -{ - DWORD w; - LARGE_INTEGER l; - static int have_perfc = 1; -# if defined(_MSC_VER) && defined(_M_X86) - static int have_tsc = 1; - DWORD cyclecount; - - if (have_tsc) { - __try { - __asm { - _emit 0x0f _emit 0x31 mov cyclecount, eax} - RAND_add(&cyclecount, sizeof(cyclecount), 1); - } - __except(EXCEPTION_EXECUTE_HANDLER) { - have_tsc = 0; - } - } -# else -# define have_tsc 0 -# endif - - if (have_perfc) { - if (QueryPerformanceCounter(&l) == 0) - have_perfc = 0; - else - RAND_add(&l, sizeof(l), 0); - } - - if (!have_tsc && !have_perfc) { - w = GetTickCount(); - RAND_add(&w, sizeof(w), 0); - } -} - #endif diff --git a/doc/man3/RAND_add.pod b/doc/man3/RAND_add.pod index f5514f092e..ee54390669 100644 --- a/doc/man3/RAND_add.pod +++ b/doc/man3/RAND_add.pod @@ -2,63 +2,76 @@ =head1 NAME -RAND_add, RAND_seed, RAND_status, RAND_event, RAND_screen - add -randomness to the PRNG +RAND_add, RAND_poll, RAND_seed, RAND_status, RAND_event, RAND_screen +- add randomness to the PRNG or get its status =head1 SYNOPSIS #include - void RAND_seed(const void *buf, int num); + int RAND_status(void); + int RAND_poll() void RAND_add(const void *buf, int num, double randomness); + void RAND_seed(const void *buf, int num); - int RAND_status(void); +Deprecated: #if OPENSSL_API_COMPAT < 0x10100000L - int RAND_event(UINT iMsg, WPARAM wParam, LPARAM lParam); + int RAND_event(UINT iMsg, WPARAM wParam, LPARAM lParam); void RAND_screen(void); #endif =head1 DESCRIPTION -RAND_add() mixes the B bytes at B into the PRNG state. Thus, -if the data at B are unpredictable to an adversary, this -increases the uncertainty about the state and makes the PRNG output -less predictable. Suitable input comes from user interaction (random -key presses, mouse movements) and certain hardware events. The -B argument is an estimate of how much randomness is contained in +Random numbers are a vital part of cryptography, including key generation, +creating salts, etc., and software-based +generators must be "seeded" with external randomness before they can be +used as a cryptographically-secure pseudo-random number generator (CSPRNG). +The availability of common hardware with special instructions and +modern operating systems, which may use items such as interrupt jitter +and network packet timings, can be reasonable sources of seeding material. + +RAND_status() indicates whether or not the CSPRNG has been sufficiently +seeded. If not, functions such as RAND_bytes(3) will fail. + +RAND_poll() uses the current capabilities to seed the CSPRNG. The +exact features used depends on how OpenSSL was configured, and can +be displayed with the OpenSSL L command. This function is +normally called automatically during OpenSSL initialization, but +can be called by the application to reseed the CSPRNG. + +RAND_add() mixes the B bytes at B into the PRNG state. +The B argument is an estimate of how much randomness is +contained in B, in bytes, and should be a number between zero and B. Details about sources of randomness and how to estimate their randomness -can be found in the literature; for example IETF RFC 4086. - -RAND_add() may be called with sensitive data such as user entered -passwords. The seed values cannot be recovered from the PRNG output. +can be found in the literature; for example NIST SP 800-90B. +The content of B cannot be recovered from subsequent CSPRNG output. +This function will not normally be needed, as RAND_poll() should have been +configured to do the appropriate seeding for the local platform. +Applications that need to keep random state in an external file should +use L. RAND_seed() is equivalent to RAND_add() with B set to B. -On systems that provide C or similar source of randomess, -it will be used -to seed the PRNG transparently. On older systems, however, it might -be necessary to use RAND_add(), L or L. - -RAND_event() and RAND_screen() are deprecated and should not be called. +RAND_event() and RAND_screen() are equivalent to RAND_poll(). =head1 RETURN VALUES -RAND_status() returns 1 if the PRNG has been seeded +RAND_status() returns 1 if the CSPRNG has been seeded with enough data, 0 otherwise. -RAND_event() calls RAND_poll() and returns RAND_status(). +RAND_poll() returns 1 if it generated seed data, 0 otherwise. -RAND_screen calls RAND_poll(). +RAND_event() returns RAND_status(). The other functions do not return values. =head1 HISTORY -RAND_event() and RAND_screen() are deprecated since OpenSSL -1.1.0. Use the functions described above instead. +RAND_event() and RAND_screen() were deprecated in OpenSSL 1.1.0 and should +not be used. =head1 SEE ALSO diff --git a/doc/man3/RAND_load_file.pod b/doc/man3/RAND_load_file.pod index 8907bdc77c..3cb7084862 100644 --- a/doc/man3/RAND_load_file.pod +++ b/doc/man3/RAND_load_file.pod @@ -20,8 +20,12 @@ RAND_load_file() reads a number of bytes from file B and adds them to the PRNG. If B is non-negative, up to B are read; if B is -1, the complete file is read. +Do not load the same file multiple times unless its contents have +been updated by RAND_write_file() between reads. +Also, note that B should be adequately protected so that an +attacker cannot replace or examine the contents. -RAND_write_file() writes a number of random bytes (currently 256) to +RAND_write_file() writes a number of random bytes (currently 128) to file B which can be used to initialize the PRNG by calling RAND_load_file() in a later session. @@ -56,8 +60,8 @@ B is too small for the path name, an error occurs. RAND_load_file() returns the number of bytes read. -RAND_write_file() returns the number of bytes written, and -1 if the -bytes written were generated without appropriate seed. +RAND_write_file() returns the number of bytes written, or -1 if the +bytes written were generated without appropriate seeding. RAND_file_name() returns a pointer to B on success, and NULL on error. -- 2.25.1