Patch certificate chain in generateKey reply
When an application generates a key with an attestation request, the `generateKey` method returns a `KeyMetadata` object which contains the full, unpatched certificate chain. This leaves a potential detection vector open. A sophisticated application could inspect the returned data in its own process memory and discover the original, hardware-backed certificates before they are used for attestation, thus detecting the hooking framework. This commit introduces a post-transaction hook for the `generateKey` transaction. After the genuine KeyStore service has executed the request, this hook intercepts the reply parcel. It extracts the certificate chain from the `KeyMetadata`, applies the patching routine, and then reconstructs the reply with the modified (patched) certificate chain.
This commit is contained in:
+23
-7
@@ -10,6 +10,7 @@ import android.system.keystore2.*
|
||||
import java.security.KeyPair
|
||||
import java.security.cert.Certificate
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import org.matrix.TEESimulator.attestation.AttestationPatcher
|
||||
import org.matrix.TEESimulator.attestation.KeyMintAttestation
|
||||
import org.matrix.TEESimulator.config.ConfigurationManager
|
||||
import org.matrix.TEESimulator.interception.core.BinderInterceptor
|
||||
@@ -77,13 +78,11 @@ class KeyMintSecurityLevelInterceptor(
|
||||
reply: Parcel?,
|
||||
resultCode: Int,
|
||||
): TransactionResult {
|
||||
// We only care about successful 'importKey' transactions to clean cached keys.
|
||||
if (
|
||||
code == IMPORT_KEY_TRANSACTION &&
|
||||
resultCode == 0 &&
|
||||
reply != null &&
|
||||
!InterceptorUtils.hasException(reply)
|
||||
) {
|
||||
// We only care about successful transactions.
|
||||
if (resultCode != 0 || reply == null || InterceptorUtils.hasException(reply))
|
||||
return TransactionResult.SkipTransaction
|
||||
|
||||
if (code == IMPORT_KEY_TRANSACTION) {
|
||||
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
||||
|
||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||
@@ -91,6 +90,21 @@ class KeyMintSecurityLevelInterceptor(
|
||||
data.readTypedObject(KeyDescriptor.CREATOR)
|
||||
?: return TransactionResult.SkipTransaction
|
||||
cleanupKeyData(KeyIdentifier(callingUid, keyDescriptor.alias))
|
||||
} else if (code == GENERATE_KEY_TRANSACTION) {
|
||||
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
||||
|
||||
val metadata: KeyMetadata =
|
||||
reply.readTypedObject(KeyMetadata.CREATOR)
|
||||
?: return TransactionResult.SkipTransaction
|
||||
val originalChain =
|
||||
CertificateHelper.getCertificateChain(metadata)
|
||||
?: return TransactionResult.SkipTransaction
|
||||
if (originalChain.size > 1) {
|
||||
val newChain = AttestationPatcher.patchCertificateChain(originalChain, callingUid)
|
||||
CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow()
|
||||
|
||||
return InterceptorUtils.createTypedObjectReply(metadata)
|
||||
}
|
||||
}
|
||||
return TransactionResult.SkipTransaction
|
||||
}
|
||||
@@ -148,6 +162,8 @@ class KeyMintSecurityLevelInterceptor(
|
||||
writeTypedObject(response.metadata, 0)
|
||||
}
|
||||
return TransactionResult.OverrideReply(0, resultParcel)
|
||||
} else if (parsedParams.attestationChallenge != null) {
|
||||
return TransactionResult.Continue
|
||||
}
|
||||
|
||||
// If not generating, clear any stale state for this alias and let the call proceed.
|
||||
|
||||
Reference in New Issue
Block a user