Pular para conteúdo

dcp-ai — SDK Rust

Publicação Rust (26/10/2026). Rust dcp-ai 2.11.0 está publicado no crates.io, incluindo correções do verificador, admissão e APIs experimentais opt-in. dcp-jcs-v1 continua sendo o perfil normativo padrão. Veja Rust 2.11.0.

Tradução informativa do README em inglês, atualizada em 02/11/2026. Identificadores e blocos de código, incluindo comentários, são preservados no idioma original para manter os exemplos verificados.

SDK oficial em Rust para o Digital Citizenship Protocol (DCP v2.0). Oferece tipos baseados em Serde, criptografia pós-quântica híbrida (Ed25519 + ML-DSA-65 + SLH-DSA-192f + ML-KEM-768), assinaturas compostas com vinculação pq_over_classical, cadeias de hash duplas, observabilidade com OpenTelemetry, comportamento DCP-04..09 e suporte opcional a WebAssembly.

A verificação confere a integridade e as assinaturas; não estabelece confiança no principal, admissão nem autorização para agir. O verificador V2 confere a cadeia de auditoria primária e, desde a 2.11.1, os elos, hashes de intenção e raiz Merkle secundários SHA3-256 opcionais que estejam presentes (os ausentes não são exigidos); a versão 2.11.0 não verifica os campos secundários. Consulte a limitação documentada do verificador e use as APIs de admissão abaixo quando for necessário estabelecer confiança.

Instalação

SDK Rust. Desde a 2.11.0, este SDK inclui construção e assinatura de bundles V2, admissão com confiança explícita no principal e strict-v1, cadeias de delegação, correções do verificador e APIs experimentais opt-in de dcp-jcs-v2. dcp-jcs-v1 continua normativo e padrão. Evidências inválidas aceitas por versões anteriores podem agora ser rejeitadas. A criptografia de sessões A2A e sua janela de replay não estão implementadas em Rust; as funções de descoberta e handshake continuam disponíveis.

SDK Rust 2.11.1. Esta versão também verifica os campos secundários SHA3-256 do VERIFICATION.md V2-11 quando estão presentes: audit_merkle_root_secondary, cada prev_hash_secondary e cada intent_hash_secondary. Os campos ausentes não são exigidos, então um bundle verificado pode não ter uma cadeia secundária completa; um campo presente que seja null, não seja uma string ou seja inconsistente é rejeitado. Não mudam as APIs, os campos de resultado, dcp-jcs-v1 nem o tratamento de hash_alg. A versão 2.11.0 não verifica esses campos. Os comandos de instalação abaixo indicam esta versão; para a disponibilidade dela no registro, veja Versões publicadas.

[dependencies]
dcp-ai = "2.11.1"

Com exportação OpenTelemetry/OTLP:

[dependencies]
dcp-ai = { version = "2.11.1", features = ["otlp"] }

Para suporte a WebAssembly:

[dependencies]
dcp-ai = { version = "2.11.1", features = ["wasm"] }

Funcionalidades

Área Estado
Provedores Ed25519 / ML-DSA-65 / SLH-DSA-192f / ML-KEM-768 Sim
Assinaturas compostas (pq_over_classical) e verificação Sim
JSON canônico v2 e separação de domínios Sim
Hash duplo (SHA-256 + SHA3-256) e raízes Merkle Sim
Verificação de bundles (V1 + V2; V2 sob uma política do verificador e o nível de segurança, verify_signed_bundle_v2_with_policy) Sim
Construção e assinatura de bundles V2 (dcp_ai::v2::bundle: build_bundle_v2, sign_bundle_v2; wasm_build_bundle / wasm_sign_bundle da funcionalidade wasm as chamam; ainda sem checkpoints PQ) Sim
Ciclo de vida dos agentes DCP-05 (comissionamento / vitalidade / descomissionamento) Sim
Sucessão digital DCP-06 (testamento, transferência de memória, cerimônia) Sim
Resolução de disputas, arbitragem e jurisprudência DCP-07 Sim
Direitos, obrigações e conformidade DCP-08 Sim
Delegação DCP-09 e cadeias de delegação verificáveis (create_sub_mandate, verify_mandate_chain, §3.5), limiar de consciência e espelho do principal Sim
Descoberta A2A DCP-04, handshake e derivação do identificador de sessão Sim
Criptografia de sessão A2A DCP-04 com AES-256-GCM Adiada para uma versão posterior
Funções auxiliares de nonce de sessão, mecanismo de níveis de segurança (compute_security_tier(&intent, Some(&policy_decision)), especificação §14.2; compute_security_tier_with_floor acrescenta o nível de uma cadeia de delegação), revogação de emergência Sim
Checkpoints PQ adiados e PQCheckpointManager Sim
RPR ocultado, autorização multipartes e funções auxiliares de avisos sobre algoritmos Sim
Códigos de erro canônicos (38 compartilhados entre todos os SDKs) e detect_wire_format Sim
Exportador OpenTelemetry / OTLP (funcionalidade opcional otlp) Sim
Admissão: principais confiáveis e perfil strict-v1 Sim
Candidato de canonicalização dcp-jcs-v2 (entrada, roteador, verificador de bytes, produtores) Experimental e de adesão explícita; não normativo

Início rápido

use dcp_ai::crypto::{generate_keypair, sign_object, verify_object, hash_object};
use serde_json::json;

fn main() {
    // 1. Generate Ed25519 keypair
    let (public_key_b64, secret_key_b64) = generate_keypair();
    println!("Public Key: {}", public_key_b64);

    // 2. Sign an object
    let obj = json!({
        "agent_id": "agent-001",
        "action": "api_call"
    });
    let signature = sign_object(&obj, &secret_key_b64).unwrap();

    // 3. Verify signature
    let valid = verify_object(&obj, &signature, &public_key_b64).unwrap();
    println!("Verified: {}", valid); // true

    // 4. SHA-256 hash
    let hash = hash_object(&obj);
    println!("SHA-256: {}", hash);
}

Verificar um bundle assinado

// Needs citizenship_bundle.signed.json in the working directory and the signer's public key in place of BASE64_PUBLIC_KEY.
use dcp_ai::verify::verify_signed_bundle;
use serde_json;
use std::fs;

fn main() {
    let data = fs::read_to_string("citizenship_bundle.signed.json").unwrap();
    let signed_bundle: serde_json::Value = serde_json::from_str(&data).unwrap();

    let result = verify_signed_bundle(&signed_bundle, Some("BASE64_PUBLIC_KEY"));
    println!("Verified: {}", result.verified);
    if let Some(errors) = &result.errors {
        println!("Errors: {:?}", errors);
    }
}

Referência da API

Criptografia (dcp_ai::crypto)

Função Assinatura Descrição
generate_keypair() () -> (String, String) Retorna (public_key_b64, secret_key_b64)
sign_object(obj, secret_key_b64) (&Value, &str) -> Result<String, String> Assina e retorna base64
verify_object(obj, sig_b64, pub_b64) (&Value, &str, &str) -> Result<bool, String> Verifica a assinatura
canonicalize(obj) (&Value) -> String JSON determinístico
hash_object(obj) (&Value) -> String SHA-256 hexadecimal
merkle_root_from_hex_leaves(leaves) (&[String]) -> Option<String> Raiz Merkle

Verificação (dcp_ai::verify)

// Signature (not runnable)
fn verify_signed_bundle(
    signed_bundle: &Value,
    public_key_b64: Option<&str>,
) -> VerificationResult

Verifica: assinatura Ed25519, bundle_hash, merkle_root, cadeia de intent_hash e cadeia de prev_hash.

Tipos (dcp_ai::types)

// Declarations, fields elided (not runnable)
pub struct ResponsiblePrincipalRecord { ... }
pub struct AgentPassport { ... }
pub struct Intent { ... }
pub struct IntentTarget { ... }
pub struct PolicyDecision { ... }
pub struct AuditEntry { ... }
pub struct AuditEvidence { ... }
pub struct CitizenshipBundle { ... }
pub struct SignedBundle { ... }
pub struct BundleSignature { ... }
pub struct Signer { ... }
pub struct VerificationResult {
    pub verified: bool,
    pub errors: Vec<String>,
}

Todas as estruturas implementam Serialize + Deserialize (serde).

Tipos V2 (dcp_ai::v2::types)

A V2 inclui estruturas derivadas de Serde para todos os artefatos de DCP-05 a DCP-09:

Especificação Tipos
DCP-05 LifecycleState, CommissioningCertificate, VitalityReport, VitalityMetrics, DecommissioningRecord, TerminationMode, DataDisposition
DCP-06 DigitalTestament, SuccessionRecord, MemoryTransferManifest, MemoryTransferEntry, SuccessorPreference, MemoryClassification, TransitionType, MemoryDisposition
DCP-07 DisputeRecord, ArbitrationResolution, JurisprudenceBundle, ObjectionRecord, DisputeType, EscalationLevel, DisputeStatus, ObjectionType, AuthorityLevel
DCP-08 RightsDeclaration, RightEntry, ObligationRecord, RightsViolationReport, RightType, ComplianceStatus
DCP-09 DelegationMandate, AdvisoryDeclaration, PrincipalMirror, InteractionRecord, AwarenessThreshold, ThresholdRule, AuthorityScopeEntry

Constantes de separação de domínios: CTX_LIFECYCLE, CTX_SUCCESSION, CTX_DISPUTE, CTX_RIGHTS, CTX_DELEGATION, CTX_AWARENESS

Todas as estruturas derivam Debug, Clone, Serialize e Deserialize. As enumerações usam #[serde(rename_all = "snake_case")]. Campos opcionais usam Option<T> com #[serde(skip_serializing_if = "Option::is_none")].

Entradas de auditoria recebidas (2.12.0 preparada, não publicada). AuditEventV2 e AuditEvidenceV2 servem para produzir entradas. Decodificar neles uma entrada recebida e codificá-la de novo perde um null explícito (em evidence, prev_hash_secondary e intent_hash_secondary) e qualquer membro desconhecido, e transforma um pq_checkpoint_ref ausente em null. Isso muda os bytes canônicos da entrada e pode quebrar a cadeia prev_hash ou a raiz Merkle de auditoria. Para guardar uma entrada recebida, use RawAuditEventV2: ele guarda o serde_json::Value decodificado, então cada membro, em qualquer profundidade, mantém sua presença e seu null, inclusive os desconhecidos, e serializa esse valor como está. Só rejeita o que não é um objeto JSON (RawAuditEventError). Mantém o que o serde_json decodificou, não o texto original, nunca repara nem assina de novo, e mantém a evidência inválida como está, para que um verificador a rejeite pelo mesmo motivo. typed() é uma visão com perdas para ler campos. Para guardar um bundle recebido, guarde-o como chegou e verifique isso.

Bundles (dcp_ai::v2::bundle)

Função Descrição
build_bundle_v2(&rpr, &passport, &intent, &policy, &audit_entries, session_nonce) Constrói o bundle V2 a partir dos quatro SignedPayload e da trilha de auditoria. Confere cada payload_hash e o session_nonce de cada artefato e entrada de auditoria e escreve o manifesto com ambas as raízes Merkle de auditoria; uma trilha vazia recebe raízes de 64 zeros
sign_bundle_v2(bundle, &classical, &pq) Aplica uma assinatura composta ao manifesto sob CTX_BUNDLE e retorna {bundle, signature}, com signer.id igual ao human_id da RPR. Coloque session_expires_at ou intended_verifier no manifesto antes de assinar; com session_expires_at e sem session_issued_at, define session_issued_at como o instante da assinatura

Validação de esquemas (V2-2; na main, não em uma versão publicada). verify_signed_bundle_v2, …_with_policy e …_at validam primeiro o envelope do bundle assinado e os quatro payloads contra schemas/v2, depois das verificações de política e de relógio e antes de o verificador conferir o manifesto, os hashes dos payloads, as chaves e as assinaturas; admit_signed_bundle_v2, que verifica primeiro, e o verificador experimental do candidato passam pela mesma barreira, enquanto evaluate_admission_v2 recebe um resultado anterior e não a executa. Um bundle que falha retorna verified: false com erros schema: <JSON pointer> <keyword>[ <member>] (no máximo 16, depois schema: further errors not listed) e sem detalhes criptográficos. Membros desconhecidos de objetos fechados são rejeitados; em objetos abertos, como o target do intent, os membros declarados mantêm suas restrições e apenas os adicionais não são inspecionados. session_issued_at e session_expires_at do manifesto devem ser strings quando presentes, não null. date-time segue a verificação do ajv-formats 3.0.1 do verificador de referência, que não é RFC 3339 estrito. Elas recebem um serde_json::Value já parseado: o que o serde_json se recusa a parsear (um surrogate isolado, 1e400) é um erro de parsing de quem chama. Mudança de aceitação: bundles inválidos pelo esquema que antes eram verificados agora são rejeitados. Não há API só de esquema. Veja o mapa de cobertura.

Cadeias de delegação (dcp_ai::v2::delegation, DCP-09 §3.5)

Função Descrição
create_delegation_mandate_with_options(ed, pq, human_classical, human_pq, params, &MandateChainOptions) Mandato raiz assinado pela pessoa, com sub_delegation e minimum_security_tier opcionais
create_sub_mandate(ed, pq, classical, pq_key, &parent, SubMandateParams { .. }) Submandato assinado pelo agente do mandato pai sob o contexto DCP-AI.v2.SubMandate
verify_mandate_chain(&chain, &revoked_ids, &MandateChainKeys { human_keys, agent_keys }, now_millis) Verifica a cadeia offline, começando pela raiz (assinaturas até a pessoa, vínculos e profundidade, sub_delegation, validade aninhada, restrição de ações, data_classes e limits, nível mínimo e revogação); retorna MandateChainResult { valid, errors, depth, effective_tier }

Admissão (dcp_ai::v2, spec/profiles/admission)

Determina se um bundle V2 verificado é admitido como evidência de um principal no qual o operador já confia, opcionalmente sob o perfil strict-v1. O conjunto de confiança, o perfil e a fonte de revogação vêm do operador, nunca do bundle nem da requisição.

Função Descrição
verify_signed_bundle_v2_at(&b, &policy, now_ms) verify_signed_bundle_v2_with_policy em now_ms (milissegundos desde a época Unix), usado em todas as verificações temporais; as outras duas funções de verificação usam o relógio do sistema. Retorna V2VerificationDetails: o mesmo V2VerificationResult em result, e signer_keys, que lista, para cada assinatura composta conferida, a chave resolvida de cada componente e se ela foi verificada. V2VerificationResult mantém sua forma de dez campos, e as outras duas funções mantêm suas assinaturas
PrincipalTrustStore::from_json(&json) Carrega um conjunto de confiança (schemas/v2/principal_trust_set.schema.json)
resolve_strict_v1_profile(&json) Carrega um perfil strict-v1 (schemas/v2/admission_profile.schema.json); policy(), verifier_id(), max_session_duration_seconds() e max_revocation_age_seconds() são somente leitura
revocation_subjects(&b, &verification) O agent_id do passaporte e os kids ordenados das chaves verificadas dos signatários, para a fonte de revogação do operador
evaluate_admission_v2(&b, &verification, &AdmissionOptions { .. }) Admissão a partir de um resultado de verificação, sem criptografia nem E/S
admit_signed_bundle_v2(&b, &AdmitOptions { .. }) Verifica em now_ms (relógio do sistema quando None) sob a política do perfil, ou sob policy sem um perfil, e então avalia

Um conjunto de confiança ou perfil que não pode ser carregado, um perfil sem conjunto de confiança e um perfil com política do chamador retornam AdmissionConfigError { code, message }. AdmissionResult é serializado com as chaves de §5.2 (verified, verification, principal_trust, profile, profile_compliance, admitted, action_authorized, recorded_decision, trust_set_version, errors); action_authorized é sempre false. A verifier_policy de um perfil pode restringir accepted_classical_algs e accepted_pq_algs, o que é aplicado pelo verificador; ela fixa min_classical, min_pq, accepted_hash_algs e max_key_age_days nos valores padrão.

// Fragment, inside a function returning a Result: profile_json, trust_set_json, signed_bundle, now_ms and revocation_source come from your application.
use dcp_ai::v2::{
    evaluate_admission_v2, resolve_strict_v1_profile, revocation_subjects, verify_signed_bundle_v2_at,
    AdmissionOptions, PrincipalTrustStore,
};

let profile = resolve_strict_v1_profile(&profile_json)?;
let trust = PrincipalTrustStore::from_json(&trust_set_json)?;
let verification = verify_signed_bundle_v2_at(&signed_bundle, profile.policy(), now_ms);
// Ask the operator's revocation source about these; None when it cannot answer.
let snapshot: Option<serde_json::Value> = revocation_source(&revocation_subjects(&signed_bundle, &verification));
let result = evaluate_admission_v2(
    &signed_bundle,
    &verification,
    &AdmissionOptions { profile: Some(&profile), trust: Some(&trust), revocation: snapshot.as_ref(), now_ms },
)?;
if result.admitted {
    // Authentic evidence from a trusted principal; not an authorization to act.
}

Experimental: candidato de canonicalização dcp-jcs-v2 (adesão explícita)

Não é normativo nem está ativo: dcp-jcs-v1 continua sendo o único perfil de canonicalização, e nenhum padrão, construtor, assinante ou verificador muda. Esses módulos estão disponíveis em 2.11.0 somente por adesão explícita. Um bundle candidato verificado não concede confiança nem admissão. Projeto e contrato: docs/proposals/canonicalization-candidate (§15 entrada, §16 roteador, §17 verificador, §18 produtores).

Módulo API
dcp_ai::v2::experimental_candidate_ingress experimental_candidate_ingress(&[u8]): CandidateIngressOutcome::Accept { value, canonical, canonical_sha256 } ou Reject { class, offset }
dcp_ai::v2::experimental_candidate_router CandidateRouter::new(CandidateRouterConfig { transport, accepted_profiles, historical }); decodificador HistoricalAdapter::serde_json_from_slice(), nenhum padrão
dcp_ai::v2::experimental_candidate_verify CandidateVerifier::new(config) e verify_at(input, policy, now_ms)
dcp_ai::v2::experimental_candidate_produce sign_payload(context, &CandidateInput, &CandidateKeys) e sign_bundle(&BundleParts, &BundleSigner, &BundleOptions); ProduceError::Reject para o domínio ou a forma das entradas (antes de executar qualquer assinante), Usage, Provider
use dcp_ai::providers::{ed25519::Ed25519Provider, ml_dsa_65::MlDsa65Provider};
use dcp_ai::v2::composite_ops::CompositeKeyInfo;
use dcp_ai::v2::crypto_provider::CryptoProvider;
use dcp_ai::v2::experimental_candidate_ingress::{experimental_candidate_ingress, CandidateIngressOutcome};
use dcp_ai::v2::experimental_candidate_produce::{sign_payload, CandidateInput, CandidateKeys};

fn main() {
    // The ingress decides on the original bytes: 1.0 is the integer 1, members are sorted.
    if let Ok(CandidateIngressOutcome::Accept { canonical, .. }) = experimental_candidate_ingress(br#"{"b":1.0,"a":[]}"#) {
        println!("{}", String::from_utf8(canonical).unwrap()); // {"a":[],"b":1}
    }
    if let Ok(CandidateIngressOutcome::Reject { class, offset }) = experimental_candidate_ingress(br#"{"a":1,"a":2}"#) {
        println!("{} {}", class.as_str(), offset); // DUPLICATE_KEY 7
    }

    // A producer signs an artifact from those bytes (hybrid keys, today's composite signature).
    let key = |p: &dyn CryptoProvider| {
        let k = p.generate_keypair().unwrap();
        CompositeKeyInfo { kid: k.kid, alg: p.alg().to_string(), secret_key_b64: k.secret_key_b64, public_key_b64: k.public_key_b64 }
    };
    let keys = CandidateKeys { classical: key(&Ed25519Provider), pq: Some(key(&MlDsa65Provider)) };
    let input = CandidateInput::Bytes(br#"{"intent_id":"intent:1","risk":1.0}"#.to_vec());
    let artifact = sign_payload("DCP-AI.v2.Intent", &input, &keys).unwrap();
    println!("{}", artifact.signed_payload["payload_hash"]); // "sha256:..." of {"intent_id":"intent:1","risk":1}
}

Funcionalidade wasm

Quando a funcionalidade wasm está habilitada, o crate expõe bindings WebAssembly:

// Declarations of the wasm-bindgen exports, bodies elided (not runnable)
// Available via wasm-bindgen
fn wasm_verify_signed_bundle(json: &str, pub_key: Option<String>) -> String;
fn wasm_verify_signed_bundle_v2(json: &str, policy_json: Option<String>) -> String; // default policy when None; V1 bundles are rejected under a policy
fn wasm_hash_object(json: &str) -> String;
fn wasm_generate_keypair() -> String;
fn wasm_build_bundle(rpr_json: &str, passport_json: &str, intent_json: &str, policy_json: &str, audit_entries_json: &str, session_nonce: &str) -> String;
fn wasm_sign_bundle(bundle_json: &str, classical_sk_b64: &str, classical_kid: &str, pq_sk_b64: &str, pq_kid: &str) -> String;

Compilar para WASM:

cargo build --target wasm32-unknown-unknown --features wasm

Desenvolvimento

# Build
cargo build

# Tests
cargo test

# Build with WASM
cargo build --features wasm

# Build for WASM target
cargo build --target wasm32-unknown-unknown --features wasm

Dependências

  • serde + serde_json — Serialização
  • sha2 — SHA-256
  • sha3 — SHA3-256 (cadeias de hash duplas)
  • ed25519-dalek — Criptografia Ed25519
  • fips203 / fips204 / fips205 — ML-KEM-768, ML-DSA-65 e SLH-DSA-192f (FIPS 203 / 204 / 205)
  • rand — Geração de números aleatórios
  • base64 — Codificação Base64
  • hex — Codificação hexadecimal
  • wasm-bindgen (opcional) — Bindings WASM

Licença

Apache-2.0