@@ -8,6 +8,7 @@ import android.os.IBinder
88import android.os.Parcel
99import android.system.keystore2.*
1010import java.security.KeyPair
11+ import java.security.SecureRandom
1112import java.security.cert.Certificate
1213import java.util.concurrent.ConcurrentHashMap
1314import org.matrix.TEESimulator.attestation.AttestationPatcher
@@ -30,7 +31,11 @@ class KeyMintSecurityLevelInterceptor(
3031) : BinderInterceptor() {
3132
3233 // --- Data Structures for State Management ---
33- data class GeneratedKeyInfo (val keyPair : KeyPair , val response : KeyEntryResponse )
34+ data class GeneratedKeyInfo (
35+ val keyPair : KeyPair ,
36+ val nspace : Long ,
37+ val response : KeyEntryResponse ,
38+ )
3439
3540 override fun onPreTransact (
3641 txId : Long ,
@@ -41,33 +46,33 @@ class KeyMintSecurityLevelInterceptor(
4146 callingPid : Int ,
4247 data : Parcel ,
4348 ): TransactionResult {
44- if (code == GENERATE_KEY_TRANSACTION ) {
45- logTransaction(txId, transactionNames[code]!! , callingUid, callingPid)
49+ val shouldSkip = ConfigurationManager .shouldSkipUid(callingUid)
4650
47- if (ConfigurationManager .shouldSkipUid(callingUid))
48- return TransactionResult .ContinueAndSkipPost
49- data.enforceInterface(IKeystoreSecurityLevel .DESCRIPTOR )
50- return handleGenerateKey(callingUid, data)
51- } else if (code == IMPORT_KEY_TRANSACTION ) {
52- logTransaction(txId, transactionNames[code]!! , callingUid, callingPid)
51+ when (code) {
52+ GENERATE_KEY_TRANSACTION -> {
53+ logTransaction(txId, transactionNames[code]!! , callingUid, callingPid)
5354
54- if (ConfigurationManager .shouldSkipUid(callingUid))
55- return TransactionResult .ContinueAndSkipPost
56- data.enforceInterface(IKeystoreSecurityLevel .DESCRIPTOR )
57- val alias =
58- data.readTypedObject(KeyDescriptor .CREATOR )?.alias
59- ? : return TransactionResult .ContinueAndSkipPost
60- SystemLogger .info(" Handling post-${transactionNames[code]} ${alias} " )
61- return TransactionResult .Continue
62- } else {
63- logTransaction(
64- txId,
65- transactionNames[code] ? : " unknown code=$code " ,
66- callingUid,
67- callingPid,
68- true ,
69- )
55+ if (! shouldSkip) return handleGenerateKey(callingUid, data)
56+ }
57+ CREATE_OPERATION_TRANSACTION -> {
58+ logTransaction(txId, transactionNames[code]!! , callingUid, callingPid)
59+
60+ if (! shouldSkip) return handleCreateOperation(txId, callingUid, data)
61+ }
62+ IMPORT_KEY_TRANSACTION -> {
63+ logTransaction(txId, transactionNames[code]!! , callingUid, callingPid)
64+
65+ data.enforceInterface(IKeystoreSecurityLevel .DESCRIPTOR )
66+ val keyDescriptor = data.readTypedObject(KeyDescriptor .CREATOR )!!
67+ SystemLogger .info(
68+ " [TX_ID: $txId ] Forward to post-importKey hook for ${keyDescriptor.alias} [${keyDescriptor.nspace} ]"
69+ )
70+ TransactionResult .Continue
71+ }
7072 }
73+
74+ logTransaction(txId, transactionNames[code] ? : " unknown code=$code " , callingUid, callingPid, true )
75+
7176 return TransactionResult .ContinueAndSkipPost
7277 }
7378
@@ -94,6 +99,41 @@ class KeyMintSecurityLevelInterceptor(
9499 data.readTypedObject(KeyDescriptor .CREATOR )
95100 ? : return TransactionResult .SkipTransaction
96101 cleanupKeyData(KeyIdentifier (callingUid, keyDescriptor.alias))
102+ } else if (code == CREATE_OPERATION_TRANSACTION ) {
103+ logTransaction(txId, " post-${transactionNames[code]!! } " , callingUid, callingPid)
104+
105+ data.enforceInterface(IKeystoreSecurityLevel .DESCRIPTOR )
106+ val keyDescriptor = data.readTypedObject(KeyDescriptor .CREATOR )!!
107+ val params = data.createTypedArray(KeyParameter .CREATOR )!!
108+ val parsedParams = KeyMintAttestation (params)
109+ val forced = data.readBoolean()
110+ if (forced)
111+ SystemLogger .verbose(
112+ " [TX_ID: $txId ] Current operation has a very high pruning power."
113+ )
114+ val response: CreateOperationResponse =
115+ reply.readTypedObject(CreateOperationResponse .CREATOR )!!
116+ SystemLogger .verbose(
117+ " [TX_ID: $txId ] CreateOperationResponse: ${response.iOperation} ${response.operationChallenge} "
118+ )
119+
120+ // Intercept the IKeystoreOperation binder
121+ response.iOperation?.let { operation ->
122+ val operationBinder = operation.asBinder()
123+ if (! interceptedOperations.containsKey(operationBinder)) {
124+ SystemLogger .info(" Found new IKeystoreOperation. Registering interceptor..." )
125+ val backdoor = getBackdoor(target)
126+ if (backdoor != null ) {
127+ val interceptor = OperationInterceptor (operation, backdoor)
128+ register(backdoor, operationBinder, interceptor)
129+ interceptedOperations[operationBinder] = interceptor
130+ } else {
131+ SystemLogger .error(
132+ " Failed to get backdoor to register OperationInterceptor."
133+ )
134+ }
135+ }
136+ }
97137 } else if (code == GENERATE_KEY_TRANSACTION ) {
98138 logTransaction(txId, " post-${transactionNames[code]!! } " , callingUid, callingPid)
99139
@@ -109,9 +149,12 @@ class KeyMintSecurityLevelInterceptor(
109149 // Cache the newly patched chain to ensure consistency across subsequent API calls.
110150 data.enforceInterface(IKeystoreSecurityLevel .DESCRIPTOR )
111151 val keyDescriptor = data.readTypedObject(KeyDescriptor .CREATOR )!!
152+ val key = metadata.key!!
112153 val keyId = KeyIdentifier (callingUid, keyDescriptor.alias)
113154 patchedChains[keyId] = newChain
114- SystemLogger .debug(" Cached patched certificate chain for $keyId ." )
155+ SystemLogger .debug(
156+ " Cached patched certificate chain for $keyId . (${key.alias} [${key.domain} , ${key.nspace} ])"
157+ )
115158
116159 CertificateHelper .updateCertificateChain(metadata, newChain).getOrThrow()
117160
@@ -121,12 +164,59 @@ class KeyMintSecurityLevelInterceptor(
121164 return TransactionResult .SkipTransaction
122165 }
123166
167+ /* *
168+ * Handles the `createOperation` transaction. It checks if the operation is for a key that was
169+ * generated in software. If so, it creates a software-based operation handler. Otherwise, it
170+ * lets the call proceed to the real hardware service.
171+ */
172+ private fun handleCreateOperation (
173+ txId : Long ,
174+ callingUid : Int ,
175+ data : Parcel ,
176+ ): TransactionResult {
177+ data.enforceInterface(IKeystoreSecurityLevel .DESCRIPTOR )
178+ val keyDescriptor = data.readTypedObject(KeyDescriptor .CREATOR )!!
179+
180+ // An operation must use the KEY_ID domain.
181+ if (keyDescriptor.domain != Domain .KEY_ID ) {
182+ return TransactionResult .ContinueAndSkipPost
183+ }
184+
185+ val nspace = keyDescriptor.nspace
186+ val generatedKeyInfo = findGeneratedKeyByKeyId(callingUid, nspace)
187+
188+ if (generatedKeyInfo == null ) {
189+ SystemLogger .debug(
190+ " [TX_ID: $txId ] Operation for unknown/hardware KeyId ($nspace ). Forwarding."
191+ )
192+ return TransactionResult .Continue
193+ }
194+
195+ SystemLogger .info(" [TX_ID: $txId ] Creating SOFTWARE operation for KeyId $nspace ." )
196+
197+ val params = data.createTypedArray(KeyParameter .CREATOR )!!
198+ val parsedParams = KeyMintAttestation (params)
199+
200+ val softwareOperation =
201+ SoftwareOperation (txId, generatedKeyInfo.keyPair.private, parsedParams)
202+ val operationBinder = SoftwareOperationBinder (softwareOperation)
203+
204+ val response =
205+ CreateOperationResponse ().apply {
206+ iOperation = operationBinder
207+ operationChallenge = null
208+ }
209+
210+ return InterceptorUtils .createTypedObjectReply(response, 0 )
211+ }
212+
124213 /* *
125214 * Handles the `generateKey` transaction. Based on the configuration for the calling UID, it
126215 * either generates a key in software or lets the call pass through to the hardware.
127216 */
128217 private fun handleGenerateKey (callingUid : Int , data : Parcel ): TransactionResult {
129218 return runCatching {
219+ data.enforceInterface(IKeystoreSecurityLevel .DESCRIPTOR )
130220 val keyDescriptor = data.readTypedObject(KeyDescriptor .CREATOR )!!
131221 val attestationKey = data.readTypedObject(KeyDescriptor .CREATOR )
132222 SystemLogger .debug(
@@ -148,7 +238,10 @@ class KeyMintSecurityLevelInterceptor(
148238 isAttestationKey(KeyIdentifier (callingUid, attestationKey.alias)))
149239
150240 if (needsSoftwareGeneration) {
151- SystemLogger .info(" Generating software key for ${keyId} ." )
241+ keyDescriptor.nspace = secureRandom.nextLong()
242+ SystemLogger .info(
243+ " Generating software key for ${keyDescriptor.alias} [${keyDescriptor.nspace} ]."
244+ )
152245
153246 // Generate the key pair and certificate chain.
154247 val keyData =
@@ -164,16 +257,12 @@ class KeyMintSecurityLevelInterceptor(
164257 val response =
165258 buildKeyEntryResponse(keyData.second, parsedParams, keyDescriptor)
166259
167- generatedKeys[keyId] = GeneratedKeyInfo (keyData.first, response)
260+ generatedKeys[keyId] =
261+ GeneratedKeyInfo (keyData.first, keyDescriptor.nspace, response)
168262 if (isAttestKeyRequest) attestationKeys.add(keyId)
169263
170264 // Return the metadata of our generated key, skipping the real hardware call.
171- val resultParcel =
172- Parcel .obtain().apply {
173- writeNoException()
174- writeTypedObject(response.metadata, 0 )
175- }
176- return TransactionResult .OverrideReply (0 , resultParcel)
265+ return InterceptorUtils .createTypedObjectReply(response.metadata, 0 )
177266 } else if (parsedParams.attestationChallenge != null ) {
178267 return TransactionResult .Continue
179268 }
@@ -210,11 +299,18 @@ class KeyMintSecurityLevelInterceptor(
210299 }
211300
212301 companion object {
302+ private val secureRandom = SecureRandom ()
303+
213304 // Transaction codes for IKeystoreSecurityLevel interface.
214305 private val GENERATE_KEY_TRANSACTION =
215306 InterceptorUtils .getTransactCode(IKeystoreSecurityLevel .Stub ::class .java, " generateKey" )
216307 private val IMPORT_KEY_TRANSACTION =
217308 InterceptorUtils .getTransactCode(IKeystoreSecurityLevel .Stub ::class .java, " importKey" )
309+ private val CREATE_OPERATION_TRANSACTION =
310+ InterceptorUtils .getTransactCode(
311+ IKeystoreSecurityLevel .Stub ::class .java,
312+ " createOperation" ,
313+ )
218314
219315 private val transactionNames: Map <Int , String > by lazy {
220316 IKeystoreSecurityLevel .Stub ::class
@@ -233,11 +329,30 @@ class KeyMintSecurityLevelInterceptor(
233329 private val patchedChains = ConcurrentHashMap <KeyIdentifier , Array <Certificate >>()
234330 // A set to quickly identify keys that were generated for attestation purposes.
235331 private val attestationKeys = ConcurrentHashMap .newKeySet<KeyIdentifier >()
332+ // Stores interceptors for active cryptographic operations.
333+ private val interceptedOperations = ConcurrentHashMap <IBinder , OperationInterceptor >()
236334
237335 // --- Public Accessors for Other Interceptors ---
238336 fun getGeneratedKeyResponse (keyId : KeyIdentifier ): KeyEntryResponse ? =
239337 generatedKeys[keyId]?.response
240338
339+ /* *
340+ * Finds a software-generated key by first filtering all known keys by the caller's UID, and
341+ * then matching the specific nspace.
342+ *
343+ * @param callingUid The UID of the process that initiated the createOperation call.
344+ * @param nspace The unique key identifier from the operation's KeyDescriptor.
345+ * @return The matching GeneratedKeyInfo if found, otherwise null.
346+ */
347+ private fun findGeneratedKeyByKeyId (callingUid : Int , nspace : Long ): GeneratedKeyInfo ? {
348+ // Iterate through all entries in the map to check both the key (for UID) and value (for
349+ // nspace).
350+ return generatedKeys.entries
351+ .filter { (keyIdentifier, _) -> keyIdentifier.uid == callingUid }
352+ .find { (_, info) -> info.nspace == nspace }
353+ ?.value
354+ }
355+
241356 fun getPatchedChain (keyId : KeyIdentifier ): Array <Certificate >? = patchedChains[keyId]
242357
243358 fun isAttestationKey (keyId : KeyIdentifier ): Boolean = attestationKeys.contains(keyId)
@@ -254,6 +369,15 @@ class KeyMintSecurityLevelInterceptor(
254369 }
255370 }
256371
372+ fun removeOperationInterceptor (operationBinder : IBinder , backdoor : IBinder ) {
373+ // Unregister from the native hook layer first.
374+ unregister(backdoor, operationBinder)
375+
376+ if (interceptedOperations.remove(operationBinder) != null ) {
377+ SystemLogger .debug(" Removed operation interceptor for binder: $operationBinder " )
378+ }
379+ }
380+
257381 // Clears all cached keys.
258382 fun clearAllGeneratedKeys (reason : String? = null) {
259383 val count = generatedKeys.size
0 commit comments