Implement multi-keybox support (#1)
This commit adds support for using different `keybox.xml` files for different applications. This allows for greater flexibility, enabling the use of distinct cryptographic identities for specific groups of apps. The `target.txt` configuration now supports a new syntax. A line with a filename in brackets, like `[demo_keybox.xml]`, sets the active keybox for all subsequent packages in the file. The implementation refactors the configuration and keybox loading logic: - PkgConfig now parses the new syntax and maps packages to their designated keybox files. - KeyBoxUtils is updated to dynamically load and cache multiple keybox files on demand, instead of relying on a single global state. - Certificate generation and hacking functions now use this new configuration to select the correct keybox for each operation based on the application's UID.
This commit is contained in:
@@ -40,22 +40,38 @@ This file provides the master cryptographic identity for the simulator. It conta
|
|||||||
</AndroidAttestation>
|
</AndroidAttestation>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mode Configuration (`target.txt`)
|
### Mode and Keybox Configuration (`target.txt`)
|
||||||
|
|
||||||
TEESimulator currently operates in two primary modes as it transitions towards full emulation. You can control this behavior on a per-package basis.
|
TEESimulator currently operates in two primary modes as it transitions towards full emulation.
|
||||||
|
You can control the simulation mode and the specific keybox.xml file used on a per-package basis.
|
||||||
|
|
||||||
|
#### Mode Suffixes
|
||||||
|
|
||||||
* **`!` → Force Generation Mode:** Creates a complete, software-based virtual key. This is the foundation of the full TEE simulation.
|
* **`!` → Force Generation Mode:** Creates a complete, software-based virtual key. This is the foundation of the full TEE simulation.
|
||||||
* **`?` → Force Leaf Hacking Mode:** A legacy mode where a real TEE key is generated, but its attestation certificate is intercepted and modified.
|
* **`?` → Force Leaf Hacking Mode:** A legacy mode where a real TEE key is generated, but its attestation certificate is intercepted and modified.
|
||||||
* **No symbol → Automatic Mode:** The module selects the most appropriate mode for the device.
|
* **No symbol → Automatic Mode:** The module selects the most appropriate mode for the device.
|
||||||
|
|
||||||
|
#### Multi-Keybox Configuration
|
||||||
|
|
||||||
|
You can specify different keybox files for different groups of applications. This is done by adding a line with the filename in square brackets (e.g., [demo_keybox.xml]).
|
||||||
|
|
||||||
|
All applications listed after this line will use the specified keybox file, until a new keybox is declared. Applications listed before any custom keybox declaration will use the default `keybox.xml`.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
```
|
```
|
||||||
# target.txt
|
# These two apps will use the default /data/adb/tricky_store/keybox.xml
|
||||||
# Use full generation/simulation for this app
|
|
||||||
com.google.android.gms!
|
com.google.android.gms!
|
||||||
|
|
||||||
# Use the legacy leaf hacking mode
|
|
||||||
io.github.vvb2060.keyattestation?
|
io.github.vvb2060.keyattestation?
|
||||||
|
|
||||||
|
# Switch to a different keybox for the following apps.
|
||||||
|
# The file must be located at /data/adb/tricky_store/aosp_keybox.xml
|
||||||
|
[aosp_keybox.xml]
|
||||||
|
com.google.android.gsf
|
||||||
|
|
||||||
|
# Switch again to another keybox.
|
||||||
|
# The file must be located at /data/adb/tricky_store/demo_keybox.xml
|
||||||
|
[demo_keybox.xml]
|
||||||
|
org.matrix.demo
|
||||||
```
|
```
|
||||||
|
|
||||||
### Security Patch Level (`security_patch.txt`)
|
### Security Patch Level (`security_patch.txt`)
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ object CertificateGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun generateChain(uid: Int, params: KeyGenParameters, keyPair: KeyPair, securityLevel: Int = 1): List<ByteArray>? = runCatching {
|
fun generateChain(uid: Int, params: KeyGenParameters, keyPair: KeyPair, securityLevel: Int = 1): List<ByteArray>? = runCatching {
|
||||||
val keybox = getKeyboxForAlgorithm(params.algorithm) ?: return null
|
val keybox = getKeyboxForAlgorithm(uid, params.algorithm) ?: return null
|
||||||
|
|
||||||
val issuer = X509CertificateHolder(keybox.certificates[0].encoded).subject
|
val issuer = X509CertificateHolder(keybox.certificates[0].encoded).subject
|
||||||
val leaf = buildCertificate(keyPair, keybox, params, issuer, uid, securityLevel)
|
val leaf = buildCertificate(keyPair, keybox, params, issuer, uid, securityLevel)
|
||||||
@@ -234,7 +234,7 @@ object CertificateGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val keyPair = generateKeyPair(params) ?: return null
|
val keyPair = generateKeyPair(params) ?: return null
|
||||||
val keybox = getKeyboxForAlgorithm(params.algorithm) ?: return null
|
val keybox = getKeyboxForAlgorithm(uid, params.algorithm) ?: return null
|
||||||
|
|
||||||
val (signingKeyPair, issuer) = if (hasAttestKey) {
|
val (signingKeyPair, issuer) = if (hasAttestKey) {
|
||||||
getAttestationKeyInfo(uid, attestKeyDescriptor!!)?.let {
|
getAttestationKeyInfo(uid, attestKeyDescriptor!!)?.let {
|
||||||
@@ -267,9 +267,10 @@ object CertificateGen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getKeyboxForAlgorithm(algorithm: Int): KeyBox? {
|
private fun getKeyboxForAlgorithm(uid: Int, algorithm: Int): KeyBox? {
|
||||||
val algorithmName = mapAlgorithmToName(algorithm) ?: return null
|
val algorithmName = mapAlgorithmToName(algorithm) ?: return null
|
||||||
return KeyBoxUtils.keyboxes[algorithmName]
|
val keyboxFileName = PkgConfig.getKeyboxFileForUid(uid)
|
||||||
|
return KeyBoxUtils.getKeybox(keyboxFileName, algorithmName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAttestationKeyInfo(uid: Int, attestKeyDescriptor: KeyDescriptor): Pair<KeyPair, X500Name>? {
|
private fun getAttestationKeyInfo(uid: Int, attestKeyDescriptor: KeyDescriptor): Pair<KeyPair, X500Name>? {
|
||||||
@@ -468,4 +469,4 @@ object CertificateGen {
|
|||||||
|
|
||||||
return DEROctetString(DERSequence(applicationIdArray).encoded)
|
return DEROctetString(DERSequence(applicationIdArray).encoded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package io.github.beakthoven.TrickyStoreOSS
|
package io.github.beakthoven.TrickyStoreOSS
|
||||||
|
|
||||||
|
import io.github.beakthoven.TrickyStoreOSS.config.PkgConfig
|
||||||
import io.github.beakthoven.TrickyStoreOSS.logging.Logger
|
import io.github.beakthoven.TrickyStoreOSS.logging.Logger
|
||||||
import org.bouncycastle.asn1.ASN1Boolean
|
import org.bouncycastle.asn1.ASN1Boolean
|
||||||
import org.bouncycastle.asn1.ASN1Encodable
|
import org.bouncycastle.asn1.ASN1Encodable
|
||||||
@@ -49,7 +50,7 @@ object CertificateHack {
|
|||||||
leafAlgorithms.clear()
|
leafAlgorithms.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hackCertificateChain(certificateChain: Array<Certificate>?): Array<Certificate> {
|
fun hackCertificateChain(certificateChain: Array<Certificate>?, uid: Int): Array<Certificate> {
|
||||||
if (certificateChain == null) {
|
if (certificateChain == null) {
|
||||||
throw UnsupportedOperationException("Certificate chain is null!")
|
throw UnsupportedOperationException("Certificate chain is null!")
|
||||||
}
|
}
|
||||||
@@ -80,8 +81,9 @@ object CertificateHack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val keybox = KeyBoxUtils.keyboxes[leaf.publicKey.algorithm]
|
val keyboxFileName = PkgConfig.getKeyboxFileForUid(uid)
|
||||||
?: throw UnsupportedOperationException("Unsupported algorithm: ${leaf.publicKey.algorithm}")
|
val algorithmName = leaf.publicKey.algorithm
|
||||||
|
val keybox = KeyBoxUtils.getKeybox(keyboxFileName, algorithmName) ?: throw UnsupportedOperationException("Unsupported algorithm '$algorithmName' in keybox '$keyboxFileName'")
|
||||||
|
|
||||||
val certificates = LinkedList(keybox.certificates)
|
val certificates = LinkedList(keybox.certificates)
|
||||||
val builder = X509v3CertificateBuilder(
|
val builder = X509v3CertificateBuilder(
|
||||||
@@ -107,7 +109,7 @@ object CertificateHack {
|
|||||||
certificates.addFirst(JcaX509CertificateConverter().getCertificate(builder.build(signer)))
|
certificates.addFirst(JcaX509CertificateConverter().getCertificate(builder.build(signer)))
|
||||||
certificates.toTypedArray()
|
certificates.toTypedArray()
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Logger.e("Failed to hack certificate chain", t)
|
Logger.e("Failed to hack certificate chain for uid=$uid", t)
|
||||||
certificateChain
|
certificateChain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,12 +124,13 @@ object CertificateHack {
|
|||||||
val algorithm = leafAlgorithms.remove(key)
|
val algorithm = leafAlgorithms.remove(key)
|
||||||
?: throw UnsupportedOperationException("No algorithm found for key $key")
|
?: throw UnsupportedOperationException("No algorithm found for key $key")
|
||||||
|
|
||||||
val keybox = KeyBoxUtils.keyboxes[algorithm]
|
val keyboxFileName = PkgConfig.getKeyboxFileForUid(uid)
|
||||||
?: throw UnsupportedOperationException("Unsupported algorithm: $algorithm")
|
val keybox = KeyBoxUtils.getKeybox(keyboxFileName, algorithm)
|
||||||
|
?: throw UnsupportedOperationException("Unsupported algorithm '$algorithm' in keybox '$keyboxFileName'")
|
||||||
|
|
||||||
CertificateUtils.run { keybox.certificates.toByteArray() } ?: caList
|
CertificateUtils.run { keybox.certificates.toByteArray() } ?: caList
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Logger.e("Failed to hack CA certificate chain", t)
|
Logger.e("Failed to hack CA certificate chain for uid=$uid", t)
|
||||||
caList
|
caList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,8 +169,10 @@ object CertificateHack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val keybox = KeyBoxUtils.keyboxes[leaf.publicKey.algorithm]
|
val keyboxFileName = PkgConfig.getKeyboxFileForUid(uid)
|
||||||
?: throw UnsupportedOperationException("Unsupported algorithm: ${leaf.publicKey.algorithm}")
|
val algorithmName = leaf.publicKey.algorithm
|
||||||
|
val keybox = KeyBoxUtils.getKeybox(keyboxFileName, algorithmName)
|
||||||
|
?: throw UnsupportedOperationException("Unsupported algorithm '$algorithmName' in keybox '$keyboxFileName'")
|
||||||
|
|
||||||
val builder = X509v3CertificateBuilder(
|
val builder = X509v3CertificateBuilder(
|
||||||
X509CertificateHolder(keybox.certificates[0].encoded).subject,
|
X509CertificateHolder(keybox.certificates[0].encoded).subject,
|
||||||
@@ -191,7 +196,7 @@ object CertificateHack {
|
|||||||
|
|
||||||
JcaX509CertificateConverter().getCertificate(builder.build(signer)).encoded
|
JcaX509CertificateConverter().getCertificate(builder.build(signer)).encoded
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Logger.e("Failed to hack user certificate", t)
|
Logger.e("Failed to hack user certificate for uid=$uid", t)
|
||||||
certificate
|
certificate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,4 +243,4 @@ object CertificateHack {
|
|||||||
return Extension(ATTESTATION_OID, false, hackedSequenceOctets)
|
return Extension(ATTESTATION_OID, false, hackedSequenceOctets)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ package io.github.beakthoven.TrickyStoreOSS
|
|||||||
import android.security.keystore.KeyProperties
|
import android.security.keystore.KeyProperties
|
||||||
import io.github.beakthoven.TrickyStoreOSS.CertificateGen.KeyBox
|
import io.github.beakthoven.TrickyStoreOSS.CertificateGen.KeyBox
|
||||||
import io.github.beakthoven.TrickyStoreOSS.CertificateHack.clearLeafAlgorithms
|
import io.github.beakthoven.TrickyStoreOSS.CertificateHack.clearLeafAlgorithms
|
||||||
|
import io.github.beakthoven.TrickyStoreOSS.config.PkgConfig
|
||||||
import io.github.beakthoven.TrickyStoreOSS.logging.Logger
|
import io.github.beakthoven.TrickyStoreOSS.logging.Logger
|
||||||
import org.xmlpull.v1.XmlPullParser
|
import org.xmlpull.v1.XmlPullParser
|
||||||
import org.xmlpull.v1.XmlPullParserException
|
import org.xmlpull.v1.XmlPullParserException
|
||||||
import org.xmlpull.v1.XmlPullParserFactory
|
import org.xmlpull.v1.XmlPullParserFactory
|
||||||
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.StringReader
|
import java.io.StringReader
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
@@ -159,12 +161,68 @@ class XmlParser(private val xmlContent: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object KeyBoxUtils {
|
object KeyBoxUtils {
|
||||||
val keyboxes = ConcurrentHashMap<String, KeyBox>()
|
private val loadedKeyboxFiles = ConcurrentHashMap<String, ConcurrentHashMap<String, KeyBox>>()
|
||||||
|
|
||||||
fun hasKeyboxes(): Boolean = keyboxes.isNotEmpty()
|
/**
|
||||||
|
* The primary public function to get a specific KeyBox for a given algorithm and file.
|
||||||
|
* It will load and cache the file on demand if it hasn't been seen before.
|
||||||
|
*
|
||||||
|
* @param keyboxFileName The simple name of the keybox file (e.g., "keybox.xml").
|
||||||
|
* @param algorithm The algorithm key (e.g., KeyProperties.KEY_ALGORITHM_EC).
|
||||||
|
* @return The requested KeyBox, or null if not found in the specified file.
|
||||||
|
*/
|
||||||
|
fun getKeybox(keyboxFileName: String, algorithm: String): KeyBox? {
|
||||||
|
val keyboxesForFile = loadedKeyboxFiles.getOrPut(keyboxFileName) {
|
||||||
|
// If this file is not in our cache, load it now.
|
||||||
|
readFromFile(keyboxFileName)
|
||||||
|
}
|
||||||
|
Logger.i("Retriving keybox $keyboxFileName [$algorithm]")
|
||||||
|
return keyboxesForFile[algorithm]
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readFromFile(fileName: String): ConcurrentHashMap<String, KeyBox> {
|
||||||
|
val filePath = File(PkgConfig.CONFIG_PATH, fileName)
|
||||||
|
Logger.i("Loading keybox file: $filePath")
|
||||||
|
|
||||||
|
val keyboxes = ConcurrentHashMap<String, KeyBox>()
|
||||||
|
|
||||||
|
if (!filePath.exists()) {
|
||||||
|
Logger.e("Keybox file not found: $filePath")
|
||||||
|
return keyboxes // Return an empty map if file doesn't exist
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val xmlData = filePath.readText()
|
||||||
|
val xmlParser = XmlParser(xmlData.sanitizeXml())
|
||||||
|
|
||||||
|
val numberOfKeyboxesResult = xmlParser.obtainPath("AndroidAttestation.NumberOfKeyboxes")
|
||||||
|
val numberOfKeyboxes = when (numberOfKeyboxesResult) {
|
||||||
|
is XmlParser.ParseResult.Success -> numberOfKeyboxesResult.attributes["text"]?.toIntOrNull() ?: 1
|
||||||
|
is XmlParser.ParseResult.Error -> throw Exception(numberOfKeyboxesResult.message, numberOfKeyboxesResult.cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
repeat(numberOfKeyboxes) { i ->
|
||||||
|
val (algorithmName, keyBox) = processKeybox(xmlParser, i)
|
||||||
|
keyboxes[algorithmName] = keyBox
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.i("Successfully loaded ${keyboxes.size} keyboxes from $fileName")
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Logger.e("Error loading XML file ($fileName)", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyboxes
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasKeyboxes(): Boolean = loadedKeyboxFiles.isNotEmpty() && loadedKeyboxFiles.values.any { it.isNotEmpty() }
|
||||||
|
|
||||||
|
// This function is now deprecated and should be removed. We keep it for now to show the transition.
|
||||||
|
// Its logic is now inside readFromFile.
|
||||||
|
@Deprecated("Use getKeybox(fileName, algorithm) instead for dynamic loading.")
|
||||||
fun readFromXml(xmlData: String?) {
|
fun readFromXml(xmlData: String?) {
|
||||||
keyboxes.clear()
|
// The old global state is gone. This function's logic is now in readFromFile.
|
||||||
|
// We could make this load into a default keybox for backward compatibility if needed.
|
||||||
|
loadedKeyboxFiles.clear()
|
||||||
clearLeafAlgorithms()
|
clearLeafAlgorithms()
|
||||||
|
|
||||||
if (xmlData == null) {
|
if (xmlData == null) {
|
||||||
@@ -209,7 +267,7 @@ object KeyBoxUtils {
|
|||||||
return content.trimEnd()
|
return content.trimEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processKeybox(xmlParser: XmlParser, index: Int) {
|
private fun processKeybox(xmlParser: XmlParser, index: Int): Pair<String, KeyBox> {
|
||||||
try {
|
try {
|
||||||
val algorithmResult = xmlParser.obtainPath("AndroidAttestation.Keybox.Key[$index]")
|
val algorithmResult = xmlParser.obtainPath("AndroidAttestation.Keybox.Key[$index]")
|
||||||
val keyboxAlgorithm = when (algorithmResult) {
|
val keyboxAlgorithm = when (algorithmResult) {
|
||||||
@@ -264,11 +322,11 @@ object KeyBoxUtils {
|
|||||||
else -> keyboxAlgorithm
|
else -> keyboxAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
keyboxes[algorithmName] = KeyBox(pemKeyPair, keyPair, certificateChain)
|
return algorithmName to KeyBox(pemKeyPair, keyPair, certificateChain)
|
||||||
|
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Logger.e("Error processing keybox $index", t)
|
Logger.e("Error processing keybox $index", t)
|
||||||
throw t
|
throw t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,18 @@ object PkgConfig {
|
|||||||
private val generatePackages = mutableSetOf<String>()
|
private val generatePackages = mutableSetOf<String>()
|
||||||
private val packageModes = mutableMapOf<String, Mode>()
|
private val packageModes = mutableMapOf<String, Mode>()
|
||||||
|
|
||||||
|
private val packageKeyboxes = mutableMapOf<String, String>()
|
||||||
|
private val keyboxRegex = Regex("^\\[([a-zA-Z0-9_.-]+\\.xml)]$")
|
||||||
|
private const val DEFAULT_KEYBOX_FILE = "keybox.xml"
|
||||||
|
|
||||||
|
fun getKeyboxFileForUid(callingUid: Int): String = runCatching {
|
||||||
|
val ps = getPm()?.getPackagesForUid(callingUid) ?: return DEFAULT_KEYBOX_FILE
|
||||||
|
for (pkg in ps) {
|
||||||
|
packageKeyboxes[pkg]?.let { return it }
|
||||||
|
}
|
||||||
|
return DEFAULT_KEYBOX_FILE
|
||||||
|
}.getOrDefault(DEFAULT_KEYBOX_FILE)
|
||||||
|
|
||||||
enum class Mode {
|
enum class Mode {
|
||||||
AUTO, LEAF_HACK, GENERATE
|
AUTO, LEAF_HACK, GENERATE
|
||||||
}
|
}
|
||||||
@@ -29,41 +41,58 @@ object PkgConfig {
|
|||||||
hackPackages.clear()
|
hackPackages.clear()
|
||||||
generatePackages.clear()
|
generatePackages.clear()
|
||||||
packageModes.clear()
|
packageModes.clear()
|
||||||
f?.readLines()?.forEach {
|
packageKeyboxes.clear()
|
||||||
if (it.isNotBlank() && !it.startsWith("#")) {
|
|
||||||
val n = it.trim()
|
var currentKeyboxFile = DEFAULT_KEYBOX_FILE
|
||||||
when {
|
|
||||||
n.endsWith("!") -> {
|
f?.readLines()?.forEach { line ->
|
||||||
val pkg = n.removeSuffix("!").trim()
|
val n = line.trim()
|
||||||
generatePackages.add(pkg)
|
if (n.isBlank() || n.startsWith("#")) {
|
||||||
packageModes[pkg] = Mode.GENERATE
|
return@forEach // Skip comments and empty lines
|
||||||
}
|
}
|
||||||
n.endsWith("?") -> {
|
|
||||||
val pkg = n.removeSuffix("?").trim()
|
val matchResult = keyboxRegex.find(n)
|
||||||
hackPackages.add(pkg)
|
if (matchResult != null) {
|
||||||
packageModes[pkg] = Mode.LEAF_HACK
|
currentKeyboxFile = matchResult.groupValues[1]
|
||||||
}
|
Logger.i("Switched to keybox file: $currentKeyboxFile for subsequent packages")
|
||||||
else -> {
|
return@forEach
|
||||||
// Auto mode
|
}
|
||||||
packageModes[n] = Mode.AUTO
|
|
||||||
}
|
when {
|
||||||
|
n.endsWith("!") -> {
|
||||||
|
val pkg = n.removeSuffix("!").trim()
|
||||||
|
generatePackages.add(pkg)
|
||||||
|
packageModes[pkg] = Mode.GENERATE
|
||||||
|
packageKeyboxes[pkg] = currentKeyboxFile
|
||||||
|
}
|
||||||
|
n.endsWith("?") -> {
|
||||||
|
val pkg = n.removeSuffix("?").trim()
|
||||||
|
hackPackages.add(pkg)
|
||||||
|
packageModes[pkg] = Mode.LEAF_HACK
|
||||||
|
packageKeyboxes[pkg] = currentKeyboxFile
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Auto mode
|
||||||
|
packageModes[n] = Mode.AUTO
|
||||||
|
packageKeyboxes[n] = currentKeyboxFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger.i("update hack packages: $hackPackages, generate packages=$generatePackages, packageModes=$packageModes")
|
Logger.i("update hack packages: $hackPackages, generate packages=$generatePackages, packageModes=$packageModes, , packageKeyboxes=$packageKeyboxes")
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
Logger.e("failed to update target files", it)
|
Logger.e("failed to update target files", it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is now deprecated in favor of a more dynamic approach, but kept for simplicity.
|
||||||
|
// The key logic is now in KeyBoxUtils which will be called from the interceptors.
|
||||||
private fun updateKeyBox(f: File?) = runCatching {
|
private fun updateKeyBox(f: File?) = runCatching {
|
||||||
KeyBoxUtils.readFromXml(f?.readText())
|
KeyBoxUtils.readFromXml(f?.readText())
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
Logger.e("failed to update keybox", it)
|
Logger.e("failed to update keybox", it)
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val CONFIG_PATH = "/data/adb/tricky_store"
|
const val CONFIG_PATH = "/data/adb/tricky_store"
|
||||||
private const val TARGET_FILE = "target.txt"
|
private const val TARGET_FILE = "target.txt"
|
||||||
private const val KEYBOX_FILE = "keybox.xml"
|
|
||||||
private const val TEE_STATUS_FILE = "tee_status"
|
private const val TEE_STATUS_FILE = "tee_status"
|
||||||
private const val PATCHLEVEL_FILE = "security_patch.txt"
|
private const val PATCHLEVEL_FILE = "security_patch.txt"
|
||||||
private val root = File(CONFIG_PATH)
|
private val root = File(CONFIG_PATH)
|
||||||
@@ -100,10 +129,15 @@ object PkgConfig {
|
|||||||
DELETE, MOVED_FROM -> null
|
DELETE, MOVED_FROM -> null
|
||||||
else -> return
|
else -> return
|
||||||
}
|
}
|
||||||
when (path) {
|
when {
|
||||||
TARGET_FILE -> updateTargetPackages(f)
|
path == TARGET_FILE -> updateTargetPackages(f)
|
||||||
KEYBOX_FILE -> updateKeyBox(f)
|
path.endsWith(".xml") -> {
|
||||||
PATCHLEVEL_FILE -> updatePatchLevel(f)
|
// This is a simplification. A more robust solution would be to reload the specific keybox if it's in use.
|
||||||
|
// For now, we assume any XML change might affect the active keyboxes, prompting a reload where needed.
|
||||||
|
// The main logic for loading is now handled dynamically in KeyBoxUtils.
|
||||||
|
Logger.i("Keybox file $path changed. It will be re-read on next use.")
|
||||||
|
}
|
||||||
|
path == PATCHLEVEL_FILE -> updatePatchLevel(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,9 +150,9 @@ object PkgConfig {
|
|||||||
} else {
|
} else {
|
||||||
Logger.e("target.txt file not found, please put it to $scope !")
|
Logger.e("target.txt file not found, please put it to $scope !")
|
||||||
}
|
}
|
||||||
val keybox = File(root, KEYBOX_FILE)
|
val keybox = File(root, DEFAULT_KEYBOX_FILE)
|
||||||
if (!keybox.exists()) {
|
if (!keybox.exists()) {
|
||||||
Logger.e("keybox file not found, please put it to $keybox !")
|
Logger.e("default keybox file not found, please put it to $keybox !")
|
||||||
} else {
|
} else {
|
||||||
updateKeyBox(keybox)
|
updateKeyBox(keybox)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -150,7 +150,7 @@ object Keystore2Interceptor : BaseKeystoreInterceptor() {
|
|||||||
if (response != null) {
|
if (response != null) {
|
||||||
val chain = CertificateUtils.run { response.getCertificateChain() }
|
val chain = CertificateUtils.run { response.getCertificateChain() }
|
||||||
if (chain != null) {
|
if (chain != null) {
|
||||||
val newChain = CertificateHack.hackCertificateChain(chain)
|
val newChain = CertificateHack.hackCertificateChain(chain, callingUid)
|
||||||
response.putCertificateChain(newChain).getOrThrow()
|
response.putCertificateChain(newChain).getOrThrow()
|
||||||
Logger.i("Hacked certificate for uid=$callingUid")
|
Logger.i("Hacked certificate for uid=$callingUid")
|
||||||
return createTypedObjectReply(response)
|
return createTypedObjectReply(response)
|
||||||
@@ -167,4 +167,4 @@ object Keystore2Interceptor : BaseKeystoreInterceptor() {
|
|||||||
}
|
}
|
||||||
return Skip
|
return Skip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user