#ifndef RANDOM_2019_09_24_H #define RANDOM_2019_09_24_H #include #include #include #include #include #include #include #if(__cplusplus >= 201703L) namespace al::crypto::random { #else namespace al { namespace crypto { namespace random { #endif template class random_device { public: using result_type = ResultType; static_assert( (std::numeric_limits::is_integer) && (!std::numeric_limits::is_signed) && (std::numeric_limits::digits <= 512), "Error: At the moment random_device's result_type must " "be an unsigned integral type having 512 bits or fewer."); random_device() = default; random_device(const random_device&) = default; random_device(random_device&&) noexcept = default; ~random_device() = default; auto operator=(const random_device&) -> random_device& = default; auto operator=(random_device&&) noexcept -> random_device& = default; auto operator()() -> result_type { return get_advance(); } static auto (min)() -> result_type { return (std::numeric_limits::min)(); } static auto (max)() -> result_type { return (std::numeric_limits::max)(); } AL_CRYPTO_NODISCARD auto entropy() const -> double { constexpr double my_entropy = 1.0; static_assert(my_entropy > 0.0, "Error: The selected entropy level must be greater than 0."); return my_entropy; } private: using clock_type = std::chrono::high_resolution_clock; static auto initial_now() -> const std::uintmax_t& { // Obtain an initial value of std::chrono::high_resolution_clock::now(). static const auto local_now = static_cast ( std::chrono::duration_cast ( clock_type::now().time_since_epoch() ).count() ); return local_now; } static auto get_advance() -> result_type { // Obtain an initial value of std::chrono::high_resolution_clock::now(). std::uintmax_t current_now { }; do { // Wait for std::chrono::high_resolution_clock::now() // to differ from the initialization value. current_now = static_cast ( std::chrono::duration_cast ( clock_type::now().time_since_epoch() ).count() ); } while(current_now == initial_now()); // Perform a logical xor of current_now with the maximum value of its type. current_now ^= (std::numeric_limits::max)(); // Extract the data bytes of current now into an array... std::array::digits / 8U> current_now_data { }; for(auto i = static_cast(0U); i < current_now_data.size(); ++i) { current_now_data[i] = static_cast(current_now >> (i * 8U)); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) } // ... and... Finally, run a SHA512 hash over the data bytes of current_now. using hash_type = al::crypto::stream::hash::hash_sha512; hash_type h; hash_type::result_type hash_result; h.hash_and_get_result(current_now_data.data(), current_now_data.size(), hash_result.data()); // Extract the required bytes from the hash result // in order to obtain the pseudo-random seed. result_type result(0U); for(auto i = static_cast(0U); i < sizeof(result_type); ++i) { result = static_cast(result | static_cast(static_cast(hash_result[i]) << (i * 8U))); } // Return the pseudo-random seed. return result; } }; template class uniform_int_distribution { public: using result_type = IntegerType; struct param_type { public: explicit param_type(const result_type& a = (std::numeric_limits::min)(), // NOLINT(bugprone-easily-swappable-parameters) const result_type& b = (std::numeric_limits::max)()) : param_a(a), param_b(b) { } param_type(const param_type& other) : param_a(other.param_a), param_b(other.param_b) { } param_type(param_type&& other) noexcept : param_a(static_cast(other.param_a)), param_b(static_cast(other.param_b)) { } ~param_type() = default; auto operator=(const param_type& other) -> param_type& // NOLINT(cert-oop54-cpp) { if(this != &other) { param_a = other.param_a; param_b = other.param_b; } return *this; } auto operator=(param_type&& other) noexcept -> param_type& { param_a = other.param_a; param_b = other.param_b; return *this; } AL_CRYPTO_NODISCARD auto get_a() const -> const result_type& { return param_a; } AL_CRYPTO_NODISCARD auto get_b() const -> const result_type& { return param_b; } auto set_a(const result_type& a) -> void { param_a = a; } auto set_b(const result_type& b) -> void { param_b = b; } private: result_type param_a; result_type param_b; }; uniform_int_distribution() : my_params() { } explicit uniform_int_distribution(const result_type& a, // NOLINT(bugprone-easily-swappable-parameters) const result_type& b = (std::numeric_limits::max)()) : my_params(param_type(a, b)) { } explicit uniform_int_distribution(const param_type& other_params) : my_params(other_params) { } uniform_int_distribution(const uniform_int_distribution& other) : my_params(other.my_params) { } uniform_int_distribution(uniform_int_distribution&& other) noexcept : my_params(other.my_params) { } ~uniform_int_distribution() = default; auto operator=(const uniform_int_distribution& other) -> uniform_int_distribution& // NOLINT(cert-oop54-cpp) { if(this != &other) { my_params = other.my_params; } return *this; } auto operator=(uniform_int_distribution&& other) noexcept -> uniform_int_distribution& { my_params = other.my_params; return *this; } auto param(const param_type& new_params) -> void { my_params = new_params; } AL_CRYPTO_NODISCARD auto param() const -> const param_type& { return my_params; } AL_CRYPTO_NODISCARD auto a() const -> const result_type& { return param().get_a(); } AL_CRYPTO_NODISCARD auto b() const -> const result_type& { return param().get_b(); } template auto operator()(GeneratorType& generator) -> result_type { return generate(generator, my_params); } template auto operator()( GeneratorType& input_generator, const param_type& input_params) -> result_type { return generate(input_generator, input_params); } private: param_type my_params; template auto generate( GeneratorType& input_generator, const param_type& input_params) -> result_type { result_type result = input_generator(); if( (input_params.get_a() != (std::numeric_limits::min)()) || (input_params.get_b() != (std::numeric_limits::max)())) { result_type range(input_params.get_b() - input_params.get_a()); ++range; result %= range; result += input_params.get_a(); } return result; } }; template auto operator==(const uniform_int_distribution& left, const uniform_int_distribution& right) -> bool { return ( (left.params().get_a() == right.params().get_a()) && (left.params().get_b() == right.params().get_b())); } template auto operator!=(const uniform_int_distribution& left, const uniform_int_distribution& right) -> bool { return ( (left.params().get_a() != right.params().get_a()) || (left.params().get_b() != right.params().get_b())); } #if(__cplusplus >= 201703L) } // namespace al::crypto::random #else } // namespace random } // namespace crypto } // namespace al #endif #endif // RANDOM_DEVICE_2019_09_24_H