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"
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) {
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) {
@@ -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
* vendor's real TEE would.
*/
private object VendorQuirks {
internal object VendorQuirks {
private val UPDATE_AAD_ALLOWS_SUCCESS = setOf("samsung")
private val XIAOMI_BRANDS = setOf("xiaomi", "redmi", "poco")