1#ifndef DICE_HASH_BLAKE2XB_HPP
2#define DICE_HASH_BLAKE2XB_HPP
9#if __has_include(<sodium.h>)
11#include <dice/hash/blake/Blake2b.hpp>
24namespace dice::hash::blake2xb {
28 inline std::byte
const *byte_iter(T
const &value)
noexcept {
29 return reinterpret_cast<std::byte
const *
>(&value);
33 inline std::byte *byte_iter_mut(T &value)
noexcept {
34 return reinterpret_cast<std::byte *
>(&value);
38 inline T little_endian(T
const &value)
noexcept {
39 if constexpr (std::endian::native == std::endian::little) {
42 auto const *input_beg = byte_iter(value);
43 auto const *input_end = input_beg +
sizeof(T);
46 std::reverse_copy(input_beg, input_end, byte_iter_mut(output));
52 inline constexpr size_t unknown_output_extent = 0;
53 inline constexpr size_t min_output_extent = 1;
54 inline constexpr size_t max_output_extent = std::numeric_limits<uint32_t>::max() - 1;
55 using ::dice::hash::blake2b::dynamic_output_extent;
57 using ::dice::hash::blake2b::salt_extent;
58 using ::dice::hash::blake2b::default_salt;
60 using ::dice::hash::blake2b::personality_extent;
61 using ::dice::hash::blake2b::default_personality;
63 using ::dice::hash::blake2b::min_key_extent;
64 using ::dice::hash::blake2b::max_key_extent;
65 using ::dice::hash::blake2b::default_key_extent;
67 using ::dice::hash::blake2b::generate_key;
72 template<
size_t OutputExtent = dynamic_output_extent>
73 requires (OutputExtent == dynamic_output_extent || (OutputExtent >= min_output_extent && OutputExtent <= max_output_extent))
78 static constexpr size_t output_extent = OutputExtent;
80 static constexpr size_t min_key_extent = ::dice::hash::blake2xb::min_key_extent;
81 static constexpr size_t max_key_extent = ::dice::hash::blake2xb::max_key_extent;
82 static constexpr size_t default_key_extent = ::dice::hash::blake2xb::default_key_extent;
85 static constexpr uint32_t unknown_output_extend_magic = std::numeric_limits<uint32_t>::max();
94 uint32_t xof_digest_len;
98 uint8_t salt[salt_extent];
99 uint8_t personality[personality_extent];
103 crypto_generichash_blake2b_state state_;
105 void init_state(std::span<std::byte const> key) {
106 static constexpr std::array<uint64_t, 8> init_vec{0x6a09e667f3bcc908ULL,
107 0xbb67ae8584caa73bULL,
108 0x3c6ef372fe94f82bULL,
109 0xa54ff53a5f1d36f1ULL,
110 0x510e527fade682d1ULL,
111 0x9b05688c2b3e6c1fULL,
112 0x1f83d9abfb41bd6bULL,
113 0x5be0cd19137e2179ULL};
115#if SODIUM_LIBRARY_VERSION_MAJOR > 10 || (SODIUM_LIBRARY_VERSION_MAJOR == 10 && SODIUM_LIBRARY_VERSION_MINOR >= 2)
120 struct Blake2bState {
128 auto *state =
reinterpret_cast<Blake2bState *
>(&state_);
130 auto *state = &state_;
133 static_assert(
sizeof(ParamBlock) ==
sizeof(init_vec));
134 std::span<uint64_t
const, init_vec.size()> param{*
reinterpret_cast<uint64_t const(*)[init_vec.size()]
>(¶m_)};
137 for (
size_t ix = 0; ix < init_vec.size(); ++ix) {
138 state->h[ix] = init_vec[ix] ^ detail::little_endian(param[ix]);
142 std::fill(detail::byte_iter_mut(state->t),
143 detail::byte_iter_mut(state->last_node) +
sizeof(state->last_node),
147 std::array<std::byte, 128> block; {
148 auto write_end = std::copy(key.begin(), key.end(), block.begin());
149 std::fill(write_end, block.end(), std::byte{});
153 sodium_memzero(block.data(), block.size());
157 struct PrivateTag {};
158 static constexpr PrivateTag private_tag{};
162 std::span<std::byte const> key,
163 std::span<std::byte const, salt_extent> salt,
164 std::span<std::byte const, personality_extent> personality) {
166 if (output_len == 0) {
167 output_len = unknown_output_extend_magic;
168 }
else if (output_len > max_output_extent) {
169 throw std::runtime_error{
"Output length too large"};
173 if (key.size() < min_key_extent || key.size() > max_key_extent) {
174 throw std::runtime_error{
"Invalid blake2b key size"};
178 if (
auto const res = sodium_init(); res == -1) {
179 throw std::runtime_error{
"Could not initialize sodium"};
182 param_.digest_len = crypto_generichash_blake2b_BYTES_MAX;
183 param_.key_len =
static_cast<uint8_t
>(key.size());
186 param_.xof_digest_len = detail::little_endian(output_len);
188 std::copy(salt.begin(), salt.end(), detail::byte_iter_mut(param_.salt));
189 std::copy(personality.begin(), personality.end(), detail::byte_iter_mut(param_.personality));
202 explicit Blake2Xb(
size_t output_len,
203 std::span<std::byte const> key = {},
204 std::span<std::byte const, salt_extent> salt = default_salt,
205 std::span<std::byte const, personality_extent> personality = default_personality)
206 requires (output_extent == dynamic_output_extent)
207 : Blake2Xb{private_tag, output_len, key, salt, personality} {
216 explicit Blake2Xb(std::span<std::byte const> key = {},
217 std::span<std::byte const, salt_extent> salt = default_salt,
218 std::span<std::byte const, personality_extent> personality = default_personality)
219 : Blake2Xb{private_tag, output_extent == dynamic_output_extent ? 0 : output_extent, key, salt, personality} {
225 void digest(std::span<std::byte const> data)
noexcept {
226 auto const res = crypto_generichash_blake2b_update(&state_,
227 reinterpret_cast<unsigned char const *
>(data.data()),
239 void finish(std::span<std::byte, output_extent> out) &&
noexcept(output_extent != dynamic_output_extent) {
240 if constexpr (output_extent == dynamic_output_extent) {
241 auto const expected_out_len = detail::little_endian(param_.xof_digest_len);
242 if (expected_out_len != unknown_output_extend_magic && out.size() != expected_out_len) {
244 throw std::runtime_error{
"Buffer length must match output length"};
248 std::array<std::byte, crypto_generichash_blake2b_BYTES_MAX> h0;
249 auto res = crypto_generichash_blake2b_final(&state_,
250 reinterpret_cast<unsigned char *
>(h0.data()),
258 param_.leaf_len = detail::little_endian(
static_cast<uint32_t
>(crypto_generichash_blake2b_BYTES_MAX));
259 param_.xof_digest_len = detail::little_endian(
static_cast<uint32_t
>(out.size()));
260 param_.node_depth = 0;
261 param_.inner_len = crypto_generichash_blake2b_BYTES_MAX;
264 size_t remaining = out.size();
266 while (remaining > 0) {
267 param_.node_off = detail::little_endian(
static_cast<uint32_t
>(pos / crypto_generichash_blake2b_BYTES_MAX));
269 size_t const len = std::min(
static_cast<size_t>(crypto_generichash_blake2b_BYTES_MAX), remaining);
270 param_.digest_len =
static_cast<uint8_t
>(len);
273 res = crypto_generichash_blake2b_update(&state_,
274 reinterpret_cast<unsigned char const *
>(h0.data()),
278 res = crypto_generichash_blake2b_final(&state_,
279 reinterpret_cast<unsigned char *
>(out.data()) + pos,
295 [[nodiscard]]
constexpr size_t concrete_output_extent() const noexcept {
296 if constexpr (output_extent == dynamic_output_extent) {
297 auto const expected_out_len = detail::little_endian(param_.xof_digest_len);
298 if (expected_out_len == unknown_output_extend_magic) {
299 return unknown_output_extent;
301 return expected_out_len;
304 return output_extent;
311 static void hash_single(std::span<std::byte const> data,
312 std::span<std::byte, output_extent> out,
313 std::span<std::byte const> key = {},
314 std::span<std::byte const, salt_extent> salt = default_salt,
315 std::span<std::byte const, personality_extent> personality = default_personality) {
317 if constexpr (output_extent == dynamic_output_extent) {
318 return Blake2Xb{out.size(), key, salt, personality};
320 return Blake2Xb{key, salt, personality};
325 std::move(blake).finish(out);
332#error "Cannot include Blake2Xb.hpp if sodium is not available"