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