diff --git a/examples/siphash/Jamfile.v2 b/examples/siphash/Jamfile.v2 new file mode 100644 index 00000000..abf4d8c5 --- /dev/null +++ b/examples/siphash/Jamfile.v2 @@ -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 + all + intel:on + gcc:"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter -Wconversion -Wno-long-long -Wfloat-equal" + darwin:"-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 ; \ No newline at end of file diff --git a/examples/siphash/README.txt b/examples/siphash/README.txt new file mode 100644 index 00000000..f43b8d1a --- /dev/null +++ b/examples/siphash/README.txt @@ -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) diff --git a/examples/siphash/siphash.cpp b/examples/siphash/siphash.cpp new file mode 100644 index 00000000..aba1d7d1 --- /dev/null +++ b/examples/siphash/siphash.cpp @@ -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(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; + } +} \ No newline at end of file diff --git a/examples/siphash/siphash.hpp b/examples/siphash/siphash.hpp new file mode 100644 index 00000000..5b2dd176 --- /dev/null +++ b/examples/siphash/siphash.hpp @@ -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 +#include + +#include +#include +#include + +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 + 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 + struct siphash_impl + { + static void update(siphash_state&, T const&); + }; + + // The implementation of the generic hash function. + + template + std::size_t siphash::operator()(T const& x) const + { + siphash_state state(key); + siphash_impl::update(state, x); + return static_cast(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 + struct enable_siphash_binary : enable_siphash_false {}; + + template + struct enable_siphash_binary_array + { + enum { value = enable_siphash_binary::value && + sizeof(T[2]) == sizeof(T) * 2 }; + }; + + // Some general purpose hash implementations, siphash_impl + // can inherit from these. + + template + struct siphash_binary_impl + { + static void update(siphash_state& state, int x) + { + state.update(&x, sizeof(x)); + } + }; + + template + struct siphash_container_impl + { + static void update(siphash_state& state, T const& x) + { + siphash_impl value_impl; + + for (typename T::const_iterator begin = x.begin(), + end = x.end(); begin != end; ++begin) + { + value_impl.update(state, *begin); + } + } + }; + + template ::value> + struct siphash_binary_container_impl; + + template + struct siphash_binary_container_impl : + siphash_container_impl {}; + + template + struct siphash_binary_container_impl + { + static void update(siphash_state& state, T const& x) + { + state.update(&*x.cbegin(), sizeof(T) * x.size()); + } + }; + + // Specialize siphash_impl for various types. + + template + struct siphash_impl::value>::type + > : siphash_binary_impl {}; + + template + struct siphash_impl > : + siphash_container_impl {}; + + template + struct siphash_impl > : + siphash_binary_container_impl {}; + + template + struct siphash_impl, Alloc> > : + siphash_binary_container_impl {}; + + // Specialize the binary trait for builtin types. + + template <> struct enable_siphash_binary : + enable_siphash_true {}; + template <> struct enable_siphash_binary : + enable_siphash_true {}; + template <> struct enable_siphash_binary : + enable_siphash_true {}; + template <> struct enable_siphash_binary : + enable_siphash_true {}; + template <> struct enable_siphash_binary : + enable_siphash_true {}; + template <> struct enable_siphash_binary : + enable_siphash_true {}; + template <> struct enable_siphash_binary : + enable_siphash_true {}; + template <> struct enable_siphash_binary : + enable_siphash_true {}; + template <> struct enable_siphash_binary : + enable_siphash_true {}; + template <> struct enable_siphash_binary : + enable_siphash_true {}; + +#if !defined(BOOST_NO_INTRINSIC_WCHAR_T) + template <> struct enable_siphash_binary : + enable_siphash_true {}; +#endif + +#if !defined(BOOST_NO_LONG_LONG) + template <> struct enable_siphash_binary : + enable_siphash_true {}; + template <> struct enable_siphash_binary : + enable_siphash_true {}; +#endif + +#if defined(BOOST_HAS_INT128) + template <> struct enable_siphash_binary : + boost::hash_detail::enable_hash_value {}; + template <> struct enable_siphash_binary : + boost::hash_detail::enable_hash_value {}; +#endif +} + +#endif \ No newline at end of file diff --git a/examples/siphash/siphash_example.cpp b/examples/siphash/siphash_example.cpp new file mode 100644 index 00000000..ef58d3de --- /dev/null +++ b/examples/siphash/siphash_example.cpp @@ -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 +#include +#include "siphash.hpp" + +int main() +{ + boost::unordered_set > x1(0, hash::generate_sipkey()); + boost::unordered_set > 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)); +} \ No newline at end of file diff --git a/examples/siphash/siphash_generate.cpp b/examples/siphash/siphash_generate.cpp new file mode 100644 index 00000000..bcc480f5 --- /dev/null +++ b/examples/siphash/siphash_generate.cpp @@ -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 +#include +#include + +namespace hash +{ + sipkey generate_sipkey() + { + boost::random_device rng; + boost::variate_generator > gen(rng, + boost::uniform_int()); + + sipkey k; + k.k0 = gen(); + k.k1 = gen(); + return k; + } +} \ No newline at end of file