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.
This commit is contained in:
Enginex0
2026-03-26 04:22:22 +01:00
parent ebb6336281
commit 47ab0225e1
4 changed files with 20 additions and 16 deletions
+7 -8
View File
@@ -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<Vec<Vec<u8>>> {
@@ -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<Vec<u8>> {
@@ -63,7 +63,6 @@ fn build_leaf_cert(
timestamp_to_datetime(params.cert_not_after)?
};
// Extensions
let extensions_der = build_extensions(attestation_ext_der, &params.purposes)?;
// TBS Certificate
@@ -256,19 +255,19 @@ fn extract_rsa_spki(pkcs8_der: &[u8]) -> Result<Vec<u8>> {
Ok(encode_der_sequence(&[&alg_id, &pub_key_bits]))
}
fn build_extensions(attestation_ext_der: &[u8], purposes: &[i32]) -> Result<Vec<u8>> {
fn build_extensions(attestation_ext_der: Option<&[u8]>, purposes: &[i32]) -> Result<Vec<u8>> {
let mut extensions: Vec<Vec<u8>> = 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))
}
+5 -2
View File
@@ -62,11 +62,14 @@ fn generate_attested_inner(env: &mut JNIEnv, config: &JObject) -> Result<jbyteAr
let keybox = keybox::parse_keybox(&params.keybox_cert_chain, &params.keybox_private_key)?;
let attest_ext = attestation::build_attestation_extension(&params)?;
let attest_ext = match params.attestation_challenge {
Some(_) => Some(attestation::build_attestation_extension(&params)?),
None => None,
};
let cert_chain = certbuilder::build_certificate_chain(
&key_pair,
&attest_ext,
attest_ext.as_deref(),
&keybox,
&params,
)?;