keelapi/keel-verifier
GitHub: keelapi/keel-verifier
一个独立运行的命令行验证器,用于离线校验 Keel 平台签发的 AI 审计导出内容和 Permit 治理证据在签名后是否被篡改。
Stars: 3 | Forks: 0
# keel-verifier
[](https://pypi.org/project/keel-verifier/)
[](https://pypi.org/project/keel-verifier/)
[](https://github.com/keelapi/keel-verifier/actions/workflows/ci.yml)
[](LICENSE)
用于 Permit 规范治理证据和 Keel 审计导出内容的独立验证器。
## 为什么需要它
大多数 AI 平台只能告诉你它们记录了什么。
此验证器用于检测导出的治理证据在签名后被篡改的情况。审计员、客户、监管机构或安全团队可以独立验证决策、已发送的请求、返回的响应以及生命周期证据的完整性。
验证在本地运行,不需要访问 Keel 的系统。
## 此验证器能证明和不能证明什么
该验证器证明导出的证据在签名后未被更改。与任何签名系统一样,信任边界包含签名时的签名者。防御特权用户在签名时的操纵行为,需要超出此验证器范围的、具有更高保障级别的签名架构。
具体而言:
- 检测到 permit 生命周期中任何元素(输入、分发、provider 响应、client 响应、关闭记录)在签名后被篡改的情况——请参阅在线文档中的篡改检测矩阵。
- 此验证器**无法**检测特权 Keel 操作员在创建证据那一刻进行的签名前操纵。防御这种威胁模型需要硬件支持的签名(TEE/HSM),这属于超出此验证器覆盖范围的独立安全能力层。
## 快速开始
```
python -m pip install keel-verifier
keel-verify export --help
```
从代码库检出的环境运行:
```
python -m pip install -e .
python -m keel_verifier --help
```
v0.2.0 的调用方式仍然有效:
```
python -m keel_verifier sample/export.json --self-attested
```
## 单文件证据包
Keel evidence bundle v1 被刻意设计为单文件、单命令、无网络依赖。
当文件包含 `schema_version=keel.evidence_bundle/v1` 时,验证器会读取
嵌入的 `body` 和 `signature_envelope`,验证 `artifact_ref.v1`,
针对规范化 `body` 重新计算 `signature_envelope.content_hash`,
使用包内的公钥验证 Ed25519 签名,并在存在回执的情况下,
根据包内的锚点检查 RFC 3161 TSA 回执印记。
```
keel-verify export evidence_bundle.json
keel-verify checkpoint checkpoint_bundle.json
```
传统的分割文件导出内容仍然可以使用 `keel-verify export export.json
manifest.json` 进行验证,但 CLI 会发出废弃警告,以提醒操作员将下载内容迁移至单文件包。
## 常用命令
| 任务 | 命令 |
| --- | --- |
| 验证自证证据包 | `keel-verify export evidence_bundle.json` |
| 验证已签名的导出文件 | `keel-verify export export.json manifest.json` |
| 遍历生命周期链式条目 | `keel-verify export export.json manifest.json --walk-events` |
| 验证关闭记录 | `keel-verify export export.json manifest.json --walk-events --verify-closure` |
| 验证 checkpoint | `keel-verify checkpoint checkpoint.json` |
| 验证语音会话证明制品 | `python -m keel_verifier voice_session_export.json` |
| 验证已注册的 claim | `keel-verify claim delegation_denied_correctly --evidence-file evidence.json` |
| 刷新缓存的信任根 | `keel-verify refresh-keys` |
| 验证已安装的 wheel | `keel-verify self-check` |
## 验证内容
`keel-verify export` 通过四个层级验证已签名的合规性导出文件:
1. 导出数据的字节与已签名的清单中的 `content_hash` 相匹配。
2. 清单的 Ed25519 签名可通过受信任的密钥验证。
3. 存在时会验证工作流证据同级文件及事件包工作流文件。
4. 可选的链式遍历检查可验证包内的链式条目和关闭记录。
`keel-verify checkpoint` 验证完整性 checkpoint JSON 制品:`chain_heads` 哈希组合、Ed25519 checkpoint 签名,以及存在时验证嵌入的 RFC 3161 时间戳 MessageImprint。
`keel-verify claim` 根据验证器的 claim 注册表判定锁定包的证据包——请参阅下方的 [Claim 验证](#claim-verification-pack-pinned-semantics)。
语音会话证明制品在传递给传统的单文件验证器入口点时,会被顶层的
`verifier_compatibility` 块自动检测。验证器同时接受原始 schema v1 制品格式
(`artifact_version=1.0.0`,嵌入规范化的 payload 素材)和主分支当前的
schema v3 纯哈希格式(`artifact_version=1.2.0`,
`payload_materialization=hash_only`):
```
python -m keel_verifier sample/voice_session_export.json
python -m keel_verifier sample/voice_session_export_v3.json
```
对于这些制品,验证器会检查会话链中每个事件的哈希
关联、针对规范化制品字节的 Ed25519 签名、嵌入的
RFC 3161 时间戳回执中的 MessageImprint 与项目链头锚点之间的对应关系,
以及锁定的 policy 快照哈希。传统的 checkpoint 制品将继续
使用现有的 checkpoint 验证路径。
`keel-verify self-check` 根据已签名的发布制品验证已安装的 `keel-verifier`
wheel 格式。它会验证 Sigstore 签名的发布
清单(完整的无密钥签名和证书链)、Rekor 包含证明、
DigiCert 和 GlobalSign 的 RFC 3161 TSA 见证(绑定级别:回执绑定到已签名的清单哈希并报告 `granted` 状态;针对 TSA 信任根的完整 CMS 签名和证书链验证可通过 `--tsa-ca-bundle` 扩展按需启用,这不属于默认自检的一部分)、
嵌入清单的 RFC 8785 JCS 绑定,以及嵌入清单中列出的 wheel 包文件。它不对二进制文件或 OCI 验证做出声明。
## 已安装 Wheel 的自检
从 PyPI 安装后运行自检:
```
python -m pip install keel-verifier
keel-verify self-check
```
成功时的输出范围仅限 wheel:
```
PASS: keel-verifier self-check passed for installed wheel form
[OK] form: wheel form selected
[OK] import_isolation: keel_verifier imported from /path/to/site-packages/keel_verifier/__init__.py matches distribution metadata
[OK] embedded_manifest: embedded release manifest is present and cycle-safe
[OK] fetch: release manifest, signature, and TSA sidecar loaded
[OK] sigstore_signature: signed release manifest verifies against expected GitHub Actions identity
[OK] rekor_inclusion: Rekor inclusion proof is present and verified by sigstore-python
[OK] tsa_witnesses: DigiCert and GlobalSign RFC 3161 receipts witness the manifest hash (bind-level; cert-chain validation is opt-in)
[OK] embedded_binding: embedded manifest JCS hash matches signed release manifest binding
[OK] per_file_digests: installed wheel files match embedded per-file digests
```
失败时的输出包含稳定的错误代码:
```
FAILED: keel-verifier self-check failed for installed wheel form
[FAIL] per_file_digests: SELF_CHECK_FILE_DIGEST_MISMATCH: installed file digest mismatch: keel_verifier/__init__.py
```
自检默认在线获取发布出处,并在 `~/.keel-verifier/cache/` 下使用 24 小时缓存。
使用 `--offline` 可强制要求使用缓存的出处,
`--no-cache` 可在不读取或写入缓存条目的情况下获取,
`--json` 可输出机器可读的阶段结果。
拥有可编辑代码库检出的开发者可以在不离开该环境的情况下验证已发布的 PyPI 制品:
```
keel-verify self-check --published-wheel
keel-verify self-check --published-wheel=VERSION
```
此模式仅为显式调用:默认的自检永远不会转而通过网络下载 wheel。发布 wheel 的输出会分别标记 PyPI wheel 来源和本地安装的副本。
## 获取已签名的导出文件
从 Keel 合规导出 API 请求审计导出,并在需要完整的生命周期遍历时包含链式条目:
```
curl -sS -X POST "https://api.keelapi.com/v1/compliance/exports?include_chain_entries=true" \
-H "Authorization: Bearer $KEEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id":"","format":"json"}'
```
下载导出工作流返回的两个制品:
- 导出 payload,例如 `export.json`
- 已签名的清单,例如 `manifest.json`
然后运行:
```
keel-verify export export.json manifest.json --walk-events --verify-closure
```
也支持显式的参数形式:
```
keel-verify export --export-file export.json --manifest manifest.json --walk-events --verify-closure
```
默认情况下,导出清单必须经过签名。内容哈希检查完成后,传统的未签名清单将报错关闭。
仅在需要进行考古挖掘或在本地测试配件(此时内容哈希一致性有用,但签发方真实性被刻意排除在范围之外)时,才使用 `--allow-unsigned`:
```
keel-verify export export.json manifest.json --allow-unsigned
```
## 链式遍历
`--walk-events` 会解析带有 `schema_version=2` 和 `include_chain_entries=true` 的 `audit_export_bundle` 文件。
它按 `chain_scope` 对条目进行分组,按 `sequence_number` 排序,重新计算每个 `record_hash`,验证导出窗口内 `prev_hash` 的连续性,并在遇到未知的 `chain_format_version` 值时报错关闭。
Schema 版本 1 的导出仍保持向后兼容。它们仍然可以在导出签名层进行验证,但不包含可供遍历的链式条目。
## 关闭记录验证
`--verify-closure` 验证 `permit.closed` 条目。
对于 `closure_v1`,它会验证关闭记录的 Ed25519 签名,并将 provider/client 响应摘要与包内的生命周期事件进行交叉比对。
对于 `closure_v2`,它还会根据 permit 的 `binding_request_hash` 验证 `dispatch_request_digest_v1`,以验证分发时的请求体是否为关闭记录所涵盖的请求体。
关闭记录验证使用用途为 `permit_binding_signing` 的公钥。需要时可显式传递清单:
```
keel-verify export export.json manifest.json \
--key-manifest permit-binding-keys.json \
--walk-events \
--verify-closure
```
包内的信任根位于 `keel_verifier/data/trust_root.json`。它是未签名的 wheel 内置锚点,包含 `https://api.keelapi.com/v1/compliance/keys` 当前提供的生产环境导出和 checkpoint 签名密钥,以及 `https://api.keelapi.com/v1/integrity/permit-binding-public-keys` 提供的生产环境 permit 绑定密钥。GitHub 信任根发布版本在 Christian/key-ops 发布步骤中,必须由真实的生产环境导出签名密钥签名为 `keel.public_key_manifest.v1`。
## 工作流意图验证
`keel-verify export` 支持随证据导出内容一起发出的 `keel.workflow_evidence/v1` 制品。在 3.x 版本过渡期间,它也会接受传统的 `keel.vanta.workflow_evidence/v1` 名称,并发出废弃警告。当已签名的导出清单包含 `sibling_artifacts.workflow_evidence` 条目时,验证器会检查同级文件哈希、导出签名、工作流声明签名、工作流修订签名、修订版本排序、声明 `effective_intent_hash`,以及主要证据中任何 permit 的 `workflow_state_json` 快照。
事件证据 zip 包保持向后兼容。不包含工作流文件的清单版本 1 的包会像以前一样进行验证。清单版本 2 的包必须包含 `workflow_declarations.jsonl` 和 `workflow_amendments.jsonl`;验证器会验证这些文件,并在遇到未知的清单版本时优雅降级。
## Claim 验证(包锁定语义)
v2.0.0 添加了**包锁定语义验证**:证据包可以声明它是在哪些语义制品下发出的(通过 `(id, sha256)` 标识),验证器会从一个永久的、只增的允许列表中复现这些确切的语义。版本锁定的包会获得可重现的判定——任何未来的验证器版本必须解析这些确切锁定的语义并得出相同的 claim 结论,或者明确拒绝。它绝不会默默地对先前的锁定 claim 重新进行解释。
对于 `closure.dispatch_binding.v1`,流式分发路径会发出单独的 `provider.response.received` 和 `client.response.delivered` 事件作为摘要载体;非流式分发路径则发出 `execution.completed` 作为已接受的摘要载体。这两种结构都携带 `provider_response_digest_v1` 和 `client_response_digest_v1`,并由验证器进行等效判定。
```
keel-verify claim delegation_denied_correctly --evidence-file evidence.json
keel-verify claim permit.operator_approval.v1 path/to/pack/
keel-verify claim permit.counter_signature.v1 path/to/pack/
keel-verify claim permit.audit_attestation.v1 path/to/pack/
```
Claim 输出默认为 JSON。为了与导出和 checkpoint 命令保持一致,也接受 `--json`。Permit v2 的槽位 claim 包可以作为包含 `export.json`、`manifest.json` 和 `key_manifest.json` 的目录传递,也可以使用显式的 `--export-file`、`--manifest` 和 `--key-manifest` 参数传递。
一个包携带两个清单块:
- `claim_set` — 包断言了哪些 claim,每个都标记为 `required: true|false`。
- `semantics_pins` — 验证器应解析哪些语义制品(通过 `(id, sha256)` 标识)。
验证器针对永久允许列表解析并验证每个锁定标识,然后对每个声明的 claim 进行判定。
### 结构化的独立 claim 判定
`--json` 输出增加了包含每个 claim 判定结果的 `claims` 数组,使用四值枚举:
| 判定结果 | 含义 |
| --- | --- |
| `supported` | 证据明确支持该 claim。 |
| `disproved` | 证据与该 claim 相矛盾。 |
| `insufficient_evidence` | 包未提供足够的信息来做出决定。 |
| `unverifiable_scope` | 该 claim 超出了验证器可以判定的范围。 |
对于锁定的包,`claim_set` 标记为 `required` 的每个 claim 必须获得 `supported` 的结果,才能使包的整体 `ok` 状态为真。传统的未锁定证据(无 `claim_set` / `semantics_pins`)将根据永久的 `keel.pre_pinning_default.v0` 配置进行评估,且不受必需 claim 强制要求的限制——v1.x 的导出将继续保持不变地进行验证。
### 规范
- [`spec/verifier-pack-pinning-v0.md`](https://github.com/keelapi/keel-permit/blob/main/spec/verifier-pack-pinning-v0.md) — 包锁定机制。
- [`spec/verifier-claims-v0.md`](https://github.com/keelapi/keel-permit/blob/main/spec/verifier-claims-v0.md) — claim 注册和判定语义。
- [`spec/permit-chain-v1.md`](https://github.com/keelapi/keel-permit/blob/main/spec/permit-chain-v1.md) — `permit_chain.delegation_denied_correctly.v1` claim。
## TSA 信任验证
Checkpoint 验证通过确认 TSA MessageImprint 与 checkpoint `composite_hash` 匹配,来检查嵌入的 RFC 3161 时间戳回执。
如需按需启用 TSA 真实性验证,请传递 CA 证书包:
```
keel-verify checkpoint checkpoint.json --tsa-ca-bundle tsa-ca-bundle.pem
```
这会使用 OpenSSL 3.x 来针对提供的 CA 证书包验证 CMS 签名、证书链和时间戳用途。它不会检查时间戳签发时的历史吊销状态。
## 篡改检测矩阵
验证器会发出稳定的 `WALK_*` 失败代码,包括:
- `WALK_RECORD_HASH_MISMATCH`
- `WALK_PREV_HASH_DISCONTINUITY`
- `WALK_SEQUENCE_INVERSION`
- `WALK_UNKNOWN_CHAIN_FORMAT`
- `WALK_CLOSURE_SIGNATURE_INVALID`
- `WALK_CLOSURE_DIGEST_MISMATCH`
- `WALK_CLOSURE_DIGEST_MISSING`
- `WALK_CLOSURE_DISPATCH_DIGEST_MISMATCH`
- `WALK_UNKNOWN_CLOSURE_FORMAT`
示例:如果 provider 响应在签名后被修改,验证将失败并提示 `WALK_CLOSURE_DIGEST_MISMATCH`。
权威矩阵维护在线文档中:https://docs.keelapi.com/12-tampering-detection-matrix
## 信任模型
有两种有用的验证类型:
- 自证:文件与自身达成一致。这仅验证内部一致性。
- 信任根验证:制品根据您信任的密钥(例如内置的生产信任根、锁定的公钥或带外获取并保存的清单)进行验证。
信任源,按强度从高到低排列:
| 模式 | 参数 | 说明 |
| --- | --- | --- |
| 锁定密钥 | `--expected-public-key ed25519:...` 或 `--public-key ed25519:...` | 通过带外途径获取时强度最高。 |
| 密钥清单 | `--key-manifest keys.json` | 支持密钥轮换和活跃窗口。 |
| 密钥清单 URL | `--key-manifest-url URL` | 显式进行网络获取。 |
| 缓存的清单 | 无(通过 `keel-verify refresh-keys` 设置) | 缓存存在时的默认值。位于 `~/.keel-verifier/trust-root.json`。 |
| 内置信任根 | 无 | 始终存在的最低安全保障。无网络回传。 |
| 自证 | `--self-attested` | 仅用于开发/示例模式。 |
针对单一的实时 checkpoint 公钥端点进行 checkpoint 验证时,也支持使用 `--public-key-url`。
未传递任何参数时,验证器按以下顺序解析信任根:显式指定的 `--key-manifest[-url]` → 缓存的 `~/.keel-verifier/trust-root.json`(如果存在)→ wheel 内置的 `data/trust_root.json`。
### 密钥轮换后刷新信任根
wheel 附带了构建时生成的信任根快照。密钥轮换后,在轮换前发布的 wheel 将无法直接验证轮换后的制品。有三种解决方案:
1. `pip install --upgrade keel-verifier` — 拉取最新的内置快照。
2. `keel-verify refresh-keys` — 从任何信任根通道获取最新的清单并将其缓存在 `~/.keel-verifier/trust-root.json`。验证器在后续运行中会优先使用缓存而不是内置快照。
3. 在审计时锁定清单:将清单与制品一起下载,并通过 `--key-manifest ` 显式传递。
`refresh-keys` 参数:
```
keel-verify refresh-keys # auto: try Keel API, then GitHub
keel-verify refresh-keys --source api # only try the Keel API
keel-verify refresh-keys --source github # only try the GitHub mirror
```
## CLI 示例
```
keel-verify export export.json manifest.json
keel-verify export export.json manifest.json --walk-events
keel-verify export export.json manifest.json --walk-events --verify-closure
keel-verify export export.json manifest.json --allow-unsigned
keel-verify checkpoint checkpoint.json
keel-verify claim delegation_denied_correctly --evidence-file evidence.json
keel-verify claim delegation_denied_correctly --evidence-file evidence.json --json
keel-verify refresh-keys
keel-verify refresh-keys --source github
python -m keel_verifier sample/export.json --self-attested
python -m keel_verifier sample/export.json --json --self-attested
```
退出代码 `0` 表示验证成功。退出代码 `1` 表示验证失败。退出代码 `2` 表示使用方法错误。
## 网络行为
正常验证不会进行网络回传。只有在运行
`keel-verify refresh-keys` 或传递了显式的 URL 信任根参数(例如 `--public-key-url` 或 `--key-manifest-url`)时,才会发生网络获取。
代码库的 CI 工作流也会联系实时的 Keel 端点,以检测内置信任根是否发生偏移。
不包含任何遥测功能。
## 库使用方式
```
import json
from pathlib import Path
from keel_verifier import (
verify,
verify_delegation_denied_correctly,
)
result = verify("sample/export.json", self_attested=True)
if not result.ok:
raise SystemExit(result.error)
evidence = json.loads(Path("evidence.json").read_text())
claim = verify_delegation_denied_correctly(evidence, include_semantics=True)
if claim["status"] != "supported":
raise SystemExit(claim)
```
## 版本控制
v2.0.0 引入了包锁定语义和结构化的 claim 判定。
文档中记录的 CLI 调用方式仍然有效,包括 `python -m keel_verifier `。
## 相关项目
- Permit 规范:https://github.com/keelapi/keel-permit
- 参考 API:https://github.com/keelapi/keel-api
- 文档:https://docs.keelapi.com
## 维护者
由 Keel API, Inc. 维护。
## 许可证
MIT。请参阅 `LICENSE`。
标签:AI治理, CVE, Python, Zenmap, 数字签名, 数据完整性, 无后门, 逆向工具, 验证工具