aic-agent — Threat model (EN)EN

aic-agent threat model

What an attacker can and cannot break, the controls the SDK enforces, and what it deliberately leaves to the deployment. Complements SECURITY.md (policy and reporting); architecture.md describes the normal operation.

In scope. Everything in package aicagent when used through its documented surfaces (Agent, Config, bearer, identity, transport) with correctly- configured trust material.

Assumed attacker. Remote, unauthenticated by definition: anyone who can reach the endpoints the agent calls, or who can intercept the wire between agent and service. She can read all public data, observe the wire, and present any challenge at the service boundary. She does not hold the agent key, the principal key, the user-signer, or the configuration.

Assets

Asset Why it matters Where it lives
Agent private key all minted credibility flows from it in-memory; the code that calls identity.GenerateKey or Load
Bearer token authenticates requests to the protected service agent state, HTTP headers (mutable per request)
Delegation authorization (DA) binds the principal's authority to this agent the token the agent presents; the signer's HTTP response
The protected service the value the attacker is trying to reach out of scope for this SDK's controls (the agent is the client)
Evidence that the service required to close an evidence-required denial held by the deployment, never the agent

Trusted components

  • The agent key (Config.Key). Whoever holds this key can mint identities the verifier will authenticate (subject to capabilities). Compromise of this key = full impersonation of the agent.
  • The user signer (Config.UserSigner). When the agent has no AIC yet, it asks the signer for a DA. The signer holds the principal's identity key; whoever controls the signer controls what agents are authorized.
  • The remote issuer (Config.RemoteIssuer). Exchanges a DA for a short-lived token; a compromised issuer can mint bearer credentials directly.
  • The protected service's CA (ServerCA). Pins the TLS identity of the backend; a compromised service key + compromised CA allows credential theft on the wire for Bearer mode.

Threats and controls

T1 — Key theft / extraction from memory

Attacker extracts the agent key from the host, a coredump, or a leaked back-up file.

  • Mitigation: the key is in-memory for the process lifetime; protect the file on disk (0600, private directory). Prefer KMS/HSM-backed signers when the deployment has one — the Key is a crypto.Signer value, not a path, for exactly this reason.
  • Residual: a runtime memory read (rooted host) defeats the key. Accept this as out of scope; use hardened runtimes (sandboxing, eBPF) at the platform layer.

T2 — Token theft on the wire / leaked log

Attacker reads the bearer token from a log, from the wire, or from a cached response body.

  • ReplayProtection (single-use jti) limits the window to one request per minted token.
  • Local Bearer mode mints a fresh token per request; the transport never reuses the same jti.
  • Use HTTPS end-to-end and short exp lifetimes. The SDK does not set them — the issuer does.
  • Residual: a stolen token is valid until first use and exp; the deployment's WAF/edge layer should rate-limit concurrent requests per identity.

T3 — CA/key compromise (principal or issuer)

Attacker holds the CA key that signed the agent's credential, or the issuer's signing key.

  • Out of scope for the client SDK. Mitigation is operational: rotate/revoke the CA, shorten token lifetimes, refresh the trust anchor.
  • ServerCA pinning protects the service identity from a compromised system CA; it does not help with issuer compromise.

T4 — Replay / downgrade of a client challenge retry

Attacker replays a CLC-CHALLENGE-v1 challenge that already expired, or tries to force a second retry.

  • The SDK retries at most once per challenge, and only when Challenge.Usable (live, not expired) at the exact instant of the retry. An expired or stale challenge is never retried — that is the load amplification the challenge spec forbids.
  • The request retried is byte-identical (same marshalled body, via GetBody); there is no mutation path the attacker can inject.
  • ChallengeUnavailable is surfaced, not silently swallowed.

T5 — Bearer token reuse across identities / across verifiers

Attacker captures a valid token and presents it to a different verifier, or a different agent, hoping cnf.jkt doesn't catch the mismatch.

  • cnf.jkt (RFC 7638 presenter thumbprint) binds the token to this agent's key; a verifier checking cnf rejects a token presented with a different key.
  • kid (SPKI hash) identifies the issuing key — not the service's key, so cross-verifier token theft doesn't help unless the verifier also trusts the same issuer CA (operational trust boundary).

T6 — Refresh-on-401 body safety

Attacker modifies a POST body, triggers a 401, and the client retries with a different (attacker-chosen) body.

  • The SDK only retries a 401 when the request is idempotent (GET/HEAD/OPTIONS/ TRACE), the body is nil, or the request carries a GetBody function so the original body can be recreated for the retry. A non-idempotent request without GetBody returns the 401 directly.

T7 — DoS / resource exhaustion

Flood of evidence-required challenges that the agent keeps retrying, or a rate of token requests that overwhelms the signer.

  • Rate limiting is the edge's and the issuer's job; the SDK does not rehammer the verifier beyond the one-retry limit.
  • Evidence-retry waits are bounded (Retry-After / retry_timing.not_before, never past expires_at) and require the deployment's provider to return true — there is no path where the SDK hammers the verifier on its own.

Escalation

  • Agent key + server CA compromise: attacker can mint valid tokens for this agent and complete the TLS handshake with the verifier's service. Mitigation: rotate the agent key, revoke the CA, rotate the verifier's service TLS cert. The client SDK cannot help with its own trust root lying.
  • Issuer + signer compromise: attacker can sign any delegation for any principal. Mitigation: operational trust boundary (different keys, separate HSMs) — this is outside the SDK's scope.

Controls matrix (quick reference)

Control Threat
per-request jti / ReplayProtection T1, T2
cnf.jkt presenter binding T2, T5
kid SPKI T2, T5
Challenge.Usable + single-retry T4, T7
GetBody / idempotency check T6
ServerCA pinning T3
AllowPlaintextHTTP = false (default) MITM on token endpoint
RootCA != ServerCA separation SDK-managed outbound vs service TLS

Out of scope

  • Agent key storage security (platform/HSM).
  • Bearer token lifetime and minting policy (the issuer's decision).
  • Rate limiting at the edge (edge responsibility).
  • Defense against a compromised trust root (see Escalation).

References