feat(interception): close remaining PR #157 compliance gaps
Full diff analysis against upstream's 50 commits revealed 8 functional gaps after v5.0. These are detectable by conformance tests or detector apps inspecting KeyMetadata authorizations and operation semantics. KeyMetadata authorizations: - Add 9 TEE-enforced tags (CALLER_NONCE, MIN_MAC_LENGTH, ROLLBACK_RESISTANCE, EARLY_BOOT_ONLY, ALLOW_WHILE_ON_BODY, TRUSTED_USER_PRESENCE_REQUIRED, TRUSTED_CONFIRMATION_REQUIRED, MAX_USES_PER_BOOT, MAX_BOOT_LEVEL) - Fix CREATION_DATETIME to SOFTWARE security level via createSwAuth - Add SOFTWARE-enforced date enforcement, USAGE_COUNT_LIMIT, UNLOCKED_DEVICE_REQUIRED Symmetric key support: - Generate AES/HMAC keys in software via javax.crypto.KeyGenerator - GeneratedKeyInfo expanded with nullable keyPair + secretKey fields - CipherPrimitive accepts java.security.Key for symmetric operations - SoftwareOperation routes ENCRYPT/DECRYPT to secretKey when available Operation compliance: - beginParameters property replaces manual IV wrapping for GCM - KeyAgreementPrimitive for ECDH AGREE_KEY operations - handleCreateOperation wrapped in runCatching (crash prevention) - SECURE_HW_COMMUNICATION_FAILED on software gen failure Certificate patching: - Import key cert chain + authorization patching in onPostTransact - patchAuthorizations added to post-generateKey PATCH mode path
This commit is contained in:
+1
@@ -364,6 +364,7 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
|
||||
KeyMintSecurityLevelInterceptor.generatedKeys[keyId] =
|
||||
KeyMintSecurityLevelInterceptor.GeneratedKeyInfo(
|
||||
keyData.first,
|
||||
null,
|
||||
keyDescriptor.nspace,
|
||||
response,
|
||||
parsedParameters,
|
||||
|
||||
+2
-1
@@ -303,9 +303,10 @@ object GeneratedKeyPersistence {
|
||||
return
|
||||
}
|
||||
|
||||
val keyPair = generatedKeyInfo.keyPair ?: return
|
||||
save(
|
||||
keyId = keyId,
|
||||
keyPair = generatedKeyInfo.keyPair,
|
||||
keyPair = keyPair,
|
||||
nspace = generatedKeyInfo.nspace,
|
||||
securityLevel = secLevel,
|
||||
certChain = newChain.toList(),
|
||||
|
||||
+145
-40
@@ -46,7 +46,8 @@ class KeyMintSecurityLevelInterceptor(
|
||||
) : BinderInterceptor() {
|
||||
|
||||
data class GeneratedKeyInfo(
|
||||
val keyPair: KeyPair,
|
||||
val keyPair: KeyPair?,
|
||||
val secretKey: javax.crypto.SecretKey?,
|
||||
val nspace: Long,
|
||||
val response: KeyEntryResponse,
|
||||
val keyParams: KeyMintAttestation? = null,
|
||||
@@ -136,6 +137,22 @@ class KeyMintSecurityLevelInterceptor(
|
||||
}
|
||||
attestationKeys.remove(keyId)
|
||||
importedKeys.add(keyId)
|
||||
|
||||
if (!ConfigurationManager.shouldSkipUid(callingUid)) {
|
||||
val metadata: KeyMetadata =
|
||||
reply.readTypedObject(KeyMetadata.CREATOR)
|
||||
?: return TransactionResult.SkipTransaction
|
||||
val originalChain = CertificateHelper.getCertificateChain(metadata)
|
||||
if (originalChain != null && originalChain.size > 1) {
|
||||
val newChain = AttestationPatcher.patchCertificateChain(originalChain, callingUid)
|
||||
CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow()
|
||||
metadata.authorizations =
|
||||
InterceptorUtils.patchAuthorizations(metadata.authorizations, callingUid)
|
||||
patchedChains[keyId] = newChain
|
||||
SystemLogger.debug("Cached patched certificate chain for imported key $keyId.")
|
||||
return InterceptorUtils.createTypedObjectReply(metadata)
|
||||
}
|
||||
}
|
||||
} else if (code == CREATE_OPERATION_TRANSACTION) {
|
||||
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
|
||||
|
||||
@@ -189,6 +206,8 @@ class KeyMintSecurityLevelInterceptor(
|
||||
val key = metadata.key!!
|
||||
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
||||
CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow()
|
||||
metadata.authorizations =
|
||||
InterceptorUtils.patchAuthorizations(metadata.authorizations, callingUid)
|
||||
|
||||
// We must clean up cached generated keys before storing the patched chain
|
||||
cleanupKeyData(keyId)
|
||||
@@ -237,7 +256,7 @@ class KeyMintSecurityLevelInterceptor(
|
||||
txId: Long,
|
||||
callingUid: Int,
|
||||
data: Parcel,
|
||||
): TransactionResult {
|
||||
): TransactionResult = runCatching {
|
||||
SystemLogger.debug("[TX_ID: $txId] createOperation parcel: dataSize=${data.dataSize()} dataAvail=${data.dataAvail()} dataPos=${data.dataPosition()}")
|
||||
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
|
||||
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!!
|
||||
@@ -287,11 +306,14 @@ class KeyMintSecurityLevelInterceptor(
|
||||
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
||||
val parsedParams = KeyMintAttestation(params).let { p ->
|
||||
if (p.algorithm != 0) p
|
||||
else p.copy(algorithm = when (generatedKeyInfo.keyPair.private.algorithm) {
|
||||
"EC", "ECDSA" -> Algorithm.EC
|
||||
"RSA" -> Algorithm.RSA
|
||||
else -> p.algorithm
|
||||
})
|
||||
else {
|
||||
val keyAlgo = generatedKeyInfo.keyPair?.private?.algorithm
|
||||
p.copy(algorithm = when (keyAlgo) {
|
||||
"EC", "ECDSA" -> Algorithm.EC
|
||||
"RSA" -> Algorithm.RSA
|
||||
else -> generatedKeyInfo.keyParams?.algorithm ?: p.algorithm
|
||||
})
|
||||
}
|
||||
}
|
||||
val forced = data.readBoolean()
|
||||
|
||||
@@ -318,7 +340,7 @@ class KeyMintSecurityLevelInterceptor(
|
||||
} else parsedParams
|
||||
|
||||
val opLatency = if (securityLevel == SecurityLevel.STRONGBOX) STRONGBOX_OP_LATENCY_FLOOR_MS else 0L
|
||||
val softwareOperation = SoftwareOperation(txId, generatedKeyInfo.keyPair, effectiveParams, opLatency)
|
||||
val softwareOperation = SoftwareOperation(txId, generatedKeyInfo.keyPair, generatedKeyInfo.secretKey, effectiveParams, opLatency)
|
||||
|
||||
if (keyParams?.usageCountLimit != null) {
|
||||
val limit = keyParams.usageCountLimit
|
||||
@@ -328,7 +350,7 @@ class KeyMintSecurityLevelInterceptor(
|
||||
if (remaining.get() <= 0) {
|
||||
cleanupKeyData(resolvedKeyId)
|
||||
usageCounters.remove(resolvedKeyId)
|
||||
throw android.os.ServiceSpecificException(RESPONSE_KEY_NOT_FOUND)
|
||||
return InterceptorUtils.createServiceSpecificErrorReply(RESPONSE_KEY_NOT_FOUND)
|
||||
}
|
||||
softwareOperation.onFinishCallback = {
|
||||
if (remaining.decrementAndGet() <= 0) {
|
||||
@@ -347,19 +369,13 @@ class KeyMintSecurityLevelInterceptor(
|
||||
CreateOperationResponse().apply {
|
||||
iOperation = operationBinder
|
||||
operationChallenge = null
|
||||
softwareOperation.iv?.let { iv ->
|
||||
parameters = KeyParameters().apply {
|
||||
keyParameter = arrayOf(
|
||||
KeyParameter().apply {
|
||||
tag = Tag.NONCE
|
||||
value = KeyParameterValue.blob(iv)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
parameters = softwareOperation.beginParameters
|
||||
}
|
||||
|
||||
return InterceptorUtils.createTypedObjectReply(response)
|
||||
InterceptorUtils.createTypedObjectReply(response)
|
||||
}.getOrElse {
|
||||
SystemLogger.error("Error during createOperation for UID $callingUid.", it)
|
||||
InterceptorUtils.createServiceSpecificErrorReply(KEYMINT_UNKNOWN_ERROR)
|
||||
}
|
||||
|
||||
private fun handleGenerateKey(txId: Long, callingUid: Int, callingPid: Int, data: Parcel): TransactionResult {
|
||||
@@ -427,11 +443,6 @@ class KeyMintSecurityLevelInterceptor(
|
||||
parsedParams.algorithm == Algorithm.HMAC ||
|
||||
parsedParams.algorithm == Algorithm.TRIPLE_DES
|
||||
|
||||
if (isSymmetric) {
|
||||
SystemLogger.debug("[TX_ID: $txId] Symmetric algorithm ${parsedParams.algorithm} → forwarding to HAL")
|
||||
return TransactionResult.ContinueAndSkipPost
|
||||
}
|
||||
|
||||
if (securityLevel == SecurityLevel.STRONGBOX && !isStrongBoxCapable(parsedParams)) {
|
||||
SystemLogger.info("[TX_ID: $txId] StrongBox-unsupported params (algo=${parsedParams.algorithm} size=${parsedParams.keySize}) → forwarding to HAL for rejection")
|
||||
return TransactionResult.ContinueAndSkipPost
|
||||
@@ -475,7 +486,7 @@ class KeyMintSecurityLevelInterceptor(
|
||||
}
|
||||
.getOrElse {
|
||||
SystemLogger.error("Error during generateKey handling for UID $callingUid.", it)
|
||||
TransactionResult.ContinueAndSkipPost
|
||||
InterceptorUtils.createServiceSpecificErrorReply(SECURE_HW_COMMUNICATION_FAILED)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,10 +498,55 @@ class KeyMintSecurityLevelInterceptor(
|
||||
keyId: KeyIdentifier,
|
||||
isAttestKeyRequest: Boolean,
|
||||
): TransactionResult {
|
||||
val startNs = System.nanoTime()
|
||||
val genStartNanos = System.nanoTime()
|
||||
keyDescriptor.nspace = secureRandom.nextLong()
|
||||
SystemLogger.info("Generating software key for ${keyDescriptor.alias}[${keyDescriptor.nspace}].")
|
||||
|
||||
cleanupKeyData(keyId)
|
||||
|
||||
val isSymmetric = parsedParams.algorithm != Algorithm.EC &&
|
||||
parsedParams.algorithm != Algorithm.RSA
|
||||
|
||||
if (isSymmetric) {
|
||||
val algoName = when (parsedParams.algorithm) {
|
||||
Algorithm.AES -> "AES"
|
||||
Algorithm.HMAC -> "HmacSHA256"
|
||||
else -> throw android.os.ServiceSpecificException(
|
||||
SECURE_HW_COMMUNICATION_FAILED,
|
||||
"Unsupported symmetric algorithm: ${parsedParams.algorithm}",
|
||||
)
|
||||
}
|
||||
val keyGen = javax.crypto.KeyGenerator.getInstance(algoName)
|
||||
keyGen.init(parsedParams.keySize)
|
||||
val secretKey = keyGen.generateKey()
|
||||
|
||||
val metadata = KeyMetadata().apply {
|
||||
keySecurityLevel = securityLevel
|
||||
key = KeyDescriptor().apply {
|
||||
domain = Domain.KEY_ID
|
||||
nspace = keyDescriptor.nspace
|
||||
alias = null
|
||||
blob = null
|
||||
}
|
||||
certificate = null
|
||||
certificateChain = null
|
||||
authorizations = parsedParams.toAuthorizations(callingUid, securityLevel)
|
||||
modificationTimeMs = System.currentTimeMillis()
|
||||
}
|
||||
val response = KeyEntryResponse().apply {
|
||||
this.metadata = metadata
|
||||
iSecurityLevel = original
|
||||
}
|
||||
generatedKeys[keyId] = GeneratedKeyInfo(null, secretKey, keyDescriptor.nspace, response, parsedParams)
|
||||
|
||||
val elapsedMs = (System.nanoTime() - genStartNanos) / 1_000_000
|
||||
val floor = if (securityLevel == SecurityLevel.STRONGBOX) STRONGBOX_KEYGEN_LATENCY_FLOOR_MS else TEE_LATENCY_FLOOR_MS
|
||||
val delayMs = floor - elapsedMs
|
||||
if (delayMs > 0) Thread.sleep(delayMs)
|
||||
|
||||
return InterceptorUtils.createTypedObjectReply(metadata)
|
||||
}
|
||||
|
||||
val keyData = if (NativeCertGen.isAvailable && attestationKey == null) {
|
||||
generateAttestedKeyPairNative(callingUid, parsedParams)
|
||||
?: CertificateGenerator.generateAttestedKeyPair(
|
||||
@@ -502,9 +558,8 @@ class KeyMintSecurityLevelInterceptor(
|
||||
)
|
||||
} ?: throw Exception("Both native and BouncyCastle cert gen failed.")
|
||||
|
||||
cleanupKeyData(keyId)
|
||||
val response = buildKeyEntryResponse(callingUid, keyData.second, parsedParams, keyDescriptor)
|
||||
generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, keyDescriptor.nspace, response, parsedParams)
|
||||
generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, null, keyDescriptor.nspace, response, parsedParams)
|
||||
if (isAttestKeyRequest) attestationKeys.add(keyId)
|
||||
|
||||
GeneratedKeyPersistence.save(
|
||||
@@ -521,7 +576,7 @@ class KeyMintSecurityLevelInterceptor(
|
||||
isAttestationKey = isAttestKeyRequest,
|
||||
)
|
||||
|
||||
val elapsedMs = (System.nanoTime() - startNs) / 1_000_000
|
||||
val elapsedMs = (System.nanoTime() - genStartNanos) / 1_000_000
|
||||
val floor = if (securityLevel == SecurityLevel.STRONGBOX) STRONGBOX_KEYGEN_LATENCY_FLOOR_MS else TEE_LATENCY_FLOOR_MS
|
||||
val delayMs = floor - elapsedMs
|
||||
if (delayMs > 0) Thread.sleep(delayMs)
|
||||
@@ -711,7 +766,7 @@ class KeyMintSecurityLevelInterceptor(
|
||||
)
|
||||
|
||||
val response = buildKeyEntryResponse(record.uid, certChain, attestation, descriptor)
|
||||
generatedKeys[keyId] = GeneratedKeyInfo(keyPair, record.nspace, response, attestation)
|
||||
generatedKeys[keyId] = GeneratedKeyInfo(keyPair, null, record.nspace, response, attestation)
|
||||
if (record.isAttestationKey) attestationKeys.add(keyId)
|
||||
|
||||
SystemLogger.debug("Restored persisted key: $keyId")
|
||||
@@ -739,6 +794,8 @@ class KeyMintSecurityLevelInterceptor(
|
||||
private const val STRONGBOX_OP_LATENCY_FLOOR_MS = 80L
|
||||
private const val KEYMINT_TOO_MANY_OPERATIONS = -29
|
||||
private const val KEYMINT_CANNOT_ATTEST_IDS = -66
|
||||
private const val KEYMINT_UNKNOWN_ERROR = -1000
|
||||
private const val SECURE_HW_COMMUNICATION_FAILED = -49
|
||||
private const val MAX_CONCURRENT_OPS_PER_UID = 15
|
||||
private const val STRONGBOX_MAX_CONCURRENT_OPS = 4
|
||||
private const val STRONGBOX_OP_WINDOW_NS = 10_000_000_000L // 10s
|
||||
@@ -900,6 +957,34 @@ private fun KeyMintAttestation.toAuthorizations(
|
||||
if (this.rsaPublicExponent != null) {
|
||||
authList.add(createAuth(Tag.RSA_PUBLIC_EXPONENT, KeyParameterValue.longInteger(this.rsaPublicExponent.toLong())))
|
||||
}
|
||||
if (this.callerNonce == true) {
|
||||
authList.add(createAuth(Tag.CALLER_NONCE, KeyParameterValue.boolValue(true)))
|
||||
}
|
||||
if (this.minMacLength != null) {
|
||||
authList.add(createAuth(Tag.MIN_MAC_LENGTH, KeyParameterValue.integer(this.minMacLength)))
|
||||
}
|
||||
if (this.rollbackResistance == true) {
|
||||
authList.add(createAuth(Tag.ROLLBACK_RESISTANCE, KeyParameterValue.boolValue(true)))
|
||||
}
|
||||
if (this.earlyBootOnly == true) {
|
||||
authList.add(createAuth(Tag.EARLY_BOOT_ONLY, KeyParameterValue.boolValue(true)))
|
||||
}
|
||||
if (this.allowWhileOnBody == true) {
|
||||
authList.add(createAuth(Tag.ALLOW_WHILE_ON_BODY, KeyParameterValue.boolValue(true)))
|
||||
}
|
||||
if (this.trustedUserPresenceRequired == true) {
|
||||
authList.add(createAuth(Tag.TRUSTED_USER_PRESENCE_REQUIRED, KeyParameterValue.boolValue(true)))
|
||||
}
|
||||
if (this.trustedConfirmationRequired == true) {
|
||||
authList.add(createAuth(Tag.TRUSTED_CONFIRMATION_REQUIRED, KeyParameterValue.boolValue(true)))
|
||||
}
|
||||
if (this.maxUsesPerBoot != null) {
|
||||
authList.add(createAuth(Tag.MAX_USES_PER_BOOT, KeyParameterValue.integer(this.maxUsesPerBoot)))
|
||||
}
|
||||
if (this.maxBootLevel != null) {
|
||||
authList.add(createAuth(Tag.MAX_BOOT_LEVEL, KeyParameterValue.integer(this.maxBootLevel)))
|
||||
}
|
||||
|
||||
authList.add(createAuth(Tag.NO_AUTH_REQUIRED, KeyParameterValue.boolValue(true)))
|
||||
authList.add(createAuth(Tag.ORIGIN, KeyParameterValue.origin(this.origin ?: KeyOrigin.GENERATED)))
|
||||
authList.add(createAuth(Tag.OS_VERSION, KeyParameterValue.integer(AndroidDeviceUtils.osVersion)))
|
||||
@@ -916,17 +1001,37 @@ private fun KeyMintAttestation.toAuthorizations(
|
||||
if (bootPatch != AndroidDeviceUtils.DO_NOT_REPORT) {
|
||||
authList.add(createAuth(Tag.BOOT_PATCHLEVEL, KeyParameterValue.integer(bootPatch)))
|
||||
}
|
||||
authList.add(createAuth(Tag.CREATION_DATETIME, KeyParameterValue.dateTime(System.currentTimeMillis())))
|
||||
authList.add(
|
||||
Authorization().apply {
|
||||
this.keyParameter =
|
||||
KeyParameter().apply {
|
||||
this.tag = Tag.USER_ID
|
||||
this.value = KeyParameterValue.integer(callingUid / 100000)
|
||||
}
|
||||
|
||||
fun createSwAuth(tag: Int, value: KeyParameterValue): Authorization {
|
||||
val param = KeyParameter().apply {
|
||||
this.tag = tag
|
||||
this.value = value
|
||||
}
|
||||
return Authorization().apply {
|
||||
this.keyParameter = param
|
||||
this.securityLevel = SecurityLevel.SOFTWARE
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
authList.add(createSwAuth(Tag.CREATION_DATETIME, KeyParameterValue.dateTime(System.currentTimeMillis())))
|
||||
|
||||
this.activeDateTime?.let {
|
||||
authList.add(createSwAuth(Tag.ACTIVE_DATETIME, KeyParameterValue.dateTime(it.time)))
|
||||
}
|
||||
this.originationExpireDateTime?.let {
|
||||
authList.add(createSwAuth(Tag.ORIGINATION_EXPIRE_DATETIME, KeyParameterValue.dateTime(it.time)))
|
||||
}
|
||||
this.usageExpireDateTime?.let {
|
||||
authList.add(createSwAuth(Tag.USAGE_EXPIRE_DATETIME, KeyParameterValue.dateTime(it.time)))
|
||||
}
|
||||
this.usageCountLimit?.let {
|
||||
authList.add(createSwAuth(Tag.USAGE_COUNT_LIMIT, KeyParameterValue.integer(it)))
|
||||
}
|
||||
if (this.unlockedDeviceRequired == true) {
|
||||
authList.add(createSwAuth(Tag.UNLOCKED_DEVICE_REQUIRED, KeyParameterValue.boolValue(true)))
|
||||
}
|
||||
|
||||
authList.add(createSwAuth(Tag.USER_ID, KeyParameterValue.integer(callingUid / 100000)))
|
||||
|
||||
return authList.toTypedArray()
|
||||
}
|
||||
|
||||
+57
-12
@@ -3,10 +3,14 @@ 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.KeyParameter
|
||||
import android.hardware.security.keymint.KeyParameterValue
|
||||
import android.hardware.security.keymint.KeyPurpose
|
||||
import android.hardware.security.keymint.PaddingMode
|
||||
import android.hardware.security.keymint.Tag
|
||||
import android.os.ServiceSpecificException
|
||||
import android.system.keystore2.IKeystoreOperation
|
||||
import android.system.keystore2.KeyParameters
|
||||
import java.security.KeyPair
|
||||
import java.security.Signature
|
||||
import java.security.SignatureException
|
||||
@@ -22,7 +26,7 @@ private sealed interface CryptoPrimitive {
|
||||
fun update(data: ByteArray?): ByteArray?
|
||||
fun finish(data: ByteArray?, signature: ByteArray?): ByteArray?
|
||||
fun abort()
|
||||
fun getIv(): ByteArray? = null
|
||||
fun getBeginParameters(): Array<KeyParameter>? = null
|
||||
}
|
||||
|
||||
private object JcaAlgorithmMapper {
|
||||
@@ -125,15 +129,14 @@ private class Verifier(keyPair: KeyPair, params: KeyMintAttestation) : CryptoPri
|
||||
}
|
||||
|
||||
private class CipherPrimitive(
|
||||
keyPair: KeyPair,
|
||||
cryptoKey: java.security.Key,
|
||||
params: KeyMintAttestation,
|
||||
private val opMode: Int,
|
||||
) : CryptoPrimitive {
|
||||
private val isAead = params.blockMode.firstOrNull() == BlockMode.GCM
|
||||
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)
|
||||
init(opMode, cryptoKey)
|
||||
}
|
||||
|
||||
override fun updateAad(aadInput: ByteArray?) {
|
||||
@@ -147,14 +150,45 @@ private class CipherPrimitive(
|
||||
override fun finish(data: ByteArray?, signature: ByteArray?): ByteArray? =
|
||||
if (data != null) cipher.doFinal(data) else cipher.doFinal()
|
||||
|
||||
override fun getIv(): ByteArray? = if (isAead) cipher.iv else null
|
||||
override fun getBeginParameters(): Array<KeyParameter>? {
|
||||
val iv = cipher.iv ?: return null
|
||||
return arrayOf(
|
||||
KeyParameter().apply {
|
||||
tag = Tag.NONCE
|
||||
value = KeyParameterValue.blob(iv)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun abort() {}
|
||||
}
|
||||
|
||||
private class KeyAgreementPrimitive(keyPair: KeyPair) : CryptoPrimitive {
|
||||
private val agreement: javax.crypto.KeyAgreement =
|
||||
javax.crypto.KeyAgreement.getInstance("ECDH").apply { init(keyPair.private) }
|
||||
|
||||
override fun update(data: ByteArray?): ByteArray? = null
|
||||
|
||||
override fun finish(data: ByteArray?, signature: ByteArray?): ByteArray? {
|
||||
if (data == null)
|
||||
throw ServiceSpecificException(
|
||||
KeystoreErrorCodes.invalidArgument,
|
||||
"Peer public key required for key agreement",
|
||||
)
|
||||
val peerKey =
|
||||
java.security.KeyFactory.getInstance("EC")
|
||||
.generatePublic(java.security.spec.X509EncodedKeySpec(data))
|
||||
agreement.doPhase(peerKey, true)
|
||||
return agreement.generateSecret()
|
||||
}
|
||||
|
||||
override fun abort() {}
|
||||
}
|
||||
|
||||
class SoftwareOperation(
|
||||
private val txId: Long,
|
||||
keyPair: KeyPair,
|
||||
keyPair: KeyPair?,
|
||||
secretKey: javax.crypto.SecretKey?,
|
||||
params: KeyMintAttestation,
|
||||
private val latencyFloorMs: Long = 0L,
|
||||
) {
|
||||
@@ -164,8 +198,12 @@ class SoftwareOperation(
|
||||
|
||||
var onFinishCallback: (() -> Unit)? = null
|
||||
|
||||
val iv: ByteArray?
|
||||
get() = primitive.getIv()
|
||||
val beginParameters: KeyParameters?
|
||||
get() {
|
||||
val params = primitive.getBeginParameters() ?: return null
|
||||
if (params.isEmpty()) return null
|
||||
return KeyParameters().apply { keyParameter = params }
|
||||
}
|
||||
|
||||
init {
|
||||
val purpose = params.purpose.firstOrNull()
|
||||
@@ -174,10 +212,17 @@ class SoftwareOperation(
|
||||
|
||||
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)
|
||||
KeyPurpose.SIGN -> Signer(keyPair!!, params)
|
||||
KeyPurpose.VERIFY -> Verifier(keyPair!!, params)
|
||||
KeyPurpose.ENCRYPT -> {
|
||||
val key: java.security.Key = secretKey ?: keyPair!!.public
|
||||
CipherPrimitive(key, params, Cipher.ENCRYPT_MODE)
|
||||
}
|
||||
KeyPurpose.DECRYPT -> {
|
||||
val key: java.security.Key = secretKey ?: keyPair!!.private
|
||||
CipherPrimitive(key, params, Cipher.DECRYPT_MODE)
|
||||
}
|
||||
KeyPurpose.AGREE_KEY -> KeyAgreementPrimitive(keyPair!!)
|
||||
else ->
|
||||
throw ServiceSpecificException(
|
||||
KeystoreErrorCodes.unsupportedPurpose,
|
||||
|
||||
Reference in New Issue
Block a user