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
|
#include "stdafx.h"
#include "RandomStream.h"
#include "Core/Exception.h"
#if defined(WINDOWS)
#define SECURITY_WIN32
#include <Wincrypt.h>
#elif defined(POSIX)
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#endif
namespace ssl {
RandomStream::RandomStream() : data(0) {
init();
}
RandomStream::RandomStream(const RandomStream &) : data(0) {
// There is no point in "copying" the old stream...
init();
}
RandomStream::~RandomStream() {
close();
}
Bool RandomStream::more() {
return data != 0;
}
#if defined(WINDOWS)
void RandomStream::init() {
HCRYPTPROV provider = NULL;
if (!CryptAcquireContext(&provider, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
throw new (this) InternalError(S("Failed to acquire a cryptographic context for random numbers."));
data = size_t(provider);
}
Buffer RandomStream::read(Buffer to) {
if (data == 0)
return to;
Nat fill = to.free();
if (!CryptGenRandom(HCRYPTPROV(data), fill, to.dataPtr() + to.filled()))
throw new (this) InternalError(S("Failed to generate random data."));
to.filled(to.filled() + fill);
return to;
}
void RandomStream::close() {
if (data)
CryptReleaseContext(HCRYPTPROV(data), 0);
data = 0;
}
#elif defined(POSIX)
void RandomStream::init() {
int fd = ::open("/dev/urandom", O_RDONLY);
if (fd < 0)
throw new (this) InternalError(S("Failed to open /dev/urandom to acquire randomness."));
fcntl(fd, F_SETFD, FD_CLOEXEC);
data = size_t(fd + 1);
}
Buffer RandomStream::read(Buffer to) {
if (data == 0)
return to;
Nat fill = to.free();
Bool done = false;
while (!done) {
// Note: This is fine since "/dev/urandom" typically does not block.
ssize_t result = ::read(int(data - 1), to.dataPtr() + to.filled(), fill);
if (result < 0) {
if (errno == EINTR)
continue;
throw new (this) InternalError(S("Failed to read from /dev/urandom!"));
} else {
done = true;
to.filled(to.filled() + result);
}
}
return to;
}
void RandomStream::close() {
if (data > 0)
::close(int(data - 1));
data = 0;
}
#endif
}
|