1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/http
7  
// Official repository: https://github.com/cppalliance/http
8  
//
8  
//
9  

9  

10  
#include "random.hpp"
10  
#include "random.hpp"
11  
#include <boost/http/detail/except.hpp>
11  
#include <boost/http/detail/except.hpp>
12  
#include <boost/system/error_code.hpp>
12  
#include <boost/system/error_code.hpp>
13  

13  

14  
#if defined(_WIN32)
14  
#if defined(_WIN32)
15  
#  ifndef WIN32_LEAN_AND_MEAN
15  
#  ifndef WIN32_LEAN_AND_MEAN
16  
#    define WIN32_LEAN_AND_MEAN
16  
#    define WIN32_LEAN_AND_MEAN
17  
#  endif
17  
#  endif
18  
#  include <windows.h>
18  
#  include <windows.h>
19  
#  include <bcrypt.h>
19  
#  include <bcrypt.h>
20  
#  ifdef _MSC_VER
20  
#  ifdef _MSC_VER
21  
#    pragma comment(lib, "bcrypt.lib")
21  
#    pragma comment(lib, "bcrypt.lib")
22  
#  endif
22  
#  endif
23  
#elif defined(__linux__)
23  
#elif defined(__linux__)
24  
#  include <sys/random.h>
24  
#  include <sys/random.h>
25  
#elif defined(__APPLE__)
25  
#elif defined(__APPLE__)
26  
#  include <Security/SecRandom.h>
26  
#  include <Security/SecRandom.h>
27  
#else
27  
#else
28  
#  include <fcntl.h>
28  
#  include <fcntl.h>
29  
#  include <unistd.h>
29  
#  include <unistd.h>
30  
#endif
30  
#endif
31  

31  

32  
namespace boost {
32  
namespace boost {
33  
namespace http {
33  
namespace http {
34  
namespace bcrypt {
34  
namespace bcrypt {
35  
namespace detail {
35  
namespace detail {
36  

36  

37  
#if defined(_WIN32)
37  
#if defined(_WIN32)
38  

38  

39  
namespace {
39  
namespace {
40  

40  

41  
class rng_provider
41  
class rng_provider
42  
{
42  
{
43  
    BCRYPT_ALG_HANDLE h_ = nullptr;
43  
    BCRYPT_ALG_HANDLE h_ = nullptr;
44  

44  

45  
public:
45  
public:
46  
    rng_provider()
46  
    rng_provider()
47  
    {
47  
    {
48  
        NTSTATUS status = BCryptOpenAlgorithmProvider(
48  
        NTSTATUS status = BCryptOpenAlgorithmProvider(
49  
            &h_,
49  
            &h_,
50  
            BCRYPT_RNG_ALGORITHM,
50  
            BCRYPT_RNG_ALGORITHM,
51  
            nullptr,
51  
            nullptr,
52  
            0);
52  
            0);
53  
        if (!BCRYPT_SUCCESS(status))
53  
        if (!BCRYPT_SUCCESS(status))
54  
            h_ = nullptr;
54  
            h_ = nullptr;
55  
    }
55  
    }
56  

56  

57  
    ~rng_provider()
57  
    ~rng_provider()
58  
    {
58  
    {
59  
        if (h_)
59  
        if (h_)
60  
            BCryptCloseAlgorithmProvider(h_, 0);
60  
            BCryptCloseAlgorithmProvider(h_, 0);
61  
    }
61  
    }
62  

62  

63  
    rng_provider(rng_provider const&) = delete;
63  
    rng_provider(rng_provider const&) = delete;
64  
    rng_provider& operator=(rng_provider const&) = delete;
64  
    rng_provider& operator=(rng_provider const&) = delete;
65  

65  

66  
    bool generate(void* buf, std::size_t n) const
66  
    bool generate(void* buf, std::size_t n) const
67  
    {
67  
    {
68  
        if (!h_)
68  
        if (!h_)
69  
            return false;
69  
            return false;
70  
        NTSTATUS status = BCryptGenRandom(
70  
        NTSTATUS status = BCryptGenRandom(
71  
            h_,
71  
            h_,
72  
            static_cast<PUCHAR>(buf),
72  
            static_cast<PUCHAR>(buf),
73  
            static_cast<ULONG>(n),
73  
            static_cast<ULONG>(n),
74  
            0);
74  
            0);
75  
        return BCRYPT_SUCCESS(status);
75  
        return BCRYPT_SUCCESS(status);
76  
    }
76  
    }
77  
};
77  
};
78  

78  

79  
rng_provider& get_rng()
79  
rng_provider& get_rng()
80  
{
80  
{
81  
    static rng_provider rng;
81  
    static rng_provider rng;
82  
    return rng;
82  
    return rng;
83  
}
83  
}
84  

84  

85  
} // namespace
85  
} // namespace
86  

86  

87  
void
87  
void
88  
fill_random(void* buf, std::size_t n)
88  
fill_random(void* buf, std::size_t n)
89  
{
89  
{
90  
    if (!get_rng().generate(buf, n))
90  
    if (!get_rng().generate(buf, n))
91  
    {
91  
    {
92  
        http::detail::throw_system_error(
92  
        http::detail::throw_system_error(
93  
            system::error_code(
93  
            system::error_code(
94  
                static_cast<int>(GetLastError()),
94  
                static_cast<int>(GetLastError()),
95  
                system::system_category()));
95  
                system::system_category()));
96  
    }
96  
    }
97  
}
97  
}
98  

98  

99  
#elif defined(__linux__)
99  
#elif defined(__linux__)
100  

100  

101  
void
101  
void
102  
fill_random(void* buf, std::size_t n)
102  
fill_random(void* buf, std::size_t n)
103  
{
103  
{
104  
    auto* p = static_cast<unsigned char*>(buf);
104  
    auto* p = static_cast<unsigned char*>(buf);
105  
    while (n > 0)
105  
    while (n > 0)
106  
    {
106  
    {
107  
        ssize_t r = getrandom(p, n, 0);
107  
        ssize_t r = getrandom(p, n, 0);
108  
        if (r < 0)
108  
        if (r < 0)
109  
        {
109  
        {
110  
            if (errno == EINTR)
110  
            if (errno == EINTR)
111  
                continue;
111  
                continue;
112  
            http::detail::throw_system_error(
112  
            http::detail::throw_system_error(
113  
                system::error_code(
113  
                system::error_code(
114  
                    errno,
114  
                    errno,
115  
                    system::system_category()));
115  
                    system::system_category()));
116  
        }
116  
        }
117  
        p += r;
117  
        p += r;
118  
        n -= static_cast<std::size_t>(r);
118  
        n -= static_cast<std::size_t>(r);
119  
    }
119  
    }
120  
}
120  
}
121  

121  

122  
#elif defined(__APPLE__)
122  
#elif defined(__APPLE__)
123  

123  

124  
void
124  
void
125  
fill_random(void* buf, std::size_t n)
125  
fill_random(void* buf, std::size_t n)
126  
{
126  
{
127  
    int err = SecRandomCopyBytes(kSecRandomDefault, n, buf);
127  
    int err = SecRandomCopyBytes(kSecRandomDefault, n, buf);
128  
    if (err != errSecSuccess)
128  
    if (err != errSecSuccess)
129  
    {
129  
    {
130  
        http::detail::throw_system_error(
130  
        http::detail::throw_system_error(
131  
            system::error_code(
131  
            system::error_code(
132  
                err,
132  
                err,
133  
                system::system_category()));
133  
                system::system_category()));
134  
    }
134  
    }
135  
}
135  
}
136  

136  

137  
#else
137  
#else
138  

138  

139  
// Fallback: /dev/urandom
139  
// Fallback: /dev/urandom
140  
void
140  
void
141  
fill_random(void* buf, std::size_t n)
141  
fill_random(void* buf, std::size_t n)
142  
{
142  
{
143  
    static int fd = -1;
143  
    static int fd = -1;
144  
    if (fd < 0)
144  
    if (fd < 0)
145  
    {
145  
    {
146  
        fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
146  
        fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
147  
        if (fd < 0)
147  
        if (fd < 0)
148  
        {
148  
        {
149  
            http::detail::throw_system_error(
149  
            http::detail::throw_system_error(
150  
                system::error_code(
150  
                system::error_code(
151  
                    errno,
151  
                    errno,
152  
                    system::system_category()));
152  
                    system::system_category()));
153  
        }
153  
        }
154  
    }
154  
    }
155  

155  

156  
    auto* p = static_cast<unsigned char*>(buf);
156  
    auto* p = static_cast<unsigned char*>(buf);
157  
    while (n > 0)
157  
    while (n > 0)
158  
    {
158  
    {
159  
        ssize_t r = read(fd, p, n);
159  
        ssize_t r = read(fd, p, n);
160  
        if (r < 0)
160  
        if (r < 0)
161  
        {
161  
        {
162  
            if (errno == EINTR)
162  
            if (errno == EINTR)
163  
                continue;
163  
                continue;
164  
            http::detail::throw_system_error(
164  
            http::detail::throw_system_error(
165  
                system::error_code(
165  
                system::error_code(
166  
                    errno,
166  
                    errno,
167  
                    system::system_category()));
167  
                    system::system_category()));
168  
        }
168  
        }
169  
        if (r == 0)
169  
        if (r == 0)
170  
        {
170  
        {
171  
            http::detail::throw_runtime_error(
171  
            http::detail::throw_runtime_error(
172  
                "unexpected EOF from /dev/urandom");
172  
                "unexpected EOF from /dev/urandom");
173  
        }
173  
        }
174  
        p += r;
174  
        p += r;
175  
        n -= static_cast<std::size_t>(r);
175  
        n -= static_cast<std::size_t>(r);
176  
    }
176  
    }
177  
}
177  
}
178  

178  

179  
#endif
179  
#endif
180  

180  

181  
} // detail
181  
} // detail
182  
} // bcrypt
182  
} // bcrypt
183  
} // http
183  
} // http
184  
} // boost
184  
} // boost