Mark 2024-06 Release (ab/AP2A.240605.024) as merged in aosp-main-future
Bug: 343100748
Merged-In: I3aa48d05f367fafab5151fa7eb6dd447840dae0d
Change-Id: I5800fdf210100e25c977b53b60a870a3126c3d69
diff --git a/fsverity/fsverity_manifest_generator.py b/fsverity/fsverity_manifest_generator.py
index 181758a..ca7ac5c 100644
--- a/fsverity/fsverity_manifest_generator.py
+++ b/fsverity/fsverity_manifest_generator.py
@@ -35,7 +35,7 @@
return bytes(bytearray.fromhex(out))
if __name__ == '__main__':
- p = argparse.ArgumentParser()
+ p = argparse.ArgumentParser(fromfile_prefix_chars='@')
p.add_argument(
'--output',
help='Path to the output manifest',
@@ -52,7 +52,7 @@
'inputs',
nargs='*',
help='input file for the build manifest')
- args = p.parse_args(sys.argv[1:])
+ args = p.parse_args()
digests = FSVerityDigests()
for f in sorted(args.inputs):
diff --git a/identity/Android.bp b/identity/Android.bp
index 6227bfe..a563532 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -59,7 +59,7 @@
"android.hardware.identity-support-lib",
"android.hardware.security.rkp-V3-cpp",
"android.security.rkp_aidl-cpp",
- "libcppbor_external",
+ "libcppbor",
"libcredstore_aidl",
"libkeymaster4support",
"librkp_support",
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 7cb7c37..ed9cd88 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -28,6 +28,7 @@
defaults: [
"keymint_use_latest_hal_aidl_rust",
"keystore2_use_latest_aidl_rust",
+ "structured_log_rust_defaults",
],
rustlibs: [
@@ -54,7 +55,6 @@
"libkeystore2_selinux",
"liblazy_static",
"liblibc",
- "liblog_event_list",
"liblog_rust",
"libmessage_macro",
"librand",
diff --git a/keystore2/OWNERS b/keystore2/OWNERS
index 6b1a95b..bf9d61b 100644
--- a/keystore2/OWNERS
+++ b/keystore2/OWNERS
@@ -5,5 +5,4 @@
hasinitg@google.com
jbires@google.com
sethmo@google.com
-trong@google.com
swillden@google.com
diff --git a/keystore2/aconfig/flags.aconfig b/keystore2/aconfig/flags.aconfig
index b67bc6c..856b42e 100644
--- a/keystore2/aconfig/flags.aconfig
+++ b/keystore2/aconfig/flags.aconfig
@@ -23,4 +23,12 @@
description: "Include support for importing keys that were previously software-emulated into KeyMint"
bug: "283077822"
is_fixed_read_only: true
+}
+
+flag {
+ name: "database_loop_timeout"
+ namespace: "hardware_backed_security"
+ description: "Abandon Keystore database retry loop after an interval"
+ bug: "319563050"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
index 5b22be0..277b9dd 100644
--- a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
+++ b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
@@ -27,10 +27,6 @@
/**
* This callback gets called by the implementing service when a pending confirmation prompt
* gets finalized.
- * @deprecated Android Protected Confirmation had a low adoption rate among Android device
- * makers and developers alike. Given the lack of devices supporting the feature,
- * it is deprecated. Developers can use auth-bound Keystore keys as a partial
- * replacement.
*
* @param result
* - ResponseCode.OK On success. In this case dataConfirmed must be non null.
diff --git a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
index 9f97847..3162224 100644
--- a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
+++ b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
@@ -35,10 +35,6 @@
/**
* Present the confirmation prompt. The caller must implement IConfirmationCallback and pass
* it to this function as listener.
- * @deprecated Android Protected Confirmation had a low adoption rate among Android device
- * makers and developers alike. Given the lack of devices supporting the
- * feature, it is deprecated. Developers can use auth-bound Keystore keys
- * as a partial replacement.
*
* @param listener Must implement IConfirmationCallback. Doubles as session identifier when
* passed to cancelPrompt.
@@ -59,11 +55,6 @@
/**
* Cancel an ongoing prompt.
- * @deprecated Android Protected Confirmation had a low adoption rate among Android device
- * makers and developers alike. Given the lack of devices supporting the
- * feature, it is deprecated. Developers can use auth-bound Keystore keys as
- * a partial replacement.
- *
*
* @param listener Must implement IConfirmationCallback, although in this context this binder
* token is only used to identify the session that is to be cancelled.
@@ -75,10 +66,6 @@
/**
* Returns true if the device supports Android Protected Confirmation.
- * @deprecated Android Protected Confirmation had a low adoption rate among Android device
- * makers and developers alike. Given the lack of devices supporting the
- * feature, it is deprecated. Developers can use auth-bound Keystore keys
- * as a partial replacement.
*/
boolean isSupported();
}
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
index a9de026..fd532f6 100644
--- a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -18,8 +18,6 @@
import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.security.authorization.AuthorizationTokens;
-// TODO: mark the interface with @SensitiveData when the annotation is ready (b/176110256).
-
/**
* IKeystoreAuthorization interface exposes the methods for other system components to
* provide keystore with the information required to enforce authorizations on key usage.
diff --git a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
index abea958..50e9828 100644
--- a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
+++ b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
@@ -112,16 +112,6 @@
void earlyBootEnded();
/**
- * Informs Keystore 2.0 that the an off body event was detected.
- *
- * ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the `ReportOffBody`
- * permission.
- * `ResponseCode::SYSTEM_ERROR` - if an unexpected error occurred.
- */
- void onDeviceOffBody();
-
- /**
* Migrate a key from one namespace to another. The caller must have use, grant, and delete
* permissions on the source namespace and rebind permissions on the destination namespace.
* The source may be specified by Domain::APP, Domain::SELINUX, or Domain::KEY_ID. The target
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index fbf9464..6abe849 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -62,16 +62,6 @@
Error::Rc(ResponseCode::OPERATION_PENDING)
}
- /// Short hand for `Error::Rc(ResponseCode::CANCELLED)`
- pub fn cancelled() -> Self {
- Error::Rc(ResponseCode::CANCELLED)
- }
-
- /// Short hand for `Error::Rc(ResponseCode::ABORTED)`
- pub fn aborted() -> Self {
- Error::Rc(ResponseCode::ABORTED)
- }
-
/// Short hand for `Error::Rc(ResponseCode::IGNORED)`
pub fn ignored() -> Self {
Error::Rc(ResponseCode::IGNORED)
diff --git a/keystore2/src/audit_log.rs b/keystore2/src/audit_log.rs
index 0e5dfeb..8d9735e 100644
--- a/keystore2/src/audit_log.rs
+++ b/keystore2/src/audit_log.rs
@@ -20,7 +20,7 @@
Domain::Domain, KeyDescriptor::KeyDescriptor,
};
use libc::uid_t;
-use log_event_list::{LogContext, LogContextError, LogIdSecurity};
+use structured_log::{structured_log, LOG_ID_SECURITY};
const TAG_KEY_GENERATED: u32 = 210024;
const TAG_KEY_IMPORTED: u32 = 210025;
@@ -58,30 +58,19 @@
/// Logs key integrity violation to NIAP audit log.
pub fn log_key_integrity_violation(key: &KeyDescriptor) {
- with_log_context(TAG_KEY_INTEGRITY_VIOLATION, |ctx| {
- let owner = key_owner(key.domain, key.nspace, key.nspace as i32);
- ctx.append_str(key.alias.as_ref().map_or("none", String::as_str))?.append_i32(owner)
- })
+ let owner = key_owner(key.domain, key.nspace, key.nspace as i32);
+ let alias = String::from(key.alias.as_ref().map_or("none", String::as_str));
+ LOGS_HANDLER.queue_lo(move |_| {
+ let _result =
+ structured_log!(log_id: LOG_ID_SECURITY, TAG_KEY_INTEGRITY_VIOLATION, alias, owner);
+ });
}
fn log_key_event(tag: u32, key: &KeyDescriptor, calling_app: uid_t, success: bool) {
- with_log_context(tag, |ctx| {
- let owner = key_owner(key.domain, key.nspace, calling_app as i32);
- ctx.append_i32(i32::from(success))?
- .append_str(key.alias.as_ref().map_or("none", String::as_str))?
- .append_i32(owner)
- })
-}
-
-fn with_log_context<F>(tag: u32, f: F)
-where
- F: Fn(LogContext) -> Result<LogContext, LogContextError>,
-{
- if let Some(ctx) = LogContext::new(LogIdSecurity, tag) {
- if let Ok(event) = f(ctx) {
- LOGS_HANDLER.queue_lo(move |_| {
- let _result = event.write();
- });
- }
- }
+ let owner = key_owner(key.domain, key.nspace, calling_app as i32);
+ let alias = String::from(key.alias.as_ref().map_or("none", String::as_str));
+ LOGS_HANDLER.queue_lo(move |_| {
+ let _result =
+ structured_log!(log_id: LOG_ID_SECURITY, tag, i32::from(success), alias, owner);
+ });
}
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index f956787..243abf1 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -128,7 +128,8 @@
fn add_auth_token(&self, auth_token: &HardwareAuthToken) -> Result<()> {
// Check keystore permission.
- check_keystore_permission(KeystorePerm::AddAuth).context(ks_err!())?;
+ check_keystore_permission(KeystorePerm::AddAuth)
+ .context(ks_err!("caller missing AddAuth permissions"))?;
log::info!(
"add_auth_token(challenge={}, userId={}, authId={}, authType={:#x}, timestamp={}ms)",
@@ -149,7 +150,8 @@
user_id,
password.is_some(),
);
- check_keystore_permission(KeystorePerm::Unlock).context(ks_err!("Unlock."))?;
+ check_keystore_permission(KeystorePerm::Unlock)
+ .context(ks_err!("caller missing Unlock permissions"))?;
ENFORCEMENTS.set_device_locked(user_id, false);
let mut skm = SUPER_KEY.write().unwrap();
@@ -160,7 +162,7 @@
.context(ks_err!("Unlock with password."))
} else {
DB.with(|db| skm.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32))
- .context(ks_err!("try_unlock_user_with_biometric failed"))
+ .context(ks_err!("try_unlock_user_with_biometric failed user_id={user_id}"))
}
}
@@ -179,7 +181,8 @@
if !android_security_flags::fix_unlocked_device_required_keys_v2() {
weak_unlock_enabled = false;
}
- check_keystore_permission(KeystorePerm::Lock).context(ks_err!("Lock"))?;
+ check_keystore_permission(KeystorePerm::Lock)
+ .context(ks_err!("caller missing Lock permission"))?;
ENFORCEMENTS.set_device_locked(user_id, true);
let mut skm = SUPER_KEY.write().unwrap();
DB.with(|db| {
@@ -198,7 +201,8 @@
if !android_security_flags::fix_unlocked_device_required_keys_v2() {
return Ok(());
}
- check_keystore_permission(KeystorePerm::Lock).context(ks_err!("Lock"))?;
+ check_keystore_permission(KeystorePerm::Lock)
+ .context(ks_err!("caller missing Lock permission"))?;
SUPER_KEY.write().unwrap().wipe_plaintext_unlocked_device_required_keys(user_id as u32);
Ok(())
}
@@ -208,7 +212,8 @@
if !android_security_flags::fix_unlocked_device_required_keys_v2() {
return Ok(());
}
- check_keystore_permission(KeystorePerm::Lock).context(ks_err!("Lock"))?;
+ check_keystore_permission(KeystorePerm::Lock)
+ .context(ks_err!("caller missing Lock permission"))?;
SUPER_KEY.write().unwrap().wipe_all_unlocked_device_required_keys(user_id as u32);
Ok(())
}
@@ -221,7 +226,8 @@
) -> Result<AuthorizationTokens> {
// Check permission. Function should return if this failed. Therefore having '?' at the end
// is very important.
- check_keystore_permission(KeystorePerm::GetAuthToken).context(ks_err!("GetAuthToken"))?;
+ check_keystore_permission(KeystorePerm::GetAuthToken)
+ .context(ks_err!("caller missing GetAuthToken permission"))?;
// If the challenge is zero, return error
if challenge == 0 {
@@ -240,7 +246,8 @@
auth_types: &[HardwareAuthenticatorType],
) -> Result<i64> {
// Check keystore permission.
- check_keystore_permission(KeystorePerm::GetLastAuthTime).context(ks_err!())?;
+ check_keystore_permission(KeystorePerm::GetLastAuthTime)
+ .context(ks_err!("caller missing GetLastAuthTime permission"))?;
let mut max_time: i64 = -1;
for auth_type in auth_types.iter() {
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
index c917a89..00cbb1c 100644
--- a/keystore2/src/crypto/zvec.rs
+++ b/keystore2/src/crypto/zvec.rs
@@ -20,6 +20,7 @@
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::ptr::write_volatile;
+use std::ptr::NonNull;
/// A semi fixed size u8 vector that is zeroed when dropped. It can shrink in
/// size but cannot grow larger than the original size (and if it shrinks it
@@ -46,7 +47,7 @@
let b = v.into_boxed_slice();
if size > 0 {
// SAFETY: The address range is part of our address space.
- unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
+ unsafe { mlock(NonNull::from(&b).cast(), b.len()) }?;
}
Ok(Self { elems: b, len: size })
}
@@ -79,9 +80,7 @@
if let Err(e) =
// SAFETY: The address range is part of our address space, and was previously locked
// by `mlock` in `ZVec::new` or the `TryFrom<Vec<u8>>` implementation.
- unsafe {
- munlock(self.elems.as_ptr() as *const std::ffi::c_void, self.elems.len())
- }
+ unsafe { munlock(NonNull::from(&self.elems).cast(), self.elems.len()) }
{
log::error!("In ZVec::drop: `munlock` failed: {:?}.", e);
}
@@ -137,7 +136,7 @@
let b = v.into_boxed_slice();
if !b.is_empty() {
// SAFETY: The address range is part of our address space.
- unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
+ unsafe { mlock(NonNull::from(&b).cast(), b.len()) }?;
}
Ok(Self { elems: b, len })
}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 2757313..ee9d246 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -95,6 +95,24 @@
#[cfg(test)]
use tests::random;
+/// If the database returns a busy error code, retry after this interval.
+const DB_BUSY_RETRY_INTERVAL: Duration = Duration::from_micros(500);
+/// If the database returns a busy error code, keep retrying for this long.
+const MAX_DB_BUSY_RETRY_PERIOD: Duration = Duration::from_secs(15);
+
+/// Check whether a database lock has timed out.
+fn check_lock_timeout(start: &std::time::Instant, timeout: Duration) -> Result<()> {
+ if keystore2_flags::database_loop_timeout() {
+ let elapsed = start.elapsed();
+ if elapsed >= timeout {
+ error!("Abandon locked DB after {elapsed:?}");
+ return Err(&KsError::Rc(ResponseCode::BACKEND_BUSY))
+ .context(ks_err!("Abandon locked DB after {elapsed:?}",));
+ }
+ }
+ Ok(())
+}
+
impl_metadata!(
/// A set of metadata for key entries.
#[derive(Debug, Default, Eq, PartialEq)]
@@ -382,11 +400,6 @@
pub fn to_millis_epoch(self) -> i64 {
self.0
}
-
- /// Returns unix epoch time in seconds.
- pub fn to_secs_epoch(self) -> i64 {
- self.0 / 1000
- }
}
impl ToSql for DateTime {
@@ -523,7 +536,7 @@
/// This function blocks until an exclusive lock for the given key entry id can
/// be acquired. It returns a guard object, that represents the lifecycle of the
/// acquired lock.
- pub fn get(&self, key_id: i64) -> KeyIdGuard {
+ fn get(&self, key_id: i64) -> KeyIdGuard {
let mut locked_keys = self.locked_keys.lock().unwrap();
while locked_keys.contains(&key_id) {
locked_keys = self.cond_var.wait(locked_keys).unwrap();
@@ -536,7 +549,7 @@
/// given key id is already taken the function returns None immediately. If a lock
/// can be acquired this function returns a guard object, that represents the
/// lifecycle of the acquired lock.
- pub fn try_get(&self, key_id: i64) -> Option<KeyIdGuard> {
+ fn try_get(&self, key_id: i64) -> Option<KeyIdGuard> {
let mut locked_keys = self.locked_keys.lock().unwrap();
if locked_keys.insert(key_id) {
Some(KeyIdGuard(key_id))
@@ -665,10 +678,6 @@
pub fn take_cert(&mut self) -> Option<Vec<u8>> {
self.cert.take()
}
- /// Exposes the optional public certificate chain.
- pub fn cert_chain(&self) -> &Option<Vec<u8>> {
- &self.cert_chain
- }
/// Extracts the optional public certificate_chain.
pub fn take_cert_chain(&mut self) -> Option<Vec<u8>> {
self.cert_chain.take()
@@ -677,10 +686,6 @@
pub fn km_uuid(&self) -> &Uuid {
&self.km_uuid
}
- /// Exposes the key parameters of this key entry.
- pub fn key_parameters(&self) -> &Vec<KeyParameter> {
- &self.parameters
- }
/// Consumes this key entry and extracts the keyparameters from it.
pub fn into_key_parameters(self) -> Vec<KeyParameter> {
self.parameters
@@ -694,10 +699,6 @@
pub fn pure_cert(&self) -> bool {
self.pure_cert
}
- /// Consumes this key entry and extracts the keyparameters and metadata from it.
- pub fn into_key_parameters_and_metadata(self) -> (Vec<KeyParameter>, KeyMetaData) {
- (self.parameters, self.metadata)
- }
}
/// Indicates the sub component of a key entry for persistent storage.
@@ -842,11 +843,6 @@
}
}
-/// Shared in-memory databases get destroyed as soon as the last connection to them gets closed.
-/// This object does not allow access to the database connection. But it keeps a database
-/// connection alive in order to keep the in memory per boot database alive.
-pub struct PerBootDbKeepAlive(Connection);
-
impl KeystoreDB {
const UNASSIGNED_KEY_ID: i64 = -1i64;
const CURRENT_DB_VERSION: u32 = 1;
@@ -1035,7 +1031,7 @@
.context("Failed to attach database persistent.")
{
if Self::is_locked_error(&e) {
- std::thread::sleep(std::time::Duration::from_micros(500));
+ std::thread::sleep(DB_BUSY_RETRY_INTERVAL);
continue;
} else {
return Err(e);
@@ -1366,99 +1362,6 @@
.context(ks_err!())
}
- /// Atomically loads a key entry and associated metadata or creates it using the
- /// callback create_new_key callback. The callback is called during a database
- /// transaction. This means that implementers should be mindful about using
- /// blocking operations such as IPC or grabbing mutexes.
- pub fn get_or_create_key_with<F>(
- &mut self,
- domain: Domain,
- namespace: i64,
- alias: &str,
- km_uuid: Uuid,
- create_new_key: F,
- ) -> Result<(KeyIdGuard, KeyEntry)>
- where
- F: Fn() -> Result<(Vec<u8>, BlobMetaData)>,
- {
- let _wp = wd::watch_millis("KeystoreDB::get_or_create_key_with", 500);
-
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let id = {
- let mut stmt = tx
- .prepare(
- "SELECT id FROM persistent.keyentry
- WHERE
- key_type = ?
- AND domain = ?
- AND namespace = ?
- AND alias = ?
- AND state = ?;",
- )
- .context(ks_err!("Failed to select from keyentry table."))?;
- let mut rows = stmt
- .query(params![KeyType::Super, domain.0, namespace, alias, KeyLifeCycle::Live])
- .context(ks_err!("Failed to query from keyentry table."))?;
-
- db_utils::with_rows_extract_one(&mut rows, |row| {
- Ok(match row {
- Some(r) => r.get(0).context("Failed to unpack id.")?,
- None => None,
- })
- })
- .context(ks_err!())?
- };
-
- let (id, entry) = match id {
- Some(id) => (
- id,
- Self::load_key_components(tx, KeyEntryLoadBits::KM, id).context(ks_err!())?,
- ),
-
- None => {
- let id = Self::insert_with_retry(|id| {
- tx.execute(
- "INSERT into persistent.keyentry
- (id, key_type, domain, namespace, alias, state, km_uuid)
- VALUES(?, ?, ?, ?, ?, ?, ?);",
- params![
- id,
- KeyType::Super,
- domain.0,
- namespace,
- alias,
- KeyLifeCycle::Live,
- km_uuid,
- ],
- )
- })
- .context(ks_err!())?;
-
- let (blob, metadata) = create_new_key().context(ks_err!())?;
- Self::set_blob_internal(
- tx,
- id,
- SubComponentType::KEY_BLOB,
- Some(&blob),
- Some(&metadata),
- )
- .context(ks_err!())?;
- (
- id,
- KeyEntry {
- id,
- key_blob_info: Some((blob, metadata)),
- pure_cert: false,
- ..Default::default()
- },
- )
- }
- };
- Ok((KEY_ID_LOCK.get(id), entry)).no_gc()
- })
- .context(ks_err!())
- }
-
/// Creates a transaction with the given behavior and executes f with the new transaction.
/// The transaction is committed only if f returns Ok and retried if DatabaseBusy
/// or DatabaseLocked is encountered.
@@ -1466,6 +1369,18 @@
where
F: Fn(&Transaction) -> Result<(bool, T)>,
{
+ self.with_transaction_timeout(behavior, MAX_DB_BUSY_RETRY_PERIOD, f)
+ }
+ fn with_transaction_timeout<T, F>(
+ &mut self,
+ behavior: TransactionBehavior,
+ timeout: Duration,
+ f: F,
+ ) -> Result<T>
+ where
+ F: Fn(&Transaction) -> Result<(bool, T)>,
+ {
+ let start = std::time::Instant::now();
loop {
let result = self
.conn
@@ -1480,7 +1395,8 @@
Ok(result) => break Ok(result),
Err(e) => {
if Self::is_locked_error(&e) {
- std::thread::sleep(std::time::Duration::from_micros(500));
+ check_lock_timeout(&start, timeout)?;
+ std::thread::sleep(DB_BUSY_RETRY_INTERVAL);
continue;
} else {
return Err(e).context(ks_err!());
@@ -1506,27 +1422,6 @@
)
}
- /// Creates a new key entry and allocates a new randomized id for the new key.
- /// The key id gets associated with a domain and namespace but not with an alias.
- /// To complete key generation `rebind_alias` should be called after all of the
- /// key artifacts, i.e., blobs and parameters have been associated with the new
- /// key id. Finalizing with `rebind_alias` makes the creation of a new key entry
- /// atomic even if key generation is not.
- pub fn create_key_entry(
- &mut self,
- domain: &Domain,
- namespace: &i64,
- key_type: KeyType,
- km_uuid: &Uuid,
- ) -> Result<KeyIdGuard> {
- let _wp = wd::watch_millis("KeystoreDB::create_key_entry", 500);
-
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- Self::create_key_entry_internal(tx, domain, namespace, key_type, km_uuid).no_gc()
- })
- .context(ks_err!())
- }
-
fn create_key_entry_internal(
tx: &Transaction,
domain: &Domain,
@@ -2229,6 +2124,7 @@
check_permission: impl Fn(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
) -> Result<(KeyIdGuard, KeyEntry)> {
let _wp = wd::watch_millis("KeystoreDB::load_key_entry", 500);
+ let start = std::time::Instant::now();
loop {
match self.load_key_entry_internal(
@@ -2241,7 +2137,8 @@
Ok(result) => break Ok(result),
Err(e) => {
if Self::is_locked_error(&e) {
- std::thread::sleep(std::time::Duration::from_micros(500));
+ check_lock_timeout(&start, MAX_DB_BUSY_RETRY_PERIOD)?;
+ std::thread::sleep(DB_BUSY_RETRY_INTERVAL);
continue;
} else {
return Err(e).context(ks_err!());
@@ -2864,26 +2761,11 @@
}
/// Find the newest auth token matching the given predicate.
- pub fn find_auth_token_entry<F>(&self, p: F) -> Option<(AuthTokenEntry, BootTime)>
+ pub fn find_auth_token_entry<F>(&self, p: F) -> Option<AuthTokenEntry>
where
F: Fn(&AuthTokenEntry) -> bool,
{
- self.perboot.find_auth_token_entry(p).map(|entry| (entry, self.get_last_off_body()))
- }
-
- /// Insert last_off_body into the metadata table at the initialization of auth token table
- pub fn insert_last_off_body(&self, last_off_body: BootTime) {
- self.perboot.set_last_off_body(last_off_body)
- }
-
- /// Update last_off_body when on_device_off_body is called
- pub fn update_last_off_body(&self, last_off_body: BootTime) {
- self.perboot.set_last_off_body(last_off_body)
- }
-
- /// Get last_off_body time when finding auth tokens
- fn get_last_off_body(&self) -> BootTime {
- self.perboot.get_last_off_body()
+ self.perboot.find_auth_token_entry(p)
}
/// Load descriptor of a key by key id
@@ -3148,12 +3030,24 @@
db.perboot.get_all_auth_token_entries()
}
+ fn create_key_entry(
+ db: &mut KeystoreDB,
+ domain: &Domain,
+ namespace: &i64,
+ key_type: KeyType,
+ km_uuid: &Uuid,
+ ) -> Result<KeyIdGuard> {
+ db.with_transaction(TransactionBehavior::Immediate, |tx| {
+ KeystoreDB::create_key_entry_internal(tx, domain, namespace, key_type, km_uuid).no_gc()
+ })
+ }
+
#[test]
fn test_persistence_for_files() -> Result<()> {
let temp_dir = TempDir::new("persistent_db_test")?;
let mut db = KeystoreDB::new(temp_dir.path(), None)?;
- db.create_key_entry(&Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
+ create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 1);
@@ -3172,8 +3066,8 @@
let mut db = new_test_db()?;
- db.create_key_entry(&Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
- db.create_key_entry(&Domain::SELINUX, &101, KeyType::Client, &KEYSTORE_UUID)?;
+ create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
+ create_key_entry(&mut db, &Domain::SELINUX, &101, KeyType::Client, &KEYSTORE_UUID)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 2);
@@ -3182,15 +3076,15 @@
// Test that we must pass in a valid Domain.
check_result_is_error_containing_string(
- db.create_key_entry(&Domain::GRANT, &102, KeyType::Client, &KEYSTORE_UUID),
+ create_key_entry(&mut db, &Domain::GRANT, &102, KeyType::Client, &KEYSTORE_UUID),
&format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
);
check_result_is_error_containing_string(
- db.create_key_entry(&Domain::BLOB, &103, KeyType::Client, &KEYSTORE_UUID),
+ create_key_entry(&mut db, &Domain::BLOB, &103, KeyType::Client, &KEYSTORE_UUID),
&format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
);
check_result_is_error_containing_string(
- db.create_key_entry(&Domain::KEY_ID, &104, KeyType::Client, &KEYSTORE_UUID),
+ create_key_entry(&mut db, &Domain::KEY_ID, &104, KeyType::Client, &KEYSTORE_UUID),
&format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
);
@@ -3206,8 +3100,8 @@
}
let mut db = new_test_db()?;
- db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
- db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+ create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+ create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 2);
assert_eq!(
@@ -4824,7 +4718,7 @@
max_usage_count: Option<i32>,
sids: &[i64],
) -> Result<KeyIdGuard> {
- let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+ let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
let mut blob_metadata = BlobMetaData::new();
blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
@@ -4883,7 +4777,7 @@
alias: &str,
logical_only: bool,
) -> Result<KeyIdGuard> {
- let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+ let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
let mut blob_metadata = BlobMetaData::new();
if !logical_only {
blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
@@ -4923,7 +4817,7 @@
super_key_id: i64,
) -> Result<KeyIdGuard> {
let domain = Domain::APP;
- let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+ let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
let mut blob_metadata = BlobMetaData::new();
blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
@@ -5033,7 +4927,7 @@
// This allows us to test repeated elements.
thread_local! {
- static RANDOM_COUNTER: RefCell<i64> = RefCell::new(0);
+ static RANDOM_COUNTER: RefCell<i64> = const { RefCell::new(0) };
}
fn reset_random() {
@@ -5051,23 +4945,6 @@
}
#[test]
- fn test_last_off_body() -> Result<()> {
- let mut db = new_test_db()?;
- db.insert_last_off_body(BootTime::now());
- let tx = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
- tx.commit()?;
- let last_off_body_1 = db.get_last_off_body();
- let one_second = Duration::from_secs(1);
- thread::sleep(one_second);
- db.update_last_off_body(BootTime::now());
- let tx2 = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
- tx2.commit()?;
- let last_off_body_2 = db.get_last_off_body();
- assert!(last_off_body_1 < last_off_body_2);
- Ok(())
- }
-
- #[test]
fn test_unbind_keys_for_user() -> Result<()> {
let mut db = new_test_db()?;
db.unbind_keys_for_user(1, false)?;
@@ -5366,7 +5243,7 @@
let mut db = new_test_db()?;
let mut working_stats = get_storage_stats_map(&mut db);
- let key_id = db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+ let key_id = create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
assert_storage_increased(
&mut db,
vec![
@@ -5491,7 +5368,7 @@
// All three entries are in the database
assert_eq!(db.perboot.auth_tokens_len(), 3);
// It selected the most recent timestamp
- assert_eq!(db.find_auth_token_entry(|_| true).unwrap().0.auth_token.mac, b"mac2".to_vec());
+ assert_eq!(db.find_auth_token_entry(|_| true).unwrap().auth_token.mac, b"mac2".to_vec());
Ok(())
}
@@ -5617,4 +5494,85 @@
assert_eq!(third_sid_apps, vec![second_app_id]);
Ok(())
}
+
+ #[test]
+ fn test_key_id_guard_immediate() -> Result<()> {
+ if !keystore2_flags::database_loop_timeout() {
+ eprintln!("Skipping test as loop timeout flag disabled");
+ return Ok(());
+ }
+ // Emit logging from test.
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore_database_tests")
+ .with_max_level(log::LevelFilter::Debug),
+ );
+
+ // Preparation: put a single entry into a test DB.
+ let temp_dir = Arc::new(TempDir::new("key_id_guard_immediate")?);
+ let temp_dir_clone_a = temp_dir.clone();
+ let temp_dir_clone_b = temp_dir.clone();
+ let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)?.0;
+
+ let (a_sender, b_receiver) = std::sync::mpsc::channel();
+ let (b_sender, a_receiver) = std::sync::mpsc::channel();
+
+ // First thread starts an immediate transaction, then waits on a synchronization channel
+ // before trying to get the `KeyIdGuard`.
+ let handle_a = thread::spawn(move || {
+ let temp_dir = temp_dir_clone_a;
+ let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+
+ // Make sure the other thread has initialized its database access before we lock it out.
+ a_receiver.recv().unwrap();
+
+ let _result = db.with_transaction_timeout(
+ TransactionBehavior::Immediate,
+ Duration::from_secs(3),
+ |_tx| {
+ // Notify the other thread that we're inside the immediate transaction...
+ a_sender.send(()).unwrap();
+ // ...then wait to be sure that the other thread has the `KeyIdGuard` before
+ // this thread also tries to get it.
+ a_receiver.recv().unwrap();
+
+ let _guard = KEY_ID_LOCK.get(key_id);
+ Ok(()).no_gc()
+ },
+ );
+ });
+
+ // Second thread gets the `KeyIdGuard`, then waits before trying to perform an immediate
+ // transaction.
+ let handle_b = thread::spawn(move || {
+ let temp_dir = temp_dir_clone_b;
+ let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+ // Notify the other thread that we are initialized (so it can lock the immediate
+ // transaction).
+ b_sender.send(()).unwrap();
+
+ let _guard = KEY_ID_LOCK.get(key_id);
+ // Notify the other thread that we have the `KeyIdGuard`...
+ b_sender.send(()).unwrap();
+ // ...then wait to be sure that the other thread is in the immediate transaction before
+ // this thread also tries to do one.
+ b_receiver.recv().unwrap();
+
+ let result = db.with_transaction_timeout(
+ TransactionBehavior::Immediate,
+ Duration::from_secs(3),
+ |_tx| Ok(()).no_gc(),
+ );
+ // Expect the attempt to get an immediate transaction to fail, and then this thread will
+ // exit and release the `KeyIdGuard`, allowing the other thread to complete.
+ assert!(result.is_err());
+ check_result_is_error_containing_string(result, "BACKEND_BUSY");
+ });
+
+ let _ = handle_a.join();
+ let _ = handle_b.join();
+
+ Ok(())
+ }
}
diff --git a/keystore2/src/database/perboot.rs b/keystore2/src/database/perboot.rs
index 1b7c80d..4727015 100644
--- a/keystore2/src/database/perboot.rs
+++ b/keystore2/src/database/perboot.rs
@@ -13,15 +13,14 @@
// limitations under the License.
//! This module implements a per-boot, shared, in-memory storage of auth tokens
-//! and last-time-on-body for the main Keystore 2.0 database module.
+//! for the main Keystore 2.0 database module.
-use super::{AuthTokenEntry, BootTime};
+use super::AuthTokenEntry;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
};
use lazy_static::lazy_static;
use std::collections::HashSet;
-use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::Arc;
use std::sync::RwLock;
@@ -62,17 +61,13 @@
impl Eq for AuthTokenEntryWrap {}
-/// Per-boot state structure. Currently only used to track auth tokens and
-/// last-off-body.
+/// Per-boot state structure. Currently only used to track auth tokens.
#[derive(Default)]
pub struct PerbootDB {
// We can use a .unwrap() discipline on this lock, because only panicking
// while holding a .write() lock will poison it. The only write usage is
// an insert call which inserts a pre-constructed pair.
auth_tokens: RwLock<HashSet<AuthTokenEntryWrap>>,
- // Ordering::Relaxed is appropriate for accessing this atomic, since it
- // does not currently need to be synchronized with anything else.
- last_off_body: AtomicI64,
}
lazy_static! {
@@ -102,14 +97,6 @@
matches.sort_by_key(|x| x.0.time_received);
matches.last().map(|x| x.0.clone())
}
- /// Get the last time the device was off the user's body
- pub fn get_last_off_body(&self) -> BootTime {
- BootTime(self.last_off_body.load(Ordering::Relaxed))
- }
- /// Set the last time the device was off the user's body
- pub fn set_last_off_body(&self, last_off_body: BootTime) {
- self.last_off_body.store(last_off_body.0, Ordering::Relaxed)
- }
/// Return how many auth tokens are currently tracked.
pub fn auth_tokens_len(&self) -> usize {
self.auth_tokens.read().unwrap().len()
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 55c9591..95dd026 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -476,7 +476,6 @@
let mut user_id: i32 = -1;
let mut user_secure_ids = Vec::<i64>::new();
let mut key_time_out: Option<i64> = None;
- let mut allow_while_on_body = false;
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;
@@ -533,9 +532,6 @@
KeyParameterValue::UnlockedDeviceRequired => {
unlocked_device_required = true;
}
- KeyParameterValue::AllowWhileOnBody => {
- allow_while_on_body = true;
- }
KeyParameterValue::UsageCountLimit(_) => {
// We don't examine the limit here because this is enforced on finish.
// Instead, we store the key_id so that finish can look up the key
@@ -607,13 +603,12 @@
let (hat, state) = if user_secure_ids.is_empty() {
(None, DeferredAuthState::NoAuthRequired)
} else if let Some(key_time_out) = key_time_out {
- let (hat, last_off_body) =
- Self::find_auth_token(|hat: &AuthTokenEntry| match user_auth_type {
- Some(auth_type) => hat.satisfies(&user_secure_ids, auth_type),
- None => false, // not reachable due to earlier check
- })
- .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
- .context(ks_err!("No suitable auth token found."))?;
+ let hat = Self::find_auth_token(|hat: &AuthTokenEntry| match user_auth_type {
+ Some(auth_type) => hat.satisfies(&user_secure_ids, auth_type),
+ None => false, // not reachable due to earlier check
+ })
+ .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+ .context(ks_err!("No suitable auth token found."))?;
let now = BootTime::now();
let token_age = now
.checked_sub(&hat.time_received())
@@ -623,9 +618,7 @@
Validity cannot be established."
))?;
- let on_body_extended = allow_while_on_body && last_off_body < hat.time_received();
-
- if token_age.seconds() > key_time_out && !on_body_extended {
+ if token_age.seconds() > key_time_out {
return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
.context(ks_err!("matching auth token is expired."));
}
@@ -660,8 +653,8 @@
let need_auth_token = timeout_bound || unlocked_device_required;
- let hat_and_last_off_body = if need_auth_token {
- let hat_and_last_off_body = Self::find_auth_token(|hat: &AuthTokenEntry| {
+ let hat = if need_auth_token {
+ let hat = Self::find_auth_token(|hat: &AuthTokenEntry| {
if let (Some(auth_type), true) = (user_auth_type, timeout_bound) {
hat.satisfies(&user_secure_ids, auth_type)
} else {
@@ -669,8 +662,7 @@
}
});
Some(
- hat_and_last_off_body
- .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+ hat.ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
.context(ks_err!("No suitable auth token found."))?,
)
} else {
@@ -678,8 +670,8 @@
};
// Now check the validity of the auth token if the key is timeout bound.
- let hat = match (hat_and_last_off_body, key_time_out) {
- (Some((hat, last_off_body)), Some(key_time_out)) => {
+ let hat = match (hat, key_time_out) {
+ (Some(hat), Some(key_time_out)) => {
let now = BootTime::now();
let token_age = now
.checked_sub(&hat.time_received())
@@ -689,15 +681,13 @@
Validity cannot be established."
))?;
- let on_body_extended = allow_while_on_body && last_off_body < hat.time_received();
-
- if token_age.seconds() > key_time_out && !on_body_extended {
+ if token_age.seconds() > key_time_out {
return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
.context(ks_err!("matching auth token is expired."));
}
Some(hat)
}
- (Some((hat, _)), None) => Some(hat),
+ (Some(hat), None) => Some(hat),
// If timeout_bound is true, above code must have retrieved a HAT or returned with
// KEY_USER_NOT_AUTHENTICATED. This arm should not be reachable.
(None, Some(_)) => panic!("Logical error."),
@@ -728,7 +718,7 @@
})
}
- fn find_auth_token<F>(p: F) -> Option<(AuthTokenEntry, BootTime)>
+ fn find_auth_token<F>(p: F) -> Option<AuthTokenEntry>
where
F: Fn(&AuthTokenEntry) -> bool,
{
@@ -848,7 +838,7 @@
(challenge == hat.challenge()) && hat.satisfies(&sids, auth_type)
});
- let auth_token = if let Some((auth_token_entry, _)) = result {
+ let auth_token = if let Some(auth_token_entry) = result {
auth_token_entry.take_auth_token()
} else {
// Filter the matching auth tokens by age.
@@ -863,7 +853,7 @@
token_valid && auth_token_entry.satisfies(&sids, auth_type)
});
- if let Some((auth_token_entry, _)) = result {
+ if let Some(auth_token_entry) = result {
auth_token_entry.take_auth_token()
} else {
return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND))
@@ -895,11 +885,7 @@
let result =
Self::find_auth_token(|entry: &AuthTokenEntry| entry.satisfies(&sids, auth_type));
- if let Some((auth_token_entry, _)) = result {
- Some(auth_token_entry.time_received())
- } else {
- None
- }
+ result.map(|auth_token_entry| auth_token_entry.time_received())
}
}
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index eb755a6..7ac1038 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -16,6 +16,7 @@
//! database connections and connections to services that Keystore needs
//! to talk to.
+use crate::async_task::AsyncTask;
use crate::gc::Gc;
use crate::km_compat::{BacklevelKeyMintWrapper, KeyMintV1};
use crate::ks_err;
@@ -23,7 +24,6 @@
use crate::legacy_importer::LegacyImporter;
use crate::super_key::SuperKeyManager;
use crate::utils::watchdog as wd;
-use crate::{async_task::AsyncTask, database::BootTime};
use crate::{
database::KeystoreDB,
database::Uuid,
@@ -68,7 +68,6 @@
DB_INIT.call_once(|| {
log::info!("Touching Keystore 2.0 database for this first time since boot.");
- db.insert_last_off_body(BootTime::now());
log::info!("Calling cleanup leftovers.");
let n = db.cleanup_leftovers().expect("Failed to cleanup database on startup.");
if n != 0 {
@@ -247,7 +246,11 @@
}
e => e,
})
- .context(ks_err!("Trying to get Legacy wrapper."))?,
+ .context(ks_err!(
+ "Trying to get Legacy wrapper. Attempt to get keystore \
+ compat service for security level {:?}",
+ *security_level
+ ))?,
None,
)
};
@@ -394,7 +397,7 @@
}
e => e,
})
- .context(ks_err!("Trying to get Legacy wrapper."))
+ .context(ks_err!("Failed attempt to get legacy secure clock."))
}?;
Ok(secureclock)
@@ -437,5 +440,5 @@
_ => None,
}
.ok_or(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
- .context(ks_err!())
+ .context(ks_err!("Failed to get rpc for sec level {:?}", *security_level))
}
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index 02a1f16..bd45207 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -912,7 +912,8 @@
/// The time in seconds for which the key is authorized for use, after user authentication
#[key_param(tag = AUTH_TIMEOUT, field = Integer)]
AuthTimeout(i32),
- /// The key may be used after authentication timeout if device is still on-body
+ /// The key's authentication timeout, if it has one, is automatically expired when the device is
+ /// removed from the user's body. No longer implemented; this tag is no longer enforced.
#[key_param(tag = ALLOW_WHILE_ON_BODY, field = BoolValue)]
AllowWhileOnBody,
/// The key must be unusable except when the user has provided proof of physical presence
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 3e34cff..8780e9e 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -14,7 +14,7 @@
//! This module implements IKeystoreMaintenance AIDL interface.
-use crate::database::{BootTime, KeyEntryLoadBits, KeyType};
+use crate::database::{KeyEntryLoadBits, KeyType};
use crate::error::map_km_error;
use crate::error::map_or_log_err;
use crate::error::Error;
@@ -224,14 +224,6 @@
Maintenance::call_on_all_security_levels("earlyBootEnded", |dev| dev.earlyBootEnded())
}
- fn on_device_off_body() -> Result<()> {
- // Security critical permission check. This statement must return on fail.
- check_keystore_permission(KeystorePerm::ReportOffBody).context(ks_err!())?;
-
- DB.with(|db| db.borrow_mut().update_last_off_body(BootTime::now()));
- Ok(())
- }
-
fn migrate_key_namespace(source: &KeyDescriptor, destination: &KeyDescriptor) -> Result<()> {
let calling_uid = ThreadState::get_calling_uid();
@@ -355,12 +347,6 @@
map_or_log_err(Self::early_boot_ended(), Ok)
}
- fn onDeviceOffBody(&self) -> BinderResult<()> {
- log::info!("onDeviceOffBody()");
- let _wp = wd::watch_millis("IKeystoreMaintenance::onDeviceOffBody", 500);
- map_or_log_err(Self::on_device_off_body(), Ok)
- }
-
fn migrateKeyNamespace(
&self,
source: &KeyDescriptor,
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index bc73744..982bc82 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -137,10 +137,7 @@
/// Checked when earlyBootEnded() is called.
#[selinux(name = early_boot_ended)]
EarlyBootEnded,
- /// Checked when IKeystoreMaintenance::onDeviceOffBody is called.
- #[selinux(name = report_off_body)]
- ReportOffBody,
- /// Checked when IkeystoreMetrics::pullMetrics is called.
+ /// Checked when IKeystoreMetrics::pullMetrics is called.
#[selinux(name = pull_metrics)]
PullMetrics,
/// Checked when IKeystoreMaintenance::deleteAllKeys is called.
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index 0ef8c95..d70fe22 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -31,7 +31,6 @@
use anyhow::{Context, Result};
use keystore2_crypto::parse_subject_from_certificate;
-use crate::database::Uuid;
use crate::error::wrapped_rkpd_error_to_ks_error;
use crate::globals::get_remotely_provisioned_component_name;
use crate::ks_err;
@@ -44,18 +43,12 @@
#[derive(Default)]
pub struct RemProvState {
security_level: SecurityLevel,
- km_uuid: Uuid,
}
impl RemProvState {
/// Creates a RemProvState struct.
- pub fn new(security_level: SecurityLevel, km_uuid: Uuid) -> Self {
- Self { security_level, km_uuid }
- }
-
- /// Returns the uuid for the KM instance attached to this RemProvState struct.
- pub fn get_uuid(&self) -> Uuid {
- self.km_uuid
+ pub fn new(security_level: SecurityLevel) -> Self {
+ Self { security_level }
}
fn is_rkp_only(&self) -> bool {
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 5f9745f..cd15527 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -99,7 +99,7 @@
hw_info,
km_uuid,
operation_db: OperationDb::new(),
- rem_prov_state: RemProvState::new(security_level, km_uuid),
+ rem_prov_state: RemProvState::new(security_level),
id_rotation_state,
},
BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 44ce9ab..11ab734 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -1011,7 +1011,7 @@
let mut errs = vec![];
for sid in &biometric.sids {
let sid = *sid;
- if let Some((auth_token_entry, _)) = db.find_auth_token_entry(|entry| {
+ if let Some(auth_token_entry) = db.find_auth_token_entry(|entry| {
entry.auth_token().userId == sid || entry.auth_token().authenticatorId == sid
}) {
let res: Result<(Arc<SuperKey>, Arc<SuperKey>)> = (|| {
diff --git a/keystore2/test_utils/run_as.rs b/keystore2/test_utils/run_as.rs
index be643b6..d39d069 100644
--- a/keystore2/test_utils/run_as.rs
+++ b/keystore2/test_utils/run_as.rs
@@ -29,13 +29,14 @@
use keystore2_selinux as selinux;
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::{
- close, fork, pipe as nix_pipe, read as nix_read, setgid, setuid, write as nix_write,
- ForkResult, Gid, Pid, Uid,
+ fork, pipe as nix_pipe, read as nix_read, setgid, setuid, write as nix_write, ForkResult, Gid,
+ Pid, Uid,
};
use serde::{de::DeserializeOwned, Serialize};
use std::io::{Read, Write};
use std::marker::PhantomData;
-use std::os::unix::io::RawFd;
+use std::os::fd::AsRawFd;
+use std::os::fd::OwnedFd;
fn transition(se_context: selinux::Context, uid: Uid, gid: Gid) {
setgid(gid).expect("Failed to set GID. This test might need more privileges.");
@@ -48,35 +49,23 @@
/// PipeReader is a simple wrapper around raw pipe file descriptors.
/// It takes ownership of the file descriptor and closes it on drop. It provides `read_all`, which
/// reads from the pipe into an expending vector, until no more data can be read.
-struct PipeReader(RawFd);
+struct PipeReader(OwnedFd);
impl Read for PipeReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
- let bytes = nix_read(self.0, buf)?;
+ let bytes = nix_read(self.0.as_raw_fd(), buf)?;
Ok(bytes)
}
}
-impl Drop for PipeReader {
- fn drop(&mut self) {
- close(self.0).expect("Failed to close reader pipe fd.");
- }
-}
-
/// PipeWriter is a simple wrapper around raw pipe file descriptors.
/// It takes ownership of the file descriptor and closes it on drop. It provides `write`, which
/// writes the given buffer into the pipe, returning the number of bytes written.
-struct PipeWriter(RawFd);
-
-impl Drop for PipeWriter {
- fn drop(&mut self) {
- close(self.0).expect("Failed to close writer pipe fd.");
- }
-}
+struct PipeWriter(OwnedFd);
impl Write for PipeWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
- let written = nix_write(self.0, buf)?;
+ let written = nix_write(&self.0, buf)?;
Ok(written)
}
diff --git a/keystore2/tests/keystore2_client_authorizations_tests.rs b/keystore2/tests/keystore2_client_authorizations_tests.rs
index 0fde7af..32be99e 100644
--- a/keystore2/tests/keystore2_client_authorizations_tests.rs
+++ b/keystore2/tests/keystore2_client_authorizations_tests.rs
@@ -442,36 +442,6 @@
delete_app_key(&keystore2, alias).unwrap();
}
-/// Generate a key with `BOOTLOADER_ONLY`. Test should successfully generate
-/// a key and verify the key characteristics. Test should fail with error code `INVALID_KEY_BLOB`
-/// during creation of an operation using this key.
-#[test]
-fn keystore2_gen_key_auth_boot_loader_only_op_fail() {
- skip_tests_if_keymaster_impl_present!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
-
- let gen_params = authorizations::AuthSetBuilder::new()
- .no_auth_required()
- .algorithm(Algorithm::EC)
- .purpose(KeyPurpose::SIGN)
- .purpose(KeyPurpose::VERIFY)
- .digest(Digest::SHA_2_256)
- .ec_curve(EcCurve::P_256)
- .attestation_challenge(b"foo".to_vec())
- .boot_loader_only();
-
- let alias = "ks_test_auth_tags_test";
- let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
- &sec_level,
- &gen_params,
- &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
- alias,
- ));
- assert!(result.is_err());
- assert_eq!(Error::Km(ErrorCode::INVALID_KEY_BLOB), result.unwrap_err());
-}
-
/// Generate a key with `EARLY_BOOT_ONLY`. Test should successfully generate
/// a key and verify the key characteristics. Test should fail with error code `EARLY_BOOT_ENDED`
/// during creation of an operation using this key.
diff --git a/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs b/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
index 4f881bc..b784adf 100644
--- a/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
+++ b/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
@@ -181,7 +181,10 @@
alias,
));
assert!(result.is_err());
- assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
+ assert!(matches!(
+ result.unwrap_err(),
+ Error::Km(ErrorCode::INVALID_ARGUMENT) | Error::Km(ErrorCode::UNSUPPORTED_TAG)
+ ));
}
/// Generate a EC key with `DEVICE_UNIQUE_ATTESTATION` using `STRONGBOX` security level.
diff --git a/keystore2/tests/keystore2_client_ec_key_tests.rs b/keystore2/tests/keystore2_client_ec_key_tests.rs
index 8267140..f2c6d0f 100644
--- a/keystore2/tests/keystore2_client_ec_key_tests.rs
+++ b/keystore2/tests/keystore2_client_ec_key_tests.rs
@@ -30,8 +30,8 @@
};
use crate::keystore2_client_test_utils::{
- delete_app_key, execute_op_run_as_child, perform_sample_sign_operation, BarrierReached,
- ForcedOp, TestOutcome,
+ delete_app_key, execute_op_run_as_child, get_vsr_api_level, perform_sample_sign_operation,
+ BarrierReached, ForcedOp, TestOutcome,
};
macro_rules! test_ec_sign_key_op_success {
@@ -374,13 +374,18 @@
)
.unwrap();
- let result = key_generations::map_ks_error(sec_level.createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
- false,
- ));
- assert!(result.is_err());
- assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+ // The KeyMint v2 API added `CURVE_25519` and specified that "Ed25519 keys only support
+ // Digest::NONE". However, this was not checked at the time so we can only be strict about
+ // checking this for more recent implementations.
+ if get_vsr_api_level() >= 35 {
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
+ false,
+ ));
+ assert!(result.is_err(), "unexpected success for digest {digest:?}");
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+ }
}
}
diff --git a/keystore2/tests/keystore2_client_import_keys_tests.rs b/keystore2/tests/keystore2_client_import_keys_tests.rs
index 31d57a2..bf787d2 100644
--- a/keystore2/tests/keystore2_client_import_keys_tests.rs
+++ b/keystore2/tests/keystore2_client_import_keys_tests.rs
@@ -37,9 +37,9 @@
};
use crate::keystore2_client_test_utils::{
- encrypt_secure_key, encrypt_transport_key, perform_sample_asym_sign_verify_op,
- perform_sample_hmac_sign_verify_op, perform_sample_sym_key_decrypt_op,
- perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+ encrypt_secure_key, encrypt_transport_key, get_vsr_api_level,
+ perform_sample_asym_sign_verify_op, perform_sample_hmac_sign_verify_op,
+ perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
};
pub fn import_rsa_sign_key_and_perform_sample_operation(
@@ -306,6 +306,13 @@
let alias = format!("ks_ec_key_test_import_1_{}{}", getuid(), 256);
+ if get_vsr_api_level() < 35 {
+ // The KeyMint spec was previously not clear as to whether EC_CURVE was optional on import
+ // of EC keys. However, this was not checked at the time so we can only be strict about
+ // checking this for implementations at VSR-V or later.
+ println!("Skipping EC_CURVE on import only strict >= VSR-V");
+ return;
+ }
// Don't specify ec-curve.
let import_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
diff --git a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
index 0335159..3be99ee 100644
--- a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
+++ b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
@@ -46,6 +46,10 @@
static AUTH_SERVICE_NAME: &str = "android.security.authorization";
const SELINUX_SHELL_NAMESPACE: i64 = 1;
+fn rkp_only() -> bool {
+ matches!(rustutils::system_properties::read("remote_provisioning.tee.rkp_only"), Ok(Some(v)) if v == "1")
+}
+
fn get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance> {
binder::get_interface(USER_MANAGER_SERVICE_NAME).unwrap()
}
@@ -162,13 +166,13 @@
.getSecurityLevel(SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT)
.unwrap();
// Generate Key BLOB and prepare legacy keystore blob files.
- let att_challenge: &[u8] = b"foo";
+ let att_challenge: Option<&[u8]> = if rkp_only() { None } else { Some(b"foo") };
let key_metadata = key_generations::generate_ec_p256_signing_key(
&sec_level,
Domain::BLOB,
SELINUX_SHELL_NAMESPACE,
None,
- Some(att_challenge),
+ att_challenge,
)
.expect("Failed to generate key blob");
@@ -212,14 +216,12 @@
.unwrap();
}
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
- path_buf.push("9910001_CACERT_authbound");
- if !path_buf.as_path().is_file() {
- make_cert_blob_file(
- path_buf.as_path(),
- key_metadata.certificateChain.as_ref().unwrap(),
- )
- .unwrap();
+ if let Some(chain) = key_metadata.certificateChain.as_ref() {
+ let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
+ path_buf.push("9910001_CACERT_authbound");
+ if !path_buf.as_path().is_file() {
+ make_cert_blob_file(path_buf.as_path(), chain).unwrap();
+ }
}
// Keystore2 disables the legacy importer when it finds the legacy database empty.
@@ -246,7 +248,7 @@
KeygenResult {
cert: key_metadata.certificate.unwrap(),
- cert_chain: key_metadata.certificateChain.unwrap(),
+ cert_chain: key_metadata.certificateChain.unwrap_or_default(),
key_parameters: key_params,
}
})
@@ -275,7 +277,7 @@
gen_key_result.cert
);
assert_eq!(
- key_entry_response.metadata.certificateChain.unwrap(),
+ key_entry_response.metadata.certificateChain.unwrap_or_default(),
gen_key_result.cert_chain
);
assert_eq!(key_entry_response.metadata.key.domain, Domain::KEY_ID);
@@ -415,13 +417,13 @@
.getSecurityLevel(SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT)
.unwrap();
// Generate Key BLOB and prepare legacy keystore blob files.
- let att_challenge: &[u8] = b"foo";
+ let att_challenge: Option<&[u8]> = if rkp_only() { None } else { Some(b"foo") };
let key_metadata = key_generations::generate_ec_p256_signing_key(
&sec_level,
Domain::BLOB,
SELINUX_SHELL_NAMESPACE,
None,
- Some(att_challenge),
+ att_challenge,
)
.expect("Failed to generate key blob");
@@ -468,15 +470,12 @@
.unwrap();
}
- let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
- path_buf.push("9810001_CACERT_authboundcertenc");
- if !path_buf.as_path().is_file() {
- make_encrypted_ca_cert_file(
- path_buf.as_path(),
- &super_key,
- key_metadata.certificateChain.as_ref().unwrap(),
- )
- .unwrap();
+ if let Some(chain) = key_metadata.certificateChain.as_ref() {
+ let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
+ path_buf.push("9810001_CACERT_authboundcertenc");
+ if !path_buf.as_path().is_file() {
+ make_encrypted_ca_cert_file(path_buf.as_path(), &super_key, chain).unwrap();
+ }
}
// Keystore2 disables the legacy importer when it finds the legacy database empty.
@@ -503,7 +502,7 @@
KeygenResult {
cert: key_metadata.certificate.unwrap(),
- cert_chain: key_metadata.certificateChain.unwrap(),
+ cert_chain: key_metadata.certificateChain.unwrap_or_default(),
key_parameters: key_params,
}
})
@@ -532,7 +531,7 @@
gen_key_result.cert
);
assert_eq!(
- key_entry_response.metadata.certificateChain.unwrap(),
+ key_entry_response.metadata.certificateChain.unwrap_or_default(),
gen_key_result.cert_chain
);
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
index d0c934d..ede1ae6 100644
--- a/provisioner/Android.bp
+++ b/provisioner/Android.bp
@@ -39,7 +39,7 @@
"android.hardware.drm-V1-ndk",
"android.hardware.security.rkp-V3-ndk",
"libbase",
- "libcppbor_external",
+ "libcppbor",
"libcppcose_rkp",
"libjsoncpp",
"libkeymint_remote_prov_support",
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
index 0a3a59a..62d62cf 100644
--- a/provisioner/rkp_factory_extraction_tool.cpp
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -24,6 +24,7 @@
#include <remote_prov/remote_prov_utils.h>
#include <sys/random.h>
+#include <future>
#include <string>
#include <vector>
@@ -91,7 +92,13 @@
// for every IRemotelyProvisionedComponent.
void getCsrForInstance(const char* name, void* /*context*/) {
auto fullName = getFullServiceName(IRemotelyProvisionedComponent::descriptor, name);
- AIBinder* rkpAiBinder = AServiceManager_getService(fullName.c_str());
+ std::future<AIBinder*> wait_for_service_func =
+ std::async(std::launch::async, AServiceManager_waitForService, fullName.c_str());
+ if (wait_for_service_func.wait_for(std::chrono::seconds(10)) == std::future_status::timeout) {
+ std::cerr << "Wait for service timed out after 10 seconds: " << fullName;
+ exit(-1);
+ }
+ AIBinder* rkpAiBinder = wait_for_service_func.get();
::ndk::SpAIBinder rkp_binder(rkpAiBinder);
auto rkp_service = IRemotelyProvisionedComponent::fromBinder(rkp_binder);
if (!rkp_service) {