Coverage Report

Created: 2026-07-23 20:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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