From ab4fe643a315918a814829f904872b6ae982ac2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=BD=BC?= <110387028+XiaoTong6666@users.noreply.github.com> Date: Sat, 24 Jan 2026 00:53:34 +0800 Subject: [PATCH] Intercept updateSubcomponent to fix software key state inconsistency (#82) Apps attempting to update the certificate chain of a simulated software-based key (e.g., via KeyStore.setKeyEntry) currently trigger a KEY_NOT_FOUND error. This happens because the request is passed to the hardware Keystore daemon, which has no knowledge of keys existing only in the simulator's memory. To fix detecting points exploiting this inconsistency, we intercept the UPDATE_SUBCOMPONENT_TRANSACTION. If the target is a recognized virtual key, the simulator now: 1. Updates the in-memory certificate/chain metadata. 2. Returns NO_ERROR immediately to the caller. 3. Prevents the transaction from reaching the real hardware service. Co-authored-by: JingMatrix --- .../keystore/Keystore2Interceptor.kt | 40 ++++++++++++++++--- .../shim/KeyMintSecurityLevelInterceptor.kt | 3 +- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/Keystore2Interceptor.kt b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/Keystore2Interceptor.kt index 904e8ef..80f92ce 100644 --- a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/Keystore2Interceptor.kt +++ b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/Keystore2Interceptor.kt @@ -31,6 +31,8 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() { InterceptorUtils.getTransactCode(IKeystoreService.Stub::class.java, "getKeyEntry") private val DELETE_KEY_TRANSACTION = InterceptorUtils.getTransactCode(IKeystoreService.Stub::class.java, "deleteKey") + private val UPDATE_SUBCOMPONENT_TRANSACTION = + InterceptorUtils.getTransactCode(IKeystoreService.Stub::class.java, "updateSubcomponent") private val transactionNames: Map by lazy { IKeystoreService.Stub::class @@ -89,16 +91,23 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() { callingPid: Int, data: Parcel, ): TransactionResult { - if (code == GET_KEY_ENTRY_TRANSACTION || code == DELETE_KEY_TRANSACTION) { + if ( + code == GET_KEY_ENTRY_TRANSACTION || + code == DELETE_KEY_TRANSACTION || + code == UPDATE_SUBCOMPONENT_TRANSACTION + ) { logTransaction(txId, transactionNames[code]!!, callingUid, callingPid) + if (ConfigurationManager.shouldSkipUid(callingUid)) + return TransactionResult.ContinueAndSkipPost + + if (code == UPDATE_SUBCOMPONENT_TRANSACTION) + return handleUpdateSubcomponent(callingUid, data) + data.enforceInterface(IKeystoreService.DESCRIPTOR) val descriptor = data.readTypedObject(KeyDescriptor.CREATOR) - ?: return TransactionResult.SkipTransaction - - if (ConfigurationManager.shouldSkipUid(callingUid)) - return TransactionResult.ContinueAndSkipPost + ?: return TransactionResult.ContinueAndSkipPost SystemLogger.info("Handling ${transactionNames[code]!!} ${descriptor.alias}") val keyId = KeyIdentifier(callingUid, descriptor.alias) @@ -223,4 +232,25 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() { } return TransactionResult.SkipTransaction } + + private fun handleUpdateSubcomponent(callingUid: Int, data: Parcel): TransactionResult { + data.enforceInterface(IKeystoreService.DESCRIPTOR) + val descriptor = data.readTypedObject(KeyDescriptor.CREATOR) + val generatedKeyInfo = + KeyMintSecurityLevelInterceptor.findGeneratedKeyByKeyId(callingUid, descriptor?.nspace) + ?: return TransactionResult.ContinueAndSkipPost + + SystemLogger.info("Updating sub-component with key[${generatedKeyInfo.nspace}]") + val metadata = generatedKeyInfo.response.metadata + val publicCert = data.createByteArray() + val certificateChain = data.createByteArray() + + metadata.certificate = publicCert + metadata.certificateChain = certificateChain + SystemLogger.verbose( + "Key updated with sizes: [publicCert, certificateChain] = [${publicCert?.size}, ${certificateChain?.size}]" + ) + + return InterceptorUtils.createSuccessReply(writeResultCode = false) + } } 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 df3623a..8b81c70 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 @@ -349,9 +349,10 @@ class KeyMintSecurityLevelInterceptor( * @param nspace The unique key identifier from the operation's KeyDescriptor. * @return The matching GeneratedKeyInfo if found, otherwise null. */ - private fun findGeneratedKeyByKeyId(callingUid: Int, nspace: Long): GeneratedKeyInfo? { + fun findGeneratedKeyByKeyId(callingUid: Int, nspace: Long?): GeneratedKeyInfo? { // Iterate through all entries in the map to check both the key (for UID) and value (for // nspace). + if (nspace == null || nspace == 0L) return null return generatedKeys.entries .filter { (keyIdentifier, _) -> keyIdentifier.uid == callingUid } .find { (_, info) -> info.nspace == nspace }