1//===-------------------------- random.cpp --------------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is dual licensed under the MIT and the University of Illinois Open
6// Source Licenses. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include <__config>
11
12#if defined(_LIBCPP_USING_WIN32_RANDOM)
13// Must be defined before including stdlib.h to enable rand_s().
14#define _CRT_RAND_S
15#endif // defined(_LIBCPP_USING_WIN32_RANDOM)
16
17#include "random"
18#include "system_error"
19
20#if defined(__sun__)
21#define rename solaris_headers_are_broken
22#endif // defined(__sun__)
23
24#include <errno.h>
25#include <stdio.h>
26#include <stdlib.h>
27
28#if defined(_LIBCPP_USING_DEV_RANDOM)
29#include <fcntl.h>
30#include <unistd.h>
31#elif defined(_LIBCPP_USING_NACL_RANDOM)
32#include <nacl/nacl_random.h>
33#endif
34
35
36_LIBCPP_BEGIN_NAMESPACE_STD
37
38#if defined(_LIBCPP_USING_ARC4_RANDOM)
39
40random_device::random_device(const string& __token)
41{
42    if (__token != "/dev/urandom")
43        __throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
44}
45
46random_device::~random_device()
47{
48}
49
50unsigned
51random_device::operator()()
52{
53    return arc4random();
54}
55
56#elif defined(_LIBCPP_USING_DEV_RANDOM)
57
58random_device::random_device(const string& __token)
59    : __f_(open(__token.c_str(), O_RDONLY))
60{
61    if (__f_ < 0)
62        __throw_system_error(errno, ("random_device failed to open " + __token).c_str());
63}
64
65random_device::~random_device()
66{
67    close(__f_);
68}
69
70unsigned
71random_device::operator()()
72{
73    unsigned r;
74    size_t n = sizeof(r);
75    char* p = reinterpret_cast<char*>(&r);
76    while (n > 0)
77    {
78        ssize_t s = read(__f_, p, n);
79        if (s == 0)
80            __throw_system_error(ENODATA, "random_device got EOF");
81        if (s == -1)
82        {
83            if (errno != EINTR)
84                __throw_system_error(errno, "random_device got an unexpected error");
85            continue;
86        }
87        n -= static_cast<size_t>(s);
88        p += static_cast<size_t>(s);
89    }
90    return r;
91}
92
93#elif defined(_LIBCPP_USING_NACL_RANDOM)
94
95random_device::random_device(const string& __token)
96{
97    if (__token != "/dev/urandom")
98        __throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
99    int error = nacl_secure_random_init();
100    if (error)
101        __throw_system_error(error, ("random device failed to open " + __token).c_str());
102}
103
104random_device::~random_device()
105{
106}
107
108unsigned
109random_device::operator()()
110{
111    unsigned r;
112    size_t n = sizeof(r);
113    size_t bytes_written;
114    int error = nacl_secure_random(&r, n, &bytes_written);
115    if (error != 0)
116        __throw_system_error(error, "random_device failed getting bytes");
117    else if (bytes_written != n)
118        __throw_runtime_error("random_device failed to obtain enough bytes");
119    return r;
120}
121
122#elif defined(_LIBCPP_USING_WIN32_RANDOM)
123
124random_device::random_device(const string& __token)
125{
126    if (__token != "/dev/urandom")
127        __throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
128}
129
130random_device::~random_device()
131{
132}
133
134unsigned
135random_device::operator()()
136{
137    unsigned r;
138    errno_t err = rand_s(&r);
139    if (err)
140        __throw_system_error(err, "random_device rand_s failed.");
141    return r;
142}
143
144#else
145#error "Random device not implemented for this architecture"
146#endif
147
148double
149random_device::entropy() const _NOEXCEPT
150{
151    return 0;
152}
153
154_LIBCPP_END_NAMESPACE_STD
155