(二)随机性与伪随机数发生器

随机性

概率分布

均匀分布

设有N个可能的事件,出现的概率分别为p1,p2...pn,则p1+p2+...+pn=1;若p1=p2=...=pn=1/n,即每个可能的事件发生的概率都相等,则称为均匀分布。

非均匀分布

事件的分布概率不全相等,例如正态分布、泊松分布等。

信息熵

熵是衡量一个系统的不确定性或无序程度的度量指标。熵越大,结果的确定性就越小。

若概率分布为p1,p2,....pn,根据信息论,则信息熵(简称熵)为概率乘其对数再求和:

-p1×log(p1)-p2×log(p2)-p3×log(p3)...-pn×log(pn) = -∑pi×log(pi)

log是以2为底的自然对数,以2为底是为了代表比特信息。

因为log(1/2^n) = -n,为了使得熵为正数,公式中采用了负号。

例如,均匀分布下随机产生32位的比特,共有2^32种可能,每一个的概率为2^-32,熵为:

-∑(2^-32)×log(2^-32) = -2^32×(2^-32×log(2^-32)=32

利用数学归纳法可以得到,n比特的字符串熵为n比特。

均匀分布的熵是最大的,这点可从数学上证明,这就说明n比特的熵<=n。

分布越不均匀,熵越低。

随机数发生器与伪随机数发生器

密码系统需要随机性来保证安全,因此需要一个组件以便从中获取密码的随机性。该组件的任务是在请求时返回随机比特。

生成随机比特需要两个条件:

1.不确定性,或者熵源。它由随机数发生器(RNG)提供。

2.一种从熵源产生高质量随机比特的密码算法。这存在于伪随机数发生器(PRNG)中。

随机性应该是不可预测的。仅基于计算机的算法是不能产生随机性的。随机性通常来自随机数发生器(RNG),它是一个软件或硬件组件,利用模拟世界中的熵在数字系统中产生不可预测的比特。例如,RNG可以直接从温度、噪声、空气湍流或静电测量中采样出比特信息。

还可以通过从附加的传感器、I/O设备、网络或磁盘活动、系统日志、运行进程以及用户活动(如按键和鼠标移动)中提取正在运行的操作系统中的熵。这种系统和人类的活动可以是熵的非常好的来源。但它们可能是脆弱的,容易被攻击者操纵,而且产生随机比特的速度很慢。

由于熵源通常难以估计,一般不直接使用RNG。

量子随机数发生器(QRNG)是一种依赖于量子力学现象(如放射性衰变、真空涨落和观测光子偏振)所产生的随机性的随机数发生器。它们可以提供的是真正的随机性,而不仅仅看起来是随机的。然而,在实践中,QRNG可能有偏差而且不能快速产生比特;所以与先前的熵源一样,它们需要一个额外的组件以高速且可靠地产生比特。

伪随机数发生器(PRNG)的功能是从一些真正随机的比特可靠地生成许多人为的随机比特。例如,如果一个RNG实现的是将鼠标移动转换为随机比特,这时如果停止移动鼠标,RNG将停止工作,而PRNG在被请求时总是返回伪随机比特。

PRNG依赖于RNG,两者区别是:

RNG以非确定的方式从模拟源相对缓慢地产生真随机比特,并且不保证高熵

PRNG以确定的方式从数字源快速生成看起来随机的比特,并具有最大熵

PRNG的原理

PRNG以规则的间隔从RNG接收随机比特,并使用它们来更新大内存缓冲区中的内容,这个大内存缓冲区的内容称为熵池。熵池是PRNG的熵源。当PRNG更新熵池时,它将熵池的比特混合在一起以便消除任何统计偏差。

为了生成伪随机比特,PRNG运行确定性随机比特生成(DRBG)算法。该算法实现的功能是将来自熵池的一些比特扩展为更长的序列。DRBG是确定性的,即对于一个固定的输入,输出始终是相同的。因此,PRNG必须确保它的DRBG不接收两次相同的输入,这样才能生成唯一的伪随机序列

RNG需要执行三个操作:

init():初始化操作,即初始化PRNG的熵池和内部状态。

refresh(R):更新操作,即使用数据R更新熵池,其中R通常来自RNG。

next(N):下一次操作,即返回N个伪随机比特并更新熵池。

init操作将PRNG重置为一个新的状态,同时将熵池重新初始化为某个默认值,并初始化RNG用于执行refresh和next操作的任何变量或内存缓冲区。

refresh操作通常称为重新播种,而R的值就是种子。当没有可用的RNG时,种子可能是系统中硬编码的唯一值。refresh操作通常由操作系统调用,而next操作通常由应用程序来调用或发出请求。next操作运行DRBG并修改熵池以确保下一个调用将产生不同的伪随机比特。

 安全问题

PRNG应该实现抗回溯和抗预测。

抗回溯(也称为前向保密)意味着之前生成的比特不可能恢复。

抗预测(也称为后向保密)意味着未来比特不可能预测。

为了实现抗回溯,PRNG应确保通过refresh操作和next操作更新状态时所执行的变换是不可逆的。这样的话,假如攻击者攻击了系统并获得熵池的值,也无法确定先前熵池的值或先前生成的比特。

为了实现抗预测,PRNG应该定期使用攻击者不知道且难以猜测的R值来调用refresh操作,从而保证即使攻击者攻击了整个熵池,也不能确定熵池的未来值。(即使能够获得所有使用过的R值,也还必须知道这些R值是第几次refresh操作和next操作得到的,才能重建熵池。)

PRNG Fortuna原理

Fortuna是windows使用的PRNG。

Fortuna包括以下内容:

32个熵池,p1,p2,…,p32,其中每个 pi 被使用 2^i 次。

密钥K和计数器C(均为16字节)。这些构成了Fortuna的DRBG的内部状态。

工作原理:

init()将K和C分别置零并清空32个熵池 。

refresh(R)将数据R置于其中一个熵池。系统选择用于产生R值的RNG,并定期调用refresh操作。

next(N)使用来自一个或多个熵池的数据更新密钥K,其中熵池的选择主要取决于已经完成了多少次K的更新。然后,把K作为密钥来加密C以产生所请求的N个比特。如果加密C的长度不够,Fortuna会加密C+1,然后是C+2,依此类推,直到获得足够长度的比特。

如果两个Fortuna实例由于共享一个种子文件(用于初始化的一些值)而处于相同的状态(这意味着它们在熵池中共享相同的数据,包括相同的C和K),那么next操作将在两个实例中返回相同的比特值。

为了防止PRNG生成同样的伪随机比特,种子文件在用于确保它们不被重复使用后应该被删除。

 加密与非加密PRNG

非加密PRNG旨在为诸如科学模拟、视频游戏等应用程序生成良好、均匀的分布。

不能在密码应用程序中使用非加密PRNG,因为它们是不安全的。

加密PRNG用于密码应用程序,加密PRNG一定要是不可预测的,同时必须能生成具有良好分布的比特序列。

 非加密PRNG--——Mersenne Twister(MT)算法分析

内部是624个32bit组成的数组,初始值为S1,S2,...,S624;

运行一次变成S2,S3,...,S625,下一次变成S3,S4,...,S626,方程为

S(n+624)=S(n+397)⊕ A((S(n) ∧ 0x80000000)∨ (S(n+1)& 0xfffffff))

A是输入为32bit的函数,当输入的32比特字x的最高比特位为0时,A输出x>>1;当输入x的最高比特位为1时,A输出(x>>1)⊕0x9908b0df。

现在有S(924)=S(697)^ A((S(300) & 0x80000000)| (S(301)& 0xfffffff))

而S(697)=S(470)^ A((S(73) & 0x80000000)| (S(74)& 0xfffffff)),

以此类推,则可以看出S(n,n>624)的值都可以被S(k,1<=k<=624)所表示,也就是可以预测的。

线性与非线性

我们将异或组合称为线性组合。

例如,如果X、Y和Z都是基于比特的,则表达式X⊕Y⊕Z就是线性组合,而表达式(X∧Y)⊕Z因为里面有与运算,所以不是线性的。如果在表达式X⊕Y⊕Z中改变X的其中1比特值,那么无论Y和Z取值如何,结果都会发生改变。相对应地,如果在表达式(X∧Y)⊕Z中改变X的其中1比特值,那么只有当同一位置的Y的比特值为1时,结果才会改变。这说明,线性组合的结果是可预测的。

如果一个算法在密码学上强度很高,那么其方程一定是非线性的

线性变换会产生很容易求解的短方程(该方程的长短与变量数量的多少相当),而非线性变换则会产生指数规模的方程,这几乎是不可解的。

已实现的PRNG

Unix系统中的PRNG

设备文件/dev/urandom是通用Unix系统中的加密PRNG的用户接口,通常用于生成可靠的随机比特。

因为它是一个设备文件,所以从/dev/urandom中请求随机比特是将随机比特作为文件通过读取来完成的。

Linux系统中的PRNG 

在Linux内核的drivers/char/random.c中定义的Linux PRNG主要使用的是哈希函数SHA-1,其将原始熵比特转换为可靠的伪随机比特。

PRNG获取熵源的途径很多,包括键盘、鼠标、磁盘和中断时序等,并具有512字节的主熵池,以及用于/dev/urandom的非阻塞熵池和用于/dev/random的阻塞熵池。

 /dev/random和/dev/urandom的区别

/dev/random试图估计熵的量级,如果熵太低,则拒绝返回比特。

这可能听起来不错,但事实并非如此。首先,熵估计是出了名的不可靠,并且可能被攻击者作假。此外,/dev/random很快就会耗尽估计的熵,这会导致拒绝服务的情况发生,从而降低了被迫等待更多熵的应用程序的速度。

在实践中,/dev/random并不比/dev/urandom好,并且产生的问题比它解决的问题还要多。

估计/dev/random的熵 

可以通过在Linux上读取/proc/sys/kernel/random/entropy/availy中的当前值来观察/dev/random的熵估计是如何演变的。

下面的shell脚本首先通过从/dev/random中读取4KB来最小化熵估计,等待达到4096比特的估计值后,就从/dev/random中读取64比特,然后显示新的估计值(读取的字节将打印到以base64编码的stdout)。

运行时,可以点击鼠标、键盘查看熵是如何变化的。

#!/bin/bash
ESTIMATE=/proc/sys/kernel/random/entropy_avail
timeout 3s dd if=/dev/random bs=4k count=1 2> /dev/null | base64
ent=`cat $ESTIMATE`

while [ $ent -lt 4096 ]
do
    sleep 3
    ent=`cat $ESTIMATE`
    echo $ent
done

dd if=/dev/random bs=8 count=1 2> /dev/null | base64
cat $ESTIMATE   

windows系统中的PRNG

CryptGenRandom()函数

在Windows中,系统PRNG的旧用户界面是Cryptography应用程序编程接口(API)中的CryptGenRandom()函数。

CryptGenRandom()函数已在最近的Windows版本中被替换为Cryptography API:

Next Generation(CNG)API中的BCryptGenRandom()函数。

Windows中的PRNG从内核模式驱动程序cng.sys(以前名为ksecdd.sys)中获取熵,其熵收集器基于Fortuna。

基于硬件的PRNG——英特尔微处理器中的RDRAND

英特尔数字随机数发生器是2012年在英特尔的Ivy Bridge微体系结构中引入的硬件PRNG,它基于NIST的SP 800-90指南,在CTR_DRBG模式下使用AES。

英特尔的PRNG可通过RDRAND汇编指令访问,该指令提供独立于操作系统的接口,原则上比软件PRNG更快。

软件PRNG试图从不可预测的来源收集熵,而RDRAND有单一的熵源,这个熵源可以提供0、1比特的熵数据序列。在硬件工程术语中,这个熵源是一个带反馈的对偶差分锁存器;本质上,这是一种小型硬件电路,它在800MHz的频率下,根据热噪声波动在两种状态(0或1)之间跳跃。这往往是相当可靠的。

RDRAND汇编指令对16位、32位或64位的寄存器写入随机值。调用时,如果目标寄存器中的数据集是有效的随机值,则RDRAND将进位(CF)标志设置为1,否则设置为0。这意味着如果直接编写汇编代码,应确保检查CF标志。请注意,常见编译器中可用的C内部函数不会检查CF标志,但会返回其值。

英特尔的PRNG框架还提供了除RDRAND之外的汇编指令:RDSEED汇编指令在经过一些调整或加密处理后直接从熵源返回随机比特,它旨在能够为其他PRNG提供种子。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值