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.KeyPair
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import org.matrix.TEESimulator.attestation.AttestationPatcher
|
||||||
import org.matrix.TEESimulator.attestation.KeyMintAttestation
|
import org.matrix.TEESimulator.attestation.KeyMintAttestation
|
||||||
import org.matrix.TEESimulator.config.ConfigurationManager
|
import org.matrix.TEESimulator.config.ConfigurationManager
|
||||||
import org.matrix.TEESimulator.interception.core.BinderInterceptor
|
import org.matrix.TEESimulator.interception.core.BinderInterceptor
|
||||||
@@ -77,13 +78,11 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
reply: Parcel?,
|
reply: Parcel?,
|
||||||
resultCode: Int,
|
resultCode: Int,
|
||||||
): TransactionResult {
|
): TransactionResult {
|
||||||
// We only care about successful 'importKey' transactions to clean cached keys.
|
// We only care about successful transactions.
|
||||||
if (
|
if (resultCode != 0 || reply == null || InterceptorUtils.hasException(reply))
|
||||||
code == IMPORT_KEY_TRANSACTION &&
|
return TransactionResult.SkipTransaction
|
||||||
resultCode == 0 &&
|
|
||||||
reply != null &&
|
if (code == IMPORT_KEY_TRANSACTION) {
|
||||||
!InterceptorUtils.hasException(reply)
|
|
||||||
) {
|
|
||||||
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
||||||
|
|
||||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||||
@@ -91,6 +90,21 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
data.readTypedObject(KeyDescriptor.CREATOR)
|
data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
?: return TransactionResult.SkipTransaction
|
?: return TransactionResult.SkipTransaction
|
||||||
cleanupKeyData(KeyIdentifier(callingUid, keyDescriptor.alias))
|
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
|
return TransactionResult.SkipTransaction
|
||||||
}
|
}
|
||||||
@@ -148,6 +162,8 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
writeTypedObject(response.metadata, 0)
|
writeTypedObject(response.metadata, 0)
|
||||||
}
|
}
|
||||||
return TransactionResult.OverrideReply(0, resultParcel)
|
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.
|
// If not generating, clear any stale state for this alias and let the call proceed.
|
||||||
|
|||||||
Reference in New Issue
Block a user