chore(deps): move to trust-tasks 0.17, TDK 0.10, vta-sdk 0.31 - #258
Conversation
aa3a769 to
c7ddb49
Compare
Takes the newly published stack: affinidi-tdk 0.8.5 -> 0.10, affinidi-messaging-sdk 0.19 -> 0.21, trust-tasks-rs 0.11 -> 0.17, trust-tasks-capability-client 0.9 -> 0.17, vta-sdk 0.27 -> 0.31, the dev-only vta-service 0.19 -> 0.22 and affinidi-messaging-test-mediator 0.2 -> 0.4, plus argon2 0.5 -> 0.6 — after which no direct requirement in this workspace is behind its latest release except the two that are held deliberately (x25519-dalek at 2.x by the OpenPGP stack, rand at 0.8 by the crypto stack), both with the reason written against the line. argon2 derives key material, so it is worth saying why the bump is safe: Argon2id is a specified KDF and `derive_argon2_key` calls `hash_password_into`, its plain implementation. Same algorithm, version and params in, same 32 bytes out — the unlock code and the `ProtectedConfig` seed are byte-identical across it and every existing config still opens. What 0.6 breaks is the `password-hash` 0.6 PHC-string surface, which we do not touch. ## did-git-sign comes from VGI#33's head, not from crates.io `did-git-sign` 0.4.6 is the latest published release and requires `vta-sdk ^0.27`. That single edge re-splits vta-sdk, affinidi-tdk, affinidi-messaging-sdk and trust-tasks-rs into two copies each — and it does not merely fail to unify types, it fails to compile: vta-keys 0.2.9 does not build against vti-common 0.15. OpenVTC/verifiable-git-infrastructure#33 is the move that ends it — the VGI workspace onto vta-sdk 0.31 and TDK 0.10, no source change, green on its own pipeline. Rather than block on the publish, a `[patch.crates-io]` entry redirects `did-git-sign` (and its path-dep `vgi-core`) to that PR's head. The requirement in `openvtc/Cargo.toml` still names 0.4.6, which is the version VGI carries at that rev; the patch decides only where 0.4.6 comes from. Pinned by rev, never by branch, so a further push to that PR cannot silently change what this builds against — the same discipline VGI itself applies to `trql-client`. Both git sources are allow-listed in `deny.toml`, and `publish = false` here, so a git source costs this workspace nothing on its own release path. Delete the patch block and raise the floor to 0.4.7 once VGI publishes. That is the fourth consecutive cycle this obligation has come due, so the note is written as the rule rather than as this version's incident. With it, `cargo tree -i vta-sdk` and `cargo tree -i trust-tasks-rs` each resolve a single node. Remaining duplicates are upstream RustCrypto 0.x / 1.0 splits no line here controls. ## Source changes - `parse_envelope_reply` in trust-tasks-capability-client 0.17 folded the SPEC §4.9 correlation check into the parse and now takes the thread id the caller is waiting on. The inbound dispatch is a fan-in point that waits on nothing in particular, so it reads the document first and classifies against the document's own `threadId`. The correlation that matters is unchanged: `apply_capability_replies` still matches the reply to the open view's `pending_thid` and drops anything else. - `JoinRequestStatusResponseBody` gained `code`, `reason` and `decided_at` (vta-sdk 0.31) — refusal detail carried on a `rejected` status. The e2e helper does not exercise a rejection, so it sends none. - vta-sdk 0.31 stopped accepting `VtaClient::new` + `set_token` for a client that dispatches Trust Tasks. A bearer token authenticates the *connection*; SPEC §7.2 items 5b and 7a want an in-band `recipient` and a document `proof`, which the client can only produce from a `ClientIdentity`. The setup wizard's REST arm was hand-rolling exactly that shape — it authenticates separately because it needs the token itself to cache — so the very next dispatch on that client, the context probe, would have failed with "authenticated but carries no ClientIdentity". It now builds the client the way `connect_auto`'s REST arm does. The two `mockvta_bootstrap_e2e` clients had the same shape and are the reason this was caught: they are `#[ignore]`d, so only the coverage job (`--include-ignored`) runs them, and they now authenticate as a real `did:key` whose token is minted for that same DID — item 6 rejects a document whose in-band issuer disagrees with the identity the transport authenticated as. - Rust 1.98's clippy flags the nested `if let` in the envelope dispatch as `collapsible_if`; it is a let chain now, which edition 2024 has had since well before the 1.95 MSRV. ## Testing cargo fmt / clippy (on 1.98, the toolchain that flagged the lint) / doc with `-D warnings` / cargo-deny (advisories, licenses, bans, sources) / `cargo check` on the 1.95 MSRV / `cargo bench --no-run` all clean. `cargo test --workspace --tests -- --include-ignored` — 921 passing, 0 failed, including the three MockVta bootstrap e2es that the coverage job runs. `cargo test --workspace --no-default-features` clean. Signed-off-by: Glenn Gore <glenn.g@affinidi.com> Claude-Session: https://claude.ai/code/session_016rR4AnzuwoVXX1MU8aaRKB
c7ddb49 to
f7de8c5
Compare
🛡️ AI Agentic Security Code Review1 AI-confirmed issue, 1 finding needs a human to review/validate. Mandatory to check: 🔒 Security Code Review Report Details🛡️ Security Code Review Report — PR #258
🗺️ Scan CoverageModules scanned: 3 · with findings: 1 · files: 10 · findings: 3
Executive Summary
🔒 Security IssuesConfirmed Vulnerabilities (1)🔵 Authentication now requires cryptographic ClientIdentity binding for VtaClient (mitigation observed, not a vulnerability)
📝 Description: The diff shows a shift from token-only VtaClient::new()+set_token() to VtaClient::authenticated() with a ClientIdentity carrying a private key. This is a security improvement (binding the bearer token to a signing identity) rather than an introduced vulnerability. No spoofing/broken-authentication flaw is evidenced in the changed code; the change removes a weaker pattern. 🌱 Root Cause: N/A — this reflects a fix, not a new weakness. The old pattern (token-only, no in-band proof) would have matched CWE-346/CWE-287 more closely, but that code is removed, not added. 🔎 Evidence: 🎯 Attack Scenario: Not applicable to the code as changed; documented here only because the threat model flagged authentication-related CWEs against code that now includes identity binding.
|
| Field | Detail |
|---|---|
| Severity | MEDIUM |
| Location | openvtc/src/state_handler/message_dispatch.rs:3 |
| Finding ID | github_pr-e9b052b4bf3f |
| CWE | CWE-770, CWE-400 |
| OWASP | A04:2021 - Insecure Design |
| MITRE ATT&CK | T1499 |
| CAPEC | CAPEC-125, CAPEC-482 |
| CVSS 4.0 | 5.3 (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N) |
| Reachability | 🔴 Reachable |
| Exploit Maturity | poc |
| Detection Source | skill_scan |
Summary: The message dispatcher performs document parsing and queue insertion for every inbound message matching the Trust-Task envelope type with no visible rate limiting or queue size bound, allowing a flood of such messages to degrade dispatcher performance and grow memory usage.
🔎 Evidence: openvtc/src/state_handler/message_dispatch.rs:3
if message.typ == openvtc_core::capabilities::TRUST_TASK_ENVELOPE_TYPE {
if let Some((thid, doc)) = openvtc_core::capabilities::parse_envelope_document(&message.body)
&& let Some(reply) = openvtc_core::capabilities::parse_capability_reply(&doc, &thid)
{ capability_replies.push((thid, reply)); }
}
🧭 Reachability:
- Network exposure: public
- Auth barrier: basic
- Attack path: EP-001 (any DIDComm message with typ=TRUST_TASK_ENVELOPE_TYPE reaching this handler) -> parse_envelope_document/parse_capability_reply (per-message allocation) -> capability_replies.push (unbounded growth)
🔧 Remediation:
⚠️ AI-generated fix. Review, test in staging, and validate against your architecture before applying.
Vulnerable code:
if message.typ == openvtc_core::capabilities::TRUST_TASK_ENVELOPE_TYPE {
if let Some((thid, doc)) = openvtc_core::capabilities::parse_envelope_document(&message.body)
&& let Some(reply) = openvtc_core::capabilities::parse_capability_reply(&doc, &thid)
{ capability_replies.push((thid, reply)); }
}
Secure code:
if message.typ == openvtc_core::capabilities::TRUST_TASK_ENVELOPE_TYPE {
if !rate_limiter.allow(&message.from) {
tracing::warn!(sender = ?message.from, "rate limit exceeded for trust-task envelope");
return;
}
if capability_replies.len() >= MAX_PENDING_REPLIES {
capability_replies.retain(|(_, r)| !r.is_stale());
}
if let Some((thid, doc)) = openvtc_core::capabilities::parse_envelope_document(&message.body)
&& let Some(reply) = openvtc_core::capabilities::parse_capability_reply(&doc, &thid)
{
capability_replies.push((thid, reply));
}
}
Additional recommendations:
- Add mediator-level and application-level rate limiting per sender DID
- Add metrics/alerting on abnormal envelope throughput
- Cap capability_replies size with time-based eviction of stale/unmatched entries
🔍 Validation Log
- Verdict:
⚠️ Must-Review-By-Human- Confidence: 45%
- AI Validation Evidence: EVIDENCE FOUND: The TRUST_TASK_ENVELOPE_TYPE branch in message_dispatch.rs executes parse_envelope_document and parse_capability_reply for every message of that type with no visible per-sender rate limiting or queue size cap at this handler:
capability_replies.push((thid, reply));. Earlier in process_inbound_message there is a body-size guard (if body_size > MAX_MESSAGE_BODY_SIZE) and a replay/dedup guard (seen.observe(&message.id)), which do provide some throttling against unbounded resource use, but no explicit rate-limit specific to this envelope type or bound on capability_replies Vec growth is visible. EVIDENCE NOT FOUND: The mediator-level throttling (affinidi-messaging-mediator) and the definition of parse_envelope_document/parse_capability_reply (cost of parsing) are not in provided files, so actual resource cost per call and whether an upstream mediator enforces per-DID quotas cannot be verified. CHANGED VS PRE-EXISTING: CHANGED — message_dispatch.rs, including this exact branch, is present in the MR's source files. VERDICT JUSTIFICATION: Partial evidence exists both for (existing MAX_MESSAGE_BODY_SIZE and seen-message dedup mitigate some DoS) and against (no per-type/per-sender rate limit visible) the finding; this is inconclusive without visibility into mediator-level protections, so a human should review.- Validation Effort: This finding was validated up to a point, but the available evidence was insufficient for a conclusive automated verdict. A human (developer / security team) must manually review the code and decide. Not dismissed — treat as an open item pending human review.
Generated by Agentic Sec — AI Security Validation Agent
This report includes full scan data + AI validation evidence. Feed to engineering copilots for automated fix deployment.
Details
🛡️ Threat Model & Affect Analysis — PR #258
| Field | Value |
|---|---|
| Repository | OpenVTC/openvtc |
| Branch | chore/deps-refresh-2026-08 → main |
| Generated | 2026-08-28 |
ℹ️ This report contains theoretical threats and impact analysis for the MR.
Unlike the Security Code Review Report (which contains confirmed, materialised issues),
these are potential risks that may or may not be exploitable. Use this for defence-in-depth planning.
📋 Affect Analysis
Change Summary
This PR is a broad dependency refresh (trust-tasks 0.17, TDK 0.10, vta-sdk 0.31) that cascades into: (1) a security-positive authentication hardening refactor replacing bearer-token-only VtaClient construction with identity-bound ClientIdentity signing across production (setup_vta_actions.rs) and test (mockvta_bootstrap_e2e.rs) code, (2) a message-correlation logic split in message_dispatch.rs to accommodate the new trust-tasks-capability-client 0.17 API, and (3) a large Cargo.lock churn (50+ packages) including a supply-chain-relevant switch of did-git-sign to an unpublished git commit and several coexisting major-version splits in security-relevant crates (jsonwebtoken, argon2, base64, sha1, blake2).
Diff: +245 / -32 lines
Types: dependency_upgrade, security, refactor
📁 File Classifications
Cargo.lock
- Type: config
openvtc-core/Cargo.toml
- Type: config
openvtc-core/src/capabilities.rs
- Type: security
openvtc-core/tests/join_lifecycle_e2e.rs
- Type: test
openvtc-core/tests/mockvta_bootstrap_e2e.rs
- Type: test
🛡️ STRIDE Threat Model
Identified Threats (12)
⚪ STRIDE-1: Git Source Dependency Pinned to Mutable Ref Override in did-git-sign Patch
| Field | Detail |
|---|---|
| Category | Tampering, Elevation of Privilege |
| Severity | High |
| Likelihood | Possible |
| CVSS | 8.1 CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Medium |
| CWE | CWE-829,CWE-494 |
| CAPEC | CAPEC-538,CAPEC-184 |
| OWASP | A08:2021 - Software and Data Integrity Failures |
Description: Cargo.lock [patch.crates-io] override for did-git-sign in vulnerable system component openvtc-core/openvtc build pipeline allows a repository-hijack-and-replace attack due to sourcing an unpublished commit directly from a git URL instead of a signed crates.io release, resulting in supply chain compromise of the DID-based git commit signing component.
Evidence: Cargo.lock:2705-2708
name = "did-git-sign"
version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c286e9d2e2257a8b08c318da6c92171fbec55fc864163ba58ee6555c9d5c3a3"
+source = "git+https://github.com/OpenVTC/verifiable-git-infrastructure?rev=462032cfd4adcec9bc00ee957e2e60b7e42c7da
Attack Scenario:
- Attacker identifies that Cargo.lock pins
did-git-signtogit+https://github.com/OpenVTC/verifiable-git-infrastructure?rev=462032cfd4adcec9bc00ee957e2e60b7e42c7dacrather than a crates.io published+checksummed release. - Attacker gains push/force-push access to the
OpenVTC/verifiable-git-infrastructurerepo (via compromised maintainer credentials, leaked CI token, or GitHub org takeover) or performs a rev-hash collision/rewrite of history at that ref. - Because a git dependency has no cryptographic checksum verification in Cargo.lock (unlike registry deps which carry a
checksumfield), Cargo trusts the content fetched from the URL+rev at build time. - Attacker rewrites the commit contents while keeping (or force-pushing over) the pinned rev, or exploits a case where CI resolves
revloosely. - Malicious code (e.g., a backdoored
did-git-signimplementing git commit signing) is compiled into openvtc and openvtc-core binaries used to sign trust-critical git commits with a private key, enabling forged/attacker-signed commits to propagate as legitimately verified. - Downstream consumers trust the compromised signing output because the DID document/commit-signing chain appears valid.
🔎 Threat Clue: Derived from COMP-002 via EP-005
- Data Flows: Build pipeline -> compiled openvtc/openvtc-core binary
Preconditions: Attacker has write or history-rewrite capability on OpenVTC/verifiable-git-infrastructure, CI/build pipeline fetches git dependency without pinned-content verification, Patch override at workspace root is not independently reviewed at every build
Existing Controls: deny.toml allow-lists the specific git source (per code comment its git source is allow-listed in deny.toml) • Pinned to a specific commit rev rather than a branch/tag
Recommended Mitigations: Vendor or mirror the git dependency with a verified, immutable content hash pin (e.g., via cargo vendor + checksum verification) • Require dependency to be published on crates.io with signed releases before merging to main • Add CI check that fails the build if the resolved commit hash of the git source changes unexpectedly between runs • Enable branch protection and required commit signing on verifiable-git-infrastructure repo • Monitor and alert on any force-push or history rewrite events on the pinned repository
⚪ STRIDE-2: Bearer-Token-Only Client Construction Bypasses Document Proof Binding in setup_vta_actions
| Field | Detail |
|---|---|
| Category | Spoofing, Tampering |
| Severity | High |
| Likelihood | Likely |
| CVSS | 7.6 CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-346,CWE-287 |
| CAPEC | CAPEC-196,CAPEC-593 |
| OWASP | A07:2021 - Identification and Authentication Failures |
Description: VtaClient construction in setup_vta_actions.rs in vulnerable system component openvtc VTA state handler allows trust-task document spoofing due to the previous pattern (VtaClient::new + set_token) authenticating only the transport connection without binding an in-band recipient/proof to a real signing identity, resulting in potential issuer/identity confusion for dispatched Trust-Task documents.
Evidence: openvtc/src/state_handler/setup_vta_actions.rs:~1-30
/* [REMOVED] let client = VtaClient::new(&vta_url); */
/* [REMOVED] client.set_token(token_result.access_token); */
VtaClient::authenticated(&vta_url, ClientIdentity { client_did: admin.admin_did.clone(), private_key_multibase: admin.admin_private_key_mb.clone(), vta_did: vta_did.clone() }, token_re
Attack Scenario:
- Historical code path
VtaClient::new(&vta_url); client.set_token(token_result.access_token)(removed in this diff) authenticates only the connection via bearer token. - Because SPEC §7.2 items 5b/7a require an in-band
recipientand cryptographicproofon the dispatched Trust-Task document, and the bearer-token-only client cannot produce those, any code path still using this pattern (e.g., other call sites, rollback, or future regressions) would dispatch documents that either fail item 6 (in-band issuer disagreeing with the authenticated identity) or, worse, succeed if the VTA server's validation of issuer-vs-connection-identity binding is not strictly enforced. - An attacker who compromises or replays a valid bearer token (e.g., via token leakage, insufficient TLS, or log exposure) could potentially dispatch Trust-Task documents that the server accepts under a different implied identity than the actual token holder if server-side binding checks are lenient or inconsistently applied across code paths.
- The new pattern (
VtaClient::authenticatedwithClientIdentity) mitigates this specific occurrence, but the underlying architectural risk is that authentication (transport) and authorization/identity-binding (document proof) are two separate mechanisms that must always be kept in lockstep across the entire codebase — any remaining or future call site using the old pattern reintroduces the gap. - Grep across the workspace for any remaining
VtaClient::new+set_tokenusage that was not migrated in this diff would reveal live instances of the vulnerable pattern.
🔎 Threat Clue: Derived from COMP-002, COMP-007 via EP-002, EP-003
- Data Flows: VTA authentication response -> VtaClient construction -> Trust-Task dispatch
Preconditions: A code path still using bearer-token-only client construction exists or is reintroduced, VTA server does not uniformly enforce in-band issuer == authenticated identity for all Trust-Task types, Attacker has obtained or can replay a valid bearer token
Existing Controls: This diff migrates the vulnerable call site in setup_vta_actions.rs to VtaClient::authenticated with ClientIdentity • Comment in code documents the SPEC §7.2 items 5b/7a requirement explicitly
Recommended Mitigations: Audit entire workspace for any remaining VtaClient::new(...).set_token(...) pattern and migrate to VtaClient::authenticated • Add a compile-time or lint-time guard (e.g., deprecate/remove set_token/set_token_async API) to prevent regression • Enforce server-side rejection of any Trust-Task document whose in-band issuer does not match the authenticated connection identity, for all document types, not just some • Add integration test asserting that bearer-token-only clients cannot successfully dispatch privileged Trust-Task documents
⚪ STRIDE-3: Private Key Multibase Cloned into ClientIdentity Struct in setup_vta_actions and Test Harness
| Field | Detail |
|---|---|
| Category | Information Disclosure |
| Severity | Medium |
| Likelihood | Possible |
| CVSS | 6.2 CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-316,CWE-226 |
| CAPEC | CAPEC-37,CAPEC-545 |
| OWASP | A02:2021 - Cryptographic Failures |
Description: ClientIdentity construction in setup_vta_actions.rs and mockvta_bootstrap_e2e.rs in vulnerable system component openvtc admin/setup wizard allows private key exposure due to plaintext String cloning of admin_private_key_mb/private_key_multibase without using a zeroizing/secrecy wrapper, resulting in private key material persisting in process memory, core dumps, or swap longer than necessary.
Evidence: openvtc/src/state_handler/setup_vta_actions.rs:~15-28
ClientIdentity {
client_did: admin.admin_did.clone(),
private_key_multibase: admin.admin_private_key_mb.clone(),
vta_did: vta_did.clone(),
},
token_result.access_token,
Attack Scenario:
- Admin private key is generated or loaded and stored as a plain
Stringfieldadmin_private_key_mbon a state struct. setup_vta_actions.rsclones this value directly intoClientIdentity { private_key_multibase: admin.admin_private_key_mb.clone(), ... }with no use ofsecrecy::Secretorzeroize::Zeroizing, despitesecrecybeing a workspace dependency (per openvtc-core/Cargo.toml).- Multiple heap-allocated copies of the raw private key now exist (original + clone), each independently garbage-collected by Rust's normal drop semantics without memory zeroization.
- An attacker with local process memory access (e.g., via a core dump after a crash, a coredump-capturing crash reporter, a co-located container escape, or a debugger attached to the process) can recover the plaintext private key from any of these un-zeroized memory regions.
- Recovered private key allows the attacker to impersonate the admin DID (
did:key:...) in any future Trust-Task document signing or VTA authentication flow. - This is compounded in
mockvta_bootstrap_e2e.rswhereEphemeralSetupKey::generate()andadmin.private_key_multibase()also flow intoClientIdentitywithout wrapper types, though test-context risk is lower.
🔎 Threat Clue: Derived from COMP-002 via EP-002
- Data Flows: Admin key generation -> state struct -> ClientIdentity construction
Preconditions: Attacker gains local memory read access to the running openvtc process (core dump, debugger, memory disclosure bug, container escape), Private key is not already protected by OS-level secure memory (mlock) or hardware-backed keystore
Existing Controls: secrecy crate is already a workspace dependency, indicating some awareness of secret-handling needs • Keys are ephemeral in the test-support paths (EphemeralSetupKey)
Recommended Mitigations: Wrap private_key_multibase fields in secrecy::SecretString or zeroize::Zeroizing<String> end-to-end from generation through ClientIdentity construction • Implement Drop with explicit zeroization on any struct holding key material • Avoid .clone() on raw key material; pass by reference or move ownership where possible • Use OS-level mlock/mlockall for pages holding key material where supported • Disable core dumps for production processes handling private keys (ulimit -c 0 / RLIMIT_CORE=0)
⚪ STRIDE-4: Missing Sender Correlation Enforcement in Trust-Task Envelope Parsing in message_dispatch
| Field | Detail |
|---|---|
| Category | Spoofing, Tampering |
| Severity | Medium |
| Likelihood | Possible |
| CVSS | 6.9 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-345,CWE-346 |
| CAPEC | CAPEC-137,CAPEC-593 |
| OWASP | A08:2021 - Software and Data Integrity Failures |
Description: TRUST_TASK_ENVELOPE_TYPE branch in message_dispatch.rs in vulnerable system component openvtc message dispatcher allows uncorrelated or forged capability reply injection due to parsing the envelope document and classifying it against its own self-declared threadId before any correlation check against pending_thid, resulting in acceptance of attacker-supplied documents into the capability_replies queue prior to downstream filtering.
Evidence: openvtc/src/state_handler/message_dispatch.rs:1-14
if let Some((thid, doc)) = openvtc_core::capabilities::parse_envelope_document(&message.body)
&& let Some(reply) = openvtc_core::capabilities::parse_capability_reply(&doc, &thid)
{
capability_replies.push((thid, reply));
}
Attack Scenario:
- A DIDComm message of type
TRUST_TASK_ENVELOPE_TYPEarrives atmessage_dispatch.rsfrom any peer able to reach the messaging transport (mediator-routed DIDComm). openvtc_core::capabilities::parse_envelope_document(&message.body)extracts a(thid, doc)pair purely from the message body's self-declaredthreadIdfield — this value is attacker-controlled since it originates from the message payload, not from a server-verified session context.parse_capability_reply(&doc, &thid)then validates the document's structure and classification against that same self-declaredthid, producing a(thid, reply)tuple.- This tuple is unconditionally pushed into
capability_replies.push((thid, reply))— i.e., the untrustedthidfrom step 2/3 is stored without first checking it against any locally-known set of outstanding/expected thread IDs at this dispatch layer. - The code comment explicitly states correlation is deferred to
apply_capability_replies(elsewhere) matching againstpending_thid, meaning between push and that later check, an attacker-forged or replayed envelope with an arbitrary/duplicatedthidsits in the queue. - If
apply_capability_replies's matching logic has any weakness (e.g., accepts the first match, does not check message provenance/signature freshness, or silently drops without alerting), an attacker who can inject a DIDComm message with athidvalue copied from an observed legitimate pending request (via traffic observation on a shared mediator) could inject a spoofed capability reply that races the real one or overwrites state, since JSON parsing occurs before authentication-equivalent correlation. - Because the parse-then-classify step trusts the message body's own DIDComm envelope signature. If the DIDComm layer's proof/signature verification is not independently confirmed to run in
parse_envelope_document/parse_capability_reply(not shown in provided source), this is a Tampering/Spoofing vector; if it is confirmed elsewhere, the risk narrows to a Denial-of-Service/queue-poisoning concern from acceptance of many uncorrelated documents.
🔎 Threat Clue: Derived from COMP-002 via EP-001
- Data Flows: Inbound DIDComm message -> parse_envelope_document -> parse_capability_reply -> capability_replies queue -> apply_capability_replies
Preconditions: Attacker can send or replay a DIDComm message of TRUST_TASK_ENVELOPE_TYPE to the dispatcher (e.g., via the mediator or a compromised intermediary), DIDComm-level signature/proof verification either does not occur before parse_envelope_document/parse_capability_reply, or occurs but does not bind thid to sender identity, apply_capability_replies' downstream correlation is the sole safety net and may accept the first matching thid without additional identity checks
Existing Controls: Correlation against pending_thid is enforced downstream in apply_capability_replies per code comment • trust-tasks-capability-client 0.17 folds a correlation check into parse_envelope_document/parse_capability_reply (partial mitigation, exact semantics not visible in this diff) • DIDComm message envelope presumably carries cryptographic proof at the messaging-stack layer (affinidi-messaging-didcomm)
Recommended Mitigations: Verify and document that parse_envelope_document enforces DIDComm sender-authentication/proof verification before returning a usable (thid, doc) pair • Add an explicit allow-list check of pending thread IDs at the point of capability_replies.push, not only in apply_capability_replies • Rate-limit or cap the size of the capability_replies queue to prevent unbounded growth from spam/replay • Add structured logging (with message sender DID) at ingestion time to support forensic correlation and repudiation defenses • Add a unit/integration test that asserts a Trust-Task envelope with an unknown/foreign thid is rejected at ingestion, not just at apply time
⚪ STRIDE-5: Unauthenticated Message Type Matching Enables Trust-Task Envelope Flooding in message_dispatch
| Field | Detail |
|---|---|
| Category | Denial of Service |
| Severity | Medium |
| Likelihood | Likely |
| CVSS | 5.3 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-770,CWE-400 |
| CAPEC | CAPEC-125,CAPEC-482 |
| OWASP | A04:2021 - Insecure Design |
Description: TRUST_TASK_ENVELOPE_TYPE type-check entry point in message_dispatch.rs in vulnerable system component openvtc message dispatcher allows resource exhaustion via message flooding due to the branch performing document parsing/allocation for every message matching the type string before any rate limiting or sender reputation check, resulting in CPU/memory exhaustion under high-volume spam.
Evidence: openvtc/src/state_handler/message_dispatch.rs:1-14
if message.typ == openvtc_core::capabilities::TRUST_TASK_ENVELOPE_TYPE {
if let Some((thid, doc)) = openvtc_core::capabilities::parse_envelope_document(&message.body)
&& let Some(reply) = openvtc_core::capabilities::parse_capability_reply(&doc, &thid)
{ capability_replies.push((thid, rep
Attack Scenario:
- Attacker with access to the shared DIDComm mediator (any registered participant, since mediators typically relay for any onboarded DID) crafts many messages with
typ == TRUST_TASK_ENVELOPE_TYPE. - Each message triggers
parse_envelope_document(&message.body)and, if that succeeds,parse_capability_reply(&doc, &thid)— both non-trivial JSON/DIDComm parsing operations performed per message with no visible rate limit at this layer. - Attacker sends messages in a tight loop, each allocating parsed document structures and, on partial success, growing the
capability_repliesVec. - Sustained flooding degrades dispatcher responsiveness, delays legitimate Trust-Task correlation (since
apply_capability_repliesmust scan a growing/poisoned queue), and can exhaust memory if the Vec has no upper bound. - Legitimate VTA operations (context creation, credential lifecycle actions) relying on timely message dispatch are degraded, impacting the availability objective of the trust framework.
🔎 Threat Clue: Derived from COMP-002 via EP-001
- Data Flows: Inbound DIDComm message flood -> message_dispatch -> capability_replies growth
Preconditions: Attacker can register with or route messages through the shared DIDComm mediator, No rate limiting, quota, or reputation scoring exists at the message_dispatch layer for this message type, capability_replies Vec has no bounded capacity or eviction policy
Existing Controls: Underlying affinidi-messaging-mediator likely provides some mediator-level throttling (not verifiable from this diff) • Message type matching restricts processing to only TRUST_TASK_ENVELOPE_TYPE, limiting blast radius to this specific handler
Recommended Mitigations: Add per-sender rate limiting/backpressure at the message_dispatch layer for TRUST_TASK_ENVELOPE_TYPE messages • Bound the size of capability_replies and evict oldest/stale unmatched entries on a timer • Add sender reputation or allow-list gating for capability envelope processing tied to known/expected DIDs • Add metrics/alerting on abnormal envelope-processing throughput to enable detection
⚪ STRIDE-6: Super-Admin Token Minting with Empty Context Vec in Test Harness Reveals Design Pattern Risk in mockvta_bootstrap_e2e
| Field | Detail |
|---|---|
| Category | Elevation of Privilege |
| Severity | Medium |
| Likelihood | Possible |
| CVSS | 5.9 CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Medium |
| CWE | CWE-269,CWE-1284 |
| CAPEC | CAPEC-122,CAPEC-233 |
| OWASP | A01:2021 - Broken Access Control |
Description: mint_token call with empty contexts vec in mockvta_bootstrap_e2e.rs in vulnerable system component VTA test harness/production token-minting logic allows implicit super-admin elevation due to an empty scope list being interpreted as unrestricted top-level authority rather than "no access", resulting in a design pattern that, if mirrored in production token issuance, would allow privilege escalation from a misconfigured or default token request.
Evidence: openvtc-core/tests/mockvta_bootstrap_e2e.rs:~5-10
// `mint_token` with an empty contexts vec is super-admin (top-level context creation is super-admin only)
let admin = EphemeralSetupKey::generate().expect("generate admin key");
let token = mock.ctx.mint_token(&admin.did, "admin", vec![]).await;
Attack Scenario:
- Test code documents: 'mint_token with an empty contexts vec is super-admin (top-level context creation is super-admin only)'.
- This confirms the production
mint_token-equivalent API on the real VTA server treats an empty/missingcontextsparameter as a super-admin grant rather than defaulting to zero privileges (fail-open on empty input). - If any production code path (e.g., a client library, migration script, or admin API caller) constructs a token request and fails to populate
contextsdue to a bug, missing input validation, or an attacker-controlled API call that omits the parameter, the resulting token silently receives super-admin rights instead of being rejected. - An attacker able to influence or trigger token-minting calls (e.g., via a vulnerable onboarding flow, an API that proxies token requests, or an SSRF/parameter-injection bug elsewhere) could omit or null out the contexts field to request/obtain a super-admin token instead of a scoped one.
- With a super-admin token, the attacker can create top-level contexts and perform administrative Trust-Task operations across the entire VTA instance, far beyond any intended scope.
🔎 Threat Clue: Derived from COMP-003, COMP-007 via EP-004
- Data Flows: Token-mint request -> VTA server authorization decision -> issued JWT/access token
Preconditions: A production caller can influence the contexts parameter passed to the token-minting API (directly or transitively), Fail-open interpretation of empty context list as super-admin exists in the real (non-mock) VTA server implementation, not just the test mock, No secondary authorization gate validates that a caller requesting super-admin scope is entitled to it
Existing Controls: This behavior is currently only exercised in test/mock code (MockVta), so production impact is unconfirmed from the provided diff • Comment explicitly documents the semantics, aiding future reviewers/auditors
Recommended Mitigations: Change the token-minting API semantics to fail-closed: require an explicit, non-empty super-admin marker/flag rather than inferring it from an empty contexts list • Add server-side validation rejecting token-mint requests where the semantic intent (super-admin vs scoped) is ambiguous • Add an explicit integration test asserting that a malformed/omitted contexts field is rejected rather than granted elevated privilege • Log and alert on every super-admin token issuance in production with full requester context for auditability
⚪ STRIDE-7: Split JWT Library Versions (jsonwebtoken 10.4.0 and 11.0.0) Coexisting in Dependency Graph
| Field | Detail |
|---|---|
| Category | Tampering, Elevation of Privilege |
| Severity | Medium |
| Likelihood | Possible |
| CVSS | 5.1 CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-1104,CWE-345 |
| CAPEC | CAPEC-459,CAPEC-115 |
| OWASP | A08:2021 - Software and Data Integrity Failures |
Description: Cargo.lock dependency graph in vulnerable system component openvtc build allows inconsistent JWT validation behavior due to two divergent jsonwebtoken crate versions (10.4.0 and 11.0.0) being linked simultaneously by different transitive dependencies, resulting in unpredictable or inconsistent token validation logic (e.g., algorithm confusion, differing default validation settings) depending on which code path processes a given token.
Evidence: Cargo.lock:4459-4477
[[package]]
name = "jsonwebtoken"
version = "11.0.0"
...
dependencies = [ "aws-lc-rs", "base64 0.22.1", "getrandom 0.2.17", "js-sys", "pem", "serde", "serde_json", "signature 2.2.0", "simple_asn1", "zeroize" ]
Attack Scenario:
- Cargo.lock shows
affinidi-messaging-mediatorandaffinidi-messaging-test-mediatordepend onjsonwebtoken 10.4.0while a separate dependency chain pulls injsonwebtoken 11.0.0(withaws-lc-rs,pem,simple_asn1etc.). - Each version may have different default
Validationstruct behaviors (e.g., required claims, allowed algorithms, clock skew tolerance) — historically thejsonwebtokencrate has shipped security-relevant default changes across major versions (e.g., algorithm allow-list handling). - If application code or a transitive dependency instantiates a
Validationobject with defaults from the older 10.4.0 crate while another part of the system assumes the newer 11.0.0 semantics (or vice versa), a token that should be rejected by one component's expected security posture could be accepted by another. - An attacker who can craft a token accepted by the more permissive version (e.g., missing
expvalidation, weaker algorithm restriction) could pass validation on one dispatch path while a security review or test coverage assumed the stricter version's guarantees were in effect everywhere. - This split-brain dependency state also complicates patching: a future
jsonwebtokenCVE fix applied by bumping one path leaves the other path's copy vulnerable, since they are entirely separate compiled crate instances.
🔎 Threat Clue: Derived from COMP-002, COMP-007 via EP-003, EP-004
- Data Flows: Bearer token -> VTA authentication -> jsonwebtoken validation (divergent code paths)
Preconditions: Multiple code paths in the compiled binary use different jsonwebtoken major versions for token validation, Security-relevant default validation behavior differs between 10.4.0 and 11.0.0, An attacker can direct a token through the more permissive validation path
Existing Controls: cargo/deny.toml may already flag duplicate crate versions as a warning (not confirmed from provided files) • Both versions are still within actively maintained major version lines
Recommended Mitigations: Force dependency graph unification via [patch] or version alignment so only one jsonwebtoken major version is compiled into the binary • Add a cargo deny (or equivalent) policy rule that fails CI on duplicate major versions of security-critical crates (jsonwebtoken, jwt-related) • Audit both jsonwebtoken call sites for explicit (not default) Validation configuration to remove ambiguity • Add a changelog/dependency review gate for any crate providing token verification when its major version changes
⚪ STRIDE-8: Deprecated set_token_async Removal Regression Risk in join_lifecycle_e2e and Related Test Harnesses
| Field | Detail |
|---|---|
| Category | Repudiation |
| Severity | Low |
| Likelihood | Unlikely |
| CVSS | 3.1 CVSS:4.0/AV:L/AC:H/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-778 |
| CAPEC | CAPEC-593 |
| OWASP | A09:2021 - Security Logging and Monitoring Failures |
Description: Removed client.set_token_async(token).await call path in mockvta_bootstrap_e2e.rs in vulnerable system component test harness allows silent test-coverage regression due to the harness now exclusively exercising VtaClient::authenticated, resulting in the previously-tested bearer-token-only path becoming untested and potentially reintroducible without detection.
Evidence: openvtc-core/tests/mockvta_bootstrap_e2e.rs:~1-10
/* [REMOVED] let client = VtaClient::new(mock.base_url()); */
/* [REMOVED] client.set_token_async(token).await; */
Attack Scenario:
- The diff removes
let client = VtaClient::new(mock.base_url()); client.set_token_async(token).await;from the e2e test harness in favor ofVtaClient::authenticated(...). - If any remaining production or library code path still supports/constructs a bearer-token-only client, no automated test in this suite would catch a regression reintroducing the weaker authentication pattern (see STRIDE-2).
- A future developer, unaware of the SPEC §7.2 rationale documented only in code comments, could reintroduce
VtaClient::new+set_tokenelsewhere without any test failing, since the negative/legacy path is no longer exercised. - This creates a repudiation-adjacent gap: there is no verifiable evidence (test or log) that the weaker pattern is actively rejected or absent from the codebase going forward, relying solely on code review vigilance and comments.
🔎 Threat Clue: Derived from COMP-003 via EP-004
- Data Flows: Test harness client construction path
Preconditions: No CI/lint rule prevents reintroduction of VtaClient::new + set_token pattern, Test suite coverage relies on comments rather than enforced assertions
Existing Controls: Detailed inline comments explain the rationale for the migration, aiding future code review • Git history preserves the removed code for reference
Recommended Mitigations: Add a negative test asserting that a bearer-token-only VtaClient cannot successfully dispatch a Trust-Task document (expect failure) • Add a clippy/custom lint or deprecation attribute on set_token/set_token_async to produce compiler warnings on any future usage • Document the migration rationale in a CONTRIBUTING.md or ADR, not only inline comments
⚪ STRIDE-9: Wide Version Bump Across Cryptographic Primitive Crates Without Explicit Security Changelog Review
| Field | Detail |
|---|---|
| Category | Tampering, Information Disclosure |
| Severity | Medium |
| Likelihood | Possible |
| CVSS | 5.5 CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-385,CWE-1104 |
| CAPEC | CAPEC-97,CAPEC-459 |
| OWASP | A02:2021 - Cryptographic Failures |
Description: Bulk Cargo.lock dependency bumps in vulnerable system component build/dependency pipeline allow introduction of unreviewed crypto behavior changes due to simultaneous version increments of aes, aes-gcm, blake2, argon2 (new 0.6.0 alongside existing 0.5.3), chacha20, and cpufeatures without a corresponding security changelog audit visible in this diff, resulting in potential silent behavioral changes to encryption, hashing, or password-hashing primitives used throughout the DIDComm/credential-vault stack.
Evidence: Cargo.lock:67-80
name = "aes-gcm"
version = "0.11.1"
dependencies = [
"aead 0.6.1",
"aes 0.9.3",
"cipher 0.5.2",
"ctr 0.10.1",
+"ctutils",
"ghash 0.6.0",
-"subtle",
]
Attack Scenario:
- Cargo.lock diff shows simultaneous bumps:
aes0.9.2->0.9.3,aes-gcm0.11.0->0.11.1 (notably dropping the explicitsubtledependency and addingctutils— a potential change in constant-time comparison implementation),chacha200.10.1->0.10.2, and introduction of a secondargon20.6.0 alongside existing 0.5.3. - The
aes-gcmdependency change fromsubtletoctutilsfor constant-time operations is a meaningful cryptographic implementation change; ifctutilshas different timing-safety guarantees or is less mature/audited thansubtle, this could reintroduce timing side-channel risk in AES-GCM tag comparison. - Two coexisting
argon2versions (0.5.3 and 0.6.0) mean password/key-derivation hashing behavior may differ (e.g., default memory/time cost parameters) depending on which dependency chain invokes which version, similar to the jsonwebtoken split-version issue. - Without an explicit review of each crate's CHANGELOG for security-relevant behavioral changes (not just semver-compatible API changes), the team may unknowingly accept a weaker default configuration in a security-critical primitive.
- An attacker with the ability to measure timing (e.g., over a shared network segment via the DIDComm mediator or local co-tenancy) could exploit a regressed constant-time guarantee in the
ctutils-based AES-GCM tag verification to perform a padding/timing oracle attack against encrypted DIDComm envelopes.
🔎 Threat Clue: Derived from COMP-007 via EP-003
- Data Flows: DIDComm encrypted envelope -> AES-GCM decryption/verification -> credential-vault operations
Preconditions: ctutils constant-time implementation has weaker guarantees than subtle for the specific comparison used, Attacker has network or co-tenancy timing measurement capability against the AES-GCM verification path, Argon2 version split results in a materially weaker KDF configuration being used somewhere in the credential-vault flow
Existing Controls: Dependencies are pinned via Cargo.lock, so behavior is at least reproducible/deterministic per build • deny.toml likely enforces some license/source restrictions (not confirmed for security-behavior review)
Recommended Mitigations: Add a mandatory security-changelog review step in CI/PR process for any bump to a crate tagged as cryptographic (aes, aes-gcm, argon2, chacha20, blake2, jsonwebtoken) • Pin and unify argon2 to a single version across the workspace to avoid KDF parameter drift • Independently verify ctutils's constant-time claims (audit status, test coverage) before accepting it as a subtle replacement • Add automated timing-variance regression tests for AES-GCM authentication tag verification • Subscribe to RustSec advisory feed and require sign-off from a security reviewer on any diff touching pinned cryptographic crate versions
⚪ STRIDE-10: Multi-Cycle Dependency-Floor Comment Debt Indicates Manual, Error-Prone SDK Synchronization Process for vta-sdk Consumers
| Field | Detail |
|---|---|
| Category | Tampering |
| Severity | Low |
| Likelihood | Possible |
| CVSS | 4.0 CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-1104,CWE-670 |
| CAPEC | CAPEC-459 |
| OWASP | A06:2021 - Vulnerable and Outdated Components |
Description: Manual version-floor annotations in openvtc/Cargo.toml in vulnerable system component openvtc build configuration allow accidental dependency graph splitting due to reliance on human-maintained comments ("the fourth consecutive cycle") rather than automated enforcement of a single vta-sdk version across the workspace, resulting in two incompatible SDK type instances (ApproveScope, etc.) coexisting silently until a hard compile failure or, worse, a silent type-confusion-adjacent logic error if the types happen to structurally match.
Evidence: openvtc/Cargo.toml:1-25
# Floor is 0.4.6, but 0.4.6 is NOT where this resolves from: the `did-git-sign`
# entry under `[patch.crates-io]` in the root manifest redirects it to VGI#33's
# head, the unpublished commit that takes VGI to vta-sdk 0.31.
Attack Scenario:
- openvtc/Cargo.toml comments explicitly document that
did-git-sign0.4.6 requiresvta-sdk ^0.27, but the workspace has moved tovta-sdk0.31/0.22, meaning without the[patch.crates-io]override to VGI's unpublished head, the graph would carry two separatevta-sdkinstances. - Two
vta-sdkinstances in one binary produce two distinct, non-unifying Rust types for the same logical type (e.g.,ApproveScope), as explicitly called out in the capabilities.rs comment ('a graph holding two sdks has two distinct ApproveScope types that do not unify'). - If a future dependency bump reintroduces this split without the corresponding
[patch.crates-io]update (a manual, comment-tracked process with no automated CI enforcement shown in the provided files), the build may either fail outright (best case, as documented forvta-keys/vti-common) or, in a worse case, compile successfully if the divergent types are used only in disjoint code paths, silently creating two incompatible views of scope/capability data across module boundaries. - This is a process/tooling weakness rather than a direct code vulnerability: a missed synchronization step could allow inconsistent capability-scope enforcement between the openvtc consumer and the VTA server if their respective
ApproveScopesemantics silently diverge due to version skew. - An attacker cannot directly trigger this, but it represents a latent integrity risk that could be exploited if it ever manifests as inconsistent authorization-scope interpretation between client and server.
🔎 Threat Clue: Derived from COMP-002 via EP-005
- Data Flows: Build-time dependency resolution -> compiled ApproveScope type instances
Preconditions: A future dependency bump to vta-sdk on either side (openvtc workspace or VGI) without corresponding coordinated update, No automated CI check enforcing single-version resolution of vta-sdk across the dependency graph
Existing Controls: Extensive inline documentation in Cargo.toml explaining the constraint and its history • [patch.crates-io] override currently keeps the graph unified • Cargo itself will hard-fail the build if incompatible transitive versions (vta-keys vs vti-common) do not compile, providing a safety net for the worst-case scenario
Recommended Mitigations: Add a CI step using cargo tree -d or equivalent to fail the build if vta-sdk (or other trust-critical SDKs) resolves to more than one version • Convert the manual comment-tracked obligation into an automated dependency-policy check (e.g., deny.toml multiple-versions ban for vta-sdk specifically) • Track the VGI publish cadence via a bot/dependabot-style automation rather than manual comment updates • Add a regression test that constructs an ApproveScope value and asserts type-level compatibility across crate boundaries where feasible
⚪ STRIDE-11: Rebuilt parse_envelope_reply/parse_capability_reply API Surface Lacks Explicit Error Differentiation in message_dispatch
| Field | Detail |
|---|---|
| Category | Repudiation |
| Severity | Low |
| Likelihood | Unlikely |
| CVSS | 3.5 CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-778,CWE-390 |
| CAPEC | CAPEC-215 |
| OWASP | A09:2021 - Security Logging and Monitoring Failures |
Description: Option-based parse_envelope_document/parse_capability_reply return signatures in message_dispatch.rs in vulnerable system component openvtc message dispatcher allow silent-failure error handling due to None being used to represent both "not this message type" and "malformed/malicious document" cases without differentiation, resulting in an attacker's malformed or malicious envelope being silently dropped without any log signal to distinguish routine noise from an active exploitation attempt.
Evidence: openvtc/src/state_handler/message_dispatch.rs:1-14
if let Some((thid, doc)) = openvtc_core::capabilities::parse_envelope_document(&message.body)
&& let Some(reply) = openvtc_core::capabilities::parse_capability_reply(&doc, &thid)
{ capability_replies.push((thid, reply)); }
Attack Scenario:
parse_envelope_document(&message.body)returnsOption<(String, Doc)>;parse_capability_reply(&doc, &thid)returnsOption<Reply>.- Both
Nonecases are handled identically viaif let Some(...) = ... && let Some(...) = ...— any parse failure, whether due to a benign non-matching message or a deliberately malformed/malicious payload crafted to probe the parser, results in silent fallthrough with no branch executed. - An attacker probing the dispatcher with malformed Trust-Task envelopes (fuzzing the JSON structure, injecting oversized fields, or testing parser edge cases for a future exploit) leaves zero audit trail, since no
else/logging branch exists to record parse failures. - This absence of negative-path logging impairs incident response: a security team investigating a suspected exploitation attempt against the envelope parser would find no evidence in logs that probing occurred, undermining detection and forensic reconstruction (non-repudiation).
- Combined with STRIDE-4/5, an attacker could iteratively fuzz this endpoint to discover a parser bug (e.g., a future CWE-20 improper input validation issue) with no risk of detection, since failures are unlogged.
🔎 Threat Clue: Derived from COMP-002 via EP-001
- Data Flows: Malformed envelope -> silent parse failure -> no audit trail
Preconditions: No logging/metrics exist on the else path of the parse_envelope_document/parse_capability_reply chain, Attacker sends a volume of malformed or boundary-testing envelopes over time
Existing Controls: The underlying trust-tasks-capability-client library may have its own internal logging (not visible in provided source) • Type-safe Option-based parsing at least prevents panics on malformed input
Recommended Mitigations: Add explicit logging/metrics on the negative branch distinguishing "not this message type" from "parse/validation failure" where the type check has already passed • Emit a security-relevant audit event when a message of TRUST_TASK_ENVELOPE_TYPE fails to parse, including sender DID and truncated payload hash • Add rate-based alerting on repeated parse failures from the same sender as an indicator of active probing
⚪ STRIDE-12: rustls-pemfile Removal Across Multiple Crates May Shift PEM Parsing Trust Boundary Without Equivalent Validation
| Field | Detail |
|---|---|
| Category | Tampering |
| Severity | Low |
| Likelihood | Unlikely |
| CVSS | 3.8 CVSS:4.0/AV:N/AC:H/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-1104,CWE-295 |
| CAPEC | CAPEC-267 |
| OWASP | A02:2021 - Cryptographic Failures |
Description: Removal of the rustls-pemfile dependency from affinidi-messaging-sdk and affinidi-tdk-common in vulnerable system component TLS/certificate handling allows unreviewed PEM-parsing behavior change due to the parsing logic likely being inlined into rustls itself or another crate without visible confirmation that equivalent strictness (e.g., rejecting malformed PEM blocks, enforcing correct headers) is preserved, resulting in potential certificate/key parsing leniency regressions affecting TLS trust establishment for VTA/DIDComm connections.
Evidence: Cargo.lock:491-510
dependencies = [
"affinidi-crypto",
...
- "rustls-pemfile",
"serde",
"serde_json",
"sha256",
Attack Scenario:
- Cargo.lock shows
rustls-pemfileremoved as a direct dependency fromaffinidi-messaging-sdkandaffinidi-tdk-commonbetween old and new lockfile states. - This suggests PEM parsing responsibility moved into a newer
rustlsversion's built-in capability or a different helper, but the diff does not show the replacement code path. - If the new parsing path is less strict about validating PEM structure (e.g., accepting malformed base64 padding, ignoring unexpected headers, or accepting multiple concatenated certs where only one was expected), an attacker who can influence a locally-loaded certificate/key file (e.g., via a compromised config file or supply-chain-planted cert bundle) could smuggle additional unexpected certificate data past validation.
- This would only be exploitable in scenarios where certificate/key material originates from a source partially influenced by an attacker (e.g., a shared config volume, a downloaded cert bundle), making this a lower-likelihood but non-zero risk given the DID/PKI-heavy nature of this application.
🔎 Threat Clue: Derived from COMP-007 via EP-003
- Data Flows: Certificate/key file -> PEM parsing -> TLS trust establishment
Preconditions: Certificate/key loading code path is reachable with attacker-influenced PEM content, New parsing path (post rustls-pemfile removal) has weaker validation than the removed crate provided, No integration test validates rejection of malformed/ambiguous PEM structures
Existing Controls: rustls itself is a well-audited, security-focused TLS library likely to maintain strict parsing standards even if it absorbed pemfile's functionality • Change appears to be a routine dependency consolidation rather than a deliberate security-relevant modification
Recommended Mitigations: Confirm via changelog/release notes that rustls' inlined PEM parsing (if applicable) maintains equivalent or stricter validation than rustls-pemfile • Add a regression test asserting malformed/ambiguous PEM bundles are rejected by the current certificate-loading code path • Document the dependency consolidation rationale in the PR description for future auditability
🍝 PASTA Threat Model
Application Purpose
OpenVTC implements a decentralized identity (DID/DIDComm/Verifiable Credential) trust-task framework enabling account/context provisioning, credential-vault lifecycle management, and Git-commit signing bound to verifiable identities.
Inherent Risks
- The system depends on multiple external, independently-versioned cryptographic and identity SDKs (vta-sdk, vta-service, affinidi-messaging-*) whose synchronization is manually tracked via code comments.
- The trust framework's core security guarantee (document proof binding to signing identity) depends on strict client-side and server-side consistency that is only partially enforced across all code paths.
- Git-based dependency sourcing for a security-critical signing component (did-git-sign) introduces supply-chain trust in a mutable external repository.
Objectives
Risk: Treat any authentication/authorization pattern regression as high priority given the identity-trust nature of the product
Business: Provide a decentralized, verifiable trust framework for account/context provisioning and Git commit signing
Security: Ensure every Trust-Task document's in-band issuer matches the authenticated signing identity; Protect private key material end-to-end from generation to use; Maintain supply-chain integrity for all cryptographic and signing dependencies
Financial: Minimize incident-response and remediation costs from supply-chain or identity-spoofing compromises
Compliance: Align with W3C DID/DIDComm and Verifiable Credential specification requirements (SPEC §7.2 items 5b/7a/6)
Functional: Support VTA authentication, context/DID provisioning, credential-vault lifecycle operations, and Trust-Task capability negotiation
Operational: Maintain build reproducibility and dependency graph unification across the workspace
Business Impact Analysis (3)
BIA-1: VTA Authentication And Context Provisioning (Critical)
End-to-end process by which an admin identity authenticates to a VTA server and establishes a top-level or child context for subsequent trust-task operations.
MTD: 01 days 00:00 hours | RTO: 00 days 04:00 hours | RPO: 00 days 00:15 hours
- Stakeholders: Admin Operators / End Users / VTA Server Operators
- Dependencies: ClientIdentity Signing Key / VTA Server / vta-sdk Client Library
- Disruptions: Authentication bypass via weak client construction pattern / Private key compromise preventing legitimate re-authentication / Dependency graph split causing type-incompatible ApproveScope values
- Impacts: Unauthorized context creation or administrative action / Loss of trust in signed Trust-Task documents / Service outage if authentication flow fails to compile/run after dependency mismatch
BIA-2: Trust-Task Capability Negotiation And Reply Correlation (High)
Process by which capability enable/disable/list requests are dispatched as DIDComm Trust-Task envelopes and their replies correlated to pending requests.
MTD: 02 days 00:00 hours | RTO: 00 days 08:00 hours | RPO: 00 days 01:00 hours
- Stakeholders: Admin Operators / VTA Server Operators / Mediator Operators
- Dependencies: trust-tasks-capability-client Library / DIDComm Mediator / message_dispatch Handler
- Disruptions: Uncorrelated or spoofed capability replies injected into the processing queue / Flooding of Trust-Task envelopes exhausting dispatcher resources
- Impacts: Incorrect capability state applied to a session / Denial of service to legitimate capability negotiation / Loss of forensic evidence for malicious envelope probing
BIA-3: Dependency Supply Chain Integrity Maintenance (High)
Ongoing process of tracking and synchronizing vta-sdk-dependent crate versions (did-git-sign/VGI) and cryptographic primitive crates across the workspace to prevent split dependency graphs and unreviewed crypto behavior drift.
MTD: 07 days 00:00 hours | RTO: 01 days 00:00 hours | RPO: 00 days 00:00 hours
- Stakeholders: OpenVTC Maintainers / VGI Maintainers / Security Reviewers
- Dependencies: Cargo.lock / deny.toml Policy / GitHub Repository Access Controls
- Disruptions: Compromise of the pinned git dependency repository / Silent introduction of weaker cryptographic defaults via unreviewed version bumps
- Impacts: Supply-chain compromise of the Git commit-signing component / Weakened cryptographic guarantees across DIDComm-encrypted traffic
Technical Scope
Roles (3): RO-1 Super-Admin · RO-2 Admin · RO-3 Unauthenticated Message Sender
Actors (4): AC-1 Admin Operator · AC-2 VTA Client Process · AC-3 VTA Server · AC-4 DIDComm Mediator Service
Entry Points (5): EP-1 Trust-Task Envelope Dispatch Handler · EP-2 VTA Authenticated Client Construction · EP-3 VTA Server REST/DIDComm Interface · EP-4 Mock VTA Token Minting/Provisioning · EP-5 Provisioning Admin Rotation Function
Threat Actors (3): TA-1 Supply Chain Attacker · TA-2 Malicious DIDComm Participant · TA-3 Local Process Memory Attacker
Infrastructure (3): IF-1 openvtc Client Runtime · IF-2 VTA Server Deployment · IF-3 CI/CD Build Pipeline
Trust Boundaries (4): TB-1 External DIDComm Mediator Network · TB-2 openvtc Client Process Boundary · TB-3 VTA Server Boundary · TB-4 Build/Supply-Chain Boundary
External Entities (3): EE-1 DIDComm Peer / Mediator Participant · EE-2 VTA Server Operator · EE-3 crates.io / GitHub Package Sources
System Components (7): SC-1 openvtc State Handler (message_dispatch, setup_vta_actions) · SC-2 openvtc-core Capabilities Module · SC-3 MockVta Test Harness · SC-4 VTA Server (vta-service) · SC-5 DIDComm Mediator · SC-6 Cargo Dependency Graph / Build Pipeline · SC-7 Private Key / ClientIdentity Store
Resources And Assets (4): RA-1 Admin Private Key Multibase · RA-2 VTA Bearer Access Token · RA-3 Trust-Task Capability Reply Queue · RA-4 did-git-sign Git-Sourced Binary Artifact
Technologies And Dependencies (5): TD-1 vta-sdk · TD-2 did-git-sign · TD-3 trust-tasks-capability-client · TD-4 jsonwebtoken · TD-5 affinidi-messaging-test-mediator
Use Cases (2)
- VTA Admin Authentication And Context Provisioning: An admin operator generates or loads a signing key, authenticates to the VTA server, receives a bearer token, constructs a signing-capable ClientIdentity-bound client, and creates a top-level account
- Trust-Task Capability Negotiation: The openvtc client dispatches a capability enable/disable request as a DIDComm envelope, and the state handler parses inbound reply envelopes, correlating them against pending thread IDs to apply the
📋 Risk Registry (1)
| ID | Title | Severity | Residual | Priority | Effort |
|---|---|---|---|---|---|
| RISK-001 | Supply-chain compromise of git-sourced signing dependency undermines Git commit trust framework | High | Medium | Short-Term | Medium |
⚔️ Attack Scenarios (3)
SC-6: Cargo Dependency Graph / Build Pipeline
---
config:
layout: dagre
look: classic
theme: dark
---
flowchart LR
subgraph SL1["1. Threat Actors"]
direction LR
TA1@{ shape: rect, label: "TA-1: Supply Chain Attacker<br><i>Compromise git-sourced signing dependency</i>" }
end
subgraph SL2["2. Threats"]
direction LR
S1@{ shape: rect, label: "STRIDE-1: Git Source Dependency Pinned to Mutable Ref Override<br><i>High / Possible</i>" }
S10@{ shape: rect, label: "STRIDE-10: Multi-Cycle Dependency-Floor Comment Debt<br><i>Low / Possible</i>" }
end
subgraph SL3["3. Attack Patterns"]
direction LR
C538@{ shape: rect, label: "CAPEC-538: Open-Source Library Manipulation" }
C459@{ shape: rect, label: "CAPEC-459: Creating a Rogue Certificate Authority Certificate" }
end
subgraph SL4["4. Weaknesses"]
direction LR
W829@{ shape: rect, label: "CWE-829: Inclusion of Functionality from Untrusted Control Sphere" }
W1104@{ shape: rect, label: "CWE-1104: Use of Unmaintained Third Party Components" }
end
subgraph SL5["5. System Component"]
direction LR
SC6@{ shape: rect, label: "SC-6: Cargo Dependency Graph / Build Pipeline" }
end
TA1 --> S1
TA1 --> S10
S1 --> C538
S10 --> C459
C538 --> W829
C459 --> W1104
W829 --> SC6
W1104 --> SC6
linkStyle 0 stroke:#FF0000,stroke-width:2px
linkStyle 1 stroke:#00FF00,stroke-width:2px
linkStyle 2 stroke:#FF0000,stroke-width:2px
linkStyle 3 stroke:#00FF00,stroke-width:2px
linkStyle 4 stroke:#FF0000,stroke-width:2px
linkStyle 5 stroke:#00FF00,stroke-width:2px
linkStyle 6 stroke:#FF0000,stroke-width:2px
linkStyle 7 stroke:#00FF00,stroke-width:2px
SC-1: openvtc State Handler (message_dispatch, setup_vta_actions)
---
config:
layout: dagre
look: classic
theme: dark
---
flowchart LR
subgraph SL1["1. Threat Actors"]
direction LR
TA2@{ shape: rect, label: "TA-2: Malicious DIDComm Participant<br><i>Inject spoofed/replayed capability replies</i>" }
TA3@{ shape: rect, label: "TA-3: Local Process Memory Attacker<br><i>Extract admin private key material</i>" }
end
subgraph SL2["2. Threats"]
direction LR
S2@{ shape: rect, label: "STRIDE-2: Bearer-Token-Only Client Construction Bypasses Proof Binding<br><i>High / Likely</i>" }
S3@{ shape: rect, label: "STRIDE-3: Private Key Multibase Cloned Without Zeroization<br><i>Medium / Possible</i>" }
S4@{ shape: rect, label: "STRIDE-4: Missing Sender Correlation Enforcement<br><i>Medium / Possible</i>" }
S5@{ shape: rect, label: "STRIDE-5: Trust-Task Envelope Flooding<br><i>Medium / Likely</i>" }
end
subgraph SL3["3. Attack Patterns"]
direction LR
C196@{ shape: rect, label: "CAPEC-196: Session Credential Falsification through Prediction" }
C37@{ shape: rect, label: "CAPEC-37: Retrieve Embedded Sensitive Data" }
C137@{ shape: rect, label: "CAPEC-137: Parameter Injection" }
C125@{ shape: rect, label: "CAPEC-125: Flooding" }
end
subgraph SL4["4. Weaknesses"]
direction LR
W346@{ shape: rect, label: "CWE-346: Origin Validation Error" }
W316@{ shape: rect, label: "CWE-316: Cleartext Storage of Sensitive Information in Memory" }
W345@{ shape: rect, label: "CWE-345: Insufficient Verification of Data Authenticity" }
W770@{ shape: rect, label: "CWE-770: Allocation of Resources Without Limits or Throttling" }
end
subgraph SL5["5. System Component"]
direction LR
SC1@{ shape: rect, label: "SC-1: openvtc State Handler" }
end
TA2 --> S2
TA3 --> S3
TA2 --> S4
TA2 --> S5
S2 --> C196
S3 --> C37
S4 --> C137
S5 --> C125
C196 --> W346
C37 --> W316
C137 --> W345
C125 --> W770
W346 --> SC1
W316 --> SC1
W345 --> SC1
W770 --> SC1
linkStyle 0 stroke:#FF0000,stroke-width:2px
linkStyle 1 stroke:#FFA500,stroke-width:2px
linkStyle 2 stroke:#FFA500,stroke-width:2px
linkStyle 3 stroke:#FFA500,stroke-width:2px
linkStyle 4 stroke:#FF0000,stroke-width:2px
linkStyle 5 stroke:#FFA500,stroke-width:2px
linkStyle 6 stroke:#FFA500,stroke-width:2px
linkStyle 7 stroke:#FFA500,stroke-width:2px
linkStyle 8 stroke:#FF0000,stroke-width:2px
linkStyle 9 stroke:#FFA500,stroke-width:2px
linkStyle 10 stroke:#FFA500,stroke-width:2px
linkStyle 11 stroke:#FFA500,stroke-width:2px
linkStyle 12 stroke:#FF0000,stroke-width:2px
linkStyle 13 stroke:#FFA500,stroke-width:2px
linkStyle 14 stroke:#FFA500,stroke-width:2px
linkStyle 15 stroke:#FFA500,stroke-width:2px
SC-3: MockVta Test Harness
---
config:
layout: dagre
look: classic
theme: dark
---
flowchart LR
subgraph SL1["1. Threat Actors"]
direction LR
TA2@{ shape: rect, label: "TA-2: Malicious DIDComm Participant<br><i>Escalate to super-admin token</i>" }
end
subgraph SL2["2. Threats"]
direction LR
S6@{ shape: rect, label: "STRIDE-6: Super-Admin Token Minting with Empty Context Vec<br><i>Medium / Possible</i>" }
S8@{ shape: rect, label: "STRIDE-8: Deprecated set_token_async Removal Regression Risk<br><i>Low / Unlikely</i>" }
end
subgraph SL3["3. Attack Patterns"]
direction LR
C122@{ shape: rect, label: "CAPEC-122: Privilege Abuse" }
C593@{ shape: rect, label: "CAPEC-593: Session Hijacking" }
end
subgraph SL4["4. Weaknesses"]
direction LR
W269@{ shape: rect, label: "CWE-269: Improper Privilege Management" }
W778@{ shape: rect, label: "CWE-778: Insufficient Logging" }
end
subgraph SL5["5. System Component"]
direction LR
SC3@{ shape: rect, label: "SC-3: MockVta Test Harness" }
end
TA2 --> S6
TA2 --> S8
S6 --> C122
S8 --> C593
C122 --> W269
C593 --> W778
W269 --> SC3
W778 --> SC3
linkStyle 0 stroke:#FFA500,stroke-width:2px
linkStyle 1 stroke:#00FF00,stroke-width:2px
linkStyle 2 stroke:#FFA500,stroke-width:2px
linkStyle 3 stroke:#00FF00,stroke-width:2px
linkStyle 4 stroke:#FFA500,stroke-width:2px
linkStyle 5 stroke:#00FF00,stroke-width:2px
linkStyle 6 stroke:#FFA500,stroke-width:2px
linkStyle 7 stroke:#00FF00,stroke-width:2px
📊 Risk Summary
Total Threats: 12
By Severity: Low: 4 · High: 2 · Medium: 6
By Category: Unknown: 12
Generated by Agentic Sec — Threat Model & Affect Analysis Agent
📊 Summary & findings
| ✅ Confirmed | |
|---|---|
| 1 | 1 |
Confirmed (1)
- 🔵 Authentication now requires cryptographic ClientIdentity binding for VtaClient (mitigation observed, not a vulnerability)
Must-Review-By-Human (1)
- 🟡 Unbounded resource consumption via unthrottled Trust-Task envelope parsing on every matching message
Part of the stack-wide refresh onto the newly published trust-tasks / TDK / VTA-SDK releases.
affinidi-tdkaffinidi-messaging-sdkaffinidi-did-resolver-cache-sdktrust-tasks-rstrust-tasks-capability-clientvta-sdkargon2vta-service(dev)affinidi-messaging-test-mediator(dev)After this, no direct requirement in the workspace is behind its latest release except the two held deliberately —
x25519-dalekat 2.x by the OpenPGP stack andrandat 0.8 by the crypto stack — each with the reason written against the line.argon2derives key material, so it is worth saying why the bump is safe: Argon2id is a specified KDF andderive_argon2_keycallshash_password_into, its plain implementation. Same algorithm, version and params in, same 32 bytes out, so the unlock code and theProtectedConfigseed are byte-identical across it and every existing config still opens. What 0.6 breaks is thepassword-hash0.6 PHC-string surface, which we do not touch.did-git-signcomes from VGI#33's head, not from crates.ioThis replaces the "blocked on a VGI release" note this PR carried.
did-git-sign0.4.6 is the latest published release and requiresvta-sdk ^0.27. That one edge re-splitsvta-sdk,affinidi-tdk,affinidi-messaging-sdkandtrust-tasks-rsinto two copies each — and it does not merely fail to unify types, it fails to compile (vta-keys0.2.9 againstvti-common0.15).OpenVTC/verifiable-git-infrastructure#33 is the move that ends it: the VGI workspace onto vta-sdk 0.31 and TDK 0.10, no source change, green on its own pipeline. Rather than block on the publish, a
[patch.crates-io]block redirectsdid-git-sign(and its path-depvgi-core) to that PR's head. The requirement inopenvtc/Cargo.tomlstill names0.4.6— the version VGI carries at that rev — and the patch decides only where that 0.4.6 comes from.Pinned by
rev(462032c), never bybranch, so a further push to that PR cannot silently change what this builds against. That is the same discipline VGI applies to its owntrql-clientpin. Both git sources are allow-listed indeny.toml, andpublish = falsehere, so a git source costs this workspace nothing on its own release path.Unwind: delete the patch block and raise the floor in
openvtc/Cargo.tomlto0.4.7the moment VGI publishes. This is the fourth consecutive cycle the obligation has come due, so the note in the manifest is written as the rule rather than as this version's incident.The check is unnarrowed and passes:
cargo tree -i vta-sdk→ single nodecargo tree -i trust-tasks-rs→ single0.17.3Source changes
parse_envelope_reply(trust-tasks-capability-client 0.17) folded the SPEC §4.9 correlation check into the parse and now takes the thread id the caller is waiting on. The inbound dispatch is a fan-in point waiting on nothing in particular, so it now reads the document first (parse_envelope_document) and classifies against the document's ownthreadId. The correlation that actually guards this path is unchanged —apply_capability_repliesmatches against the open view'spending_thidand drops everything else.JoinRequestStatusResponseBody(vta-sdk 0.31) gainedcode,reason,decided_at, the refusal detail carried on arejectedstatus. The e2e helper does not exercise a rejection, so it sends none.VtaClient::new+set_tokenfor a client that dispatches Trust Tasks. A bearer token authenticates the connection; SPEC §7.2 items 5b and 7a want an in-bandrecipientand a documentproof, which the client can only produce from aClientIdentity. The setup wizard's REST arm hand-rolls that shape — it authenticates separately because it needs the token itself to cache — so the very next dispatch on that client, the context probe, would have failed with "authenticated but carries no ClientIdentity". It now builds the client the wayconnect_auto's REST arm does. The twomockvta_bootstrap_e2eclients had the same shape and are how this was caught: they are#[ignore]d, so only the coverage job (--include-ignored) runs them.if letin the envelope dispatch ascollapsible_if; it is a let chain now, which edition 2024 has had since well before the 1.95 MSRV.Testing
cargo fmt/clippy(on 1.98, the toolchain that flagged the lint) /docwith-D warnings/cargo-deny(advisories, licenses, bans, sources) /cargo checkon the 1.95 MSRV /cargo bench --no-runall clean.cargo test --workspace --tests -- --include-ignored— 921 passing, 0 failed, including the three MockVta bootstrap e2es the coverage job runs.cargo test --workspace --no-default-featuresclean.