diff --git a/app/src/main/java/org/matrix/TEESimulator/App.kt b/app/src/main/java/org/matrix/TEESimulator/App.kt index b694cbe..d8230e0 100644 --- a/app/src/main/java/org/matrix/TEESimulator/App.kt +++ b/app/src/main/java/org/matrix/TEESimulator/App.kt @@ -10,6 +10,7 @@ import java.security.Security import org.bouncycastle.jce.provider.BouncyCastleProvider import org.matrix.TEESimulator.config.BootStateManager 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.Keystore2Interceptor import org.matrix.TEESimulator.interception.keystore.KeystoreInterceptor @@ -46,6 +47,7 @@ object App { // Load the package configuration. ConfigurationManager.initialize() BootStateManager.apply() + PatchLevelManager.initialize() // Set up the device's boot key and hash, which are crucial for attestation. AndroidDeviceUtils.setupBootKeyAndHash() diff --git a/app/src/main/java/org/matrix/TEESimulator/config/PatchLevelManager.kt b/app/src/main/java/org/matrix/TEESimulator/config/PatchLevelManager.kt new file mode 100644 index 0000000..6a7a215 --- /dev/null +++ b/app/src/main/java/org/matrix/TEESimulator/config/PatchLevelManager.kt @@ -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, + ) + } +}