使用python的硬件rng

时间:2014-03-27 07:01:07

标签: python random numpy rdrand

是否有现成的库,以便numpy程序可以使用intel硬件prng(rdrand)来填充随机数的缓冲区?

如果没有这个,有人会指出我正确的方向,我可以适应或使用一些C代码(我使用CPython和Cython与numpy,所以最小的封装器就足够了)。

我想要的随机生成器是[0,1)之间的均匀随机数。

3 个答案:

答案 0 :(得分:5)

此代码将使用/ dev / urandom(Unix)或CryptGenRandom API(Windows)。使用哪种RNG,硬件或软件,取决于操作系统。

如果要准确控制使用哪个生成器,则必须通过其硬件驱动程序或库进行查询。当您将随机位作为字符串时,您使用np.fromstring进行类似于此代码的操作。

通常我们可以信任操作系统为其加密服务使用最佳熵源,包括随机比特生成器。如果存在硬件RNG,则可能会使用它,通常与其他熵源组合使用。

import os
import numpy as np

def cryptorand(n):
    a = np.fromstring(os.urandom(n*4), dtype=np.uint32) >> 5
    b = np.fromstring(os.urandom(n*4), dtype=np.uint32) >> 6
    return (a * 67108864.0 + b) / 9007199254740992.0

以下是在Mac OS X上使用此方法生成的1,000,000个随机偏差的分布。正如您所看到的,在[0,1)上非常均匀:

enter image description here

如果您需要非常强大的加密随机偏差,可以使用/ dev / random而不是/ dev / urandom。这仅适用于类Unix系统,而不适用于Windows:

import numpy as np

def cryptorand(n):
    with open('/dev/random','rb') as rnd:
        a = np.fromstring(rnd.read(n*4), dtype=np.uint32) >> 5
        b = np.fromstring(rnd.read(n*4), dtype=np.uint32) >> 6
        return (a * 67108864.0 + b) / 9007199254740992.0

请注意,与使用os.urandom作为熵源的版本不同,此函数可能会阻塞。

(编辑1:更新了标准化以等同于NumPy的标准化)

编辑2:评论表明问题的原因是速度,而不是加密强度。硬件RNG的目的不是速度而是强度,因此它使问题无效。然而,一个快速和良好的PRNG可以替代Mersenne Twister是George Marsaglia的乘法携带发生器。这是Cython中的一个简单实现:

import numpy as np
cimport numpy as cnp

cdef cnp.uint32_t gw = 152315241 # any number except 0 or 0x464fffff
cdef cnp.uint32_t gz = 273283728 # any number except 0 or 0x9068ffff

def rand(cnp.intp_t n):
    """Generate n random numbers using George Marsaglia's 
    multiply-with-carry method."""
    global gw, gz
    cdef cnp.uint32_t w, z, a, b
    cdef cnp.intp_t i
    cdef cnp.ndarray x = cnp.PyArray_SimpleNew(1, &n, cnp.NPY_FLOAT64)
    cdef cnp.float64_t *r = <cnp.float64_t*> cnp.PyArray_DATA(x)
    w,z = gw,gz
    for i in range(n): 
        z = 36969 * (z & 65535) + (z >> 16)
        w = 18000 * (w & 65535) + (w >> 16)
        a = (z << 16) + w
        z = 36969 * (z & 65535) + (z >> 16)
        w = 18000 * (w & 65535) + (w >> 16)
        b = (z << 16) + w
        r[i] = (a * 67108864.0 + b) / 9007199254740992.0
    gw,gz = w,z
    return x

请注意,Mersenne Twister和multip-with-carry都没有加密强度。

答案 1 :(得分:2)

答案 2 :(得分:0)

这里有一篇论文(http://iopscience.iop.org/article/10.3847/1538-4357/aa7ede/meta;jsessionid=A9DA9DDB925E6522D058F3CEEC7D0B21.ip-10-40-2-120)和(非支付版本)(https://arxiv.org/abs/1707.02212),描述了如何使用英特尔安全密钥,这是一个加密安全的随机数生成器-芯片。它可以通过RdRand和RdSeed指令访问。

但作者似乎说你应该用C / C ++而不是Python来实现它。 rdrand python模块运行速度比Python默认随机数生成器慢大约100倍,比Numpy慢一倍(文章5.2节)。