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 010bc2d..176a7da 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 @@ -33,6 +33,7 @@ import org.matrix.TEESimulator.pki.CertificateHelper import org.matrix.TEESimulator.pki.KeyBoxManager import org.matrix.TEESimulator.pki.NativeCertGen import org.matrix.TEESimulator.util.AndroidDeviceUtils +import org.matrix.TEESimulator.util.AndroidPermissionUtils class KeyMintSecurityLevelInterceptor( private val original: IKeystoreSecurityLevel, @@ -267,11 +268,24 @@ class KeyMintSecurityLevelInterceptor( return InterceptorUtils.createErrorReply(RESPONSE_INVALID_ARGUMENT) } - if (params.any { it.tag == Tag.DEVICE_UNIQUE_ATTESTATION }) { + if (params.any { it.tag == Tag.DEVICE_UNIQUE_ATTESTATION } && !AndroidPermissionUtils.hasUniqueIdAttestationPermission(callingUid)) { SystemLogger.warning("[TX_ID: $txId] Rejecting DEVICE_UNIQUE_ATTESTATION for uid=$callingUid") return InterceptorUtils.createErrorReply(KEYMINT_CANNOT_ATTEST_IDS) } + val hasDeviceIdAttestation = params.any { + it.tag == Tag.ATTESTATION_ID_IMEI || + it.tag == Tag.ATTESTATION_ID_MEID || + it.tag == Tag.ATTESTATION_ID_SERIAL || + it.tag == Tag.DEVICE_UNIQUE_ATTESTATION || + it.tag == Tag.ATTESTATION_ID_SECOND_IMEI + } + + if(hasDeviceIdAttestation && !AndroidPermissionUtils.hasDeviceAttestationPermission(callingUid)) { + SystemLogger.warning("[TX_ID: $txId] Rejecting DEVICE_ID_ATTESTATION for uid=$callingUid") + return InterceptorUtils.createErrorReply(KEYMINT_CANNOT_ATTEST_IDS) + } + val keyId = KeyIdentifier(callingUid, keyDescriptor.alias) val isAttestKeyRequest = parsedParams.isAttestKey() diff --git a/app/src/main/java/org/matrix/TEESimulator/util/AndroidPermissionUtils.kt b/app/src/main/java/org/matrix/TEESimulator/util/AndroidPermissionUtils.kt new file mode 100644 index 0000000..d5877f3 --- /dev/null +++ b/app/src/main/java/org/matrix/TEESimulator/util/AndroidPermissionUtils.kt @@ -0,0 +1,72 @@ +package org.matrix.TEESimulator.util + +import android.annotation.SuppressLint +import android.content.Context +import android.content.pm.PackageManager +import org.matrix.TEESimulator.logging.SystemLogger + +object AndroidPermissionUtils { + + @SuppressLint("PrivateApi", "DiscouragedPrivateApi") + private fun getGlobalContext(): Context? { + return try { + // 1. Get the hidden ActivityThread class via reflection + val activityThreadClass = Class.forName("android.app.ActivityThread") + + // 2. Invoke the static currentActivityThread() method + val currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread") + currentActivityThreadMethod.isAccessible = true + val activityThread = currentActivityThreadMethod.invoke(null) + + if (activityThread == null) { + SystemLogger.warning("Reflection: ActivityThread.currentActivityThread() returned null") + return null + } + + // 3. Try to get the application context + val getApplicationMethod = activityThreadClass.getDeclaredMethod("getApplication") + getApplicationMethod.isAccessible = true + val application = getApplicationMethod.invoke(activityThread) as? Context + + if (application != null) return application + + // 4. Fallback to getSystemContext() if application is null (often happens in system_server) + val getSystemContextMethod = activityThreadClass.getDeclaredMethod("getSystemContext") + getSystemContextMethod.isAccessible = true + getSystemContextMethod.invoke(activityThread) as? Context + + } catch (e: Exception) { + SystemLogger.error("Reflection failed to get global context for permission check", e) + null + } + } + + /** + * Core permission check. + */ + fun hasPermission(uid: Int, permission: String): Boolean { + val context = getGlobalContext() ?: run { + SystemLogger.warning("AndroidPermissionUtils: Context is null, failing permission check safely.") + return false + } + + val result = context.checkPermission(permission, -1, uid) + return result == PackageManager.PERMISSION_GRANTED + } + + fun hasDeviceAttestationPermission(uid: Int): Boolean { + return hasPermission(uid, "android.permission.READ_PRIVILEGED_PHONE_STATE") + } + + fun hasUniqueIdAttestationPermission(uid: Int): Boolean { + return hasPermission(uid, "android.permission.REQUEST_UNIQUE_ID_ATTESTATION") + } + + fun hasManageUsersPermission(uid: Int): Boolean { + return hasPermission(uid, "android.permission.MANAGE_USERS") + } + + fun hasDumpPermission(uid: Int): Boolean { + return hasPermission(uid, "android.permission.DUMP") + } +} \ No newline at end of file