From f5d4ab1f446a4a9ddb9675cb4f212f32ed96dc99 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Fri, 28 Nov 2025 15:25:00 +0100 Subject: [PATCH] Patch certificate chain in generateKey reply When an application generates a key with an attestation request, the `generateKey` method returns a `KeyMetadata` object which contains the full, unpatched certificate chain. This leaves a potential detection vector open. A sophisticated application could inspect the returned data in its own process memory and discover the original, hardware-backed certificates before they are used for attestation, thus detecting the hooking framework. This commit introduces a post-transaction hook for the `generateKey` transaction. After the genuine KeyStore service has executed the request, this hook intercepts the reply parcel. It extracts the certificate chain from the `KeyMetadata`, applies the patching routine, and then reconstructs the reply with the modified (patched) certificate chain. --- .../shim/KeyMintSecurityLevelInterceptor.kt | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) 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 7d1f7bc..169bc29 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 @@ -10,6 +10,7 @@ import android.system.keystore2.* import java.security.KeyPair import java.security.cert.Certificate import java.util.concurrent.ConcurrentHashMap +import org.matrix.TEESimulator.attestation.AttestationPatcher import org.matrix.TEESimulator.attestation.KeyMintAttestation import org.matrix.TEESimulator.config.ConfigurationManager import org.matrix.TEESimulator.interception.core.BinderInterceptor @@ -77,13 +78,11 @@ class KeyMintSecurityLevelInterceptor( reply: Parcel?, resultCode: Int, ): TransactionResult { - // We only care about successful 'importKey' transactions to clean cached keys. - if ( - code == IMPORT_KEY_TRANSACTION && - resultCode == 0 && - reply != null && - !InterceptorUtils.hasException(reply) - ) { + // We only care about successful transactions. + if (resultCode != 0 || reply == null || InterceptorUtils.hasException(reply)) + return TransactionResult.SkipTransaction + + if (code == IMPORT_KEY_TRANSACTION) { logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid) data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR) @@ -91,6 +90,21 @@ class KeyMintSecurityLevelInterceptor( data.readTypedObject(KeyDescriptor.CREATOR) ?: return TransactionResult.SkipTransaction cleanupKeyData(KeyIdentifier(callingUid, keyDescriptor.alias)) + } else if (code == GENERATE_KEY_TRANSACTION) { + logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid) + + val metadata: KeyMetadata = + reply.readTypedObject(KeyMetadata.CREATOR) + ?: return TransactionResult.SkipTransaction + val originalChain = + CertificateHelper.getCertificateChain(metadata) + ?: return TransactionResult.SkipTransaction + if (originalChain.size > 1) { + val newChain = AttestationPatcher.patchCertificateChain(originalChain, callingUid) + CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow() + + return InterceptorUtils.createTypedObjectReply(metadata) + } } return TransactionResult.SkipTransaction } @@ -148,6 +162,8 @@ class KeyMintSecurityLevelInterceptor( writeTypedObject(response.metadata, 0) } return TransactionResult.OverrideReply(0, resultParcel) + } else if (parsedParams.attestationChallenge != null) { + return TransactionResult.Continue } // If not generating, clear any stale state for this alias and let the call proceed.