java: Overhaul detection of attestation properties
- Reuse and extend the attestation we perform for detection of functional TEE. - Extract properties like boot hash, os version, attest version, keymaster version from the generated certificate. - Use previous methods as fallback for devices without functional TEE. - Set ro.boot.vbmeta.digest during initialization with the boot hash value. Signed-off-by: Dakkshesh <beakthoven@gmail.com>
This commit is contained in:
@@ -23,20 +23,61 @@ fun getTransactCode(clazz: Class<*>, method: String): Int =
|
|||||||
clazz.getDeclaredField("TRANSACTION_$method").apply { isAccessible = true }
|
clazz.getDeclaredField("TRANSACTION_$method").apply { isAccessible = true }
|
||||||
.getInt(null)
|
.getInt(null)
|
||||||
|
|
||||||
val bootHash: ByteArray by lazy {
|
// cache attest data to avoid running attestation multiple times
|
||||||
getBootHashFromProp() ?: randomBytes()
|
private val cachedAttestData: AttestationData? by lazy {
|
||||||
|
getAttestData() // from CertHacker
|
||||||
}
|
}
|
||||||
|
|
||||||
val bootKey: ByteArray by lazy {
|
val bootKey: ByteArray by lazy {
|
||||||
randomBytes()
|
randomBytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setupBootHash() {
|
||||||
|
getBootHashFromProp()?.also {
|
||||||
|
Logger.d("Using boot hash from system property: ${it.toHex()}")
|
||||||
|
}
|
||||||
|
?: getBootHashFromAttestation()?.also {
|
||||||
|
Logger.d("Using boot hash from attestation: ${it.toHex()}")
|
||||||
|
setBootHashProp(it)
|
||||||
|
}
|
||||||
|
?: randomBytes().also {
|
||||||
|
Logger.d("Generating random boot hash: ${it.toHex()}")
|
||||||
|
setBootHashProp(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
private fun getBootHashFromProp(): ByteArray? {
|
fun getBootHashFromProp(): ByteArray? {
|
||||||
val digest = SystemProperties.get("ro.boot.vbmeta.digest", null) ?: return null
|
val digest = SystemProperties.get("ro.boot.vbmeta.digest", null) ?: return null
|
||||||
|
Logger.d("System property ro.boot.vbmeta.digest: $digest")
|
||||||
|
|
||||||
|
if (digest.isBlank()) {
|
||||||
|
Logger.d("Property is blank")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return if (digest.length == 64) digest.hexToByteArray() else null
|
return if (digest.length == 64) digest.hexToByteArray() else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getBootHashFromAttestation(): ByteArray? {
|
||||||
|
return try {
|
||||||
|
cachedAttestData?.verifiedBootHash
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.e("Failed to get boot hash from attestation: ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setBootHashProp(bytes: ByteArray) {
|
||||||
|
val hex = bytes.toHex()
|
||||||
|
try {
|
||||||
|
Logger.d("Setting ro.boot.vbmeta.digest to: $hex")
|
||||||
|
SystemProperties.set("ro.boot.vbmeta.digest", hex)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.e("Exception setting vbmeta digest: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun randomBytes(): ByteArray = ByteArray(32).also {
|
private fun randomBytes(): ByteArray = ByteArray(32).also {
|
||||||
ThreadLocalRandom.current().nextBytes(it)
|
ThreadLocalRandom.current().nextBytes(it)
|
||||||
}
|
}
|
||||||
@@ -114,9 +155,6 @@ private fun parsePatchLevelValue(value: String, component: String, isLong: Boole
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val osVersion: Int
|
|
||||||
get() = getOsVersion(Build.VERSION.SDK_INT)
|
|
||||||
|
|
||||||
private val osVersionMap = mapOf(
|
private val osVersionMap = mapOf(
|
||||||
Build.VERSION_CODES.BAKLAVA to 160000,
|
Build.VERSION_CODES.BAKLAVA to 160000,
|
||||||
Build.VERSION_CODES.VANILLA_ICE_CREAM to 150000,
|
Build.VERSION_CODES.VANILLA_ICE_CREAM to 150000,
|
||||||
@@ -128,7 +166,8 @@ private val osVersionMap = mapOf(
|
|||||||
Build.VERSION_CODES.Q to 100000
|
Build.VERSION_CODES.Q to 100000
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getOsVersion(sdkVersion: Int): Int = osVersionMap[sdkVersion] ?: 160000
|
val osVersion: Int
|
||||||
|
get() = cachedAttestData?.osVersion ?: osVersionMap[Build.VERSION.SDK_INT] ?: 160000
|
||||||
|
|
||||||
private val attestVersionMap = mapOf(
|
private val attestVersionMap = mapOf(
|
||||||
Build.VERSION_CODES.Q to 4, // Keymaster 4.1
|
Build.VERSION_CODES.Q to 4, // Keymaster 4.1
|
||||||
@@ -142,11 +181,10 @@ private val attestVersionMap = mapOf(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val attestVersion: Int
|
val attestVersion: Int
|
||||||
get() = attestVersionMap[Build.VERSION.SDK_INT] ?: 400
|
get() = cachedAttestData?.attestVersion ?: attestVersionMap[Build.VERSION.SDK_INT] ?: 400
|
||||||
|
|
||||||
val keymasterVersion: Int
|
val keymasterVersion: Int
|
||||||
get() = if (attestVersion == 4) 41 else attestVersion
|
get() = cachedAttestData?.keymasterVersion ?: if (attestVersion == 4) 41 else attestVersion
|
||||||
|
|
||||||
|
|
||||||
fun String.convertPatchLevel(isLong: Boolean): Int = runCatching {
|
fun String.convertPatchLevel(isLong: Boolean): Int = runCatching {
|
||||||
val parts = split("-")
|
val parts = split("-")
|
||||||
@@ -195,4 +233,5 @@ val moduleHash: ByteArray by lazy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.trimLine(): String = trim().split("\n").joinToString("\n") { it.trim() }
|
fun String.trimLine(): String = trim().split("\n").joinToString("\n") { it.trim() }
|
||||||
|
fun ByteArray.toHex(): String = joinToString("") { "%02x".format(it) }
|
||||||
@@ -10,14 +10,28 @@ import android.hardware.security.keymint.Algorithm
|
|||||||
import android.hardware.security.keymint.EcCurve
|
import android.hardware.security.keymint.EcCurve
|
||||||
import android.hardware.security.keymint.KeyParameter
|
import android.hardware.security.keymint.KeyParameter
|
||||||
import android.hardware.security.keymint.Tag
|
import android.hardware.security.keymint.Tag
|
||||||
|
import android.os.Build
|
||||||
|
import android.security.keystore.KeyGenParameterSpec
|
||||||
import android.security.keystore.KeyProperties
|
import android.security.keystore.KeyProperties
|
||||||
import android.system.keystore2.KeyDescriptor
|
import android.system.keystore2.KeyDescriptor
|
||||||
import android.util.Pair
|
import android.util.Pair
|
||||||
import io.github.beakthoven.TrickyStoreOSS.*
|
|
||||||
import io.github.beakthoven.TrickyStoreOSS.core.config.Config
|
import io.github.beakthoven.TrickyStoreOSS.core.config.Config
|
||||||
import io.github.beakthoven.TrickyStoreOSS.core.logging.Logger
|
import io.github.beakthoven.TrickyStoreOSS.core.logging.Logger
|
||||||
import io.github.beakthoven.TrickyStoreOSS.interceptors.SecurityLevelInterceptor
|
import io.github.beakthoven.TrickyStoreOSS.interceptors.SecurityLevelInterceptor
|
||||||
import org.bouncycastle.asn1.*
|
import org.bouncycastle.asn1.ASN1Boolean
|
||||||
|
import org.bouncycastle.asn1.ASN1Encodable
|
||||||
|
import org.bouncycastle.asn1.ASN1EncodableVector
|
||||||
|
import org.bouncycastle.asn1.ASN1Enumerated
|
||||||
|
import org.bouncycastle.asn1.ASN1Integer
|
||||||
|
import org.bouncycastle.asn1.ASN1ObjectIdentifier
|
||||||
|
import org.bouncycastle.asn1.ASN1OctetString
|
||||||
|
import org.bouncycastle.asn1.ASN1Sequence
|
||||||
|
import org.bouncycastle.asn1.ASN1TaggedObject
|
||||||
|
import org.bouncycastle.asn1.DERNull
|
||||||
|
import org.bouncycastle.asn1.DEROctetString
|
||||||
|
import org.bouncycastle.asn1.DERSequence
|
||||||
|
import org.bouncycastle.asn1.DERSet
|
||||||
|
import org.bouncycastle.asn1.DERTaggedObject
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x509.Extension
|
import org.bouncycastle.asn1.x509.Extension
|
||||||
import org.bouncycastle.asn1.x509.KeyUsage
|
import org.bouncycastle.asn1.x509.KeyUsage
|
||||||
@@ -31,16 +45,21 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.security.*
|
import java.security.KeyPair
|
||||||
|
import java.security.KeyPairGenerator
|
||||||
|
import java.security.KeyStore
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import java.security.Security
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
import java.security.cert.CertificateFactory
|
import java.security.cert.CertificateFactory
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.security.spec.ECGenParameterSpec
|
import java.security.spec.ECGenParameterSpec
|
||||||
import java.security.spec.RSAKeyGenParameterSpec
|
import java.security.spec.RSAKeyGenParameterSpec
|
||||||
import java.util.*
|
import java.util.Date
|
||||||
|
import java.util.LinkedList
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
import android.os.Build
|
|
||||||
|
|
||||||
object CertificateHacker {
|
object CertificateHacker {
|
||||||
|
|
||||||
@@ -625,7 +644,7 @@ object CertificateHacker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (verifiedBootHash == null) {
|
if (verifiedBootHash == null) {
|
||||||
verifiedBootHash = bootHash
|
verifiedBootHash = getBootHashFromProp()
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootOfTrustElements = arrayOf(
|
val rootOfTrustElements = arrayOf(
|
||||||
@@ -684,7 +703,9 @@ object CertificateHacker {
|
|||||||
private fun createAttestationExtension(params: KeyGenParameters, uid: Int, securityLevel: Int = 1): Extension {
|
private fun createAttestationExtension(params: KeyGenParameters, uid: Int, securityLevel: Int = 1): Extension {
|
||||||
try {
|
try {
|
||||||
val key = bootKey
|
val key = bootKey
|
||||||
val hash = bootHash
|
val hash = getBootHashFromProp()
|
||||||
|
|
||||||
|
Logger.d("Using boothash ${hash?.toHex() ?: 0}")
|
||||||
|
|
||||||
val rootOfTrustEncodables = arrayOf(
|
val rootOfTrustEncodables = arrayOf(
|
||||||
DEROctetString(key),
|
DEROctetString(key),
|
||||||
@@ -834,4 +855,130 @@ object CertificateHacker {
|
|||||||
return DEROctetString(DERSequence(applicationIdArray).encoded)
|
return DEROctetString(DERSequence(applicationIdArray).encoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class AttestationData(
|
||||||
|
val verifiedBootHash: ByteArray?,
|
||||||
|
val attestVersion: Int?,
|
||||||
|
val keymasterVersion: Int?,
|
||||||
|
val osVersion: Int?,
|
||||||
|
)
|
||||||
|
|
||||||
|
val keygen_alias = "tricky_store_oss_attest"
|
||||||
|
|
||||||
|
val teeStatus: Boolean by lazy { isTEEWorking() }
|
||||||
|
|
||||||
|
private fun isTEEWorking(): Boolean {
|
||||||
|
return try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
android.app.ActivityThread.initializeMainlineModules()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
android.security.keystore2.AndroidKeyStoreProvider.install()
|
||||||
|
} else {
|
||||||
|
android.security.keystore.AndroidKeyStoreProvider.install()
|
||||||
|
}
|
||||||
|
|
||||||
|
val keyStore = KeyStore.getInstance("AndroidKeyStore")
|
||||||
|
keyStore.load(null)
|
||||||
|
|
||||||
|
val keyPairGenerator = KeyPairGenerator.getInstance(
|
||||||
|
KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore")
|
||||||
|
|
||||||
|
val challenge = ByteArray(16).apply {
|
||||||
|
SecureRandom().nextBytes(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
val parameterSpec = KeyGenParameterSpec.Builder(
|
||||||
|
keygen_alias,
|
||||||
|
KeyProperties.PURPOSE_SIGN
|
||||||
|
)
|
||||||
|
.setAlgorithmParameterSpec(ECGenParameterSpec("secp256r1"))
|
||||||
|
.setDigests(KeyProperties.DIGEST_SHA256)
|
||||||
|
.setAttestationChallenge(challenge)
|
||||||
|
.setIsStrongBoxBacked(false)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
keyPairGenerator.initialize(parameterSpec)
|
||||||
|
keyPairGenerator.generateKeyPair()
|
||||||
|
|
||||||
|
Logger.d("TEE check: successful")
|
||||||
|
|
||||||
|
// keyStore.deleteEntry(keygen_alias)
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.w("TEE check failure: ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAttestCert(): X509Certificate? {
|
||||||
|
return if (teeStatus) {
|
||||||
|
val keyStore = KeyStore.getInstance("AndroidKeyStore")
|
||||||
|
keyStore.load(null)
|
||||||
|
|
||||||
|
val certChain = keyStore.getCertificateChain(keygen_alias)
|
||||||
|
if (certChain == null || certChain.isEmpty()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
keyStore.deleteEntry(keygen_alias)
|
||||||
|
certChain[0] as X509Certificate
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAttestData(): AttestationData? {
|
||||||
|
val leaf: X509Certificate = getAttestCert() ?: return null
|
||||||
|
val ATTESTATION_OID = ASN1ObjectIdentifier("1.3.6.1.4.1.11129.2.1.17")
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val leafHolder = X509CertificateHolder(leaf.encoded)
|
||||||
|
val ext: Extension = leafHolder.getExtension(ATTESTATION_OID) ?: run {
|
||||||
|
Logger.i("No attestation extension found on certificate")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val keyDescriptionSeq = ASN1Sequence.getInstance(ext.extnValue.octets)
|
||||||
|
val encodables = keyDescriptionSeq.toArray()
|
||||||
|
|
||||||
|
val attestVersion = ASN1Integer.getInstance(encodables[0]).value.intValueExact()
|
||||||
|
val keymasterVersion = ASN1Integer.getInstance(encodables[2]).value.intValueExact()
|
||||||
|
var attestVerifiedBootHash: ByteArray? = null
|
||||||
|
var attestOSVersion: Int? = null
|
||||||
|
|
||||||
|
val teeEnforced = ASN1Sequence.getInstance(encodables[7])
|
||||||
|
|
||||||
|
teeEnforced.forEach { element ->
|
||||||
|
val tagged = element as ASN1TaggedObject
|
||||||
|
when (tagged.tagNo) {
|
||||||
|
704 -> { // Parse Root of Trust
|
||||||
|
val rootOfTrustSeq = ASN1Sequence.getInstance(tagged.baseObject.toASN1Primitive())
|
||||||
|
if (rootOfTrustSeq.size() >= 4) {
|
||||||
|
attestVerifiedBootHash = ASN1OctetString.getInstance(rootOfTrustSeq.getObjectAt(3)).octets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
705 -> { // Parse OS Version
|
||||||
|
attestOSVersion = ASN1Integer.getInstance(tagged.baseObject.toASN1Primitive()).value.intValueExact()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.i("Extracted attestationVersion: $attestVersion")
|
||||||
|
Logger.i("Extracted keymasterVersion: $keymasterVersion")
|
||||||
|
Logger.i("Extracted verifiedBootHash: ${attestVerifiedBootHash?.toHex() ?: 0}")
|
||||||
|
Logger.i("Extracted osVersion: $attestOSVersion")
|
||||||
|
|
||||||
|
AttestationData(
|
||||||
|
verifiedBootHash = attestVerifiedBootHash,
|
||||||
|
attestVersion = attestVersion,
|
||||||
|
keymasterVersion = keymasterVersion,
|
||||||
|
osVersion = attestOSVersion
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.e("Failed to parse attestation data", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,7 @@ fun main(args: Array<String>) {
|
|||||||
Logger.i("Welcome to TrickyStoreOSS!")
|
Logger.i("Welcome to TrickyStoreOSS!")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
setupBootHash()
|
||||||
initializeInterceptors()
|
initializeInterceptors()
|
||||||
maintainService()
|
maintainService()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -6,18 +6,12 @@
|
|||||||
package io.github.beakthoven.TrickyStoreOSS.core.config
|
package io.github.beakthoven.TrickyStoreOSS.core.config
|
||||||
|
|
||||||
import android.content.pm.IPackageManager
|
import android.content.pm.IPackageManager
|
||||||
import android.os.Build
|
|
||||||
import android.os.FileObserver
|
import android.os.FileObserver
|
||||||
import android.os.ServiceManager
|
import android.os.ServiceManager
|
||||||
import android.security.keystore.KeyGenParameterSpec
|
|
||||||
import android.security.keystore.KeyProperties
|
|
||||||
import io.github.beakthoven.TrickyStoreOSS.CertificateHacker
|
import io.github.beakthoven.TrickyStoreOSS.CertificateHacker
|
||||||
import io.github.beakthoven.TrickyStoreOSS.core.logging.Logger
|
import io.github.beakthoven.TrickyStoreOSS.core.logging.Logger
|
||||||
|
import io.github.beakthoven.TrickyStoreOSS.teeStatus
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.security.KeyPairGenerator
|
|
||||||
import java.security.KeyStore
|
|
||||||
import java.security.SecureRandom
|
|
||||||
import java.security.spec.ECGenParameterSpec
|
|
||||||
|
|
||||||
object Config {
|
object Config {
|
||||||
private val hackPackages = mutableSetOf<String>()
|
private val hackPackages = mutableSetOf<String>()
|
||||||
@@ -74,58 +68,12 @@ object Config {
|
|||||||
@Volatile
|
@Volatile
|
||||||
private var teeBroken: Boolean? = null
|
private var teeBroken: Boolean? = null
|
||||||
|
|
||||||
private fun isTEEWorking(): Boolean {
|
|
||||||
val alias = "tee_attest_test_key"
|
|
||||||
return try {
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
android.app.ActivityThread.initializeMainlineModules();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
||||||
android.security.keystore2.AndroidKeyStoreProvider.install();
|
|
||||||
} else {
|
|
||||||
android.security.keystore.AndroidKeyStoreProvider.install();
|
|
||||||
}
|
|
||||||
|
|
||||||
val keyStore = KeyStore.getInstance("AndroidKeyStore")
|
|
||||||
keyStore.load(null)
|
|
||||||
|
|
||||||
val keyPairGenerator = KeyPairGenerator.getInstance(
|
|
||||||
KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore")
|
|
||||||
|
|
||||||
val challenge = ByteArray(16).apply {
|
|
||||||
SecureRandom().nextBytes(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
val parameterSpec = KeyGenParameterSpec.Builder(
|
|
||||||
alias,
|
|
||||||
KeyProperties.PURPOSE_SIGN
|
|
||||||
)
|
|
||||||
.setAlgorithmParameterSpec(ECGenParameterSpec("secp256r1"))
|
|
||||||
.setDigests(KeyProperties.DIGEST_SHA256)
|
|
||||||
.setAttestationChallenge(challenge)
|
|
||||||
.setIsStrongBoxBacked(false)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
keyPairGenerator.initialize(parameterSpec)
|
|
||||||
keyPairGenerator.generateKeyPair()
|
|
||||||
|
|
||||||
keyStore.deleteEntry(alias)
|
|
||||||
true
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Logger.e("TEE check failure: ${e.message}")
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun storeTEEStatus(root: File) {
|
private fun storeTEEStatus(root: File) {
|
||||||
val statusFile = File(root, TEE_STATUS_FILE)
|
val statusFile = File(root, TEE_STATUS_FILE)
|
||||||
val status = isTEEWorking()
|
teeBroken = !teeStatus
|
||||||
teeBroken = !status
|
|
||||||
try {
|
try {
|
||||||
statusFile.writeText("teeBroken=${!status}")
|
statusFile.writeText("teeBroken=${teeBroken}")
|
||||||
|
Logger.i("TEE status written to $statusFile: teeBroken=$teeBroken")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Logger.e("Failed to write TEE status: ${e.message}")
|
Logger.e("Failed to write TEE status: ${e.message}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,8 @@ public class SystemProperties {
|
|||||||
public static String get(String key, String def) {
|
public static String get(String key, String def) {
|
||||||
throw new RuntimeException("");
|
throw new RuntimeException("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void set(String key, String val) {
|
||||||
|
throw new RuntimeException("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user