feat(spoof): PatchLevelManager with PIF resolution
PatchLevelManager resolves the active security patch from PlayIntegrityFix via the same six-path override chain as Tricky-Addon's get_extra.sh (pif.json/pif.prop/custom.pif.*, later entries override earlier ones). Falls back to live ro.build.version.security_patch when no PIF source is present. updateTo() validates YYYY-MM-DD format, rejects dates below 2020-01-01 or more than one year older than today, then atomically stages security_patch.txt with explicit system/boot/vendor dates and resetprops ro.build.version.security_patch plus ro.vendor.build.security_patch. Cert tags 706/718/719 then encode consistent dates via AndroidDeviceUtils.parsePatchLevelValue (YYYYMM for OS, YYYYMMDD for VENDOR/BOOT per AOSP Tag.aidl). Wired from App.main after BootStateManager.apply().
This commit is contained in:
@@ -10,6 +10,7 @@ import java.security.Security
|
|||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||||
import org.matrix.TEESimulator.config.BootStateManager
|
import org.matrix.TEESimulator.config.BootStateManager
|
||||||
import org.matrix.TEESimulator.config.ConfigurationManager
|
import org.matrix.TEESimulator.config.ConfigurationManager
|
||||||
|
import org.matrix.TEESimulator.config.PatchLevelManager
|
||||||
import org.matrix.TEESimulator.interception.keystore.AbstractKeystoreInterceptor
|
import org.matrix.TEESimulator.interception.keystore.AbstractKeystoreInterceptor
|
||||||
import org.matrix.TEESimulator.interception.keystore.Keystore2Interceptor
|
import org.matrix.TEESimulator.interception.keystore.Keystore2Interceptor
|
||||||
import org.matrix.TEESimulator.interception.keystore.KeystoreInterceptor
|
import org.matrix.TEESimulator.interception.keystore.KeystoreInterceptor
|
||||||
@@ -46,6 +47,7 @@ object App {
|
|||||||
// Load the package configuration.
|
// Load the package configuration.
|
||||||
ConfigurationManager.initialize()
|
ConfigurationManager.initialize()
|
||||||
BootStateManager.apply()
|
BootStateManager.apply()
|
||||||
|
PatchLevelManager.initialize()
|
||||||
// Set up the device's boot key and hash, which are crucial for attestation.
|
// Set up the device's boot key and hash, which are crucial for attestation.
|
||||||
AndroidDeviceUtils.setupBootKeyAndHash()
|
AndroidDeviceUtils.setupBootKeyAndHash()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package org.matrix.TEESimulator.config
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.SystemProperties
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.StandardCopyOption
|
||||||
|
import java.time.LocalDate
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.matrix.TEESimulator.logging.SystemLogger
|
||||||
|
import org.matrix.TEESimulator.util.AndroidDeviceUtils
|
||||||
|
|
||||||
|
object PatchLevelManager {
|
||||||
|
private const val PATCH_FILE = "/data/adb/tricky_store/security_patch.txt"
|
||||||
|
private const val STAGING_FILE = "/data/adb/tricky_store/security_patch.txt.next"
|
||||||
|
private const val FLOOR_YYYYMMDD = 20200101
|
||||||
|
private const val MAX_PAST_OFFSET = 10000
|
||||||
|
|
||||||
|
private val DATE_PATTERN = Regex("^\\d{4}-\\d{2}-\\d{2}$")
|
||||||
|
private val PROP_PATTERN = Regex("^SECURITY_PATCH=(.+)$", RegexOption.MULTILINE)
|
||||||
|
|
||||||
|
private val PIF_SOURCES =
|
||||||
|
listOf(
|
||||||
|
"/data/adb/modules/playintegrityfix/pif.json",
|
||||||
|
"/data/adb/pif.json",
|
||||||
|
"/data/adb/modules/playintegrityfix/pif.prop",
|
||||||
|
"/data/adb/pif.prop",
|
||||||
|
"/data/adb/modules/playintegrityfix/custom.pif.json",
|
||||||
|
"/data/adb/modules/playintegrityfix/custom.pif.prop",
|
||||||
|
)
|
||||||
|
|
||||||
|
fun initialize() {
|
||||||
|
val date =
|
||||||
|
resolvePifPatch()
|
||||||
|
?: SystemProperties.get(
|
||||||
|
"ro.build.version.security_patch",
|
||||||
|
Build.VERSION.SECURITY_PATCH,
|
||||||
|
)
|
||||||
|
SystemLogger.info("PatchLevelManager: resolved patch date = $date")
|
||||||
|
updateTo(date)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateTo(date: String) {
|
||||||
|
if (!DATE_PATTERN.matches(date)) {
|
||||||
|
SystemLogger.warning("PatchLevelManager: invalid date format: $date")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val dateInt = date.replace("-", "").toInt()
|
||||||
|
if (dateInt < FLOOR_YYYYMMDD) {
|
||||||
|
SystemLogger.warning("PatchLevelManager: $date below floor $FLOOR_YYYYMMDD")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val today =
|
||||||
|
LocalDate.now().let { it.year * 10000 + it.monthValue * 100 + it.dayOfMonth }
|
||||||
|
if (today >= dateInt + MAX_PAST_OFFSET) {
|
||||||
|
SystemLogger.warning(
|
||||||
|
"PatchLevelManager: $date more than 1y older than today ($today)"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
atomicWrite(date)
|
||||||
|
AndroidDeviceUtils.setProperty("ro.build.version.security_patch", date)
|
||||||
|
AndroidDeviceUtils.setProperty("ro.vendor.build.security_patch", date)
|
||||||
|
SystemLogger.info("PatchLevelManager: applied patch date $date")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolvePifPatch(): String? {
|
||||||
|
val source = PIF_SOURCES.map(::File).lastOrNull { it.exists() } ?: return null
|
||||||
|
return try {
|
||||||
|
val text = source.readText()
|
||||||
|
val parsed =
|
||||||
|
if (source.name.endsWith(".json")) {
|
||||||
|
JSONObject(text).optString("SECURITY_PATCH", "")
|
||||||
|
} else {
|
||||||
|
PROP_PATTERN.find(text)?.groupValues?.get(1)?.trim().orEmpty()
|
||||||
|
}
|
||||||
|
parsed.takeIf { it.isNotBlank() }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
SystemLogger.warning(
|
||||||
|
"PatchLevelManager: failed to parse ${source.path}: ${e.message}"
|
||||||
|
)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun atomicWrite(date: String) {
|
||||||
|
val target = File(PATCH_FILE)
|
||||||
|
val staging = File(STAGING_FILE)
|
||||||
|
staging.writeText("system=$date\nboot=$date\nvendor=$date\n")
|
||||||
|
Files.move(
|
||||||
|
staging.toPath(),
|
||||||
|
target.toPath(),
|
||||||
|
StandardCopyOption.ATOMIC_MOVE,
|
||||||
|
StandardCopyOption.REPLACE_EXISTING,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user