热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何简洁,便携,彻底地播种mt19937PRNG?

如何解决《如何简洁,便携,彻底地播种mt19937PRNG?》经验,为你挑选了3个好方法。

我似乎看到很多答案,有人建议使用它来生成随机数,通常伴随着这样的代码:

std::random_device rd;  
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 5);
dis(gen);

通常这会取代某种"邪恶的憎恶",例如:

srand(time(NULL));
rand()%6;

我们可能会批评旧的方式,认为time(NULL)提供低熵,time(NULL)可预测,最终结果是不均匀的.

但所有这一切都适用于新的方式:它只有一个更光亮的贴面.

rd()返回一个unsigned int.这至少有16位,可能是32位.这还不足以为MT的19937位状态提供种子.

使用std::mt19937 gen(rd());gen()(以32位播种并查看第一个输出)不能提供良好的输出分布.7和13永远不会是第一个输出.两粒种子产生0.十二粒种子产生1226181350.(链接)

std::random_device可以(有时是)实现为具有固定种子的简单PRNG.因此,它可能在每次运行时产生相同的序列.(链接)这甚至比time(NULL).

更糟糕的是,尽管存在它们包含的问题,但复制和粘贴上述代码片段非常容易.对此的一些解决方案需要获得可能不适合每个人的大型 库.

鉴于此,我的问题是如何在C++中简洁,便携,彻底地播种mt19937 PRNG?

鉴于上述问题,一个很好的答案:

必须完全播种mt19937/mt19937_64.

不能单独依赖std::random_devicetime(NULL)作为熵的来源.

不应该依赖Boost或其他图书馆.

应该适合少量的线条,这样看起来很好,可以复制粘贴到答案中.

思考

我目前的想法是,输出来自std::random_device(可能通过XOR)time(NULL),从地址空间随机化得到的值,以及硬编码常量(可以在分配期间设置)以获得熵的最佳努力.

std::random_device::entropy() 没有很好地说明std::random_device可能做什么或不做什么.

Alexander Hu.. 56

我认为最大的缺陷std::random_device是,如果没有可用的CSPRNG,它将被允许确定性回退.仅这一点是不使用PRNG种子的一个很好的理由std::random_device,因为产生的字节可能是确定性的.遗憾的是,它不提供API来查明何时发生这种情况,或者请求失败而不是低质量的随机数.

也就是说,没有完全可移植的解决方案:但是,有一种体面的,最小的方法.您可以使用CSPRNG(定义sysrandom如下)的最小包装来为PRNG播种.

视窗


您可以信赖CryptGenRandomCSPRNG.例如,您可以使用以下代码:

bool acquire_context(HCRYPTPROV *ctx)
{
    if (!CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, 0)) {
        return CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET);
    }
    return true;
}


size_t sysrandom(void* dst, size_t dstlen)
{
    HCRYPTPROV ctx;
    if (!acquire_context(&ctx)) {
        throw std::runtime_error("Unable to initialize Win32 crypt library.");
    }

    BYTE* buffer = reinterpret_cast(dst);
    if(!CryptGenRandom(ctx, dstlen, buffer)) {
        throw std::runtime_error("Unable to generate random bytes.");
    }

    if (!CryptReleaseContext(ctx, 0)) {
        throw std::runtime_error("Unable to release Win32 crypt library.");
    }

    return dstlen;
}

类Unix


在许多类Unix系统上,应尽可能使用/ dev/urandom(尽管不能保证在POSIX兼容系统上存在).

size_t sysrandom(void* dst, size_t dstlen)
{
    char* buffer = reinterpret_cast(dst);
    std::ifstream stream("/dev/urandom", std::ios_base::binary | std::ios_base::in);
    stream.read(buffer, dstlen);

    return dstlen;
}

其他


如果没有可用的CSPRNG,您可以选择依赖std::random_device.但是,如果可能的话,我会避免这种情况,因为各种编译器(最值得注意的是,MinGW)将它作为PRNG实现(实际上,每次产生相同的序列以提醒人们它不是随机的).

播种


现在我们有了最小的开销,我们可以生成所需的随机熵位来为我们的PRNG播种.该示例使用(显然不足)32位来为PRNG播种,您应该增加此值(这取决于您的CSPRNG).

std::uint_least32_t seed;    
sysrandom(&seed, sizeof(seed));
std::mt19937 gen(seed);

提升比较


在快速查看源代码之后,我们可以看到boost :: random_device(一个真正的CSPRNG)的相似之处.Boost MS_DEF_PROV在Windows上使用,它是提供者类型PROV_RSA_FULL.唯一缺少的就是验证加密上下文,这可以完成CRYPT_VERIFYCONTEXT.在*Nix上,Boost使用/dev/urandom.IE,这个解决方案是便携式的,经过良好测试,易于使用.

Linux专业化


如果您愿意牺牲简洁性来保证安全性,getrandom那么在Linux 3.17及更高版本以及最近的Solaris上是一个很好的选择.getrandom行为完全相同/dev/urandom,除非它在启动后内核尚未初始化其CSPRNG时阻塞.以下代码段检测Linux getrandom是否可用,如果没有,则回退到/dev/urandom.

#if defined(__linux__) || defined(linux) || defined(__linux)
#   // Check the kernel version. `getrandom` is only Linux 3.17 and above.
#   include 
#   if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
#       define HAVE_GETRANDOM
#   endif
#endif

// also requires glibc 2.25 for the libc wrapper
#if defined(HAVE_GETRANDOM)
#   include 
#   include 

size_t sysrandom(void* dst, size_t dstlen)
{
    int bytes = syscall(SYS_getrandom, dst, dstlen, 0);
    if (bytes != dstlen) {
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    return dstlen;
}

#elif defined(_WIN32)

// Windows sysrandom here.

#else

// POSIX sysrandom here.

#endif

OpenBSD系统


最后一个警告:现代OpenBSD没有/dev/urandom.你应该使用getentropy.

#if defined(__OpenBSD__)
#   define HAVE_GETENTROPY
#endif

#if defined(HAVE_GETENTROPY)
#   include 

size_t sysrandom(void* dst, size_t dstlen)
{
    int bytes = getentropy(dst, dstlen);
    if (bytes != dstlen) {
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    return dstlen;
}

#endif

其他想法


如果您需要加密安全的随机字节,您应该用POSIX的无缓冲打开/读取/关闭替换fstream.这是因为两者basic_filebufFILE包含一个内部缓冲器,这将通过标准的分配器被分配(因此不从存储器擦拭).

这可以通过更改sysrandom为:

size_t sysrandom(void* dst, size_t dstlen)
{
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd == -1) {
        throw std::runtime_error("Unable to open /dev/urandom.");
    }
    if (read(fd, dst, dstlen) != dstlen) {
        close(fd);
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    close(fd);
    return dstlen;
}

谢谢


特别感谢Ben Voigt指出FILE使用缓冲读取,因此不应使用.

我还要感谢Peter Cordes的提及getrandom,以及OpenBSD的缺乏/dev/urandom.



1> Alexander Hu..:

我认为最大的缺陷std::random_device是,如果没有可用的CSPRNG,它将被允许确定性回退.仅这一点是不使用PRNG种子的一个很好的理由std::random_device,因为产生的字节可能是确定性的.遗憾的是,它不提供API来查明何时发生这种情况,或者请求失败而不是低质量的随机数.

也就是说,没有完全可移植的解决方案:但是,有一种体面的,最小的方法.您可以使用CSPRNG(定义sysrandom如下)的最小包装来为PRNG播种.

视窗


您可以信赖CryptGenRandomCSPRNG.例如,您可以使用以下代码:

bool acquire_context(HCRYPTPROV *ctx)
{
    if (!CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, 0)) {
        return CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET);
    }
    return true;
}


size_t sysrandom(void* dst, size_t dstlen)
{
    HCRYPTPROV ctx;
    if (!acquire_context(&ctx)) {
        throw std::runtime_error("Unable to initialize Win32 crypt library.");
    }

    BYTE* buffer = reinterpret_cast(dst);
    if(!CryptGenRandom(ctx, dstlen, buffer)) {
        throw std::runtime_error("Unable to generate random bytes.");
    }

    if (!CryptReleaseContext(ctx, 0)) {
        throw std::runtime_error("Unable to release Win32 crypt library.");
    }

    return dstlen;
}

类Unix


在许多类Unix系统上,应尽可能使用/ dev/urandom(尽管不能保证在POSIX兼容系统上存在).

size_t sysrandom(void* dst, size_t dstlen)
{
    char* buffer = reinterpret_cast(dst);
    std::ifstream stream("/dev/urandom", std::ios_base::binary | std::ios_base::in);
    stream.read(buffer, dstlen);

    return dstlen;
}

其他


如果没有可用的CSPRNG,您可以选择依赖std::random_device.但是,如果可能的话,我会避免这种情况,因为各种编译器(最值得注意的是,MinGW)将它作为PRNG实现(实际上,每次产生相同的序列以提醒人们它不是随机的).

播种


现在我们有了最小的开销,我们可以生成所需的随机熵位来为我们的PRNG播种.该示例使用(显然不足)32位来为PRNG播种,您应该增加此值(这取决于您的CSPRNG).

std::uint_least32_t seed;    
sysrandom(&seed, sizeof(seed));
std::mt19937 gen(seed);

提升比较


在快速查看源代码之后,我们可以看到boost :: random_device(一个真正的CSPRNG)的相似之处.Boost MS_DEF_PROV在Windows上使用,它是提供者类型PROV_RSA_FULL.唯一缺少的就是验证加密上下文,这可以完成CRYPT_VERIFYCONTEXT.在*Nix上,Boost使用/dev/urandom.IE,这个解决方案是便携式的,经过良好测试,易于使用.

Linux专业化


如果您愿意牺牲简洁性来保证安全性,getrandom那么在Linux 3.17及更高版本以及最近的Solaris上是一个很好的选择.getrandom行为完全相同/dev/urandom,除非它在启动后内核尚未初始化其CSPRNG时阻塞.以下代码段检测Linux getrandom是否可用,如果没有,则回退到/dev/urandom.

#if defined(__linux__) || defined(linux) || defined(__linux)
#   // Check the kernel version. `getrandom` is only Linux 3.17 and above.
#   include 
#   if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
#       define HAVE_GETRANDOM
#   endif
#endif

// also requires glibc 2.25 for the libc wrapper
#if defined(HAVE_GETRANDOM)
#   include 
#   include 

size_t sysrandom(void* dst, size_t dstlen)
{
    int bytes = syscall(SYS_getrandom, dst, dstlen, 0);
    if (bytes != dstlen) {
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    return dstlen;
}

#elif defined(_WIN32)

// Windows sysrandom here.

#else

// POSIX sysrandom here.

#endif

OpenBSD系统


最后一个警告:现代OpenBSD没有/dev/urandom.你应该使用getentropy.

#if defined(__OpenBSD__)
#   define HAVE_GETENTROPY
#endif

#if defined(HAVE_GETENTROPY)
#   include 

size_t sysrandom(void* dst, size_t dstlen)
{
    int bytes = getentropy(dst, dstlen);
    if (bytes != dstlen) {
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    return dstlen;
}

#endif

其他想法


如果您需要加密安全的随机字节,您应该用POSIX的无缓冲打开/读取/关闭替换fstream.这是因为两者basic_filebufFILE包含一个内部缓冲器,这将通过标准的分配器被分配(因此不从存储器擦拭).

这可以通过更改sysrandom为:

size_t sysrandom(void* dst, size_t dstlen)
{
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd == -1) {
        throw std::runtime_error("Unable to open /dev/urandom.");
    }
    if (read(fd, dst, dstlen) != dstlen) {
        close(fd);
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    close(fd);
    return dstlen;
}

谢谢


特别感谢Ben Voigt指出FILE使用缓冲读取,因此不应使用.

我还要感谢Peter Cordes的提及getrandom,以及OpenBSD的缺乏/dev/urandom.


这就是我过去所做的,但是,或者至少是一个问题,WTF不能让这些平台的图书馆作者为我们这样做吗?我希望文件访问和线程(例如)可以通过库实现进行抽象,那么为什么不进行随机数生成呢?
我认为`/ dev/random`是播种RNG的更好选择,但显然[`/ dev/urandom`仍被视为计算安全](https://www.2uo.de/myths-about-urandom/ )即使`/ dev/random`会因为低可用熵而阻塞,所以`urandom`是除了一次性填充之外的所有东西的推荐选择.另见https://unix.stackexchange.com/questions/324209/when-to-use-dev-random-vs-dev-urandom.但是,在启动后很早就要注意来自"urandom"的可预测种子.
OP此处:如果此答案证明播种好一点,那就太好了。我希望尽可能多的答案能够生成可复制复制的代码,而不是我在问题中发布的简单示例,而不需要编码器方面的太多技术解释,而是可以做的更好。
Linux的[`getrandom(2)`](http://man7.org/linux/man-pages/man2/getrandom.2.html)系统调用就像打开和读取`/ dev/urandom`,除了它会阻塞如果内核的随机源尚未初始化.我认为这样可以避免早期启动的低质量随机性问题,而不会像`/ dev/random`那样在其他情况下阻塞.

2> einpoklum - ..:

从某种意义上说,这不可能是便携式的.也就是说,人们可以设想一个运行C++的有效的完全确定性平台(例如,一个模拟器,它确定性地处理机器时钟,并且具有"确定的"I/O),其中没有随机性来源PRNG.


我很欣赏这种反应,但也感觉好像一个程序应该做出合理的尽力尝试.
@Richard同意,但问题是C++标准编写者必须(或至少尝试他们最好的)适应这些奇怪的情况.这就是为什么你得到这些类似的标准定义,你可能得到不错的结果,但编译器仍然可以符合标准,即使它回馈了功能上毫无价值的东西. - 因此,您的限制("简短且不能依赖其他库")排除任何响应,因为您实际上需要逐个平台/逐个编译器的特殊套管.(例如Boost做得很好.)
@Richard它解释的是,你得到了你在标准*中得到的东西,因为*没有可行的方法可以做得更好.如果你想做得更好(这是一个崇高的目标),你将不得不接受一些或多或少的憎恶:)

3> ratchet frea..:

您可以使用a std::seed_seq并使用Alexander Huszagh的获取熵的方法将其填充到生成器的至少所需状态大小:

size_t sysrandom(void* dst, size_t dstlen); //from Alexander Huszagh answer above

void foo(){

    std::uint_fast32_t[std::mt19937::state_size] state;
    sysrandom(state, sizeof(state));
    std::seed_seq s(std::begin(state), std::end(state));

    std::mt19937 g;
    g.seed(s);
}

如果有填充或创建一个适当的方式SeedSequence从UniformRandomBitGenerator在使用标准库std::random_device播种正确就会简单得多.


推荐阅读
  • 图像因存在错误而无法显示 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
author-avatar
mobiledu2502891657
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有