From 6a1071ccd370adf04fef27f59ecf93ce3c0a7b82 Mon Sep 17 00:00:00 2001 From: Howard Hinnant Date: Mon, 28 Jul 2014 08:59:16 -0700 Subject: [PATCH] Tidy up hardened_hash: * Added siphash as a HashAlgorithm * Refactored class responsibilities --- beast/container/Container.unity.cpp | 1 + beast/container/hardened_hash.h | 174 +++++++++---------- beast/container/hash_append.h | 54 +++++- beast/container/impl/siphash.cpp | 166 ++++++++++++++++++ beast/container/tests/hardened_hash.test.cpp | 12 +- beast/crypto/UnsignedInteger.h | 2 +- beast/utility/static_initializer.h | 36 ++++ 7 files changed, 344 insertions(+), 101 deletions(-) create mode 100644 beast/container/impl/siphash.cpp diff --git a/beast/container/Container.unity.cpp b/beast/container/Container.unity.cpp index 994129dff..d631d45ea 100644 --- a/beast/container/Container.unity.cpp +++ b/beast/container/Container.unity.cpp @@ -22,6 +22,7 @@ #endif #include +#include #include #include diff --git a/beast/container/hardened_hash.h b/beast/container/hardened_hash.h index 2c632d06a..612fb3c9f 100644 --- a/beast/container/hardened_hash.h +++ b/beast/container/hardened_hash.h @@ -21,16 +21,17 @@ #define BEAST_CONTAINER_HARDENED_HASH_H_INCLUDED #include - +#include // +#include // #include +#include + #include #include #include #include -#include // #include #include -#include // // When set to 1, makes the seed per-process instead // of per default-constructed instance of hardened_hash @@ -44,78 +45,86 @@ #endif namespace beast { -namespace detail { -template -class hardened_hash_base +using seed_pair = std::pair; + +template +seed_pair +get_seed_pair() noexcept { -public: - typedef Result result_type; + struct state_t + { + std::mutex mutex; + std::random_device rng; + std::mt19937_64 gen {rng()}; + std::uniform_int_distribution dist; -private: + state_t() : gen(rng()) {} + // state_t(state_t const&) = delete; + // state_t& operator=(state_t const&) = delete; + }; + static static_initializer state; + std::lock_guard lock (state->mutex); + return {state->dist(state->gen), state->dist(state->gen)}; +} + +template +class basic_hardened_hash; + +/** + * Seed functor once per process +*/ +template +class basic_hardened_hash +{ static + seed_pair const& + init_seed_pair() + { + static static_initializer const + p(get_seed_pair<>()); + return *p; + } + +public: + using result_type = typename HashAlgorithm::result_type; + + template result_type - next_seed() noexcept + operator()(T const& t) const noexcept { - static std::mutex mutex; - static std::random_device rng; - static std::mt19937_64 gen (rng()); - std::lock_guard lock (mutex); - std::uniform_int_distribution dist; - result_type value; - for(;;) - { - value = dist (gen); - // VFALCO Do we care if 0 is picked? - if (value != 0) - break; - } - return value; + std::uint64_t seed0; + std::uint64_t seed1; + std::tie(seed0, seed1) = init_seed_pair(); + HashAlgorithm h(seed0, seed1); + hash_append(h, t); + return static_cast(h); } - -#if BEAST_NO_HARDENED_HASH_INSTANCE_SEED -protected: - hardened_hash_base() noexcept = default; - - hardened_hash_base(result_type) noexcept - { - } - - result_type - seed() const noexcept - { - static result_type const value (next_seed()); - return value; - } - -#else -protected: - hardened_hash_base() noexcept - : m_seed (next_seed()) - { - } - - hardened_hash_base(result_type seed) noexcept - : m_seed (seed) - { - } - - result_type - seed() const noexcept - { - return m_seed; - } - -private: - // VFALCO Should seed be per process or per hash function? - result_type m_seed; - -#endif }; -//------------------------------------------------------------------------------ +/** + * Seed functor once per construction +*/ +template +class basic_hardened_hash +{ + seed_pair m_seeds; +public: + using result_type = typename HashAlgorithm::result_type; -} // detail + basic_hardened_hash() + : m_seeds(get_seed_pair<>()) + {} + + template + result_type + operator()(T const& t) const noexcept + { + HashAlgorithm h(m_seeds.first, m_seeds.second); + hash_append(h, t); + return static_cast(h); + } +}; //------------------------------------------------------------------------------ @@ -140,31 +149,18 @@ private: } @endcode + + Do not use any version of Murmur or CityHash for the Hasher + template parameter (the hashing algorithm). For details + see https://131002.net/siphash/#at */ -template -class hardened_hash - : public detail::hardened_hash_base -{ - typedef detail::hardened_hash_base base; -public: - typedef T argument_type; - using detail::hardened_hash_base ::result_type; - -public: - hardened_hash() = default; - explicit hardened_hash(result_type seed) - : base (seed) - { - } - - result_type - operator() (argument_type const& key) const noexcept - { - Hasher h {base::seed()}; - hash_append (h, key); - return static_cast (h); - } -}; +#if BEAST_NO_HARDENED_HASH_INSTANCE_SEED +template + using hardened_hash = basic_hardened_hash; +#else +template + using hardened_hash = basic_hardened_hash; +#endif } // beast diff --git a/beast/container/hash_append.h b/beast/container/hash_append.h index fa5a73454..b8d195154 100644 --- a/beast/container/hash_append.h +++ b/beast/container/hash_append.h @@ -656,16 +656,38 @@ hash_append (Hasher& h, T0 const& t0, T1 const& t1, T const& ...t) noexcept hash_append (h, t1, t...); } -namespace detail +// See http://www.isthe.com/chongo/tech/comp/fnv/ +class fnv1a { + std::uint64_t state_ = 14695981039346656037ULL; +public: -class spooky_wrapper + using result_type = std::size_t; + + void + append (void const* key, std::size_t len) noexcept + { + unsigned char const* p = static_cast(key); + unsigned char const* const e = p + len; + for (; p < e; ++p) + state_ = (state_ ^ *p) * 1099511628211ULL; + } + + explicit + operator std::size_t() noexcept + { + return static_cast(state_); + } +}; + +// See http://burtleburtle.net/bob/hash/spooky.html +class spooky { SpookyHash state_; public: using result_type = std::size_t; - spooky_wrapper (std::size_t seed1 = 1, std::size_t seed2 = 2) noexcept + spooky (std::size_t seed1 = 1, std::size_t seed2 = 2) noexcept { state_.Init (seed1, seed2); } @@ -685,9 +707,30 @@ public: } }; -} // detail +// See https://131002.net/siphash/ +class siphash +{ + std::uint64_t v0_ = 0x736f6d6570736575ULL; + std::uint64_t v1_ = 0x646f72616e646f6dULL; + std::uint64_t v2_ = 0x6c7967656e657261ULL; + std::uint64_t v3_ = 0x7465646279746573ULL; + unsigned char buf_[8]; + unsigned bufsize_ = 0; + unsigned total_length_ = 0; +public: + using result_type = std::size_t; -template + siphash() = default; + explicit siphash(std::uint64_t k0, std::uint64_t k1 = 0) noexcept; + + void + append (void const* key, std::size_t len) noexcept; + + explicit + operator std::size_t() noexcept; +}; + +template struct uhash { using result_type = typename Hasher::result_type; @@ -702,6 +745,7 @@ struct uhash } }; + } // beast #endif diff --git a/beast/container/impl/siphash.cpp b/beast/container/impl/siphash.cpp new file mode 100644 index 000000000..ef5181b4d --- /dev/null +++ b/beast/container/impl/siphash.cpp @@ -0,0 +1,166 @@ +//------------------------------- siphash.h ------------------------------------ +// +// This software is in the public domain. The only restriction on its use is +// that no one can remove it from the public domain by claiming ownership of it, +// including the original authors. +// +// There is no warranty of correctness on the software contained herein. Use +// at your own risk. +// +// Derived from: +// +// SipHash reference C implementation +// +// Written in 2012 by Jean-Philippe Aumasson +// Daniel J. Bernstein +// +// To the extent possible under law, the author(s) have dedicated all copyright +// and related and neighboring rights to this software to the public domain +// worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication along +// with this software. If not, see +// . +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include + +// namespace acme is used to demonstrate example code. It is not proposed. + +namespace beast +{ + +namespace +{ + +typedef std::uint64_t u64; +typedef std::uint32_t u32; +typedef std::uint8_t u8; + +inline +u64 +rotl(u64 x, u64 b) +{ + return (x << b) | (x >> (64 - b)); +} + +inline +u64 +u8to64_le(const u8* p) +{ +#if BEAST_LITTLE_ENDIAN + return *static_cast(static_cast(p)); +#else + return static_cast(p[7]) << 56 | static_cast(p[6]) << 48 | + static_cast(p[5]) << 40 | static_cast(p[4]) << 32 | + static_cast(p[3]) << 24 | static_cast(p[2]) << 16 | + static_cast(p[1]) << 8 | static_cast(p[0]); +#endif +} + +inline +void +sipround(u64& v0, u64& v1, u64& v2, u64& v3) +{ + v0 += v1; + v1 = rotl(v1, 13); + v1 ^= v0; + v0 = rotl(v0, 32); + v2 += v3; + v3 = rotl(v3, 16); + v3 ^= v2; + v0 += v3; + v3 = rotl(v3, 21); + v3 ^= v0; + v2 += v1; + v1 = rotl(v1, 17); + v1 ^= v2; + v2 = rotl(v2, 32); +} + +} // unnamed + +siphash::siphash(std::uint64_t k0, std::uint64_t k1) noexcept +{ + v3_ ^= k1; + v2_ ^= k0; + v1_ ^= k1; + v0_ ^= k0; +} + +void +siphash::append (void const* key, std::size_t inlen) noexcept +{ + u8 const* in = static_cast(key); + total_length_ += inlen; + if (bufsize_ + inlen < 8) + { + std::copy(in, in+inlen, buf_ + bufsize_); + bufsize_ += inlen; + return; + } + if (bufsize_ > 0) + { + auto t = 8 - bufsize_; + std::copy(in, in+t, buf_ + bufsize_); + u64 m = u8to64_le( buf_ ); + v3_ ^= m; + sipround(v0_, v1_, v2_, v3_); + sipround(v0_, v1_, v2_, v3_); + v0_ ^= m; + in += t; + inlen -= t; + } + bufsize_ = inlen & 7; + u8 const* const end = in + (inlen - bufsize_); + for ( ; in != end; in += 8 ) + { + u64 m = u8to64_le( in ); + v3_ ^= m; + sipround(v0_, v1_, v2_, v3_); + sipround(v0_, v1_, v2_, v3_); + v0_ ^= m; + } + std::copy(end, end + bufsize_, buf_); +} + +siphash::operator std::size_t() noexcept +{ + std::size_t b = static_cast(total_length_) << 56; + switch(bufsize_) + { + case 7: + b |= static_cast(buf_[6]) << 48; + case 6: + b |= static_cast(buf_[5]) << 40; + case 5: + b |= static_cast(buf_[4]) << 32; + case 4: + b |= static_cast(buf_[3]) << 24; + case 3: + b |= static_cast(buf_[2]) << 16; + case 2: + b |= static_cast(buf_[1]) << 8; + case 1: + b |= static_cast(buf_[0]); + case 0: + break; + } + v3_ ^= b; + sipround(v0_, v1_, v2_, v3_); + sipround(v0_, v1_, v2_, v3_); + v0_ ^= b; + v2_ ^= 0xff; + sipround(v0_, v1_, v2_, v3_); + sipround(v0_, v1_, v2_, v3_); + sipround(v0_, v1_, v2_, v3_); + sipround(v0_, v1_, v2_, v3_); + b = v0_ ^ v1_ ^ v2_ ^ v3_; + return b; +} + +} // beast diff --git a/beast/container/tests/hardened_hash.test.cpp b/beast/container/tests/hardened_hash.test.cpp index f60f18bed..a90aaa1ee 100644 --- a/beast/container/tests/hardened_hash.test.cpp +++ b/beast/container/tests/hardened_hash.test.cpp @@ -90,19 +90,19 @@ namespace detail { template using test_hardened_unordered_set = - std::unordered_set >; + std::unordered_set >; template using test_hardened_unordered_map = - std::unordered_map >; + std::unordered_map >; template using test_hardened_unordered_multiset = - std::unordered_multiset >; + std::unordered_multiset >; template using test_hardened_unordered_multimap = - std::unordered_multimap >; + std::unordered_multimap >; } // beast @@ -196,7 +196,7 @@ public: check () { T t{}; - hardened_hash () (t); + hardened_hash <>() (t); pass(); } @@ -280,7 +280,7 @@ public: log << "sizeof(std::size_t) == " << sizeof(std::size_t); - hardened_hash h; + hardened_hash <> h; for (int i = 0; i < 100; ++i) { sha256_t v (sha256_t::from_number (i)); diff --git a/beast/crypto/UnsignedInteger.h b/beast/crypto/UnsignedInteger.h index 99346be6b..de2adbfd4 100644 --- a/beast/crypto/UnsignedInteger.h +++ b/beast/crypto/UnsignedInteger.h @@ -65,7 +65,7 @@ public: attackers from exploiting crafted inputs to produce degenerate containers. */ - typedef hardened_hash hasher; + typedef hardened_hash <> hasher; /** Determins if two UnsignedInteger objects are equal. */ class equal diff --git a/beast/utility/static_initializer.h b/beast/utility/static_initializer.h index 2033dd39c..237af40b4 100644 --- a/beast/utility/static_initializer.h +++ b/beast/utility/static_initializer.h @@ -83,17 +83,35 @@ public: T& get() noexcept; + T const& + get() const noexcept + { + return const_cast(*this).get(); + } + T& operator*() noexcept { return get(); } + T const& + operator*() const noexcept + { + return get(); + } + T* operator->() noexcept { return &get(); } + + T const* + operator->() const noexcept + { + return &get(); + } }; //------------------------------------------------------------------------------ @@ -187,17 +205,35 @@ public: return *instance_; } + T const& + get() const noexcept + { + return const_cast(*this).get(); + } + T& operator*() noexcept { return get(); } + T const& + operator*() const noexcept + { + return get(); + } + T* operator->() noexcept { return &get(); } + + T const* + operator->() const noexcept + { + return &get(); + } }; template