aboutsummaryrefslogtreecommitdiff
path: root/src/random.h
diff options
context:
space:
mode:
authorPieter Wuille <pieter@wuille.net>2024-03-10 15:16:20 -0400
committerPieter Wuille <pieter@wuille.net>2024-07-01 10:26:46 -0400
commit6cfdc5b104caf9952393f9dac2a36539d964077f (patch)
tree1cef33e30adb25e9bef98e9eab2561d0c58f2f04 /src/random.h
parent8cc2f45065fc1864f879248d1e1444588e27076b (diff)
random: convert XoRoShiRo128PlusPlus into full RNG
Convert XoRoShiRo128PlusPlus into a full RandomMixin-based RNG class, providing all utility functionality that FastRandomContext has. In doing so, it is renamed to InsecureRandomContext, highlighting its non-cryptographic nature. To do this, a fillrand fallback is added to RandomMixin (where it is used by InsecureRandomContext), but FastRandomContext still uses its own fillrand.
Diffstat (limited to 'src/random.h')
-rw-r--r--src/random.h45
1 files changed, 28 insertions, 17 deletions
diff --git a/src/random.h b/src/random.h
index e870867170..cdf2f3a66d 100644
--- a/src/random.h
+++ b/src/random.h
@@ -147,8 +147,6 @@ template<typename T>
concept RandomNumberGenerator = requires(T& rng, Span<std::byte> s) {
// A random number generator must provide rand64().
{ rng.rand64() } noexcept -> std::same_as<uint64_t>;
- // A random number generator must provide randfill(Span<std::byte>).
- { rng.fillrand(s) } noexcept;
// A random number generator must derive from RandomMixin, which adds other rand* functions.
requires std::derived_from<std::remove_reference_t<T>, RandomMixin<std::remove_reference_t<T>>>;
};
@@ -261,6 +259,25 @@ public:
}
}
+ /** Fill a Span with random bytes. */
+ void fillrand(Span<std::byte> span) noexcept
+ {
+ while (span.size() >= 8) {
+ uint64_t gen = Impl().rand64();
+ WriteLE64(UCharCast(span.data()), gen);
+ span = span.subspan(8);
+ }
+ if (span.size() >= 4) {
+ uint32_t gen = Impl().rand32();
+ WriteLE32(UCharCast(span.data()), gen);
+ span = span.subspan(4);
+ }
+ while (span.size()) {
+ span[0] = std::byte(Impl().template randbits<8>());
+ span = span.subspan(1);
+ }
+ }
+
/** Generate random bytes. */
template <BasicByte B = unsigned char>
std::vector<B> randbytes(size_t len) noexcept
@@ -345,19 +362,19 @@ public:
return ReadLE64(UCharCast(buf.data()));
}
- /** Fill a byte Span with random bytes. */
+ /** Fill a byte Span with random bytes. This overrides the RandomMixin version. */
void fillrand(Span<std::byte> output) noexcept;
};
/** xoroshiro128++ PRNG. Extremely fast, not appropriate for cryptographic purposes.
*
- * Memory footprint is 128bit, period is 2^128 - 1.
+ * Memory footprint is very small, period is 2^128 - 1.
* This class is not thread-safe.
*
* Reference implementation available at https://prng.di.unimi.it/xoroshiro128plusplus.c
* See https://prng.di.unimi.it/
*/
-class XoRoShiRo128PlusPlus
+class InsecureRandomContext : public RandomMixin<InsecureRandomContext>
{
uint64_t m_s0;
uint64_t m_s1;
@@ -371,21 +388,19 @@ class XoRoShiRo128PlusPlus
}
public:
- using result_type = uint64_t;
-
- constexpr explicit XoRoShiRo128PlusPlus(uint64_t seedval) noexcept
+ constexpr explicit InsecureRandomContext(uint64_t seedval) noexcept
: m_s0(SplitMix64(seedval)), m_s1(SplitMix64(seedval)) {}
// no copy - that is dangerous, we don't want accidentally copy the RNG and then have two streams
// with exactly the same results.
- XoRoShiRo128PlusPlus(const XoRoShiRo128PlusPlus&) = delete;
- XoRoShiRo128PlusPlus& operator=(const XoRoShiRo128PlusPlus&) = delete;
+ InsecureRandomContext(const InsecureRandomContext&) = delete;
+ InsecureRandomContext& operator=(const InsecureRandomContext&) = delete;
// allow moves
- XoRoShiRo128PlusPlus(XoRoShiRo128PlusPlus&&) = default;
- XoRoShiRo128PlusPlus& operator=(XoRoShiRo128PlusPlus&&) = default;
+ InsecureRandomContext(InsecureRandomContext&&) = default;
+ InsecureRandomContext& operator=(InsecureRandomContext&&) = default;
- constexpr result_type operator()() noexcept
+ constexpr uint64_t rand64() noexcept
{
uint64_t s0 = m_s0, s1 = m_s1;
const uint64_t result = std::rotl(s0 + s1, 17) + s0;
@@ -394,10 +409,6 @@ public:
m_s1 = std::rotl(s1, 28);
return result;
}
-
- static constexpr result_type min() noexcept { return std::numeric_limits<result_type>::min(); }
- static constexpr result_type max() noexcept { return std::numeric_limits<result_type>::max(); }
- static constexpr double entropy() noexcept { return 0.0; }
};
/** More efficient than using std::shuffle on a FastRandomContext.