From 8a693c468ab51e8eaae92da0493d2e9c2f2aef3e Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Sat, 26 Nov 2022 11:33:05 +0100 Subject: [PATCH] Refactored random generation with unified thread-local generator --- examples/client-benchmark/main.cpp | 8 ++++--- examples/client/main.cpp | 9 +++++--- src/description.cpp | 6 ++--- src/impl/icetransport.cpp | 7 +++--- src/impl/utils.cpp | 20 +++++++++++++++++ src/impl/utils.hpp | 35 +++++++++++++++++++++++++++++- src/impl/wshandshake.cpp | 6 +---- src/impl/wstransport.cpp | 8 ++----- src/rtppacketizationconfig.cpp | 11 ++++++---- 9 files changed, 80 insertions(+), 30 deletions(-) diff --git a/examples/client-benchmark/main.cpp b/examples/client-benchmark/main.cpp index ebcbb33b8..2c669a643 100644 --- a/examples/client-benchmark/main.cpp +++ b/examples/client-benchmark/main.cpp @@ -479,11 +479,13 @@ shared_ptr createPeerConnection(const rtc::Configuration &c // Helper function to generate a random ID std::string randomId(size_t length) { + using std::chrono::system_clock; + static thread_local std::mt19937 rng( + static_cast(system_clock::now().time_since_epoch().count())); static const std::string characters( "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); std::string id(length, '0'); - std::default_random_engine rng(std::random_device{}()); - std::uniform_int_distribution dist(0, int(characters.size() - 1)); - std::generate(id.begin(), id.end(), [&]() { return characters.at(dist(rng)); }); + std::uniform_int_distribution uniform(0, int(characters.size() - 1)); + std::generate(id.begin(), id.end(), [&]() { return characters.at(uniform(rng)); }); return id; } diff --git a/examples/client/main.cpp b/examples/client/main.cpp index df050d29b..f49721aca 100644 --- a/examples/client/main.cpp +++ b/examples/client/main.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -263,11 +264,13 @@ shared_ptr createPeerConnection(const rtc::Configuration &c // Helper function to generate a random ID std::string randomId(size_t length) { + using std::chrono::system_clock; + static thread_local std::mt19937 rng( + static_cast(system_clock::now().time_since_epoch().count())); static const std::string characters( "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); std::string id(length, '0'); - std::default_random_engine rng(std::random_device{}()); - std::uniform_int_distribution dist(0, int(characters.size() - 1)); - std::generate(id.begin(), id.end(), [&]() { return characters.at(dist(rng)); }); + std::uniform_int_distribution uniform(0, int(characters.size() - 1)); + std::generate(id.begin(), id.end(), [&]() { return characters.at(uniform(rng)); }); return id; } diff --git a/src/description.cpp b/src/description.cpp index 03bd4592e..ea7579362 100644 --- a/src/description.cpp +++ b/src/description.cpp @@ -162,10 +162,8 @@ Description::Description(const string &sdp, Type type, Role role) mUsername = "rtc"; if (mSessionId.empty()) { - auto seed = static_cast(system_clock::now().time_since_epoch().count()); - std::default_random_engine generator(seed); - std::uniform_int_distribution uniform; - mSessionId = std::to_string(uniform(generator)); + auto uniform = std::bind(std::uniform_int_distribution{}, utils::random_engine()); + mSessionId = std::to_string(uniform()); } } diff --git a/src/impl/icetransport.cpp b/src/impl/icetransport.cpp index e3dca57c4..8cd589f74 100644 --- a/src/impl/icetransport.cpp +++ b/src/impl/icetransport.cpp @@ -20,6 +20,7 @@ #include "configuration.hpp" #include "internals.hpp" #include "transport.hpp" +#include "utils.hpp" #include #include @@ -103,8 +104,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi // Randomize servers order std::vector servers = config.iceServers; - auto seed = static_cast(system_clock::now().time_since_epoch().count()); - std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed)); + std::shuffle(servers.begin(), servers.end(), utils::random_engine()); // Pick a STUN server for (auto &server : servers) { @@ -451,8 +451,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi // Randomize order std::vector servers = config.iceServers; - auto seed = static_cast(system_clock::now().time_since_epoch().count()); - std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed)); + std::shuffle(servers.begin(), servers.end(), utils::random_engine()); // Add one STUN server bool success = false; diff --git a/src/impl/utils.cpp b/src/impl/utils.cpp index c05f9c8ee..60677818c 100644 --- a/src/impl/utils.cpp +++ b/src/impl/utils.cpp @@ -21,6 +21,7 @@ #include "impl/internals.hpp" #include +#include #include #include #include @@ -110,4 +111,23 @@ string base64_encode(const binary &data) { return out; } +std::seed_seq random_seed() { + std::vector seed; + + // Seed with random device + try { + // On some systems an exception might be thrown if the random_device can't be initialized + std::random_device device; + seed.push_back(device()); + } catch (const std::exception &) { + // Ignore + } + + // Seed with current time + using std::chrono::system_clock; + seed.push_back(static_cast(system_clock::now().time_since_epoch().count())); + + return std::seed_seq(seed.begin(), seed.end()); +} + } // namespace rtc::impl::utils diff --git a/src/impl/utils.hpp b/src/impl/utils.hpp index b3752f642..d8ff80272 100644 --- a/src/impl/utils.hpp +++ b/src/impl/utils.hpp @@ -21,6 +21,9 @@ #include "common.hpp" +#include +#include +#include #include namespace rtc::impl::utils { @@ -36,6 +39,36 @@ string url_decode(const string &str); // See https://www.rfc-editor.org/rfc/rfc4648.html#section-4 string base64_encode(const binary &data); -} // namespace rtc::impl +// Return a random seed sequence +std::seed_seq random_seed(); + +template +struct random_engine_wrapper { + Generator &engine; + using result_type = Result; + static constexpr result_type min() { return static_cast(Generator::min()); } + static constexpr result_type max() { return static_cast(Generator::max()); } + inline result_type operator()() { return static_cast(engine()); } + inline void discard(unsigned long long z) { engine.discard(z); } +}; + +// Return a wrapped thread-local seeded random number generator +template +auto random_engine() { + static thread_local std::seed_seq seed = random_seed(); + static thread_local Generator engine{seed}; + return random_engine_wrapper{engine}; +} + +// Return a wrapped thread-local seeded random bytes generator +template auto random_bytes_engine() { + using char_independent_bits_engine = + std::independent_bits_engine; + static_assert(char_independent_bits_engine::min() == std::numeric_limits::min()); + static_assert(char_independent_bits_engine::max() == std::numeric_limits::max()); + return random_engine(); +} + +} // namespace rtc::impl::utils #endif diff --git a/src/impl/wshandshake.cpp b/src/impl/wshandshake.cpp index 8df5b59de..ff553d515 100644 --- a/src/impl/wshandshake.cpp +++ b/src/impl/wshandshake.cpp @@ -36,8 +36,6 @@ namespace rtc::impl { using std::to_string; using std::chrono::system_clock; -using random_bytes_engine = - std::independent_bits_engine; WsHandshake::WsHandshake() {} @@ -240,11 +238,9 @@ string WsHandshake::generateKey() { // RFC 6455: The request MUST include a header field with the name Sec-WebSocket-Key. The value // of this header field MUST be a nonce consisting of a randomly selected 16-byte value that has // been base64-encoded. [...] The nonce MUST be selected randomly for each connection. - auto seed = static_cast(system_clock::now().time_since_epoch().count()); - random_bytes_engine generator(seed); binary key(16); auto k = reinterpret_cast(key.data()); - std::generate(k, k + key.size(), [&]() { return uint8_t(generator()); }); + std::generate(k, k + key.size(), utils::random_bytes_engine()); return utils::base64_encode(key); } diff --git a/src/impl/wstransport.cpp b/src/impl/wstransport.cpp index 878295f98..59bc725fe 100644 --- a/src/impl/wstransport.cpp +++ b/src/impl/wstransport.cpp @@ -20,6 +20,7 @@ #include "tcptransport.hpp" #include "threadpool.hpp" #include "tlstransport.hpp" +#include "utils.hpp" #if RTC_ENABLE_WEBSOCKET @@ -50,8 +51,6 @@ namespace rtc::impl { using std::to_integer; using std::to_string; using std::chrono::system_clock; -using random_bytes_engine = - std::independent_bits_engine; WsTransport::WsTransport(variant, shared_ptr> lower, shared_ptr handshake, int maxOutstandingPings, @@ -366,13 +365,10 @@ bool WsTransport::sendFrame(const Frame &frame) { } if (frame.mask) { - auto seed = static_cast(system_clock::now().time_since_epoch().count()); - random_bytes_engine generator(seed); - byte *maskingKey = reinterpret_cast(cur); auto u = reinterpret_cast(maskingKey); - std::generate(u, u + 4, [&]() { return uint8_t(generator()); }); + std::generate(u, u + 4, utils::random_bytes_engine()); cur += 4; for (size_t i = 0; i < frame.length; ++i) diff --git a/src/rtppacketizationconfig.cpp b/src/rtppacketizationconfig.cpp index e7531d932..d2e3fd892 100644 --- a/src/rtppacketizationconfig.cpp +++ b/src/rtppacketizationconfig.cpp @@ -20,12 +20,16 @@ #include "rtppacketizationconfig.hpp" +#include "impl/utils.hpp" + #include #include #include namespace rtc { +namespace utils = impl::utils; + RtpPacketizationConfig::RtpPacketizationConfig(SSRC ssrc, string cname, uint8_t payloadType, uint32_t clockRate, uint8_t videoOrientationId) : ssrc(ssrc), cname(cname), payloadType(payloadType), clockRate(clockRate), @@ -35,10 +39,9 @@ RtpPacketizationConfig::RtpPacketizationConfig(SSRC ssrc, string cname, uint8_t // RFC 3550: The initial value of the sequence number SHOULD be random (unpredictable) to make // known-plaintext attacks on encryption more difficult [...] The initial value of the timestamp // SHOULD be random, as for the sequence number. - std::default_random_engine rng(std::random_device{}()); - std::uniform_int_distribution dist(0, std::numeric_limits::max()); - sequenceNumber = static_cast(dist(rng)); - timestamp = startTimestamp = dist(rng); + auto uniform = std::bind(std::uniform_int_distribution{}, utils::random_engine()); + sequenceNumber = static_cast(uniform()); + timestamp = startTimestamp = uniform(); } double RtpPacketizationConfig::getSecondsFromTimestamp(uint32_t timestamp, uint32_t clockRate) {