Echo Writes Code

random.cpp

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
#include "crucible/core/random.hpp"

#include "crucible/core/platform.hpp"

#if CRUCIBLE_PLATFORM_IS_POSIX_LIKE
#include <cerrno>

#include <fcntl.h>
#include <unistd.h>
#endif // CRUCIBLE_PLATFORM_IS_POSIX_LIKE

namespace crucible
{
	auto read_random_bytes(std::size_t const count) -> random_result<heap_buffer<std::byte>>
	{
		auto buffer { make_heap_buffer<std::byte>(count) };
		CRUCIBLE_TRY(read_random_bytes_into(buffer.view()));
		return make_success(std::move(buffer));
	}

	auto read_random_bytes_into(mutable_view<std::byte> sink) -> random_result<none>
	{
		int const fd = ::open("/dev/urandom", O_RDONLY);

		if (fd < 0) {
			return make_failure<random_error>(randomness_source_unavailable {});
		}

		int read_status { 0 };
		std::size_t bytes_read { 0 };

		// ::read() is allowed to not read the whole buffer, so we do it in a loop until we're sure we're done
		do {
			errno = 0;
			read_status = ::read(fd, sink.data() + bytes_read, sink.size() - bytes_read);

			if (read_status > 0) {
				bytes_read += read_status;
			}
		} while (bytes_read < sink.size() && read_status > 0);

		if (read_status < 0 || bytes_read < sink.size()) {
			return make_failure<random_error>(randomness_source_read_failed {});
		}

		return make_success<none>();
	}
}