ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

NIST STS 2.1.2 完整使用教程:下载、编译、测试与结果判定

2026/10/7 1:05:24 拓冰建站 浏览量
NIST STS 2.1.2 完整使用教程:下载、编译、测试与结果判定 最近被问到最多的问题除了这批随机数到底过没过 NIST就是NIST 那个测试软件到底怎么装、怎么用。这事情听起来简单真上手了坑不少官网页面上挂着的是压缩包老代码在 64 位 Linux 上编译经常报警告跑完测试看报告又分不清 P 值、比例、均匀性三者到底哪个说了算。这篇我按实际踩过的路径写一份完整教程覆盖 NIST STS 2.1.2 的下载、编译、数据准备、交互式测试和结果判定顺便把我在实践中踩过的几个典型坑也交代清楚。这套软件的核心价值在于它不是测数据看起来像不像随机而是用 15 项统计检验去量化你的随机数发生器是否存在某种可辨识的模式。无论你做的是硬件 RNG、PRNG、还是某个密码学算法输出客户和审计方认的通常就是这份 SP 800-22 测试报告。下文我会把整个流程讲透普通工程师照着做就能跑通不会让你卡在半路。1. 为什么推荐 NIST STS而不是只看看分布和自相关1.1 随机性没法证明只能用假设检验去找毛病大多数自己做随机数质量验证的人初级阶段会用均值、方差、直方图、自相关这些手段看一眼数据。坦白说这些检查很有必要但远远不够。一个差异明显的非随机序列在简单的统计量上完全可以伪装成随机序列。比如线性同余发生器的低位比特从分布上看各值出现次数可能非常均匀但它的周期性和线性结构用 NIST 套件里的线性复杂度测试一测就会暴露。NIST SP 800-22 的做法是把随机性测试看成一组假设检验原假设是这段序列是随机的然后每种测试构造一个统计量去计算如果序列真的是随机的出现当前这种极端情况的概率是多少。这个概率就是 P 值。P 值很低说明序列行为太反常我们就有理由怀疑它不随机。这个逻辑和你做质量检验时超过控制限就判异常是一个思路。所以 NIST STS 不是一个证明随机性的工具它是一个寻找非随机证据的工具。这也是为什么正规流程里要求测试样品要足够多、样本量要足够大因为检验的统计功效很大程度依赖样本量。你拿一兆比特的测试数据去跑和拿几十吉比特去跑结论的可靠性完全不在一个量级。1.2 套件里的 15 项测试分别盯住哪些缺陷STS 2.1.2 从菜单里能直接看到 15 个测试项目外加一个自定义组合功能。很多人第一次打开菜单是懵的这里把每一项的核心作用梳理一下Frequency (单比特频数检测)检查 0 和 1 的总数是否接近各一半。这是最基础的检测但非常重要因为它能看到大部分明显的偏差。Block Frequency (块内频数检测)把序列分成若干块看每块内 1 的比例是否稳定。它能捕捉到局部不随机但全局恰好平衡的情况。Cumulative Sums (累加和检测)把比特映射成 1/-1 后做随机游走看游走路径是否偏离随机游走的预期范围。适合发现长程相关。Runs (游程检测)检查连续相同比特段的个数和长度分布是否异常。Longest Run of Ones (最长游程检测)在块内检查最长连续 1 的长度。Rank (矩阵秩检测)把比特构成二进制矩阵检查矩阵秩分布。它能发现线性相关结构。FFT (离散傅里叶变换检测)通过频谱峰值分布来找周期性。Non-overlapping Template Matching (非重叠模板匹配)检查特定短模式的出现频率是否超出预期。Overlapping Template Matching (重叠模板匹配)上述测试的重叠版本对局部模式更敏感。Universal (通用统计检测)基于无损压缩的思想看序列是否可以被明显压缩。能压缩就意味着有规律。Approximate Entropy (近似熵检测)比较相邻长度模式的频率差异频率差异越大越不随机。Random Excursions (随机游动检测)把序列映射成随机游走检查访问某个状态的次数分布。Random Excursions Variant (随机游动变体检测)同样基于随机游走从不同统计角度验证。Serial (序列检测)检查长度 m 的比特模式出现频率是否均匀。Linear Complexity (线性复杂度检测)用 Berlekamp-Massey 算法计算序列的线性复杂度看是否存在低阶线性反馈移位寄存器结构。简单理解频数类测试管总体比例游程和自相关类测试管时序关系矩阵和线性复杂度管代数结构熵和压缩类测试管可预测性。一个合格的随机数输出应该在这些维度上都找不到明显破绽。2. 下载前先搞清楚版本和来源避免拿到改过的假包2.1 官方发布与社区维护的版本区别很多人在搜索引擎里找 NIST 随机数测试软件一不小心就跑到第三方下载站去了。那些站点提供的压缩包可能版本陈旧甚至被人动过手脚测试代码一旦被篡改你的测试结果就完全不可信。所以第一步必须是确认来源。NIST 官方项目页面在 csrc.nist.gov 上对应的名称是 Random Bit Generation里面提供 SP 800-22 的文档和 STS 软件包。目前广泛使用的 C 语言版本是 sts-2.1.2它对应的规范文档是 NIST SP 800-22 Revision 1a发布于 2010 年。虽然发布时间比较早但直到今天它仍然是业界验收时默认采用的实现版本。除了官方页面GitHub 上也有不少镜像仓库比较典型的是以 sts-2.1.2 命名的仓库。我一般用官方连不上或者下载太慢的时候才会走 GitHub 镜像但用之前一定要看仓库的 star、提交历史和 issue确认源码包没有被二次修改。最稳妥的做法是从官方拿到压缩包后再去找已知的 SHA-256 值或对照官方页面的发布信息做核验。如果两个来源的文件哈希不一致坚决不要用。2.2 下载文件完整性校验不能省你可能会觉得哈希校验是洁癖行为但在测试工具这件事上文件完整性直接关系到测试结论的公正性。我的习惯是下载后立刻执行sha256sum sts-2.1.2.zip然后把算出来的哈希值和你信得过的源做对比。社区里流传的版本很多至少保证你自己本地保留的这份是可复现的。另外解压前最好确认压缩包内没有可疑文件比如多余的脚本、可执行文件之类。STS 源码包本身就是一组 C 源码和文档里面出现任何不明二进制都有问题。解压后目录结构大致是这样的assess编译后生成的主程序data/示例数据文件可以用来快速验证安装是否成功experiments/测试输出目录include/、src/头文件和源码makefile编译脚本doc/说明文档到这一步你对下载的东西心里就有数了。3. 源码编译安装全过程Linux 为主Windows 绕行方案3.1 Linux 下从 make 到生成 assess 可执行文件STS 2.1.2 的编译依赖很简单只要系统里有 gcc 和 make 就能干活。以 Ubuntu/Debian 为例先确认环境sudo apt update sudo apt install -y build-essentialbuild-essential 会一次性把 gcc、g、make 等基础工具装好。接着进入源码目录执行unzip sts-2.1.2.zip cd sts-2.1.2 make正常情况下编译结束后目录下会多出一个名为assess的可执行文件。这里有个老代码常见的现象编译过程中会刷出很多警告尤其是implicit declaration of function gets之类。这是因为 STS 源码写于十几年前当年gets还是一个合法函数后来因为安全问题被移除了。只要进程能正常结束并且生成了assess这些警告可以暂时不理会。为了验证编译是否成功直接用自带的示例数据试跑一下./assess 1000000程序会进入交互式界面这时候输入0选择全部测试再输入数据目录里的某个测试文件名比如data/data.bits。如果程序能正常往下走并生成报告文件说明安装没问题。3.2 没有 Linux 环境怎么办WSL、Docker 与本地移植不少做嵌入式或者纯 Windows 开发的朋友机器上没有 Linux 环境。STS 是老式 C 项目在 Windows 上直接编译需要 Cygwin 或 MSYS2 那套 POSIX 模拟层搞起来比较麻烦。我的建议是按顺序试下面三种方案如果你的系统是 Windows 10/11首选 WSL。装好 WSL 的 Ubuntu 发行版后在 WSL 终端里执行sudo apt install build-essential然后按照 Linux 的流程走就行。WSL 的文件系统能和 Windows 互通测试用的数据文件放在/mnt/c/路径下也能正常读取。不想动系统的可以用 Docker。拉一个通用的 Ubuntu 镜像把源码目录挂载进去编译。命令大致是这样docker run --rm -it -v /path/to/sts-2.1.2:/work ubuntu:22.04 bash cd /work apt update apt install -y gcc make make这种方式好处是不污染宿主机环境编译完的东西直接落在挂载目录里。坏处是 Docker 本身对新手有点学习成本。最后才是 Cygwin 或 MSYS2。这两个工具能模拟大部分 Linux API但我在实践中发现 STS 在 Cygwin 下的编译偶尔会碰到路径分隔符和换行符的诡异问题能用前两种方案就不要为了原生去折腾 Cygwin。4. 第一个有效的测试流程数据准备、参数配置、跑通 assess4.1 测试数据应该怎么生成ASCII 与二进制如何选择进入交互界面后第一个关键选择是待测数据格式。NIST 支持两种输入ASCII 格式和二进制格式。ASCII 格式简单说就是文本文件里只有字符 0 和 1每个比特占一个字节二进制格式就是每个字节存储 8 个比特传输效率高。从易用性角度新手用 ASCII 格式最直观因为肉眼能检查文件内容是否正常。但要注意 ASCII 文件必须是一长串连续的 0/1 字符不要带空格、换行、回车否则程序会把空白字符也当成数据读进去导致测试结果错得离谱。生成 80,000,000 比特的 ASCII 文件用 Python 大概是这样import random with open(random_ascii.txt, w) as f: for _ in range(80000000): f.write(1 if random.getrandbits(1) else 0)但这样生成一个文件要几十秒磁盘占用也接近 80 MB。如果数据量更大我更推荐二进制格式。生成 1 亿比特约 100 Mbit的二进制随机文件import os with open(random.bin, wb) as f: f.write(os.urandom(12500000))注意os.urandom在 Linux 上是从/dev/urandom读取的属于系统级伪随机数适合用来验证工具流程。如果你要测试的是自己的 PRNG 或者硬件 RNG就把random.bin替换成你自己采集的数据。二进制格式下NIST 程序会按字节读取并且默认把每个字节的最高位当作序列中该字节的第一比特。这一点必须仔细核对因为不同硬件采集卡对位序的处理不一样如果位序颠倒性能好的随机数也可能测出一堆问题。采集设备通常会在文档里标注 MSB first 还是 LSB firstNIST STS 的假设是 MSB first。如果你的设备是 LSB first需要先把每个字节的位序翻转再做测试。4.2 assess 交互式参数逐个拆解假设我们现在有一份二进制随机数据文件random.bin大小 12.5 MB代表 1 亿比特。启动测试./assess 1000000这里的1000000是单个序列的比特长度。NIST 的一个基本概念是你会把整个文件分成若干个长度为 n 的序列每个序列单独参与所有测试。n 的选择直接影响测试的统计功效SP 800-22 建议 n 至少取 1000000。运行后程序会依次问你几个问题第一项是选择测试方式。输入0表示运行全部 15 个测试输入1到15中的数字跑单项输入16是自定义组合。第一次跑通建议直接0省事。第二项是输入数据文件名。注意这里只要你输入文件名本身程序默认从当前工作目录读取。如果你把文件放在了data/子目录下就要写data/random.bin。第三项是询问你要将文件分割成多少个序列。这个数值决定了样本量 m。比如我们的文件有 1 亿比特n 取 1,000,000最多可以分成 100 个不重叠序列。如果文件大小不够整除程序会报错提示数据不足。这里不要贪多样本量真实是多少就填多少程序不会替你编造数据。接下来程序还会问你二进制输入时要不要转换、要不要修改初始参数等。对第一次使用全部接受默认值就行。默认参数对应 SP 800-22 文档里推荐的配置比如块内频数检测的块长、序列检测的模式长度等。等你有经验了再按自己的测试需求改。4.3 跑完以后结果文件在哪STS 交互跑完后所有测试结果都写在当前目录下的experiments/AlgorithmTesting/文件夹里。不同时间跑的结果会放在带时间戳的子目录下方便你回溯。最核心的文件是finalAnalysisReport.txt里面汇总了每一轮测试的 P 值、通过比例和均匀性 p 值。我建议每次跑完测试后立即把整个experiments/AlgorithmTesting/下的时间戳目录改名为有业务含义的名字比如20250115_entropy_source_v1。因为 STS 默认不会自动清理旧结果隔几天再跑几个样本目录一多容易搞混尤其是出报告的时候拿错文件会非常尴尬。5. 从 finalAnalysisReport.txt 判断随机性比例、P 值和整套判定逻辑5.1 什么是 P 值0.01 阈值从哪来拿到finalAnalysisReport.txt你会看到大量数字。不要慌核心只有两列需要盯P-value 和 Proportion。P 值的含义是假如这段序列真的来自真随机源那么测试统计量观察到当前数值或更极端数值的概率。NIST 选定的显著性水平是 0.01意思是如果 P 值小于 0.01就认为统计结果与随机假设严重不符该序列被判定为非随机。这和我们日常说的置信水平 99%是对应的。但注意一个常见误区不是说 P 值越大越好。P 值在 0 到 1 之间均匀分布才是理想随机表现。如果 100 个序列的 P 值全部集中在 0.99 附近反而说明测试统计量的分布异常这不正常。所以报告里除了每个序列的 P 值还会算一个P 值的均匀性指标检验这些 P 值本身是否均匀。这一点经常被新手忽略但审计专家往往会看。5.2 如何快速检查通过比例以及全过不一定万事大吉报告里每个测试项都有一行通过比例分子是 P 值大于等于 0.01 的序列数分母是参与该测试的序列总数。NIST 对通过比例的判断不是拍脑袋定 95%而是给出一个区间[ \hat p - 3 \sqrt{\frac{\hat p (1 - \hat p)}{m}} \leq \text{通过比例} \leq \hat p 3 \sqrt{\frac{\hat p (1 - \hat p)}{m}} ]其中 (\hat p 1 - 0.01 0.99)(m) 是序列数。当 (m 100) 时标准差是 (\sqrt{0.99 \times 0.01 / 100} 0.00995)3 倍标准差约为 0.02985所以可接受的通过比例下限是 (0.99 - 0.02985 0.96015)上限超过 1取 1。也就是说100 个序列中至少有 96 个序列通过才算合格。如果 (m) 更小比如 10 个序列那么区间会非常宽几乎失去判断意义。这就是为什么我不建议你用很小的样本量去跑宁可多采集数据也不要用少于 100 个序列的结果出去汇报。另外有个细节Random Excursions 和 Random Excursions Variant 这两个测试只对数据中能构成完整随机游走周期的段落进行统计所以参与分子分母的序列数可能比其他测试少。报告中会看到分母不足百甚至只有几十这不一定代表测试失败但你要能解释为什么会这样。还有一句经验如果测试报告显示某个单项 100% 全部通过不要急着庆祝。请先检查是不是数据量不够、序列数太少或者测试参数被调得过于宽松。真正可靠的做法是同时看 P 值分布、通过比例和均匀性 p 值三件事。三项都正常才能说未发现非随机证据。6. 实测中遇到的编译、数据格式和结果误读问题6.1 编译失败gcc 版本和旧代码库的兼容性我在三台不同版本 Linux 上编译过 STS最典型的问题出现在老版本 GCC 与新版本之间。早期源码里有一些写法在新编译器下直接不再支持比如隐式函数声明变成了错误或者链接时找不到数学库。解决方法很简单确保系统安装了gcc和make并且如果缺少数学库就在编译时加上-lm。有些集成开发环境默认不给自动链接数学库所以你可能会在链接阶段看到undefined reference to pow这类报错。我习惯直接修改 makefile 里的CFLAGS和LDFLAGS把优化级别和链接参数写清楚make CFLAGS-O2 -Wall LDFLAGS-lm如果源码目录里已经有之前编译失败产生的.o文件最好先make clean再重新编译否则那些过期的中间文件会一直捣乱。6.2 数据格式错误导致的文件太小和乱结果很多人的第一步不是倒在编译上而是倒在数据准备上。最典型的报错是程序提示数据文件不够大可你明明生成了一个很大的文件。检查思路很简单先用文件大小反推比特数再看你的 n 和序列数是否匹配。二进制格式下文件字节数乘以 8 就是总比特数。比如一个 100 MB 的文件总比特数是 800,000,000。如果 n 取 1,000,000最多分成 800 个序列。如果你填的序列数超过这个数程序必然报错。ASCII 格式的坑更多。如果你用文本编辑器生成 0/1 文件保存时自动加了换行符或者 UTF-8 BOM程序读取时会把\n、\r、BOM 都当成数据。这会让序列长度计算错乱最后算出来的统计量毫无意义。我的建议是无论 ASCII 还是二进制生成后先做一个可视化检查看一眼文件开头和文件大小是否符合预期然后不管以后传输到哪里都要记录sha256确保数据在拷贝过程中没有被改动。NIST 程序对输入行数也有要求ASCII 模式下一个文件应该是一整串连续的 0/1 字符或者每一行是一个完整的待测序列。把序列切行和不切行混在一起也容易出问题所以规范一点就整成一整行或者每行等长。6.3 开始测试时不要盲目跑所有项的几条建议最后说几条实战建议。第一次跑测试环境验证时建议生成一份纯随机数据比如直接用/dev/urandom把全流程跑一遍。这样你能确认工具本身工作正常也熟悉了报告的格式。之后再拿自己的随机数源去测遇到异常时才有参照系。正式测试时不要一上来就跑全部 15 项也不要只选一两个测。我的做法是先用 Frequency、Block Frequency、Runs、FFT 这四个对常见缺陷最敏感的测试快速筛查一遍。如果这一关就有问题直接去查发生器和采集链路没必要浪费时间跑完所有项目。如果没问题再跑全部项目并加大样本量。样本量和测试时间是矛盾的。全部 15 个测试、100 个序列、每个序列 1,000,000 比特单机跑完可能要几十分钟到几个小时取决于你的 CPU 和内存。如果数据是几百吉比特的规模我通常会把文件按序列长度预先切好后台跑测试同时记录开始和结束时间避免中间被人误杀进程。还有一个小技巧assess默认是交互式程序不适合在脚本里无人值守跑。我的做法是用expect脚本与它交互或者写一个简单的管道输入提前把所有选项按顺序写入。比如printf 0\nrandom.bin\n100\n0\n | ./assess 1000000这样在 CI 环境里也能批量跑测试。NIST STS 这个工具说到底是一个帮你找茬的框架它不能证明你的随机源绝对随机只能帮你发现它有没有明显的结构性问题。实际项目里我会把 STS 结果和实采数据的波形、频谱、自相关图放在一起综合判断。工具也是会骗人的但如果你连工具的正确用法都还没掌握那就不是工具在骗你而是自己在骗自己了。