Haruyuki Mohri

毛利春幸のブログです。

CryptGenRandom()を使う。

C++Builder 10.2 RTLの場合Random()関数

Delphi / C++Builderの場合RTLで使えるRandomize(), Random()があるので、
9までの数字を作る場合

//C++Builder
int _tmain(int argc, _TCHAR* argv[])
{
    Randomize();
    std::cout << Random(10) << std::endl;
    return 0;
}

こんな感じでシンプルにランダムが使えます。
ですが、RTLの場合内部的にQueryPerformanceCounter()だったり、GetTickCount()だったり
予測できそうで、若干気になります。

CryptGenRandom()を使う。

VC++rand_s()CryptGenRandom()のラッパーとの事です

//VC++
#define _CRT_RAND_S  
#include "stdlib.h"
    unsigned int i{ 0 };
    rand_s(&i);

MSC30-C. 疑似乱数の生成に rand() 関数を使用しない

上記にも書かれていますが、Windows OSの場合、CryptAcquireContext()CryptGenRandom()を使う方法があります。

//C++Builder
#include <wincrypt.h>
#include <functional>

int _tmain(int argc, _TCHAR* argv[])
{

    HCRYPTPROV hprov;
    std::function<bool()> crypt_acquire_context{[&hprov]()->bool
    {
        if (! CryptAcquireContext(&hprov, nullptr, nullptr, PROV_RSA_FULL, 0))
        {
            if (GetLastError() == NTE_BAD_KEYSET)
            {
                return CryptAcquireContext(&hprov, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET);
            }
            return false;
        }
        else
            return true;
    }};
    if ( crypt_acquire_context() )
    {
        int res{0};
        if (CryptGenRandom(hprov, sizeof(res), reinterpret_cast<BYTE*>(&res)))
        {
            std::mt19937_64 lm(res);
            std::uniform_int_distribution<int> ld(0,9);
            std::cout << ld(lm) << std::endl;
        }
        if (! CryptReleaseContext(hprov, 0)) {
            std::cout << "Err=" << GetLastError() << std::endl;
        }
    }
    return 0;
}

初っ端CRYPT_NEWKEYSETを指定すれば次回から必要ないです。
std::random_deviceでもよかった気が。。

f:id:mojeld:20171212171448p:plain