随机数与熵:密码学的基石

密码学概念 · 2026-09-21

概述

随机数是密码系统的根基。密钥生成、nonce 派生、Challenge-Response 协议——任何依赖不确定性的安全机制都建立在高质量随机数的基础上。一个可预测的随机数源足以摧毁整个密码体系。

本文从密码学随机数的理论要求出发,深入分析熵源的设计、CSPRNG 的实现原理,以及 Linux /dev/urandom 与现代操作系统随机数子系统的工程实践。

什么是密码学安全随机数?

基本要求

密码学安全随机数生成器(CSPRNG)必须满足两个核心性质:

不可预测性(Unpredictability):给定历史输出序列,无法预测下一个输出比特。即使是知道所有先前输出的攻击者,也无法以显著优于 50% 的概率预测下一比特。

状态恢复抗性(State Compromise Extensibility):即使攻击者获知当前内部状态,也无法推断先前输出的随机数。这一性质确保随机数生成器具有"前向安全"特征。

随机性与密码学的关系

CODE
安全参数 n = 128 bit 的密钥空间 = 2^128 ≈ 3.4 × 10^38 种可能

如果密钥生成使用了质量低劣的随机数源,实际熵可能远低于设计值。例如 2008 年 Debian OpenSSL 漏洞:一段错误的代码注释移除了熵收集机制,导致密钥空间从 $2^{128}$ 骤降至约 $2^{15}$,任何攻击者可在数小时内枚举出所有可能的私钥。

熵源设计

硬件熵源

硬件随机数生成器(HRNG)利用物理过程中的内在随机性:

  • 热噪声:电阻器中的电子热运动产生 Johnson-Nyquist 噪声
  • 散粒噪声:PN 结中载流子的离散性
  • 振荡器相位抖动:环形振荡器的时钟抖动
  • 放射性衰变:Geiger 计数器的衰变间隔时间
Linux 内核从这些物理源收集熵,维护一个熵池(entropy pool)。现代 x86 平台通过 RDSEED 和 RDRAND 指令直接访问硬件 RNG。

软件熵源

纯软件方案无法独立产生密码学随机数,必须结合外部事件:

  • 键盘/鼠标输入时间戳
  • 磁盘 I/O 中断时序
  • 网络数据包到达间隔
  • CPU 缓存命中/未命中时间差

熵估计

熵是衡量不确定性的信息论量:

$$H(X) = -\sum_{x \in X} p(x) \log_2 p(x)$$

对于均匀分布的 n 位随机数,熵值为 $H = n$ 比特。实际系统中,每个事件源提供的熵远小于其输出长度。例如,一个微秒精度的时间戳可能只贡献 6-10 比特的实际熵。

操作系统随机数子系统

Linux /dev/random 与 /dev/urandom

特性/dev/random/dev/urandom
阻塞行为熵不足时阻塞永不阻塞
背压机制有无
推荐场景长期密钥种子几乎所有场景
关键结论:自 Linux 5.6(2020 年)起,/dev/random 与 /dev/urandom 在安全性上等价——两者均使用 ChaCha20 CSPRNG,且 /dev/urandom 不再受背压限制。因此,现代 Linux 系统应优先使用 /dev/urandom。

getrandom() 系统调用

getrandom() 是比读取设备文件更现代的接口:

C
#include <sys/random.h>
#include <unistd.h>

ssize_t n = getrandom(buf, len, 0);
// GRND_RANDOM: 使用慢速熵源(已废弃)
// GRND_NONBLOCK: 熵不足时不阻塞,返回 -1

优势:

  • 不经过 VFS 层,性能更优
  • 明确的语义:成功返回随机字节,失败返回错误
  • 内核自动处理阻塞/非阻塞行为

Windows 随机数

Windows 使用 BCryptGenRandom() API,基于 CNG(Crypto Next Generation)框架。底层实现与 Linux 不同,但遵循相同的 CSPRNG 原则。

CSPRNG 算法

主流算法

算法来源特点
ChaCha20-CTRLinux 5.6+快速、简洁、抗侧信道
AES-CTROpenSSL、Windows广泛支持、硬件加速
HMAC-DRBGNIST SP 800-90A可证明安全、标准规范
FortunaSchneier 等人多池设计、抗状态恢复

ChaCha20 CSPRNG

Linux 5.6+ 的 /dev/urandom 使用 ChaCha20 构造的 CTR 模式:

CODE
状态初始化:seed → ChaCha20 密钥调度
输出生成:CTR 计数 + ChaCha20 加密 → 伪随机流
状态更新:定期注入新熵

HMAC-DRBG(NIST SP 800-90A)

HMAC-DRBG 基于 HMAC 构造,提供可证明的安全性:

CODE
重加载: V = HMAC(K, V || 0x00 || personalization)
更新:   (K, V) = HMAC-KDF(K, V || input || additional)
输出:   V = HMAC(K, V || 0x00); return V

国密随机数标准

GM/T 0005-2012《随机性检测规范》

GM/T 0005-2012 规定了密码产品中随机数发生器的检测方法和要求。该标准与 NIST SP 800-22 类似,包含以下核心测试:

测试项目的判定标准
频度测试检测 0/1 比例偏离$\chi^2$ 检验,p ≥ 0.01
游程测试检测连续相同比特游程数量符合分布
功率谱测试检测周期性成分峰值数量合理
串行测试检测比特间相关性各子序列均匀分布
扑克测试检测子序列重复模式频数符合分布
近似熵测试检测规律性模式近似熵接近理论值
注意:GM/T 0005-2012 是检测规范,不是随机数生成算法规范。随机数生成器的设计和实现应遵循 GM/T 0028-2014《密码模块安全技术要求》中第 4.3 节的熵源要求。

与 NIST SP 800-22 的对比

维度NIST SP 800-22GM/T 0005-2012
测试套件数量15 个约 8 个核心测试
适用对象通用随机数密码产品专用
判定标准p ≥ 0.01p ≥ 0.01(基本一致)
国密合规不适用密评必需

工程实践:Python 随机数

标准库推荐用法

避免的错误用法

PYTHON
# ❌ 错误:非密码学安全
import random
key = random.getrandbits(256)  # Mersenne Twister,可预测

# ❌ 错误:时间戳作为种子
import time
seed = int(time.time() * 1000)  # 熵源极弱

# ✅ 正确:使用系统熵源
import os
key = os.urandom(32)  # 基于 /dev/urandom 或 getrandom()

多语言安全随机数对照

语言推荐 API底层实现
Pythonos.urandom(), secrets/dev/urandom 或 getrandom()
Gocrypto/rand.Read()/dev/urandom 或 RDRAND
JavaSecureRandomOS-specific CSPRNG
C/C++getrandom(), CryptGenRandom平台原生 API
Rustgetrandom crate/dev/urandom 或 RDRAND

性能与阻塞

熵池耗尽问题

早期 Linux 系统中,/dev/random 在熵池耗尽时会阻塞。这在高并发场景(如 TLS 握手)中可能导致性能问题。Linux 5.6 后,/dev/urandom 不再受此限制,推荐使用。

性能对比

方法吞吐量阻塞风险
/dev/random(旧)慢,受熵率限制高
/dev/urandom(Linux 5.6+)快(与内存带宽相当)无
getrandom()最快(直接系统调用)启动早期可能阻塞
RDRAND 指令最快(硬件直接)无
建议:生产环境应使用 getrandom() 或 os.urandom(),避免使用 /dev/random。

虚拟机与容器的熵问题

虚拟机和容器共享宿主机熵源,在启动早期可能面临熵不足问题:

解决方案

  • 启用熵收集设备:
BASH
# 安装 entropy daemon
   sudo apt install haveged
   sudo systemctl enable haveged
  • 使用 TPM 硬件熵源(VM 环境中):
BASH
# 通过 TPM 传递宿主机的熵
   echo 1 | sudo tee /sys/module/tpm/parameters/pass_through
  • 容器内预填充熵:
DOCKERFILE
# Dockerfile 示例
   FROM ubuntu:22.04
   RUN apt-get update && apt-get install -y haveged
   CMD ["/usr/sbin/haveged", "-w", "1024"]

安全建议

  • 始终使用 CSPRNG:严禁使用非密码学随机数生成器生成密钥、token 或 nonce
  • 避免硬编码种子:random.seed(12345) 是可预测的
  • 验证熵源充足:在生产环境中监控熵池状态
  • 关注 OS 版本:Linux 5.6+ 的 /dev/urandom 是最简单可靠的选择
  • 定期更新密码库:确保使用支持最新随机数 API 的库版本

相关实践文章

参考标准

  • GM/T 0005-2012《随机性检测规范》
  • NIST SP 800-22 Rev. 1a《A Statistical Test Suite for Random and Pseudorandom Number Generators for Cryptographic Applications》
  • NIST SP 800-90A Rev. 1《Recommendation for Random Number Generation Using Deterministic Random Bit Generators》
  • GM/T 0028-2014《密码模块安全技术要求》