mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 05:32:22 +02:00
crypto: add generic SipHash-1-3-UJ
Add `SipHasher13UJ`, a block-oriented implementation accepting arbitrarily mixed 64-bit normal blocks and 256-bit jumbo blocks. For hash-table use, cryptographic hash outputs must make up all but a small bounded number of retained jumbo blocks. The generic interface serves as the executable specification for the fixed-width overloads added next. Co-authored-by: Lőrinc <pap.lorinc@gmail.com> Co-authored-by: Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
This commit is contained in:
@@ -49,6 +49,23 @@ uint64_t CSipHasher::Finalize() const
|
||||
.Finalize4();
|
||||
}
|
||||
|
||||
SipHasher13UJ& SipHasher13UJ::Write(uint64_t data) noexcept
|
||||
{
|
||||
m_state.Compress1(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SipHasher13UJ& SipHasher13UJ::WriteJumbo(const uint256& hash) noexcept
|
||||
{
|
||||
m_state.Compress1Jumbo(hash);
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint64_t SipHasher13UJ::Finalize() const noexcept
|
||||
{
|
||||
return m_state.Copy().Finalize3U();
|
||||
}
|
||||
|
||||
uint64_t PresaltedSipHasher::operator()(const uint256& val) const noexcept
|
||||
{
|
||||
return m_state.Copy()
|
||||
|
||||
@@ -20,6 +20,9 @@ class SipHashState
|
||||
static constexpr uint64_t C0{0x736f6d6570736575}, C1{0x646f72616e646f6d}, C2{0x6c7967656e657261}, C3{0x7465646279746573};
|
||||
/** SipHash v2 finalizer constant. */
|
||||
static constexpr uint64_t FINALIZER{0xFF};
|
||||
/** SipHash custom unpadded finalizer constant. */
|
||||
static constexpr uint64_t FINALIZER_UNPADDED{0x6465646461706e75};
|
||||
static_assert(FINALIZER_UNPADDED != FINALIZER);
|
||||
|
||||
/** Construct a SipHashState with the specified values as state. */
|
||||
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} {}
|
||||
@@ -43,6 +46,23 @@ public:
|
||||
explicit ALWAYS_INLINE SipHashState(uint64_t k0, uint64_t k1) noexcept : SipHashState{C0 ^ k0, C1 ^ k1, C2 ^ k0, C3 ^ k1} {}
|
||||
/** Construct a copy of this state. */
|
||||
ALWAYS_INLINE SipHashState Copy() const noexcept { return {m_v0, m_v1, m_v2, m_v3}; }
|
||||
/** Mutably compress one block into this state, with 1 SipRound. */
|
||||
ALWAYS_INLINE SipHashState& Compress1(uint64_t data) noexcept
|
||||
{
|
||||
m_v3 ^= data;
|
||||
SipRound();
|
||||
m_v0 ^= data;
|
||||
return *this;
|
||||
}
|
||||
/** Mutably compress one jumbo block into this state, with 1 SipRound. */
|
||||
ALWAYS_INLINE SipHashState& Compress1Jumbo(const uint256& data) noexcept
|
||||
{
|
||||
const uint64_t d0{data.GetUint64(0)}, d1{data.GetUint64(1)}, d2{data.GetUint64(2)}, d3{data.GetUint64(3)};
|
||||
m_v3 ^= d0; m_v0 ^= d1; m_v1 ^= d2; m_v2 ^= d3;
|
||||
SipRound();
|
||||
m_v0 ^= d0; m_v1 ^= d1; m_v2 ^= d2; m_v3 ^= d3;
|
||||
return *this;
|
||||
}
|
||||
/** Mutably compress one block into this state, with 2 SipRounds. */
|
||||
ALWAYS_INLINE SipHashState& Compress2(uint64_t data) noexcept
|
||||
{
|
||||
@@ -62,6 +82,16 @@ public:
|
||||
SipRound();
|
||||
return m_v0 ^ m_v1 ^ m_v2 ^ m_v3;
|
||||
}
|
||||
/** Mutably finalize this state with 3 SipRounds using the unpadded finalizer, and return the
|
||||
* resulting hash. */
|
||||
ALWAYS_INLINE uint64_t Finalize3U() noexcept
|
||||
{
|
||||
m_v2 ^= FINALIZER_UNPADDED;
|
||||
SipRound();
|
||||
SipRound();
|
||||
SipRound();
|
||||
return m_v0 ^ m_v1 ^ m_v2 ^ m_v3;
|
||||
}
|
||||
};
|
||||
|
||||
/** General SipHash-2-4 implementation. */
|
||||
@@ -85,6 +115,62 @@ public:
|
||||
uint64_t Finalize() const;
|
||||
};
|
||||
|
||||
/** A custom weaker variant of SipHash-1-3 without padding, and supporting "jumbo" inputs.
|
||||
*
|
||||
* Compared to the traditional SipHash-2-4, this incorporates 3 changes:
|
||||
*
|
||||
* - Use SipHash-1-3: This common variant reduces the number of rounds between input blocks from 2
|
||||
* to 1, and the number of finalization rounds at the end from 4 to 3.
|
||||
* It is a standard faster choice with weaker security guarantees. It is
|
||||
* considered strong enough for use in hash tables and other applications with
|
||||
* only weak security requirements, in particular when attackers cannot directly
|
||||
* observe the hash function output, and cannot control k0 and k1.
|
||||
*
|
||||
* - Remove padding: Standard SipHash operates on byte-oriented inputs, which are converted into
|
||||
* 8-byte blocks internally by adding a padding of 1-8 bytes to obtain a multiple
|
||||
* of 8 bytes. In this variant, the input is a sequence of blocks instead, which
|
||||
* are used without padding. This saves 1 round for inputs that are a multiple of
|
||||
* 8 bytes already. The padding normally adds a commitment to the input's byte
|
||||
* length, but since our input is block oriented, and there is one round per
|
||||
* input, the function of that commitment is directly fulfilled by the number of
|
||||
* rounds. The empty input is permitted; it yields the result of finalizing the
|
||||
* initialization directly. Out of an abundance of caution the finalizing
|
||||
* v2 ^= 0xff is changed to v2 ^= 0x6465646461706e75 ("unpadded" in LE64), to
|
||||
* avoid collisions between standard SipHash-1-3 and this variant.
|
||||
*
|
||||
* - Jumbo blocks: We generalize the input to consist of a sequence of blocks which are each either
|
||||
* exactly 8 bytes (normal blocks) or 32 bytes (jumbo blocks), and may be
|
||||
* arbitrarily mixed. For hash-table use, cryptographic hash outputs must make up
|
||||
* all but a small bounded number of retained jumbo blocks. Note that the block
|
||||
* structure matters: hashing one jumbo block is not the same as hashing the same
|
||||
* data split up into four normal blocks. Jumbo blocks are interpreted as 4 LE64 integers
|
||||
* (d0,d1,d2,d3), and XOR'ed into the state before a round as
|
||||
* (v0,v1,v2,v3) ^= (d1,d2,d3,d0), and XOR'ed into the state after a round as
|
||||
* (v0,v1,v2,v3) ^= (d0,d1,d2,d3). This is a strict generalization of normal block
|
||||
* processing, in the sense that if d1..d3=0, it is identical to processing a normal
|
||||
* block d0 (a single LE64 integer). Of course, making d1..d3=0 should be impossible
|
||||
* in the output of a cryptographic hash function. These jumbo blocks are acceptable
|
||||
* because even though they may give the attacker more control within a single
|
||||
* round, that control is limited by the cryptographic hash in between.
|
||||
*
|
||||
* Other components are unchanged: initialization constants, SipRound, and 64-bit output.
|
||||
* This interface serves as an executable specification for fixed-width implementations.
|
||||
*/
|
||||
class SipHasher13UJ
|
||||
{
|
||||
SipHashState m_state;
|
||||
|
||||
public:
|
||||
/** Construct a SipHash-1-3-UJ calculator initialized with 128-bit key (k0, k1). */
|
||||
SipHasher13UJ(uint64_t k0, uint64_t k1) noexcept : m_state{k0, k1} {}
|
||||
/** Hash a normal 64-bit value. */
|
||||
SipHasher13UJ& Write(uint64_t data) noexcept;
|
||||
/** Hash a 256-bit value as a jumbo block. For hash-table use, non-hash inputs must remain few and bounded. */
|
||||
SipHasher13UJ& WriteJumbo(const uint256& hash) noexcept;
|
||||
/** Compute the 64-bit SipHash-1-3-UJ of the data written so far. The object remains untouched. */
|
||||
uint64_t Finalize() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Optimized SipHash-2-4 implementation for uint256.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user