1#ifndef DICE_HASH_BLAKE2B_HPP
2#define DICE_HASH_BLAKE2B_HPP
8#if __has_include(<sodium.h>)
18namespace dice::hash::blake2b {
20 inline constexpr size_t min_output_extent = crypto_generichash_blake2b_BYTES_MIN;
21 inline constexpr size_t max_output_extent = crypto_generichash_blake2b_BYTES_MAX;
22 inline constexpr size_t default_output_extent = crypto_generichash_blake2b_BYTES;
23 inline constexpr size_t dynamic_output_extent = std::dynamic_extent;
25 inline constexpr size_t salt_extent = crypto_generichash_blake2b_SALTBYTES;
26 inline constexpr std::array<std::byte, salt_extent> default_salt{};
28 inline constexpr size_t personality_extent = crypto_generichash_blake2b_PERSONALBYTES;
29 inline constexpr std::array<std::byte, personality_extent> default_personality{};
31 inline constexpr size_t min_key_extent = crypto_generichash_blake2b_KEYBYTES_MIN;
32 inline constexpr size_t max_key_extent = crypto_generichash_blake2b_KEYBYTES_MAX;
33 inline constexpr size_t default_key_extent = crypto_generichash_blake2b_KEYBYTES;
38 template<
size_t KeyExtent>
39 requires (KeyExtent == std::dynamic_extent || (KeyExtent >= min_key_extent && KeyExtent <= max_key_extent))
40 void generate_key(std::span<std::byte, KeyExtent> key_out) {
41 if constexpr (KeyExtent == std::dynamic_extent) {
42 if (key_out.size() < min_key_extent || key_out.size() > max_key_extent) {
43 throw std::runtime_error{
"Invalid blake2b key size"};
47 using byte_utype = std::underlying_type_t<std::byte>;
49 std::random_device rng;
50 std::uniform_int_distribution<byte_utype> dist{std::numeric_limits<byte_utype>::min(), std::numeric_limits<byte_utype>::max()};
52 std::generate(key_out.begin(), key_out.end(), [&]() {
53 return static_cast<std::byte>(dist(rng));
58 template<
size_t InnerOutputExtent>
60 crypto_generichash_blake2b_state state_;
64 struct Blake2bInner<dynamic_output_extent> {
65 crypto_generichash_blake2b_state state_;
66 size_t specified_output_len_;
70 template<
size_t OutputExtent = dynamic_output_extent>
71 requires (OutputExtent == dynamic_output_extent || (OutputExtent >= min_output_extent && OutputExtent <= max_output_extent))
76 static constexpr size_t output_extent = OutputExtent;
79 detail::Blake2bInner<output_extent> inner_;
81 void init(
size_t output_len,
82 std::span<std::byte const> key,
83 std::span<std::byte const, salt_extent> salt,
84 std::span<std::byte const, personality_extent> personality) {
86 if (output_len < min_output_extent || output_len > max_output_extent) {
87 throw std::runtime_error{
"Invalid blake2b output size"};
91 if (key.size() < min_key_extent || key.size() > max_key_extent) {
92 throw std::runtime_error{
"Invalid blake2b key size"};
96 if (
auto const res = sodium_init(); res == -1) {
97 throw std::runtime_error{
"Could not initialize sodium"};
100 if constexpr (output_extent == dynamic_output_extent) {
101 inner_.specified_output_len_ = output_len;
104 auto const res = crypto_generichash_blake2b_init_salt_personal(&inner_.state_,
105 reinterpret_cast<unsigned char const *
>(key.data()),
108 reinterpret_cast<unsigned char const *
>(salt.data()),
109 reinterpret_cast<unsigned char const *
>(personality.data()));
125 explicit Blake2b(
size_t output_len,
126 std::span<std::byte const> key = {},
127 std::span<std::byte const, salt_extent> salt = default_salt,
128 std::span<std::byte const, personality_extent> personality = default_personality)
129 requires (output_extent == dynamic_output_extent) {
130 init(output_len, key, salt, personality);
139 explicit Blake2b(std::span<std::byte const> key = {},
140 std::span<std::byte const, salt_extent> salt = default_salt,
141 std::span<std::byte const, personality_extent> personality = default_personality)
142 requires (output_extent != dynamic_output_extent) {
143 init(output_extent, key, salt, personality);
149 void digest(std::span<std::byte const> data)
noexcept {
150 auto const res = crypto_generichash_blake2b_update(&inner_.state_,
151 reinterpret_cast<unsigned char const *
>(data.data()),
162 void finish(std::span<std::byte, output_extent> out) &&
noexcept(output_extent != dynamic_output_extent ) {
163 if constexpr (output_extent == dynamic_output_extent) {
164 auto const expected_out_len = inner_.specified_output_len_;
165 if (expected_out_len != 0 && out.size() != expected_out_len) {
167 throw std::runtime_error{
"Buffer length must match output length"};
171 auto const res = crypto_generichash_blake2b_final(&inner_.state_,
172 reinterpret_cast<unsigned char *
>(out.data()),
183 [[nodiscard]]
constexpr size_t concrete_output_extent() const noexcept {
184 if constexpr (output_extent == dynamic_output_extent) {
185 return inner_.specified_output_len_;
187 return output_extent;
194 static void hash_single(std::span<std::byte const> data,
195 std::span<std::byte, output_extent> out,
196 std::span<std::byte const> key = {},
197 std::span<std::byte const, salt_extent> salt = default_salt,
198 std::span<std::byte const, personality_extent> personality = default_personality) {
200 if constexpr (output_extent == dynamic_output_extent) {
201 return Blake2b{out.size(), key, salt, personality};
203 return Blake2b{key, salt, personality};
208 std::move(blake).finish(out);
215#error "Cannot include Blake2b.hpp if sodium is not available"