fix(interception): absorb upstream correctness fixes and patch error reply format

Cherry-pick three upstream fixes: Parcel position reset in hasException()
so the method doesn't consume reply data (bab7093), list_past_alias
enumeration filter inversion (71f75de), and KeyMetadata alignment with
AOSP semantics, modificationTimeMs, Tag.ORIGIN, KeyDescriptor
normalization (4e3dcc5).

Additionally, createErrorReply() was missing the empty remote stack
trace header int between the exception message and error code, per
AOSP Status.cpp:196. Binder readers expecting the standard
EX_SERVICE_SPECIFIC wire format would misparse our error replies.
This commit is contained in:
Enginex0
2026-03-16 13:23:36 +01:00
parent 4b9c5fe1d0
commit 1475c0be02
3 changed files with 21 additions and 3 deletions
@@ -18,6 +18,7 @@ object InterceptorUtils {
val parcel = Parcel.obtain().apply { val parcel = Parcel.obtain().apply {
writeInt(EX_SERVICE_SPECIFIC) writeInt(EX_SERVICE_SPECIFIC)
writeString(null) writeString(null)
writeInt(0) // empty remote stack trace header (AOSP Status.cpp:196)
writeInt(errorCode) writeInt(errorCode)
} }
return BinderInterceptor.TransactionResult.OverrideReply(parcel) return BinderInterceptor.TransactionResult.OverrideReply(parcel)
@@ -119,6 +120,8 @@ object InterceptorUtils {
/** Checks if a reply parcel contains an exception without consuming it. */ /** Checks if a reply parcel contains an exception without consuming it. */
fun hasException(reply: Parcel): Boolean { fun hasException(reply: Parcel): Boolean {
return runCatching { reply.readException() }.exceptionOrNull() != null val exception = runCatching { reply.readException() }.exceptionOrNull()
if (exception != null) reply.setDataPosition(0)
return exception != null
} }
} }
@@ -129,7 +129,7 @@ object ListEntriesHandler {
startPastAlias: String?, startPastAlias: String?,
): List<KeyDescriptor> { ): List<KeyDescriptor> {
return KeyMintSecurityLevelInterceptor.generatedKeys.keys return KeyMintSecurityLevelInterceptor.generatedKeys.keys
.filter { it.uid == uid && (startPastAlias == null || it.alias < startPastAlias) } .filter { it.uid == uid && (startPastAlias == null || it.alias > startPastAlias) }
.map { keyId -> .map { keyId ->
KeyDescriptor().apply { KeyDescriptor().apply {
this.domain = Domain.APP this.domain = Domain.APP
@@ -3,6 +3,7 @@ package org.matrix.TEESimulator.interception.keystore.shim
import android.hardware.security.keymint.Algorithm import android.hardware.security.keymint.Algorithm
import android.hardware.security.keymint.KeyParameter import android.hardware.security.keymint.KeyParameter
import android.hardware.security.keymint.KeyParameterValue import android.hardware.security.keymint.KeyParameterValue
import android.hardware.security.keymint.KeyOrigin
import android.hardware.security.keymint.Tag import android.hardware.security.keymint.Tag
import android.os.IBinder import android.os.IBinder
import android.os.Parcel import android.os.Parcel
@@ -425,12 +426,20 @@ class KeyMintSecurityLevelInterceptor(
params: KeyMintAttestation, params: KeyMintAttestation,
descriptor: KeyDescriptor, descriptor: KeyDescriptor,
): KeyEntryResponse { ): KeyEntryResponse {
val normalizedKeyDescriptor =
KeyDescriptor().apply {
domain = Domain.KEY_ID
nspace = descriptor.nspace
alias = null
blob = null
}
val metadata = val metadata =
KeyMetadata().apply { KeyMetadata().apply {
keySecurityLevel = securityLevel keySecurityLevel = securityLevel
key = descriptor key = normalizedKeyDescriptor
CertificateHelper.updateCertificateChain(this, chain.toTypedArray()).getOrThrow() CertificateHelper.updateCertificateChain(this, chain.toTypedArray()).getOrThrow()
authorizations = params.toAuthorizations(securityLevel) authorizations = params.toAuthorizations(securityLevel)
modificationTimeMs = System.currentTimeMillis()
} }
return KeyEntryResponse().apply { return KeyEntryResponse().apply {
this.metadata = metadata this.metadata = metadata
@@ -665,6 +674,12 @@ private fun KeyMintAttestation.toAuthorizations(securityLevel: Int): Array<Autho
authList.add(createAuth(Tag.ALGORITHM, KeyParameterValue.algorithm(this.algorithm))) authList.add(createAuth(Tag.ALGORITHM, KeyParameterValue.algorithm(this.algorithm)))
authList.add(createAuth(Tag.KEY_SIZE, KeyParameterValue.integer(this.keySize))) authList.add(createAuth(Tag.KEY_SIZE, KeyParameterValue.integer(this.keySize)))
authList.add(createAuth(Tag.EC_CURVE, KeyParameterValue.ecCurve(this.ecCurve))) authList.add(createAuth(Tag.EC_CURVE, KeyParameterValue.ecCurve(this.ecCurve)))
authList.add(
createAuth(
Tag.ORIGIN,
KeyParameterValue.origin(this.origin ?: KeyOrigin.GENERATED),
)
)
authList.add(createAuth(Tag.NO_AUTH_REQUIRED, KeyParameterValue.boolValue(true))) authList.add(createAuth(Tag.NO_AUTH_REQUIRED, KeyParameterValue.boolValue(true)))
return authList.toTypedArray() return authList.toTypedArray()