Skip to content

Threat Model

This document enumerates the attacks that Kanoniv Agent Auth is designed to resist, the specific mechanisms that prevent each attack, and the residual risks that remain.

Attacker Model

We assume an attacker who can:

  • Observe all network traffic between agents and servers (passive MitM)
  • Inject or modify messages in transit (active MitM)
  • Compromise one or more intermediate agents in a delegation chain
  • Gain write access to the provenance store
  • Craft arbitrary JSON payloads and submit them to MCP servers

We assume an attacker who cannot:

  • Obtain an agent's Ed25519 private key without compromising that agent's runtime
  • Break Ed25519 signatures (requires 2^128 operations)
  • Break SHA-256 collision resistance (birthday bound at 2^128)

Attacks and Mitigations

1. Agent Impersonation

Attack: An attacker claims to be Agent A by using Agent A's DID in a signed message or delegation.

Mitigation: Every signed message includes an Ed25519 signature over canonical bytes. Verification recomputes the canonical form from {nonce, payload, signer_did, timestamp} and checks the signature against the claimed signer's public key. Without Agent A's private key, the attacker cannot produce a valid signature for Agent A's DID.

Verification code path: SignedMessage::verify() checks that self.signer_did == identity.did, then verifies the Ed25519 signature over the canonical bytes using the identity's public key.

2. Privilege Escalation via Delegation Tampering

Attack: Agent B receives a delegation with action_scope: ["resolve"]. Agent B modifies the outer caveats field to ["resolve", "merge", "split"] before delegating to Agent C.

Mitigation: During verification, caveats are extracted from the signed payload (delegation.proof.payload["caveats"]), not from the outer delegation.caveats field. The signed payload is immutable - any modification invalidates the signature. The outer field is ignored for authorization decisions.

Test: test_tampered_delegation_caveats_detected - modifies outer caveats to widen scope, verifies that the invocation is still blocked by the original signed caveats.

3. Delegation Chain Forgery

Attack: Agent C constructs a fake delegation chain claiming Root -> B -> C, without B's involvement.

Mitigation: Each delegation embeds the issuer's public key (issuer_public_key field) and is signed by the issuer. Verification:

  1. Reconstructs the issuer identity from the embedded public key
  2. Checks that the reconstructed DID matches issuer_did
  3. Verifies the delegation signature using the reconstructed identity
  4. Checks that parent.delegate_did == current.issuer_did (chain linkage)

Without B's private key, C cannot produce a valid signature for B's DID. The embedded public key prevents substitution because the DID is derived from SHA-256(public_key)[0..16] - a different key produces a different DID.

4. Replay Attacks

Attack: An attacker captures a valid signed message and replays it to perform the same action again.

Mitigation: Every SignedMessage includes a UUID v4 nonce field that is part of the signed canonical bytes. Verifiers MAY maintain a nonce registry and reject previously-seen nonces. The timestamp field (also signed) enables time-window-based replay rejection.

Residual risk: Replay protection requires the verifier to maintain state (a nonce set or time window). The protocol provides the nonce; enforcement is application-specific. A stateless verifier that does not track nonces is vulnerable to replay within the delegation's validity period.

5. Man-in-the-Middle

Attack: An attacker intercepts an MCP tool call, modifies the arguments (e.g., changes entity_id), and forwards the modified request to the server.

Mitigation: The invocation's proof field contains an Ed25519 signature over {invoker_did, action, args, delegation_hash}. Modifying any argument changes the signed payload, invalidating the signature. The server verifies the invocation signature before processing.

Note: The invocation signs the args that were provided at invocation time. If the MCP tool call's top-level arguments differ from the signed args, the server detects tampering during proof extraction.

6. Compromised Intermediate Agent

Attack: Agent B (in chain Root -> B -> C) is compromised. The attacker uses B's key to issue new delegations to arbitrary agents with wider scope.

Mitigation: Three mechanisms limit the damage:

  1. Caveat accumulation. B cannot issue delegations with fewer caveats than it received. Any delegation from B carries Root's caveats plus any additional restrictions B adds. The attacker cannot use B to grant merge authority if Root only granted resolve.

  2. Independent signature verification. Every delegation in the chain is independently verified. Compromising B does not give the attacker the ability to forge Root's signature. The chain must trace back to a valid Root signature.

  3. Revocation. The verify_invocation_with_revocation function accepts an is_revoked callback. When B is known to be compromised, revoke B's delegation by content hash. All downstream delegations (B -> C, B -> D, etc.) become invalid because the chain walks through B's revoked delegation.

Residual risk: Between compromise and revocation, B can issue new delegations within its authorized scope.

7. DoS via Deep Delegation Chains

Attack: An attacker constructs a delegation chain of depth 10,000 and submits it for verification, consuming excessive CPU time walking the chain.

Mitigation: Hard limit of MAX_CHAIN_DEPTH = 32. Both creation and verification enforce this limit. Delegation::create_inner checks parent.depth() >= MAX_CHAIN_DEPTH before creating a new delegation. verify_invocation_with_revocation tracks steps and returns DelegationChainBroken when the limit is exceeded.

32 levels is sufficient for practical delegation hierarchies (organization -> team -> agent -> sub-agent) while preventing unbounded computation.

8. DID Collision

Attack: An attacker generates many keypairs to find one whose DID matches a target agent's DID (second-preimage attack).

Mitigation: The DID uses 128 bits of SHA-256 output (SHA-256(public_key)[0..16]). Finding a second preimage requires approximately 2^128 operations. For birthday-bound collisions (finding any two DIDs that collide), the bound is approximately 2^64 identities.

Residual risk: At 2^64 identities (~1.8 x 10^19), collision probability becomes non-negligible. This is adequate for practical agent populations. If a system manages more than 2^40 (~10^12) agents, the collision probability is still below 2^-48.

9. Provenance Entry Tampering

Attack: An attacker with write access to the provenance store modifies a historical entry (e.g., changes action: merge to action: resolve or modifies entity_ids).

Mitigation: ProvenanceEntry::verify() performs two checks:

  1. Verifies the Ed25519 signature on the signed_envelope
  2. Compares every outer field (agent_did, action, entity_ids, parent_ids, metadata) against the corresponding field in the signed payload

Modifying any outer field without re-signing produces an IntegrityMismatch error. Re-signing requires the original agent's private key.

DAG integrity: Entries reference parents by content hash (SHA-256 of the canonicalized signed message). Modifying a parent entry changes its content hash, breaking all references from child entries. An attacker would need to re-sign the entire subgraph downstream of the tampered entry.

10. Embedded Public Key Substitution

Attack: Agent C modifies a delegation's issuer_public_key to substitute a different key (one C controls), then forges a new signature using that key.

Mitigation: Verification reconstructs the DID from the embedded public key and checks it against issuer_did. Since the DID is SHA-256(public_key)[0..16], a different public key produces a different DID. The check issuer_identity.did != current.issuer_did catches the substitution.

11. Root Key Compromise

Attack: The root authority's private key is stolen.

Mitigation: This is the highest-severity failure. All delegations signed by the root are now suspect. Recovery requires:

  1. Revoking all delegations issued by the compromised root (via the is_revoked callback)
  2. Generating a new root keypair (new DID)
  3. Re-issuing all delegations from the new root

Residual risk: Between compromise and detection, the attacker can issue arbitrary delegations as the root authority. There is no key rotation mechanism within the DID - the DID is permanently bound to the key. This is a deliberate design choice: in-protocol key rotation adds complexity and its own attack surface (key rotation hijacking).

Out of Scope

The following are explicitly not addressed by the cryptographic protocol:

  • Confidentiality. Messages are signed, not encrypted. An observer can read payloads. Use TLS for transport confidentiality.
  • Availability. The protocol does not prevent denial-of-service at the network layer. The depth limit prevents algorithmic DoS only.
  • Key storage. How private keys are stored at rest (HSM, encrypted file, environment variable) is the deployer's responsibility. The protocol specifies the key format (32-byte Ed25519 secret), not the storage mechanism.
  • Semantic authorization. Caveats constrain actions and resources syntactically. Whether resolve on entity:customer:123 is semantically appropriate for a given business context is application logic, not protocol logic.

The identity and delegation layer for AI agents.