1// osrng.cpp - written and placed in the public domain by Wei Dai
2
3// Thanks to Leonard Janke for the suggestion for AutoSeededRandomPool.
4
5#include "pch.h"
6
7#ifndef CRYPTOPP_IMPORTS
8
9#include "osrng.h"
10
11#ifdef OS_RNG_AVAILABLE
12
13#include "rng.h"
14
15#ifdef CRYPTOPP_WIN32_AVAILABLE
16#ifndef _WIN32_WINNT
17#define _WIN32_WINNT 0x0400
18#endif
19#include <windows.h>
20#include <wincrypt.h>
21#endif
22
23#ifdef CRYPTOPP_UNIX_AVAILABLE
24#include <errno.h>
25#include <fcntl.h>
26#include <unistd.h>
27#endif
28
29NAMESPACE_BEGIN(CryptoPP)
30
31#if defined(NONBLOCKING_RNG_AVAILABLE) || defined(BLOCKING_RNG_AVAILABLE)
32OS_RNG_Err::OS_RNG_Err(const std::string &operation)
33	: Exception(OTHER_ERROR, "OS_Rng: " + operation + " operation failed with error " +
34#ifdef CRYPTOPP_WIN32_AVAILABLE
35		"0x" + IntToString(GetLastError(), 16)
36#else
37		IntToString(errno)
38#endif
39		)
40{
41}
42#endif
43
44#ifdef NONBLOCKING_RNG_AVAILABLE
45
46#ifdef CRYPTOPP_WIN32_AVAILABLE
47
48MicrosoftCryptoProvider::MicrosoftCryptoProvider()
49{
50	if(!CryptAcquireContext(&m_hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
51		throw OS_RNG_Err("CryptAcquireContext");
52}
53
54MicrosoftCryptoProvider::~MicrosoftCryptoProvider()
55{
56	CryptReleaseContext(m_hProvider, 0);
57}
58
59#endif
60
61NonblockingRng::NonblockingRng()
62{
63#ifndef CRYPTOPP_WIN32_AVAILABLE
64	m_fd = open("/dev/urandom",O_RDONLY);
65	if (m_fd == -1)
66		throw OS_RNG_Err("open /dev/urandom");
67#endif
68}
69
70NonblockingRng::~NonblockingRng()
71{
72#ifndef CRYPTOPP_WIN32_AVAILABLE
73	close(m_fd);
74#endif
75}
76
77void NonblockingRng::GenerateBlock(byte *output, size_t size)
78{
79#ifdef CRYPTOPP_WIN32_AVAILABLE
80#	ifdef WORKAROUND_MS_BUG_Q258000
81		const MicrosoftCryptoProvider &m_Provider = Singleton<MicrosoftCryptoProvider>().Ref();
82#	endif
83	if (!CryptGenRandom(m_Provider.GetProviderHandle(), (DWORD)size, output))
84		throw OS_RNG_Err("CryptGenRandom");
85#else
86	if (read(m_fd, output, size) != size)
87		throw OS_RNG_Err("read /dev/urandom");
88#endif
89}
90
91#endif
92
93// *************************************************************
94
95#ifdef BLOCKING_RNG_AVAILABLE
96
97#ifndef CRYPTOPP_BLOCKING_RNG_FILENAME
98#ifdef __OpenBSD__
99#define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/srandom"
100#else
101#define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/random"
102#endif
103#endif
104
105BlockingRng::BlockingRng()
106{
107	m_fd = open(CRYPTOPP_BLOCKING_RNG_FILENAME,O_RDONLY);
108	if (m_fd == -1)
109		throw OS_RNG_Err("open " CRYPTOPP_BLOCKING_RNG_FILENAME);
110}
111
112BlockingRng::~BlockingRng()
113{
114	close(m_fd);
115}
116
117void BlockingRng::GenerateBlock(byte *output, size_t size)
118{
119	while (size)
120	{
121		// on some systems /dev/random will block until all bytes
122		// are available, on others it will returns immediately
123		ssize_t len = read(m_fd, output, size);
124		if (len < 0)
125			throw OS_RNG_Err("read " CRYPTOPP_BLOCKING_RNG_FILENAME);
126		size -= len;
127		output += len;
128		if (size)
129			sleep(1);
130	}
131}
132
133#endif
134
135// *************************************************************
136
137void OS_GenerateRandomBlock(bool blocking, byte *output, size_t size)
138{
139#ifdef NONBLOCKING_RNG_AVAILABLE
140	if (blocking)
141#endif
142	{
143#ifdef BLOCKING_RNG_AVAILABLE
144		BlockingRng rng;
145		rng.GenerateBlock(output, size);
146#endif
147	}
148
149#ifdef BLOCKING_RNG_AVAILABLE
150	if (!blocking)
151#endif
152	{
153#ifdef NONBLOCKING_RNG_AVAILABLE
154		NonblockingRng rng;
155		rng.GenerateBlock(output, size);
156#endif
157	}
158}
159
160void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize)
161{
162	SecByteBlock seed(seedSize);
163	OS_GenerateRandomBlock(blocking, seed, seedSize);
164	IncorporateEntropy(seed, seedSize);
165}
166
167NAMESPACE_END
168
169#endif
170
171#endif
172