From fff1db973096a893475f3ec96ee6f28e35c4a67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 21 May 2018 15:48:17 +0200 Subject: [PATCH] Common: Add Random utilities This makes it easier to generate random numbers or fill a buffer with random data in a cryptographically secure way. This also replaces existing usages of RNG functions in the codebase: * is pretty hard to use correctly, and std::random_device does not give enough guarantees about its results (it's implementation-defined, non cryptographically secure and could be deterministic on some platforms). Doing things correctly is error prone and verbose. * rand() is terrible and should not be used especially in crypto code. --- Source/Core/Common/CMakeLists.txt | 2 + Source/Core/Common/Common.vcxproj | 4 +- Source/Core/Common/Common.vcxproj.filters | 4 +- Source/Core/Common/Crypto/ec.cpp | 26 ++++-------- Source/Core/Common/Network.cpp | 9 +---- Source/Core/Common/Random.cpp | 49 +++++++++++++++++++++++ Source/Core/Common/Random.h | 15 +++++++ Source/Core/Common/TraversalClient.cpp | 14 +------ Source/Core/Common/TraversalServer.cpp | 31 ++------------ Source/Core/Core/Analytics.cpp | 8 ++-- 10 files changed, 91 insertions(+), 71 deletions(-) create mode 100644 Source/Core/Common/Random.cpp create mode 100644 Source/Core/Common/Random.h diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 0feb950b91..4b32f97607 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(common PerformanceCounter.cpp Profiler.cpp QoSSession.cpp + Random.cpp SDCardUtil.cpp SettingsHandler.cpp StringUtil.cpp @@ -156,6 +157,7 @@ endif() if(UNIX) # Posix networking code needs to be fixed for Windows add_executable(traversal_server TraversalServer.cpp) + target_link_libraries(traversal_server PRIVATE common) if(SYSTEMD_FOUND) target_link_libraries(traversal_server ${SYSTEMD_LIBRARIES}) endif() diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 995c6e733e..b1a7261f11 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -142,6 +142,7 @@ + @@ -206,6 +207,7 @@ + @@ -250,4 +252,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index dd041d490b..a5163abff6 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -63,6 +63,7 @@ + @@ -295,6 +296,7 @@ + @@ -357,4 +359,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Common/Crypto/ec.cpp b/Source/Core/Common/Crypto/ec.cpp index 9a57300f89..78cd346ebd 100644 --- a/Source/Core/Common/Crypto/ec.cpp +++ b/Source/Core/Common/Crypto/ec.cpp @@ -15,6 +15,8 @@ #include "Common/Common.h" #include "Common/Crypto/bn.h" #include "Common/Crypto/ec.h" +#include "Common/Random.h" +#include "Common/StringUtil.h" #ifdef _MSC_VER #pragma warning(push) @@ -230,30 +232,18 @@ static Point operator*(const u8* a, const Point& b) return d; } -static void silly_random(u8* rndArea, u8 count) -{ - u16 i; - srand((unsigned)(time(nullptr))); - - for (i = 0; i < count; i++) - { - rndArea[i] = rand(); - } -} - Signature Sign(const u8* key, const u8* hash) { u8 e[30]{}; memcpy(e + 10, hash, 20); - // Changing random number generator to a lame one... u8 m[30]; - silly_random(m, sizeof(m)); - // fp = fopen("/dev/random", "rb"); - // if (fread(m, sizeof m, 1, fp) != 1) - // fatal("reading random"); - // fclose(fp); - m[0] = 0; + do + { + // Generate 240 bits and keep 233. + Common::Random::Generate(m, sizeof(m)); + m[0] &= 1; + } while (bn_compare(m, ec_N, sizeof(m)) >= 0); Elt r = (m * ec_G).X(); if (bn_compare(r.data.data(), ec_N, 30) >= 0) diff --git a/Source/Core/Common/Network.cpp b/Source/Core/Common/Network.cpp index f492efd5ae..1d95e0c8a4 100644 --- a/Source/Core/Common/Network.cpp +++ b/Source/Core/Common/Network.cpp @@ -5,11 +5,10 @@ #include #include #include -#include #include "Common/Network.h" +#include "Common/Random.h" #include "Common/StringUtil.h" -#include "Common/Timer.h" namespace Common { @@ -31,11 +30,7 @@ void GenerateMacAddress(const MACConsumer type, u8* mac) } // Generate the 24-bit NIC-specific portion of the MAC address. - std::default_random_engine generator(Common::Timer::GetTimeMs()); - std::uniform_int_distribution distribution(0x00, 0xFF); - mac[3] = static_cast(distribution(generator)); - mac[4] = static_cast(distribution(generator)); - mac[5] = static_cast(distribution(generator)); + Common::Random::Generate(&mac[3], 3); } std::string MacAddressToString(const u8* mac) diff --git a/Source/Core/Common/Random.cpp b/Source/Core/Common/Random.cpp new file mode 100644 index 0000000000..c512e7695d --- /dev/null +++ b/Source/Core/Common/Random.cpp @@ -0,0 +1,49 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/Random.h" + +#include +#include + +#include "Common/Assert.h" + +namespace Common::Random +{ +class CSPRNG final +{ +public: + CSPRNG() + { + mbedtls_entropy_init(&m_entropy); + mbedtls_hmac_drbg_init(&m_context); + const int ret = mbedtls_hmac_drbg_seed(&m_context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), + mbedtls_entropy_func, &m_entropy, nullptr, 0); + ASSERT(ret == 0); + } + + ~CSPRNG() + { + mbedtls_hmac_drbg_free(&m_context); + mbedtls_entropy_free(&m_entropy); + } + + void Generate(void* buffer, std::size_t size) + { + const int ret = mbedtls_hmac_drbg_random(&m_context, static_cast(buffer), size); + ASSERT(ret == 0); + } + +private: + mbedtls_entropy_context m_entropy; + mbedtls_hmac_drbg_context m_context; +}; + +static thread_local CSPRNG s_csprng; + +void Generate(void* buffer, std::size_t size) +{ + s_csprng.Generate(buffer, size); +} +} // namespace Common::Random diff --git a/Source/Core/Common/Random.h b/Source/Core/Common/Random.h new file mode 100644 index 0000000000..430b24201c --- /dev/null +++ b/Source/Core/Common/Random.h @@ -0,0 +1,15 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Common/CommonTypes.h" + +namespace Common::Random +{ +/// Fill `buffer` with random bytes using a cryptographically secure pseudo-random number generator. +void Generate(void* buffer, std::size_t size); +} // namespace Common::Random diff --git a/Source/Core/Common/TraversalClient.cpp b/Source/Core/Common/TraversalClient.cpp index 4c415b2c64..36eb431c7f 100644 --- a/Source/Core/Common/TraversalClient.cpp +++ b/Source/Core/Common/TraversalClient.cpp @@ -4,22 +4,12 @@ #include #include -#include #include #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" - -static void GetRandomishBytes(u8* buf, size_t size) -{ - // We don't need high quality random numbers (which might not be available), - // just non-repeating numbers! - static std::mt19937 prng(enet_time_get()); - static std::uniform_int_distribution u8_distribution(0, 255); - for (size_t i = 0; i < size; i++) - buf[i] = u8_distribution(prng); -} +#include "Common/Random.h" TraversalClient::TraversalClient(ENetHost* netHost, const std::string& server, const u16 port) : m_NetHost(netHost), m_Server(server), m_port(port) @@ -280,7 +270,7 @@ TraversalRequestId TraversalClient::SendTraversalPacket(const TraversalPacket& p { OutgoingTraversalPacketInfo info; info.packet = packet; - GetRandomishBytes((u8*)&info.packet.requestId, sizeof(info.packet.requestId)); + Common::Random::Generate(&info.packet.requestId, sizeof(info.packet.requestId)); info.tries = 0; m_OutgoingTraversalPackets.push_back(info); ResendPacket(&m_OutgoingTraversalPackets.back()); diff --git a/Source/Core/Common/TraversalServer.cpp b/Source/Core/Common/TraversalServer.cpp index 5da45a05fe..097418b143 100644 --- a/Source/Core/Common/TraversalServer.cpp +++ b/Source/Core/Common/TraversalServer.cpp @@ -20,6 +20,7 @@ #include #endif +#include "Common/Random.h" #include "Common/TraversalProto.h" #define DEBUG 0 @@ -114,7 +115,6 @@ struct hash } static int sock; -static int urandomFd; static std::unordered_map outgoingPackets; static std::unordered_map> connectedClients; @@ -166,28 +166,11 @@ static sockaddr_in6 MakeSinAddr(const TraversalInetAddress& addr) return result; } -static void GetRandomBytes(void* output, size_t size) -{ - static u8 bytes[8192]; - static size_t bytesLeft = 0; - if (bytesLeft < size) - { - ssize_t rv = read(urandomFd, bytes, sizeof(bytes)); - if (rv != sizeof(bytes)) - { - perror("read from /dev/urandom"); - exit(1); - } - bytesLeft = sizeof(bytes); - } - memcpy(output, bytes + (bytesLeft -= size), size); -} - static void GetRandomHostId(TraversalHostId* hostId) { char buf[9]; u32 num; - GetRandomBytes(&num, sizeof(num)); + Common::Random::Generate(&num, sizeof(num)); sprintf(buf, "%08x", num); memcpy(hostId->data(), buf, 8); } @@ -215,7 +198,7 @@ static void TrySend(const void* buffer, size_t size, sockaddr_in6* addr) static TraversalPacket* AllocPacket(const sockaddr_in6& dest, TraversalRequestId misc = 0) { TraversalRequestId requestId; - GetRandomBytes(&requestId, sizeof(requestId)); + Common::Random::Generate(&requestId, sizeof(requestId)); OutgoingPacketInfo* info = &outgoingPackets[requestId]; info->dest = dest; info->misc = misc; @@ -376,14 +359,6 @@ static void HandlePacket(TraversalPacket* packet, sockaddr_in6* addr) int main() { int rv; - - urandomFd = open("/dev/urandom", O_RDONLY); - if (urandomFd < 0) - { - perror("open /dev/urandom"); - return 1; - } - sock = socket(PF_INET6, SOCK_DGRAM, 0); if (sock == -1) { diff --git a/Source/Core/Core/Analytics.cpp b/Source/Core/Core/Analytics.cpp index 6c45c1e400..dcd635f5b8 100644 --- a/Source/Core/Core/Analytics.cpp +++ b/Source/Core/Core/Analytics.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #if defined(_WIN32) @@ -16,6 +15,7 @@ #include "Common/Analytics.h" #include "Common/CPUDetect.h" #include "Common/CommonTypes.h" +#include "Common/Random.h" #include "Common/StringUtil.h" #include "Common/Version.h" #include "Core/ConfigManager.h" @@ -73,9 +73,9 @@ void DolphinAnalytics::ReloadConfig() void DolphinAnalytics::GenerateNewIdentity() { - std::random_device rd; - u64 id_high = (static_cast(rd()) << 32) | rd(); - u64 id_low = (static_cast(rd()) << 32) | rd(); + u64 id_high, id_low; + Common::Random::Generate(&id_high, sizeof(id_high)); + Common::Random::Generate(&id_low, sizeof(id_low)); m_unique_id = StringFromFormat("%016" PRIx64 "%016" PRIx64, id_high, id_low); // Save the new id in the configuration.