mirror of
https://github.com/boostorg/beast.git
synced 2025-08-02 14:24:31 +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
|
- export PATH="`pwd`":$PATH
|
||||||
- git submodule update --init tools/build
|
- git submodule update --init tools/build
|
||||||
- git submodule update --init tools/boostdep
|
- git submodule update --init tools/boostdep
|
||||||
|
- git submodule update --init libs/align
|
||||||
- git submodule update --init libs/asio
|
- git submodule update --init libs/asio
|
||||||
- git submodule update --init libs/assert
|
- git submodule update --init libs/assert
|
||||||
- git submodule update --init libs/config
|
- git submodule update --init libs/config
|
||||||
|
@@ -2,6 +2,7 @@ Version 176:
|
|||||||
|
|
||||||
* Tidy up Quick Reference
|
* Tidy up Quick Reference
|
||||||
* Fix array end calculation in utf8 assertion
|
* Fix array end calculation in utf8 assertion
|
||||||
|
* WebSocket masks use secure PRNG by default
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@@ -23,6 +23,7 @@ install:
|
|||||||
- cd boost-root
|
- cd boost-root
|
||||||
- git submodule update --init tools/build
|
- git submodule update --init tools/build
|
||||||
- git submodule update --init tools/boostdep
|
- git submodule update --init tools/boostdep
|
||||||
|
- git submodule update --init libs/align
|
||||||
- git submodule update --init libs/asio
|
- git submodule update --init libs/asio
|
||||||
- git submodule update --init libs/assert
|
- git submodule update --init libs/assert
|
||||||
- git submodule update --init libs/config
|
- git submodule update --init libs/config
|
||||||
|
@@ -10,8 +10,11 @@
|
|||||||
[section Release Notes]
|
[section Release Notes]
|
||||||
|
|
||||||
This version fixes a missing executor work guard in all composed operations
|
This version fixes a missing executor work guard in all composed operations
|
||||||
used in the implementatio. Users who are experiencing crashes related to
|
used in the implementation. Users who are experiencing crashes related to
|
||||||
asynchronous completion handlers are encouraged to upgrade.
|
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]
|
[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__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]
|
[*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 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`]
|
* ([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]
|
[*Fixes]
|
||||||
|
|
||||||
* ([issue 1073]) Fix race in advanced server examples
|
* ([issue 1073]) Fix race in advanced server examples
|
||||||
|
@@ -137,6 +137,7 @@
|
|||||||
<simplelist type="vert" columns="1">
|
<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__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__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>
|
<member><link linkend="beast.ref.boost__beast__websocket__teardown">teardown</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Options</bridgehead>
|
<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/string.hpp>
|
||||||
#include <boost/beast/core/detail/base64.hpp>
|
#include <boost/beast/core/detail/base64.hpp>
|
||||||
#include <boost/beast/core/detail/sha1.hpp>
|
#include <boost/beast/core/detail/sha1.hpp>
|
||||||
|
#include <boost/beast/websocket/detail/stream_base.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -31,14 +32,15 @@ using sec_ws_key_type = static_string<
|
|||||||
using sec_ws_accept_type = static_string<
|
using sec_ws_accept_type = static_string<
|
||||||
beast::detail::base64::encoded_size(20)>;
|
beast::detail::base64::encoded_size(20)>;
|
||||||
|
|
||||||
template<class Gen>
|
inline
|
||||||
void
|
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];
|
char a[16];
|
||||||
for(int i = 0; i < 16; i += 4)
|
for(int i = 0; i < 16; i += 4)
|
||||||
{
|
{
|
||||||
auto const v = g();
|
auto const v = p->secure();
|
||||||
a[i ] = v & 0xff;
|
a[i ] = v & 0xff;
|
||||||
a[i+1] = (v >> 8) & 0xff;
|
a[i+1] = (v >> 8) & 0xff;
|
||||||
a[i+2] = (v >> 16) & 0xff;
|
a[i+2] = (v >> 16) & 0xff;
|
||||||
|
@@ -24,66 +24,6 @@ namespace beast {
|
|||||||
namespace websocket {
|
namespace websocket {
|
||||||
namespace detail {
|
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>;
|
using prepared_key = std::array<unsigned char, 4>;
|
||||||
|
|
||||||
inline
|
inline
|
||||||
|
@@ -16,9 +16,23 @@
|
|||||||
#include <boost/beast/zlib/inflate_stream.hpp>
|
#include <boost/beast/zlib/inflate_stream.hpp>
|
||||||
#include <boost/beast/core/buffers_suffix.hpp>
|
#include <boost/beast/core/buffers_suffix.hpp>
|
||||||
#include <boost/beast/core/error.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 <boost/asio/buffer.hpp>
|
||||||
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#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 boost {
|
||||||
namespace beast {
|
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>
|
template<bool deflateSupported>
|
||||||
struct stream_base
|
struct stream_base : stream_prng
|
||||||
{
|
{
|
||||||
// State information for the permessage-deflate extension
|
// State information for the permessage-deflate extension
|
||||||
struct pmd_type
|
struct pmd_type
|
||||||
@@ -165,7 +362,7 @@ struct stream_base
|
|||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct stream_base<false>
|
struct stream_base<false> : stream_prng
|
||||||
{
|
{
|
||||||
// These stubs are for avoiding linking in the zlib
|
// These stubs are for avoiding linking in the zlib
|
||||||
// code when permessage-deflate is not enabled.
|
// code when permessage-deflate is not enabled.
|
||||||
|
@@ -556,7 +556,7 @@ write_close(DynamicBuffer& db, close_reason const& cr)
|
|||||||
if(role_ == role_type::client)
|
if(role_ == role_type::client)
|
||||||
{
|
{
|
||||||
fh.mask = true;
|
fh.mask = true;
|
||||||
fh.key = wr_gen_();
|
fh.key = this->create_mask();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -608,7 +608,7 @@ write_ping(DynamicBuffer& db,
|
|||||||
fh.len = data.size();
|
fh.len = data.size();
|
||||||
fh.mask = role_ == role_type::client;
|
fh.mask = role_ == role_type::client;
|
||||||
if(fh.mask)
|
if(fh.mask)
|
||||||
fh.key = wr_gen_();
|
fh.key = this->create_mask();
|
||||||
detail::write(db, fh);
|
detail::write(db, fh);
|
||||||
if(data.empty())
|
if(data.empty())
|
||||||
return;
|
return;
|
||||||
@@ -641,7 +641,7 @@ build_request(detail::sec_ws_key_type& key,
|
|||||||
req.set(http::field::host, host);
|
req.set(http::field::host, host);
|
||||||
req.set(http::field::upgrade, "websocket");
|
req.set(http::field::upgrade, "websocket");
|
||||||
req.set(http::field::connection, "upgrade");
|
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_key, key);
|
||||||
req.set(http::field::sec_websocket_version, "13");
|
req.set(http::field::sec_websocket_version, "13");
|
||||||
build_request_pmd(req, is_deflate_supported{});
|
build_request_pmd(req, is_deflate_supported{});
|
||||||
|
@@ -406,7 +406,7 @@ operator()(
|
|||||||
remain_ = buffer_size(cb_);
|
remain_ = buffer_size(cb_);
|
||||||
fh_.fin = fin_;
|
fh_.fin = fin_;
|
||||||
fh_.len = remain_;
|
fh_.len = remain_;
|
||||||
fh_.key = ws_.wr_gen_();
|
fh_.key = ws_.create_mask();
|
||||||
detail::prepare_key(key_, fh_.key);
|
detail::prepare_key(key_, fh_.key);
|
||||||
ws_.wr_fb_.reset();
|
ws_.wr_fb_.reset();
|
||||||
detail::write<flat_static_buffer_base>(
|
detail::write<flat_static_buffer_base>(
|
||||||
@@ -458,7 +458,7 @@ operator()(
|
|||||||
n = clamp(remain_, ws_.wr_buf_size_);
|
n = clamp(remain_, ws_.wr_buf_size_);
|
||||||
remain_ -= n;
|
remain_ -= n;
|
||||||
fh_.len = n;
|
fh_.len = n;
|
||||||
fh_.key = ws_.wr_gen_();
|
fh_.key = ws_.create_mask();
|
||||||
fh_.fin = fin_ ? remain_ == 0 : false;
|
fh_.fin = fin_ ? remain_ == 0 : false;
|
||||||
detail::prepare_key(key_, fh_.key);
|
detail::prepare_key(key_, fh_.key);
|
||||||
buffer_copy(buffer(
|
buffer_copy(buffer(
|
||||||
@@ -521,7 +521,7 @@ operator()(
|
|||||||
}
|
}
|
||||||
if(fh_.mask)
|
if(fh_.mask)
|
||||||
{
|
{
|
||||||
fh_.key = ws_.wr_gen_();
|
fh_.key = ws_.create_mask();
|
||||||
detail::prepared_key key;
|
detail::prepared_key key;
|
||||||
detail::prepare_key(key, fh_.key);
|
detail::prepare_key(key, fh_.key);
|
||||||
detail::mask_inplace(b, key);
|
detail::mask_inplace(b, key);
|
||||||
@@ -666,7 +666,7 @@ write_some(bool fin,
|
|||||||
}
|
}
|
||||||
if(fh.mask)
|
if(fh.mask)
|
||||||
{
|
{
|
||||||
fh.key = wr_gen_();
|
fh.key = this->create_mask();
|
||||||
detail::prepared_key key;
|
detail::prepared_key key;
|
||||||
detail::prepare_key(key, fh.key);
|
detail::prepare_key(key, fh.key);
|
||||||
detail::mask_inplace(b, key);
|
detail::mask_inplace(b, key);
|
||||||
@@ -740,7 +740,7 @@ write_some(bool fin,
|
|||||||
// mask, no autofrag
|
// mask, no autofrag
|
||||||
fh.fin = fin;
|
fh.fin = fin;
|
||||||
fh.len = remain;
|
fh.len = remain;
|
||||||
fh.key = wr_gen_();
|
fh.key = this->create_mask();
|
||||||
detail::prepared_key key;
|
detail::prepared_key key;
|
||||||
detail::prepare_key(key, fh.key);
|
detail::prepare_key(key, fh.key);
|
||||||
detail::fh_buffer fh_buf;
|
detail::fh_buffer fh_buf;
|
||||||
@@ -784,7 +784,7 @@ write_some(bool fin,
|
|||||||
ConstBufferSequence> cb{buffers};
|
ConstBufferSequence> cb{buffers};
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
fh.key = wr_gen_();
|
fh.key = this->create_mask();
|
||||||
detail::prepared_key key;
|
detail::prepared_key key;
|
||||||
detail::prepare_key(key, fh.key);
|
detail::prepare_key(key, fh.key);
|
||||||
auto const n = clamp(remain, wr_buf_size_);
|
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
|
std::size_t wr_buf_opt_ // write buffer size option setting
|
||||||
= 4096;
|
= 4096;
|
||||||
detail::fh_buffer wr_fb_; // header buffer used for writes
|
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_rd_; // paused read op
|
||||||
detail::pausation paused_wr_; // paused write op
|
detail::pausation paused_wr_; // paused write op
|
||||||
@@ -633,6 +632,37 @@ public:
|
|||||||
return rd_msg_max_;
|
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.
|
/** Set the write buffer size option.
|
||||||
|
|
||||||
Sets the size of the write buffer used by the implementation to
|
Sets the size of the write buffer used by the implementation to
|
||||||
@@ -3563,6 +3593,36 @@ private:
|
|||||||
error_code& ec);
|
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
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
} // boost
|
} // boost
|
||||||
|
@@ -23,7 +23,6 @@ add_executable (tests-beast-websocket
|
|||||||
error.cpp
|
error.cpp
|
||||||
frame.cpp
|
frame.cpp
|
||||||
handshake.cpp
|
handshake.cpp
|
||||||
mask.cpp
|
|
||||||
option.cpp
|
option.cpp
|
||||||
ping.cpp
|
ping.cpp
|
||||||
read1.cpp
|
read1.cpp
|
||||||
|
@@ -13,7 +13,6 @@ local SOURCES =
|
|||||||
error.cpp
|
error.cpp
|
||||||
frame.cpp
|
frame.cpp
|
||||||
handshake.cpp
|
handshake.cpp
|
||||||
mask.cpp
|
|
||||||
option.cpp
|
option.cpp
|
||||||
ping.cpp
|
ping.cpp
|
||||||
read1.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
|
void
|
||||||
testOptions()
|
testOptions()
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
std::seed_seq ss{42};
|
||||||
|
seed_prng(ss);
|
||||||
|
}
|
||||||
|
|
||||||
stream<test::stream> ws{ioc_};
|
stream<test::stream> ws{ioc_};
|
||||||
ws.auto_fragment(true);
|
ws.auto_fragment(true);
|
||||||
ws.write_buffer_size(2048);
|
ws.write_buffer_size(2048);
|
||||||
@@ -37,6 +42,9 @@ public:
|
|||||||
pass();
|
pass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ws.secure_prng(true);
|
||||||
|
ws.secure_prng(false);
|
||||||
|
|
||||||
auto const bad =
|
auto const bad =
|
||||||
[&](permessage_deflate const& pmd)
|
[&](permessage_deflate const& pmd)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user