Coverage Report

Created: 2025-07-01 06:55

/src/libtorrent/src/random.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
3
Copyright (c) 2011-2012, 2014-2015, 2017-2021, Arvid Norberg
4
Copyright (c) 2016, 2019, Alden Torres
5
Copyright (c) 2017, 2019, Andrei Kurushin
6
All rights reserved.
7
8
Redistribution and use in source and binary forms, with or without
9
modification, are permitted provided that the following conditions
10
are met:
11
12
    * Redistributions of source code must retain the above copyright
13
      notice, this list of conditions and the following disclaimer.
14
    * Redistributions in binary form must reproduce the above copyright
15
      notice, this list of conditions and the following disclaimer in
16
      the documentation and/or other materials provided with the distribution.
17
    * Neither the name of the author nor the names of its
18
      contributors may be used to endorse or promote products derived
19
      from this software without specific prior written permission.
20
21
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
POSSIBILITY OF SUCH DAMAGE.
32
33
*/
34
35
#include "libtorrent/config.hpp"
36
#include "libtorrent/random.hpp"
37
#include "libtorrent/error_code.hpp"
38
#include "libtorrent/aux_/throw.hpp"
39
40
#if defined BOOST_NO_CXX11_THREAD_LOCAL
41
#include <mutex>
42
#endif
43
44
#if TORRENT_BROKEN_RANDOM_DEVICE
45
#include "libtorrent/time.hpp"
46
#include <atomic>
47
#endif
48
49
#if TORRENT_USE_CNG
50
#include "libtorrent/aux_/win_cng.hpp"
51
52
#elif TORRENT_USE_CRYPTOAPI
53
#include "libtorrent/aux_/win_crypto_provider.hpp"
54
55
#elif defined TORRENT_USE_LIBCRYPTO && !defined TORRENT_USE_WOLFSSL
56
57
#include "libtorrent/aux_/disable_warnings_push.hpp"
58
extern "C" {
59
#include <openssl/rand.h>
60
#include <openssl/err.h>
61
}
62
#include "libtorrent/aux_/disable_warnings_pop.hpp"
63
64
#elif TORRENT_USE_GETRANDOM
65
66
#include <sys/random.h>
67
// this is the fall-back in case getrandom() fails
68
#include "libtorrent/aux_/dev_random.hpp"
69
70
#elif TORRENT_USE_DEV_RANDOM
71
#include "libtorrent/aux_/dev_random.hpp"
72
#endif
73
74
#ifdef BOOST_NO_CXX11_THREAD_LOCAL
75
namespace {
76
  // if the random number generator can't be thread local, just protect it with
77
  // a mutex. Not ideal, but hopefully not too many people are affected by old
78
  // systems
79
  std::mutex rng_mutex;
80
}
81
#endif
82
83
namespace libtorrent {
84
namespace aux {
85
86
    std::mt19937& random_engine()
87
0
    {
88
#ifdef TORRENT_BUILD_SIMULATOR
89
      // make sure random numbers are deterministic. Seed with a fixed number
90
      static std::mt19937 rng(0x82daf973);
91
#else
92
93
#if TORRENT_BROKEN_RANDOM_DEVICE
94
      struct {
95
        std::uint32_t operator()() const
96
        {
97
          std::uint32_t ret;
98
          crypto_random_bytes({reinterpret_cast<char*>(&ret), sizeof(ret)});
99
          return ret;
100
        }
101
      } dev;
102
#else
103
0
      static std::random_device dev;
104
0
#endif
105
#ifdef BOOST_NO_CXX11_THREAD_LOCAL
106
      static std::seed_seq seed({dev(), dev(), dev(), dev()});
107
      static std::mt19937 rng(seed);
108
#else
109
0
      thread_local static std::seed_seq seed({dev(), dev(), dev(), dev()});
110
0
      thread_local static std::mt19937 rng(seed);
111
0
#endif
112
0
#endif
113
0
      return rng;
114
0
    }
115
116
    void random_bytes(span<char> buffer)
117
0
    {
118
#ifdef TORRENT_BUILD_SIMULATOR
119
      // simulator
120
      for (auto& b : buffer) b = char(random(0xff));
121
#else
122
0
      std::generate(buffer.begin(), buffer.end(), [] { return char(random(0xff)); });
123
0
#endif
124
0
    }
125
126
    void crypto_random_bytes(span<char> buffer)
127
0
    {
128
#ifdef TORRENT_BUILD_SIMULATOR
129
      // In the simulator we want deterministic random numbers
130
      std::generate(buffer.begin(), buffer.end(), [] { return char(random(0xff)); });
131
#elif TORRENT_USE_CNG
132
      aux::cng_gen_random(buffer);
133
#elif TORRENT_USE_CRYPTOAPI
134
      // windows
135
      aux::crypt_gen_random(buffer);
136
#elif defined TORRENT_USE_LIBCRYPTO && !defined TORRENT_USE_WOLFSSL
137
// wolfSSL uses wc_RNG_GenerateBlock as the internal function for the
138
// openssl compatibility layer. This function API does not support
139
// an arbitrary buffer size (openssl does), it is limited by the
140
// constant RNG_MAX_BLOCK_LEN.
141
// TODO: improve calling RAND_bytes multiple times, using fallback for now
142
143
      // openssl
144
0
      int r = RAND_bytes(reinterpret_cast<unsigned char*>(buffer.data())
145
0
        , int(buffer.size()));
146
0
      if (r != 1) aux::throw_ex<system_error>(errors::no_entropy);
147
#elif TORRENT_USE_GETRANDOM
148
      ssize_t const r = ::getrandom(buffer.data(), static_cast<std::size_t>(buffer.size()), 0);
149
      if (r == ssize_t(buffer.size())) return;
150
      if (r == -1 && errno != ENOSYS) aux::throw_ex<system_error>(error_code(errno, generic_category()));
151
      static dev_random dev;
152
      dev.read(buffer);
153
#elif TORRENT_USE_DEV_RANDOM
154
      static dev_random dev;
155
      dev.read(buffer);
156
#else
157
158
#if TORRENT_BROKEN_RANDOM_DEVICE
159
      // even pseudo random numbers rely on being able to seed the random
160
      // generator
161
#error "no entropy source available"
162
#else
163
#ifdef TORRENT_I_WANT_INSECURE_RANDOM_NUMBERS
164
      std::generate(buffer.begin(), buffer.end(), [] { return char(random(0xff)); });
165
#else
166
#error "no secure entropy source available. If you really want insecure random numbers, define TORRENT_I_WANT_INSECURE_RANDOM_NUMBERS"
167
#endif
168
#endif
169
170
#endif
171
0
    }
172
}
173
174
  std::uint32_t random(std::uint32_t const max)
175
0
  {
176
0
    if (max == 0) return 0;
177
#ifdef BOOST_NO_CXX11_THREAD_LOCAL
178
    std::lock_guard<std::mutex> l(rng_mutex);
179
#endif
180
#ifdef TORRENT_BUILD_SIMULATOR
181
    std::uint32_t mask = max | (max >> 16);
182
    mask |= mask >> 8;
183
    mask |= mask >> 4;
184
    mask |= mask >> 2;
185
    mask |= mask >> 1;
186
    auto& rng = aux::random_engine();
187
    std::uint32_t ret;
188
    do
189
    {
190
      ret = rng() & mask;
191
    } while (ret > max);
192
#else
193
0
    auto const ret = std::uniform_int_distribution<std::uint32_t>(0, max)(aux::random_engine());
194
0
#endif
195
0
    return ret;
196
0
  }
197
198
}