跳转至

迁移指南:从 DCP-AI v1.0 到 v2.0

本文是英文版的参考译文,更新于 28/10/2026。如有出入,以英文版为准。标识符、命令和代码块保持原文,不作翻译。

规范版本: 2.0 最后更新: 2026 年 10 月 适用于: main 上的 SDK 和已发布的 @dcp-ai/sdk 2.2.1。npm 版本已于 2026 年 10 月 28 日核实;见 NPM_RELEASES.md 和 SECURITY.md。其他 SDK 的版本见已发布版本。

本指南带你把现有的 DCP-AI v1.0 集成迁移到 v2.0。迁移会引入后量子复合签名、双哈希链、会话绑定、自适应安全等级和智能体间通信;同时,有效的 v1.0 bundle 在验证器策略允许的情况下仍可通过验证(参见 1.2)。


1. v2.0 的变化

1.1 变化概要

功能 v1.0 v2.0
签名 仅 Ed25519 复合:Ed25519 + ML-DSA-65(PQ)
哈希链 仅 SHA-256 双链:SHA-256 + SHA3-256
会话绑定 无 每个构件都带 session_nonce
安全等级 无 自适应:routine / standard / elevated / maximum
bundle 清单 bundle_hash + merkle_root 完整清单,包含每个构件的哈希、次级 Merkle 根和 PQ 检查点
签名格式 { alg, public_key_b64, sig_b64 } { composite_sig: { classical, pq, binding } }
A2A 通信 未规定 DCP-04:发现、握手、加密会话
验证策略 由智能体声明 由验证器决定
域分离 无 所有签名都带上下文标签
kid 导出 依实现而定 确定性:hex(SHA-256(alg || 0x00 || pk))[0:32]
数值字段 混合(int/float) 仅整数(禁止 float)
隐私 仅完整 RPR 提供盲化 RPR 模式
密钥恢复 未规定 M-of-N 社交恢复(Shamir SSS)
紧急吊销 未规定 预先登记的吊销令牌
算法治理 未规定 带自动响应的已签名公告
线上格式 仅 JSON JSON(默认)+ CBOR(可选)

1.2 不兼容的变更

v1.0 格式仍然有效,但有以下限制:

  • 同时处理两个版本的验证器,在其策略为 allow_v1_bundles: true(默认值)且实际生效的模式能容忍仅经典签名时 (见下文),会接受有效的 v1.0 bundle。verifySignedBundleV2 本身 只接受 v2.0:请把 v1.0 bundle 交给 V1 验证器(参见 3.2)。
  • 接受条件比以前的版本更严格:例如,S 不小于群阶 L 的 Ed25519 签名现在会被拒绝 (参见变更日志的 [Unreleased]),因此以前的验证器接受过的 已存储 v1.0 bundle 可能不再通过验证。
  • v1.0 验证器会拒绝 v2.0 bundle("unsupported dcp_version 2.0")。

v1.0 bundle 只有一个 Ed25519 签名,没有后量子签名,因此验证中间件(@dcp-ai/express、@dcp-ai/openclaw、dcp_ai.fastapi)只有在仅经典签名的 v2.0 bundle 能通过的情况下才接受它(VERIFICATION.md V2-10):V1 护照 risk_tier 对应的策略模式,与根据 V1 意图和策略决定计算出的安全等级模式(V1 的 risk_score 取值 0 到 1,乘以 1000;缺失时按 elevated 计)两者中较严格者,必须是 classical_only 或 hybrid_preferred。在默认策略下,意图为 routine 或 standard 的 V1 bundle 会通过,而涉及支付、敏感数据、高预估影响或风险分数 0.5 及以上的 V1 bundle 会被拒绝;Express 的 requireHybrid 会拒绝所有 V1 bundle。

迁移是增量式的:v2.0 在 v1.0 格式旁边引入新的字段和结构。你可以分别独立地迁移生产方(智能体)和消费方(验证器)。


2. 分步迁移

步骤 1:更新 SDK

示例适用于已发布的 TypeScript SDK 2.2.1、Python dcp-ai 2.10.1、Go v2.11.1 和 Rust dcp-ai 2.11.1。见 Python、Go、Rust 发布说明及各 SDK README 中记录的限制。

TypeScript(2026 年 10 月 28 日已发布:2.2.1):

npm install @dcp-ai/sdk@^2.2.1

Python:

pip install -e /path/to/dcp-ai/sdks/python     # main
# or the published version, with optional framework extras: pip install --upgrade 'dcp-ai[fastapi]'

Go(注意 Go 模块要求的主版本后缀 v2):

go get github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2@v2.11.1

Rust:

dcp-ai = "2.11.1"

WebAssembly: 从检出副本构建 sdks/wasm(npm run build),然后执行 npm install /path/to/dcp-ai/sdks/wasm。

步骤 2:生成混合密钥对

本次混合迁移使用经典(Ed25519)和后量子(ML-DSA-65)两种密钥对。是否接受仅经典签名,仍取决于实际生效的验证模式(1.2)。

TypeScript:

import { getDefaultRegistry, registerDefaultProviders } from '@dcp-ai/sdk';

registerDefaultProviders(); // Ed25519, ML-DSA-65, SLH-DSA-192f into the default registry
const registry = getDefaultRegistry();

const classicalKp = await registry.getSigner('ed25519').generateKeypair();
const pqKp = await registry.getSigner('ml-dsa-65').generateKeypair();
// each: { kid, publicKeyB64, secretKeyB64 }; kid = deriveKid(alg, publicKey)

Python:

import asyncio
from dcp_ai.providers.ed25519_provider import Ed25519Provider
from dcp_ai.providers.ml_dsa_65_provider import MlDsa65Provider

async def generate_hybrid():
    return await Ed25519Provider().generate_keypair(), await MlDsa65Provider().generate_keypair()

classical, pq = asyncio.run(generate_hybrid())
# each: {"kid", "public_key_b64", "secret_key_b64"}

Python SDK 使用 dcp_ai.v2(BundleBuilderV2、sign_bundle_v2、 verify_signed_bundle_v2;参见 5.2)构建、签署和验证 V2 bundle。

CLI(检出副本中的 bin/dcp.js):

dcp keygen --hybrid ./keys/
# Creates: ed25519_{public_key,secret_key,kid}.txt and ml_dsa_65_{public_key,secret_key,kid}.txt

步骤 3:更新构件的构建方式

V2 的 RPR、护照、意图和策略决定都增加了 dcp_version: "2.0" 和 bundle 的 session_nonce,并作为一个 SignedPayload 单独签名(步骤 4)。密钥移到了构件内部:RPR 在 binding_keys 中列出委托人的密钥, 护照在 keys 中列出智能体的密钥,每个密钥都带有 kid、alg、public_key_b64、created_at、 expires_at 和 status。

之前(v1.0 RPR): 一个针对去掉自身后的对象所做的分离式 Ed25519 signature。

{
  "dcp_version": "1.0",
  "human_id": "human:example-001",
  "legal_name": "Example Principal",
  "entity_type": "natural_person",
  "jurisdiction": "ES",
  "liability_mode": "owner_responsible",
  "override_rights": true,
  "issued_at": "2026-10-25T00:00:00Z",
  "expires_at": null,
  "contact": null,
  "signature": "<base64 Ed25519 signature>"
}

之后(v2.0 RPR 载荷): 没有 signature 字段;复合签名位于载荷旁边(步骤 4)。

{
  "dcp_version": "2.0",
  "human_id": "human:example-001",
  "session_nonce": "<64 hex characters>",
  "legal_name": "Example Principal",
  "entity_type": "natural_person",
  "jurisdiction": "ES",
  "liability_mode": "owner_responsible",
  "override_rights": true,
  "issued_at": "2026-10-25T00:00:00Z",
  "expires_at": null,
  "contact": null,
  "binding_keys": [
    { "kid": "<32 hex>", "alg": "ed25519", "public_key_b64": "...", "created_at": "2026-01-01T00:00:00Z", "expires_at": null, "status": "active" },
    { "kid": "<32 hex>", "alg": "ml-dsa-65", "public_key_b64": "...", "created_at": "2026-01-01T00:00:00Z", "expires_at": null, "status": "active" }
  ]
}

主要区别: - dcp_version 为 "2.0",session_nonce(64 个十六进制字符,256 位随机数)在 bundle 的所有 构件中都相同。 - 护照用 keys(Ed25519 和 ML-DSA-65 条目)取代 public_key,策略决定中的 risk_score 变为 0 到 1000 的整数(规范 §14.2)。 - 签名从载荷中移出。

步骤 4:改用复合签名

这四个 V2 构件各自都是一个 SignedPayload:载荷、载荷的哈希,以及在该构件的域分离上下文下所做的 复合签名(RPR 用委托人的密钥,护照、意图和策略决定用智能体的密钥)。

{
  "payload": { "dcp_version": "2.0", "...": "..." },
  "payload_hash": "sha256:<hex of the canonical payload>",
  "composite_sig": {
    "classical": { "alg": "ed25519", "kid": "<32 hex>", "sig_b64": "..." },
    "pq": { "alg": "ml-dsa-65", "kid": "<32 hex>", "sig_b64": "..." },
    "binding": "pq_over_classical"
  }
}

主要区别: - 每个分量通过 kid(确定性的,见步骤 2)指明其密钥,而不是携带公钥。 - binding: "pq_over_classical":PQ 签名覆盖经典签名,因此无法在不使 PQ 签名失效的情况下替换经典签名。但 PQ 签名本身仍可被移除,并把绑定改标为 classical_only:验证器在 classical_only 和 hybrid_preferred 模式下接受这种签名 (后者附带警告),在 hybrid_required 和 pq_only 模式下拒绝。存在但无效的 PQ 签名在任何模式下都会被拒绝。

TypeScript SDK(这是一个片段;TypeScript SDK 的 Quick Start (V2) 是完整、可运行的流程):

import { compositeSign, preparePayload, DCP_CONTEXTS } from '@dcp-ai/sdk';

// payload: a V2 artifact payload; registry and agentKeys (a CompositeKeyPair) as in Step 2
const { canonicalBytes, payloadHash } = preparePayload(payload);
const signedIntent = {
  payload,
  payload_hash: payloadHash,
  composite_sig: await compositeSign(registry, DCP_CONTEXTS.Intent, canonicalBytes, agentKeys),
};

步骤 5:加入域分离

每个 V2 签名都针对 UTF8(context) || 0x00 || canonical_bytes 计算,其中 context 是该构件的上下文 (DCP-AI.v2.Intent、DCP-AI.v2.Bundle 等,见 DCP_CONTEXTS)。compositeSign、signBundleV2 以及其他 SDK 中的 对应函数会替你完成这一步。如果你自己构建消息,请使用 V2 规范化 (dcp-jcs-v1):

import { canonicalizeV2, domainSeparatedMessage, DCP_CONTEXTS } from '@dcp-ai/sdk';

// manifest: the bundle manifest (Step 7)
const message = domainSeparatedMessage(DCP_CONTEXTS.Bundle, new TextEncoder().encode(canonicalizeV2(manifest)));
// classical signature over `message`; the PQ signature over `message || classical signature bytes`

步骤 6:更新审计条目

之前(v1.0):

{
  "dcp_version": "1.0",
  "audit_id": "audit:example-001",
  "prev_hash": "GENESIS",
  "timestamp": "2026-10-25T00:01:00Z",
  "agent_id": "agent:example-001",
  "human_id": "human:example-001",
  "intent_id": "intent:example-001",
  "intent_hash": "<hex>",
  "policy_decision": "approved",
  "outcome": "call_completed",
  "evidence": { "tool": "http", "result_ref": null }
}

之后(v2.0):

{
  "dcp_version": "2.0",
  "audit_id": "audit:example-001",
  "session_nonce": "<the bundle's nonce>",
  "prev_hash": "GENESIS",
  "hash_alg": "sha256",
  "timestamp": "2026-10-25T00:01:00Z",
  "agent_id": "agent:example-001",
  "human_id": "human:example-001",
  "intent_id": "intent:example-001",
  "intent_hash": "sha256:<hex of the canonical intent payload>",
  "policy_decision": "approved",
  "outcome": "call_completed",
  "evidence": { "tool": "http", "result_ref": null, "evidence_hash": null },
  "pq_checkpoint_ref": null
}

新字段:session_nonce、hash_alg、evidence.evidence_hash 和 pq_checkpoint_ref;哈希带有 sha256: 前缀。双哈希链会增加 prev_hash_secondary(SHA3-256)和 hash_alg: "sha256+sha3-256"。 自 @dcp-ai/sdk 2.2.1、@dcp-ai/wasm 2.1.1、dcp-ai 2.10.1(PyPI)、Go v2.11.1 和 Rust dcp-ai 2.11.1 起,V2 验证器(包括实验性候选)会检查存在的次级链接、意图哈希和 audit_merkle_root_secondary;不存在的不作要求,而更早的版本不检查它们,因此只有生成方和验证方都包含它时,生成这些字段才能带来额外保护 (STORAGE_AND_ANCHORING.md)。 审计条目不逐条签名:清单通过审计 Merkle 根对它们作出承诺,PQ 检查点则对其中的区间签名。

步骤 7:构建 bundle 清单

V1 对整个 bundle 签名(signature.bundle_hash);V2 对一个承诺每个构件的清单签名。

之前(v1.0 已签名 bundle):

{
  "bundle": {
    "responsible_principal_record": { "...": "..." },
    "agent_passport": { "...": "..." },
    "intent": { "...": "..." },
    "policy_decision": { "...": "..." },
    "audit_entries": [{ "...": "..." }]
  },
  "signature": {
    "alg": "ed25519",
    "created_at": "2026-10-25T00:02:00Z",
    "signer": { "type": "human", "id": "human:example-001", "public_key_b64": "..." },
    "bundle_hash": "sha256:<hex>",
    "merkle_root": "sha256:<hex>",
    "sig_b64": "..."
  }
}

之后(v2.0 已签名 bundle):

{
  "bundle": {
    "dcp_bundle_version": "2.0",
    "manifest": {
      "session_nonce": "<64 hex>",
      "rpr_hash": "sha256:...",
      "passport_hash": "sha256:...",
      "intent_hash": "sha256:...",
      "policy_hash": "sha256:...",
      "audit_merkle_root": "sha256:...",
      "audit_count": 1,
      "canonicalization_profile": "dcp-jcs-v1"
    },
    "responsible_principal_record": { "payload": { "...": "..." }, "payload_hash": "sha256:...", "composite_sig": { "...": "..." } },
    "agent_passport": { "payload": { "...": "..." }, "payload_hash": "sha256:...", "composite_sig": { "...": "..." } },
    "intent": { "payload": { "...": "..." }, "payload_hash": "sha256:...", "composite_sig": { "...": "..." } },
    "policy_decision": { "payload": { "...": "..." }, "payload_hash": "sha256:...", "composite_sig": { "...": "..." } },
    "audit_entries": [{ "...": "..." }]
  },
  "signature": {
    "hash_alg": "sha256",
    "created_at": "2026-10-25T00:02:00Z",
    "signer": { "type": "human", "id": "human:example-001", "kids": ["<classical kid>", "<pq kid>"] },
    "manifest_hash": "sha256:<hex of the canonical manifest>",
    "composite_sig": { "classical": { "...": "..." }, "pq": { "...": "..." }, "binding": "pq_over_classical" }
  }
}

清单: - 每个构件有一个哈希(各构件的 payload_hash),外加审计 Merkle 根; - 可以增加 audit_merkle_root_secondary(SHA3-256)、pq_checkpoints、已签名的会话窗口 (session_issued_at、session_expires_at)、intended_verifier 和 canonicalization_profile; - 由责任委托人在 DCP-AI.v2.Bundle 上下文下用复合签名签署。

步骤 8:更新验证器配置

V2 验证接受一个由验证器而非 bundle 决定的验证器策略。从 DEFAULT_VERIFIER_POLICY 开始,修改你需要的部分:你省略的字段不会用默认值填充 (例如,没有 max_key_age_days 时就不会产生密钥年龄警告;该检查只警告,不拒绝)。

import { DEFAULT_VERIFIER_POLICY, verifySignedBundleV2 } from '@dcp-ai/sdk';
import type { VerifierPolicy } from '@dcp-ai/sdk';

const policy: VerifierPolicy = {
  ...DEFAULT_VERIFIER_POLICY,
  default_mode: 'hybrid_required',
  risk_overrides: { high: 'hybrid_required', medium: 'hybrid_required', low: 'hybrid_preferred' },
  allow_v1_bundles: true,
};
// signedBundle and registry as in the TypeScript SDK's Quick Start (V2)
const result = await verifySignedBundleV2(signedBundle, registry, policy);

字段说明见 API 参考。在过渡期间请保持 allow_v1_bundles: true。 V1 bundle 仍需要一种能容忍仅经典签名的模式:在此策略下 (medium 和 high 等级为 hybrid_required),验证中间件只接受来自 低风险护照、意图为 routine 或 standard 的 V1 bundle。

步骤 9:加入安全等级支持(可选)

根据意图以及携带 risk_score 的策略决定计算安全等级:

// Fragment: the intent and the policy decision are V2 payloads, shortened here.
import { computeSecurityTier } from '@dcp-ai/sdk';
import type { IntentV2, PolicyDecisionV2 } from '@dcp-ai/sdk';

const intent = {
  intent_id: 'intent:...',
  action_type: 'api_call',
  data_classes: ['pii'],
  estimated_impact: 'medium',
  // ...
} as IntentV2;
const policyDecision = { risk_score: 350 /* , ... */ } as PolicyDecisionV2;

const tier = computeSecurityTier(intent, policyDecision);
// tier = "elevated" (pii is sensitive data; risk_score 350 alone would give "standard")

步骤 10:运行一致性测试

在检出副本的根目录运行(每条命令在各自的子 shell 中):

# TypeScript
(cd sdks/typescript && npm test)

# Python
(cd sdks/python && pytest)

# NIST KAT compliance only
(cd sdks/typescript && npx vitest run src/__tests__/nist-kat.test.ts)

# Protocol conformance (V1 + V2)
npm run conformance

3. 向后兼容

3.1 兼容性矩阵

V1 Verifier + V1 Bundle  ->  PASS (unchanged)
V1 Verifier + V2 Bundle  ->  REJECT "unsupported dcp_version 2.0"
V2 Verifier + V1 Bundle  ->  PASS (when allow_v1_bundles=true and the effective mode
                                    for the bundle is classical_only or hybrid_preferred)
V2 Verifier + V2 Bundle  ->  PASS (full V2 verification)

3.2 共存策略

在迁移期间,你的系统可以按版本分流,同时处理 v1.0 和 v2.0 bundle:

// Fragment: signedBundle comes from the request; v1PublicKeyB64, registry and policy from configuration.
import { detectDcpVersion, verifySignedBundle, verifySignedBundleV2 } from '@dcp-ai/sdk';

const version = detectDcpVersion(signedBundle);  // '2.0', '1.0', or null for anything else
let result;
if (version === '2.0') {
  result = await verifySignedBundleV2(signedBundle, registry, policy);  // composite verification, tier enforcement
} else if (version === '1.0' && policy.allow_v1_bundles) {
  result = verifySignedBundle(signedBundle, v1PublicKeyB64);           // the Ed25519 signature, hashes and chain
} else {
  result = { verified: false, errors: ['unsupported or disallowed bundle version'] };
}

这只是按格式分流。V1 分支不会对 V1 bundle 应用 V2 策略模式或安全等级 (VERIFICATION.md V2-10),两个分支也都不决定信任:前者由验证中间件负责, 后者由准入负责。verifySignedBundleV2 只接受 v2.0 bundle;参考 CLI (dcp verify-bundle)和中间件会自行按版本分流。

3.3 模式选择

验证器在检查模式之前,先按 bundle 的版本分流:

  • V2 已签名 bundle 带有 bundle.dcp_bundle_version: "2.0" 和 bundle.manifest → schemas/v2/*.schema.json;
  • V1 bundle 的构件带有 dcp_version: "1.0" → schemas/v1/*.schema.json。

3.4 何时停止支持 V1

按照规范,V2 验证器必须(MUST)无限期支持 V1 bundle。不过,各个部署可以在过渡期之后设置拒绝 V1 bundle 的策略:

{
  "verifier_policy": {
    "allow_v1_bundles": false
  }
}

只有在你的生态系统中所有智能体都已迁移到 v2.0 之后,才应这样做。


4. bundle 格式的变化(详细)

4.1 已签名 bundle 的封装

完整的 V1 和 V2 封装见步骤 7。主要的结构差异:

  • 构件单独签名: RPR、护照、意图和策略决定各自是一个 SignedPayload (payload、payload_hash、composite_sig),在各自的上下文下签名。审计条目保持不签名, 通过审计 Merkle 根被承诺。
  • 清单: bundle.manifest 对每个构件的哈希以及审计 Merkle 根作出承诺;它取代了 V1 的 signature.bundle_hash。
  • 会话 nonce: 每个构件、每条审计条目和清单中都是同一个 session_nonce。
  • bundle 签名: signature.composite_sig 签署的是清单(signature.manifest_hash),而不是整个 bundle;signature.signer.kids 指明密钥,而不是携带公钥。
  • 版本标记: bundle.dcp_bundle_version: "2.0"。

4.2 复合签名对象

{
  "composite_sig": {
    "classical": {
      "alg": "ed25519",
      "kid": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
      "sig_b64": "<base64-encoded Ed25519 signature>"
    },
    "pq": {
      "alg": "ml-dsa-65",
      "kid": "f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3",
      "sig_b64": "<base64-encoded ML-DSA-65 signature>"
    },
    "binding": "pq_over_classical"
  }
}

4.3 审计条目的变化

字段 v1.0 v2.0
dcp_version "1.0" "2.0"
session_nonce 不存在 必需
prev_hash 前一条目的十六进制 SHA-256,或 "GENESIS" 前一条目的 sha256:<hex>,或 "GENESIS"
hash_alg 不存在 必需:sha256,或在双哈希链下为 sha256+sha3-256
prev_hash_secondary 不存在 可选:sha3-256:<hex>(双哈希链)
intent_hash 十六进制 SHA-256 sha256:<hex>
intent_hash_secondary 不存在 可选:sha3-256:<hex>
evidence.evidence_hash 不存在 存在,可以为 null
pq_checkpoint_ref 不存在 必需,可以为 null
签名 不逐条签名;由 bundle 签名覆盖 不逐条签名;由清单中的审计 Merkle 根承诺,PQ 检查点对其中的区间签名

5. SDK API 的变化

5.1 TypeScript SDK

v1.0 API v2.0 API 说明
signBundle(bundle, secretKey) signBundleV2(bundle, { registry, signerType, signerId, keys }) 对清单的复合签名
verifySignedBundle(sb, publicKey) verifySignedBundleV2(sb, registry, policy?) 由策略驱动的验证
new BundleBuilder()…build() new BundleBuilderV2(sessionNonce)…build() 必须提供会话 nonce;构件为 SignedPayload
hashObject(obj) hashObject(obj)(不变) 仍为 SHA-256 + JCS
N/A dualHashCanonical(canonicalJson) SHA-256 + SHA3-256
N/A registerDefaultProviders() + getDefaultRegistry() 通过算法注册表提供 Ed25519、ML-DSA-65、SLH-DSA-192f
N/A computeSecurityTier(intent, policyDecision?) 自适应等级计算(规范 §14.2)
N/A deriveKid(alg, publicKeyBytes) 确定性 kid
N/A compositeSign(registry, context, payloadBytes, keys) 手动复合签名
N/A compositeVerify(registry, context, payloadBytes, sig, classicalPk, pqPk?) 手动复合验证

5.2 Python SDK

v1.0 API v2.0 API 说明
sign_bundle(bundle, secret_key) 每个构件执行 prepare_payload + await composite_sign(registry, context, payload_bytes, classical_key, pq_key),然后 BundleBuilderV2(session_nonce)…build() + await sign_bundle_v2(bundle, registry, signer_type, signer_id, classical_key, pq_key)(dcp_ai.v2) 对每个构件和清单的复合签名
verify_signed_bundle(sb, public_key) await verify_signed_bundle_v2(signed_bundle, registry, policy=None)(dcp_ai.v2) 由策略驱动
N/A Ed25519Provider / MlDsa65Provider(dcp_ai.providers),异步的 generate_keypair() 混合密钥
N/A compute_security_tier(intent, policy_decision=None)(dcp_ai.v2) 自适应等级(规范 §14.2)
N/A derive_kid(alg, public_key_bytes)(dcp_ai.v2) 确定性 kid

5.3 Go SDK

v1.0 API v2.0 API 说明
SignObject(bundle, secretKeyB64) 每个构件执行 v2.SignPayload(registry, context, payload, classicalKey, pqKey),然后 v2.BuildBundleV2(input) + v2.SignBundleV2(registry, bundle, classicalKey, pqKey) 对每个构件和清单的复合签名
VerifySignedBundle(sb, publicKeyB64) v2.VerifySignedBundleV2(registry, signedBundleJSON) 由策略驱动
N/A providers.Ed25519Provider / providers.MlDsa65Provider 上的 GenerateKeypair() 混合密钥
N/A v2.ComputeSecurityTier(input)、v2.SecurityTierInputFromPayloads(intent, policyDecision) 自适应等级(规范 §14.2)
N/A v2.DeriveKid(alg, publicKey) 确定性 kid

5.4 Rust SDK

v1.0 API v2.0 API 说明
sign_object(bundle, secret_key_b64) 每个构件执行 prepare_payload(dcp_ai::v2::signed_payload)+ composite_sign(ed, pq, context, payload_bytes, classical_key, pq_key)(dcp_ai::v2::composite_ops),然后 build_bundle_v2(&rpr, &passport, &intent, &policy, &audit_entries, session_nonce) + sign_bundle_v2(bundle, &classical_key, &pq_key)(dcp_ai::v2::bundle) 对每个构件和清单的复合签名
verify_signed_bundle(sb, public_key_b64) verify_signed_bundle_v2_with_policy(&signed_bundle, &policy)(dcp_ai::v2::verify_bundle) 由策略驱动

5.5 CLI 的变化

v1.0 命令 v2.0 命令 说明
dcp keygen [out_dir] dcp keygen --hybrid [out_dir] 生成两种类型的密钥
dcp sign-bundle dcp sign-bundle --composite <bundle_v2.json> <ed25519_sk> <ml_dsa_65_sk> [out] 通过 @dcp-ai/sdk 进行复合签名
dcp verify-bundle dcp verify-bundle [--policy <mode>] <signed.json> [public_key.txt] 完整的 V2 验证;<mode> 为 classical_only、hybrid_preferred、hybrid_required 或 pq_only;密钥用于固定经典签名者
N/A dcp kid <public_key_file> --alg <algorithm> 计算 kid
N/A dcp recovery-setup --threshold <M> --shares <N> [out] M-of-N 社交恢复
N/A dcp emergency-revoke --agent <id> --token <secret> 紧急按钮式吊销
N/A dcp rotate-key --old-kid <kid> --new-alg <alg> 带持有证明(PoP)的密钥轮换
N/A dcp capabilities <endpoint_url> 查询端点能力
N/A dcp advisory check [url] 检查算法公告

6. 测试你的迁移

6.1 验证检查清单

逐项检查以下清单,确认迁移已完成:

  • 密钥生成:混合密钥对(Ed25519 + ML-DSA-65)成功生成
  • kid 导出:deriveKid('ed25519', pk) 与期望值一致
  • kid 导出:deriveKid('ml-dsa-65', pk) 与期望值一致
  • RPR 构建:v2.0 RPR 包含 dcp_version、session_nonce 和两种类型的密钥
  • 护照构建:v2.0 护照包含 dcp_version 和 session_nonce
  • 意图构建:v2.0 意图包含 dcp_version 和 session_nonce(以及可选的、只能提高计算所得等级的 security_tier)
  • 复合签名:signBundleV2 生成有效的复合签名
  • 域分离:签名包含上下文标签
  • 复合绑定:PQ 签名覆盖经典签名的字节
  • 双哈希链:审计条目包含 prev_hash_secondary(SHA3-256);自 @dcp-ai/sdk 2.2.1、@dcp-ai/wasm 2.1.1、dcp-ai 2.10.1(PyPI)、Go v2.11.1 和 Rust dcp-ai 2.11.1 起,验证器会检查每个存在的链接(步骤 6);更早的版本不会
  • 清单:bundle 清单包含所有构件的哈希,使用双哈希链时还包含次级 Merkle 根,自 @dcp-ai/sdk 2.2.1、@dcp-ai/wasm 2.1.1、dcp-ai 2.10.1(PyPI)、Go v2.11.1 和 Rust dcp-ai 2.11.1 起,验证器会在该根存在时检查它
  • 会话 nonce:bundle 中的所有构件共享同一个 nonce
  • V2 验证:正确签名的 v2.0 bundle 能通过 verifySignedBundleV2(或 dcp verify-bundle)
  • V1 兼容性:v1.0 bundle 仍能通过 V1 函数 verifySignedBundle(只接受 v1.0)、dcp verify-bundle 或 3.2 中那样的按版本分流的路由器验证;V2 函数 verifySignedBundleV2 只接受 v2.0
  • 篡改检测:修改任何构件字段都会导致验证失败
  • 剥离检测:移除 PQ 签名(并把绑定改标为 classical_only)在 hybrid_required 和 pq_only 下失败,在 classical_only 和 hybrid_preferred 下被接受;存在但无效的 PQ 签名在任何模式下都会失败
  • NIST KAT:所有密码学操作都通过 KAT 验证
  • 跨 SDK:由一个 SDK 签署的 bundle 能在另一个 SDK 中通过验证

6.2 常见的迁移问题

问题:验证时出现“kid mismatch”

v2.0 的 kid 导出是确定性的:hex(SHA-256(UTF8(alg) || 0x00 || raw_pk))[0:32]。如果你的 v1.0 实现使用了其他 kid 方案,请用 v2.0 的公式重新生成 kid。SDK 的 deriveKid 函数会处理这一点。

问题:构件之间“session_nonce mismatch”

同一个 bundle 中的所有构件都必须(MUST)共享同一个 session_nonce。每创建一个 bundle 只生成一次 nonce,并把它传给所有构件的构造函数。

问题:“composite_sig.pq verification failed”

PQ 签名签署的是 context || 0x00 || payload || classical_sig。请确保在进行 PQ 签名之前,把经典签名的字节(原始字节,而非 base64)追加到被签名的消息中。

问题:v1.0 验证器报告“unsupported dcp_version 2.0”

这是预期行为。v1.0 验证器无法处理 v2.0 bundle。上游验证器需要升级到支持 v2.0 的 SDK。

问题:已签名载荷中的 float 值

v2.0 禁止在所有已签名载荷中使用浮点数(规范化存在歧义)。请把 risk_score: 0.75 换成整数刻度:risk_score: 750(0-1000)。

问题:bundle 体积较大

v2.0 bundle 更大,主要是由于 ML-DSA-65 签名(每个 3309 B)。请以验证器实际读取的格式测量你最大的 bundle,并为请求体设定明确的 大小预算(参见 OPERATOR_GUIDE.md)。参考验证服务器只读取 JSON 请求体,尽管其能力文档列出了 application/cbor;只有 TypeScript SDK 有 CBOR 编码器(cborEncode),本仓库没有公布它的实测体积缩减数据。 presentCompact 会移除 PQ 签名(绑定为 classical_only),因此这样的 bundle 只在有效模式为 classical_only 或 hybrid_preferred 时才能通过。传输大小不是降低安全等级的理由。

6.3 验证迁移后的 bundle

CLI 没有从 v1.0 到 v2.0 的迁移检查工具。请改用 V2 验证器验证每个迁移后的 bundle:

dcp verify-bundle --policy hybrid_required signed_bundle_v2.json

它会检查清单哈希、会话绑定、复合签名和审计链。risk_score 属于策略决定,是 0 到 1000 的整数;V2 意图模式不允许包含它(规范 §14.2)。


7. 推荐的迁移顺序

对于迁移生产系统的团队,推荐的顺序是:

  1. 更新 SDK 到支持 v2.0 的最新版本。
  2. 先更新验证器,并设置 allow_v1_bundles: true。这能确保你的验证器在过渡期间同时处理 v1.0 和 v2.0 bundle。
  3. 为所有智能体和人类生成混合密钥对。
  4. 更新构件的构建方式,加入 dcp_version: "2.0"、session_nonce 和双密钥条目。
  5. 使用 v2.0 签名 API 改用复合签名。
  6. 为审计条目加入双哈希链。
  7. 实现 bundle 清单结构。
  8. 用混合签名签署敏感意图。 V2 验证器会执行安全等级(规范 §14.3):对于支付、敏感数据、高影响或策略决定 risk_score 为 500 及以上的情况,classical_only bundle 会被拒绝。
  9. 运行完整的一致性测试,包括 NIST KAT 验证。
  10. 当生态系统中所有智能体都已迁移后,停止接受 v1.0 bundle。

8. 时间表与支持

阶段 时间 工作
准备 第 1-2 周 更新 SDK、生成混合密钥、阅读本指南
验证器升级 第 2-3 周 部署兼容 v1 的 v2.0 验证器
智能体迁移 第 3-6 周 更新构件构建方式,改用复合签名
验证 第 6-8 周 运行一致性测试和跨 SDK 互操作测试
切换 第 8 周及以后 准备就绪后设置 allow_v1_bundles: false

迁移支持: - GitHub Issues 或 Discussions:dcp-ai-protocol/dcp-ai - 通过 GitHub 的安全公告界面私下报告漏洞 - 按照 GOVERNANCE.md 给维护者发邮件

目前没有专门的 Slack、Discord 或答疑时间。当前的支持模式参见 EARLY_ADOPTERS.md。


参考资料