#ifndef RANDOM_STREAM_FORTUNA_2020_28_01_H #define RANDOM_STREAM_FORTUNA_2020_28_01_H #include #include #include #include #include #include #include #include #include #if(__cplusplus >= 201703L) namespace al::crypto::random { #else namespace al { namespace crypto { namespace random { #endif // TBD: The Fortuna implementation should properly include entropy pools. class random_stream_fortuna : public al::crypto::random::random_stream_base { private: using hash_type = al::crypto::stream::hash::hash_sha256; using counter_array_type = std::array; public: explicit random_stream_fortuna(const std::uint8_t* seed_input_data = nullptr, const std::size_t seed_input_count = static_cast(0U)) { my_key.fill(0U); my_counters.fill(0U); my_reseed(seed_input_data, seed_input_count); } random_stream_fortuna(const random_stream_fortuna&) = default; random_stream_fortuna(random_stream_fortuna&& other) noexcept : random_stream_base(static_cast(other)), my_hash (static_cast(other.my_hash)), my_key (other.my_key), my_counters(other.my_counters) { } template explicit random_stream_fortuna(const UnsignedIntegralType u, typename std::enable_if<( (std::is_fundamental::value) && (std::is_integral ::value) && (std::is_unsigned ::value))>::type* p_nullparam = nullptr) { static_cast(p_nullparam); my_key.fill(0U); my_counters.fill(0U); auto u_local = static_cast(u); std::array array_of_8_seed_bytes = { UINT8_C(0) }; auto it = array_of_8_seed_bytes.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto) while((it != array_of_8_seed_bytes.end()) && (u_local != 0U)) { *it++ = static_cast(u_local); u_local >>= 8U; } my_reseed(array_of_8_seed_bytes.data(), static_cast(UINT8_C(8))); } ~random_stream_fortuna() override = default; auto operator=(const random_stream_fortuna& other) -> random_stream_fortuna& { static_cast(random_stream_base::operator=(other)); if(this != &other) { my_hash = other.my_hash; my_key = other.my_key; my_counters = other.my_counters; } return *this; } auto operator=(random_stream_fortuna&& other) noexcept -> random_stream_fortuna& { static_cast(random_stream_base::operator=(static_cast(other))); my_hash = static_cast(other.my_hash); my_key = other.my_key; my_counters = other.my_counters; return *this; } auto reseed(const std::uint8_t* seed_input_data = nullptr, // NOLINT(google-default-arguments) const std::size_t seed_input_count = static_cast(0U)) -> void override { my_reseed(seed_input_data, seed_input_count); } auto get_bytes(std::uint8_t* output_buffer, const std::size_t count) -> void override { using counters_input_as_uint8_array_type = std::array::value * 4U>; constexpr std::size_t chunk_size = std::tuple_size::value; const auto count_of_full____chunks = static_cast(count / chunk_size); // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) const auto count_in_partial_chunk = static_cast(count % chunk_size); const auto count_of_total___chunks = static_cast( count_of_full____chunks // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) + ((count_in_partial_chunk != 0U) ? 1U : 0U)); for(auto chunk_index = static_cast(0U); chunk_index < count_of_total___chunks; ++chunk_index) { const counters_input_as_uint8_array_type counters_input_as_uint8_array = {{ static_cast(my_counters[0U] >> 0U), static_cast(my_counters[0U] >> 8U), static_cast(my_counters[0U] >> 16U), static_cast(my_counters[0U] >> 24U), static_cast(my_counters[1U] >> 0U), static_cast(my_counters[1U] >> 8U), static_cast(my_counters[1U] >> 16U), static_cast(my_counters[1U] >> 24U), static_cast(my_counters[2U] >> 0U), static_cast(my_counters[2U] >> 8U), static_cast(my_counters[2U] >> 16U), static_cast(my_counters[2U] >> 24U), static_cast(my_counters[3U] >> 0U), static_cast(my_counters[3U] >> 8U), static_cast(my_counters[3U] >> 16U), static_cast(my_counters[3U] >> 24U), }}; using cipher_type = al::crypto::stream::cipher::cipher_aes_ecb<256U>; typename cipher_type::input_output_container_type cipher_result; cipher_type().encrypt(counters_input_as_uint8_array.data(), chunk_size, my_key.data(), nullptr, nullptr, cipher_result); const std::size_t output_buffer_destination_index = chunk_index * chunk_size; const std::size_t output_buffer_destination_count = ((chunk_index < count_of_full____chunks) ? chunk_size : count_in_partial_chunk); std::copy(cipher_result.cbegin(), cipher_result.cbegin() + std::ptrdiff_t(output_buffer_destination_count), output_buffer + output_buffer_destination_index); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) increment_counters(); } } private: hash_type my_hash { }; std::array my_key { }; std::array my_counters { }; auto my_reseed(const std::uint8_t* seed_input_data, const std::size_t seed_input_count) -> void { const std::uint8_t* seed_input_data__to_use = nullptr; // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) std::size_t seed_input_count_to_use = 0U; if(seed_input_data == nullptr) { using random_engine_type = al::crypto::random::random_pcg32; using random_device_type = al::crypto::random::random_device; random_engine_type rng; random_device_type dev; const auto sd = static_cast(dev()); std::array random_data { }; al::crypto::random::random_bytes(random_data.data(), random_data.size(), rng, &sd); seed_input_data__to_use = random_data.data(); seed_input_count_to_use = random_data.size(); } else { seed_input_data__to_use = seed_input_data; seed_input_count_to_use = seed_input_count; } my_hash.hash(seed_input_data__to_use, seed_input_count_to_use); my_key = my_hash.get_result(); increment_counters(); } auto increment_counters() -> void { for(auto i = static_cast(0U); i < my_counters.size(); ++i) { if(++my_counters[i] != static_cast(UINT8_C(0))) // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) { break; } } } }; #if(__cplusplus >= 201703L) } // namespace al::crypto::random #else } // namespace random } // namespace crypto } // namespace al #endif #endif // RANDOM_STREAM_FORTUNA_2020_28_01_H