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"