Support SipHash with arbitrary byte writes

This commit is contained in:
Pieter Wuille 2016-05-22 11:06:18 +02:00
parent 053930ffc4
commit 9bf156bb9e
3 changed files with 69 additions and 8 deletions

View file

@ -100,12 +100,15 @@ CSipHasher::CSipHasher(uint64_t k0, uint64_t k1)
v[2] = 0x6c7967656e657261ULL ^ k0; v[2] = 0x6c7967656e657261ULL ^ k0;
v[3] = 0x7465646279746573ULL ^ k1; v[3] = 0x7465646279746573ULL ^ k1;
count = 0; count = 0;
tmp = 0;
} }
CSipHasher& CSipHasher::Write(uint64_t data) CSipHasher& CSipHasher::Write(uint64_t data)
{ {
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
assert(count % 8 == 0);
v3 ^= data; v3 ^= data;
SIPROUND; SIPROUND;
SIPROUND; SIPROUND;
@ -116,7 +119,35 @@ CSipHasher& CSipHasher::Write(uint64_t data)
v[2] = v2; v[2] = v2;
v[3] = v3; v[3] = v3;
count++; count += 8;
return *this;
}
CSipHasher& CSipHasher::Write(const unsigned char* data, size_t size)
{
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
uint64_t t = tmp;
int c = count;
while (size--) {
t |= ((uint64_t)(*(data++))) << (8 * (c % 8));
c++;
if ((c & 7) == 0) {
v3 ^= t;
SIPROUND;
SIPROUND;
v0 ^= t;
t = 0;
}
}
v[0] = v0;
v[1] = v1;
v[2] = v2;
v[3] = v3;
count = c;
tmp = t;
return *this; return *this;
} }
@ -124,10 +155,12 @@ uint64_t CSipHasher::Finalize() const
{ {
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
v3 ^= ((uint64_t)count) << 59; uint64_t t = tmp | (((uint64_t)count) << 56);
v3 ^= t;
SIPROUND; SIPROUND;
SIPROUND; SIPROUND;
v0 ^= ((uint64_t)count) << 59; v0 ^= t;
v2 ^= 0xFF; v2 ^= 0xFF;
SIPROUND; SIPROUND;
SIPROUND; SIPROUND;

View file

@ -171,19 +171,38 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]); void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]);
/** SipHash-2-4, using a uint64_t-based (rather than byte-based) interface */ /** SipHash-2-4 */
class CSipHasher class CSipHasher
{ {
private: private:
uint64_t v[4]; uint64_t v[4];
uint64_t tmp;
int count; int count;
public: public:
/** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */
CSipHasher(uint64_t k0, uint64_t k1); CSipHasher(uint64_t k0, uint64_t k1);
/** Hash a 64-bit integer worth of data
* It is treated as if this was the little-endian interpretation of 8 bytes.
* This function can only be used when a multiple of 8 bytes have been written so far.
*/
CSipHasher& Write(uint64_t data); CSipHasher& Write(uint64_t data);
/** Hash arbitrary bytes. */
CSipHasher& Write(const unsigned char* data, size_t size);
/** Compute the 64-bit SipHash-2-4 of the data written so far. The object remains untouched. */
uint64_t Finalize() const; uint64_t Finalize() const;
}; };
/** Optimized SipHash-2-4 implementation for uint256.
*
* It is identical to:
* SipHasher(k0, k1)
* .Write(val.GetUint64(0))
* .Write(val.GetUint64(1))
* .Write(val.GetUint64(2))
* .Write(val.GetUint64(3))
* .Finalize()
*/
uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val); uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val);
#endif // BITCOIN_HASH_H #endif // BITCOIN_HASH_H

View file

@ -51,13 +51,22 @@ BOOST_AUTO_TEST_CASE(siphash)
{ {
CSipHasher hasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); CSipHasher hasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x726fdb47dd0e0e31ull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x726fdb47dd0e0e31ull);
hasher.Write(0x0706050403020100ULL); static const unsigned char t0[1] = {0};
hasher.Write(t0, 1);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x74f839c593dc67fdull);
static const unsigned char t1[7] = {1,2,3,4,5,6,7};
hasher.Write(t1, 7);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x93f5f5799a932462ull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x93f5f5799a932462ull);
hasher.Write(0x0F0E0D0C0B0A0908ULL); hasher.Write(0x0F0E0D0C0B0A0908ULL);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x3f2acc7f57c29bdbull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x3f2acc7f57c29bdbull);
hasher.Write(0x1716151413121110ULL); static const unsigned char t2[2] = {16,17};
BOOST_CHECK_EQUAL(hasher.Finalize(), 0xb8ad50c6f649af94ull); hasher.Write(t2, 2);
hasher.Write(0x1F1E1D1C1B1A1918ULL); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x4bc1b3f0968dd39cull);
static const unsigned char t3[9] = {18,19,20,21,22,23,24,25,26};
hasher.Write(t3, 9);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x2f2e6163076bcfadull);
static const unsigned char t4[5] = {27,28,29,30,31};
hasher.Write(t4, 5);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x7127512f72f27cceull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x7127512f72f27cceull);
hasher.Write(0x2726252423222120ULL); hasher.Write(0x2726252423222120ULL);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x0e3ea96b5304a7d0ull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x0e3ea96b5304a7d0ull);