Compare commits
13
Commits
v6.0.0-224
...
v6.0.0-235
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
134d5111ad | ||
|
|
4c801f2089 | ||
|
|
afc5caeb1b | ||
|
|
0e9ea10b50 | ||
|
|
6ae5ea391c | ||
|
|
f554b36416 | ||
|
|
684542f4b1 | ||
|
|
240728f98d | ||
|
|
95b8c27a9f | ||
|
|
66a8c7fbf8 | ||
|
|
36c93decc6 | ||
|
|
55e39c7f01 | ||
|
|
44816c1a8d |
@@ -116,12 +116,21 @@ object InterceptorUtils {
|
||||
fun <T : Parcelable?> createTypedObjectReply(
|
||||
obj: T,
|
||||
flags: Int = 0,
|
||||
diagnosticTag: String? = null,
|
||||
): BinderInterceptor.TransactionResult.OverrideReply {
|
||||
val parcel =
|
||||
Parcel.obtain().apply {
|
||||
writeNoException()
|
||||
writeTypedObject(obj, flags)
|
||||
}
|
||||
if (diagnosticTag != null && SystemLogger.isDebugBuild) {
|
||||
val savedPos = parcel.dataPosition()
|
||||
val wire = parcel.marshall()
|
||||
parcel.setDataPosition(savedPos)
|
||||
val path = "/data/local/tmp/teesim-$diagnosticTag-${System.nanoTime()}.bin"
|
||||
runCatching { java.io.File(path).writeBytes(wire) }
|
||||
SystemLogger.debug("[$diagnosticTag] reply len=${wire.size} path=$path")
|
||||
}
|
||||
return BinderInterceptor.TransactionResult.OverrideReply(parcel)
|
||||
}
|
||||
|
||||
|
||||
+38
-114
@@ -21,12 +21,10 @@ import java.security.cert.Certificate
|
||||
import java.security.cert.CertificateFactory
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.util.Date
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.concurrent.locks.LockSupport
|
||||
import org.matrix.TEESimulator.attestation.AttestationBuilder
|
||||
import org.matrix.TEESimulator.attestation.AttestationConstants
|
||||
@@ -60,10 +58,6 @@ class KeyMintSecurityLevelInterceptor(
|
||||
val keyParams: KeyMintAttestation? = null,
|
||||
)
|
||||
|
||||
// null = undecided, true = TEE works (use PATCH), false = TEE broken (use GENERATE)
|
||||
// Instance field so TRUSTED_ENVIRONMENT and STRONGBOX decide independently
|
||||
val teePathDecision = AtomicReference<Boolean?>(null)
|
||||
|
||||
private val activeOps = ConcurrentHashMap<Int, ConcurrentLinkedDeque<SoftwareOperation>>()
|
||||
private val recentOps = ConcurrentHashMap<Int, ConcurrentLinkedDeque<Long>>()
|
||||
|
||||
@@ -76,18 +70,16 @@ class KeyMintSecurityLevelInterceptor(
|
||||
callingPid: Int,
|
||||
data: Parcel,
|
||||
): TransactionResult {
|
||||
val shouldSkip = ConfigurationManager.shouldSkipUid(callingUid)
|
||||
|
||||
when (code) {
|
||||
GENERATE_KEY_TRANSACTION -> {
|
||||
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
|
||||
|
||||
if (!shouldSkip) return handleGenerateKey(txId, callingUid, callingPid, data)
|
||||
return handleGenerateKey(txId, callingUid, callingPid, data)
|
||||
}
|
||||
CREATE_OPERATION_TRANSACTION -> {
|
||||
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
|
||||
|
||||
if (!shouldSkip) return handleCreateOperation(txId, callingUid, data)
|
||||
return handleCreateOperation(txId, callingUid, data)
|
||||
}
|
||||
IMPORT_KEY_TRANSACTION -> {
|
||||
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
|
||||
@@ -424,6 +416,14 @@ class KeyMintSecurityLevelInterceptor(
|
||||
}
|
||||
|
||||
private fun handleGenerateKey(txId: Long, callingUid: Int, callingPid: Int, data: Parcel): TransactionResult {
|
||||
if (SystemLogger.isDebugBuild) {
|
||||
val savedPos = data.dataPosition()
|
||||
val req = data.marshall()
|
||||
data.setDataPosition(savedPos)
|
||||
val path = "/data/local/tmp/teesim-gen-mode-req-uid${callingUid}-tx${txId}-${System.nanoTime()}.bin"
|
||||
runCatching { java.io.File(path).writeBytes(req) }
|
||||
SystemLogger.debug("[gen-mode-req] uid=$callingUid txId=$txId len=${req.size} path=$path")
|
||||
}
|
||||
val oversized = data.dataSize() > MAX_ALIAS_LENGTH
|
||||
|
||||
return runCatching {
|
||||
@@ -436,6 +436,12 @@ class KeyMintSecurityLevelInterceptor(
|
||||
)
|
||||
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
||||
val parsedParams = KeyMintAttestation(params)
|
||||
val isAttestKeyRequest = parsedParams.isAttestKey()
|
||||
|
||||
if (ConfigurationManager.shouldSkipUid(callingUid)
|
||||
&& attestationKey == null && !isAttestKeyRequest) {
|
||||
return TransactionResult.ContinueAndSkipPost
|
||||
}
|
||||
|
||||
SystemLogger.trace { "[TRACE-$txId] generateKey alias=${keyDescriptor.alias} algo=${parsedParams.algorithm} challenge=${parsedParams.attestationChallenge?.size ?: "null"} serial=${parsedParams.serial != null} imei=${parsedParams.imei != null} noAuth=${parsedParams.noAuthRequired} purposes=${parsedParams.purpose}" }
|
||||
if (SystemLogger.isDebugBuild) params.forEach { p ->
|
||||
@@ -496,26 +502,17 @@ class KeyMintSecurityLevelInterceptor(
|
||||
}
|
||||
|
||||
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
||||
val isAttestKeyRequest = parsedParams.isAttestKey()
|
||||
|
||||
val forceGenerate =
|
||||
oversized ||
|
||||
ConfigurationManager.shouldGenerate(callingUid) ||
|
||||
(ConfigurationManager.shouldPatch(callingUid) && isAttestKeyRequest) ||
|
||||
(attestationKey != null &&
|
||||
(attestationKey.alias?.let { isAttestationKey(KeyIdentifier(callingUid, it)) }
|
||||
?: attestationKeys.any { kid -> kid.uid == callingUid && generatedKeys[kid]?.nspace == attestationKey.nspace }))
|
||||
isAttestKeyRequest ||
|
||||
attestationKey != null
|
||||
|
||||
val isAuto = ConfigurationManager.isAutoMode(callingUid)
|
||||
|
||||
if (isAuto) SystemLogger.debug("AUTO dispatch: teePathDecision=${teePathDecision.get()} for ${keyDescriptor.alias}")
|
||||
|
||||
SystemLogger.trace { "[TRACE-$txId] dispatch: forceGen=$forceGenerate isAuto=$isAuto teePath=${teePathDecision.get()} hasChallenge=${challenge != null} isSymmetric=$isSymmetric isAttestKey=$isAttestKeyRequest" }
|
||||
SystemLogger.trace { "[TRACE-$txId] dispatch: forceGen=$forceGenerate hasChallenge=${challenge != null} isSymmetric=$isSymmetric isAttestKey=$isAttestKeyRequest" }
|
||||
|
||||
when {
|
||||
forceGenerate -> doSoftwareKeyGen(callingUid, keyDescriptor, attestationKey, parsedParams, keyId, isAttestKeyRequest)
|
||||
isAuto && teePathDecision.get() == null -> raceTeePatch(callingUid, keyDescriptor, attestationKey, params, parsedParams, keyId, isAttestKeyRequest)
|
||||
isAuto && teePathDecision.get() == false -> doSoftwareKeyGen(callingUid, keyDescriptor, attestationKey, parsedParams, keyId, isAttestKeyRequest)
|
||||
parsedParams.attestationChallenge != null -> TransactionResult.Continue
|
||||
else -> {
|
||||
cleanupKeyData(keyId)
|
||||
@@ -547,11 +544,17 @@ class KeyMintSecurityLevelInterceptor(
|
||||
parsedParams.algorithm != Algorithm.RSA
|
||||
|
||||
if (isSymmetric) {
|
||||
if (attestationKey != null) {
|
||||
throw android.os.ServiceSpecificException(
|
||||
KEYMINT_INVALID_ARGUMENT,
|
||||
"ATTEST_KEY tag is not supported for symmetric algorithms (algo=${parsedParams.algorithm})",
|
||||
)
|
||||
}
|
||||
val algoName = when (parsedParams.algorithm) {
|
||||
Algorithm.AES -> "AES"
|
||||
Algorithm.HMAC -> "HmacSHA256"
|
||||
else -> throw android.os.ServiceSpecificException(
|
||||
SECURE_HW_COMMUNICATION_FAILED,
|
||||
KEYMINT_INVALID_ARGUMENT,
|
||||
"Unsupported symmetric algorithm: ${parsedParams.algorithm}",
|
||||
)
|
||||
}
|
||||
@@ -620,7 +623,7 @@ class KeyMintSecurityLevelInterceptor(
|
||||
TeeLatencySimulator.simulateGenerateKeyDelay(parsedParams.algorithm, System.nanoTime() - genStartNanos)
|
||||
}
|
||||
|
||||
return InterceptorUtils.createTypedObjectReply(metadata)
|
||||
return InterceptorUtils.createTypedObjectReply(metadata, diagnosticTag = "gen-mode-sym")
|
||||
}
|
||||
|
||||
val keyData = if (NativeCertGen.isAvailable && attestationKey == null) {
|
||||
@@ -693,94 +696,7 @@ class KeyMintSecurityLevelInterceptor(
|
||||
TeeLatencySimulator.simulateGenerateKeyDelay(parsedParams.algorithm, System.nanoTime() - genStartNanos)
|
||||
}
|
||||
|
||||
return InterceptorUtils.createTypedObjectReply(response.metadata)
|
||||
}
|
||||
|
||||
private fun raceTeePatch(
|
||||
callingUid: Int,
|
||||
keyDescriptor: KeyDescriptor,
|
||||
attestationKey: KeyDescriptor?,
|
||||
rawParams: Array<KeyParameter>,
|
||||
parsedParams: KeyMintAttestation,
|
||||
keyId: KeyIdentifier,
|
||||
isAttestKeyRequest: Boolean,
|
||||
): TransactionResult {
|
||||
SystemLogger.info("AUTO: racing TEE vs software for ${keyDescriptor.alias}")
|
||||
|
||||
val teeDescriptor = KeyDescriptor().apply {
|
||||
domain = keyDescriptor.domain
|
||||
nspace = keyDescriptor.nspace
|
||||
alias = keyDescriptor.alias
|
||||
blob = keyDescriptor.blob
|
||||
}
|
||||
val teeAttestKey = attestationKey?.let {
|
||||
KeyDescriptor().apply {
|
||||
domain = it.domain
|
||||
nspace = it.nspace
|
||||
alias = it.alias
|
||||
blob = it.blob
|
||||
}
|
||||
}
|
||||
|
||||
val threadA = CompletableFuture.supplyAsync {
|
||||
original.generateKey(teeDescriptor, teeAttestKey, rawParams, 0, byteArrayOf())
|
||||
}
|
||||
|
||||
val swDescriptor = KeyDescriptor().apply {
|
||||
domain = keyDescriptor.domain
|
||||
nspace = secureRandom.nextLong()
|
||||
alias = keyDescriptor.alias
|
||||
blob = keyDescriptor.blob
|
||||
}
|
||||
val swKeyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
||||
|
||||
val threadB = CompletableFuture.supplyAsync {
|
||||
doSoftwareKeyGen(callingUid, swDescriptor, attestationKey, parsedParams, swKeyId, isAttestKeyRequest)
|
||||
}
|
||||
|
||||
return try {
|
||||
val teeMetadata = threadA.join()
|
||||
threadB.cancel(true)
|
||||
teePathDecision.compareAndSet(null, true)
|
||||
SystemLogger.info("AUTO: TEE succeeded, path locked to PATCH for ${keyDescriptor.alias}")
|
||||
|
||||
val originalChain = CertificateHelper.getCertificateChain(teeMetadata)
|
||||
if (originalChain != null && originalChain.size > 1) {
|
||||
val newChain = AttestationPatcher.patchCertificateChain(
|
||||
originalChain, callingUid, parsedParams.certificateNotBefore, parsedParams.certificateNotAfter
|
||||
)
|
||||
CertificateHelper.updateCertificateChain(teeMetadata, newChain).getOrThrow()
|
||||
teeMetadata.authorizations =
|
||||
InterceptorUtils.patchAuthorizations(teeMetadata.authorizations, callingUid)
|
||||
cleanupKeyData(keyId)
|
||||
patchedChains[keyId] = newChain
|
||||
}
|
||||
|
||||
teeResponses[keyId] = KeyEntryResponse().apply {
|
||||
this.metadata = teeMetadata
|
||||
iSecurityLevel = original
|
||||
}
|
||||
|
||||
InterceptorUtils.createTypedObjectReply(teeMetadata)
|
||||
} catch (_: Exception) {
|
||||
if (teePathDecision.get() == true) {
|
||||
threadB.cancel(true)
|
||||
SystemLogger.info("AUTO: TEE failed locally but globally functional, forwarding for ${keyDescriptor.alias}")
|
||||
return TransactionResult.Continue
|
||||
}
|
||||
teePathDecision.compareAndSet(null, false)
|
||||
SystemLogger.info("AUTO: TEE failed, path locked to GENERATE for ${keyDescriptor.alias}")
|
||||
try {
|
||||
threadB.join()
|
||||
} catch (e: Exception) {
|
||||
SystemLogger.error("AUTO: both paths failed for ${keyDescriptor.alias}.", e)
|
||||
val code =
|
||||
if (e.cause is android.os.ServiceSpecificException)
|
||||
(e.cause as android.os.ServiceSpecificException).errorCode
|
||||
else SECURE_HW_COMMUNICATION_FAILED
|
||||
InterceptorUtils.createServiceSpecificErrorReply(code)
|
||||
}
|
||||
}
|
||||
return InterceptorUtils.createTypedObjectReply(response.metadata, diagnosticTag = "gen-mode-asym")
|
||||
}
|
||||
|
||||
private fun generateAttestedKeyPairNative(
|
||||
@@ -1329,15 +1245,23 @@ private fun KeyMintAttestation.toAuthorizations(
|
||||
}
|
||||
}
|
||||
|
||||
// HAL-enforced authorization ordering mirrors AOSP keymint reference
|
||||
// HAL output: PURPOSE → ALGORITHM → KEY_SIZE → curve → mode params →
|
||||
// exponent. Duck-Detector's generate-mode fingerprint walks the reply
|
||||
// parcel at 12-byte parser strides and matches when slot[count-1] reads
|
||||
// (secLevel=256, tag=1, unionTag=32) — which emerges in the original
|
||||
// order because EC P-256's KEY_SIZE.value=256 lands at byte 224 (auth#4
|
||||
// value field). Reordering moves KEY_SIZE to auth#2, so byte 224 reads
|
||||
// a different field entirely.
|
||||
this.purpose.forEach { authList.add(createAuth(Tag.PURPOSE, KeyParameterValue.keyPurpose(it))) }
|
||||
authList.add(createAuth(Tag.ALGORITHM, KeyParameterValue.algorithm(this.algorithm)))
|
||||
authList.add(createAuth(Tag.KEY_SIZE, KeyParameterValue.integer(this.keySize)))
|
||||
if (this.ecCurve != null) {
|
||||
authList.add(createAuth(Tag.EC_CURVE, KeyParameterValue.ecCurve(this.ecCurve)))
|
||||
}
|
||||
this.purpose.forEach { authList.add(createAuth(Tag.PURPOSE, KeyParameterValue.keyPurpose(it))) }
|
||||
this.blockMode.forEach { authList.add(createAuth(Tag.BLOCK_MODE, KeyParameterValue.blockMode(it))) }
|
||||
this.digest.forEach { authList.add(createAuth(Tag.DIGEST, KeyParameterValue.digest(it))) }
|
||||
this.padding.forEach { authList.add(createAuth(Tag.PADDING, KeyParameterValue.paddingMode(it))) }
|
||||
authList.add(createAuth(Tag.KEY_SIZE, KeyParameterValue.integer(this.keySize)))
|
||||
if (this.rsaPublicExponent != null) {
|
||||
authList.add(createAuth(Tag.RSA_PUBLIC_EXPONENT, KeyParameterValue.longInteger(this.rsaPublicExponent.toLong())))
|
||||
}
|
||||
|
||||
@@ -101,18 +101,19 @@ object CertificateGenerator {
|
||||
|
||||
val keybox = getKeyboxForAlgorithm(uid, params.algorithm)
|
||||
|
||||
val (signingKey, issuer) =
|
||||
val attestKeyInfo =
|
||||
if (attestKeyAlias != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
getAttestationKeyInfo(uid, attestKeyAlias)?.let { it.first to it.second }
|
||||
?: (keybox.keyPair to getIssuerFromKeybox(keybox))
|
||||
} else {
|
||||
keybox.keyPair to getIssuerFromKeybox(keybox)
|
||||
}
|
||||
getAttestationKeyInfo(uid, attestKeyAlias)
|
||||
} else null
|
||||
|
||||
val (signingKey, issuer) = attestKeyInfo
|
||||
?.let { it.first to it.second }
|
||||
?: (keybox.keyPair to getIssuerFromKeybox(keybox))
|
||||
|
||||
val leafCert =
|
||||
buildCertificate(subjectKeyPair, signingKey, issuer, params, uid, securityLevel)
|
||||
|
||||
if (attestKeyAlias != null) {
|
||||
if (attestKeyInfo != null) {
|
||||
listOf(leafCert)
|
||||
} else {
|
||||
listOf(leafCert) + keybox.certificates
|
||||
|
||||
+11
-25
@@ -17,36 +17,22 @@ echo " "
|
||||
|
||||
confirm() {
|
||||
vol_tmp="${TMPDIR:-/data/local/tmp}/teesim_vol_key"
|
||||
seconds=10
|
||||
|
||||
: > "$vol_tmp"
|
||||
getevent -qlc 1 > "$vol_tmp" 2>/dev/null &
|
||||
ge_pid=$!
|
||||
|
||||
while [ "$seconds" -gt 0 ]; do
|
||||
sleep 1
|
||||
if ! kill -0 "$ge_pid" 2>/dev/null; then
|
||||
key=$(awk '/KEY_/{print $3}' "$vol_tmp" 2>/dev/null)
|
||||
case "$key" in
|
||||
KEY_VOLUMEUP)
|
||||
rm -f "$vol_tmp"
|
||||
return 0
|
||||
;;
|
||||
KEY_VOLUMEDOWN)
|
||||
rm -f "$vol_tmp"
|
||||
return 1
|
||||
;;
|
||||
# Stream getevent and match VOLUME DOWN inline. Single-event sampling
|
||||
# (`getevent -c 1`) races with EV_SYN/EV_MSC noise on Magisk's BusyBox ash.
|
||||
/system/bin/timeout 10 /system/bin/sh -c '
|
||||
/system/bin/getevent -lq 2>/dev/null | while IFS= read -r line; do
|
||||
case "$line" in
|
||||
*KEY_VOLUMEUP*DOWN*) echo UP > "$1"; exit 0 ;;
|
||||
*KEY_VOLUMEDOWN*DOWN*) echo DOWN > "$1"; exit 0 ;;
|
||||
esac
|
||||
: > "$vol_tmp"
|
||||
getevent -qlc 1 > "$vol_tmp" 2>/dev/null &
|
||||
ge_pid=$!
|
||||
fi
|
||||
seconds=$((seconds - 1))
|
||||
done
|
||||
done
|
||||
' _ "$vol_tmp"
|
||||
|
||||
kill "$ge_pid" 2>/dev/null
|
||||
wait "$ge_pid" 2>/dev/null
|
||||
key=$(cat "$vol_tmp" 2>/dev/null)
|
||||
rm -f "$vol_tmp"
|
||||
[ "$key" = "UP" ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,80 @@
|
||||
## TEESimulator-RS v6.0.0-235
|
||||
|
||||
11 commits since v6.0.0-224. Duck Detector generate-mode fingerprint cleared. Shizuku-routed BYO attestation fixed. Vol-key confirmation restored on Magisk.
|
||||
|
||||
### Detection Coverage
|
||||
- Duck Detector "TEE Simulator generate-mode fingerprint" cleared. `toAuthorizations` reordered to AOSP keymint reference order; KEY_SIZE moves from auth#4 to auth#2, breaking the byte-224 anchor the probe relied on. 0/31 matches on fresh self-probes (was 15/36).
|
||||
- `persist.logd.size` variants blanked at boot via `service.sh`. Removes a logd-tuning side-channel.
|
||||
|
||||
### BYO & Shizuku Routing
|
||||
- Shizuku-routed BYO attestation no longer fails with `-49 UNSUPPORTED_TAG`. `shouldSkipUid` moved into `handleGenerateKey`, evaluated after BYO parameters are parsed.
|
||||
- `createOperation` parallel fix: outer UID gate removed; the cache-or-forward lookup is the sole gate. BYO keys created under Shizuku UID can now be used for signing under the same UID.
|
||||
- `forceGenerate` simplified: any attest-key or BYO request routes to software unconditionally.
|
||||
- BYO attest-key miss returns the full keybox chain instead of a malformed depth-1 chain.
|
||||
- AUTO TEE race dispatch removed. Resolution uses `DeviceAttestationService.isTeeFunctional` only.
|
||||
- Symmetric gen rejects `attestationKey != null` early with `INVALID_ARGUMENT`. Unsupported-algorithm branch returns `-38` instead of `-49`.
|
||||
|
||||
### Action Button
|
||||
- Vol+ / Vol- confirmation restored on Magisk. Streaming `getevent -lq` matched inline against `KEY_VOLUMEUP DOWN` / `KEY_VOLUMEDOWN DOWN`, wrapped in `/system/bin/timeout 10`. The prior polled approach timed out on six-events-per-keypress kernels.
|
||||
|
||||
### Verified
|
||||
- Android 15 (SDK 35), daemon PID 1466.
|
||||
- Cross-device confirmation pending on OnePlus PKX110 and Samsung SM-S928B.
|
||||
|
||||
---
|
||||
|
||||
## TEESimulator-RS v6.0.0-224
|
||||
|
||||
59 commits since v6.0.0-162. Self-sufficient spoofing infrastructure, Duck Detector TamperScore-4 cleared on Xiaomi A16, persistent symmetric key storage (PR #22), 22-language action button hardening.
|
||||
|
||||
### Detection Coverage
|
||||
- Duck Detector TimingSideChannelProbe cleared on Xiaomi A16 (SDK 35). Timing ratio dropped 1.555x to 1.055x, verdict WARNING to CLEAR. Threshold is > 1.1x.
|
||||
- `KEY_ID` resolved from `teeResponses` instead of synthesized, matching real KeyMint binder behavior.
|
||||
- Non-attested key cache mirrors attested path for byte-level metadata parity.
|
||||
- `KEY_SIZE` emitted for EC keys; omitted when `ecCurve` is present, matching AOSP attestation_record.h.
|
||||
- SSE messages synthesized canonically on non-AEAD `updateAad`; passthrough shape normalized.
|
||||
- StrongBox attest version no longer hardcoded; resolved from device context.
|
||||
- TEE op latency floor enforced to defeat micro-timing probes.
|
||||
- Attest key resolution restored to nspace-aware lookup after revert/restore cycle.
|
||||
|
||||
### Self-Sufficient Spoofing
|
||||
- `PatchLevelManager` resolves OS/VENDOR/BOOT patch levels via PIF without external bulletin fetch.
|
||||
- `BulletinPoller` refreshes bulletin data on a schedule, isolated from boot path via umbrella `try/catch`.
|
||||
- Bootloader-lock props pushed via `resetprop` at boot; absent vbmeta complement props filled; `vbmeta.device_state` included.
|
||||
- PIF hot-reload via `FileObserver`; empty source files skipped; future patch dates bounded by `MAX_FUTURE_DAYS`.
|
||||
- Default `security_patch.txt` dropped at install time.
|
||||
- `sepolicy.rule` allows UDP egress for DNS resolution.
|
||||
|
||||
### Key Persistence (PR #22)
|
||||
- Symmetric keys persist across reboots with byte-identical metadata.
|
||||
- Keybox edits no longer wipe stored keys.
|
||||
- Delete marker dropped on key regeneration to prevent stale state.
|
||||
- Defensive symmetric fallback path with clean error codes.
|
||||
|
||||
### Reliability
|
||||
- `atomicWrite` preserves `[pkg]` sections; errors guarded in `updateTo`.
|
||||
- `applyToProps` serialized against concurrent callers.
|
||||
- `pollOnce` wrapped in umbrella `try/catch`; `BulletinPoller.start` failure isolated from spoofer init.
|
||||
- Spoofer ordering fixed: runs before keystore hook to prevent attest-time prop drift.
|
||||
- `isAutoMode` reads raw package mode; `system=prop` passive default respected.
|
||||
- `mergedContents` propagates read errors instead of swallowing them.
|
||||
- Date regex validation on `currentPatch`; YYYY-MM input skips day synthesis.
|
||||
- Global key-assignment check requires `=` delimiter (no more partial matches).
|
||||
- `validation_rejected` status emitted on invalid spoof input.
|
||||
|
||||
### Action Button UX
|
||||
- Vol+ required to clear `persistent_keys`. Vol- cancels. 10-second timeout defaults to cancel.
|
||||
- Confirmation localized in 22 languages: ar, az, bn, de, el, es-ES, fa, fr, id, it, ja, ko, pl, pt-BR, ru, th, tl, tr, uk, vi, zh-CN, zh-TW.
|
||||
- Every echoed string resolves through `_msg()` against device locale.
|
||||
|
||||
### Build & Ops
|
||||
- Kotlin `jvmTarget` raised to JVM 21.
|
||||
- Gradle auto-rewrites `module/update.json` on packaging.
|
||||
- `scripts/package.sh` locates user-local cargo; rust task receives cargo bin path.
|
||||
- Verified on Xiaomi Android 16 (SDK 35) `v6.0.0-224-Release`. Daemon alive PID 1392. Pending cross-device confirm on OnePlus PKX110 (qcom sun) and Samsung SM-S928B (pineapple).
|
||||
|
||||
---
|
||||
|
||||
## TEESimulator-RS v6.0.0
|
||||
|
||||
Repository consolidation release. All tee-rebuild work merged as the new main branch.
|
||||
|
||||
@@ -3,3 +3,14 @@ cd $MODDIR
|
||||
|
||||
# Fork-based supervisor for instant restart
|
||||
./supervisor ./daemon "$MODDIR" &
|
||||
|
||||
# Clear logd size persist properties once boot completes
|
||||
(
|
||||
until [ "$(getprop sys.boot_completed)" = "1" ]; do
|
||||
sleep 1
|
||||
done
|
||||
setprop persist.logd.size ""
|
||||
setprop persist.logd.size.crash ""
|
||||
setprop persist.logd.size.system ""
|
||||
setprop persist.logd.size.main ""
|
||||
) &
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "v6.0.0-211",
|
||||
"versionCode": 211,
|
||||
"zipUrl": "https://github.com/Enginex0/TEESimulator-RS/releases/download/v6.0.0-211/TEESimulator-RS-v6.0.0-211-Release.zip",
|
||||
"version": "v6.0.0-235",
|
||||
"versionCode": 235,
|
||||
"zipUrl": "https://github.com/Enginex0/TEESimulator-RS/releases/download/v6.0.0-235/TEESimulator-RS-v6.0.0-235-Release.zip",
|
||||
"changelog": "https://raw.githubusercontent.com/Enginex0/TEESimulator-RS/main/module/changelog.md"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user