$ verify.py ./the-agent-that-held-your-keys.aep.json

Who Said the Agent
Could Do That?

A correctly-signed action can still be an unauthorized action — and you can catch it.

The setup

Your AI agent holds your keys.
Every signature it produces verifies.

So who checks that it stayed inside the job you actually gave it?

The lay of the land

A spectrum of machine-checkable legitimacy

Mechanism
The question it answers
Anchored by
How it breaks
BotGuardbot detection
Is there a real human or genuine browser?
environment heuristics — no anchor
solve it / reverse-engineer it
PATprivate access token
Is this a genuine client device?
hardware (Secure Enclave)
attests the client, not the action
PACTagent legitimacy
Is this a legitimate agent?
no hardware anchor
forge / replay the claim
AEPaction evidence
What did it do, under whose authority, in mandate?
issuer key + TPM + scope
the target of this talk
The attack surface migrates: presenceauthority

Why the ground is shifting

The first moat is draining.

The once-esoteric TLS fingerprinting that made bot-detection an expert's game — cipher ordering, JA3/JA4, automation signals — is now a couple of prompts away.

Large language models made the expertise ambient. “Is there a human?” stops being a question worth defending.

So the question moves up

Not who is at the keyboard — but what the action was.

What

What did the agent actually do?

Whose authority

Under whose grant did it act?

In scope?

Was the act inside the mandate it was given?

The sandbox

Public, MIT, offline. One dependency. It exists to be broken.

github.com/tyche-institute/aep-sandbox
$ git clone github.com/tyche-institute/aep-sandbox $ pip install -r requirements.txt # cryptography (Ed25519), Python 3.8+

Three layers, weakest first, so the room can feel what each one buys — and where it gives. A fresh clone ships the public trust store and pre-signed samples: breaking it without the private keys is the game.

Layer 1 · unsigned hash chain

Live break

A hash chain only stops an attacker who can't recompute it.

forge_rechain.py
# edit a single field, skip the hash $ python3 attacks/tamper_field.pyDENY:content_mutated # rewrite the record AND recompute every hash $ python3 attacks/forge_rechain.pyledger PASSES

The lesson, on purpose: hashing without anchoring is un-anchored legitimacy in miniature. Catch it in your own logging pipelines.

Layer 2 · signed AEP + scoped mandate

Three signatures, one grant of authority.

issuer— signs →a credential: this principal owns this public key
principal— signs →a mandate: this agent may act within THIS scope, until exp
agent— signs →the AEP: this is the action I took, and its outcome

verifier checks all three + the scope + freshness + one-shot replay → ALLOW / DENY

The attack matrix

Six easy breaks crypto already closes. One that it can't.

tamper_field — edit a field, skip the hash
DENY:content_mutated
forge_rechain — rewrite + recompute hashes
ledger passes · DENY:aep_sig_invalid
forge_full — mint a whole AEP with your keys
DENY:issuer_not_listed
swap_mandate — splice in a broader mandate
DENY:aep_sig_invalid
strip_sig — blank the signature
DENY:aep_sig_invalid
replay — submit the same AEP twice
ALLOW · then DENY:replayed
exceed_scope — valid signature, out-of-scope action
DENY:scope_violation

The first six are the ones cryptography already closes. The seventh — every signature valid, still refused — is the point.

Layer 3 · TPM-bound outcome

Fold the outcome digest into a TPM quote.

Forging the outcome, or replaying a stale quote, dies at tpm2_checkquote.

Honest on stage: this is an emulated TPM (swtpm), not a hardware root of trust. I show exactly where that seam sits rather than hand-waving it.

The whole thesis, in one command

$ verify.py samples/exceed-scope.aep.json  →  DENY:scope_violation

Correctly signed. /Out of mandate. /Rejected.

The agent is not its own judge.

Why this is the hard part

“Faking a mandate” is really two attacks.

Forge / escalate the token

Mint or widen the mandate itself. Cryptography catches this — bad issuer, bad signature, done.

Act outside a genuine mandate

A real, correctly-signed grant — used past its intent. Only mandate-conformance checking catches this.

Design principle, not magic: the agent must never be the sole and final judge of its own mandate.

What I'm honest about

The gaps, named — not hand-waved.

Freshness

Offline evidence proves integrity, authority and scope. Whether a mandate still stands needs short-lived mandates or signed status lists.

vs. RATS

Remote attestation attests the platform — not the act-under-mandate.

vs. capability tokens

Macaroons, biscuits, UCAN authorize actions but leave no offline per-action evidence.

Break it live

CTF · from your seat

Make verify.py say ALLOW for an action the mandate does not authorize.

did_you_break_it.py
$ cp my_forgery.aep.json attacks/out/CHALLENGE.aep.json $ python3 did_you_break_it.py # a wrongful ALLOW is a break

A refund over the cap · a method off the allow-list · an issuer outside the trust anchor. My own four-lens pre-publication bypass hunt found no key-free bypass. Prove me wrong — during the talk.

The sentence to bring home

A correctly-signed action can still be an unauthorized action —
and you can catch it.

Who Said the Agent Could Do That? 01 / 15
← → move  ·  O overview  ·  F fullscreen