feat(intercept): inject SSE on non-AEAD updateAad

Real MediaTek mt6768 KeyMint silently returns OK on non-AEAD
updateAad, contradicting AOSP's mandate at
system/keymint/ta/src/operation.rs:430-446 to throw InvalidTag
when aad_allowed is false. Duck-detector flags this divergence
as "updateAad mismatch" in OperationErrorPathProbe.

Wire UPDATE_AAD into OperationInterceptor and inject
ServiceSpecificException(INVALID_TAG) when create params
indicate non-AEAD. AEAD (BlockMode.GCM) passes through to real
KeyMint untouched so AES-GCM round-trips remain valid.
This commit is contained in:
Enginex0
2026-05-19 13:05:36 +01:00
parent a7e7e454e7
commit 59836e143c
2 changed files with 9 additions and 2 deletions
@@ -194,7 +194,8 @@ class KeyMintSecurityLevelInterceptor(
SystemLogger.info("Found new IKeystoreOperation. Registering interceptor...") SystemLogger.info("Found new IKeystoreOperation. Registering interceptor...")
val backdoor = getBackdoor(target) val backdoor = getBackdoor(target)
if (backdoor != null) { if (backdoor != null) {
val interceptor = OperationInterceptor(operation, backdoor) val isAead = parsedParams.blockMode.firstOrNull() == BlockMode.GCM
val interceptor = OperationInterceptor(operation, backdoor, isAead)
register(backdoor, operationBinder, interceptor, OperationInterceptor.INTERCEPTED_CODES) register(backdoor, operationBinder, interceptor, OperationInterceptor.INTERCEPTED_CODES)
interceptedOperations[operationBinder] = interceptor interceptedOperations[operationBinder] = interceptor
} else { } else {
@@ -13,6 +13,7 @@ import org.matrix.TEESimulator.interception.keystore.InterceptorUtils
class OperationInterceptor( class OperationInterceptor(
private val original: IKeystoreOperation, private val original: IKeystoreOperation,
private val backdoor: IBinder, private val backdoor: IBinder,
private val isAead: Boolean,
) : BinderInterceptor() { ) : BinderInterceptor() {
override fun onPreTransact( override fun onPreTransact(
@@ -27,6 +28,10 @@ class OperationInterceptor(
val methodName = transactionNames[code] ?: "unknown code=$code" val methodName = transactionNames[code] ?: "unknown code=$code"
logTransaction(txId, methodName, callingUid, callingPid, true) logTransaction(txId, methodName, callingUid, callingPid, true)
if (code == UPDATE_AAD_TRANSACTION && !isAead) {
return InterceptorUtils.createServiceSpecificErrorReply(KeystoreErrorCodes.invalidTag)
}
if (code == FINISH_TRANSACTION || code == ABORT_TRANSACTION) { if (code == FINISH_TRANSACTION || code == ABORT_TRANSACTION) {
KeyMintSecurityLevelInterceptor.removeOperationInterceptor(target, backdoor) KeyMintSecurityLevelInterceptor.removeOperationInterceptor(target, backdoor)
} }
@@ -44,7 +49,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) val INTERCEPTED_CODES =
intArrayOf(UPDATE_AAD_TRANSACTION, 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