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};
}