文档 技术细节 随机数生成器

# 随机数生成器

VeraCrypt随机数生成器（RNG）用于生成主加密密钥、辅助密钥（XTS模式）、盐值和密钥文件。它在随机存取存储器（内存）中创建一个随机值池。这个320字节长的池由以下来源的数据填充：

- 鼠标移动
- 按键操作
- Mac OS X和Linux：内置随机数生成器生成的值（包括/dev/random和/dev/urandom）
- 仅适用于MS Windows：MS Windows CryptoAPI（每隔500毫秒定期收集）
- 仅适用于MS Windows：网络接口统计信息（NETAPI32）
- 仅适用于MS Windows：各种Win32句柄、时间变量和计数器（每隔500毫秒定期收集）

从上述任何来源获取的值在写入池之前，会被分割成单个字节（例如，一个32位的数字会被分割成四个字节）。然后，这些字节通过模28加法运算（而不是替换池中的旧值），分别写入池的游标位置。写入一个字节后，池游标的位置会向前移动一个字节。当游标到达池的末尾时，其位置会被设置为池的开头。每写入16个字节到池中后，会自动对整个池应用池混合函数（见下文）。

## 池混合函数

此函数的目的是进行扩散[2]。扩散将单个“原始”输入位的影响尽可能地分散到池状态的更多部分，这也隐藏了统计关系。每写入16个字节到池中后，会对整个池应用此函数。

池混合函数的描述：

1. 设R为随机数池。
2. 设H为用户选择的哈希函数（SHA - 512、BLAKE2S - 256或Whirlpool）。
3. l = 哈希函数H输出的字节大小（即，如果H是BLAKE2S - 256，则l = 20；如果H是SHA - 512，则l = 64）
4. z = 随机数池R的字节大小（320字节）
5. q = z / l - 1（例如，如果H是Whirlpool，则q = 4）
6. R被分割成l字节的块B0...Bq。

   对于0 ≤ i ≤ q（即，对于每个块B），执行以下步骤：

   1. M = H (B0 || B1 || ... || Bq) [即，使用哈希函数H对随机数池进行哈希处理，生成哈希值M]
   2. Bi = Bi ^ M
7. R = B0 || B1 || ... || Bq

例如，如果q = 1，随机数池将按以下方式混合：

1. (B0 || B1) = R
2. B0 = B0 ^ H(B0 || B1)
3. B1 = B1 ^ H(B0 || B1)
4. R = B0 || B1

## 生成的值

随机数生成器池的内容永远不会直接导出（即使VeraCrypt指示随机数生成器生成并导出一个值）。因此，即使攻击者获得了随机数生成器生成的值，他也无法确定或预测（使用所获得的值）会话期间随机数生成器生成的任何其他值（从随机数生成器生成的值确定池的内容是不可行的）。

每当VeraCrypt指示随机数生成器生成并导出一个值时，随机数生成器通过执行以下步骤来确保这一点：

1. 将从上述来源获得的数据按上述方式添加到池中。
2. 将请求的字节数从池复制到输出缓冲区（复制从池游标的位置开始；当到达池的末尾时，复制从池的开头继续；如果请求的字节数大于池的大小，则不生成值并返回错误）。
3. 反转池中每个位的状态（即，0变为1，1变为0）。
4. 将从上述某些来源获得的数据按上述方式添加到池中。
5. 使用池混合函数转换池的内容。注意：该函数使用用户选择的加密安全单向哈希函数（有关更多信息，请参阅上面的池混合函数部分）。
6. 将转换后的池内容按以下方式异或到输出缓冲区：
   1. 将输出缓冲区写入游标设置为0（缓冲区的第一个字节）。
   2. 从池的游标位置读取一个字节，并将其与输出缓冲区中写入游标位置的字节进行异或运算。
   3. 将池游标的位置向前移动一个字节。如果到达池的末尾，则将游标位置设置为0（池的第一个字节）。
   4. 将输出缓冲区写入游标的位置向前移动一个字节。
   5. 对输出缓冲区的每个剩余字节（其长度等于请求的字节数）重复步骤b - d。
   6. 导出输出缓冲区的内容，即随机数生成器生成的最终值。

## 设计起源

随机数生成器的设计和实现基于以下工作：

- Peter Gutmann的《软件生成实用强随机数》[10]
- Carl Ellison的《加密随机数》[11]

下一部分 >>