feat(keystore): vendor-gate non-AEAD updateAad
Mirror Duck Detector's OperationErrorPathProbe: real Samsung and Xiaomi-MTK TEEs return success for updateAad on a non-AEAD operation, while other vendors reject it with INVALID_TAG. The shim reads the same Build identity the probe reads and answers accordingly, in both the CryptoPrimitive default (sign/verify) and CipherPrimitive paths. Forward hardening for the #28 detector: the prior unconditional ServiceSpecificException throw already passes the probe on every vendor, so this guards against stricter future probes rather than fixing a current failure.
This commit is contained in:
+38
-1
@@ -8,7 +8,9 @@ import android.hardware.security.keymint.KeyParameterValue
|
|||||||
import android.hardware.security.keymint.KeyPurpose
|
import android.hardware.security.keymint.KeyPurpose
|
||||||
import android.hardware.security.keymint.PaddingMode
|
import android.hardware.security.keymint.PaddingMode
|
||||||
import android.hardware.security.keymint.Tag
|
import android.hardware.security.keymint.Tag
|
||||||
|
import android.os.Build
|
||||||
import android.os.ServiceSpecificException
|
import android.os.ServiceSpecificException
|
||||||
|
import android.os.SystemProperties
|
||||||
import android.system.keystore2.IKeystoreOperation
|
import android.system.keystore2.IKeystoreOperation
|
||||||
import android.system.keystore2.KeyParameters
|
import android.system.keystore2.KeyParameters
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
@@ -20,10 +22,40 @@ import org.matrix.TEESimulator.attestation.KeyMintAttestation
|
|||||||
import org.matrix.TEESimulator.logging.KeyMintParameterLogger
|
import org.matrix.TEESimulator.logging.KeyMintParameterLogger
|
||||||
import org.matrix.TEESimulator.logging.SystemLogger
|
import org.matrix.TEESimulator.logging.SystemLogger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mirrors the per-vendor TEE quirk that Duck Detector's OperationErrorPathProbe checks: real
|
||||||
|
* Samsung and Xiaomi-MTK TrustZone return success for updateAad on a non-AEAD operation, while
|
||||||
|
* every other vendor rejects it with a service-specific INVALID_TAG. The module reads the same
|
||||||
|
* device-identity fields the probe reads, so a forged software operation answers exactly as that
|
||||||
|
* vendor's real TEE would.
|
||||||
|
*/
|
||||||
|
private object VendorQuirks {
|
||||||
|
private val UPDATE_AAD_ALLOWS_SUCCESS = setOf("samsung")
|
||||||
|
private val XIAOMI_BRANDS = setOf("xiaomi", "redmi", "poco")
|
||||||
|
|
||||||
|
fun nonAeadUpdateAadSucceeds(): Boolean {
|
||||||
|
val manufacturer = Build.MANUFACTURER.lowercase()
|
||||||
|
val brand = Build.BRAND.lowercase()
|
||||||
|
if (manufacturer in UPDATE_AAD_ALLOWS_SUCCESS || brand in UPDATE_AAD_ALLOWS_SUCCESS) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (manufacturer != "xiaomi" && brand !in XIAOMI_BRANDS) return false
|
||||||
|
return isMediaTek()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isMediaTek(): Boolean {
|
||||||
|
val roHardware = SystemProperties.get("ro.hardware", "")
|
||||||
|
return roHardware.startsWith("mt") || Build.HARDWARE.startsWith("mt", ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed interface CryptoPrimitive {
|
private sealed interface CryptoPrimitive {
|
||||||
fun updateAad(aadInput: ByteArray?) {
|
fun updateAad(aadInput: ByteArray?) {
|
||||||
|
// Real Samsung / Xiaomi-MTK TEEs accept updateAad on non-AEAD ops; others reject it.
|
||||||
|
if (!VendorQuirks.nonAeadUpdateAadSucceeds()) {
|
||||||
throw ServiceSpecificException(KeystoreErrorCodes.invalidTag)
|
throw ServiceSpecificException(KeystoreErrorCodes.invalidTag)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun update(data: ByteArray?): ByteArray?
|
fun update(data: ByteArray?): ByteArray?
|
||||||
|
|
||||||
@@ -158,7 +190,12 @@ private class CipherPrimitive(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAad(aadInput: ByteArray?) {
|
override fun updateAad(aadInput: ByteArray?) {
|
||||||
if (!isAead) throw ServiceSpecificException(KeystoreErrorCodes.invalidTag)
|
if (!isAead) {
|
||||||
|
if (!VendorQuirks.nonAeadUpdateAadSucceeds()) {
|
||||||
|
throw ServiceSpecificException(KeystoreErrorCodes.invalidTag)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
if (aadInput != null) cipher.updateAAD(aadInput)
|
if (aadInput != null) cipher.updateAAD(aadInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user