diff --git a/src/Rand.cpp b/src/Rand.cpp index bc0b3c3..82f5666 100644 --- a/src/Rand.cpp +++ b/src/Rand.cpp @@ -1,4 +1,5 @@ #include "Rand.hpp" +#include "core/Core.hpp" std::unique_ptr Rand::generator; @@ -33,6 +34,58 @@ float Rand::randFloat() { return Rand::randFloat(0.0f, 1.0f); } +#define RANDBYTES 8 + +/* + * Cryptographically secure RNG. Borrowed from bcrypt_gensalt(). + */ +uint64_t Rand::cryptoRand() { + uint8_t buf[RANDBYTES]; + +#ifdef _WIN32 + HCRYPTPROV p; + + // Acquire a crypt context for generating random bytes. + if (CryptAcquireContext(&p, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) == FALSE) { + goto fail; + } + + if (CryptGenRandom(p, RANDBYTES, (BYTE*)buf) == FALSE) { + goto fail; + } + + if (CryptReleaseContext(p, 0) == FALSE) { + goto fail; + } +#else + int fd; + + // Get random bytes on Unix/Linux. + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + perror("open"); + goto fail; + } + + if (read(fd, buf, RANDBYTES) < RANDBYTES) { + perror("read"); + close(fd); + goto fail; + } + + close(fd); +#endif + + return *(uint64_t*)buf; + +fail: + std::cout << "[FATAL] Failed to generate cryptographic random number" << std::endl; + terminate(0); + + /* not reached */ + return 0; +} + void Rand::init(uint64_t seed) { Rand::generator = std::make_unique(std::mt19937(seed)); } diff --git a/src/Rand.hpp b/src/Rand.hpp index 1c770e2..8f93053 100644 --- a/src/Rand.hpp +++ b/src/Rand.hpp @@ -14,6 +14,8 @@ namespace Rand { int32_t randWeighted(const std::vector& weights); + uint64_t cryptoRand(); + float randFloat(float startInclusive, float endExclusive); float randFloat(float endExclusive); float randFloat(); diff --git a/src/servers/CNLoginServer.cpp b/src/servers/CNLoginServer.cpp index c8c2087..fd152f3 100644 --- a/src/servers/CNLoginServer.cpp +++ b/src/servers/CNLoginServer.cpp @@ -477,7 +477,7 @@ void CNLoginServer::characterSelect(CNSocket* sock, CNPacketData* data) { if (lm->plr.iID == 0) return invalidCharacter(sock); - resp.iEnterSerialKey = Rand::rand(); // TODO: cryptographic RNG + resp.iEnterSerialKey = Rand::cryptoRand(); // transfer ownership of connection data to CNShared CNShared::storeLoginMetadata(resp.iEnterSerialKey, lm);