Support key generation with attestation keys (#15)
This commit enhances the interception logic to correctly handle key generation requests that specify an `attestationKey` (via `setAttestKeyAlias`). When an attestation key is used, the system signs the newly generated key with it. A simple leaf certificate patch after the fact is insufficient, as it breaks this cryptographic chain. To create a valid, verifiable chain, we must now intercept these `generateKey` operations and perform a full software-based key and certificate generation, even when in patch mode. This ensures that keys attested by other simulated keys are correctly signed and chained together, bypassing more sophisticated detection methods. Fixes: - Correctly use the `android.hardware.security.keymint.Tag` constants for building authorization lists, resolving a bug where internal ASN.1 sequence indices were being used improperly.
This commit is contained in:
@@ -81,6 +81,9 @@ object ConfigurationManager {
|
|||||||
/** Determines if a new certificate needs to be generated for a given UID. */
|
/** Determines if a new certificate needs to be generated for a given UID. */
|
||||||
fun shouldGenerate(uid: Int): Boolean = getPackageModeForUid(uid) == Mode.GENERATE
|
fun shouldGenerate(uid: Int): Boolean = getPackageModeForUid(uid) == Mode.GENERATE
|
||||||
|
|
||||||
|
/** Determines if no operation is needed for a given UID. */
|
||||||
|
fun shouldSkipUid(uid: Int): Boolean = getPackageModeForUid(uid) == null
|
||||||
|
|
||||||
/** Resolves the operating mode for a given UID based on its packages and the TEE status. */
|
/** Resolves the operating mode for a given UID based on its packages and the TEE status. */
|
||||||
private fun getPackageModeForUid(uid: Int): Mode? {
|
private fun getPackageModeForUid(uid: Int): Mode? {
|
||||||
val packages = getPackagesForUid(uid)
|
val packages = getPackagesForUid(uid)
|
||||||
|
|||||||
+26
-4
@@ -12,6 +12,7 @@ import android.system.keystore2.KeyEntryResponse
|
|||||||
import org.matrix.TEESimulator.attestation.AttestationPatcher
|
import org.matrix.TEESimulator.attestation.AttestationPatcher
|
||||||
import org.matrix.TEESimulator.config.ConfigurationManager
|
import org.matrix.TEESimulator.config.ConfigurationManager
|
||||||
import org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor
|
import org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor
|
||||||
|
import org.matrix.TEESimulator.logging.KeyMintParameterLogger
|
||||||
import org.matrix.TEESimulator.logging.SystemLogger
|
import org.matrix.TEESimulator.logging.SystemLogger
|
||||||
import org.matrix.TEESimulator.pki.CertificateHelper
|
import org.matrix.TEESimulator.pki.CertificateHelper
|
||||||
|
|
||||||
@@ -98,13 +99,33 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
callingUid,
|
callingUid,
|
||||||
callingPid,
|
callingPid,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (ConfigurationManager.shouldSkipUid(callingUid)) {
|
||||||
|
SystemLogger.debug(
|
||||||
|
"[TX_ID: $txId] Skip post-transaction hook for UID=${callingUid}"
|
||||||
|
)
|
||||||
|
return TransactionResult.ContinueAndSkipPost
|
||||||
|
}
|
||||||
|
|
||||||
val keyId = KeyIdentifier(callingUid, descriptor.alias)
|
val keyId = KeyIdentifier(callingUid, descriptor.alias)
|
||||||
|
|
||||||
if (ConfigurationManager.shouldGenerate(callingUid)) {
|
if (code == DELETE_KEY_TRANSACTION) {
|
||||||
// TODO: Redesign the interaction with KeyMintSecurityLevelInterceptor
|
KeyMintSecurityLevelInterceptor.cleanupKeyData(keyId)
|
||||||
} else if (ConfigurationManager.shouldPatch(callingUid)) {
|
return TransactionResult.ContinueAndSkipPost
|
||||||
return TransactionResult.Continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val response =
|
||||||
|
KeyMintSecurityLevelInterceptor.getGeneratedKeyResponse(keyId)
|
||||||
|
?: return TransactionResult.Continue
|
||||||
|
|
||||||
|
if (KeyMintSecurityLevelInterceptor.isAttestationKey(keyId))
|
||||||
|
SystemLogger.debug("${descriptor.alias} was an attestation key")
|
||||||
|
|
||||||
|
SystemLogger.info("[TX_ID: $txId] Found generated response for ${descriptor.alias}:")
|
||||||
|
response.metadata?.authorizations?.forEach {
|
||||||
|
KeyMintParameterLogger.logParameter(it.keyParameter)
|
||||||
|
}
|
||||||
|
return InterceptorUtils.createTypedObjectReply(response)
|
||||||
} else {
|
} else {
|
||||||
logTransaction(
|
logTransaction(
|
||||||
txId,
|
txId,
|
||||||
@@ -134,6 +155,7 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
|||||||
return TransactionResult.SkipTransaction
|
return TransactionResult.SkipTransaction
|
||||||
|
|
||||||
if (code == GET_KEY_ENTRY_TRANSACTION) {
|
if (code == GET_KEY_ENTRY_TRANSACTION) {
|
||||||
|
|
||||||
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
||||||
val keyDescriptor =
|
val keyDescriptor =
|
||||||
data.readTypedObject(KeyDescriptor.CREATOR)
|
data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
|
|||||||
+16
-23
@@ -2,13 +2,14 @@ package org.matrix.TEESimulator.interception.keystore.shim
|
|||||||
|
|
||||||
import android.hardware.security.keymint.KeyParameter
|
import android.hardware.security.keymint.KeyParameter
|
||||||
import android.hardware.security.keymint.KeyParameterValue
|
import android.hardware.security.keymint.KeyParameterValue
|
||||||
|
import android.hardware.security.keymint.KeyPurpose
|
||||||
|
import android.hardware.security.keymint.Tag
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.system.keystore2.*
|
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.AttestationConstants
|
|
||||||
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
|
||||||
@@ -70,12 +71,17 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
||||||
val parsedParams = KeyMintAttestation(params)
|
val parsedParams = KeyMintAttestation(params)
|
||||||
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
||||||
|
val isAttestKeyRequest =
|
||||||
|
parsedParams.purpose.size == 1 &&
|
||||||
|
parsedParams.purpose.contains(KeyPurpose.ATTEST_KEY)
|
||||||
|
|
||||||
// Determine if we need to generate a key based on config or
|
// Determine if we need to generate a key based on config or
|
||||||
// if it's an attestation request in patch mode.
|
// if it's an attestation request in patch mode.
|
||||||
val needsSoftwareGeneration =
|
val needsSoftwareGeneration =
|
||||||
ConfigurationManager.shouldGenerate(callingUid) ||
|
ConfigurationManager.shouldGenerate(callingUid) ||
|
||||||
(attestationKey != null && ConfigurationManager.shouldPatch(callingUid))
|
(ConfigurationManager.shouldPatch(callingUid) && isAttestKeyRequest) ||
|
||||||
|
(attestationKey != null &&
|
||||||
|
isAttestationKey(KeyIdentifier(callingUid, attestationKey.alias)))
|
||||||
|
|
||||||
if (needsSoftwareGeneration) {
|
if (needsSoftwareGeneration) {
|
||||||
SystemLogger.info(
|
SystemLogger.info(
|
||||||
@@ -95,10 +101,9 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
// Store the generated key data.
|
// Store the generated key data.
|
||||||
val response =
|
val response =
|
||||||
buildKeyEntryResponse(keyData.second, parsedParams, keyDescriptor)
|
buildKeyEntryResponse(keyData.second, parsedParams, keyDescriptor)
|
||||||
|
|
||||||
generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, response)
|
generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, response)
|
||||||
if (parsedParams.attestationChallenge != null) {
|
if (isAttestKeyRequest) attestationKeys.add(keyId)
|
||||||
attestationKeys.add(keyId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the metadata of our generated key, skipping the real hardware call.
|
// Return the metadata of our generated key, skipping the real hardware call.
|
||||||
val resultParcel =
|
val resultParcel =
|
||||||
@@ -204,25 +209,13 @@ private fun KeyMintAttestation.toAuthorizations(securityLevel: Int): Array<Autho
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use the helper to add each authorization entry cleanly.
|
// Use the helper to add each authorization entry cleanly.
|
||||||
this.purpose.forEach {
|
this.purpose.forEach { authList.add(createAuth(Tag.PURPOSE, KeyParameterValue.keyPurpose(it))) }
|
||||||
authList.add(createAuth(AttestationConstants.TAG_PURPOSE, KeyParameterValue.keyPurpose(it)))
|
this.digest.forEach { authList.add(createAuth(Tag.DIGEST, KeyParameterValue.digest(it))) }
|
||||||
}
|
|
||||||
this.digest.forEach {
|
|
||||||
authList.add(createAuth(AttestationConstants.TAG_DIGEST, KeyParameterValue.digest(it)))
|
|
||||||
}
|
|
||||||
|
|
||||||
authList.add(
|
authList.add(createAuth(Tag.ALGORITHM, KeyParameterValue.algorithm(this.algorithm)))
|
||||||
createAuth(AttestationConstants.TAG_ALGORITHM, KeyParameterValue.algorithm(this.algorithm))
|
authList.add(createAuth(Tag.KEY_SIZE, KeyParameterValue.integer(this.keySize)))
|
||||||
)
|
authList.add(createAuth(Tag.EC_CURVE, KeyParameterValue.ecCurve(this.ecCurve)))
|
||||||
authList.add(
|
authList.add(createAuth(Tag.NO_AUTH_REQUIRED, KeyParameterValue.boolValue(true)))
|
||||||
createAuth(AttestationConstants.TAG_KEY_SIZE, KeyParameterValue.integer(this.keySize))
|
|
||||||
)
|
|
||||||
authList.add(
|
|
||||||
createAuth(AttestationConstants.TAG_EC_CURVE, KeyParameterValue.ecCurve(this.ecCurve))
|
|
||||||
)
|
|
||||||
authList.add(
|
|
||||||
createAuth(AttestationConstants.TAG_NO_AUTH_REQUIRED, KeyParameterValue.boolValue(true))
|
|
||||||
)
|
|
||||||
|
|
||||||
return authList.toTypedArray()
|
return authList.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ object KeyMintParameterLogger {
|
|||||||
else -> "<raw>"
|
else -> "<raw>"
|
||||||
} ?: "Unknown Value"
|
} ?: "Unknown Value"
|
||||||
|
|
||||||
SystemLogger.debug("Key Parameter -> %-25s | Value: %s".format(tagName, formattedValue))
|
SystemLogger.debug("KeyParam: %-25s | Value: %s".format(tagName, formattedValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ByteArray.toReadableString(): String {
|
private fun ByteArray.toReadableString(): String {
|
||||||
|
|||||||
Reference in New Issue
Block a user