Tidy up hardened_hash:

* Added siphash as a HashAlgorithm
* Refactored class responsibilities
This commit is contained in:
Howard Hinnant
2014-07-28 08:59:16 -07:00
committed by Vinnie Falco
parent 11d667c830
commit 6a1071ccd3
7 changed files with 344 additions and 101 deletions

View File

@@ -22,6 +22,7 @@
#endif
#include <beast/container/impl/spookyv2.cpp>
#include <beast/container/impl/siphash.cpp>
#include <beast/container/tests/aged_associative_container.test.cpp>
#include <beast/container/tests/buffer_view.test.cpp>

View File

@@ -21,16 +21,17 @@
#define BEAST_CONTAINER_HARDENED_HASH_H_INCLUDED
#include <beast/container/hash_append.h>
#include <beast/cxx14/utility.h> // <utility>
#include <beast/cxx14/type_traits.h> // <type_traits>
#include <beast/utility/noexcept.h>
#include <beast/utility/static_initializer.h>
#include <cstdint>
#include <functional>
#include <mutex>
#include <random>
#include <beast/cxx14/type_traits.h> // <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <beast/cxx14/utility.h> // <utility>
// 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 Result>
class hardened_hash_base
using seed_pair = std::pair<std::uint64_t, std::uint64_t>;
template <bool = true>
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 <std::uint64_t> dist;
private:
state_t() : gen(rng()) {}
// state_t(state_t const&) = delete;
// state_t& operator=(state_t const&) = delete;
};
static static_initializer <state_t> state;
std::lock_guard <std::mutex> lock (state->mutex);
return {state->dist(state->gen), state->dist(state->gen)};
}
template <class HashAlgorithm, bool ProcessSeeded>
class basic_hardened_hash;
/**
* Seed functor once per process
*/
template <class HashAlgorithm>
class basic_hardened_hash<HashAlgorithm, true>
{
static
seed_pair const&
init_seed_pair()
{
static static_initializer <seed_pair, basic_hardened_hash> const
p(get_seed_pair<>());
return *p;
}
public:
using result_type = typename HashAlgorithm::result_type;
template <class T>
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 <std::mutex> lock (mutex);
std::uniform_int_distribution <result_type> dist;
result_type value;
for(;;)
{
value = dist (gen);
// VFALCO Do we care if 0 is picked?
if (value != 0)
break;
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<result_type>(h);
}
return value;
}
#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 HashAlgorithm>
class basic_hardened_hash<HashAlgorithm, false>
{
seed_pair m_seeds;
public:
using result_type = typename HashAlgorithm::result_type;
} // detail
basic_hardened_hash()
: m_seeds(get_seed_pair<>())
{}
template <class T>
result_type
operator()(T const& t) const noexcept
{
HashAlgorithm h(m_seeds.first, m_seeds.second);
hash_append(h, t);
return static_cast<result_type>(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 T, class Hasher = detail::spooky_wrapper>
class hardened_hash
: public detail::hardened_hash_base <std::size_t>
{
typedef detail::hardened_hash_base <std::size_t> base;
public:
typedef T argument_type;
using detail::hardened_hash_base <std::size_t>::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<result_type> (h);
}
};
#if BEAST_NO_HARDENED_HASH_INSTANCE_SEED
template <class HashAlgorithm = siphash>
using hardened_hash = basic_hardened_hash<HashAlgorithm, true>;
#else
template <class HashAlgorithm = siphash>
using hardened_hash = basic_hardened_hash<HashAlgorithm, false>;
#endif
} // beast

View File

@@ -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<unsigned char const*>(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<std::size_t>(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 <class Hasher = detail::spooky_wrapper>
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 <class Hasher = spooky>
struct uhash
{
using result_type = typename Hasher::result_type;
@@ -702,6 +745,7 @@ struct uhash
}
};
} // beast
#endif

View File

@@ -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 <jeanphilippe.aumasson@gmail.com>
// Daniel J. Bernstein <djb@cr.yp.to>
//
// 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
// <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//------------------------------------------------------------------------------
#include <beast/container/hash_append.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
// 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<u64 const*>(static_cast<void const*>(p));
#else
return static_cast<u64>(p[7]) << 56 | static_cast<u64>(p[6]) << 48 |
static_cast<u64>(p[5]) << 40 | static_cast<u64>(p[4]) << 32 |
static_cast<u64>(p[3]) << 24 | static_cast<u64>(p[2]) << 16 |
static_cast<u64>(p[1]) << 8 | static_cast<u64>(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<const u8*>(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<u64>(total_length_) << 56;
switch(bufsize_)
{
case 7:
b |= static_cast<u64>(buf_[6]) << 48;
case 6:
b |= static_cast<u64>(buf_[5]) << 40;
case 5:
b |= static_cast<u64>(buf_[4]) << 32;
case 4:
b |= static_cast<u64>(buf_[3]) << 24;
case 3:
b |= static_cast<u64>(buf_[2]) << 16;
case 2:
b |= static_cast<u64>(buf_[1]) << 8;
case 1:
b |= static_cast<u64>(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

View File

@@ -90,19 +90,19 @@ namespace detail {
template <class T>
using test_hardened_unordered_set =
std::unordered_set <T, hardened_hash <T>>;
std::unordered_set <T, hardened_hash <>>;
template <class T>
using test_hardened_unordered_map =
std::unordered_map <T, int, hardened_hash <T>>;
std::unordered_map <T, int, hardened_hash <>>;
template <class T>
using test_hardened_unordered_multiset =
std::unordered_multiset <T, hardened_hash <T>>;
std::unordered_multiset <T, hardened_hash <>>;
template <class T>
using test_hardened_unordered_multimap =
std::unordered_multimap <T, int, hardened_hash <T>>;
std::unordered_multimap <T, int, hardened_hash <>>;
} // beast
@@ -196,7 +196,7 @@ public:
check ()
{
T t{};
hardened_hash <T>() (t);
hardened_hash <>() (t);
pass();
}
@@ -280,7 +280,7 @@ public:
log <<
"sizeof(std::size_t) == " << sizeof(std::size_t);
hardened_hash <sha256_t> h;
hardened_hash <> h;
for (int i = 0; i < 100; ++i)
{
sha256_t v (sha256_t::from_number (i));

View File

@@ -65,7 +65,7 @@ public:
attackers from exploiting crafted inputs to produce degenerate
containers.
*/
typedef hardened_hash <UnsignedInteger> hasher;
typedef hardened_hash <> hasher;
/** Determins if two UnsignedInteger objects are equal. */
class equal

View File

@@ -83,17 +83,35 @@ public:
T&
get() noexcept;
T const&
get() const noexcept
{
return const_cast<static_initializer&>(*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<static_initializer&>(*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 <class T, class Tag>