我需要获得少量"加密好"的随机字节.(在我的情况下是8个字节.)是否有任何Windows API?
PS.如果这些API向后兼容Windows XP,那就太好了.但如果没有,它仍然有效.谢谢.
我知道我最初询问的是Windows API,但是自从我的原帖后我有时间做研究.所以我想分享我的发现.
事实证明,自从他们的Ivy Bridge芯片组以来,英特尔通过RDRAND CPU指令提供了一个非常酷的硬件随机数发生器.
由于这是关于Windows实现和大多数Windows PC在英特尔芯片组上运行的问题,我决定编写一个小类(我不敢相信,我说它)似乎生成了真正的随机数.以下是对其工作原理的描述,以及对英特尔RNG 的分析.
我也假设这个代码是为32位进程编译的(如果有人需要它来进行64位实现,你将不得不调整asm部分.)谨慎地说,不应该这样做假设它将在任何英特尔硬件上运行.正如我上面所说,它需要一个相对较新的英特尔的Ivy Bridge或更高版本的芯片组.(我在后来的Haswell系统板上进行了测试.)好消息是,几乎没有时间来确定是否支持RDRAND指令,如果不支持,最明显的路线应该是使用任何OS提供的API,在其他帖子中描述.(同时结合两种方法的结果也可以增加最终结果的熵.)
所以这就是我如何调用方法来生成随机数:
CHardwareRandomNumberGenerator h; BYTE arr[4096] = {0}; UINT ncbSz = sizeof(arr); int r = h.GetHardwareRandomBytes(arr, &ncbSz); if(ncbSz != sizeof(arr)) //We'll need only the full array { //Use an alternate RNG method: //- RtlGenRandom() //or //- CryptGenRandom() } _tprintf(L"RdRand result is %d\n", r); if(ncbSz > 0) { _tprintf(L"Random Bytes (%d): ", ncbSz); for(UINT i = 0; i < ncbSz; i++) { _tprintf(L"%02x", arr[i]); } _tprintf(L"\n"); }
这是头文件:
//This class uses the Intel RdRand CPU instruction for //the random number generator that is compliant with security //and cryptographic standards: // // http://en.wikipedia.org/wiki/RdRand // #pragma once class CHardwareRandomNumberGenerator { public: CHardwareRandomNumberGenerator(void); ~CHardwareRandomNumberGenerator(void); int GetHardwareRandomBytes(BYTE* pOutRndVals = NULL, UINT* pncbInOutSzRndVals = NULL, DWORD dwmsMaxWait = 5 * 1000); private: BOOL bRdRandSupported; static BOOL __is_cpuid_supported(void); static BOOL __cpuid(int data[4], int nID); int __fillHardwareRandomBytes(BYTE* pOutRndVals, UINT* pncbInOutSzRndVals, UINT& ncbOutSzWritten, DWORD dwmsMaxWait); };
和实施文件:
//This class uses the Intel RdRand CPU instruction for //the random number generator that is compliant with security //and cryptographic standards: // // http://en.wikipedia.org/wiki/RdRand // //[32-bit Intel-only implementation] // #include "HardwareRandomNumberGenerator.h" CHardwareRandomNumberGenerator::CHardwareRandomNumberGenerator(void) : bRdRandSupported(FALSE) { //Check that RdRand instruction is supported if(__is_cpuid_supported()) { //It must be Intel CPU int name[4] = {0}; if(__cpuid(name, 0)) { if(name[1] == 0x756e6547 && //uneG name[2] == 0x6c65746e && //letn name[3] == 0x49656e69) //Ieni { //Get flag itself int data[4] = {0}; if(__cpuid(data, 1)) { //Check bit 30 on the 2nd index (ECX register) if(data[2] & (0x1 << 30)) { //Supported! bRdRandSupported = TRUE; } } } } } } CHardwareRandomNumberGenerator::~CHardwareRandomNumberGenerator(void) { } int CHardwareRandomNumberGenerator::GetHardwareRandomBytes(BYTE* pOutRndVals, UINT* pncbInOutSzRndVals, DWORD dwmsMaxWait) { //Generate random numbers into the 'pOutRndVals' buffer //INFO: This function uses CPU/hardware to generate a set of // random numbers that are cryptographically strong. //INFO: For more details refer to: // http://electronicdesign.com/learning-resources/understanding-intels-ivy-bridge-random-number-generator //INFO: To review the "ANALYSIS OF INTEL’S IVY BRIDGE DIGITAL RANDOM NUMBER GENERATOR" check: // http://www.cryptography.com/public/pdf/Intel_TRNG_Report_20120312.pdf //'pOutRndVals' = if not NULL, points to the buffer that receives random bytes //'pncbInOutSzRndVals' = if not NULL, on the input must contain the number of BYTEs to write into the 'pOutRndVals' buffer // on the output will contain the number of BYTEs actually written into the 'pOutRndVals' buffer //'dwmsMaxWait' = timeout for this method, expressed in milliseconds //RETURN: // = 1 if hardware random number generator is supported & the buffer in 'pOutRndVals' was successfully filled out with random numbers // = 0 if hardware random number generator is supported, but timed out while filling out the buffer in 'pOutRndVals' // INFO: Check 'pncbInOutSzRndVals', it will contain the number of BYTEs actually written into the 'pOutRndVals' array // = -1 if general error // = -2 if hardware random number generator is not supported on this hardware // INFO: Requires Intel Ivy Bridge, or later chipset. UINT ncbSzWritten = 0; int nRes = __fillHardwareRandomBytes(pOutRndVals, pncbInOutSzRndVals, ncbSzWritten, dwmsMaxWait); if(pncbInOutSzRndVals) *pncbInOutSzRndVals = ncbSzWritten; return nRes; } int CHardwareRandomNumberGenerator::__fillHardwareRandomBytes(BYTE* pOutRndVals, UINT* pncbInOutSzRndVals, UINT& ncbOutSzWritten, DWORD dwmsMaxWait) { //INTERNAL METHOD ncbOutSzWritten = 0; //Check support if(!bRdRandSupported) return -2; __try { //We must have a buffer to fill out if(pOutRndVals && pncbInOutSzRndVals && (int*)*pncbInOutSzRndVals > 0) { //Begin timing ticks in ms DWORD dwmsIniTicks = ::GetTickCount(); UINT ncbSzRndVals = *pncbInOutSzRndVals; //Fill in data array for(UINT i = 0; i < ncbSzRndVals; i += sizeof(DWORD)) { DWORD random_value; int got_value; int nFailureCount = 0; //Since RdRand instruction may not have enough random numbers //in its buffer, we may need to "loop" while waiting for it to //generate more results... //For the first 10 failures we'll simply loop around, after which we //will wait for 1 ms per each failed iteration to save on the overall //CPU cycles that this method may consume. for(;; nFailureCount++ < 10 ? 1 : ::Sleep(1)) { __asm { push eax push edx xor eax, eax ;RDRAND instruction = Set random value into EAX. Will set overflow [C] flag if success _emit 0x0F _emit 0xC7 _emit 0xF0 mov edx, 1 ;Check if the value was available in the RNG buffer jc lbl_set_it ;It wasn't available xor edx, edx xor eax, eax lbl_set_it: mov dword ptr [got_value], edx mov dword ptr [random_value], eax pop edx pop eax } if(got_value) { //Got random value OK break; } //Otherwise RdRand instruction failed to produce a random value //See if we timed out? if(::GetTickCount() - dwmsIniTicks > dwmsMaxWait) { //Timed out return 0; } //Try again } //We now have a 4-byte, or DWORD, random value //So let's put it into our array if(i + sizeof(DWORD) <= ncbSzRndVals) { *(DWORD*)(pOutRndVals + i) = random_value; ncbOutSzWritten += sizeof(DWORD); } else if(i + sizeof(WORD) + sizeof(BYTE) <= ncbSzRndVals) { *(WORD*)(pOutRndVals + i) = (WORD)random_value; *(BYTE*)(pOutRndVals + i + sizeof(WORD)) = (BYTE)(random_value >> 16); ncbOutSzWritten += sizeof(WORD) + sizeof(BYTE); } else if(i + sizeof(WORD) <= ncbSzRndVals) { *(WORD*)(pOutRndVals + i) = (WORD)random_value; ncbOutSzWritten += sizeof(WORD); } else if(i + sizeof(BYTE) <= ncbSzRndVals) { *(BYTE*)(pOutRndVals + i) = (BYTE)random_value; ncbOutSzWritten += sizeof(BYTE); } else { //Shouldn't even be here ASSERT(NULL); return -1; } } } } __except(1) { //A generic catch-all just to be sure... return -1; } return 1; } BOOL CHardwareRandomNumberGenerator::__is_cpuid_supported(void) { //See if CPUID command is supported //INFO: Some really old CPUs may not support it! //RETURN: = TRUE if yes, and __cpuid() can be called BOOL bSupported; DWORD nEFlags = 0; __try { #define FLAG_VALUE (0x1 << 21) _asm { //remember EFLAGS & EAX pushfd push eax //Set bit 21 in EFLAGS pushfd pop eax or eax, FLAG_VALUE push eax popfd //Check if bit 21 in EFLAGS was set pushfd pop eax mov nEFlags, eax //Restore EFLAGS & EAX pop eax popfd } bSupported = (nEFlags & FLAG_VALUE) ? TRUE : FALSE; } __except(1) { //A generic catch-all just to be sure... bSupported = FALSE; } return bSupported; } BOOL CHardwareRandomNumberGenerator::__cpuid(int data[4], int nID) { //INFO: Call __is_cpuid_supported() first to see if this function is supported //RETURN: // = TRUE if success, check 'data' for results BOOL bRes = TRUE; __try { _asm { push eax push ebx push ecx push edx push esi //Call CPUID mov eax, nID _emit 0x0f ;CPUID _emit 0xa2 //Save 4 registers mov esi, data mov dword ptr [esi], eax mov dword ptr [esi + 4], ebx mov dword ptr [esi + 8], ecx mov dword ptr [esi + 12], edx pop esi pop edx pop ecx pop ebx pop eax } } __except(1) { //A generic catch-all just to be sure... bRes = FALSE; } return bRes; }
所以,伙计们,伙计们,我还没有对上述方法产生的数据做过任何广泛的加密分析......所以你将成为法官.欢迎任何更新!