Fix big-endian websocket masking

fix #1030
This commit is contained in:
Vinnie Falco
2018-02-20 11:04:52 -08:00
parent 593ccb15cd
commit c505e32210
4 changed files with 33 additions and 152 deletions

View File

@ -1,6 +1,7 @@
Version 157: Version 157:
* Fix teardown for TIME_WAIT * Fix teardown for TIME_WAIT
* Fix big-endian websocket masking
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -105,6 +105,8 @@ to update to the latest Boost release.
* [issue 1024] Fix teardown for TIME_WAIT * [issue 1024] Fix teardown for TIME_WAIT
* [issue 1030] Fix big-endian websocket masking
[*API Changes] [*API Changes]
* Remove unintended public members of * Remove unintended public members of

View File

@ -11,6 +11,7 @@
#define BOOST_BEAST_WEBSOCKET_DETAIL_MASK_HPP #define BOOST_BEAST_WEBSOCKET_DETAIL_MASK_HPP
#include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <array> #include <array>
#include <climits> #include <climits>
@ -83,181 +84,59 @@ using maskgen = maskgen_t<std::minstd_rand>;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using prepared_key = using prepared_key = std::array<unsigned char, 4>;
std::conditional<sizeof(void*) == 8,
std::uint64_t, std::uint32_t>::type;
inline inline
void void
prepare_key(std::uint32_t& prepared, std::uint32_t key) prepare_key(prepared_key& prepared, std::uint32_t key)
{ {
prepared = key; prepared[0] = (key >> 0) & 0xff;
prepared[1] = (key >> 8) & 0xff;
prepared[2] = (key >> 16) & 0xff;
prepared[3] = (key >> 24) & 0xff;
} }
inline template<std::size_t N>
void void
prepare_key(std::uint64_t& prepared, std::uint32_t key) rol(std::array<unsigned char, N>& v, unsigned n)
{ {
prepared = auto v0 = v;
(static_cast<std::uint64_t>(key) << 32) | key; for(std::size_t i = 0; i < v.size(); ++i )
v[i] = v0[(i + n) % v.size()];
} }
template<class T> // Apply mask in place
inline
typename std::enable_if<std::is_integral<T>::value, T>::type
ror(T t, unsigned n = 1)
{
auto constexpr bits =
static_cast<unsigned>(
sizeof(T) * CHAR_BIT);
n &= bits-1;
return static_cast<T>((t << (bits - n)) | (
static_cast<typename std::make_unsigned<T>::type>(t) >> n));
}
// 32-bit optimized
// //
template<class = void> inline
void void
mask_inplace_fast( mask_inplace(boost::asio::mutable_buffer& b, prepared_key& key)
boost::asio::mutable_buffer const& b,
std::uint32_t& key)
{ {
auto n = b.size(); auto n = b.size();
auto p = reinterpret_cast<std::uint8_t*>(b.data()); auto mask = key; // avoid aliasing
if(n >= sizeof(key)) auto p = reinterpret_cast<unsigned char*>(b.data());
while(n >= 4)
{ {
// Bring p to 4-byte alignment for(int i = 0; i < 4; ++i)
auto const i = reinterpret_cast< p[i] ^= mask[i];
std::uintptr_t>(p) & (sizeof(key)-1); p += 4;
switch(i) n -= 4;
{
case 1: p[2] ^= static_cast<std::uint8_t>(key >> 16); BOOST_FALLTHROUGH;
case 2: p[1] ^= static_cast<std::uint8_t>(key >> 8); BOOST_FALLTHROUGH;
case 3: p[0] ^= static_cast<std::uint8_t>(key);
{
auto const d = static_cast<unsigned>(sizeof(key) - i);
key = ror(key, 8*d);
n -= d;
p += d;
BOOST_FALLTHROUGH;
}
default:
break;
}
} }
if(n > 0)
// Mask 4 bytes at a time
for(auto i = n / sizeof(key); i; --i)
{ {
*reinterpret_cast< for(std::size_t i = 0; i < n; ++i)
std::uint32_t*>(p) ^= key; p[i] ^= mask[i];
p += sizeof(key); rol(key, 4 - n);
} }
// Leftovers
n &= sizeof(key)-1;
switch(n)
{
case 3: p[2] ^= static_cast<std::uint8_t>(key >> 16); BOOST_FALLTHROUGH;
case 2: p[1] ^= static_cast<std::uint8_t>(key >> 8); BOOST_FALLTHROUGH;
case 1: p[0] ^= static_cast<std::uint8_t>(key);
key = ror(key, static_cast<unsigned>(8*n));
BOOST_FALLTHROUGH;
default:
break;
}
}
// 64-bit optimized
//
template<class = void>
void
mask_inplace_fast(
boost::asio::mutable_buffer const& b,
std::uint64_t& key)
{
auto n = b.size();
auto p = reinterpret_cast<std::uint8_t*>(b.data());
if(n >= sizeof(key))
{
// Bring p to 8-byte alignment
auto const i = reinterpret_cast<
std::uintptr_t>(p) & (sizeof(key)-1);
switch(i)
{
case 1: p[6] ^= static_cast<std::uint8_t>(key >> 48); BOOST_FALLTHROUGH;
case 2: p[5] ^= static_cast<std::uint8_t>(key >> 40); BOOST_FALLTHROUGH;
case 3: p[4] ^= static_cast<std::uint8_t>(key >> 32); BOOST_FALLTHROUGH;
case 4: p[3] ^= static_cast<std::uint8_t>(key >> 24); BOOST_FALLTHROUGH;
case 5: p[2] ^= static_cast<std::uint8_t>(key >> 16); BOOST_FALLTHROUGH;
case 6: p[1] ^= static_cast<std::uint8_t>(key >> 8); BOOST_FALLTHROUGH;
case 7: p[0] ^= static_cast<std::uint8_t>(key);
{
auto const d = static_cast<
unsigned>(sizeof(key) - i);
key = ror(key, 8*d);
n -= d;
p += d;
BOOST_FALLTHROUGH;
}
default:
break;
}
}
// Mask 8 bytes at a time
for(auto i = n / sizeof(key); i; --i)
{
*reinterpret_cast<
std::uint64_t*>(p) ^= key;
p += sizeof(key);
}
// Leftovers
n &= sizeof(key)-1;
switch(n)
{
case 7: p[6] ^= static_cast<std::uint8_t>(key >> 48); BOOST_FALLTHROUGH;
case 6: p[5] ^= static_cast<std::uint8_t>(key >> 40); BOOST_FALLTHROUGH;
case 5: p[4] ^= static_cast<std::uint8_t>(key >> 32); BOOST_FALLTHROUGH;
case 4: p[3] ^= static_cast<std::uint8_t>(key >> 24); BOOST_FALLTHROUGH;
case 3: p[2] ^= static_cast<std::uint8_t>(key >> 16); BOOST_FALLTHROUGH;
case 2: p[1] ^= static_cast<std::uint8_t>(key >> 8); BOOST_FALLTHROUGH;
case 1: p[0] ^= static_cast<std::uint8_t>(key);
key = ror(key, static_cast<unsigned>(8*n));
BOOST_FALLTHROUGH;
default:
break;
}
}
inline
void
mask_inplace(
boost::asio::mutable_buffer const& b,
std::uint32_t& key)
{
mask_inplace_fast(b, key);
}
inline
void
mask_inplace(
boost::asio::mutable_buffer const& b,
std::uint64_t& key)
{
mask_inplace_fast(b, key);
} }
// Apply mask in place // Apply mask in place
// //
template<class MutableBuffers, class KeyType> template<class MutableBuffers, class KeyType>
void void
mask_inplace( mask_inplace(MutableBuffers const& bs, KeyType& key)
MutableBuffers const& bs, KeyType& key)
{ {
for(boost::asio::mutable_buffer b : bs) for(boost::asio::mutable_buffer b :
beast::detail::buffers_range(bs))
mask_inplace(b, key); mask_inplace(b, key);
} }

View File

@ -166,8 +166,7 @@ class stream
std::uint64_t rd_remain_ // message frame bytes left in current frame std::uint64_t rd_remain_ // message frame bytes left in current frame
= 0; = 0;
detail::frame_header rd_fh_; // current frame header detail::frame_header rd_fh_; // current frame header
detail::prepared_key rd_key_ // current stateful mask key detail::prepared_key rd_key_; // current stateful mask key
= 0;
detail::frame_buffer rd_fb_; // to write control frames (during reads) detail::frame_buffer rd_fb_; // to write control frames (during reads)
detail::utf8_checker rd_utf8_; // to validate utf8 detail::utf8_checker rd_utf8_; // to validate utf8
static_buffer< static_buffer<