feat(interception): add binder tx code filtering and keystore2 service compliance
Native binder_interceptor now accepts a filtered_codes vector per registration, skipping JNI round-trip for non-intercepted transaction codes. Keystore2Interceptor adds getNumberOfEntries software key counting, deleteKey KEY_ID domain resolution, patchAuthorizations for OS/VENDOR/BOOT patch levels, importedKeys tracking to prevent stale attest-key overrides, and nspace consistency fix in the attest-key override path. InterceptorUtils gains createServiceSpecificErrorReply for AIDL-compliant error serialization and patchAuthorizations for authorization array patching.
This commit is contained in:
@@ -235,19 +235,21 @@ class BinderInterceptor : public BBinder {
|
|||||||
struct RegistrationEntry {
|
struct RegistrationEntry {
|
||||||
wp<IBinder> target;
|
wp<IBinder> target;
|
||||||
sp<IBinder> callback_interface;
|
sp<IBinder> callback_interface;
|
||||||
|
std::vector<uint32_t> filtered_codes;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reader-Writer lock for the registry to allow concurrent reads (lookups)
|
|
||||||
mutable std::shared_mutex registry_mutex_;
|
mutable std::shared_mutex registry_mutex_;
|
||||||
std::map<wp<IBinder>, RegistrationEntry> registry_;
|
std::map<wp<IBinder>, RegistrationEntry> registry_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BinderInterceptor() = default;
|
BinderInterceptor() = default;
|
||||||
|
|
||||||
// Checks if a specific Binder instance is currently registered for interception
|
bool shouldIntercept(const wp<BBinder> &target, uint32_t code) const {
|
||||||
bool isBinderIntercepted(const wp<BBinder> &target) const {
|
|
||||||
std::shared_lock lock(registry_mutex_);
|
std::shared_lock lock(registry_mutex_);
|
||||||
return registry_.find(target) != registry_.end();
|
auto it = registry_.find(target);
|
||||||
|
if (it == registry_.end()) return false;
|
||||||
|
const auto &codes = it->second.filtered_codes;
|
||||||
|
return codes.empty() || std::find(codes.begin(), codes.end(), code) != codes.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main entry point for processing the "Man-in-the-Middle" logic
|
// Main entry point for processing the "Man-in-the-Middle" logic
|
||||||
@@ -393,7 +395,7 @@ void inspectAndRewriteTransaction(binder_transaction_data *txn_data) {
|
|||||||
// This is safe because we are holding a strong reference.
|
// This is safe because we are holding a strong reference.
|
||||||
wp<BBinder> wp_target = target_binder_ptr;
|
wp<BBinder> wp_target = target_binder_ptr;
|
||||||
|
|
||||||
if (g_interceptor_instance->isBinderIntercepted(wp_target)) {
|
if (g_interceptor_instance->shouldIntercept(wp_target, txn_data->code)) {
|
||||||
info.transaction_code = txn_data->code;
|
info.transaction_code = txn_data->code;
|
||||||
info.target_binder = wp_target; // Assign the valid weak pointer
|
info.target_binder = wp_target; // Assign the valid weak pointer
|
||||||
hijack = true;
|
hijack = true;
|
||||||
@@ -538,18 +540,29 @@ status_t BinderInterceptor::handleRegister(const Parcel &data) {
|
|||||||
if (data.readStrongBinder(&callback) != OK || !callback)
|
if (data.readStrongBinder(&callback) != OK || !callback)
|
||||||
return BAD_VALUE;
|
return BAD_VALUE;
|
||||||
|
|
||||||
// We can only intercept local Binders (BBinder), not remote proxies (BpBinder)
|
|
||||||
if (target->localBinder() == nullptr) {
|
if (target->localBinder() == nullptr) {
|
||||||
LOGE("Cannot intercept remote binder proxies.");
|
LOGE("Cannot intercept remote binder proxies.");
|
||||||
return BAD_TYPE;
|
return BAD_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> codes;
|
||||||
|
int32_t code_count = 0;
|
||||||
|
if (data.dataAvail() >= sizeof(int32_t) && data.readInt32(&code_count) == OK && code_count > 0) {
|
||||||
|
codes.reserve(code_count);
|
||||||
|
for (int32_t i = 0; i < code_count; i++) {
|
||||||
|
uint32_t c = 0;
|
||||||
|
if (data.readUint32(&c) == OK) codes.push_back(c);
|
||||||
|
}
|
||||||
|
LOGI("Interceptor registered for binder %p with %zu filtered codes", target.get(), codes.size());
|
||||||
|
} else {
|
||||||
|
LOGI("Interceptor registered for binder %p (all codes)", target.get());
|
||||||
|
}
|
||||||
|
|
||||||
wp<IBinder> weak_target = target;
|
wp<IBinder> weak_target = target;
|
||||||
|
|
||||||
std::unique_lock lock(registry_mutex_);
|
std::unique_lock lock(registry_mutex_);
|
||||||
registry_[weak_target] = {weak_target, callback};
|
registry_[weak_target] = {weak_target, callback, std::move(codes)};
|
||||||
|
|
||||||
LOGI("Interceptor registered for binder %p", target.get());
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -293,15 +293,21 @@ abstract class BinderInterceptor : Binder() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Uses the backdoor binder to register an interceptor for a specific target service. */
|
fun register(
|
||||||
fun register(backdoor: IBinder, target: IBinder, interceptor: BinderInterceptor) {
|
backdoor: IBinder,
|
||||||
|
target: IBinder,
|
||||||
|
interceptor: BinderInterceptor,
|
||||||
|
filteredCodes: IntArray = intArrayOf(),
|
||||||
|
) {
|
||||||
val data = Parcel.obtain()
|
val data = Parcel.obtain()
|
||||||
val reply = Parcel.obtain()
|
val reply = Parcel.obtain()
|
||||||
try {
|
try {
|
||||||
data.writeStrongBinder(target)
|
data.writeStrongBinder(target)
|
||||||
data.writeStrongBinder(interceptor)
|
data.writeStrongBinder(interceptor)
|
||||||
|
data.writeInt(filteredCodes.size)
|
||||||
|
for (code in filteredCodes) data.writeInt(code)
|
||||||
backdoor.transact(REGISTER_INTERCEPTOR_CODE, data, reply, 0)
|
backdoor.transact(REGISTER_INTERCEPTOR_CODE, data, reply, 0)
|
||||||
SystemLogger.info("Registered interceptor for target: $target")
|
SystemLogger.info("Registered interceptor for target: $target (${filteredCodes.size} filtered codes)")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
SystemLogger.error("Failed to register binder interceptor.", e)
|
SystemLogger.error("Failed to register binder interceptor.", e)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
+3
-2
@@ -68,11 +68,12 @@ abstract class AbstractKeystoreInterceptor : BinderInterceptor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Registers this interceptor with the native hook layer and sets up a death recipient. */
|
protected open val interceptedCodes: IntArray = intArrayOf()
|
||||||
|
|
||||||
private fun setupInterceptor(service: IBinder, backdoor: IBinder) {
|
private fun setupInterceptor(service: IBinder, backdoor: IBinder) {
|
||||||
keystoreService = service
|
keystoreService = service
|
||||||
SystemLogger.info("Registering interceptor for service: $serviceName")
|
SystemLogger.info("Registering interceptor for service: $serviceName")
|
||||||
register(backdoor, service, this)
|
register(backdoor, service, this, interceptedCodes)
|
||||||
service.linkToDeath(createDeathRecipient(), 0)
|
service.linkToDeath(createDeathRecipient(), 0)
|
||||||
onInterceptorReady(service, backdoor)
|
onInterceptorReady(service, backdoor)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
package org.matrix.TEESimulator.interception.keystore
|
package org.matrix.TEESimulator.interception.keystore
|
||||||
|
|
||||||
|
import android.hardware.security.keymint.KeyParameter
|
||||||
|
import android.hardware.security.keymint.KeyParameterValue
|
||||||
|
import android.hardware.security.keymint.Tag
|
||||||
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 android.security.keystore.KeystoreResponse
|
import android.security.keystore.KeystoreResponse
|
||||||
|
import android.system.keystore2.Authorization
|
||||||
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
|
||||||
|
import org.matrix.TEESimulator.util.AndroidDeviceUtils
|
||||||
|
|
||||||
data class KeyIdentifier(val uid: Int, val alias: String)
|
data class KeyIdentifier(val uid: Int, val alias: String)
|
||||||
|
|
||||||
@@ -124,4 +129,53 @@ object InterceptorUtils {
|
|||||||
if (exception != null) reply.setDataPosition(0)
|
if (exception != null) reply.setDataPosition(0)
|
||||||
return exception != null
|
return exception != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createServiceSpecificErrorReply(
|
||||||
|
errorCode: Int
|
||||||
|
): BinderInterceptor.TransactionResult.OverrideReply {
|
||||||
|
val parcel =
|
||||||
|
Parcel.obtain().apply {
|
||||||
|
writeException(android.os.ServiceSpecificException(errorCode))
|
||||||
|
}
|
||||||
|
return BinderInterceptor.TransactionResult.OverrideReply(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun patchAuthorizations(
|
||||||
|
authorizations: Array<Authorization>?,
|
||||||
|
callingUid: Int,
|
||||||
|
): Array<Authorization>? {
|
||||||
|
if (authorizations == null) return null
|
||||||
|
|
||||||
|
val osPatch = AndroidDeviceUtils.getPatchLevel(callingUid)
|
||||||
|
val vendorPatch = AndroidDeviceUtils.getVendorPatchLevelLong(callingUid)
|
||||||
|
val bootPatch = AndroidDeviceUtils.getBootPatchLevelLong(callingUid)
|
||||||
|
|
||||||
|
return authorizations
|
||||||
|
.map { auth ->
|
||||||
|
val replacement =
|
||||||
|
when (auth.keyParameter.tag) {
|
||||||
|
Tag.OS_PATCHLEVEL ->
|
||||||
|
if (osPatch != AndroidDeviceUtils.DO_NOT_REPORT) osPatch else null
|
||||||
|
Tag.VENDOR_PATCHLEVEL ->
|
||||||
|
if (vendorPatch != AndroidDeviceUtils.DO_NOT_REPORT) vendorPatch
|
||||||
|
else null
|
||||||
|
Tag.BOOT_PATCHLEVEL ->
|
||||||
|
if (bootPatch != AndroidDeviceUtils.DO_NOT_REPORT) bootPatch else null
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
if (replacement != null) {
|
||||||
|
Authorization().apply {
|
||||||
|
keyParameter =
|
||||||
|
KeyParameter().apply {
|
||||||
|
tag = auth.keyParameter.tag
|
||||||
|
value = KeyParameterValue.integer(replacement)
|
||||||
|
}
|
||||||
|
securityLevel = auth.securityLevel
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toTypedArray()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+118
-24
@@ -5,6 +5,7 @@ import android.hardware.security.keymint.SecurityLevel
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
|
import android.system.keystore2.Domain
|
||||||
import android.system.keystore2.IKeystoreService
|
import android.system.keystore2.IKeystoreService
|
||||||
import android.system.keystore2.KeyDescriptor
|
import android.system.keystore2.KeyDescriptor
|
||||||
import android.system.keystore2.KeyEntryResponse
|
import android.system.keystore2.KeyEntryResponse
|
||||||
@@ -45,6 +46,8 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
if (Build.VERSION.SDK_INT >= 34)
|
if (Build.VERSION.SDK_INT >= 34)
|
||||||
InterceptorUtils.getTransactCode(stubBinderClass, "listEntriesBatched")
|
InterceptorUtils.getTransactCode(stubBinderClass, "listEntriesBatched")
|
||||||
else null
|
else null
|
||||||
|
private val GET_NUMBER_OF_ENTRIES_TRANSACTION =
|
||||||
|
InterceptorUtils.getTransactCode(stubBinderClass, "getNumberOfEntries")
|
||||||
|
|
||||||
private val transactionNames: Map<Int, String> by lazy {
|
private val transactionNames: Map<Int, String> by lazy {
|
||||||
stubBinderClass.declaredFields
|
stubBinderClass.declaredFields
|
||||||
@@ -57,11 +60,24 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
|
|
||||||
private const val RESPONSE_KEY_NOT_FOUND = 7
|
private const val RESPONSE_KEY_NOT_FOUND = 7
|
||||||
private val deletedSoftwareKeys: MutableSet<KeyIdentifier> = ConcurrentHashMap.newKeySet()
|
private val deletedSoftwareKeys: MutableSet<KeyIdentifier> = ConcurrentHashMap.newKeySet()
|
||||||
|
private val userUpdatedKeys = ConcurrentHashMap.newKeySet<KeyIdentifier>()
|
||||||
|
|
||||||
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"
|
||||||
|
|
||||||
|
override val interceptedCodes: IntArray by lazy {
|
||||||
|
listOfNotNull(
|
||||||
|
GET_KEY_ENTRY_TRANSACTION,
|
||||||
|
DELETE_KEY_TRANSACTION,
|
||||||
|
UPDATE_SUBCOMPONENT_TRANSACTION,
|
||||||
|
LIST_ENTRIES_TRANSACTION,
|
||||||
|
LIST_ENTRIES_BATCHED_TRANSACTION,
|
||||||
|
GET_NUMBER_OF_ENTRIES_TRANSACTION,
|
||||||
|
)
|
||||||
|
.toIntArray()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called once the main service is hooked. It proceeds to find and hook the
|
* This method is called once the main service is hooked. It proceeds to find and hook the
|
||||||
* security level sub-services (e.g., TEE, StrongBox).
|
* security level sub-services (e.g., TEE, StrongBox).
|
||||||
@@ -78,7 +94,12 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
SystemLogger.info("Found TEE SecurityLevel. Registering interceptor...")
|
SystemLogger.info("Found TEE SecurityLevel. Registering interceptor...")
|
||||||
val interceptor =
|
val interceptor =
|
||||||
KeyMintSecurityLevelInterceptor(tee, SecurityLevel.TRUSTED_ENVIRONMENT)
|
KeyMintSecurityLevelInterceptor(tee, SecurityLevel.TRUSTED_ENVIRONMENT)
|
||||||
register(backdoor, tee.asBinder(), interceptor)
|
register(
|
||||||
|
backdoor,
|
||||||
|
tee.asBinder(),
|
||||||
|
interceptor,
|
||||||
|
KeyMintSecurityLevelInterceptor.INTERCEPTED_CODES,
|
||||||
|
)
|
||||||
interceptor.loadPersistedKeys()
|
interceptor.loadPersistedKeys()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +111,12 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
SystemLogger.info("Found StrongBox SecurityLevel. Registering interceptor...")
|
SystemLogger.info("Found StrongBox SecurityLevel. Registering interceptor...")
|
||||||
val interceptor =
|
val interceptor =
|
||||||
KeyMintSecurityLevelInterceptor(strongbox, SecurityLevel.STRONGBOX)
|
KeyMintSecurityLevelInterceptor(strongbox, SecurityLevel.STRONGBOX)
|
||||||
register(backdoor, strongbox.asBinder(), interceptor)
|
register(
|
||||||
|
backdoor,
|
||||||
|
strongbox.asBinder(),
|
||||||
|
interceptor,
|
||||||
|
KeyMintSecurityLevelInterceptor.INTERCEPTED_CODES,
|
||||||
|
)
|
||||||
interceptor.loadPersistedKeys()
|
interceptor.loadPersistedKeys()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +132,12 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
callingPid: Int,
|
callingPid: Int,
|
||||||
data: Parcel,
|
data: Parcel,
|
||||||
): TransactionResult {
|
): TransactionResult {
|
||||||
if (code == LIST_ENTRIES_TRANSACTION || code == LIST_ENTRIES_BATCHED_TRANSACTION) {
|
if (code == GET_NUMBER_OF_ENTRIES_TRANSACTION) {
|
||||||
|
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid, true)
|
||||||
|
return if (ConfigurationManager.shouldSkipUid(callingUid))
|
||||||
|
TransactionResult.ContinueAndSkipPost
|
||||||
|
else TransactionResult.Continue
|
||||||
|
} else if (code == LIST_ENTRIES_TRANSACTION || code == LIST_ENTRIES_BATCHED_TRANSACTION) {
|
||||||
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid, true)
|
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid, true)
|
||||||
|
|
||||||
val packages = ConfigurationManager.getPackagesForUid(callingUid).joinToString()
|
val packages = ConfigurationManager.getPackagesForUid(callingUid).joinToString()
|
||||||
@@ -149,29 +180,40 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
data.readTypedObject(KeyDescriptor.CREATOR)
|
data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
?: return TransactionResult.ContinueAndSkipPost
|
?: return TransactionResult.ContinueAndSkipPost
|
||||||
|
|
||||||
if (descriptor.alias != null) {
|
|
||||||
SystemLogger.info("Handling ${transactionNames[code]!!} ${descriptor.alias}")
|
|
||||||
} else {
|
|
||||||
SystemLogger.info(
|
|
||||||
"Skip ${transactionNames[code]!!} for key [alias, blob, domain, nspace]: [${descriptor.alias}, ${descriptor.blob}, ${descriptor.domain}, ${descriptor.nspace}]"
|
|
||||||
)
|
|
||||||
return TransactionResult.ContinueAndSkipPost
|
|
||||||
}
|
|
||||||
val keyId = KeyIdentifier(callingUid, descriptor.alias)
|
|
||||||
|
|
||||||
if (code == DELETE_KEY_TRANSACTION) {
|
if (code == DELETE_KEY_TRANSACTION) {
|
||||||
val wasSoftwareKey = KeyMintSecurityLevelInterceptor.getGeneratedKeyResponse(keyId) != null
|
val keyId =
|
||||||
KeyMintSecurityLevelInterceptor.cleanupKeyData(keyId)
|
if (descriptor.alias != null) {
|
||||||
if (wasSoftwareKey) {
|
KeyIdentifier(callingUid, descriptor.alias)
|
||||||
deletedSoftwareKeys.add(keyId)
|
} else if (descriptor.domain == Domain.KEY_ID) {
|
||||||
SystemLogger.info(
|
KeyMintSecurityLevelInterceptor.findGeneratedKeyByKeyId(
|
||||||
"[TX_ID: $txId] Deleted cached keypair ${descriptor.alias}, replying with empty response."
|
callingUid, descriptor.nspace
|
||||||
)
|
)?.let { info ->
|
||||||
return InterceptorUtils.createSuccessReply(writeResultCode = false)
|
KeyMintSecurityLevelInterceptor.generatedKeys.entries
|
||||||
|
.find { it.value.nspace == info.nspace && it.key.uid == callingUid }
|
||||||
|
?.key
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
|
||||||
|
if (keyId != null) {
|
||||||
|
val isSoftwareKey =
|
||||||
|
KeyMintSecurityLevelInterceptor.generatedKeys.containsKey(keyId)
|
||||||
|
KeyMintSecurityLevelInterceptor.cleanupKeyData(keyId)
|
||||||
|
if (isSoftwareKey) {
|
||||||
|
deletedSoftwareKeys.add(keyId)
|
||||||
|
SystemLogger.info(
|
||||||
|
"[TX_ID: $txId] Deleted cached keypair ${keyId.alias}, replying with empty response."
|
||||||
|
)
|
||||||
|
return InterceptorUtils.createSuccessReply(writeResultCode = false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return TransactionResult.ContinueAndSkipPost
|
return TransactionResult.ContinueAndSkipPost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (descriptor.alias == null) {
|
||||||
|
return TransactionResult.ContinueAndSkipPost
|
||||||
|
}
|
||||||
|
val keyId = KeyIdentifier(callingUid, descriptor.alias)
|
||||||
|
|
||||||
val response = KeyMintSecurityLevelInterceptor.getGeneratedKeyResponse(keyId)
|
val response = KeyMintSecurityLevelInterceptor.getGeneratedKeyResponse(keyId)
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
if (deletedSoftwareKeys.remove(keyId)) {
|
if (deletedSoftwareKeys.remove(keyId)) {
|
||||||
@@ -217,7 +259,26 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
if (target != keystoreService || reply == null || InterceptorUtils.hasException(reply))
|
if (target != keystoreService || reply == null || InterceptorUtils.hasException(reply))
|
||||||
return TransactionResult.SkipTransaction
|
return TransactionResult.SkipTransaction
|
||||||
|
|
||||||
if (code == LIST_ENTRIES_TRANSACTION || code == LIST_ENTRIES_BATCHED_TRANSACTION) {
|
if (code == GET_NUMBER_OF_ENTRIES_TRANSACTION) {
|
||||||
|
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
||||||
|
return runCatching {
|
||||||
|
val hardwareCount = reply.readInt()
|
||||||
|
val softwareCount =
|
||||||
|
KeyMintSecurityLevelInterceptor.generatedKeys.keys.count {
|
||||||
|
it.uid == callingUid
|
||||||
|
}
|
||||||
|
val totalCount = hardwareCount + softwareCount
|
||||||
|
val parcel = Parcel.obtain().apply {
|
||||||
|
writeNoException()
|
||||||
|
writeInt(totalCount)
|
||||||
|
}
|
||||||
|
TransactionResult.OverrideReply(parcel)
|
||||||
|
}
|
||||||
|
.getOrElse {
|
||||||
|
SystemLogger.error("[TX_ID: $txId] Failed to modify getNumberOfEntries.", it)
|
||||||
|
TransactionResult.SkipTransaction
|
||||||
|
}
|
||||||
|
} else if (code == LIST_ENTRIES_TRANSACTION || code == LIST_ENTRIES_BATCHED_TRANSACTION) {
|
||||||
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
||||||
|
|
||||||
return runCatching {
|
return runCatching {
|
||||||
@@ -252,6 +313,11 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
val response = reply.readTypedObject(KeyEntryResponse.CREATOR)!!
|
val response = reply.readTypedObject(KeyEntryResponse.CREATOR)!!
|
||||||
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
||||||
|
|
||||||
|
if (userUpdatedKeys.remove(keyId)) {
|
||||||
|
SystemLogger.debug("[TX_ID: $txId] Skipping cert patch for user-updated key $keyId.")
|
||||||
|
return TransactionResult.SkipTransaction
|
||||||
|
}
|
||||||
|
|
||||||
val authorizations = response.metadata.authorizations
|
val authorizations = response.metadata.authorizations
|
||||||
val parsedParameters =
|
val parsedParameters =
|
||||||
KeyMintAttestation(
|
KeyMintAttestation(
|
||||||
@@ -269,6 +335,11 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
return InterceptorUtils.createTypedObjectReply(response)
|
return InterceptorUtils.createTypedObjectReply(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (KeyMintSecurityLevelInterceptor.importedKeys.contains(keyId)) {
|
||||||
|
SystemLogger.debug("[TX_ID: $txId] Skipping attest-key override for imported key $keyId")
|
||||||
|
return TransactionResult.SkipTransaction
|
||||||
|
}
|
||||||
|
|
||||||
if (parsedParameters.isAttestKey()) {
|
if (parsedParameters.isAttestKey()) {
|
||||||
SystemLogger.warning(
|
SystemLogger.warning(
|
||||||
"[TX_ID: $txId] Found hardware attest key ${keyId.alias} in the reply."
|
"[TX_ID: $txId] Found hardware attest key ${keyId.alias} in the reply."
|
||||||
@@ -289,11 +360,13 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
|
|
||||||
keyDescriptor.nspace = SecureRandom().nextLong()
|
keyDescriptor.nspace = SecureRandom().nextLong()
|
||||||
|
response.metadata.key.nspace = keyDescriptor.nspace
|
||||||
KeyMintSecurityLevelInterceptor.generatedKeys[keyId] =
|
KeyMintSecurityLevelInterceptor.generatedKeys[keyId] =
|
||||||
KeyMintSecurityLevelInterceptor.GeneratedKeyInfo(
|
KeyMintSecurityLevelInterceptor.GeneratedKeyInfo(
|
||||||
keyData.first,
|
keyData.first,
|
||||||
keyDescriptor.nspace,
|
keyDescriptor.nspace,
|
||||||
response,
|
response,
|
||||||
|
parsedParameters,
|
||||||
)
|
)
|
||||||
KeyMintSecurityLevelInterceptor.attestationKeys.add(keyId)
|
KeyMintSecurityLevelInterceptor.attestationKeys.add(keyId)
|
||||||
|
|
||||||
@@ -342,6 +415,11 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
|
|
||||||
CertificateHelper.updateCertificateChain(response.metadata, finalChain)
|
CertificateHelper.updateCertificateChain(response.metadata, finalChain)
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
|
response.metadata.authorizations =
|
||||||
|
InterceptorUtils.patchAuthorizations(
|
||||||
|
response.metadata.authorizations,
|
||||||
|
callingUid,
|
||||||
|
)
|
||||||
|
|
||||||
return InterceptorUtils.createTypedObjectReply(response)
|
return InterceptorUtils.createTypedObjectReply(response)
|
||||||
}
|
}
|
||||||
@@ -359,9 +437,25 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
private fun handleUpdateSubcomponent(callingUid: Int, data: Parcel): TransactionResult {
|
private fun handleUpdateSubcomponent(callingUid: Int, data: Parcel): TransactionResult {
|
||||||
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
||||||
val descriptor = data.readTypedObject(KeyDescriptor.CREATOR)
|
val descriptor = data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
|
?: return TransactionResult.ContinueAndSkipPost
|
||||||
|
|
||||||
val generatedKeyInfo =
|
val generatedKeyInfo =
|
||||||
KeyMintSecurityLevelInterceptor.findGeneratedKeyByKeyId(callingUid, descriptor?.nspace)
|
when (descriptor.domain) {
|
||||||
?: return TransactionResult.ContinueAndSkipPost
|
Domain.KEY_ID ->
|
||||||
|
KeyMintSecurityLevelInterceptor.findGeneratedKeyByKeyId(
|
||||||
|
callingUid, descriptor.nspace
|
||||||
|
)
|
||||||
|
Domain.APP ->
|
||||||
|
descriptor.alias?.let {
|
||||||
|
KeyMintSecurityLevelInterceptor.generatedKeys[KeyIdentifier(callingUid, it)]
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generatedKeyInfo == null) {
|
||||||
|
descriptor.alias?.let { userUpdatedKeys.add(KeyIdentifier(callingUid, it)) }
|
||||||
|
return TransactionResult.ContinueAndSkipPost
|
||||||
|
}
|
||||||
|
|
||||||
SystemLogger.info("Updating sub-component with key[${generatedKeyInfo.nspace}]")
|
SystemLogger.info("Updating sub-component with key[${generatedKeyInfo.nspace}]")
|
||||||
val metadata = generatedKeyInfo.response.metadata
|
val metadata = generatedKeyInfo.response.metadata
|
||||||
|
|||||||
+17
@@ -431,6 +431,23 @@ private data class LegacyKeygenParameters(
|
|||||||
manufacturer = null,
|
manufacturer = null,
|
||||||
model = null,
|
model = null,
|
||||||
secondImei = null,
|
secondImei = null,
|
||||||
|
activeDateTime = null,
|
||||||
|
originationExpireDateTime = null,
|
||||||
|
usageExpireDateTime = null,
|
||||||
|
usageCountLimit = null,
|
||||||
|
callerNonce = null,
|
||||||
|
unlockedDeviceRequired = null,
|
||||||
|
includeUniqueId = null,
|
||||||
|
rollbackResistance = null,
|
||||||
|
earlyBootOnly = null,
|
||||||
|
allowWhileOnBody = null,
|
||||||
|
trustedUserPresenceRequired = null,
|
||||||
|
trustedConfirmationRequired = null,
|
||||||
|
noAuthRequired = null,
|
||||||
|
maxUsesPerBoot = null,
|
||||||
|
maxBootLevel = null,
|
||||||
|
minMacLength = null,
|
||||||
|
rsaOaepMgfDigest = emptyList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
@@ -44,6 +44,8 @@ class OperationInterceptor(
|
|||||||
private val ABORT_TRANSACTION =
|
private val ABORT_TRANSACTION =
|
||||||
InterceptorUtils.getTransactCode(IKeystoreOperation.Stub::class.java, "abort")
|
InterceptorUtils.getTransactCode(IKeystoreOperation.Stub::class.java, "abort")
|
||||||
|
|
||||||
|
val INTERCEPTED_CODES = intArrayOf(FINISH_TRANSACTION, ABORT_TRANSACTION)
|
||||||
|
|
||||||
private val transactionNames: Map<Int, String> by lazy {
|
private val transactionNames: Map<Int, String> by lazy {
|
||||||
IKeystoreOperation.Stub::class
|
IKeystoreOperation.Stub::class
|
||||||
.java
|
.java
|
||||||
|
|||||||
Reference in New Issue
Block a user