From a58c4798c309c891cc560bc6a057acdc0760829e Mon Sep 17 00:00:00 2001 From: Enginex0 Date: Sat, 30 May 2026 04:35:16 +0100 Subject: [PATCH] fix(keystore): mirror TEE device-ID capability generateKey synthesized device-property attestation unconditionally: the BRAND/DEVICE/PRODUCT/MANUFACTURER/MODEL tags that setDevicePropertiesAttestationIncluded emits. A forged key thus succeeded where real silicon returns CANNOT_ATTEST_IDS. Hardware that never provisioned device IDs cannot attest them, so forging them is an over-capability tell: a genuine device of the same class fails the identical request. Add DeviceAttestationService.canAttestDeviceIds, a lazy probe that asks the real TEE to attest device properties once and caches the verdict. It is gated behind isTeeFunctional, so a silent or dead TEE short-circuits to "cannot attest" without a second doomed probe. handleGenerateKey now returns KEYMINT_CANNOT_ATTEST_IDS for any device-ID or device-property attestation the real TEE cannot satisfy, uniformly across AUTO, PATCH, and GENERATE. Basic attestation carries none of these tags and is untouched. Verified on 23106RN0DA: kknd under GENERATE now WARNs, matching a stock locked-bootloader device. Principle: forge health, mirror capability. --- .../attestation/DeviceAttestationService.kt | 43 +++++++++++++++++++ .../shim/KeyMintSecurityLevelInterceptor.kt | 17 ++++++++ 2 files changed, 60 insertions(+) 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 aef1fcb..443cd8b 100644 --- a/app/src/main/java/org/matrix/TEESimulator/attestation/DeviceAttestationService.kt +++ b/app/src/main/java/org/matrix/TEESimulator/attestation/DeviceAttestationService.kt @@ -1,6 +1,7 @@ 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 @@ -60,12 +61,23 @@ 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. @@ -106,6 +118,37 @@ 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. 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 69955cf..4573953 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,6 +29,7 @@ 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 @@ -477,6 +478,22 @@ 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) + } + if(hasDeviceIdAttestation && !AndroidPermissionUtils.hasDeviceAttestationPermission(callingUid)) { SystemLogger.warning("[TX_ID: $txId] Rejecting DEVICE_ID_ATTESTATION for uid=$callingUid") return InterceptorUtils.createErrorReply(KEYMINT_CANNOT_ATTEST_IDS)