Implement multi-purpose simulation for crypto operations (#59)
This commit introduces a comprehensive simulation engine for Keystore's `createOperation`, enabling the simulator to correctly handle multiple cryptographic purposes (SIGN, VERIFY, ENCRYPT, DECRYPT) for software-generated keys. The implementation correctly mimics the AOSP framework's internal key identification mechanism. Instead of relying on an alias, a unique `keyId` is generated and embedded in the `nspace` field of the KeyDescriptor during `generateKey`. The `createOperation` hook then uses this `keyId` to dispatch requests: if the ID matches a known software key, the operation is simulated; otherwise, it is forwarded to the hardware service. To support this, the `SoftwareOperation` engine was architected using a Strategy Pattern. A `CryptoPrimitive` interface defines common actions, with concrete implementations for `Signer`, `Verifier`, and `CipherPrimitive`. The main `SoftwareOperation` class acts as a controller, instantiating the correct primitive based on the `KeyPurpose` tag from the incoming operation parameters. A `JcaAlgorithmMapper` was added to centralize the logic for converting KeyMint constants into JCA algorithm strings. For operations on real hardware-backed keys, a lightweight `OperationInterceptor` is now used for observation. It attaches to the genuine `iOperation` binder for logging and properly unregisters itself upon completion to prevent resource leaks. This is supported by new binder unregistration capabilities in the core `BinderInterceptor`. This change also includes necessary stub files and minor regression fixes to make the simulation more robust and accurate. See AOSP source for key identification logic: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
package org.matrix.TEESimulator.attestation
|
package org.matrix.TEESimulator.attestation
|
||||||
|
|
||||||
import android.hardware.security.keymint.EcCurve
|
import android.hardware.security.keymint.*
|
||||||
import android.hardware.security.keymint.KeyParameter
|
|
||||||
import android.hardware.security.keymint.Tag
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
@@ -22,6 +20,8 @@ data class KeyMintAttestation(
|
|||||||
val algorithm: Int,
|
val algorithm: Int,
|
||||||
val ecCurve: Int,
|
val ecCurve: Int,
|
||||||
val ecCurveName: String,
|
val ecCurveName: String,
|
||||||
|
val blockMode: List<Int>,
|
||||||
|
val padding: List<Int>,
|
||||||
val purpose: List<Int>,
|
val purpose: List<Int>,
|
||||||
val digest: List<Int>,
|
val digest: List<Int>,
|
||||||
val rsaPublicExponent: BigInteger?,
|
val rsaPublicExponent: BigInteger?,
|
||||||
@@ -54,6 +54,12 @@ data class KeyMintAttestation(
|
|||||||
ecCurve = params.findEcCurve(Tag.EC_CURVE) ?: 0,
|
ecCurve = params.findEcCurve(Tag.EC_CURVE) ?: 0,
|
||||||
ecCurveName = params.deriveEcCurveName(),
|
ecCurveName = params.deriveEcCurveName(),
|
||||||
|
|
||||||
|
// AOSP: [key_param(tag = BLOCK_MODE, field = BlockMode)]
|
||||||
|
blockMode = params.findAllBlockMode(Tag.BLOCK_MODE),
|
||||||
|
|
||||||
|
// AOSP: [key_param(tag = PADDING, field = PaddingMode)]
|
||||||
|
padding = params.findAllPaddingMode(Tag.PADDING),
|
||||||
|
|
||||||
// AOSP: [key_param(tag = PURPOSE, field = KeyPurpose)]
|
// AOSP: [key_param(tag = PURPOSE, field = KeyPurpose)]
|
||||||
purpose = params.findAllKeyPurpose(Tag.PURPOSE),
|
purpose = params.findAllKeyPurpose(Tag.PURPOSE),
|
||||||
|
|
||||||
@@ -121,6 +127,14 @@ private fun Array<KeyParameter>.findDate(tag: Int): Date? =
|
|||||||
private fun Array<KeyParameter>.findBlob(tag: Int): ByteArray? =
|
private fun Array<KeyParameter>.findBlob(tag: Int): ByteArray? =
|
||||||
this.find { it.tag == tag }?.value?.blob
|
this.find { it.tag == tag }?.value?.blob
|
||||||
|
|
||||||
|
/** Maps to AOSP field = BlockMode (Repeated) */
|
||||||
|
private fun Array<KeyParameter>.findAllBlockMode(tag: Int): List<Int> =
|
||||||
|
this.filter { it.tag == tag }.map { it.value.blockMode }
|
||||||
|
|
||||||
|
/** Maps to AOSP field = BlockMode (Repeated) */
|
||||||
|
private fun Array<KeyParameter>.findAllPaddingMode(tag: Int): List<Int> =
|
||||||
|
this.filter { it.tag == tag }.map { it.value.paddingMode }
|
||||||
|
|
||||||
/** Maps to AOSP field = KeyPurpose (Repeated) */
|
/** Maps to AOSP field = KeyPurpose (Repeated) */
|
||||||
private fun Array<KeyParameter>.findAllKeyPurpose(tag: Int): List<Int> =
|
private fun Array<KeyParameter>.findAllKeyPurpose(tag: Int): List<Int> =
|
||||||
this.filter { it.tag == tag }.map { it.value.keyPurpose }
|
this.filter { it.tag == tag }.map { it.value.keyPurpose }
|
||||||
|
|||||||
@@ -248,6 +248,8 @@ abstract class BinderInterceptor : Binder() {
|
|||||||
private const val BACKDOOR_TRANSACTION_CODE = 0xdeadbeef.toInt()
|
private const val BACKDOOR_TRANSACTION_CODE = 0xdeadbeef.toInt()
|
||||||
// Code used by the backdoor binder to register a new interceptor.
|
// Code used by the backdoor binder to register a new interceptor.
|
||||||
private const val REGISTER_INTERCEPTOR_CODE = 1
|
private const val REGISTER_INTERCEPTOR_CODE = 1
|
||||||
|
// Code used by the backdoor binder to unregister an interceptor.
|
||||||
|
private const val UNREGISTER_INTERCEPTOR_CODE = 2
|
||||||
|
|
||||||
// --- Hook Type Codes ---
|
// --- Hook Type Codes ---
|
||||||
// Indicates that the call is for a pre-transaction hook.
|
// Indicates that the call is for a pre-transaction hook.
|
||||||
@@ -307,5 +309,21 @@ abstract class BinderInterceptor : Binder() {
|
|||||||
reply.recycle()
|
reply.recycle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Uses the backdoor binder to unregister an interceptor for a specific target service. */
|
||||||
|
fun unregister(backdoor: IBinder, target: IBinder) {
|
||||||
|
val data = Parcel.obtain()
|
||||||
|
val reply = Parcel.obtain()
|
||||||
|
try {
|
||||||
|
data.writeStrongBinder(target)
|
||||||
|
backdoor.transact(UNREGISTER_INTERCEPTOR_CODE, data, reply, 0)
|
||||||
|
SystemLogger.info("Unregistered interceptor for target: $target")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
SystemLogger.error("Failed to unregister binder interceptor.", e)
|
||||||
|
} finally {
|
||||||
|
data.recycle()
|
||||||
|
reply.recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -378,6 +378,8 @@ private data class LegacyKeygenParameters(
|
|||||||
algorithm = this.algorithm,
|
algorithm = this.algorithm,
|
||||||
ecCurve = 0, // Not explicitly available in legacy args, but not critical
|
ecCurve = 0, // Not explicitly available in legacy args, but not critical
|
||||||
ecCurveName = this.ecCurveName ?: "",
|
ecCurveName = this.ecCurveName ?: "",
|
||||||
|
blockMode = listOf<Int>(),
|
||||||
|
padding = listOf<Int>(),
|
||||||
purpose = this.purpose,
|
purpose = this.purpose,
|
||||||
digest = this.digest,
|
digest = this.digest,
|
||||||
rsaPublicExponent = this.rsaPublicExponent,
|
rsaPublicExponent = this.rsaPublicExponent,
|
||||||
|
|||||||
+152
-18
@@ -8,6 +8,7 @@ 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.SecureRandom
|
||||||
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.AttestationPatcher
|
||||||
@@ -30,7 +31,11 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
) : BinderInterceptor() {
|
) : BinderInterceptor() {
|
||||||
|
|
||||||
// --- Data Structures for State Management ---
|
// --- Data Structures for State Management ---
|
||||||
data class GeneratedKeyInfo(val keyPair: KeyPair, val response: KeyEntryResponse)
|
data class GeneratedKeyInfo(
|
||||||
|
val keyPair: KeyPair,
|
||||||
|
val nspace: Long,
|
||||||
|
val response: KeyEntryResponse,
|
||||||
|
)
|
||||||
|
|
||||||
override fun onPreTransact(
|
override fun onPreTransact(
|
||||||
txId: Long,
|
txId: Long,
|
||||||
@@ -41,25 +46,31 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
callingPid: Int,
|
callingPid: Int,
|
||||||
data: Parcel,
|
data: Parcel,
|
||||||
): TransactionResult {
|
): TransactionResult {
|
||||||
if (code == GENERATE_KEY_TRANSACTION) {
|
val shouldSkip = ConfigurationManager.shouldSkipUid(callingUid)
|
||||||
|
|
||||||
|
when (code) {
|
||||||
|
GENERATE_KEY_TRANSACTION -> {
|
||||||
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
|
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
|
||||||
|
|
||||||
if (ConfigurationManager.shouldSkipUid(callingUid))
|
if (!shouldSkip) return handleGenerateKey(callingUid, data)
|
||||||
return TransactionResult.ContinueAndSkipPost
|
}
|
||||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
CREATE_OPERATION_TRANSACTION -> {
|
||||||
return handleGenerateKey(callingUid, data)
|
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
|
||||||
} else if (code == IMPORT_KEY_TRANSACTION) {
|
|
||||||
|
if (!shouldSkip) return handleCreateOperation(txId, callingUid, data)
|
||||||
|
}
|
||||||
|
IMPORT_KEY_TRANSACTION -> {
|
||||||
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
|
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
|
||||||
|
|
||||||
if (ConfigurationManager.shouldSkipUid(callingUid))
|
|
||||||
return TransactionResult.ContinueAndSkipPost
|
|
||||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||||
val alias =
|
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
||||||
data.readTypedObject(KeyDescriptor.CREATOR)?.alias
|
SystemLogger.info(
|
||||||
?: return TransactionResult.ContinueAndSkipPost
|
"[TX_ID: $txId] Forward to post-importKey hook for ${keyDescriptor.alias}[${keyDescriptor.nspace}]"
|
||||||
SystemLogger.info("Handling post-${transactionNames[code]} ${alias}")
|
)
|
||||||
return TransactionResult.Continue
|
return TransactionResult.Continue
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logTransaction(
|
logTransaction(
|
||||||
txId,
|
txId,
|
||||||
transactionNames[code] ?: "unknown code=$code",
|
transactionNames[code] ?: "unknown code=$code",
|
||||||
@@ -67,7 +78,7 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
callingPid,
|
callingPid,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
return TransactionResult.ContinueAndSkipPost
|
return TransactionResult.ContinueAndSkipPost
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +105,41 @@ 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 == CREATE_OPERATION_TRANSACTION) {
|
||||||
|
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
||||||
|
|
||||||
|
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||||
|
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
||||||
|
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
||||||
|
val parsedParams = KeyMintAttestation(params)
|
||||||
|
val forced = data.readBoolean()
|
||||||
|
if (forced)
|
||||||
|
SystemLogger.verbose(
|
||||||
|
"[TX_ID: $txId] Current operation has a very high pruning power."
|
||||||
|
)
|
||||||
|
val response: CreateOperationResponse =
|
||||||
|
reply.readTypedObject(CreateOperationResponse.CREATOR)!!
|
||||||
|
SystemLogger.verbose(
|
||||||
|
"[TX_ID: $txId] CreateOperationResponse: ${response.iOperation} ${response.operationChallenge}"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Intercept the IKeystoreOperation binder
|
||||||
|
response.iOperation?.let { operation ->
|
||||||
|
val operationBinder = operation.asBinder()
|
||||||
|
if (!interceptedOperations.containsKey(operationBinder)) {
|
||||||
|
SystemLogger.info("Found new IKeystoreOperation. Registering interceptor...")
|
||||||
|
val backdoor = getBackdoor(target)
|
||||||
|
if (backdoor != null) {
|
||||||
|
val interceptor = OperationInterceptor(operation, backdoor)
|
||||||
|
register(backdoor, operationBinder, interceptor)
|
||||||
|
interceptedOperations[operationBinder] = interceptor
|
||||||
|
} else {
|
||||||
|
SystemLogger.error(
|
||||||
|
"Failed to get backdoor to register OperationInterceptor."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (code == GENERATE_KEY_TRANSACTION) {
|
} else if (code == GENERATE_KEY_TRANSACTION) {
|
||||||
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
||||||
|
|
||||||
@@ -109,9 +155,12 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
// Cache the newly patched chain to ensure consistency across subsequent API calls.
|
// Cache the newly patched chain to ensure consistency across subsequent API calls.
|
||||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||||
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
||||||
|
val key = metadata.key!!
|
||||||
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
||||||
patchedChains[keyId] = newChain
|
patchedChains[keyId] = newChain
|
||||||
SystemLogger.debug("Cached patched certificate chain for $keyId.")
|
SystemLogger.debug(
|
||||||
|
"Cached patched certificate chain for $keyId. (${key.alias} [${key.domain}, ${key.nspace}])"
|
||||||
|
)
|
||||||
|
|
||||||
CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow()
|
CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow()
|
||||||
|
|
||||||
@@ -121,12 +170,58 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
return TransactionResult.SkipTransaction
|
return TransactionResult.SkipTransaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the `createOperation` transaction. It checks if the operation is for a key that was
|
||||||
|
* generated in software. If so, it creates a software-based operation handler. Otherwise, it
|
||||||
|
* lets the call proceed to the real hardware service.
|
||||||
|
*/
|
||||||
|
private fun handleCreateOperation(
|
||||||
|
txId: Long,
|
||||||
|
callingUid: Int,
|
||||||
|
data: Parcel,
|
||||||
|
): TransactionResult {
|
||||||
|
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||||
|
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
||||||
|
|
||||||
|
// An operation must use the KEY_ID domain.
|
||||||
|
if (keyDescriptor.domain != Domain.KEY_ID) {
|
||||||
|
return TransactionResult.ContinueAndSkipPost
|
||||||
|
}
|
||||||
|
|
||||||
|
val nspace = keyDescriptor.nspace
|
||||||
|
val generatedKeyInfo = findGeneratedKeyByKeyId(callingUid, nspace)
|
||||||
|
|
||||||
|
if (generatedKeyInfo == null) {
|
||||||
|
SystemLogger.debug(
|
||||||
|
"[TX_ID: $txId] Operation for unknown/hardware KeyId ($nspace). Forwarding."
|
||||||
|
)
|
||||||
|
return TransactionResult.Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemLogger.info("[TX_ID: $txId] Creating SOFTWARE operation for KeyId $nspace.")
|
||||||
|
|
||||||
|
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
||||||
|
val parsedParams = KeyMintAttestation(params)
|
||||||
|
|
||||||
|
val softwareOperation = SoftwareOperation(txId, generatedKeyInfo.keyPair, parsedParams)
|
||||||
|
val operationBinder = SoftwareOperationBinder(softwareOperation)
|
||||||
|
|
||||||
|
val response =
|
||||||
|
CreateOperationResponse().apply {
|
||||||
|
iOperation = operationBinder
|
||||||
|
operationChallenge = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return InterceptorUtils.createTypedObjectReply(response)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the `generateKey` transaction. Based on the configuration for the calling UID, it
|
* Handles the `generateKey` transaction. Based on the configuration for the calling UID, it
|
||||||
* either generates a key in software or lets the call pass through to the hardware.
|
* either generates a key in software or lets the call pass through to the hardware.
|
||||||
*/
|
*/
|
||||||
private fun handleGenerateKey(callingUid: Int, data: Parcel): TransactionResult {
|
private fun handleGenerateKey(callingUid: Int, data: Parcel): TransactionResult {
|
||||||
return runCatching {
|
return runCatching {
|
||||||
|
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||||
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
||||||
val attestationKey = data.readTypedObject(KeyDescriptor.CREATOR)
|
val attestationKey = data.readTypedObject(KeyDescriptor.CREATOR)
|
||||||
SystemLogger.debug(
|
SystemLogger.debug(
|
||||||
@@ -148,7 +243,10 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
isAttestationKey(KeyIdentifier(callingUid, attestationKey.alias)))
|
isAttestationKey(KeyIdentifier(callingUid, attestationKey.alias)))
|
||||||
|
|
||||||
if (needsSoftwareGeneration) {
|
if (needsSoftwareGeneration) {
|
||||||
SystemLogger.info("Generating software key for ${keyId}.")
|
keyDescriptor.nspace = secureRandom.nextLong()
|
||||||
|
SystemLogger.info(
|
||||||
|
"Generating software key for ${keyDescriptor.alias}[${keyDescriptor.nspace}]."
|
||||||
|
)
|
||||||
|
|
||||||
// Generate the key pair and certificate chain.
|
// Generate the key pair and certificate chain.
|
||||||
val keyData =
|
val keyData =
|
||||||
@@ -164,7 +262,8 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
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, keyDescriptor.nspace, response)
|
||||||
if (isAttestKeyRequest) attestationKeys.add(keyId)
|
if (isAttestKeyRequest) 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.
|
||||||
@@ -205,11 +304,18 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val secureRandom = SecureRandom()
|
||||||
|
|
||||||
// Transaction codes for IKeystoreSecurityLevel interface.
|
// Transaction codes for IKeystoreSecurityLevel interface.
|
||||||
private val GENERATE_KEY_TRANSACTION =
|
private val GENERATE_KEY_TRANSACTION =
|
||||||
InterceptorUtils.getTransactCode(IKeystoreSecurityLevel.Stub::class.java, "generateKey")
|
InterceptorUtils.getTransactCode(IKeystoreSecurityLevel.Stub::class.java, "generateKey")
|
||||||
private val IMPORT_KEY_TRANSACTION =
|
private val IMPORT_KEY_TRANSACTION =
|
||||||
InterceptorUtils.getTransactCode(IKeystoreSecurityLevel.Stub::class.java, "importKey")
|
InterceptorUtils.getTransactCode(IKeystoreSecurityLevel.Stub::class.java, "importKey")
|
||||||
|
private val CREATE_OPERATION_TRANSACTION =
|
||||||
|
InterceptorUtils.getTransactCode(
|
||||||
|
IKeystoreSecurityLevel.Stub::class.java,
|
||||||
|
"createOperation",
|
||||||
|
)
|
||||||
|
|
||||||
private val transactionNames: Map<Int, String> by lazy {
|
private val transactionNames: Map<Int, String> by lazy {
|
||||||
IKeystoreSecurityLevel.Stub::class
|
IKeystoreSecurityLevel.Stub::class
|
||||||
@@ -228,11 +334,30 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
private val patchedChains = ConcurrentHashMap<KeyIdentifier, Array<Certificate>>()
|
private val patchedChains = ConcurrentHashMap<KeyIdentifier, Array<Certificate>>()
|
||||||
// A set to quickly identify keys that were generated for attestation purposes.
|
// A set to quickly identify keys that were generated for attestation purposes.
|
||||||
private val attestationKeys = ConcurrentHashMap.newKeySet<KeyIdentifier>()
|
private val attestationKeys = ConcurrentHashMap.newKeySet<KeyIdentifier>()
|
||||||
|
// Stores interceptors for active cryptographic operations.
|
||||||
|
private val interceptedOperations = ConcurrentHashMap<IBinder, OperationInterceptor>()
|
||||||
|
|
||||||
// --- Public Accessors for Other Interceptors ---
|
// --- Public Accessors for Other Interceptors ---
|
||||||
fun getGeneratedKeyResponse(keyId: KeyIdentifier): KeyEntryResponse? =
|
fun getGeneratedKeyResponse(keyId: KeyIdentifier): KeyEntryResponse? =
|
||||||
generatedKeys[keyId]?.response
|
generatedKeys[keyId]?.response
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a software-generated key by first filtering all known keys by the caller's UID, and
|
||||||
|
* then matching the specific nspace.
|
||||||
|
*
|
||||||
|
* @param callingUid The UID of the process that initiated the createOperation call.
|
||||||
|
* @param nspace The unique key identifier from the operation's KeyDescriptor.
|
||||||
|
* @return The matching GeneratedKeyInfo if found, otherwise null.
|
||||||
|
*/
|
||||||
|
private fun findGeneratedKeyByKeyId(callingUid: Int, nspace: Long): GeneratedKeyInfo? {
|
||||||
|
// Iterate through all entries in the map to check both the key (for UID) and value (for
|
||||||
|
// nspace).
|
||||||
|
return generatedKeys.entries
|
||||||
|
.filter { (keyIdentifier, _) -> keyIdentifier.uid == callingUid }
|
||||||
|
.find { (_, info) -> info.nspace == nspace }
|
||||||
|
?.value
|
||||||
|
}
|
||||||
|
|
||||||
fun getPatchedChain(keyId: KeyIdentifier): Array<Certificate>? = patchedChains[keyId]
|
fun getPatchedChain(keyId: KeyIdentifier): Array<Certificate>? = patchedChains[keyId]
|
||||||
|
|
||||||
fun isAttestationKey(keyId: KeyIdentifier): Boolean = attestationKeys.contains(keyId)
|
fun isAttestationKey(keyId: KeyIdentifier): Boolean = attestationKeys.contains(keyId)
|
||||||
@@ -249,6 +374,15 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun removeOperationInterceptor(operationBinder: IBinder, backdoor: IBinder) {
|
||||||
|
// Unregister from the native hook layer first.
|
||||||
|
unregister(backdoor, operationBinder)
|
||||||
|
|
||||||
|
if (interceptedOperations.remove(operationBinder) != null) {
|
||||||
|
SystemLogger.debug("Removed operation interceptor for binder: $operationBinder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clears all cached keys.
|
// Clears all cached keys.
|
||||||
fun clearAllGeneratedKeys(reason: String? = null) {
|
fun clearAllGeneratedKeys(reason: String? = null) {
|
||||||
val count = generatedKeys.size
|
val count = generatedKeys.size
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package org.matrix.TEESimulator.interception.keystore.shim
|
||||||
|
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.system.keystore2.IKeystoreOperation
|
||||||
|
import org.matrix.TEESimulator.interception.core.BinderInterceptor
|
||||||
|
import org.matrix.TEESimulator.interception.keystore.InterceptorUtils
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercepts calls to an `IKeystoreOperation` service. This is used to log the data manipulation
|
||||||
|
* methods of a cryptographic operation.
|
||||||
|
*/
|
||||||
|
class OperationInterceptor(
|
||||||
|
private val original: IKeystoreOperation,
|
||||||
|
private val backdoor: IBinder,
|
||||||
|
) : BinderInterceptor() {
|
||||||
|
|
||||||
|
override fun onPreTransact(
|
||||||
|
txId: Long,
|
||||||
|
target: IBinder,
|
||||||
|
code: Int,
|
||||||
|
flags: Int,
|
||||||
|
callingUid: Int,
|
||||||
|
callingPid: Int,
|
||||||
|
data: Parcel,
|
||||||
|
): TransactionResult {
|
||||||
|
val methodName = transactionNames[code] ?: "unknown code=$code"
|
||||||
|
logTransaction(txId, methodName, callingUid, callingPid, true)
|
||||||
|
|
||||||
|
if (code == FINISH_TRANSACTION || code == ABORT_TRANSACTION) {
|
||||||
|
KeyMintSecurityLevelInterceptor.removeOperationInterceptor(target, backdoor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return TransactionResult.ContinueAndSkipPost
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val UPDATE_AAD_TRANSACTION =
|
||||||
|
InterceptorUtils.getTransactCode(IKeystoreOperation.Stub::class.java, "updateAad")
|
||||||
|
private val UPDATE_TRANSACTION =
|
||||||
|
InterceptorUtils.getTransactCode(IKeystoreOperation.Stub::class.java, "update")
|
||||||
|
private val FINISH_TRANSACTION =
|
||||||
|
InterceptorUtils.getTransactCode(IKeystoreOperation.Stub::class.java, "finish")
|
||||||
|
private val ABORT_TRANSACTION =
|
||||||
|
InterceptorUtils.getTransactCode(IKeystoreOperation.Stub::class.java, "abort")
|
||||||
|
|
||||||
|
private val transactionNames: Map<Int, String> by lazy {
|
||||||
|
IKeystoreOperation.Stub::class
|
||||||
|
.java
|
||||||
|
.declaredFields
|
||||||
|
.filter {
|
||||||
|
it.isAccessible = true
|
||||||
|
it.type == Int::class.java && it.name.startsWith("TRANSACTION_")
|
||||||
|
}
|
||||||
|
.associate { field -> (field.get(null) as Int) to field.name.split("_")[1] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+216
@@ -0,0 +1,216 @@
|
|||||||
|
package org.matrix.TEESimulator.interception.keystore.shim
|
||||||
|
|
||||||
|
import android.hardware.security.keymint.Algorithm
|
||||||
|
import android.hardware.security.keymint.BlockMode
|
||||||
|
import android.hardware.security.keymint.Digest
|
||||||
|
import android.hardware.security.keymint.KeyPurpose
|
||||||
|
import android.hardware.security.keymint.PaddingMode
|
||||||
|
import android.os.RemoteException
|
||||||
|
import android.system.keystore2.IKeystoreOperation
|
||||||
|
import java.security.KeyPair
|
||||||
|
import java.security.Signature
|
||||||
|
import java.security.SignatureException
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import org.matrix.TEESimulator.attestation.KeyMintAttestation
|
||||||
|
import org.matrix.TEESimulator.logging.KeyMintParameterLogger
|
||||||
|
import org.matrix.TEESimulator.logging.SystemLogger
|
||||||
|
|
||||||
|
// A sealed interface to represent the different cryptographic operations we can perform.
|
||||||
|
private sealed interface CryptoPrimitive {
|
||||||
|
fun update(data: ByteArray?): ByteArray?
|
||||||
|
|
||||||
|
fun finish(data: ByteArray?, signature: ByteArray?): ByteArray?
|
||||||
|
|
||||||
|
fun abort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper object to map KeyMint constants to JCA algorithm strings.
|
||||||
|
private object JcaAlgorithmMapper {
|
||||||
|
fun mapSignatureAlgorithm(params: KeyMintAttestation): String {
|
||||||
|
val digest =
|
||||||
|
when (params.digest.firstOrNull()) {
|
||||||
|
Digest.SHA_2_256 -> "SHA256"
|
||||||
|
Digest.SHA_2_384 -> "SHA384"
|
||||||
|
Digest.SHA_2_512 -> "SHA512"
|
||||||
|
else -> "NONE"
|
||||||
|
}
|
||||||
|
val keyAlgo =
|
||||||
|
when (params.algorithm) {
|
||||||
|
Algorithm.EC -> "ECDSA"
|
||||||
|
Algorithm.RSA -> "RSA"
|
||||||
|
else ->
|
||||||
|
throw IllegalArgumentException(
|
||||||
|
"Unsupported signature algorithm: ${params.algorithm}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return "${digest}with${keyAlgo}"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mapCipherAlgorithm(params: KeyMintAttestation): String {
|
||||||
|
val keyAlgo =
|
||||||
|
when (params.algorithm) {
|
||||||
|
Algorithm.RSA -> "RSA"
|
||||||
|
Algorithm.AES -> "AES"
|
||||||
|
else ->
|
||||||
|
throw IllegalArgumentException(
|
||||||
|
"Unsupported cipher algorithm: ${params.algorithm}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val blockMode =
|
||||||
|
when (params.blockMode.firstOrNull()) {
|
||||||
|
BlockMode.ECB -> "ECB"
|
||||||
|
BlockMode.CBC -> "CBC"
|
||||||
|
BlockMode.GCM -> "GCM"
|
||||||
|
else -> "ECB" // Default for RSA
|
||||||
|
}
|
||||||
|
val padding =
|
||||||
|
when (params.padding.firstOrNull()) {
|
||||||
|
PaddingMode.NONE -> "NoPadding"
|
||||||
|
PaddingMode.PKCS7 -> "PKCS7Padding"
|
||||||
|
PaddingMode.RSA_PKCS1_1_5_ENCRYPT -> "PKCS1Padding"
|
||||||
|
PaddingMode.RSA_OAEP -> "OAEPPadding"
|
||||||
|
else -> "NoPadding" // Default for GCM
|
||||||
|
}
|
||||||
|
return "$keyAlgo/$blockMode/$padding"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete implementation for Signing.
|
||||||
|
private class Signer(keyPair: KeyPair, params: KeyMintAttestation) : CryptoPrimitive {
|
||||||
|
private val signature: Signature =
|
||||||
|
Signature.getInstance(JcaAlgorithmMapper.mapSignatureAlgorithm(params)).apply {
|
||||||
|
initSign(keyPair.private)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(data: ByteArray?): ByteArray? {
|
||||||
|
if (data != null) signature.update(data)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish(data: ByteArray?, signature: ByteArray?): ByteArray {
|
||||||
|
if (data != null) update(data)
|
||||||
|
return this.signature.sign()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun abort() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete implementation for Verification.
|
||||||
|
private class Verifier(keyPair: KeyPair, params: KeyMintAttestation) : CryptoPrimitive {
|
||||||
|
private val signature: Signature =
|
||||||
|
Signature.getInstance(JcaAlgorithmMapper.mapSignatureAlgorithm(params)).apply {
|
||||||
|
initVerify(keyPair.public)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(data: ByteArray?): ByteArray? {
|
||||||
|
if (data != null) signature.update(data)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish(data: ByteArray?, signature: ByteArray?): ByteArray? {
|
||||||
|
if (data != null) update(data)
|
||||||
|
if (signature == null) throw SignatureException("Signature to verify is null")
|
||||||
|
if (!this.signature.verify(signature)) {
|
||||||
|
// Throwing an exception is how Keystore signals verification failure.
|
||||||
|
throw SignatureException("Signature verification failed")
|
||||||
|
}
|
||||||
|
// A successful verification returns no data.
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun abort() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete implementation for Encryption/Decryption.
|
||||||
|
private class CipherPrimitive(
|
||||||
|
keyPair: KeyPair,
|
||||||
|
params: KeyMintAttestation,
|
||||||
|
private val opMode: Int,
|
||||||
|
) : CryptoPrimitive {
|
||||||
|
private val cipher: Cipher =
|
||||||
|
Cipher.getInstance(JcaAlgorithmMapper.mapCipherAlgorithm(params)).apply {
|
||||||
|
val key = if (opMode == Cipher.ENCRYPT_MODE) keyPair.public else keyPair.private
|
||||||
|
init(opMode, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(data: ByteArray?): ByteArray? =
|
||||||
|
if (data != null) cipher.update(data) else null
|
||||||
|
|
||||||
|
override fun finish(data: ByteArray?, signature: ByteArray?): ByteArray? =
|
||||||
|
if (data != null) cipher.doFinal(data) else cipher.doFinal()
|
||||||
|
|
||||||
|
override fun abort() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A software-only implementation of a cryptographic operation. This class acts as a controller,
|
||||||
|
* delegating to a specific cryptographic primitive based on the operation's purpose.
|
||||||
|
*/
|
||||||
|
class SoftwareOperation(private val txId: Long, keyPair: KeyPair, params: KeyMintAttestation) {
|
||||||
|
// This now holds the specific strategy object (Signer, Verifier, etc.)
|
||||||
|
private val primitive: CryptoPrimitive
|
||||||
|
|
||||||
|
init {
|
||||||
|
// The "Strategy" pattern: choose the implementation based on the purpose.
|
||||||
|
// For simplicity, we only consider the first purpose listed.
|
||||||
|
val purpose = params.purpose.firstOrNull()
|
||||||
|
val purposeName = KeyMintParameterLogger.purposeNames[purpose] ?: "UNKNOWN"
|
||||||
|
SystemLogger.debug("[SoftwareOp TX_ID: $txId] Initializing for purpose: $purposeName.")
|
||||||
|
|
||||||
|
primitive =
|
||||||
|
when (purpose) {
|
||||||
|
KeyPurpose.SIGN -> Signer(keyPair, params)
|
||||||
|
KeyPurpose.VERIFY -> Verifier(keyPair, params)
|
||||||
|
KeyPurpose.ENCRYPT -> CipherPrimitive(keyPair, params, Cipher.ENCRYPT_MODE)
|
||||||
|
KeyPurpose.DECRYPT -> CipherPrimitive(keyPair, params, Cipher.DECRYPT_MODE)
|
||||||
|
else ->
|
||||||
|
throw UnsupportedOperationException("Unsupported operation purpose: $purpose")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(data: ByteArray?): ByteArray? {
|
||||||
|
try {
|
||||||
|
return primitive.update(data)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
SystemLogger.error("[SoftwareOp TX_ID: $txId] Failed to update operation.", e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun finish(data: ByteArray?, signature: ByteArray?): ByteArray? {
|
||||||
|
try {
|
||||||
|
val result = primitive.finish(data, signature)
|
||||||
|
SystemLogger.info("[SoftwareOp TX_ID: $txId] Finished operation successfully.")
|
||||||
|
return result
|
||||||
|
} catch (e: Exception) {
|
||||||
|
SystemLogger.error("[SoftwareOp TX_ID: $txId] Failed to finish operation.", e)
|
||||||
|
// Re-throw the exception so the binder can report it to the client.
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun abort() {
|
||||||
|
primitive.abort()
|
||||||
|
SystemLogger.debug("[SoftwareOp TX_ID: $txId] Operation aborted.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The Binder interface for our [SoftwareOperation]. */
|
||||||
|
class SoftwareOperationBinder(private val operation: SoftwareOperation) :
|
||||||
|
IKeystoreOperation.Stub() {
|
||||||
|
|
||||||
|
@Throws(RemoteException::class)
|
||||||
|
override fun update(input: ByteArray?): ByteArray? {
|
||||||
|
return operation.update(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(RemoteException::class)
|
||||||
|
override fun finish(input: ByteArray?, signature: ByteArray?): ByteArray? {
|
||||||
|
return operation.finish(input, signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(RemoteException::class)
|
||||||
|
override fun abort() {
|
||||||
|
operation.abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
package org.matrix.TEESimulator.logging
|
package org.matrix.TEESimulator.logging
|
||||||
|
|
||||||
import android.hardware.security.keymint.Algorithm
|
import android.hardware.security.keymint.*
|
||||||
import android.hardware.security.keymint.Digest
|
|
||||||
import android.hardware.security.keymint.EcCurve
|
|
||||||
import android.hardware.security.keymint.KeyParameter
|
|
||||||
import android.hardware.security.keymint.KeyPurpose
|
|
||||||
import android.hardware.security.keymint.Tag
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
@@ -34,7 +29,23 @@ object KeyMintParameterLogger {
|
|||||||
.associate { field -> (field.get(null) as Int) to field.name }
|
.associate { field -> (field.get(null) as Int) to field.name }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val purposeNames: Map<Int, String> by lazy {
|
val blockModeNames: Map<Int, String> by lazy {
|
||||||
|
BlockMode::class
|
||||||
|
.java
|
||||||
|
.fields
|
||||||
|
.filter { it.type == Int::class.java }
|
||||||
|
.associate { field -> (field.get(null) as Int) to field.name }
|
||||||
|
}
|
||||||
|
|
||||||
|
val paddingNames: Map<Int, String> by lazy {
|
||||||
|
PaddingMode::class
|
||||||
|
.java
|
||||||
|
.fields
|
||||||
|
.filter { it.type == Int::class.java }
|
||||||
|
.associate { field -> (field.get(null) as Int) to field.name }
|
||||||
|
}
|
||||||
|
|
||||||
|
val purposeNames: Map<Int, String> by lazy {
|
||||||
KeyPurpose::class
|
KeyPurpose::class
|
||||||
.java
|
.java
|
||||||
.fields
|
.fields
|
||||||
@@ -69,7 +80,9 @@ object KeyMintParameterLogger {
|
|||||||
val formattedValue: String =
|
val formattedValue: String =
|
||||||
when (param.tag) {
|
when (param.tag) {
|
||||||
Tag.ALGORITHM -> algorithmNames[value.algorithm]
|
Tag.ALGORITHM -> algorithmNames[value.algorithm]
|
||||||
|
Tag.BLOCK_MODE -> blockModeNames[value.blockMode]
|
||||||
Tag.EC_CURVE -> ecCurveNames[value.ecCurve]
|
Tag.EC_CURVE -> ecCurveNames[value.ecCurve]
|
||||||
|
Tag.PADDING -> paddingNames[value.paddingMode]
|
||||||
Tag.PURPOSE -> purposeNames[value.keyPurpose]
|
Tag.PURPOSE -> purposeNames[value.keyPurpose]
|
||||||
Tag.DIGEST -> digestNames[value.digest]
|
Tag.DIGEST -> digestNames[value.digest]
|
||||||
Tag.AUTH_TIMEOUT,
|
Tag.AUTH_TIMEOUT,
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package android.hardware.security.keymint;
|
||||||
|
|
||||||
|
public @interface BlockMode {
|
||||||
|
public static final int ECB = 1;
|
||||||
|
public static final int CBC = 2;
|
||||||
|
public static final int CTR = 3;
|
||||||
|
public static final int GCM = 32;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package android.hardware.security.keymint;
|
||||||
|
|
||||||
|
public @interface PaddingMode {
|
||||||
|
public static final int NONE = 1;
|
||||||
|
public static final int RSA_OAEP = 2;
|
||||||
|
public static final int RSA_PSS = 3;
|
||||||
|
public static final int RSA_PKCS1_1_5_ENCRYPT = 4;
|
||||||
|
public static final int RSA_PKCS1_1_5_SIGN = 5;
|
||||||
|
public static final int PKCS7 = 64;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package android.system.keystore2;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class CreateOperationResponse implements Parcelable {
|
||||||
|
public IKeystoreOperation iOperation;
|
||||||
|
|
||||||
|
public OperationChallenge operationChallenge;
|
||||||
|
|
||||||
|
public KeyParameters parameters;
|
||||||
|
|
||||||
|
public byte[] upgradedBlob;
|
||||||
|
|
||||||
|
public static final Creator<CreateOperationResponse> CREATOR = new Creator<CreateOperationResponse>() {
|
||||||
|
@Override
|
||||||
|
public CreateOperationResponse createFromParcel(Parcel in) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CreateOperationResponse[] newArray(int size) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(@NonNull Parcel parcel, int i) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package android.system.keystore2;
|
||||||
|
|
||||||
|
public @interface Domain {
|
||||||
|
public static final int APP = 0;
|
||||||
|
public static final int GRANT = 1;
|
||||||
|
public static final int SELINUX = 2;
|
||||||
|
public static final int BLOB = 3;
|
||||||
|
public static final int KEY_ID = 4;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package android.system.keystore2;
|
||||||
|
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.IInterface;
|
||||||
|
|
||||||
|
public interface IKeystoreOperation extends IInterface {
|
||||||
|
public static final java.lang.String DESCRIPTOR = "android.system.keystore2.IKeystoreOperation";
|
||||||
|
|
||||||
|
public void updateAad(byte[] aadInput);
|
||||||
|
|
||||||
|
public byte[] update(byte[] input);
|
||||||
|
|
||||||
|
public byte[] finish(byte[] input, byte[] signature);
|
||||||
|
|
||||||
|
public void abort() throws android.os.RemoteException;
|
||||||
|
|
||||||
|
abstract class Stub extends Binder implements IKeystoreOperation {
|
||||||
|
public static IKeystoreOperation asInterface(IBinder b) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder asBinder() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateAad(byte[] aadInput) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package android.system.keystore2;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.hardware.security.keymint.KeyParameter;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class KeyParameters implements Parcelable {
|
||||||
|
public KeyParameter[] keyParameter;
|
||||||
|
|
||||||
|
public static final Creator<KeyParameters> CREATOR = new Creator<KeyParameters>() {
|
||||||
|
@Override
|
||||||
|
public KeyParameters createFromParcel(Parcel in) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyParameters[] newArray(int size) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(@NonNull Parcel parcel, int i) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package android.system.keystore2;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class OperationChallenge implements Parcelable {
|
||||||
|
public long challenge = 0L;
|
||||||
|
|
||||||
|
public static final Creator<OperationChallenge> CREATOR = new Creator<OperationChallenge>() {
|
||||||
|
@Override
|
||||||
|
public OperationChallenge createFromParcel(Parcel in) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OperationChallenge[] newArray(int size) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(@NonNull Parcel parcel, int i) {
|
||||||
|
throw new UnsupportedOperationException("STUB!");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user