1、概念
有两个最主要的方法来生成随机数:
a) 通过测量某些随机的物理现象,然后补偿测量过程中可能出现的偏差。比如大气噪声、热噪声和其他外部电磁以及量子现象。从自然资源中获取熵的速度取决于被测量的潜在物理现象,因此它们的速率是有限的,往往比较慢。
b) 使用计算算法,可以产生明显随机结果的长序列,实际上由较短的初始值(称为种子)确定。结果是,如果种子的值是已知的,整个看似随机的序列可以被重复产生。这类随机数生成器被称为伪随机数生成器。这类生成器类型是非阻塞的,可以大量产生数据。
2、不同的随机数生成方法
-
类Linux系统下的 /dev/random
/dev/random 可以用作随机数生成器或者伪随机数生成器,取决于不同的实现。
在linux下,随机数生成器有一个容纳噪声数据的熵池,在读取时,/dev/random设备会返回小于熵池噪声总数的随机字节。/dev/random可生成高随机性的公钥或一次性密码本。若熵池空了,对/dev/random的读操作将会被阻塞,直到收集到了足够的环境噪声为止。这样的设计使得/dev/random是真正的随机数发生器,提供了最大可能的随机数据熵,建议在需要生成高强度的密钥时使用。
/dev/urandom(“unblocked”,非阻塞的随机数发生器),它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。它可以作为生成较低强度密码的伪随机数生成器,不建议用于生成高强度长期密码。
FreeBSD操作系统实现了256位的Yarrow算法变体,以提供伪随机数流。与Linux的/dev/random不同,FreeBSD的/dev/random不会产生阻塞,与Linux的/dev/urandom相似,提供了密码学安全的伪随机数发生器,而不是基于熵池。而FreeBSD的/dev/urandom则只是简单的链接到了/dev/random。
void GetDevURandom(unsigned char *ent32)
{
int f = open("/dev/urandom", O_RDONLY);
if (f == -1) {
RandFailure();
}
int have = 0;
do {
ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have);
if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) {
close(f);
RandFailure();
}
have += n;
} while (have < NUM_OS_RANDOM_BYTES);
close(f);
}
-
类Linux系统其他获取随机数方法
Linux下的SYS_getrandom
int rv = syscall(SYS_getrandom, ent32, NUM_OS_RANDOM_BYTES, 0);
- OpenBSD下的getentropy
getentropy(ent32, NUM_OS_RANDOM_BYTES)
- FreeBSD下的sysctl
static const int name[2] = {CTL_KERN, KERN_ARND};
int have = 0;
do {
size_t len = NUM_OS_RANDOM_BYTES - have;
if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, nullptr, 0) != 0) {
RandFailure();
}
have += len;
} while (have < NUM_OS_RANDOM_BYTES);
-
OpenSSL的随机数生成
OpenSSL的提供了RAND_bytes方法生成随机数。在使用之前需要使用RAND_add向PRNG中添加种子及熵值。种子可以通过TSC生成。
void RandAddSeed()
{
// Seed with CPU performance counter
int64_t nCounter = GetPerformanceCounter();
RAND_add(&nCounter, sizeof(nCounter), 1.5);
memory_cleanse((void*)&nCounter, sizeof(nCounter));
}
void GetRandBytes(unsigned char* buf, int num)
{
if (RAND_bytes(buf, num) != 1) {
RandFailure();
}
}
-
Windows平台
CryptoAPI提供了CryptGenRandom方法产生随机数。CryptGenRandom已经被废弃,应该使用新版CNG API:BCryptGenRandom
HCRYPTPROV hProvider;
int ret = CryptAcquireContextW(&hProvider, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
if (!ret) {
RandFailure();
}
ret = CryptGenRandom(hProvider, NUM_OS_RANDOM_BYTES, ent32);
if (!ret) {
RandFailure();
}
CryptReleaseContext(hProvider, 0);
-
硬件方法:
CPU指令rdrand从芯片的硬件随机数生成器中获取随机数
uint32_t eax, ebx, ecx, edx;
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) {
}
32位:
uint32_t r1, r2;
__asm__ volatile (".byte 0x0f, 0xc7, 0xf0;" // rdrand %eax
".byte 0x0f, 0xc7, 0xf2;" // rdrand %edx
"setc %2" :
"=a"(r1), "=d"(r2), "=q"(ok) :: "cc");
if (!ok) return false;
WriteLE32(ent32 + 8 * iter, r1);
WriteLE32(ent32 + 8 * iter + 4, r2);
}
64位:
uint64_t r1, r2, r3, r4;
__asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0, " // rdrand %rax
"0x48, 0x0f, 0xc7, 0xf3, " // rdrand %rbx
"0x48, 0x0f, 0xc7, 0xf1, " // rdrand %rcx
"0x48, 0x0f, 0xc7, 0xf2; " // rdrand %rdx
"setc %4" :
"=a"(r1), "=b"(r2), "=c"(r3), "=d"(r4), "=q"(ok) :: "cc");
if (!ok) return false;
WriteLE64(ent32, r1);
WriteLE64(ent32 + 8, r2);
WriteLE64(ent32 + 16, r3);
WriteLE64(ent32 + 24, r4);
3、BTC生成随机数的方法
BTC采用了多种方式混合的形式,将OpenSSL随机数生成、OS随机数生成、硬件随机数生成再混淆随机数发生器的状态来生成强随机数。
void GetStrongRandBytes(unsigned char* out, int num)
{
assert(num <= 32);
CSHA512 hasher;
unsigned char buf[64];
// First source: OpenSSL's RNG
RandAddSeedPerfmon();
GetRandBytes(buf, 32);
hasher.Write(buf, 32);
// Second source: OS RNG
GetOSRand(buf);
hasher.Write(buf, 32);
// Third source: HW RNG, if available.
if (GetHWRand(buf)) {
hasher.Write(buf, 32);
}
// Combine with and update state
{
std::unique_lock<std::mutex> lock(cs_rng_state);
hasher.Write(rng_state, sizeof(rng_state));
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
++rng_counter;
hasher.Finalize(buf);
memcpy(rng_state, buf + 32, 32);
}
// Produce output
memcpy(out, buf, num);
memory_cleanse(buf, 64);
}
参考文档:
https://en.wikipedia.org/wiki/Random_number_generation#%22True%22_vs._pseudo-random_numbers
https://zh.wikipedia.org/wiki//dev/random
https://www.openssl.org/docs/man1.0.2/crypto/RAND_seed.html
https://zh.wikipedia.org/zh-hans/RdRand
版权归本人所有,如需转载,请注明出处