BTC之随机数生成

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 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

版权归本人所有,如需转载,请注明出处

你可能感兴趣的:(BTC之随机数生成)