siphash.cpp raw

   1  // Copyright (c) 2016-2020 The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <crypto/common.h>
   6  #include <crypto/siphash.h>
   7  
   8  #include <algorithm>
   9  #include <bit>
  10  
  11  #define SIPROUND do { \
  12      v0 += v1; v1 = std::rotl(v1, 13); v1 ^= v0; \
  13      v0 = std::rotl(v0, 32); \
  14      v2 += v3; v3 = std::rotl(v3, 16); v3 ^= v2; \
  15      v0 += v3; v3 = std::rotl(v3, 21); v3 ^= v0; \
  16      v2 += v1; v1 = std::rotl(v1, 17); v1 ^= v2; \
  17      v2 = std::rotl(v2, 32); \
  18  } while (0)
  19  
  20  CSipHasher::CSipHasher(uint64_t k0, uint64_t k1)
  21  {
  22      v[0] = 0x736f6d6570736575ULL ^ k0;
  23      v[1] = 0x646f72616e646f6dULL ^ k1;
  24      v[2] = 0x6c7967656e657261ULL ^ k0;
  25      v[3] = 0x7465646279746573ULL ^ k1;
  26      count = 0;
  27      tmp = 0;
  28  }
  29  
  30  CSipHasher& CSipHasher::Write(uint64_t data)
  31  {
  32      uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
  33  
  34      assert(count % 8 == 0);
  35  
  36      v3 ^= data;
  37      SIPROUND;
  38      SIPROUND;
  39      v0 ^= data;
  40  
  41      v[0] = v0;
  42      v[1] = v1;
  43      v[2] = v2;
  44      v[3] = v3;
  45  
  46      count += 8;
  47      return *this;
  48  }
  49  
  50  
  51  /// Load a uint64_t from 0 to 7 bytes.
  52  inline uint64_t ReadU64ByLenLE(const unsigned char* data, size_t len)
  53  {
  54      assert(len < 8);
  55      uint64_t out = 0;
  56      for (size_t i = 0; i < len; ++i) {
  57          out |= (uint64_t)data[i] << (i * 8);
  58      }
  59      return out;
  60  }
  61  
  62  CSipHasher& CSipHasher::Write(Span<const unsigned char> data)
  63  {
  64      uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
  65      auto ntail = count & 0x07;
  66      count += data.size();
  67  
  68      size_t needed = 0;
  69  
  70      if (ntail != 0) {
  71          needed = 8 - ntail;
  72          tmp |= ReadU64ByLenLE(data.data(), std::min(data.size(), needed)) << 8 * ntail;
  73          if (data.size() < needed) {
  74              return *this;
  75          } else {
  76              v3 ^= tmp;
  77              SIPROUND;
  78              SIPROUND;
  79              v0 ^= tmp;
  80          }
  81      }
  82  
  83      size_t len = data.size() - needed;
  84      auto left = len & 0x07;
  85  
  86      auto i = needed;
  87      while (i < len - left) {
  88          uint64_t mi = ReadLE64(data.data() + i);
  89          v3 ^= mi;
  90          SIPROUND;
  91          SIPROUND;
  92          v0 ^= mi;
  93          i += 8;
  94      }
  95  
  96      v[0] = v0;
  97      v[1] = v1;
  98      v[2] = v2;
  99      v[3] = v3;
 100      tmp = ReadU64ByLenLE(data.data() + i, left);
 101  
 102      return *this;
 103  }
 104  
 105  uint64_t CSipHasher::Finalize() const
 106  {
 107      uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
 108  
 109      uint64_t t = tmp | (((uint64_t)count) << 56);
 110  
 111      v3 ^= t;
 112      SIPROUND;
 113      SIPROUND;
 114      v0 ^= t;
 115      v2 ^= 0xFF;
 116      SIPROUND;
 117      SIPROUND;
 118      SIPROUND;
 119      SIPROUND;
 120      return v0 ^ v1 ^ v2 ^ v3;
 121  }
 122  
 123  uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val)
 124  {
 125      /* Specialized implementation for efficiency */
 126      uint64_t d = val.GetUint64(0);
 127  
 128      uint64_t v0 = 0x736f6d6570736575ULL ^ k0;
 129      uint64_t v1 = 0x646f72616e646f6dULL ^ k1;
 130      uint64_t v2 = 0x6c7967656e657261ULL ^ k0;
 131      uint64_t v3 = 0x7465646279746573ULL ^ k1 ^ d;
 132  
 133      SIPROUND;
 134      SIPROUND;
 135      v0 ^= d;
 136      d = val.GetUint64(1);
 137      v3 ^= d;
 138      SIPROUND;
 139      SIPROUND;
 140      v0 ^= d;
 141      d = val.GetUint64(2);
 142      v3 ^= d;
 143      SIPROUND;
 144      SIPROUND;
 145      v0 ^= d;
 146      d = val.GetUint64(3);
 147      v3 ^= d;
 148      SIPROUND;
 149      SIPROUND;
 150      v0 ^= d;
 151      v3 ^= (uint64_t{4}) << 59;
 152      SIPROUND;
 153      SIPROUND;
 154      v0 ^= (uint64_t{4}) << 59;
 155      v2 ^= 0xFF;
 156      SIPROUND;
 157      SIPROUND;
 158      SIPROUND;
 159      SIPROUND;
 160      return v0 ^ v1 ^ v2 ^ v3;
 161  }
 162  
 163  uint64_t SipHashUint256Extra(uint64_t k0, uint64_t k1, const uint256& val, uint32_t extra)
 164  {
 165      /* Specialized implementation for efficiency */
 166      uint64_t d = val.GetUint64(0);
 167  
 168      uint64_t v0 = 0x736f6d6570736575ULL ^ k0;
 169      uint64_t v1 = 0x646f72616e646f6dULL ^ k1;
 170      uint64_t v2 = 0x6c7967656e657261ULL ^ k0;
 171      uint64_t v3 = 0x7465646279746573ULL ^ k1 ^ d;
 172  
 173      SIPROUND;
 174      SIPROUND;
 175      v0 ^= d;
 176      d = val.GetUint64(1);
 177      v3 ^= d;
 178      SIPROUND;
 179      SIPROUND;
 180      v0 ^= d;
 181      d = val.GetUint64(2);
 182      v3 ^= d;
 183      SIPROUND;
 184      SIPROUND;
 185      v0 ^= d;
 186      d = val.GetUint64(3);
 187      v3 ^= d;
 188      SIPROUND;
 189      SIPROUND;
 190      v0 ^= d;
 191      d = ((uint64_t{36}) << 56) | extra;
 192      v3 ^= d;
 193      SIPROUND;
 194      SIPROUND;
 195      v0 ^= d;
 196      v2 ^= 0xFF;
 197      SIPROUND;
 198      SIPROUND;
 199      SIPROUND;
 200      SIPROUND;
 201      return v0 ^ v1 ^ v2 ^ v3;
 202  }
 203