From 254fb0f0a9f3c2fea013ac5e8bca0c6c1876d365 Mon Sep 17 00:00:00 2001 From: Enginex0 Date: Sat, 30 May 2026 14:51:48 +0100 Subject: [PATCH] fix(keystore): forge device-property attestation The canAttestDeviceIds gate (3575c74) probed the live TEE to decide whether to honor device-property/ID attestation. That probe is gated on isTeeFunctional, which is false on every dead-TEE device the module serves, so GENERATE mode rejected all such requests with CANNOT_ATTEST_IDS, including GMS Play Integrity's hardware path, which broke BHIM and any UPI/Play-Integrity-gated app. Remove the gate. Device-property attestation (BRAND/MODEL/...) now forges unconditionally, as genuine devices universally attest it. Device-ID attestation stays governed by the pre-existing caller-permission check, the real KeyMint rule: privileged callers get it, ordinary apps do not. Drop the now-unused DeviceAttestationService.canAttestDeviceIds probe. --- .../attestation/DeviceAttestationService.kt | 50 ++----------------- .../shim/KeyMintSecurityLevelInterceptor.kt | 22 ++------ 2 files changed, 9 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/org/matrix/TEESimulator/attestation/DeviceAttestationService.kt b/app/src/main/java/org/matrix/TEESimulator/attestation/DeviceAttestationService.kt index 443cd8b..09b25e4 100644 --- a/app/src/main/java/org/matrix/TEESimulator/attestation/DeviceAttestationService.kt +++ b/app/src/main/java/org/matrix/TEESimulator/attestation/DeviceAttestationService.kt @@ -1,7 +1,6 @@ package org.matrix.TEESimulator.attestation import android.annotation.SuppressLint -import android.os.Build import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import java.security.KeyPairGenerator @@ -61,23 +60,12 @@ object DeviceAttestationService { // A unique alias for the key used to perform the TEE functionality check. private const val TEE_CHECK_KEY_ALIAS = "TEESimulator_AttestationCheck" - // Alias for the device-ID attestation capability probe. - private const val DEVICE_ID_CHECK_KEY_ALIAS = "TEESimulator_DeviceIdCheck" - /** * Lazily determines if the device's TEE is functional by attempting to generate an * attestation-backed key pair. The result is cached. */ val isTeeFunctional: Boolean by lazy { checkTeeFunctionality() } - /** - * Lazily mirrors whether the real TEE can attest device identifiers/properties (the tags added - * by `setDevicePropertiesAttestationIncluded`). Hardware that never provisioned device IDs - * returns CANNOT_ATTEST_IDS; the synthesizer consults this so it never forges a capability the - * real silicon lacks. Cached. - */ - val canAttestDeviceIds: Boolean by lazy { checkDeviceIdAttestation() } - /** * Lazily fetches and parses attestation data from a genuinely generated certificate. The result * is cached. Returns null if the TEE is not functional or parsing fails. @@ -118,37 +106,6 @@ object DeviceAttestationService { } } - /** - * Probes whether the real TEE can satisfy device-ID/property attestation, mirroring its actual - * capability. Gated behind [isTeeFunctional] so a dead TEE never triggers a second doomed - * probe — it simply reports `false` (cannot attest), the faithful result for such hardware. - */ - private fun checkDeviceIdAttestation(): Boolean { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return false - if (!isTeeFunctional) return false - return try { - val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } - val keyPairGenerator = - KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore") - val challenge = ByteArray(16).apply { SecureRandom().nextBytes(this) } - val spec = - KeyGenParameterSpec.Builder(DEVICE_ID_CHECK_KEY_ALIAS, KeyProperties.PURPOSE_SIGN) - .setAlgorithmParameterSpec(ECGenParameterSpec("secp256r1")) - .setDigests(KeyProperties.DIGEST_SHA256) - .setAttestationChallenge(challenge) - .setDevicePropertiesAttestationIncluded(true) - .build() - keyPairGenerator.initialize(spec) - keyPairGenerator.generateKeyPair() - runCatching { keyStore.deleteEntry(DEVICE_ID_CHECK_KEY_ALIAS) } - SystemLogger.info("Device-ID attestation supported by TEE.") - true - } catch (_: Exception) { - SystemLogger.info("Device-ID attestation not supported by TEE; mirroring as cannot-attest.") - false - } - } - /** * Retrieves the attestation certificate generated during the TEE check. The key entry is * deleted after retrieval to clean up. @@ -192,9 +149,10 @@ object DeviceAttestationService { // The extension's value is an ASN.1 sequence. val keyDescriptionSeq = ASN1Sequence.getInstance(extension.extnValue.octets) SystemLogger.verbose { - val formattedString = keyDescriptionSeq.joinToString(separator = ", ") { - AttestationPatcher.formatAsn1Primitive(it) - } + val formattedString = + keyDescriptionSeq.joinToString(separator = ", ") { + AttestationPatcher.formatAsn1Primitive(it) + } "Cached attestation data: $formattedString" } val fields = keyDescriptionSeq.toArray() diff --git a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt index a6aa379..7ad5b9c 100644 --- a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt +++ b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt @@ -29,7 +29,6 @@ import java.util.concurrent.locks.LockSupport import org.matrix.TEESimulator.attestation.AttestationBuilder import org.matrix.TEESimulator.attestation.AttestationConstants import org.matrix.TEESimulator.attestation.AttestationPatcher -import org.matrix.TEESimulator.attestation.DeviceAttestationService import org.matrix.TEESimulator.attestation.KeyMintAttestation import org.matrix.TEESimulator.config.ConfigurationManager import org.matrix.TEESimulator.interception.core.BinderInterceptor @@ -478,22 +477,11 @@ class KeyMintSecurityLevelInterceptor( it.tag == Tag.ATTESTATION_ID_SECOND_IMEI } - val hasDevicePropertyAttestation = parsedParams.brand != null || - parsedParams.device != null || - parsedParams.product != null || - parsedParams.manufacturer != null || - parsedParams.model != null - - // Mirror the real TEE's capability: hardware that never provisioned device IDs - // returns CANNOT_ATTEST_IDS. Synthesizing device-ID/property attestation a chip of - // this class cannot produce is an over-capability tell — a genuine device fails the - // same request. Forge health, mirror capability. - if ((hasDeviceIdAttestation || hasDevicePropertyAttestation) && - !DeviceAttestationService.canAttestDeviceIds) { - SystemLogger.info("[TX_ID: $txId] Real TEE cannot attest device IDs; returning CANNOT_ATTEST_IDS for uid=$callingUid (mirroring hardware)") - return InterceptorUtils.createErrorReply(KEYMINT_CANNOT_ATTEST_IDS) - } - + // Device-ID attestation mirrors a real KeyMint: privileged callers (GMS/system) + // get it, ordinary apps get CANNOT_ATTEST_IDS. The permission check below is that + // rule. Device-property attestation (BRAND/MODEL/...) is honored unconditionally — + // genuine devices universally attest it, and it is what Play Integrity's hardware + // path needs. Capability is keyed to the device we present, not the real (dead) TEE. if(hasDeviceIdAttestation && !AndroidPermissionUtils.hasDeviceAttestationPermission(callingUid)) { SystemLogger.warning("[TX_ID: $txId] Rejecting DEVICE_ID_ATTESTATION for uid=$callingUid") return InterceptorUtils.createErrorReply(KEYMINT_CANNOT_ATTEST_IDS)