/* * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany */ #include "Randomizer.h" #include "SingletonHelper.h" #include #include #include #ifdef Q_OS_WIN #include #elif defined(Q_OS_UNIX) #if defined(Q_OS_Linux) && !defined(Q_OS_ANDROID) #include #endif #if defined(SYS_getrandom) #include #include #else #include #include #endif #if defined(Q_OS_IOS) || defined(Q_OS_MACOS) #include #endif #endif using namespace governikus; defineSingleton(Randomizer) template union UniversalBuffer { T number; U data[sizeof(T)] = {}; }; template QList Randomizer::getEntropy() { QList entropy; entropy += static_cast(std::chrono::system_clock::now().time_since_epoch().count()); entropy += std::random_device()(); entropy += static_cast(qrand()); entropy += QRandomGenerator::securelySeeded().generate(); UniversalBuffer buffer; if (RAND_bytes(buffer.data, sizeof(buffer.data))) { entropy += buffer.number; } entropy += getEntropyWin(); entropy += getEntropyUnixoid(); entropy += getEntropyApple(); return entropy; } template QList Randomizer::getEntropyWin() { QList entropy; #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) UniversalBuffer buffer; HCRYPTPROV provider = 0; if (CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { if (CryptGenRandom(provider, sizeof(buffer.data), buffer.data)) { entropy += buffer.number; } CryptReleaseContext(provider, 0); } #endif return entropy; } template QList Randomizer::getEntropyUnixoid() { QList entropy; #ifdef SYS_getrandom UniversalBuffer buffer; if (syscall(SYS_getrandom, buffer.data, sizeof(buffer.data), GRND_NONBLOCK)) { entropy += buffer.number; } #elif defined(Q_OS_UNIX) // Fallback for unixoid systems without SYS_getrandom (like linux before version 3.17 ). // This is mainly relevant for older androids. UniversalBuffer buffer; QFile file(QStringLiteral("/dev/urandom")); if (file.open(QIODevice::ReadOnly)) { qint64 bytesToRead = sizeof(buffer.data); do { const qint64 bytesRead = file.read(buffer.data, bytesToRead); if (bytesRead < 0) { qCritical() << "Failed to read" << file.fileName(); break; } bytesToRead -= bytesRead; } while (bytesToRead > 0); entropy += buffer.number; file.close(); } else { qCritical() << "Failed to open" << file.fileName(); } #endif return entropy; } template QList Randomizer::getEntropyApple() { QList entropy; #if defined(Q_OS_IOS) || defined(Q_OS_MACOS) UniversalBuffer buffer; if (SecRandomCopyBytes(kSecRandomDefault, sizeof(buffer.data), buffer.data) == 0) { entropy += buffer.number; } #endif return entropy; } Randomizer::Randomizer() { const auto& entropy = getEntropy(); std::seed_seq seed(entropy.cbegin(), entropy.cend()); mGenerator.seed(seed); // We need to seed pseudo random pool of openssl. // yes, OpenSSL is an entropy source, too. But not the only one! UniversalBuffer buffer; buffer.number = mGenerator(); RAND_seed(buffer.data, sizeof(std::mt19937::result_type)); static const int MINIMUM_ENTROPY_SOURCES = 5; mSecureRandom = seed.size() >= MINIMUM_ENTROPY_SOURCES; } Randomizer::~Randomizer() { } Randomizer& Randomizer::getInstance() { return *Instance; } std::mt19937& Randomizer::getGenerator() { return mGenerator; } bool Randomizer::isSecureRandom() const { return mSecureRandom; }