7 May 2026 · 5 min read

What hardware-backed actually promises

A key in the Android Keystore cannot be copied off the device, and that is the whole promise. An attacker with root can still use it. Most vault designs quietly assume more.

"Hardware-backed" is the most reassuring phrase in Android security, and it promises less than most people hear. When I built the encrypted media vault inside the Minimalist Calculator app, the key that protects every file lives in the Android Keystore, bound to the device's secure hardware. That is the right design. It is also a design whose guarantee fits in one sentence, and the sentence is not "your data is safe from an attacker who controls the phone". It is "your key cannot leave the phone". The difference between those two sentences is the subject of this post.

The promise, in Google's words

Google's Keystore documentation describes two measures under the heading extraction prevention. First, key material never enters the application process: an app hands plaintext to a system process that performs the operation and hands the result back, so an attacker who compromises the app can use the app's keys but cannot read them. Second, key material can be bound to secure hardware, a trusted execution environment or a secure element, so that it is never exposed outside that hardware at all. Then comes the sentence that matters: if the Android OS is compromised or an attacker can read the device's internal storage, the attacker might be able to use any app's Keystore keys on that device, but cannot extract them from it.

Read that carefully, because it is unusually honest, and because it is the sentence most product copy quietly omits. Hardware-backed keys defend against one thing, extraction. They do not defend against use. An attacker with root on the phone can call the same decrypt function the app calls, with the same key, and get the same plaintext. What they cannot do is walk away with a file that lets them decrypt tomorrow, on another machine, without the phone.

Where the extraction line and the borrow line sit Four stacked layers from top to bottom: the app process, the Keystore system process, KeyMint in the trusted execution environment, and the StrongBox secure element. A highlighted line between the app process and the Keystore process marks where extraction stops. A second line above the app process marks where borrowing starts: anything running as the app or as root can ask the layers below to use the key. Two lines, not one The layers a Keystore operation passes through App process holds plaintext, asks for operations Keystore system process carries out operations on the app's behalf KeyMint in the TEE key material never leaves StrongBox secure element separate chip, API 28 and up the brick line is where extraction stops; borrowing starts anywhere above it
Illustrative: the layers named in the Android Keystore documentation, drawn to show what each line defends.

The borrow test

So the question to ask about any key is not whether it is hardware-backed. It is what an attacker who has root on this device can do by borrowing the key in place: sign, decrypt, silently, for how long, and how many times. I call it the borrow test, and a key passes only when borrowing it without the user noticing is impossible. That standard sounds severe, and it is the only one that matches what the vault was built to protect against.

The Keystore has parameters that change the answer to the borrow test, and they are the parameters people leave at their defaults. A key can require user authentication, so that an operation succeeds only if the user has unlocked the device recently, or, with a timeout of zero, only for one operation after a fresh biometric or credential prompt. A key can require the device to be unlocked at all. A key can be generated inside StrongBox rather than the TEE, so that even a compromised TEE cannot use it without the separate chip's cooperation. And a key's properties can be attested, so that a server can verify that the key it is talking to really does live in hardware and really does carry those constraints.

Attestation deserves a sentence of its own, because it is the only one of these that reaches off the device. The KeyMint and Keymaster implementations can produce a certificate chain, rooted in a Google key, that states where a key lives and what constraints it carries. A server that receives that chain can refuse to talk to a key that is not in hardware, or that lacks authentication binding, before any data is sent. For a vault with no server it does nothing. For a wallet or a banking client it is the mechanism that lets the other side check your configuration rather than take your word for it.

Each of those changes what borrowing costs. A key with no authentication binding can be borrowed silently and forever: the attacker with root decrypts every file while the phone sits on the desk. A key bound to a per-operation biometric can be borrowed only while the user's finger is on the sensor, once per press, which makes borrowing loud and bounded. A key that requires an unlocked device cannot be borrowed from a phone that was seized locked. None of these stops extraction, because extraction was already stopped. They stop the thing extraction prevention never addressed.

What each key configuration still protects against, by attacker A grid with four attacker columns: a compromised app, root on the OS, physical seizure of a locked device, and chip-level extraction. Three key configurations as rows: a plain hardware-backed key, a key requiring an unlocked device, and a key requiring authentication per operation in StrongBox. Filled cells mean the configuration still protects the data. The plain key protects only against chip-level extraction. The unlocked-device key adds seizure while locked. The per-operation key adds the compromised app and root cases as long as the user is not tricked into authenticating. The borrow test as a grid Filled: the data stays protected against that attacker APP ROOT SEIZED EXTRACT Hardware-backed, no binding Plus unlocked device required Plus auth per operation, StrongBox APP: the app process is compromised. ROOT: the OS is compromised. SEIZED: the locked phone is taken. EXTRACT: the chip is attacked directly. the last row holds only while the user is not tricked into authenticating
Illustrative: a reading of the guarantees described in the Keystore documentation, not a test result; the last row's protection depends on the user refusing prompts they did not expect.

What the vault does with this

The Minimalist Calculator is a working calculator that opens an AES-GCM encrypted vault when a secret tap sequence is entered. Run the borrow test against it and the honest answer is in three parts. Against a stranger who borrows the unlocked phone for a minute, the disguise and the tap sequence do the work; the Keystore is irrelevant, because that attacker never reaches the key. Against someone who images the storage, the Keystore does everything: the files on disk are ciphertext and the key is not on the disk. Against someone with root on the running device, the only thing that helps is authentication binding, because without it root can ask the Keystore to decrypt exactly as the app does.

That third case is the one the phrase hardware-backed is quietly assumed to cover, and it does not. The design decision that actually covers it is to bind the vault key to a fresh user authentication, so that decrypting a file requires the owner's biometric at the moment of decryption, and so that an attacker with root gets a prompt the owner did not ask for. It costs a prompt. It is the only part of the design that turns silent borrowing into a visible event.

When each Keystore guarantee arrived, by API level A timeline of API levels: 23, hardware-backed key flags and secure-hardware checks; 24, key attestation; 28, StrongBox and the unlocked-device requirement; 30, per-operation authentication parameters. The API 28 point is highlighted. API 23 Secure-hardware flags API 24 Key attestation API 28 StrongBox, unlocked-device API 30 Auth per operation
Source: the Android Keystore documentation and the KeyGenParameterSpec.Builder reference, which lists the API level of each parameter.

Why the gap survives

The gap between extraction and use survives in product designs for a simple reason: extraction is the threat that sounds like a movie, and use is the threat that sounds like an inconvenience. A key leaving the device is a scene; a prompt appearing at an odd moment is a support ticket. So designs invest in the guarantee they already have and skip the one they need, and the marketing copy that says hardware-backed is, in the narrow sense, true.

The other reason is that the parameters that close the gap have costs a demo never shows. Per-operation authentication means a biometric prompt every time the vault opens a file, which is fine for a vault and unacceptable for a messaging app. Unlocked-device binding means background work cannot use the key, which breaks a sync that runs overnight. StrongBox has its own limits on key sizes and throughput. Each product has to decide how much borrowing it can tolerate and pay for the binding that bounds it, and the decision cannot be made by reading a feature list.

The rule

For each key, write down what an attacker with root can do by borrowing it: which operation, how silently, how often, for how long. Then set the key's authentication, device-state and hardware parameters until the answer is "not without the owner noticing". If you cannot get there, say so in the threat model rather than in the marketing. Hardware-backed means the key stays on the phone. What the phone does with the key is still up to you.

AndroidKey ManagementHardware Security
All writing

Written by Mohd Shayan

Get new posts by email

Occasional essays on engineering, AI, and building for the people technology leaves behind.

One email per new post. Unsubscribe any time.

Subscribe with RSS