Keystore 2.0: Enforce MAX_BOOT_LEVEL in software

Test: Initialize keystore.boot_level in init.rc; then
      adb setprop keystore.boot_level 40 and check logs
Test: Test program creates key with MAX_BOOT_LEVEL tag; ensure
      it can be used before bumping keystore.boot_level but not after
Bug: 176450483
Change-Id: I94ea178e0fd524bf0a5d65b016559ddd7766205f
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index cc59c32..aa852f4 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -32,14 +32,15 @@
 };
 use android_system_keystore2::binder::Strong;
 use anyhow::{Context, Result};
-use std::sync::{
-    mpsc::{channel, Receiver, Sender},
-    Arc, Mutex, Weak,
-};
-use std::time::SystemTime;
+use keystore2_system_property::PropertyWatcher;
 use std::{
     collections::{HashMap, HashSet},
-    sync::mpsc::TryRecvError,
+    sync::{
+        atomic::{AtomicI32, Ordering},
+        mpsc::{channel, Receiver, Sender, TryRecvError},
+        Arc, Mutex, Weak,
+    },
+    time::SystemTime,
 };
 
 #[derive(Debug)]
@@ -352,6 +353,7 @@
 }
 
 /// Enforcements data structure
+#[derive(Default)]
 pub struct Enforcements {
     /// This hash set contains the user ids for whom the device is currently unlocked. If a user id
     /// is not in the set, it implies that the device is locked for the user.
@@ -365,18 +367,11 @@
     /// The enforcement module will try to get a confirmation token from this channel whenever
     /// an operation that requires confirmation finishes.
     confirmation_token_receiver: Arc<Mutex<Option<Receiver<Vec<u8>>>>>,
+    /// Highest boot level seen in keystore.boot_level; used to enforce MAX_BOOT_LEVEL tag.
+    boot_level: AtomicI32,
 }
 
 impl Enforcements {
-    /// Creates an enforcement object with the two data structures it holds and the sender as None.
-    pub fn new() -> Self {
-        Enforcements {
-            device_unlocked_set: Mutex::new(HashSet::new()),
-            op_auth_map: Default::default(),
-            confirmation_token_receiver: Default::default(),
-        }
-    }
-
     /// Install the confirmation token receiver. The enforcement module will try to get a
     /// confirmation token from this channel whenever an operation that requires confirmation
     /// finishes.
@@ -475,6 +470,7 @@
         let mut unlocked_device_required = false;
         let mut key_usage_limited: Option<i64> = None;
         let mut confirmation_token_receiver: Option<Arc<Mutex<Option<Receiver<Vec<u8>>>>>> = None;
+        let mut max_boot_level: Option<i32> = None;
 
         // iterate through key parameters, recording information we need for authorization
         // enforcements later, or enforcing authorizations in place, where applicable
@@ -541,6 +537,9 @@
                 KeyParameterValue::TrustedConfirmationRequired => {
                     confirmation_token_receiver = Some(self.confirmation_token_receiver.clone());
                 }
+                KeyParameterValue::MaxBootLevel(level) => {
+                    max_boot_level = Some(*level);
+                }
                 // NOTE: as per offline discussion, sanitizing key parameters and rejecting
                 // create operation if any non-allowed tags are present, is not done in
                 // authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
@@ -594,6 +593,13 @@
             }
         }
 
+        if let Some(level) = max_boot_level {
+            if level < self.boot_level.load(Ordering::SeqCst) {
+                return Err(Error::Km(Ec::BOOT_LEVEL_EXCEEDED))
+                    .context("In authorize_create: boot level is too late.");
+            }
+        }
+
         if !unlocked_device_required && no_auth_required {
             return Ok((
                 None,
@@ -760,11 +766,34 @@
 
         auth_bound && !skip_lskf_binding
     }
-}
 
-impl Default for Enforcements {
-    fn default() -> Self {
-        Self::new()
+    /// Watch the `keystore.boot_level` system property, and keep self.boot_level up to date.
+    /// Blocks waiting for system property changes, so must be run in its own thread.
+    pub fn watch_boot_level(&self) -> Result<()> {
+        let mut w = PropertyWatcher::new("keystore.boot_level")?;
+        loop {
+            fn parse_value(_name: &str, value: &str) -> Result<Option<i32>> {
+                Ok(if value == "end" { None } else { Some(value.parse::<i32>()?) })
+            }
+            match w.read(parse_value)? {
+                Some(level) => {
+                    let old = self.boot_level.fetch_max(level, Ordering::SeqCst);
+                    log::info!(
+                        "Read keystore.boot_level: {}; boot level {} -> {}",
+                        level,
+                        old,
+                        std::cmp::max(old, level)
+                    );
+                }
+                None => {
+                    log::info!("keystore.boot_level is `end`, finishing.");
+                    self.boot_level.fetch_max(i32::MAX, Ordering::SeqCst);
+                    break;
+                }
+            }
+            w.wait()?;
+        }
+        Ok(())
     }
 }
 
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index e1b41b5..04bfbc9 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -160,7 +160,7 @@
     /// priorities.
     pub static ref ASYNC_TASK: Arc<AsyncTask> = Default::default();
     /// Singleton for enforcements.
-    pub static ref ENFORCEMENTS: Enforcements = Enforcements::new();
+    pub static ref ENFORCEMENTS: Enforcements = Default::default();
     /// LegacyBlobLoader is initialized and exists globally.
     /// The same directory used by the database is used by the LegacyBlobLoader as well.
     pub static ref LEGACY_BLOB_LOADER: Arc<LegacyBlobLoader> = Arc::new(LegacyBlobLoader::new(
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index 117dea8..c10da95 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -965,6 +965,9 @@
     /// Used to deliver the not after date in milliseconds to KeyMint during key generation/import.
     #[key_param(tag = CERTIFICATE_NOT_AFTER, field = DateTime)]
     CertificateNotAfter(i64),
+    /// Specifies a maximum boot level at which a key should function
+    #[key_param(tag = MAX_BOOT_LEVEL, field = Integer)]
+    MaxBootLevel(i32),
 }
 }
 
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 9dc59a2..821d728 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -65,6 +65,13 @@
 
     ENFORCEMENTS.install_confirmation_token_receiver(confirmation_token_receiver);
 
+    info!("Starting boot level watcher.");
+    std::thread::spawn(|| {
+        keystore2::globals::ENFORCEMENTS
+            .watch_boot_level()
+            .unwrap_or_else(|e| error!("watch_boot_level failed: {}", e));
+    });
+
     info!("Starting thread pool now.");
     binder::ProcessState::start_thread_pool();
 
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index b36b78a..c2b4669 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -740,6 +740,9 @@
     case KMV1::Tag::CERTIFICATE_NOT_AFTER:
         // These tags do not exist in KM < KeyMint 1.0.
         break;
+    case KMV1::Tag::MAX_BOOT_LEVEL:
+        // Does not exist in API level 30 or below.
+        break;
     }
     return V4_0::KeyParameter{.tag = V4_0::Tag::INVALID};
 }