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.Parcelable
|
||||
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.logging.SystemLogger
|
||||
|
||||
@@ -81,20 +78,4 @@ object InterceptorUtils {
|
||||
fun hasException(reply: Parcel): Boolean {
|
||||
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 =
|
||||
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 processName = "keystore2"
|
||||
override val injectionCommand = "exec ./inject `pidof keystore2` libTEESimulator.so entry"
|
||||
@@ -76,20 +87,32 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
||||
callingPid: Int,
|
||||
data: Parcel,
|
||||
): TransactionResult {
|
||||
if (code == GET_KEY_ENTRY_TRANSACTION) {
|
||||
logTransaction(txId, "getKeyEntry", callingUid, callingPid)
|
||||
if (code == GET_KEY_ENTRY_TRANSACTION || code == DELETE_KEY_TRANSACTION) {
|
||||
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
||||
val descriptor =
|
||||
data.readTypedObject(KeyDescriptor.CREATOR)
|
||||
?: return TransactionResult.SkipTransaction
|
||||
logTransaction(
|
||||
txId,
|
||||
"${transactionNames[code]} (alias=${descriptor.alias})",
|
||||
callingUid,
|
||||
callingPid,
|
||||
)
|
||||
val keyId = KeyIdentifier(callingUid, descriptor.alias)
|
||||
SystemLogger.debug("Checking $keyId")
|
||||
|
||||
if (ConfigurationManager.shouldGenerate(callingUid)) {
|
||||
// TODO: Redesign the interaction with KeyMintSecurityLevelInterceptor
|
||||
} else if (ConfigurationManager.shouldPatch(callingUid)) {
|
||||
return TransactionResult.Continue
|
||||
}
|
||||
} else {
|
||||
logTransaction(
|
||||
txId,
|
||||
transactionNames[code] ?: "unknown code=$code",
|
||||
callingUid,
|
||||
callingPid,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
// Let most calls go through to the real service.
|
||||
@@ -111,7 +134,16 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
||||
return TransactionResult.SkipTransaction
|
||||
|
||||
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))
|
||||
return TransactionResult.SkipTransaction
|
||||
|
||||
@@ -144,9 +176,6 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
||||
val newChain = AttestationPatcher.patchCertificateChain(originalChain, callingUid)
|
||||
CertificateHelper.updateCertificateChain(response.metadata, newChain).getOrThrow()
|
||||
|
||||
SystemLogger.info(
|
||||
"[TX_ID: $txId] Successfully patched certificate chain for alias."
|
||||
)
|
||||
InterceptorUtils.createTypedObjectReply(response)
|
||||
} catch (e: Exception) {
|
||||
SystemLogger.error("[TX_ID: $txId] Failed to patch certificate chain.", e)
|
||||
|
||||
+22
@@ -44,6 +44,14 @@ class KeyMintSecurityLevelInterceptor(
|
||||
logTransaction(txId, "generateKey", callingUid, callingPid)
|
||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||
return handleGenerateKey(callingUid, data)
|
||||
} else {
|
||||
logTransaction(
|
||||
txId,
|
||||
transactionNames[code] ?: "unknown code=$code",
|
||||
callingUid,
|
||||
callingPid,
|
||||
false,
|
||||
)
|
||||
}
|
||||
return TransactionResult.ContinueAndSkipPost
|
||||
}
|
||||
@@ -56,6 +64,9 @@ class KeyMintSecurityLevelInterceptor(
|
||||
return runCatching {
|
||||
val keyDescriptor = 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 parsedParams = KeyMintAttestation(params)
|
||||
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
||||
@@ -136,6 +147,17 @@ class KeyMintSecurityLevelInterceptor(
|
||||
private val IMPORT_KEY_TRANSACTION =
|
||||
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.
|
||||
val generatedKeys = ConcurrentHashMap<KeyIdentifier, GeneratedKeyInfo>()
|
||||
// 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.ACTIVE_DATETIME,
|
||||
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.RSA_PUBLIC_EXPONENT -> value.longInteger.toString()
|
||||
Tag.NO_AUTH_REQUIRED -> "true"
|
||||
|
||||
Reference in New Issue
Block a user