arXiv 2606.15822 · core reproduction

TrustedARI
一个诚实中继如何让 Agent 安全地调用付费 API

Agent A 想通过问责中继 R 调用服务 S:查询内容要对 R 保密、 R 不能把请求错路由到别的服务、S 返回的计费数字 A 不能低报。 三方用 2PC 共同模拟出一个"虚拟 TLS 客户端"——S 完全无感。

🤖
Agent A
持有查询意图与 tk_sapp;
验证证书防 misrouting
A⇄R
OT 通道
📡
ARI Relay R
只转发密文;
持有密钥份额而非密钥
R⇄S
标准 TLS
🛰️
Provider S
普通 TLS 1.3 服务;
不知道 A/R 的存在
A 与 R 各持一半会话密钥;任何一方都无法单独读写通道内容。

Three questions

一句话版:为什么它安全

01

为什么中转 R 看不到明文?

因为密钥根本不存在于任何一方手里——应用密钥被拆成 tk = tkA ⊕ tkR 两份份额, R 拿着密文加半个份额等于拿一张白纸;每次解密都必须双方在 2PC 电路里共同完成, GMW+OT 保证交互本身不泄露任何份额。连握手期的 Server Flight 都只用 tkshs 在 A 本地解密——R 从始至终只转发看不懂的字节。

02

为什么没法偷换路由?

因为验证书的权力没有下放给 R。证书、CertificateVerify、ServerFinished 由 A 在本地解密验证:X.509 证书绑定服务域名,签名密钥只有真正的 S 才有。 R 若把流量转去 api2.evil.com,A 端立刻看到域名错配的证书 → 握手中止。 "路由完整性"退化成一句 A 端证书的域名校验,R 对"它是谁"没有裁量权。

03

为什么没法注水 token?

因为计费字段不是谁申报就是多少。响应是 S 用 TLS 加密的——R 或 A 改一个字节,GCM tag 立刻作废;而 A 自报的数字 R 也不信:双方把响应密文的 一个窗口在 2PC 里解密成份额,电路内逐字节比对 pattern、用 JSON FSM 校验字段所在深度,再用 comA = H(tkA∥r) 钉死 A 的密钥份额防止换把"好说话"的密钥。任何一项对不上 → accept=0, 且 R 全程只学到一个比特。

Baselines

为什么以前的方法做不到

对"隐私(R 看不到明文)× 计费(A/R 报多少可验)"两个目标,已有方案各占一头—— 缺的不是 HTTPS,而是"密钥无人独占"的结构:

A S TLS 计费 = A 自报 ✗

直连 A→S(无中转)

隐私天然成立,但没有审计方——token 用量只能信 A 自报,ARI 模型不成立。

✗ 计费不可验

A R S TLS₁ TLS₂ 🔓 握有完整密钥 能解密 → 明文可见 · 计费仍自报

TLS 终结代理(网关 / CDN)

R 就是连接的一端,完整密钥在手——能解就能看;计费还是它自己说。

✗ 隐私失败

A R S 只见密文 只按字节/时长计费 · 字段不可验

纯转发隧道

TLS 直接穿过 R:明文保住了,但字段级用量(如 total_tokens)无从核验。

✗ 计费失败

A enclave S ⚠ 信任 Intel + 无侧信道 信任转移而非消除

TEE enclave(SGX 代理)

隐私与计费都成立——但前提是相信芯片厂商、远程证明和没有侧信道漏洞。

△ 额外硬件信任假设

A V S 2PC 查询模板对 verifier 可见 面向 provenance · 无边界隐藏/计费电路

DECO 类三方握手(zkTLS)

同样拆密钥,但目标是"证明数据来自某站":verifier 参与构造查询, 没有 SHC/LDC 边界隐藏,也没有字段级计费电路。

△ 查询半公开 · 不做计费

A R S 2PC · OT TLS tk = tk_A ⊕ tk_R 密钥无人独占 ✓ 隐私 ✓ 计费可验 ✓ S 零改动

TrustedARI(本工作)

密钥以份额存在于 A、R 双方:电路内可验、电路外不可读, 不需要 TEE,S 完全无感。

✓ 两个目标同时成立

注:TEE 在其安全假设下确实可行,区别是信任转移而非消除; DECO 的"△"指查询对 verifier 只能有限隐藏——它保护的是来源,不是查询内容。

Phase 1 · Π_ths handshake

三方握手:密钥拆成两半,证书仍由 Agent 验

R 本质上是"半个客户端":ECDHE 秘密拆成 ZA+ZR, HKDF 全链在 2PC 中执行,R 只拿到 tk_chs/tk_shs 的份额。 服务端 flight(证书+CV+SF)只在 A 处解密——R 无法伪造服务商身份。

A → R → S
ClientHello:r_i → Z_i,CKS = Z_A + Z_R
双方各自贡献 ephemeral share,合成压缩点作为 key_share
A ⇄ R · 2PC
ECtF 点加 → DHE 份额 → HKDF 链
ssk_i = Yr_i,点加只取 x 坐标;fld→bits 后 Extract/Expand 出 CHTS/SHTS/dHS
S → R → A
加密的 Server Flight(EE/CRT/CV/SF)
A 重建 tk_shs 解密:验证书链、域名、CertificateVerify、ServerFinished
A ⇄ R · 2PC
MS → CATS/SATS → tk_capp/tk_sapp;A 提交 com_A = H(tk_A_sapp ∥ r)
tk_sapp 份额留给 R 用于计费核验;iv 公开
A → R → S
Client Finished(tk_chs 加密)
CF 由 A 在本地组装,经 R 转发——S 收到标准记录,握手完成
✗ misrouting:R 把流量转到 api2.evil.com → A 端证书名校验失败,会话中止(已测试)。

Phase 2 · Π_query

模板查询:连字段边界都对 R 隐藏

查询由模板拼装:公共段 P、定长私有段 Af/Rf变长私有段 As/Rs(真实长度 g 都是份额)。 段间用 SHC(Structure-Hiding Concat)抹平边界,最后经 2PC-AEAD 加密。

P 公共模板 Af 定长·A 私有 As 变长·长度也是秘密 Π_I2S 生成的 Content-Length

Π_LDC

公共长度拼接直接本地完成——零交互、零成本。

Π_SHC(Protocol 1)

va=sl‖0hr,v0=0hl‖sr; 对右段做 blind_rotate_left 后异或。 成本 (hl+hr)·⌈log₂⌉,装配顺序由 DP 规划 Γ 最小化。

Π_2PC-AEAD

整条查询以 tk_capp = tk_A ⊕ tk_R 加密;密文+tag 双开, R 转发的是一条完全合法的 TLS 记录。

Phase 3 · verifiable billing

可验证计费:电路里跑一个 JSON 解析器

A 向 R 汇报被计费字段 φ:v,R 不信——双方在 2PC 里把 响应密文的一个窗口解密成明文份额,原地跑 JSON FSM。

边界窗口 ω=(o,ℓ,ρ)

前缀窗口从 JSON 起点正向解析,后缀窗口从尾部反向解析, 取较短者——大响应里只解密目标附近的几块。

串/转义/深度状态机

e_i=(1−e)·b·κ92  b_i=b⊕κ34·(1−e)
d_i=d+(1−b)(κ123−κ125) —— 字符串里的 { }\" 不计深度。

承诺重检

comA ?= H(tkA,sapp∥r) —— A 无法换一把 "更好说话"的密钥份额来解释密文。

单比特输出

accept = com_ok ∧ bytes_ok ∧ depth_ok,
R 只学到 accept——响应明文始终不可见。

Live run

端到端实际输出

python3 demo.py

  

5 套测试全绿:引擎 / 电路 / 握手(含 misrouting 拒绝)/ 查询 / 计费(含 4 类攻击拒绝); 计费电路在真实 OT 后端下同样通过(1-block 窗口 ≈ 16 s、A↔R 各 ~4.3 MB)。

Threats covered

威胁模型覆盖

攻击者行为结果
R把流量错路由到其他服务 ✗ A 端证书名校验失败
R读取查询 / 响应明文 ✓ 只有密钥份额,通道为密文
A低报计费字段(如 42→00) ✗ bytes_ok 失配
A声称字段位于错误深度 ✗ depth_ok 失配
A更换密钥份额伪造解释 ✗ com_A = H(tk_A∥r) 失配
R篡改自身 tk_sapp 份额 ✗ 解密出的明文对不上字段