1. 随机数应用
- 生成密钥,如对称密码和MAC
- 生成公钥密码对
- 生成IV
- 生成nonce
- 生成盐
2. 随机数的性质
- 随机性 - 弱伪随机数
- 不可预测性 - 强伪随机数
- 不可重现性 - 真随机数
以上性质前者为后者的子集。
随机性
如果伪随机数列不存在统计学偏差,则可认定随机。
GMSSL - 国密SSL实验室提供了随机数检测工具GMTools.jar。
不可预测性
伪随机数生成器的算法是公开的,如果不知道种子,则不能根据已知伪随机数列预测出后面的伪随机数。
英特尔新型CPU的RDRAND指令可以生成不可预测的随即数,方法是基于NIST SP 800-90A中的CTR-DRBG。
不可重现性
仅靠软件无法实现,因为计算机具备有限的内部状态。
凡是具备周期的数列,都不具备不可重现性。
要想生成真随机数列,需要借助物理信息,如温度、声音、鼠标位置、键盘输入间隔等。
目前,利用电路中的热噪声可以生成真随机数列。如英特尔新型CPU的RDSEED指令可以生成不可重现的随机数,随机信号经过AES-CBC-MAC算法处理,生成256 bits随机数据,作为伪随机数生成器的种子。
3. 伪随机数生成器(PRNG)
seed
|
|
|
+-----------------+
| | |
| +->inner state |
| |
| |
| PRNG |---> random numbers
+-----------------+
内部状态就是PRNG的内存,根据内存值计算出下一个随机数。
内部状态绝不能被外部知晓。
下面介绍一下PRNG的各种生成方法。
杂乱算法
这种方法不可取,因为无法评估随机性。
线性同余
linear congruential method
应用很广泛,但不能用于密码技术。
已知A, C, M为常量,且A, C小于M,随机数列R生成如下:
seed作为内部状态
R0 = (A * seed + C) mod M
R0成为新的内部状态
R1 = (A * R0 + C) mod M
所以说,线性同余法不具备不可预测性,很多语言的随机函数,如C的rand,java的java.util.Random都是不能用于密码技术的。但Java有一个java.security.SecureRandom类。
利用hash函数
Hash函数具有单向性,可用于隐藏内部状态。
PRNG内部状态是一个计数器。
ctr = seed;
R0 = hash(ctr);
ctr += 1;
R1 = hash(ctr);
利用加密算法
相比hash方法,除了计数器初始值还需要一个加密密钥作为PRNG输入,即seed = CTR_IV + key.
ANSI X9.17
相比加密算法,内部状态的更新更复杂一些,PGP使用了这种方法,伪代码如下:
PRNG(iv, key)
{
static state = iv;
cipher1 = encrypt(key, cur_time);
cipher2 = encrypt(key, state ^ cipher1);
cipher3 = encrypt(key, cipher1 ^ cipher2);
state = cipher3;
return cipher2;
}
相关文档:FIPS 171, Key Management Using ANSI X9.17 | CSRC (nist.gov)
4. 攻击方法
- 窃取种子;
- 窃取伪随机数池,它存储了后续要使用的种子。