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:

* <random> 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.
This commit is contained in:
Léo Lam 2018-05-21 15:48:17 +02:00
parent dd77ace56a
commit fff1db9730
10 changed files with 91 additions and 71 deletions

View file

@ -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()

View file

@ -142,6 +142,7 @@
<ClInclude Include="PcapFile.h" />
<ClInclude Include="Profiler.h" />
<ClInclude Include="QoSSession.h" />
<ClInclude Include="Random.h" />
<ClInclude Include="Result.h" />
<ClInclude Include="ScopeGuard.h" />
<ClInclude Include="SDCardUtil.h" />
@ -206,6 +207,7 @@
<ClCompile Include="PcapFile.cpp" />
<ClCompile Include="Profiler.cpp" />
<ClCompile Include="QoSSession.cpp" />
<ClCompile Include="Random.cpp" />
<ClCompile Include="SDCardUtil.cpp" />
<ClCompile Include="SettingsHandler.cpp" />
<ClCompile Include="StringUtil.cpp" />
@ -250,4 +252,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -63,6 +63,7 @@
<ClInclude Include="PcapFile.h" />
<ClInclude Include="Profiler.h" />
<ClInclude Include="QoSSession.h" />
<ClInclude Include="Random.h" />
<ClInclude Include="Result.h" />
<ClInclude Include="ScopeGuard.h" />
<ClInclude Include="SDCardUtil.h" />
@ -295,6 +296,7 @@
<ClCompile Include="Network.cpp" />
<ClCompile Include="PcapFile.cpp" />
<ClCompile Include="Profiler.cpp" />
<ClCompile Include="Random.cpp" />
<ClCompile Include="SDCardUtil.cpp" />
<ClCompile Include="SettingsHandler.cpp" />
<ClCompile Include="StringUtil.cpp" />
@ -357,4 +359,4 @@
<ItemGroup>
<Natvis Include="BitField.natvis" />
</ItemGroup>
</Project>
</Project>

View file

@ -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)

View file

@ -5,11 +5,10 @@
#include <cctype>
#include <cstring>
#include <ctime>
#include <random>
#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<int> distribution(0x00, 0xFF);
mac[3] = static_cast<u8>(distribution(generator));
mac[4] = static_cast<u8>(distribution(generator));
mac[5] = static_cast<u8>(distribution(generator));
Common::Random::Generate(&mac[3], 3);
}
std::string MacAddressToString(const u8* mac)

View file

@ -0,0 +1,49 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Common/Random.h"
#include <mbedtls/entropy.h>
#include <mbedtls/hmac_drbg.h>
#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<u8*>(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

View file

@ -0,0 +1,15 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#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

View file

@ -4,22 +4,12 @@
#include <cstddef>
#include <cstring>
#include <random>
#include <string>
#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<unsigned int> 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());

View file

@ -20,6 +20,7 @@
#include <systemd/sd-daemon.h>
#endif
#include "Common/Random.h"
#include "Common/TraversalProto.h"
#define DEBUG 0
@ -114,7 +115,6 @@ struct hash<TraversalHostId>
}
static int sock;
static int urandomFd;
static std::unordered_map<TraversalRequestId, OutgoingPacketInfo> outgoingPackets;
static std::unordered_map<TraversalHostId, EvictEntry<TraversalInetAddress>> 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)
{

View file

@ -4,7 +4,6 @@
#include <mbedtls/sha1.h>
#include <memory>
#include <mutex>
#include <random>
#include <string>
#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<u64>(rd()) << 32) | rd();
u64 id_low = (static_cast<u64>(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.