fix(attestation): reject oversized challenges and rewrite cert DER encoding
DuckDetector flagged two issues: 1. Oversized challenge accepted, 256-byte attestation challenge should return INVALID_INPUT_LENGTH (-21) like real KeyMint. Added early check in handleGenerateKey before any path decision. 2. Issuer/subject chain mismatch, rcgen's HashMap loses DN attribute ordering and converts PrintableString to UTF8String, producing different DER bytes. Replaced rcgen with manual DER assembly that injects raw keybox issuer_dn_der bytes directly. Verified on device: TX_ID 315 rejects 256-byte challenge, TX_ID 501 generates valid 4-cert chain with correct issuer linkage.
This commit is contained in:
@@ -12,6 +12,17 @@ data class KeyIdentifier(val uid: Int, val alias: String)
|
||||
/** A collection of utility functions to support binder interception. */
|
||||
object InterceptorUtils {
|
||||
|
||||
private const val EX_SERVICE_SPECIFIC = -8
|
||||
|
||||
fun createErrorReply(errorCode: Int): BinderInterceptor.TransactionResult.OverrideReply {
|
||||
val parcel = Parcel.obtain().apply {
|
||||
writeInt(EX_SERVICE_SPECIFIC)
|
||||
writeInt(errorCode)
|
||||
writeString(null)
|
||||
}
|
||||
return BinderInterceptor.TransactionResult.OverrideReply(parcel)
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses reflection to get the integer transaction code for a given method name from a Stub
|
||||
* class. This is necessary for older Android versions where codes are not public constants.
|
||||
|
||||
+9
@@ -18,6 +18,7 @@ import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import org.matrix.TEESimulator.attestation.AttestationBuilder
|
||||
import org.matrix.TEESimulator.attestation.AttestationConstants
|
||||
import org.matrix.TEESimulator.attestation.AttestationPatcher
|
||||
import org.matrix.TEESimulator.attestation.KeyMintAttestation
|
||||
import org.matrix.TEESimulator.config.ConfigurationManager
|
||||
@@ -246,6 +247,13 @@ class KeyMintSecurityLevelInterceptor(
|
||||
)
|
||||
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
||||
val parsedParams = KeyMintAttestation(params)
|
||||
|
||||
val challenge = parsedParams.attestationChallenge
|
||||
if (challenge != null && challenge.size > AttestationConstants.CHALLENGE_LENGTH_LIMIT) {
|
||||
SystemLogger.warning("[TX_ID: $txId] Rejecting oversized attestation challenge: ${challenge.size} bytes (max ${AttestationConstants.CHALLENGE_LENGTH_LIMIT})")
|
||||
return InterceptorUtils.createErrorReply(KEYMINT_INVALID_INPUT_LENGTH)
|
||||
}
|
||||
|
||||
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
|
||||
val isAttestKeyRequest = parsedParams.isAttestKey()
|
||||
|
||||
@@ -506,6 +514,7 @@ class KeyMintSecurityLevelInterceptor(
|
||||
// Maximum alias length to prevent binder buffer exhaustion (Issue #109)
|
||||
// Binder buffer is ~1MB; 256KB provides 4x safety margin for transaction overhead
|
||||
private const val MAX_ALIAS_LENGTH = 256 * 1024
|
||||
private const val KEYMINT_INVALID_INPUT_LENGTH = -21
|
||||
private const val MAX_CONCURRENT_HW_KEYGEN_PER_UID = 2
|
||||
// Sliding window: max hardware keygen permits per UID within the burst window
|
||||
private const val MAX_HW_KEYGEN_PER_WINDOW = 2
|
||||
|
||||
Reference in New Issue
Block a user