Compare commits

...
3 Commits
Author SHA1 Message Date
Enginex0 09d9896228 docs(release): bump to v4.1 with changelog and update metadata
versionCode=94 matches post-commit count.
2026-03-10 13:00:57 +01:00
Enginex0 5a599025ad fix(attestation): persist vbmeta boot key and hash across reboots
resetprop overrides for ro.boot.* props don't survive reboots. On
devices where the kernel doesn't set ro.boot.vbmeta.public_key_digest,
the fallback chain hit random generation on every boot — producing a
different RootOfTrust hash each time.

Added file-based persistence (boot_hash.bin, boot_key.bin) as a
fallback layer between TEE cache and random generation. Once a value
is determined from any source, it's written to disk and reused on
subsequent boots.

Verified on Redmi 14C: second boot reads from persistent file instead
of regenerating random bytes.
2026-03-10 12:58:49 +01:00
Enginex0 f5c2bcc024 fix(module): align update.json versionCode with release ZIP
Release ZIP was built at 89 commits (versionCode=89) but update.json
had versionCode=90, causing an infinite update loop in KSU Manager.
2026-03-10 04:40:53 +01:00
5 changed files with 53 additions and 9 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ val gitExecutor = objects.newInstance(GitExecutor::class.java)
val gitCommitCount = gitExecutor.execute("git rev-list HEAD --count", rootDir).toInt()
val gitCommitHash = gitExecutor.execute("git rev-parse --verify --short HEAD", rootDir)
val verName = "v4.0"
val verName = "v4.1"
android {
namespace = "org.matrix.TEESimulator"
@@ -91,27 +91,33 @@ object AndroidDeviceUtils {
attestationValueProvider: () -> ByteArray?,
expectedSize: Int,
): ByteArray {
// 1. Attempt to get the value from the system property.
getProperty(propertyName, expectedSize)?.let {
SystemLogger.debug("Using $propertyName from system property: ${it.toHex()}")
persistToFile(propertyName, it)
return it
}
// 2. Fallback to the value from a cached TEE attestation.
try {
attestationValueProvider()?.let {
SystemLogger.debug("Using $propertyName from TEE attestation: ${it.toHex()}")
setProperty(propertyName, it) // Persist for consistency
setProperty(propertyName, it)
persistToFile(propertyName, it)
return it
}
} catch (e: Exception) {
SystemLogger.error("Failed to get $propertyName from attestation.", e)
}
// 3. As a final fallback, generate a random value.
readFromFile(propertyName, expectedSize)?.let {
SystemLogger.debug("Using $propertyName from persistent file: ${it.toHex()}")
setProperty(propertyName, it)
return it
}
return generateRandomBytes(expectedSize).also {
SystemLogger.debug("Using randomly generated $propertyName: ${it.toHex()}")
setProperty(propertyName, it)
persistToFile(propertyName, it)
}
}
@@ -158,10 +164,37 @@ object AndroidDeviceUtils {
}
}
/** Generates a cryptographically random byte array of a specified length. */
private fun generateRandomBytes(size: Int): ByteArray =
ByteArray(size).also { ThreadLocalRandom.current().nextBytes(it) }
private val PERSIST_DIR = File("/data/adb/tricky_store")
private fun fileForProperty(propertyName: String): File = when (propertyName) {
"ro.boot.vbmeta.digest" -> File(PERSIST_DIR, "boot_hash.bin")
"ro.boot.vbmeta.public_key_digest" -> File(PERSIST_DIR, "boot_key.bin")
else -> File(PERSIST_DIR, "${propertyName.replace('.', '_')}.bin")
}
private fun persistToFile(propertyName: String, bytes: ByteArray) {
try {
fileForProperty(propertyName).writeBytes(bytes)
} catch (e: Exception) {
SystemLogger.error("Failed to persist $propertyName to file.", e)
}
}
private fun readFromFile(propertyName: String, expectedSize: Int): ByteArray? {
return try {
val file = fileForProperty(propertyName)
if (!file.exists()) return null
val bytes = file.readBytes()
if (bytes.size == expectedSize) bytes else null
} catch (e: Exception) {
SystemLogger.error("Failed to read $propertyName from file.", e)
null
}
}
// --- Patch Level Properties ---
fun getPatchLevel(uid: Int): Int {
+10
View File
@@ -1,3 +1,13 @@
## TEESimulator v4.1: Boot Identity Persistence
Bugfix release. The vbmeta boot key digest was randomizing on every reboot, producing a different RootOfTrust in attestation certificates each boot.
On devices where the kernel doesn't set `ro.boot.vbmeta.public_key_digest`, the fallback chain hit random generation every boot because `resetprop` overrides for `ro.boot.*` props don't survive reboots. Added file-based persistence (`boot_hash.bin`, `boot_key.bin`) between the TEE cache and random fallback. Once determined, boot identity values persist across reboots.
Verified on Redmi 14C: second boot reads from persistent file instead of regenerating.
---
## TEESimulator v4.0: Native Rust Cert Generation
Major release. Certificate chain generation rebuilt from the ground up in Rust, replacing the BouncyCastle Java path for EC and RSA keys. Hardened against every known detector app.
+1
View File
@@ -9,3 +9,4 @@ done
rm -rf "$CONFIG_DIR/persistent_keys"
rm -f "$CONFIG_DIR/tee_status.txt"
rm -f "$CONFIG_DIR/boot_hash.bin" "$CONFIG_DIR/boot_key.bin"
+3 -3
View File
@@ -1,6 +1,6 @@
{
"version": "v4.0",
"versionCode": 90,
"zipUrl": "https://github.com/Enginex0/TEESimulator/releases/download/v4.0/TEESimulator-v4.0-Release.zip",
"version": "v4.1",
"versionCode": 94,
"zipUrl": "https://github.com/Enginex0/TEESimulator/releases/download/v4.1/TEESimulator-v4.1-Release.zip",
"changelog": "https://raw.githubusercontent.com/Enginex0/TEESimulator/main/module/changelog.md"
}