首页
社区
课程
招聘
[求助]~~~~~~怎样编写一个随机算法~~~~~~~~~~~~~~~
发表于: 2005-4-10 19:18 7652

[求助]~~~~~~怎样编写一个随机算法~~~~~~~~~~~~~~~

lee 活跃值
3
2005-4-10 19:18
7652
怎样编写一个随机算法啊。。

我一点思路都没有啊。。

就是根据一个CString对象,随机创建一个密码(每次创建的都不一样啊),并且创建出来的也要是CString对象

还不能有\ ,等等之类的特殊符号啊。。。谢谢

还有一个问题。。为什么我在我的程序里加了反调试技术(主要是检测工具)后就为什么异常啊。。。。。。。。。。。。昏。。。。

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (12)
雪    币: 339
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
2
随机数可以采用API函数:rand, 一次不够随机可以多调用几次。
2005-4-10 19:42
0
雪    币: 603
活跃值: (617)
能力值: ( LV12,RANK:660 )
在线值:
发帖
回帖
粉丝
3
记得用时间做种 srand
2005-4-10 20:17
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
基本上在c里面
srand(time(NULL));
rand();
就可以产生一个随机数
单独用rand()产生的是伪随机数
2005-4-10 21:20
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
5
最初由 dlk0222 发布
基本上在c里面
srand(time(NULL));
rand();
就可以产生一个随机数
单独用rand()产生的是伪随机数


头文件:
#include <stdlib.h>
#include <time.h>
2005-4-10 23:06
0
雪    币: 255
活跃值: (175)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
6
谢谢!!!!

还有一个问题。。为什么我在我的程序里加了反调试技术(主要是检测工具)后就为什么异常啊。。。。。。。。。。。。昏。。。。
2005-4-11 11:40
0
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
7
用guid做种子略好一点
2005-4-11 11:46
0
雪    币: 266
活跃值: (191)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
我想这个对你有用:
http://www.agner.org/random/
2005-4-11 12:30
0
雪    币: 47147
活跃值: (20465)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
9
看看论坛精华6中的《 Billy Belceb 病毒编写教程for Win32 ----Win32多态》,多态引擎中最重要的部分是随机数发生器,上面有些介绍。
你也可以用 多态 为关键字搜索相关资料,可能会找到相关资料。
2005-4-11 15:05
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
随机数最好用自己的算法一般是:网卡id+TickCount+随机数
不过一般开发语言提供的随机数算法生成的随机数在16或者18毫秒内是一样的,说明是跟时间相关的。用guid是最好的了,不过转换成可打印字符后比较长,
用数据库的序列值或者自己用一个整形变量+1生成,锁定对它的写操作。
2005-4-11 17:27
0
雪    币: 209
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
很久以前就听说Intel要提供以CPU温度为基础的随机数,不过现在还没看到。
随机函数是门大学问哦。
还记得ASProtect吗?他就是用鼠标事件时间作随机数。
2005-4-11 22:31
0
雪    币: 209
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
Code Red 的作者竟然也会犯随机数的错误,
他使得 Code Red 生成的随机攻击 IP 地址, 实际上是一个相同的序列.
所以, 开始的半个月, 被感染的机器实际上在重复攻击相同的 IP, 因而影响并不大.

转贴:
真正安全的软件需要精确的随机数生成器
Gary McGraw 和 John Viega
Reliable Software Technologies
2000 年 4 月 4 日

计算机一直是具有完全确定性的机器,所以,特别在行为随机性方面表现不尽人意(软件缺陷情况除外)。所以当程序员需要一个或一组真正的随机数时,他们必须通过各种方式近似地生成随机数。在本专题,关于这个主题的三篇文章的第一篇中,Gary McGraw 和 John Viega 分析了随机数生成器是如何工作的,并展示了各种作为结果可以实现的技巧。在本系列的下一部分,Gary 和 John 将讨论如何通过硬件来真正地生成随机数。
在本文中,我们来讨论最近在新闻中一直议论的话题:随机数的生成。嗯,这样说还不精确。事实上,是对新闻中提到的有关随机数生成问题的揭秘,而对于产生问题的原因不深究。您也许已经看到了有关 Reliable Software Technology 的因特网赌博揭秘的 CNN 新闻(请参阅参考资料),或者您也许想起几年前关于 Netscape 的 SSL 实现被泄露这一事件。即使这两个应用听起来差别很大,其实每个问题的根本原因是一样的。

正当许多开发人员认为 random() 函数已在他们喜欢使用的语言中正确命名时,问题就发生了。但遗憾的是,这是一个有缺陷的假定。通常,调用 random() 实际上被认为是调用“伪随机”数生成器,当然,这些随机数不是真正随机的。这个事实对软件的安全性有着深远的影响。这里提供了两个示例来演示为什么会这样。

随机性不是您所想象的那样呆板:一些数字流要比其它一些更随机。计算机一直是具有完全确定性的机器,所以,特别在随机的行为方面表现不尽人意(这里撇开那些不怎么样的 OS 软件不谈)。事实上,(部分)随机数真正的唯一来源涉及到测量物理现象,譬如,放射性衰变的计时,它可以用某些数学方面的技巧来提取纯的随机序列。

无须利用实际的设备,计算机程序需要随机数时,会自己来生成这些数字。但这种计算机的决定机制使得生成随机数的算法很困难。正是由于这样,而使大多数程序员转向伪随机数。在本文中,将看一些伪随机数生成器 (PRNG)。分析它们是如何工作的,并显示它们为什么对于安全性不是很好。在下一个专题,来看一些针对随机性的基于硬件的解决方案。然后,来看一下介于中间的软件解决方案,这些方案提供的安全性要比伪随机数生成器更安全,而比更为安全的基于硬件解决方案要更切实可行。

伪随机数是如何工作的?
我们建立了真正调用伪随机数生成器的 random()。但什么是伪随机数生成器?假定需要生成介于 1 和 10 之间的随机数,每一个数出现的几率都是一样的。理想情况下,应生成 0 到 1 之间的一个值,不考虑以前值,这个范围中的每一个值出现的几率都是一样的,然后再将该值乘以 10。请注意,在 0 和 1 之间有无穷多个值,而计算机不能提供这样的精度。

为了编写代码来实现类似于前面提到的算法,常见情况下,伪随机数生成器生成 0 到 N 之间的一个整数,返回的整数再除以 N。得出的数字总是处于 0 和 1 之间。对生成器随后的调用采用第一次运行产生的整数,并将它传给一个函数,以生成 0 到 N 之间的一个新整数,然后再将新整数除以 N 返回。这意味着,由任何伪随机数生成器返回的数目会受到 0 到 N 之间整数数目的限制。

在大多数的常见随机数发生器中,N 是 232?1 (大约等于 40 亿),对于 32 位数字来说,这是最大的值。换句话说,我们经常碰到的这类生成器能够至多生成 40 亿个可能值。而这 40 亿个数根本不算大,只是指尖这么大。

伪随机数生成器将作为“种子”的数当作初始整数传给函数。这粒种子会使这个球(生成伪随机数)一直滚下去。伪随机数生成器的结果仅仅是不可预测。由伪随机数生成器返回的每一个值完全由它返回的前一个值所决定(最终,该种子决定了一切)。如果知道用于计算任何一个值的那个整数,那么就可以算出从这个生成器返回的下一个值。

结果,伪随机数生成器是一个生成完全可预料的数列(称为流)的确定性程序。一个编写得很好的的 PRNG 可以创建一个序列,而这个序列的属性与许多真正随机数的序列的属性是一样的。例如:

PRNG 可以以相同几率在一个范围内生成任何数字。
PRNG 可以生成带任何统计分布的流。
由 PRNG 生成的数字流不具备可辨别的模式。
PRNG 所不能做的是不可预测。如果知道种子和算法,就可以很容易地推算出这个序列。

一方面,伪随机数生成器有许多有用的应用方面用途。它们对于蒙特卡罗仿真(一组使用随机数来仿真物理事件或解决数学问题的方法)和其它统计采样或仿真模型很适用。这些应用通常只需要它们有中度复杂程度的“随机性”,并且序列中的模式不与自然发生的序列相混即可。

另一方面,在那种需要不可预料性(如洗虚拟牌或加密数据)的应用中,伪随机数生成器很难以一种安全的方式来使用。

示例:
下面再现了用 Borland 编译器分配的伪随机数生成器。这个生成器是属于线形拟合生成器一类的。这类生成器相当普遍,它们采用很具体的数学公式:

Xn+1 = (aXn + b) mod c

换句话说,第 n+1 个数等于第 n 个数乘以某个常数 a,再加上常数 b。如果结果大于或等于某个常数 c,那么通过除以 c,并取它的余数来将这个值限制在一定范围内。请注意,a、b 和 c 通常是质数。Donald Knuth 在 "The Art of Computer Programming" (请参阅参考资料)一书中详细介绍了对于这些常数,如何挑选好的值。

回到 Borland 生成器。如果知道 RandSeed 的当前值是 12345,那么下一个生成的整数是 1655067934。只要将种子设为 12345,那么每次得到的结果都是一样的。

Random () 的 Borland 的实现 long long RandSeed = #### ;

unsigned long Random(long max)
{
long long x ;
double i ;
unsigned long final ;
x = 0xffffffff;
x += 1 ;

RandSeed *= ((long long)134775813);
RandSeed += 1 ;
RandSeed = RandSeed % x ;
i = ((double)RandSeed) / (double)0xffffffff ;
final = (long) (max * i) ;

return (unsigned long)final;
}

修补破坏的种子
基于历史先例,PRNG 的种子通常是参照系统时钟生成的。这个想法是使用系统时间的某一点来作为种子。这意味着如果能算出生成器什么时间发生,那么就可以知道由生成器生成的每一个值(包括数字出现的次序)。这样的结果说明,用时钟播种的伪随机数,所有结果不是不可预测的。正如我们所见,这个事实对于洗牌算法和密钥有着深远的影响。

您可能认为,对于 PRNG,如果取一个真正的随机数作为种子开始,那么可能会是安全的。毕竟,如果我们的确定性流是从随机点开始的,那么一切都没事了,对吗?错!即使攻击者不知道生成器采用哪个特殊数字作为种子,他仍然可以通过观察您的程序和做一些猜测来预测出 PRNG 生成的数字。这个问题是攻击者只需要看从生成器中产生的一个数字来预测下一个(以及所有其它随后的数字)。如果您选择 0 和 50 之间的数字,对于攻击者来说这个工作要困难些,但它仍然不是件难事。

下面说明原因。在标准的随机数“轮盘”(带有许多随机数生成算法,包括线形拟合生成器;在 0 和 4,294,967,295 之间的所有数字确切地生成一次,然后重复该序列)上只有 40 亿个可能的位置。如果我们观察足够多的数字,即使调整在 0 和 50 之间的数字,最终都可以算出生成器是如何播种的。我们可以通过一定的顺序尝试所有 40 亿个可能的种子,并在 PRNG 流中查找与您程序中展示的一致的子序列。现在,40 亿不是一个大数目。如果有几台高性能的 PC 机,假定有足够大的空间来“蛮力”执行,几乎可以实时地完成这项任务。

这里,我们的问题之一是大多数机器目前使用的是 32 位的 PRNG。幸运的是,我们可以解决这个问题。使用真正的随机数来作为 64 位 PRNG 的种子,会使得这较难被攻破(提醒您,这不是不可能,即对于好的 PRNG),因为,即使有极高运算能力的计算机,破解 64 位可能也需要花费数月。如果使用好的 128 位的 PRNG 和真正的随机种子,那么这恐怕是足够好的(虽然并不总是,但也确是;我们过一会儿讨论为什么)。种子空间的改进与密钥改进的原因是一模一样的:使用的位数越多,这两个密钥就越难破解(只要正确使用所有东西)。

让我们来研究一下密码密钥/种子之间的相似性。蛮力的密码术攻击为了破解一个秘密消息,需要按某种顺序尝试每一个可能的密钥。同样地,蛮力攻击 PRNG,需要尝试每一个可能的种子。国际上有一些重要的研究组织正在研究必需的密钥长度。一般来讲,类似于以下情况(1999 年 10 月):

算法 弱密钥 典型密钥 强密钥
DES 40 或 56 56 Triple-DES
RC4 60 80 128
RSA 512 768 或 1024 2048
ECC 125 170 230

过去人们常常认为实时破解 56 位 DES,由于花费太长时间而觉得它是不可行的,但历史却证明是另外一回事。1997 年 1 月,在 96 天内恢复 DES 密钥。后来这个时间缩短到 41 天,然后是 56 小时,在 1999 年 1 月,是 22 小时 15 分钟。对于长度较短的密钥或小集合的 PRNG 种子,这种跳跃前进的破解能力不是一个好的兆头。

人们甚至发明了特殊的机器来破解密码算法。在 1998 年,EFF 发明了用于破解 DES 消息的专用机器。这台机器的目的只是强调 DES(一个流行的、政府支持的密码算法)是如何易受攻击的。(如果想要了解有关 DES 破解器的更多情况,请参阅参考资料。)“破解” DES 的难易程度直接与密钥的长度相关。所以,发明专门用于恢复 RNG 种子的机器也是在可能的范围之内。

128 位的种子能解决所有问题吗?它提供的足够大的搜索空间排除了蛮力攻击的可能。但是,生成器可能会受到其它类型的攻击。实际上,它取决于正在使用的、生成随机数的算法。线形拟合生成器(事实上,所有多重拟合生成器)被证实为易于受到相对较小的信息的攻击,因此要不惜一切代价避免。从现在开始的两篇文章将论述基于密码原语的 PRNG 算法,这种算法似乎比传统的生成器要好些。

尝试不要使它可预测
我们已经注意到了传统 PRNG 的一个重要问题,但还没有进入实质问题!这里不想误导您认为 64 位的 PRNG (或更好)和好的算法一起使用就会有任何的安全性。即使 128 位的 PRNG 也是完全可以预测的!如果想要安全性,需要为数字生成器播种一个真正的随机数。您不能凭主观意愿将一个值编制硬代码到程序中。攻击者很有可能注意到每次运行该代码时生成相同的数字序列。同样地,您不能请其他人手工地输入数字,因为人为的输入不是很好的随机性来源。

当调用随机数生成器时,其它用于种子的常见来源包括网络地址、主机名、人名和程序员的娘家姓氏。这种数据也常用作密钥。请注意,这些数据不会经常更改,或根本不会更改。如果攻击者可以算出您是如何挑选数据的(通常,这是一个安全的赌注),那么这个数据是攻击者穷追直至获取的必不可少的信息。而且,有时这类信息和时钟值一样容易猜测,甚至更容易些。

每个伪随机数生成器算法都有其可比较的优点和缺点,同时也有许多可供选择的算法。没有一个算法能适合所有的任务。事实上,对于上面所描述的原因,没有一个基于易于理解的算法的伪随机数生成器算法是(靠它自己)完全适合于安全性的。

如果有一个视伪随机数是必不可少的应用,那么这里有几个可以进一步了解不同算法特性的有价值的资料。较著名的参考资料是 Donald Knuth 的 "The Art of Computer Programming,Volume 2 (Seminumerical Algorithms)" 的第 3 章(请参阅参考资料)。除此以外,还有在 Salzburg 大学数学系的 pLab 中的研究人员,他们花费了所有时间从理论上和实践上来研究随机数的生成。他们的网页(请参阅参考资料)提供了许多有价值的线索来帮助您选择适合于您的应用的生成器。

不正确地使用伪随机数生成器会导致惊人的安全性问题。希望您不会发生这类问题。

如何在在线赌博中欺骗
Reliable Software Technologies (RST)(请参阅参考资料)的软件安全性组最近在 Texas Hold 'em Poker(由 ASF 软件公司发行的)(请参阅参考资料)实现中发现了一系列缺陷。这个揭秘允许欺骗性的玩家可以实时计算每人手中确切的牌。这意味着,使用这个揭秘的玩家可以知道每个对家手中的牌和将要发出的牌(指在一圈下赌后,将它面朝上放置的牌)。欺骗者每次可以“知道什么时候持有它们以及什么时候牌面朝下退出”。一个臭名昭著的攻击者可能使用这个揭秘,在未被发现的情况下来诈骗不知情玩家的钱财。

这个缺陷存在于用来生成每副牌的洗牌算法中。具有讽刺意味的是,这个代码曾公布在 http://www.planetpoker.com/ppfaq.htm 上,为的是显示这个游戏是很公平的,来吸引玩家(这个页面从公布了它的缺陷后已经被拿掉了)。在这段代码中,调用 randomize() 来在每副牌生成前生成一副随机牌。这个实现是用 Delphi 4 (一种 Pascal IDE)构建的,随机数生成器的种子是按照系统时钟,用午夜后的毫秒数选取的。这意味着随机数生成器的输出是容易预测的。正如我们所讨论的,可预测的随机数生成器是一个很严重的安全性问题。

ASF 软件中使用的洗牌算法总是以一副有序的牌开始,然后生成用来记录纸牌的随机数序列。在实际的纸牌中,有 52!(大约是 2226)种可能的、不同的洗牌顺序。回想前面所讲,用于 32 位的随机数生成器的种子必须是 32 位数字,这意味者只有刚刚超过 40 亿个的可能的种子。由于在每次洗牌前,纸牌要重新初始化并重新生成生成器的种子,而这个算法只有 40 亿个可能的洗牌方法。40 亿可能的洗牌方法远远少于 52!。

使事情更糟糕的是,这个有缺陷的算法使用 Pascal 函数 Randomize() 来挑选用于随机数生成器的种子。这个特定的 Randomize() 函数基于午夜后的毫秒数挑选种子。一天之内只有 86,400,000 个毫秒。由于这个数字被用作用于随机数生成器的种子,所以现在可能的洗牌方法数量减少到 86,400,000。86,400,000 远远少于 40 亿。但这还不是最糟的。还有更糟糕的。

系统时钟种子甚至进一步使 Reliable Software Technologies 减少可能的洗牌方法数。通过将他们的程序与生成伪随机数的服务器上的系统时钟同步,使得可能组合的数量减少到大约 200,000 种可能性。在这之后,系统就变得不堪一击,由于在这个微小集合的洗牌方法中进行搜索是不费吹灰之力的,这可以在 PC 机上实时地做到。

发现这个薄弱环节的 RST 开发的工具需要知道纸牌中的五张牌。基于这五张牌,程序从几十万个可能的洗牌方法中搜索并推出最优匹配。在 Texas Hold 'em Poker 情形中,这意味着程序将欺骗性玩家手中的两张牌作为输入,再加上三个玩家的第一张翻开的牌(发牌)。这五张牌在第一圈下赌注后是已知的,这足够确定(实时,在玩牌期间)确切的洗牌方法。这幅图显示了 RST 揭开这个赌博事实的图形界面。左上角的 Site Parameters 框是用来同步时钟的。右上角的 Game Parameters 框是用来输入这五张牌和启动搜索的。这副图是程序已经确定所有牌之后捕捉的屏幕。欺骗性的攻击者提前知道谁手中拿有什么牌,余下的发牌会象什么样,以及谁将会胜利。

Reliable Software Technology 的因特网扑克牌揭秘的界面

一旦程序知道了这五张牌,它会生成洗牌顺序,直到发现包含这五张已知牌正确次序的洗牌顺序。由于 Randomize() 函数是基于服务器的系统时间,所以,合理地、并有一定程度精确性地猜出启始种子不是很困难的。(您越接近,所经历的可能洗牌顺序也越少。)虽然这是一个令人吃惊的结果:一旦发现了正确的种子之后,有可能在几秒之内将这个揭秘计划与服务器程序同步。这个公布出来的、实际的同步允许程序决定随机数生成器所使用的种子,并在一秒之内识别出所有以后游戏中使用的洗牌顺序!

撇开技术细节,该揭秘收到了令人惊叹的新闻效应。这个新闻效应强调了在此类发现中人为的一面。请访问 RST 网站(已在参考资料中列出)来了解原始的新闻发布、CNN 录像片段和纽约时报的新闻。

如何读“秘密的”Netscape 消息
在另一个独立的但也同等重要的开发中,Ian Goldberg 和 David Wagner 从 1996 年 1 月开始的研究,演示了随机数对于密码安全性是如何的重要(请参阅参考资料)。他们研究出来的这份揭秘显示了 Netscape 早期的 SSL 实现之一存在着严重的缺陷,这使得有可能解密编码的通信数据。当然,这个缺陷已经成为历史,但带来的教训却足以让我们不能再重蹈覆辙。

SSL 用一个密钥来加密消息(象大多数密码算法一样)。这个想法是创建一个密钥,这个密钥是只有消息发送方和接受方知道的大的随机数。正如洗牌这个情形一样,如果密钥是可预测的,那么整个系统会象一幢纸牌做的房子一样坍塌掉。很简单,密钥必须要从不可预测的来源得到。(听起来很类似?)

Netscape 的问题是它们选择了一种不好的方法来给伪随机数生成器播种。它们的种子完全可由一天的时间、进程标识和父进程标识来决定。这不是很好。

使用与被攻击的浏览器相同的机器(如在多用户机器上)的攻击者可以通过使用 ps 命令很容易发现进程标识(UNIX 环境下)。Goldberg 和 Wagner 还详细说明了攻击可以在不需要知道进程标识情况下(基于通常每个进程只有 15 位的事实)实现。剩下的工作就是猜测一天的时间。嗅探器可以用来偷取通过包的精确时间,利用这就能够猜测运行 Netscape 系统上的的时间。在一秒之内就可以获得,这相当的容易,并且也可以在毫秒的级别范围内实时地破解密码(正如扑克揭秘所演示的)。

Goldberg 和 Wagner 使用这种攻击方式成功地攻击了 Netscape 版本 1.1。40 位的国际版本和 128 位的国内版都可被攻破。给我们带来的教训很简单:如果需要生成密码术中使用的随机数,要小心谨慎。

在本文的下两个专题中,将要提供关于在硬件方面和软件方面如何正确地处理随机数生成的一些指示(以及大量代码)。如果您想提前了解这些,那么很好,可以看 "Randomness Recommendations for Security" (RFC 1750) 这本资料,但它有点枯燥。另外一本值得看的资料是 Bruce Schneier 的优秀作品 "Applied Cryptography"(请参阅参考资料)。

参考资料

请参阅 developerWorks 上有关随机数生成系列的第 2 篇文章使您的软件运转起来:打破偏见。
请参阅 Donald Knuth 的 The Art of Computer Programming, 第一卷,第 3 版(Addison Wesley Longman 公司,1997 年 6 月,ISBN 0201896834),以及 The Art of Computer Programming, (Seminumerical Algorithms) 第 2 卷,第 3 版 (Addison Wesley Longman 公司,1997 年 10 月,ISBN 0201896842)。
请访问 RST,阅读有关他们的 1999 在线扑克揭秘和他们的软件安全性组。
请阅读有关由 EFF 创建的 DES 破解器。
请访问在 Salzburg 大学数学系的 pLab。
请阅读由 ASF 软件发布的 Texas Hold'em 扑克。
请阅读 Ian Goldberg 和 David Wagner 在 1996 年的 Netscape 揭秘。
请阅读由 Bruce Schneier 著作的“已应用的密码术”。
请访问 RSA 的有关用于密钥的随机数的常见问题解答。
关于作者
Gary McGraw 是 Reliable Software Technologies 负责企业技术的副总裁,该公司位于美国弗吉尼亚州杜勒斯 (Dulles)。他从事咨询服务和研究工作,帮助决定技术研究和开发方向。McGraw 在 Reliable Software Technologies 从一个研究科学家作起,从事软件工程和计算机安全性方面的研究。他拥有印第安那大学认知科学和计算机科学双博士学位,弗吉尼亚大学的哲学学士学位。他为技术刊物撰写了 40 余篇经同行检测的文章,担任过主要的电子贸易供应商,包括 Visa 和 Federal Reserve 的顾问职务,并在空军研究实验室、DARPA、国家科学基金会,以及 NIST 的高级项目赞助下担任其首席调研员。

McGraw 是移动代码安全性方面著名的权威人士,并且与普林斯顿的教授 Ed Felten 合作撰写了 "Java Security: Hostile Applets, Holes, & Antidotes" (Wiley, 1996) 以及 "Securing Java: Getting down to business with mobile code" (Wiley, 1999)。McGraw 和 RST 创始人之一、首席科学家 Dr. Jeffrey Voas 一起编写了 “Software Fault Injection: Inoculating Programs Against Errors”(Wiley, 1998)。McGraw 定期为一些受欢迎的商业出版物撰稿,而且其文章经常在全国出版的文章中所引用。

John Viega 是一个高级副研究员,Software Security Group 的创始人之一,并担任 Reliable Software Technologies 的高级顾问。他是 DARPA 赞助的开发标准编程语言安全性扩展的首席调研员。John 已撰写了 30 余篇涉及软件安全性和测试领域的技术性文章。他负责在主要网络和电子贸易产品中查找一些众所周知的安全性薄弱环节,包括最近在 Netscape 安全性中的缺陷。他还是开放源码软件社区的重要成员,编写过 Mailman、 GNU Mailing List Manager 以及最近发布的 ITS4(一种在 C 和 C++ 代码中查找安全性薄弱环节的工具)。Viega 拥有弗吉尼亚大学计算机科学的硕士学位。

使您的软件运行起来:消除偏差

如何通过硬件真正实现随机数生成
Gary McGraw 和 John Viega
Reliable Software Technologies
2000 年 4 月 11 日

内容:

它们如何工作?
消除偏差
硬件源
它们有多好?
结束语
参考资料
关于作者

在本系列的第一部分中,Gary McGraw 和 John Viega 讨论了精确随机数生成。在这一部分,即本系列的第二部分中,Gary 和 John 研究了随机数的硬件源。这些源有时可以提供比全软件解决方案更高的安全性保证(虽然我们也将处理基于硬件的随机数发生器中的缺点)。

从本专栏的上一部分中,我们知道了随机数通常会产生很重要的安全性后果。问题是确定性的机器很难实现随机。最佳答案就是使用好的物理度量来生成随机数(或至少是伪随机数发生器的种子)。许多自然现象就具备这种条件。其诀窍就是它们必须有一些可测量的特性,而且行为至少要尽可能随机(如果不能真正做到随机)。(这种随机性是在量子力学级别上提出的。还不知道随机性真正是量子力学的固有部分,还是我们的科学理论还不够好,不能预测我们应该能够预测的结果。)收集随机性的真正硬件设备是基于测量诸如原子的放射性衰变或热辐射中的波动(某些 Pentium III 处理器就测量后者)之类的事情。

在这一部分中,我们将研究随机数的硬件源。这些源有时可以提供比全软件解决方案更高的保证(虽然也有许多糟糕的硬件随机数发生器)。我们确实意识到基于硬件的解决方案并不总是可行,只要这句话就够了。例如,您也许希望将应用程序分发给全球数以万计的用户,而且还需要每部台式机上有好的随机数。直到世界上每台计算机都在硬件中安装了随机数发生器为止,总是需要一种尽可能接近真正随机性的软件。

在我们深入研究之前,我们需要考虑是否可能满足这种随机性需要。我们在上一部分中讨论了传统的伪随机数发生器,而且讨论了将它们用于注重安全性的用途上有多可怕。PRNG 算法也无法满足需要。硬件(在下面讨论)是一个好的选择,但是却很昂贵且不易使用。

幸好,有一个折中方法。使用软件执行合理的随机性作业,而不必求助于基于硬件的解决方案,这点是可能的。当然,您需要牢记这一点:硬件始终可以比软件做得更好,即使不是永远。我们将在下一部分中讨论替代软件解决方案。

真正的随机数发生器如何工作?
真正的随机数发生器是非确定的。那表示,作为旁观者,应该永远无法以任何一致性猜测到设备的输出,即使您知道设备使用的算法。例如,如果设备输出一系列 0 和 1,0 和 1 在任何特定输出中出现的机会应该相等。即使掌握了设备内部工作的全部知识,任何猜中的可能性也只有 50% 左右(对于某些范围的行为)。

在计算机上执行非确定性事情的唯一方法是从一些自然的随机过程中收集数据。我们最喜欢的一种方法涉及使用电子 Geiger 计数器,每次当它检测到放射性衰变时,它就会生成一个脉冲。衰变之间的时间是一个实足的、纯粹的随机部分。尤其是,没有人可以预测到下一次衰变的时间大于还是小于自上次衰变以来的时间。那就产生了一位随机信息。我们可能得到更多。

比如说,假设我们正在观察某些我们期望每秒都发生衰变的材料,但衰变也许会前后相差十分之一秒发生。与其比较两次衰变之间的时间,不如记录衰变的时间,并且使用独立数字位作为独立的十进制随机数。也许百分之一秒是一个数字位,而千分之一秒是另一个数字位。我们可以根据计时器的精确程度来按比例抽取数字。此技术也许够好,也许不够好;在没有尝试使用它并对数据进行一些统计分析的情况下,很难下定论!如果表示百分之一秒的数字几乎总是 0,而表示偶尔发生的数字是 1,将会怎么样?那不是非常随机。如果我们的时钟没有那么强的功能,而且表示千分之一秒的数字始终是均匀的,或者至少很可能是均匀的,将会怎么样?

实际上,这种问题是经常遇到的。所有事都可能出错,从而使数据产生偏差。另一方面,如果我们保守一点,尝试以保守方式从衰变中抽取出数据的单一位,就很少有机会产生测量引起的偏差。不幸的是,如果有一些偏差,很难断定偏差有多大。我们关心的是平均信息量(真正的随机性)中有多少位是真正可用的。我们希望能够以某些精度测量该数字。为使我们的工作更简单,我们将只研究一位。

任何源都有潜在的偏差。我们检查了一种流行的硬件发生器,发现它返回 1 的概率是 49.81%,这表示发生器的偏差非常小,趋向于零。或许基本自然现象的偏差也是这样,但通常我们的测量工具(尚有不足之处)有错误。

消除偏差,混淆相关性
我们如何消除偏差?一种常用方法是同时 XOR 位。如果某一位是 0 的概率是 0.5 + c,那么当我们同时从一个发生器中 XOR 两位时,概率就变成 0.5 + 2c2。如果我们 XOR 更多位,概率就会越来越接近均匀。当然,概率永远不会达到均匀,但每个 XOR 操作都会消除许多偏差;您只需确定愿意接受多少偏差。由于我们使用两位来创建一个偏差,我们就带来了新问题,因为我们现在生成随机位的速度只有以前的一半。

上述消除偏差的技术的一个潜在问题是即使在执行了 XOR 之后,两个连续位(或甚至是两个不相邻的位)之间有相关性,而攻击者也许会利用这一点。实际上,XOR 可以帮助消除任何偏差,但它放大了位之间的相关性。

请考虑我们给出的用于从计时事件中抽取单一位的技术。如果我们使用同样的技术尝试从敲击键盘中获取随机性,那会怎么样?基本思路是记录两次击键期之间的持续时间。如果某段持续时间大于前一段持续时间,则生成 1;否则生成 0。此技术在需要生成密钥的程序中很常见,并且会使那些让他们敲击键盘几分钟也不会抱怨的用户坐在计算机前。例如,某些版本的 PGP 就使用此技术(其它版本则利用鼠标事件执行类似的功能)。

但并非一切都能在意料之中。这里就是可能出错的地方。按键之间的计时并不完全是随机现象。我们已经看到了向用户提供需要反复输入的样本文本的程序。问题是某些键比其它键更易于触到。如果正在输入单词“kiss”,输入“i”和“s”之间的时间几乎总是大于输入两个“s”字符之间的时间。产生一个位的事件肯定与产生下一个位的事件相关,从而导致最终偏差的增加。

我们如何消除这种相关性呢?一个解决方案就是采用多个随机数据源,并且将数据流 XOR 到一起。按惯例,人们使用伪随机流作为数据的第二来源。当然,如果某人可以破坏伪随机流,那么仍可以辨别出相关性(而且可以使用相关性来对付您,就象我们将要演示的)。

怎么回事?谁会关心随机数发生器中是否有一点偏差?在理论上,偏差会使攻击者的工作变得更容易。然而,实际上,小的偏差并不太可能导致系统破坏。例如,Bruce Schneier 留意到即使发生器产生的数字中 55% 都是 0,每位仍然能产生 0.99277 位的平均信息量。那意味着,精确导向地强力搜索 168 位密钥,平均起来,2,109 次尝试中会成功一次,而不是如果不存在偏差时所预期的 2,111 次尝试中成功一次。但是,万一人们可能犯错,大多数人宁愿犯安全性方面的错误。

一种通过消除偏差而解决上述所有问题的常用解决方案是对随机字节流应用某种算法,该算法可以自然地消除任何统计趋势。通常都使用压缩算法和散列函数。

什么是好的硬件源?
偏差的另一个源(特别隐秘的一种)是在外部现象中,即攻击者有一些控制,或者有机会进行复制。如果攻击者对所测量现象的影响程度足够产生重大的偏差,那么必将发生一个攻击高潮,尤其是在一段很长的时期内。例如,如果将网络流量用作测量的现象,那么这个攻击就很可行。攻击者可以偷偷地控制到您机器的网络流量,并且以这种方法产生很大的偏差。应该避免这些容易受到控制或影响的现象。另一种技巧就是从 WAV 文件或声卡提取背景噪声(通常没有连接话筒)。许多时候,这些源都没有很多平均信息量;如果它们受到攻击,那么就会变得毫无价值。同样,实际上偏差可能是不相关的,但应该防患于未然。

也许常用的随机数最佳来源是测量放射性衰变。它根本不易于产生偏差,而是会带来相对较小的自然偏差。另一个好的来源是测量半导体二极管散发的热噪声,然而如果得不到正确防护,附近的硬件可能会使输出产生偏差。

生成随机数的几种硬件设备都是商业用的。也许最广泛使用的设备是 ComScire QNG(请参阅参考资料),它是使用并行端口连接到 PC 的外部设备。人们都认为此设备是经过精心设计的,而且在其输出的统计分析方面做得极其好。它可以在每秒钟生成 20,000 位(2,500 字节),这对于大多数注重安全性的应用程序来说已经足够了。即使发生器产生的数据还不够时,发生器的数据可以预先存储,或者可以使用多个发生器。这个包的另一个好处是:当生成数字时,设备驱动程序会对它们进行统计测试,而且如果开始生成在统计上非随机的数据,它会返回错误。在撰写本文时,该设备价值 295 美元,可以从 ComScire 购得。Robert Davies 撰写的关于此发生器的无偏见评论(请参阅参考资料)声称它“看来是唯一为统计目的而特别设计的发生器,而且制造商花了一定的精力来了解最终产生的数字上的偏差和相关性的作用。”

一种类似设备来自 Tundra 的 RBG 1210(请参阅参考资料)。它们的设备有时会产生小的偏差,然而在别的方面却通过了许多统计测试。明确建议使用转换来消除偏差。此产品过去常常有一些装配问题而通常会导致彻底失效,但相信它们会得到修复。

RBG 1210 是 PC 的内部卡,它有可变采样率。如果采样过多,连续的位就会有很高的相关性,因为内部硬件不会很快更改输出以符合采样率。但是,此设备将会每秒产生最多 160,000 位(20,000 字节)。

另一种很好玩的资源是 lavarand(请参阅参考资料)。这种热闹的方法依赖于一组正在工作的 Lava Lite 灯中固有的混乱。一架数字照相机安置在六盏 lava 灯前,用于每隔一段时间拍摄一张照片。所产生的“噪音”数字图像然后被以密码方式散列到 140 位的种子值中。然后,将这个种子值馈送给出色的伪随机数发生器(我们将在下一部分中讨论)。但是,不要在注重安全性的代码中使用 lavarand!请记住,每个人,包括坏蛋们,可以在 Web 上看到 lavarand 序列。SGI 并不销售 lavarand 安装程序。如果您遵循 lavarand 网页上细致、详细的建议,亲自构建 lavarand 并不困难。

但是,lavarand 并不希望与我们讨论过的其它设备竞争。大多数安装都很昂贵(因为近来 lava 灯的高价),并且发生器每秒只输出 550 位(略少于 70 字节)。这样一个设备对空间要求很高,需要一盏灯和一架照相机。最后,它很容易失败,如坏掉的硬件。要解决后一个问题,SGI 使用六盏灯作为自动计时器,其中三盏始终打开。系统中每样东西都工作得格外好,只是带宽相当低。问题的部分原因是需要拍摄和扫描照片。但是,问题的大部分是因为数据很可能有统计偏差,因此大量精力花在了使用七个密码散列和一个相当慢(但非常好)的伪随机数发生器来消除此类问题上。

当去年 Intel 宣布他们将开始在其芯片组中添加基于热能的硬件随机数发生器,而且基本上不会增加客户的成本时,许多安全性社区都变得很兴奋。当著名的密码人员宣称该发生器是随机数的优秀来源时,人们变得更兴奋了。迄今为止,已经交付了一些带有硬件 RNG 的 CPU。所有带 8xx 芯片组(810 或更高)的 Pentium III 都应该拥有此功能。由于我们熟悉带这种发生器的硬件,当此功能可用时,我们会给您一些代码供您使用。(我们已经听说一个令人不愉快的谣言:RNG 正遭到长期禁止,因为人们对它失去了兴趣。我们希望这最终只是一个谣言。)

还有其它鲜为人知的产品用于在硬件中生成随机数。在 Web 上有几个资源可以向熟悉构建硬件的人们演示如何廉价构建自己的随机数发生器(请参阅参考资料)。

我的随机数有多好?
一旦拥有了随机数的硬件源,您应该问:“我的随机数够随机吗?”答案也许并不总是“是的”,您当然想要知道什么时候发生这种情况。在开始使用他人的发生器之前先进行测试,一点不为过。但是当构建自己的随机数发生器时,认真测试就尤其重要(但是,这样做很容易扰乱我们的建议,我们建议购买一个高质量的发生器,而不是构建一个)。

现在有许多统计测试。它们采用各种形式,但共同思路是它们全都以统计方式检查来自发生器的数据流,尝试发现如果数据真是随机的,是否可以在还没有显示的数据中找到任何模式。ComScire 设备在您使用它时会执行这些测试,如果测试不成功,它会在运行时失败。那是种很好的安全措施,但实际上,人们通常(也许很少)只在第一次使用之前预先检查流,以确保发生器正在开足马力运行。

确保数据流随机性的最广为人知的测试套件就是 George Marsaglia 的 DIEHARD 软件包(请参阅参考资料)。它对数据流执行无数的测试。另一个适合此类测试的合理软件包是 pLab(请参阅参考资料)。这些测试做什么或者不做什么其实并不重要;我们只是必须相信它们正在测试发生器的质量,并且认真听从我们正在使用的发生器是否没有通过测试。

在构建用于随机数的硬件设备时,有一些诸如 DIEHARD 这样的测试套件也捕捉不到的缺陷。例如,某些类型的偏差不会在大多数统计测试中显示出来。由于这个原因,我们强烈建议:如果有可能的话,使用信誉好的商业硬件源来生成随机数,而不要构建自己的硬件源,因为您也许最终会不正确地测试您的发生器。如果您仍认定需要亲自测试发生器,我们建议您阅读文章“True Random Number Generators”(请参阅参考资料)。

结束语
在前两部分中,我们展示了随机数生成的两个极端。一个极端是传统的伪随机数发生器,它很容易被攻破,因此不适合注重安全性的应用程序。另一个极端是基于硬件的随机数发生器,它可以执行非常好的性能,但通常由于其昂贵的价格或缺少普遍可用性而不实用。

下次,我们将研究折中方法,在此方法中,随机数确实很好,即使它们是在不使用硬件设备的情况下生成的,即使它们的生成速度有一点慢。我们将讨论一类功能更加强大的伪随机数发生器,并讨论如何在不借助于特殊硬件的情况下从好的来源中收集随机性。

参考资料

请访问 developerWorks 上这个讲述随机数生成系列的第一篇文章“使您的软件运行起来:摆弄数字”。
请访问 ComScire 以了解关于 ComScire QNG 的信息。
请阅读 Robert Davies 撰写的评论“True Random Number Generators”。
请访问 Tundra 以了解关于 Tundra RBG 1210 的信息。
请访问 lavarand 网站,以轻松地查看随机数生成。
请阅读文章“Hardware Random Bit Generator”以了解如何构建自己的硬件随机数发生器。
请访问 DIEHARD 以获取一组随机数发生器的测试。
请访问 pLab,这是一个用于生成和测试随机数的面向对象系统。
Reliable Software Technologies.
关于作者
Gary McGraw 是 Reliable Software Technologies 负责企业技术的副总裁,该公司位于美国弗吉尼亚州杜勒斯(Dulles)。他从事咨询服务和研究工作,帮助决定技术研究和开发方向。McGraw 在 Reliable Software Technologies 从一个研究科学家作起,从事软件工程和计算机安全性方面的研究。他拥有印第安那大学认知科学和计算机科学双博士学位,弗吉尼亚大学的哲学学士学位。他为技术刊物撰写了 40 余篇经同行审查的文章,担任过主要的电子贸易供应商,包括 Visa 和 Federal Reserve 的顾问职务,并在空军研究实验室、DARPA、国家科学基金会,以及 NIST 的高级技术项目赞助下担任其首席调研员。

McGraw 是移动代码安全性方面著名的权威人士,并且与普林斯顿的教授 Ed Felten 合作撰写了“Java Security: Hostile Applets, Holes, & Antidotes”(Wiley, 1996)以及“Securing Java: Getting down to business with mobile code”(Wiley, 1999)。McGraw 和 RST 创始人之一、首席科学家 Dr. Jeffrey Voas 一起编写了“Software Fault Injection: Inoculating Programs Against Errors”(Wiley, 1998)。McGraw 定期为一些受欢迎的商业出版物撰稿,而且其文章经常在全国出版的文章中所引用。

John Viega 是一名高级副研究员,Software Security Group 的共同创始人,并担任 Reliable Software Technologies 的高级顾问。他是 DARPA 赞助的开发标准编程语言安全性扩展的首席调研员。John 已撰写了 30 余篇涉及软件安全性和测试领域的技术性文章。他负责在主要网络和电子贸易产品中查找一些众所周知的安全性脆弱性,包括最近在 Netscape 安全性中的缺陷。他还是开放源码软件社区的重要成员,编写过 Mailman、 GNU Mailing List Manager 以及最近发布的 ITS4(一种在 C 和 C++ 代码中查找安全性脆弱性的工具)。Viega 拥有弗吉尼亚大学的计算机科学硕士学位。
2005-4-11 22:35
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
最初由 dbgateway 发布
随机数最好用自己的算法一般是:网卡id+TickCount+随机数
不过一般开发语言提供的随机数算法生成的随机数在16或者18毫秒内是一样的,说明是跟时间相关的。用guid是最好的了,不过转换成可打印字符后比较长,
用数据库的序列值或者自己用一个整形变量+1生成,锁定对它的写操作。

你这样的结果一定是频繁初始化random seed的结果。
正确的使用方法是,srand只在程序开始做一次,而且尽可能使用某硬件随机源数据。比如毫秒数甚至纳秒数(如RDTSC指令的结果),还有键盘等待、磁盘定位等等一些不确定的数据源。
至于有人建议多次调用,或者多个随机数组合运算,千万要谨慎使用,弄不好反而引入了相关性,破坏了随机性或者其分布特性。
当然,伪随机数和物理随机数,做简单加法或异或运算,还是可行的。
比如:random()+time(0)类似的式子。
总之srand不要频繁调用就对,这也是一般使用者误以为可以增加随机性的地方。
2005-4-12 17:05
0
游客
登录 | 注册 方可回帖
返回
//