Refactor L0 key handling ahead of fix

Bug: 241241178
Test: treehugger
Change-Id: I3b9c6dcda0a9b8d57d6d4e8be8f9eddf59d3b89f
diff --git a/keystore2/src/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
index 08c52af..6320c7b 100644
--- a/keystore2/src/boot_level_keys.rs
+++ b/keystore2/src/boot_level_keys.rs
@@ -21,26 +21,37 @@
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, Digest::Digest, KeyParameter::KeyParameter as KmKeyParameter,
-    KeyParameterValue::KeyParameterValue as KmKeyParameterValue, KeyPurpose::KeyPurpose,
-    SecurityLevel::SecurityLevel, Tag::Tag,
+    KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
 };
 use anyhow::{Context, Result};
 use keystore2_crypto::{hkdf_expand, ZVec, AES_256_KEY_LENGTH};
 use std::{collections::VecDeque, convert::TryFrom};
 
-fn get_preferred_km_instance_for_level_zero_key() -> Result<KeyMintDevice> {
+/// Strategies used to prevent later boot stages from using the KM key that protects the level 0
+/// key
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+enum DenyLaterStrategy {
+    /// set MaxUsesPerBoot to 1. This is much less secure, since the attacker can replace the key
+    /// itself, and therefore create artifacts which appear to come from early boot.
+    MaxUsesPerBoot,
+    /// set the EarlyBootOnly property. This property is only supported in KM from 4.1 on, but
+    /// it ensures that the level 0 key was genuinely created in early boot
+    EarlyBootOnly,
+}
+
+fn get_level_zero_key_km_and_strategy() -> Result<(KeyMintDevice, DenyLaterStrategy)> {
     let tee = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
-        .context("In get_preferred_km_instance_for_level_zero_key: Get TEE instance failed.")?;
+        .context("In get_level_zero_key_km_and_strategy: Get TEE instance failed.")?;
     if tee.version() >= KeyMintDevice::KEY_MASTER_V4_1 {
-        Ok(tee)
+        Ok((tee, DenyLaterStrategy::EarlyBootOnly))
     } else {
-        match KeyMintDevice::get_or_none(SecurityLevel::STRONGBOX).context(
-            "In get_preferred_km_instance_for_level_zero_key: Get Strongbox instance failed.",
-        )? {
+        match KeyMintDevice::get_or_none(SecurityLevel::STRONGBOX)
+            .context("In get_level_zero_key_km_and_strategy: Get Strongbox instance failed.")?
+        {
             Some(strongbox) if strongbox.version() >= KeyMintDevice::KEY_MASTER_V4_1 => {
-                Ok(strongbox)
+                Ok((strongbox, DenyLaterStrategy::EarlyBootOnly))
             }
-            _ => Ok(tee),
+            _ => Ok((tee, DenyLaterStrategy::MaxUsesPerBoot)),
         }
     }
 }
@@ -49,52 +60,49 @@
 /// In practice the caller is SuperKeyManager and the lock is the
 /// Mutex on its internal state.
 pub fn get_level_zero_key(db: &mut KeystoreDB) -> Result<ZVec> {
-    let km_dev = get_preferred_km_instance_for_level_zero_key()
+    let (km_dev, deny_later_strategy) = get_level_zero_key_km_and_strategy()
         .context("In get_level_zero_key: get preferred KM instance failed")?;
-
-    let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string());
-    let mut params = vec![
+    log::info!(
+        "In get_level_zero_key: security_level={:?}, deny_later_strategy={:?}",
+        km_dev.security_level(),
+        deny_later_strategy
+    );
+    let required_security_level = km_dev.security_level();
+    let required_param: KmKeyParameter = match deny_later_strategy {
+        DenyLaterStrategy::EarlyBootOnly => KeyParameterValue::EarlyBootOnly,
+        DenyLaterStrategy::MaxUsesPerBoot => KeyParameterValue::MaxUsesPerBoot(1),
+    }
+    .into();
+    let params = vec![
         KeyParameterValue::Algorithm(Algorithm::HMAC).into(),
         KeyParameterValue::Digest(Digest::SHA_2_256).into(),
         KeyParameterValue::KeySize(256).into(),
         KeyParameterValue::MinMacLength(256).into(),
         KeyParameterValue::KeyPurpose(KeyPurpose::SIGN).into(),
         KeyParameterValue::NoAuthRequired.into(),
+        required_param.clone(),
     ];
 
-    let has_early_boot_only = km_dev.version() >= KeyMintDevice::KEY_MASTER_V4_1;
-
-    if has_early_boot_only {
-        params.push(KeyParameterValue::EarlyBootOnly.into());
-    } else {
-        params.push(KeyParameterValue::MaxUsesPerBoot(1).into())
-    }
-
+    let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string());
     let (key_id_guard, key_entry) = km_dev
         .lookup_or_generate_key(db, &key_desc, KeyType::Client, &params, |key_characteristics| {
             key_characteristics.iter().any(|kc| {
-                if kc.securityLevel == km_dev.security_level() {
-                    kc.authorizations.iter().any(|a| {
-                        matches!(
-                            (has_early_boot_only, a),
-                            (
-                                true,
-                                KmKeyParameter {
-                                    tag: Tag::EARLY_BOOT_ONLY,
-                                    value: KmKeyParameterValue::BoolValue(true)
-                                }
-                            ) | (
-                                false,
-                                KmKeyParameter {
-                                    tag: Tag::MAX_USES_PER_BOOT,
-                                    value: KmKeyParameterValue::Integer(1)
-                                }
-                            )
-                        )
-                    })
-                } else {
-                    false
+                if kc.securityLevel != required_security_level {
+                    log::error!(
+                        "In get_level_zero_key: security level expected={:?} got={:?}",
+                        required_security_level,
+                        kc.securityLevel
+                    );
+                    return false;
                 }
+                if !kc.authorizations.iter().any(|a| a == &required_param) {
+                    log::error!(
+                        "In get_level_zero_key: required param absent {:?}",
+                        required_param
+                    );
+                    return false;
+                }
+                true
             })
         })
         .context("In get_level_zero_key: lookup_or_generate_key failed")?;