From 47ab0225e1b41b7154f421fdb9f692ac5782bc32 Mon Sep 17 00:00:00 2001 From: Enginex0 Date: Thu, 26 Mar 2026 04:22:22 +0100 Subject: [PATCH] fix(certgen): omit attestation extension when no challenge provided AOSP KeyMint only includes the attestation extension (OID 1.3.6.1.4.1.11129.2.1.17) when ATTESTATION_CHALLENGE is present. Without a challenge, generateKey produces a plain self-signed cert. Our code unconditionally added the extension, which behavioral probes detect by generating a key without a challenge and checking for the OID. Fixes both the Rust native-certgen and BouncyCastle paths. Also skips AAID computation when no challenge is provided, matching keystore2 security_level.rs:457 behavior. --- .../shim/KeyMintSecurityLevelInterceptor.kt | 5 +++-- .../TEESimulator/pki/CertificateGenerator.kt | 9 +++++---- native-certgen/src/certbuilder.rs | 15 +++++++-------- native-certgen/src/lib.rs | 7 +++++-- 4 files changed, 20 insertions(+), 16 deletions(-) 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 086d0e6..c65586f 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 @@ -708,7 +708,8 @@ class KeyMintSecurityLevelInterceptor( val attestVersion = AndroidDeviceUtils.getAttestVersion(securityLevel) val keymasterVersion = AndroidDeviceUtils.getKeymasterVersion(securityLevel) - val appId = AttestationBuilder.createApplicationId(callingUid) + val hasChallenge = params.attestationChallenge != null + val appId = if (hasChallenge) AttestationBuilder.createApplicationId(callingUid) else null val config = CertGenConfig( algorithm = params.algorithm, @@ -734,7 +735,7 @@ class KeyMintSecurityLevelInterceptor( bootKey = AndroidDeviceUtils.bootKey, bootHash = AndroidDeviceUtils.bootHash, creationDatetime = System.currentTimeMillis(), - attestationApplicationId = appId.octets, + attestationApplicationId = appId?.octets ?: ByteArray(0), moduleHash = if (attestVersion >= 400) AndroidDeviceUtils.moduleHash else null, idBrand = params.brand, idDevice = params.device, 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 4b27058..6b25b31 100644 --- a/app/src/main/java/org/matrix/TEESimulator/pki/CertificateGenerator.kt +++ b/app/src/main/java/org/matrix/TEESimulator/pki/CertificateGenerator.kt @@ -238,10 +238,11 @@ object CertificateGenerator { if (keyUsageBits != 0) { builder.addExtension(Extension.keyUsage, true, KeyUsage(keyUsageBits)) } - // Add our custom, simulated attestation extension. - builder.addExtension( - AttestationBuilder.buildAttestationExtension(params, uid, securityLevel) - ) + if (params.attestationChallenge != null) { + builder.addExtension( + AttestationBuilder.buildAttestationExtension(params, uid, securityLevel) + ) + } val signerAlgorithm = when (signingKeyPair.private.algorithm) { diff --git a/native-certgen/src/certbuilder.rs b/native-certgen/src/certbuilder.rs index d87b7a2..e0c77fc 100644 --- a/native-certgen/src/certbuilder.rs +++ b/native-certgen/src/certbuilder.rs @@ -16,7 +16,7 @@ const OID_KEY_USAGE: &[u64] = &[2, 5, 29, 15]; pub fn build_certificate_chain( key_pair: &GeneratedKeyPair, - attestation_ext_der: &[u8], + attestation_ext_der: Option<&[u8]>, keybox: &ParsedKeybox, params: &CertGenParams, ) -> Result>> { @@ -33,7 +33,7 @@ pub fn build_certificate_chain( fn build_leaf_cert( key_pair: &GeneratedKeyPair, - attestation_ext_der: &[u8], + attestation_ext_der: Option<&[u8]>, keybox: &ParsedKeybox, params: &CertGenParams, ) -> Result> { @@ -63,7 +63,6 @@ fn build_leaf_cert( timestamp_to_datetime(params.cert_not_after)? }; - // Extensions let extensions_der = build_extensions(attestation_ext_der, ¶ms.purposes)?; // TBS Certificate @@ -256,19 +255,19 @@ fn extract_rsa_spki(pkcs8_der: &[u8]) -> Result> { Ok(encode_der_sequence(&[&alg_id, &pub_key_bits])) } -fn build_extensions(attestation_ext_der: &[u8], purposes: &[i32]) -> Result> { +fn build_extensions(attestation_ext_der: Option<&[u8]>, purposes: &[i32]) -> Result> { let mut extensions: Vec> = Vec::new(); - // KeyUsage extension (critical) let ku_byte = map_key_usage_byte(purposes); if ku_byte != 0 { let ku_ext = build_key_usage_extension(ku_byte); extensions.push(ku_ext); } - // Attestation extension (non-critical) - let attest_ext = build_extension(&encode_der_oid(ATTESTATION_OID), false, attestation_ext_der); - extensions.push(attest_ext); + if let Some(attest_der) = attestation_ext_der { + let attest_ext = build_extension(&encode_der_oid(ATTESTATION_OID), false, attest_der); + extensions.push(attest_ext); + } Ok(encode_der_sequence_of(&extensions)) } diff --git a/native-certgen/src/lib.rs b/native-certgen/src/lib.rs index 0e1abc3..c1275a9 100644 --- a/native-certgen/src/lib.rs +++ b/native-certgen/src/lib.rs @@ -62,11 +62,14 @@ fn generate_attested_inner(env: &mut JNIEnv, config: &JObject) -> Result Some(attestation::build_attestation_extension(¶ms)?), + None => None, + }; let cert_chain = certbuilder::build_certificate_chain( &key_pair, - &attest_ext, + attest_ext.as_deref(), &keybox, ¶ms, )?;