diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d62cf1a..9f7d2bcf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,6 +249,8 @@ list ( hash/murmur/murmur2.hpp hash/murmur/murmur3.cpp hash/murmur/murmur3.hpp + hash/siphash.cpp + hash/siphash.hpp hash/wang.hpp hash/xxhash.cpp hash/xxhash.hpp @@ -469,6 +471,7 @@ if (TESTS) hash/fasthash hash/fnv1a hash/murmur + hash/siphash hash/xxhash hton introspection diff --git a/hash/siphash.cpp b/hash/siphash.cpp new file mode 100644 index 00000000..c505b637 --- /dev/null +++ b/hash/siphash.cpp @@ -0,0 +1,123 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2018 Danny Robson + */ + +#include "siphash.hpp" + +#include "../bitwise.hpp" +#include "../debug.hpp" +#include "../endian.hpp" + +using util::hash::siphash; + + +/////////////////////////////////////////////////////////////////////////////// +static constexpr +uint64_t INITIALISERS[4] = { + 0x736f6d6570736575, + 0x646f72616e646f6d, + 0x6c7967656e657261, + 0x7465646279746573, +}; + + +/////////////////////////////////////////////////////////////////////////////// +void +round (uint64_t v[4]) +{ + using util::rotatel; + + v[0] += v[1]; v[2] += v[3]; + v[1] = rotatel (v[1], 13); v[3] = rotatel (v[3], 16); + v[1] ^= v[0]; v[3] ^= v[2]; + v[0] = rotatel (v[0], 32); + + v[2] += v[1]; v[0] += v[3]; + v[1] = rotatel (v[1], 17); v[3] = rotatel (v[3], 21); + v[1] ^= v[2]; v[3] ^= v[0]; + v[2] = rotatel (v[2], 32); +} + + +/////////////////////////////////////////////////////////////////////////////// +template +ValueT +readle (const void *ptr) +{ + return util::htol (*reinterpret_cast (ptr)); +} + + +/////////////////////////////////////////////////////////////////////////////// +template +siphash::siphash (std::array _key) noexcept: + m_key (_key) +{ ; } + + +//----------------------------------------------------------------------------- +template +typename siphash::digest_t +siphash::operator() (util::view data) const noexcept +{ + // init + uint64_t state[4] = { + m_key[0] ^ INITIALISERS[0], + m_key[1] ^ INITIALISERS[1], + m_key[0] ^ INITIALISERS[2], + m_key[1] ^ INITIALISERS[3], + }; + + // update + auto cursor = data.begin (); + for ( ; data.end () - cursor > 8; cursor += sizeof (uint64_t)) { + auto word = readle (cursor); + + state[3] ^= word; + for (int c = 0; c < C; ++c) + round (state); + state[0] ^= word; + } + + // drain + union { + uint64_t d64 = 0; + uint8_t d08[8]; + } accum; + + if (cursor != data.cend ()) { + std::copy (cursor, data.cend (), std::begin (accum.d08)); + cursor = data.cend (); + } + + CHECK_EQ (cursor, data.cend ()); + // append the length + accum.d08[7] = data.size (); + + state[3] ^= accum.d64; + for (int c = 0; c < C; ++c) + round (state); + state[0] ^= accum.d64; + + // finalisation + state[2] ^= 0xff; + for (int d = 0; d < D; ++d) + round (state); + + return state[0] ^ state[1] ^ state[2] ^ state[3]; +} + +/////////////////////////////////////////////////////////////////////////////// +template class util::hash::siphash<2,4>; diff --git a/hash/siphash.hpp b/hash/siphash.hpp new file mode 100644 index 00000000..18e1b7de --- /dev/null +++ b/hash/siphash.hpp @@ -0,0 +1,43 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2016-2018 Danny Robson + */ + +#ifndef CRUFT_UTIL_HASH_SIPHASH_HPP +#define CRUFT_UTIL_HASH_SIPHASH_HPP + +#include "../view.hpp" + +#include +#include + +namespace util::hash { + template + class siphash { + public: + using digest_t = std::uint64_t; + + static constexpr auto compression_rounds = CompressionsV; + static constexpr auto finalisation_rounds = FinalisationsV; + + siphash (std::array) noexcept; + + digest_t operator() (util::view data) const noexcept; + + private: + std::array m_key; + }; +}; + +#endif diff --git a/test/hash/siphash.cpp b/test/hash/siphash.cpp new file mode 100644 index 00000000..5f41ef83 --- /dev/null +++ b/test/hash/siphash.cpp @@ -0,0 +1,35 @@ + +#include "tap.hpp" + +#include "hash/siphash.hpp" + + +/////////////////////////////////////////////////////////////////////////////// +static const struct { + std::array key; + std::vector data; + uint64_t digest; + const char *message; +} TESTS[] = { + { + { 0x0706050403020100, 0x0f0e0d0c0b0a0908 }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e + }, + 0xa129ca6149be45e5, + "byte sequence", + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +int +main () +{ + util::TAP::logger tap; + + for (const auto &t: TESTS) { + util::hash::siphash<2,4> h (t.key); + tap.expect_eq (h (t.data), t.digest, "%s", t.message); + } +} \ No newline at end of file