ROOTVIELROOTVIEL
← Academy

Module 3 · Cryptographic delegation

Delegation chains

When a principal delegates to a delegate that delegates again, authority forms a chain of signed links. The chain is only sound if every link is verified: each signature valid, each hop a subset of the one before, the whole chain rooted in a principal the verifier trusts, with no cycles and a bounded depth. Verification is end-to-end — a single broken or widening link invalidates everything downstream.

Learning objective

Learn how multi-hop delegation forms a chain, how to verify a chain end-to-end back to a trusted root, and how to bound its depth, reject cycles, and prevent authority widening anywhere along the way.

Track
Engineering
Module
3 · Cryptographic delegation
Prerequisites
Scope attenuation · Principals, delegates and capabilities · Digital signatures

Conceptual introduction

A delegation chain is what you get when delegation repeats: a principal grants authority to A, A grants a narrowed slice to B, B grants a narrower slice still to C. Each grant is a signed link that says "I, holding this authority, pass this (smaller) part of it to you." To trust the authority C presents, you cannot look only at C's link — you must walk the whole chain back to a root you trust, checking every link along the way.

This is structurally the same idea as a certificate chain in X.509 (a leaf cert signed by an intermediate, signed by a root CA) or an authorization chain in SPKI/SDSI, but for authority rather than identity. The discipline is identical: trust flows from a root you already trust, through links that each only narrow, to a leaf — and verification is the act of re-walking that path.

Note

A chain is exactly as trustworthy as its weakest link AND its root. A perfect leaf token means nothing if an intermediate link is invalid, widening, expired, or roots in a principal you do not actually trust.

Technical foundation

Each link in the chain binds: who issued it (a key), who received it (the next holder's key), the authority conveyed, and a signature over all of that. Verifying the chain means establishing an unbroken signed path from a trusted root to the final holder, where every hop is a valid attenuation of its parent. The effective authority of the leaf is the intersection of every link — never more than the most restrictive one.

Link (delegation)
One signed hop: issuer key, subject/holder key, the authority granted, and a signature binding them.
Root of trust
The principal the verifier independently decides to trust (its key is configured, not taken from the chain itself). Every valid chain must terminate here.
End-to-end verification
Checking the entire path root→leaf: every signature, every subset/attenuation step, every constraint, before honouring the leaf.
Depth bound
A maximum allowed chain length, so a delegate cannot sub-delegate indefinitely.
Cycle
A chain where a key delegates (directly or transitively) back to itself, which must be rejected — it conveys no new authority and can mask loops/abuse.

The model: walking the chain

Verification is a fold from the root down. Start with the trusted root authority, then for each link confirm it was signed by the current holder, that its holder matches the next link's issuer, that it attenuates (subset of) the running authority, and that its constraints hold. Keep the set of keys already seen to detect cycles, and count hops against a depth bound.

end-to-end chain verification (schematic)
def verify_chain(chain, trusted_roots, now, MAX_DEPTH=8): if len(chain) == 0 or len(chain) > MAX_DEPTH: return REJECT # empty, or too deep root = chain[0] if root.issuer not in trusted_roots: return REJECT # not anchored in a root WE trust authority = root.granted # running authority, narrows each hop current_signer = root.issuer seen = {root.issuer} for link in chain: if not verify_sig(link, by=current_signer): return REJECT # broken signature if not subset(link.granted, authority): return REJECT # WIDENING -> reject (see attenuation) if not constraints_hold(link, now): return REJECT # expired / wrong audience / unknown caveat if link.holder in seen: return REJECT # CYCLE seen.add(link.holder) authority = intersect(authority, link.granted) current_signer = link.holder # next link must be signed by this holder return ACCEPT, authority # effective = intersection of all hops

Three defensive choices are doing the heavy lifting: anchoring in trusted_roots (not trusting the chain to vouch for itself), rejecting any non-subset hop (no widening), and failing closed on unknown constraints. Skip any one and the chain can be forged, widened, or quietly over-broad.

Step by step: building and checking a chain

  1. 1The root principal issues link 1, granting a narrowed authority to the first delegate and signing it.
  2. 2Each delegate, to sub-delegate, issues a further link bound to the next holder, attenuating again, signed with its own key.
  3. 3The leaf holder presents the full chain (all links, in order) plus proof it controls the final holder key.
  4. 4The verifier anchors on a trusted root key, then walks every link: signature valid, holder→issuer continuity, subset attenuation, constraints satisfied.
  5. 5It tracks seen keys to reject cycles and counts hops against a depth bound, computing the effective authority as the intersection of all links.
  6. 6Only if the entire walk succeeds is the leaf's requested action authorized — and only within that effective authority.

Synthetic example

synthetic — a three-hop chain, verified and attacked
# Trusted root: finance-root.pub (configured in the verifier) link1: finance-root -> planner : read+write vault:A/reports/* exp=+1h sig(finance-root) link2: planner -> worker : read vault:A/reports/* exp=+30m sig(planner) link3: worker -> summarizer: read vault:A/reports/q3.pdf exp=+5m sig(worker) # Verify: root trusted? yes. Each sig valid? yes. Each hop subset? yes. # Continuity: link2 signed by planner (= link1 holder); link3 by worker (= link2 holder). OK. # Effective authority at leaf = read, vault:A/reports/q3.pdf, exp<+5m. # Attack A (widening): summarizer forges link4 granting ITSELF "write". # subset(write, read-only parent) = False -> REJECT. # Attack B (forged root): a chain anchored on attacker.pub not in trusted_roots -> REJECT. # Attack C (cycle): link4: summarizer -> planner (already seen) -> REJECT. # Attack D (too deep): a 20-hop chain exceeds MAX_DEPTH -> REJECT.

Every attack fails at a specific, local check — which is the point of walking the chain end-to-end rather than trusting the token the leaf happens to wave.

Application to agents

Agent trees are delegation chains made concrete: planner → worker → tool-agent → sub-task. Carrying the chain explicitly turns "some agent somewhere asked for this" into a precise, auditable statement: "finance-root granted the planner, which granted the worker, which granted this leaf exactly read on q3.pdf for five minutes." When something goes wrong, the chain is the audit trail — you can see exactly which link conveyed the authority that was abused.

Bounding depth matters especially for agents, because autonomous sub-delegation can otherwise sprawl without limit; a depth bound caps how far authority can travel from its source. Cycle rejection matters because agent graphs are not always trees — an agent may call back into a coordinator — and a self-referential loop must not be mistaken for fresh authority.

Note

A fully verified chain proves the leaf's authority is genuine and bounded. It does not prove the leaf should act. "The chain verifies" answers "is this authorized?", never "is doing this now correct and safe?" — the latter is a separate decision the verification does not make.

Risks & limitations

  • ▸Partial verification is the big trap: checking only the leaf, or only signatures without the subset/attenuation check, lets a widening or forged intermediate through.
  • ▸Trusting the chain to name its own root defeats the purpose. The root must be anchored independently in the verifier's own trust store.
  • ▸Unbounded depth invites denial-of-service and runaway sub-delegation; always cap length and fail closed when exceeded.
  • ▸Revocation across a chain is hard: revoking an intermediate must invalidate everything below it, which long-lived chains make expensive — prefer short TTLs so broken links expire quickly (see Revocation & independent verification).
  • ▸Clock and constraint handling must fail closed: an expired link, a wrong audience, or a caveat the verifier does not understand must reject the chain, never be skipped.
  • ▸A valid chain authorizes; it does not judge. Bounded, genuine authority can still be pointed at the wrong action.

Exercise

Note

You receive a three-link delegation chain. The leaf signature is valid and the leaf's requested operation is within the leaf link's granted scope. List at least four additional checks you must still perform before authorizing, and explain what attack each one stops.

Expected direction: At minimum: (1) every intermediate signature is valid and each link's issuer equals the previous link's holder — stops forged/spliced links; (2) the chain roots in a key your verifier independently trusts — stops attacker-chosen roots; (3) each hop is a subset/attenuation of its parent (effective authority = intersection) — stops widening; (4) all constraints hold now (expiry, audience, purpose) and unknown caveats fail closed — stops expired/misdirected use; (5) no key repeats — stops cycles; (6) depth within bound — stops runaway sub-delegation/DoS.

Check your understanding

Why is leaf-only verification insufficient for a delegation chain?
Because authority flows from the root through every intermediate; a forged, widening, or expired intermediate invalidates the leaf's authority even if the leaf link itself looks fine. You must verify end-to-end back to a trusted root.
Where must the root of trust come from?
From the verifier's own independently configured trust store — never from the chain itself, which could otherwise name an attacker-controlled root.
Why bound chain depth and reject cycles?
A depth bound stops unbounded sub-delegation and DoS; cycle rejection prevents a key delegating back to itself being mistaken for new authority and masking loops.

References

  1. [1]Ellison et al. — SPKI Certificate Theory (RFC 2693)
  2. [2]Cooper et al. — Internet X.509 PKI Certificate and CRL Profile (RFC 5280, path validation)
  3. [3]Birgisson et al. — Macaroons: Cookies with Contextual Caveats (2014)

References point to public, well-known technical sources for further study.

Related articles