Security Model

Threat table

ThreatAttack vectorDRS mitigationResidual risk
Forged root DRAttacker creates a fake delegationEd25519 EUF-CMA: forgery requires solving the discrete logPrivate key theft
Chain splicingCompromised agent substitutes unrelated tokenprev_dr_hash: any substitution changes the hash chain — fails Block BImplementation bugs in hash computation
Policy escalationSub-DR claims wider permissions than parentcheck_policy_attenuation() at issuance + Block D at verificationPolicy schema gaps
Policy violationAgent passes arguments exceeding constraintsBlock D evaluates all policies conjunctivelyUnlisted policy fields are not checked
DR tamperingAttacker modifies a signed DREd25519 signature fails — fails Block CNone — structural
Chain injectionInsert a fake intermediate DRprev_dr_hash changes break subsequent links — fails Block BNone — structural
Replay after revocationAgent replays a revoked DRBlock F: Bitstring Status List (5-min cache TTL)Up to 5-minute stale cache window
JSON malleabilityDifferent canonical bytes for same logical JSONRFC 8785 JCS enforced at both issuance and verification endsNon-conforming JCS at one end
Signature malleability(R, S) and (R, S+L) both verify under naive checked25519-dalek 2.x enforces S < L via verify_strict()None — library enforces
DID spoofingAttacker impersonates a legitimate issuerdid:key DIDs are derived from the public key — impossible without the private keydid:web requires DNS/TLS security
Prompt injectionAttacker embeds instructions in tool contentDRS records every invocation chainOut of scope — model/runtime responsibility
Model-level bypassAdversarial prompts bypass safety constraintsModel safety ≠ execution safetyEntirely outside DRS scope

Fail-closed principle

DRS verification is fail-closed. Any error in any block returns an error and rejects the request. This applies to:

  • Unresolvable DIDs
  • Malformed JWTs
  • Network errors fetching the Bitstring Status List
  • Policy fields the verifier does not recognise

A partially valid chain is an invalid chain.

Constant-time operations

All security-sensitive comparisons use constant-time equality to prevent timing side-channels:

LanguageSafeUnsafe
Gocrypto/subtle.ConstantTimeComparebytes.Equal, ==
Rustsubtle::ConstantTimeEq== on byte slices

This applies specifically to multicodec prefix checks when resolving did:key DIDs. The two-byte prefix [0xed, 0x01] identifies an Ed25519 key. Checking it with a short-circuit comparison leaks timing information about where the mismatch occurs.

Key management

Key typeRecommended storageRotation
Human root keyHardware Security Module or Secure EnclaveNot rotated (DID is derived from key)
Operator root keyHSM required for productionAnnual with overlap period
Agent session keyEphemeral — generated per sessionPer-session, never persisted

did:key is the preferred DID method: the DID encodes the public key directly. No registry, no DNS, no trust anchor beyond the key itself. did:web is supported but requires DNS and TLS security.

What DRS does not protect against

  • Prompt injection: An attacker embedding instructions in tool output or environment data. This is a model-layer problem. DRS records that the invocation happened and under what authorisation — it does not prevent the model from following injected instructions.
  • Key compromise: If a private key is stolen, the attacker can forge receipts signed by that key. Mitigation: rotate keys, revoke outstanding delegations.
  • Post-compromise recovery: DRS does not define how to recover a system after key material is compromised. That is an operational problem.