/tmp/bitcoin/src/crypto/siphash.h
Line | Count | Source |
1 | | // Copyright (c) 2016-present The Bitcoin Core 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 | | #ifndef BITCOIN_CRYPTO_SIPHASH_H |
6 | | #define BITCOIN_CRYPTO_SIPHASH_H |
7 | | |
8 | | #include <attributes.h> |
9 | | #include <uint256.h> |
10 | | |
11 | | #include <bit> |
12 | | #include <cstdint> |
13 | | #include <span> |
14 | | |
15 | | /** Shared SipHash state (v0..v3) with its round, compression, and finalization primitives. |
16 | | * Internal building block, only meant to be composed by the hasher classes below. */ |
17 | | class SipHashState |
18 | | { |
19 | | /** SipHash initialization constants. */ |
20 | | static constexpr uint64_t C0{0x736f6d6570736575}, C1{0x646f72616e646f6d}, C2{0x6c7967656e657261}, C3{0x7465646279746573}; |
21 | | /** SipHash v2 finalizer constant. */ |
22 | | static constexpr uint64_t FINALIZER{0xFF}; |
23 | | /** SipHash custom unpadded finalizer constant. */ |
24 | | static constexpr uint64_t FINALIZER_UNPADDED{0x6465646461706e75}; |
25 | | static_assert(FINALIZER_UNPADDED != FINALIZER); |
26 | | |
27 | | /** Construct a SipHashState with the specified values as state. */ |
28 | 330M | ALWAYS_INLINE SipHashState(uint64_t v0, uint64_t v1, uint64_t v2, uint64_t v3) noexcept : m_v0{v0}, m_v1{v1}, m_v2{v2}, m_v3{v3} {} |
29 | | |
30 | | /** State variables. */ |
31 | | uint64_t m_v0, m_v1, m_v2, m_v3; |
32 | | |
33 | | /** Mutably perform one SipRound on this state. */ |
34 | | ALWAYS_INLINE void SipRound() noexcept |
35 | 2.06G | { |
36 | 2.06G | m_v0 += m_v1; m_v1 = std::rotl(m_v1, 13); m_v1 ^= m_v0; |
37 | 2.06G | m_v0 = std::rotl(m_v0, 32); |
38 | 2.06G | m_v2 += m_v3; m_v3 = std::rotl(m_v3, 16); m_v3 ^= m_v2; |
39 | 2.06G | m_v0 += m_v3; m_v3 = std::rotl(m_v3, 21); m_v3 ^= m_v0; |
40 | 2.06G | m_v2 += m_v1; m_v1 = std::rotl(m_v1, 17); m_v1 ^= m_v2; |
41 | 2.06G | m_v2 = std::rotl(m_v2, 32); |
42 | 2.06G | } |
43 | | |
44 | | public: |
45 | | /** Construct a SipHashState initialized with the specified key. */ |
46 | 8.32M | explicit ALWAYS_INLINE SipHashState(uint64_t k0, uint64_t k1) noexcept : SipHashState{C0 ^ k0, C1 ^ k1, C2 ^ k0, C3 ^ k1} {} |
47 | | /** Construct a copy of this state. */ |
48 | 321M | ALWAYS_INLINE SipHashState Copy() const noexcept { return {m_v0, m_v1, m_v2, m_v3}; } |
49 | | /** Mutably compress one block into this state, with 1 SipRound. */ |
50 | | ALWAYS_INLINE SipHashState& Compress1(uint64_t data) noexcept |
51 | 260M | { |
52 | 260M | m_v3 ^= data; |
53 | 260M | SipRound(); |
54 | 260M | m_v0 ^= data; |
55 | 260M | return *this; |
56 | 260M | } |
57 | | /** Mutably compress one jumbo block into this state, with 1 SipRound. */ |
58 | | ALWAYS_INLINE SipHashState& Compress1Jumbo(const uint256& data) noexcept |
59 | 260M | { |
60 | 260M | const uint64_t d0{data.GetUint64(0)}, d1{data.GetUint64(1)}, d2{data.GetUint64(2)}, d3{data.GetUint64(3)}; |
61 | 260M | m_v3 ^= d0; m_v0 ^= d1; m_v1 ^= d2; m_v2 ^= d3; |
62 | 260M | SipRound(); |
63 | 260M | m_v0 ^= d0; m_v1 ^= d1; m_v2 ^= d2; m_v3 ^= d3; |
64 | 260M | return *this; |
65 | 260M | } |
66 | | /** Mutably compress one block into this state, with 2 SipRounds. */ |
67 | | ALWAYS_INLINE SipHashState& Compress2(uint64_t data) noexcept |
68 | 273M | { |
69 | 273M | m_v3 ^= data; |
70 | 273M | SipRound(); |
71 | 273M | SipRound(); |
72 | 273M | m_v0 ^= data; |
73 | 273M | return *this; |
74 | 273M | } |
75 | | /** Mutably finalize this state with 4 SipRounds, and return the resulting hash. */ |
76 | | ALWAYS_INLINE uint64_t Finalize4() noexcept |
77 | 54.4M | { |
78 | 54.4M | m_v2 ^= FINALIZER; |
79 | 54.4M | SipRound(); |
80 | 54.4M | SipRound(); |
81 | 54.4M | SipRound(); |
82 | 54.4M | SipRound(); |
83 | 54.4M | return m_v0 ^ m_v1 ^ m_v2 ^ m_v3; |
84 | 54.4M | } |
85 | | /** Mutably finalize this state with 3 SipRounds using the unpadded finalizer, and return the |
86 | | * resulting hash. */ |
87 | | ALWAYS_INLINE uint64_t Finalize3U() noexcept |
88 | 260M | { |
89 | 260M | m_v2 ^= FINALIZER_UNPADDED; |
90 | 260M | SipRound(); |
91 | 260M | SipRound(); |
92 | 260M | SipRound(); |
93 | 260M | return m_v0 ^ m_v1 ^ m_v2 ^ m_v3; |
94 | 260M | } |
95 | | }; |
96 | | |
97 | | /** General SipHash-2-4 implementation. */ |
98 | | class CSipHasher |
99 | | { |
100 | | SipHashState m_state; |
101 | | uint64_t m_tmp{0}; |
102 | | uint8_t m_count{0}; //!< Only the low 8 bits of the input size matter. |
103 | | |
104 | | public: |
105 | | /** Construct a SipHash calculator initialized with 128-bit key (k0, k1). */ |
106 | | CSipHasher(uint64_t k0, uint64_t k1); |
107 | | /** Hash a 64-bit integer worth of data. |
108 | | * It is treated as if this was the little-endian interpretation of 8 bytes. |
109 | | * This function can only be used when a multiple of 8 bytes have been written so far. |
110 | | */ |
111 | | CSipHasher& Write(uint64_t data); |
112 | | /** Hash arbitrary bytes. */ |
113 | | CSipHasher& Write(std::span<const unsigned char> data); |
114 | | /** Compute the 64-bit SipHash-2-4 of the data written so far. The object remains untouched. */ |
115 | | uint64_t Finalize() const; |
116 | | }; |
117 | | |
118 | | /** A custom weaker variant of SipHash-1-3 without padding, and supporting "jumbo" inputs. |
119 | | * |
120 | | * Compared to the traditional SipHash-2-4, this incorporates 3 changes: |
121 | | * |
122 | | * - Use SipHash-1-3: This common variant reduces the number of rounds between input blocks from 2 |
123 | | * to 1, and the number of finalization rounds at the end from 4 to 3. |
124 | | * It is a standard faster choice with weaker security guarantees. It is |
125 | | * considered strong enough for use in hash tables and other applications with |
126 | | * only weak security requirements, in particular when attackers cannot directly |
127 | | * observe the hash function output, and cannot control k0 and k1. |
128 | | * |
129 | | * - Remove padding: Standard SipHash operates on byte-oriented inputs, which are converted into |
130 | | * 8-byte blocks internally by adding a padding of 1-8 bytes to obtain a multiple |
131 | | * of 8 bytes. In this variant, the input is a sequence of blocks instead, which |
132 | | * are used without padding. This saves 1 round for inputs that are a multiple of |
133 | | * 8 bytes already. The padding normally adds a commitment to the input's byte |
134 | | * length, but since our input is block oriented, and there is one round per |
135 | | * input, the function of that commitment is directly fulfilled by the number of |
136 | | * rounds. The empty input is permitted; it yields the result of finalizing the |
137 | | * initialization directly. Out of an abundance of caution the finalizing |
138 | | * v2 ^= 0xff is changed to v2 ^= 0x6465646461706e75 ("unpadded" in LE64), to |
139 | | * avoid collisions between standard SipHash-1-3 and this variant. |
140 | | * |
141 | | * - Jumbo blocks: We generalize the input to consist of a sequence of blocks which are each either |
142 | | * exactly 8 bytes (normal blocks) or 32 bytes (jumbo blocks), and may be |
143 | | * arbitrarily mixed. For hash-table use, cryptographic hash outputs must make up |
144 | | * all but a small bounded number of retained jumbo blocks. Note that the block |
145 | | * structure matters: hashing one jumbo block is not the same as hashing the same |
146 | | * data split up into four normal blocks. Jumbo blocks are interpreted as 4 LE64 integers |
147 | | * (d0,d1,d2,d3), and XOR'ed into the state before a round as |
148 | | * (v0,v1,v2,v3) ^= (d1,d2,d3,d0), and XOR'ed into the state after a round as |
149 | | * (v0,v1,v2,v3) ^= (d0,d1,d2,d3). This is a strict generalization of normal block |
150 | | * processing, in the sense that if d1..d3=0, it is identical to processing a normal |
151 | | * block d0 (a single LE64 integer). Of course, making d1..d3=0 should be impossible |
152 | | * in the output of a cryptographic hash function. These jumbo blocks are acceptable |
153 | | * because even though they may give the attacker more control within a single |
154 | | * round, that control is limited by the cryptographic hash in between. |
155 | | * |
156 | | * Other components are unchanged: initialization constants, SipRound, and 64-bit output. |
157 | | * This interface serves as an executable specification for fixed-width implementations. |
158 | | * The Hash overloads optimize fixed-width inputs without modifying the accumulated state. |
159 | | */ |
160 | | class SipHasher13UJ |
161 | | { |
162 | | SipHashState m_state; |
163 | | |
164 | | public: |
165 | | /** Construct a SipHash-1-3-UJ calculator initialized with 128-bit key (k0, k1). */ |
166 | 517k | SipHasher13UJ(uint64_t k0, uint64_t k1) noexcept : m_state{k0, k1} {} |
167 | | /** Hash a normal 64-bit value. */ |
168 | | SipHasher13UJ& Write(uint64_t data) noexcept; |
169 | | /** Hash a 256-bit value as a jumbo block. For hash-table use, non-hash inputs must remain few and bounded. */ |
170 | | SipHasher13UJ& WriteJumbo(const uint256& hash) noexcept; |
171 | | /** Compute the 64-bit SipHash-1-3-UJ of the data written so far. The object remains untouched. */ |
172 | | uint64_t Finalize() const noexcept; |
173 | | |
174 | | /** Hash a jumbo block after the data written so far and finalize without modifying the object. */ |
175 | | ALWAYS_INLINE uint64_t Hash(const uint256& hash) const noexcept |
176 | 201k | { |
177 | 201k | return m_state.Copy() |
178 | 201k | .Compress1Jumbo(hash) |
179 | 201k | .Finalize3U(); |
180 | 201k | } |
181 | | |
182 | | /** |
183 | | * Hash a jumbo block followed by a normal block after the data written so far, |
184 | | * and finalize without modifying the object. |
185 | | */ |
186 | | ALWAYS_INLINE uint64_t Hash(const uint256& hash, uint64_t extra) const noexcept |
187 | 260M | { |
188 | 260M | return m_state.Copy() |
189 | 260M | .Compress1Jumbo(hash) |
190 | 260M | .Compress1(extra) |
191 | 260M | .Finalize3U(); |
192 | 260M | } |
193 | | }; |
194 | | |
195 | | /** |
196 | | * Optimized SipHash-2-4 implementation for uint256. |
197 | | * |
198 | | * This class caches the initial SipHash state (v0..v3) derived from (k0, k1) |
199 | | * and implements a specialized hashing path for uint256 values, with or |
200 | | * without an extra 32-bit word. The internal state is immutable, so |
201 | | * PresaltedSipHasher instances can be reused for multiple hashes with the |
202 | | * same key. |
203 | | */ |
204 | | class PresaltedSipHasher |
205 | | { |
206 | | const SipHashState m_state; |
207 | | |
208 | | public: |
209 | 547k | explicit PresaltedSipHasher(uint64_t k0, uint64_t k1) noexcept : m_state{k0, k1} {} |
210 | | |
211 | | /** Equivalent to CSipHasher(k0, k1).Write(val).Finalize(). */ |
212 | | uint64_t operator()(const uint256& val) const noexcept; |
213 | | |
214 | | /** |
215 | | * Equivalent to CSipHasher(k0, k1).Write(val).Write(extra).Finalize(), |
216 | | * with `extra` encoded as 4 little-endian bytes. |
217 | | */ |
218 | | uint64_t operator()(const uint256& val, uint32_t extra) const noexcept; |
219 | | }; |
220 | | |
221 | | #endif // BITCOIN_CRYPTO_SIPHASH_H |