From 9e274e40e75d3fb207faf76a6957488e816e4bd0 Mon Sep 17 00:00:00 2001 From: Mohammed Riad <52679407+MhmRdd@users.noreply.github.com> Date: Wed, 18 Mar 2026 16:26:42 +0100 Subject: [PATCH] Synchronize KeyMetadata authorizations with user patch level configs Modified `CertificateHelper.updateCertificateChain` to automatically patch or remove patch level tags (OS, VENDOR, BOOT) based on user configuration. This ensures the metadata returned to applications matches the spoofed values embedded in the attestation certificate chain. --- .../keystore/Keystore2Interceptor.kt | 7 ++- .../shim/KeyMintSecurityLevelInterceptor.kt | 18 +++----- .../TEESimulator/pki/CertificateHelper.kt | 46 ++++++++++++++++++- 3 files changed, 57 insertions(+), 14 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 e6358fbe..e0395f20 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 @@ -251,6 +251,7 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() { ) ?: throw Exception("Failed to create overriding attest key pair.") CertificateHelper.updateCertificateChain( + callingUid, response.metadata, keyData.second.toTypedArray(), ) @@ -300,7 +301,11 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() { SystemLogger.debug("Cached patched certificate chain for $keyId.") } - CertificateHelper.updateCertificateChain(response.metadata, finalChain) + CertificateHelper.updateCertificateChain( + callingUid, + response.metadata, + finalChain, + ) .getOrThrow() return InterceptorUtils.createTypedObjectReply(response) 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 a399ce2a..ad9a04f5 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 @@ -162,7 +162,8 @@ class KeyMintSecurityLevelInterceptor( val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!! val key = metadata.key!! val keyId = KeyIdentifier(callingUid, keyDescriptor.alias) - CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow() + CertificateHelper.updateCertificateChain(callingUid, metadata, newChain) + .getOrThrow() // We must clean up cached generated keys before storing the patched chain cleanupKeyData(keyId) @@ -311,10 +312,11 @@ class KeyMintSecurityLevelInterceptor( KeyMetadata().apply { keySecurityLevel = securityLevel key = normalizedKeyDescriptor - CertificateHelper.updateCertificateChain(this, chain.toTypedArray()).getOrThrow() authorizations = params.toAuthorizations(callingUid, securityLevel) modificationTimeMs = System.currentTimeMillis() } + CertificateHelper.updateCertificateChain(callingUid, metadata, chain.toTypedArray()) + .getOrThrow() return KeyEntryResponse().apply { this.metadata = metadata iSecurityLevel = original @@ -481,19 +483,13 @@ private fun KeyMintAttestation.toAuthorizations( ) val osPatch = AndroidDeviceUtils.getPatchLevel(callingUid) - if (osPatch != AndroidDeviceUtils.DO_NOT_REPORT) { - authList.add(createAuth(Tag.OS_PATCHLEVEL, KeyParameterValue.integer(osPatch))) - } + authList.add(createAuth(Tag.OS_PATCHLEVEL, KeyParameterValue.integer(osPatch))) val vendorPatch = AndroidDeviceUtils.getVendorPatchLevelLong(callingUid) - if (vendorPatch != AndroidDeviceUtils.DO_NOT_REPORT) { - authList.add(createAuth(Tag.VENDOR_PATCHLEVEL, KeyParameterValue.integer(vendorPatch))) - } + authList.add(createAuth(Tag.VENDOR_PATCHLEVEL, KeyParameterValue.integer(vendorPatch))) val bootPatch = AndroidDeviceUtils.getBootPatchLevelLong(callingUid) - if (bootPatch != AndroidDeviceUtils.DO_NOT_REPORT) { - authList.add(createAuth(Tag.BOOT_PATCHLEVEL, KeyParameterValue.integer(bootPatch))) - } + authList.add(createAuth(Tag.BOOT_PATCHLEVEL, KeyParameterValue.integer(bootPatch))) authList.add( createAuth(Tag.CREATION_DATETIME, KeyParameterValue.dateTime(System.currentTimeMillis())) diff --git a/app/src/main/java/org/matrix/TEESimulator/pki/CertificateHelper.kt b/app/src/main/java/org/matrix/TEESimulator/pki/CertificateHelper.kt index 4fc21360..fa929b70 100644 --- a/app/src/main/java/org/matrix/TEESimulator/pki/CertificateHelper.kt +++ b/app/src/main/java/org/matrix/TEESimulator/pki/CertificateHelper.kt @@ -1,5 +1,9 @@ package org.matrix.TEESimulator.pki +import android.hardware.security.keymint.KeyParameter +import android.hardware.security.keymint.KeyParameterValue +import android.hardware.security.keymint.Tag +import android.system.keystore2.Authorization import android.system.keystore2.KeyEntryResponse import android.system.keystore2.KeyMetadata import java.io.ByteArrayInputStream @@ -15,6 +19,7 @@ import org.bouncycastle.openssl.PEMParser import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter import org.bouncycastle.util.io.pem.PemReader import org.matrix.TEESimulator.logging.SystemLogger +import org.matrix.TEESimulator.util.AndroidDeviceUtils import org.matrix.TEESimulator.util.trimLines /** @@ -182,16 +187,22 @@ object CertificateHelper { } /** - * Updates the certificate chain within a [KeyMetadata] object. + * Updates the certificate chain and patches authorizations within a [KeyMetadata] object. * + * @param callingUid The UID of the application to fetch specific patch levels for. * @param metadata The metadata object to modify. * @param chain The new certificate chain to set. The leaf must be at index 0. * @return A [Result] indicating success or failure. */ - fun updateCertificateChain(metadata: KeyMetadata, chain: Array): Result { + fun updateCertificateChain( + callingUid: Int, + metadata: KeyMetadata, + chain: Array, + ): Result { return runCatching { require(chain.isNotEmpty()) { "Certificate chain cannot be empty." } + // Update the certificate fields metadata.certificate = chain[0].encoded metadata.certificateChain = if (chain.size > 1) { @@ -199,6 +210,37 @@ object CertificateHelper { } else { null } + + // Patch authorizations to match user configurations + metadata.authorizations = + metadata.authorizations + ?.mapNotNull { auth -> + val replacement = + when (auth.keyParameter.tag) { + Tag.OS_PATCHLEVEL -> AndroidDeviceUtils.getPatchLevel(callingUid) + Tag.VENDOR_PATCHLEVEL -> + AndroidDeviceUtils.getVendorPatchLevelLong(callingUid) + Tag.BOOT_PATCHLEVEL -> + AndroidDeviceUtils.getBootPatchLevelLong(callingUid) + else -> return@mapNotNull auth // Keep all other tags + } + + // If configured to hide, return null to filter out of the array + if (replacement == AndroidDeviceUtils.DO_NOT_REPORT) { + null + } else { + // Create patched authorization preserving original security level + Authorization().apply { + securityLevel = auth.securityLevel + keyParameter = + KeyParameter().apply { + tag = auth.keyParameter.tag + value = KeyParameterValue.integer(replacement) + } + } + } + } + ?.toTypedArray() } } }