ccAI 精读(二):不换 GPU 也能机密计算——Adaptor 与 PCIe-SC

接上一篇《ccAI 精读(一):为什么 GPU 跑 AI 需要保密?》:CPU 侧用 TVM 保护好了,但数据一上 PCIe 就是明文。ccAI 的答案是——在 CPU 和 xPU 之间的 PCIe 上插一个门卫,再在软件侧配一个翻译员,让老 GPU 也能机密计算。

本篇讲清四个组件怎么配合:Adaptor(兼容)、Packet Filter(决策)、Packet Handler(执行)、HRoT-Blade(信任),最后落到 §7 作者真做出来的原型。

🗺️ 总架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  Remote User
│ encrypted workload
↓
┌──────────────────────────────┐
│ TVM │ ← 软件侧、受保护虚拟机(并列装三样组件)
│ │
│ 1. xPU Native Software Stack │ ← xPU 原生软件栈(App/Driver/Libs,不改)
│ 2. Adaptor │ ← 新增层:与 PCIe-SC 交互、识别并加密数据
│ 3. TVM Trust Modules │ ← ccAI 部署:密钥管理、远程证明
└──────────────────────────────┘
│ PCIe
↓
┌──────────────────────────────┐
│ PCIe-SC │ ← 硬件,插在服务器 PCIe 端口
│ │
│ 1. Packet Filter │ ← L1/L2 决策 A1~A4
│ 2. Packet Handlers │ ← AES 加密与完整性
│ 3. HRoT-Blade │ ← 信任控制面(Secure Boot / 远程证明 / 密钥),不处理数据包
└──────────────────────────────┘
│ Internal PCIe ← xPU 连到 PCIe-SC 上
↓
GPU / NPU
组件 角色 解决什么
Adaptor 🧑‍💼 翻译员/协调员 G1 兼容性:让老软件不用改
Packet Filter 🚧 门卫 判断每个 packet 该 A1/A2/A3/A4
Packet Handler 🔐 执行员 真的做加解密、完整性检查
HRoT-Blade 🔑 信任中心 Secure Boot / 远程证明 / 密钥交换

职责分工:Adaptor 解决怎么兼容原来的 xPU 软件,PCIe-SC 解决怎么保护 PCIe 上的数据。

下面沿着一个 packet 的旅程走一遍。

🚧 第一站:Packet Filter——这个包该怎么处理?

为什么不能全都加密?

直觉方案是把 PCIe 流量全部加密。但 PCIe 上跑的东西重要程度完全不同:

  • AI 模型参数、输入数据 → 高度机密
  • 中断、普通状态寄存器读取 → 不敏感
  • 恶意/未知设备的请求 → 压根不该出现

全都加密既浪费性能,也管不住不该出现的包。所以论文走细粒度安全(fine-grained security)路线:按 packet 属性分类处置。

四种安全动作 A1~A4 ⭐

Action 全称含义 做什么 类比
A1 Prohibited 禁止,直接丢弃 🚫 不让进
A2 Write-Read Protected 加密/解密 + 完整性检查 🔐🛡️ 特级安检
A3 Write Protected 完整性检查 + 安全校验(如核对 xPU 页表寄存器) 🛡️ 普通安检
A4 Full Accessible 直接通过 ➡️ 免检

安全性从高到低:A2 > A3 > A4。安全性越高,性能代价越大,所以才要分级,而不是一刀切全加密。

Filter 看三个属性

1
2
3
① Packet Type            这个包是干嘛的?Memory Read / Write / Interrupt / MMIO / DMA
② Interacting Parties 谁和谁在通信?合法 TVM ↔ xPU?还是未知设备 → xPU?
③ Address Space Sensitivity 访问的地址是不是敏感区域?(如模型参数所在内存)

同样是 Memory Write,发往敏感区域就该 A2,发往普通区域只要 A3;未知设备发来的,A1 直接丢。

L1 / L2 两级表:先挡坏人,再给好人分类

一个包进来,先过两道门:

1
2
3
4
5
6
7
8
9
10
11
12
13
  PCIe Packet
↓
┌────────────┐
│ L1 Table │ ← 你有资格进来吗?(查 requester 是否授权)
└──────┬─────┘
├──────→ A1 DROP ← 未授权:禁止,直接丢
↓
┌────────────┐
│ L2 Table │ ← 该享受什么待遇?(查 type + 地址是否敏感)
└──────┬─────┘
├──────→ A3 安全校验 / A4 直接放行
↓
A2 ← 敏感:加密 + 完整性

L1 负责挡坏人(不授权 → A1),L2 负责给好人分类(按敏感度给 A2/A3/A4)。

为了让规则不至于为每种 xPU 写爆,还引入了 Mask:一条规则可以声明它只关心哪几个字段,其余字段忽略。

规则本身也要保护

攻击者若把敏感地址的处理从 A2 改成 A4,保护就全失效了——规则表本身也是攻击面。那么问题来了:规则由谁来定、怎么下发、以什么形式存放?这里藏着三个容易想岔的疑问:

Q1:策略是写死在代码里的吗?为什么能让用户下发?

不是。策略不是代码,而是数据——L1/L2 表里的一条条规则(32 字节一条),内容是每类包该怎么处理。就像防火墙规则可以热更新:管理员下发规则,根本不需要防火墙的源码。

谁来定规则?负载的主人(用户)。因为只有他知道:这段内存装的是模型权重(要 A2 加密),那段只是普通 buffer(A3 就够)。Adaptor 里写死的只是下发规则的通道(pkt_filter_manage),规则内容永远是动态的。这也正是 ccAI 兼容新 xPU 的底气:厂商想给私有管理包加规则,直接通过 MMIO 寄存器加一条就行,不用重写固件。

用户定规则、Adaptor 搬运规则、Packet Filter 按规则分级

角色 职责 类比
用户(负载所有者) 制定规则内容:哪些包/地址敏感、该怎么处理 🏛️ 立法者
Adaptor 把用户给好的规则装载进 PCIe-SC 的 L1/L2 表 📋 录入员
Packet Filter 每个包查 L1/L2 表,命中哪条就给哪个动作(A1~A4) 👮 执法者

所以前文说的 Filter 分级 A1~A4 是执行视角(查表贴标签),这里的用户下发是制定视角(规则从哪来)——两句话不矛盾:分级的依据,正是用户预先放进表里的那些规则。另外注意,A1~A4 这四档动作是论文设计好的固定分类,用户能定的是每个包落到哪一档,不能发明第五档。

Q2:Adaptor 是云厂商的闭源模块,用户凭什么信它?

凭度量,不凭源码。云里的 Adaptor(约 2.1K 行)和 PCIe-SC 固件本来就是云厂商的闭源二进制,用户拿不到、也不该拿到源码——机密计算里,平台代码可信不可信不靠读过源码,而靠它被量测过、结果可远程证明(机制见下文第四站)。

所以真正要紧的不是开源,而是够小、可审计:论文把 CPU 侧新增的 TCB 压到约 3.1K 行(Adaptor 2.1K + Trust Modules 1.0K),越小越容易审清楚。

Q3:规则为什么要加密保存、用的时候才解密?

因为保存规则的 configuration space 在 PCIe-SC 上,是总线可达的区域,正落在威胁模型里(攻击者能 snoop / tamper PCIe)。明文放在那里有两个洞:

  • 泄露:攻击者读到规则,等于拿到你工作负载的资产地图——哪些地址区间被当敏感数据处理;
  • 注入:攻击者把自己的包改成 A4 直通、把别人的包标成 A1 丢弃,防护形同虚设。

所以规则落盘是密文,装载进 L1/L2 表时才用密钥解开——密钥是远程证明后由 HRoT-Blade / 信任模块颁发的。说到底:门禁名单也得加密保存,不然小偷先看名单,再挑没监控的门。

完整例子

TVM-1 向 xPU 写一段模型参数(地址落在敏感区 0x2000~0x3000):

  1. L1:Requester = TVM-1,是授权方 → ✅ 放行
  2. L2:Type = Memory Write + 地址敏感 → A2
  3. 转交 Packet Handler 执行

反过来,未知设备发同样的包:L1 就不授权 → A1,直接丢,根本走不到加密环节。

🔐 第二站:Packet Handler——真正把安全做出来

Filter 只是做决定,真正动手的是 Handler:加解密、安全检查、完整性验证。

为什么光加密不够?

加密解决 Confidentiality(别人看不懂),但不解决 Integrity(别人改不了)。攻击者看不懂密文,却可能篡改它,让 GPU 拿到被改过的数据。所以 A2 要同时上两道:

1
2
Confidentiality  → 加密(AES)
Integrity → Authentication Tag(认证标签)

Authentication Tag 可以理解成防篡改封条:发送前对数据算出一个 tag(如 ABC123)跟着数据走;接收方拿到数据重算 tag,不一致就说明被动过手脚。

一个 packet 是怎么被处理的?

Handler 不会把整包囫囵处理,而是把 Header 和 Payload 分开:

1
2
3
4
5
6
7
8
9
10
11
12
PCIe Packet
├── Header ──────→ Header Queue(原样走,不动)
└── Payload ─────→ Payload Queue
↓
Security Engine
┌─────┴─────┐
↓ ↓
AES-SHA Authentication
Engine Tag Manager
└─────┬─────┘
↓
重新组合 Packet

A2 的完整五步——下面以 D2H 方向(xPU 算完、把结果返回 TVM)为例:此时数据在 PCIe-SC 的 Handler 里才真正被加密(AES 引擎双向工作,H2D 任务下发方向则由 TVM 侧 Adaptor 加密,见下一站):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1. xPU 结果包到达 PCIe-SC
┌────────┬──────────────┐
│ Header │ Result Data │
└────────┴──────────────┘
↓
2. Filter 判断:敏感结果数据 → A2
↓
3. 拆包:Header → Header Queue(原样走);Payload → Payload Queue
↓
4. 处理 Payload:AES 加密 → Encrypted Data
Integrity 计算 → Authentication Tag
↓
5. 重组发出
┌────────┬────────────────┬────────────┐
│ Header │ Encrypted Data │ Auth Tag │
└────────┴────────────────┴────────────┘
↓ PCIe
TVM

任务下发(H2D)方向是镜像流程:payload 在 TVM 侧已被 Adaptor 加密后才上 PCIe,PCIe-SC 的 Handler 收到后负责解密和完整性校验,再以明文交给 xPU——Adaptor 这端留到下一站讲。

为什么这套能兼容各种 xPU?

不同 xPU 的 DMA/MMIO 实现各不相同,但它们的流量最终都是标准格式的 PCIe packet。Filter 只认 packet 属性、Handler 只动 header/payload,跟发起方是 NVIDIA 还是某 NPU 无关。这就是兼容性的技术根源。

🧑‍💼 第三站:Adaptor——为什么不用改驱动?

你可能会问:PCIe-SC 都能拦包加密了,还要 Adaptor 干嘛?

关键区别:PCIe-SC 只看得见 PCIe packet,看不懂 AI 任务。它看到一个包访问 0x12340000,但不知道这块内存是模型参数、输入数据还是临时 buffer。而 Adaptor 住在 TVM 里,知道当前任务的数据在哪、哪些敏感。所以:

1
2
3
Adaptor(软件语义):这个任务要保护哪些数据 → 准备数据
↓
PCIe-SC(通信安全):这个 packet 该怎么处理 → 加密/完整性/放行

Adaptor 干两件事:

  1. 通过 MMIO 与 PCIe-SC 交互:查询任务 metadata(数据地址、大小);装载安全规则——注意规则内容由用户授权(见上文 Q1),Adaptor 只负责把它们写进 PCIe-SC 的 L1/L2 表。
  2. 处理 xPU task:找到敏感数据 → 加密 → 放进 bounce buffer → 传给 xPU。

bounce buffer = 加密后再发货的临时中转仓库:敏感数据不直接上 PCIe,先被 Adaptor 加密到一块安全装货区,PCIe 上跑的已经是密文。

这样做的最大收益是不动老软件:不是去改 AI App、Runtime、Driver,而是在旁边新增一个兼容层。论文原型里新增的是一个叫 ccAI_adaptor 的 kernel module——原生 xPU driver 一个字节都不改。

换一种 xPU 怎么办?比如从 NVIDIA A100 换成某个 NPU:不需要重设计 PCIe-SC,只需给 Adaptor 更新 software-based kernel patches(经 secure boot 校验后激活),就能支持新的内存分配方式——这就是论文说的 compatible system updates。

🔑 第四站:Trust Establishment——凭什么信这台机器?

前面解决的是数据怎么保护,但还有个更根本的问题:

你怎么知道这台 PCIe-SC 里跑的还是 ccAI 的代码,而不是被攻击者换成了会偷密钥再加密的木马版?

如果门卫本身不可信,加密就是给贼上锁。§6 用三步建立信任:

1
2
3
4
5
Secure Boot(先确认机器没被换)
↓
Remote Attestation(再向远程用户证明)
↓
Key Exchange(最后才把密钥交给它)

Secure Boot + HRoT-Blade

CPU 侧靠 TDX module 这类 TEE 隔离保护 TVM(并有 CPU 平台自带的硬件信任根 HRoT 支撑启动量测),但 PCIe-SC 是另一块独立硬件,CPU 的信任链管不到它,所以论文给 PCIe-SC 配了自己的信任根:HRoT-Blade(一个 TPM-compatible 的信任模块)。注意这里有两类信任组件要分清:平台自带的 CPU HRoT 是 ccAI 借来给 TVM / Adaptor 做安全启动量测的;而前面总架构图里 TVM 中的 TVM Trust Modules,论文明确说是 ccAI 部署进 TVM 的信任模块(ccAI deploys trust modules on both CPU-side TVMs and the PCIe-SC),负责存密钥、参与远程证明——不是 TDX 平台自带的。

启动时 HRoT-Blade 做 Measurement——给每个组件算指纹(hash):

1
2
3
4
5
HRoT-Blade
↓ 读取并验证 PCIe-SC firmware(解密 bitstream/firmware)
↓ 读取并验证 Packet Filter
↓ 测量结果记入 PCR(Platform Configuration Register,安全启动的记录本)
↓ 全部符合预期 → 启动 PCIe-SC

firmware 被改过 → 指纹不同 → 拒绝启动。

Remote Attestation

用户人在远程,看不到云服务器内部。服务器光说自己没问题没用,必须给出密码学上可验证的证明。Attestation Report 大概是:

1
2
3
4
r = (n, PCRs, S(PCRs))
│ │ └─ 对 PCR 的签名
│ └──────── 当前各组件安全状态
└───────────── nonce:用户这次临时出的随机数

Nonce 是防重放的:用户每次给一个新随机数,要求把它签进报告里。否则攻击者可以拿机器健康时期的旧报告反复骗人。

Key Exchange

环境可信了,最后一步是把 workload 加密密钥安全交给平台(不能明文走网络)。之后,从用户把任务送进来到结果取回,数据全程的明文/密文状态是这样流转的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  Remote User(明文:模型与输入)
│ 1. 用户用协商好的密钥加密 workload
↓
网络与云主机(密文传输)
│ 2. TVM 收到后解密
↓
┌───────────────────────────┐
│ TVM │ ← 任务解密后变回明文(暂存在 TVM 内存)
│ │
│ 3. Adaptor encrypt_data() │ ← AES-128;把密文写入 bounce buffer
└───────────────────────────┘
│ 4. PCIe 上跑的是密文
↓
┌───────────────────────────┐
│ PCIe-SC │ ← Packet Filter 决策(A2)
│ │
│ 5. Packet Handler │ ← 解密 payload 并校验完整性
└───────────────────────────┘
│ 6. 明文交给 xPU
↓
xPU 计算(明文:模型、中间结果、结果为明文)
│ 7. 明文结果经 PCIe-SC 加密(D2H)
↓
┌───────────────────────────┐
│ TVM │ ← 等待远程用户取回
│ │
│ 8. Adaptor decrypt_data() │ ← 解密,得到明文结果
└───────────────────────────┘

这里出现了两道加密,不是重复劳动——各管一段:

① 网络段 ② PCIe 段
密钥 SessionKey:远程证明后用户与平台协商 TVM 与 PCIe-SC 在各自信任模块里协商的对称密钥
保护范围 用户 → 云主机:网络与云主机上的窃听 TVM → xPU 的 PCIe 总线(威胁模型里攻击者可 snoop)
生命周期 会话期间 任务结束双方销毁,防数据泄露

明文只出现在两个可信域:

  • TVM 内部;
  • 被机箱密封 + 物理传感器守护的 xPU 侧:PCIe-SC ↔︎ xPU 的内部总线与 xPU 内部(论文 §6 的 chassis sealing)。

🛠️ 第五站:Implementation——作者真做出来了

论文用 Intel 服务器 + Intel Agilex 7 SoC FPGA 搭了原型,实现两个东西:

① Adaptor(软件)

  • 新增 ccAI_adaptor kernel module(不改 CUDA / GPU Driver / AI Application)
  • 提供 encrypt_data() / decrypt_data(),用 AES-128 + Intel AES-NI 加速
  • 分到 64 KB MMIO 区域 控制 PCIe-SC(装载规则到 L1/L2、启停 H2D/D2H 传输、查状态)

② PCIe-SC(FPGA 硬件)

  • 分配 4 KB BAR 空间存放 L1/L2 规则表
  • Packet Filter 从 header 提取三要素做匹配:Packet Type(Memory Read/Write…)、Route ID(谁发/发给谁)、Payload Metadata(start/end address,访问哪段内存)

📌 小结

组件 职责 关键点
Packet Filter 决策 L1 挡坏人 → L2 给好人分级 → A1~A4
Packet Handler 执行 Header/Payload 分离,AES + Auth Tag
Adaptor 兼容 加密进 bounce buffer、ccAI_adaptor 新增不改老驱动
HRoT-Blade 信任 Measurement → PCR → Attestation → Key Exchange

到这里,能不能做、怎么做都清楚了。剩下最后一个问题:加了这么多安全操作,GPU 会不会慢成蜗牛? 论文实验里号称开销只有 0.05%~5.67%——下一篇就看作者怎么用 RQ1~RQ6 六个实验证明这套东西真的又快又兼容:ccAI 精读(三):实现与实验。

(本系列为 MICRO 2025 论文 ccAI: A Compatible and Confidential System for AI Computing 的入门精读笔记。)