En büyük kusurun, std::random_deviceCSPRNG mevcut olmadığında deterministik bir geri dönüşe izin verilmesi olduğunu iddia ediyorum . std::random_deviceÜretilen baytlar deterministik olabileceğinden, bu tek başına bir PRNG'yi tohumlamamak için iyi bir nedendir . Ne yazık ki, bunun ne zaman olduğunu bulmak veya düşük kaliteli rastgele sayılar yerine hata istemek için bir API sağlamaz.
Yani, tamamen taşınabilir bir çözüm yoktur : ancak, makul, asgari bir yaklaşım vardır. PRNG'yi tohumlamak için bir CSPRNG'nin ( sysrandomaşağıda tanımlandığı gibi ) etrafında minimal bir sarmalayıcı kullanabilirsiniz .
pencereler
CryptGenRandomBir CSPRNG'ye güvenebilirsiniz . Örneğin, aşağıdaki kodu kullanabilirsiniz:
bool acquire_context(HCRYPTPROV *ctx)
{
if (!CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, 0)) {
return CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET);
}
return true;
}
size_t sysrandom(void* dst, size_t dstlen)
{
HCRYPTPROV ctx;
if (!acquire_context(&ctx)) {
throw std::runtime_error("Unable to initialize Win32 crypt library.");
}
BYTE* buffer = reinterpret_cast<BYTE*>(dst);
if(!CryptGenRandom(ctx, dstlen, buffer)) {
throw std::runtime_error("Unable to generate random bytes.");
}
if (!CryptReleaseContext(ctx, 0)) {
throw std::runtime_error("Unable to release Win32 crypt library.");
}
return dstlen;
}
Unix gibi
Pek çok Unix benzeri sistemde, mümkün olduğunda / dev / urandom kullanmalısınız (bunun POSIX uyumlu sistemlerde varlığı garanti edilmese de).
size_t sysrandom(void* dst, size_t dstlen)
{
char* buffer = reinterpret_cast<char*>(dst);
std::ifstream stream("/dev/urandom", std::ios_base::binary | std::ios_base::in);
stream.read(buffer, dstlen);
return dstlen;
}
Diğer
CSPRNG yoksa, güvenmeyi seçebilirsiniz std::random_device. Bununla birlikte, mümkünse bundan kaçınırım, çünkü çeşitli derleyiciler (en önemlisi, MinGW) bunu bir PRNG olarak uygular (aslında, her seferinde insanları rastgele olmadığı konusunda uyarmak için aynı diziyi üretir).
Tohumlama
Artık minimum ek yükü olan parçalarımıza sahip olduğumuza göre, PRNG'mizi tohumlamak için istenen rastgele entropi bitlerini üretebiliriz. Örnek, PRNG'yi başlatmak için (açıkça yetersiz) 32 bit kullanır ve bu değeri artırmanız gerekir (bu, CSPRNG'nize bağlıdır).
std::uint_least32_t seed;
sysrandom(&seed, sizeof(seed));
std::mt19937 gen(seed);
Artırmak İçin Karşılaştırma
Kaynak koduna hızlıca baktıktan sonra :: random_device (gerçek bir CSPRNG) geliştirmek için paralellikler görebiliriz . Boost MS_DEF_PROV, için sağlayıcı türü olan Windows'ta kullanır PROV_RSA_FULL. Eksik olan tek şey, kriptografik bağlamın doğrulanması olacaktır ki bu da yapılabilir CRYPT_VERIFYCONTEXT. * Nix'te, Boost kullanır /dev/urandom. IE, bu çözüm taşınabilir, iyi test edilmiş ve kullanımı kolaydır.
Linux Uzmanlığı
Güvenlik için özünden ödün vermeye istekliysen getrandom, Linux 3.17 ve üzeri ve son Solaris'te mükemmel bir seçimdir. Çekirdek, önyüklemeden sonra CSPRNG'sini henüz başlatmamışsa engellemesi dışında, ile getrandomaynı şekilde davranır /dev/urandom. Aşağıdaki kod parçası, Linux'un getrandomkullanılabilir olup olmadığını ve yoksa geri dönüp dönmediğini tespit eder /dev/urandom.
#if defined(__linux__) || defined(linux) || defined(__linux)
# // Check the kernel version. `getrandom` is only Linux 3.17 and above.
# include <linux/version.h>
# if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
# define HAVE_GETRANDOM
# endif
#endif
// also requires glibc 2.25 for the libc wrapper
#if defined(HAVE_GETRANDOM)
# include <sys/syscall.h>
# include <linux/random.h>
size_t sysrandom(void* dst, size_t dstlen)
{
int bytes = syscall(SYS_getrandom, dst, dstlen, 0);
if (bytes != dstlen) {
throw std::runtime_error("Unable to read N bytes from CSPRNG.");
}
return dstlen;
}
#elif defined(_WIN32)
// Windows sysrandom here.
#else
// POSIX sysrandom here.
#endif
OpenBSD
Son bir uyarı var: modern OpenBSD'de yok /dev/urandom. Bunun yerine getentropy kullanmalısınız.
#if defined(__OpenBSD__)
# define HAVE_GETENTROPY
#endif
#if defined(HAVE_GETENTROPY)
# include <unistd.h>
size_t sysrandom(void* dst, size_t dstlen)
{
int bytes = getentropy(dst, dstlen);
if (bytes != dstlen) {
throw std::runtime_error("Unable to read N bytes from CSPRNG.");
}
return dstlen;
}
#endif
diğer düşünceler
Kriptografik olarak güvenli rasgele baytlara ihtiyacınız varsa, muhtemelen fstream'i POSIX'in arabelleğe alınmamış açma / okuma / kapatma ile değiştirmelisiniz. Her iki Bunun nedeni, basic_filebufve FILEstandart bir ayırıcı ile ayrılan (ve dolayısıyla bellekten sildi) olacak bir iç tampon içerir.
Bu, aşağıdaki şekilde değiştirilerek kolayca yapılabilir sysrandom:
size_t sysrandom(void* dst, size_t dstlen)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd == -1) {
throw std::runtime_error("Unable to open /dev/urandom.");
}
if (read(fd, dst, dstlen) != dstlen) {
close(fd);
throw std::runtime_error("Unable to read N bytes from CSPRNG.");
}
close(fd);
return dstlen;
}
Teşekkürler
Ben Voigt'e özel olarak işaret ettiği için ara belleğe FILEalınmış okumalar kullanır ve bu nedenle kullanılmamalıdır.
Ayrıca bahsettiği getrandomve OpenBSD'nin eksikliğinden dolayı Peter Cordes'e teşekkür ederim /dev/urandom.