fix(interception): harden daemon against binder stress crashes
BinderInterceptor.onTransact now catches Throwable, preventing any exception on a binder thread from killing the daemon. Adds a global uncaught exception handler as defense in depth. Replace Thread.sleep with TeeLatencySimulator (LockSupport.parkNanos + statistical delay model) for keygen latency, reducing binder thread blocking. Move GeneratedKeyPersistence.save to a background executor to avoid disk I/O on binder threads. Convert force-unwrap parcel reads to safe calls with early returns in onPreTransact/onPostTransact hot paths. Add -DNDEBUG to native release builds to compile out verbose logging from the ioctl hook. Targets G2 (ping overhead) and G10 (stress attestation consistency).
This commit is contained in:
@@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 23)
|
|||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||||
|
|
||||||
# LSPlt configuration
|
# LSPlt configuration
|
||||||
OPTION(LSPLT_BUILD_SHARED OFF)
|
OPTION(LSPLT_BUILD_SHARED OFF)
|
||||||
|
|||||||
@@ -33,8 +33,11 @@ object App {
|
|||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
SystemLogger.info("Welcome to TEESimulator!")
|
SystemLogger.info("Welcome to TEESimulator!")
|
||||||
|
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
|
||||||
|
SystemLogger.error("Uncaught exception on ${thread.name}", throwable)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialize the Android framework environment
|
|
||||||
prepareEnvironment()
|
prepareEnvironment()
|
||||||
// Initialize and start the appropriate keystore interceptors.
|
// Initialize and start the appropriate keystore interceptors.
|
||||||
initializeInterceptors()
|
initializeInterceptors()
|
||||||
|
|||||||
@@ -109,17 +109,17 @@ abstract class BinderInterceptor : Binder() {
|
|||||||
* `handlePostTransact`).
|
* `handlePostTransact`).
|
||||||
*/
|
*/
|
||||||
final override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
|
final override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
|
||||||
// The native hook prepends a transaction ID to the data parcel.
|
|
||||||
val txId = data.readLong()
|
val txId = data.readLong()
|
||||||
val result =
|
val result = try {
|
||||||
when (code) {
|
when (code) {
|
||||||
// These codes are defined in the native layer to distinguish hook types.
|
|
||||||
PRE_TRANSACT_CODE -> handlePreTransact(txId, data)
|
PRE_TRANSACT_CODE -> handlePreTransact(txId, data)
|
||||||
POST_TRANSACT_CODE -> handlePostTransact(txId, data)
|
POST_TRANSACT_CODE -> handlePostTransact(txId, data)
|
||||||
else -> return super.onTransact(code, data, reply, flags)
|
else -> return super.onTransact(code, data, reply, flags)
|
||||||
}
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
// The reply parcel is guaranteed to be non-null for our custom transactions.
|
SystemLogger.error("[TX_ID: $txId] Interceptor exception, falling through to HAL", e)
|
||||||
|
TransactionResult.ContinueAndSkipPost
|
||||||
|
}
|
||||||
writeResultToReply(result, reply!!)
|
writeResultToReply(result, reply!!)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-15
@@ -22,7 +22,9 @@ import java.security.cert.CertificateFactory
|
|||||||
import java.security.spec.PKCS8EncodedKeySpec
|
import java.security.spec.PKCS8EncodedKeySpec
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque
|
import java.util.concurrent.ConcurrentLinkedDeque
|
||||||
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import java.util.concurrent.locks.LockSupport
|
||||||
import org.matrix.TEESimulator.attestation.AttestationBuilder
|
import org.matrix.TEESimulator.attestation.AttestationBuilder
|
||||||
import org.matrix.TEESimulator.attestation.AttestationConstants
|
import org.matrix.TEESimulator.attestation.AttestationConstants
|
||||||
import org.matrix.TEESimulator.attestation.AttestationPatcher
|
import org.matrix.TEESimulator.attestation.AttestationPatcher
|
||||||
@@ -39,6 +41,7 @@ import org.matrix.TEESimulator.pki.KeyBoxManager
|
|||||||
import org.matrix.TEESimulator.pki.NativeCertGen
|
import org.matrix.TEESimulator.pki.NativeCertGen
|
||||||
import org.matrix.TEESimulator.util.AndroidDeviceUtils
|
import org.matrix.TEESimulator.util.AndroidDeviceUtils
|
||||||
import org.matrix.TEESimulator.util.AndroidPermissionUtils
|
import org.matrix.TEESimulator.util.AndroidPermissionUtils
|
||||||
|
import org.matrix.TEESimulator.util.TeeLatencySimulator
|
||||||
|
|
||||||
class KeyMintSecurityLevelInterceptor(
|
class KeyMintSecurityLevelInterceptor(
|
||||||
private val original: IKeystoreSecurityLevel,
|
private val original: IKeystoreSecurityLevel,
|
||||||
@@ -82,7 +85,8 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
|
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
|
||||||
|
|
||||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||||
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
|
?: return TransactionResult.ContinueAndSkipPost
|
||||||
SystemLogger.info(
|
SystemLogger.info(
|
||||||
"[TX_ID: $txId] Forward to post-importKey hook for ${keyDescriptor.alias}[${keyDescriptor.nspace}]"
|
"[TX_ID: $txId] Forward to post-importKey hook for ${keyDescriptor.alias}[${keyDescriptor.nspace}]"
|
||||||
)
|
)
|
||||||
@@ -161,8 +165,10 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
||||||
|
|
||||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||||
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
?: return TransactionResult.SkipTransaction
|
||||||
|
val params = data.createTypedArray(KeyParameter.CREATOR)
|
||||||
|
?: return TransactionResult.SkipTransaction
|
||||||
val parsedParams = KeyMintAttestation(params)
|
val parsedParams = KeyMintAttestation(params)
|
||||||
val forced = data.readBoolean()
|
val forced = data.readBoolean()
|
||||||
if (forced)
|
if (forced)
|
||||||
@@ -170,7 +176,8 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
"[TX_ID: $txId] Current operation has a very high pruning power."
|
"[TX_ID: $txId] Current operation has a very high pruning power."
|
||||||
)
|
)
|
||||||
val response: CreateOperationResponse =
|
val response: CreateOperationResponse =
|
||||||
reply.readTypedObject(CreateOperationResponse.CREATOR)!!
|
reply.readTypedObject(CreateOperationResponse.CREATOR)
|
||||||
|
?: return TransactionResult.SkipTransaction
|
||||||
SystemLogger.verbose(
|
SystemLogger.verbose(
|
||||||
"[TX_ID: $txId] CreateOperationResponse: ${response.iOperation} ${response.operationChallenge}"
|
"[TX_ID: $txId] CreateOperationResponse: ${response.iOperation} ${response.operationChallenge}"
|
||||||
)
|
)
|
||||||
@@ -206,8 +213,10 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
|
|
||||||
// Cache the newly patched chain to ensure consistency across subsequent API calls.
|
// Cache the newly patched chain to ensure consistency across subsequent API calls.
|
||||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||||
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
val key = metadata.key!!
|
?: return TransactionResult.SkipTransaction
|
||||||
|
val key = metadata.key
|
||||||
|
?: return TransactionResult.SkipTransaction
|
||||||
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
||||||
CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow()
|
CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow()
|
||||||
metadata.authorizations =
|
metadata.authorizations =
|
||||||
@@ -547,10 +556,12 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
}
|
}
|
||||||
generatedKeys[keyId] = GeneratedKeyInfo(null, secretKey, keyDescriptor.nspace, response, parsedParams)
|
generatedKeys[keyId] = GeneratedKeyInfo(null, secretKey, keyDescriptor.nspace, response, parsedParams)
|
||||||
|
|
||||||
val elapsedMs = (System.nanoTime() - genStartNanos) / 1_000_000
|
if (securityLevel == SecurityLevel.STRONGBOX) {
|
||||||
val floor = if (securityLevel == SecurityLevel.STRONGBOX) STRONGBOX_KEYGEN_LATENCY_FLOOR_MS else TEE_LATENCY_FLOOR_MS
|
val delayMs = STRONGBOX_KEYGEN_LATENCY_FLOOR_MS - (System.nanoTime() - genStartNanos) / 1_000_000
|
||||||
val delayMs = floor - elapsedMs
|
if (delayMs > 0) LockSupport.parkNanos(delayMs * 1_000_000)
|
||||||
if (delayMs > 0) Thread.sleep(delayMs)
|
} else {
|
||||||
|
TeeLatencySimulator.simulateGenerateKeyDelay(parsedParams.algorithm, System.nanoTime() - genStartNanos)
|
||||||
|
}
|
||||||
|
|
||||||
return InterceptorUtils.createTypedObjectReply(metadata)
|
return InterceptorUtils.createTypedObjectReply(metadata)
|
||||||
}
|
}
|
||||||
@@ -570,12 +581,14 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, null, keyDescriptor.nspace, response, parsedParams)
|
generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, null, keyDescriptor.nspace, response, parsedParams)
|
||||||
if (isAttestKeyRequest) attestationKeys.add(keyId)
|
if (isAttestKeyRequest) attestationKeys.add(keyId)
|
||||||
|
|
||||||
|
val certChainCopy = keyData.second.toList()
|
||||||
|
persistExecutor.execute {
|
||||||
GeneratedKeyPersistence.save(
|
GeneratedKeyPersistence.save(
|
||||||
keyId = keyId,
|
keyId = keyId,
|
||||||
keyPair = keyData.first,
|
keyPair = keyData.first,
|
||||||
nspace = keyDescriptor.nspace,
|
nspace = keyDescriptor.nspace,
|
||||||
securityLevel = securityLevel,
|
securityLevel = securityLevel,
|
||||||
certChain = keyData.second.toList(),
|
certChain = certChainCopy,
|
||||||
algorithm = parsedParams.algorithm,
|
algorithm = parsedParams.algorithm,
|
||||||
keySize = parsedParams.keySize,
|
keySize = parsedParams.keySize,
|
||||||
ecCurve = parsedParams.ecCurve ?: 0,
|
ecCurve = parsedParams.ecCurve ?: 0,
|
||||||
@@ -583,11 +596,14 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
digests = parsedParams.digest,
|
digests = parsedParams.digest,
|
||||||
isAttestationKey = isAttestKeyRequest,
|
isAttestationKey = isAttestKeyRequest,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val elapsedMs = (System.nanoTime() - genStartNanos) / 1_000_000
|
if (securityLevel == SecurityLevel.STRONGBOX) {
|
||||||
val floor = if (securityLevel == SecurityLevel.STRONGBOX) STRONGBOX_KEYGEN_LATENCY_FLOOR_MS else TEE_LATENCY_FLOOR_MS
|
val delayMs = STRONGBOX_KEYGEN_LATENCY_FLOOR_MS - (System.nanoTime() - genStartNanos) / 1_000_000
|
||||||
val delayMs = floor - elapsedMs
|
if (delayMs > 0) LockSupport.parkNanos(delayMs * 1_000_000)
|
||||||
if (delayMs > 0) Thread.sleep(delayMs)
|
} else {
|
||||||
|
TeeLatencySimulator.simulateGenerateKeyDelay(parsedParams.algorithm, System.nanoTime() - genStartNanos)
|
||||||
|
}
|
||||||
|
|
||||||
return InterceptorUtils.createTypedObjectReply(response.metadata)
|
return InterceptorUtils.createTypedObjectReply(response.metadata)
|
||||||
}
|
}
|
||||||
@@ -875,6 +891,8 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
.associate { field -> (field.get(null) as Int) to field.name.split("_")[1] }
|
.associate { field -> (field.get(null) as Int) to field.name.split("_")[1] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val persistExecutor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
val generatedKeys = ConcurrentHashMap<KeyIdentifier, GeneratedKeyInfo>()
|
val generatedKeys = ConcurrentHashMap<KeyIdentifier, GeneratedKeyInfo>()
|
||||||
val teeResponses = ConcurrentHashMap<KeyIdentifier, KeyEntryResponse>()
|
val teeResponses = ConcurrentHashMap<KeyIdentifier, KeyEntryResponse>()
|
||||||
val patchedChains = ConcurrentHashMap<KeyIdentifier, Array<Certificate>>()
|
val patchedChains = ConcurrentHashMap<KeyIdentifier, Array<Certificate>>()
|
||||||
|
|||||||
+2
-1
@@ -9,6 +9,7 @@ import android.hardware.security.keymint.KeyPurpose
|
|||||||
import android.hardware.security.keymint.PaddingMode
|
import android.hardware.security.keymint.PaddingMode
|
||||||
import android.hardware.security.keymint.Tag
|
import android.hardware.security.keymint.Tag
|
||||||
import android.os.ServiceSpecificException
|
import android.os.ServiceSpecificException
|
||||||
|
import java.util.concurrent.locks.LockSupport
|
||||||
import android.system.keystore2.IKeystoreOperation
|
import android.system.keystore2.IKeystoreOperation
|
||||||
import android.system.keystore2.KeyParameters
|
import android.system.keystore2.KeyParameters
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
@@ -275,7 +276,7 @@ class SoftwareOperation(
|
|||||||
if (latencyFloorMs > 0) {
|
if (latencyFloorMs > 0) {
|
||||||
val elapsedMs = (System.nanoTime() - startNs) / 1_000_000
|
val elapsedMs = (System.nanoTime() - startNs) / 1_000_000
|
||||||
val delayMs = latencyFloorMs - elapsedMs
|
val delayMs = latencyFloorMs - elapsedMs
|
||||||
if (delayMs > 0) Thread.sleep(delayMs)
|
if (delayMs > 0) LockSupport.parkNanos(delayMs * 1_000_000)
|
||||||
}
|
}
|
||||||
finalized = true
|
finalized = true
|
||||||
onFinishCallback?.invoke()
|
onFinishCallback?.invoke()
|
||||||
|
|||||||
Reference in New Issue
Block a user