From b1f3b5d28dde96af62e9142c766653d5516664c4 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Wed, 28 Jan 2026 13:50:05 +0100 Subject: [PATCH] Fix cache consistency on key overwrite (#97) Android allows applications to generate a new key using an existing alias without explicitly calling `deleteKey` first. In this scenario, the new key effectively replaces the old one. As a simulator, we must strictly follow this logic to prevent returning stale data. Previously, `KeyMintSecurityLevelInterceptor` did not enforce mutual exclusion between the software key cache (`generatedKeys`) and the hardware chain cache (`patchedChains`). This led to state desynchronization where a stale software key could shadow a newly patched hardware chain if the alias was reused. This change ensures `cleanupKeyData` is invoked immediately before caching a new key / chain in both the software (`handleGenerateKey`) and hardware (`onPostTransact`) paths, ensuring the simulator returns the correct key for the most recent generation request. --- .../sessions/kotlin-compiler-7816880656539169480.salive | 0 .../keystore/shim/KeyMintSecurityLevelInterceptor.kt | 9 ++++++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 .kotlin/sessions/kotlin-compiler-7816880656539169480.salive diff --git a/.kotlin/sessions/kotlin-compiler-7816880656539169480.salive b/.kotlin/sessions/kotlin-compiler-7816880656539169480.salive new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt index 8b81c70..3c5942f 100644 --- a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt +++ b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt @@ -157,13 +157,15 @@ class KeyMintSecurityLevelInterceptor( val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!! val key = metadata.key!! val keyId = KeyIdentifier(callingUid, keyDescriptor.alias) + CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow() + + // We must clean up cached generated keys before storing the patched chain + cleanupKeyData(keyId) patchedChains[keyId] = newChain SystemLogger.debug( "Cached patched certificate chain for $keyId. (${key.alias} [${key.domain}, ${key.nspace}])" ) - CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow() - return InterceptorUtils.createTypedObjectReply(metadata) } } @@ -258,10 +260,11 @@ class KeyMintSecurityLevelInterceptor( securityLevel, ) ?: throw Exception("CertificateGenerator failed to create key pair.") + // It is unnecessary but a good practice to clean up possible caches + cleanupKeyData(keyId) // Store the generated key data. val response = buildKeyEntryResponse(keyData.second, parsedParams, keyDescriptor) - generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, keyDescriptor.nspace, response) if (isAttestKeyRequest) attestationKeys.add(keyId)