fix(keymint): enforce operation authorizations
Add checkOperationAuthorizations to the AuthorizeCreate chain so the interceptor rejects operations whose parameters are incompatible with the key, matching real KeyMint HAL behavior: - block mode, padding, digest, and RSA-OAEP MGF digest must each be a subset of the key's authorized set; - AES-GCM rejects a requested MAC length below the key minimum; - RSA-OAEP requires a digest. Add the four backing KeyMint error codes (INCOMPATIBLE_BLOCK_MODE, INCOMPATIBLE_PADDING_MODE, INCOMPATIBLE_DIGEST, INVALID_MAC_LENGTH) to KeystoreErrorCodes, resolved at runtime with AOSP-correct fallbacks. The check reads the raw request params (AuthorizeCreate.check is called with parsedParams), so no op-param construction change is needed, and execution is unaffected: our SoftwareOperation already runs GCM (128-bit tag) and OAEP.
This commit is contained in:
+43
@@ -1,8 +1,10 @@
|
|||||||
package org.matrix.TEESimulator.interception.keystore.shim
|
package org.matrix.TEESimulator.interception.keystore.shim
|
||||||
|
|
||||||
import android.hardware.security.keymint.Algorithm
|
import android.hardware.security.keymint.Algorithm
|
||||||
|
import android.hardware.security.keymint.BlockMode
|
||||||
import android.hardware.security.keymint.KeyParameter
|
import android.hardware.security.keymint.KeyParameter
|
||||||
import android.hardware.security.keymint.KeyPurpose
|
import android.hardware.security.keymint.KeyPurpose
|
||||||
|
import android.hardware.security.keymint.PaddingMode
|
||||||
import android.hardware.security.keymint.Tag
|
import android.hardware.security.keymint.Tag
|
||||||
import org.matrix.TEESimulator.attestation.KeyMintAttestation
|
import org.matrix.TEESimulator.attestation.KeyMintAttestation
|
||||||
|
|
||||||
@@ -18,6 +20,7 @@ object AuthorizeCreate {
|
|||||||
// Algorithm-level rejection runs before purpose-list check (AOSP HAL behavior)
|
// Algorithm-level rejection runs before purpose-list check (AOSP HAL behavior)
|
||||||
return checkAlgorithmPurpose(keyParams, purpose)
|
return checkAlgorithmPurpose(keyParams, purpose)
|
||||||
?: checkPurpose(keyParams, purpose)
|
?: checkPurpose(keyParams, purpose)
|
||||||
|
?: checkOperationAuthorizations(keyParams, opParams)
|
||||||
?: checkTemporalValidity(keyParams, purpose)
|
?: checkTemporalValidity(keyParams, purpose)
|
||||||
?: checkCallerNonce(keyParams, purpose, rawOpParams)
|
?: checkCallerNonce(keyParams, purpose, rawOpParams)
|
||||||
}
|
}
|
||||||
@@ -41,6 +44,46 @@ object AuthorizeCreate {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkOperationAuthorizations(
|
||||||
|
keyParams: KeyMintAttestation,
|
||||||
|
opParams: KeyMintAttestation,
|
||||||
|
): Int? {
|
||||||
|
if (opParams.blockMode.any { it !in keyParams.blockMode }) {
|
||||||
|
return KeystoreErrorCodes.incompatibleBlockMode
|
||||||
|
}
|
||||||
|
if (opParams.padding.any { it !in keyParams.padding }) {
|
||||||
|
return KeystoreErrorCodes.incompatiblePaddingMode
|
||||||
|
}
|
||||||
|
if (opParams.digest.any { it !in keyParams.digest }) {
|
||||||
|
return KeystoreErrorCodes.incompatibleDigest
|
||||||
|
}
|
||||||
|
if (opParams.rsaOaepMgfDigest.any { it !in keyParams.rsaOaepMgfDigest }) {
|
||||||
|
return KeystoreErrorCodes.incompatibleDigest
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyParams.algorithm == Algorithm.AES && opParams.blockMode.contains(BlockMode.GCM)) {
|
||||||
|
val requestedMacLength = opParams.minMacLength
|
||||||
|
val keyMinMacLength = keyParams.minMacLength
|
||||||
|
if (
|
||||||
|
requestedMacLength != null &&
|
||||||
|
keyMinMacLength != null &&
|
||||||
|
requestedMacLength < keyMinMacLength
|
||||||
|
) {
|
||||||
|
return KeystoreErrorCodes.invalidMacLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
keyParams.algorithm == Algorithm.RSA &&
|
||||||
|
opParams.padding.contains(PaddingMode.RSA_OAEP) &&
|
||||||
|
opParams.digest.isEmpty()
|
||||||
|
) {
|
||||||
|
return KeystoreErrorCodes.incompatibleDigest
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkTemporalValidity(keyParams: KeyMintAttestation, purpose: Int): Int? {
|
private fun checkTemporalValidity(keyParams: KeyMintAttestation, purpose: Int): Int? {
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
|
|
||||||
|
|||||||
+16
@@ -501,6 +501,22 @@ internal object KeystoreErrorCodes {
|
|||||||
resolveField("android.hardware.security.keymint.ErrorCode", "UNKNOWN_ERROR", -1000)
|
resolveField("android.hardware.security.keymint.ErrorCode", "UNKNOWN_ERROR", -1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val incompatibleBlockMode: Int by lazy {
|
||||||
|
resolveField("android.hardware.security.keymint.ErrorCode", "INCOMPATIBLE_BLOCK_MODE", -8)
|
||||||
|
}
|
||||||
|
|
||||||
|
val incompatiblePaddingMode: Int by lazy {
|
||||||
|
resolveField("android.hardware.security.keymint.ErrorCode", "INCOMPATIBLE_PADDING_MODE", -11)
|
||||||
|
}
|
||||||
|
|
||||||
|
val incompatibleDigest: Int by lazy {
|
||||||
|
resolveField("android.hardware.security.keymint.ErrorCode", "INCOMPATIBLE_DIGEST", -13)
|
||||||
|
}
|
||||||
|
|
||||||
|
val invalidMacLength: Int by lazy {
|
||||||
|
resolveField("android.hardware.security.keymint.ErrorCode", "INVALID_MAC_LENGTH", -57)
|
||||||
|
}
|
||||||
|
|
||||||
fun resolveField(className: String, fieldName: String, fallback: Int): Int =
|
fun resolveField(className: String, fieldName: String, fallback: Int): Int =
|
||||||
runCatching { Class.forName(className).getField(fieldName).getInt(null) }
|
runCatching { Class.forName(className).getField(fieldName).getInt(null) }
|
||||||
.getOrElse {
|
.getOrElse {
|
||||||
|
|||||||
Reference in New Issue
Block a user