mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
146 lines
2.8 KiB
C++
146 lines
2.8 KiB
C++
#include "SSL.h"
|
|
|
|
#define LLOG(x) // DLOG("SecureRandomGenerator: " << x)
|
|
|
|
namespace Upp {
|
|
|
|
namespace {
|
|
|
|
std::atomic<bool> sForked(false);
|
|
std::atomic<uint64> sId(0);
|
|
std::atomic<uint64> sCounter(0);
|
|
SpinLock sLock;
|
|
|
|
constexpr const int NONCE_MIN = 12;
|
|
constexpr const int NONCE_STRUCTURED_MIN = 16;
|
|
|
|
inline void FillRandom(void* ptr, int len)
|
|
{
|
|
if(len <= 0)
|
|
return;
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
if(RAND_status() != 1) {
|
|
RAND_poll();
|
|
if(RAND_status() != 1)
|
|
throw Exc("SecureRandom: RNG not seeded");
|
|
}
|
|
#endif
|
|
if(RAND_bytes(reinterpret_cast<byte*>(ptr), len) != 1)
|
|
throw Exc("SecureRandom: RAND_bytes failed");
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
static_assert(sizeof(uint64) == 8, "Secure random/nonce generator requires 64-bit integers");
|
|
|
|
SslInitThread();
|
|
|
|
ONCELOCK {
|
|
uint32 seed = 0;
|
|
FillRandom(&seed, sizeof(seed));
|
|
sCounter = (uint64) seed;
|
|
#ifdef PLATFORM_POSIX
|
|
pthread_atfork(nullptr, nullptr, [] {
|
|
sForked = true;
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
RAND_cleanup();
|
|
#endif
|
|
});
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
void HandleFork()
|
|
{
|
|
#ifdef PLATFORM_POSIX
|
|
if(!sForked.load())
|
|
return;
|
|
// After fork(), child inherits RNG state. We must reseed once to avoid
|
|
// nonce/counter reuse. SpinLock ensures only one thread performs reseed
|
|
// while others wait until state becomes consistent.
|
|
SpinLock::Lock __(sLock);
|
|
if(sForked.load()) {
|
|
uint32 seed = 0;
|
|
FillRandom(&seed, sizeof(seed));
|
|
sCounter = (uint64) seed;
|
|
sId = 0;
|
|
sForked = false;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
uint64 GetNonceDomainId()
|
|
{
|
|
if(uint64 v = sId.load(); v)
|
|
return v;
|
|
|
|
uint64 x = 0;
|
|
FillRandom(&x, sizeof(x));
|
|
if(!x) x = 1;
|
|
|
|
uint64 expected = 0;
|
|
if(sId.compare_exchange_strong(expected, x))
|
|
return x;
|
|
|
|
return sId.load();
|
|
}
|
|
|
|
uint64 NextCounter()
|
|
{
|
|
// simple atomic increment is enough here
|
|
uint64 v = ++sCounter;
|
|
if(v == 0)
|
|
throw Exc("SecureRandom: counter overflow");
|
|
return v;
|
|
}
|
|
|
|
}
|
|
|
|
SecureBuffer<byte> SecureRandom(int n)
|
|
{
|
|
Init();
|
|
HandleFork();
|
|
n = max(1, n);
|
|
SecureBuffer<byte> out(n);
|
|
FillRandom(~out, n);
|
|
return pick(out);
|
|
}
|
|
|
|
SecureBuffer<byte> SecureNonce(int n)
|
|
{
|
|
Init();
|
|
HandleFork();
|
|
uint64 did = GetNonceDomainId();
|
|
uint64 cnt = NextCounter();
|
|
|
|
n = max(n, NONCE_MIN);
|
|
SecureBuffer<byte> out(n);
|
|
|
|
byte *p = ~out;
|
|
|
|
// 12-15 byte layout
|
|
// 4 bytes PID | 8 bytes counter | [random tail]
|
|
if(n < NONCE_STRUCTURED_MIN) {
|
|
Poke32(p, (dword) did);
|
|
p += sizeof(dword);
|
|
Poke64(p, (int64) cnt);
|
|
p += sizeof(int64);
|
|
if(int len = n - NONCE_MIN; len > 0)
|
|
FillRandom(p, len);
|
|
return pick(out);
|
|
}
|
|
|
|
// 16-byte structured layout
|
|
// 8 bytes PID | 8 bytes counter | [random tail]
|
|
Poke64(p, (int64) did);
|
|
p += sizeof(int64);
|
|
Poke64(p, (int64) cnt);
|
|
p += sizeof(int64);
|
|
if(int len = n - NONCE_STRUCTURED_MIN; len > 0)
|
|
FillRandom(p, len);
|
|
return pick(out);
|
|
}
|
|
|
|
|
|
}
|