siphash.py
 1  #!/usr/bin/env python3
 2  # Copyright (c) 2016-present The Bitcoin Core developers
 3  # Distributed under the MIT software license, see the accompanying
 4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
 5  """SipHash-2-4 implementation.
 6  
 7  This implements SipHash-2-4. For convenience, an interface taking 256-bit
 8  integers is provided in addition to the one accepting generic data.
 9  """
10  
11  def rotl64(n, b):
12      return n >> (64 - b) | (n & ((1 << (64 - b)) - 1)) << b
13  
14  
15  def siphash_round(v0, v1, v2, v3):
16      v0 = (v0 + v1) & ((1 << 64) - 1)
17      v1 = rotl64(v1, 13)
18      v1 ^= v0
19      v0 = rotl64(v0, 32)
20      v2 = (v2 + v3) & ((1 << 64) - 1)
21      v3 = rotl64(v3, 16)
22      v3 ^= v2
23      v0 = (v0 + v3) & ((1 << 64) - 1)
24      v3 = rotl64(v3, 21)
25      v3 ^= v0
26      v2 = (v2 + v1) & ((1 << 64) - 1)
27      v1 = rotl64(v1, 17)
28      v1 ^= v2
29      v2 = rotl64(v2, 32)
30      return (v0, v1, v2, v3)
31  
32  
33  def siphash(k0, k1, data):
34      assert type(data) is bytes
35      v0 = 0x736f6d6570736575 ^ k0
36      v1 = 0x646f72616e646f6d ^ k1
37      v2 = 0x6c7967656e657261 ^ k0
38      v3 = 0x7465646279746573 ^ k1
39      c = 0
40      t = 0
41      for d in data:
42          t |= d << (8 * (c % 8))
43          c = (c + 1) & 0xff
44          if (c & 7) == 0:
45              v3 ^= t
46              v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
47              v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
48              v0 ^= t
49              t = 0
50      t = t | (c << 56)
51      v3 ^= t
52      v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
53      v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
54      v0 ^= t
55      v2 ^= 0xff
56      v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
57      v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
58      v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
59      v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
60      return v0 ^ v1 ^ v2 ^ v3
61  
62  
63  def siphash256(k0, k1, num):
64      assert type(num) is int
65      return siphash(k0, k1, num.to_bytes(32, 'little'))