/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 Danny Robson */ #include "siphash.hpp" #include "../bitwise.hpp" #include "../debug/assert.hpp" #include "../endian.hpp" #include using cruft::hash::siphash; /////////////////////////////////////////////////////////////////////////////// static constexpr u64 INITIALISERS[4] = { 0x736f6d6570736575, 0x646f72616e646f6d, 0x6c7967656e657261, 0x7465646279746573, }; /////////////////////////////////////////////////////////////////////////////// static void round (u64 v[4]) { using cruft::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); } //----------------------------------------------------------------------------- static void compress (int const iterations, u64 state[4], u08 const data[8]) { u64 word = cruft::readle (data); state[3] ^= word; for (int c = 0; c < iterations; ++c) round (state); state[0] ^= word; } /////////////////////////////////////////////////////////////////////////////// static std::array bytes_to_u64 (std::span val) { u64 r0 = u64 (val[0 + 0]) << u64 (0 * 8) | u64 (val[0 + 1]) << u64 (1 * 8) | u64 (val[0 + 2]) << u64 (2 * 8) | u64 (val[0 + 3]) << u64 (3 * 8) | u64 (val[0 + 4]) << u64 (4 * 8) | u64 (val[0 + 5]) << u64 (5 * 8) | u64 (val[0 + 6]) << u64 (6 * 8) | u64 (val[0 + 7]) << u64 (7 * 8); u64 r1 = u64 (val[8 + 0]) << u64 (0 * 8) | u64 (val[8 + 1]) << u64 (1 * 8) | u64 (val[8 + 2]) << u64 (2 * 8) | u64 (val[8 + 3]) << u64 (3 * 8) | u64 (val[8 + 4]) << u64 (4 * 8) | u64 (val[8 + 5]) << u64 (5 * 8) | u64 (val[8 + 6]) << u64 (6 * 8) | u64 (val[8 + 7]) << u64 (7 * 8); return { r0, r1 }; } /////////////////////////////////////////////////////////////////////////////// template siphash< CompressionsV, FinalisationsV, OutputV >::siphash (std::array _key) noexcept: m_key (_key) { ; } //----------------------------------------------------------------------------- template siphash< CompressionsV, FinalisationsV, OutputV >::siphash (std::span _key) noexcept : siphash (bytes_to_u64 (_key)) { ; } /////////////////////////////////////////////////////////////////////////////// template typename siphash::digest_t siphash< CompressionsV, FinalisationsV, OutputV >::operator() (cruft::view data) const noexcept { // init u64 state[4] = { m_key[0] ^ INITIALISERS[0], m_key[1] ^ INITIALISERS[1], m_key[0] ^ INITIALISERS[2], m_key[1] ^ INITIALISERS[3], }; if (OutputV == 16) state[1] ^= 0xee; // update auto const tailsize = data.size() % 8; auto cursor = data.begin (); for (auto const last = data.begin () + data.size () - tailsize; cursor != last; cursor += sizeof (u64)) compress (compression_rounds, state, cursor); // drain u08 remain[8] {}; memcpy (remain, cursor, tailsize); cursor += tailsize; CHECK_EQ (cursor, data.cend ()); // append the length remain[7] = data.size (); compress (compression_rounds, state, remain); // finalisation if constexpr (OutputV == 8) state[2] ^= 0xff; else state[2] ^= 0xee; for (int d = 0; d < FinalisationsV; ++d) round (state); auto const res0 = cruft::htol (state[0] ^ state[1] ^ state[2] ^ state[3]); if constexpr (OutputV == 8) { return std::bit_cast (res0); } else { state[1] ^= 0xdd; for (int i = 0; i < FinalisationsV; ++i) round (state); auto const res1 = cruft::htol (state[0] ^ state[1] ^ state[2] ^ state[3]); std::array res; memcpy (res.data () + 0, &res0, sizeof (res0)); memcpy (res.data () + 8, &res1, sizeof (res1)); return res; } } /////////////////////////////////////////////////////////////////////////////// template class cruft::hash::siphash<2,4, 8>; template class cruft::hash::siphash<2,4, 16>;