fix(keystore): vendor-gate real-op updateAad

OperationInterceptor rejected non-AEAD updateAad with INVALID_TAG
unconditionally, while SoftwareOperation's VendorQuirks gate returns
success on Samsung and Xiaomi-MTK. On those devices the real-key and
forged-key paths disagreed, and the genuine TEE accepts the call, so
the inconsistency fingerprinted the injection layer through Duck
Detector's operation error-path probe.

Apply the same gate to the real-op path: a void success reply where
nonAeadUpdateAadSucceeds(), else the INVALID_TAG reply. Promote
VendorQuirks to internal so both paths share one decision.

Refs #36
This commit is contained in:
Enginex0
2026-06-25 00:58:00 +01:00
parent 9193b79a6a
commit e2dc7aa210
2 changed files with 9 additions and 2 deletions
@@ -28,8 +28,15 @@ 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)
// Mirror SoftwareOperation's vendor gate: a real-key op must answer non-AEAD updateAad
// exactly as the forged-key path does. Samsung and Xiaomi-MTK TEEs accept it; rejecting
// here while the forged path accepts diverges the two and fingerprints the injection.
if (code == UPDATE_AAD_TRANSACTION && !isAead) { if (code == UPDATE_AAD_TRANSACTION && !isAead) {
return InterceptorUtils.createServiceSpecificErrorReply(KeystoreErrorCodes.invalidTag) return if (VendorQuirks.nonAeadUpdateAadSucceeds()) {
InterceptorUtils.createSuccessReply(writeResultCode = false)
} else {
InterceptorUtils.createServiceSpecificErrorReply(KeystoreErrorCodes.invalidTag)
}
} }
if (code == FINISH_TRANSACTION || code == ABORT_TRANSACTION) { if (code == FINISH_TRANSACTION || code == ABORT_TRANSACTION) {
@@ -29,7 +29,7 @@ import org.matrix.TEESimulator.logging.SystemLogger
* device-identity fields the probe reads, so a forged software operation answers exactly as that * device-identity fields the probe reads, so a forged software operation answers exactly as that
* vendor's real TEE would. * vendor's real TEE would.
*/ */
private object VendorQuirks { internal object VendorQuirks {
private val UPDATE_AAD_ALLOWS_SUCCESS = setOf("samsung") private val UPDATE_AAD_ALLOWS_SUCCESS = setOf("samsung")
private val XIAOMI_BRANDS = setOf("xiaomi", "redmi", "poco") private val XIAOMI_BRANDS = setOf("xiaomi", "redmi", "poco")