我需要获得少量“加密好”的随机字节。 (在我的情况下是8个字节。)是否有任何Windows API?
PS。如果这些API向后兼容Windows XP,那就太好了。但如果没有,它仍然有效。感谢。
答案 0 :(得分:14)
我知道我最初询问的是Windows API,但是自从我的原帖后我有时间做研究。所以我想分享我的发现。
事实证明,自从他们的Ivy Bridge芯片组以来,英特尔通过RDRAND CPU instruction提供了一个非常酷的硬件随机数生成器。
由于这是关于Windows实施和大多数Windows PC在英特尔芯片组上运行的问题,我决定编写一个小类(我不敢相信我说的话)似乎正在生成真正的随机数。 Here's the description它的工作原理,以及英特尔RNG的here's the analysis。
我还假设这个代码是为32位进程编译的(如果有人需要它来进行64位实现,你必须调整asm部分。)同样谨慎地说不应该假设它将在任何英特尔硬件上运行。正如我上面所说,它需要一个相对较新的英特尔的Ivy Bridge或更高版本的芯片组。 (我在后来的Haswell系统板上进行了测试。)好消息是,几乎没有时间来确定是否支持RDRAND指令,如果没有,最明显的路线应该是使用任何{{3其他帖子中描述的。 (同时结合两种方法的结果也可以增加最终结果的熵。)
所以这就是我如何调用该方法来生成随机数:
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;
}
所以,伙计们,伙计们,我没有对上述方法产生的数据进行过任何广泛的加密分析......所以你将成为法官。欢迎任何更新!
答案 1 :(得分:3)
这里有一些代码可以产生一系列密码强大的代码。字节,使用Microsoft Cryptography API ...我自己也使用过它,除了其他任何东西它是一个很好的方式来获得一个体面的随机数字序列...我没有使用它用于加密:
#include <wincrypt.h>
class RandomSequence
{
HCRYPTPROV hProvider;
public:
RandomSequence(void) : hProvider(NULL) {
if (FALSE == CryptAcquireContext(&hProvider, NULL, NULL, PROV_RSA_FULL, 0)) {
// failed, should we try to create a default provider?
if (NTE_BAD_KEYSET == GetLastError()) {
if (FALSE == CryptAcquireContext(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
// ensure the provider is NULL so we could use a backup plan
hProvider = NULL;
}
}
}
}
~RandomSequence(void) {
if (NULL != hProvider) {
CryptReleaseContext(hProvider, 0U);
}
}
BOOL generate(BYTE* buf, DWORD len) {
if (NULL != hProvider) {
return CryptGenRandom(hProvider, len, buf);
}
return FALSE;
}
};
这是一个简单的小班,试图获得一个RSA加密&#34;提供商&#34;,如果失败,它会尝试创建一个。然后,如果一切顺利,generate
将用爱充满你的缓冲区。嗯...我的意思是随机字节。
这对我有用,包括XP,Win7和Win8,以及我实际上并没有将它用于加密,我只需要一个不错的随机字节序列。
答案 2 :(得分:2)
#include <stdexcept>
#include <string>
#include <sstream>
#ifndef __linux__
// For Windows
// Also Works with: MinGW Compiler
#include <windows.h>
#include <wincrypt.h> /* CryptAcquireContext, CryptGenRandom */
int RandBytes(void* const byte_buf, const size_t byte_len) {
HCRYPTPROV p;
ULONG i;
if (CryptAcquireContext(&p, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) == FALSE) {
throw runtime_error{"RandBtyes(): CryptAcquireContext failed."};
}
if (CryptGenRandom(p, byte_len, (BYTE*)byte_buf) == FALSE) {
throw runtime_error{"RandBytes(): CryptGenRandom failed."};
}
CryptReleaseContext(p, 0);
return 0;
}
#endif // Not Linux
#if __linux__
#include <fctl.h>
int RandBytes(void* const byte_buf, const size_t byte_len) {
// NOTE: /dev/random is supposately cryptographically safe
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
throw runtime_error{"RandBytes(): failed to open"};
}
int rd_len = 0;
while(rd_len < byte_len) {
int n = read(fd, byte_buf, byte_len);
if (n < 0){
stringstream ss;
ss << "RandBytes(): failed (n=" << n << ") " << "(rd_len=" << rd_len << ")";
throw runtime_error{ss.str()};
}
rd_len += n;
}
close(fd);
return 0;
}
#endif
答案 3 :(得分:0)
不确定它的便携性,可能只是BSD / Mac;但这里是arc4random_buf
:
void arc4random_buf(void *buf, size_t nbytes);
MacOS man
页面显示:
这些函数使用密码伪随机数生成器非常快速地生成高质量的随机字节。