En büyük kusurun, std::random_device
CSPRNG 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 ( sysrandom
aşağıda tanımlandığı gibi ) etrafında minimal bir sarmalayıcı kullanabilirsiniz .
pencereler
CryptGenRandom
Bir 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 getrandom
aynı şekilde davranır /dev/urandom
. Aşağıdaki kod parçası, Linux'un getrandom
kullanı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_filebuf
ve FILE
standart 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 FILE
alınmış okumalar kullanır ve bu nedenle kullanılmamalıdır.
Ayrıca bahsettiği getrandom
ve OpenBSD'nin eksikliğinden dolayı Peter Cordes'e teşekkür ederim /dev/urandom
.