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() } } }