AusweisApp2/src/global/Randomizer.cpp

186 lines
3.7 KiB
C++

/*
* \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany
*/
#include "Randomizer.h"
#include "SingletonHelper.h"
#include <chrono>
#include <openssl/rand.h>
#include <QRandomGenerator>
#ifdef Q_OS_WIN
#include <windows.h>
#elif defined(Q_OS_UNIX)
#if defined(Q_OS_Linux) && !defined(Q_OS_ANDROID)
#include <syscall.h>
#endif
#if defined(SYS_getrandom)
#include <linux/random.h>
#include <unistd.h>
#else
#include <QDebug>
#include <QFile>
#endif
#if defined(Q_OS_IOS) || defined(Q_OS_MACOS)
#include <Security/SecRandom.h>
#endif
#endif
using namespace governikus;
defineSingleton(Randomizer)
template<typename T, typename U = uchar> union UniversalBuffer
{
T number;
U data[sizeof(T)] = {};
};
template<typename T> QList<T> Randomizer::getEntropy()
{
QList<T> entropy;
entropy += static_cast<T>(std::chrono::system_clock::now().time_since_epoch().count());
entropy += std::random_device()();
entropy += static_cast<T>(qrand());
entropy += QRandomGenerator::securelySeeded().generate();
UniversalBuffer<T> buffer;
if (RAND_bytes(buffer.data, sizeof(buffer.data)))
{
entropy += buffer.number;
}
entropy += getEntropyWin<T>();
entropy += getEntropyUnixoid<T>();
entropy += getEntropyApple<T>();
return entropy;
}
template<typename T> QList<T> Randomizer::getEntropyWin()
{
QList<T> entropy;
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
UniversalBuffer<T> 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<typename T> QList<T> Randomizer::getEntropyUnixoid()
{
QList<T> entropy;
#ifdef SYS_getrandom
UniversalBuffer<T> 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<T, char> 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<typename T> QList<T> Randomizer::getEntropyApple()
{
QList<T> entropy;
#if defined(Q_OS_IOS) || defined(Q_OS_MACOS)
UniversalBuffer<T> buffer;
if (SecRandomCopyBytes(kSecRandomDefault, sizeof(buffer.data), buffer.data) == 0)
{
entropy += buffer.number;
}
#endif
return entropy;
}
Randomizer::Randomizer()
{
const auto& entropy = getEntropy<std::mt19937::result_type>();
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<std::mt19937::result_type> 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;
}