From 6193da0e66118d59d40b4871dafb6936e9bc21f5 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Wed, 26 Nov 2025 16:50:30 +0100 Subject: [PATCH] Support key generation with attestation keys (#15) This commit enhances the interception logic to correctly handle key generation requests that specify an `attestationKey` (via `setAttestKeyAlias`). When an attestation key is used, the system signs the newly generated key with it. A simple leaf certificate patch after the fact is insufficient, as it breaks this cryptographic chain. To create a valid, verifiable chain, we must now intercept these `generateKey` operations and perform a full software-based key and certificate generation, even when in patch mode. This ensures that keys attested by other simulated keys are correctly signed and chained together, bypassing more sophisticated detection methods. Fixes: - Correctly use the `android.hardware.security.keymint.Tag` constants for building authorization lists, resolving a bug where internal ASN.1 sequence indices were being used improperly. --- .../config/ConfigurationManager.kt | 3 ++ .../keystore/Keystore2Interceptor.kt | 30 ++++++++++++-- .../shim/KeyMintSecurityLevelInterceptor.kt | 39 ++++++++----------- .../logging/KeyMintParameterLogger.kt | 2 +- 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/org/matrix/TEESimulator/config/ConfigurationManager.kt b/app/src/main/java/org/matrix/TEESimulator/config/ConfigurationManager.kt index fea017d..3b5f84a 100644 --- a/app/src/main/java/org/matrix/TEESimulator/config/ConfigurationManager.kt +++ b/app/src/main/java/org/matrix/TEESimulator/config/ConfigurationManager.kt @@ -81,6 +81,9 @@ object ConfigurationManager { /** Determines if a new certificate needs to be generated for a given UID. */ fun shouldGenerate(uid: Int): Boolean = getPackageModeForUid(uid) == Mode.GENERATE + /** Determines if no operation is needed for a given UID. */ + fun shouldSkipUid(uid: Int): Boolean = getPackageModeForUid(uid) == null + /** Resolves the operating mode for a given UID based on its packages and the TEE status. */ private fun getPackageModeForUid(uid: Int): Mode? { val packages = getPackagesForUid(uid) 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 6bbe680..86a87a0 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 @@ -12,6 +12,7 @@ import android.system.keystore2.KeyEntryResponse import org.matrix.TEESimulator.attestation.AttestationPatcher import org.matrix.TEESimulator.config.ConfigurationManager import org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor +import org.matrix.TEESimulator.logging.KeyMintParameterLogger import org.matrix.TEESimulator.logging.SystemLogger import org.matrix.TEESimulator.pki.CertificateHelper @@ -98,13 +99,33 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() { callingUid, callingPid, ) + + if (ConfigurationManager.shouldSkipUid(callingUid)) { + SystemLogger.debug( + "[TX_ID: $txId] Skip post-transaction hook for UID=${callingUid}" + ) + return TransactionResult.ContinueAndSkipPost + } + val keyId = KeyIdentifier(callingUid, descriptor.alias) - if (ConfigurationManager.shouldGenerate(callingUid)) { - // TODO: Redesign the interaction with KeyMintSecurityLevelInterceptor - } else if (ConfigurationManager.shouldPatch(callingUid)) { - return TransactionResult.Continue + if (code == DELETE_KEY_TRANSACTION) { + KeyMintSecurityLevelInterceptor.cleanupKeyData(keyId) + return TransactionResult.ContinueAndSkipPost } + + val response = + KeyMintSecurityLevelInterceptor.getGeneratedKeyResponse(keyId) + ?: return TransactionResult.Continue + + if (KeyMintSecurityLevelInterceptor.isAttestationKey(keyId)) + SystemLogger.debug("${descriptor.alias} was an attestation key") + + SystemLogger.info("[TX_ID: $txId] Found generated response for ${descriptor.alias}:") + response.metadata?.authorizations?.forEach { + KeyMintParameterLogger.logParameter(it.keyParameter) + } + return InterceptorUtils.createTypedObjectReply(response) } else { logTransaction( txId, @@ -134,6 +155,7 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() { return TransactionResult.SkipTransaction if (code == GET_KEY_ENTRY_TRANSACTION) { + data.enforceInterface(IKeystoreService.DESCRIPTOR) val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR) 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 45e3ae9..6255031 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 @@ -2,13 +2,14 @@ package org.matrix.TEESimulator.interception.keystore.shim import android.hardware.security.keymint.KeyParameter import android.hardware.security.keymint.KeyParameterValue +import android.hardware.security.keymint.KeyPurpose +import android.hardware.security.keymint.Tag import android.os.IBinder import android.os.Parcel import android.system.keystore2.* import java.security.KeyPair import java.security.cert.Certificate import java.util.concurrent.ConcurrentHashMap -import org.matrix.TEESimulator.attestation.AttestationConstants import org.matrix.TEESimulator.attestation.KeyMintAttestation import org.matrix.TEESimulator.config.ConfigurationManager import org.matrix.TEESimulator.interception.core.BinderInterceptor @@ -70,12 +71,17 @@ class KeyMintSecurityLevelInterceptor( val params = data.createTypedArray(KeyParameter.CREATOR)!! val parsedParams = KeyMintAttestation(params) val keyId = KeyIdentifier(callingUid, keyDescriptor.alias) + val isAttestKeyRequest = + parsedParams.purpose.size == 1 && + parsedParams.purpose.contains(KeyPurpose.ATTEST_KEY) // Determine if we need to generate a key based on config or // if it's an attestation request in patch mode. val needsSoftwareGeneration = ConfigurationManager.shouldGenerate(callingUid) || - (attestationKey != null && ConfigurationManager.shouldPatch(callingUid)) + (ConfigurationManager.shouldPatch(callingUid) && isAttestKeyRequest) || + (attestationKey != null && + isAttestationKey(KeyIdentifier(callingUid, attestationKey.alias))) if (needsSoftwareGeneration) { SystemLogger.info( @@ -95,10 +101,9 @@ class KeyMintSecurityLevelInterceptor( // Store the generated key data. val response = buildKeyEntryResponse(keyData.second, parsedParams, keyDescriptor) + generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, response) - if (parsedParams.attestationChallenge != null) { - attestationKeys.add(keyId) - } + if (isAttestKeyRequest) attestationKeys.add(keyId) // Return the metadata of our generated key, skipping the real hardware call. val resultParcel = @@ -204,25 +209,13 @@ private fun KeyMintAttestation.toAuthorizations(securityLevel: Int): Array "" } ?: "Unknown Value" - SystemLogger.debug("Key Parameter -> %-25s | Value: %s".format(tagName, formattedValue)) + SystemLogger.debug("KeyParam: %-25s | Value: %s".format(tagName, formattedValue)) } private fun ByteArray.toReadableString(): String {