मैं इसके साथ सबसे बड़ी खामी का तर्क दूंगा std::random_device
कि अगर कोई CSPRNG उपलब्ध नहीं है, तो यह एक निर्धारक गिरावट की अनुमति है। यह अकेले एक PRNG का उपयोग न करने का एक अच्छा कारण है std::random_device
, क्योंकि उत्पादित बाइट्स निर्धारक हो सकती हैं। दुर्भाग्यवश ऐसा होने पर, या निम्न-गुणवत्ता वाले यादृच्छिक संख्याओं के बजाय विफलता का अनुरोध करने के लिए एक एपीआई प्रदान नहीं करता है।
यही है, कोई पूरी तरह से पोर्टेबल समाधान नहीं है: हालांकि, एक सभ्य, न्यूनतम दृष्टिकोण है। sysrandom
PRNG को बोने के लिए आप CSPRNG ( नीचे के रूप में परिभाषित ) के आसपास एक न्यूनतम आवरण का उपयोग कर सकते हैं ।
खिड़कियाँ
आप CryptGenRandom
एक CSPRNG पर भरोसा कर सकते हैं । उदाहरण के लिए, आप निम्नलिखित कोड का उपयोग कर सकते हैं:
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;
}
यूनिक्स की तरह
कई यूनिक्स जैसी प्रणालियों पर, आपको जब संभव हो तो / dev / urandom का उपयोग करना चाहिए (हालाँकि यह POSIX-compliant सिस्टम पर मौजूद होने की गारंटी नहीं है)।
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;
}
अन्य
यदि कोई CSPRNG उपलब्ध नहीं है, तो आप भरोसा करना चुन सकते हैं std::random_device
। हालाँकि, यदि संभव हो तो मैं इससे बचता हूँ, क्योंकि विभिन्न संकलक (सबसे विशेष रूप से, MinGW) इसे PRNG के रूप में कार्यान्वित करते हैं (वास्तव में, मनुष्यों को सचेत करने के लिए हर बार उसी क्रम का निर्माण करना कि यह ठीक से यादृच्छिक नहीं है)।
बोने
अब हमारे पास न्यूनतम ओवरहेड के साथ हमारे टुकड़े हैं, हम अपने PRNG को सीड करने के लिए यादृच्छिक एंट्रोपी के वांछित बिट्स उत्पन्न कर सकते हैं। PRNG को बोने के लिए उदाहरण (स्पष्ट रूप से अपर्याप्त) 32-बिट्स का उपयोग करता है, और आपको इस मान को बढ़ाना चाहिए (जो आपके CSPRNG पर निर्भर है)।
std::uint_least32_t seed;
sysrandom(&seed, sizeof(seed));
std::mt19937 gen(seed);
बूस्ट से तुलना
हम स्रोत कोड पर त्वरित नज़र डालने के बाद :: random_device (एक सच्चा CSPRNG) को बढ़ावा देने के लिए समानताएं देख सकते हैं । बूस्ट MS_DEF_PROV
विंडोज पर उपयोग करता है, जिसके लिए प्रदाता प्रकार है PROV_RSA_FULL
। केवल एक चीज गायब है जो क्रिप्टोग्राफिक संदर्भ की पुष्टि करेगी, जिसके साथ किया जा सकता है CRYPT_VERIFYCONTEXT
। * निक्स पर, बूस्ट का उपयोग करता है /dev/urandom
। IE, यह समाधान पोर्टेबल, अच्छी तरह से परीक्षण किया गया है, और उपयोग में आसान है।
लिनक्स विशेषज्ञता
यदि आप सुरक्षा के लिए getrandom
पर्याप्तता का त्याग करने के लिए तैयार हैं, तो लिनक्स 3.17 और इसके बाद के संस्करण और हाल ही के सोलारिस पर एक उत्कृष्ट विकल्प है। यह पता getrandom
लगाने के लिए /dev/urandom
कि क्या कर्नेल ने अपने CSPRNG को बूट करने के बाद अभी तक इनिशियलाइज़ नहीं किया है, को छोड़कर, यह व्यवहारिक रूप से व्यवहार करता है । यदि लिनक्स getrandom
उपलब्ध है, तो निम्नलिखित स्निपेट पता लगाता है और यदि वापस नहीं आता है /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
एक अंतिम चेतावनी है: आधुनिक ओपनबीएसडी में नहीं है /dev/urandom
। आपको इसके बजाय गेटेंट्रोपी का उपयोग करना चाहिए ।
#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
अन्य विचार
यदि आपको क्रिप्टोग्राफिक रूप से सुरक्षित रैंडम बाइट्स की आवश्यकता है, तो आपको संभवतः पॉस्क्स के अनबर्डेड ओपन / रीड / क्लोज़ के साथ फ़्लोस्ट को बदलना चाहिए। इसका कारण यह है दोनों है basic_filebuf
और FILE
एक आंतरिक बफर है, जो एक मानक संभाजक के माध्यम से आवंटित किया जाएगा (और इसलिए स्मृति से मिटा दिया नहीं) होते हैं।
इसे आसानी से बदलकर किया जा सकता है 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;
}
धन्यवाद
इंगित करने के लिए बेन वोइग्ट के लिए विशेष धन्यवाद FILE
बफ़र्ड रीड का उपयोग करता है, और इसलिए इसका उपयोग नहीं किया जाना चाहिए।
मैं पीटर कॉर्ड्स का उल्लेख करने के लिए धन्यवाद देना चाहूंगा getrandom
, और ओपनबीएसडी की कमी /dev/urandom
।