Mobile Security Research — Hardware-Backed Attestation
Hardware-backed key attestation is the modern Android answer to the question: "how does a server know it's actually talking to a real device — and not a script, an emulator, or a tampered app?" Done correctly, it's a strong primitive: the device proves possession of a private key whose existence is rooted in a TEE or StrongBox, and the server gets a cryptographic chain of evidence back to a trusted root. Done with subtle architectural mistakes, the chain looks complete but isn't actually binding the right things.
I've spent time on the second case — analysing how a production Android application implements hardware-backed attestation, where the trust boundaries actually sit, and where the architecture's stated guarantees diverge from what the code actually enforces. The work below describes the approach without describing the target.
What Hardware-Backed Attestation Is Supposed to Do
The intended trust model, simplified:
- The app generates a keypair inside the Android Keystore with
setAttestationChallenge(), asking the OS to bind the key to hardware (TEE / StrongBox). - The OS returns an X.509 certificate chain. The leaf certificate's public key is the one just generated; intermediate and root certificates are signed by Google's hardware attestation roots.
- The app sends this chain to a server. The server verifies the chain against Google's published roots, parses the attestation extension, and confirms that the key is hardware-backed and not extractable.
- From this point on, signatures from that key prove the app is running on real hardware with the expected security properties.
That's the intended behaviour. The interesting work — and the work that produces meaningful security findings — is comparing that intended behaviour against what production codebases actually do.
How I Approach This Kind of Analysis
Every attestation analysis I've done follows roughly the same structure. The order matters: start broad, narrow only after you understand the protocol's intended shape.
1 — Static Analysis of the Application
Decompile the application and identify the attestation-related classes — anything referencing KeyStore, KeyGenParameterSpec, setAttestationChallenge, setIsStrongBoxBacked, or the relevant cryptographic primitives. Map the lifecycle of the keypair: where it's generated, where it's stored, where the resulting certificate chain is read, and where each piece is sent over the wire. The shape of the code tells you most of what you need to know about the trust model.
2 — Live Traffic Observation
Pair the static picture with live observation. Run the app under a controlled MITM and watch the actual attestation handshake — what gets sent, what the server accepts, what it rejects. Discrepancies between the decompiled flow and the observed flow are usually where the most interesting questions live.
3 — Threat Modelling Against Realistic Adversaries
For each piece of identifying or binding data the app sends to the server, ask: what is this binding actually proving? A field that looks like it identifies the device may not be cryptographically bound to the hardware-attested key — it might just be sent alongside it. The trust model only holds if every claim the server depends on is anchored in something the attacker can't forge.
4 — Multi-Tenant Device Reasoning
Most attestation reasoning assumes one user per device. The interesting attacks live in the gaps when multiple users share a device, or when one user changes accounts, or when an attacker has temporary physical access. I reason explicitly about these scenarios when reviewing attestation flows — they're where stated guarantees most often break down.
Methodology Notes
A few principles I've found generally true across this kind of work:
- Read the spec, not the blog post. Google's hardware attestation documentation, Android's
KeyMint/KeymasterHAL specifications, and the relevant X.509 RFCs are the primary sources. Tutorial posts almost universally simplify away the parts where things can go wrong. - What's bound matters more than what's signed. Strong cryptography over the wrong inputs is still wrong. The question to keep asking is "what does this signature actually attest to?"
- The certificate chain is necessary but not sufficient. A valid chain proves a key exists in hardware. It does not, by itself, prove which app is using it, which user, or which device-identity tuple the server is claiming a relationship with.
- Document everything as you go. Mobile RE work moves through dozens of decompiled methods and intercepted requests. A scratch file of "what I observed, what I expected, what surprised me" is the difference between a finding and a vague hunch.
Disclosure Posture
Findings from this kind of work belong in private hands until they're remediated. I document analyses fully but don't publish identifying details, vulnerability specifics, or proof-of-concept material on public surfaces. Where appropriate, findings are shared with the relevant party under standard responsible-disclosure terms.
This page exists to signal the discipline, not to disclose the artefact.
Adjacent Work on This Site
The cryptographic chops this work uses overlap heavily with several other projects:
- Distributed Cryptographic Signing Platform — implementing a proprietary protocol byte-for-byte across multiple runtimes; same depth of cryptographic spec-reading.
- TLS Certificate Chain Inspector — chain validation, trust-store comparison, OCSP/CRL checking.
- X.509 Encryption Service — self-signed PKI from scratch as a learning artefact.