mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +02:00
Generated WebSocket masks use a secure PRNG by default:
This resolves a medium vulnerability described in the Beast Hybrid Assessment Report by Bishop Fox, where masks generated for use with outgoing WebSocket client frames use an insufficient source of entropy and a non-cryptographically secure pseudo-random number generator. By default, all newly constructed WebSocket streams will use a uniquely seeded secure PRNG (ChaCha20 in counter mode). As this may result in increased CPU resource consumption, the function websocket::stream::secure_prng() may be used to select a faster but less secure PRNG, for the case where the caller knows that the secure generator is not necessary. On some systems, std::random_device may produce insufficient entropy to securely seed the PRNG. As this condition cannot be detected by Beast, callers may use the function websocket::seed_prng() called once at startup to provide at least 256 bits of entropy which will be used to uniquely seed all subsequent PRNGs.
This commit is contained in:
@@ -129,6 +129,7 @@ install:
|
||||
- export PATH="`pwd`":$PATH
|
||||
- git submodule update --init tools/build
|
||||
- git submodule update --init tools/boostdep
|
||||
- git submodule update --init libs/align
|
||||
- git submodule update --init libs/asio
|
||||
- git submodule update --init libs/assert
|
||||
- git submodule update --init libs/config
|
||||
|
@@ -2,6 +2,7 @@ Version 176:
|
||||
|
||||
* Tidy up Quick Reference
|
||||
* Fix array end calculation in utf8 assertion
|
||||
* WebSocket masks use secure PRNG by default
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@@ -23,6 +23,7 @@ install:
|
||||
- cd boost-root
|
||||
- git submodule update --init tools/build
|
||||
- git submodule update --init tools/boostdep
|
||||
- git submodule update --init libs/align
|
||||
- git submodule update --init libs/asio
|
||||
- git submodule update --init libs/assert
|
||||
- git submodule update --init libs/config
|
||||
|
@@ -10,8 +10,11 @@
|
||||
[section Release Notes]
|
||||
|
||||
This version fixes a missing executor work guard in all composed operations
|
||||
used in the implementatio. Users who are experiencing crashes related to
|
||||
asynchronous completion handlers are encouraged to upgrade.
|
||||
used in the implementation. Users who are experiencing crashes related to
|
||||
asynchronous completion handlers are encouraged to upgrade. Also included
|
||||
is an improved mechanism for generating random numbers used to mask outgoing
|
||||
websocket frames when operating in the client mode. This resolves a
|
||||
vulnerability described in the Beast Hybrid Assessment Report from Bishop Fox.
|
||||
|
||||
[heading Boost 1.68]
|
||||
|
||||
@@ -35,14 +38,20 @@ in future versions.
|
||||
|
||||
* New [link beast.ref.boost__beast__http__is_mutable_body_writer `http::is_mutable_body_writer`] metafunction
|
||||
|
||||
* New [link beast.ref.boost__beast__websocket__seed_prng `websocket::seed_prng`] for manually providing entropy to the PRNG
|
||||
|
||||
* New [link beast.ref.boost__beast__websocket__stream.secure_prng `websocket::stream::secure_prng`] to control whether the connection uses a secure PRNG
|
||||
|
||||
[*Improvements]
|
||||
|
||||
* Generated WebSocket masks use a secure PRNG by default
|
||||
|
||||
* Improvements to [link beast.ref.boost__beast__buffers_adapter `buffers_adapter`]
|
||||
|
||||
* ([issue 1109]) Use a shared string for example HTTP server doc roots
|
||||
|
||||
* ([issue 1079]) Add [link beast.ref.boost__beast__handler_ptr.has_value `handler_ptr::has_value`]
|
||||
|
||||
* Improvements to [link beast.ref.boost__beast__buffers_adapter `buffers_adapter`]
|
||||
|
||||
[*Fixes]
|
||||
|
||||
* ([issue 1073]) Fix race in advanced server examples
|
||||
|
@@ -137,6 +137,7 @@
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.boost__beast__websocket__async_teardown">async_teardown</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__websocket__is_upgrade">is_upgrade</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__websocket__seed_prng">seed_prng</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__websocket__teardown">teardown</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Options</bridgehead>
|
||||
|
196
include/boost/beast/core/detail/chacha.hpp
Normal file
196
include/boost/beast/core/detail/chacha.hpp
Normal file
@@ -0,0 +1,196 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
//
|
||||
// This is a derivative work, original copyright follows:
|
||||
//
|
||||
|
||||
/*
|
||||
Copyright (c) 2015 Orson Peters <orsonpeters@gmail.com>
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In no event will the
|
||||
authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial
|
||||
applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you wrote the
|
||||
original software. If you use this software in a product, an acknowledgment in the product
|
||||
documentation would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as
|
||||
being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_DETAIL_CHACHA_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_CHACHA_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<std::size_t R>
|
||||
class chacha
|
||||
{
|
||||
void generate_block();
|
||||
void chacha_core();
|
||||
|
||||
alignas(16) std::uint32_t block_[16];
|
||||
std::uint32_t keysetup_[8];
|
||||
std::uint64_t ctr_ = 0;
|
||||
int idx_ = 16;
|
||||
|
||||
public:
|
||||
static constexpr std::size_t state_size = sizeof(chacha::keysetup_);
|
||||
|
||||
using result_type = std::uint32_t;
|
||||
|
||||
chacha(std::uint32_t const* v, std::uint64_t stream);
|
||||
|
||||
std::uint32_t
|
||||
operator()();
|
||||
|
||||
#if 0
|
||||
template<std::size_t R_>
|
||||
friend
|
||||
bool
|
||||
operator==(chacha<R_> const& lhs, chacha<R_> const& rhs);
|
||||
|
||||
template<std::size_t R_>
|
||||
friend
|
||||
bool
|
||||
operator!=(chacha<R_> const& lhs, chacha<R_> const& rhs);
|
||||
|
||||
static
|
||||
constexpr
|
||||
std::uint32_t
|
||||
min()
|
||||
{
|
||||
return (std::numeric_limits<std::uint32_t>::min)();
|
||||
}
|
||||
|
||||
static
|
||||
constexpr
|
||||
std::uint32_t
|
||||
max()
|
||||
{
|
||||
return (std::numeric_limits<std::uint32_t>::max)();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template<std::size_t R>
|
||||
chacha<R>::
|
||||
chacha(std::uint32_t const* v, std::uint64_t stream)
|
||||
{
|
||||
for (int i = 0; i < 6; ++i)
|
||||
keysetup_[i] = v[i];
|
||||
keysetup_[6] = v[6] + (stream & 0xffffffff);
|
||||
keysetup_[7] = v[7] + ((stream >> 32) & 0xffffffff);
|
||||
}
|
||||
|
||||
template<std::size_t R>
|
||||
std::uint32_t
|
||||
chacha<R>::
|
||||
operator()()
|
||||
{
|
||||
if(idx_ == 16)
|
||||
{
|
||||
idx_ = 0;
|
||||
++ctr_;
|
||||
generate_block();
|
||||
}
|
||||
return block_[idx_++];
|
||||
}
|
||||
|
||||
template<std::size_t R>
|
||||
void
|
||||
chacha<R>::
|
||||
generate_block()
|
||||
{
|
||||
std::uint32_t constexpr constants[4] = {
|
||||
0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
|
||||
std::uint32_t input[16];
|
||||
for (int i = 0; i < 4; ++i)
|
||||
input[i] = constants[i];
|
||||
for (int i = 0; i < 8; ++i)
|
||||
input[4 + i] = keysetup_[i];
|
||||
input[12] = (ctr_ / 16) & 0xffffffffu;
|
||||
input[13] = (ctr_ / 16) >> 32;
|
||||
input[14] = input[15] = 0xdeadbeef; // Could use 128-bit counter.
|
||||
for (int i = 0; i < 16; ++i)
|
||||
block_[i] = input[i];
|
||||
chacha_core();
|
||||
for (int i = 0; i < 16; ++i)
|
||||
block_[i] += input[i];
|
||||
}
|
||||
|
||||
template<std::size_t R>
|
||||
void
|
||||
chacha<R>::
|
||||
chacha_core()
|
||||
{
|
||||
#define BOOST_BEAST_CHACHA_ROTL32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
||||
|
||||
#define BOOST_BEAST_CHACHA_QUARTERROUND(x, a, b, c, d) \
|
||||
x[a] = x[a] + x[b]; x[d] ^= x[a]; x[d] = BOOST_BEAST_CHACHA_ROTL32(x[d], 16); \
|
||||
x[c] = x[c] + x[d]; x[b] ^= x[c]; x[b] = BOOST_BEAST_CHACHA_ROTL32(x[b], 12); \
|
||||
x[a] = x[a] + x[b]; x[d] ^= x[a]; x[d] = BOOST_BEAST_CHACHA_ROTL32(x[d], 8); \
|
||||
x[c] = x[c] + x[d]; x[b] ^= x[c]; x[b] = BOOST_BEAST_CHACHA_ROTL32(x[b], 7)
|
||||
|
||||
for (unsigned i = 0; i < R; i += 2)
|
||||
{
|
||||
BOOST_BEAST_CHACHA_QUARTERROUND(block_, 0, 4, 8, 12);
|
||||
BOOST_BEAST_CHACHA_QUARTERROUND(block_, 1, 5, 9, 13);
|
||||
BOOST_BEAST_CHACHA_QUARTERROUND(block_, 2, 6, 10, 14);
|
||||
BOOST_BEAST_CHACHA_QUARTERROUND(block_, 3, 7, 11, 15);
|
||||
BOOST_BEAST_CHACHA_QUARTERROUND(block_, 0, 5, 10, 15);
|
||||
BOOST_BEAST_CHACHA_QUARTERROUND(block_, 1, 6, 11, 12);
|
||||
BOOST_BEAST_CHACHA_QUARTERROUND(block_, 2, 7, 8, 13);
|
||||
BOOST_BEAST_CHACHA_QUARTERROUND(block_, 3, 4, 9, 14);
|
||||
}
|
||||
|
||||
#undef BOOST_BEAST_CHACHA_QUARTERROUND
|
||||
#undef BOOST_BEAST_CHACHA_ROTL32
|
||||
}
|
||||
|
||||
//#endif
|
||||
|
||||
#if 0
|
||||
// Implement <random> interface.
|
||||
|
||||
template<std::size_t R>
|
||||
bool
|
||||
operator==(chacha<R> const& lhs, chacha<R> const& rhs)
|
||||
{
|
||||
for (int i = 0; i < 8; ++i)
|
||||
if (lhs.keysetup_[i] != rhs.keysetup_[i])
|
||||
return false;
|
||||
return lhs.ctr_ == rhs.ctr_;
|
||||
}
|
||||
|
||||
template<std::size_t R>
|
||||
bool
|
||||
operator!=(chacha<R> const& lhs, chacha<R> const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
@@ -14,6 +14,7 @@
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/detail/base64.hpp>
|
||||
#include <boost/beast/core/detail/sha1.hpp>
|
||||
#include <boost/beast/websocket/detail/stream_base.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
@@ -31,14 +32,15 @@ using sec_ws_key_type = static_string<
|
||||
using sec_ws_accept_type = static_string<
|
||||
beast::detail::base64::encoded_size(20)>;
|
||||
|
||||
template<class Gen>
|
||||
inline
|
||||
void
|
||||
make_sec_ws_key(sec_ws_key_type& key, Gen& g)
|
||||
make_sec_ws_key(sec_ws_key_type& key)
|
||||
{
|
||||
auto p = stream_prng::prng();
|
||||
char a[16];
|
||||
for(int i = 0; i < 16; i += 4)
|
||||
{
|
||||
auto const v = g();
|
||||
auto const v = p->secure();
|
||||
a[i ] = v & 0xff;
|
||||
a[i+1] = (v >> 8) & 0xff;
|
||||
a[i+2] = (v >> 16) & 0xff;
|
||||
|
@@ -24,66 +24,6 @@ namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// Pseudo-random source of mask keys
|
||||
//
|
||||
template<class Generator>
|
||||
class maskgen_t
|
||||
{
|
||||
Generator g_;
|
||||
|
||||
public:
|
||||
using result_type =
|
||||
typename Generator::result_type;
|
||||
|
||||
maskgen_t();
|
||||
|
||||
result_type
|
||||
operator()() noexcept;
|
||||
|
||||
void
|
||||
rekey();
|
||||
};
|
||||
|
||||
template<class Generator>
|
||||
maskgen_t<Generator>::maskgen_t()
|
||||
{
|
||||
rekey();
|
||||
}
|
||||
|
||||
template<class Generator>
|
||||
auto
|
||||
maskgen_t<Generator>::operator()() noexcept ->
|
||||
result_type
|
||||
{
|
||||
for(;;)
|
||||
if(auto key = g_())
|
||||
return key;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
void
|
||||
maskgen_t<_>::rekey()
|
||||
{
|
||||
std::random_device rng;
|
||||
#if 0
|
||||
std::array<std::uint32_t, 32> e;
|
||||
for(auto& i : e)
|
||||
i = rng();
|
||||
// VFALCO This constructor causes
|
||||
// address sanitizer to fail, no idea why.
|
||||
std::seed_seq ss(e.begin(), e.end());
|
||||
g_.seed(ss);
|
||||
#else
|
||||
g_.seed(rng());
|
||||
#endif
|
||||
}
|
||||
|
||||
// VFALCO NOTE This generator has 5KB of state!
|
||||
//using maskgen = maskgen_t<std::mt19937>;
|
||||
using maskgen = maskgen_t<std::minstd_rand>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using prepared_key = std::array<unsigned char, 4>;
|
||||
|
||||
inline
|
||||
|
@@ -16,9 +16,23 @@
|
||||
#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/integer_sequence.hpp>
|
||||
#include <boost/align/aligned_alloc.hpp>
|
||||
#include <boost/asio/buffer.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 {
|
||||
@@ -105,8 +119,191 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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_([&other]
|
||||
{
|
||||
auto p = other.p_;
|
||||
other.p_ = nullptr;
|
||||
return p;
|
||||
}())
|
||||
{
|
||||
}
|
||||
|
||||
#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
|
||||
struct stream_base : stream_prng
|
||||
{
|
||||
// State information for the permessage-deflate extension
|
||||
struct pmd_type
|
||||
@@ -165,7 +362,7 @@ struct stream_base
|
||||
};
|
||||
|
||||
template<>
|
||||
struct stream_base<false>
|
||||
struct stream_base<false> : stream_prng
|
||||
{
|
||||
// These stubs are for avoiding linking in the zlib
|
||||
// code when permessage-deflate is not enabled.
|
||||
|
@@ -556,7 +556,7 @@ write_close(DynamicBuffer& db, close_reason const& cr)
|
||||
if(role_ == role_type::client)
|
||||
{
|
||||
fh.mask = true;
|
||||
fh.key = wr_gen_();
|
||||
fh.key = this->create_mask();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -608,7 +608,7 @@ write_ping(DynamicBuffer& db,
|
||||
fh.len = data.size();
|
||||
fh.mask = role_ == role_type::client;
|
||||
if(fh.mask)
|
||||
fh.key = wr_gen_();
|
||||
fh.key = this->create_mask();
|
||||
detail::write(db, fh);
|
||||
if(data.empty())
|
||||
return;
|
||||
@@ -641,7 +641,7 @@ build_request(detail::sec_ws_key_type& key,
|
||||
req.set(http::field::host, host);
|
||||
req.set(http::field::upgrade, "websocket");
|
||||
req.set(http::field::connection, "upgrade");
|
||||
detail::make_sec_ws_key(key, wr_gen_);
|
||||
detail::make_sec_ws_key(key);
|
||||
req.set(http::field::sec_websocket_key, key);
|
||||
req.set(http::field::sec_websocket_version, "13");
|
||||
build_request_pmd(req, is_deflate_supported{});
|
||||
|
@@ -406,7 +406,7 @@ operator()(
|
||||
remain_ = buffer_size(cb_);
|
||||
fh_.fin = fin_;
|
||||
fh_.len = remain_;
|
||||
fh_.key = ws_.wr_gen_();
|
||||
fh_.key = ws_.create_mask();
|
||||
detail::prepare_key(key_, fh_.key);
|
||||
ws_.wr_fb_.reset();
|
||||
detail::write<flat_static_buffer_base>(
|
||||
@@ -458,7 +458,7 @@ operator()(
|
||||
n = clamp(remain_, ws_.wr_buf_size_);
|
||||
remain_ -= n;
|
||||
fh_.len = n;
|
||||
fh_.key = ws_.wr_gen_();
|
||||
fh_.key = ws_.create_mask();
|
||||
fh_.fin = fin_ ? remain_ == 0 : false;
|
||||
detail::prepare_key(key_, fh_.key);
|
||||
buffer_copy(buffer(
|
||||
@@ -521,7 +521,7 @@ operator()(
|
||||
}
|
||||
if(fh_.mask)
|
||||
{
|
||||
fh_.key = ws_.wr_gen_();
|
||||
fh_.key = ws_.create_mask();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, fh_.key);
|
||||
detail::mask_inplace(b, key);
|
||||
@@ -666,7 +666,7 @@ write_some(bool fin,
|
||||
}
|
||||
if(fh.mask)
|
||||
{
|
||||
fh.key = wr_gen_();
|
||||
fh.key = this->create_mask();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, fh.key);
|
||||
detail::mask_inplace(b, key);
|
||||
@@ -740,7 +740,7 @@ write_some(bool fin,
|
||||
// mask, no autofrag
|
||||
fh.fin = fin;
|
||||
fh.len = remain;
|
||||
fh.key = wr_gen_();
|
||||
fh.key = this->create_mask();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, fh.key);
|
||||
detail::fh_buffer fh_buf;
|
||||
@@ -784,7 +784,7 @@ write_some(bool fin,
|
||||
ConstBufferSequence> cb{buffers};
|
||||
for(;;)
|
||||
{
|
||||
fh.key = wr_gen_();
|
||||
fh.key = this->create_mask();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, fh.key);
|
||||
auto const n = clamp(remain, wr_buf_size_);
|
||||
|
@@ -206,7 +206,6 @@ class stream
|
||||
std::size_t wr_buf_opt_ // write buffer size option setting
|
||||
= 4096;
|
||||
detail::fh_buffer wr_fb_; // header buffer used for writes
|
||||
detail::maskgen wr_gen_; // source of mask keys
|
||||
|
||||
detail::pausation paused_rd_; // paused read op
|
||||
detail::pausation paused_wr_; // paused write op
|
||||
@@ -633,6 +632,37 @@ public:
|
||||
return rd_msg_max_;
|
||||
}
|
||||
|
||||
/** Set whether the PRNG is cryptographically secure
|
||||
|
||||
This controls whether or not the source of pseudo-random
|
||||
numbers used to produce the masks required by the WebSocket
|
||||
protocol are of cryptographic quality. When the setting is
|
||||
`true`, a strong algorithm is used which cannot be guessed
|
||||
by observing outputs. When the setting is `false`, a much
|
||||
faster algorithm is used.
|
||||
Masking is only performed by streams operating in the client
|
||||
mode. For streams operating in the server mode, this setting
|
||||
has no effect.
|
||||
By default, newly constructed streams use a secure PRNG.
|
||||
|
||||
If the WebSocket stream is used with an encrypted SSL or TLS
|
||||
next layer, if it is known to the application that intermediate
|
||||
proxies are not vulnerable to cache poisoning, or if the
|
||||
application is designed such that an attacker cannot send
|
||||
arbitrary inputs to the stream interface, then the faster
|
||||
algorithm may be used.
|
||||
|
||||
For more information please consult the WebSocket protocol RFC.
|
||||
|
||||
@param value `true` if the PRNG algorithm should be
|
||||
cryptographically secure.
|
||||
*/
|
||||
void
|
||||
secure_prng(bool value)
|
||||
{
|
||||
this->secure_prng_ = value;
|
||||
}
|
||||
|
||||
/** Set the write buffer size option.
|
||||
|
||||
Sets the size of the write buffer used by the implementation to
|
||||
@@ -3563,6 +3593,36 @@ private:
|
||||
error_code& ec);
|
||||
};
|
||||
|
||||
/** Manually provide a one-time seed to initialize the PRNG
|
||||
|
||||
This function invokes the specified seed sequence to produce a seed
|
||||
suitable for use with the pseudo-random number generator used to
|
||||
create masks and perform WebSocket protocol handshakes.
|
||||
|
||||
If a seed is not manually provided, the implementation will
|
||||
perform a one-time seed generation using `std::random_device`. This
|
||||
function may be used when the application runs in an environment
|
||||
where the random device is unreliable or does not provide sufficient
|
||||
entropy.
|
||||
|
||||
@par Preconditions
|
||||
|
||||
This function may not be called after any websocket @ref stream objects
|
||||
have been constructed.
|
||||
|
||||
@param ss A reference to a `std::seed_seq` which will be used to seed
|
||||
the pseudo-random number generator. The seed sequence should have at
|
||||
least 256 bits of entropy.
|
||||
|
||||
@see stream::secure_prng
|
||||
*/
|
||||
inline
|
||||
void
|
||||
seed_prng(std::seed_seq& ss)
|
||||
{
|
||||
detail::stream_prng::seed(&ss);
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
@@ -23,7 +23,6 @@ add_executable (tests-beast-websocket
|
||||
error.cpp
|
||||
frame.cpp
|
||||
handshake.cpp
|
||||
mask.cpp
|
||||
option.cpp
|
||||
ping.cpp
|
||||
read1.cpp
|
||||
|
@@ -13,7 +13,6 @@ local SOURCES =
|
||||
error.cpp
|
||||
frame.cpp
|
||||
handshake.cpp
|
||||
mask.cpp
|
||||
option.cpp
|
||||
ping.cpp
|
||||
read1.cpp
|
||||
|
@@ -1,58 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/beast/websocket/detail/mask.hpp>
|
||||
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
class mask_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
struct test_generator
|
||||
{
|
||||
using result_type = std::uint32_t;
|
||||
|
||||
result_type n = 0;
|
||||
|
||||
void
|
||||
seed(std::seed_seq const&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
seed(result_type const&)
|
||||
{
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
operator()()
|
||||
{
|
||||
return n++;
|
||||
}
|
||||
};
|
||||
|
||||
void run() override
|
||||
{
|
||||
maskgen_t<test_generator> mg;
|
||||
BEAST_EXPECT(mg() != 0);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(beast,websocket,mask);
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
@@ -22,6 +22,11 @@ public:
|
||||
void
|
||||
testOptions()
|
||||
{
|
||||
{
|
||||
std::seed_seq ss{42};
|
||||
seed_prng(ss);
|
||||
}
|
||||
|
||||
stream<test::stream> ws{ioc_};
|
||||
ws.auto_fragment(true);
|
||||
ws.write_buffer_size(2048);
|
||||
@@ -37,6 +42,9 @@ public:
|
||||
pass();
|
||||
}
|
||||
|
||||
ws.secure_prng(true);
|
||||
ws.secure_prng(false);
|
||||
|
||||
auto const bad =
|
||||
[&](permessage_deflate const& pmd)
|
||||
{
|
||||
|
Reference in New Issue
Block a user