OpenSSL v0.9.8a随机数发生器分析(合集)

本文详细解析了OpenSSL v0.9.8a版本的随机数生成器原理及其实现细节,涵盖随机数发生器内部状态维护、关键函数说明、熵源采集等核心内容,特别针对Windows平台的熵源采集进行了详尽分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OpenSSL随机数发生器

本文档对OpenSSL使用的随机数进行研究分析,主要涉及OpenSSL v0.9.8a的随机数发生器以及其在Windows系统下的熵源采集情况。

目录

1. 概况... 1

1.1 OpenSSL随机数发生器概况... 1

1.2 OpenSSL 0.9.8a的随机数发生器概述... 2

2. 随机数发生器内部状态... 3

3. 函数说明... 4

3.1 ssleay_rand_cleanup置零... 4

3.2 ssleay_rand_status状态信息... 4

3.3 ssleay_rand_seed添加满熵种子... 5

3.4 ssleay_rand_pseudo_bytes产生伪随机数... 5

3.5 ssleay_rand_add添加熵信息... 6

3.6 ssleay_rand_bytes产生随机数... 8

4. 熵采集... 10

4.1 RAND_poll在Windows下采集熵... 10

4.2 RAND_poll在unix下采集熵... 14

1. 概况

1.1 OpenSSL随机数发生器概况

OpenSSL不同版本使用的随机数发生器不太相同,主要差别在于:

  1. 非FIPS版本:默认使用OpenSSL内部集成的一款基于HASH算法(默认为SHA1)的随机数发生器;熵源则根据不同的系统有不同的生成方式。
  2. FIPS版本:使用NIST SP 800-90A的三种随机数发生器,熵源由调用者通过回调函数的方式从外部提供。

非FIPS版本的熵源,根据不同的系统有不同的生成方式,

  1. Windows系统见.\crypto\rand\rand_win.c文件。
    1. 低版本的OpenSSL使用大量系统信息作为熵(本文研究重点)。
    2. 但高版本的则依赖于Windows系统提供的随机数,在Windows 7或更高版本中使用BCrypt代替了CryptoAPI。
  2. 其他系统的熵源生成方式均可见.\crypto\rand\文件夹。Linux系统使用系统提供的熵源,同样不同大版本使用的熵源差异较大。

下表是OpenSSL几个不同版本使用的随机数发生器的概况。

表1 OpenSSL不同版本使用的随机数发生器概况表

版本

随机数发生器

Windows下熵源

OpenSSL 0.9.8a

自己定义一套随机数发生器

见.\crypto\rand\md_rand.c

使用大量系统信息

OpenSSL 1.1.0

同上

仅使用Windows提供的随机数API,不再使用大量的系统信息。在Windows 7或更高版本中使用BCrypt代替了CryptoAPI。

OpenSSL FIPS 2.0.11

NIST SP 800-90A的三种随机数发生器

见.\ fips\rand\目录

外部提供

本文研究OpenSSL 0.9.8a的随机数发生器以及熵源的详情。

OpenSSL在Linux系统下使用的熵源可参见rand_unix.c文件。

OpenSSL FIPS 2.0.11使用的NIST SP 800-90A的三种随机数发生器参见《NIST SP 800-90系列(随机数发生器)笔记》。

1.2 OpenSSL 0.9.8a的随机数发生器概述

内部默认的随机数发生器代码参见.\crypto\rand\md_rand.c文件,这里提供了随机数发生器的状态信息以及一系列的调用函数

  • ssleay_rand_seed                  添加种子
  • ssleay_rand_bytes                产生随机数据
  • ssleay_rand_cleanup             置零
  • ssleay_rand_add                   添加信息
  • ssleay_rand_pseudo_bytes         产生伪随机数据
  • ssleay_rand_status               获取状态信息

熵源

  • RAND_poll                            不同版本使用不同的熵源产生方式

2. 随机数发生器内部状态

代码参见.\crypto\rand\md_rand.c文件

随机数发生器的内部状态定义为全局变量(变量名与文件中描述对应),即下表的数据均为static型。

STATE_SIZE 1023

MD_DIGEST_LENGTH   默认使用SHA1算法,所以为20

ENTROPY_NEEDED      所需的熵值,单位字节,值为32

2随机数发生器内部状态

数据

长度

类型

含义

state

STATE_SIZE+

MD_DIGEST_LENGTH字节

BYTE

内部状态数据,环形缓冲区。

state_num

4字节

int

内部状态实际有效字节数

state_index <= state_num <= STATE_SIZE

state_index

4字节

int

缓冲区偏移量(当前应处理的内部状态位置)

md

MD_DIGEST_LENGTH字节

BYTE

上一次添加种子(即rand_add)处理得到的摘要值

md_count[0]

4字节

long

随机数生成函数rand_bytes的调用次数

md_count[1]

4字节

long

记录添加种子函数rand_add送入种子的累积分块数,每次送入的分块数是 向上取整(种子长度/分块长度)

entropy

8字节

double

熵值,单位字节。

initialized

1比特

BOOL

是否初始化

3. 函数说明

默认的随机数发生器的调用函数为(代码.\crypto\rand\md_rand.c

  1. ssleay_rand_cleanup             置零
  2. ssleay_rand_status               获取状态信息
  3. ssleay_rand_seed                 添加满熵种子
  4. ssleay_rand_add                  添加信息
  5. ssleay_rand_bytes                产生随机数据
  6. ssleay_rand_pseudo_bytes         产生伪随机数据

熵源Windows下的代码.\crypto\rand\rand_win.c

  1. RAND_poll                            不同版本使用不同的熵源产生方式

3.1 ssleay_rand_cleanup置零

函    数:  void ssleay_rand_cleanup(void)

功能描述:  置零

说    明:  相当于init、uninit、reset

注    意:  无

参数说明:  无

返 回 值:  无

执行步骤:

步骤1:内部状态全部清零/重置。

3.2 ssleay_rand_status状态信息

函    数:  int ssleay_rand_status(void)

功能描述:  获取状态信息

说    明:  无

注    意:  无

参数说明:  无

返 回 值:  当前的熵值entropy是否大于32字节(256比特)

执行步骤:

步骤1:若未初始化(initialized0),执行采集系统熵信息的函数RAND_poll(),此函数将改变熵值entropy;并将initialized置为1

步骤2:返回当前的熵值entropy是否大于32字节(256比特)。

3.3 ssleay_rand_seed添加满熵种子

函    数:  void ssleay_rand_seed(const void *buf, int num)

功能描述:  添加满熵种子

说    明:  无

注    意:  这里认为种子是满熵的,所以将种子的熵值也设定为种子长度

参数说明: 

     buf     (in)     种子

     num   (in)     种子的字节长度

返 回 值:  无

执行步骤:

步骤1:直接调用ssleay_rand_add(buf, num, num)函数并返回。

3.4 ssleay_rand_pseudo_bytes产生伪随机数

函    数:  int ssleay_rand_pseudo_bytes(unsigned char *buf, int num)

功能描述:  产生伪随机数

说    明:  无

注    意:  这里简单地设置两个函数ssleay_rand_pseudo_bytes和ssleay_rand_add是一样,其实应有差别。

参数说明: 

     buf     (out)   伪随机数

     num   (in)     需要的伪随机数的字节长度

返 回 值:  1 [成功],0 [失败]

执行步骤:

步骤1:直接调用ssleay_rand_add(buf, num, num)函数并返回。

3.5 ssleay_rand_add添加熵信息

函    数:  void ssleay_rand_add(const void *buf, int num, double add)

功能描述:  添加熵信息

说    明:  更新内部状态;默认的HASH函数为SHA1,输出长度为20字节。

注    意:  添加信息的熵值(add)可为小数。

参数说明: 

     buf     (in)     添加信息

     num   (in)     添加信息的字节长度

     add    (in)     添加信息的熵值,单位:字节。

返 回 值:  无

执行步骤:

步骤1:更新state_num(状态的实际有效字节数)

state_num =min(state_num + numSTATE_SIZE)

步骤2:将输入数据buf以及内部状态中的环形缓冲区statestatestate_index起的num字节数据),分别按杂凑函数HASH的输出大小MD_DIGEST_LENGTH分割为n个块(最后一个分块可能为不完整分块):

B0||B1||...||Bn-1=buf

S0||S1||...||Sn-1=state[state_index,..., state_index+num-1]

n=Ceil(num/MD_DIGEST_LENGTH)

步骤3:local_md = md

步骤4:对每个块i = 0,1,2,...,n-1 执行

      1. 4.1 计算杂凑值

local_md = HASH (local_md ||Si||Bi||md_count[0]||md_count[1])

      1. 4.2 更新Si = Si local_md
      2. 4.3 更新md_count[1] = md_count[1]+1

步骤5:用更新的Sii=0,1,2,...,n-1更新状态state

state[state_index,..., state_index+num-1] = S0||S1||...||Sn-1

步骤6:用最后得到的local_md更新md = mdlocal_md

步骤7:更新熵值:entropy = min(32, entropy + add)。

图中的简写

C0md_count[0]

C1即md_count[1]

idx即state_index

3.6 ssleay_rand_bytes产生随机数

函    数:  int ssleay_rand_bytes(unsigned char *buf, int num)

功能描述:  产生随机数

说    明:  无

注    意:  添加信息的熵值(add)可为小数。

参数说明: 

     buf     (in)     添加信息

     num   (in)     添加信息的字节长度

     add    (in)     添加信息的熵值,单位:字节。

返 回 值:  无

执行步骤:

步骤1:若未初始化(initialized为0),则执行RAND_poll()增加熵值。

步骤2:计算几个基本记号

L2 = MD_DIGEST_LENGTH/2

L1 = MD_DIGEST_LENGTH

n=Ceil(num/ L2 ),

num_ceil = Ceil (num/ L)* L

步骤3:若熵源池未搅拌(本函数内定义的静态变量stirred_pool),则执行m = Ceil(STATE_SIZE/ L1) 次添加熵信息

rand_add("....................",L1, 0.0)。

步骤4:local_md = md

步骤5:将输出数据缓冲区buf以及内部状态中的环形缓冲区statestatestate_index起的num字节数据),分别L2分割为n=Ceil(num/ L2 )个块(最后一个分块可能为不完整分块):

B0||B1||...||Bn-1=buf

S0||S1||...||Sn-1=state[state_index,..., state_index+num-1]

步骤6:for k = 0,1, 2,...,n-1,执行

      1. 6.1 设置字符串pad:若k为0,则pad = getpid(),否则,pad = NULL为空串。
      2. 6.2 计算杂凑值

local_md = HASH( pad || local_md || md_count[0] || md_count[1] || Bi || Si)

注意:Bi是输出缓冲区,但这里读取了其中的数据

      1. 6.3 更新SiBi

Si = Si ⊕ local_md[0,..., L2-1]

Bi = Bi ⊕ local_md[L2,..., L1-1]

注意:这里没有更新md_count[0]md_count[1]的步骤。

步骤7:更新md

md = HASH(md_count[0] || md_count[1]|| local_md || md)

步骤8:用更新的Sii=0,1,2,...,n-1更新状态state

state[state_index,..., state_index+num-1] = S0||S1||...||Sn-1

步骤9:更新state_index =( state_index + num_ceil )mod state_num

步骤10:更新md_count[0] = md_count[0]+ 1;

步骤11:输出随机数buf = B0||B1||...||Bn-1

4. 熵采集

熵采集函数为RAND_poll,它在不同操作系统下使用不同的熵源产生方式,Windows下的代码.\crypto\rand\rand_win.c

此外,不同的OpenSSL版本中其熵采集方式也有较大出入。

OpenSSL版本

熵采集

v0.9.8a

使用了大量的各种信息,详情见本章后续部分。

v1.1.0

依赖于Windows提供的随机数,不再去采集各种信息。

FIPS版本

使用者自行提供熵,以回调函数方式设置,内部不提供熵。

4.1 RAND_poll在Windows下采集熵

作做熵源的信息包括下列信息。

函    数:  int RAND_poll(void)

功能描述:  采集各种熵源添加到内部状态

说    明:  此为Windows版本

注    意:  -

参数说明:  无

返 回 值:  无

执行步骤:下面主要说明添加的各种信息

信息1NetStatisticsGet45字节熵;netstatget17字节熵;

NetStatisticsGet() is a Unicode only function, STAT_WORKSTATION_0 contains 45 fields and STAT_SERVER_0 contains 17 fields.

NetStatisticsGet(NULL, L"LanmanWorkstation", 0, 0, &outbuf);

RAND_add(outbuf, sizeof(STAT_WORKSTATION_0), 45);

netstatget(NULL, L"LanmanServer", 0, 0, &outbuf);

RAND_add(outbuf, sizeof(STAT_SERVER_0), 17);

信息2Windows API 提供的随机数,熵值为sizeof(buf)字节

  1. 2.a如果BCryptGenRandom api存在,则执行,

BCryptGenRandom(NULL, buf, sizeof(buf), flag)

RAND_add(buf, sizeof(buf), sizeof(buf));

这里flag = BCRYPT_USE_SYSTEM_PREFERRED_RNG

  1. 2.b否则,即BCryptGenRandom api不存在,则执行
    1. 2.b.1 windowsCryptGenRandom,熵值为0字节

CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))

CryptGenRandom(hProvider, sizeof(buf), buf);

RAND_add(buf, sizeof(buf), 0);

    1. 2.b.2 intelCryptGenRandom,熵值为sizeof(buf)字节

CryptAcquireContextW(&hProvider, 0, INTEL_DEF_PROV, PROV_INTEL_SEC, 0))

CryptGenRandom(hProvider, sizeof(buf), buf);

RAND_add(buf, sizeof(buf), sizeof(buf));

信息3windows句柄,熵值为0字节

HWND h = GetForegroundWindow();

RAND_add(&h, sizeof(h), 0);

信息4:鼠标位置,熵值2字节

GetCursorInfo(&ci);

RAND_add(&ci, sizeof(CURSORINFO), 2);

信息5GetQueueStatus状态,熵值1字节

w = GetQueueStatus(QS_ALLEVENTS);

RAND_add(&w, sizeof(w), 1);

信息6:堆信息与遍历,熵值多字节

HEAPLIST32 contains 3 fields that will change with each entry, Consider each field a source of 1 byte of entropy.

HEAPENTRY32 contains 5 fields that will change with each entry, Consider each field a source of 1 byte of entropy.

handle = CreateToolhelp32Snapshot(TH32CS_SNAPALL,0);

hlist.dwSize = sizeof(HEAPLIST32);       

if (good) stoptime = GetTickCount() + MAXDELAY;(为避免时间过长,设置了一个时间阈值)

if (Heap32ListFirst (handle, &hlist)){

      do{

             RAND_add(&hlist, hlist.dwSize, 3);

             hentry.dwSize = sizeof(HEAPENTRY32);

             if (Heap32First(&hentry, hlist.th32ProcessID, hlist.th32HeapID)){

                   int entrycnt = 80;

                   do{

                         RAND_add(&hentry, hentry.dwSize, 5);

                   }while (heap_next(&hentry) && --entrycnt > 0);

             }//end if

      } while (Heap32ListNext(handle, &hlist) && GetTickCount() < stoptime);

}//end if

信息7:进程信息遍历,熵值9字节/进程

PROCESSENTRY32 contains 9 fields that will change with each entry. Consider each field a source of 1 byte of entropy.

p.dwSize = sizeof(PROCESSENTRY32);

if (good) stoptime = GetTickCount() + MAXDELAY;(为避免时间过长,设置了一个时间阈值)

if (Process32First(handle, &p)){

      do

      {

             RAND_add(&p, p.dwSize, 9);

      }while (Process32Next(handle, &p) && GetTickCount() < stoptime);

}//end if

信息8:线程信息遍历,熵值6字节/线程

THREADENTRY32 contains 6 fields that will change with each entry. Consider each field a source of 1 byte of entropy.

t.dwSize = sizeof(THREADENTRY32);

if (good) stoptime = GetTickCount() + MAXDELAY;

if (Thread32First(handle, &t)){

      do{

             RAND_add(&t, t.dwSize, 6);

      }while (Thread32Next(handle, &t) && GetTickCount() < stoptime);

}//end if

信息9:模块遍历,熵值9字节/模块

MODULEENTRY32 contains 9 fields that will change with each entry.  Consider each field a source of  1 byte of entropy.

m.dwSize = sizeof(MODULEENTRY32);

if (good) stoptime = GetTickCount() + MAXDELAY;

if (Module32First(handle, &m)){

      do

      {

             RAND_add(&m, m.dwSize, 9);

      }while (Module32Next(handle, &m) && (GetTickCount() < stoptime));

}//end if

信息10:时间信息,熵值1字节

调用readtimer()获取时间信息加入RAND_add1.1.0版也在用。

1. 优先调用 RDTSC,并增加RAND_add(,,1),即熵加1

2. 上述步骤不可用则调用QueryPerformanceCounter,并增加RAND_add(,,0),即熵加0

3. 上述步骤不可用则调用GetTickCount,并增加RAND_add(,,0),即熵加0

信息11:内存状态信息,熵值1字节

GlobalMemoryStatus Function Retrieves information about the system's current usage of both physical and virtual memory.

GlobalMemoryStatus(&m); /*1.1.0版在用*/

RAND_add(&m, sizeof(m), 1);

信息12:进程ID,熵值1字节

w = GetCurrentProcessId();/*1.1.0版在用*/

RAND_add(&w, sizeof(w), 1);

4.2 RAND_poll在unix下采集熵

函数:int RAND_poll(void)

使用系统提供的随机数作为熵源

此外还如下信息

RAND_add (PID,,0);

RAND_add (UID,,0);

RAND_add (time,,0);

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值