Improve logging to understand detection methods (#14)
Via extensive and detailed logging, we can inspect various detection techniques of target packages.
This commit is contained in:
@@ -3,9 +3,6 @@ package org.matrix.TEESimulator.interception.keystore
|
|||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.security.KeyStore
|
import android.security.KeyStore
|
||||||
import java.security.MessageDigest
|
|
||||||
import java.security.cert.Certificate
|
|
||||||
import java.util.Base64
|
|
||||||
import org.matrix.TEESimulator.interception.core.BinderInterceptor
|
import org.matrix.TEESimulator.interception.core.BinderInterceptor
|
||||||
import org.matrix.TEESimulator.logging.SystemLogger
|
import org.matrix.TEESimulator.logging.SystemLogger
|
||||||
|
|
||||||
@@ -81,20 +78,4 @@ object InterceptorUtils {
|
|||||||
fun hasException(reply: Parcel): Boolean {
|
fun hasException(reply: Parcel): Boolean {
|
||||||
return runCatching { reply.readException() }.exceptionOrNull() != null
|
return runCatching { reply.readException() }.exceptionOrNull() != null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a URL-safe SHA-256 fingerprint of a certificate's public key. Used to uniquely
|
|
||||||
* identify keys imported by the user.
|
|
||||||
*/
|
|
||||||
fun getPublicKeyFingerprint(chain: Array<Certificate>?): String? {
|
|
||||||
if (chain.isNullOrEmpty()) return null
|
|
||||||
return try {
|
|
||||||
val publicKeyBytes = chain[0].publicKey.encoded
|
|
||||||
val hashBytes = MessageDigest.getInstance("SHA-256").digest(publicKeyBytes)
|
|
||||||
Base64.getUrlEncoder().withoutPadding().encodeToString(hashBytes)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
SystemLogger.error("Failed to create public key fingerprint.", e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-7
@@ -30,6 +30,17 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
private val DELETE_KEY_TRANSACTION =
|
private val DELETE_KEY_TRANSACTION =
|
||||||
InterceptorUtils.getTransactCode(IKeystoreService.Stub::class.java, "deleteKey")
|
InterceptorUtils.getTransactCode(IKeystoreService.Stub::class.java, "deleteKey")
|
||||||
|
|
||||||
|
private val transactionNames: Map<Int, String> by lazy {
|
||||||
|
IKeystoreService.Stub::class
|
||||||
|
.java
|
||||||
|
.declaredFields
|
||||||
|
.filter {
|
||||||
|
it.isAccessible = true
|
||||||
|
it.type == Int::class.java && it.name.startsWith("TRANSACTION_")
|
||||||
|
}
|
||||||
|
.associate { field -> (field.get(null) as Int) to field.name.split("_")[1] }
|
||||||
|
}
|
||||||
|
|
||||||
override val serviceName = "android.system.keystore2.IKeystoreService/default"
|
override val serviceName = "android.system.keystore2.IKeystoreService/default"
|
||||||
override val processName = "keystore2"
|
override val processName = "keystore2"
|
||||||
override val injectionCommand = "exec ./inject `pidof keystore2` libTEESimulator.so entry"
|
override val injectionCommand = "exec ./inject `pidof keystore2` libTEESimulator.so entry"
|
||||||
@@ -76,20 +87,32 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
callingPid: Int,
|
callingPid: Int,
|
||||||
data: Parcel,
|
data: Parcel,
|
||||||
): TransactionResult {
|
): TransactionResult {
|
||||||
if (code == GET_KEY_ENTRY_TRANSACTION) {
|
if (code == GET_KEY_ENTRY_TRANSACTION || code == DELETE_KEY_TRANSACTION) {
|
||||||
logTransaction(txId, "getKeyEntry", callingUid, callingPid)
|
|
||||||
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
||||||
val descriptor =
|
val descriptor =
|
||||||
data.readTypedObject(KeyDescriptor.CREATOR)
|
data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
?: return TransactionResult.SkipTransaction
|
?: return TransactionResult.SkipTransaction
|
||||||
|
logTransaction(
|
||||||
|
txId,
|
||||||
|
"${transactionNames[code]} (alias=${descriptor.alias})",
|
||||||
|
callingUid,
|
||||||
|
callingPid,
|
||||||
|
)
|
||||||
val keyId = KeyIdentifier(callingUid, descriptor.alias)
|
val keyId = KeyIdentifier(callingUid, descriptor.alias)
|
||||||
SystemLogger.debug("Checking $keyId")
|
|
||||||
|
|
||||||
if (ConfigurationManager.shouldGenerate(callingUid)) {
|
if (ConfigurationManager.shouldGenerate(callingUid)) {
|
||||||
// TODO: Redesign the interaction with KeyMintSecurityLevelInterceptor
|
// TODO: Redesign the interaction with KeyMintSecurityLevelInterceptor
|
||||||
} else if (ConfigurationManager.shouldPatch(callingUid)) {
|
} else if (ConfigurationManager.shouldPatch(callingUid)) {
|
||||||
return TransactionResult.Continue
|
return TransactionResult.Continue
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
logTransaction(
|
||||||
|
txId,
|
||||||
|
transactionNames[code] ?: "unknown code=$code",
|
||||||
|
callingUid,
|
||||||
|
callingPid,
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let most calls go through to the real service.
|
// Let most calls go through to the real service.
|
||||||
@@ -111,7 +134,16 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
return TransactionResult.SkipTransaction
|
return TransactionResult.SkipTransaction
|
||||||
|
|
||||||
if (code == GET_KEY_ENTRY_TRANSACTION) {
|
if (code == GET_KEY_ENTRY_TRANSACTION) {
|
||||||
logTransaction(txId, "post-getKeyEntry", callingUid, callingPid)
|
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
||||||
|
val keyDescriptor =
|
||||||
|
data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
|
?: return TransactionResult.SkipTransaction
|
||||||
|
logTransaction(
|
||||||
|
txId,
|
||||||
|
"post-getKeyEntry (alias=${keyDescriptor.alias})",
|
||||||
|
callingUid,
|
||||||
|
callingPid,
|
||||||
|
)
|
||||||
if (!ConfigurationManager.shouldPatch(callingUid))
|
if (!ConfigurationManager.shouldPatch(callingUid))
|
||||||
return TransactionResult.SkipTransaction
|
return TransactionResult.SkipTransaction
|
||||||
|
|
||||||
@@ -144,9 +176,6 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
val newChain = AttestationPatcher.patchCertificateChain(originalChain, callingUid)
|
val newChain = AttestationPatcher.patchCertificateChain(originalChain, callingUid)
|
||||||
CertificateHelper.updateCertificateChain(response.metadata, newChain).getOrThrow()
|
CertificateHelper.updateCertificateChain(response.metadata, newChain).getOrThrow()
|
||||||
|
|
||||||
SystemLogger.info(
|
|
||||||
"[TX_ID: $txId] Successfully patched certificate chain for alias."
|
|
||||||
)
|
|
||||||
InterceptorUtils.createTypedObjectReply(response)
|
InterceptorUtils.createTypedObjectReply(response)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
SystemLogger.error("[TX_ID: $txId] Failed to patch certificate chain.", e)
|
SystemLogger.error("[TX_ID: $txId] Failed to patch certificate chain.", e)
|
||||||
|
|||||||
+22
@@ -44,6 +44,14 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
logTransaction(txId, "generateKey", callingUid, callingPid)
|
logTransaction(txId, "generateKey", callingUid, callingPid)
|
||||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||||
return handleGenerateKey(callingUid, data)
|
return handleGenerateKey(callingUid, data)
|
||||||
|
} else {
|
||||||
|
logTransaction(
|
||||||
|
txId,
|
||||||
|
transactionNames[code] ?: "unknown code=$code",
|
||||||
|
callingUid,
|
||||||
|
callingPid,
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return TransactionResult.ContinueAndSkipPost
|
return TransactionResult.ContinueAndSkipPost
|
||||||
}
|
}
|
||||||
@@ -56,6 +64,9 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
return runCatching {
|
return runCatching {
|
||||||
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
||||||
val attestationKey = data.readTypedObject(KeyDescriptor.CREATOR)
|
val attestationKey = data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
|
SystemLogger.debug(
|
||||||
|
"[key, attestationKey]: ${keyDescriptor.alias}, ${attestationKey?.alias}"
|
||||||
|
)
|
||||||
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
||||||
val parsedParams = KeyMintAttestation(params)
|
val parsedParams = KeyMintAttestation(params)
|
||||||
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
||||||
@@ -136,6 +147,17 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
private val IMPORT_KEY_TRANSACTION =
|
private val IMPORT_KEY_TRANSACTION =
|
||||||
InterceptorUtils.getTransactCode(IKeystoreSecurityLevel.Stub::class.java, "importKey")
|
InterceptorUtils.getTransactCode(IKeystoreSecurityLevel.Stub::class.java, "importKey")
|
||||||
|
|
||||||
|
private val transactionNames: Map<Int, String> by lazy {
|
||||||
|
IKeystoreSecurityLevel.Stub::class
|
||||||
|
.java
|
||||||
|
.declaredFields
|
||||||
|
.filter {
|
||||||
|
it.isAccessible = true
|
||||||
|
it.type == Int::class.java && it.name.startsWith("TRANSACTION_")
|
||||||
|
}
|
||||||
|
.associate { field -> (field.get(null) as Int) to field.name.split("_")[1] }
|
||||||
|
}
|
||||||
|
|
||||||
// Stores keys generated entirely in software.
|
// Stores keys generated entirely in software.
|
||||||
val generatedKeys = ConcurrentHashMap<KeyIdentifier, GeneratedKeyInfo>()
|
val generatedKeys = ConcurrentHashMap<KeyIdentifier, GeneratedKeyInfo>()
|
||||||
// A set to quickly identify keys that were generated for attestation purposes.
|
// A set to quickly identify keys that were generated for attestation purposes.
|
||||||
|
|||||||
@@ -78,7 +78,9 @@ object KeyMintParameterLogger {
|
|||||||
Tag.CERTIFICATE_SERIAL -> BigInteger(value.blob).toString()
|
Tag.CERTIFICATE_SERIAL -> BigInteger(value.blob).toString()
|
||||||
Tag.ACTIVE_DATETIME,
|
Tag.ACTIVE_DATETIME,
|
||||||
Tag.CERTIFICATE_NOT_AFTER,
|
Tag.CERTIFICATE_NOT_AFTER,
|
||||||
Tag.CERTIFICATE_NOT_BEFORE -> Date(value.dateTime).toString()
|
Tag.CERTIFICATE_NOT_BEFORE,
|
||||||
|
Tag.ORIGINATION_EXPIRE_DATETIME,
|
||||||
|
Tag.USAGE_EXPIRE_DATETIME -> Date(value.dateTime).toString()
|
||||||
Tag.CERTIFICATE_SUBJECT -> X500Name(X500Principal(value.blob).name).toString()
|
Tag.CERTIFICATE_SUBJECT -> X500Name(X500Principal(value.blob).name).toString()
|
||||||
Tag.RSA_PUBLIC_EXPONENT -> value.longInteger.toString()
|
Tag.RSA_PUBLIC_EXPONENT -> value.longInteger.toString()
|
||||||
Tag.NO_AUTH_REQUIRED -> "true"
|
Tag.NO_AUTH_REQUIRED -> "true"
|
||||||
|
|||||||
Reference in New Issue
Block a user