Add detail/prng.hpp

This commit is contained in:
Vinnie Falco
2019-01-19 07:23:47 -08:00
parent 785c0ad93d
commit 4d0849595c
9 changed files with 529 additions and 193 deletions

View File

@@ -2,6 +2,7 @@ Version 205
* Doc work
* Add detail/soft_mutex.hpp
* Add detail/prng.hpp
--------------------------------------------------------------------------------

View File

@@ -36,11 +36,11 @@ inline
void
make_sec_ws_key(sec_ws_key_type& key)
{
auto p = stream_prng::prng();
auto g = make_prng(true);
char a[16];
for(int i = 0; i < 16; i += 4)
{
auto const v = p->secure();
auto const v = g();
a[i ] = v & 0xff;
a[i+1] = (v >> 8) & 0xff;
a[i+2] = (v >> 16) & 0xff;

View File

@@ -0,0 +1,315 @@
//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_PRNG_IPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_PRNG_IPP
#include <boost/beast/core/detail/chacha.hpp>
#include <boost/align/aligned_alloc.hpp>
#include <boost/throw_exception.hpp>
#include <atomic>
#include <cstdlib>
#include <mutex>
#include <new>
#include <random>
#include <stdexcept>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
//------------------------------------------------------------------------------
std::uint32_t const*
prng_seed(std::seed_seq* ss)
{
struct data
{
std::uint32_t v[8];
explicit
data(std::seed_seq* pss)
{
if(! pss)
{
std::random_device g;
std::seed_seq ss{
g(), g(), g(), g(),
g(), g(), g(), g()};
ss.generate(v, v+8);
}
else
{
pss->generate(v, v+8);
}
}
};
static data const d(ss);
return d.v;
}
//------------------------------------------------------------------------------
template<class T>
class prng_pool
{
std::mutex m_;
T* head_ = nullptr;
public:
static
prng_pool&
instance()
{
static prng_pool p;
return p;
}
~prng_pool()
{
for(auto p = head_; p;)
{
auto next = p->next;
p->~T();
boost::alignment::aligned_free(p);
p = next;
}
}
prng::ref
acquire()
{
{
std::lock_guard<
std::mutex> g(m_);
if(head_)
{
auto p = head_;
head_ = head_->next;
return prng::ref(*p);
}
}
auto p = boost::alignment::aligned_alloc(
16, sizeof(T));
if(! p)
BOOST_THROW_EXCEPTION(std::bad_alloc{});
return prng::ref(*(::new(p) T()));
}
void
release(T& t)
{
std::lock_guard<
std::mutex> g(m_);
t.next = head_;
head_ = &t;
}
};
prng::ref
make_prng_no_tls(bool secure)
{
class fast_prng final : public prng
{
int refs_ = 0;
std::minstd_rand r_;
public:
fast_prng* next;
fast_prng()
: r_([]
{
static std::atomic<
std::uint64_t> nonce{0};
auto const pv = prng_seed();
return static_cast<value_type>(
pv[0] + pv[1] + pv[2] + pv[3] +
pv[4] + pv[5] + pv[6] + pv[7] +
++nonce);
}())
{
}
protected:
prng&
acquire() noexcept override
{
++refs_;
return *this;
}
void
release() noexcept override
{
if(--refs_ == 0)
prng_pool<fast_prng>::instance().release(*this);
}
value_type
operator()() noexcept override
{
return r_();
}
};
class secure_prng final : public prng
{
int refs_ = 0;
beast::detail::chacha<20> r_;
public:
secure_prng* next;
secure_prng()
: r_(prng_seed(), []
{
static std::atomic<
std::uint64_t> nonce{0};
return ++nonce;
}())
{
}
protected:
prng&
acquire() noexcept override
{
++refs_;
return *this;
}
void
release() noexcept override
{
if(--refs_ == 0)
prng_pool<secure_prng>::instance().release(*this);
}
value_type
operator()() noexcept override
{
return r_();
}
};
if(secure)
return prng_pool<secure_prng>::instance().acquire();
return prng_pool<fast_prng>::instance().acquire();
}
//------------------------------------------------------------------------------
#if ! BOOST_BEAST_NO_THREAD_LOCAL
prng::ref
make_prng_tls(bool secure)
{
class fast_prng final : public prng
{
std::minstd_rand r_;
public:
fast_prng()
: r_([]
{
static std::atomic<
std::uint64_t> nonce{0};
auto const pv = prng_seed();
return static_cast<value_type>(
pv[0] + pv[1] + pv[2] + pv[3] +
pv[4] + pv[5] + pv[6] + pv[7] +
++nonce);
}())
{
}
protected:
prng&
acquire() noexcept override
{
return *this;
}
void
release() noexcept override
{
}
value_type
operator()() noexcept override
{
return r_();
}
};
class secure_prng final : public prng
{
beast::detail::chacha<20> r_;
public:
secure_prng()
: r_(prng_seed(), []
{
static std::atomic<
std::uint64_t> nonce{0};
return ++nonce;
}())
{
}
protected:
prng&
acquire() noexcept override
{
return *this;
}
void
release() noexcept override
{
}
value_type
operator()() noexcept override
{
return r_();
}
};
if(secure)
{
thread_local secure_prng sp;
return prng::ref(sp);
}
thread_local fast_prng fp;
return prng::ref(fp);
}
#endif
//------------------------------------------------------------------------------
prng::ref
make_prng(bool secure)
{
#if BOOST_BEAST_NO_THREAD_LOCAL
return make_prng_no_tls(secure);
#else
return make_prng_tls(secure);
#endif
}
} // detail
} // websocket
} // beast
} // boost
#endif

View File

@@ -0,0 +1,138 @@
//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_PRNG_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_PRNG_HPP
#include <boost/beast/core/detail/config.hpp>
#include <cstdint>
#include <limits>
#include <random>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
// Type-erased UniformRandomBitGenerator
// with 32-bit unsigned integer value_type
//
class prng
{
protected:
virtual ~prng() = default;
virtual prng& acquire() noexcept = 0;
virtual void release() noexcept = 0;
virtual std::uint32_t operator()() noexcept = 0;
public:
using value_type = std::uint32_t;
class ref
{
prng& p_;
public:
ref& operator=(ref const&) = delete;
~ref()
{
p_.release();
}
explicit
ref(prng& p) noexcept
: p_(p.acquire())
{
}
ref(ref const& other) noexcept
: p_(other.p_.acquire())
{
}
value_type
operator()() noexcept
{
return p_();
}
static
value_type constexpr
min() noexcept
{
return (std::numeric_limits<
value_type>::min)();
}
static
value_type constexpr
max() noexcept
{
return (std::numeric_limits<
value_type>::max)();
}
};
};
//------------------------------------------------------------------------------
// Manually seed the prngs, must be called
// before acquiring a prng for the first time.
//
BOOST_BEAST_DECL
std::uint32_t const*
prng_seed(std::seed_seq* ss = nullptr);
// Acquire a PRNG using the no-TLS implementation
//
BOOST_BEAST_DECL
prng::ref
make_prng_no_tls(bool secure);
// Acquire a PRNG using the TLS implementation
//
#if ! BOOST_BEAST_NO_THREAD_LOCAL
BOOST_BEAST_DECL
prng::ref
make_prng_tls(bool secure);
#endif
// Acquire a PRNG using the TLS implementation if it
// is available, otherwise using the no-TLS implementation.
//
BOOST_BEAST_DECL
prng::ref
make_prng(bool secure);
//------------------------------------------------------------------------------
struct stream_prng
{
bool secure_prng_ = true;
std::uint32_t
create_mask()
{
auto g = make_prng(secure_prng_);
for(;;)
if(auto key = g())
return key;
}
};
} // detail
} // websocket
} // beast
} // boost
#include <boost/beast/websocket/detail/impl/prng.ipp>
#endif

View File

@@ -15,210 +15,21 @@
#include <boost/beast/http/string_body.hpp>
#include <boost/beast/websocket/option.hpp>
#include <boost/beast/websocket/detail/pmd_extension.hpp>
#include <boost/beast/websocket/detail/prng.hpp>
#include <boost/beast/zlib/deflate_stream.hpp>
#include <boost/beast/zlib/inflate_stream.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/detail/chacha.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/align/aligned_alloc.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/core/exchange.hpp>
#include <atomic>
#include <cstdint>
#include <memory>
#include <new>
#include <random>
// Turn this on to avoid using thread_local
//#define BOOST_BEAST_NO_THREAD_LOCAL 1
#ifdef BOOST_BEAST_NO_THREAD_LOCAL
#include <atomic>
#include <mutex>
#endif
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
struct stream_prng
{
bool secure_prng_ = true;
struct prng_type
{
std::minstd_rand fast;
beast::detail::chacha<20> secure;
#if BOOST_BEAST_NO_THREAD_LOCAL
prng_type* next = nullptr;
#endif
prng_type(std::uint32_t const* v, std::uint64_t stream)
: fast(static_cast<typename decltype(fast)::result_type>(
v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6] + v[7] + stream))
, secure(v, stream)
{
}
};
class prng_ref
{
prng_type* p_;
public:
prng_ref& operator=(prng_ref&&) = delete;
explicit
prng_ref(prng_type& p)
: p_(&p)
{
}
prng_ref(prng_ref&& other)
: p_(boost::exchange(other.p_, nullptr))
{
}
#ifdef BOOST_BEAST_NO_THREAD_LOCAL
~prng_ref()
{
if(p_)
pool::impl().release(*p_);
}
#endif
prng_type*
operator->() const
{
return p_;
}
};
#ifndef BOOST_BEAST_NO_THREAD_LOCAL
static
prng_ref
prng()
{
static std::atomic<std::uint64_t> stream{0};
thread_local prng_type p{seed(), stream++};
return prng_ref(p);
}
#else
static
prng_ref
prng()
{
return prng_ref(pool::impl().acquire());
}
#endif
static
std::uint32_t const*
seed(std::seed_seq* ss = nullptr)
{
static seed_data d(ss);
return d.v;
}
std::uint32_t
create_mask()
{
auto p = prng();
if(secure_prng_)
for(;;)
if(auto key = p->secure())
return key;
for(;;)
if(auto key = p->fast())
return key;
}
private:
struct seed_data
{
std::uint32_t v[8];
explicit
seed_data(std::seed_seq* pss)
{
if(! pss)
{
std::random_device g;
std::seed_seq ss{
g(), g(), g(), g(), g(), g(), g(), g()};
ss.generate(v, v+8);
}
else
{
pss->generate(v, v+8);
}
}
};
#ifdef BOOST_BEAST_NO_THREAD_LOCAL
class pool
{
prng_type* head_ = nullptr;
std::atomic<std::uint64_t> n_{0};
std::mutex m_;
public:
~pool()
{
for(auto p = head_; p;)
{
auto next = p->next;
p->~prng_type();
boost::alignment::aligned_free(p);
p = next;
}
}
prng_type&
acquire()
{
for(;;)
{
std::lock_guard<std::mutex> lock(m_);
if(! head_)
break;
auto p = head_;
head_ = head_->next;
return *p;
}
auto p = boost::alignment::aligned_alloc(
16, sizeof(prng_type));
if(! p)
BOOST_THROW_EXCEPTION(std::bad_alloc{});
return *(::new(p) prng_type(seed(), n_++));
}
void
release(prng_type& p)
{
std::lock_guard<std::mutex> lock(m_);
p.next = head_;
head_ = &p;
}
static
pool&
impl()
{
static pool instance;
return instance;
}
};
#endif
};
//------------------------------------------------------------------------------
template<bool deflateSupported>
struct stream_base : stream_prng
{

View File

@@ -3511,7 +3511,7 @@ inline
void
seed_prng(std::seed_seq& ss)
{
detail::stream_prng::seed(&ss);
detail::prng_seed(&ss);
}
} // websocket

View File

@@ -17,6 +17,7 @@ add_executable (tests-beast-websocket
${TEST_MAIN}
Jamfile
test.hpp
_detail_prng.cpp
accept.cpp
close.cpp
error.cpp

View File

@@ -8,6 +8,7 @@
#
local SOURCES =
_detail_prng.cpp
accept.cpp
close.cpp
error.cpp

View File

@@ -0,0 +1,69 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
// Test that header file is self-contained.
#include <boost/beast/websocket/detail/prng.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
class prng_test
: public beast::unit_test::suite
{
public:
#if 0
char const* name() const noexcept //override
{
return "boost.beast.websocket.detail.prng";
}
#endif
template <class F>
void
testPrng(F const& f)
{
{
auto v = f()();
BEAST_EXPECT(
v >= (prng::ref::min)() &&
v <= (prng::ref::max)());
}
{
auto v = f()();
BEAST_EXPECT(
v >= (prng::ref::min)() &&
v <= (prng::ref::max)());
}
}
void
run() override
{
testPrng([]{ return make_prng(true); });
testPrng([]{ return make_prng(false); });
testPrng([]{ return make_prng_no_tls(true); });
testPrng([]{ return make_prng_no_tls(false); });
#if ! BOOST_BEAST_NO_THREAD_LOCAL
testPrng([]{ return make_prng_tls(true); });
testPrng([]{ return make_prng_tls(false); });
#endif
}
};
//static prng_test t;
BEAST_DEFINE_TESTSUITE(beast,websocket,prng);
} // detail
} // websocket
} // beast
} // boost