Skip to content

Commit 998d1a8

Browse files
authored
Merge pull request #1569 from evoskuil/master
Add single byte and quarter block hash functions.
2 parents 2051639 + 95e9fd3 commit 998d1a8

7 files changed

Lines changed: 166 additions & 2 deletions

File tree

include/bitcoin/system/hash/sha/algorithm.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class algorithm
6666

6767
/// Byte-based types.
6868
using byte_t = uint8_t;
69+
using quart_t = std_array<byte_t, to_half(SHA::chunk_words) * SHA::word_bytes>;
6970
using half_t = std_array<byte_t, SHA::chunk_words * SHA::word_bytes>;
7071
using block_t = std_array<byte_t, SHA::block_words * SHA::word_bytes>;
7172
using digest_t = std_array<byte_t, bytes<SHA::digest>>;
@@ -76,7 +77,7 @@ class algorithm
7677
using iblocks_t = iterable<block_t>;
7778
using digests_t = std::vector<digest_t>;
7879

79-
/// Constants.
80+
/// Count types.
8081
/// -----------------------------------------------------------------------
8182

8283
/// count_t is uint64_t (sha160/256) or uint128_t (sha512).
@@ -91,11 +92,14 @@ class algorithm
9192

9293
/// Single hashing.
9394
/// -----------------------------------------------------------------------
95+
9496
template <size_t Size>
9597
static constexpr digest_t hash(const ablocks_t<Size>& blocks) NOEXCEPT;
9698
static constexpr digest_t hash(const block_t& block) NOEXCEPT;
9799
static constexpr digest_t hash(const half_t& half) NOEXCEPT;
98100
static constexpr digest_t hash(const half_t& left, const half_t& right) NOEXCEPT;
101+
static constexpr digest_t hash(const quart_t& left, const quart_t& right) NOEXCEPT;
102+
static constexpr digest_t hash(uint8_t byte) NOEXCEPT;
99103
static digest_t hash(iblocks_t&& blocks) NOEXCEPT;
100104

101105
/// Double hashing (sha256/512).
@@ -232,6 +236,8 @@ class algorithm
232236
/// -----------------------------------------------------------------------
233237

234238
INLINE static constexpr void input(buffer_t& buffer, const block_t& block) NOEXCEPT;
239+
INLINE static constexpr void input_left(auto& buffer, const quart_t& quarter) NOEXCEPT;
240+
INLINE static constexpr void input_right(auto& buffer, const quart_t& quarter) NOEXCEPT;
235241
INLINE static constexpr void input_left(auto& buffer, const half_t& half) NOEXCEPT;
236242
INLINE static constexpr void input_right(auto& buffer, const half_t& half) NOEXCEPT;
237243
INLINE static constexpr void inject_left(auto& buffer, const auto& left) NOEXCEPT;
@@ -403,6 +409,7 @@ class algorithm
403409
static digest_t native_hash(const block_t& block) NOEXCEPT;
404410
static digest_t native_hash(const half_t& half) NOEXCEPT;
405411
static digest_t native_hash(const half_t& left, const half_t& right) NOEXCEPT;
412+
static digest_t native_hash(uint8_t byte) NOEXCEPT;
406413

407414
static digest_t native_double_hash(const block_t& block) NOEXCEPT;
408415
static digest_t native_double_hash(const half_t& half) NOEXCEPT;

include/bitcoin/system/impl/hash/accumulator.ipp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ serialize(size_t size) NOEXCEPT
151151
// as word_t: { [0x00000000], [0x00000200] }
152152
//
153153
// block_t (128 bytes), counter_t (128 bits), words_t (64 bits), byte_t (8 bits).
154-
// One block bit count (0x0400) in SHA256 (BE):
154+
// One block bit count (0x0400) in SHA512 (BE):
155155
// as byte_t: { (0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
156156
// (0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00) }
157157
// as word_t: { [0x0000000000000000], [0x0000000000000400] }

include/bitcoin/system/impl/hash/sha/algorithm_native.ipp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,19 @@ native_hash(const half_t& left, const half_t& right) NOEXCEPT
344344
return native_finalize<one>(state);
345345
}
346346

347+
TEMPLATE
348+
typename CLASS::digest_t CLASS::
349+
native_hash(uint8_t byte) NOEXCEPT
350+
{
351+
constexpr auto pad = bit_hi<uint8_t>;
352+
353+
block_t block{};
354+
block.at(0) = byte;
355+
block.at(1) = pad;
356+
block.at(63) = byte_bits;
357+
return native_hash(block);
358+
}
359+
347360
// Double hash functions start with BE data and end with BE digest_t.
348361
// ----------------------------------------------------------------------------
349362

include/bitcoin/system/impl/hash/sha/algorithm_parsing.ipp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,65 @@ input_right(auto& buffer, const half_t& half) NOEXCEPT
217217
}
218218
}
219219

220+
TEMPLATE
221+
INLINE constexpr void CLASS::
222+
input_left(auto& buffer, const quart_t& quarter) NOEXCEPT
223+
{
224+
using word = array_element<buffer_t>;
225+
226+
if (std::is_constant_evaluated())
227+
{
228+
constexpr auto size = SHA::word_bytes;
229+
from_big<0 * size>(buffer.at(0), quarter);
230+
from_big<1 * size>(buffer.at(1), quarter);
231+
from_big<2 * size>(buffer.at(2), quarter);
232+
from_big<3 * size>(buffer.at(3), quarter);
233+
}
234+
else if constexpr (bc::is_little_endian)
235+
{
236+
const auto& in = array_cast<word>(quarter);
237+
buffer[0] = native_from_big_end(in[0]);
238+
buffer[1] = native_from_big_end(in[1]);
239+
buffer[2] = native_from_big_end(in[2]);
240+
buffer[3] = native_from_big_end(in[3]);
241+
}
242+
else
243+
{
244+
constexpr auto short_words = to_half(SHA::chunk_words);
245+
array_cast<word, short_words>(buffer) = array_cast<word>(quarter);
246+
}
247+
}
248+
249+
TEMPLATE
250+
INLINE constexpr void CLASS::
251+
input_right(auto& buffer, const quart_t& quarter) NOEXCEPT
252+
{
253+
using word = array_element<buffer_t>;
254+
255+
if (std::is_constant_evaluated())
256+
{
257+
constexpr auto size = SHA::word_bytes;
258+
from_big<0 * size>(buffer.at(4), quarter);
259+
from_big<1 * size>(buffer.at(5), quarter);
260+
from_big<2 * size>(buffer.at(6), quarter);
261+
from_big<3 * size>(buffer.at(7), quarter);
262+
}
263+
else if constexpr (bc::is_little_endian)
264+
{
265+
const auto& in = array_cast<word>(quarter);
266+
buffer[4] = native_from_big_end(in[0]);
267+
buffer[5] = native_from_big_end(in[1]);
268+
buffer[6] = native_from_big_end(in[2]);
269+
buffer[7] = native_from_big_end(in[3]);
270+
}
271+
else
272+
{
273+
constexpr auto short_words = to_half(SHA::chunk_words);
274+
array_cast<word, short_words, short_words>(buffer) =
275+
array_cast<word>(quarter);
276+
}
277+
}
278+
220279
TEMPLATE
221280
INLINE constexpr typename CLASS::digest_t CLASS::
222281
output(const state_t& state) NOEXCEPT

include/bitcoin/system/impl/hash/sha/algorithm_single.ipp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,69 @@ hash(const half_t& left, const half_t& right) NOEXCEPT
131131
}
132132
}
133133

134+
TEMPLATE
135+
constexpr typename CLASS::digest_t CLASS::
136+
hash(const quart_t& left, const quart_t& right) NOEXCEPT
137+
{
138+
const auto hasher = [](const quart_t& left, const quart_t& right) NOEXCEPT
139+
{
140+
auto state = H::get;
141+
buffer_t buffer{};
142+
input_left(buffer, left);
143+
input_right(buffer, right);
144+
pad_half(buffer);
145+
schedule(buffer);
146+
compress(state, buffer);
147+
return output(state);
148+
};
149+
150+
if (std::is_constant_evaluated())
151+
{
152+
return hasher(left, right);
153+
}
154+
else if constexpr (native && SHA::strength == 256)
155+
{
156+
return native_hash(left, right);
157+
}
158+
else
159+
{
160+
return hasher(left, right);
161+
}
162+
}
163+
164+
TEMPLATE
165+
constexpr typename CLASS::digest_t CLASS::
166+
hash(uint8_t byte) NOEXCEPT
167+
{
168+
const auto hasher = [](uint8_t byte) NOEXCEPT
169+
{
170+
// Shift the pad sentinel one byte to make room for the value.
171+
// Byte swapped for the sake of simplicity and will be reversed below.
172+
constexpr auto pad = shift_left<word_t>(bit_hi<uint8_t>, byte_bits);
173+
174+
auto state = H::get;
175+
buffer_t buffer{};
176+
buffer.at(0) = byteswap(bit_or<word_t>(pad, byte));
177+
buffer.at(15) = byte_bits;
178+
schedule(buffer);
179+
compress(state, buffer);
180+
return output(state);
181+
};
182+
183+
if (std::is_constant_evaluated())
184+
{
185+
return hasher(byte);
186+
}
187+
else if constexpr (native && SHA::strength == 256)
188+
{
189+
return native_hash(byte);
190+
}
191+
else
192+
{
193+
return hasher(byte);
194+
}
195+
}
196+
134197
} // namespace sha
135198
} // namespace system
136199
} // namespace libbitcoin

test/hash/sha/sha256.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,21 @@ BOOST_AUTO_TEST_CASE(sha256__hash__half_blocks__expected)
194194
BOOST_CHECK_EQUAL(sha256::hash(sha256::half_t{ 0 }, sha256::half_t{ 0 }), expected);
195195
}
196196

197+
BOOST_AUTO_TEST_CASE(sha256__hash__quart_blocks__expected)
198+
{
199+
constexpr auto expected = sha256::hash(sha256::quart_t{ 0 }, sha256::quart_t{ 0 });
200+
static_assert(sha256::hash(sha256::half_t{ 0 }) == expected);
201+
BOOST_CHECK_EQUAL(sha256::hash(sha256::quart_t{ 0 }, sha256::quart_t{ 0 }), expected);
202+
}
203+
204+
BOOST_AUTO_TEST_CASE(sha256__hash__byte__expected)
205+
{
206+
// github.com/mit-dci/rustreexo/blob/main/src/accumulator/node_hash.rs#L338
207+
constexpr auto expected = base16_array("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d");
208+
static_assert(sha256::hash(0) == expected);
209+
BOOST_CHECK_EQUAL(sha256::hash(0), expected);
210+
}
211+
197212
// sha256::double_hash
198213
BOOST_AUTO_TEST_CASE(sha256__double_hash__full_block__expected)
199214
{

test/hash/sha/sha512.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,13 @@ BOOST_AUTO_TEST_CASE(sha512__hash__half_blocks__expected)
256256
BOOST_CHECK_EQUAL(sha512::hash(sha512::half_t{ 0 }, sha512::half_t{ 0 }), expected);
257257
}
258258

259+
BOOST_AUTO_TEST_CASE(sha512__hash__quart_blocks__expected)
260+
{
261+
constexpr auto expected = sha512::hash(sha512::quart_t{ 0 }, sha512::quart_t{ 0 });
262+
static_assert(sha512::hash(sha512::half_t{ 0 }) == expected);
263+
BOOST_CHECK_EQUAL(sha512::hash(sha512::quart_t{ 0 }, sha512::quart_t{ 0 }), expected);
264+
}
265+
259266
// sha512::double_hash
260267
BOOST_AUTO_TEST_CASE(sha512__double_hash__full_block__expected)
261268
{

0 commit comments

Comments
 (0)