File: RandomStream.cpp

package info (click to toggle)
storm-lang 0.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,028 kB
  • sloc: ansic: 261,471; cpp: 140,432; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (108 lines) | stat: -rw-r--r-- 2,133 bytes parent folder | download | duplicates (3)
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

}