mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-29 23:45:51 +00:00
Tidy up hardened_hash:
* Added siphash as a HashAlgorithm * Refactored class responsibilities
This commit is contained in:
committed by
Vinnie Falco
parent
11d667c830
commit
6a1071ccd3
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
166
beast/container/impl/siphash.cpp
Normal file
166
beast/container/impl/siphash.cpp
Normal 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
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user