summaryrefslogtreecommitdiff
path: root/osrng.h
blob: f8b098b78f0abfc0af6046209edd87406e79bfa2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// osrng.h - originally written and placed in the public domain by Wei Dai

/// \file osrng.h
/// \brief Classes for access to the operating system's random number generators

#ifndef CRYPTOPP_OSRNG_H
#define CRYPTOPP_OSRNG_H

#include "config.h"

#if !defined(NO_OS_DEPENDENCE) && defined(OS_RNG_AVAILABLE)

#include "cryptlib.h"
#include "randpool.h"
#include "smartptr.h"
#include "fips140.h"
#include "hkdf.h"
#include "rng.h"
#include "aes.h"
#include "sha.h"

NAMESPACE_BEGIN(CryptoPP)

/// \brief Exception thrown when an operating system error is encountered
class CRYPTOPP_DLL OS_RNG_Err : public Exception
{
public:
	/// \brief Constructs an OS_RNG_Err
	/// \param operation the operation or API call when the error occurs
	OS_RNG_Err(const std::string &operation);
};

#ifdef NONBLOCKING_RNG_AVAILABLE

#ifdef CRYPTOPP_WIN32_AVAILABLE
/// \brief Wrapper for Microsoft crypto service provider
/// \sa \def USE_MS_CRYPTOAPI, \def USE_MS_CNGAPI
class CRYPTOPP_DLL MicrosoftCryptoProvider
{
public:
	/// \brief Construct a MicrosoftCryptoProvider
	MicrosoftCryptoProvider();
	~MicrosoftCryptoProvider();

// type HCRYPTPROV and BCRYPT_ALG_HANDLE, avoid #include <windows.h>
#if defined(USE_MS_CRYPTOAPI)
# if defined(__CYGWIN__) && defined(__x86_64__)
	typedef unsigned long long ProviderHandle;
# elif defined(WIN64) || defined(_WIN64)
	typedef unsigned __int64 ProviderHandle;
# else
	typedef unsigned long ProviderHandle;
# endif
#elif defined(USE_MS_CNGAPI)
	typedef void *PVOID;
	typedef PVOID ProviderHandle;
#endif // USE_MS_CRYPTOAPI or USE_MS_CNGAPI

	/// \brief Retrieves the provider handle
	/// \return CryptoAPI provider handle
	/// \details If USE_MS_CRYPTOAPI is in effect, then CryptAcquireContext()
	///  acquires then handle and CryptReleaseContext() releases the handle
	///  upon destruction. If USE_MS_CNGAPI is in effect, then
	///  BCryptOpenAlgorithmProvider() acquires then handle and
	///  BCryptCloseAlgorithmProvider() releases the handle upon destruction.
	ProviderHandle GetProviderHandle() const {return m_hProvider;}

private:
	ProviderHandle m_hProvider;
};

#if defined(_MSC_VER) && defined(USE_MS_CRYPTOAPI)
# pragma comment(lib, "advapi32.lib")
#endif

#if defined(_MSC_VER) && defined(USE_MS_CNGAPI)
# pragma comment(lib, "bcrypt.lib")
#endif

#endif // CRYPTOPP_WIN32_AVAILABLE

/// \brief Wrapper class for /dev/random and /dev/srandom
/// \details Encapsulates CryptoAPI's CryptGenRandom() or CryptoNG's BCryptGenRandom()
///  on Windows, or /dev/urandom on Unix and compatibles.
class CRYPTOPP_DLL NonblockingRng : public RandomNumberGenerator
{
public:
	CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() { return "NonblockingRng"; }

	~NonblockingRng();

	/// \brief Construct a NonblockingRng
	NonblockingRng();

	/// \brief Generate random array of bytes
	/// \param output the byte buffer
	/// \param size the length of the buffer, in bytes
	/// \details GenerateIntoBufferedTransformation() calls are routed to GenerateBlock().
	void GenerateBlock(byte *output, size_t size);

protected:
#ifdef CRYPTOPP_WIN32_AVAILABLE
	MicrosoftCryptoProvider m_Provider;
#else
	int m_fd;
#endif
};

#endif

#if defined(BLOCKING_RNG_AVAILABLE) || defined(CRYPTOPP_DOXYGEN_PROCESSING)

/// \brief Wrapper class for /dev/random and /dev/srandom
/// \details Encapsulates /dev/random on Linux, OS X and Unix; and /dev/srandom on the BSDs.
/// \note On Linux the /dev/random interface is effectively deprecated. According to the
///  Kernel Crypto developers, /dev/urandom or getrandom(2) should be used instead. Also
///  see <A HREF="https://lkml.org/lkml/2017/7/20/993">[RFC PATCH v12 3/4] Linux Random
///  Number Generator</A> on the kernel-crypto mailing list.
class CRYPTOPP_DLL BlockingRng : public RandomNumberGenerator
{
public:
	CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() { return "BlockingRng"; }

	~BlockingRng();

	/// \brief Construct a BlockingRng
	BlockingRng();

	/// \brief Generate random array of bytes
	/// \param output the byte buffer
	/// \param size the length of the buffer, in bytes
	/// \details GenerateIntoBufferedTransformation() calls are routed to GenerateBlock().
	void GenerateBlock(byte *output, size_t size);

protected:
	int m_fd;
};

#endif

/// OS_GenerateRandomBlock
/// \brief Generate random array of bytes
/// \param blocking specifies whether a blocking or non-blocking generator should be used
/// \param output the byte buffer
/// \param size the length of the buffer, in bytes
/// \details OS_GenerateRandomBlock() uses the underlying operating system's
///  random number generator. On Windows, CryptGenRandom() is called using NonblockingRng.
/// \details On Unix and compatibles, /dev/urandom is called if blocking is false using
///  NonblockingRng. If blocking is true, then either /dev/randomd or /dev/srandom is used
///  by way of BlockingRng, if available.
CRYPTOPP_DLL void CRYPTOPP_API OS_GenerateRandomBlock(bool blocking, byte *output, size_t size);

/// \brief Automatically Seeded Randomness Pool
/// \details This class seeds itself using an operating system provided RNG.
///  AutoSeededRandomPool was suggested by Leonard Janke.
/// \details You should reseed the generator after a fork() to avoid multiple generators
///  with the same internal state.
class CRYPTOPP_DLL AutoSeededRandomPool : public RandomPool
{
public:
	CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() { return "AutoSeededRandomPool"; }

	~AutoSeededRandomPool() {}

	/// \brief Construct an AutoSeededRandomPool
	/// \param blocking controls seeding with BlockingRng or NonblockingRng
	/// \param seedSize the size of the seed, in bytes
	/// \details Use blocking to choose seeding with BlockingRng or NonblockingRng.
	///  The parameter is ignored if only one of these is available.
	explicit AutoSeededRandomPool(bool blocking = false, unsigned int seedSize = 32)
		{Reseed(blocking, seedSize);}

	/// \brief Reseed an AutoSeededRandomPool
	/// \param blocking controls seeding with BlockingRng or NonblockingRng
	/// \param seedSize the size of the seed, in bytes
	void Reseed(bool blocking = false, unsigned int seedSize = 32);
};

/// \tparam BLOCK_CIPHER a block cipher
/// \brief Automatically Seeded X9.17 RNG
/// \details AutoSeededX917RNG is from ANSI X9.17 Appendix C, seeded using an OS provided RNG.
///  If 3-key TripleDES (DES_EDE3) is used, then its a X9.17 conforming generator. If AES is
///  used, then its a X9.31 conforming generator.
/// \details Though ANSI X9 prescribes 3-key TripleDES, the template parameter BLOCK_CIPHER
///  can be any BlockTransformation derived class.
/// \details You should reseed the generator after a fork() to avoid multiple generators
///  with the same internal state.
/// \sa X917RNG, DefaultAutoSeededRNG
template <class BLOCK_CIPHER>
class AutoSeededX917RNG : public RandomNumberGenerator, public NotCopyable
{
public:
	static std::string StaticAlgorithmName() {
		return std::string("AutoSeededX917RNG(") + BLOCK_CIPHER::StaticAlgorithmName() + std::string(")");
	}

	~AutoSeededX917RNG() {}

	/// \brief Construct an AutoSeededX917RNG
	/// \param blocking controls seeding with BlockingRng or NonblockingRng
	/// \param autoSeed controls auto seeding of the generator
	/// \details Use blocking to choose seeding with BlockingRng or NonblockingRng.
	///  The parameter is ignored if only one of these is available.
	/// \sa X917RNG
	explicit AutoSeededX917RNG(bool blocking = false, bool autoSeed = true)
		{if (autoSeed) Reseed(blocking);}

	/// \brief Reseed an AutoSeededX917RNG
	/// \param blocking controls seeding with BlockingRng or NonblockingRng
	/// \param input additional entropy to add to the generator
	/// \param length the size of the additional entropy, in bytes
	/// \details Internally, the generator uses SHA256 to extract the entropy from
	///  from the seed and then stretch the material for the block cipher's key
	///  and initialization vector.
	void Reseed(bool blocking = false, const byte *input = NULLPTR, size_t length = 0);

	/// \brief Deterministically reseed an AutoSeededX917RNG for testing
	/// \param key the key to use for the deterministic reseeding
	/// \param keylength the size of the key, in bytes
	/// \param seed the seed to use for the deterministic reseeding
	/// \param timeVector a time vector to use for deterministic reseeding
	/// \details This is a testing interface for testing purposes, and should \a NOT
	///  be used in production.
	void Reseed(const byte *key, size_t keylength, const byte *seed, const byte *timeVector);

	bool CanIncorporateEntropy() const {return true;}
	void IncorporateEntropy(const byte *input, size_t length) {Reseed(false, input, length);}
	void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword length)
		{m_rng->GenerateIntoBufferedTransformation(target, channel, length);}

	std::string AlgorithmProvider() const;

private:
	member_ptr<RandomNumberGenerator> m_rng;
};

template <class BLOCK_CIPHER>
void AutoSeededX917RNG<BLOCK_CIPHER>::Reseed(const byte *key, size_t keylength, const byte *seed, const byte *timeVector)
{
	m_rng.reset(new X917RNG(new typename BLOCK_CIPHER::Encryption(key, keylength), seed, timeVector));
}

template <class BLOCK_CIPHER>
void AutoSeededX917RNG<BLOCK_CIPHER>::Reseed(bool blocking, const byte *input, size_t length)
{
	enum {BlockSize=BLOCK_CIPHER::BLOCKSIZE};
	enum {KeyLength=BLOCK_CIPHER::DEFAULT_KEYLENGTH};
	enum {SeedSize=EnumToInt(BlockSize)+EnumToInt(KeyLength)};

	SecByteBlock seed(SeedSize), temp(SeedSize);
	const byte label[] = "X9.17 key generation";
	const byte *key=NULLPTR;

	do
	{
		OS_GenerateRandomBlock(blocking, temp, temp.size());

		HKDF<SHA256> hkdf;
		hkdf.DeriveKey(
			seed, seed.size(),  // derived secret
			temp, temp.size(),  // instance secret
			input, length,      // user secret
			label, 20           // unique label
		);

		key = seed + BlockSize;
	}	// check that seed and key don't have same value
	while (std::memcmp(key, seed, STDMIN((size_t)BlockSize, (size_t)KeyLength)) == 0);

	Reseed(key, KeyLength, seed, NULLPTR);
}

template <class BLOCK_CIPHER>
std::string AutoSeededX917RNG<BLOCK_CIPHER>::AlgorithmProvider() const
{
	// Hack for now... We need to instantiate one
	typename BLOCK_CIPHER::Encryption bc;
	return bc.AlgorithmProvider();
}

CRYPTOPP_DLL_TEMPLATE_CLASS AutoSeededX917RNG<AES>;

#if defined(CRYPTOPP_DOXYGEN_PROCESSING)
/// \brief A typedef providing a default generator
/// \details DefaultAutoSeededRNG is a typedef of either AutoSeededX917RNG<AES> or AutoSeededRandomPool.
///  If CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 is defined, then DefaultAutoSeededRNG is
///  AutoSeededX917RNG<AES>. Otherwise, DefaultAutoSeededRNG is AutoSeededRandomPool.
/// \details You should reseed the generator after a fork() to avoid multiple generators
///  with the same internal state.
class DefaultAutoSeededRNG {}
#else
// AutoSeededX917RNG<AES> in FIPS mode, otherwise it's AutoSeededRandomPool
#if CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2
typedef AutoSeededX917RNG<AES> DefaultAutoSeededRNG;
#else
typedef AutoSeededRandomPool DefaultAutoSeededRNG;
#endif
#endif // CRYPTOPP_DOXYGEN_PROCESSING

NAMESPACE_END

#endif

#endif