QRandomGenerator: add support for hardware-based random generators

This commit adds support for the x86 RDRAND instruction for
QRandomGenerator. This is the same that libstdc++-v3 uses for
std::random_device() by default. If it fails because the hardware does
not have enough entropy collected, we fall back to the operating system
generator, which often has more entropy collected from other sources.

Change-Id: Icd0e0d4b27cb4e5eb892fffd14b5167214e1ea3f
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
bb10
Thiago Macieira 2017-04-13 15:45:30 -07:00
parent dfdc466dc1
commit 0c355e78f0
1 changed files with 46 additions and 2 deletions

View File

@ -44,6 +44,7 @@
#include "qrandom_p.h"
#include <qobjectdefs.h>
#include <qthreadstorage.h>
#include <private/qsimd_p.h>
#if QT_HAS_INCLUDE(<random>)
# include <random>
@ -70,6 +71,37 @@ DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG Rando
QT_BEGIN_NAMESPACE
#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
static qssize_t qt_random_cpu(void *buffer, qssize_t count);
# ifdef Q_PROCESSOR_X86_64
# define _rdrandXX_step _rdrand64_step
# else
# define _rdrandXX_step _rdrand32_step
# endif
static QT_FUNCTION_TARGET(RDRND) qssize_t qt_random_cpu(void *buffer, qssize_t count)
{
unsigned *ptr = reinterpret_cast<unsigned *>(buffer);
unsigned *end = ptr + count;
while (ptr + sizeof(qregisteruint)/sizeof(*ptr) <= end) {
if (_rdrandXX_step(reinterpret_cast<qregisteruint *>(ptr)) == 0)
goto out;
ptr += sizeof(qregisteruint)/sizeof(*ptr);
}
if (sizeof(*ptr) != sizeof(qregisteruint) && ptr != end) {
if (_rdrand32_step(ptr))
goto out;
++ptr;
}
out:
return ptr - reinterpret_cast<unsigned *>(buffer);
}
#endif
namespace {
#ifdef Q_OS_UNIX
class SystemRandom
@ -196,6 +228,18 @@ static Q_NORETURN void fallback_fill(quint32 *, qssize_t)
}
#endif
static qssize_t fill_cpu(quint32 *buffer, qssize_t count)
{
#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
if (qCpuHasFeature(RDRND) && (uint(qt_randomdevice_control) & SkipHWRNG) == 0)
return qt_random_cpu(buffer, count);
#else
Q_UNUSED(buffer);
Q_UNUSED(count);
#endif
return 0;
}
static void fill_internal(quint32 *buffer, qssize_t count)
{
if (Q_UNLIKELY(uint(qt_randomdevice_control) & SetRandomData)) {
@ -204,8 +248,8 @@ static void fill_internal(quint32 *buffer, qssize_t count)
return;
}
qssize_t filled = 0;
if (uint(qt_randomdevice_control) & SkipSystemRNG) == 0) {
qssize_t filled = fill_cpu(buffer, count);
if (filled != count && (uint(qt_randomdevice_control) & SkipSystemRNG) == 0) {
qssize_t bytesFilled =
SystemRandom::fillBuffer(buffer + filled, (count - filled) * qssize_t(sizeof(*buffer)));
filled += bytesFilled / qssize_t(sizeof(*buffer));