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.
Com exportação OpenTelemetry/OTLP:
Para suporte a WebAssembly:
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:
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çãosha2— SHA-256sha3— SHA3-256 (cadeias de hash duplas)ed25519-dalek— Criptografia Ed25519fips203/fips204/fips205— ML-KEM-768, ML-DSA-65 e SLH-DSA-192f (FIPS 203 / 204 / 205)rand— Geração de números aleatóriosbase64— Codificação Base64hex— Codificação hexadecimalwasm-bindgen(opcional) — Bindings WASM
Licença¶
Apache-2.0