Unordered: Initial stab at siphash example.

[SVN r81973]
This commit is contained in:
Daniel James
2012-12-15 16:42:44 +00:00
parent 64994d76dd
commit bf1f24ab04
6 changed files with 413 additions and 0 deletions

View File

@ -0,0 +1,18 @@
# Copyright 2012 Daniel James.
# 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)
import testing ;
project unordered-test/example/siphash
: requirements
<warnings>all
<toolset>intel:<warnings>on
<toolset>gcc:<cxxflags>"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter -Wconversion -Wno-long-long -Wfloat-equal"
<toolset>darwin:<cxxflags>"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter -Wconversion -Wfloat-equal"
;
lib siphash : siphash.cpp siphash_generate.cpp /boost/random//boost_random ;
run siphash_example.cpp siphash ;

View File

@ -0,0 +1,35 @@
Siphash example
---------------
Most of the commonly used hash functions are vunerable to
'hash-flooding' attacks. These can happen if the table is filled with
values which are likely to cause hash collisions. SipHash[1] is a fast
hash function proposed by Jean-Philippe Aumasson and Daniel J.
Bernstein2 to resist these attacks. While young and relatively untested,
it's been demonstrated that it's less vulnerable to attacks than popular
hash functions, such as MurmurHash and CityHash.
This a nice example, as it has a distinct practial use to boost::hash
and demonstrates a hash function with state. It also shows how a generic
hash function might be built on top of a binary hash function.
Note that this is an example, and far from fully polished. Some issues
with the current implementation:
- It's slow.
- Only works on little endian machines.
- Only supports a subset of the standard library.
I should improve it a bit before it's properly released. But if you wish
to implement something better, there are several C implementations
listed on the SipHash page[1]. I probably won't even try to compete with
them. If anyone is willing to implement a high quality SipHash library for
boost, that would be very much appreciated.
[1] https://131002.net/siphash/
--------------------------------------------------------------------------------
Copyright 2012 Daniel James.
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)

View File

@ -0,0 +1,91 @@
// Copyright 2012 Daniel James.
// 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)
// This is also released into the public domain.
#include "siphash.hpp"
namespace hash
{
const unsigned siphash_c = 2;
const unsigned siphash_d = 4;
siphash_state::siphash_state(sipkey const& key) :
v0(0x736f6d6570736575ull ^ key.k0),
v1(0x646f72616e646f6dull ^ key.k1),
v2(0x6c7967656e657261ull ^ key.k0),
v3(0x7465646279746573ull ^ key.k1),
buffered(0), b(0)
{
}
namespace
{
inline boost::uint64_t rotate_left(boost::uint64_t x, unsigned c)
{
return (x << c) | (x >> (64-c));
}
}
void siphash_state::sip_round(unsigned rounds)
{
for (unsigned i = 0; i < rounds; ++i) {
v0 += v1;
v1 = rotate_left(v1, 13);
v1 ^= v0;
v0 = rotate_left(v0, 32);
v2 += v3;
v3 = rotate_left(v3, 16);
v3 ^= v2;
v2 += v1;
v1 = rotate_left(v1, 17);
v1 ^= v2;
v2 = rotate_left(v2, 32);
v0 += v3;
v3 = rotate_left(v3, 21);
v3 ^= v0;
}
}
void siphash_state::update(void const* data, unsigned length)
{
char const* ptr = static_cast<char const*>(data);
while (length > 0) {
buffer[buffered++] = *ptr++;
++b;
--length;
if (buffered == 8)
{
v3 ^= m;
sip_round(siphash_c);
v0 ^= m;
buffered = 0;
}
}
}
boost::uint64_t siphash_state::finalize()
{
// Compress the final 8 bytes.
while (buffered < 7) buffer[buffered++] = 0;
buffer[7] = b;
v3 ^= m;
sip_round(siphash_c);
v0 ^= m;
// Finalize the hash
v2 ^= 0xff;
sip_round(siphash_d);
return v0 ^ v1 ^ v2 ^ v3;
}
}

View File

@ -0,0 +1,204 @@
// Copyright 2012 Daniel James.
// 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)
// This is also released into the public domain.
#if !defined(BOOST_HASH_EXAMPLES_SIPHASH_HEADER)
#define BOOST_HASH_EXAMPLES_SIPHASH_HEADER
#include <boost/cstdint.hpp>
#include <boost/utility/enable_if.hpp>
#include <string>
#include <vector>
#include <list>
namespace hash
{
// Random key used to make sure the hash function isn't predictable.
struct sipkey
{
boost::uint64_t k0, k1;
};
// Generate a secure sipkey.
// This only works when boost::random_device is available.
sipkey generate_sipkey();
// This is the class that does the actual hashing.
struct siphash_state
{
boost::uint64_t v0, v1, v2, v3;
union {
boost::uint64_t m;
unsigned char buffer[8];
};
unsigned char buffered;
unsigned char b;
explicit siphash_state(sipkey const&);
void update(void const*, unsigned);
boost::uint64_t finalize();
private:
void sip_round(unsigned);
};
// The genereric hash function.
//
// Don't sepecialize this. Unless you really want to.
template <typename T>
struct siphash
{
sipkey key;
/* implicit */ siphash(sipkey const& k) : key(k) {}
std::size_t operator()(T const&) const;
};
// Add support for a type by specializing this class.
//
// 'Enable' is used as a SFINAE hook.
template <typename T, typename Enable = void>
struct siphash_impl
{
static void update(siphash_state&, T const&);
};
// The implementation of the generic hash function.
template <typename T>
std::size_t siphash<T>::operator()(T const& x) const
{
siphash_state state(key);
siphash_impl<T>::update(state, x);
return static_cast<std::size_t>(state.finalize());
}
// A couple of basic traits for hashing binary data.
struct enable_siphash_false { enum { value = false }; };
struct enable_siphash_true { enum { value = true }; };
template <typename T>
struct enable_siphash_binary : enable_siphash_false {};
template <typename T>
struct enable_siphash_binary_array
{
enum { value = enable_siphash_binary<T>::value &&
sizeof(T[2]) == sizeof(T) * 2 };
};
// Some general purpose hash implementations, siphash_impl<T>
// can inherit from these.
template <typename T>
struct siphash_binary_impl
{
static void update(siphash_state& state, int x)
{
state.update(&x, sizeof(x));
}
};
template <typename T>
struct siphash_container_impl
{
static void update(siphash_state& state, T const& x)
{
siphash_impl<typename T::value_type> value_impl;
for (typename T::const_iterator begin = x.begin(),
end = x.end(); begin != end; ++begin)
{
value_impl.update(state, *begin);
}
}
};
template <typename T, bool Enable = enable_siphash_binary_array<T>::value>
struct siphash_binary_container_impl;
template <typename T>
struct siphash_binary_container_impl<T, false> :
siphash_container_impl<T> {};
template <typename T>
struct siphash_binary_container_impl<T, true>
{
static void update(siphash_state& state, T const& x)
{
state.update(&*x.cbegin(), sizeof(T) * x.size());
}
};
// Specialize siphash_impl for various types.
template <typename T>
struct siphash_impl<T,
typename boost::enable_if_c<enable_siphash_binary<T>::value>::type
> : siphash_binary_impl<T> {};
template <typename T, typename Alloc>
struct siphash_impl<std::list<T, Alloc> > :
siphash_container_impl<T> {};
template <typename T, typename Alloc>
struct siphash_impl<std::vector<T, Alloc> > :
siphash_binary_container_impl<T> {};
template <typename T, typename Alloc>
struct siphash_impl<std::basic_string<T, std::char_traits<T>, Alloc> > :
siphash_binary_container_impl<T> {};
// Specialize the binary trait for builtin types.
template <> struct enable_siphash_binary<bool> :
enable_siphash_true {};
template <> struct enable_siphash_binary<char> :
enable_siphash_true {};
template <> struct enable_siphash_binary<unsigned char> :
enable_siphash_true {};
template <> struct enable_siphash_binary<signed char> :
enable_siphash_true {};
template <> struct enable_siphash_binary<short> :
enable_siphash_true {};
template <> struct enable_siphash_binary<unsigned short> :
enable_siphash_true {};
template <> struct enable_siphash_binary<int> :
enable_siphash_true {};
template <> struct enable_siphash_binary<unsigned int> :
enable_siphash_true {};
template <> struct enable_siphash_binary<long> :
enable_siphash_true {};
template <> struct enable_siphash_binary<unsigned long> :
enable_siphash_true {};
#if !defined(BOOST_NO_INTRINSIC_WCHAR_T)
template <> struct enable_siphash_binary<wchar_t> :
enable_siphash_true {};
#endif
#if !defined(BOOST_NO_LONG_LONG)
template <> struct enable_siphash_binary<boost::long_long_type> :
enable_siphash_true {};
template <> struct enable_siphash_binary<boost::ulong_long_type> :
enable_siphash_true {};
#endif
#if defined(BOOST_HAS_INT128)
template <> struct enable_siphash_binary<boost::int128_type> :
boost::hash_detail::enable_hash_value {};
template <> struct enable_siphash_binary<boost::uint128_type> :
boost::hash_detail::enable_hash_value {};
#endif
}
#endif

View File

@ -0,0 +1,38 @@
// Copyright 2012 Daniel James.
// 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)
// This is also released into the public domain.
#include <boost/unordered_set.hpp>
#include <cassert>
#include "siphash.hpp"
int main()
{
boost::unordered_set<int, hash::siphash<int> > x1(0, hash::generate_sipkey());
boost::unordered_set<int, hash::siphash<int> > x2(0, hash::generate_sipkey());
assert(x1 == x2);
x1.insert(10);
x1.insert(30);
x1.insert(50);
assert(x1 != x2);
x2.insert(100);
assert(x1 != x2);
x1.insert(x2.begin(), x2.end());
x2.insert(x1.begin(), x1.end());
assert(x1 == x2);
assert(x1.bucket(10) != x2.bucket(10));
assert(x1.bucket(30) != x2.bucket(30));
x1 = x2;
assert(x1.bucket(10) == x2.bucket(10));
assert(x1.bucket(30) == x2.bucket(30));
}

View File

@ -0,0 +1,27 @@
// Copyright 2012 Daniel James.
// 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)
// This is also released into the public domain.
#include "siphash.hpp"
#include <boost/random/random_device.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/random/uniform_int.hpp>
namespace hash
{
sipkey generate_sipkey()
{
boost::random_device rng;
boost::variate_generator<boost::random_device&,
boost::uniform_int<boost::uint64_t> > gen(rng,
boost::uniform_int<boost::uint64_t>());
sipkey k;
k.k0 = gen();
k.k1 = gen();
return k;
}
}