./research / verifiable-audit-layer

面向 AI 的可验证审计层.

面向模型推理与智能体执行的防篡改审计层:规范化序列化、按域分离的哈希、DSSE 签名、每次运行的 Merkle DAG,以及跨运行的 Merkle Mountain Range,用以证明没有任何一次运行被删除。每一个原语都是从与真实备选方案的正面基准测试中选出的,并附上已提交的原始数据。本文即其设计与背后的实验。

审计密码学基准测试shipped

威胁模型

我们假设一个控制存储、并能在事后重写记录的对手,包括生成这些记录的运营方本身。目标是:对已封存运行的任何改动都可被检测;记录在以年计的留存期内保持可验证;验证过程不暴露任何私有输入。全程始终区分两种属性:单次运行的完整性(integrity),以及运行集合的完备性(completeness)。每次运行的回执给出前者,对后者却只字未提,因此两者分别单独构建。

下面每一项选择都以同样的方式做出。我们并不断言某个原语最优;而是在同一台机器上将其与可信的备选方案对比,并提交原始数据,因此这里的任何数字都能用一条命令复现。计时取多次运行中最快的一次,先剔除预热,并保留均值与标准差,使不稳定的测量被标记而非被隐藏。这些数字来自 CPython 3.14 上的单台 AMD64 主机,作为比值有意义,而非绝对值。

实验与结果

每个里程碑都是与真实备选方案的正面测试,而非单次测量。总览如下;各自的详细结果见后续各节。

里程碑 对比对象 结果
规范化形式 朴素 JSON、CBOR JCS,逐字节一致,约 3x 成本
哈希 SHA-256、BLAKE3、keccak-256 keccak 原生 17x,字节相同
原生内核 pip 后端 vs Rust 封存 6x,根逐字节一致
签名 ECDSA、BLS、ML-DSA Ed25519,64 B,确定性
后量子 ML-DSA 44 / 65 / 87 分阶段,验证持平,38x 体积
每次运行轨迹 8 种伪造攻击,开销 全部捕获,约 121 us / 事件
跨运行日志 朴素 Merkle 重建 MMR,平坦追加,483x

规范化序列化

一个胶囊必须在我们的 Python 内核、TypeScript 客户端以及审计者自己的工具中哈希出相同的字节,否则一条有效记录就会验证失败。我们采用 RFC 8785(JSON Canonicalization Scheme),它固定了数字格式、按 UTF-16 码元的键排序,以及对非有限值的拒绝。我们将其与朴素按键排序的 JSON 以及 CBOR 做了对比。

方法 Python vs Node 成本
朴素排序 JSON 在 Unicode 与数字上分歧 基线
RFC 8785 JCS 逐字节一致 序列化约 3x
CBOR 二进制,需解码器方可阅读 紧凑

朴素 JSON 在 Unicode 排序与数字格式上于不同运行时之间产生分歧,因而会悄然破坏跨语言验证。JCS 在两者中逐字节一致,序列化时间约为 3x,每次哈希只付一次,相对于周围的哈希与网络开销可忽略。我们只保留其唯一一份实现。当我们的原生模块附带自己的 JSON 序列化器时,我们把它移除了,因为一个在普通输入上一致、却在对手可选择的边界情形上分歧的第二个规范化器,比没有更糟。

哈希与原生内核

叶子用 BLAKE3 哈希,Merkle 节点用 keccak-256,二者都采用 RFC 6962 的按域分离(为叶子与内部节点使用不同前缀),以封堵将内部节点冒充为叶子的第二原像一类。keccak-256 不是 SHA3-256;两者共用一个置换,却在一个填充字节上不同,而链上验证者说的是 keccak,因此一次悄然替换会让每个根在链上都失败。为此哈希层要么计算所请求的算法,要么抛出错误,绝不产出以假乱真者,并在每个胶囊中记录算法,使验证者复现确切函数,而非从本地已安装之物推断。

原语,64 B 输入 吞吐 备注
SHA-256(stdlib) 1.50M ops/s 基线,始终可用
BLAKE3(pip) 1.44M ops/s 与 SHA-256 持平
keccak-256(pip) 99k ops/s 慢 15x,瓶颈所在
keccak-256(Rust 内核) 1.69M ops/s 比 pip 快 17x

keccak 没有标准库实现,这正是原生模块值得构建的原因。差距在事件大小的输入上最大,因为 pycryptodome 在此处的成本是每次调用的对象分配而非哈希,恰是审计轨迹哈希得最多的尺寸;端到端上,原生内核把 1000 事件运行的封存从每秒 78 次提到 491 次,约 6x。关键在于:启用加速器不会改变结果:对同一次运行在有无该 crate 下封存,得到逐比特一致的根;一套一致性测试用算法作者发布的向量核对两条路径;CI 构建单个稳定 ABI(abi3)的 wheel,并在从 3.10 到 3.14 的每个 Python 上验证这唯一产物。安装它只改变速度,别无其他。

面向十年记录的签名

审计记录上的签名,必须在该记录可被争议的整个期间内都成立,而 AI 的日志留存义务如今正逼近十年。若方案在记录仍处于留存窗口时被攻破,对手便可伪造并回填日期,于是整个归档一并失效。我们在吞吐与体积上,将 Ed25519 与 ECDSA、BLS 以及 NIST 后量子家族 ML-DSA 做了基准对比。

方案 验证(ops/s) 签名体积
ECDSA P-256 16,887 ~72 B
ML-DSA-44(后量子) 10,725 2,420 B
Ed25519 10,375 64 B
BLS12-381 仅参考实现 96 B,可聚合

有两个结果推翻了我们原本带着的假设,两者我们都记录在案。Ed25519 并非验证最快者:ECDSA P-256 在优化过的库汇编上测得约快 1.6x。Ed25519 仍是默认,凭的是签名体积与确定性(RFC 8032),后者消除了 ECDSA 的 nonce 复用故障——该故障会在签名仍能验证之际泄露私钥。而后量子签名验证并不慢:ML-DSA-44 略胜 Ed25519,其代价在签名(约慢 13x)与体积(约 38x 字节)。由于一条轨迹只签一次、却要验证数年,昂贵的那一侧正是我们做得最少的一侧。BLS 是因一项需求而非因某个数字被弃:聚合把众多签名压成 96 字节,但聚合体只能全有或全无地验证,并需要每一把密钥与每一条消息,这与「披露某个决策的一条分支而不泄露其余」不相容。

真正重要的重新框定:签名并不保密,因此「先收割、后解密」并不适用,真正的威胁是留存窗口内的追溯性伪造。将根锚定到公共账本,可独立于签名方案证明记录何时存在,于是一把被攻破的密钥所失去的,是证明封存了记录的能力,而非何时何物。因此锚定处于关键路径上,ML-DSA 在其后分阶段推进,DSSE 信封保持算法敏捷,算法逐胶囊记录。

每次运行的审计轨迹

每次运行都是一条仅追加、以哈希链接的事件序列(LLM 调用、工具调用、检索、子智能体派生、人工批准、护栏决策),其上有覆盖全部事件的 Merkle 根,以及对该根的 DSSE 签名。只存储输入与输出的哈希,从不存储载荷,因此该轨迹默认保护隐私。验证者不只返回有效或无效;它会指名被破坏的那条保证。

操作(102 事件运行) 成本 伸缩
记录一个事件 ~121 us 平坦,低于一步的 0.01%
封存,构建根 0.15 ms 随事件线性
验证,重算所有叶子 9.2 ms 线性,约为封存的 60x

记录不到一次典型智能体步骤的百分之一的百分之一,而该步骤由模型往返主导,因此开销不构成抽样的理由,轨迹保持完整而非统计式。验证远比封存沉重,因为它从内容重算每一片叶子,而封存只在已有的哈希之上构建树。这种不对称对审计轨迹是正确的:记录恒定、验证罕见;它也标出批量再验证正是原生内核与并行接下来见效之处。为把保证落到实处,一个演示以八种方式攻击一次已封存的运行,每一种都以具体理由被拒:

攻击 被拒为
重写一个模型输出 叶哈希不匹配
删除或插入一个事件 事件计数不匹配
重排事件 链断裂
截断该运行 链首不匹配
降级哈希算法 叶哈希不匹配
用错误的密钥呈交 签名无法验证

跨运行的透明日志

一个有效的单次运行胶囊,无法证明运行集合是完备的。一个丢弃某次不便运行的运营方,手握一堆各自完美的胶囊,以及一段有洞的历史。我们以单个仅追加的胶囊根日志来封堵,它支持包含证明(某次运行在日志中)与一致性证明(较早规模的日志是当前日志的确切前缀)。一致性可捕获审查:一个删去某运行后重建的日志,其内部结构良好,每个幸存胶囊也仍能验证,但它在与此前发布之根的一致性核对中失败,同一核对也拒绝回填日期的插入。这只在对抗一个于篡改之前就已提交的根时才成立,因此那个根必须驻留在运营方无法修订之处,例如一个公共锚点。

我们采用 Merkle Mountain Range 而非朴素 Merkle 树,因为一次追加只添加节点、从不重写任何节点,这让日志得以驻留在一次写入或对象存储上,并在日志增长时保持旧的包含证明有效。该性质近乎最优,而不只是方便:ePrint 2025/234 证明任何简洁的仅追加承诺都必然引发超线性数量的见证更新,而一个相近的 MMR 变体基本达到了该界。

日志规模 MMR 构建 朴素重建
500 1.3 ms 158 ms(120x)
1,000 2.6 ms 618 ms(237x)
2,000 5.2 ms 2,491 ms(483x)

随日志增长,追加保持平坦(在 100 与 10,000 条目下均约每秒 390k 次追加),而重建一棵朴素树在同一区间内从每秒 8,176 个根跌至 84 个——这正是二次方对拟线性的特征。存储稳定在每条目 2.00 个已存哈希,包含证明在 100 至 100,000 条目之间仅从 7 步增至 17 步。我们输在哪里:我们的一致性证明为 O(log^2 n),为旧日志的每个峰各带一条路径,而非可达的 O(log n),在 100,000 条目时多出几千字节——这是为「一个审阅者读代码即可核对的构造」而有意做出的取舍。

我们不越过的界限

哈希给出的是篡改可证(tamper evidence),而非篡改不能(tamper proofing)。一个重建某运行并重算其根的对手,会产出一份自洽的记录,因为 Merkle 根证明的是事件与根相符,而非该根就是被发布的那个。签名捕获这一点,公共锚点证明时间。透明日志在大处依赖同一假设:它只能相对于一个在删除之前就已提交的根,才检测到某运行被删除。我们在产品及其演示中申明这些边界,并有一项测试断言伪造演示会持续展示那个我们并不阻止的攻击,使这一诚实情形无法悄然消失。

可复现性

这里的每个数字都来自一个已提交的基准,用一条命令即可重跑,与测试、跨语言与跨实现的一致性测试套件,以及在有无原生内核下都会演练 Python 3.10 至 3.14 的 CI 矩阵并列。方法论、原始 JSON 以及决策笔记(含每项选择落败的维度)都在仓库中,因为一个让 AI 可审计的层,自己也必须可审计。