Index: source/lib/sysdep/os/unix/unix.cpp =================================================================== --- source/lib/sysdep/os/unix/unix.cpp +++ source/lib/sysdep/os/unix/unix.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2021 Wildfire Games. +/* Copyright (c) 2022 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -320,29 +320,6 @@ return L""; } -Status sys_generate_random_bytes(u8* buf, size_t count) -{ - FILE* f = fopen("/dev/urandom", "rb"); - if (!f) - WARN_RETURN(ERR::FAIL); - - while (count) - { - size_t numread = fread(buf, 1, count, f); - if (numread == 0) - { - fclose(f); - WARN_RETURN(ERR::FAIL); - } - buf += numread; - count -= numread; - } - - fclose(f); - - return INFO::OK; -} - Status sys_get_proxy_config(const std::wstring& UNUSED(url), std::wstring& UNUSED(proxy)) { return INFO::SKIPPED; Index: source/lib/sysdep/os/win/wsysdep.cpp =================================================================== --- source/lib/sysdep/os/win/wsysdep.cpp +++ source/lib/sysdep/os/win/wsysdep.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -503,23 +503,6 @@ } -Status sys_generate_random_bytes(u8* buffer, size_t size) -{ - HCRYPTPROV hCryptProv = 0; - if(!CryptAcquireContext(&hCryptProv, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) - WARN_RETURN(StatusFromWin()); - - memset(buffer, 0, size); - if(!CryptGenRandom(hCryptProv, (DWORD)size, (BYTE*)buffer)) - WARN_RETURN(StatusFromWin()); - - if(!CryptReleaseContext(hCryptProv, 0)) - WARN_RETURN(StatusFromWin()); - - return INFO::OK; -} - - #if CONFIG_ENABLE_BOOST /* Index: source/lib/sysdep/sysdep.h =================================================================== --- source/lib/sysdep/sysdep.h +++ source/lib/sysdep/sysdep.h @@ -157,14 +157,6 @@ **/ extern size_t sys_max_sector_size(); -/** - * generate high-quality random bytes. - * - * this should only be used with small numbers of bytes, to avoid - * hogging the system's entropy. - **/ -Status sys_generate_random_bytes(u8* buf, size_t count); - /** * get the proxy address for accessing the given HTTP URL. * Index: source/lib/sysdep/tests/test_sysdep.h =================================================================== --- source/lib/sysdep/tests/test_sysdep.h +++ source/lib/sysdep/tests/test_sysdep.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -37,14 +37,6 @@ class TestSysdep : public CxxTest::TestSuite { public: - void test_random() - { - u64 a = 0, b = 0; - TS_ASSERT_OK(sys_generate_random_bytes((u8*)&a, sizeof(a))); - TS_ASSERT_OK(sys_generate_random_bytes((u8*)&b, sizeof(b))); - TS_ASSERT_DIFFERS(a, b); - } - void test_sys_ExecutablePathname() { OsPath path = sys_ExecutablePathname(); Index: source/lobby/XmppClient.cpp =================================================================== --- source/lobby/XmppClient.cpp +++ source/lobby/XmppClient.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -106,7 +106,7 @@ m_xpartamuppId = sXpartamupp + "@" + m_server + "/CC"; m_echelonId = sEchelon + "@" + m_server + "/CC"; // Generate a unique, unpredictable resource to allow multiple 0 A.D. instances to connect to the lobby. - glooxwrapper::JID clientJid(sUsername + "@" + m_server + "/0ad-" + ps_generate_guid()); + glooxwrapper::JID clientJid(sUsername + "@" + m_server + "/0ad-" + PS::GenerateGUID()); glooxwrapper::JID roomJid(m_room + "@conference." + m_server + "/" + sNick); // If we are connecting, use the full jid and a password Index: source/network/NetServer.cpp =================================================================== --- source/network/NetServer.cpp +++ source/network/NetServer.cpp @@ -938,7 +938,7 @@ return false; } - CStr guid = ps_generate_guid(); + CStr guid = PS::GenerateGUID(); int count = 0; // Ensure unique GUID while(std::find_if( @@ -951,7 +951,7 @@ session->Disconnect(NDR_GUID_FAILED); return true; } - guid = ps_generate_guid(); + guid = PS::GenerateGUID(); } session->SetGUID(guid); Index: source/network/scripting/JSInterface_Network.cpp =================================================================== --- source/network/scripting/JSInterface_Network.cpp +++ source/network/scripting/JSInterface_Network.cpp @@ -97,7 +97,7 @@ } // Generate a secret to identify the host client. - std::string secret = ps_generate_guid(); + std::string secret = PS::GenerateGUID(); g_NetServer->SetControllerSecret(secret); g_Game = new CGame(storeReplay); Index: source/ps/GUID.h =================================================================== --- source/ps/GUID.h +++ source/ps/GUID.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -20,6 +20,9 @@ #include "ps/CStr.h" -CStr8 ps_generate_guid(void); +namespace PS +{ +CStr GenerateGUID(); +} #endif Index: source/ps/GUID.cpp =================================================================== --- source/ps/GUID.cpp +++ source/ps/GUID.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -15,27 +15,41 @@ * along with 0 A.D. If not, see . */ #include "precompiled.h" -#include "lib/sysdep/sysdep.h" -#include "ps/CStr.h" -CStr ps_generate_guid(void) +#include "ps/GUID.h" + +#include +#include +#include + +// TODO: Ideally this will be guaranteed unique (and verified +// cryptographically) since we'll rely on it to identify hosts +// and associate them with player controls (e.g. to support +// leaving/rejoining in-progress games), and we don't want +// a host to masquerade as someone else. +// For now, just try to pick a very random number. + +namespace PS +{ +static u64 GenerateGUIDNumber() { - // TODO: Ideally this will be guaranteed unique (and verified - // cryptographically) since we'll rely on it to identify hosts - // and associate them with player controls (e.g. to support - // leaving/rejoining in-progress games), and we don't want - // a host to masquerade as someone else. - // For now, just try to pick a very random number. + std::random_device rd; + std::uniform_int_distribution distrib; - CStr guid; - for (size_t i = 0; i < 2; ++i) + if (rd.entropy() > 0) + return distrib(rd); + + std::default_random_engine fallback { - u32 r = 0; - sys_generate_random_bytes((u8*)&r, sizeof(r)); - char buf[32]; - sprintf_s(buf, ARRAY_SIZE(buf), "%08X", r); - guid += buf; - } + static_cast( + std::chrono::high_resolution_clock::now().time_since_epoch().count()) + }; + return distrib(fallback); +} - return guid; +CStr GenerateGUID() +{ + const u64 r = GenerateGUIDNumber(); + return fmt::format("{:016X}", r); +} } Index: source/ps/UserReport.h =================================================================== --- source/ps/UserReport.h +++ source/ps/UserReport.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ #ifndef INCLUDED_USERREPORT #define INCLUDED_USERREPORT -#include +#include "ps/CStr.h" class CUserReporterWorker; @@ -55,7 +55,7 @@ void SubmitReport(const std::string& type, int version, const std::string& data, const std::string& dataHumanReadable); private: - std::string LoadUserID(); + CStr LoadUserID(); CUserReporterWorker* m_Worker; }; Index: source/ps/UserReport.cpp =================================================================== --- source/ps/UserReport.cpp +++ source/ps/UserReport.cpp @@ -28,6 +28,7 @@ #include "lib/sysdep/sysdep.h" #include "ps/ConfigDB.h" #include "ps/Filesystem.h" +#include "ps/GUID.h" #include "ps/Profiler2.h" #include "ps/Pyrogenesis.h" #include "ps/Threading.h" @@ -91,7 +92,7 @@ class CUserReporterWorker { public: - CUserReporterWorker(const std::string& userID, const std::string& url) : + CUserReporterWorker(const CStr& userID, const CStr& url) : m_URL(url), m_UserID(userID), m_Enabled(false), m_Shutdown(false), m_Status("disabled"), m_PauseUntilTime(timer_Time()), m_LastUpdateTime(timer_Time()) { @@ -516,33 +517,23 @@ ENSURE(!m_Worker); // Deinitialize should have been called before shutdown } -std::string CUserReporter::LoadUserID() +CStr CUserReporter::LoadUserID() { - std::string userID; + CStr userID; // Read the user ID from user.cfg (if there is one) CFG_GET_VAL("userreport.id", userID); - // If we don't have a validly-formatted user ID, generate a new one - if (userID.length() != 16) - { - u8 bytes[8] = {0}; - sys_generate_random_bytes(bytes, ARRAY_SIZE(bytes)); - // ignore failures - there's not much we can do about it + if (userID.length() == 16) + return userID; - userID = ""; - for (size_t i = 0; i < ARRAY_SIZE(bytes); ++i) - { - char hex[3]; - sprintf_s(hex, ARRAY_SIZE(hex), "%02x", (unsigned int)bytes[i]); - userID += hex; - } + // We don't have a validly-formatted user ID, generate a new one + const CStr newUserID = PS::GenerateGUID(); - g_ConfigDB.SetValueString(CFG_USER, "userreport.id", userID); - g_ConfigDB.WriteValueToFile(CFG_USER, "userreport.id", userID); - } + g_ConfigDB.SetValueString(CFG_USER, "userreport.id", newUserID); + g_ConfigDB.WriteValueToFile(CFG_USER, "userreport.id", newUserID); - return userID; + return newUserID; } bool CUserReporter::IsReportingEnabled() @@ -574,8 +565,8 @@ { ENSURE(!m_Worker); // must only be called once - std::string userID = LoadUserID(); - std::string url; + CStr userID = LoadUserID(); + CStr url; CFG_GET_VAL("userreport.url_upload", url); m_Worker = new CUserReporterWorker(userID, url); Index: source/ps/scripting/JSInterface_Main.cpp =================================================================== --- source/ps/scripting/JSInterface_Main.cpp +++ source/ps/scripting/JSInterface_Main.cpp @@ -71,7 +71,7 @@ std::wstring GetMatchID() { - return ps_generate_guid().FromUTF8(); + return PS::GenerateGUID().FromUTF8(); } JS::Value LoadMapSettings(const ScriptInterface& scriptInterface, const VfsPath& pathname) Index: source/ps/tests/test_GUID.h =================================================================== --- /dev/null +++ source/ps/tests/test_GUID.h @@ -0,0 +1,33 @@ +/* Copyright (C) 2022 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#include "lib/self_test.h" + +#include "ps/GUID.h" + +class TestGUID : public CxxTest::TestSuite +{ +public: + void test_GUID() + { + const CStr testAgainst = PS::GenerateGUID(); + for(size_t i = 0; i != 100; ++i) + { + TS_ASSERT_DIFFERS(PS::GenerateGUID(), testAgainst); + } + } +};