1. 知识点:为什么密码学需要"KAT 红线"
普通工程代码可以"看起来对 + 边界测试过"就发布。密码学不行:
- 错一位就是整体失效:哈希/加解密是雪崩式的,一个常量抄错,输出跟标准毫无相似度,但"看起来"很随机——靠人眼根本验不出来;
- 标准答案免费可得:GM/T 0004(SM3)等标准附录给了官方向量(如
SM3("abc") = 66c7f0f4…)。把这些向量写进自检,等于给实现装了"出厂校准"——先有答案,再写实现; - 红线要能隔离:理想的自检是"哪个原语错就哪个红",而不是一团 FAIL 让你猜——所以 KAT 要按原语分组跑。
引擎的源码注释(vllm_crypto.c4–5 行)把这条纪律写得很直白:位级正确性由独立测试驱动用国密标准 KAT 向量逐字节验证(red line)。文档进一步说明该驱动(vllm_crypto_test.c)未随仓库分发(权重保护与可验证推理方案 224 行)——本日我们自己搭最小 harness,把这条红线在板上立起来。
2. 对应代码:最小 KAT harness(crypto_kat.c)
对着vllm_crypto.h的公开 API,写一个零依赖 harness,验证四件事:
vc_sm3("abc", 3, d) → 与 66c7f0f4… 对拍(SM3 官方向量) vc_sm3("", 0, d) → 与 1ab21d83… 对拍(空串向量) vc_sm3 分块输入(3+61 字节喂 update)→ 与 "abcd"*16 的 64B 向量对拍(走填充+缓冲路径) vc_sm4_setkey_enc + crypt_block → 与标准单块向量 681edf34… 对拍 vc_sm2_sign/verify → 功能回环 + 篡改 r 后必须验不过编译方式(板端,与引擎同一份vllm_crypto.c):
gcc -O2 -I<repo>/include -I<repo>/include/common \ crypto_kat.c <repo>/src/common/vllm_crypto.c -o crypto_kat -lm3. 改动后果:板端 KAT 实测——红线为什么是红线
实测口径:RK3588 / aarch64 / 2026-09-07 / gcc -O2。
原始实现: [PASS] SM3("abc") 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0 [PASS] SM3("") 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b [PASS] SM3(64B "abcd"*16, 3+61) debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732 [PASS] SM4-ECB KAT (block) [PASS] SM2 sign/verify roundtrip [PASS] SM2 tamper rejected (r' != r) KAT: ALL PASS (red line green) 篡改后(把 SM3_IV[0] 的 0x7380166f 改成 0x7380166e,其余不动): [FAIL] SM3("abc") 7a3773dc0de682e8…(与标准无一位相同) [FAIL] SM3("") 1d6d2431… [FAIL] SM3(64B, 3+61) 8dfe48ba… [PASS] SM4-ECB KAT (block) ← 不受影响 [PASS] SM2 sign/verify roundtrip ← 不受影响 KAT: FAILURES PRESENT三个教训:
- 一个十六进制位 = 输出全变:
0x7380166f → 0x7380166e只是 IV 最低位 -1,三个 SM3 输出立刻与标准毫无相似度——这正是"人眼验不出来、KAT 一秒钟抓出来"的实证; - 红线能隔离:改的是 SM3 的 IV,SM4/SM2 的 PASS 不受牵连——如果有一天自检"全红",那是更严重的问题(可能构建链被污染);单条红,则精确指向那一个原语;
- 为什么驱动不随仓库分发也拦不住我们:标准向量是公开知识,任何拿到
vllm_crypto.c的人都能自己把红线立起来复验——源码开放 + 标准公开 = 可审计,这正是本仓库 AGPL 双许可叙事下"可验证"的技术底座。
4. 学员调试任务
A 档(板端/本地动手)
- 复刻本文:搭
crypto_kat.c编译运行,确认 6 行 PASS; - 依次单独篡改:a)
SM3_IV[0];b) 压缩常量0x79cc4519;c)P0的旋转位数——每次重编跑 KAT,记录"哪几行 FAIL、SM4/SM2 是否受影响",体会红线的隔离粒度; - 给 harness 加一个你自己的向量:
SM3("中国")的期望值先用另一个可信实现算好(python 有hashlib.new('sm3')的话),再让引擎实现跑,体会"先有答案再实现"。
B 档(纯读源码)
读vllm_crypto.c1–127 行,回答:
GET_U32_BE/PUT_U32_BE宏(22–28 行)在做什么,为什么 SM3 要大端取字?vc_sm3_update的"整块直压 + 余块缓冲"两条路径(90–103 行)分别处理什么情况?- 若把
vc_sm3_final里while (buflen != 56)的填充循环改成先算要补几个 0 再一次性 update,行为等价吗?为什么这样写更"防御"(提示:长度计数c->n的进位)?
预期输出:你自己的 KAT 输出(全绿)+ 至少一次"篡改→单原语 FAIL"的实验记录。
收尾
- 本篇源码点名:vllm_crypto.c(红线注释 4–5、IV 33–36)、vllm_crypto.h、权重保护与可验证推理方案.md
- 开源仓库:Kestrel-LLM (Gitee)(AGPL-3.0-or-later 或商业许可,二选一)
- 下篇预告:红线证明实现是对的,那 SM3 内部到底怎么把"abc"搅成 32 字节摘要的?23-2 逐段导读:填充、消息扩展 W/W'、64 轮压缩。