fix(keymint): execute HMAC operations
This commit is contained in:
@@ -58,6 +58,7 @@ data class KeyMintAttestation(
|
||||
val maxUsesPerBoot: Int?,
|
||||
val maxBootLevel: Int?,
|
||||
val minMacLength: Int?,
|
||||
val macLength: Int? = null,
|
||||
val rsaOaepMgfDigest: List<Int>,
|
||||
) {
|
||||
/** Secondary constructor that populates the fields by parsing an array of `KeyParameter`. */
|
||||
@@ -134,6 +135,7 @@ data class KeyMintAttestation(
|
||||
maxUsesPerBoot = params.findInteger(Tag.MAX_USES_PER_BOOT),
|
||||
maxBootLevel = params.findInteger(Tag.MAX_BOOT_LEVEL),
|
||||
minMacLength = params.findInteger(Tag.MIN_MAC_LENGTH),
|
||||
macLength = params.findInteger(Tag.MAC_LENGTH),
|
||||
rsaOaepMgfDigest = params.findAllDigests(Tag.RSA_OAEP_MGF_DIGEST),
|
||||
) {
|
||||
// Log all parsed parameters for debugging purposes.
|
||||
|
||||
+8
-1
@@ -2,6 +2,7 @@ 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.EcCurve
|
||||
import android.hardware.security.keymint.KeyOrigin
|
||||
import android.hardware.security.keymint.KeyParameter
|
||||
@@ -459,6 +460,7 @@ class KeyMintSecurityLevelInterceptor(
|
||||
padding = parsedParams.padding.ifEmpty { keyParams.padding },
|
||||
nonce = parsedParams.nonce,
|
||||
minMacLength = parsedParams.minMacLength ?: keyParams.minMacLength,
|
||||
macLength = parsedParams.macLength,
|
||||
)
|
||||
} else parsedParams
|
||||
|
||||
@@ -798,7 +800,12 @@ class KeyMintSecurityLevelInterceptor(
|
||||
val algoName =
|
||||
when (parsedParams.algorithm) {
|
||||
Algorithm.AES -> "AES"
|
||||
Algorithm.HMAC -> "HmacSHA256"
|
||||
Algorithm.HMAC ->
|
||||
when (parsedParams.digest.firstOrNull()) {
|
||||
Digest.SHA_2_384 -> "HmacSHA384"
|
||||
Digest.SHA_2_512 -> "HmacSHA512"
|
||||
else -> "HmacSHA256"
|
||||
}
|
||||
else ->
|
||||
throw android.os.ServiceSpecificException(
|
||||
KEYMINT_INVALID_ARGUMENT,
|
||||
|
||||
+71
@@ -129,6 +129,14 @@ private object JcaAlgorithmMapper {
|
||||
Digest.SHA_2_512 -> "SHA-512"
|
||||
else -> "SHA-256"
|
||||
}
|
||||
|
||||
fun mapMacAlgorithm(params: KeyMintAttestation): String =
|
||||
when (params.digest.firstOrNull()) {
|
||||
Digest.SHA_2_256 -> "HmacSHA256"
|
||||
Digest.SHA_2_384 -> "HmacSHA384"
|
||||
Digest.SHA_2_512 -> "HmacSHA512"
|
||||
else -> "HmacSHA256"
|
||||
}
|
||||
}
|
||||
|
||||
private class Signer(keyPair: KeyPair, params: KeyMintAttestation) : CryptoPrimitive {
|
||||
@@ -271,6 +279,53 @@ private class KeyAgreementPrimitive(keyPair: KeyPair) : CryptoPrimitive {
|
||||
override fun abort() {}
|
||||
}
|
||||
|
||||
private class MacPrimitive(
|
||||
secretKey: javax.crypto.SecretKey,
|
||||
private val params: KeyMintAttestation,
|
||||
private val txId: Long,
|
||||
) : CryptoPrimitive {
|
||||
private val mac: javax.crypto.Mac =
|
||||
javax.crypto.Mac.getInstance(JcaAlgorithmMapper.mapMacAlgorithm(params)).apply {
|
||||
init(secretKey)
|
||||
}
|
||||
|
||||
override fun update(data: ByteArray?): ByteArray? {
|
||||
if (data != null) mac.update(data)
|
||||
return null
|
||||
}
|
||||
|
||||
override fun finish(data: ByteArray?, signature: ByteArray?): ByteArray? {
|
||||
if (data != null) mac.update(data)
|
||||
val full = mac.doFinal()
|
||||
// Tag.MAC_LENGTH is optional on the AndroidKeyStore Mac SPI; default to the
|
||||
// full digest length so real Mac use keeps working when it is omitted.
|
||||
val tagBytes = (params.macLength ?: (full.size * 8)) / 8
|
||||
val tag = full.copyOf(tagBytes)
|
||||
if (params.purpose.firstOrNull() == KeyPurpose.VERIFY) {
|
||||
if (signature == null) {
|
||||
throw ServiceSpecificException(
|
||||
KeystoreErrorCodes.verificationFailed,
|
||||
"MAC to verify is null",
|
||||
)
|
||||
}
|
||||
if (!java.security.MessageDigest.isEqual(tag, signature)) {
|
||||
throw ServiceSpecificException(
|
||||
KeystoreErrorCodes.verificationFailed,
|
||||
"MAC verification failed",
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
SystemLogger.debug {
|
||||
"[SoftwareOp TX_ID: $txId] hmac-op digest=${params.digest.firstOrNull()} " +
|
||||
"macLen=${params.macLength} tag=${tag.size}B result=ok"
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
override fun abort() {}
|
||||
}
|
||||
|
||||
class SoftwareOperation(
|
||||
private val txId: Long,
|
||||
keyPair: KeyPair?,
|
||||
@@ -319,6 +374,21 @@ class SoftwareOperation(
|
||||
}
|
||||
|
||||
primitive =
|
||||
if (params.algorithm == Algorithm.HMAC) {
|
||||
// An HMAC key is symmetric (secretKey set, keyPair null), so it must
|
||||
// not fall through to the purpose-keyed Signer/Verifier paths, which
|
||||
// require a keyPair. secretKey is populated at HMAC keygen and restore,
|
||||
// so the throw is a defensive floor, not a live path.
|
||||
MacPrimitive(
|
||||
secretKey
|
||||
?: throw ServiceSpecificException(
|
||||
KeystoreErrorCodes.invalidArgument,
|
||||
"[SoftwareOp TX_ID: $txId] HMAC op but secretKey null",
|
||||
),
|
||||
params,
|
||||
txId,
|
||||
)
|
||||
} else {
|
||||
when (purpose) {
|
||||
KeyPurpose.SIGN -> {
|
||||
val kp =
|
||||
@@ -374,6 +444,7 @@ class SoftwareOperation(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkActive() {
|
||||
if (finalized) {
|
||||
|
||||
Reference in New Issue
Block a user