xref: /llvm-project-15.0.7/libcxx/src/random.cpp (revision 6e2c6c9d)
1eb8650a7SLouis Dionne //===----------------------------------------------------------------------===//
23e519524SHoward Hinnant //
357b08b09SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
457b08b09SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
557b08b09SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63e519524SHoward Hinnant //
73e519524SHoward Hinnant //===----------------------------------------------------------------------===//
83e519524SHoward Hinnant 
94a12eab8SSaleem Abdulrasool #include <__config>
104a12eab8SSaleem Abdulrasool 
1117f5dbefSEd Schouten #if defined(_LIBCPP_USING_WIN32_RANDOM)
120354b929SMarshall Clow     // Must be defined before including stdlib.h to enable rand_s().
130354b929SMarshall Clow #   define _CRT_RAND_S
1417f5dbefSEd Schouten #endif // defined(_LIBCPP_USING_WIN32_RANDOM)
150354b929SMarshall Clow 
16bbb0f2c7SArthur O'Dwyer #include <limits>
17bbb0f2c7SArthur O'Dwyer #include <random>
18bbb0f2c7SArthur O'Dwyer #include <system_error>
193e519524SHoward Hinnant 
2057148cbcSJF Bastien #if defined(__sun__)
2114c25b80SDavid Chisnall #   define rename solaris_headers_are_broken
2257148cbcSJF Bastien #endif // defined(__sun__)
2317f5dbefSEd Schouten 
2417f5dbefSEd Schouten #include <errno.h>
2517f5dbefSEd Schouten #include <stdio.h>
2617f5dbefSEd Schouten #include <stdlib.h>
2717f5dbefSEd Schouten 
285c704281SPetr Hosek #if defined(_LIBCPP_USING_GETENTROPY)
295c704281SPetr Hosek #   include <sys/random.h>
305c704281SPetr Hosek #elif defined(_LIBCPP_USING_DEV_RANDOM)
313e519524SHoward Hinnant #   include <fcntl.h>
323e519524SHoward Hinnant #   include <unistd.h>
33f3b979b6SMarek Kurdej #   if __has_include(<sys/ioctl.h>) && __has_include(<linux/random.h>)
34f3b979b6SMarek Kurdej #       include <sys/ioctl.h>
35f3b979b6SMarek Kurdej #       include <linux/random.h>
36f3b979b6SMarek Kurdej #   endif
3717f5dbefSEd Schouten #elif defined(_LIBCPP_USING_NACL_RANDOM)
3857148cbcSJF Bastien #   include <nacl/nacl_random.h>
393064dd8cSRoland McGrath #elif defined(_LIBCPP_USING_FUCHSIA_CPRNG)
403064dd8cSRoland McGrath #  include <zircon/syscalls.h>
4117f5dbefSEd Schouten #endif
4217f5dbefSEd Schouten 
433e519524SHoward Hinnant 
443e519524SHoward Hinnant _LIBCPP_BEGIN_NAMESPACE_STD
453e519524SHoward Hinnant 
465c704281SPetr Hosek #if defined(_LIBCPP_USING_GETENTROPY)
475c704281SPetr Hosek 
random_device(const string & __token)485c704281SPetr Hosek random_device::random_device(const string& __token)
495c704281SPetr Hosek {
505c704281SPetr Hosek     if (__token != "/dev/urandom")
515c704281SPetr Hosek         __throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
525c704281SPetr Hosek }
535c704281SPetr Hosek 
~random_device()545c704281SPetr Hosek random_device::~random_device()
555c704281SPetr Hosek {
565c704281SPetr Hosek }
575c704281SPetr Hosek 
585c704281SPetr Hosek unsigned
operator ()()595c704281SPetr Hosek random_device::operator()()
605c704281SPetr Hosek {
615c704281SPetr Hosek     unsigned r;
625c704281SPetr Hosek     size_t n = sizeof(r);
635c704281SPetr Hosek     int err = getentropy(&r, n);
645c704281SPetr Hosek     if (err)
655c704281SPetr Hosek         __throw_system_error(errno, "random_device getentropy failed");
665c704281SPetr Hosek     return r;
675c704281SPetr Hosek }
685c704281SPetr Hosek 
695c704281SPetr Hosek #elif defined(_LIBCPP_USING_ARC4_RANDOM)
7057148cbcSJF Bastien 
71d202c764SLouis Dionne random_device::random_device(const string&)
7257148cbcSJF Bastien {
7357148cbcSJF Bastien }
7457148cbcSJF Bastien 
7557148cbcSJF Bastien random_device::~random_device()
7657148cbcSJF Bastien {
7757148cbcSJF Bastien }
7857148cbcSJF Bastien 
7957148cbcSJF Bastien unsigned
8057148cbcSJF Bastien random_device::operator()()
8157148cbcSJF Bastien {
8217f5dbefSEd Schouten     return arc4random();
8357148cbcSJF Bastien }
8457148cbcSJF Bastien 
8517f5dbefSEd Schouten #elif defined(_LIBCPP_USING_DEV_RANDOM)
8657148cbcSJF Bastien 
873e519524SHoward Hinnant random_device::random_device(const string& __token)
883e519524SHoward Hinnant     : __f_(open(__token.c_str(), O_RDONLY))
893e519524SHoward Hinnant {
901e9592a9SDavid Majnemer     if (__f_ < 0)
913e519524SHoward Hinnant         __throw_system_error(errno, ("random_device failed to open " + __token).c_str());
923e519524SHoward Hinnant }
933e519524SHoward Hinnant 
943e519524SHoward Hinnant random_device::~random_device()
953e519524SHoward Hinnant {
963e519524SHoward Hinnant     close(__f_);
973e519524SHoward Hinnant }
983e519524SHoward Hinnant 
993e519524SHoward Hinnant unsigned
1003e519524SHoward Hinnant random_device::operator()()
1013e519524SHoward Hinnant {
1023e519524SHoward Hinnant     unsigned r;
1032dfdfdf4SDavid Majnemer     size_t n = sizeof(r);
1042dfdfdf4SDavid Majnemer     char* p = reinterpret_cast<char*>(&r);
1052dfdfdf4SDavid Majnemer     while (n > 0)
1062dfdfdf4SDavid Majnemer     {
1072dfdfdf4SDavid Majnemer         ssize_t s = read(__f_, p, n);
1082dfdfdf4SDavid Majnemer         if (s == 0)
1092dfdfdf4SDavid Majnemer             __throw_system_error(ENODATA, "random_device got EOF");
1102dfdfdf4SDavid Majnemer         if (s == -1)
1112dfdfdf4SDavid Majnemer         {
1122dfdfdf4SDavid Majnemer             if (errno != EINTR)
1132dfdfdf4SDavid Majnemer                 __throw_system_error(errno, "random_device got an unexpected error");
1142dfdfdf4SDavid Majnemer             continue;
1152dfdfdf4SDavid Majnemer         }
1162dfdfdf4SDavid Majnemer         n -= static_cast<size_t>(s);
1172dfdfdf4SDavid Majnemer         p += static_cast<size_t>(s);
1182dfdfdf4SDavid Majnemer     }
1193e519524SHoward Hinnant     return r;
1203e519524SHoward Hinnant }
12157148cbcSJF Bastien 
12217f5dbefSEd Schouten #elif defined(_LIBCPP_USING_NACL_RANDOM)
12317f5dbefSEd Schouten 
12417f5dbefSEd Schouten random_device::random_device(const string& __token)
12517f5dbefSEd Schouten {
12617f5dbefSEd Schouten     if (__token != "/dev/urandom")
12717f5dbefSEd Schouten         __throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
12817f5dbefSEd Schouten     int error = nacl_secure_random_init();
12917f5dbefSEd Schouten     if (error)
13017f5dbefSEd Schouten         __throw_system_error(error, ("random device failed to open " + __token).c_str());
13117f5dbefSEd Schouten }
13217f5dbefSEd Schouten 
13317f5dbefSEd Schouten random_device::~random_device()
13417f5dbefSEd Schouten {
13517f5dbefSEd Schouten }
13617f5dbefSEd Schouten 
13717f5dbefSEd Schouten unsigned
13817f5dbefSEd Schouten random_device::operator()()
13917f5dbefSEd Schouten {
14017f5dbefSEd Schouten     unsigned r;
14117f5dbefSEd Schouten     size_t n = sizeof(r);
14217f5dbefSEd Schouten     size_t bytes_written;
14317f5dbefSEd Schouten     int error = nacl_secure_random(&r, n, &bytes_written);
14417f5dbefSEd Schouten     if (error != 0)
14517f5dbefSEd Schouten         __throw_system_error(error, "random_device failed getting bytes");
14617f5dbefSEd Schouten     else if (bytes_written != n)
14717f5dbefSEd Schouten         __throw_runtime_error("random_device failed to obtain enough bytes");
14817f5dbefSEd Schouten     return r;
14917f5dbefSEd Schouten }
15017f5dbefSEd Schouten 
15117f5dbefSEd Schouten #elif defined(_LIBCPP_USING_WIN32_RANDOM)
15217f5dbefSEd Schouten 
15317f5dbefSEd Schouten random_device::random_device(const string& __token)
15417f5dbefSEd Schouten {
15517f5dbefSEd Schouten     if (__token != "/dev/urandom")
15617f5dbefSEd Schouten         __throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
15717f5dbefSEd Schouten }
15817f5dbefSEd Schouten 
15917f5dbefSEd Schouten random_device::~random_device()
16017f5dbefSEd Schouten {
16117f5dbefSEd Schouten }
16217f5dbefSEd Schouten 
16317f5dbefSEd Schouten unsigned
16417f5dbefSEd Schouten random_device::operator()()
16517f5dbefSEd Schouten {
16617f5dbefSEd Schouten     unsigned r;
16717f5dbefSEd Schouten     errno_t err = rand_s(&r);
16817f5dbefSEd Schouten     if (err)
16917f5dbefSEd Schouten         __throw_system_error(err, "random_device rand_s failed.");
17017f5dbefSEd Schouten     return r;
17117f5dbefSEd Schouten }
17217f5dbefSEd Schouten 
1733064dd8cSRoland McGrath #elif defined(_LIBCPP_USING_FUCHSIA_CPRNG)
1743064dd8cSRoland McGrath 
1753064dd8cSRoland McGrath random_device::random_device(const string& __token) {
1763064dd8cSRoland McGrath   if (__token != "/dev/urandom")
1773064dd8cSRoland McGrath     __throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
1783064dd8cSRoland McGrath }
1793064dd8cSRoland McGrath 
1803064dd8cSRoland McGrath random_device::~random_device() {}
1813064dd8cSRoland McGrath 
1823064dd8cSRoland McGrath unsigned random_device::operator()() {
1833064dd8cSRoland McGrath   // Implicitly link against the vDSO system call ABI without
1843064dd8cSRoland McGrath   // requiring the final link to specify -lzircon explicitly when
1853064dd8cSRoland McGrath   // statically linking libc++.
1863064dd8cSRoland McGrath #  pragma comment(lib, "zircon")
1873064dd8cSRoland McGrath 
1883064dd8cSRoland McGrath   // The system call cannot fail.  It returns only when the bits are ready.
1893064dd8cSRoland McGrath   unsigned r;
1903064dd8cSRoland McGrath   _zx_cprng_draw(&r, sizeof(r));
1913064dd8cSRoland McGrath   return r;
1923064dd8cSRoland McGrath }
1933064dd8cSRoland McGrath 
19417f5dbefSEd Schouten #else
19517f5dbefSEd Schouten #error "Random device not implemented for this architecture"
19617f5dbefSEd Schouten #endif
1973e519524SHoward Hinnant 
1983e519524SHoward Hinnant double
entropy() const1995601305fSLouis Dionne random_device::entropy() const noexcept
2003e519524SHoward Hinnant {
201f3b979b6SMarek Kurdej #if defined(_LIBCPP_USING_DEV_RANDOM) && defined(RNDGETENTCNT)
202f3b979b6SMarek Kurdej   int ent;
203f3b979b6SMarek Kurdej   if (::ioctl(__f_, RNDGETENTCNT, &ent) < 0)
2043e519524SHoward Hinnant     return 0;
205f3b979b6SMarek Kurdej 
206f3b979b6SMarek Kurdej   if (ent < 0)
207f3b979b6SMarek Kurdej     return 0;
208f3b979b6SMarek Kurdej 
209f3b979b6SMarek Kurdej   if (ent > std::numeric_limits<result_type>::digits)
210f3b979b6SMarek Kurdej     return std::numeric_limits<result_type>::digits;
211f3b979b6SMarek Kurdej 
212f3b979b6SMarek Kurdej   return ent;
213*6e2c6c9dSBrad Smith #elif defined(_LIBCPP_USING_ARC4_RANDOM) || defined(_LIBCPP_USING_FUCHSIA_CPRNG)
2144b6d7fddSBrad Smith   return std::numeric_limits<result_type>::digits;
215f3b979b6SMarek Kurdej #else
216f3b979b6SMarek Kurdej   return 0;
217f3b979b6SMarek Kurdej #endif
2183e519524SHoward Hinnant }
2193e519524SHoward Hinnant 
2203e519524SHoward Hinnant _LIBCPP_END_NAMESPACE_STD
221