Threat modeling a secure enclave
TL;DR - A secure enclave is not "a safe you cannot open." It is a coprocessor that answers requests from a computer you have already conceded to the attacker, with no screen of its own and no reliable way to know who is really asking. The interesting engineering is not the vault. It is everything downstream of admitting that the enclave's sole caller is hostile: binding policy to keys instead of to requests, treating human presence as a load bearing security primitive with no cryptographic substitute, and accepting that some final root of trust cannot be verified, only trusted.
This is the second entry in a series about threat modeling everyday technology. The first entry modeled the macOS Trusted Computing Base from the outside in, starting from a promise and working out to the components that had to hold it up. This one goes further in one direction: it takes a single idea, an isolated key custodian on the die, and derives the entire threat model from the shape of that idea, before checking it against anything Apple, Google, or a hardware wallet vendor actually shipped. Apple's Secure Enclave appears once, briefly, near the end, as one instantiation of a pattern this post is trying to derive independently.
1. The pitch
Someone pitches you a new product. A small, physically isolated coprocessor sits on the same die as the main application processor. It has its own processor core, its own memory, its own storage. It holds cryptographic keys. The main operating system can ask it to perform operations with those keys, sign this, decrypt that, but the main OS can never read the key material itself. The pitch's closing line is always some version of: so even if the rest of the system is fully compromised, the keys stay safe.
That sentence is airtight against a question nobody asked. It promises that the key cannot be read out. It says nothing about whether the key can be misused without ever being read. Three narrower questions pull that gap open, and everything in this post follows from them.
2. Three questions that break the pitch open
Safe how?
Not extractable, or not usable. Those are two different products, and the pitch quietly assumes the second while only proving the first.
If I cannot steal your key but can ask you to sign anything I want, I never needed to steal it. Confidentiality of the key material and authorized use of the key are separate security properties, and the second one is the harder engineering problem by a wide margin. A vault that a thief can never pick, but that opens for anyone who asks it politely, is not a vault. It is a formality with good marketing.
Who is your client?
The enclave has exactly one channel in and out of itself: the main processor. In this threat model, the main processor is already conceded to the attacker, because that concession is the entire premise of building an enclave in the first place. So structurally, one hundred percent of the enclave's callers, on every single request, are potentially the attacker. There is no separate wire for "the legitimate app" and "the attacker." They are the same wire.
Authenticating the caller through that channel is not merely difficult. It is structurally impossible. Whatever arrives labeled "this request came from the banking app, approved by the user" is, from the enclave's position, just bytes that the untrusted OS chose to send. The label is not evidence. It is a claim made by the party you have already stopped trusting.
You have no screen.
When a human approves an operation, how do they learn what they are approving? The prompt text, the amount, the recipient, the operation name, all of it is rendered by the main OS: the exact party this whole design distrusts. The consent mechanism, reduced to its actual mechanics, is: ask a liar to describe a question accurately, then treat the human's answer as informed consent to that question.
These three questions are the spine of everything that follows. Each one produces a constraint the architecture has no way around, only ways to live inside.
3. Constraint one: policy has to be bound at creation, not at request
You cannot stop the hostile client from asking. Extraction resistance, keeping the bits themselves from leaving the die, is a tractable engineering problem: shielding, doping tricks, tamper sensors, all covered later in this post. Use resistance enforced through the request channel is not tractable, for a simple reason: anything the enclave checks at request time is itself attacker controlled. A flag that says "this operation was pre-approved by the user" is just another field in a message from the party you distrust. Checking it proves nothing.
So the decision has to move. Instead of deciding at request time whether an operation is allowed, the key itself carries its own rules, fixed permanently at the moment it is created: requires presence, requires a specific biometric enrollment, expires after a date, is scoped to one algorithm and nothing else. Those rules cannot be renegotiated later by a request, because a request is exactly the thing you cannot trust.
Generalized, the pattern is: when you cannot trust the ask, bind authority to the object instead of the request. This shows up anywhere a system has to make decisions on behalf of an untrusted caller, it is the underlying reason capability-based designs tend to outlive access-control-list designs under adversarial pressure.
There is a sharp consequence hiding in this. Whoever creates the key decides its policy, permanently, at a moment when the pressure is to ship something that works. A convenient API default that mints unconditional keys, no presence requirement, no biometric binding, quietly discards the entire model above, and it does so at the one layer, the API surface, where nothing in the hardware architecture can catch the mistake. The enclave's threat model has, at this point, become a human factors problem inherited by the architecture rather than solved by it.
4. Constraint two: living with a hostile client
You cannot authenticate the caller. But you do own state the caller cannot touch: your own memory, your own counters, your own execution. Every workable defense against a hostile client comes from that one asymmetry, and the pattern is consistent enough to state as a rule: a defense only counts if the enclave can enforce it alone, without needing the caller's honesty for any part of it.
- Counting has to happen inside. You can track failed attempts, but only if the counter lives in memory the caller cannot write to. An external counter is just more attacker-controlled state: rewrite it, replay an old value, and the count resets for free.
- Refuse to be a general-purpose oracle. Offer narrow business operations, "sign this transaction of this exact shape," not generic primitives like "decrypt this blob" or "sign this hash." A generic primitive lets the caller decide what the operation means. A narrow one keeps that decision inside the boundary.
- Make requests non-replayable. Nonces and session binding mean a captured request and response pair cannot be recorded and reissued later to reproduce an approval that happened once.
- Control what answers reveal. Timing, error codes, and response length are all side channels once you accept that the hostile caller can invoke you as many times as it wants. A distinct error for "wrong PIN" versus "no such key" is a free oracle for account enumeration. Every answer needs to look the same regardless of which internal branch produced it.
- Refuse to let concurrent requests interleave. Two half-finished operations executing side by side inside the one component that was supposed to be safe is exactly how a race condition gets built inside the trust boundary meant to prevent races.
Notice what all five have in common: none of them require the caller to behave. That is the actual design constraint here, not a style preference. Anything that depends on the caller being honest for even one step has already been defeated by the premise of this section.
5. Constraint three: the human is the only thing the attacker cannot forge
An attacker can produce any bit pattern the enclave will accept. They cannot produce a person. That single asymmetry makes human presence a load bearing security primitive, with a physical basis instead of a cryptographic one, and it deserves the same scrutiny as any other primitive in this model.
There are two channels here, and they are wildly different in difficulty.
Inbound, human to enclave, is solvable. Wire the sensor or the button directly to the silicon, and bind the peripheral cryptographically, so that a swapped sensor, one that always reports "yes" regardless of what actually touched it, is not trusted by the enclave. This is a hardware engineering problem with hardware engineering answers.
Outbound, enclave to human, is the hard one, because it inherits the liar-screen problem from section two directly. There are only three honest options, and none of them make the problem disappear.
- Give the enclave its own display. It is worth pausing on the fact that an entire industry, hardware wallets, arrived at this exact constraint independently and added a screen. That convergence is evidence the constraint is real, not evidence that a screen is a nice-to-have feature.
- Accept the lying screen, and make the approval gesture unforgeable and specific enough that a compromise cannot be silent or operate at scale: a distinct physical action per approval, rate limited, with no "approve everything for the next hour" toggle reachable through that same untrusted screen.
- Find an out-of-band path entirely, a second device or a second channel, which trades one set of problems (cost, usability, another thing to lose) for the original one, and which still ultimately renders something, somewhere, in software.
The irreducible result, stated plainly: without a trusted output path, you can prove that a human approved something. You cannot prove they approved this operation, on this resource, for this amount. The design consequence is to bound tightly what sits behind a single approval, one narrowly scoped operation rather than a standing grant, and to keep presence fresh and tied to that one operation instead of opening a window of implicit trust after it.
One more trap belongs here, distinct from the liar screen: presence is not the same thing as consent. A finger touching a sensor proves a finger touched a sensor. It does not prove a mind agreed to the operation, someone can physically guide another person's hand to the sensor. The two failures, an untrustworthy description of the request and an untrustworthy interpretation of the gesture, are separate, and a design that only solves one has only solved half the problem.
6. State that must survive power loss, and the rollback trap
The enclave needs some state to survive a power cycle: attempt counters, revocation records, key metadata. On-die non-volatile storage is expensive and has limited write endurance, so in practice some of this state ends up living outside the enclave, in ordinary storage the main OS can reach.
Here is the trap. Encryption gives confidentiality. Authentication gives integrity. Neither one gives freshness. An old message that was validly encrypted and validly signed is not a forged message, it is a completely genuine message from an earlier point in time, replayed. Two consequences follow immediately: rewind the attempt counter and brute force becomes unlimited again, because every rewind buys a fresh set of tries. Restore a snapshot from before a key was revoked and that key is alive again, with a perfectly valid signature attesting to a state of the world that no longer holds.
The fix has to be state that is physically monotonic, something that literally cannot decrease even for an attacker with full read and write access to the external storage and full knowledge of whatever secret is supposed to protect it, because any secret protecting freshness is itself just more state with the identical rollback problem. That chain has to terminate somewhere physical: a fuse that can be blown but never unblown, or a dedicated tamper-resistant counter chip with its own limited endurance.
Two related traps are easy to miss. First, an attacker who cannot roll state backward can instead simply withhold it, refuse to deliver the latest value at all, which has to be treated as equivalent to a rollback attempt: fail closed, or withholding becomes a slower version of the same reset. Second, a power cut in the middle of a state write is a hazard independent of any attacker intent, persistent updates must be atomic, commit or do not commit, with nothing observable in between, or a well-timed power interruption becomes its own manipulation primitive with no attacker sophistication required at all.
7. Boot: the thing that verifies everything cannot itself be verified
If the main processor is the one loading the enclave's firmware, the enclave inherits its trust from precisely the component it exists to be independent of. So it has to boot on its own, before, and independently of, the main processor being trusted at all.
Something in that boot chain has to go first, and by construction, the very first thing cannot be checked against anything else, only trusted outright. That first thing is immutable mask ROM, physically burned in at fabrication. State the bargain this makes plainly: unforgeability was purchased by giving up patchability. A bug discovered in that first-stage code is permanent for the entire life of that hardware generation. No update will ever reach it, because updatability is exactly the property that was traded away to get unforgeability.
Firmware loaded after that first stage reintroduces the freshness problem from section six in a new form. An old firmware image was validly signed at the time it was released, which means old-but-signed is not the same thing as safe, so anti-rollback protection for firmware versions is required for the same underlying reason attempt counters need it. Signatures on that firmware also have to bind to the specific device, or at minimum a narrow device class, or a single old, validly signed image becomes a universal skeleton key: capture it once, replay it onto every unit of that model, and downgrade the entire fleet at once.
Debug and test interfaces used during bring-up and manufacturing have to be irreversibly fused off before the device ever leaves that controlled environment. Skip this and the hardware marketed as tamper resistant ships with its own back door, installed as a manufacturing convenience nobody remembered to remove.
One structural inversion is worth naming directly, because it is easy to walk past. Since the enclave boots first and independently, it is positioned to gate whether the main processor is allowed to boot at all. The small, mostly forgotten coprocessor becomes the arbiter for the large general-purpose one, not the other way around. That is an unusual power relationship for a component most users have never heard of.
8. Side channels and physical attacks
These layer by required attacker capability, from a laptop to a lab.
Passive, no physical intrusion required. Timing, power draw, electromagnetic emission, and contention on anything the enclave shares with the untrusted world, cache lines, a bus, a power rail. The sting specific to this design is section four's hostile client: it can invoke the enclave a million times overnight and average away noise that no single measurement would ever reveal. Constant-time behavior is therefore not a hardening pass bolted on afterward, it is structural. Any branch or memory access pattern that depends on secret data is a channel a patient, unauthenticated, effectively unlimited caller will eventually resolve.
Active fault injection. The goal is not reading the secret directly, it is making the enclave skip a comparison: a glitched clock edge or a voltage spike timed to turn a rejection check into a pass-through. Defenses are redundant checks (compute the same comparison twice, independently, and compare the results too), randomized timing so an attacker cannot predict the exact instant to glitch, and self-monitoring of voltage and clock so the enclave can refuse to operate at all outside its specified envelope, rather than operate incorrectly inside a manipulated one.
Invasive. Decapsulation, physical probing, optically reading fuse states under a microscope. Defended with shielding, signal routing buried away from the surface, and tamper sensors that trigger a response, key zeroization, on detected intrusion.
External memory, the state discussed in section six, needs the same three properties as any data crossing a hostile channel: encryption, authentication, and replay protection, with a key that itself changes every boot, so that physically extracting the encrypted blob does not help without also breaking the current session's key.
The honest framing to close on: none of this raises the cost to infinity. It raises the cost, and more importantly, it requires physical possession of the specific device. That asymmetry, not invulnerability, is the actual product. An attacker who breaks one unit in a lab has not broken the fleet, and that is by design rather than by accident.
9. Availability: your own defenses are a weapon you hand to the attacker
Lockout is a deliberate feature: fail attempts enough times and the enclave locks. But whoever can trigger failed attempts controls the lockout, and if lockout means wipe, that hands the attacker a destruction primitive for free. They do not need to break in. They only need to trigger the failure path.
There is no clean setting here, only a choice of preferred failure. Wipe-on-lockout stops brute force at the cost of enabling attacker-triggered destruction. No-wipe avoids that destruction at the cost of a weaker deterrent. Either choice is defensible. Presenting either one as if it were the obviously correct answer is not.
Hardware death is its own failure mode: if the chip physically dies, the keys inside die with it. Someone always proposes escrow to soften that. Escrow is usually the point where the careful architecture built across the rest of this post unravels, because it opens a second route to the secret, and that route typically lives somewhere far softer than tamper-resistant silicon, a cloud account reachable by phishing a support representative, for instance. Every property described so far, unforgeable channels, physical resistance, fail-closed state, buys nothing if there is a parallel path around all of it.
The single most important availability point in this whole model: the attacker does not need to defeat the enclave. They only need to block it, and observe what the rest of the system does in its absence. Any software fallback anywhere, "enclave unreachable, continuing with a software-only path", is the actual attack surface, and everything described in this post becomes irrelevant to reaching it. Fail-closed has to be enforced by every caller of the enclave, not merely asserted inside the enclave itself, because the enclave has no mechanism to enforce behavior in a caller it has already conceded is hostile.
A smaller, related failure mode: finite key storage. An attacker, or simply careless key management, can exhaust the enclave's storage slots, which is its own denial-of-service surface, distinct from lockout but living in the same family of problems.
10. Manufacturing: the boundary you cannot re-cross, and the trust you cannot check
The device secret at the root of everything: was it generated on the die itself, or injected from outside during manufacturing? These are very different claims. Generated on die means, in principle, that no external party ever possessed it. Injected means some external system generated it and wrote it in, which quietly makes that system, and everyone with access to it, part of the trust base whether or not it is documented as such.
If it was injected, destruction of that external copy is a claim, not a checkable fact. Nobody downstream of the factory can verify that the injection system deleted its copy rather than, say, logging it.
Whoever holds the private key matching the verification key burned into the boot ROM can sign firmware that every device in the field will accept, indefinitely, until a hardware revision changes the embedded verification key. That makes this fundamentally an organizational problem sitting underneath the silicon: key custody, insider risk, and legal compulsion of whoever holds that signing key, not just an engineering one.
The supply chain compounds this. The part is handled by the fab, by packaging, by test, each a separate organization with separate access. Factory test interfaces need to be irreversibly disabled before the part leaves that environment, and the honest open question is: verified by whom? Someone has to attest that the fuse actually blew, and that attestation is itself unverifiable by the eventual end customer.
The close, stated directly: none of this is verifiable from outside. Attestation is circular, because the enclave proves its identity using a key whose own provenance is the exact question being asked. Every hardware root of trust bottoms out in trust the manufacturer. That is not a flaw waiting for one more layer of cryptography to fix. It is the axiom the entire model rests on. The honest move is to say so plainly, and ask what breaks if it turns out to be false, rather than let a long enough chain of signatures quietly stand in for an answer.
11. The complete sketch
The diagram below is the working model for this post: the untrusted main processor on one side of a single narrow channel, the enclave with its internal blocks on the other, the human wired in directly but with no way back except through the processor it should be bypassing, external state that only means anything relative to an on-die counter, a manufacturing boundary crossed exactly once, and a ring of physical attack surface around all of it.
12. Security invariants
Policy and keys
- A key's authorization policy is fixed at creation and cannot be loosened by a later request.
- No API path mints a key with no presence or authentication requirement by default.
- The enclave exposes narrow, named operations, never a generic sign-anything or decrypt-anything primitive.
Client and requests
- Every counter that gates an attempt lives in memory the caller cannot write.
- Requests are bound to a session and cannot be replayed from a captured transcript.
- Error responses do not vary in timing, content, or shape based on which internal branch produced them.
- Concurrent requests cannot interleave inside a single stateful operation.
Human presence
- The sensor or button reporting presence is cryptographically bound to the enclave; a substituted peripheral is rejected.
- A single approval authorizes one specific, narrowly scoped operation, never a standing window of future operations.
- Presence freshness is checked per operation, not cached across a session.
State and freshness
- Security-relevant counters are physically monotonic and cannot be rolled back even with full access to external storage.
- Withholding the latest state is treated the same as an attempted rollback: the enclave fails closed.
- State updates are atomic; a power interruption mid-write never leaves an ambiguous or exploitable partial state.
Boot and firmware
- The enclave's first-stage code is immutable and boots independently of the main processor.
- Firmware anti-rollback prevents an old, validly signed image from being reinstalled.
- Firmware signatures bind to the specific device or a narrow device class, not to the model as a whole.
- Debug and test interfaces are irreversibly disabled before the device leaves the manufacturing environment.
Physical and side channel
- No enclave operation has a data-dependent timing, power, or memory access signature.
- Security checks are redundant and independently computed, not single points of failure against fault injection.
- The enclave refuses to operate outside its specified voltage and clock envelope.
- External state storage is encrypted, authenticated, and replay-protected with a key that rotates every boot.
Availability
- No caller anywhere in the system has a software fallback path that bypasses the enclave.
- The consequence of lockout, whether wipe or freeze, is a documented, deliberate choice, not an accident of implementation.
- No key escrow path exists outside the enclave's own tamper-resistant boundary.
Manufacturing
- Device secret provenance, generated on die versus injected, is documented and not asserted without evidence.
- Firmware signing key custody is treated as a governed organizational control, not merely a technical one.
- Factory test access disablement is independently verified, not only self-attested by the party performing it.
13. The attack tree
Goal: extract or misuse a key protected by the enclave
|
|-- Extract the key material directly
| |-- Passive side channel (timing, power, EM) over many calls
| |-- Active fault injection to skip a comparison
| |-- Invasive probing after decapsulation
| +-- Compromise the manufacturing injection path
|
|-- Misuse the key without extracting it
| |-- Send requests as the hostile client, no presence needed
| | (only works if policy binding at creation failed)
| |-- Replay a captured request or response
| |-- Exploit an interleaved, racing pair of requests
| +-- Exploit a generic sign-anything or decrypt-anything primitive
|
|-- Defeat the human presence requirement
| |-- Spoof or substitute the presence sensor
| |-- Render a misleading prompt on the untrusted screen
| |-- Physically guide presence without informed consent
| +-- Abuse a standing approval window instead of a single operation
|
|-- Defeat freshness and rollback protection
| |-- Roll back the attempt counter via a restored snapshot
| |-- Roll back firmware to an old, validly signed version
| |-- Withhold the latest state instead of rewinding it
| +-- Interrupt a state write mid-commit
|
|-- Force the system around the enclave entirely
| |-- Trigger lockout to force a software fallback path
| |-- Exhaust finite key storage as denial of service
| +-- Reach an escrowed copy of the key through a softer channel
|
+-- Undermine the root of trust itself
|-- Compromise the firmware signing key's custody
|-- Falsify or bypass factory test-interface disablement
+-- Exploit an unverified claim about secret provenance
14. Residual risk
Even a carefully built enclave, one that satisfies every invariant above, leaves risk on the table. Naming it plainly is the point of a threat model, not a mark against the design.
- A compromised OS can still obtain anything a key's policy allows without presence, for any key whose creator chose not to require presence. The enclave enforces policy; it does not choose good policy on your behalf.
- Presence does not distinguish coerced presence from consenting presence. A held hand and a willing one produce the same signal.
- Even with an unforgeable approval gesture, the lying screen can still obtain broad, vague consent. It cannot obtain silent, bulk consent, but "please confirm" is not the same guarantee as "confirm this exact transfer of this exact amount to this exact recipient."
- The hardware root of trust is fundamentally unverifiable from outside. Trust in the manufacturer is irreducible, not a temporary gap waiting on better tooling.
- Physical attacks do not scale, but they do work, and they work well against one specific, high-value, targeted individual, which is a meaningfully different risk than mass compromise.
- A software fallback path anywhere in the system defeats fail-closed behavior entirely, regardless of how well the enclave itself is built.
- Any escrow path defeats every property in this document at once, because it offers a second, usually softer, route to the same secret.
15. Validation: what you can actually go check
A threat model is only useful once it produces something a reader can test against a real system.
- Enumerate which of your keys require no user presence at all. That list is your standing blast radius the moment the main OS is ever fully compromised.
- Test that a biometric-bound key actually invalidates when enrollment changes. Add a new fingerprint or face, and check whether keys bound to the old enrollment still work. If they do, the binding is cosmetic.
- Measure how many operations succeed after a single authentication event. That count is the real size of the window an attacker gets from one moment of presence, not the size the documentation claims.
- Deny a permission prompt and watch what happens next. Does the app simply fail, or does it fall back to something weaker that never asked for presence at all?
- Read your own prompt text as literally as an attacker would want a user to. Does it name the specific operation and the specific resource, or could the same text plausibly describe almost any request the app might send?
16. Final verdict
None of the constraints in this post came from reading a datasheet. They came from taking the pitch seriously and asking what has to be true for it to hold: safe against extraction is not safe against misuse, a component with one caller and no way to authenticate that caller has to defend itself without the caller's cooperation, and a component with no display of its own can never fully close the gap between "a human approved something" and "a human approved this."
Apple's Secure Enclave, and the hardware wallets that independently converged on adding their own screens, are instantiations of this pattern, not the source of it. The pattern holds for any coprocessor that holds a secret, answers a hostile client, and has to involve a human it cannot directly see or speak to. That is the useful part of a threat model: it turns a specific product into a general shape, one that can be checked, argued with, and applied somewhere the vendor never tested it.