X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=crypto%2Frand%2Frand_unix.c;h=beb35a30a4548ae7b6f94c32e9182f52ee9a214b;hp=ecba2dc9841424ad26d85fbcbb446c858db287f9;hb=560096f804a3712eea161726a8f085beefe8838a;hpb=3ce2fdabe6e33952bf3011acf5b68107e6352603 diff --git a/crypto/rand/rand_unix.c b/crypto/rand/rand_unix.c index ecba2dc984..beb35a30a4 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-2018 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 @@ -7,91 +7,112 @@ * https://www.openssl.org/source/license.html */ -#include - -#define USE_SOCKETS #include "e_os.h" +#include #include "internal/cryptlib.h" #include #include "rand_lcl.h" - -#if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI)) - +#include "internal/rand_int.h" +#include +#ifdef OPENSSL_SYS_UNIX # 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)) +# include + +static uint64_t get_time_stamp(void); +static uint64_t get_timer_bits(void); + +/* 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 +#endif + +#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_WINDOWS) || defined(OPENSSL_SYS_WIN32) \ + || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) \ + || defined(OPENSSL_SYS_UEFI)) # 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 * uneven execution speed of the code (due to factors such as cache misses, * interrupts, bus activity, and scheduling) and upon the rather large * relative difference between the speed of the clock and the rate at which - * it can be read. + * it can be read. If it is ported to an environment where execution speed + * is more constant or where the RTC ticks at a much slower rate, or the + * clock can be read with fewer instructions, it is likely that the results + * would be far more predictable. This should only be used for legacy + * platforms. * - * If this code is ported to an environment where execution speed is more - * constant or where the RTC ticks at a much slower rate, or the clock can be - * read with fewer instructions, it is likely that the results would be far - * more predictable. - * - * As a precaution, we generate 4 times the minimum required amount of seed - * data. + * As a precaution, we assume only 2 bits of entropy per byte. */ - -int RAND_poll(void) +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 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 */ - - /* - * 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; - - curr_pid = getpid(); - RAND_add(&curr_pid, sizeof curr_pid, 1); - curr_pid = 0; +# endif - curr_uid = getuid(); - RAND_add(&curr_uid, sizeof curr_uid, 1); - curr_uid = 0; + bytes_needed = rand_pool_bytes_needed(pool, 2 /*entropy_per_byte*/); - for (i = 0; i < (ENTROPY_NEEDED * 4); i++) { + for (i = 0; i < bytes_needed; i++) { /* * burn some cpu; hope for interrupts, cache collisions, bus * interference, etc. @@ -104,221 +125,287 @@ 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); - RAND_add(&v, sizeof v, 1); - v = 0; + v = (unsigned char)(ts.tv_nsec & 0xFF); + rand_pool_add(pool, arg, &v, sizeof(v) , 2); } - return 1; + return rand_pool_entropy_available(pool); } -# elif defined __OpenBSD__ -int RAND_poll(void) -{ - u_int32_t rnd = 0, i; - unsigned char buf[ENTROPY_NEEDED]; - - for (i = 0; i < sizeof(buf); i++) { - if (i % 4 == 0) - rnd = arc4random(); - buf[i] = rnd; - rnd >>= 8; - } - RAND_add(buf, sizeof(buf), ENTROPY_NEEDED); - OPENSSL_cleanse(buf, sizeof(buf)); - return 1; -} -# else /* !defined(__OpenBSD__) */ -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[ENTROPY_NEEDED]; - int n = 0; +# else + +# 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 - static const char *randomfiles[] = { DEVRANDOM }; - struct stat randomstats[OSSL_NELEM(randomfiles)]; - int fd; - unsigned int i; + +# if defined(OPENSSL_RAND_SEED_DEVRANDOM) && !defined(DEVRANDOM) +# error "Seeding uses urandom but DEVRANDOM is not configured" # 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_OS) +# if !defined(DEVRANDOM) +# error "OS seeding requires DEVRANDOM to be configured" +# endif +# define OPENSSL_RAND_SEED_DEVRANDOM +# if defined(__GLIBC__) && defined(__GLIBC_PREREQ) +# if __GLIBC_PREREQ(2, 25) +# define OPENSSL_RAND_SEED_GETRANDOM +# endif +# endif # endif -# ifdef DEVRANDOM - memset(randomstats, 0, sizeof(randomstats)); - /* - * Use a random entropy pool 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. - */ +# ifdef OPENSSL_RAND_SEED_GETRANDOM +# include +# endif - for (i = 0; (i < OSSL_NELEM(randomfiles)) && (n < ENTROPY_NEEDED); i++) { - if ((fd = open(randomfiles[i], O_RDONLY -# ifdef O_NONBLOCK - | O_NONBLOCK -# endif -# ifdef O_BINARY - | O_BINARY +# if defined(OPENSSL_RAND_SEED_LIBRANDOM) +# error "librandom not (yet) supported" +# endif + +/* + * 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). + */ +size_t rand_pool_acquire_entropy(RAND_POOL *pool) +{ +# ifdef OPENSSL_RAND_SEED_NONE + return rand_pool_entropy_available(pool); +# else + size_t bytes_needed; + size_t entropy_available = 0; + unsigned char *buffer; + +# ifdef OPENSSL_RAND_SEED_GETRANDOM + 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 (getrandom(buffer, bytes_needed, 0) == (int)bytes_needed) + bytes = bytes_needed; + + rand_pool_add_end(pool, bytes, 8 * bytes); + entropy_available = rand_pool_entropy_available(pool); + } + if (entropy_available > 0) + return entropy_available; # 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_LIBRANDOM) + { + /* Not yet implemented. */ + } # 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); + +# 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; + + for (i = 0; paths[i] != NULL; i++) { + if ((fp = fopen(paths[i], "rb")) == NULL) continue; + setbuf(fp, NULL); + 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; + + rand_pool_add_end(pool, bytes, 8 * bytes); + entropy_available = rand_pool_entropy_available(pool); } + fclose(fp); + if (entropy_available > 0) + return entropy_available; + + bytes_needed = rand_pool_bytes_needed(pool, 8 /*entropy_per_byte*/); + } + } +# endif - 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; - -# else - /* use select() */ - fd_set fset; - struct timeval t; - - t.tv_sec = 0; - t.tv_usec = usec; - - 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); - - 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_RDTSC + entropy_available = rand_acquire_entropy_from_tsc(pool); + if (entropy_available > 0) + return entropy_available; # endif - if (try_read) { - r = read(fd, (unsigned char *)tmpbuf + n, - ENTROPY_NEEDED - n); - if (r > 0) - n += r; - } else - r = -1; - - /* - * 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; - } - while ((r > 0 || - (errno == EINTR || errno == EAGAIN)) && usec != 0 - && n < ENTROPY_NEEDED); +# ifdef OPENSSL_RAND_SEED_RDCPU + entropy_available = rand_acquire_entropy_from_cpu(pool); + if (entropy_available > 0) + return entropy_available; +# endif - close(fd); +# 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++) { + 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; + + rand_pool_add_end(pool, bytes, 8 * bytes); + entropy_available = rand_pool_entropy_available(pool); + } + if (entropy_available > 0) + return entropy_available; } } -# endif /* defined(DEVRANDOM) */ +# endif + + return rand_pool_entropy_available(pool); +# endif +} +# endif +#endif + +#ifdef OPENSSL_SYS_UNIX +int rand_pool_add_nonce_data(RAND_POOL *pool) +{ + struct { + pid_t pid; + CRYPTO_THREAD_ID tid; + uint64_t time; + } data = { 0 }; -# if !defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD) /* - * Use an EGD socket to read entropy from an EGD or PRNGD entropy - * collecting daemon. + * Add process id, thread id, and a high resolution timestamp to + * ensure that the nonce is unique whith high probability for + * different process instances. */ + data.pid = getpid(); + data.tid = CRYPTO_THREAD_get_current_id(); + data.time = get_time_stamp(); - for (egdsocket = egdsockets; *egdsocket && n < ENTROPY_NEEDED; - egdsocket++) { - int r; + return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0); +} - r = RAND_query_egd_bytes(*egdsocket, (unsigned char *)tmpbuf + n, - ENTROPY_NEEDED - n); - if (r > 0) - n += r; +int rand_pool_add_additional_data(RAND_POOL *pool) +{ + struct { + CRYPTO_THREAD_ID tid; + uint64_t time; + } data = { 0 }; + + /* + * Add some noise from the thread id and a high resolution timer. + * The thread id adds a little randomness if the drbg is accessed + * concurrently (which is the case for the drbg). + */ + data.tid = CRYPTO_THREAD_get_current_id(); + data.time = get_timer_bits(); + + return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0); +} + + + +/* + * Get the current time with the highest possible resolution + * + * The time stamp is added to the nonce, so it is optimized for not repeating. + * The current time is ideal for this purpose, provided the computer's clock + * is synchronized. + */ +static uint64_t get_time_stamp(void) +{ +# if defined(OSSL_POSIX_TIMER_OKAY) + { + struct timespec ts; + + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) + return TWO32TO64(ts.tv_sec, ts.tv_nsec); } -# endif /* defined(DEVRANDOM_EGD) */ +# endif +# if defined(__unix__) \ + || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) + { + struct timeval tv; -# if defined(DEVRANDOM) || (!defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD)) - if (n > 0) { - RAND_add(tmpbuf, sizeof tmpbuf, (double)n); - OPENSSL_cleanse(tmpbuf, n); + if (gettimeofday(&tv, NULL) == 0) + return TWO32TO64(tv.tv_sec, tv.tv_usec); } -# endif +# endif + return time(NULL); +} + +/* + * Get an arbitrary timer value of the highest possible resolution + * + * The timer value is added as random noise to the additional data, + * which is not considered a trusted entropy sourec, so any result + * is acceptable. + */ +static uint64_t get_timer_bits(void) +{ + uint64_t res = OPENSSL_rdtsc(); - /* 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); + if (res != 0) + return res; - l = time(NULL); - RAND_add(&l, sizeof(l), 0.0); +# if defined(__sun) || defined(__hpux) + return gethrtime(); +# elif defined(_AIX) + { + timebasestruct_t t; -# if defined(DEVRANDOM) || (!defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD)) - return 1; + read_wall_time(&t, TIMEBASE_SZ); + return TWO32TO64(t.tb_high, t.tb_low); + } +# elif defined(OSSL_POSIX_TIMER_OKAY) + { + struct timespec ts; + +# ifdef CLOCK_BOOTTIME +# define CLOCK_TYPE CLOCK_BOOTTIME +# elif defined(_POSIX_MONOTONIC_CLOCK) +# define CLOCK_TYPE CLOCK_MONOTONIC # else - return 0; +# define CLOCK_TYPE CLOCK_REALTIME # endif -} -# endif /* defined(__OpenBSD__) */ -#endif /* !(defined(OPENSSL_SYS_WINDOWS) || - * defined(OPENSSL_SYS_WIN32) || - * defined(OPENSSL_SYS_VMS) || - * defined(OPENSSL_SYS_VXWORKS) */ + if (clock_gettime(CLOCK_TYPE, &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 defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI) -int RAND_poll(void) -{ - return 0; + if (gettimeofday(&tv, NULL) == 0) + return TWO32TO64(tv.tv_sec, tv.tv_usec); + } +# endif + return time(NULL); } #endif