如何用 FIPS 140 快照校验 Go crypto 标准库?GOFIPS140 测试流程与 fips140.sum 校验
【免费下载链接】goThe Go programming language项目地址: https://gitcode.com/GitHub_Trending/go/go
如果你手上有一份 Go 源码树(带lib/fips140目录),需要回答两个问题:某个 FIPS 140 快照替换crypto/internal/fips140后,整组 crypto 标准库测试能否通过;以及lib/fips140里的快照 zip 文件是否被篡改过。前者通过GOFIPS140构建设置驱动go test完成,后者由fips140.sum记录的 SHA-256 校验和来保证。
FIPS 140 快照与 GOFIPS140 的取值规则
lib/fips140目录存放crypto/internal/fips140树的快照,这些快照正处在 FIPS-140 验证与认证流程中。lib/fips140/README.md 说明了目录内各文件的分工:
x.txt(如inprocess.txt、certified.txt)定义 FIPS 版本别名x,文件内容就是它实际指向的精确版本号;- zip 文件是快照本体,由 src/cmd/go/internal/fips140/mkzip.go 生成;
- lib/fips140/fips140.sum 列出各 zip 的校验和,具体配方见 lib/fips140/Makefile。
当前源码树中两个别名分别指向:lib/fips140/inprocess.txt 内容为v1.26.0,lib/fips140/certified.txt 内容为v1.0.0-c2097c7c。
GOFIPS140构建设置控制两件事(见 src/cmd/go/internal/fips140/fips140.go 的包注释):
- 编译出的二进制是否默认以 FIPS-140 模式运行,即是否默认
GODEBUG=fips140=on; - 构建使用哪一份
crypto/internal/fips140源码:GOFIPS140为off/空 时行为不变;latest使用GOROOT/src/crypto/internal/fips140;其他取值则必须是lib/fips140中存在的快照版本(直接给版本号,或给一个.txt别名)。
使用快照时,构建系统会做两件替换:快照包通过虚拟文件系统覆盖crypto/internal/fips140,使std、crypto...这类包模式自动看到快照包;同时ResolveImport把形如crypto/internal/fips140/sha256的导入路径改写成crypto/internal/fips140/v1.2.3/sha256,定位到解包后的真实目录,保证go list -json等工具能找到源码。此外还会追加一个由版本号主次要部分推导的fips140构建标签。
GOFIPS140取值不合法时,go 命令直接报错退出,常见的有:
- 取值含
/、\或..:go: malformed GOFIPS140 version %q - 缺少
GOROOT:go: missing GOROOT for GOFIPS140 - 既不是已知版本也没有对应 zip:
go: unknown GOFIPS140 version %q - 与
GOEXPERIMENT=boringcrypto或purego构建标签同时使用:分别报go: cannot use GOFIPS140 with GOEXPERIMENT=boringcrypto、go: cannot use GOFIPS140 with the purego build tag
用 GOFIPS140 运行 crypto 测试
lib/fips140/Makefile 提供了针对某个快照跑 crypto 测试的配方,以v1.26.0为例:
# 在 Go 源码树的 lib/fips140 目录下执行 make v1.26.0.test该目标展开为:
GOFIPS140=v1.26.0 go test -short crypto...也可以不用 make,直接执行上面这条命令;把v1.26.0换成inprocess、certified这类别名同样有效,go 命令会先读对应的.txt文件解析出真实版本号。如果crypto...全部通过、命令正常退出,即说明该快照通过了 crypto 测试流程。
执行过程中 go 命令内部会做如下动作(来自 fips140.go 的实现):
- 解析
GOFIPS140取值,若是别名则读取lib/fips140/<别名>.txt得到版本号,并要求对应的<版本>.zip存在; - 若该快照尚未解包进模块缓存(模块路径为
golang.org/fips140@<版本>),先按 FIPS 140-3 Security Policy 的要求把 zip 的 SHA-256 与fips140.sum核对一次,再解包; - 用解包出的
fips140子目录作为crypto/internal/fips140的源码位置继续构建。
校验失败时你会看到go: verifying GOFIPS140=<版本>: <错误>形式的报错,其中错误文本由verifyZipSum给出(见下一节)。解包本身失败则报go: unpacking GOFIPS140=<版本>: <错误>。
fips140.sum 校验:自动核对与主动检查
lib/fips140/fips140.sum 当前内容为:
# SHA256 checksums of snapshot zip files in this directory. # These checksums are included in the FIPS security policy # (validation instructions sent to the lab) and MUST NOT CHANGE. ... v1.0.0-c2097c7c.zip daf3614e0406f67ae6323c902db3f953a1effb199142362a039e7526dfb9368b v1.26.0.zip 9b28f847fdf1db4a36cb2b2f8ec09443c039383f085630a03ecfaddf6db7ea23文件头注释强调:这些校验和被包含在送交认证实验室的 FIPS 安全策略中,不允许改动,也就是 zip 文件本身一旦发布就不能再变;允许向列表新增条目,也允许在 zip 从目录中移除时删掉对应条目。
主动核对整份fips140.sum的命令是:
go test cmd/go/internal/fips140对应 src/cmd/go/internal/fips140/fips_test.go 中的TestSums:它对lib/fips140下每个*.zip重新计算 SHA-256,与fips140.sum中已登记的行比对。两种失败判定:
- 某 zip 已登记但哈希变了:报
policy violation: zip file hash is changing,同时给出新旧两行 diff; - sum 文件与目录内容不同步(缺行、多余行或哈希不符):报
GOROOT/lib/fips140/fips140.sum out of date. changes needed:并附上+/-形式的差异。
单文件层面的核对逻辑是verifyZipSum(fips140.go):逐行解析 sum 文件(#开头的行和空行跳过),取与 zip 同名条目期望的哈希,重新计算 zip 的 SHA-256 后比较。条目缺失时报no SHA-256 hash for <name> in <sumfile>,哈希不符时报SHA-256 hash of <name> is <实际值>, want <期望值> (from <sumfile>)。fips140.sum文件不存在时校验同样失败。
当 zip 定稿后需要登记或修正条目时,运行:
go test cmd/go/internal/fips140 -update或等价地make updatesum。-update会把重新计算的哈希写回fips140.sum(注释行保留、缺失行按名称排序追加),并打印变更 diff。注意这与上一条不矛盾:-update用于「发布新 zip 后登记新条目」或「zip 从目录移除后清理条目」,而不是修改已发布 zip 对应的既有哈希——TestSums会明确把已登记 zip 的哈希变化判定为 policy violation。
生成或重建快照 zip(可选分支)
如果需要从一个分支制作新的快照 zip,Makefile 的配方是:
# 在 lib/fips140 目录下,v1.2.3 替换为目标版本号 make v1.2.3.zip它先执行git fetch origin master,再运行:
go run ../../src/cmd/go/internal/fips140/mkzip.go v1.2.3mkzip.go 的约束:必须在lib/fips140目录下运行;版本号必须是合法的vX.Y.Z;默认从origin/master取源码(避免误带本地提交),可用-b <branch>指定其他分支;同名 zip 已存在时拒绝覆盖。
Makefile 注释给出的完整迭代流程是:make v1.2.3.zip生成,make v1.2.3.test测试,需要修改就把改动合入 master 后运行make v1.2.3.rm再重来;zip 定稿后运行make updatesum登记哈希。
make v1.2.3.rm有明确的删除副作用,执行前确认版本号:它会删除lib/fips140/v1.2.3.zip本身,并把模块缓存中的对应目录一并清掉(先chmod -R u+w解除只读再rm -rf):
rm -f v1.2.3.zip chmod -R u+w $$(go env GOMODCACHE)/golang.org/fips140@v1.2.3 2>/dev/null || true rm -rf $$(go env GOMODCACHE)/golang.org/fips140@v1.2.3只影响该版本对应的 zip 与模块缓存副本,不动其他版本的快照;模块缓存目录路径取决于go env GOMODCACHE的输出。
结果判定与限制汇总
按上面的顺序操作后,判定标准是:
GOFIPS140=<版本> go test -short crypto...正常退出且 crypto 各包测试通过——快照通过了 crypto 测试流程;go test cmd/go/internal/fips140通过——fips140.sum与lib/fips140中的 zip 一致,且没有任何已登记哈希发生变化;- 若 go 命令在解析
GOFIPS140时打印malformed/unknown version/missing GOROOT/verifying任一类错误,按前面各节给出的错误文本定位:前者是取值或目录问题,后者是 zip 与fips140.sum不匹配。
两条硬限制值得记住:已发布的快照 zip 永远不应被修改,fips140.sum中已登记的哈希因此也不允许变化;GOFIPS140不能与GOEXPERIMENT=boringcrypto或purego构建标签同时使用。另外,包注释还提到一个可用于核对构建产物的现象:GOFIPS140=latest go build -work my/binary会在$WORK/b001留下fips.o(构建未命中缓存时),它是链接阶段为启动时重新哈希 FIPS 代码而保存的副本。
【免费下载链接】goThe Go programming language项目地址: https://gitcode.com/GitHub_Trending/go/go
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考