4. 离线统计测试:NIST测试套件详解
好,咱们进入正题。离线统计测试,说白了就是拿一堆随机数,用数学方法去“审问”它——你到底是不是真随机?NIST SP 800-22 是这个领域的圣经。我这些年做硬件安全,几乎每个项目都要跑一遍这套测试。今天我就把几个最核心的测试项掰开揉碎了讲给你听。
4.1 频率测试(Mono-bit Frequency Test)
这是最基础、也最直观的测试。它只问一个问题:你的序列里,0 和 1 的数量是不是差不多?
理想情况下,一个真随机序列应该有接近一半的 0 和一半的 1。如果偏差太大,说明你的熵源可能有问题。比如,我遇到过某个 TRNG 芯片,因为电源噪声耦合进了 VCO,导致输出 1 的比例总是比 0 多 2% 左右。频率测试第一个就把它揪出来了。
核心公式:
S_n = sum(2*bit_i - 1) // 将 0→-1, 1→+1 后求和
P_value = erfc(|S_n| / sqrt(2*n))
其中 n 是序列长度,erfc 是互补误差函数。P_value 小于 0.01 就判定为非随机。
我的经验:频率测试对序列长度很敏感。太短(比如 n < 100)测不出偏差;太长(n > 10^6)又可能因为微小但统计显著的偏差而误判。我个人习惯用 n = 10000 作为标准长度。
4.2 块内频数测试(Frequency Test within a Block)
频率测试看的是全局。但有时候,全局比例是 50/50,局部却有问题。比如,前 1000 位全是 0,后 1000 位全是 1——整体还是平衡的,但这显然不是随机。
块内频数测试就是干这个的。它把序列切成 M 位一块,分别统计每块里 1 的比例,然后用卡方检验看这些比例是否均匀。
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 块大小 M | 100 ~ 1000 | M 太小则块数太多,统计不稳定;M 太大则失去局部检测能力 |
| 序列总长 n | ≥ 100 * M | 保证至少有 100 个块,卡方检验才有效 |
嗯,这里要注意:块大小 M 的选择很关键。我见过有人用 M=8,结果每个块只有 8 位,统计量根本不够,测试形同虚设。我一般取 M=128,既够统计量,又能捕捉到中等尺度的局部偏差。
4.3 游程测试(Runs Test)
游程,就是连续相同位的序列。比如 000111000 里有三个游程:000、111、000。游程测试检查的是:你的序列里,游程的数量和长度是否在预期范围内?
为什么会这样?因为真随机序列里,游程的分布是有规律的。太长的游程(比如连续 20 个 1)出现概率极低。如果出现了,说明你的随机源可能有记忆效应——也就是状态依赖。
我曾经踩过的坑:有一次测试一个基于环形振荡器的 TRNG,游程测试总是不过。查了很久才发现,是振荡器之间的耦合导致了一个 3 位的偏好模式反复出现。频率测试完全正常,但游程测试把它暴露了。所以,频率测试和游程测试一定要搭配使用,缺一不可。
4.4 离散傅里叶变换测试(DFT Test)
这个测试稍微硬核一点。它把随机序列当成一个时域信号,做 FFT 变换到频域,然后检查频谱中是否有明显的尖峰。
你想想看,如果序列是纯随机的,它的频谱应该是平坦的——白噪声嘛。但如果存在周期性成分,频谱上就会出现峰值。比如,某个 TRNG 因为 50Hz 工频干扰,输出里就带了一个 50Hz 的周期分量。DFT 测试能把它抓出来。
具体做法是:
- 将比特序列映射为 ±1 序列
- 做离散傅里叶变换,得到频谱
- 统计频谱中超过某个阈值的峰值数量
- 与理论预期比较,计算 P 值
关键参数:阈值通常取 sqrt(n * log(1/0.05)),对应 95% 置信水平。超过这个阈值的峰值数量如果太多或太少,都说明有问题。
4.5 测试参数选择与 P 值解读
跑 NIST 测试,参数选不对,结果就是废纸。我总结了几条铁律:
- 序列长度 n:NIST 官方建议 n ≥ 10^6。但实际项目中,我经常用 n = 10^5 做快速筛选,通过后再用 10^6 做正式认证。
- 显著性水平 α:通常取 0.01。这意味着有 1% 的概率把真随机误判为非随机。如果你做的是金融级安全,建议取 0.001。
- P 值解读:P ≥ 0.01 通过,P < 0.01 不通过。但要注意,P 值接近 0.01 时(比如 0.011),不要急着下结论。我建议多测几组序列,看 P 值的分布是否均匀。
| P 值范围 | 解读 | 建议操作 |
|---|---|---|
| 0.5 ~ 1.0 | 非常随机 | 继续其他测试 |
| 0.1 ~ 0.5 | 正常随机 | 正常通过 |
| 0.01 ~ 0.1 | 边缘 | 增加样本量复测 |
| < 0.01 | 不随机 | 检查熵源 |
4.6 测试序列长度要求
这个问题很多人问。NIST 对每个测试都有最小序列长度要求,我整理了一张表:
| 测试名称 | 最小序列长度 | 推荐长度 |
|---|---|---|
| 频率测试 | 100 | 10,000 |
| 块内频数测试 | 100 * M | 100,000 |
| 游程测试 | 100 | 10,000 |
| DFT 测试 | 1,000 | 100,000 |
但说实话,这些最小值只是理论下限。你想想看,用 100 位去判断一个随机数发生器好不好,这本身就不靠谱。我个人的底线是:每个测试至少用 10^5 位,整套测试至少用 10^6 位。低于这个量级,测试结果没有统计意义。
一个小技巧:如果你需要快速迭代测试(比如调试阶段),可以用 10^4 位跑一遍所有测试,大概 1 秒出结果。等参数调好了,再用 10^6 位做正式测试。这样效率高很多。
这张图把整个测试流程串起来了。从原始序列出发,经过预处理,分四条路径并行测试,最后汇聚到 P 值判定。你每次跑测试,脑子里都要有这个流程——知道每一步在干什么,为什么这么干。