From 4a96491e63bab1f905c2505a9d24562300d24ff8 Mon Sep 17 00:00:00 2001 From: Enginex0 Date: Tue, 10 Mar 2026 14:23:33 +0100 Subject: [PATCH] fix(attestation): correct leaf CN casing and enforce keystore2 parameter policy Leaf cert Subject CN used "KeyStore" (capital S) but AOSP KeyGenParameterSpec uses "Keystore" (lowercase s). Fixed in both the Rust native certgen and BouncyCastle paths. Replicate keystore2's security_level.rs parameter validation for software-generated keys: reject CREATION_DATETIME (output-only tag, ResponseCode 20) and device ID attestation tags (CANNOT_ATTEST_IDS -66) that real keystore2 blocks before they reach the HAL. Also fix createErrorReply parcel write order, AIDL protocol expects exception_code, message, error_code but we had message and error_code swapped, causing malformed replies for positive error codes. --- .../interception/keystore/InterceptorUtils.kt | 2 +- .../shim/KeyMintSecurityLevelInterceptor.kt | 14 ++++++++++++++ .../TEESimulator/pki/CertificateGenerator.kt | 2 +- native-certgen/src/certbuilder.rs | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/InterceptorUtils.kt b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/InterceptorUtils.kt index d187f1e..bbfaeb4 100644 --- a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/InterceptorUtils.kt +++ b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/InterceptorUtils.kt @@ -17,8 +17,8 @@ object InterceptorUtils { fun createErrorReply(errorCode: Int): BinderInterceptor.TransactionResult.OverrideReply { val parcel = Parcel.obtain().apply { writeInt(EX_SERVICE_SPECIFIC) - writeInt(errorCode) writeString(null) + writeInt(errorCode) } return BinderInterceptor.TransactionResult.OverrideReply(parcel) } diff --git a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt index 30fe8e6..aadcad8 100644 --- a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt +++ b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt @@ -254,6 +254,18 @@ class KeyMintSecurityLevelInterceptor( return InterceptorUtils.createErrorReply(KEYMINT_INVALID_INPUT_LENGTH) } + if (params.any { it.tag == Tag.CREATION_DATETIME }) { + SystemLogger.warning("[TX_ID: $txId] Rejecting CREATION_DATETIME in generateKey params") + return InterceptorUtils.createErrorReply(RESPONSE_INVALID_ARGUMENT) + } + + if (parsedParams.serial != null || parsedParams.imei != null || + parsedParams.meid != null || parsedParams.secondImei != null || + params.any { it.tag == Tag.DEVICE_UNIQUE_ATTESTATION }) { + SystemLogger.warning("[TX_ID: $txId] Rejecting device ID attestation for uid=$callingUid") + return InterceptorUtils.createErrorReply(KEYMINT_CANNOT_ATTEST_IDS) + } + val keyId = KeyIdentifier(callingUid, keyDescriptor.alias) val isAttestKeyRequest = parsedParams.isAttestKey() @@ -515,6 +527,8 @@ class KeyMintSecurityLevelInterceptor( // 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 RESPONSE_INVALID_ARGUMENT = 20 + private const val KEYMINT_CANNOT_ATTEST_IDS = -66 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 diff --git a/app/src/main/java/org/matrix/TEESimulator/pki/CertificateGenerator.kt b/app/src/main/java/org/matrix/TEESimulator/pki/CertificateGenerator.kt index a5b3517..63c4c70 100644 --- a/app/src/main/java/org/matrix/TEESimulator/pki/CertificateGenerator.kt +++ b/app/src/main/java/org/matrix/TEESimulator/pki/CertificateGenerator.kt @@ -213,7 +213,7 @@ object CertificateGenerator { uid: Int, securityLevel: Int, ): Certificate { - val subject = params.certificateSubject ?: X500Name("CN=Android KeyStore Key") + val subject = params.certificateSubject ?: X500Name("CN=Android Keystore Key") val leafNotAfter = (signingKeyPair.public as? X509Certificate)?.notAfter ?: Date(System.currentTimeMillis() + 31536000000L) diff --git a/native-certgen/src/certbuilder.rs b/native-certgen/src/certbuilder.rs index 77d87a8..d87b7a2 100644 --- a/native-certgen/src/certbuilder.rs +++ b/native-certgen/src/certbuilder.rs @@ -51,7 +51,7 @@ fn build_leaf_cert( let subject_dn_der = if let Some(ref subject) = params.cert_subject { subject.clone() } else { - encode_simple_cn_dn("Android KeyStore Key") + encode_simple_cn_dn("Android Keystore Key") }; // Validity