1 /*
2  * \copyright Copyright (c) 2015-2021 Governikus GmbH & Co. KG, Germany
3  */
4 
5 #include "Randomizer.h"
6 
7 #include "SingletonHelper.h"
8 
9 #include <chrono>
10 #include <openssl/rand.h>
11 #include <QRandomGenerator>
12 
13 #ifdef Q_OS_WIN
14 	#include <windows.h>
15 #elif defined(Q_OS_UNIX)
16 	#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
17 		#include <syscall.h>
18 	#endif
19 	#if defined(SYS_getrandom)
20 		#include <linux/random.h>
21 		#include <unistd.h>
22 	#else
23 		#include <QDebug>
24 		#include <QFile>
25 	#endif
26 	#if defined(Q_OS_IOS) || defined(Q_OS_MACOS)
27 		#include <Security/SecRandom.h>
28 	#endif
29 #endif
30 
31 
32 using namespace governikus;
33 
34 defineSingleton(Randomizer)
35 
36 
37 template<typename T, typename U = uchar> union UniversalBuffer
38 {
39 	T number;
40 	U data[sizeof(T)] = {};
41 };
42 
getEntropy()43 template<typename T> QList<T> Randomizer::getEntropy()
44 {
45 	QList<T> entropy;
46 
47 	entropy += static_cast<T>(std::chrono::system_clock::now().time_since_epoch().count());
48 	entropy += std::random_device()();
49 	entropy += QRandomGenerator::securelySeeded().generate();
50 
51 	if (UniversalBuffer<T> buffer; RAND_bytes(buffer.data, sizeof(buffer.data)))
52 	{
53 		entropy += buffer.number;
54 	}
55 
56 	entropy += getEntropyWin<T>();
57 	entropy += getEntropyUnixoid<T>();
58 	entropy += getEntropyApple<T>();
59 
60 	return entropy;
61 }
62 
63 
getEntropyWin()64 template<typename T> QList<T> Randomizer::getEntropyWin()
65 {
66 	QList<T> entropy;
67 
68 #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
69 	UniversalBuffer<T> buffer;
70 	HCRYPTPROV provider = 0;
71 	if (CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
72 	{
73 		if (CryptGenRandom(provider, sizeof(buffer.data), buffer.data))
74 		{
75 			entropy += buffer.number;
76 		}
77 
78 		CryptReleaseContext(provider, 0);
79 	}
80 #endif
81 
82 	return entropy;
83 }
84 
85 
getEntropyUnixoid()86 template<typename T> QList<T> Randomizer::getEntropyUnixoid()
87 {
88 	QList<T> entropy;
89 
90 #ifdef SYS_getrandom
91 	if (UniversalBuffer<T> buffer; syscall(SYS_getrandom, buffer.data, sizeof(buffer.data), GRND_NONBLOCK))
92 	{
93 		entropy += buffer.number;
94 	}
95 #elif defined(Q_OS_UNIX)
96 	// Fallback for unixoid systems without SYS_getrandom (like linux before version 3.17 ).
97 	// This is mainly relevant for older androids.
98 
99 	UniversalBuffer<T, char> buffer;
100 	QFile file(QStringLiteral("/dev/urandom"));
101 	if (file.open(QIODevice::ReadOnly))
102 	{
103 		qint64 bytesToRead = sizeof(buffer.data);
104 		do
105 		{
106 			const qint64 bytesRead = file.read(buffer.data, bytesToRead);
107 			if (bytesRead < 0)
108 			{
109 				qCritical() << "Failed to read" << file.fileName();
110 				break;
111 			}
112 			bytesToRead -= bytesRead;
113 		}
114 		while (bytesToRead > 0);
115 
116 		entropy += buffer.number;
117 		file.close();
118 	}
119 	else
120 	{
121 		qCritical() << "Failed to open" << file.fileName();
122 	}
123 #endif
124 
125 	return entropy;
126 }
127 
128 
getEntropyApple()129 template<typename T> QList<T> Randomizer::getEntropyApple()
130 {
131 	QList<T> entropy;
132 
133 #if defined(Q_OS_IOS) || defined(Q_OS_MACOS)
134 	if (UniversalBuffer<T> buffer; SecRandomCopyBytes(kSecRandomDefault, sizeof(buffer.data), buffer.data) == 0)
135 	{
136 		entropy += buffer.number;
137 	}
138 #endif
139 
140 	return entropy;
141 }
142 
143 
Randomizer()144 Randomizer::Randomizer()
145 {
146 	const auto& entropy = getEntropy<std::mt19937::result_type>();
147 	std::seed_seq seed(entropy.cbegin(), entropy.cend());
148 	mGenerator.seed(seed);
149 
150 	// We need to seed pseudo random pool of openssl.
151 	// yes, OpenSSL is an entropy source, too. But not the only one!
152 	UniversalBuffer<std::mt19937::result_type> buffer;
153 	buffer.number = mGenerator();
154 	RAND_seed(buffer.data, sizeof(std::mt19937::result_type));
155 
156 	static const int MINIMUM_ENTROPY_SOURCES = 4;
157 	mSecureRandom = seed.size() >= MINIMUM_ENTROPY_SOURCES;
158 }
159 
160 
~Randomizer()161 Randomizer::~Randomizer()
162 {
163 }
164 
165 
getGenerator()166 std::mt19937& Randomizer::getGenerator()
167 {
168 	return mGenerator;
169 }
170 
171 
isSecureRandom() const172 bool Randomizer::isSecureRandom() const
173 {
174 	return mSecureRandom;
175 }
176