{"id":224728,"date":"2026-09-07T16:11:48","date_gmt":"2026-09-07T16:11:48","guid":{"rendered":"https:\/\/neodata.com.uy\/labs\/?p=224728"},"modified":"2026-09-07T16:11:48","modified_gmt":"2026-09-07T16:11:48","slug":"what-hardware-backed-means-on-android","status":"publish","type":"post","link":"https:\/\/neodata.com.uy\/labs\/what-hardware-backed-means-on-android\/","title":{"rendered":"What &#8220;hardware-backed&#8221; means on Android."},"content":{"rendered":"\n\n<p class=\"wp-block-paragraph\"><strong>Type:<\/strong> Primer<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Category:<\/strong> Research \u2014 Keys &amp; Secure Hardware<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Series:<\/strong> Key containers on mobile devices, part 1 of\n3<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Reading time:<\/strong> ~8 min<\/p>\n\n\n\n\n\n<h2 id=\"three-security-levels-one-keystore-api-and-a-certification-requirement-that-stops-short-of-the-strongest-one\" class=\"wp-block-heading\">Three\nsecurity levels, one keystore API, and a certification requirement that\nstops short of the strongest one.<\/h2>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">An application that generates a key through the Android Keystore\nreceives a handle and a promise. The promise is that the private key\nmaterial is protected by something better than the filesystem. What that\nsomething is varies by device, is not selected by the application, and\nis not evident from the handle.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">This article establishes the vocabulary. The two that follow cover\nwhat a device can prove about the key, and how an SDK should choose\namong the options.<\/p>\n\n\n\n\n\n<h2 id=\"the-hierarchy\" class=\"wp-block-heading\">The hierarchy<\/h2>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">Three security levels are defined: <strong>Software<\/strong>,\n<strong>TrustedEnvironment<\/strong>, and <strong>StrongBox<\/strong>.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Software<\/strong> means the key is protected by the operating\nsystem and nothing else. The key material is encrypted at rest, but the\nencryption and the enforcement of the key's usage constraints happen in\ncode running on the main processor, under the main kernel. A compromise\nof the OS is a compromise of the key.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>TrustedEnvironment<\/strong> \u2014 commonly called the TEE \u2014 means\nthe key lives in an isolated partition of the <em>same<\/em>\nsystem-on-chip that runs the application processor. On ARM devices this\nis normally implemented with TrustZone: a \"secure world\" with its own\nsmall operating system, most often Trusty or a vendor's own. The\napplication processor can request operations but cannot read the key. It\nshares silicon, memory controllers and power rails with the untrusted\nside.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>StrongBox<\/strong> means the key lives in a physically\nseparate tamper-resistant secure element with its own processor, its own\nsecure storage, and its own true random number generator. AOSP's\nhardware guidance requires a StrongBox implementation to provide a\ndiscrete CPU, secure storage, a high-quality TRNG, tamper-resistant\npackaging and side-channel resistance.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">The distinction between the second and third levels is the one that\nmatters for threat modelling. A TEE resists a compromised OS. A discrete\nsecure element additionally resists physical attack on the main SoC and\nthe side channels that come with sharing it.<\/p>\n\n\n\n\n\n<h2 id=\"what-certification-actually-requires\" class=\"wp-block-heading\">What certification\nactually requires<\/h2>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">This is where expectations and reality diverge, and it is the most\nuseful thing in this article.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">The Android Compatibility Definition Document, section 9.11, sets\nwhat a device must implement to be certified. Reading it carefully:<\/p>\n\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>A hardware-backed keystore, backed by an isolated execution\nenvironment, is a <strong>MUST<\/strong> for devices launching with\nrecent Android versions. Devices already launched on an earlier version\nare grandfathered, unless they declare the fingerprint feature, which\nrequires it.<\/li>\n\n\n<li>The mandated algorithm set for that keystore is RSA, AES, ECDSA and\nHMAC, with the MD5, SHA-1 and SHA-2 hash families.<\/li>\n\n\n<li>Key attestation with a hardware-protected signing key is a\n<strong>MUST<\/strong>.<\/li>\n\n\n<li><strong>StrongBox is STRONGLY RECOMMENDED, not required.<\/strong>\nThe CDD notes that it will likely become a requirement in a future\nrelease. Where it is implemented, the device must provide dedicated\nsecure hardware, and evaluation at EAL5 augmented with AVA_VAN.5 is\nstrongly recommended rather than mandatory.<\/li>\n\n<\/ul>\n\n\n\n\n<p class=\"wp-block-paragraph\">So a certified Android device is guaranteed to have a TEE-backed\nkeystore and key attestation. It is not guaranteed to have a discrete\nsecure element, and at the low end of the market it almost certainly\ndoes not.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">The algorithm set is narrower still at the top level. Where StrongBox\nexists, the mandated subset is RSA-2048, AES-128 and AES-256, ECDSA and\nECDH on P-256, HMAC-SHA-256, and Triple DES. <strong>StrongBox is not\nrequired to support any curve other than P-256, and does not support\nCurve25519.<\/strong> An application that requests Ed25519 in StrongBox\nis requesting something the specification does not promise.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><em>(Facts in this section reflect the CDD as of September 2026;\nsection 9.11 changes between releases and should be re-read against the\nversion a deployment targets.)<\/em><\/p>\n\n\n\n\n\n<h2 id=\"the-version-ladder\" class=\"wp-block-heading\">The version ladder<\/h2>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">The hardware abstraction layer was called <strong>Keymaster<\/strong>\nthrough Android 11 and renamed <strong>KeyMint<\/strong> in Android 12.\nThe application-facing Java API \u2014 the <code>AndroidKeyStore<\/code>\nprovider, <code>KeyGenParameterSpec<\/code>, <code>KeyInfo<\/code> \u2014 is\nstable across the rename.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">Each HAL version corresponds to an attestation schema version, and\nthe mapping is not linear:<\/p>\n\n\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\">\n<thead>\n<tr class=\"header\">\n<th>HAL version<\/th>\n<th>Android release<\/th>\n<th>Attestation schema<\/th>\n<th>Added<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr class=\"odd\">\n<td>Keymaster 1<\/td>\n<td>6.0 (API 23)<\/td>\n<td>\u2014<\/td>\n<td>Symmetric keys; <code>isInsideSecureHardware()<\/code><\/td>\n<\/tr>\n<tr class=\"even\">\n<td>Keymaster 2<\/td>\n<td>7.0 (API 24)<\/td>\n<td>1<\/td>\n<td>Key attestation; version binding<\/td>\n<\/tr>\n<tr class=\"odd\">\n<td>Keymaster 3<\/td>\n<td>8.0 (API 26)<\/td>\n<td>2<\/td>\n<td>ID attestation<\/td>\n<\/tr>\n<tr class=\"even\">\n<td>Keymaster 4<\/td>\n<td>9 (API 28)<\/td>\n<td>3<\/td>\n<td><strong>StrongBox<\/strong>; per-partition patch levels; secure key\nimport<\/td>\n<\/tr>\n<tr class=\"odd\">\n<td>Keymaster 4.1<\/td>\n<td>10 (API 29)<\/td>\n<td>4<\/td>\n<td><code>earlyBootOnly<\/code>, <code>unlockedDeviceReq<\/code>,\n<code>deviceUniqueAttestation<\/code><\/td>\n<\/tr>\n<tr class=\"even\">\n<td>KeyMint 1.0<\/td>\n<td>12 (API 31)<\/td>\n<td>100<\/td>\n<td>Rename; AIDL HAL; <code>KeyInfo.getSecurityLevel()<\/code><\/td>\n<\/tr>\n<tr class=\"odd\">\n<td>KeyMint 2.0<\/td>\n<td>13 (API 33)<\/td>\n<td>200<\/td>\n<td>Curve25519 for signing and key agreement<\/td>\n<\/tr>\n<tr class=\"even\">\n<td>KeyMint 3.0<\/td>\n<td>14 (API 34)<\/td>\n<td>300<\/td>\n<td><code>attestationIdSecondImei<\/code>; RKP as updatable module<\/td>\n<\/tr>\n<tr class=\"odd\">\n<td>KeyMint 4.0<\/td>\n<td>15 (API 35)<\/td>\n<td>400<\/td>\n<td><code>moduleHash<\/code> groundwork<\/td>\n<\/tr>\n<tr class=\"even\">\n<td>KeyMint 5.0<\/td>\n<td>16 (API 36)<\/td>\n<td>500<\/td>\n<td><code>moduleHash<\/code>; <code>ML_DSA<\/code> algorithm value and\n<code>mlDsaVariant<\/code> field in the attestation schema<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">Two notes on the last row, because it is easy to over-read.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">The presence of <code>ML_DSA<\/code> as an algorithm value and\n<code>mlDsaVariant<\/code> as an attestation field means the\n<em>interface<\/em> accommodates post-quantum signatures. It does not\nmean devices generate them. Google's own account places the\ndeveloper-facing ML-DSA API in Android 17, exposed through\n<code>KeyPairGenerator<\/code> for ML-DSA-65 and ML-DSA-87, implemented\nin the TEE \u2014 not in StrongBox, and not as a mandated capability. ML-DSA\nis absent from the HAL's list of primitives every implementation must\nprovide, and absent from the CDD's mandated algorithm set.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A capability defined in an interface is not a capability\navailable on a device.<\/strong> This distinction recurs throughout the\nsubject and is worth holding onto.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">There is also no clean public Java API to read the HAL version at\nruntime. The practical route is to generate a key with an attestation\nchallenge and parse the version out of the resulting certificate.<\/p>\n\n\n\n\n\n<h2 id=\"determining-what-you-got\" class=\"wp-block-heading\">Determining what you got<\/h2>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">Four mechanisms, with their constraints:<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>KeyInfo.getSecurityLevel()<\/code><\/strong>, API 31 and\nabove. Returns <code>SECURITY_LEVEL_SOFTWARE<\/code>,\n<code>SECURITY_LEVEL_TRUSTED_ENVIRONMENT<\/code>,\n<code>SECURITY_LEVEL_STRONGBOX<\/code>, or an unknown value. This is the\npreferred local check where available.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>KeyInfo.isInsideSecureHardware()<\/code><\/strong>,\navailable from API 23, deprecated at API 31. Returns a boolean and\ncannot distinguish TEE from StrongBox. Below API 31 it is the only\noption.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>KeyGenParameterSpec.Builder.setIsStrongBoxBacked(true)<\/code><\/strong>.\nRequests StrongBox explicitly. If StrongBox is unavailable for the\nrequested algorithm or key size, key generation throws\n<code>StrongBoxUnavailableException<\/code>. This is the sound way to\nprobe: attempt, catch, fall back.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>PackageManager.FEATURE_STRONGBOX_KEYSTORE<\/code><\/strong>\nand <strong><code>FEATURE_HARDWARE_KEYSTORE<\/code><\/strong>. System\nfeatures carrying integer version attributes that correspond to the HAL\nversion implemented at that level. Querying with a version lets an SDK\nread the declared HAL version per level without generating a key.<\/p>\n\n\n\n\n\n<h2 id=\"two-hazards\" class=\"wp-block-heading\">Two hazards<\/h2>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Failure is loud in one direction and quiet in the\nother.<\/strong> An explicit StrongBox request fails with an exception,\nso there is no silent downgrade from StrongBox to TEE. But a plain key\nrequest on a device with no hardware-backed keystore can be satisfied in\nsoftware, and only inspecting the resulting <code>KeyInfo<\/code> reveals\nit. An SDK that generates a key and never checks what it received has no\nidea what it is holding.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>All four mechanisms run in the application's own\nprocess.<\/strong> They report what the platform tells them. On a device\nwhere the platform has been modified, they report whatever the\nmodification chooses. They are appropriate for deciding what to attempt\nand for telling the user what protection is in effect. They are not\nevidence, and they cannot be the basis of an assurance claim made to\nanyone else.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">What can serve as evidence is the subject of the next article.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Series: Key containers on mobile devices<\/strong><\/p>\n\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><a href=\"https:\/\/neodata.com.uy\/labs\/the-certificate-has-two-lists\/\">Part\n2: The certificate has two lists.<\/a><\/li>\n\n\n<li><a href=\"https:\/\/neodata.com.uy\/labs\/seven-containers-ranked-by-what-you-can-prove\/\">Part\n3: Seven containers, ranked by what you can prove.<\/a><\/li>\n\n<\/ul>\n\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n\n\n<h2 id=\"references\" class=\"wp-block-heading\">References<\/h2>\n\n\n\n\n<ol class=\"wp-block-list\">\n\n<li>Android Open Source Project. <em>Hardware-backed Keystore.<\/em> <a href=\"https:\/\/source.android.com\/docs\/security\/features\/keystore\">https:\/\/source.android.com\/docs\/security\/features\/keystore<\/a><\/li>\n\n\n<li>Android Open Source Project. <em>Key and ID attestation.<\/em> <a href=\"https:\/\/source.android.com\/docs\/security\/features\/keystore\/attestation\">https:\/\/source.android.com\/docs\/security\/features\/keystore\/attestation<\/a><\/li>\n\n\n<li>Android Open Source Project. <a href=\"https:\/\/source.android.com\/docs\/compatibility\/16\/android-16-cdd\"><em>Compatibility\nDefinition Document<\/em><\/a>, \u00a79.11 Keys and Credentials.<\/li>\n\n\n<li>Android Developers. <em>Android Keystore system.<\/em> <a href=\"https:\/\/developer.android.com\/privacy-and-security\/keystore\">https:\/\/developer.android.com\/privacy-and-security\/keystore<\/a><\/li>\n\n\n<li>Android Developers. <a href=\"https:\/\/developer.android.com\/reference\/android\/security\/keystore\/KeyInfo\"><em>KeyInfo<\/em><\/a>\nand <a href=\"https:\/\/developer.android.com\/reference\/android\/security\/keystore\/KeyGenParameterSpec.Builder\"><em>KeyGenParameterSpec.Builder<\/em><\/a>\nAPI reference.<\/li>\n\n\n<li>Google Online Security Blog (25 March 2026). <a href=\"https:\/\/blog.google\/security\/security-for-the-quantum-era-implementing-post-quantum-cryptography-in-android\/\"><em>Security\nfor the Quantum Era: Implementing Post-Quantum Cryptography in\nAndroid.<\/em><\/a><\/li>\n\n<\/ol>\n\n","protected":false},"excerpt":{"rendered":"<p>Android exposes software, TEE-backed and StrongBox-backed keys through one keystore API. The effective security level, mandated algorithm set and available attestation evidence vary by device and release.<\/p>\n","protected":false},"author":2,"featured_media":224727,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10],"tags":[45,51,47,43,40,44],"nlabs_article_type":[36],"class_list":["post-224728","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-research","tag-android-keystore","tag-key-attestation","tag-keymint","tag-ml-dsa","tag-strongbox","tag-tee","nlabs_article_type-primer"],"_links":{"self":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts\/224728","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/comments?post=224728"}],"version-history":[{"count":1,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts\/224728\/revisions"}],"predecessor-version":[{"id":224770,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts\/224728\/revisions\/224770"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/media\/224727"}],"wp:attachment":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/media?parent=224728"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/categories?post=224728"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/tags?post=224728"},{"taxonomy":"nlabs_article_type","embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/nlabs_article_type?post=224728"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}