summaryrefslogtreecommitdiff
path: root/keystore2
diff options
context:
space:
mode:
Diffstat (limited to 'keystore2')
-rw-r--r--keystore2/Android.bp7
-rw-r--r--keystore2/OWNERS1
-rw-r--r--keystore2/aconfig/flags.aconfig10
-rw-r--r--keystore2/aidl/android/security/apc/IConfirmationCallback.aidl4
-rw-r--r--keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl13
-rw-r--r--keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl2
-rw-r--r--keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl25
-rw-r--r--keystore2/legacykeystore/lib.rs83
-rw-r--r--keystore2/rkpd_client/src/lib.rs329
-rw-r--r--keystore2/rkpd_client/src/tests.rs338
-rw-r--r--keystore2/src/apc.rs66
-rw-r--r--keystore2/src/async_task.rs279
-rw-r--r--keystore2/src/async_task/tests.rs287
-rw-r--r--keystore2/src/audit_log.rs41
-rw-r--r--keystore2/src/authorization.rs151
-rw-r--r--keystore2/src/crypto/zvec.rs9
-rw-r--r--keystore2/src/database.rs2998
-rw-r--r--keystore2/src/database/perboot.rs19
-rw-r--r--keystore2/src/database/tests.rs2528
-rw-r--r--keystore2/src/database/versioning.rs4
-rw-r--r--keystore2/src/enforcements.rs192
-rw-r--r--keystore2/src/error.rs291
-rw-r--r--keystore2/src/error/tests.rs218
-rw-r--r--keystore2/src/gc.rs10
-rw-r--r--keystore2/src/globals.rs61
-rw-r--r--keystore2/src/key_parameter.rs502
-rw-r--r--keystore2/src/key_parameter/basic_tests.rs28
-rw-r--r--keystore2/src/key_parameter/generated_key_parameter_tests.rs95
-rw-r--r--keystore2/src/key_parameter/storage_tests.rs263
-rw-r--r--keystore2/src/key_parameter/wire_tests.rs119
-rw-r--r--keystore2/src/km_compat.rs8
-rw-r--r--keystore2/src/legacy_blob.rs675
-rw-r--r--keystore2/src/legacy_blob/tests.rs676
-rw-r--r--keystore2/src/legacy_importer.rs14
-rw-r--r--keystore2/src/maintenance.rs130
-rw-r--r--keystore2/src/metrics.rs6
-rw-r--r--keystore2/src/operation.rs83
-rw-r--r--keystore2/src/permission.rs438
-rw-r--r--keystore2/src/permission/tests.rs434
-rw-r--r--keystore2/src/raw_device.rs18
-rw-r--r--keystore2/src/remote_provisioning.rs13
-rw-r--r--keystore2/src/security_level.rs155
-rw-r--r--keystore2/src/service.rs53
-rw-r--r--keystore2/src/super_key.rs496
-rw-r--r--keystore2/src/super_key/tests.rs287
-rw-r--r--keystore2/src/sw_keyblob.rs481
-rw-r--r--keystore2/src/sw_keyblob/tests.rs449
-rw-r--r--keystore2/src/utils.rs135
-rw-r--r--keystore2/src/utils/tests.rs125
-rw-r--r--keystore2/src/watchdog_helper.rs24
-rw-r--r--keystore2/test_utils/authorizations.rs9
-rw-r--r--keystore2/test_utils/key_generations.rs211
-rw-r--r--keystore2/test_utils/lib.rs73
-rw-r--r--keystore2/test_utils/run_as.rs27
-rw-r--r--keystore2/tests/Android.bp5
-rw-r--r--keystore2/tests/AndroidTest.xml1
-rw-r--r--keystore2/tests/keystore2_client_3des_key_tests.rs56
-rw-r--r--keystore2/tests/keystore2_client_aes_key_tests.rs104
-rw-r--r--keystore2/tests/keystore2_client_attest_key_tests.rs209
-rw-r--r--keystore2/tests/keystore2_client_authorizations_tests.rs307
-rw-r--r--keystore2/tests/keystore2_client_delete_key_tests.rs56
-rw-r--r--keystore2/tests/keystore2_client_device_unique_attestation_tests.rs115
-rw-r--r--keystore2/tests/keystore2_client_ec_key_tests.rs114
-rw-r--r--keystore2/tests/keystore2_client_grant_key_tests.rs127
-rw-r--r--keystore2/tests/keystore2_client_hmac_key_tests.rs64
-rw-r--r--keystore2/tests/keystore2_client_import_keys_tests.rs170
-rw-r--r--keystore2/tests/keystore2_client_key_agreement_tests.rs45
-rw-r--r--keystore2/tests/keystore2_client_key_id_domain_tests.rs56
-rw-r--r--keystore2/tests/keystore2_client_keystore_engine_tests.rs85
-rw-r--r--keystore2/tests/keystore2_client_list_entries_tests.rs155
-rw-r--r--keystore2/tests/keystore2_client_operation_tests.rs32
-rw-r--r--keystore2/tests/keystore2_client_rsa_key_tests.rs97
-rw-r--r--keystore2/tests/keystore2_client_test_utils.rs119
-rw-r--r--keystore2/tests/keystore2_client_tests.rs3
-rw-r--r--keystore2/tests/keystore2_client_update_subcomponent_tests.rs30
-rw-r--r--keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs92
-rw-r--r--keystore2/tests/user_auth.rs245
-rw-r--r--keystore2/watchdog/src/lib.rs179
-rw-r--r--keystore2/watchdog/src/tests.rs86
79 files changed, 8031 insertions, 8514 deletions
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 7cb7c37a..28bdfea9 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -28,6 +28,7 @@ rust_defaults {
defaults: [
"keymint_use_latest_hal_aidl_rust",
"keystore2_use_latest_aidl_rust",
+ "structured_log_rust_defaults",
],
rustlibs: [
@@ -54,7 +55,6 @@ rust_defaults {
"libkeystore2_selinux",
"liblazy_static",
"liblibc",
- "liblog_event_list",
"liblog_rust",
"libmessage_macro",
"librand",
@@ -162,6 +162,11 @@ aconfig_declarations {
srcs: ["aconfig/flags.aconfig"],
}
+java_aconfig_library {
+ name: "keystore2_flags_java",
+ aconfig_declarations: "keystore2_flags",
+}
+
rust_aconfig_library {
name: "libkeystore2_flags_rust",
crate_name: "keystore2_flags",
diff --git a/keystore2/OWNERS b/keystore2/OWNERS
index 6b1a95bd..bf9d61b3 100644
--- a/keystore2/OWNERS
+++ b/keystore2/OWNERS
@@ -5,5 +5,4 @@ drysdale@google.com
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 b67bc6cb..05dae462 100644
--- a/keystore2/aconfig/flags.aconfig
+++ b/keystore2/aconfig/flags.aconfig
@@ -18,9 +18,17 @@ flag {
}
flag {
+ name: "disable_legacy_keystore_get"
+ namespace: "hardware_backed_security"
+ description: "This flag disables legacy keystore get and makes it so that get returns an error"
+ bug: "307460850"
+ is_fixed_read_only: true
+}
+
+flag {
name: "import_previously_emulated_keys"
namespace: "hardware_backed_security"
description: "Include support for importing keys that were previously software-emulated into KeyMint"
bug: "283077822"
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 5b22be01..277b9dd8 100644
--- a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
+++ b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
@@ -27,10 +27,6 @@ interface IConfirmationCallback {
/**
* 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 9f978479..3162224f 100644
--- a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
+++ b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
@@ -35,10 +35,6 @@ interface IProtectedConfirmation {
/**
* 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 @@ interface IProtectedConfirmation {
/**
* 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 @@ interface IProtectedConfirmation {
/**
* 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 a9de0263..fd532f62 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.HardwareAuthToken;
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 abea9582..ecc1f4b1 100644
--- a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
+++ b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
@@ -77,21 +77,6 @@ interface IKeystoreMaintenance {
void onUserLskfRemoved(in int userId);
/**
- * Allows LockSettingsService to inform keystore about password change of a user.
- * Callers require 'ChangePassword' permission.
- *
- * ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the callers does not have the 'ChangePassword'
- * permission.
- * `ResponseCode::SYSTEM_ERROR` - if failed to delete the super encrypted keys of the user.
- * `ResponseCode::Locked' - if the keystore is locked for the given user.
- *
- * @param userId - Android user id
- * @param password - a secret derived from the synthetic password of the user
- */
- void onUserPasswordChanged(in int userId, in @nullable byte[] password);
-
- /**
* This function deletes all keys within a namespace. It mainly gets called when an app gets
* removed and all resources of this app need to be cleaned up.
*
@@ -112,16 +97,6 @@ interface IKeystoreMaintenance {
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/legacykeystore/lib.rs b/keystore2/legacykeystore/lib.rs
index f7a81983..b173da83 100644
--- a/keystore2/legacykeystore/lib.rs
+++ b/keystore2/legacykeystore/lib.rs
@@ -55,7 +55,7 @@ impl DB {
F: Fn(&Transaction) -> Result<T>,
{
loop {
- match self
+ let result = self
.conn
.transaction_with_behavior(behavior)
.context("In with_transaction.")
@@ -63,7 +63,8 @@ impl DB {
.and_then(|(result, tx)| {
tx.commit().context("In with_transaction: Failed to commit transaction.")?;
Ok(result)
- }) {
+ });
+ match result {
Ok(result) => break Ok(result),
Err(e) => {
if Self::is_locked_error(&e) {
@@ -133,6 +134,7 @@ impl DB {
}
fn get(&mut self, caller_uid: u32, alias: &str) -> Result<Option<Vec<u8>>> {
+ ensure_keystore_get_is_enabled()?;
self.with_transaction(TransactionBehavior::Deferred, |tx| {
tx.query_row(
"SELECT profile FROM profiles WHERE owner = ? AND alias = ?;",
@@ -209,41 +211,22 @@ impl Error {
}
}
-/// This function should be used by legacykeystore service calls to translate error conditions
-/// into service specific exceptions.
+/// Translate an error into a service specific exception, logging along the way.
///
-/// All error conditions get logged by this function, except for ERROR_ENTRY_NOT_FOUND error.
-///
-/// `Error::Error(x)` variants get mapped onto a service specific error code of `x`.
-///
-/// All non `Error` error conditions get mapped onto `ERROR_SYSTEM_ERROR`.
-///
-/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
-/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
-/// typically returns Ok(value).
-fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
-where
- F: FnOnce(U) -> BinderResult<T>,
-{
- result.map_or_else(
- |e| {
- let root_cause = e.root_cause();
- let (rc, log_error) = match root_cause.downcast_ref::<Error>() {
- // Make the entry not found errors silent.
- Some(Error::Error(ERROR_ENTRY_NOT_FOUND)) => (ERROR_ENTRY_NOT_FOUND, false),
- Some(Error::Error(e)) => (*e, true),
- Some(Error::Binder(_, _)) | None => (ERROR_SYSTEM_ERROR, true),
- };
- if log_error {
- log::error!("{:?}", e);
- }
- Err(BinderStatus::new_service_specific_error(
- rc,
- anyhow_error_to_cstring(&e).as_deref(),
- ))
- },
- handle_ok,
- )
+/// `Error::Error(x)` variants get mapped onto a service specific error code of `x`, other errors
+/// are mapped to `ERROR_SYSTEM_ERROR`.
+fn into_logged_binder(e: anyhow::Error) -> BinderStatus {
+ let root_cause = e.root_cause();
+ let (rc, log_error) = match root_cause.downcast_ref::<Error>() {
+ // Make the entry not found errors silent.
+ Some(Error::Error(ERROR_ENTRY_NOT_FOUND)) => (ERROR_ENTRY_NOT_FOUND, false),
+ Some(Error::Error(e)) => (*e, true),
+ Some(Error::Binder(_, _)) | None => (ERROR_SYSTEM_ERROR, true),
+ };
+ if log_error {
+ log::error!("{:?}", e);
+ }
+ BinderStatus::new_service_specific_error(rc, anyhow_error_to_cstring(&e).as_deref())
}
fn ensure_keystore_put_is_enabled() -> Result<()> {
@@ -257,6 +240,17 @@ fn ensure_keystore_put_is_enabled() -> Result<()> {
}
}
+fn ensure_keystore_get_is_enabled() -> Result<()> {
+ if keystore2_flags::disable_legacy_keystore_get() {
+ Err(Error::deprecated()).context(concat!(
+ "Retrieving from Keystore's legacy database is ",
+ "no longer supported, store in an app-specific database instead"
+ ))
+ } else {
+ Ok(())
+ }
+}
+
struct LegacyKeystoreDeleteListener {
legacy_keystore: Arc<LegacyKeystore>,
}
@@ -331,6 +325,7 @@ impl LegacyKeystore {
}
fn get(&self, alias: &str, uid: i32) -> Result<Vec<u8>> {
+ ensure_keystore_get_is_enabled()?;
let mut db = self.open_db().context("In get.")?;
let uid = Self::get_effective_uid(uid).context("In get.")?;
@@ -550,20 +545,20 @@ impl binder::Interface for LegacyKeystoreService {}
impl ILegacyKeystore for LegacyKeystoreService {
fn get(&self, alias: &str, uid: i32) -> BinderResult<Vec<u8>> {
- let _wp = wd::watch_millis("ILegacyKeystore::get", 500);
- map_or_log_err(self.legacy_keystore.get(alias, uid), Ok)
+ let _wp = wd::watch("ILegacyKeystore::get");
+ self.legacy_keystore.get(alias, uid).map_err(into_logged_binder)
}
fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> BinderResult<()> {
- let _wp = wd::watch_millis("ILegacyKeystore::put", 500);
- map_or_log_err(self.legacy_keystore.put(alias, uid, entry), Ok)
+ let _wp = wd::watch("ILegacyKeystore::put");
+ self.legacy_keystore.put(alias, uid, entry).map_err(into_logged_binder)
}
fn remove(&self, alias: &str, uid: i32) -> BinderResult<()> {
- let _wp = wd::watch_millis("ILegacyKeystore::remove", 500);
- map_or_log_err(self.legacy_keystore.remove(alias, uid), Ok)
+ let _wp = wd::watch("ILegacyKeystore::remove");
+ self.legacy_keystore.remove(alias, uid).map_err(into_logged_binder)
}
fn list(&self, prefix: &str, uid: i32) -> BinderResult<Vec<String>> {
- let _wp = wd::watch_millis("ILegacyKeystore::list", 500);
- map_or_log_err(self.legacy_keystore.list(prefix, uid), Ok)
+ let _wp = wd::watch("ILegacyKeystore::list");
+ self.legacy_keystore.list(prefix, uid).map_err(into_logged_binder)
}
}
diff --git a/keystore2/rkpd_client/src/lib.rs b/keystore2/rkpd_client/src/lib.rs
index d8a5276c..936fe3d6 100644
--- a/keystore2/rkpd_client/src/lib.rs
+++ b/keystore2/rkpd_client/src/lib.rs
@@ -310,331 +310,4 @@ pub fn store_rkpd_attestation_key(
}
#[cfg(test)]
-mod tests {
- use super::*;
- use android_security_rkp_aidl::aidl::android::security::rkp::IRegistration::BnRegistration;
- use std::sync::atomic::{AtomicU32, Ordering};
- use std::sync::{Arc, Mutex};
-
- const DEFAULT_RPC_SERVICE_NAME: &str =
- "android.hardware.security.keymint.IRemotelyProvisionedComponent/default";
-
- struct MockRegistrationValues {
- key: RemotelyProvisionedKey,
- latency: Option<Duration>,
- thread_join_handles: Vec<Option<std::thread::JoinHandle<()>>>,
- }
-
- struct MockRegistration(Arc<Mutex<MockRegistrationValues>>);
-
- impl MockRegistration {
- pub fn new_native_binder(
- key: &RemotelyProvisionedKey,
- latency: Option<Duration>,
- ) -> Strong<dyn IRegistration> {
- let result = Self(Arc::new(Mutex::new(MockRegistrationValues {
- key: RemotelyProvisionedKey {
- keyBlob: key.keyBlob.clone(),
- encodedCertChain: key.encodedCertChain.clone(),
- },
- latency,
- thread_join_handles: Vec::new(),
- })));
- BnRegistration::new_binder(result, BinderFeatures::default())
- }
- }
-
- impl Drop for MockRegistration {
- fn drop(&mut self) {
- let mut values = self.0.lock().unwrap();
- for handle in values.thread_join_handles.iter_mut() {
- // These are test threads. So, no need to worry too much about error handling.
- handle.take().unwrap().join().unwrap();
- }
- }
- }
-
- impl Interface for MockRegistration {}
-
- impl IRegistration for MockRegistration {
- fn getKey(&self, _: i32, cb: &Strong<dyn IGetKeyCallback>) -> binder::Result<()> {
- let mut values = self.0.lock().unwrap();
- let key = RemotelyProvisionedKey {
- keyBlob: values.key.keyBlob.clone(),
- encodedCertChain: values.key.encodedCertChain.clone(),
- };
- let latency = values.latency;
- let get_key_cb = cb.clone();
-
- // Need a separate thread to trigger timeout in the caller.
- let join_handle = std::thread::spawn(move || {
- if let Some(duration) = latency {
- std::thread::sleep(duration);
- }
- get_key_cb.onSuccess(&key).unwrap();
- });
- values.thread_join_handles.push(Some(join_handle));
- Ok(())
- }
-
- fn cancelGetKey(&self, _: &Strong<dyn IGetKeyCallback>) -> binder::Result<()> {
- Ok(())
- }
-
- fn storeUpgradedKeyAsync(
- &self,
- _: &[u8],
- _: &[u8],
- cb: &Strong<dyn IStoreUpgradedKeyCallback>,
- ) -> binder::Result<()> {
- // We are primarily concerned with timing out correctly. Storing the key in this mock
- // registration isn't particularly interesting, so skip that part.
- let values = self.0.lock().unwrap();
- let store_cb = cb.clone();
- let latency = values.latency;
-
- std::thread::spawn(move || {
- if let Some(duration) = latency {
- std::thread::sleep(duration);
- }
- store_cb.onSuccess().unwrap();
- });
- Ok(())
- }
- }
-
- fn get_mock_registration(
- key: &RemotelyProvisionedKey,
- latency: Option<Duration>,
- ) -> Result<binder::Strong<dyn IRegistration>> {
- let (tx, rx) = oneshot::channel();
- let cb = GetRegistrationCallback::new_native_binder(tx);
- let mock_registration = MockRegistration::new_native_binder(key, latency);
-
- assert!(cb.onSuccess(&mock_registration).is_ok());
- tokio_rt().block_on(rx).unwrap()
- }
-
- // Using the same key ID makes test cases race with each other. So, we use separate key IDs for
- // different test cases.
- fn get_next_key_id() -> u32 {
- static ID: AtomicU32 = AtomicU32::new(0);
- ID.fetch_add(1, Ordering::Relaxed)
- }
-
- #[test]
- fn test_get_registration_cb_success() {
- let key: RemotelyProvisionedKey = Default::default();
- let registration = get_mock_registration(&key, /*latency=*/ None);
- assert!(registration.is_ok());
- }
-
- #[test]
- fn test_get_registration_cb_cancel() {
- let (tx, rx) = oneshot::channel();
- let cb = GetRegistrationCallback::new_native_binder(tx);
- assert!(cb.onCancel().is_ok());
-
- let result = tokio_rt().block_on(rx).unwrap();
- assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RequestCancelled);
- }
-
- #[test]
- fn test_get_registration_cb_error() {
- let (tx, rx) = oneshot::channel();
- let cb = GetRegistrationCallback::new_native_binder(tx);
- assert!(cb.onError("error").is_ok());
-
- let result = tokio_rt().block_on(rx).unwrap();
- assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::GetRegistrationFailed);
- }
-
- #[test]
- fn test_get_key_cb_success() {
- let mock_key =
- RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
- let (tx, rx) = oneshot::channel();
- let cb = GetKeyCallback::new_native_binder(tx);
- assert!(cb.onSuccess(&mock_key).is_ok());
-
- let key = tokio_rt().block_on(rx).unwrap().unwrap();
- assert_eq!(key, mock_key);
- }
-
- #[test]
- fn test_get_key_cb_cancel() {
- let (tx, rx) = oneshot::channel();
- let cb = GetKeyCallback::new_native_binder(tx);
- assert!(cb.onCancel().is_ok());
-
- let result = tokio_rt().block_on(rx).unwrap();
- assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RequestCancelled);
- }
-
- #[test]
- fn test_get_key_cb_error() {
- for get_key_error in GetKeyErrorCode::enum_values() {
- let (tx, rx) = oneshot::channel();
- let cb = GetKeyCallback::new_native_binder(tx);
- assert!(cb.onError(get_key_error, "error").is_ok());
-
- let result = tokio_rt().block_on(rx).unwrap();
- assert_eq!(
- result.unwrap_err().downcast::<Error>().unwrap(),
- Error::GetKeyFailed(get_key_error),
- );
- }
- }
-
- #[test]
- fn test_store_upgraded_cb_success() {
- let (tx, rx) = oneshot::channel();
- let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
- assert!(cb.onSuccess().is_ok());
-
- tokio_rt().block_on(rx).unwrap().unwrap();
- }
-
- #[test]
- fn test_store_upgraded_key_cb_error() {
- let (tx, rx) = oneshot::channel();
- let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
- assert!(cb.onError("oh no! it failed").is_ok());
-
- let result = tokio_rt().block_on(rx).unwrap();
- assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::StoreUpgradedKeyFailed);
- }
-
- #[test]
- fn test_get_mock_key_success() {
- let mock_key =
- RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
- let registration = get_mock_registration(&mock_key, /*latency=*/ None).unwrap();
-
- let key = tokio_rt()
- .block_on(get_rkpd_attestation_key_from_registration_async(&registration, 0))
- .unwrap();
- assert_eq!(key, mock_key);
- }
-
- #[test]
- fn test_get_mock_key_timeout() {
- let mock_key =
- RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
- let latency = RKPD_TIMEOUT + Duration::from_secs(1);
- let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
-
- let result =
- tokio_rt().block_on(get_rkpd_attestation_key_from_registration_async(&registration, 0));
- assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RetryableTimeout);
- }
-
- #[test]
- fn test_store_mock_key_success() {
- let mock_key =
- RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
- let registration = get_mock_registration(&mock_key, /*latency=*/ None).unwrap();
- tokio_rt()
- .block_on(store_rkpd_attestation_key_with_registration_async(&registration, &[], &[]))
- .unwrap();
- }
-
- #[test]
- fn test_store_mock_key_timeout() {
- let mock_key =
- RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
- let latency = RKPD_TIMEOUT + Duration::from_secs(1);
- let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
-
- let result = tokio_rt().block_on(store_rkpd_attestation_key_with_registration_async(
- &registration,
- &[],
- &[],
- ));
- assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::Timeout);
- }
-
- #[test]
- fn test_get_rkpd_attestation_key() {
- binder::ProcessState::start_thread_pool();
- let key_id = get_next_key_id();
- let key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
- assert!(!key.keyBlob.is_empty());
- assert!(!key.encodedCertChain.is_empty());
- }
-
- #[test]
- fn test_get_rkpd_attestation_key_same_caller() {
- binder::ProcessState::start_thread_pool();
- let key_id = get_next_key_id();
-
- // Multiple calls should return the same key.
- let first_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
- let second_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
-
- assert_eq!(first_key.keyBlob, second_key.keyBlob);
- assert_eq!(first_key.encodedCertChain, second_key.encodedCertChain);
- }
-
- #[test]
- fn test_get_rkpd_attestation_key_different_caller() {
- binder::ProcessState::start_thread_pool();
- let first_key_id = get_next_key_id();
- let second_key_id = get_next_key_id();
-
- // Different callers should be getting different keys.
- let first_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, first_key_id).unwrap();
- let second_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, second_key_id).unwrap();
-
- assert_ne!(first_key.keyBlob, second_key.keyBlob);
- assert_ne!(first_key.encodedCertChain, second_key.encodedCertChain);
- }
-
- #[test]
- // Couple of things to note:
- // 1. This test must never run with UID of keystore. Otherwise, it can mess up keys stored by
- // keystore.
- // 2. Storing and reading the stored key is prone to race condition. So, we only do this in one
- // test case.
- fn test_store_rkpd_attestation_key() {
- binder::ProcessState::start_thread_pool();
- let key_id = get_next_key_id();
- let key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
- let new_blob: [u8; 8] = rand::random();
-
- assert!(
- store_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, &key.keyBlob, &new_blob).is_ok()
- );
-
- let new_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
-
- // Restore original key so that we don't leave RKPD with invalid blobs.
- assert!(
- store_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, &new_blob, &key.keyBlob).is_ok()
- );
- assert_eq!(new_key.keyBlob, new_blob);
- }
-
- #[test]
- fn test_stress_get_rkpd_attestation_key() {
- binder::ProcessState::start_thread_pool();
- let key_id = get_next_key_id();
- let mut threads = vec![];
- const NTHREADS: u32 = 10;
- const NCALLS: u32 = 1000;
-
- for _ in 0..NTHREADS {
- threads.push(std::thread::spawn(move || {
- for _ in 0..NCALLS {
- let key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
- assert!(!key.keyBlob.is_empty());
- assert!(!key.encodedCertChain.is_empty());
- }
- }));
- }
-
- for t in threads {
- assert!(t.join().is_ok());
- }
- }
-}
+mod tests;
diff --git a/keystore2/rkpd_client/src/tests.rs b/keystore2/rkpd_client/src/tests.rs
new file mode 100644
index 00000000..fd0468f7
--- /dev/null
+++ b/keystore2/rkpd_client/src/tests.rs
@@ -0,0 +1,338 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! RKPD tests.
+
+use super::*;
+use android_security_rkp_aidl::aidl::android::security::rkp::IRegistration::BnRegistration;
+use std::sync::atomic::{AtomicU32, Ordering};
+use std::sync::{Arc, Mutex};
+
+const DEFAULT_RPC_SERVICE_NAME: &str =
+ "android.hardware.security.keymint.IRemotelyProvisionedComponent/default";
+
+struct MockRegistrationValues {
+ key: RemotelyProvisionedKey,
+ latency: Option<Duration>,
+ thread_join_handles: Vec<Option<std::thread::JoinHandle<()>>>,
+}
+
+struct MockRegistration(Arc<Mutex<MockRegistrationValues>>);
+
+impl MockRegistration {
+ pub fn new_native_binder(
+ key: &RemotelyProvisionedKey,
+ latency: Option<Duration>,
+ ) -> Strong<dyn IRegistration> {
+ let result = Self(Arc::new(Mutex::new(MockRegistrationValues {
+ key: RemotelyProvisionedKey {
+ keyBlob: key.keyBlob.clone(),
+ encodedCertChain: key.encodedCertChain.clone(),
+ },
+ latency,
+ thread_join_handles: Vec::new(),
+ })));
+ BnRegistration::new_binder(result, BinderFeatures::default())
+ }
+}
+
+impl Drop for MockRegistration {
+ fn drop(&mut self) {
+ let mut values = self.0.lock().unwrap();
+ for handle in values.thread_join_handles.iter_mut() {
+ // These are test threads. So, no need to worry too much about error handling.
+ handle.take().unwrap().join().unwrap();
+ }
+ }
+}
+
+impl Interface for MockRegistration {}
+
+impl IRegistration for MockRegistration {
+ fn getKey(&self, _: i32, cb: &Strong<dyn IGetKeyCallback>) -> binder::Result<()> {
+ let mut values = self.0.lock().unwrap();
+ let key = RemotelyProvisionedKey {
+ keyBlob: values.key.keyBlob.clone(),
+ encodedCertChain: values.key.encodedCertChain.clone(),
+ };
+ let latency = values.latency;
+ let get_key_cb = cb.clone();
+
+ // Need a separate thread to trigger timeout in the caller.
+ let join_handle = std::thread::spawn(move || {
+ if let Some(duration) = latency {
+ std::thread::sleep(duration);
+ }
+ get_key_cb.onSuccess(&key).unwrap();
+ });
+ values.thread_join_handles.push(Some(join_handle));
+ Ok(())
+ }
+
+ fn cancelGetKey(&self, _: &Strong<dyn IGetKeyCallback>) -> binder::Result<()> {
+ Ok(())
+ }
+
+ fn storeUpgradedKeyAsync(
+ &self,
+ _: &[u8],
+ _: &[u8],
+ cb: &Strong<dyn IStoreUpgradedKeyCallback>,
+ ) -> binder::Result<()> {
+ // We are primarily concerned with timing out correctly. Storing the key in this mock
+ // registration isn't particularly interesting, so skip that part.
+ let values = self.0.lock().unwrap();
+ let store_cb = cb.clone();
+ let latency = values.latency;
+
+ std::thread::spawn(move || {
+ if let Some(duration) = latency {
+ std::thread::sleep(duration);
+ }
+ store_cb.onSuccess().unwrap();
+ });
+ Ok(())
+ }
+}
+
+fn get_mock_registration(
+ key: &RemotelyProvisionedKey,
+ latency: Option<Duration>,
+) -> Result<binder::Strong<dyn IRegistration>> {
+ let (tx, rx) = oneshot::channel();
+ let cb = GetRegistrationCallback::new_native_binder(tx);
+ let mock_registration = MockRegistration::new_native_binder(key, latency);
+
+ assert!(cb.onSuccess(&mock_registration).is_ok());
+ tokio_rt().block_on(rx).unwrap()
+}
+
+// Using the same key ID makes test cases race with each other. So, we use separate key IDs for
+// different test cases.
+fn get_next_key_id() -> u32 {
+ static ID: AtomicU32 = AtomicU32::new(0);
+ ID.fetch_add(1, Ordering::Relaxed)
+}
+
+#[test]
+fn test_get_registration_cb_success() {
+ let key: RemotelyProvisionedKey = Default::default();
+ let registration = get_mock_registration(&key, /*latency=*/ None);
+ assert!(registration.is_ok());
+}
+
+#[test]
+fn test_get_registration_cb_cancel() {
+ let (tx, rx) = oneshot::channel();
+ let cb = GetRegistrationCallback::new_native_binder(tx);
+ assert!(cb.onCancel().is_ok());
+
+ let result = tokio_rt().block_on(rx).unwrap();
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RequestCancelled);
+}
+
+#[test]
+fn test_get_registration_cb_error() {
+ let (tx, rx) = oneshot::channel();
+ let cb = GetRegistrationCallback::new_native_binder(tx);
+ assert!(cb.onError("error").is_ok());
+
+ let result = tokio_rt().block_on(rx).unwrap();
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::GetRegistrationFailed);
+}
+
+#[test]
+fn test_get_key_cb_success() {
+ let mock_key =
+ RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+ let (tx, rx) = oneshot::channel();
+ let cb = GetKeyCallback::new_native_binder(tx);
+ assert!(cb.onSuccess(&mock_key).is_ok());
+
+ let key = tokio_rt().block_on(rx).unwrap().unwrap();
+ assert_eq!(key, mock_key);
+}
+
+#[test]
+fn test_get_key_cb_cancel() {
+ let (tx, rx) = oneshot::channel();
+ let cb = GetKeyCallback::new_native_binder(tx);
+ assert!(cb.onCancel().is_ok());
+
+ let result = tokio_rt().block_on(rx).unwrap();
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RequestCancelled);
+}
+
+#[test]
+fn test_get_key_cb_error() {
+ for get_key_error in GetKeyErrorCode::enum_values() {
+ let (tx, rx) = oneshot::channel();
+ let cb = GetKeyCallback::new_native_binder(tx);
+ assert!(cb.onError(get_key_error, "error").is_ok());
+
+ let result = tokio_rt().block_on(rx).unwrap();
+ assert_eq!(
+ result.unwrap_err().downcast::<Error>().unwrap(),
+ Error::GetKeyFailed(get_key_error),
+ );
+ }
+}
+
+#[test]
+fn test_store_upgraded_cb_success() {
+ let (tx, rx) = oneshot::channel();
+ let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
+ assert!(cb.onSuccess().is_ok());
+
+ tokio_rt().block_on(rx).unwrap().unwrap();
+}
+
+#[test]
+fn test_store_upgraded_key_cb_error() {
+ let (tx, rx) = oneshot::channel();
+ let cb = StoreUpgradedKeyCallback::new_native_binder(tx);
+ assert!(cb.onError("oh no! it failed").is_ok());
+
+ let result = tokio_rt().block_on(rx).unwrap();
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::StoreUpgradedKeyFailed);
+}
+
+#[test]
+fn test_get_mock_key_success() {
+ let mock_key =
+ RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+ let registration = get_mock_registration(&mock_key, /*latency=*/ None).unwrap();
+
+ let key = tokio_rt()
+ .block_on(get_rkpd_attestation_key_from_registration_async(&registration, 0))
+ .unwrap();
+ assert_eq!(key, mock_key);
+}
+
+#[test]
+fn test_get_mock_key_timeout() {
+ let mock_key =
+ RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+ let latency = RKPD_TIMEOUT + Duration::from_secs(1);
+ let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
+
+ let result =
+ tokio_rt().block_on(get_rkpd_attestation_key_from_registration_async(&registration, 0));
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RetryableTimeout);
+}
+
+#[test]
+fn test_store_mock_key_success() {
+ let mock_key =
+ RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+ let registration = get_mock_registration(&mock_key, /*latency=*/ None).unwrap();
+ tokio_rt()
+ .block_on(store_rkpd_attestation_key_with_registration_async(&registration, &[], &[]))
+ .unwrap();
+}
+
+#[test]
+fn test_store_mock_key_timeout() {
+ let mock_key =
+ RemotelyProvisionedKey { keyBlob: vec![1, 2, 3], encodedCertChain: vec![4, 5, 6] };
+ let latency = RKPD_TIMEOUT + Duration::from_secs(1);
+ let registration = get_mock_registration(&mock_key, Some(latency)).unwrap();
+
+ let result = tokio_rt().block_on(store_rkpd_attestation_key_with_registration_async(
+ &registration,
+ &[],
+ &[],
+ ));
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::Timeout);
+}
+
+#[test]
+fn test_get_rkpd_attestation_key() {
+ binder::ProcessState::start_thread_pool();
+ let key_id = get_next_key_id();
+ let key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
+ assert!(!key.keyBlob.is_empty());
+ assert!(!key.encodedCertChain.is_empty());
+}
+
+#[test]
+fn test_get_rkpd_attestation_key_same_caller() {
+ binder::ProcessState::start_thread_pool();
+ let key_id = get_next_key_id();
+
+ // Multiple calls should return the same key.
+ let first_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
+ let second_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
+
+ assert_eq!(first_key.keyBlob, second_key.keyBlob);
+ assert_eq!(first_key.encodedCertChain, second_key.encodedCertChain);
+}
+
+#[test]
+fn test_get_rkpd_attestation_key_different_caller() {
+ binder::ProcessState::start_thread_pool();
+ let first_key_id = get_next_key_id();
+ let second_key_id = get_next_key_id();
+
+ // Different callers should be getting different keys.
+ let first_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, first_key_id).unwrap();
+ let second_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, second_key_id).unwrap();
+
+ assert_ne!(first_key.keyBlob, second_key.keyBlob);
+ assert_ne!(first_key.encodedCertChain, second_key.encodedCertChain);
+}
+
+#[test]
+// Couple of things to note:
+// 1. This test must never run with UID of keystore. Otherwise, it can mess up keys stored by
+// keystore.
+// 2. Storing and reading the stored key is prone to race condition. So, we only do this in one
+// test case.
+fn test_store_rkpd_attestation_key() {
+ binder::ProcessState::start_thread_pool();
+ let key_id = get_next_key_id();
+ let key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
+ let new_blob: [u8; 8] = rand::random();
+
+ assert!(store_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, &key.keyBlob, &new_blob).is_ok());
+
+ let new_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
+
+ // Restore original key so that we don't leave RKPD with invalid blobs.
+ assert!(store_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, &new_blob, &key.keyBlob).is_ok());
+ assert_eq!(new_key.keyBlob, new_blob);
+}
+
+#[test]
+fn test_stress_get_rkpd_attestation_key() {
+ binder::ProcessState::start_thread_pool();
+ let key_id = get_next_key_id();
+ let mut threads = vec![];
+ const NTHREADS: u32 = 10;
+ const NCALLS: u32 = 1000;
+
+ for _ in 0..NTHREADS {
+ threads.push(std::thread::spawn(move || {
+ for _ in 0..NCALLS {
+ let key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
+ assert!(!key.keyBlob.is_empty());
+ assert!(!key.encodedCertChain.is_empty());
+ }
+ }));
+ }
+
+ for t in threads {
+ assert!(t.join().is_ok());
+ }
+}
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index fbf94649..fc36a0c1 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -62,16 +62,6 @@ impl Error {
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)
@@ -83,42 +73,24 @@ impl Error {
}
}
-/// This function should be used by confirmation service calls to translate error conditions
-/// into service specific exceptions.
-///
-/// All error conditions get logged by this function.
+/// Translate an error into a service-specific exception, logging along the way.
///
/// `Error::Rc(x)` variants get mapped onto a service specific error code of `x`.
/// `selinux::Error::perm()` is mapped on `ResponseCode::PERMISSION_DENIED`.
///
/// All non `Error` error conditions get mapped onto ResponseCode::SYSTEM_ERROR`.
-///
-/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
-/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
-/// typically returns Ok(value).
-pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
-where
- F: FnOnce(U) -> BinderResult<T>,
-{
- result.map_or_else(
- |e| {
- log::error!("{:#?}", e);
- let root_cause = e.root_cause();
- let rc = match root_cause.downcast_ref::<Error>() {
- Some(Error::Rc(rcode)) => rcode.0,
- Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
- None => match root_cause.downcast_ref::<selinux::Error>() {
- Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
- _ => ResponseCode::SYSTEM_ERROR.0,
- },
- };
- Err(BinderStatus::new_service_specific_error(
- rc,
- anyhow_error_to_cstring(&e).as_deref(),
- ))
+pub fn into_logged_binder(e: anyhow::Error) -> BinderStatus {
+ log::error!("{:#?}", e);
+ let root_cause = e.root_cause();
+ let rc = match root_cause.downcast_ref::<Error>() {
+ Some(Error::Rc(rcode)) => rcode.0,
+ Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
+ None => match root_cause.downcast_ref::<selinux::Error>() {
+ Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
+ _ => ResponseCode::SYSTEM_ERROR.0,
},
- handle_ok,
- )
+ };
+ BinderStatus::new_service_specific_error(rc, anyhow_error_to_cstring(&e).as_deref())
}
/// Rate info records how many failed attempts a client has made to display a protected
@@ -364,20 +336,18 @@ impl IProtectedConfirmation for ApcManager {
) -> BinderResult<()> {
// presentPrompt can take more time than other operations.
let _wp = wd::watch_millis("IProtectedConfirmation::presentPrompt", 3000);
- map_or_log_err(
- self.present_prompt(listener, prompt_text, extra_data, locale, ui_option_flags),
- Ok,
- )
+ self.present_prompt(listener, prompt_text, extra_data, locale, ui_option_flags)
+ .map_err(into_logged_binder)
}
fn cancelPrompt(
&self,
listener: &binder::Strong<dyn IConfirmationCallback>,
) -> BinderResult<()> {
- let _wp = wd::watch_millis("IProtectedConfirmation::cancelPrompt", 500);
- map_or_log_err(self.cancel_prompt(listener), Ok)
+ let _wp = wd::watch("IProtectedConfirmation::cancelPrompt");
+ self.cancel_prompt(listener).map_err(into_logged_binder)
}
fn isSupported(&self) -> BinderResult<bool> {
- let _wp = wd::watch_millis("IProtectedConfirmation::isSupported", 500);
- map_or_log_err(Self::is_supported(), Ok)
+ let _wp = wd::watch("IProtectedConfirmation::isSupported");
+ Self::is_supported().map_err(into_logged_binder)
}
}
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
index 6548445f..16401a4e 100644
--- a/keystore2/src/async_task.rs
+++ b/keystore2/src/async_task.rs
@@ -27,6 +27,9 @@ use std::{
thread,
};
+#[cfg(test)]
+mod tests;
+
#[derive(Debug, PartialEq, Eq)]
enum State {
Exiting,
@@ -256,279 +259,3 @@ impl AsyncTask {
state.state = State::Running;
}
}
-
-#[cfg(test)]
-mod tests {
- use super::{AsyncTask, Shelf};
- use std::sync::{
- mpsc::{channel, sync_channel, RecvTimeoutError},
- Arc,
- };
- use std::time::Duration;
-
- #[test]
- fn test_shelf() {
- let mut shelf = Shelf::default();
-
- let s = "A string".to_string();
- assert_eq!(shelf.put(s), None);
-
- let s2 = "Another string".to_string();
- assert_eq!(shelf.put(s2), Some("A string".to_string()));
-
- // Put something of a different type on the shelf.
- #[derive(Debug, PartialEq, Eq)]
- struct Elf {
- pub name: String,
- }
- let e1 = Elf { name: "Glorfindel".to_string() };
- assert_eq!(shelf.put(e1), None);
-
- // The String value is still on the shelf.
- let s3 = shelf.get_downcast_ref::<String>().unwrap();
- assert_eq!(s3, "Another string");
-
- // As is the Elf.
- {
- let e2 = shelf.get_downcast_mut::<Elf>().unwrap();
- assert_eq!(e2.name, "Glorfindel");
- e2.name = "Celeborn".to_string();
- }
-
- // Take the Elf off the shelf.
- let e3 = shelf.remove_downcast_ref::<Elf>().unwrap();
- assert_eq!(e3.name, "Celeborn");
-
- assert_eq!(shelf.remove_downcast_ref::<Elf>(), None);
-
- // No u64 value has been put on the shelf, so getting one gives the default value.
- {
- let i = shelf.get_mut::<u64>();
- assert_eq!(*i, 0);
- *i = 42;
- }
- let i2 = shelf.get_downcast_ref::<u64>().unwrap();
- assert_eq!(*i2, 42);
-
- // No i32 value has ever been seen near the shelf.
- assert_eq!(shelf.get_downcast_ref::<i32>(), None);
- assert_eq!(shelf.get_downcast_mut::<i32>(), None);
- assert_eq!(shelf.remove_downcast_ref::<i32>(), None);
- }
-
- #[test]
- fn test_async_task() {
- let at = AsyncTask::default();
-
- // First queue up a job that blocks until we release it, to avoid
- // unpredictable synchronization.
- let (start_sender, start_receiver) = channel();
- at.queue_hi(move |shelf| {
- start_receiver.recv().unwrap();
- // Put a trace vector on the shelf
- shelf.put(Vec::<String>::new());
- });
-
- // Queue up some high-priority and low-priority jobs.
- for i in 0..3 {
- let j = i;
- at.queue_lo(move |shelf| {
- let trace = shelf.get_mut::<Vec<String>>();
- trace.push(format!("L{}", j));
- });
- let j = i;
- at.queue_hi(move |shelf| {
- let trace = shelf.get_mut::<Vec<String>>();
- trace.push(format!("H{}", j));
- });
- }
-
- // Finally queue up a low priority job that emits the trace.
- let (trace_sender, trace_receiver) = channel();
- at.queue_lo(move |shelf| {
- let trace = shelf.get_downcast_ref::<Vec<String>>().unwrap();
- trace_sender.send(trace.clone()).unwrap();
- });
-
- // Ready, set, go.
- start_sender.send(()).unwrap();
- let trace = trace_receiver.recv().unwrap();
-
- assert_eq!(trace, vec!["H0", "H1", "H2", "L0", "L1", "L2"]);
- }
-
- #[test]
- fn test_async_task_chain() {
- let at = Arc::new(AsyncTask::default());
- let (sender, receiver) = channel();
- // Queue up a job that will queue up another job. This confirms
- // that the job is not invoked with any internal AsyncTask locks held.
- let at_clone = at.clone();
- at.queue_hi(move |_shelf| {
- at_clone.queue_lo(move |_shelf| {
- sender.send(()).unwrap();
- });
- });
- receiver.recv().unwrap();
- }
-
- #[test]
- #[should_panic]
- fn test_async_task_panic() {
- let at = AsyncTask::default();
- at.queue_hi(|_shelf| {
- panic!("Panic from queued job");
- });
- // Queue another job afterwards to ensure that the async thread gets joined.
- let (done_sender, done_receiver) = channel();
- at.queue_hi(move |_shelf| {
- done_sender.send(()).unwrap();
- });
- done_receiver.recv().unwrap();
- }
-
- #[test]
- fn test_async_task_idle() {
- let at = AsyncTask::new(Duration::from_secs(3));
- // Need a SyncSender as it is Send+Sync.
- let (idle_done_sender, idle_done_receiver) = sync_channel::<()>(3);
- at.add_idle(move |_shelf| {
- idle_done_sender.send(()).unwrap();
- });
-
- // Queue up some high-priority and low-priority jobs that take time.
- for _i in 0..3 {
- at.queue_lo(|_shelf| {
- std::thread::sleep(Duration::from_millis(500));
- });
- at.queue_hi(|_shelf| {
- std::thread::sleep(Duration::from_millis(500));
- });
- }
- // Final low-priority job.
- let (done_sender, done_receiver) = channel();
- at.queue_lo(move |_shelf| {
- done_sender.send(()).unwrap();
- });
-
- // Nothing happens until the last job completes.
- assert_eq!(
- idle_done_receiver.recv_timeout(Duration::from_secs(1)),
- Err(RecvTimeoutError::Timeout)
- );
- done_receiver.recv().unwrap();
- // Now that the last low-priority job has completed, the idle task should
- // fire pretty much immediately.
- idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
-
- // Idle callback not executed again even if we wait for a while.
- assert_eq!(
- idle_done_receiver.recv_timeout(Duration::from_secs(3)),
- Err(RecvTimeoutError::Timeout)
- );
-
- // However, if more work is done then there's another chance to go idle.
- let (done_sender, done_receiver) = channel();
- at.queue_hi(move |_shelf| {
- std::thread::sleep(Duration::from_millis(500));
- done_sender.send(()).unwrap();
- });
- // Idle callback not immediately executed, because the high priority
- // job is taking a while.
- assert_eq!(
- idle_done_receiver.recv_timeout(Duration::from_millis(1)),
- Err(RecvTimeoutError::Timeout)
- );
- done_receiver.recv().unwrap();
- idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
- }
-
- #[test]
- fn test_async_task_multiple_idle() {
- let at = AsyncTask::new(Duration::from_secs(3));
- let (idle_sender, idle_receiver) = sync_channel::<i32>(5);
- // Queue a high priority job to start things off
- at.queue_hi(|_shelf| {
- std::thread::sleep(Duration::from_millis(500));
- });
-
- // Multiple idle callbacks.
- for i in 0..3 {
- let idle_sender = idle_sender.clone();
- at.add_idle(move |_shelf| {
- idle_sender.send(i).unwrap();
- });
- }
-
- // Nothing happens immediately.
- assert_eq!(
- idle_receiver.recv_timeout(Duration::from_millis(1)),
- Err(RecvTimeoutError::Timeout)
- );
- // Wait for a moment and the idle jobs should have run.
- std::thread::sleep(Duration::from_secs(1));
-
- let mut results = Vec::new();
- while let Ok(i) = idle_receiver.recv_timeout(Duration::from_millis(1)) {
- results.push(i);
- }
- assert_eq!(results, [0, 1, 2]);
- }
-
- #[test]
- fn test_async_task_idle_queues_job() {
- let at = Arc::new(AsyncTask::new(Duration::from_secs(1)));
- let at_clone = at.clone();
- let (idle_sender, idle_receiver) = sync_channel::<i32>(100);
- // Add an idle callback that queues a low-priority job.
- at.add_idle(move |shelf| {
- at_clone.queue_lo(|_shelf| {
- // Slow things down so the channel doesn't fill up.
- std::thread::sleep(Duration::from_millis(50));
- });
- let i = shelf.get_mut::<i32>();
- idle_sender.send(*i).unwrap();
- *i += 1;
- });
-
- // Nothing happens immediately.
- assert_eq!(
- idle_receiver.recv_timeout(Duration::from_millis(1500)),
- Err(RecvTimeoutError::Timeout)
- );
-
- // Once we queue a normal job, things start.
- at.queue_hi(|_shelf| {});
- assert_eq!(0, idle_receiver.recv_timeout(Duration::from_millis(200)).unwrap());
-
- // The idle callback queues a job, and completion of that job
- // means the task is going idle again...so the idle callback will
- // be called repeatedly.
- assert_eq!(1, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
- assert_eq!(2, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
- assert_eq!(3, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
- }
-
- #[test]
- #[should_panic]
- fn test_async_task_idle_panic() {
- let at = AsyncTask::new(Duration::from_secs(1));
- let (idle_sender, idle_receiver) = sync_channel::<()>(3);
- // Add an idle callback that panics.
- at.add_idle(move |_shelf| {
- idle_sender.send(()).unwrap();
- panic!("Panic from idle callback");
- });
- // Queue a job to trigger idleness and ensuing panic.
- at.queue_hi(|_shelf| {});
- idle_receiver.recv().unwrap();
-
- // Queue another job afterwards to ensure that the async thread gets joined
- // and the panic detected.
- let (done_sender, done_receiver) = channel();
- at.queue_hi(move |_shelf| {
- done_sender.send(()).unwrap();
- });
- done_receiver.recv().unwrap();
- }
-}
diff --git a/keystore2/src/async_task/tests.rs b/keystore2/src/async_task/tests.rs
new file mode 100644
index 00000000..e67303e6
--- /dev/null
+++ b/keystore2/src/async_task/tests.rs
@@ -0,0 +1,287 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Async task tests.
+use super::{AsyncTask, Shelf};
+use std::sync::{
+ mpsc::{channel, sync_channel, RecvTimeoutError},
+ Arc,
+};
+use std::time::Duration;
+
+#[test]
+fn test_shelf() {
+ let mut shelf = Shelf::default();
+
+ let s = "A string".to_string();
+ assert_eq!(shelf.put(s), None);
+
+ let s2 = "Another string".to_string();
+ assert_eq!(shelf.put(s2), Some("A string".to_string()));
+
+ // Put something of a different type on the shelf.
+ #[derive(Debug, PartialEq, Eq)]
+ struct Elf {
+ pub name: String,
+ }
+ let e1 = Elf { name: "Glorfindel".to_string() };
+ assert_eq!(shelf.put(e1), None);
+
+ // The String value is still on the shelf.
+ let s3 = shelf.get_downcast_ref::<String>().unwrap();
+ assert_eq!(s3, "Another string");
+
+ // As is the Elf.
+ {
+ let e2 = shelf.get_downcast_mut::<Elf>().unwrap();
+ assert_eq!(e2.name, "Glorfindel");
+ e2.name = "Celeborn".to_string();
+ }
+
+ // Take the Elf off the shelf.
+ let e3 = shelf.remove_downcast_ref::<Elf>().unwrap();
+ assert_eq!(e3.name, "Celeborn");
+
+ assert_eq!(shelf.remove_downcast_ref::<Elf>(), None);
+
+ // No u64 value has been put on the shelf, so getting one gives the default value.
+ {
+ let i = shelf.get_mut::<u64>();
+ assert_eq!(*i, 0);
+ *i = 42;
+ }
+ let i2 = shelf.get_downcast_ref::<u64>().unwrap();
+ assert_eq!(*i2, 42);
+
+ // No i32 value has ever been seen near the shelf.
+ assert_eq!(shelf.get_downcast_ref::<i32>(), None);
+ assert_eq!(shelf.get_downcast_mut::<i32>(), None);
+ assert_eq!(shelf.remove_downcast_ref::<i32>(), None);
+}
+
+#[test]
+fn test_async_task() {
+ let at = AsyncTask::default();
+
+ // First queue up a job that blocks until we release it, to avoid
+ // unpredictable synchronization.
+ let (start_sender, start_receiver) = channel();
+ at.queue_hi(move |shelf| {
+ start_receiver.recv().unwrap();
+ // Put a trace vector on the shelf
+ shelf.put(Vec::<String>::new());
+ });
+
+ // Queue up some high-priority and low-priority jobs.
+ for i in 0..3 {
+ let j = i;
+ at.queue_lo(move |shelf| {
+ let trace = shelf.get_mut::<Vec<String>>();
+ trace.push(format!("L{}", j));
+ });
+ let j = i;
+ at.queue_hi(move |shelf| {
+ let trace = shelf.get_mut::<Vec<String>>();
+ trace.push(format!("H{}", j));
+ });
+ }
+
+ // Finally queue up a low priority job that emits the trace.
+ let (trace_sender, trace_receiver) = channel();
+ at.queue_lo(move |shelf| {
+ let trace = shelf.get_downcast_ref::<Vec<String>>().unwrap();
+ trace_sender.send(trace.clone()).unwrap();
+ });
+
+ // Ready, set, go.
+ start_sender.send(()).unwrap();
+ let trace = trace_receiver.recv().unwrap();
+
+ assert_eq!(trace, vec!["H0", "H1", "H2", "L0", "L1", "L2"]);
+}
+
+#[test]
+fn test_async_task_chain() {
+ let at = Arc::new(AsyncTask::default());
+ let (sender, receiver) = channel();
+ // Queue up a job that will queue up another job. This confirms
+ // that the job is not invoked with any internal AsyncTask locks held.
+ let at_clone = at.clone();
+ at.queue_hi(move |_shelf| {
+ at_clone.queue_lo(move |_shelf| {
+ sender.send(()).unwrap();
+ });
+ });
+ receiver.recv().unwrap();
+}
+
+#[test]
+#[should_panic]
+fn test_async_task_panic() {
+ let at = AsyncTask::default();
+ at.queue_hi(|_shelf| {
+ panic!("Panic from queued job");
+ });
+ // Queue another job afterwards to ensure that the async thread gets joined.
+ let (done_sender, done_receiver) = channel();
+ at.queue_hi(move |_shelf| {
+ done_sender.send(()).unwrap();
+ });
+ done_receiver.recv().unwrap();
+}
+
+#[test]
+fn test_async_task_idle() {
+ let at = AsyncTask::new(Duration::from_secs(3));
+ // Need a SyncSender as it is Send+Sync.
+ let (idle_done_sender, idle_done_receiver) = sync_channel::<()>(3);
+ at.add_idle(move |_shelf| {
+ idle_done_sender.send(()).unwrap();
+ });
+
+ // Queue up some high-priority and low-priority jobs that take time.
+ for _i in 0..3 {
+ at.queue_lo(|_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ });
+ at.queue_hi(|_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ });
+ }
+ // Final low-priority job.
+ let (done_sender, done_receiver) = channel();
+ at.queue_lo(move |_shelf| {
+ done_sender.send(()).unwrap();
+ });
+
+ // Nothing happens until the last job completes.
+ assert_eq!(
+ idle_done_receiver.recv_timeout(Duration::from_secs(1)),
+ Err(RecvTimeoutError::Timeout)
+ );
+ done_receiver.recv().unwrap();
+ // Now that the last low-priority job has completed, the idle task should
+ // fire pretty much immediately.
+ idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
+
+ // Idle callback not executed again even if we wait for a while.
+ assert_eq!(
+ idle_done_receiver.recv_timeout(Duration::from_secs(3)),
+ Err(RecvTimeoutError::Timeout)
+ );
+
+ // However, if more work is done then there's another chance to go idle.
+ let (done_sender, done_receiver) = channel();
+ at.queue_hi(move |_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ done_sender.send(()).unwrap();
+ });
+ // Idle callback not immediately executed, because the high priority
+ // job is taking a while.
+ assert_eq!(
+ idle_done_receiver.recv_timeout(Duration::from_millis(1)),
+ Err(RecvTimeoutError::Timeout)
+ );
+ done_receiver.recv().unwrap();
+ idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
+}
+
+#[test]
+fn test_async_task_multiple_idle() {
+ let at = AsyncTask::new(Duration::from_secs(3));
+ let (idle_sender, idle_receiver) = sync_channel::<i32>(5);
+ // Queue a high priority job to start things off
+ at.queue_hi(|_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ });
+
+ // Multiple idle callbacks.
+ for i in 0..3 {
+ let idle_sender = idle_sender.clone();
+ at.add_idle(move |_shelf| {
+ idle_sender.send(i).unwrap();
+ });
+ }
+
+ // Nothing happens immediately.
+ assert_eq!(
+ idle_receiver.recv_timeout(Duration::from_millis(1)),
+ Err(RecvTimeoutError::Timeout)
+ );
+ // Wait for a moment and the idle jobs should have run.
+ std::thread::sleep(Duration::from_secs(1));
+
+ let mut results = Vec::new();
+ while let Ok(i) = idle_receiver.recv_timeout(Duration::from_millis(1)) {
+ results.push(i);
+ }
+ assert_eq!(results, [0, 1, 2]);
+}
+
+#[test]
+fn test_async_task_idle_queues_job() {
+ let at = Arc::new(AsyncTask::new(Duration::from_secs(1)));
+ let at_clone = at.clone();
+ let (idle_sender, idle_receiver) = sync_channel::<i32>(100);
+ // Add an idle callback that queues a low-priority job.
+ at.add_idle(move |shelf| {
+ at_clone.queue_lo(|_shelf| {
+ // Slow things down so the channel doesn't fill up.
+ std::thread::sleep(Duration::from_millis(50));
+ });
+ let i = shelf.get_mut::<i32>();
+ idle_sender.send(*i).unwrap();
+ *i += 1;
+ });
+
+ // Nothing happens immediately.
+ assert_eq!(
+ idle_receiver.recv_timeout(Duration::from_millis(1500)),
+ Err(RecvTimeoutError::Timeout)
+ );
+
+ // Once we queue a normal job, things start.
+ at.queue_hi(|_shelf| {});
+ assert_eq!(0, idle_receiver.recv_timeout(Duration::from_millis(200)).unwrap());
+
+ // The idle callback queues a job, and completion of that job
+ // means the task is going idle again...so the idle callback will
+ // be called repeatedly.
+ assert_eq!(1, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+ assert_eq!(2, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+ assert_eq!(3, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+}
+
+#[test]
+#[should_panic]
+fn test_async_task_idle_panic() {
+ let at = AsyncTask::new(Duration::from_secs(1));
+ let (idle_sender, idle_receiver) = sync_channel::<()>(3);
+ // Add an idle callback that panics.
+ at.add_idle(move |_shelf| {
+ idle_sender.send(()).unwrap();
+ panic!("Panic from idle callback");
+ });
+ // Queue a job to trigger idleness and ensuing panic.
+ at.queue_hi(|_shelf| {});
+ idle_receiver.recv().unwrap();
+
+ // Queue another job afterwards to ensure that the async thread gets joined
+ // and the panic detected.
+ let (done_sender, done_receiver) = channel();
+ at.queue_hi(move |_shelf| {
+ done_sender.send(()).unwrap();
+ });
+ done_receiver.recv().unwrap();
+}
diff --git a/keystore2/src/audit_log.rs b/keystore2/src/audit_log.rs
index 0e5dfeb6..4952b3bf 100644
--- a/keystore2/src/audit_log.rs
+++ b/keystore2/src/audit_log.rs
@@ -20,7 +20,7 @@ use android_system_keystore2::aidl::android::system::keystore2::{
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;
@@ -34,8 +34,8 @@ fn key_owner(domain: Domain, nspace: i64, uid: i32) -> i32 {
match domain {
Domain::APP => uid,
Domain::SELINUX => (nspace | FLAG_NAMESPACE) as i32,
- _ => {
- log::info!("Not logging audit event for key with unexpected domain");
+ d => {
+ log::info!("Not logging audit event for key with domain {d:?}");
0
}
}
@@ -58,30 +58,19 @@ pub fn log_key_deleted(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
/// 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 f9567875..c76f86b0 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -51,10 +51,7 @@ pub enum Error {
Binder(ExceptionCode, i32),
}
-/// This function should be used by authorization service calls to translate error conditions
-/// into service specific exceptions.
-///
-/// All error conditions get logged by this function.
+/// Translate an error into a service specific exception, logging along the way.
///
/// `Error::Rc(x)` variants get mapped onto a service specific error code of `x`.
/// Certain response codes may be returned from keystore/ResponseCode.aidl by the keystore2 modules,
@@ -64,53 +61,36 @@ pub enum Error {
/// `selinux::Error::perm()` is mapped on `ResponseCode::PERMISSION_DENIED`.
///
/// All non `Error` error conditions get mapped onto ResponseCode::SYSTEM_ERROR`.
-///
-/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
-/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
-/// typically returns Ok(value).
-pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
-where
- F: FnOnce(U) -> BinderResult<T>,
-{
- result.map_or_else(
- |e| {
- log::error!("{:#?}", e);
- let root_cause = e.root_cause();
- if let Some(KeystoreError::Rc(ks_rcode)) = root_cause.downcast_ref::<KeystoreError>() {
- let rc = match *ks_rcode {
- // Although currently keystore2/ResponseCode.aidl and
- // authorization/ResponseCode.aidl share the same integer values for the
- // common response codes, this may deviate in the future, hence the
- // conversion here.
- KsResponseCode::SYSTEM_ERROR => ResponseCode::SYSTEM_ERROR.0,
- KsResponseCode::KEY_NOT_FOUND => ResponseCode::KEY_NOT_FOUND.0,
- KsResponseCode::VALUE_CORRUPTED => ResponseCode::VALUE_CORRUPTED.0,
- KsResponseCode::INVALID_ARGUMENT => ResponseCode::INVALID_ARGUMENT.0,
- // If the code paths of IKeystoreAuthorization aidl's methods happen to return
- // other error codes from KsResponseCode in the future, they should be converted
- // as well.
- _ => ResponseCode::SYSTEM_ERROR.0,
- };
- return Err(BinderStatus::new_service_specific_error(
- rc,
- anyhow_error_to_cstring(&e).as_deref(),
- ));
- }
- let rc = match root_cause.downcast_ref::<Error>() {
- Some(Error::Rc(rcode)) => rcode.0,
- Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
- None => match root_cause.downcast_ref::<selinux::Error>() {
- Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
- _ => ResponseCode::SYSTEM_ERROR.0,
- },
- };
- Err(BinderStatus::new_service_specific_error(
- rc,
- anyhow_error_to_cstring(&e).as_deref(),
- ))
- },
- handle_ok,
- )
+pub fn into_logged_binder(e: anyhow::Error) -> BinderStatus {
+ log::error!("{:#?}", e);
+ let root_cause = e.root_cause();
+ if let Some(KeystoreError::Rc(ks_rcode)) = root_cause.downcast_ref::<KeystoreError>() {
+ let rc = match *ks_rcode {
+ // Although currently keystore2/ResponseCode.aidl and
+ // authorization/ResponseCode.aidl share the same integer values for the
+ // common response codes, this may deviate in the future, hence the
+ // conversion here.
+ KsResponseCode::SYSTEM_ERROR => ResponseCode::SYSTEM_ERROR.0,
+ KsResponseCode::KEY_NOT_FOUND => ResponseCode::KEY_NOT_FOUND.0,
+ KsResponseCode::VALUE_CORRUPTED => ResponseCode::VALUE_CORRUPTED.0,
+ KsResponseCode::INVALID_ARGUMENT => ResponseCode::INVALID_ARGUMENT.0,
+ // If the code paths of IKeystoreAuthorization aidl's methods happen to return
+ // other error codes from KsResponseCode in the future, they should be converted
+ // as well.
+ _ => ResponseCode::SYSTEM_ERROR.0,
+ };
+ BinderStatus::new_service_specific_error(rc, anyhow_error_to_cstring(&e).as_deref())
+ } else {
+ let rc = match root_cause.downcast_ref::<Error>() {
+ Some(Error::Rc(rcode)) => rcode.0,
+ Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
+ None => match root_cause.downcast_ref::<selinux::Error>() {
+ Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
+ _ => ResponseCode::SYSTEM_ERROR.0,
+ },
+ };
+ BinderStatus::new_service_specific_error(rc, anyhow_error_to_cstring(&e).as_deref())
+ }
}
/// This struct is defined to implement the aforementioned AIDL interface.
@@ -128,7 +108,8 @@ impl AuthorizationManager {
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 +130,8 @@ impl AuthorizationManager {
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 +142,7 @@ impl AuthorizationManager {
.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}"))
}
}
@@ -168,7 +150,7 @@ impl AuthorizationManager {
&self,
user_id: i32,
unlocking_sids: &[i64],
- mut weak_unlock_enabled: bool,
+ weak_unlock_enabled: bool,
) -> Result<()> {
log::info!(
"on_device_locked(user_id={}, unlocking_sids={:?}, weak_unlock_enabled={})",
@@ -176,10 +158,8 @@ impl AuthorizationManager {
unlocking_sids,
weak_unlock_enabled
);
- 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| {
@@ -195,20 +175,16 @@ impl AuthorizationManager {
fn on_weak_unlock_methods_expired(&self, user_id: i32) -> Result<()> {
log::info!("on_weak_unlock_methods_expired(user_id={})", user_id);
- 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(())
}
fn on_non_lskf_unlock_methods_expired(&self, user_id: i32) -> Result<()> {
log::info!("on_non_lskf_unlock_methods_expired(user_id={})", user_id);
- 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 +197,8 @@ impl AuthorizationManager {
) -> 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 +217,8 @@ impl AuthorizationManager {
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() {
@@ -264,13 +242,13 @@ impl Interface for AuthorizationManager {}
impl IKeystoreAuthorization for AuthorizationManager {
fn addAuthToken(&self, auth_token: &HardwareAuthToken) -> BinderResult<()> {
- let _wp = wd::watch_millis("IKeystoreAuthorization::addAuthToken", 500);
- map_or_log_err(self.add_auth_token(auth_token), Ok)
+ let _wp = wd::watch("IKeystoreAuthorization::addAuthToken");
+ self.add_auth_token(auth_token).map_err(into_logged_binder)
}
fn onDeviceUnlocked(&self, user_id: i32, password: Option<&[u8]>) -> BinderResult<()> {
- let _wp = wd::watch_millis("IKeystoreAuthorization::onDeviceUnlocked", 500);
- map_or_log_err(self.on_device_unlocked(user_id, password.map(|pw| pw.into())), Ok)
+ let _wp = wd::watch("IKeystoreAuthorization::onDeviceUnlocked");
+ self.on_device_unlocked(user_id, password.map(|pw| pw.into())).map_err(into_logged_binder)
}
fn onDeviceLocked(
@@ -279,18 +257,19 @@ impl IKeystoreAuthorization for AuthorizationManager {
unlocking_sids: &[i64],
weak_unlock_enabled: bool,
) -> BinderResult<()> {
- let _wp = wd::watch_millis("IKeystoreAuthorization::onDeviceLocked", 500);
- map_or_log_err(self.on_device_locked(user_id, unlocking_sids, weak_unlock_enabled), Ok)
+ let _wp = wd::watch("IKeystoreAuthorization::onDeviceLocked");
+ self.on_device_locked(user_id, unlocking_sids, weak_unlock_enabled)
+ .map_err(into_logged_binder)
}
fn onWeakUnlockMethodsExpired(&self, user_id: i32) -> BinderResult<()> {
- let _wp = wd::watch_millis("IKeystoreAuthorization::onWeakUnlockMethodsExpired", 500);
- map_or_log_err(self.on_weak_unlock_methods_expired(user_id), Ok)
+ let _wp = wd::watch("IKeystoreAuthorization::onWeakUnlockMethodsExpired");
+ self.on_weak_unlock_methods_expired(user_id).map_err(into_logged_binder)
}
fn onNonLskfUnlockMethodsExpired(&self, user_id: i32) -> BinderResult<()> {
- let _wp = wd::watch_millis("IKeystoreAuthorization::onNonLskfUnlockMethodsExpired", 500);
- map_or_log_err(self.on_non_lskf_unlock_methods_expired(user_id), Ok)
+ let _wp = wd::watch("IKeystoreAuthorization::onNonLskfUnlockMethodsExpired");
+ self.on_non_lskf_unlock_methods_expired(user_id).map_err(into_logged_binder)
}
fn getAuthTokensForCredStore(
@@ -299,15 +278,9 @@ impl IKeystoreAuthorization for AuthorizationManager {
secure_user_id: i64,
auth_token_max_age_millis: i64,
) -> binder::Result<AuthorizationTokens> {
- let _wp = wd::watch_millis("IKeystoreAuthorization::getAuthTokensForCredStore", 500);
- map_or_log_err(
- self.get_auth_tokens_for_credstore(
- challenge,
- secure_user_id,
- auth_token_max_age_millis,
- ),
- Ok,
- )
+ let _wp = wd::watch("IKeystoreAuthorization::getAuthTokensForCredStore");
+ self.get_auth_tokens_for_credstore(challenge, secure_user_id, auth_token_max_age_millis)
+ .map_err(into_logged_binder)
}
fn getLastAuthTime(
@@ -316,7 +289,7 @@ impl IKeystoreAuthorization for AuthorizationManager {
auth_types: &[HardwareAuthenticatorType],
) -> binder::Result<i64> {
if aconfig_android_hardware_biometrics_rust::last_authentication_time() {
- map_or_log_err(self.get_last_auth_time(secure_user_id, auth_types), Ok)
+ self.get_last_auth_time(secure_user_id, auth_types).map_err(into_logged_binder)
} else {
Err(BinderStatus::new_service_specific_error(
ResponseCode::PERMISSION_DENIED.0,
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
index c917a898..00cbb1c8 100644
--- a/keystore2/src/crypto/zvec.rs
+++ b/keystore2/src/crypto/zvec.rs
@@ -20,6 +20,7 @@ use std::convert::TryFrom;
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 @@ impl ZVec {
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 @@ impl Drop for ZVec {
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 @@ impl TryFrom<Vec<u8>> for ZVec {
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 b526daac..9ce6506b 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -45,8 +45,11 @@ mod perboot;
pub(crate) mod utils;
mod versioning;
+#[cfg(test)]
+pub mod tests;
+
use crate::gc::Gc;
-use crate::impl_metadata; // This is in db_utils.rs
+use crate::impl_metadata; // This is in database/utils.rs
use crate::key_parameter::{KeyParameter, KeyParameterValue, Tag};
use crate::ks_err;
use crate::permission::KeyPermSet;
@@ -82,7 +85,7 @@ use rusqlite::{
types::FromSqlResult,
types::ToSqlOutput,
types::{FromSqlError, Value, ValueRef},
- Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior,
+ Connection, OptionalExtension, ToSql, Transaction,
};
use std::{
@@ -92,9 +95,40 @@ use std::{
time::{Duration, SystemTime},
};
+use TransactionBehavior::Immediate;
+
#[cfg(test)]
use tests::random;
+/// Wrapper for `rusqlite::TransactionBehavior` which includes information about the transaction
+/// being performed.
+#[derive(Clone, Copy)]
+enum TransactionBehavior {
+ Deferred,
+ Immediate(&'static str),
+}
+
+impl From<TransactionBehavior> for rusqlite::TransactionBehavior {
+ fn from(val: TransactionBehavior) -> Self {
+ match val {
+ TransactionBehavior::Deferred => rusqlite::TransactionBehavior::Deferred,
+ TransactionBehavior::Immediate(_) => rusqlite::TransactionBehavior::Immediate,
+ }
+ }
+}
+
+impl TransactionBehavior {
+ fn name(&self) -> Option<&'static str> {
+ match self {
+ TransactionBehavior::Deferred => None,
+ TransactionBehavior::Immediate(v) => Some(v),
+ }
+ }
+}
+
+/// If the database returns a busy error code, retry after this interval.
+const DB_BUSY_RETRY_INTERVAL: Duration = Duration::from_micros(500);
+
impl_metadata!(
/// A set of metadata for key entries.
#[derive(Debug, Default, Eq, PartialEq)]
@@ -382,11 +416,6 @@ impl DateTime {
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 +552,7 @@ impl KeyIdLockDb {
/// 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 +565,7 @@ impl KeyIdLockDb {
/// 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 +694,6 @@ impl KeyEntry {
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 +702,6 @@ impl KeyEntry {
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 +715,6 @@ impl KeyEntry {
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,10 +859,17 @@ impl AuthTokenEntry {
}
}
-/// 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);
+/// Information about a superseded blob (a blob that is no longer the
+/// most recent blob of that type for a given key, due to upgrade or
+/// replacement).
+pub struct SupersededBlob {
+ /// ID
+ pub blob_id: i64,
+ /// Contents.
+ pub blob: Vec<u8>,
+ /// Metadata.
+ pub metadata: BlobMetaData,
+}
impl KeystoreDB {
const UNASSIGNED_KEY_ID: i64 = -1i64;
@@ -861,13 +885,13 @@ impl KeystoreDB {
/// KeystoreDB cannot be used by multiple threads.
/// Each thread should open their own connection using `thread_local!`.
pub fn new(db_root: &Path, gc: Option<Arc<Gc>>) -> Result<Self> {
- let _wp = wd::watch_millis("KeystoreDB::new", 500);
+ let _wp = wd::watch("KeystoreDB::new");
let persistent_path = Self::make_persistent_path(db_root)?;
let conn = Self::make_connection(&persistent_path)?;
let mut db = Self { conn, gc, perboot: perboot::PERBOOT_DB.clone() };
- db.with_transaction(TransactionBehavior::Immediate, |tx| {
+ db.with_transaction(Immediate("TX_new"), |tx| {
versioning::upgrade_database(tx, Self::CURRENT_DB_VERSION, Self::UPGRADERS)
.context(ks_err!("KeystoreDB::new: trying to upgrade database."))?;
Self::init_tables(tx).context("Trying to initialize tables.").no_gc()
@@ -1035,7 +1059,7 @@ impl KeystoreDB {
.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);
@@ -1096,7 +1120,7 @@ impl KeystoreDB {
/// types that map to a table, information about the table's storage is
/// returned. Requests for storage types that are not DB tables return None.
pub fn get_storage_stat(&mut self, storage_type: MetricsStorage) -> Result<StorageStats> {
- let _wp = wd::watch_millis("KeystoreDB::get_storage_stat", 500);
+ let _wp = wd::watch("KeystoreDB::get_storage_stat");
match storage_type {
MetricsStorage::DATABASE => self.get_total_size(),
@@ -1158,9 +1182,9 @@ impl KeystoreDB {
&mut self,
blob_ids_to_delete: &[i64],
max_blobs: usize,
- ) -> Result<Vec<(i64, Vec<u8>, BlobMetaData)>> {
- let _wp = wd::watch_millis("KeystoreDB::handle_next_superseded_blob", 500);
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ ) -> Result<Vec<SupersededBlob>> {
+ let _wp = wd::watch("KeystoreDB::handle_next_superseded_blob");
+ self.with_transaction(Immediate("TX_handle_next_superseded_blob"), |tx| {
// Delete the given blobs.
for blob_id in blob_ids_to_delete {
tx.execute(
@@ -1174,8 +1198,9 @@ impl KeystoreDB {
Self::cleanup_unreferenced(tx).context("Trying to cleanup unreferenced.")?;
- // Find up to max_blobx more superseded key blobs, load their metadata and return it.
+ // Find up to `max_blobs` more superseded key blobs, load their metadata and return it.
let result: Vec<(i64, Vec<u8>)> = {
+ let _wp = wd::watch("KeystoreDB::handle_next_superseded_blob find_next");
let mut stmt = tx
.prepare(
"SELECT id, blob FROM persistent.blobentry
@@ -1206,12 +1231,17 @@ impl KeystoreDB {
.context("Trying to extract superseded blobs.")?
};
+ let _wp = wd::watch("KeystoreDB::handle_next_superseded_blob load_metadata");
let result = result
.into_iter()
.map(|(blob_id, blob)| {
- Ok((blob_id, blob, BlobMetaData::load_from_db(blob_id, tx)?))
+ Ok(SupersededBlob {
+ blob_id,
+ blob,
+ metadata: BlobMetaData::load_from_db(blob_id, tx)?,
+ })
})
- .collect::<Result<Vec<(i64, Vec<u8>, BlobMetaData)>>>()
+ .collect::<Result<Vec<_>>>()
.context("Trying to load blob metadata.")?;
if !result.is_empty() {
return Ok(result).no_gc();
@@ -1219,6 +1249,7 @@ impl KeystoreDB {
// We did not find any superseded key blob, so let's remove other superseded blob in
// one transaction.
+ let _wp = wd::watch("KeystoreDB::handle_next_superseded_blob delete");
tx.execute(
"DELETE FROM persistent.blobentry
WHERE NOT subcomponent_type = ?
@@ -1247,9 +1278,9 @@ impl KeystoreDB {
/// Unlike with `mark_unreferenced`, we don't need to purge grants, because only keys that made
/// it to `KeyLifeCycle::Live` may have grants.
pub fn cleanup_leftovers(&mut self) -> Result<usize> {
- let _wp = wd::watch_millis("KeystoreDB::cleanup_leftovers", 500);
+ let _wp = wd::watch("KeystoreDB::cleanup_leftovers");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_cleanup_leftovers"), |tx| {
tx.execute(
"UPDATE persistent.keyentry SET state = ? WHERE state = ?;",
params![KeyLifeCycle::Unreferenced, KeyLifeCycle::Existing],
@@ -1268,9 +1299,9 @@ impl KeystoreDB {
alias: &str,
key_type: KeyType,
) -> Result<bool> {
- let _wp = wd::watch_millis("KeystoreDB::key_exists", 500);
+ let _wp = wd::watch("KeystoreDB::key_exists");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_key_exists"), |tx| {
let key_descriptor =
KeyDescriptor { domain, nspace, alias: Some(alias.to_string()), blob: None };
let result = Self::load_key_entry_id(tx, &key_descriptor, key_type);
@@ -1295,9 +1326,9 @@ impl KeystoreDB {
blob_metadata: &BlobMetaData,
key_metadata: &KeyMetaData,
) -> Result<KeyEntry> {
- let _wp = wd::watch_millis("KeystoreDB::store_super_key", 500);
+ let _wp = wd::watch("KeystoreDB::store_super_key");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_store_super_key"), |tx| {
let key_id = Self::insert_with_retry(|id| {
tx.execute(
"INSERT into persistent.keyentry
@@ -1340,9 +1371,9 @@ impl KeystoreDB {
key_type: &SuperKeyType,
user_id: u32,
) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
- let _wp = wd::watch_millis("KeystoreDB::load_super_key", 500);
+ let _wp = wd::watch("KeystoreDB::load_super_key");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_load_super_key"), |tx| {
let key_descriptor = KeyDescriptor {
domain: Domain::APP,
nspace: user_id as i64,
@@ -1366,99 +1397,6 @@ impl KeystoreDB {
.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,20 +1404,25 @@ impl KeystoreDB {
where
F: Fn(&Transaction) -> Result<(bool, T)>,
{
+ let name = behavior.name();
loop {
- match self
+ let result = self
.conn
- .transaction_with_behavior(behavior)
+ .transaction_with_behavior(behavior.into())
.context(ks_err!())
- .and_then(|tx| f(&tx).map(|result| (result, tx)))
+ .and_then(|tx| {
+ let _wp = name.map(wd::watch);
+ f(&tx).map(|result| (result, tx))
+ })
.and_then(|(result, tx)| {
tx.commit().context(ks_err!("Failed to commit transaction."))?;
Ok(result)
- }) {
+ });
+ match result {
Ok(result) => break Ok(result),
Err(e) => {
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).context(ks_err!());
@@ -1505,27 +1448,6 @@ impl KeystoreDB {
)
}
- /// 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,
@@ -1574,9 +1496,9 @@ impl KeystoreDB {
blob: Option<&[u8]>,
blob_metadata: Option<&BlobMetaData>,
) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::set_blob", 500);
+ let _wp = wd::watch("KeystoreDB::set_blob");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_set_blob"), |tx| {
Self::set_blob_internal(tx, key_id.0, sc_type, blob, blob_metadata).need_gc()
})
.context(ks_err!())
@@ -1587,9 +1509,9 @@ impl KeystoreDB {
/// We use this to insert key blobs into the database which can then be garbage collected
/// lazily by the key garbage collector.
pub fn set_deleted_blob(&mut self, blob: &[u8], blob_metadata: &BlobMetaData) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::set_deleted_blob", 500);
+ let _wp = wd::watch("KeystoreDB::set_deleted_blob");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_set_deleted_blob"), |tx| {
Self::set_blob_internal(
tx,
Self::UNASSIGNED_KEY_ID,
@@ -1648,7 +1570,7 @@ impl KeystoreDB {
/// and associates them with the given `key_id`.
#[cfg(test)]
fn insert_keyparameter(&mut self, key_id: &KeyIdGuard, params: &[KeyParameter]) -> Result<()> {
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_insert_keyparameter"), |tx| {
Self::insert_keyparameter_internal(tx, key_id, params).no_gc()
})
.context(ks_err!())
@@ -1681,7 +1603,7 @@ impl KeystoreDB {
/// Insert a set of key entry specific metadata into the database.
#[cfg(test)]
fn insert_key_metadata(&mut self, key_id: &KeyIdGuard, metadata: &KeyMetaData) -> Result<()> {
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_insert_key_metadata"), |tx| {
metadata.store_in_db(key_id.0, tx).no_gc()
})
.context(ks_err!())
@@ -1749,7 +1671,7 @@ impl KeystoreDB {
caller_uid: u32,
check_permission: impl Fn(&KeyDescriptor) -> Result<()>,
) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::migrate_key_namespace", 500);
+ let _wp = wd::watch("KeystoreDB::migrate_key_namespace");
let destination = match destination.domain {
Domain::APP => KeyDescriptor { nspace: caller_uid as i64, ..(*destination).clone() },
@@ -1769,7 +1691,7 @@ impl KeystoreDB {
.ok_or(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
.context(ks_err!("Alias must be specified."))?;
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_migrate_key_namespace"), |tx| {
// Query the destination location. If there is a key, the migration request fails.
if tx
.query_row(
@@ -1820,7 +1742,7 @@ impl KeystoreDB {
metadata: &KeyMetaData,
km_uuid: &Uuid,
) -> Result<KeyIdGuard> {
- let _wp = wd::watch_millis("KeystoreDB::store_new_key", 500);
+ let _wp = wd::watch("KeystoreDB::store_new_key");
let (alias, domain, namespace) = match key {
KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
@@ -1832,7 +1754,7 @@ impl KeystoreDB {
.context(ks_err!("Need alias and domain must be APP or SELINUX."));
}
};
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_store_new_key"), |tx| {
let key_id = Self::create_key_entry_internal(tx, &domain, namespace, key_type, km_uuid)
.context("Trying to create new key entry.")?;
let BlobInfo { blob, metadata: blob_metadata, superseded_blob } = *blob_info;
@@ -1899,7 +1821,7 @@ impl KeystoreDB {
cert: &[u8],
km_uuid: &Uuid,
) -> Result<KeyIdGuard> {
- let _wp = wd::watch_millis("KeystoreDB::store_new_certificate", 500);
+ let _wp = wd::watch("KeystoreDB::store_new_certificate");
let (alias, domain, namespace) = match key {
KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
@@ -1911,7 +1833,7 @@ impl KeystoreDB {
.context(ks_err!("Need alias and domain must be APP or SELINUX."));
}
};
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_store_new_certificate"), |tx| {
let key_id = Self::create_key_entry_internal(tx, &domain, namespace, key_type, km_uuid)
.context("Trying to create new key entry.")?;
@@ -2179,9 +2101,9 @@ impl KeystoreDB {
/// zero, the key also gets marked unreferenced and scheduled for deletion.
/// Returns Ok(true) if the key was marked unreferenced as a hint to the garbage collector.
pub fn check_and_update_key_usage_count(&mut self, key_id: i64) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::check_and_update_key_usage_count", 500);
+ let _wp = wd::watch("KeystoreDB::check_and_update_key_usage_count");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_check_and_update_key_usage_count"), |tx| {
let limit: Option<i32> = tx
.query_row(
"SELECT data FROM persistent.keyparameter WHERE keyentryid = ? AND tag = ?;",
@@ -2227,7 +2149,7 @@ impl KeystoreDB {
caller_uid: u32,
check_permission: impl Fn(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
) -> Result<(KeyIdGuard, KeyEntry)> {
- let _wp = wd::watch_millis("KeystoreDB::load_key_entry", 500);
+ let _wp = wd::watch("KeystoreDB::load_key_entry");
loop {
match self.load_key_entry_internal(
@@ -2240,7 +2162,7 @@ impl KeystoreDB {
Ok(result) => break Ok(result),
Err(e) => {
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).context(ks_err!());
@@ -2355,9 +2277,9 @@ impl KeystoreDB {
caller_uid: u32,
check_permission: impl Fn(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::unbind_key", 500);
+ let _wp = wd::watch("KeystoreDB::unbind_key");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_unbind_key"), |tx| {
let (key_id, access_key_descriptor, access_vector) =
Self::load_access_tuple(tx, key, key_type, caller_uid)
.context("Trying to get access tuple.")?;
@@ -2386,12 +2308,12 @@ impl KeystoreDB {
/// Delete all artifacts belonging to the namespace given by the domain-namespace tuple.
/// This leaves all of the blob entries orphaned for subsequent garbage collection.
pub fn unbind_keys_for_namespace(&mut self, domain: Domain, namespace: i64) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::unbind_keys_for_namespace", 500);
+ let _wp = wd::watch("KeystoreDB::unbind_keys_for_namespace");
if !(domain == Domain::APP || domain == Domain::SELINUX) {
return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT)).context(ks_err!());
}
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_unbind_keys_for_namespace"), |tx| {
tx.execute(
"DELETE FROM persistent.keymetadata
WHERE keyentryid IN (
@@ -2431,7 +2353,7 @@ impl KeystoreDB {
}
fn cleanup_unreferenced(tx: &Transaction) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::cleanup_unreferenced", 500);
+ let _wp = wd::watch("KeystoreDB::cleanup_unreferenced");
{
tx.execute(
"DELETE FROM persistent.keymetadata
@@ -2471,18 +2393,11 @@ impl KeystoreDB {
.context(ks_err!())
}
- /// Delete the keys created on behalf of the user, denoted by the user id.
- /// Delete all the keys unless 'keep_non_super_encrypted_keys' set to true.
- /// Returned boolean is to hint the garbage collector to delete the unbound keys.
- /// The caller of this function should notify the gc if the returned value is true.
- pub fn unbind_keys_for_user(
- &mut self,
- user_id: u32,
- keep_non_super_encrypted_keys: bool,
- ) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::unbind_keys_for_user", 500);
+ /// Deletes all keys for the given user, including both client keys and super keys.
+ pub fn unbind_keys_for_user(&mut self, user_id: u32) -> Result<()> {
+ let _wp = wd::watch("KeystoreDB::unbind_keys_for_user");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_unbind_keys_for_user"), |tx| {
let mut stmt = tx
.prepare(&format!(
"SELECT id from persistent.keyentry
@@ -2527,17 +2442,6 @@ impl KeystoreDB {
let mut notify_gc = false;
for key_id in key_ids {
- if keep_non_super_encrypted_keys {
- // Load metadata and filter out non-super-encrypted keys.
- if let (_, Some((_, blob_metadata)), _, _) =
- Self::load_blob_components(key_id, KeyEntryLoadBits::KM, tx)
- .context(ks_err!("Trying to load blob info."))?
- {
- if blob_metadata.encrypted_by().is_none() {
- continue;
- }
- }
- }
notify_gc = Self::mark_unreferenced(tx, key_id)
.context("In unbind_keys_for_user.")?
|| notify_gc;
@@ -2557,9 +2461,9 @@ impl KeystoreDB {
/// be unlocked should remain usable when the lock screen is set to Swipe or None, as the device
/// is always considered "unlocked" in that case.
pub fn unbind_auth_bound_keys_for_user(&mut self, user_id: u32) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::unbind_auth_bound_keys_for_user", 500);
+ let _wp = wd::watch("KeystoreDB::unbind_auth_bound_keys_for_user");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_unbind_auth_bound_keys_for_user"), |tx| {
let mut stmt = tx
.prepare(&format!(
"SELECT id from persistent.keyentry
@@ -2652,7 +2556,7 @@ impl KeystoreDB {
key_type: KeyType,
start_past_alias: Option<&str>,
) -> Result<Vec<KeyDescriptor>> {
- let _wp = wd::watch_millis("KeystoreDB::list_past_alias", 500);
+ let _wp = wd::watch("KeystoreDB::list_past_alias");
let query = format!(
"SELECT DISTINCT alias FROM persistent.keyentry
@@ -2707,7 +2611,7 @@ impl KeystoreDB {
namespace: i64,
key_type: KeyType,
) -> Result<usize> {
- let _wp = wd::watch_millis("KeystoreDB::countKeys", 500);
+ let _wp = wd::watch("KeystoreDB::countKeys");
let num_keys = self.with_transaction(TransactionBehavior::Deferred, |tx| {
tx.query_row(
@@ -2740,9 +2644,9 @@ impl KeystoreDB {
access_vector: KeyPermSet,
check_permission: impl Fn(&KeyDescriptor, &KeyPermSet) -> Result<()>,
) -> Result<KeyDescriptor> {
- let _wp = wd::watch_millis("KeystoreDB::grant", 500);
+ let _wp = wd::watch("KeystoreDB::grant");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_grant"), |tx| {
// Load the key_id and complete the access control tuple.
// We ignore the access vector here because grants cannot be granted.
// The access vector returned here expresses the permissions the
@@ -2806,9 +2710,9 @@ impl KeystoreDB {
grantee_uid: u32,
check_permission: impl Fn(&KeyDescriptor) -> Result<()>,
) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::ungrant", 500);
+ let _wp = wd::watch("KeystoreDB::ungrant");
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_ungrant"), |tx| {
// Load the key_id and complete the access control tuple.
// We ignore the access vector here because grants cannot be granted.
let (key_id, access_key_descriptor, _) =
@@ -2863,31 +2767,16 @@ impl KeystoreDB {
}
/// 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
pub fn load_key_descriptor(&mut self, key_id: i64) -> Result<Option<KeyDescriptor>> {
- let _wp = wd::watch_millis("KeystoreDB::load_key_descriptor", 500);
+ let _wp = wd::watch("KeystoreDB::load_key_descriptor");
self.with_transaction(TransactionBehavior::Deferred, |tx| {
tx.query_row(
@@ -2918,9 +2807,9 @@ impl KeystoreDB {
user_id: i32,
secure_user_id: i64,
) -> Result<Vec<i64>> {
- let _wp = wd::watch_millis("KeystoreDB::get_app_uids_affected_by_sid", 500);
+ let _wp = wd::watch("KeystoreDB::get_app_uids_affected_by_sid");
- let key_ids_and_app_uids = self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ let ids = self.with_transaction(Immediate("TX_get_app_uids_affected_by_sid"), |tx| {
let mut stmt = tx
.prepare(&format!(
"SELECT id, namespace from persistent.keyentry
@@ -2949,13 +2838,13 @@ impl KeystoreDB {
Ok(key_ids_and_app_uids).no_gc()
})?;
let mut app_uids_affected_by_sid: HashSet<i64> = Default::default();
- for (key_id, app_uid) in key_ids_and_app_uids {
+ for (key_id, app_uid) in ids {
// Read the key parameters for each key in its own transaction. It is OK to ignore
// an error to get the properties of a particular key since it might have been deleted
// under our feet after the previous transaction concluded. If the key was deleted
// then it is no longer applicable if it was auth-bound or not.
if let Ok(is_key_bound_to_sid) =
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ self.with_transaction(Immediate("TX_get_app_uids_affects_by_sid 2"), |tx| {
let params = Self::load_key_parameters(key_id, tx)
.context("Failed to load key parameters.")?;
// Check if the key is bound to this secure user ID.
@@ -2978,2642 +2867,3 @@ impl KeystoreDB {
Ok(app_uids_vec)
}
}
-
-#[cfg(test)]
-pub mod tests {
-
- use super::*;
- use crate::key_parameter::{
- Algorithm, BlockMode, Digest, EcCurve, HardwareAuthenticatorType, KeyOrigin, KeyParameter,
- KeyParameterValue, KeyPurpose, PaddingMode, SecurityLevel,
- };
- use crate::key_perm_set;
- use crate::permission::{KeyPerm, KeyPermSet};
- use crate::super_key::{SuperKeyManager, USER_AFTER_FIRST_UNLOCK_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType};
- use keystore2_test_utils::TempDir;
- use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- HardwareAuthToken::HardwareAuthToken,
- HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
- };
- use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- Timestamp::Timestamp,
- };
- use rusqlite::TransactionBehavior;
- use std::cell::RefCell;
- use std::collections::BTreeMap;
- use std::fmt::Write;
- use std::sync::atomic::{AtomicU8, Ordering};
- use std::sync::Arc;
- use std::thread;
- use std::time::{Duration, SystemTime};
- use crate::utils::AesGcm;
- #[cfg(disabled)]
- use std::time::Instant;
-
- pub fn new_test_db() -> Result<KeystoreDB> {
- let conn = KeystoreDB::make_connection("file::memory:")?;
-
- let mut db = KeystoreDB { conn, gc: None, perboot: Arc::new(perboot::PerbootDB::new()) };
- db.with_transaction(TransactionBehavior::Immediate, |tx| {
- KeystoreDB::init_tables(tx).context("Failed to initialize tables.").no_gc()
- })?;
- Ok(db)
- }
-
- fn rebind_alias(
- db: &mut KeystoreDB,
- newid: &KeyIdGuard,
- alias: &str,
- domain: Domain,
- namespace: i64,
- ) -> Result<bool> {
- db.with_transaction(TransactionBehavior::Immediate, |tx| {
- KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace, KeyType::Client).no_gc()
- })
- .context(ks_err!())
- }
-
- #[test]
- fn datetime() -> Result<()> {
- let conn = Connection::open_in_memory()?;
- conn.execute("CREATE TABLE test (ts DATETIME);", [])?;
- let now = SystemTime::now();
- let duration = Duration::from_secs(1000);
- let then = now.checked_sub(duration).unwrap();
- let soon = now.checked_add(duration).unwrap();
- conn.execute(
- "INSERT INTO test (ts) VALUES (?), (?), (?);",
- params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?],
- )?;
- let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?;
- let mut rows = stmt.query([])?;
- assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?);
- assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?);
- assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?);
- assert!(rows.next()?.is_none());
- assert!(DateTime::try_from(then)? < DateTime::try_from(now)?);
- assert!(DateTime::try_from(then)? < DateTime::try_from(soon)?);
- assert!(DateTime::try_from(now)? < DateTime::try_from(soon)?);
- Ok(())
- }
-
- // Ensure that we're using the "injected" random function, not the real one.
- #[test]
- fn test_mocked_random() {
- let rand1 = random();
- let rand2 = random();
- let rand3 = random();
- if rand1 == rand2 {
- assert_eq!(rand2 + 1, rand3);
- } else {
- assert_eq!(rand1 + 1, rand2);
- assert_eq!(rand2, rand3);
- }
- }
-
- // Test that we have the correct tables.
- #[test]
- fn test_tables() -> Result<()> {
- let db = new_test_db()?;
- let tables = db
- .conn
- .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")?
- .query_map(params![], |row| row.get(0))?
- .collect::<rusqlite::Result<Vec<String>>>()?;
- assert_eq!(tables.len(), 6);
- assert_eq!(tables[0], "blobentry");
- assert_eq!(tables[1], "blobmetadata");
- assert_eq!(tables[2], "grant");
- assert_eq!(tables[3], "keyentry");
- assert_eq!(tables[4], "keymetadata");
- assert_eq!(tables[5], "keyparameter");
- Ok(())
- }
-
- #[test]
- fn test_auth_token_table_invariant() -> Result<()> {
- let mut db = new_test_db()?;
- let auth_token1 = HardwareAuthToken {
- challenge: i64::MAX,
- userId: 200,
- authenticatorId: 200,
- authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
- timestamp: Timestamp { milliSeconds: 500 },
- mac: String::from("mac").into_bytes(),
- };
- db.insert_auth_token(&auth_token1);
- let auth_tokens_returned = get_auth_tokens(&db);
- assert_eq!(auth_tokens_returned.len(), 1);
-
- // insert another auth token with the same values for the columns in the UNIQUE constraint
- // of the auth token table and different value for timestamp
- let auth_token2 = HardwareAuthToken {
- challenge: i64::MAX,
- userId: 200,
- authenticatorId: 200,
- authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
- timestamp: Timestamp { milliSeconds: 600 },
- mac: String::from("mac").into_bytes(),
- };
-
- db.insert_auth_token(&auth_token2);
- let mut auth_tokens_returned = get_auth_tokens(&db);
- assert_eq!(auth_tokens_returned.len(), 1);
-
- if let Some(auth_token) = auth_tokens_returned.pop() {
- assert_eq!(auth_token.auth_token.timestamp.milliSeconds, 600);
- }
-
- // insert another auth token with the different values for the columns in the UNIQUE
- // constraint of the auth token table
- let auth_token3 = HardwareAuthToken {
- challenge: i64::MAX,
- userId: 201,
- authenticatorId: 200,
- authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
- timestamp: Timestamp { milliSeconds: 600 },
- mac: String::from("mac").into_bytes(),
- };
-
- db.insert_auth_token(&auth_token3);
- let auth_tokens_returned = get_auth_tokens(&db);
- assert_eq!(auth_tokens_returned.len(), 2);
-
- Ok(())
- }
-
- // utility function for test_auth_token_table_invariant()
- fn get_auth_tokens(db: &KeystoreDB) -> Vec<AuthTokenEntry> {
- db.perboot.get_all_auth_token_entries()
- }
-
- #[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)?;
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 1);
-
- let db = KeystoreDB::new(temp_dir.path(), None)?;
-
- let entries_new = get_keyentry(&db)?;
- assert_eq!(entries, entries_new);
- Ok(())
- }
-
- #[test]
- fn test_create_key_entry() -> Result<()> {
- fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>, Uuid) {
- (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref(), ke.km_uuid.unwrap())
- }
-
- 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)?;
-
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None, KEYSTORE_UUID));
- assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None, KEYSTORE_UUID));
-
- // 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),
- &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),
- &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),
- &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
- );
-
- Ok(())
- }
-
- #[test]
- fn test_rebind_alias() -> Result<()> {
- fn extractor(
- ke: &KeyEntryRow,
- ) -> (Option<Domain>, Option<i64>, Option<&str>, Option<Uuid>) {
- (ke.domain, ke.namespace, ke.alias.as_deref(), ke.km_uuid)
- }
-
- 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)?;
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(
- extractor(&entries[0]),
- (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
- );
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
- );
-
- // Test that the first call to rebind_alias sets the alias.
- rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[0].id), "foo", Domain::APP, 42)?;
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(
- extractor(&entries[0]),
- (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
- );
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
- );
-
- // Test that the second call to rebind_alias also empties the old one.
- rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[1].id), "foo", Domain::APP, 42)?;
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
- );
-
- // Test that we must pass in a valid Domain.
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42),
- &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
- );
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42),
- &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
- );
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42),
- &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
- );
-
- // Test that we correctly handle setting an alias for something that does not exist.
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::SELINUX, 42),
- "Expected to update a single entry but instead updated 0",
- );
- // Test that we correctly abort the transaction in this case.
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
- );
-
- Ok(())
- }
-
- #[test]
- fn test_grant_ungrant() -> Result<()> {
- const CALLER_UID: u32 = 15;
- const GRANTEE_UID: u32 = 12;
- const SELINUX_NAMESPACE: i64 = 7;
-
- let mut db = new_test_db()?;
- db.conn.execute(
- "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias, state, km_uuid)
- VALUES (1, 0, 0, 15, 'key', 1, ?), (2, 0, 2, 7, 'yek', 1, ?);",
- params![KEYSTORE_UUID, KEYSTORE_UUID],
- )?;
- let app_key = KeyDescriptor {
- domain: super::Domain::APP,
- nspace: 0,
- alias: Some("key".to_string()),
- blob: None,
- };
- const PVEC1: KeyPermSet = key_perm_set![KeyPerm::Use, KeyPerm::GetInfo];
- const PVEC2: KeyPermSet = key_perm_set![KeyPerm::Use];
-
- // Reset totally predictable random number generator in case we
- // are not the first test running on this thread.
- reset_random();
- let next_random = 0i64;
-
- let app_granted_key = db
- .grant(&app_key, CALLER_UID, GRANTEE_UID, PVEC1, |k, a| {
- assert_eq!(*a, PVEC1);
- assert_eq!(
- *k,
- KeyDescriptor {
- domain: super::Domain::APP,
- // namespace must be set to the caller_uid.
- nspace: CALLER_UID as i64,
- alias: Some("key".to_string()),
- blob: None,
- }
- );
- Ok(())
- })
- .unwrap();
-
- assert_eq!(
- app_granted_key,
- KeyDescriptor {
- domain: super::Domain::GRANT,
- // The grantid is next_random due to the mock random number generator.
- nspace: next_random,
- alias: None,
- blob: None,
- }
- );
-
- let selinux_key = KeyDescriptor {
- domain: super::Domain::SELINUX,
- nspace: SELINUX_NAMESPACE,
- alias: Some("yek".to_string()),
- blob: None,
- };
-
- let selinux_granted_key = db
- .grant(&selinux_key, CALLER_UID, 12, PVEC1, |k, a| {
- assert_eq!(*a, PVEC1);
- assert_eq!(
- *k,
- KeyDescriptor {
- domain: super::Domain::SELINUX,
- // namespace must be the supplied SELinux
- // namespace.
- nspace: SELINUX_NAMESPACE,
- alias: Some("yek".to_string()),
- blob: None,
- }
- );
- Ok(())
- })
- .unwrap();
-
- assert_eq!(
- selinux_granted_key,
- KeyDescriptor {
- domain: super::Domain::GRANT,
- // The grantid is next_random + 1 due to the mock random number generator.
- nspace: next_random + 1,
- alias: None,
- blob: None,
- }
- );
-
- // This should update the existing grant with PVEC2.
- let selinux_granted_key = db
- .grant(&selinux_key, CALLER_UID, 12, PVEC2, |k, a| {
- assert_eq!(*a, PVEC2);
- assert_eq!(
- *k,
- KeyDescriptor {
- domain: super::Domain::SELINUX,
- // namespace must be the supplied SELinux
- // namespace.
- nspace: SELINUX_NAMESPACE,
- alias: Some("yek".to_string()),
- blob: None,
- }
- );
- Ok(())
- })
- .unwrap();
-
- assert_eq!(
- selinux_granted_key,
- KeyDescriptor {
- domain: super::Domain::GRANT,
- // Same grant id as before. The entry was only updated.
- nspace: next_random + 1,
- alias: None,
- blob: None,
- }
- );
-
- {
- // Limiting scope of stmt, because it borrows db.
- let mut stmt = db
- .conn
- .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
- let mut rows = stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>([], |row| {
- Ok((row.get(0)?, row.get(1)?, row.get(2)?, KeyPermSet::from(row.get::<_, i32>(3)?)))
- })?;
-
- let r = rows.next().unwrap().unwrap();
- assert_eq!(r, (next_random, GRANTEE_UID, 1, PVEC1));
- let r = rows.next().unwrap().unwrap();
- assert_eq!(r, (next_random + 1, GRANTEE_UID, 2, PVEC2));
- assert!(rows.next().is_none());
- }
-
- debug_dump_keyentry_table(&mut db)?;
- println!("app_key {:?}", app_key);
- println!("selinux_key {:?}", selinux_key);
-
- db.ungrant(&app_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
- db.ungrant(&selinux_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
-
- Ok(())
- }
-
- static TEST_KEY_BLOB: &[u8] = b"my test blob";
- static TEST_CERT_BLOB: &[u8] = b"my test cert";
- static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain";
-
- #[test]
- fn test_set_blob() -> Result<()> {
- let key_id = KEY_ID_LOCK.get(3000);
- let mut db = new_test_db()?;
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
- db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
- db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
- drop(key_id);
-
- let mut stmt = db.conn.prepare(
- "SELECT subcomponent_type, keyentryid, blob, id FROM persistent.blobentry
- ORDER BY subcomponent_type ASC;",
- )?;
- let mut rows = stmt
- .query_map::<((SubComponentType, i64, Vec<u8>), i64), _, _>([], |row| {
- Ok(((row.get(0)?, row.get(1)?, row.get(2)?), row.get(3)?))
- })?;
- let (r, id) = rows.next().unwrap().unwrap();
- assert_eq!(r, (SubComponentType::KEY_BLOB, 3000, TEST_KEY_BLOB.to_vec()));
- let (r, _) = rows.next().unwrap().unwrap();
- assert_eq!(r, (SubComponentType::CERT, 3000, TEST_CERT_BLOB.to_vec()));
- let (r, _) = rows.next().unwrap().unwrap();
- assert_eq!(r, (SubComponentType::CERT_CHAIN, 3000, TEST_CERT_CHAIN_BLOB.to_vec()));
-
- drop(rows);
- drop(stmt);
-
- assert_eq!(
- db.with_transaction(TransactionBehavior::Immediate, |tx| {
- BlobMetaData::load_from_db(id, tx).no_gc()
- })
- .expect("Should find blob metadata."),
- blob_metadata
- );
- Ok(())
- }
-
- static TEST_ALIAS: &str = "my super duper key";
-
- #[test]
- fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_domain_app")?
- .0;
- let (_key_guard, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_insert_and_load_certificate_entry_domain_app() -> Result<()> {
- let mut db = new_test_db()?;
-
- db.store_new_certificate(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- TEST_CERT_BLOB,
- &KEYSTORE_UUID,
- )
- .expect("Trying to insert cert.");
-
- let (_key_guard, mut key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::PUBLIC,
- 1,
- |_k, _av| Ok(()),
- )
- .expect("Trying to read certificate entry.");
-
- assert!(key_entry.pure_cert());
- assert!(key_entry.cert().is_none());
- assert_eq!(key_entry.take_cert_chain(), Some(TEST_CERT_BLOB.to_vec()));
-
- db.unbind_key(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_domain_selinux")?
- .0;
- let (_key_guard, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_domain_key_id")?
- .0;
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_check_and_update_key_usage_count_with_limited_use_key() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(123))
- .context("test_check_and_update_key_usage_count_with_limited_use_key")?
- .0;
- // Update the usage count of the limited use key.
- db.check_and_update_key_usage_count(key_id)?;
-
- let (_key_guard, key_entry) = db.load_key_entry(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )?;
-
- // The usage count is decremented now.
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, Some(122)));
-
- Ok(())
- }
-
- #[test]
- fn test_check_and_update_key_usage_count_with_exhausted_limited_use_key() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(1))
- .context("test_check_and_update_key_usage_count_with_exhausted_limited_use_key")?
- .0;
- // Update the usage count of the limited use key.
- db.check_and_update_key_usage_count(key_id).expect(concat!(
- "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
- "This should succeed."
- ));
-
- // Try to update the exhausted limited use key.
- let e = db.check_and_update_key_usage_count(key_id).expect_err(concat!(
- "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
- "This should fail."
- ));
- assert_eq!(
- &KsError::Km(ErrorCode::INVALID_KEY_BLOB),
- e.root_cause().downcast_ref::<KsError>().unwrap()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant")?
- .0;
-
- let granted_key = db
- .grant(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- 1,
- 2,
- key_perm_set![KeyPerm::Use],
- |_k, _av| Ok(()),
- )
- .unwrap();
-
- debug_dump_grant_table(&mut db)?;
-
- let (_key_guard, key_entry) = db
- .load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::BOTH, 2, |k, av| {
- assert_eq!(Domain::GRANT, k.domain);
- assert!(av.unwrap().includes(KeyPerm::Use));
- Ok(())
- })
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(&granted_key, KeyType::Client, 2, |_, _| Ok(())).unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &granted_key,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 2,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // This test attempts to load a key by key id while the caller is not the owner
- // but a grant exists for the given key and the caller.
- #[test]
- fn test_insert_and_load_full_keyentry_from_grant_by_key_id() -> Result<()> {
- let mut db = new_test_db()?;
- const OWNER_UID: u32 = 1u32;
- const GRANTEE_UID: u32 = 2u32;
- const SOMEONE_ELSE_UID: u32 = 3u32;
- let key_id = make_test_key_entry(&mut db, Domain::APP, OWNER_UID as i64, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?
- .0;
-
- db.grant(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- OWNER_UID,
- GRANTEE_UID,
- key_perm_set![KeyPerm::Use],
- |_k, _av| Ok(()),
- )
- .unwrap();
-
- debug_dump_grant_table(&mut db)?;
-
- let id_descriptor =
- KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, ..Default::default() };
-
- let (_, key_entry) = db
- .load_key_entry(
- &id_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- GRANTEE_UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(OWNER_UID as i64, k.nspace);
- assert!(av.unwrap().includes(KeyPerm::Use));
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- let (_, key_entry) = db
- .load_key_entry(
- &id_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- SOMEONE_ELSE_UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(OWNER_UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(&id_descriptor, KeyType::Client, OWNER_UID, |_, _| Ok(())).unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &id_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- GRANTEE_UID,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // Creates a key migrates it to a different location and then tries to access it by the old
- // and new location.
- #[test]
- fn test_migrate_key_app_to_app() -> Result<()> {
- let mut db = new_test_db()?;
- const SOURCE_UID: u32 = 1u32;
- const DESTINATION_UID: u32 = 2u32;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
- let key_id_guard =
- make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
-
- let source_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(SOURCE_ALIAS.to_string()),
- blob: None,
- };
-
- let destination_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(DESTINATION_ALIAS.to_string()),
- blob: None,
- };
-
- let key_id = key_id_guard.id();
-
- db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| {
- Ok(())
- })
- .unwrap();
-
- let (_, key_entry) = db
- .load_key_entry(
- &destination_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- DESTINATION_UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(DESTINATION_UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &source_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- SOURCE_UID,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // Creates a key migrates it to a different location and then tries to access it by the old
- // and new location.
- #[test]
- fn test_migrate_key_app_to_selinux() -> Result<()> {
- let mut db = new_test_db()?;
- const SOURCE_UID: u32 = 1u32;
- const DESTINATION_UID: u32 = 2u32;
- const DESTINATION_NAMESPACE: i64 = 1000i64;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
- let key_id_guard =
- make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
-
- let source_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(SOURCE_ALIAS.to_string()),
- blob: None,
- };
-
- let destination_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: DESTINATION_NAMESPACE,
- alias: Some(DESTINATION_ALIAS.to_string()),
- blob: None,
- };
-
- let key_id = key_id_guard.id();
-
- db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| {
- Ok(())
- })
- .unwrap();
-
- let (_, key_entry) = db
- .load_key_entry(
- &destination_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- DESTINATION_UID,
- |k, av| {
- assert_eq!(Domain::SELINUX, k.domain);
- assert_eq!(DESTINATION_NAMESPACE, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &source_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- SOURCE_UID,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // Creates two keys and tries to migrate the first to the location of the second which
- // is expected to fail.
- #[test]
- fn test_migrate_key_destination_occupied() -> Result<()> {
- let mut db = new_test_db()?;
- const SOURCE_UID: u32 = 1u32;
- const DESTINATION_UID: u32 = 2u32;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
- let key_id_guard =
- make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
- make_test_key_entry(&mut db, Domain::APP, DESTINATION_UID as i64, DESTINATION_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
-
- let destination_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(DESTINATION_ALIAS.to_string()),
- blob: None,
- };
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::INVALID_ARGUMENT)),
- db.migrate_key_namespace(
- key_id_guard,
- &destination_descriptor,
- DESTINATION_UID,
- |_k| Ok(())
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_upgrade_0_to_1() {
- const ALIAS1: &str = "test_upgrade_0_to_1_1";
- const ALIAS2: &str = "test_upgrade_0_to_1_2";
- const ALIAS3: &str = "test_upgrade_0_to_1_3";
- const UID: u32 = 33;
- let temp_dir = Arc::new(TempDir::new("test_upgrade_0_to_1").unwrap());
- let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
- let key_id_untouched1 =
- make_test_key_entry(&mut db, Domain::APP, UID as i64, ALIAS1, None).unwrap().id();
- let key_id_untouched2 =
- make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS2, false).unwrap().id();
- let key_id_deleted =
- make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS3, true).unwrap().id();
-
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS1.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS2.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS3.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_deleted, true));
-
- db.with_transaction(TransactionBehavior::Immediate, |tx| {
- KeystoreDB::from_0_to_1(tx).no_gc()
- })
- .unwrap();
-
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS1.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS2.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS3.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
- }
-
- static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
-
- #[test]
- fn test_insert_and_load_full_keyentry_domain_app_concurrently() -> Result<()> {
- let handle = {
- let temp_dir = Arc::new(TempDir::new("id_lock_test")?);
- let temp_dir_clone = temp_dir.clone();
- let mut db = KeystoreDB::new(temp_dir.path(), None)?;
- let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_domain_app")?
- .0;
- let (_key_guard, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 33,
- |_k, _av| Ok(()),
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
- let state = Arc::new(AtomicU8::new(1));
- let state2 = state.clone();
-
- // Spawning a second thread that attempts to acquire the key id lock
- // for the same key as the primary thread. The primary thread then
- // waits, thereby forcing the secondary thread into the second stage
- // of acquiring the lock (see KEY ID LOCK 2/2 above).
- // The test succeeds if the secondary thread observes the transition
- // of `state` from 1 to 2, despite having a whole second to overtake
- // the primary thread.
- let handle = thread::spawn(move || {
- let temp_dir = temp_dir_clone;
- let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
- assert!(db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 33,
- |_k, _av| Ok(()),
- )
- .is_ok());
- // We should only see a 2 here because we can only return
- // from load_key_entry when the `_key_guard` expires,
- // which happens at the end of the scope.
- assert_eq!(2, state2.load(Ordering::Relaxed));
- });
-
- thread::sleep(std::time::Duration::from_millis(1000));
-
- assert_eq!(Ok(1), state.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed));
-
- // Return the handle from this scope so we can join with the
- // secondary thread after the key id lock has expired.
- handle
- // This is where the `_key_guard` goes out of scope,
- // which is the reason for concurrent load_key_entry on the same key
- // to unblock.
- };
- // Join with the secondary thread and unwrap, to propagate failing asserts to the
- // main test thread. We will not see failing asserts in secondary threads otherwise.
- handle.join().unwrap();
- Ok(())
- }
-
- #[test]
- fn test_database_busy_error_code() {
- let temp_dir =
- TempDir::new("test_database_busy_error_code_").expect("Failed to create temp dir.");
-
- let mut db1 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database1.");
- let mut db2 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database2.");
-
- let _tx1 = db1
- .conn
- .transaction_with_behavior(TransactionBehavior::Immediate)
- .expect("Failed to create first transaction.");
-
- let error = db2
- .conn
- .transaction_with_behavior(TransactionBehavior::Immediate)
- .context("Transaction begin failed.")
- .expect_err("This should fail.");
- let root_cause = error.root_cause();
- if let Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. }) =
- root_cause.downcast_ref::<rusqlite::ffi::Error>()
- {
- return;
- }
- panic!(
- "Unexpected error {:?} \n{:?} \n{:?}",
- error,
- root_cause,
- root_cause.downcast_ref::<rusqlite::ffi::Error>()
- )
- }
-
- #[cfg(disabled)]
- #[test]
- fn test_large_number_of_concurrent_db_manipulations() -> Result<()> {
- let temp_dir = Arc::new(
- TempDir::new("test_large_number_of_concurrent_db_manipulations_")
- .expect("Failed to create temp dir."),
- );
-
- let test_begin = Instant::now();
-
- const KEY_COUNT: u32 = 500u32;
- let mut db =
- new_test_db_with_gc(temp_dir.path(), |_, _| Ok(())).expect("Failed to open database.");
- const OPEN_DB_COUNT: u32 = 50u32;
-
- let mut actual_key_count = KEY_COUNT;
- // First insert KEY_COUNT keys.
- for count in 0..KEY_COUNT {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
- actual_key_count = count;
- break;
- }
- let alias = format!("test_alias_{}", count);
- make_test_key_entry(&mut db, Domain::APP, 1, &alias, None)
- .expect("Failed to make key entry.");
- }
-
- // Insert more keys from a different thread and into a different namespace.
- let temp_dir1 = temp_dir.clone();
- let handle1 = thread::spawn(move || {
- let mut db = new_test_db_with_gc(temp_dir1.path(), |_, _| Ok(()))
- .expect("Failed to open database.");
-
- for count in 0..actual_key_count {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let alias = format!("test_alias_{}", count);
- make_test_key_entry(&mut db, Domain::APP, 2, &alias, None)
- .expect("Failed to make key entry.");
- }
-
- // then unbind them again.
- for count in 0..actual_key_count {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let key = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(format!("test_alias_{}", count)),
- blob: None,
- };
- db.unbind_key(&key, KeyType::Client, 2, |_, _| Ok(())).expect("Unbind Failed.");
- }
- });
-
- // And start unbinding the first set of keys.
- let temp_dir2 = temp_dir.clone();
- let handle2 = thread::spawn(move || {
- let mut db = new_test_db_with_gc(temp_dir2.path(), |_, _| Ok(()))
- .expect("Failed to open database.");
-
- for count in 0..actual_key_count {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let key = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(format!("test_alias_{}", count)),
- blob: None,
- };
- db.unbind_key(&key, KeyType::Client, 1, |_, _| Ok(())).expect("Unbind Failed.");
- }
- });
-
- // While a lot of inserting and deleting is going on we have to open database connections
- // successfully and use them.
- // This clone is not redundant, because temp_dir needs to be kept alive until db goes
- // out of scope.
- #[allow(clippy::redundant_clone)]
- let temp_dir4 = temp_dir.clone();
- let handle4 = thread::spawn(move || {
- for count in 0..OPEN_DB_COUNT {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let mut db = new_test_db_with_gc(temp_dir4.path(), |_, _| Ok(()))
- .expect("Failed to open database.");
-
- let alias = format!("test_alias_{}", count);
- make_test_key_entry(&mut db, Domain::APP, 3, &alias, None)
- .expect("Failed to make key entry.");
- let key = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(alias),
- blob: None,
- };
- db.unbind_key(&key, KeyType::Client, 3, |_, _| Ok(())).expect("Unbind Failed.");
- }
- });
-
- handle1.join().expect("Thread 1 panicked.");
- handle2.join().expect("Thread 2 panicked.");
- handle4.join().expect("Thread 4 panicked.");
-
- Ok(())
- }
-
- #[test]
- fn list() -> Result<()> {
- let temp_dir = TempDir::new("list_test")?;
- let mut db = KeystoreDB::new(temp_dir.path(), None)?;
- static LIST_O_ENTRIES: &[(Domain, i64, &str)] = &[
- (Domain::APP, 1, "test1"),
- (Domain::APP, 1, "test2"),
- (Domain::APP, 1, "test3"),
- (Domain::APP, 1, "test4"),
- (Domain::APP, 1, "test5"),
- (Domain::APP, 1, "test6"),
- (Domain::APP, 1, "test7"),
- (Domain::APP, 2, "test1"),
- (Domain::APP, 2, "test2"),
- (Domain::APP, 2, "test3"),
- (Domain::APP, 2, "test4"),
- (Domain::APP, 2, "test5"),
- (Domain::APP, 2, "test6"),
- (Domain::APP, 2, "test8"),
- (Domain::SELINUX, 100, "test1"),
- (Domain::SELINUX, 100, "test2"),
- (Domain::SELINUX, 100, "test3"),
- (Domain::SELINUX, 100, "test4"),
- (Domain::SELINUX, 100, "test5"),
- (Domain::SELINUX, 100, "test6"),
- (Domain::SELINUX, 100, "test9"),
- ];
-
- let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES
- .iter()
- .map(|(domain, ns, alias)| {
- let entry =
- make_test_key_entry(&mut db, *domain, *ns, alias, None).unwrap_or_else(|e| {
- panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e)
- });
- (entry.id(), *ns)
- })
- .collect();
-
- for (domain, namespace) in
- &[(Domain::APP, 1i64), (Domain::APP, 2i64), (Domain::SELINUX, 100i64)]
- {
- let mut list_o_descriptors: Vec<KeyDescriptor> = LIST_O_ENTRIES
- .iter()
- .filter_map(|(domain, ns, alias)| match ns {
- ns if *ns == *namespace => Some(KeyDescriptor {
- domain: *domain,
- nspace: *ns,
- alias: Some(alias.to_string()),
- blob: None,
- }),
- _ => None,
- })
- .collect();
- list_o_descriptors.sort();
- let mut list_result = db.list_past_alias(*domain, *namespace, KeyType::Client, None)?;
- list_result.sort();
- assert_eq!(list_o_descriptors, list_result);
-
- let mut list_o_ids: Vec<i64> = list_o_descriptors
- .into_iter()
- .map(|d| {
- let (_, entry) = db
- .load_key_entry(
- &d,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- *namespace as u32,
- |_, _| Ok(()),
- )
- .unwrap();
- entry.id()
- })
- .collect();
- list_o_ids.sort_unstable();
- let mut loaded_entries: Vec<i64> = list_o_keys
- .iter()
- .filter_map(|(id, ns)| match ns {
- ns if *ns == *namespace => Some(*id),
- _ => None,
- })
- .collect();
- loaded_entries.sort_unstable();
- assert_eq!(list_o_ids, loaded_entries);
- }
- assert_eq!(
- Vec::<KeyDescriptor>::new(),
- db.list_past_alias(Domain::SELINUX, 101, KeyType::Client, None)?
- );
-
- Ok(())
- }
-
- // Helpers
-
- // Checks that the given result is an error containing the given string.
- fn check_result_is_error_containing_string<T>(result: Result<T>, target: &str) {
- let error_str = format!(
- "{:#?}",
- result.err().unwrap_or_else(|| panic!("Expected the error: {}", target))
- );
- assert!(
- error_str.contains(target),
- "The string \"{}\" should contain \"{}\"",
- error_str,
- target
- );
- }
-
- #[derive(Debug, PartialEq)]
- struct KeyEntryRow {
- id: i64,
- key_type: KeyType,
- domain: Option<Domain>,
- namespace: Option<i64>,
- alias: Option<String>,
- state: KeyLifeCycle,
- km_uuid: Option<Uuid>,
- }
-
- fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
- db.conn
- .prepare("SELECT * FROM persistent.keyentry;")?
- .query_map([], |row| {
- Ok(KeyEntryRow {
- id: row.get(0)?,
- key_type: row.get(1)?,
- domain: row.get::<_, Option<_>>(2)?.map(Domain),
- namespace: row.get(3)?,
- alias: row.get(4)?,
- state: row.get(5)?,
- km_uuid: row.get(6)?,
- })
- })?
- .map(|r| r.context("Could not read keyentry row."))
- .collect::<Result<Vec<_>>>()
- }
-
- fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
- make_test_params_with_sids(max_usage_count, &[42])
- }
-
- // Note: The parameters and SecurityLevel associations are nonsensical. This
- // collection is only used to check if the parameters are preserved as expected by the
- // database.
- fn make_test_params_with_sids(
- max_usage_count: Option<i32>,
- user_secure_ids: &[i64],
- ) -> Vec<KeyParameter> {
- let mut params = vec![
- KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
- KeyParameter::new(
- KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::Algorithm(Algorithm::RSA),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::TRUSTED_ENVIRONMENT),
- KeyParameter::new(
- KeyParameterValue::BlockMode(BlockMode::ECB),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::BlockMode(BlockMode::GCM),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::Digest(Digest::NONE), SecurityLevel::STRONGBOX),
- KeyParameter::new(
- KeyParameterValue::Digest(Digest::MD5),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::Digest(Digest::SHA_2_224),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::Digest(Digest::SHA_2_256),
- SecurityLevel::STRONGBOX,
- ),
- KeyParameter::new(
- KeyParameterValue::PaddingMode(PaddingMode::NONE),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::PaddingMode(PaddingMode::RSA_OAEP),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS),
- SecurityLevel::STRONGBOX,
- ),
- KeyParameter::new(
- KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::TRUSTED_ENVIRONMENT),
- KeyParameter::new(KeyParameterValue::MinMacLength(256), SecurityLevel::STRONGBOX),
- KeyParameter::new(
- KeyParameterValue::EcCurve(EcCurve::P_224),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::STRONGBOX),
- KeyParameter::new(
- KeyParameterValue::EcCurve(EcCurve::P_384),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::EcCurve(EcCurve::P_521),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::RSAPublicExponent(3),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::IncludeUniqueID,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::BootLoaderOnly, SecurityLevel::STRONGBOX),
- KeyParameter::new(KeyParameterValue::RollbackResistance, SecurityLevel::STRONGBOX),
- KeyParameter::new(
- KeyParameterValue::ActiveDateTime(1234567890),
- SecurityLevel::STRONGBOX,
- ),
- KeyParameter::new(
- KeyParameterValue::OriginationExpireDateTime(1234567890),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::UsageExpireDateTime(1234567890),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::MinSecondsBetweenOps(1234567890),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::MaxUsesPerBoot(1234567890),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
- KeyParameter::new(
- KeyParameterValue::NoAuthRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::PASSWORD),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::AuthTimeout(1234567890), SecurityLevel::SOFTWARE),
- KeyParameter::new(KeyParameterValue::AllowWhileOnBody, SecurityLevel::SOFTWARE),
- KeyParameter::new(
- KeyParameterValue::TrustedUserPresenceRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::TrustedConfirmationRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::UnlockedDeviceRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::ApplicationID(vec![1u8, 2u8, 3u8, 4u8]),
- SecurityLevel::SOFTWARE,
- ),
- KeyParameter::new(
- KeyParameterValue::ApplicationData(vec![4u8, 3u8, 2u8, 1u8]),
- SecurityLevel::SOFTWARE,
- ),
- KeyParameter::new(
- KeyParameterValue::CreationDateTime(12345677890),
- SecurityLevel::SOFTWARE,
- ),
- KeyParameter::new(
- KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::RootOfTrust(vec![3u8, 2u8, 1u8, 4u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(KeyParameterValue::OSVersion(1), SecurityLevel::TRUSTED_ENVIRONMENT),
- KeyParameter::new(KeyParameterValue::OSPatchLevel(2), SecurityLevel::SOFTWARE),
- KeyParameter::new(
- KeyParameterValue::UniqueID(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::SOFTWARE,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationChallenge(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationApplicationID(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdBrand(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdDevice(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdProduct(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdSerial(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdIMEI(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdSecondIMEI(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdMEID(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdManufacturer(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AttestationIdModel(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::VendorPatchLevel(3),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::BootPatchLevel(4),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::AssociatedData(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::Nonce(vec![4u8, 3u8, 1u8, 2u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::MacLength(256),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::ResetSinceIdRotation,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- KeyParameter::new(
- KeyParameterValue::ConfirmationToken(vec![5u8, 5u8, 5u8, 5u8]),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ),
- ];
- if let Some(value) = max_usage_count {
- params.push(KeyParameter::new(
- KeyParameterValue::UsageCountLimit(value),
- SecurityLevel::SOFTWARE,
- ));
- }
-
- for sid in user_secure_ids.iter() {
- params.push(KeyParameter::new(
- KeyParameterValue::UserSecureID(*sid),
- SecurityLevel::STRONGBOX,
- ));
- }
- params
- }
-
- pub fn make_test_key_entry(
- db: &mut KeystoreDB,
- domain: Domain,
- namespace: i64,
- alias: &str,
- max_usage_count: Option<i32>,
- ) -> Result<KeyIdGuard> {
- make_test_key_entry_with_sids(db, domain, namespace, alias, max_usage_count, &[42])
- }
-
- pub fn make_test_key_entry_with_sids(
- db: &mut KeystoreDB,
- domain: Domain,
- namespace: i64,
- alias: &str,
- max_usage_count: Option<i32>,
- sids: &[i64],
- ) -> Result<KeyIdGuard> {
- let key_id = db.create_key_entry(&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]));
- blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
- blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
- db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
- db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
-
- let params = make_test_params_with_sids(max_usage_count, sids);
- db.insert_keyparameter(&key_id, &params)?;
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
- rebind_alias(db, &key_id, alias, domain, namespace)?;
- Ok(key_id)
- }
-
- fn make_test_key_entry_test_vector(key_id: i64, max_usage_count: Option<i32>) -> KeyEntry {
- let params = make_test_params(max_usage_count);
-
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
- blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
- blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
- blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
-
- KeyEntry {
- id: key_id,
- key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
- cert: Some(TEST_CERT_BLOB.to_vec()),
- cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
- km_uuid: KEYSTORE_UUID,
- parameters: params,
- metadata,
- pure_cert: false,
- }
- }
-
- pub fn make_bootlevel_key_entry(
- db: &mut KeystoreDB,
- domain: Domain,
- namespace: i64,
- alias: &str,
- logical_only: bool,
- ) -> Result<KeyIdGuard> {
- let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
- let mut blob_metadata = BlobMetaData::new();
- if !logical_only {
- blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
- }
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
- db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
- db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
-
- let mut params = make_test_params(None);
- params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
-
- db.insert_keyparameter(&key_id, &params)?;
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
- rebind_alias(db, &key_id, alias, domain, namespace)?;
- Ok(key_id)
- }
-
- // Creates an app key that is marked as being superencrypted by the given
- // super key ID and that has the given authentication and unlocked device
- // parameters. This does not actually superencrypt the key blob.
- fn make_superencrypted_key_entry(
- db: &mut KeystoreDB,
- namespace: i64,
- alias: &str,
- requires_authentication: bool,
- requires_unlocked_device: bool,
- super_key_id: i64,
- ) -> Result<KeyIdGuard> {
- let domain = Domain::APP;
- let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
-
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
-
- let mut params = vec![];
- if requires_unlocked_device {
- params.push(KeyParameter::new(
- KeyParameterValue::UnlockedDeviceRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ));
- }
- if requires_authentication {
- params.push(KeyParameter::new(
- KeyParameterValue::UserSecureID(42),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ));
- }
- db.insert_keyparameter(&key_id, &params)?;
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
-
- rebind_alias(db, &key_id, alias, domain, namespace)?;
- Ok(key_id)
- }
-
- fn make_bootlevel_test_key_entry_test_vector(key_id: i64, logical_only: bool) -> KeyEntry {
- let mut params = make_test_params(None);
- params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
-
- let mut blob_metadata = BlobMetaData::new();
- if !logical_only {
- blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
- }
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
-
- KeyEntry {
- id: key_id,
- key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
- cert: Some(TEST_CERT_BLOB.to_vec()),
- cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
- km_uuid: KEYSTORE_UUID,
- parameters: params,
- metadata,
- pure_cert: false,
- }
- }
-
- fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
- let mut stmt = db.conn.prepare(
- "SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
- )?;
- let rows = stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle, Uuid), _, _>(
- [],
- |row| {
- Ok((
- row.get(0)?,
- row.get(1)?,
- row.get(2)?,
- row.get(3)?,
- row.get(4)?,
- row.get(5)?,
- row.get(6)?,
- ))
- },
- )?;
-
- println!("Key entry table rows:");
- for r in rows {
- let (id, key_type, domain, namespace, alias, state, km_uuid) = r.unwrap();
- println!(
- " id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?} KmUuid: {:?}",
- id, key_type, domain, namespace, alias, state, km_uuid
- );
- }
- Ok(())
- }
-
- fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> {
- let mut stmt = db
- .conn
- .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
- let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>([], |row| {
- Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
- })?;
-
- println!("Grant table rows:");
- for r in rows {
- let (id, gt, ki, av) = r.unwrap();
- println!(" id: {} grantee: {} key_id: {} access_vector: {}", id, gt, ki, av);
- }
- Ok(())
- }
-
- // Use a custom random number generator that repeats each number once.
- // This allows us to test repeated elements.
-
- thread_local! {
- static RANDOM_COUNTER: RefCell<i64> = RefCell::new(0);
- }
-
- fn reset_random() {
- RANDOM_COUNTER.with(|counter| {
- *counter.borrow_mut() = 0;
- })
- }
-
- pub fn random() -> i64 {
- RANDOM_COUNTER.with(|counter| {
- let result = *counter.borrow() / 2;
- *counter.borrow_mut() += 1;
- result
- })
- }
-
- #[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)?;
-
- make_test_key_entry(&mut db, Domain::APP, 210000, TEST_ALIAS, None)?;
- make_test_key_entry(&mut db, Domain::APP, 110000, TEST_ALIAS, None)?;
- db.unbind_keys_for_user(2, false)?;
-
- assert_eq!(1, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
- assert_eq!(0, db.list_past_alias(Domain::APP, 210000, KeyType::Client, None)?.len());
-
- db.unbind_keys_for_user(1, true)?;
- assert_eq!(0, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
-
- Ok(())
- }
-
- #[test]
- fn test_unbind_keys_for_user_removes_superkeys() -> Result<()> {
- let mut db = new_test_db()?;
- let super_key = keystore2_crypto::generate_aes256_key()?;
- let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
- let (encrypted_super_key, metadata) =
- SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
-
- let key_name_enc = SuperKeyType {
- alias: "test_super_key_1",
- algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
- name: "test_super_key_1",
- };
-
- let key_name_nonenc = SuperKeyType {
- alias: "test_super_key_2",
- algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
- name: "test_super_key_2",
- };
-
- // Install two super keys.
- db.store_super_key(
- 1,
- &key_name_nonenc,
- &super_key,
- &BlobMetaData::new(),
- &KeyMetaData::new(),
- )?;
- db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
-
- // Check that both can be found in the database.
- assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
-
- // Install the same keys for a different user.
- db.store_super_key(
- 2,
- &key_name_nonenc,
- &super_key,
- &BlobMetaData::new(),
- &KeyMetaData::new(),
- )?;
- db.store_super_key(2, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
-
- // Check that the second pair of keys can be found in the database.
- assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
-
- // Delete only encrypted keys.
- db.unbind_keys_for_user(1, true)?;
-
- // The encrypted superkey should be gone now.
- assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
-
- // Reinsert the encrypted key.
- db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
-
- // Check that both can be found in the database, again..
- assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
-
- // Delete all even unencrypted keys.
- db.unbind_keys_for_user(1, false)?;
-
- // Both should be gone now.
- assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_none());
-
- // Check that the second pair of keys was untouched.
- assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
-
- Ok(())
- }
-
- fn app_key_exists(db: &mut KeystoreDB, nspace: i64, alias: &str) -> Result<bool> {
- db.key_exists(Domain::APP, nspace, alias, KeyType::Client)
- }
-
- // Tests the unbind_auth_bound_keys_for_user() function.
- #[test]
- fn test_unbind_auth_bound_keys_for_user() -> Result<()> {
- let mut db = new_test_db()?;
- let user_id = 1;
- let nspace: i64 = (user_id * AID_USER_OFFSET).into();
- let other_user_id = 2;
- let other_user_nspace: i64 = (other_user_id * AID_USER_OFFSET).into();
- let super_key_type = &USER_AFTER_FIRST_UNLOCK_SUPER_KEY;
-
- // Create a superencryption key.
- let super_key = keystore2_crypto::generate_aes256_key()?;
- let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
- let (encrypted_super_key, blob_metadata) =
- SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
- db.store_super_key(
- user_id,
- super_key_type,
- &encrypted_super_key,
- &blob_metadata,
- &KeyMetaData::new(),
- )?;
- let super_key_id = db.load_super_key(super_key_type, user_id)?.unwrap().0 .0;
-
- // Store 4 superencrypted app keys, one for each possible combination of
- // (authentication required, unlocked device required).
- make_superencrypted_key_entry(&mut db, nspace, "noauth_noud", false, false, super_key_id)?;
- make_superencrypted_key_entry(&mut db, nspace, "noauth_ud", false, true, super_key_id)?;
- make_superencrypted_key_entry(&mut db, nspace, "auth_noud", true, false, super_key_id)?;
- make_superencrypted_key_entry(&mut db, nspace, "auth_ud", true, true, super_key_id)?;
- assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
- assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
- assert!(app_key_exists(&mut db, nspace, "auth_noud")?);
- assert!(app_key_exists(&mut db, nspace, "auth_ud")?);
-
- // Also store a key for a different user that requires authentication.
- make_superencrypted_key_entry(
- &mut db,
- other_user_nspace,
- "auth_ud",
- true,
- true,
- super_key_id,
- )?;
-
- db.unbind_auth_bound_keys_for_user(user_id)?;
-
- // Verify that only the user's app keys that require authentication were
- // deleted. Keys that require an unlocked device but not authentication
- // should *not* have been deleted, nor should the super key have been
- // deleted, nor should other users' keys have been deleted.
- assert!(db.load_super_key(super_key_type, user_id)?.is_some());
- assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
- assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
- assert!(!app_key_exists(&mut db, nspace, "auth_noud")?);
- assert!(!app_key_exists(&mut db, nspace, "auth_ud")?);
- assert!(app_key_exists(&mut db, other_user_nspace, "auth_ud")?);
-
- Ok(())
- }
-
- #[test]
- fn test_store_super_key() -> Result<()> {
- let mut db = new_test_db()?;
- let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
- let super_key = keystore2_crypto::generate_aes256_key()?;
- let secret_bytes = b"keystore2 is great.";
- let (encrypted_secret, iv, tag) =
- keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
-
- let (encrypted_super_key, metadata) =
- SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
- db.store_super_key(
- 1,
- &USER_AFTER_FIRST_UNLOCK_SUPER_KEY,
- &encrypted_super_key,
- &metadata,
- &KeyMetaData::new(),
- )?;
-
- // Check if super key exists.
- assert!(db.key_exists(
- Domain::APP,
- 1,
- USER_AFTER_FIRST_UNLOCK_SUPER_KEY.alias,
- KeyType::Super
- )?);
-
- let (_, key_entry) = db.load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, 1)?.unwrap();
- let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
- USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm,
- key_entry,
- &pw,
- None,
- )?;
-
- let decrypted_secret_bytes = loaded_super_key.decrypt(&encrypted_secret, &iv, &tag)?;
- assert_eq!(secret_bytes, &*decrypted_secret_bytes);
-
- Ok(())
- }
-
- fn get_valid_statsd_storage_types() -> Vec<MetricsStorage> {
- vec![
- MetricsStorage::KEY_ENTRY,
- MetricsStorage::KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
- MetricsStorage::BLOB_ENTRY,
- MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_PARAMETER,
- MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_METADATA,
- MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX,
- MetricsStorage::GRANT,
- MetricsStorage::AUTH_TOKEN,
- MetricsStorage::BLOB_METADATA,
- MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
- ]
- }
-
- /// Perform a simple check to ensure that we can query all the storage types
- /// that are supported by the DB. Check for reasonable values.
- #[test]
- fn test_query_all_valid_table_sizes() -> Result<()> {
- const PAGE_SIZE: i32 = 4096;
-
- let mut db = new_test_db()?;
-
- for t in get_valid_statsd_storage_types() {
- let stat = db.get_storage_stat(t)?;
- // AuthToken can be less than a page since it's in a btree, not sqlite
- // TODO(b/187474736) stop using if-let here
- if let MetricsStorage::AUTH_TOKEN = t {
- } else {
- assert!(stat.size >= PAGE_SIZE);
- }
- assert!(stat.size >= stat.unused_size);
- }
-
- Ok(())
- }
-
- fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, StorageStats> {
- get_valid_statsd_storage_types()
- .into_iter()
- .map(|t| (t.0, db.get_storage_stat(t).unwrap()))
- .collect()
- }
-
- fn assert_storage_increased(
- db: &mut KeystoreDB,
- increased_storage_types: Vec<MetricsStorage>,
- baseline: &mut BTreeMap<i32, StorageStats>,
- ) {
- for storage in increased_storage_types {
- // Verify the expected storage increased.
- let new = db.get_storage_stat(storage).unwrap();
- let old = &baseline[&storage.0];
- assert!(new.size >= old.size, "{}: {} >= {}", storage.0, new.size, old.size);
- assert!(
- new.unused_size <= old.unused_size,
- "{}: {} <= {}",
- storage.0,
- new.unused_size,
- old.unused_size
- );
-
- // Update the baseline with the new value so that it succeeds in the
- // later comparison.
- baseline.insert(storage.0, new);
- }
-
- // Get an updated map of the storage and verify there were no unexpected changes.
- let updated_stats = get_storage_stats_map(db);
- assert_eq!(updated_stats.len(), baseline.len());
-
- for &k in baseline.keys() {
- let stringify = |map: &BTreeMap<i32, StorageStats>| -> String {
- let mut s = String::new();
- for &k in map.keys() {
- writeln!(&mut s, " {}: {}, {}", &k, map[&k].size, map[&k].unused_size)
- .expect("string concat failed");
- }
- s
- };
-
- assert!(
- updated_stats[&k].size == baseline[&k].size
- && updated_stats[&k].unused_size == baseline[&k].unused_size,
- "updated_stats:\n{}\nbaseline:\n{}",
- stringify(&updated_stats),
- stringify(baseline)
- );
- }
- }
-
- #[test]
- fn test_verify_key_table_size_reporting() -> Result<()> {
- 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)?;
- assert_storage_increased(
- &mut db,
- vec![
- MetricsStorage::KEY_ENTRY,
- MetricsStorage::KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
- ],
- &mut working_stats,
- );
-
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
- db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), None)?;
- assert_storage_increased(
- &mut db,
- vec![
- MetricsStorage::BLOB_ENTRY,
- MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
- MetricsStorage::BLOB_METADATA,
- MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
- ],
- &mut working_stats,
- );
-
- let params = make_test_params(None);
- db.insert_keyparameter(&key_id, &params)?;
- assert_storage_increased(
- &mut db,
- vec![MetricsStorage::KEY_PARAMETER, MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX],
- &mut working_stats,
- );
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
- assert_storage_increased(
- &mut db,
- vec![MetricsStorage::KEY_METADATA, MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX],
- &mut working_stats,
- );
-
- let mut sum = 0;
- for stat in working_stats.values() {
- sum += stat.size;
- }
- let total = db.get_storage_stat(MetricsStorage::DATABASE)?.size;
- assert!(sum <= total, "Expected sum <= total. sum: {}, total: {}", sum, total);
-
- Ok(())
- }
-
- #[test]
- fn test_verify_auth_table_size_reporting() -> Result<()> {
- let mut db = new_test_db()?;
- let mut working_stats = get_storage_stats_map(&mut db);
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 456,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 10 },
- mac: b"mac".to_vec(),
- });
- assert_storage_increased(&mut db, vec![MetricsStorage::AUTH_TOKEN], &mut working_stats);
- Ok(())
- }
-
- #[test]
- fn test_verify_grant_table_size_reporting() -> Result<()> {
- const OWNER: i64 = 1;
- let mut db = new_test_db()?;
- make_test_key_entry(&mut db, Domain::APP, OWNER, TEST_ALIAS, None)?;
-
- let mut working_stats = get_storage_stats_map(&mut db);
- db.grant(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- OWNER as u32,
- 123,
- key_perm_set![KeyPerm::Use],
- |_, _| Ok(()),
- )?;
-
- assert_storage_increased(&mut db, vec![MetricsStorage::GRANT], &mut working_stats);
-
- Ok(())
- }
-
- #[test]
- fn find_auth_token_entry_returns_latest() -> Result<()> {
- let mut db = new_test_db()?;
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 456,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 10 },
- mac: b"mac0".to_vec(),
- });
- std::thread::sleep(std::time::Duration::from_millis(1));
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 457,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 12 },
- mac: b"mac1".to_vec(),
- });
- std::thread::sleep(std::time::Duration::from_millis(1));
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 458,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 3 },
- mac: b"mac2".to_vec(),
- });
- // 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());
- Ok(())
- }
-
- #[test]
- fn test_load_key_descriptor() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)?.0;
-
- let key = db.load_key_descriptor(key_id)?.unwrap();
-
- assert_eq!(key.domain, Domain::APP);
- assert_eq!(key.nspace, 1);
- assert_eq!(key.alias, Some(TEST_ALIAS.to_string()));
-
- // No such id
- assert_eq!(db.load_key_descriptor(key_id + 1)?, None);
- Ok(())
- }
-
- #[test]
- fn test_get_list_app_uids_for_sid() -> Result<()> {
- let uid: i32 = 1;
- let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
- let first_sid = 667;
- let second_sid = 669;
- let first_app_id: i64 = 123 + uid_offset;
- let second_app_id: i64 = 456 + uid_offset;
- let third_app_id: i64 = 789 + uid_offset;
- let unrelated_app_id: i64 = 1011 + uid_offset;
- let mut db = new_test_db()?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- first_app_id,
- TEST_ALIAS,
- None,
- &[first_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- second_app_id,
- "alias2",
- None,
- &[first_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- second_app_id,
- TEST_ALIAS,
- None,
- &[second_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- third_app_id,
- "alias3",
- None,
- &[second_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- unrelated_app_id,
- TEST_ALIAS,
- None,
- &[],
- )
- .context("test_get_list_app_uids_for_sid")?;
-
- let mut first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
- first_sid_apps.sort();
- assert_eq!(first_sid_apps, vec![first_app_id, second_app_id]);
- let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
- second_sid_apps.sort();
- assert_eq!(second_sid_apps, vec![second_app_id, third_app_id]);
- Ok(())
- }
-
- #[test]
- fn test_get_list_app_uids_with_multiple_sids() -> Result<()> {
- let uid: i32 = 1;
- let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
- let first_sid = 667;
- let second_sid = 669;
- let third_sid = 772;
- let first_app_id: i64 = 123 + uid_offset;
- let second_app_id: i64 = 456 + uid_offset;
- let mut db = new_test_db()?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- first_app_id,
- TEST_ALIAS,
- None,
- &[first_sid, second_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- second_app_id,
- "alias2",
- None,
- &[second_sid, third_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
-
- let first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
- assert_eq!(first_sid_apps, vec![first_app_id]);
-
- let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
- second_sid_apps.sort();
- assert_eq!(second_sid_apps, vec![first_app_id, second_app_id]);
-
- let third_sid_apps = db.get_app_uids_affected_by_sid(uid, third_sid)?;
- assert_eq!(third_sid_apps, vec![second_app_id]);
- Ok(())
- }
-}
diff --git a/keystore2/src/database/perboot.rs b/keystore2/src/database/perboot.rs
index 1b7c80d6..4727015f 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 PartialEq<AuthTokenEntryWrap> for AuthTokenEntryWrap {
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 @@ impl PerbootDB {
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/database/tests.rs b/keystore2/src/database/tests.rs
new file mode 100644
index 00000000..031d749b
--- /dev/null
+++ b/keystore2/src/database/tests.rs
@@ -0,0 +1,2528 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Database tests.
+
+use super::*;
+use crate::key_parameter::{
+ Algorithm, BlockMode, Digest, EcCurve, HardwareAuthenticatorType, KeyOrigin, KeyParameter,
+ KeyParameterValue, KeyPurpose, PaddingMode, SecurityLevel,
+};
+use crate::key_perm_set;
+use crate::permission::{KeyPerm, KeyPermSet};
+use crate::super_key::{SuperKeyManager, USER_AFTER_FIRST_UNLOCK_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType};
+use keystore2_test_utils::TempDir;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+ Timestamp::Timestamp,
+};
+use std::cell::RefCell;
+use std::collections::BTreeMap;
+use std::fmt::Write;
+use std::sync::atomic::{AtomicU8, Ordering};
+use std::sync::Arc;
+use std::thread;
+use std::time::{Duration, SystemTime};
+use crate::utils::AesGcm;
+#[cfg(disabled)]
+use std::time::Instant;
+
+pub fn new_test_db() -> Result<KeystoreDB> {
+ let conn = KeystoreDB::make_connection("file::memory:")?;
+
+ let mut db = KeystoreDB { conn, gc: None, perboot: Arc::new(perboot::PerbootDB::new()) };
+ db.with_transaction(Immediate("TX_new_test_db"), |tx| {
+ KeystoreDB::init_tables(tx).context("Failed to initialize tables.").no_gc()
+ })?;
+ Ok(db)
+}
+
+fn rebind_alias(
+ db: &mut KeystoreDB,
+ newid: &KeyIdGuard,
+ alias: &str,
+ domain: Domain,
+ namespace: i64,
+) -> Result<bool> {
+ db.with_transaction(Immediate("TX_rebind_alias"), |tx| {
+ KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace, KeyType::Client).no_gc()
+ })
+ .context(ks_err!())
+}
+
+#[test]
+fn datetime() -> Result<()> {
+ let conn = Connection::open_in_memory()?;
+ conn.execute("CREATE TABLE test (ts DATETIME);", [])?;
+ let now = SystemTime::now();
+ let duration = Duration::from_secs(1000);
+ let then = now.checked_sub(duration).unwrap();
+ let soon = now.checked_add(duration).unwrap();
+ conn.execute(
+ "INSERT INTO test (ts) VALUES (?), (?), (?);",
+ params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?],
+ )?;
+ let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?;
+ let mut rows = stmt.query([])?;
+ assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?);
+ assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?);
+ assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?);
+ assert!(rows.next()?.is_none());
+ assert!(DateTime::try_from(then)? < DateTime::try_from(now)?);
+ assert!(DateTime::try_from(then)? < DateTime::try_from(soon)?);
+ assert!(DateTime::try_from(now)? < DateTime::try_from(soon)?);
+ Ok(())
+}
+
+// Ensure that we're using the "injected" random function, not the real one.
+#[test]
+fn test_mocked_random() {
+ let rand1 = random();
+ let rand2 = random();
+ let rand3 = random();
+ if rand1 == rand2 {
+ assert_eq!(rand2 + 1, rand3);
+ } else {
+ assert_eq!(rand1 + 1, rand2);
+ assert_eq!(rand2, rand3);
+ }
+}
+
+// Test that we have the correct tables.
+#[test]
+fn test_tables() -> Result<()> {
+ let db = new_test_db()?;
+ let tables = db
+ .conn
+ .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")?
+ .query_map(params![], |row| row.get(0))?
+ .collect::<rusqlite::Result<Vec<String>>>()?;
+ assert_eq!(tables.len(), 6);
+ assert_eq!(tables[0], "blobentry");
+ assert_eq!(tables[1], "blobmetadata");
+ assert_eq!(tables[2], "grant");
+ assert_eq!(tables[3], "keyentry");
+ assert_eq!(tables[4], "keymetadata");
+ assert_eq!(tables[5], "keyparameter");
+ Ok(())
+}
+
+#[test]
+fn test_auth_token_table_invariant() -> Result<()> {
+ let mut db = new_test_db()?;
+ let auth_token1 = HardwareAuthToken {
+ challenge: i64::MAX,
+ userId: 200,
+ authenticatorId: 200,
+ authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+ timestamp: Timestamp { milliSeconds: 500 },
+ mac: String::from("mac").into_bytes(),
+ };
+ db.insert_auth_token(&auth_token1);
+ let auth_tokens_returned = get_auth_tokens(&db);
+ assert_eq!(auth_tokens_returned.len(), 1);
+
+ // insert another auth token with the same values for the columns in the UNIQUE constraint
+ // of the auth token table and different value for timestamp
+ let auth_token2 = HardwareAuthToken {
+ challenge: i64::MAX,
+ userId: 200,
+ authenticatorId: 200,
+ authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+ timestamp: Timestamp { milliSeconds: 600 },
+ mac: String::from("mac").into_bytes(),
+ };
+
+ db.insert_auth_token(&auth_token2);
+ let mut auth_tokens_returned = get_auth_tokens(&db);
+ assert_eq!(auth_tokens_returned.len(), 1);
+
+ if let Some(auth_token) = auth_tokens_returned.pop() {
+ assert_eq!(auth_token.auth_token.timestamp.milliSeconds, 600);
+ }
+
+ // insert another auth token with the different values for the columns in the UNIQUE
+ // constraint of the auth token table
+ let auth_token3 = HardwareAuthToken {
+ challenge: i64::MAX,
+ userId: 201,
+ authenticatorId: 200,
+ authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+ timestamp: Timestamp { milliSeconds: 600 },
+ mac: String::from("mac").into_bytes(),
+ };
+
+ db.insert_auth_token(&auth_token3);
+ let auth_tokens_returned = get_auth_tokens(&db);
+ assert_eq!(auth_tokens_returned.len(), 2);
+
+ Ok(())
+}
+
+// utility function for test_auth_token_table_invariant()
+fn get_auth_tokens(db: &KeystoreDB) -> Vec<AuthTokenEntry> {
+ 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(Immediate("TX_create_key_entry"), |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)?;
+
+ create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 1);
+
+ let db = KeystoreDB::new(temp_dir.path(), None)?;
+
+ let entries_new = get_keyentry(&db)?;
+ assert_eq!(entries, entries_new);
+ Ok(())
+}
+
+#[test]
+fn test_create_key_entry() -> Result<()> {
+ fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>, Uuid) {
+ (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref(), ke.km_uuid.unwrap())
+ }
+
+ let mut db = new_test_db()?;
+
+ 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);
+ assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None, KEYSTORE_UUID));
+ assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None, KEYSTORE_UUID));
+
+ // Test that we must pass in a valid Domain.
+ check_result_is_error_containing_string(
+ 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(
+ 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(
+ create_key_entry(&mut db, &Domain::KEY_ID, &104, KeyType::Client, &KEYSTORE_UUID),
+ &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_rebind_alias() -> Result<()> {
+ fn extractor(ke: &KeyEntryRow) -> (Option<Domain>, Option<i64>, Option<&str>, Option<Uuid>) {
+ (ke.domain, ke.namespace, ke.alias.as_deref(), ke.km_uuid)
+ }
+
+ let mut db = new_test_db()?;
+ 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!(extractor(&entries[0]), (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID)));
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID)));
+
+ // Test that the first call to rebind_alias sets the alias.
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[0].id), "foo", Domain::APP, 42)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(
+ extractor(&entries[0]),
+ (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+ );
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID)));
+
+ // Test that the second call to rebind_alias also empties the old one.
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[1].id), "foo", Domain::APP, 42)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
+ assert_eq!(
+ extractor(&entries[1]),
+ (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+ );
+
+ // Test that we must pass in a valid Domain.
+ check_result_is_error_containing_string(
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42),
+ &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
+ );
+ check_result_is_error_containing_string(
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42),
+ &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
+ );
+ check_result_is_error_containing_string(
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42),
+ &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
+ );
+
+ // Test that we correctly handle setting an alias for something that does not exist.
+ check_result_is_error_containing_string(
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::SELINUX, 42),
+ "Expected to update a single entry but instead updated 0",
+ );
+ // Test that we correctly abort the transaction in this case.
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
+ assert_eq!(
+ extractor(&entries[1]),
+ (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_grant_ungrant() -> Result<()> {
+ const CALLER_UID: u32 = 15;
+ const GRANTEE_UID: u32 = 12;
+ const SELINUX_NAMESPACE: i64 = 7;
+
+ let mut db = new_test_db()?;
+ db.conn.execute(
+ "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias, state, km_uuid)
+ VALUES (1, 0, 0, 15, 'key', 1, ?), (2, 0, 2, 7, 'yek', 1, ?);",
+ params![KEYSTORE_UUID, KEYSTORE_UUID],
+ )?;
+ let app_key = KeyDescriptor {
+ domain: super::Domain::APP,
+ nspace: 0,
+ alias: Some("key".to_string()),
+ blob: None,
+ };
+ const PVEC1: KeyPermSet = key_perm_set![KeyPerm::Use, KeyPerm::GetInfo];
+ const PVEC2: KeyPermSet = key_perm_set![KeyPerm::Use];
+
+ // Reset totally predictable random number generator in case we
+ // are not the first test running on this thread.
+ reset_random();
+ let next_random = 0i64;
+
+ let app_granted_key = db
+ .grant(&app_key, CALLER_UID, GRANTEE_UID, PVEC1, |k, a| {
+ assert_eq!(*a, PVEC1);
+ assert_eq!(
+ *k,
+ KeyDescriptor {
+ domain: super::Domain::APP,
+ // namespace must be set to the caller_uid.
+ nspace: CALLER_UID as i64,
+ alias: Some("key".to_string()),
+ blob: None,
+ }
+ );
+ Ok(())
+ })
+ .unwrap();
+
+ assert_eq!(
+ app_granted_key,
+ KeyDescriptor {
+ domain: super::Domain::GRANT,
+ // The grantid is next_random due to the mock random number generator.
+ nspace: next_random,
+ alias: None,
+ blob: None,
+ }
+ );
+
+ let selinux_key = KeyDescriptor {
+ domain: super::Domain::SELINUX,
+ nspace: SELINUX_NAMESPACE,
+ alias: Some("yek".to_string()),
+ blob: None,
+ };
+
+ let selinux_granted_key = db
+ .grant(&selinux_key, CALLER_UID, 12, PVEC1, |k, a| {
+ assert_eq!(*a, PVEC1);
+ assert_eq!(
+ *k,
+ KeyDescriptor {
+ domain: super::Domain::SELINUX,
+ // namespace must be the supplied SELinux
+ // namespace.
+ nspace: SELINUX_NAMESPACE,
+ alias: Some("yek".to_string()),
+ blob: None,
+ }
+ );
+ Ok(())
+ })
+ .unwrap();
+
+ assert_eq!(
+ selinux_granted_key,
+ KeyDescriptor {
+ domain: super::Domain::GRANT,
+ // The grantid is next_random + 1 due to the mock random number generator.
+ nspace: next_random + 1,
+ alias: None,
+ blob: None,
+ }
+ );
+
+ // This should update the existing grant with PVEC2.
+ let selinux_granted_key = db
+ .grant(&selinux_key, CALLER_UID, 12, PVEC2, |k, a| {
+ assert_eq!(*a, PVEC2);
+ assert_eq!(
+ *k,
+ KeyDescriptor {
+ domain: super::Domain::SELINUX,
+ // namespace must be the supplied SELinux
+ // namespace.
+ nspace: SELINUX_NAMESPACE,
+ alias: Some("yek".to_string()),
+ blob: None,
+ }
+ );
+ Ok(())
+ })
+ .unwrap();
+
+ assert_eq!(
+ selinux_granted_key,
+ KeyDescriptor {
+ domain: super::Domain::GRANT,
+ // Same grant id as before. The entry was only updated.
+ nspace: next_random + 1,
+ alias: None,
+ blob: None,
+ }
+ );
+
+ {
+ // Limiting scope of stmt, because it borrows db.
+ let mut stmt = db
+ .conn
+ .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
+ let mut rows = stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>([], |row| {
+ Ok((row.get(0)?, row.get(1)?, row.get(2)?, KeyPermSet::from(row.get::<_, i32>(3)?)))
+ })?;
+
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (next_random, GRANTEE_UID, 1, PVEC1));
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (next_random + 1, GRANTEE_UID, 2, PVEC2));
+ assert!(rows.next().is_none());
+ }
+
+ debug_dump_keyentry_table(&mut db)?;
+ println!("app_key {:?}", app_key);
+ println!("selinux_key {:?}", selinux_key);
+
+ db.ungrant(&app_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
+ db.ungrant(&selinux_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
+
+ Ok(())
+}
+
+static TEST_KEY_BLOB: &[u8] = b"my test blob";
+static TEST_CERT_BLOB: &[u8] = b"my test cert";
+static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain";
+
+#[test]
+fn test_set_blob() -> Result<()> {
+ let key_id = KEY_ID_LOCK.get(3000);
+ let mut db = new_test_db()?;
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+ db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+ db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+ drop(key_id);
+
+ let mut stmt = db.conn.prepare(
+ "SELECT subcomponent_type, keyentryid, blob, id FROM persistent.blobentry
+ ORDER BY subcomponent_type ASC;",
+ )?;
+ let mut rows = stmt.query_map::<((SubComponentType, i64, Vec<u8>), i64), _, _>([], |row| {
+ Ok(((row.get(0)?, row.get(1)?, row.get(2)?), row.get(3)?))
+ })?;
+ let (r, id) = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::KEY_BLOB, 3000, TEST_KEY_BLOB.to_vec()));
+ let (r, _) = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::CERT, 3000, TEST_CERT_BLOB.to_vec()));
+ let (r, _) = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::CERT_CHAIN, 3000, TEST_CERT_CHAIN_BLOB.to_vec()));
+
+ drop(rows);
+ drop(stmt);
+
+ assert_eq!(
+ db.with_transaction(Immediate("TX_test"), |tx| {
+ BlobMetaData::load_from_db(id, tx).no_gc()
+ })
+ .expect("Should find blob metadata."),
+ blob_metadata
+ );
+ Ok(())
+}
+
+static TEST_ALIAS: &str = "my super duper key";
+
+#[test]
+fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_domain_app")?
+ .0;
+ let (_key_guard, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ db.unbind_key(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ 1,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_insert_and_load_certificate_entry_domain_app() -> Result<()> {
+ let mut db = new_test_db()?;
+
+ db.store_new_certificate(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ TEST_CERT_BLOB,
+ &KEYSTORE_UUID,
+ )
+ .expect("Trying to insert cert.");
+
+ let (_key_guard, mut key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::PUBLIC,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .expect("Trying to read certificate entry.");
+
+ assert!(key_entry.pure_cert());
+ assert!(key_entry.cert().is_none());
+ assert_eq!(key_entry.take_cert_chain(), Some(TEST_CERT_BLOB.to_vec()));
+
+ db.unbind_key(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ 1,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_domain_selinux")?
+ .0;
+ let (_key_guard, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ db.unbind_key(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ 1,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_domain_key_id")?
+ .0;
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ db.unbind_key(
+ &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+ KeyType::Client,
+ 1,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_check_and_update_key_usage_count_with_limited_use_key() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(123))
+ .context("test_check_and_update_key_usage_count_with_limited_use_key")?
+ .0;
+ // Update the usage count of the limited use key.
+ db.check_and_update_key_usage_count(key_id)?;
+
+ let (_key_guard, key_entry) = db.load_key_entry(
+ &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )?;
+
+ // The usage count is decremented now.
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, Some(122)));
+
+ Ok(())
+}
+
+#[test]
+fn test_check_and_update_key_usage_count_with_exhausted_limited_use_key() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(1))
+ .context("test_check_and_update_key_usage_count_with_exhausted_limited_use_key")?
+ .0;
+ // Update the usage count of the limited use key.
+ db.check_and_update_key_usage_count(key_id).expect(concat!(
+ "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
+ "This should succeed."
+ ));
+
+ // Try to update the exhausted limited use key.
+ let e = db.check_and_update_key_usage_count(key_id).expect_err(concat!(
+ "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
+ "This should fail."
+ ));
+ assert_eq!(
+ &KsError::Km(ErrorCode::INVALID_KEY_BLOB),
+ e.root_cause().downcast_ref::<KsError>().unwrap()
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant")?
+ .0;
+
+ let granted_key = db
+ .grant(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ 1,
+ 2,
+ key_perm_set![KeyPerm::Use],
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+
+ debug_dump_grant_table(&mut db)?;
+
+ let (_key_guard, key_entry) = db
+ .load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::BOTH, 2, |k, av| {
+ assert_eq!(Domain::GRANT, k.domain);
+ assert!(av.unwrap().includes(KeyPerm::Use));
+ Ok(())
+ })
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ db.unbind_key(&granted_key, KeyType::Client, 2, |_, _| Ok(())).unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::NONE, 2, |_k, _av| Ok(
+ ()
+ ),)
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+// This test attempts to load a key by key id while the caller is not the owner
+// but a grant exists for the given key and the caller.
+#[test]
+fn test_insert_and_load_full_keyentry_from_grant_by_key_id() -> Result<()> {
+ let mut db = new_test_db()?;
+ const OWNER_UID: u32 = 1u32;
+ const GRANTEE_UID: u32 = 2u32;
+ const SOMEONE_ELSE_UID: u32 = 3u32;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, OWNER_UID as i64, TEST_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?
+ .0;
+
+ db.grant(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ OWNER_UID,
+ GRANTEE_UID,
+ key_perm_set![KeyPerm::Use],
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+
+ debug_dump_grant_table(&mut db)?;
+
+ let id_descriptor =
+ KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, ..Default::default() };
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &id_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ GRANTEE_UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(OWNER_UID as i64, k.nspace);
+ assert!(av.unwrap().includes(KeyPerm::Use));
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &id_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ SOMEONE_ELSE_UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(OWNER_UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ db.unbind_key(&id_descriptor, KeyType::Client, OWNER_UID, |_, _| Ok(())).unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &id_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ GRANTEE_UID,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+// Creates a key migrates it to a different location and then tries to access it by the old
+// and new location.
+#[test]
+fn test_migrate_key_app_to_app() -> Result<()> {
+ let mut db = new_test_db()?;
+ const SOURCE_UID: u32 = 1u32;
+ const DESTINATION_UID: u32 = 2u32;
+ static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ let key_id_guard =
+ make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+ let source_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(SOURCE_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let destination_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(DESTINATION_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let key_id = key_id_guard.id();
+
+ db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| Ok(()))
+ .unwrap();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &destination_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ DESTINATION_UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(DESTINATION_UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &source_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ SOURCE_UID,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+// Creates a key migrates it to a different location and then tries to access it by the old
+// and new location.
+#[test]
+fn test_migrate_key_app_to_selinux() -> Result<()> {
+ let mut db = new_test_db()?;
+ const SOURCE_UID: u32 = 1u32;
+ const DESTINATION_UID: u32 = 2u32;
+ const DESTINATION_NAMESPACE: i64 = 1000i64;
+ static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ let key_id_guard =
+ make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+ let source_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(SOURCE_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let destination_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: DESTINATION_NAMESPACE,
+ alias: Some(DESTINATION_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let key_id = key_id_guard.id();
+
+ db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| Ok(()))
+ .unwrap();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &destination_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ DESTINATION_UID,
+ |k, av| {
+ assert_eq!(Domain::SELINUX, k.domain);
+ assert_eq!(DESTINATION_NAMESPACE, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &source_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ SOURCE_UID,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+// Creates two keys and tries to migrate the first to the location of the second which
+// is expected to fail.
+#[test]
+fn test_migrate_key_destination_occupied() -> Result<()> {
+ let mut db = new_test_db()?;
+ const SOURCE_UID: u32 = 1u32;
+ const DESTINATION_UID: u32 = 2u32;
+ static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ let key_id_guard =
+ make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+ make_test_key_entry(&mut db, Domain::APP, DESTINATION_UID as i64, DESTINATION_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+ let destination_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(DESTINATION_ALIAS.to_string()),
+ blob: None,
+ };
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::INVALID_ARGUMENT)),
+ db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| Ok(
+ ()
+ ))
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_upgrade_0_to_1() {
+ const ALIAS1: &str = "test_upgrade_0_to_1_1";
+ const ALIAS2: &str = "test_upgrade_0_to_1_2";
+ const ALIAS3: &str = "test_upgrade_0_to_1_3";
+ const UID: u32 = 33;
+ let temp_dir = Arc::new(TempDir::new("test_upgrade_0_to_1").unwrap());
+ let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+ let key_id_untouched1 =
+ make_test_key_entry(&mut db, Domain::APP, UID as i64, ALIAS1, None).unwrap().id();
+ let key_id_untouched2 =
+ make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS2, false).unwrap().id();
+ let key_id_deleted =
+ make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS3, true).unwrap().id();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS1.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS2.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS3.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_deleted, true));
+
+ db.with_transaction(Immediate("TX_test"), |tx| KeystoreDB::from_0_to_1(tx).no_gc()).unwrap();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS1.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS2.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS3.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+}
+
+static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
+
+#[test]
+fn test_insert_and_load_full_keyentry_domain_app_concurrently() -> Result<()> {
+ let handle = {
+ let temp_dir = Arc::new(TempDir::new("id_lock_test")?);
+ let temp_dir_clone = temp_dir.clone();
+ let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_domain_app")?
+ .0;
+ let (_key_guard, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 33,
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+ let state = Arc::new(AtomicU8::new(1));
+ let state2 = state.clone();
+
+ // Spawning a second thread that attempts to acquire the key id lock
+ // for the same key as the primary thread. The primary thread then
+ // waits, thereby forcing the secondary thread into the second stage
+ // of acquiring the lock (see KEY ID LOCK 2/2 above).
+ // The test succeeds if the secondary thread observes the transition
+ // of `state` from 1 to 2, despite having a whole second to overtake
+ // the primary thread.
+ let handle = thread::spawn(move || {
+ let temp_dir = temp_dir_clone;
+ let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+ assert!(db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 33,
+ |_k, _av| Ok(()),
+ )
+ .is_ok());
+ // We should only see a 2 here because we can only return
+ // from load_key_entry when the `_key_guard` expires,
+ // which happens at the end of the scope.
+ assert_eq!(2, state2.load(Ordering::Relaxed));
+ });
+
+ thread::sleep(std::time::Duration::from_millis(1000));
+
+ assert_eq!(Ok(1), state.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed));
+
+ // Return the handle from this scope so we can join with the
+ // secondary thread after the key id lock has expired.
+ handle
+ // This is where the `_key_guard` goes out of scope,
+ // which is the reason for concurrent load_key_entry on the same key
+ // to unblock.
+ };
+ // Join with the secondary thread and unwrap, to propagate failing asserts to the
+ // main test thread. We will not see failing asserts in secondary threads otherwise.
+ handle.join().unwrap();
+ Ok(())
+}
+
+#[test]
+fn test_database_busy_error_code() {
+ let temp_dir =
+ TempDir::new("test_database_busy_error_code_").expect("Failed to create temp dir.");
+
+ let mut db1 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database1.");
+ let mut db2 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database2.");
+
+ let _tx1 = db1
+ .conn
+ .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
+ .expect("Failed to create first transaction.");
+
+ let error = db2
+ .conn
+ .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
+ .context("Transaction begin failed.")
+ .expect_err("This should fail.");
+ let root_cause = error.root_cause();
+ if let Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. }) =
+ root_cause.downcast_ref::<rusqlite::ffi::Error>()
+ {
+ return;
+ }
+ panic!(
+ "Unexpected error {:?} \n{:?} \n{:?}",
+ error,
+ root_cause,
+ root_cause.downcast_ref::<rusqlite::ffi::Error>()
+ )
+}
+
+#[cfg(disabled)]
+#[test]
+fn test_large_number_of_concurrent_db_manipulations() -> Result<()> {
+ let temp_dir = Arc::new(
+ TempDir::new("test_large_number_of_concurrent_db_manipulations_")
+ .expect("Failed to create temp dir."),
+ );
+
+ let test_begin = Instant::now();
+
+ const KEY_COUNT: u32 = 500u32;
+ let mut db =
+ new_test_db_with_gc(temp_dir.path(), |_, _| Ok(())).expect("Failed to open database.");
+ const OPEN_DB_COUNT: u32 = 50u32;
+
+ let mut actual_key_count = KEY_COUNT;
+ // First insert KEY_COUNT keys.
+ for count in 0..KEY_COUNT {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
+ actual_key_count = count;
+ break;
+ }
+ let alias = format!("test_alias_{}", count);
+ make_test_key_entry(&mut db, Domain::APP, 1, &alias, None)
+ .expect("Failed to make key entry.");
+ }
+
+ // Insert more keys from a different thread and into a different namespace.
+ let temp_dir1 = temp_dir.clone();
+ let handle1 = thread::spawn(move || {
+ let mut db =
+ new_test_db_with_gc(temp_dir1.path(), |_, _| Ok(())).expect("Failed to open database.");
+
+ for count in 0..actual_key_count {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let alias = format!("test_alias_{}", count);
+ make_test_key_entry(&mut db, Domain::APP, 2, &alias, None)
+ .expect("Failed to make key entry.");
+ }
+
+ // then unbind them again.
+ for count in 0..actual_key_count {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let key = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(format!("test_alias_{}", count)),
+ blob: None,
+ };
+ db.unbind_key(&key, KeyType::Client, 2, |_, _| Ok(())).expect("Unbind Failed.");
+ }
+ });
+
+ // And start unbinding the first set of keys.
+ let temp_dir2 = temp_dir.clone();
+ let handle2 = thread::spawn(move || {
+ let mut db =
+ new_test_db_with_gc(temp_dir2.path(), |_, _| Ok(())).expect("Failed to open database.");
+
+ for count in 0..actual_key_count {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let key = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(format!("test_alias_{}", count)),
+ blob: None,
+ };
+ db.unbind_key(&key, KeyType::Client, 1, |_, _| Ok(())).expect("Unbind Failed.");
+ }
+ });
+
+ // While a lot of inserting and deleting is going on we have to open database connections
+ // successfully and use them.
+ // This clone is not redundant, because temp_dir needs to be kept alive until db goes
+ // out of scope.
+ #[allow(clippy::redundant_clone)]
+ let temp_dir4 = temp_dir.clone();
+ let handle4 = thread::spawn(move || {
+ for count in 0..OPEN_DB_COUNT {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let mut db = new_test_db_with_gc(temp_dir4.path(), |_, _| Ok(()))
+ .expect("Failed to open database.");
+
+ let alias = format!("test_alias_{}", count);
+ make_test_key_entry(&mut db, Domain::APP, 3, &alias, None)
+ .expect("Failed to make key entry.");
+ let key =
+ KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None };
+ db.unbind_key(&key, KeyType::Client, 3, |_, _| Ok(())).expect("Unbind Failed.");
+ }
+ });
+
+ handle1.join().expect("Thread 1 panicked.");
+ handle2.join().expect("Thread 2 panicked.");
+ handle4.join().expect("Thread 4 panicked.");
+
+ Ok(())
+}
+
+#[test]
+fn list() -> Result<()> {
+ let temp_dir = TempDir::new("list_test")?;
+ let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+ static LIST_O_ENTRIES: &[(Domain, i64, &str)] = &[
+ (Domain::APP, 1, "test1"),
+ (Domain::APP, 1, "test2"),
+ (Domain::APP, 1, "test3"),
+ (Domain::APP, 1, "test4"),
+ (Domain::APP, 1, "test5"),
+ (Domain::APP, 1, "test6"),
+ (Domain::APP, 1, "test7"),
+ (Domain::APP, 2, "test1"),
+ (Domain::APP, 2, "test2"),
+ (Domain::APP, 2, "test3"),
+ (Domain::APP, 2, "test4"),
+ (Domain::APP, 2, "test5"),
+ (Domain::APP, 2, "test6"),
+ (Domain::APP, 2, "test8"),
+ (Domain::SELINUX, 100, "test1"),
+ (Domain::SELINUX, 100, "test2"),
+ (Domain::SELINUX, 100, "test3"),
+ (Domain::SELINUX, 100, "test4"),
+ (Domain::SELINUX, 100, "test5"),
+ (Domain::SELINUX, 100, "test6"),
+ (Domain::SELINUX, 100, "test9"),
+ ];
+
+ let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES
+ .iter()
+ .map(|(domain, ns, alias)| {
+ let entry =
+ make_test_key_entry(&mut db, *domain, *ns, alias, None).unwrap_or_else(|e| {
+ panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e)
+ });
+ (entry.id(), *ns)
+ })
+ .collect();
+
+ for (domain, namespace) in
+ &[(Domain::APP, 1i64), (Domain::APP, 2i64), (Domain::SELINUX, 100i64)]
+ {
+ let mut list_o_descriptors: Vec<KeyDescriptor> = LIST_O_ENTRIES
+ .iter()
+ .filter_map(|(domain, ns, alias)| match ns {
+ ns if *ns == *namespace => Some(KeyDescriptor {
+ domain: *domain,
+ nspace: *ns,
+ alias: Some(alias.to_string()),
+ blob: None,
+ }),
+ _ => None,
+ })
+ .collect();
+ list_o_descriptors.sort();
+ let mut list_result = db.list_past_alias(*domain, *namespace, KeyType::Client, None)?;
+ list_result.sort();
+ assert_eq!(list_o_descriptors, list_result);
+
+ let mut list_o_ids: Vec<i64> = list_o_descriptors
+ .into_iter()
+ .map(|d| {
+ let (_, entry) = db
+ .load_key_entry(
+ &d,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ *namespace as u32,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+ entry.id()
+ })
+ .collect();
+ list_o_ids.sort_unstable();
+ let mut loaded_entries: Vec<i64> = list_o_keys
+ .iter()
+ .filter_map(|(id, ns)| match ns {
+ ns if *ns == *namespace => Some(*id),
+ _ => None,
+ })
+ .collect();
+ loaded_entries.sort_unstable();
+ assert_eq!(list_o_ids, loaded_entries);
+ }
+ assert_eq!(
+ Vec::<KeyDescriptor>::new(),
+ db.list_past_alias(Domain::SELINUX, 101, KeyType::Client, None)?
+ );
+
+ Ok(())
+}
+
+// Helpers
+
+// Checks that the given result is an error containing the given string.
+fn check_result_is_error_containing_string<T>(result: Result<T>, target: &str) {
+ let error_str =
+ format!("{:#?}", result.err().unwrap_or_else(|| panic!("Expected the error: {}", target)));
+ assert!(
+ error_str.contains(target),
+ "The string \"{}\" should contain \"{}\"",
+ error_str,
+ target
+ );
+}
+
+#[derive(Debug, PartialEq)]
+struct KeyEntryRow {
+ id: i64,
+ key_type: KeyType,
+ domain: Option<Domain>,
+ namespace: Option<i64>,
+ alias: Option<String>,
+ state: KeyLifeCycle,
+ km_uuid: Option<Uuid>,
+}
+
+fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
+ db.conn
+ .prepare("SELECT * FROM persistent.keyentry;")?
+ .query_map([], |row| {
+ Ok(KeyEntryRow {
+ id: row.get(0)?,
+ key_type: row.get(1)?,
+ domain: row.get::<_, Option<_>>(2)?.map(Domain),
+ namespace: row.get(3)?,
+ alias: row.get(4)?,
+ state: row.get(5)?,
+ km_uuid: row.get(6)?,
+ })
+ })?
+ .map(|r| r.context("Could not read keyentry row."))
+ .collect::<Result<Vec<_>>>()
+}
+
+fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
+ make_test_params_with_sids(max_usage_count, &[42])
+}
+
+// Note: The parameters and SecurityLevel associations are nonsensical. This
+// collection is only used to check if the parameters are preserved as expected by the
+// database.
+fn make_test_params_with_sids(
+ max_usage_count: Option<i32>,
+ user_secure_ids: &[i64],
+) -> Vec<KeyParameter> {
+ let mut params = vec![
+ KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(
+ KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Algorithm(Algorithm::RSA),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(
+ KeyParameterValue::BlockMode(BlockMode::ECB),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::BlockMode(BlockMode::GCM),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::Digest(Digest::NONE), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::Digest(Digest::MD5),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Digest(Digest::SHA_2_224),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::Digest(Digest::SHA_2_256), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::NONE),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::RSA_OAEP),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS),
+ SecurityLevel::STRONGBOX,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(KeyParameterValue::MinMacLength(256), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::EcCurve(EcCurve::P_224),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::EcCurve(EcCurve::P_384),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::EcCurve(EcCurve::P_521),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::RSAPublicExponent(3),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::IncludeUniqueID, SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(KeyParameterValue::BootLoaderOnly, SecurityLevel::STRONGBOX),
+ KeyParameter::new(KeyParameterValue::RollbackResistance, SecurityLevel::STRONGBOX),
+ KeyParameter::new(KeyParameterValue::ActiveDateTime(1234567890), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::OriginationExpireDateTime(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::UsageExpireDateTime(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::MinSecondsBetweenOps(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::MaxUsesPerBoot(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
+ KeyParameter::new(KeyParameterValue::NoAuthRequired, SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(
+ KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::PASSWORD),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::AuthTimeout(1234567890), SecurityLevel::SOFTWARE),
+ KeyParameter::new(KeyParameterValue::AllowWhileOnBody, SecurityLevel::SOFTWARE),
+ KeyParameter::new(
+ KeyParameterValue::TrustedUserPresenceRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::TrustedConfirmationRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::UnlockedDeviceRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ApplicationID(vec![1u8, 2u8, 3u8, 4u8]),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ApplicationData(vec![4u8, 3u8, 2u8, 1u8]),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::CreationDateTime(12345677890),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::RootOfTrust(vec![3u8, 2u8, 1u8, 4u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::OSVersion(1), SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(KeyParameterValue::OSPatchLevel(2), SecurityLevel::SOFTWARE),
+ KeyParameter::new(
+ KeyParameterValue::UniqueID(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationChallenge(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationApplicationID(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdBrand(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdDevice(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdProduct(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdSerial(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdIMEI(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdSecondIMEI(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdMEID(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdManufacturer(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdModel(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::VendorPatchLevel(3),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::BootPatchLevel(4), SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(
+ KeyParameterValue::AssociatedData(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Nonce(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::MacLength(256), SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(
+ KeyParameterValue::ResetSinceIdRotation,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ConfirmationToken(vec![5u8, 5u8, 5u8, 5u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ ];
+ if let Some(value) = max_usage_count {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UsageCountLimit(value),
+ SecurityLevel::SOFTWARE,
+ ));
+ }
+
+ for sid in user_secure_ids.iter() {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UserSecureID(*sid),
+ SecurityLevel::STRONGBOX,
+ ));
+ }
+ params
+}
+
+pub fn make_test_key_entry(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ max_usage_count: Option<i32>,
+) -> Result<KeyIdGuard> {
+ make_test_key_entry_with_sids(db, domain, namespace, alias, max_usage_count, &[42])
+}
+
+pub fn make_test_key_entry_with_sids(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ max_usage_count: Option<i32>,
+ sids: &[i64],
+) -> Result<KeyIdGuard> {
+ 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]));
+ blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
+ blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+ db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+ db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+
+ let params = make_test_params_with_sids(max_usage_count, sids);
+ db.insert_keyparameter(&key_id, &params)?;
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+ rebind_alias(db, &key_id, alias, domain, namespace)?;
+ Ok(key_id)
+}
+
+fn make_test_key_entry_test_vector(key_id: i64, max_usage_count: Option<i32>) -> KeyEntry {
+ let params = make_test_params(max_usage_count);
+
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+ blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
+ blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
+ blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+
+ KeyEntry {
+ id: key_id,
+ key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
+ cert: Some(TEST_CERT_BLOB.to_vec()),
+ cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+ km_uuid: KEYSTORE_UUID,
+ parameters: params,
+ metadata,
+ pure_cert: false,
+ }
+}
+
+pub fn make_bootlevel_key_entry(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ logical_only: bool,
+) -> Result<KeyIdGuard> {
+ 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));
+ }
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+ db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+ db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+
+ let mut params = make_test_params(None);
+ params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
+
+ db.insert_keyparameter(&key_id, &params)?;
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+ rebind_alias(db, &key_id, alias, domain, namespace)?;
+ Ok(key_id)
+}
+
+// Creates an app key that is marked as being superencrypted by the given
+// super key ID and that has the given authentication and unlocked device
+// parameters. This does not actually superencrypt the key blob.
+fn make_superencrypted_key_entry(
+ db: &mut KeystoreDB,
+ namespace: i64,
+ alias: &str,
+ requires_authentication: bool,
+ requires_unlocked_device: bool,
+ super_key_id: i64,
+) -> Result<KeyIdGuard> {
+ let domain = Domain::APP;
+ 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));
+ blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+
+ let mut params = vec![];
+ if requires_unlocked_device {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UnlockedDeviceRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ));
+ }
+ if requires_authentication {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UserSecureID(42),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ));
+ }
+ db.insert_keyparameter(&key_id, &params)?;
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+
+ rebind_alias(db, &key_id, alias, domain, namespace)?;
+ Ok(key_id)
+}
+
+fn make_bootlevel_test_key_entry_test_vector(key_id: i64, logical_only: bool) -> KeyEntry {
+ let mut params = make_test_params(None);
+ params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
+
+ let mut blob_metadata = BlobMetaData::new();
+ if !logical_only {
+ blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
+ }
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+
+ KeyEntry {
+ id: key_id,
+ key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
+ cert: Some(TEST_CERT_BLOB.to_vec()),
+ cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+ km_uuid: KEYSTORE_UUID,
+ parameters: params,
+ metadata,
+ pure_cert: false,
+ }
+}
+
+fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
+ let mut stmt = db.conn.prepare(
+ "SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
+ )?;
+ let rows =
+ stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle, Uuid), _, _>([], |row| {
+ Ok((
+ row.get(0)?,
+ row.get(1)?,
+ row.get(2)?,
+ row.get(3)?,
+ row.get(4)?,
+ row.get(5)?,
+ row.get(6)?,
+ ))
+ })?;
+
+ println!("Key entry table rows:");
+ for r in rows {
+ let (id, key_type, domain, namespace, alias, state, km_uuid) = r.unwrap();
+ println!(
+ " id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?} KmUuid: {:?}",
+ id, key_type, domain, namespace, alias, state, km_uuid
+ );
+ }
+ Ok(())
+}
+
+fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> {
+ let mut stmt =
+ db.conn.prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
+ let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>([], |row| {
+ Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
+ })?;
+
+ println!("Grant table rows:");
+ for r in rows {
+ let (id, gt, ki, av) = r.unwrap();
+ println!(" id: {} grantee: {} key_id: {} access_vector: {}", id, gt, ki, av);
+ }
+ Ok(())
+}
+
+// Use a custom random number generator that repeats each number once.
+// This allows us to test repeated elements.
+
+thread_local! {
+ static RANDOM_COUNTER: RefCell<i64> = const { RefCell::new(0) };
+}
+
+fn reset_random() {
+ RANDOM_COUNTER.with(|counter| {
+ *counter.borrow_mut() = 0;
+ })
+}
+
+pub fn random() -> i64 {
+ RANDOM_COUNTER.with(|counter| {
+ let result = *counter.borrow() / 2;
+ *counter.borrow_mut() += 1;
+ result
+ })
+}
+
+#[test]
+fn test_unbind_keys_for_user() -> Result<()> {
+ let mut db = new_test_db()?;
+ db.unbind_keys_for_user(1)?;
+
+ make_test_key_entry(&mut db, Domain::APP, 210000, TEST_ALIAS, None)?;
+ make_test_key_entry(&mut db, Domain::APP, 110000, TEST_ALIAS, None)?;
+ db.unbind_keys_for_user(2)?;
+
+ assert_eq!(1, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
+ assert_eq!(0, db.list_past_alias(Domain::APP, 210000, KeyType::Client, None)?.len());
+
+ db.unbind_keys_for_user(1)?;
+ assert_eq!(0, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
+
+ Ok(())
+}
+
+#[test]
+fn test_unbind_keys_for_user_removes_superkeys() -> Result<()> {
+ let mut db = new_test_db()?;
+ let super_key = keystore2_crypto::generate_aes256_key()?;
+ let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+ let (encrypted_super_key, metadata) = SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+
+ let key_name_enc = SuperKeyType {
+ alias: "test_super_key_1",
+ algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+ name: "test_super_key_1",
+ };
+
+ let key_name_nonenc = SuperKeyType {
+ alias: "test_super_key_2",
+ algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+ name: "test_super_key_2",
+ };
+
+ // Install two super keys.
+ db.store_super_key(1, &key_name_nonenc, &super_key, &BlobMetaData::new(), &KeyMetaData::new())?;
+ db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
+
+ // Check that both can be found in the database.
+ assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
+ assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
+
+ // Install the same keys for a different user.
+ db.store_super_key(2, &key_name_nonenc, &super_key, &BlobMetaData::new(), &KeyMetaData::new())?;
+ db.store_super_key(2, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
+
+ // Check that the second pair of keys can be found in the database.
+ assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
+ assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
+
+ // Delete all keys for user 1.
+ db.unbind_keys_for_user(1)?;
+
+ // All of user 1's keys should be gone.
+ assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
+ assert!(db.load_super_key(&key_name_nonenc, 1)?.is_none());
+
+ // User 2's keys should not have been touched.
+ assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
+ assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
+
+ Ok(())
+}
+
+fn app_key_exists(db: &mut KeystoreDB, nspace: i64, alias: &str) -> Result<bool> {
+ db.key_exists(Domain::APP, nspace, alias, KeyType::Client)
+}
+
+// Tests the unbind_auth_bound_keys_for_user() function.
+#[test]
+fn test_unbind_auth_bound_keys_for_user() -> Result<()> {
+ let mut db = new_test_db()?;
+ let user_id = 1;
+ let nspace: i64 = (user_id * AID_USER_OFFSET).into();
+ let other_user_id = 2;
+ let other_user_nspace: i64 = (other_user_id * AID_USER_OFFSET).into();
+ let super_key_type = &USER_AFTER_FIRST_UNLOCK_SUPER_KEY;
+
+ // Create a superencryption key.
+ let super_key = keystore2_crypto::generate_aes256_key()?;
+ let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+ let (encrypted_super_key, blob_metadata) =
+ SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+ db.store_super_key(
+ user_id,
+ super_key_type,
+ &encrypted_super_key,
+ &blob_metadata,
+ &KeyMetaData::new(),
+ )?;
+ let super_key_id = db.load_super_key(super_key_type, user_id)?.unwrap().0 .0;
+
+ // Store 4 superencrypted app keys, one for each possible combination of
+ // (authentication required, unlocked device required).
+ make_superencrypted_key_entry(&mut db, nspace, "noauth_noud", false, false, super_key_id)?;
+ make_superencrypted_key_entry(&mut db, nspace, "noauth_ud", false, true, super_key_id)?;
+ make_superencrypted_key_entry(&mut db, nspace, "auth_noud", true, false, super_key_id)?;
+ make_superencrypted_key_entry(&mut db, nspace, "auth_ud", true, true, super_key_id)?;
+ assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
+ assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
+ assert!(app_key_exists(&mut db, nspace, "auth_noud")?);
+ assert!(app_key_exists(&mut db, nspace, "auth_ud")?);
+
+ // Also store a key for a different user that requires authentication.
+ make_superencrypted_key_entry(&mut db, other_user_nspace, "auth_ud", true, true, super_key_id)?;
+
+ db.unbind_auth_bound_keys_for_user(user_id)?;
+
+ // Verify that only the user's app keys that require authentication were
+ // deleted. Keys that require an unlocked device but not authentication
+ // should *not* have been deleted, nor should the super key have been
+ // deleted, nor should other users' keys have been deleted.
+ assert!(db.load_super_key(super_key_type, user_id)?.is_some());
+ assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
+ assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
+ assert!(!app_key_exists(&mut db, nspace, "auth_noud")?);
+ assert!(!app_key_exists(&mut db, nspace, "auth_ud")?);
+ assert!(app_key_exists(&mut db, other_user_nspace, "auth_ud")?);
+
+ Ok(())
+}
+
+#[test]
+fn test_store_super_key() -> Result<()> {
+ let mut db = new_test_db()?;
+ let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+ let super_key = keystore2_crypto::generate_aes256_key()?;
+ let secret_bytes = b"keystore2 is great.";
+ let (encrypted_secret, iv, tag) = keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
+
+ let (encrypted_super_key, metadata) = SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+ db.store_super_key(
+ 1,
+ &USER_AFTER_FIRST_UNLOCK_SUPER_KEY,
+ &encrypted_super_key,
+ &metadata,
+ &KeyMetaData::new(),
+ )?;
+
+ // Check if super key exists.
+ assert!(db.key_exists(
+ Domain::APP,
+ 1,
+ USER_AFTER_FIRST_UNLOCK_SUPER_KEY.alias,
+ KeyType::Super
+ )?);
+
+ let (_, key_entry) = db.load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, 1)?.unwrap();
+ let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
+ USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm,
+ key_entry,
+ &pw,
+ None,
+ )?;
+
+ let decrypted_secret_bytes = loaded_super_key.decrypt(&encrypted_secret, &iv, &tag)?;
+ assert_eq!(secret_bytes, &*decrypted_secret_bytes);
+
+ Ok(())
+}
+
+fn get_valid_statsd_storage_types() -> Vec<MetricsStorage> {
+ vec![
+ MetricsStorage::KEY_ENTRY,
+ MetricsStorage::KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
+ MetricsStorage::BLOB_ENTRY,
+ MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_PARAMETER,
+ MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_METADATA,
+ MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::GRANT,
+ MetricsStorage::AUTH_TOKEN,
+ MetricsStorage::BLOB_METADATA,
+ MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
+ ]
+}
+
+/// Perform a simple check to ensure that we can query all the storage types
+/// that are supported by the DB. Check for reasonable values.
+#[test]
+fn test_query_all_valid_table_sizes() -> Result<()> {
+ const PAGE_SIZE: i32 = 4096;
+
+ let mut db = new_test_db()?;
+
+ for t in get_valid_statsd_storage_types() {
+ let stat = db.get_storage_stat(t)?;
+ // AuthToken can be less than a page since it's in a btree, not sqlite
+ // TODO(b/187474736) stop using if-let here
+ if let MetricsStorage::AUTH_TOKEN = t {
+ } else {
+ assert!(stat.size >= PAGE_SIZE);
+ }
+ assert!(stat.size >= stat.unused_size);
+ }
+
+ Ok(())
+}
+
+fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, StorageStats> {
+ get_valid_statsd_storage_types()
+ .into_iter()
+ .map(|t| (t.0, db.get_storage_stat(t).unwrap()))
+ .collect()
+}
+
+fn assert_storage_increased(
+ db: &mut KeystoreDB,
+ increased_storage_types: Vec<MetricsStorage>,
+ baseline: &mut BTreeMap<i32, StorageStats>,
+) {
+ for storage in increased_storage_types {
+ // Verify the expected storage increased.
+ let new = db.get_storage_stat(storage).unwrap();
+ let old = &baseline[&storage.0];
+ assert!(new.size >= old.size, "{}: {} >= {}", storage.0, new.size, old.size);
+ assert!(
+ new.unused_size <= old.unused_size,
+ "{}: {} <= {}",
+ storage.0,
+ new.unused_size,
+ old.unused_size
+ );
+
+ // Update the baseline with the new value so that it succeeds in the
+ // later comparison.
+ baseline.insert(storage.0, new);
+ }
+
+ // Get an updated map of the storage and verify there were no unexpected changes.
+ let updated_stats = get_storage_stats_map(db);
+ assert_eq!(updated_stats.len(), baseline.len());
+
+ for &k in baseline.keys() {
+ let stringify = |map: &BTreeMap<i32, StorageStats>| -> String {
+ let mut s = String::new();
+ for &k in map.keys() {
+ writeln!(&mut s, " {}: {}, {}", &k, map[&k].size, map[&k].unused_size)
+ .expect("string concat failed");
+ }
+ s
+ };
+
+ assert!(
+ updated_stats[&k].size == baseline[&k].size
+ && updated_stats[&k].unused_size == baseline[&k].unused_size,
+ "updated_stats:\n{}\nbaseline:\n{}",
+ stringify(&updated_stats),
+ stringify(baseline)
+ );
+ }
+}
+
+#[test]
+fn test_verify_key_table_size_reporting() -> Result<()> {
+ let mut db = new_test_db()?;
+ let mut working_stats = get_storage_stats_map(&mut db);
+
+ let key_id = create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+ assert_storage_increased(
+ &mut db,
+ vec![
+ MetricsStorage::KEY_ENTRY,
+ MetricsStorage::KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
+ ],
+ &mut working_stats,
+ );
+
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), None)?;
+ assert_storage_increased(
+ &mut db,
+ vec![
+ MetricsStorage::BLOB_ENTRY,
+ MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::BLOB_METADATA,
+ MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
+ ],
+ &mut working_stats,
+ );
+
+ let params = make_test_params(None);
+ db.insert_keyparameter(&key_id, &params)?;
+ assert_storage_increased(
+ &mut db,
+ vec![MetricsStorage::KEY_PARAMETER, MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX],
+ &mut working_stats,
+ );
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+ assert_storage_increased(
+ &mut db,
+ vec![MetricsStorage::KEY_METADATA, MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX],
+ &mut working_stats,
+ );
+
+ let mut sum = 0;
+ for stat in working_stats.values() {
+ sum += stat.size;
+ }
+ let total = db.get_storage_stat(MetricsStorage::DATABASE)?.size;
+ assert!(sum <= total, "Expected sum <= total. sum: {}, total: {}", sum, total);
+
+ Ok(())
+}
+
+#[test]
+fn test_verify_auth_table_size_reporting() -> Result<()> {
+ let mut db = new_test_db()?;
+ let mut working_stats = get_storage_stats_map(&mut db);
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 456,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 10 },
+ mac: b"mac".to_vec(),
+ });
+ assert_storage_increased(&mut db, vec![MetricsStorage::AUTH_TOKEN], &mut working_stats);
+ Ok(())
+}
+
+#[test]
+fn test_verify_grant_table_size_reporting() -> Result<()> {
+ const OWNER: i64 = 1;
+ let mut db = new_test_db()?;
+ make_test_key_entry(&mut db, Domain::APP, OWNER, TEST_ALIAS, None)?;
+
+ let mut working_stats = get_storage_stats_map(&mut db);
+ db.grant(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ OWNER as u32,
+ 123,
+ key_perm_set![KeyPerm::Use],
+ |_, _| Ok(()),
+ )?;
+
+ assert_storage_increased(&mut db, vec![MetricsStorage::GRANT], &mut working_stats);
+
+ Ok(())
+}
+
+#[test]
+fn find_auth_token_entry_returns_latest() -> Result<()> {
+ let mut db = new_test_db()?;
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 456,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 10 },
+ mac: b"mac0".to_vec(),
+ });
+ std::thread::sleep(std::time::Duration::from_millis(1));
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 457,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 12 },
+ mac: b"mac1".to_vec(),
+ });
+ std::thread::sleep(std::time::Duration::from_millis(1));
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 458,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 3 },
+ mac: b"mac2".to_vec(),
+ });
+ // 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().auth_token.mac, b"mac2".to_vec());
+ Ok(())
+}
+
+#[test]
+fn test_load_key_descriptor() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)?.0;
+
+ let key = db.load_key_descriptor(key_id)?.unwrap();
+
+ assert_eq!(key.domain, Domain::APP);
+ assert_eq!(key.nspace, 1);
+ assert_eq!(key.alias, Some(TEST_ALIAS.to_string()));
+
+ // No such id
+ assert_eq!(db.load_key_descriptor(key_id + 1)?, None);
+ Ok(())
+}
+
+#[test]
+fn test_get_list_app_uids_for_sid() -> Result<()> {
+ let uid: i32 = 1;
+ let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
+ let first_sid = 667;
+ let second_sid = 669;
+ let first_app_id: i64 = 123 + uid_offset;
+ let second_app_id: i64 = 456 + uid_offset;
+ let third_app_id: i64 = 789 + uid_offset;
+ let unrelated_app_id: i64 = 1011 + uid_offset;
+ let mut db = new_test_db()?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ first_app_id,
+ TEST_ALIAS,
+ None,
+ &[first_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ "alias2",
+ None,
+ &[first_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ TEST_ALIAS,
+ None,
+ &[second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ third_app_id,
+ "alias3",
+ None,
+ &[second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(&mut db, Domain::APP, unrelated_app_id, TEST_ALIAS, None, &[])
+ .context("test_get_list_app_uids_for_sid")?;
+
+ let mut first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
+ first_sid_apps.sort();
+ assert_eq!(first_sid_apps, vec![first_app_id, second_app_id]);
+ let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
+ second_sid_apps.sort();
+ assert_eq!(second_sid_apps, vec![second_app_id, third_app_id]);
+ Ok(())
+}
+
+#[test]
+fn test_get_list_app_uids_with_multiple_sids() -> Result<()> {
+ let uid: i32 = 1;
+ let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
+ let first_sid = 667;
+ let second_sid = 669;
+ let third_sid = 772;
+ let first_app_id: i64 = 123 + uid_offset;
+ let second_app_id: i64 = 456 + uid_offset;
+ let mut db = new_test_db()?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ first_app_id,
+ TEST_ALIAS,
+ None,
+ &[first_sid, second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ "alias2",
+ None,
+ &[second_sid, third_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+
+ let first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
+ assert_eq!(first_sid_apps, vec![first_app_id]);
+
+ let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
+ second_sid_apps.sort();
+ assert_eq!(second_sid_apps, vec![first_app_id, second_app_id]);
+
+ let third_sid_apps = db.get_app_uids_affected_by_sid(uid, third_sid)?;
+ assert_eq!(third_sid_apps, vec![second_app_id]);
+ Ok(())
+}
diff --git a/keystore2/src/database/versioning.rs b/keystore2/src/database/versioning.rs
index 2c816f44..bc68f159 100644
--- a/keystore2/src/database/versioning.rs
+++ b/keystore2/src/database/versioning.rs
@@ -15,7 +15,7 @@
use anyhow::{anyhow, Context, Result};
use rusqlite::{params, OptionalExtension, Transaction};
-pub fn create_or_get_version(tx: &Transaction, current_version: u32) -> Result<u32> {
+fn create_or_get_version(tx: &Transaction, current_version: u32) -> Result<u32> {
tx.execute(
"CREATE TABLE IF NOT EXISTS persistent.version (
id INTEGER PRIMARY KEY,
@@ -61,7 +61,7 @@ pub fn create_or_get_version(tx: &Transaction, current_version: u32) -> Result<u
Ok(version)
}
-pub fn update_version(tx: &Transaction, new_version: u32) -> Result<()> {
+fn update_version(tx: &Transaction, new_version: u32) -> Result<()> {
let updated = tx
.execute("UPDATE persistent.version SET version = ? WHERE id = 0;", params![new_version])
.context("In update_version: Failed to update row.")?;
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 55c9591f..70383237 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -50,8 +50,6 @@ use std::{
enum AuthRequestState {
/// An outstanding per operation authorization request.
OpAuth,
- /// An outstanding request for per operation authorization and secure timestamp.
- TimeStampedOpAuth(Mutex<Receiver<Result<TimeStampToken, Error>>>),
/// An outstanding request for a timestamp token.
TimeStamp(Mutex<Receiver<Result<TimeStampToken, Error>>>),
}
@@ -59,8 +57,7 @@ enum AuthRequestState {
#[derive(Debug)]
struct AuthRequest {
state: AuthRequestState,
- /// This need to be set to Some to fulfill a AuthRequestState::OpAuth or
- /// AuthRequestState::TimeStampedOpAuth.
+ /// This need to be set to Some to fulfill an AuthRequestState::OpAuth.
hat: Mutex<Option<HardwareAuthToken>>,
}
@@ -69,13 +66,6 @@ impl AuthRequest {
Arc::new(Self { state: AuthRequestState::OpAuth, hat: Mutex::new(None) })
}
- fn timestamped_op_auth(receiver: Receiver<Result<TimeStampToken, Error>>) -> Arc<Self> {
- Arc::new(Self {
- state: AuthRequestState::TimeStampedOpAuth(Mutex::new(receiver)),
- hat: Mutex::new(None),
- })
- }
-
fn timestamp(
hat: HardwareAuthToken,
receiver: Receiver<Result<TimeStampToken, Error>>,
@@ -100,7 +90,7 @@ impl AuthRequest {
.context(ks_err!("No operation auth token received."))?;
let tst = match &self.state {
- AuthRequestState::TimeStampedOpAuth(recv) | AuthRequestState::TimeStamp(recv) => {
+ AuthRequestState::TimeStamp(recv) => {
let result = recv
.lock()
.unwrap()
@@ -132,9 +122,6 @@ enum DeferredAuthState {
/// loaded from the database, but it has to be accompanied by a time stamp token to inform
/// the target KM with a different clock about the time on the authenticators.
TimeStampRequired(HardwareAuthToken),
- /// Indicates that both an operation bound auth token and a verification token are
- /// before the operation can commence.
- TimeStampedOpAuthRequired,
/// In this state the auth info is waiting for the deferred authorizations to come in.
/// We block on timestamp tokens, because we can always make progress on these requests.
/// The per-op auth tokens might never come, which means we fail if the client calls
@@ -254,16 +241,6 @@ impl AuthInfo {
self.state = DeferredAuthState::Waiting(auth_request);
Some(OperationChallenge { challenge })
}
- DeferredAuthState::TimeStampedOpAuthRequired => {
- let (sender, receiver) = channel::<Result<TimeStampToken, Error>>();
- let auth_request = AuthRequest::timestamped_op_auth(receiver);
- let token_receiver = TokenReceiver(Arc::downgrade(&auth_request));
- ENFORCEMENTS.register_op_auth_receiver(challenge, token_receiver);
-
- ASYNC_TASK.queue_hi(move |_| timestamp_token_request(challenge, sender));
- self.state = DeferredAuthState::Waiting(auth_request);
- Some(OperationChallenge { challenge })
- }
DeferredAuthState::TimeStampRequired(hat) => {
let hat = (*hat).clone();
let (sender, receiver) = channel::<Result<TimeStampToken, Error>>();
@@ -349,9 +326,7 @@ impl AuthInfo {
match &self.state {
DeferredAuthState::NoAuthRequired => Ok((None, None)),
DeferredAuthState::Token(hat, tst) => Ok((Some((*hat).clone()), (*tst).clone())),
- DeferredAuthState::OpAuthRequired
- | DeferredAuthState::TimeStampedOpAuthRequired
- | DeferredAuthState::TimeStampRequired(_) => {
+ DeferredAuthState::OpAuthRequired | DeferredAuthState::TimeStampRequired(_) => {
Err(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED)).context(ks_err!(
"No operation auth token requested??? \
This should not happen."
@@ -476,7 +451,6 @@ impl Enforcements {
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 +507,6 @@ impl Enforcements {
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
@@ -603,132 +574,39 @@ impl Enforcements {
}
}
- if android_security_flags::fix_unlocked_device_required_keys_v2() {
- 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 now = BootTime::now();
- let token_age = now
- .checked_sub(&hat.time_received())
- .ok_or_else(Error::sys)
- .context(ks_err!(
- "Overflow while computing Auth token validity. \
- 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 {
- return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
- .context(ks_err!("matching auth token is expired."));
- }
- let state = if requires_timestamp {
- DeferredAuthState::TimeStampRequired(hat.auth_token().clone())
- } else {
- DeferredAuthState::NoAuthRequired
- };
- (Some(hat.take_auth_token()), state)
+ let (hat, state) = if user_secure_ids.is_empty() {
+ (None, DeferredAuthState::NoAuthRequired)
+ } else if let Some(key_time_out) = key_time_out {
+ 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()).ok_or_else(Error::sys).context(ks_err!(
+ "Overflow while computing Auth token validity. \
+ Validity cannot be established."
+ ))?;
+
+ if token_age.seconds() > key_time_out {
+ return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+ .context(ks_err!("matching auth token is expired."));
+ }
+ let state = if requires_timestamp {
+ DeferredAuthState::TimeStampRequired(hat.auth_token().clone())
} else {
- (None, DeferredAuthState::OpAuthRequired)
+ DeferredAuthState::NoAuthRequired
};
- return Ok((hat, AuthInfo { state, key_usage_limited, confirmation_token_receiver }));
- }
-
- if !unlocked_device_required && no_auth_required {
- return Ok((
- None,
- AuthInfo {
- state: DeferredAuthState::NoAuthRequired,
- key_usage_limited,
- confirmation_token_receiver,
- },
- ));
- }
-
- let has_sids = !user_secure_ids.is_empty();
-
- let timeout_bound = key_time_out.is_some() && has_sids;
-
- let per_op_bound = key_time_out.is_none() && has_sids;
-
- 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| {
- if let (Some(auth_type), true) = (user_auth_type, timeout_bound) {
- hat.satisfies(&user_secure_ids, auth_type)
- } else {
- unlocked_device_required
- }
- });
- Some(
- hat_and_last_off_body
- .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
- .context(ks_err!("No suitable auth token found."))?,
- )
+ (Some(hat.take_auth_token()), state)
} else {
- None
+ (None, DeferredAuthState::OpAuthRequired)
};
-
- // 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 now = BootTime::now();
- let token_age = now
- .checked_sub(&hat.time_received())
- .ok_or_else(Error::sys)
- .context(ks_err!(
- "Overflow while computing Auth token validity. \
- 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 {
- return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
- .context(ks_err!("matching auth token is expired."));
- }
- 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."),
- _ => None,
- };
-
- Ok(match (hat, requires_timestamp, per_op_bound) {
- // Per-op-bound and Some(hat) can only happen if we are both per-op bound and unlocked
- // device required. In addition, this KM instance needs a timestamp token.
- // So the HAT cannot be presented on create. So on update/finish we present both
- // an per-op-bound auth token and a timestamp token.
- (Some(_), true, true) => (None, DeferredAuthState::TimeStampedOpAuthRequired),
- (Some(hat), true, false) => (
- Some(hat.auth_token().clone()),
- DeferredAuthState::TimeStampRequired(hat.take_auth_token()),
- ),
- (Some(hat), false, true) => {
- (Some(hat.take_auth_token()), DeferredAuthState::OpAuthRequired)
- }
- (Some(hat), false, false) => {
- (Some(hat.take_auth_token()), DeferredAuthState::NoAuthRequired)
- }
- (None, _, true) => (None, DeferredAuthState::OpAuthRequired),
- (None, _, false) => (None, DeferredAuthState::NoAuthRequired),
- })
- .map(|(hat, state)| {
- (hat, AuthInfo { state, key_usage_limited, confirmation_token_receiver })
- })
+ Ok((hat, AuthInfo { state, key_usage_limited, confirmation_token_receiver }))
}
- 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 +726,7 @@ impl Enforcements {
(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 +741,7 @@ impl Enforcements {
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 +773,7 @@ impl Enforcements {
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/error.rs b/keystore2/src/error.rs
index f0d0d27b..5e80266e 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -21,8 +21,8 @@
//!
//! `SerializedError` is used send error codes on the wire.
//!
-//! `map_or_log_err` is a convenience method used to convert `anyhow::Error` into `SerializedError`
-//! wire type.
+//! `into_[logged_]binder` is a convenience method used to convert `anyhow::Error` into
+//! `SerializedError` wire type.
//!
//! Keystore functions should use `anyhow::Result` to return error conditions, and context should
//! be added every time an error is forwarded.
@@ -38,6 +38,9 @@ use rkpd_client::Error as RkpdError;
use std::cmp::PartialEq;
use std::ffi::CString;
+#[cfg(test)]
+pub mod tests;
+
/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
@@ -128,14 +131,11 @@ pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
// Non negative error codes cannot be KM error codes.
// So we create an `Error::Binder` variant to preserve
// the service specific error code for logging.
- // `map_or_log_err` will map this on a system error,
- // but not before logging the details to logcat.
Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
}
}
// We create `Error::Binder` to preserve the exception code
// for logging.
- // `map_or_log_err` will map this on a system error.
e_code => Error::Binder(e_code, 0),
}
})
@@ -163,46 +163,17 @@ pub fn map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error> {
r.map_err(Error::BinderTransaction)
}
-/// This function should be used by Keystore service calls to translate error conditions
-/// into service specific exceptions.
-///
-/// All error conditions get logged by this function, except for KEY_NOT_FOUND error.
-///
-/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
-/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
-/// typically returns Ok(value).
-///
-/// # Examples
-///
-/// ```
-/// fn loadKey() -> anyhow::Result<Vec<u8>> {
-/// if (good_but_auth_required) {
-/// Ok(vec!['k', 'e', 'y'])
-/// } else {
-/// Err(anyhow!(Error::Rc(ResponseCode::KEY_NOT_FOUND)))
-/// }
-/// }
-///
-/// map_or_log_err(loadKey(), Ok)
-/// ```
-pub fn map_or_log_err<T, U, F>(result: anyhow::Result<U>, handle_ok: F) -> BinderResult<T>
-where
- F: FnOnce(U) -> BinderResult<T>,
-{
- map_err_with(
- result,
- |e| {
- // Make the key not found errors silent.
- if !matches!(
- e.root_cause().downcast_ref::<Error>(),
- Some(Error::Rc(ResponseCode::KEY_NOT_FOUND))
- ) {
- log::error!("{:?}", e);
- }
- e
- },
- handle_ok,
- )
+/// Convert an [`anyhow::Error`] to a [`binder::Status`], logging the value
+/// along the way (except if it is `KEY_NOT_FOUND`).
+pub fn into_logged_binder(e: anyhow::Error) -> BinderStatus {
+ // Log everything except key not found.
+ if !matches!(
+ e.root_cause().downcast_ref::<Error>(),
+ Some(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+ ) {
+ log::error!("{:?}", e);
+ }
+ into_binder(e)
}
/// This function turns an anyhow error into an optional CString.
@@ -219,29 +190,10 @@ pub fn anyhow_error_to_cstring(e: &anyhow::Error) -> Option<CString> {
}
}
-/// This function behaves similar to map_or_log_error, but it does not log the errors, instead
-/// it calls map_err on the error before mapping it to a binder result allowing callers to
-/// log or transform the error before mapping it.
-pub fn map_err_with<T, U, F1, F2>(
- result: anyhow::Result<U>,
- map_err: F1,
- handle_ok: F2,
-) -> BinderResult<T>
-where
- F1: FnOnce(anyhow::Error) -> anyhow::Error,
- F2: FnOnce(U) -> BinderResult<T>,
-{
- result.map_or_else(
- |e| {
- let e = map_err(e);
- let rc = anyhow_error_to_serialized_error(&e);
- Err(BinderStatus::new_service_specific_error(
- rc.0,
- anyhow_error_to_cstring(&e).as_deref(),
- ))
- },
- handle_ok,
- )
+/// Convert an [`anyhow::Error`] to a [`binder::Status`].
+pub fn into_binder(e: anyhow::Error) -> binder::Status {
+ let rc = anyhow_error_to_serialized_error(&e);
+ BinderStatus::new_service_specific_error(rc.0, anyhow_error_to_cstring(&e).as_deref())
}
/// This type is used to send error codes on the wire.
@@ -283,206 +235,3 @@ pub fn anyhow_error_to_serialized_error(e: &anyhow::Error) -> SerializedError {
},
}
}
-
-#[cfg(test)]
-pub mod tests {
-
- use super::*;
- use android_system_keystore2::binder::{
- ExceptionCode, Result as BinderResult, Status as BinderStatus,
- };
- use anyhow::{anyhow, Context};
-
- fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
- Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
- }
-
- fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
- nested_nested_rc(rc).context("nested rc")
- }
-
- fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
- Err(anyhow!(Error::Km(ec))).context("nested nested ec")
- }
-
- fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
- nested_nested_ec(ec).context("nested ec")
- }
-
- fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
- Ok(rc)
- }
-
- fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
- nested_nested_ok(rc).context("nested ok")
- }
-
- fn nested_nested_selinux_perm() -> anyhow::Result<()> {
- Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
- }
-
- fn nested_selinux_perm() -> anyhow::Result<()> {
- nested_nested_selinux_perm().context("nested selinux permission denied")
- }
-
- #[derive(Debug, thiserror::Error)]
- enum TestError {
- #[error("TestError::Fail")]
- Fail = 0,
- }
-
- fn nested_nested_other_error() -> anyhow::Result<()> {
- Err(anyhow!(TestError::Fail)).context("nested nested other error")
- }
-
- fn nested_other_error() -> anyhow::Result<()> {
- nested_nested_other_error().context("nested other error")
- }
-
- fn binder_sse_error(sse: i32) -> BinderResult<()> {
- Err(BinderStatus::new_service_specific_error(sse, None))
- }
-
- fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
- Err(BinderStatus::new_exception(ex, None))
- }
-
- #[test]
- fn keystore_error_test() -> anyhow::Result<(), String> {
- android_logger::init_once(
- android_logger::Config::default()
- .with_tag("keystore_error_tests")
- .with_max_level(log::LevelFilter::Debug),
- );
- // All Error::Rc(x) get mapped on a service specific error
- // code of x.
- for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 {
- assert_eq!(
- Result::<(), i32>::Err(rc),
- map_or_log_err(nested_rc(ResponseCode(rc)), |_| Err(BinderStatus::ok()))
- .map_err(|s| s.service_specific_error())
- );
- }
-
- // All Keystore Error::Km(x) get mapped on a service
- // specific error of x.
- for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
- assert_eq!(
- Result::<(), i32>::Err(ec),
- map_or_log_err(nested_ec(ErrorCode(ec)), |_| Err(BinderStatus::ok()))
- .map_err(|s| s.service_specific_error())
- );
- }
-
- // All Keymint errors x received through a Binder Result get mapped on
- // a service specific error of x.
- for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
- assert_eq!(
- Result::<(), i32>::Err(ec),
- map_or_log_err(
- map_km_error(binder_sse_error(ec))
- .with_context(|| format!("Km error code: {}.", ec)),
- |_| Err(BinderStatus::ok())
- )
- .map_err(|s| s.service_specific_error())
- );
- }
-
- // map_km_error creates an Error::Binder variant storing
- // ExceptionCode::SERVICE_SPECIFIC and the given
- // service specific error.
- let sse = map_km_error(binder_sse_error(1));
- assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
- // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
- assert_eq!(
- Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
- map_or_log_err(sse.context("Non negative service specific error."), |_| Err(
- BinderStatus::ok()
- ))
- .map_err(|s| ResponseCode(s.service_specific_error()))
- );
-
- // map_km_error creates a Error::Binder variant storing the given exception code.
- let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
- assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
- // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
- assert_eq!(
- Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
- map_or_log_err(binder_exception.context("Binder Exception."), |_| Err(
- BinderStatus::ok()
- ))
- .map_err(|s| ResponseCode(s.service_specific_error()))
- );
-
- // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
- assert_eq!(
- Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
- map_or_log_err(nested_selinux_perm(), |_| Err(BinderStatus::ok()))
- .map_err(|s| ResponseCode(s.service_specific_error()))
- );
-
- // All other errors get mapped on System Error.
- assert_eq!(
- Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
- map_or_log_err(nested_other_error(), |_| Err(BinderStatus::ok()))
- .map_err(|s| ResponseCode(s.service_specific_error()))
- );
-
- // Result::Ok variants get passed to the ok handler.
- assert_eq!(Ok(ResponseCode::LOCKED), map_or_log_err(nested_ok(ResponseCode::LOCKED), Ok));
- assert_eq!(
- Ok(ResponseCode::SYSTEM_ERROR),
- map_or_log_err(nested_ok(ResponseCode::SYSTEM_ERROR), Ok)
- );
-
- Ok(())
- }
-
- //Helper function to test whether error cases are handled as expected.
- pub fn check_result_contains_error_string<T>(
- result: anyhow::Result<T>,
- expected_error_string: &str,
- ) {
- let error_str = format!(
- "{:#?}",
- result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
- );
- assert!(
- error_str.contains(expected_error_string),
- "The string \"{}\" should contain \"{}\"",
- error_str,
- expected_error_string
- );
- }
-
- #[test]
- fn rkpd_error_is_in_sync_with_response_code() {
- let error_mapping = [
- (RkpdError::RequestCancelled, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
- (RkpdError::GetRegistrationFailed, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
- (
- RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_UNKNOWN),
- ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
- ),
- (
- RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PERMANENT),
- ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
- ),
- (
- RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY),
- ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
- ),
- (
- RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH),
- ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
- ),
- (RkpdError::StoreUpgradedKeyFailed, ResponseCode::SYSTEM_ERROR),
- (RkpdError::RetryableTimeout, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
- (RkpdError::Timeout, ResponseCode::SYSTEM_ERROR),
- ];
- for (rkpd_error, expected_response_code) in error_mapping {
- let e: Error = rkpd_error.into();
- assert_eq!(e, Error::Rc(expected_response_code));
- }
- }
-} // mod tests
diff --git a/keystore2/src/error/tests.rs b/keystore2/src/error/tests.rs
new file mode 100644
index 00000000..d50091b0
--- /dev/null
+++ b/keystore2/src/error/tests.rs
@@ -0,0 +1,218 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Error handling tests.
+
+use super::*;
+use android_system_keystore2::binder::{
+ ExceptionCode, Result as BinderResult, Status as BinderStatus,
+};
+use anyhow::{anyhow, Context};
+
+fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
+ Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
+}
+
+fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
+ nested_nested_rc(rc).context("nested rc")
+}
+
+fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
+ Err(anyhow!(Error::Km(ec))).context("nested nested ec")
+}
+
+fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
+ nested_nested_ec(ec).context("nested ec")
+}
+
+fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
+ Ok(rc)
+}
+
+fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
+ nested_nested_ok(rc).context("nested ok")
+}
+
+fn nested_nested_selinux_perm() -> anyhow::Result<()> {
+ Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
+}
+
+fn nested_selinux_perm() -> anyhow::Result<()> {
+ nested_nested_selinux_perm().context("nested selinux permission denied")
+}
+
+#[derive(Debug, thiserror::Error)]
+enum TestError {
+ #[error("TestError::Fail")]
+ Fail = 0,
+}
+
+fn nested_nested_other_error() -> anyhow::Result<()> {
+ Err(anyhow!(TestError::Fail)).context("nested nested other error")
+}
+
+fn nested_other_error() -> anyhow::Result<()> {
+ nested_nested_other_error().context("nested other error")
+}
+
+fn binder_sse_error(sse: i32) -> BinderResult<()> {
+ Err(BinderStatus::new_service_specific_error(sse, None))
+}
+
+fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
+ Err(BinderStatus::new_exception(ex, None))
+}
+
+#[test]
+fn keystore_error_test() -> anyhow::Result<(), String> {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore_error_tests")
+ .with_max_level(log::LevelFilter::Debug),
+ );
+ // All Error::Rc(x) get mapped on a service specific error
+ // code of x.
+ for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 {
+ assert_eq!(
+ Result::<(), i32>::Err(rc),
+ nested_rc(ResponseCode(rc))
+ .map_err(into_logged_binder)
+ .map_err(|s| s.service_specific_error())
+ );
+ }
+
+ // All Keystore Error::Km(x) get mapped on a service
+ // specific error of x.
+ for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
+ assert_eq!(
+ Result::<(), i32>::Err(ec),
+ nested_ec(ErrorCode(ec))
+ .map_err(into_logged_binder)
+ .map_err(|s| s.service_specific_error())
+ );
+ }
+
+ // All Keymint errors x received through a Binder Result get mapped on
+ // a service specific error of x.
+ for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
+ assert_eq!(
+ Result::<(), i32>::Err(ec),
+ map_km_error(binder_sse_error(ec))
+ .with_context(|| format!("Km error code: {}.", ec))
+ .map_err(into_logged_binder)
+ .map_err(|s| s.service_specific_error())
+ );
+ }
+
+ // map_km_error creates an Error::Binder variant storing
+ // ExceptionCode::SERVICE_SPECIFIC and the given
+ // service specific error.
+ let sse = map_km_error(binder_sse_error(1));
+ assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
+ // into_binder then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ sse.context("Non negative service specific error.")
+ .map_err(into_logged_binder)
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // map_km_error creates a Error::Binder variant storing the given exception code.
+ let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
+ assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
+ // into_binder then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ binder_exception
+ .context("Binder Exception.")
+ .map_err(into_logged_binder)
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
+ nested_selinux_perm()
+ .map_err(into_logged_binder)
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // All other errors get mapped on System Error.
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ nested_other_error()
+ .map_err(into_logged_binder)
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // Result::Ok variants get passed to the ok handler.
+ assert_eq!(
+ Ok(ResponseCode::LOCKED),
+ nested_ok(ResponseCode::LOCKED).map_err(into_logged_binder)
+ );
+ assert_eq!(
+ Ok(ResponseCode::SYSTEM_ERROR),
+ nested_ok(ResponseCode::SYSTEM_ERROR).map_err(into_logged_binder)
+ );
+
+ Ok(())
+}
+
+//Helper function to test whether error cases are handled as expected.
+pub fn check_result_contains_error_string<T>(
+ result: anyhow::Result<T>,
+ expected_error_string: &str,
+) {
+ let error_str = format!(
+ "{:#?}",
+ result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
+ );
+ assert!(
+ error_str.contains(expected_error_string),
+ "The string \"{}\" should contain \"{}\"",
+ error_str,
+ expected_error_string
+ );
+}
+
+#[test]
+fn rkpd_error_is_in_sync_with_response_code() {
+ let error_mapping = [
+ (RkpdError::RequestCancelled, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (RkpdError::GetRegistrationFailed, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_UNKNOWN),
+ ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
+ ),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PERMANENT),
+ ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
+ ),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY),
+ ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
+ ),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH),
+ ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
+ ),
+ (RkpdError::StoreUpgradedKeyFailed, ResponseCode::SYSTEM_ERROR),
+ (RkpdError::RetryableTimeout, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (RkpdError::Timeout, ResponseCode::SYSTEM_ERROR),
+ ];
+ for (rkpd_error, expected_response_code) in error_mapping {
+ let e: Error = rkpd_error.into();
+ assert_eq!(e, Error::Rc(expected_response_code));
+ }
+}
diff --git a/keystore2/src/gc.rs b/keystore2/src/gc.rs
index a0333568..f2341e3c 100644
--- a/keystore2/src/gc.rs
+++ b/keystore2/src/gc.rs
@@ -21,7 +21,7 @@
use crate::ks_err;
use crate::{
async_task,
- database::{BlobMetaData, KeystoreDB, Uuid},
+ database::{KeystoreDB, SupersededBlob, Uuid},
super_key::SuperKeyManager,
};
use anyhow::{Context, Result};
@@ -84,7 +84,7 @@ impl Gc {
struct GcInternal {
deleted_blob_ids: Vec<i64>,
- superseded_blobs: Vec<(i64, Vec<u8>, BlobMetaData)>,
+ superseded_blobs: Vec<SupersededBlob>,
invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
db: KeystoreDB,
async_task: std::sync::Weak<AsyncTask>,
@@ -109,7 +109,7 @@ impl GcInternal {
self.superseded_blobs = blobs;
}
- if let Some((blob_id, blob, blob_metadata)) = self.superseded_blobs.pop() {
+ if let Some(SupersededBlob { blob_id, blob, metadata }) = self.superseded_blobs.pop() {
// Add the next blob_id to the deleted blob ids list. So it will be
// removed from the database regardless of whether the following
// succeeds or not.
@@ -119,12 +119,12 @@ impl GcInternal {
// and delete the key, unwrapping if necessary and possible.
// (At this time keys may get deleted without having the super encryption
// key in this case we can only delete the key from the database.)
- if let Some(uuid) = blob_metadata.km_uuid() {
+ if let Some(uuid) = metadata.km_uuid() {
let blob = self
.super_key
.read()
.unwrap()
- .unwrap_key_if_required(&blob_metadata, &blob)
+ .unwrap_key_if_required(&metadata, &blob)
.context(ks_err!("Trying to unwrap to-be-deleted blob.",))?;
(self.invalidate_key)(uuid, &blob).context(ks_err!("Trying to invalidate key."))?;
}
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index eb755a6b..a90ba798 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_blob::LegacyBlobLoader;
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,
@@ -44,8 +44,8 @@ use android_hardware_security_secureclock::aidl::android::hardware::security::se
};
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
use anyhow::{Context, Result};
-use binder::get_declared_instances;
use binder::FromIBinder;
+use binder::{get_declared_instances, is_declared};
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex, RwLock};
use std::{cell::RefCell, sync::Once};
@@ -62,22 +62,25 @@ static DB_INIT: Once = Once::new();
/// is run only once, as long as the ASYNC_TASK instance is the same. So only one additional
/// database connection is created for the garbage collector worker.
pub fn create_thread_local_db() -> KeystoreDB {
- let db_path = DB_PATH.read().expect("Could not get the database directory.");
-
- let mut db = KeystoreDB::new(&db_path, Some(GC.clone())).expect("Failed to open database.");
+ let db_path = DB_PATH.read().expect("Could not get the database directory");
+
+ let result = KeystoreDB::new(&db_path, Some(GC.clone()));
+ let mut db = match result {
+ Ok(db) => db,
+ Err(e) => {
+ log::error!("Failed to open Keystore database at {db_path:?}: {e:?}");
+ log::error!("Has /data been mounted correctly?");
+ panic!("Failed to open database for Keystore, cannot continue: {e:?}")
+ }
+ };
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.");
+ let n = db.cleanup_leftovers().expect("Failed to cleanup database on startup");
if n != 0 {
log::info!(
- concat!(
- "Cleaned up {} failed entries. ",
- "This indicates keystore crashed during key generation."
- ),
- n
+ "Cleaned up {n} failed entries, indicating keystore crash on key generation"
);
}
});
@@ -89,8 +92,7 @@ thread_local! {
/// same database multiple times is safe as long as each connection is
/// used by only one thread. So we store one database connection per
/// thread in this thread local key.
- pub static DB: RefCell<KeystoreDB> =
- RefCell::new(create_thread_local_db());
+ pub static DB: RefCell<KeystoreDB> = RefCell::new(create_thread_local_db());
}
struct DevicesMap<T: FromIBinder + ?Sized> {
@@ -155,7 +157,7 @@ lazy_static! {
/// 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(
- &DB_PATH.read().expect("Could not get the database path for legacy blob loader.")));
+ &DB_PATH.read().expect("Could not determine database path for legacy blob loader")));
/// Legacy migrator. Atomically migrates legacy blobs to the database.
pub static ref LEGACY_IMPORTER: Arc<LegacyImporter> =
Arc::new(LegacyImporter::new(Arc::new(Default::default())));
@@ -166,12 +168,12 @@ lazy_static! {
(
Box::new(|uuid, blob| {
let km_dev = get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?;
- let _wp = wd::watch_millis("In invalidate key closure: calling deleteKey", 500);
+ let _wp = wd::watch("invalidate key closure: calling IKeyMintDevice::deleteKey");
map_km_error(km_dev.deleteKey(blob))
.context(ks_err!("Trying to invalidate key blob."))
}),
- KeystoreDB::new(&DB_PATH.read().expect("Could not get the database directory."), None)
- .expect("Failed to open database."),
+ KeystoreDB::new(&DB_PATH.read().expect("Could not determine database path for GC"), None)
+ .expect("Failed to open database"),
SUPER_KEY.clone(),
)
}));
@@ -247,7 +249,11 @@ fn connect_keymint(
}
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,
)
};
@@ -303,7 +309,7 @@ fn connect_keymint(
}
};
- let wp = wd::watch_millis("In connect_keymint: calling getHardwareInfo()", 500);
+ let wp = wd::watch("connect_keymint: calling IKeyMintDevice::getHardwareInfo()");
let mut hw_info =
map_km_error(keymint.getHardwareInfo()).context(ks_err!("Failed to get hardware info."))?;
drop(wp);
@@ -394,7 +400,7 @@ fn connect_secureclock() -> Result<Strong<dyn ISecureClock>> {
}
e => e,
})
- .context(ks_err!("Trying to get Legacy wrapper."))
+ .context(ks_err!("Failed attempt to get legacy secure clock."))
}?;
Ok(secureclock)
@@ -417,19 +423,20 @@ pub fn get_timestamp_service() -> Result<Strong<dyn ISecureClock>> {
pub fn get_remotely_provisioned_component_name(security_level: &SecurityLevel) -> Result<String> {
let remote_prov_descriptor: &str =
<BpRemotelyProvisionedComponent as IRemotelyProvisionedComponent>::get_descriptor();
- let remotely_prov_instances = get_declared_instances(remote_prov_descriptor).unwrap();
match *security_level {
SecurityLevel::TRUSTED_ENVIRONMENT => {
- if remotely_prov_instances.iter().any(|instance| *instance == "default") {
- Some(format!("{}/default", remote_prov_descriptor))
+ let instance = format!("{}/default", remote_prov_descriptor);
+ if is_declared(&instance)? {
+ Some(instance)
} else {
None
}
}
SecurityLevel::STRONGBOX => {
- if remotely_prov_instances.iter().any(|instance| *instance == "strongbox") {
- Some(format!("{}/strongbox", remote_prov_descriptor))
+ let instance = format!("{}/strongbox", remote_prov_descriptor);
+ if is_declared(&instance)? {
+ Some(instance)
} else {
None
}
@@ -437,5 +444,5 @@ pub fn get_remotely_provisioned_component_name(security_level: &SecurityLevel) -
_ => 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 02a1f16c..466fb505 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -111,6 +111,18 @@ use serde::de::Deserializer;
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
+#[cfg(test)]
+mod generated_key_parameter_tests;
+
+#[cfg(test)]
+mod basic_tests;
+
+#[cfg(test)]
+mod storage_tests;
+
+#[cfg(test)]
+mod wire_tests;
+
/// This trait is used to associate a primitive to any type that can be stored inside a
/// KeyParameterValue, especially the AIDL enum types, e.g., keymint::{Algorithm, Digest, ...}.
/// This allows for simplifying the macro rules, e.g., for reading from the SQL database.
@@ -912,7 +924,8 @@ pub enum KeyParameterValue {
/// 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
@@ -1090,490 +1103,3 @@ impl KeyParameter {
Authorization { securityLevel: self.security_level, keyParameter: self.value.into() }
}
}
-
-#[cfg(test)]
-mod generated_key_parameter_tests {
- use super::*;
- use android_hardware_security_keymint::aidl::android::hardware::security::keymint::TagType::TagType;
-
- fn get_field_by_tag_type(tag: Tag) -> KmKeyParameterValue {
- let tag_type = TagType((tag.0 as u32 & 0xF0000000) as i32);
- match tag {
- Tag::ALGORITHM => return KmKeyParameterValue::Algorithm(Default::default()),
- Tag::BLOCK_MODE => return KmKeyParameterValue::BlockMode(Default::default()),
- Tag::PADDING => return KmKeyParameterValue::PaddingMode(Default::default()),
- Tag::DIGEST => return KmKeyParameterValue::Digest(Default::default()),
- Tag::RSA_OAEP_MGF_DIGEST => return KmKeyParameterValue::Digest(Default::default()),
- Tag::EC_CURVE => return KmKeyParameterValue::EcCurve(Default::default()),
- Tag::ORIGIN => return KmKeyParameterValue::Origin(Default::default()),
- Tag::PURPOSE => return KmKeyParameterValue::KeyPurpose(Default::default()),
- Tag::USER_AUTH_TYPE => {
- return KmKeyParameterValue::HardwareAuthenticatorType(Default::default())
- }
- Tag::HARDWARE_TYPE => return KmKeyParameterValue::SecurityLevel(Default::default()),
- _ => {}
- }
- match tag_type {
- TagType::INVALID => return KmKeyParameterValue::Invalid(Default::default()),
- TagType::ENUM | TagType::ENUM_REP => {}
- TagType::UINT | TagType::UINT_REP => {
- return KmKeyParameterValue::Integer(Default::default())
- }
- TagType::ULONG | TagType::ULONG_REP => {
- return KmKeyParameterValue::LongInteger(Default::default())
- }
- TagType::DATE => return KmKeyParameterValue::DateTime(Default::default()),
- TagType::BOOL => return KmKeyParameterValue::BoolValue(Default::default()),
- TagType::BIGNUM | TagType::BYTES => {
- return KmKeyParameterValue::Blob(Default::default())
- }
- _ => {}
- }
- panic!("Unknown tag/tag_type: {:?} {:?}", tag, tag_type);
- }
-
- fn check_field_matches_tag_type(list_o_parameters: &[KmKeyParameter]) {
- for kp in list_o_parameters.iter() {
- match (&kp.value, get_field_by_tag_type(kp.tag)) {
- (&KmKeyParameterValue::Algorithm(_), KmKeyParameterValue::Algorithm(_))
- | (&KmKeyParameterValue::BlockMode(_), KmKeyParameterValue::BlockMode(_))
- | (&KmKeyParameterValue::PaddingMode(_), KmKeyParameterValue::PaddingMode(_))
- | (&KmKeyParameterValue::Digest(_), KmKeyParameterValue::Digest(_))
- | (&KmKeyParameterValue::EcCurve(_), KmKeyParameterValue::EcCurve(_))
- | (&KmKeyParameterValue::Origin(_), KmKeyParameterValue::Origin(_))
- | (&KmKeyParameterValue::KeyPurpose(_), KmKeyParameterValue::KeyPurpose(_))
- | (
- &KmKeyParameterValue::HardwareAuthenticatorType(_),
- KmKeyParameterValue::HardwareAuthenticatorType(_),
- )
- | (&KmKeyParameterValue::SecurityLevel(_), KmKeyParameterValue::SecurityLevel(_))
- | (&KmKeyParameterValue::Invalid(_), KmKeyParameterValue::Invalid(_))
- | (&KmKeyParameterValue::Integer(_), KmKeyParameterValue::Integer(_))
- | (&KmKeyParameterValue::LongInteger(_), KmKeyParameterValue::LongInteger(_))
- | (&KmKeyParameterValue::DateTime(_), KmKeyParameterValue::DateTime(_))
- | (&KmKeyParameterValue::BoolValue(_), KmKeyParameterValue::BoolValue(_))
- | (&KmKeyParameterValue::Blob(_), KmKeyParameterValue::Blob(_)) => {}
- (actual, expected) => panic!(
- "Tag {:?} associated with variant {:?} expected {:?}",
- kp.tag, actual, expected
- ),
- }
- }
- }
-
- #[test]
- fn key_parameter_value_field_matches_tag_type() {
- check_field_matches_tag_type(&KeyParameterValue::make_field_matches_tag_type_test_vector());
- }
-
- #[test]
- fn key_parameter_serialization_test() {
- let params = KeyParameterValue::make_key_parameter_defaults_vector();
- let mut out_buffer: Vec<u8> = Default::default();
- serde_cbor::to_writer(&mut out_buffer, &params)
- .expect("Failed to serialize key parameters.");
- let deserialized_params: Vec<KeyParameter> =
- serde_cbor::from_reader(&mut out_buffer.as_slice())
- .expect("Failed to deserialize key parameters.");
- assert_eq!(params, deserialized_params);
- }
-}
-
-#[cfg(test)]
-mod basic_tests {
- use crate::key_parameter::*;
-
- // Test basic functionality of KeyParameter.
- #[test]
- fn test_key_parameter() {
- let key_parameter = KeyParameter::new(
- KeyParameterValue::Algorithm(Algorithm::RSA),
- SecurityLevel::STRONGBOX,
- );
-
- assert_eq!(key_parameter.get_tag(), Tag::ALGORITHM);
-
- assert_eq!(
- *key_parameter.key_parameter_value(),
- KeyParameterValue::Algorithm(Algorithm::RSA)
- );
-
- assert_eq!(*key_parameter.security_level(), SecurityLevel::STRONGBOX);
- }
-}
-
-/// The storage_tests module first tests the 'new_from_sql' method for KeyParameters of different
-/// data types and then tests 'to_sql' method for KeyParameters of those
-/// different data types. The five different data types for KeyParameter values are:
-/// i) enums of u32
-/// ii) u32
-/// iii) u64
-/// iv) Vec<u8>
-/// v) bool
-#[cfg(test)]
-mod storage_tests {
- use crate::error::*;
- use crate::key_parameter::*;
- use anyhow::Result;
- use rusqlite::types::ToSql;
- use rusqlite::{params, Connection};
-
- /// Test initializing a KeyParameter (with key parameter value corresponding to an enum of i32)
- /// from a database table row.
- #[test]
- fn test_new_from_sql_enum_i32() -> Result<()> {
- let db = init_db()?;
- insert_into_keyparameter(
- &db,
- 1,
- Tag::ALGORITHM.0,
- &Algorithm::RSA.0,
- SecurityLevel::STRONGBOX.0,
- )?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(Tag::ALGORITHM, key_param.get_tag());
- assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::Algorithm(Algorithm::RSA));
- assert_eq!(*key_param.security_level(), SecurityLevel::STRONGBOX);
- Ok(())
- }
-
- /// Test initializing a KeyParameter (with key parameter value which is of i32)
- /// from a database table row.
- #[test]
- fn test_new_from_sql_i32() -> Result<()> {
- let db = init_db()?;
- insert_into_keyparameter(&db, 1, Tag::KEY_SIZE.0, &1024, SecurityLevel::STRONGBOX.0)?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(Tag::KEY_SIZE, key_param.get_tag());
- assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::KeySize(1024));
- Ok(())
- }
-
- /// Test initializing a KeyParameter (with key parameter value which is of i64)
- /// from a database table row.
- #[test]
- fn test_new_from_sql_i64() -> Result<()> {
- let db = init_db()?;
- // max value for i64, just to test corner cases
- insert_into_keyparameter(
- &db,
- 1,
- Tag::RSA_PUBLIC_EXPONENT.0,
- &(i64::MAX),
- SecurityLevel::STRONGBOX.0,
- )?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(Tag::RSA_PUBLIC_EXPONENT, key_param.get_tag());
- assert_eq!(
- *key_param.key_parameter_value(),
- KeyParameterValue::RSAPublicExponent(i64::MAX)
- );
- Ok(())
- }
-
- /// Test initializing a KeyParameter (with key parameter value which is of bool)
- /// from a database table row.
- #[test]
- fn test_new_from_sql_bool() -> Result<()> {
- let db = init_db()?;
- insert_into_keyparameter(&db, 1, Tag::CALLER_NONCE.0, &Null, SecurityLevel::STRONGBOX.0)?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(Tag::CALLER_NONCE, key_param.get_tag());
- assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::CallerNonce);
- Ok(())
- }
-
- /// Test initializing a KeyParameter (with key parameter value which is of Vec<u8>)
- /// from a database table row.
- #[test]
- fn test_new_from_sql_vec_u8() -> Result<()> {
- let db = init_db()?;
- let app_id = String::from("MyAppID");
- let app_id_bytes = app_id.into_bytes();
- insert_into_keyparameter(
- &db,
- 1,
- Tag::APPLICATION_ID.0,
- &app_id_bytes,
- SecurityLevel::STRONGBOX.0,
- )?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(Tag::APPLICATION_ID, key_param.get_tag());
- assert_eq!(
- *key_param.key_parameter_value(),
- KeyParameterValue::ApplicationID(app_id_bytes)
- );
- Ok(())
- }
-
- /// Test storing a KeyParameter (with key parameter value which corresponds to an enum of i32)
- /// in the database
- #[test]
- fn test_to_sql_enum_i32() -> Result<()> {
- let db = init_db()?;
- let kp = KeyParameter::new(
- KeyParameterValue::Algorithm(Algorithm::RSA),
- SecurityLevel::STRONGBOX,
- );
- store_keyparameter(&db, 1, &kp)?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(kp.get_tag(), key_param.get_tag());
- assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
- assert_eq!(kp.security_level(), key_param.security_level());
- Ok(())
- }
-
- /// Test storing a KeyParameter (with key parameter value which is of i32) in the database
- #[test]
- fn test_to_sql_i32() -> Result<()> {
- let db = init_db()?;
- let kp = KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::STRONGBOX);
- store_keyparameter(&db, 1, &kp)?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(kp.get_tag(), key_param.get_tag());
- assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
- assert_eq!(kp.security_level(), key_param.security_level());
- Ok(())
- }
-
- /// Test storing a KeyParameter (with key parameter value which is of i64) in the database
- #[test]
- fn test_to_sql_i64() -> Result<()> {
- let db = init_db()?;
- // max value for i64, just to test corner cases
- let kp = KeyParameter::new(
- KeyParameterValue::RSAPublicExponent(i64::MAX),
- SecurityLevel::STRONGBOX,
- );
- store_keyparameter(&db, 1, &kp)?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(kp.get_tag(), key_param.get_tag());
- assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
- assert_eq!(kp.security_level(), key_param.security_level());
- Ok(())
- }
-
- /// Test storing a KeyParameter (with key parameter value which is of Vec<u8>) in the database
- #[test]
- fn test_to_sql_vec_u8() -> Result<()> {
- let db = init_db()?;
- let kp = KeyParameter::new(
- KeyParameterValue::ApplicationID(String::from("MyAppID").into_bytes()),
- SecurityLevel::STRONGBOX,
- );
- store_keyparameter(&db, 1, &kp)?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(kp.get_tag(), key_param.get_tag());
- assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
- assert_eq!(kp.security_level(), key_param.security_level());
- Ok(())
- }
-
- /// Test storing a KeyParameter (with key parameter value which is of i32) in the database
- #[test]
- fn test_to_sql_bool() -> Result<()> {
- let db = init_db()?;
- let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
- store_keyparameter(&db, 1, &kp)?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(kp.get_tag(), key_param.get_tag());
- assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
- assert_eq!(kp.security_level(), key_param.security_level());
- Ok(())
- }
-
- #[test]
- /// Test Tag::Invalid
- fn test_invalid_tag() -> Result<()> {
- let db = init_db()?;
- insert_into_keyparameter(&db, 1, 0, &123, 1)?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(Tag::INVALID, key_param.get_tag());
- Ok(())
- }
-
- #[test]
- fn test_non_existing_enum_variant() -> Result<()> {
- let db = init_db()?;
- insert_into_keyparameter(&db, 1, 100, &123, 1)?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(Tag::INVALID, key_param.get_tag());
- Ok(())
- }
-
- #[test]
- fn test_invalid_conversion_from_sql() -> Result<()> {
- let db = init_db()?;
- insert_into_keyparameter(&db, 1, Tag::ALGORITHM.0, &Null, 1)?;
- tests::check_result_contains_error_string(
- query_from_keyparameter(&db),
- "Failed to read sql data for tag: ALGORITHM.",
- );
- Ok(())
- }
-
- /// Helper method to init database table for key parameter
- fn init_db() -> Result<Connection> {
- let db = Connection::open_in_memory().context("Failed to initialize sqlite connection.")?;
- db.execute("ATTACH DATABASE ? as 'persistent';", params![""])
- .context("Failed to attach databases.")?;
- db.execute(
- "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
- keyentryid INTEGER,
- tag INTEGER,
- data ANY,
- security_level INTEGER);",
- [],
- )
- .context("Failed to initialize \"keyparameter\" table.")?;
- Ok(db)
- }
-
- /// Helper method to insert an entry into key parameter table, with individual parameters
- fn insert_into_keyparameter<T: ToSql>(
- db: &Connection,
- key_id: i64,
- tag: i32,
- value: &T,
- security_level: i32,
- ) -> Result<()> {
- db.execute(
- "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
- VALUES(?, ?, ?, ?);",
- params![key_id, tag, *value, security_level],
- )?;
- Ok(())
- }
-
- /// Helper method to store a key parameter instance.
- fn store_keyparameter(db: &Connection, key_id: i64, kp: &KeyParameter) -> Result<()> {
- db.execute(
- "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
- VALUES(?, ?, ?, ?);",
- params![key_id, kp.get_tag().0, kp.key_parameter_value(), kp.security_level().0],
- )?;
- Ok(())
- }
-
- /// Helper method to query a row from keyparameter table
- fn query_from_keyparameter(db: &Connection) -> Result<KeyParameter> {
- let mut stmt =
- db.prepare("SELECT tag, data, security_level FROM persistent.keyparameter")?;
- let mut rows = stmt.query([])?;
- let row = rows.next()?.unwrap();
- KeyParameter::new_from_sql(
- Tag(row.get(0)?),
- &SqlField::new(1, row),
- SecurityLevel(row.get(2)?),
- )
- }
-}
-
-/// The wire_tests module tests the 'convert_to_wire' and 'convert_from_wire' methods for
-/// KeyParameter, for the four different types used in KmKeyParameter, in addition to Invalid
-/// key parameter.
-/// i) bool
-/// ii) integer
-/// iii) longInteger
-/// iv) blob
-#[cfg(test)]
-mod wire_tests {
- use crate::key_parameter::*;
- /// unit tests for to conversions
- #[test]
- fn test_convert_to_wire_invalid() {
- let kp = KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::STRONGBOX);
- assert_eq!(
- KmKeyParameter { tag: Tag::INVALID, value: KmKeyParameterValue::Invalid(0) },
- kp.value.into()
- );
- }
- #[test]
- fn test_convert_to_wire_bool() {
- let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
- assert_eq!(
- KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) },
- kp.value.into()
- );
- }
- #[test]
- fn test_convert_to_wire_integer() {
- let kp = KeyParameter::new(
- KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
- SecurityLevel::STRONGBOX,
- );
- assert_eq!(
- KmKeyParameter {
- tag: Tag::PURPOSE,
- value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT)
- },
- kp.value.into()
- );
- }
- #[test]
- fn test_convert_to_wire_long_integer() {
- let kp =
- KeyParameter::new(KeyParameterValue::UserSecureID(i64::MAX), SecurityLevel::STRONGBOX);
- assert_eq!(
- KmKeyParameter {
- tag: Tag::USER_SECURE_ID,
- value: KmKeyParameterValue::LongInteger(i64::MAX)
- },
- kp.value.into()
- );
- }
- #[test]
- fn test_convert_to_wire_blob() {
- let kp = KeyParameter::new(
- KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
- SecurityLevel::STRONGBOX,
- );
- assert_eq!(
- KmKeyParameter {
- tag: Tag::CONFIRMATION_TOKEN,
- value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes())
- },
- kp.value.into()
- );
- }
-
- /// unit tests for from conversion
- #[test]
- fn test_convert_from_wire_invalid() {
- let aidl_kp = KmKeyParameter { tag: Tag::INVALID, ..Default::default() };
- assert_eq!(KeyParameterValue::Invalid, aidl_kp.into());
- }
- #[test]
- fn test_convert_from_wire_bool() {
- let aidl_kp =
- KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) };
- assert_eq!(KeyParameterValue::CallerNonce, aidl_kp.into());
- }
- #[test]
- fn test_convert_from_wire_integer() {
- let aidl_kp = KmKeyParameter {
- tag: Tag::PURPOSE,
- value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
- };
- assert_eq!(KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), aidl_kp.into());
- }
- #[test]
- fn test_convert_from_wire_long_integer() {
- let aidl_kp = KmKeyParameter {
- tag: Tag::USER_SECURE_ID,
- value: KmKeyParameterValue::LongInteger(i64::MAX),
- };
- assert_eq!(KeyParameterValue::UserSecureID(i64::MAX), aidl_kp.into());
- }
- #[test]
- fn test_convert_from_wire_blob() {
- let aidl_kp = KmKeyParameter {
- tag: Tag::CONFIRMATION_TOKEN,
- value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes()),
- };
- assert_eq!(
- KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
- aidl_kp.into()
- );
- }
-}
diff --git a/keystore2/src/key_parameter/basic_tests.rs b/keystore2/src/key_parameter/basic_tests.rs
new file mode 100644
index 00000000..2bb37246
--- /dev/null
+++ b/keystore2/src/key_parameter/basic_tests.rs
@@ -0,0 +1,28 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::key_parameter::*;
+
+// Test basic functionality of KeyParameter.
+#[test]
+fn test_key_parameter() {
+ let key_parameter =
+ KeyParameter::new(KeyParameterValue::Algorithm(Algorithm::RSA), SecurityLevel::STRONGBOX);
+
+ assert_eq!(key_parameter.get_tag(), Tag::ALGORITHM);
+
+ assert_eq!(*key_parameter.key_parameter_value(), KeyParameterValue::Algorithm(Algorithm::RSA));
+
+ assert_eq!(*key_parameter.security_level(), SecurityLevel::STRONGBOX);
+}
diff --git a/keystore2/src/key_parameter/generated_key_parameter_tests.rs b/keystore2/src/key_parameter/generated_key_parameter_tests.rs
new file mode 100644
index 00000000..a5c0a8ba
--- /dev/null
+++ b/keystore2/src/key_parameter/generated_key_parameter_tests.rs
@@ -0,0 +1,95 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use super::*;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::TagType::TagType;
+
+fn get_field_by_tag_type(tag: Tag) -> KmKeyParameterValue {
+ let tag_type = TagType((tag.0 as u32 & 0xF0000000) as i32);
+ match tag {
+ Tag::ALGORITHM => return KmKeyParameterValue::Algorithm(Default::default()),
+ Tag::BLOCK_MODE => return KmKeyParameterValue::BlockMode(Default::default()),
+ Tag::PADDING => return KmKeyParameterValue::PaddingMode(Default::default()),
+ Tag::DIGEST => return KmKeyParameterValue::Digest(Default::default()),
+ Tag::RSA_OAEP_MGF_DIGEST => return KmKeyParameterValue::Digest(Default::default()),
+ Tag::EC_CURVE => return KmKeyParameterValue::EcCurve(Default::default()),
+ Tag::ORIGIN => return KmKeyParameterValue::Origin(Default::default()),
+ Tag::PURPOSE => return KmKeyParameterValue::KeyPurpose(Default::default()),
+ Tag::USER_AUTH_TYPE => {
+ return KmKeyParameterValue::HardwareAuthenticatorType(Default::default())
+ }
+ Tag::HARDWARE_TYPE => return KmKeyParameterValue::SecurityLevel(Default::default()),
+ _ => {}
+ }
+ match tag_type {
+ TagType::INVALID => return KmKeyParameterValue::Invalid(Default::default()),
+ TagType::ENUM | TagType::ENUM_REP => {}
+ TagType::UINT | TagType::UINT_REP => {
+ return KmKeyParameterValue::Integer(Default::default())
+ }
+ TagType::ULONG | TagType::ULONG_REP => {
+ return KmKeyParameterValue::LongInteger(Default::default())
+ }
+ TagType::DATE => return KmKeyParameterValue::DateTime(Default::default()),
+ TagType::BOOL => return KmKeyParameterValue::BoolValue(Default::default()),
+ TagType::BIGNUM | TagType::BYTES => return KmKeyParameterValue::Blob(Default::default()),
+ _ => {}
+ }
+ panic!("Unknown tag/tag_type: {:?} {:?}", tag, tag_type);
+}
+
+fn check_field_matches_tag_type(list_o_parameters: &[KmKeyParameter]) {
+ for kp in list_o_parameters.iter() {
+ match (&kp.value, get_field_by_tag_type(kp.tag)) {
+ (&KmKeyParameterValue::Algorithm(_), KmKeyParameterValue::Algorithm(_))
+ | (&KmKeyParameterValue::BlockMode(_), KmKeyParameterValue::BlockMode(_))
+ | (&KmKeyParameterValue::PaddingMode(_), KmKeyParameterValue::PaddingMode(_))
+ | (&KmKeyParameterValue::Digest(_), KmKeyParameterValue::Digest(_))
+ | (&KmKeyParameterValue::EcCurve(_), KmKeyParameterValue::EcCurve(_))
+ | (&KmKeyParameterValue::Origin(_), KmKeyParameterValue::Origin(_))
+ | (&KmKeyParameterValue::KeyPurpose(_), KmKeyParameterValue::KeyPurpose(_))
+ | (
+ &KmKeyParameterValue::HardwareAuthenticatorType(_),
+ KmKeyParameterValue::HardwareAuthenticatorType(_),
+ )
+ | (&KmKeyParameterValue::SecurityLevel(_), KmKeyParameterValue::SecurityLevel(_))
+ | (&KmKeyParameterValue::Invalid(_), KmKeyParameterValue::Invalid(_))
+ | (&KmKeyParameterValue::Integer(_), KmKeyParameterValue::Integer(_))
+ | (&KmKeyParameterValue::LongInteger(_), KmKeyParameterValue::LongInteger(_))
+ | (&KmKeyParameterValue::DateTime(_), KmKeyParameterValue::DateTime(_))
+ | (&KmKeyParameterValue::BoolValue(_), KmKeyParameterValue::BoolValue(_))
+ | (&KmKeyParameterValue::Blob(_), KmKeyParameterValue::Blob(_)) => {}
+ (actual, expected) => panic!(
+ "Tag {:?} associated with variant {:?} expected {:?}",
+ kp.tag, actual, expected
+ ),
+ }
+ }
+}
+
+#[test]
+fn key_parameter_value_field_matches_tag_type() {
+ check_field_matches_tag_type(&KeyParameterValue::make_field_matches_tag_type_test_vector());
+}
+
+#[test]
+fn key_parameter_serialization_test() {
+ let params = KeyParameterValue::make_key_parameter_defaults_vector();
+ let mut out_buffer: Vec<u8> = Default::default();
+ serde_cbor::to_writer(&mut out_buffer, &params).expect("Failed to serialize key parameters.");
+ let deserialized_params: Vec<KeyParameter> =
+ serde_cbor::from_reader(&mut out_buffer.as_slice())
+ .expect("Failed to deserialize key parameters.");
+ assert_eq!(params, deserialized_params);
+}
diff --git a/keystore2/src/key_parameter/storage_tests.rs b/keystore2/src/key_parameter/storage_tests.rs
new file mode 100644
index 00000000..38a57e41
--- /dev/null
+++ b/keystore2/src/key_parameter/storage_tests.rs
@@ -0,0 +1,263 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! The storage_tests module first tests the 'new_from_sql' method for KeyParameters of different
+//! data types and then tests 'to_sql' method for KeyParameters of those
+//! different data types. The five different data types for KeyParameter values are:
+//! i) enums of u32
+//! ii) u32
+//! iii) u64
+//! iv) Vec<u8>
+//! v) bool
+
+use crate::error::*;
+use crate::key_parameter::*;
+use anyhow::Result;
+use rusqlite::types::ToSql;
+use rusqlite::{params, Connection};
+
+/// Test initializing a KeyParameter (with key parameter value corresponding to an enum of i32)
+/// from a database table row.
+#[test]
+fn test_new_from_sql_enum_i32() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(
+ &db,
+ 1,
+ Tag::ALGORITHM.0,
+ &Algorithm::RSA.0,
+ SecurityLevel::STRONGBOX.0,
+ )?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::ALGORITHM, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::Algorithm(Algorithm::RSA));
+ assert_eq!(*key_param.security_level(), SecurityLevel::STRONGBOX);
+ Ok(())
+}
+
+/// Test initializing a KeyParameter (with key parameter value which is of i32)
+/// from a database table row.
+#[test]
+fn test_new_from_sql_i32() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, Tag::KEY_SIZE.0, &1024, SecurityLevel::STRONGBOX.0)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::KEY_SIZE, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::KeySize(1024));
+ Ok(())
+}
+
+/// Test initializing a KeyParameter (with key parameter value which is of i64)
+/// from a database table row.
+#[test]
+fn test_new_from_sql_i64() -> Result<()> {
+ let db = init_db()?;
+ // max value for i64, just to test corner cases
+ insert_into_keyparameter(
+ &db,
+ 1,
+ Tag::RSA_PUBLIC_EXPONENT.0,
+ &(i64::MAX),
+ SecurityLevel::STRONGBOX.0,
+ )?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::RSA_PUBLIC_EXPONENT, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::RSAPublicExponent(i64::MAX));
+ Ok(())
+}
+
+/// Test initializing a KeyParameter (with key parameter value which is of bool)
+/// from a database table row.
+#[test]
+fn test_new_from_sql_bool() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, Tag::CALLER_NONCE.0, &Null, SecurityLevel::STRONGBOX.0)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::CALLER_NONCE, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::CallerNonce);
+ Ok(())
+}
+
+/// Test initializing a KeyParameter (with key parameter value which is of Vec<u8>)
+/// from a database table row.
+#[test]
+fn test_new_from_sql_vec_u8() -> Result<()> {
+ let db = init_db()?;
+ let app_id = String::from("MyAppID");
+ let app_id_bytes = app_id.into_bytes();
+ insert_into_keyparameter(
+ &db,
+ 1,
+ Tag::APPLICATION_ID.0,
+ &app_id_bytes,
+ SecurityLevel::STRONGBOX.0,
+ )?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::APPLICATION_ID, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::ApplicationID(app_id_bytes));
+ Ok(())
+}
+
+/// Test storing a KeyParameter (with key parameter value which corresponds to an enum of i32)
+/// in the database
+#[test]
+fn test_to_sql_enum_i32() -> Result<()> {
+ let db = init_db()?;
+ let kp =
+ KeyParameter::new(KeyParameterValue::Algorithm(Algorithm::RSA), SecurityLevel::STRONGBOX);
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+}
+
+/// Test storing a KeyParameter (with key parameter value which is of i32) in the database
+#[test]
+fn test_to_sql_i32() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::STRONGBOX);
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+}
+
+/// Test storing a KeyParameter (with key parameter value which is of i64) in the database
+#[test]
+fn test_to_sql_i64() -> Result<()> {
+ let db = init_db()?;
+ // max value for i64, just to test corner cases
+ let kp =
+ KeyParameter::new(KeyParameterValue::RSAPublicExponent(i64::MAX), SecurityLevel::STRONGBOX);
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+}
+
+/// Test storing a KeyParameter (with key parameter value which is of Vec<u8>) in the database
+#[test]
+fn test_to_sql_vec_u8() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(
+ KeyParameterValue::ApplicationID(String::from("MyAppID").into_bytes()),
+ SecurityLevel::STRONGBOX,
+ );
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+}
+
+/// Test storing a KeyParameter (with key parameter value which is of i32) in the database
+#[test]
+fn test_to_sql_bool() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+}
+
+#[test]
+/// Test Tag::Invalid
+fn test_invalid_tag() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, 0, &123, 1)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::INVALID, key_param.get_tag());
+ Ok(())
+}
+
+#[test]
+fn test_non_existing_enum_variant() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, 100, &123, 1)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::INVALID, key_param.get_tag());
+ Ok(())
+}
+
+#[test]
+fn test_invalid_conversion_from_sql() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, Tag::ALGORITHM.0, &Null, 1)?;
+ tests::check_result_contains_error_string(
+ query_from_keyparameter(&db),
+ "Failed to read sql data for tag: ALGORITHM.",
+ );
+ Ok(())
+}
+
+/// Helper method to init database table for key parameter
+fn init_db() -> Result<Connection> {
+ let db = Connection::open_in_memory().context("Failed to initialize sqlite connection.")?;
+ db.execute("ATTACH DATABASE ? as 'persistent';", params![""])
+ .context("Failed to attach databases.")?;
+ db.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
+ keyentryid INTEGER,
+ tag INTEGER,
+ data ANY,
+ security_level INTEGER);",
+ [],
+ )
+ .context("Failed to initialize \"keyparameter\" table.")?;
+ Ok(db)
+}
+
+/// Helper method to insert an entry into key parameter table, with individual parameters
+fn insert_into_keyparameter<T: ToSql>(
+ db: &Connection,
+ key_id: i64,
+ tag: i32,
+ value: &T,
+ security_level: i32,
+) -> Result<()> {
+ db.execute(
+ "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
+ VALUES(?, ?, ?, ?);",
+ params![key_id, tag, *value, security_level],
+ )?;
+ Ok(())
+}
+
+/// Helper method to store a key parameter instance.
+fn store_keyparameter(db: &Connection, key_id: i64, kp: &KeyParameter) -> Result<()> {
+ db.execute(
+ "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
+ VALUES(?, ?, ?, ?);",
+ params![key_id, kp.get_tag().0, kp.key_parameter_value(), kp.security_level().0],
+ )?;
+ Ok(())
+}
+
+/// Helper method to query a row from keyparameter table
+fn query_from_keyparameter(db: &Connection) -> Result<KeyParameter> {
+ let mut stmt = db.prepare("SELECT tag, data, security_level FROM persistent.keyparameter")?;
+ let mut rows = stmt.query([])?;
+ let row = rows.next()?.unwrap();
+ KeyParameter::new_from_sql(Tag(row.get(0)?), &SqlField::new(1, row), SecurityLevel(row.get(2)?))
+}
diff --git a/keystore2/src/key_parameter/wire_tests.rs b/keystore2/src/key_parameter/wire_tests.rs
new file mode 100644
index 00000000..278b7669
--- /dev/null
+++ b/keystore2/src/key_parameter/wire_tests.rs
@@ -0,0 +1,119 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! The wire_tests module tests the 'convert_to_wire' and 'convert_from_wire' methods for
+//! KeyParameter, for the four different types used in KmKeyParameter, in addition to Invalid
+//! key parameter.
+//! i) bool
+//! ii) integer
+//! iii) longInteger
+//! iv) blob
+
+use crate::key_parameter::*;
+/// unit tests for to conversions
+#[test]
+fn test_convert_to_wire_invalid() {
+ let kp = KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::STRONGBOX);
+ assert_eq!(
+ KmKeyParameter { tag: Tag::INVALID, value: KmKeyParameterValue::Invalid(0) },
+ kp.value.into()
+ );
+}
+#[test]
+fn test_convert_to_wire_bool() {
+ let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
+ assert_eq!(
+ KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) },
+ kp.value.into()
+ );
+}
+#[test]
+fn test_convert_to_wire_integer() {
+ let kp = KeyParameter::new(
+ KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+ SecurityLevel::STRONGBOX,
+ );
+ assert_eq!(
+ KmKeyParameter {
+ tag: Tag::PURPOSE,
+ value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT)
+ },
+ kp.value.into()
+ );
+}
+#[test]
+fn test_convert_to_wire_long_integer() {
+ let kp = KeyParameter::new(KeyParameterValue::UserSecureID(i64::MAX), SecurityLevel::STRONGBOX);
+ assert_eq!(
+ KmKeyParameter {
+ tag: Tag::USER_SECURE_ID,
+ value: KmKeyParameterValue::LongInteger(i64::MAX)
+ },
+ kp.value.into()
+ );
+}
+#[test]
+fn test_convert_to_wire_blob() {
+ let kp = KeyParameter::new(
+ KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
+ SecurityLevel::STRONGBOX,
+ );
+ assert_eq!(
+ KmKeyParameter {
+ tag: Tag::CONFIRMATION_TOKEN,
+ value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes())
+ },
+ kp.value.into()
+ );
+}
+
+/// unit tests for from conversion
+#[test]
+fn test_convert_from_wire_invalid() {
+ let aidl_kp = KmKeyParameter { tag: Tag::INVALID, ..Default::default() };
+ assert_eq!(KeyParameterValue::Invalid, aidl_kp.into());
+}
+#[test]
+fn test_convert_from_wire_bool() {
+ let aidl_kp =
+ KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) };
+ assert_eq!(KeyParameterValue::CallerNonce, aidl_kp.into());
+}
+#[test]
+fn test_convert_from_wire_integer() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::PURPOSE,
+ value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+ };
+ assert_eq!(KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), aidl_kp.into());
+}
+#[test]
+fn test_convert_from_wire_long_integer() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::USER_SECURE_ID,
+ value: KmKeyParameterValue::LongInteger(i64::MAX),
+ };
+ assert_eq!(KeyParameterValue::UserSecureID(i64::MAX), aidl_kp.into());
+}
+#[test]
+fn test_convert_from_wire_blob() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::CONFIRMATION_TOKEN,
+ value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes()),
+ };
+ assert_eq!(
+ KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
+ aidl_kp.into()
+ );
+}
diff --git a/keystore2/src/km_compat.rs b/keystore2/src/km_compat.rs
index 03c9d027..5e3bdfa7 100644
--- a/keystore2/src/km_compat.rs
+++ b/keystore2/src/km_compat.rs
@@ -16,7 +16,7 @@
//! be emulated on back-level devices.
use crate::ks_err;
-use crate::error::{map_binder_status, map_binder_status_code, map_or_log_err, Error, ErrorCode};
+use crate::error::{map_binder_status, map_binder_status_code, into_logged_binder, Error, ErrorCode};
use android_hardware_security_keymint::binder::{BinderFeatures, StatusCode, Strong};
use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::TimeStampToken::TimeStampToken;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
@@ -226,7 +226,7 @@ where
) -> binder::Result<KeyCreationResult> {
if self.emu.emulation_required(key_params, &KeyImportData::None) {
let mut result = self.soft.generateKey(key_params, attestation_key)?;
- result.keyBlob = map_or_log_err(wrap_keyblob(&result.keyBlob), Ok)?;
+ result.keyBlob = wrap_keyblob(&result.keyBlob).map_err(into_logged_binder)?;
Ok(result)
} else {
self.real.generateKey(key_params, attestation_key)
@@ -242,7 +242,7 @@ where
if self.emu.emulation_required(key_params, &KeyImportData::new(key_format, key_data)?) {
let mut result =
self.soft.importKey(key_params, key_format, key_data, attestation_key)?;
- result.keyBlob = map_or_log_err(wrap_keyblob(&result.keyBlob), Ok)?;
+ result.keyBlob = wrap_keyblob(&result.keyBlob).map_err(into_logged_binder)?;
Ok(result)
} else {
self.real.importKey(key_params, key_format, key_data, attestation_key)
@@ -281,7 +281,7 @@ where
KeyBlob::Wrapped(keyblob) => {
// Re-wrap the upgraded keyblob.
let upgraded_keyblob = self.soft.upgradeKey(keyblob, upgrade_params)?;
- map_or_log_err(wrap_keyblob(&upgraded_keyblob), Ok)
+ wrap_keyblob(&upgraded_keyblob).map_err(into_logged_binder)
}
}
}
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 2bb7f27b..c27a0508 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -36,6 +36,9 @@ use std::{
const SUPPORTED_LEGACY_BLOB_VERSION: u8 = 3;
+#[cfg(test)]
+mod tests;
+
mod flags {
/// This flag is deprecated. It is here to support keys that have been written with this flag
/// set, but we don't create any new keys with this flag.
@@ -1645,675 +1648,3 @@ pub mod test_utils {
Ok(())
}
}
-
-#[cfg(test)]
-mod test {
- #![allow(dead_code)]
- use super::*;
- use crate::legacy_blob::test_utils::legacy_blob_test_vectors::*;
- use crate::legacy_blob::test_utils::*;
- use anyhow::{anyhow, Result};
- use keystore2_crypto::aes_gcm_decrypt;
- use keystore2_test_utils::TempDir;
- use rand::Rng;
- use std::convert::TryInto;
- use std::ops::Deref;
- use std::string::FromUtf8Error;
-
- #[test]
- fn decode_encode_alias_test() {
- static ALIAS: &str = "#({}test[])😗";
- static ENCODED_ALIAS: &str = "+S+X{}test[]+Y.`-O-H-G";
- // Second multi byte out of range ------v
- static ENCODED_ALIAS_ERROR1: &str = "+S+{}test[]+Y";
- // Incomplete multi byte ------------------------v
- static ENCODED_ALIAS_ERROR2: &str = "+S+X{}test[]+";
- // Our encoding: ".`-O-H-G"
- // is UTF-8: 0xF0 0x9F 0x98 0x97
- // is UNICODE: U+1F617
- // is 😗
- // But +H below is a valid encoding for 0x18 making this sequence invalid UTF-8.
- static ENCODED_ALIAS_ERROR_UTF8: &str = ".`-O+H-G";
-
- assert_eq!(ENCODED_ALIAS, &LegacyBlobLoader::encode_alias(ALIAS));
- assert_eq!(ALIAS, &LegacyBlobLoader::decode_alias(ENCODED_ALIAS).unwrap());
- assert_eq!(
- Some(&Error::BadEncoding),
- LegacyBlobLoader::decode_alias(ENCODED_ALIAS_ERROR1)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<Error>()
- );
- assert_eq!(
- Some(&Error::BadEncoding),
- LegacyBlobLoader::decode_alias(ENCODED_ALIAS_ERROR2)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<Error>()
- );
- assert!(LegacyBlobLoader::decode_alias(ENCODED_ALIAS_ERROR_UTF8)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<FromUtf8Error>()
- .is_some());
-
- for _i in 0..100 {
- // Any valid UTF-8 string should be en- and decoded without loss.
- let alias_str = rand::thread_rng().gen::<[char; 20]>().iter().collect::<String>();
- let random_alias = alias_str.as_bytes();
- let encoded = LegacyBlobLoader::encode_alias(&alias_str);
- let decoded = match LegacyBlobLoader::decode_alias(&encoded) {
- Ok(d) => d,
- Err(_) => panic!("random_alias: {:x?}\nencoded {}", random_alias, encoded),
- };
- assert_eq!(random_alias.to_vec(), decoded.bytes().collect::<Vec<u8>>());
- }
- }
-
- #[test]
- fn read_golden_key_blob_test() -> anyhow::Result<()> {
- let blob = LegacyBlobLoader::new_from_stream_decrypt_with(&mut &*BLOB, |_, _, _, _, _| {
- Err(anyhow!("should not be called"))
- })
- .unwrap();
- assert!(!blob.is_encrypted());
- assert!(!blob.is_fallback());
- assert!(!blob.is_strongbox());
- assert!(!blob.is_critical_to_device_encryption());
- assert_eq!(blob.value(), &BlobValue::Generic([0xde, 0xed, 0xbe, 0xef].to_vec()));
-
- let blob = LegacyBlobLoader::new_from_stream_decrypt_with(
- &mut &*REAL_LEGACY_BLOB,
- |_, _, _, _, _| Err(anyhow!("should not be called")),
- )
- .unwrap();
- assert!(!blob.is_encrypted());
- assert!(!blob.is_fallback());
- assert!(!blob.is_strongbox());
- assert!(!blob.is_critical_to_device_encryption());
- assert_eq!(
- blob.value(),
- &BlobValue::Decrypted(REAL_LEGACY_BLOB_PAYLOAD.try_into().unwrap())
- );
- Ok(())
- }
-
- #[test]
- fn read_aes_gcm_encrypted_key_blob_test() {
- let blob = LegacyBlobLoader::new_from_stream_decrypt_with(
- &mut &*AES_GCM_ENCRYPTED_BLOB,
- |d, iv, tag, salt, key_size| {
- assert_eq!(salt, None);
- assert_eq!(key_size, None);
- assert_eq!(
- iv,
- &[
- 0xbd, 0xdb, 0x8d, 0x69, 0x72, 0x56, 0xf0, 0xf5, 0xa4, 0x02, 0x88, 0x7f,
- 0x00, 0x00, 0x00, 0x00,
- ]
- );
- assert_eq!(
- tag,
- &[
- 0x50, 0xd9, 0x97, 0x95, 0x37, 0x6e, 0x28, 0x6a, 0x28, 0x9d, 0x51, 0xb9,
- 0xb9, 0xe0, 0x0b, 0xc3
- ][..]
- );
- aes_gcm_decrypt(d, iv, tag, AES_KEY).context("Trying to decrypt blob.")
- },
- )
- .unwrap();
- assert!(blob.is_encrypted());
- assert!(!blob.is_fallback());
- assert!(!blob.is_strongbox());
- assert!(!blob.is_critical_to_device_encryption());
-
- assert_eq!(blob.value(), &BlobValue::Decrypted(DECRYPTED_PAYLOAD.try_into().unwrap()));
- }
-
- #[test]
- fn read_golden_key_blob_too_short_test() {
- let error =
- LegacyBlobLoader::new_from_stream_decrypt_with(&mut &BLOB[0..15], |_, _, _, _, _| {
- Err(anyhow!("should not be called"))
- })
- .unwrap_err();
- assert_eq!(Some(&Error::BadLen), error.root_cause().downcast_ref::<Error>());
- }
-
- #[test]
- fn test_is_empty() {
- let temp_dir = TempDir::new("test_is_empty").expect("Failed to create temp dir.");
- let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
-
- assert!(legacy_blob_loader.is_empty().expect("Should succeed and be empty."));
-
- let _db = crate::database::KeystoreDB::new(temp_dir.path(), None)
- .expect("Failed to open database.");
-
- assert!(legacy_blob_loader.is_empty().expect("Should succeed and still be empty."));
-
- std::fs::create_dir(&*temp_dir.build().push("user_0")).expect("Failed to create user_0.");
-
- assert!(!legacy_blob_loader.is_empty().expect("Should succeed but not be empty."));
-
- std::fs::create_dir(&*temp_dir.build().push("user_10")).expect("Failed to create user_10.");
-
- assert!(!legacy_blob_loader.is_empty().expect("Should succeed but still not be empty."));
-
- std::fs::remove_dir_all(&*temp_dir.build().push("user_0"))
- .expect("Failed to remove user_0.");
-
- assert!(!legacy_blob_loader.is_empty().expect("Should succeed but still not be empty."));
-
- std::fs::remove_dir_all(&*temp_dir.build().push("user_10"))
- .expect("Failed to remove user_10.");
-
- assert!(legacy_blob_loader.is_empty().expect("Should succeed and be empty again."));
- }
-
- #[test]
- fn test_legacy_blobs() -> anyhow::Result<()> {
- let temp_dir = TempDir::new("legacy_blob_test").unwrap();
- std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
-
- std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
-
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND_CHR,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
- USRCERT_AUTHBOUND,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
- CACERT_AUTHBOUND,
- )
- .unwrap();
-
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRPKEY_non_authbound"),
- USRPKEY_NON_AUTHBOUND,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_non_authbound"),
- USRPKEY_NON_AUTHBOUND_CHR,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRCERT_non_authbound"),
- USRCERT_NON_AUTHBOUND,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_CACERT_non_authbound"),
- CACERT_NON_AUTHBOUND,
- )
- .unwrap();
-
- let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
-
- if let (Some((Blob { flags, value }, _params)), Some(cert), Some(chain)) =
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
- {
- assert_eq!(flags, 4);
- assert_eq!(
- value,
- BlobValue::Encrypted {
- data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
- iv: USRPKEY_AUTHBOUND_IV.to_vec(),
- tag: USRPKEY_AUTHBOUND_TAG.to_vec()
- }
- );
- assert_eq!(&cert[..], LOADED_CERT_AUTHBOUND);
- assert_eq!(&chain[..], LOADED_CACERT_AUTHBOUND);
- } else {
- panic!("");
- }
-
- if let (Some((Blob { flags, value: _ }, _params)), Some(cert), Some(chain)) =
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
- {
- assert_eq!(flags, 4);
- //assert_eq!(value, BlobValue::Encrypted(..));
- assert_eq!(&cert[..], LOADED_CERT_AUTHBOUND);
- assert_eq!(&chain[..], LOADED_CACERT_AUTHBOUND);
- } else {
- panic!("");
- }
- if let (Some((Blob { flags, value }, _params)), Some(cert), Some(chain)) =
- legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", &None)?
- {
- assert_eq!(flags, 0);
- assert_eq!(value, BlobValue::Decrypted(LOADED_USRPKEY_NON_AUTHBOUND.try_into()?));
- assert_eq!(&cert[..], LOADED_CERT_NON_AUTHBOUND);
- assert_eq!(&chain[..], LOADED_CACERT_NON_AUTHBOUND);
- } else {
- panic!("");
- }
-
- legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
- legacy_blob_loader
- .remove_keystore_entry(10223, "non_authbound")
- .expect("This should succeed.");
-
- assert_eq!(
- (None, None, None),
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
- );
- assert_eq!(
- (None, None, None),
- legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", &None)?
- );
-
- // The database should not be empty due to the super key.
- assert!(!legacy_blob_loader.is_empty()?);
- assert!(!legacy_blob_loader.is_empty_user(0)?);
-
- // The database should be considered empty for user 1.
- assert!(legacy_blob_loader.is_empty_user(1)?);
-
- legacy_blob_loader.remove_super_key(0);
-
- // Now it should be empty.
- assert!(legacy_blob_loader.is_empty_user(0)?);
- assert!(legacy_blob_loader.is_empty()?);
-
- Ok(())
- }
-
- struct TestKey(ZVec);
-
- impl crate::utils::AesGcmKey for TestKey {
- fn key(&self) -> &[u8] {
- &self.0
- }
- }
-
- impl Deref for TestKey {
- type Target = [u8];
- fn deref(&self) -> &Self::Target {
- &self.0
- }
- }
-
- #[test]
- fn test_with_encrypted_characteristics() -> anyhow::Result<()> {
- let temp_dir = TempDir::new("test_with_encrypted_characteristics").unwrap();
- std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
-
- let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
- let super_key =
- Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
-
- std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
-
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND,
- )
- .unwrap();
- make_encrypted_characteristics_file(
- &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
- &super_key,
- KEY_PARAMETERS,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
- USRCERT_AUTHBOUND,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
- CACERT_AUTHBOUND,
- )
- .unwrap();
-
- let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
-
- assert_eq!(
- legacy_blob_loader
- .load_by_uid_alias(10223, "authbound", &None)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<Error>(),
- Some(&Error::LockedComponent)
- );
-
- assert_eq!(
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &Some(super_key)).unwrap(),
- (
- Some((
- Blob {
- flags: 4,
- value: BlobValue::Encrypted {
- data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
- iv: USRPKEY_AUTHBOUND_IV.to_vec(),
- tag: USRPKEY_AUTHBOUND_TAG.to_vec()
- }
- },
- structured_test_params()
- )),
- Some(LOADED_CERT_AUTHBOUND.to_vec()),
- Some(LOADED_CACERT_AUTHBOUND.to_vec())
- )
- );
-
- legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
-
- assert_eq!(
- (None, None, None),
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None).unwrap()
- );
-
- // The database should not be empty due to the super key.
- assert!(!legacy_blob_loader.is_empty().unwrap());
- assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
-
- // The database should be considered empty for user 1.
- assert!(legacy_blob_loader.is_empty_user(1).unwrap());
-
- legacy_blob_loader.remove_super_key(0);
-
- // Now it should be empty.
- assert!(legacy_blob_loader.is_empty_user(0).unwrap());
- assert!(legacy_blob_loader.is_empty().unwrap());
-
- Ok(())
- }
-
- #[test]
- fn test_with_encrypted_certificates() -> anyhow::Result<()> {
- let temp_dir = TempDir::new("test_with_encrypted_certificates").unwrap();
- std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
-
- let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
- let super_key =
- Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
-
- std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
-
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND_CHR,
- )
- .unwrap();
- make_encrypted_usr_cert_file(
- &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
- &super_key,
- LOADED_CERT_AUTHBOUND,
- )
- .unwrap();
- make_encrypted_ca_cert_file(
- &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
- &super_key,
- LOADED_CACERT_AUTHBOUND,
- )
- .unwrap();
-
- let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
-
- assert_eq!(
- legacy_blob_loader
- .load_by_uid_alias(10223, "authbound", &None)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<Error>(),
- Some(&Error::LockedComponent)
- );
-
- assert_eq!(
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &Some(super_key)).unwrap(),
- (
- Some((
- Blob {
- flags: 4,
- value: BlobValue::Encrypted {
- data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
- iv: USRPKEY_AUTHBOUND_IV.to_vec(),
- tag: USRPKEY_AUTHBOUND_TAG.to_vec()
- }
- },
- structured_test_params_cache()
- )),
- Some(LOADED_CERT_AUTHBOUND.to_vec()),
- Some(LOADED_CACERT_AUTHBOUND.to_vec())
- )
- );
-
- legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
-
- assert_eq!(
- (None, None, None),
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None).unwrap()
- );
-
- // The database should not be empty due to the super key.
- assert!(!legacy_blob_loader.is_empty().unwrap());
- assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
-
- // The database should be considered empty for user 1.
- assert!(legacy_blob_loader.is_empty_user(1).unwrap());
-
- legacy_blob_loader.remove_super_key(0);
-
- // Now it should be empty.
- assert!(legacy_blob_loader.is_empty_user(0).unwrap());
- assert!(legacy_blob_loader.is_empty().unwrap());
-
- Ok(())
- }
-
- #[test]
- fn test_in_place_key_migration() -> anyhow::Result<()> {
- let temp_dir = TempDir::new("test_in_place_key_migration").unwrap();
- std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
-
- let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
- let super_key =
- Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
-
- std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
-
- std::fs::write(
- &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND,
- )
- .unwrap();
- std::fs::write(
- &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
- USRPKEY_AUTHBOUND_CHR,
- )
- .unwrap();
- make_encrypted_usr_cert_file(
- &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
- &super_key,
- LOADED_CERT_AUTHBOUND,
- )
- .unwrap();
- make_encrypted_ca_cert_file(
- &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
- &super_key,
- LOADED_CACERT_AUTHBOUND,
- )
- .unwrap();
-
- let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
-
- assert_eq!(
- legacy_blob_loader
- .load_by_uid_alias(10223, "authbound", &None)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<Error>(),
- Some(&Error::LockedComponent)
- );
-
- let super_key: Option<Arc<dyn AesGcm>> = Some(super_key);
-
- assert_eq!(
- legacy_blob_loader.load_by_uid_alias(10223, "authbound", &super_key).unwrap(),
- (
- Some((
- Blob {
- flags: 4,
- value: BlobValue::Encrypted {
- data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
- iv: USRPKEY_AUTHBOUND_IV.to_vec(),
- tag: USRPKEY_AUTHBOUND_TAG.to_vec()
- }
- },
- structured_test_params_cache()
- )),
- Some(LOADED_CERT_AUTHBOUND.to_vec()),
- Some(LOADED_CACERT_AUTHBOUND.to_vec())
- )
- );
-
- legacy_blob_loader.move_keystore_entry(10223, 10224, "authbound", "boundauth").unwrap();
-
- assert_eq!(
- legacy_blob_loader
- .load_by_uid_alias(10224, "boundauth", &None)
- .unwrap_err()
- .root_cause()
- .downcast_ref::<Error>(),
- Some(&Error::LockedComponent)
- );
-
- assert_eq!(
- legacy_blob_loader.load_by_uid_alias(10224, "boundauth", &super_key).unwrap(),
- (
- Some((
- Blob {
- flags: 4,
- value: BlobValue::Encrypted {
- data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
- iv: USRPKEY_AUTHBOUND_IV.to_vec(),
- tag: USRPKEY_AUTHBOUND_TAG.to_vec()
- }
- },
- structured_test_params_cache()
- )),
- Some(LOADED_CERT_AUTHBOUND.to_vec()),
- Some(LOADED_CACERT_AUTHBOUND.to_vec())
- )
- );
-
- legacy_blob_loader.remove_keystore_entry(10224, "boundauth").expect("This should succeed.");
-
- assert_eq!(
- (None, None, None),
- legacy_blob_loader.load_by_uid_alias(10224, "boundauth", &None).unwrap()
- );
-
- // The database should not be empty due to the super key.
- assert!(!legacy_blob_loader.is_empty().unwrap());
- assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
-
- // The database should be considered empty for user 1.
- assert!(legacy_blob_loader.is_empty_user(1).unwrap());
-
- legacy_blob_loader.remove_super_key(0);
-
- // Now it should be empty.
- assert!(legacy_blob_loader.is_empty_user(0).unwrap());
- assert!(legacy_blob_loader.is_empty().unwrap());
-
- Ok(())
- }
-
- #[test]
- fn list_non_existing_user() -> Result<()> {
- let temp_dir = TempDir::new("list_non_existing_user").unwrap();
- let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
-
- assert!(legacy_blob_loader.list_user(20)?.is_empty());
-
- Ok(())
- }
-
- #[test]
- fn list_legacy_keystore_entries_on_non_existing_user() -> Result<()> {
- let temp_dir = TempDir::new("list_legacy_keystore_entries_on_non_existing_user").unwrap();
- let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
-
- assert!(legacy_blob_loader.list_legacy_keystore_entries_for_user(20)?.is_empty());
-
- Ok(())
- }
-
- #[test]
- fn test_move_keystore_entry() {
- let temp_dir = TempDir::new("test_move_keystore_entry").unwrap();
- std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
-
- const SOME_CONTENT: &[u8] = b"some content";
- const ANOTHER_CONTENT: &[u8] = b"another content";
- const SOME_FILENAME: &str = "some_file";
- const ANOTHER_FILENAME: &str = "another_file";
-
- std::fs::write(&*temp_dir.build().push("user_0").push(SOME_FILENAME), SOME_CONTENT)
- .unwrap();
-
- std::fs::write(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME), ANOTHER_CONTENT)
- .unwrap();
-
- // Non existent source id silently ignored.
- assert!(LegacyBlobLoader::move_keystore_file_if_exists(
- 1,
- 2,
- "non_existent",
- ANOTHER_FILENAME,
- "ignored",
- |_, alias, _| temp_dir.build().push("user_0").push(alias).to_path_buf()
- )
- .is_ok());
-
- // Content of another_file has not changed.
- let another_content =
- std::fs::read(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME)).unwrap();
- assert_eq!(&another_content, ANOTHER_CONTENT);
-
- // Check that some_file still exists.
- assert!(temp_dir.build().push("user_0").push(SOME_FILENAME).exists());
- // Existing target files are silently overwritten.
-
- assert!(LegacyBlobLoader::move_keystore_file_if_exists(
- 1,
- 2,
- SOME_FILENAME,
- ANOTHER_FILENAME,
- "ignored",
- |_, alias, _| temp_dir.build().push("user_0").push(alias).to_path_buf()
- )
- .is_ok());
-
- // Content of another_file is now "some content".
- let another_content =
- std::fs::read(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME)).unwrap();
- assert_eq!(&another_content, SOME_CONTENT);
-
- // Check that some_file no longer exists.
- assert!(!temp_dir.build().push("user_0").push(SOME_FILENAME).exists());
- }
-}
diff --git a/keystore2/src/legacy_blob/tests.rs b/keystore2/src/legacy_blob/tests.rs
new file mode 100644
index 00000000..53fe03ff
--- /dev/null
+++ b/keystore2/src/legacy_blob/tests.rs
@@ -0,0 +1,676 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Tests for legacy keyblob processing.
+
+#![allow(dead_code)]
+use super::*;
+use crate::legacy_blob::test_utils::legacy_blob_test_vectors::*;
+use crate::legacy_blob::test_utils::*;
+use anyhow::{anyhow, Result};
+use keystore2_crypto::aes_gcm_decrypt;
+use keystore2_test_utils::TempDir;
+use rand::Rng;
+use std::convert::TryInto;
+use std::ops::Deref;
+use std::string::FromUtf8Error;
+
+#[test]
+fn decode_encode_alias_test() {
+ static ALIAS: &str = "#({}test[])😗";
+ static ENCODED_ALIAS: &str = "+S+X{}test[]+Y.`-O-H-G";
+ // Second multi byte out of range ------v
+ static ENCODED_ALIAS_ERROR1: &str = "+S+{}test[]+Y";
+ // Incomplete multi byte ------------------------v
+ static ENCODED_ALIAS_ERROR2: &str = "+S+X{}test[]+";
+ // Our encoding: ".`-O-H-G"
+ // is UTF-8: 0xF0 0x9F 0x98 0x97
+ // is UNICODE: U+1F617
+ // is 😗
+ // But +H below is a valid encoding for 0x18 making this sequence invalid UTF-8.
+ static ENCODED_ALIAS_ERROR_UTF8: &str = ".`-O+H-G";
+
+ assert_eq!(ENCODED_ALIAS, &LegacyBlobLoader::encode_alias(ALIAS));
+ assert_eq!(ALIAS, &LegacyBlobLoader::decode_alias(ENCODED_ALIAS).unwrap());
+ assert_eq!(
+ Some(&Error::BadEncoding),
+ LegacyBlobLoader::decode_alias(ENCODED_ALIAS_ERROR1)
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<Error>()
+ );
+ assert_eq!(
+ Some(&Error::BadEncoding),
+ LegacyBlobLoader::decode_alias(ENCODED_ALIAS_ERROR2)
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<Error>()
+ );
+ assert!(LegacyBlobLoader::decode_alias(ENCODED_ALIAS_ERROR_UTF8)
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<FromUtf8Error>()
+ .is_some());
+
+ for _i in 0..100 {
+ // Any valid UTF-8 string should be en- and decoded without loss.
+ let alias_str = rand::thread_rng().gen::<[char; 20]>().iter().collect::<String>();
+ let random_alias = alias_str.as_bytes();
+ let encoded = LegacyBlobLoader::encode_alias(&alias_str);
+ let decoded = match LegacyBlobLoader::decode_alias(&encoded) {
+ Ok(d) => d,
+ Err(_) => panic!("random_alias: {:x?}\nencoded {}", random_alias, encoded),
+ };
+ assert_eq!(random_alias.to_vec(), decoded.bytes().collect::<Vec<u8>>());
+ }
+}
+
+#[test]
+fn read_golden_key_blob_test() -> anyhow::Result<()> {
+ let blob = LegacyBlobLoader::new_from_stream_decrypt_with(&mut &*BLOB, |_, _, _, _, _| {
+ Err(anyhow!("should not be called"))
+ })
+ .unwrap();
+ assert!(!blob.is_encrypted());
+ assert!(!blob.is_fallback());
+ assert!(!blob.is_strongbox());
+ assert!(!blob.is_critical_to_device_encryption());
+ assert_eq!(blob.value(), &BlobValue::Generic([0xde, 0xed, 0xbe, 0xef].to_vec()));
+
+ let blob =
+ LegacyBlobLoader::new_from_stream_decrypt_with(&mut &*REAL_LEGACY_BLOB, |_, _, _, _, _| {
+ Err(anyhow!("should not be called"))
+ })
+ .unwrap();
+ assert!(!blob.is_encrypted());
+ assert!(!blob.is_fallback());
+ assert!(!blob.is_strongbox());
+ assert!(!blob.is_critical_to_device_encryption());
+ assert_eq!(blob.value(), &BlobValue::Decrypted(REAL_LEGACY_BLOB_PAYLOAD.try_into().unwrap()));
+ Ok(())
+}
+
+#[test]
+fn read_aes_gcm_encrypted_key_blob_test() {
+ let blob = LegacyBlobLoader::new_from_stream_decrypt_with(
+ &mut &*AES_GCM_ENCRYPTED_BLOB,
+ |d, iv, tag, salt, key_size| {
+ assert_eq!(salt, None);
+ assert_eq!(key_size, None);
+ assert_eq!(
+ iv,
+ &[
+ 0xbd, 0xdb, 0x8d, 0x69, 0x72, 0x56, 0xf0, 0xf5, 0xa4, 0x02, 0x88, 0x7f, 0x00,
+ 0x00, 0x00, 0x00,
+ ]
+ );
+ assert_eq!(
+ tag,
+ &[
+ 0x50, 0xd9, 0x97, 0x95, 0x37, 0x6e, 0x28, 0x6a, 0x28, 0x9d, 0x51, 0xb9, 0xb9,
+ 0xe0, 0x0b, 0xc3
+ ][..]
+ );
+ aes_gcm_decrypt(d, iv, tag, AES_KEY).context("Trying to decrypt blob.")
+ },
+ )
+ .unwrap();
+ assert!(blob.is_encrypted());
+ assert!(!blob.is_fallback());
+ assert!(!blob.is_strongbox());
+ assert!(!blob.is_critical_to_device_encryption());
+
+ assert_eq!(blob.value(), &BlobValue::Decrypted(DECRYPTED_PAYLOAD.try_into().unwrap()));
+}
+
+#[test]
+fn read_golden_key_blob_too_short_test() {
+ let error =
+ LegacyBlobLoader::new_from_stream_decrypt_with(&mut &BLOB[0..15], |_, _, _, _, _| {
+ Err(anyhow!("should not be called"))
+ })
+ .unwrap_err();
+ assert_eq!(Some(&Error::BadLen), error.root_cause().downcast_ref::<Error>());
+}
+
+#[test]
+fn test_is_empty() {
+ let temp_dir = TempDir::new("test_is_empty").expect("Failed to create temp dir.");
+ let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+ assert!(legacy_blob_loader.is_empty().expect("Should succeed and be empty."));
+
+ let _db =
+ crate::database::KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database.");
+
+ assert!(legacy_blob_loader.is_empty().expect("Should succeed and still be empty."));
+
+ std::fs::create_dir(&*temp_dir.build().push("user_0")).expect("Failed to create user_0.");
+
+ assert!(!legacy_blob_loader.is_empty().expect("Should succeed but not be empty."));
+
+ std::fs::create_dir(&*temp_dir.build().push("user_10")).expect("Failed to create user_10.");
+
+ assert!(!legacy_blob_loader.is_empty().expect("Should succeed but still not be empty."));
+
+ std::fs::remove_dir_all(&*temp_dir.build().push("user_0")).expect("Failed to remove user_0.");
+
+ assert!(!legacy_blob_loader.is_empty().expect("Should succeed but still not be empty."));
+
+ std::fs::remove_dir_all(&*temp_dir.build().push("user_10")).expect("Failed to remove user_10.");
+
+ assert!(legacy_blob_loader.is_empty().expect("Should succeed and be empty again."));
+}
+
+#[test]
+fn test_legacy_blobs() -> anyhow::Result<()> {
+ let temp_dir = TempDir::new("legacy_blob_test").unwrap();
+ std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
+
+ std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
+
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
+ USRPKEY_AUTHBOUND,
+ )
+ .unwrap();
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
+ USRPKEY_AUTHBOUND_CHR,
+ )
+ .unwrap();
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
+ USRCERT_AUTHBOUND,
+ )
+ .unwrap();
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
+ CACERT_AUTHBOUND,
+ )
+ .unwrap();
+
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_USRPKEY_non_authbound"),
+ USRPKEY_NON_AUTHBOUND,
+ )
+ .unwrap();
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_non_authbound"),
+ USRPKEY_NON_AUTHBOUND_CHR,
+ )
+ .unwrap();
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_USRCERT_non_authbound"),
+ USRCERT_NON_AUTHBOUND,
+ )
+ .unwrap();
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_CACERT_non_authbound"),
+ CACERT_NON_AUTHBOUND,
+ )
+ .unwrap();
+
+ let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+ if let (Some((Blob { flags, value }, _params)), Some(cert), Some(chain)) =
+ legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
+ {
+ assert_eq!(flags, 4);
+ assert_eq!(
+ value,
+ BlobValue::Encrypted {
+ data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
+ iv: USRPKEY_AUTHBOUND_IV.to_vec(),
+ tag: USRPKEY_AUTHBOUND_TAG.to_vec()
+ }
+ );
+ assert_eq!(&cert[..], LOADED_CERT_AUTHBOUND);
+ assert_eq!(&chain[..], LOADED_CACERT_AUTHBOUND);
+ } else {
+ panic!("");
+ }
+
+ if let (Some((Blob { flags, value: _ }, _params)), Some(cert), Some(chain)) =
+ legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
+ {
+ assert_eq!(flags, 4);
+ //assert_eq!(value, BlobValue::Encrypted(..));
+ assert_eq!(&cert[..], LOADED_CERT_AUTHBOUND);
+ assert_eq!(&chain[..], LOADED_CACERT_AUTHBOUND);
+ } else {
+ panic!("");
+ }
+ if let (Some((Blob { flags, value }, _params)), Some(cert), Some(chain)) =
+ legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", &None)?
+ {
+ assert_eq!(flags, 0);
+ assert_eq!(value, BlobValue::Decrypted(LOADED_USRPKEY_NON_AUTHBOUND.try_into()?));
+ assert_eq!(&cert[..], LOADED_CERT_NON_AUTHBOUND);
+ assert_eq!(&chain[..], LOADED_CACERT_NON_AUTHBOUND);
+ } else {
+ panic!("");
+ }
+
+ legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
+ legacy_blob_loader.remove_keystore_entry(10223, "non_authbound").expect("This should succeed.");
+
+ assert_eq!(
+ (None, None, None),
+ legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None)?
+ );
+ assert_eq!(
+ (None, None, None),
+ legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", &None)?
+ );
+
+ // The database should not be empty due to the super key.
+ assert!(!legacy_blob_loader.is_empty()?);
+ assert!(!legacy_blob_loader.is_empty_user(0)?);
+
+ // The database should be considered empty for user 1.
+ assert!(legacy_blob_loader.is_empty_user(1)?);
+
+ legacy_blob_loader.remove_super_key(0);
+
+ // Now it should be empty.
+ assert!(legacy_blob_loader.is_empty_user(0)?);
+ assert!(legacy_blob_loader.is_empty()?);
+
+ Ok(())
+}
+
+struct TestKey(ZVec);
+
+impl crate::utils::AesGcmKey for TestKey {
+ fn key(&self) -> &[u8] {
+ &self.0
+ }
+}
+
+impl Deref for TestKey {
+ type Target = [u8];
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+#[test]
+fn test_with_encrypted_characteristics() -> anyhow::Result<()> {
+ let temp_dir = TempDir::new("test_with_encrypted_characteristics").unwrap();
+ std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
+
+ let pw: Password = PASSWORD.into();
+ let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
+ let super_key =
+ Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
+
+ std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
+
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
+ USRPKEY_AUTHBOUND,
+ )
+ .unwrap();
+ make_encrypted_characteristics_file(
+ &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
+ &super_key,
+ KEY_PARAMETERS,
+ )
+ .unwrap();
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
+ USRCERT_AUTHBOUND,
+ )
+ .unwrap();
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
+ CACERT_AUTHBOUND,
+ )
+ .unwrap();
+
+ let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+ assert_eq!(
+ legacy_blob_loader
+ .load_by_uid_alias(10223, "authbound", &None)
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<Error>(),
+ Some(&Error::LockedComponent)
+ );
+
+ assert_eq!(
+ legacy_blob_loader.load_by_uid_alias(10223, "authbound", &Some(super_key)).unwrap(),
+ (
+ Some((
+ Blob {
+ flags: 4,
+ value: BlobValue::Encrypted {
+ data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
+ iv: USRPKEY_AUTHBOUND_IV.to_vec(),
+ tag: USRPKEY_AUTHBOUND_TAG.to_vec()
+ }
+ },
+ structured_test_params()
+ )),
+ Some(LOADED_CERT_AUTHBOUND.to_vec()),
+ Some(LOADED_CACERT_AUTHBOUND.to_vec())
+ )
+ );
+
+ legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
+
+ assert_eq!(
+ (None, None, None),
+ legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None).unwrap()
+ );
+
+ // The database should not be empty due to the super key.
+ assert!(!legacy_blob_loader.is_empty().unwrap());
+ assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
+
+ // The database should be considered empty for user 1.
+ assert!(legacy_blob_loader.is_empty_user(1).unwrap());
+
+ legacy_blob_loader.remove_super_key(0);
+
+ // Now it should be empty.
+ assert!(legacy_blob_loader.is_empty_user(0).unwrap());
+ assert!(legacy_blob_loader.is_empty().unwrap());
+
+ Ok(())
+}
+
+#[test]
+fn test_with_encrypted_certificates() -> anyhow::Result<()> {
+ let temp_dir = TempDir::new("test_with_encrypted_certificates").unwrap();
+ std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
+
+ let pw: Password = PASSWORD.into();
+ let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
+ let super_key =
+ Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
+
+ std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
+
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
+ USRPKEY_AUTHBOUND,
+ )
+ .unwrap();
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
+ USRPKEY_AUTHBOUND_CHR,
+ )
+ .unwrap();
+ make_encrypted_usr_cert_file(
+ &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
+ &super_key,
+ LOADED_CERT_AUTHBOUND,
+ )
+ .unwrap();
+ make_encrypted_ca_cert_file(
+ &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
+ &super_key,
+ LOADED_CACERT_AUTHBOUND,
+ )
+ .unwrap();
+
+ let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+ assert_eq!(
+ legacy_blob_loader
+ .load_by_uid_alias(10223, "authbound", &None)
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<Error>(),
+ Some(&Error::LockedComponent)
+ );
+
+ assert_eq!(
+ legacy_blob_loader.load_by_uid_alias(10223, "authbound", &Some(super_key)).unwrap(),
+ (
+ Some((
+ Blob {
+ flags: 4,
+ value: BlobValue::Encrypted {
+ data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
+ iv: USRPKEY_AUTHBOUND_IV.to_vec(),
+ tag: USRPKEY_AUTHBOUND_TAG.to_vec()
+ }
+ },
+ structured_test_params_cache()
+ )),
+ Some(LOADED_CERT_AUTHBOUND.to_vec()),
+ Some(LOADED_CACERT_AUTHBOUND.to_vec())
+ )
+ );
+
+ legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
+
+ assert_eq!(
+ (None, None, None),
+ legacy_blob_loader.load_by_uid_alias(10223, "authbound", &None).unwrap()
+ );
+
+ // The database should not be empty due to the super key.
+ assert!(!legacy_blob_loader.is_empty().unwrap());
+ assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
+
+ // The database should be considered empty for user 1.
+ assert!(legacy_blob_loader.is_empty_user(1).unwrap());
+
+ legacy_blob_loader.remove_super_key(0);
+
+ // Now it should be empty.
+ assert!(legacy_blob_loader.is_empty_user(0).unwrap());
+ assert!(legacy_blob_loader.is_empty().unwrap());
+
+ Ok(())
+}
+
+#[test]
+fn test_in_place_key_migration() -> anyhow::Result<()> {
+ let temp_dir = TempDir::new("test_in_place_key_migration").unwrap();
+ std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
+
+ let pw: Password = PASSWORD.into();
+ let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
+ let super_key =
+ Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
+
+ std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY).unwrap();
+
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
+ USRPKEY_AUTHBOUND,
+ )
+ .unwrap();
+ std::fs::write(
+ &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
+ USRPKEY_AUTHBOUND_CHR,
+ )
+ .unwrap();
+ make_encrypted_usr_cert_file(
+ &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
+ &super_key,
+ LOADED_CERT_AUTHBOUND,
+ )
+ .unwrap();
+ make_encrypted_ca_cert_file(
+ &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
+ &super_key,
+ LOADED_CACERT_AUTHBOUND,
+ )
+ .unwrap();
+
+ let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+ assert_eq!(
+ legacy_blob_loader
+ .load_by_uid_alias(10223, "authbound", &None)
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<Error>(),
+ Some(&Error::LockedComponent)
+ );
+
+ let super_key: Option<Arc<dyn AesGcm>> = Some(super_key);
+
+ assert_eq!(
+ legacy_blob_loader.load_by_uid_alias(10223, "authbound", &super_key).unwrap(),
+ (
+ Some((
+ Blob {
+ flags: 4,
+ value: BlobValue::Encrypted {
+ data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
+ iv: USRPKEY_AUTHBOUND_IV.to_vec(),
+ tag: USRPKEY_AUTHBOUND_TAG.to_vec()
+ }
+ },
+ structured_test_params_cache()
+ )),
+ Some(LOADED_CERT_AUTHBOUND.to_vec()),
+ Some(LOADED_CACERT_AUTHBOUND.to_vec())
+ )
+ );
+
+ legacy_blob_loader.move_keystore_entry(10223, 10224, "authbound", "boundauth").unwrap();
+
+ assert_eq!(
+ legacy_blob_loader
+ .load_by_uid_alias(10224, "boundauth", &None)
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<Error>(),
+ Some(&Error::LockedComponent)
+ );
+
+ assert_eq!(
+ legacy_blob_loader.load_by_uid_alias(10224, "boundauth", &super_key).unwrap(),
+ (
+ Some((
+ Blob {
+ flags: 4,
+ value: BlobValue::Encrypted {
+ data: USRPKEY_AUTHBOUND_ENC_PAYLOAD.to_vec(),
+ iv: USRPKEY_AUTHBOUND_IV.to_vec(),
+ tag: USRPKEY_AUTHBOUND_TAG.to_vec()
+ }
+ },
+ structured_test_params_cache()
+ )),
+ Some(LOADED_CERT_AUTHBOUND.to_vec()),
+ Some(LOADED_CACERT_AUTHBOUND.to_vec())
+ )
+ );
+
+ legacy_blob_loader.remove_keystore_entry(10224, "boundauth").expect("This should succeed.");
+
+ assert_eq!(
+ (None, None, None),
+ legacy_blob_loader.load_by_uid_alias(10224, "boundauth", &None).unwrap()
+ );
+
+ // The database should not be empty due to the super key.
+ assert!(!legacy_blob_loader.is_empty().unwrap());
+ assert!(!legacy_blob_loader.is_empty_user(0).unwrap());
+
+ // The database should be considered empty for user 1.
+ assert!(legacy_blob_loader.is_empty_user(1).unwrap());
+
+ legacy_blob_loader.remove_super_key(0);
+
+ // Now it should be empty.
+ assert!(legacy_blob_loader.is_empty_user(0).unwrap());
+ assert!(legacy_blob_loader.is_empty().unwrap());
+
+ Ok(())
+}
+
+#[test]
+fn list_non_existing_user() -> Result<()> {
+ let temp_dir = TempDir::new("list_non_existing_user").unwrap();
+ let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+ assert!(legacy_blob_loader.list_user(20)?.is_empty());
+
+ Ok(())
+}
+
+#[test]
+fn list_legacy_keystore_entries_on_non_existing_user() -> Result<()> {
+ let temp_dir = TempDir::new("list_legacy_keystore_entries_on_non_existing_user").unwrap();
+ let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+ assert!(legacy_blob_loader.list_legacy_keystore_entries_for_user(20)?.is_empty());
+
+ Ok(())
+}
+
+#[test]
+fn test_move_keystore_entry() {
+ let temp_dir = TempDir::new("test_move_keystore_entry").unwrap();
+ std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
+
+ const SOME_CONTENT: &[u8] = b"some content";
+ const ANOTHER_CONTENT: &[u8] = b"another content";
+ const SOME_FILENAME: &str = "some_file";
+ const ANOTHER_FILENAME: &str = "another_file";
+
+ std::fs::write(&*temp_dir.build().push("user_0").push(SOME_FILENAME), SOME_CONTENT).unwrap();
+
+ std::fs::write(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME), ANOTHER_CONTENT)
+ .unwrap();
+
+ // Non existent source id silently ignored.
+ assert!(LegacyBlobLoader::move_keystore_file_if_exists(
+ 1,
+ 2,
+ "non_existent",
+ ANOTHER_FILENAME,
+ "ignored",
+ |_, alias, _| temp_dir.build().push("user_0").push(alias).to_path_buf()
+ )
+ .is_ok());
+
+ // Content of another_file has not changed.
+ let another_content =
+ std::fs::read(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME)).unwrap();
+ assert_eq!(&another_content, ANOTHER_CONTENT);
+
+ // Check that some_file still exists.
+ assert!(temp_dir.build().push("user_0").push(SOME_FILENAME).exists());
+ // Existing target files are silently overwritten.
+
+ assert!(LegacyBlobLoader::move_keystore_file_if_exists(
+ 1,
+ 2,
+ SOME_FILENAME,
+ ANOTHER_FILENAME,
+ "ignored",
+ |_, alias, _| temp_dir.build().push("user_0").push(alias).to_path_buf()
+ )
+ .is_ok());
+
+ // Content of another_file is now "some content".
+ let another_content =
+ std::fs::read(&*temp_dir.build().push("user_0").push(ANOTHER_FILENAME)).unwrap();
+ assert_eq!(&another_content, SOME_CONTENT);
+
+ // Check that some_file no longer exists.
+ assert!(!temp_dir.build().push("user_0").push(SOME_FILENAME).exists());
+}
diff --git a/keystore2/src/legacy_importer.rs b/keystore2/src/legacy_importer.rs
index 7dcb98d6..24f32637 100644
--- a/keystore2/src/legacy_importer.rs
+++ b/keystore2/src/legacy_importer.rs
@@ -202,7 +202,7 @@ impl LegacyImporter {
/// List all aliases for uid in the legacy database.
pub fn list_uid(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
- let _wp = wd::watch_millis("LegacyImporter::list_uid", 500);
+ let _wp = wd::watch("LegacyImporter::list_uid");
let uid = match (domain, namespace) {
(Domain::APP, namespace) => namespace as u32,
@@ -299,7 +299,7 @@ impl LegacyImporter {
where
F: Fn() -> Result<T>,
{
- let _wp = wd::watch_millis("LegacyImporter::with_try_import", 500);
+ let _wp = wd::watch("LegacyImporter::with_try_import");
// Access the key and return on success.
match key_accessor() {
@@ -355,7 +355,7 @@ impl LegacyImporter {
where
F: FnMut() -> Result<Option<T>>,
{
- let _wp = wd::watch_millis("LegacyImporter::with_try_import_super_key", 500);
+ let _wp = wd::watch("LegacyImporter::with_try_import_super_key");
match key_accessor() {
Ok(Some(result)) => return Ok(Some(result)),
@@ -379,7 +379,7 @@ impl LegacyImporter {
/// Deletes all keys belonging to the given namespace, importing them into the database
/// for subsequent garbage collection if necessary.
pub fn bulk_delete_uid(&self, domain: Domain, nspace: i64) -> Result<()> {
- let _wp = wd::watch_millis("LegacyImporter::bulk_delete_uid", 500);
+ let _wp = wd::watch("LegacyImporter::bulk_delete_uid");
let uid = match (domain, nspace) {
(Domain::APP, nspace) => nspace as u32,
@@ -402,7 +402,7 @@ impl LegacyImporter {
user_id: u32,
keep_non_super_encrypted_keys: bool,
) -> Result<()> {
- let _wp = wd::watch_millis("LegacyImporter::bulk_delete_user", 500);
+ let _wp = wd::watch("LegacyImporter::bulk_delete_user");
let result = self.do_serialized(move |importer_state| {
importer_state
@@ -923,11 +923,11 @@ fn get_key_characteristics_without_app_data(
blob,
&[],
|blob| {
- let _wd = wd::watch_millis("Calling GetKeyCharacteristics.", 500);
+ let _wd = wd::watch("get_key_characteristics_without_app_data: calling IKeyMintDevice::getKeyCharacteristics");
map_km_error(km_dev.getKeyCharacteristics(blob, &[], &[]))
},
|_| Ok(()),
)
- .context(ks_err!())?;
+ .context(ks_err!("getKeyCharacteristics failed: possibly invalid keyblob for uuid {uuid:?}"))?;
Ok((key_characteristics_to_internal(characteristics), upgraded_blob))
}
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 3e34cff3..61277f10 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -14,21 +14,21 @@
//! This module implements IKeystoreMaintenance AIDL interface.
-use crate::database::{BootTime, KeyEntryLoadBits, KeyType};
+use crate::database::{KeyEntryLoadBits, KeyType};
+use crate::error::into_logged_binder;
use crate::error::map_km_error;
-use crate::error::map_or_log_err;
use crate::error::Error;
use crate::globals::get_keymint_device;
use crate::globals::{DB, LEGACY_IMPORTER, SUPER_KEY};
use crate::ks_err;
use crate::permission::{KeyPerm, KeystorePerm};
-use crate::super_key::{SuperKeyManager, UserState};
+use crate::super_key::SuperKeyManager;
use crate::utils::{
check_get_app_uids_affected_by_sid_permissions, check_key_permission,
check_keystore_permission, uid_to_android_user, watchdog as wd,
};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- IKeyMintDevice::IKeyMintDevice, SecurityLevel::SecurityLevel,
+ ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice, SecurityLevel::SecurityLevel,
};
use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::{
BnKeystoreMaintenance, IKeystoreMaintenance,
@@ -69,40 +69,6 @@ impl Maintenance {
))
}
- fn on_user_password_changed(user_id: i32, password: Option<Password>) -> Result<()> {
- // Check permission. Function should return if this failed. Therefore having '?' at the end
- // is very important.
- check_keystore_permission(KeystorePerm::ChangePassword).context(ks_err!())?;
-
- let mut skm = SUPER_KEY.write().unwrap();
-
- if let Some(pw) = password.as_ref() {
- DB.with(|db| {
- skm.unlock_unlocked_device_required_keys(&mut db.borrow_mut(), user_id as u32, pw)
- })
- .context(ks_err!("unlock_unlocked_device_required_keys failed"))?;
- }
-
- if let UserState::BeforeFirstUnlock = DB
- .with(|db| skm.get_user_state(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32))
- .context(ks_err!("Could not get user state while changing password!"))?
- {
- // Error - password can not be changed when the device is locked
- return Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."));
- }
-
- DB.with(|db| match password {
- Some(pass) => {
- skm.init_user(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32, &pass)
- }
- None => {
- // User transitioned to swipe.
- skm.reset_user(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32)
- }
- })
- .context(ks_err!("Failed to change user password!"))
- }
-
fn add_or_remove_user(&self, user_id: i32) -> Result<()> {
// Check permission. Function should return if this failed. Therefore having '?' at the end
// is very important.
@@ -177,9 +143,7 @@ impl Maintenance {
let (km_dev, _, _) =
get_keymint_device(&sec_level).context(ks_err!("getting keymint device"))?;
- let _wp = wd::watch_millis_with("In call_with_watchdog", 500, move || {
- format!("Seclevel: {:?} Op: {}", sec_level, name)
- });
+ let _wp = wd::watch_millis_with("Maintenance::call_with_watchdog", 500, (sec_level, name));
map_km_error(op(km_dev)).with_context(|| ks_err!("calling {}", name))?;
Ok(())
}
@@ -200,12 +164,21 @@ impl Maintenance {
name,
&sec_level_string
),
- Err(ref e) => log::error!(
- "Call to {} failed for security level {}: {}.",
- name,
- &sec_level_string,
- e
- ),
+ Err(ref e) => {
+ if *sec_level == SecurityLevel::STRONGBOX
+ && e.downcast_ref::<Error>()
+ == Some(&Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+ {
+ log::info!("Call to {} failed for StrongBox as it is not available", name,)
+ } else {
+ log::error!(
+ "Call to {} failed for security level {}: {}.",
+ name,
+ &sec_level_string,
+ e
+ )
+ }
+ }
}
curr_result
})
@@ -224,14 +197,6 @@ impl Maintenance {
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();
@@ -304,20 +269,10 @@ impl Maintenance {
impl Interface for Maintenance {}
impl IKeystoreMaintenance for Maintenance {
- fn onUserPasswordChanged(&self, user_id: i32, password: Option<&[u8]>) -> BinderResult<()> {
- log::info!(
- "onUserPasswordChanged(user={}, password.is_some()={})",
- user_id,
- password.is_some()
- );
- let _wp = wd::watch_millis("IKeystoreMaintenance::onUserPasswordChanged", 500);
- map_or_log_err(Self::on_user_password_changed(user_id, password.map(|pw| pw.into())), Ok)
- }
-
fn onUserAdded(&self, user_id: i32) -> BinderResult<()> {
log::info!("onUserAdded(user={user_id})");
- let _wp = wd::watch_millis("IKeystoreMaintenance::onUserAdded", 500);
- map_or_log_err(self.add_or_remove_user(user_id), Ok)
+ let _wp = wd::watch("IKeystoreMaintenance::onUserAdded");
+ self.add_or_remove_user(user_id).map_err(into_logged_binder)
}
fn initUserSuperKeys(
@@ -327,38 +282,33 @@ impl IKeystoreMaintenance for Maintenance {
allow_existing: bool,
) -> BinderResult<()> {
log::info!("initUserSuperKeys(user={user_id}, allow_existing={allow_existing})");
- let _wp = wd::watch_millis("IKeystoreMaintenance::initUserSuperKeys", 500);
- map_or_log_err(self.init_user_super_keys(user_id, password.into(), allow_existing), Ok)
+ let _wp = wd::watch("IKeystoreMaintenance::initUserSuperKeys");
+ self.init_user_super_keys(user_id, password.into(), allow_existing)
+ .map_err(into_logged_binder)
}
fn onUserRemoved(&self, user_id: i32) -> BinderResult<()> {
log::info!("onUserRemoved(user={user_id})");
- let _wp = wd::watch_millis("IKeystoreMaintenance::onUserRemoved", 500);
- map_or_log_err(self.add_or_remove_user(user_id), Ok)
+ let _wp = wd::watch("IKeystoreMaintenance::onUserRemoved");
+ self.add_or_remove_user(user_id).map_err(into_logged_binder)
}
fn onUserLskfRemoved(&self, user_id: i32) -> BinderResult<()> {
log::info!("onUserLskfRemoved(user={user_id})");
- let _wp = wd::watch_millis("IKeystoreMaintenance::onUserLskfRemoved", 500);
- map_or_log_err(Self::on_user_lskf_removed(user_id), Ok)
+ let _wp = wd::watch("IKeystoreMaintenance::onUserLskfRemoved");
+ Self::on_user_lskf_removed(user_id).map_err(into_logged_binder)
}
fn clearNamespace(&self, domain: Domain, nspace: i64) -> BinderResult<()> {
log::info!("clearNamespace({domain:?}, nspace={nspace})");
- let _wp = wd::watch_millis("IKeystoreMaintenance::clearNamespace", 500);
- map_or_log_err(self.clear_namespace(domain, nspace), Ok)
+ let _wp = wd::watch("IKeystoreMaintenance::clearNamespace");
+ self.clear_namespace(domain, nspace).map_err(into_logged_binder)
}
fn earlyBootEnded(&self) -> BinderResult<()> {
log::info!("earlyBootEnded()");
- let _wp = wd::watch_millis("IKeystoreMaintenance::earlyBootEnded", 500);
- 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)
+ let _wp = wd::watch("IKeystoreMaintenance::earlyBootEnded");
+ Self::early_boot_ended().map_err(into_logged_binder)
}
fn migrateKeyNamespace(
@@ -367,14 +317,14 @@ impl IKeystoreMaintenance for Maintenance {
destination: &KeyDescriptor,
) -> BinderResult<()> {
log::info!("migrateKeyNamespace(src={source:?}, dest={destination:?})");
- let _wp = wd::watch_millis("IKeystoreMaintenance::migrateKeyNamespace", 500);
- map_or_log_err(Self::migrate_key_namespace(source, destination), Ok)
+ let _wp = wd::watch("IKeystoreMaintenance::migrateKeyNamespace");
+ Self::migrate_key_namespace(source, destination).map_err(into_logged_binder)
}
fn deleteAllKeys(&self) -> BinderResult<()> {
- log::warn!("deleteAllKeys()");
- let _wp = wd::watch_millis("IKeystoreMaintenance::deleteAllKeys", 500);
- map_or_log_err(Self::delete_all_keys(), Ok)
+ log::warn!("deleteAllKeys() invoked, indicating initial setup or post-factory reset");
+ let _wp = wd::watch("IKeystoreMaintenance::deleteAllKeys");
+ Self::delete_all_keys().map_err(into_logged_binder)
}
fn getAppUidsAffectedBySid(
@@ -383,7 +333,7 @@ impl IKeystoreMaintenance for Maintenance {
secure_user_id: i64,
) -> BinderResult<std::vec::Vec<i64>> {
log::info!("getAppUidsAffectedBySid(secure_user_id={secure_user_id:?})");
- let _wp = wd::watch_millis("IKeystoreMaintenance::getAppUidsAffectedBySid", 500);
- map_or_log_err(Self::get_app_uids_affected_by_sid(user_id, secure_user_id), Ok)
+ let _wp = wd::watch("IKeystoreMaintenance::getAppUidsAffectedBySid");
+ Self::get_app_uids_affected_by_sid(user_id, secure_user_id).map_err(into_logged_binder)
}
}
diff --git a/keystore2/src/metrics.rs b/keystore2/src/metrics.rs
index cd1cd75d..47577393 100644
--- a/keystore2/src/metrics.rs
+++ b/keystore2/src/metrics.rs
@@ -14,7 +14,7 @@
//! This module implements the IKeystoreMetrics AIDL interface, which exposes the API method for the
//! proxy in the system server to pull the aggregated metrics in keystore.
-use crate::error::map_or_log_err;
+use crate::error::into_logged_binder;
use crate::ks_err;
use crate::metrics_store::METRICS_STORE;
use crate::permission::KeystorePerm;
@@ -51,7 +51,7 @@ impl Interface for Metrics {}
impl IKeystoreMetrics for Metrics {
fn pullMetrics(&self, atom_id: AtomID) -> BinderResult<Vec<KeystoreAtom>> {
- let _wp = wd::watch_millis("IKeystoreMetrics::pullMetrics", 500);
- map_or_log_err(self.pull_metrics(atom_id), Ok)
+ let _wp = wd::watch("IKeystoreMetrics::pullMetrics");
+ self.pull_metrics(atom_id).map_err(into_logged_binder)
}
}
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index eabc1abb..27f8ef69 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -127,7 +127,7 @@
use crate::enforcements::AuthInfo;
use crate::error::{
- error_to_serialized_error, map_err_with, map_km_error, map_or_log_err, Error, ErrorCode,
+ error_to_serialized_error, into_binder, into_logged_binder, map_km_error, Error, ErrorCode,
ResponseCode, SerializedError,
};
use crate::ks_err;
@@ -286,11 +286,11 @@ impl Operation {
}
*locked_outcome = Outcome::Pruned;
- let _wp = wd::watch_millis("In Operation::prune: calling abort()", 500);
+ let _wp = wd::watch("Operation::prune: calling IKeyMintOperation::abort()");
// We abort the operation. If there was an error we log it but ignore it.
if let Err(e) = map_km_error(self.km_op.abort()) {
- log::error!("In prune: KeyMint::abort failed with {:?}.", e);
+ log::warn!("In prune: KeyMint::abort failed with {:?}.", e);
}
Ok(())
@@ -362,7 +362,7 @@ impl Operation {
.context(ks_err!("Trying to get auth tokens."))?;
self.update_outcome(&mut outcome, {
- let _wp = wd::watch_millis("Operation::update_aad: calling updateAad", 500);
+ let _wp = wd::watch("Operation::update_aad: calling IKeyMintOperation::updateAad");
map_km_error(self.km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref()))
})
.context(ks_err!("Update failed."))?;
@@ -386,7 +386,7 @@ impl Operation {
let output = self
.update_outcome(&mut outcome, {
- let _wp = wd::watch_millis("Operation::update: calling update", 500);
+ let _wp = wd::watch("Operation::update: calling IKeyMintOperation::update");
map_km_error(self.km_op.update(input, hat.as_ref(), tst.as_ref()))
})
.context(ks_err!("Update failed."))?;
@@ -416,7 +416,7 @@ impl Operation {
let output = self
.update_outcome(&mut outcome, {
- let _wp = wd::watch_millis("Operation::finish: calling finish", 500);
+ let _wp = wd::watch("Operation::finish: calling IKeyMintOperation::finish");
map_km_error(self.km_op.finish(
input,
signature,
@@ -447,7 +447,7 @@ impl Operation {
*locked_outcome = outcome;
{
- let _wp = wd::watch_millis("Operation::abort: calling abort", 500);
+ let _wp = wd::watch("Operation::abort: calling IKeyMintOperation::abort");
map_km_error(self.km_op.abort()).context(ks_err!("KeyMint::abort failed."))
}
}
@@ -821,59 +821,50 @@ impl binder::Interface for KeystoreOperation {}
impl IKeystoreOperation for KeystoreOperation {
fn updateAad(&self, aad_input: &[u8]) -> binder::Result<()> {
- let _wp = wd::watch_millis("IKeystoreOperation::updateAad", 500);
- map_or_log_err(
- self.with_locked_operation(
- |op| op.update_aad(aad_input).context(ks_err!("KeystoreOperation::updateAad")),
- false,
- ),
- Ok,
+ let _wp = wd::watch("IKeystoreOperation::updateAad");
+ self.with_locked_operation(
+ |op| op.update_aad(aad_input).context(ks_err!("KeystoreOperation::updateAad")),
+ false,
)
+ .map_err(into_logged_binder)
}
fn update(&self, input: &[u8]) -> binder::Result<Option<Vec<u8>>> {
- let _wp = wd::watch_millis("IKeystoreOperation::update", 500);
- map_or_log_err(
- self.with_locked_operation(
- |op| op.update(input).context(ks_err!("KeystoreOperation::update")),
- false,
- ),
- Ok,
+ let _wp = wd::watch("IKeystoreOperation::update");
+ self.with_locked_operation(
+ |op| op.update(input).context(ks_err!("KeystoreOperation::update")),
+ false,
)
+ .map_err(into_logged_binder)
}
fn finish(
&self,
input: Option<&[u8]>,
signature: Option<&[u8]>,
) -> binder::Result<Option<Vec<u8>>> {
- let _wp = wd::watch_millis("IKeystoreOperation::finish", 500);
- map_or_log_err(
- self.with_locked_operation(
- |op| op.finish(input, signature).context(ks_err!("KeystoreOperation::finish")),
- true,
- ),
- Ok,
+ let _wp = wd::watch("IKeystoreOperation::finish");
+ self.with_locked_operation(
+ |op| op.finish(input, signature).context(ks_err!("KeystoreOperation::finish")),
+ true,
)
+ .map_err(into_logged_binder)
}
fn abort(&self) -> binder::Result<()> {
- let _wp = wd::watch_millis("IKeystoreOperation::abort", 500);
- map_err_with(
- self.with_locked_operation(
- |op| op.abort(Outcome::Abort).context(ks_err!("KeystoreOperation::abort")),
- true,
- ),
- |e| {
- match e.root_cause().downcast_ref::<Error>() {
- // Calling abort on expired operations is something very common.
- // There is no reason to clutter the log with it. It is never the cause
- // for a true problem.
- Some(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => {}
- _ => log::error!("{:?}", e),
- };
- e
- },
- Ok,
- )
+ let _wp = wd::watch("IKeystoreOperation::abort");
+ let result = self.with_locked_operation(
+ |op| op.abort(Outcome::Abort).context(ks_err!("KeystoreOperation::abort")),
+ true,
+ );
+ result.map_err(|e| {
+ match e.root_cause().downcast_ref::<Error>() {
+ // Calling abort on expired operations is something very common.
+ // There is no reason to clutter the log with it. It is never the cause
+ // for a true problem.
+ Some(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => {}
+ _ => log::error!("{:?}", e),
+ };
+ into_binder(e)
+ })
}
}
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index bc73744f..d79445b1 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -38,6 +38,9 @@ use selinux::getcon;
#[cfg(test)]
use tests::test_getcon as getcon;
+#[cfg(test)]
+mod tests;
+
lazy_static! {
// Panicking here is allowed because keystore cannot function without this backend
// and it would happen early and indicate a gross misconfiguration of the device.
@@ -137,10 +140,7 @@ implement_class!(
/// 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.
@@ -400,433 +400,3 @@ pub fn check_key_permission(
selinux::check_permission(caller_ctx, &target_context, perm)
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use anyhow::anyhow;
- use anyhow::Result;
- use keystore2_selinux::*;
-
- const ALL_PERMS: KeyPermSet = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Grant,
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- KeyPerm::ConvertStorageKeyToEphemeral,
- ];
-
- const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- // No KeyPerm::Grant
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
-
- const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- // No KeyPerm::Grant
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- KeyPerm::ConvertStorageKeyToEphemeral,
- ];
-
- const UNPRIV_PERMS: KeyPermSet = key_perm_set![
- KeyPerm::Delete,
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
-
- /// The su_key namespace as defined in su.te and keystore_key_contexts of the
- /// SePolicy (system/sepolicy).
- const SU_KEY_NAMESPACE: i32 = 0;
- /// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
- /// SePolicy (system/sepolicy).
- const SHELL_KEY_NAMESPACE: i32 = 1;
-
- pub fn test_getcon() -> Result<Context> {
- Context::new("u:object_r:keystore:s0")
- }
-
- // This macro evaluates the given expression and checks that
- // a) evaluated to Result::Err() and that
- // b) the wrapped error is selinux::Error::perm() (permission denied).
- // We use a macro here because a function would mask which invocation caused the failure.
- //
- // TODO b/164121720 Replace this macro with a function when `track_caller` is available.
- macro_rules! assert_perm_failed {
- ($test_function:expr) => {
- let result = $test_function;
- assert!(result.is_err(), "Permission check should have failed.");
- assert_eq!(
- Some(&selinux::Error::perm()),
- result.err().unwrap().root_cause().downcast_ref::<selinux::Error>()
- );
- };
- }
-
- fn check_context() -> Result<(selinux::Context, i32, bool)> {
- // Calling the non mocked selinux::getcon here intended.
- let context = selinux::getcon()?;
- match context.to_str().unwrap() {
- "u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
- "u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
- c => Err(anyhow!(format!(
- "This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
- c
- ))),
- }
- }
-
- #[test]
- fn check_keystore_permission_test() -> Result<()> {
- let system_server_ctx = Context::new("u:r:system_server:s0")?;
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::AddAuth).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearNs).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Lock).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Reset).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Unlock).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangeUser).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangePassword).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearUID).is_ok());
- let shell_ctx = Context::new("u:r:shell:s0")?;
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::AddAuth));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearNs));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::List));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Lock));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Reset));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Unlock));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangeUser));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangePassword));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearUID));
- Ok(())
- }
-
- #[test]
- fn check_grant_permission_app() -> Result<()> {
- let system_server_ctx = Context::new("u:r:system_server:s0")?;
- let shell_ctx = Context::new("u:r:shell:s0")?;
- let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
- check_grant_permission(&system_server_ctx, SYSTEM_SERVER_PERMISSIONS_NO_GRANT, &key)
- .expect("Grant permission check failed.");
-
- // attempts to grant the grant permission must always fail even when privileged.
- assert_perm_failed!(check_grant_permission(
- &system_server_ctx,
- KeyPerm::Grant.into(),
- &key
- ));
- // unprivileged grant attempts always fail. shell does not have the grant permission.
- assert_perm_failed!(check_grant_permission(&shell_ctx, UNPRIV_PERMS, &key));
- Ok(())
- }
-
- #[test]
- fn check_grant_permission_selinux() -> Result<()> {
- let (sctx, namespace, is_su) = check_context()?;
- let key = KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: namespace as i64,
- alias: None,
- blob: None,
- };
- if is_su {
- assert!(check_grant_permission(&sctx, NOT_GRANT_PERMS, &key).is_ok());
- // attempts to grant the grant permission must always fail even when privileged.
- assert_perm_failed!(check_grant_permission(&sctx, KeyPerm::Grant.into(), &key));
- } else {
- // unprivileged grant attempts always fail. shell does not have the grant permission.
- assert_perm_failed!(check_grant_permission(&sctx, UNPRIV_PERMS, &key));
- }
- Ok(())
- }
-
- #[test]
- fn check_key_permission_domain_grant() -> Result<()> {
- let key = KeyDescriptor { domain: Domain::GRANT, nspace: 0, alias: None, blob: None };
-
- assert_perm_failed!(check_key_permission(
- 0,
- &selinux::Context::new("ignored").unwrap(),
- KeyPerm::Grant,
- &key,
- &Some(UNPRIV_PERMS)
- ));
-
- check_key_permission(
- 0,
- &selinux::Context::new("ignored").unwrap(),
- KeyPerm::Use,
- &key,
- &Some(ALL_PERMS),
- )
- }
-
- #[test]
- fn check_key_permission_domain_app() -> Result<()> {
- let system_server_ctx = Context::new("u:r:system_server:s0")?;
- let shell_ctx = Context::new("u:r:shell:s0")?;
- let gmscore_app = Context::new("u:r:gmscore_app:s0")?;
-
- let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
-
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Use, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Delete, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Rebind, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Update, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Grant, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::UseDevId, &key, &None).is_ok());
- assert!(check_key_permission(0, &gmscore_app, KeyPerm::GenUniqueId, &key, &None).is_ok());
-
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Use, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Delete, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Rebind, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Update, &key, &None).is_ok());
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::Grant, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ReqForcedOp, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ManageBlob, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::UseDevId, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::GenUniqueId, &key, &None));
-
- // Also make sure that the permission fails if the caller is not the owner.
- assert_perm_failed!(check_key_permission(
- 1, // the owner is 0
- &system_server_ctx,
- KeyPerm::Use,
- &key,
- &None
- ));
- // Unless there was a grant.
- assert!(check_key_permission(
- 1,
- &system_server_ctx,
- KeyPerm::Use,
- &key,
- &Some(key_perm_set![KeyPerm::Use])
- )
- .is_ok());
- // But fail if the grant did not cover the requested permission.
- assert_perm_failed!(check_key_permission(
- 1,
- &system_server_ctx,
- KeyPerm::Use,
- &key,
- &Some(key_perm_set![KeyPerm::GetInfo])
- ));
-
- Ok(())
- }
-
- #[test]
- fn check_key_permission_domain_selinux() -> Result<()> {
- let (sctx, namespace, is_su) = check_context()?;
- let key = KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: namespace as i64,
- alias: None,
- blob: None,
- };
-
- assert!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::Delete, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::GetInfo, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::Rebind, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::Update, &key, &None).is_ok());
-
- if is_su {
- assert!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None).is_ok());
- } else {
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None));
- }
- Ok(())
- }
-
- #[test]
- fn check_key_permission_domain_blob() -> Result<()> {
- let (sctx, namespace, is_su) = check_context()?;
- let key = KeyDescriptor {
- domain: Domain::BLOB,
- nspace: namespace as i64,
- alias: None,
- blob: None,
- };
-
- if is_su {
- check_key_permission(0, &sctx, KeyPerm::Use, &key, &None)
- } else {
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None));
- Ok(())
- }
- }
-
- #[test]
- fn check_key_permission_domain_key_id() -> Result<()> {
- let key = KeyDescriptor { domain: Domain::KEY_ID, nspace: 0, alias: None, blob: None };
-
- assert_eq!(
- Some(&KsError::sys()),
- check_key_permission(
- 0,
- &selinux::Context::new("ignored").unwrap(),
- KeyPerm::Use,
- &key,
- &None
- )
- .err()
- .unwrap()
- .root_cause()
- .downcast_ref::<KsError>()
- );
- Ok(())
- }
-
- #[test]
- fn key_perm_set_all_test() {
- let v = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Grant,
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use // Test if the macro accepts missing comma at the end of the list.
- ];
- let mut i = v.into_iter();
- assert_eq!(i.next().unwrap().name(), "delete");
- assert_eq!(i.next().unwrap().name(), "gen_unique_id");
- assert_eq!(i.next().unwrap().name(), "get_info");
- assert_eq!(i.next().unwrap().name(), "grant");
- assert_eq!(i.next().unwrap().name(), "manage_blob");
- assert_eq!(i.next().unwrap().name(), "rebind");
- assert_eq!(i.next().unwrap().name(), "req_forced_op");
- assert_eq!(i.next().unwrap().name(), "update");
- assert_eq!(i.next().unwrap().name(), "use");
- assert_eq!(i.next().unwrap().name(), "use_dev_id");
- assert_eq!(None, i.next());
- }
- #[test]
- fn key_perm_set_sparse_test() {
- let v = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Update,
- KeyPerm::Use, // Test if macro accepts the comma at the end of the list.
- ];
- let mut i = v.into_iter();
- assert_eq!(i.next().unwrap().name(), "gen_unique_id");
- assert_eq!(i.next().unwrap().name(), "manage_blob");
- assert_eq!(i.next().unwrap().name(), "req_forced_op");
- assert_eq!(i.next().unwrap().name(), "update");
- assert_eq!(i.next().unwrap().name(), "use");
- assert_eq!(None, i.next());
- }
- #[test]
- fn key_perm_set_empty_test() {
- let v = key_perm_set![];
- let mut i = v.into_iter();
- assert_eq!(None, i.next());
- }
- #[test]
- fn key_perm_set_include_subset_test() {
- let v1 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Grant,
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
- let v2 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
- assert!(v1.includes(v2));
- assert!(!v2.includes(v1));
- }
- #[test]
- fn key_perm_set_include_equal_test() {
- let v1 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
- let v2 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
- assert!(v1.includes(v2));
- assert!(v2.includes(v1));
- }
- #[test]
- fn key_perm_set_include_overlap_test() {
- let v1 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::Grant, // only in v1
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
- let v2 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::ReqForcedOp, // only in v2
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
- assert!(!v1.includes(v2));
- assert!(!v2.includes(v1));
- }
- #[test]
- fn key_perm_set_include_no_overlap_test() {
- let v1 = key_perm_set![KeyPerm::ManageBlob, KeyPerm::Delete, KeyPerm::Grant,];
- let v2 =
- key_perm_set![KeyPerm::ReqForcedOp, KeyPerm::Rebind, KeyPerm::Update, KeyPerm::Use,];
- assert!(!v1.includes(v2));
- assert!(!v2.includes(v1));
- }
-}
diff --git a/keystore2/src/permission/tests.rs b/keystore2/src/permission/tests.rs
new file mode 100644
index 00000000..f555c12c
--- /dev/null
+++ b/keystore2/src/permission/tests.rs
@@ -0,0 +1,434 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Access control tests.
+
+use super::*;
+use crate::key_perm_set;
+use anyhow::anyhow;
+use anyhow::Result;
+use keystore2_selinux::*;
+
+const ALL_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::UseDevId,
+ KeyPerm::ReqForcedOp,
+ KeyPerm::GenUniqueId,
+ KeyPerm::Grant,
+ KeyPerm::GetInfo,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ KeyPerm::ConvertStorageKeyToEphemeral,
+];
+
+const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
+ KeyPerm::Delete,
+ KeyPerm::UseDevId,
+ // No KeyPerm::Grant
+ KeyPerm::GetInfo,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+];
+
+const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::UseDevId,
+ KeyPerm::ReqForcedOp,
+ KeyPerm::GenUniqueId,
+ // No KeyPerm::Grant
+ KeyPerm::GetInfo,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ KeyPerm::ConvertStorageKeyToEphemeral,
+];
+
+const UNPRIV_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::Delete,
+ KeyPerm::GetInfo,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+];
+
+/// The su_key namespace as defined in su.te and keystore_key_contexts of the
+/// SePolicy (system/sepolicy).
+const SU_KEY_NAMESPACE: i32 = 0;
+/// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
+/// SePolicy (system/sepolicy).
+const SHELL_KEY_NAMESPACE: i32 = 1;
+
+pub fn test_getcon() -> Result<Context> {
+ Context::new("u:object_r:keystore:s0")
+}
+
+// This macro evaluates the given expression and checks that
+// a) evaluated to Result::Err() and that
+// b) the wrapped error is selinux::Error::perm() (permission denied).
+// We use a macro here because a function would mask which invocation caused the failure.
+//
+// TODO b/164121720 Replace this macro with a function when `track_caller` is available.
+macro_rules! assert_perm_failed {
+ ($test_function:expr) => {
+ let result = $test_function;
+ assert!(result.is_err(), "Permission check should have failed.");
+ assert_eq!(
+ Some(&selinux::Error::perm()),
+ result.err().unwrap().root_cause().downcast_ref::<selinux::Error>()
+ );
+ };
+}
+
+fn check_context() -> Result<(selinux::Context, i32, bool)> {
+ // Calling the non mocked selinux::getcon here intended.
+ let context = selinux::getcon()?;
+ match context.to_str().unwrap() {
+ "u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
+ "u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
+ c => Err(anyhow!(format!(
+ "This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
+ c
+ ))),
+ }
+}
+
+#[test]
+fn check_keystore_permission_test() -> Result<()> {
+ let system_server_ctx = Context::new("u:r:system_server:s0")?;
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::AddAuth).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearNs).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Lock).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Reset).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Unlock).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangeUser).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangePassword).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearUID).is_ok());
+ let shell_ctx = Context::new("u:r:shell:s0")?;
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::AddAuth));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearNs));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::List));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Lock));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Reset));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Unlock));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangeUser));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangePassword));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearUID));
+ Ok(())
+}
+
+#[test]
+fn check_grant_permission_app() -> Result<()> {
+ let system_server_ctx = Context::new("u:r:system_server:s0")?;
+ let shell_ctx = Context::new("u:r:shell:s0")?;
+ let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
+ check_grant_permission(&system_server_ctx, SYSTEM_SERVER_PERMISSIONS_NO_GRANT, &key)
+ .expect("Grant permission check failed.");
+
+ // attempts to grant the grant permission must always fail even when privileged.
+ assert_perm_failed!(check_grant_permission(&system_server_ctx, KeyPerm::Grant.into(), &key));
+ // unprivileged grant attempts always fail. shell does not have the grant permission.
+ assert_perm_failed!(check_grant_permission(&shell_ctx, UNPRIV_PERMS, &key));
+ Ok(())
+}
+
+#[test]
+fn check_grant_permission_selinux() -> Result<()> {
+ let (sctx, namespace, is_su) = check_context()?;
+ let key = KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: namespace as i64,
+ alias: None,
+ blob: None,
+ };
+ if is_su {
+ assert!(check_grant_permission(&sctx, NOT_GRANT_PERMS, &key).is_ok());
+ // attempts to grant the grant permission must always fail even when privileged.
+ assert_perm_failed!(check_grant_permission(&sctx, KeyPerm::Grant.into(), &key));
+ } else {
+ // unprivileged grant attempts always fail. shell does not have the grant permission.
+ assert_perm_failed!(check_grant_permission(&sctx, UNPRIV_PERMS, &key));
+ }
+ Ok(())
+}
+
+#[test]
+fn check_key_permission_domain_grant() -> Result<()> {
+ let key = KeyDescriptor { domain: Domain::GRANT, nspace: 0, alias: None, blob: None };
+
+ assert_perm_failed!(check_key_permission(
+ 0,
+ &selinux::Context::new("ignored").unwrap(),
+ KeyPerm::Grant,
+ &key,
+ &Some(UNPRIV_PERMS)
+ ));
+
+ check_key_permission(
+ 0,
+ &selinux::Context::new("ignored").unwrap(),
+ KeyPerm::Use,
+ &key,
+ &Some(ALL_PERMS),
+ )
+}
+
+#[test]
+fn check_key_permission_domain_app() -> Result<()> {
+ let system_server_ctx = Context::new("u:r:system_server:s0")?;
+ let shell_ctx = Context::new("u:r:shell:s0")?;
+ let gmscore_app = Context::new("u:r:gmscore_app:s0")?;
+
+ let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
+
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Use, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Delete, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Rebind, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Update, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Grant, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::UseDevId, &key, &None).is_ok());
+ assert!(check_key_permission(0, &gmscore_app, KeyPerm::GenUniqueId, &key, &None).is_ok());
+
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::Use, &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::Delete, &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::Rebind, &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::Update, &key, &None).is_ok());
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::Grant, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ReqForcedOp, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ManageBlob, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::UseDevId, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::GenUniqueId, &key, &None));
+
+ // Also make sure that the permission fails if the caller is not the owner.
+ assert_perm_failed!(check_key_permission(
+ 1, // the owner is 0
+ &system_server_ctx,
+ KeyPerm::Use,
+ &key,
+ &None
+ ));
+ // Unless there was a grant.
+ assert!(check_key_permission(
+ 1,
+ &system_server_ctx,
+ KeyPerm::Use,
+ &key,
+ &Some(key_perm_set![KeyPerm::Use])
+ )
+ .is_ok());
+ // But fail if the grant did not cover the requested permission.
+ assert_perm_failed!(check_key_permission(
+ 1,
+ &system_server_ctx,
+ KeyPerm::Use,
+ &key,
+ &Some(key_perm_set![KeyPerm::GetInfo])
+ ));
+
+ Ok(())
+}
+
+#[test]
+fn check_key_permission_domain_selinux() -> Result<()> {
+ let (sctx, namespace, is_su) = check_context()?;
+ let key = KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: namespace as i64,
+ alias: None,
+ blob: None,
+ };
+
+ assert!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::Delete, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::GetInfo, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::Rebind, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::Update, &key, &None).is_ok());
+
+ if is_su {
+ assert!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None).is_ok());
+ } else {
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None));
+ }
+ Ok(())
+}
+
+#[test]
+fn check_key_permission_domain_blob() -> Result<()> {
+ let (sctx, namespace, is_su) = check_context()?;
+ let key =
+ KeyDescriptor { domain: Domain::BLOB, nspace: namespace as i64, alias: None, blob: None };
+
+ if is_su {
+ check_key_permission(0, &sctx, KeyPerm::Use, &key, &None)
+ } else {
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None));
+ Ok(())
+ }
+}
+
+#[test]
+fn check_key_permission_domain_key_id() -> Result<()> {
+ let key = KeyDescriptor { domain: Domain::KEY_ID, nspace: 0, alias: None, blob: None };
+
+ assert_eq!(
+ Some(&KsError::sys()),
+ check_key_permission(
+ 0,
+ &selinux::Context::new("ignored").unwrap(),
+ KeyPerm::Use,
+ &key,
+ &None
+ )
+ .err()
+ .unwrap()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+ Ok(())
+}
+
+#[test]
+fn key_perm_set_all_test() {
+ let v = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::UseDevId,
+ KeyPerm::ReqForcedOp,
+ KeyPerm::GenUniqueId,
+ KeyPerm::Grant,
+ KeyPerm::GetInfo,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use // Test if the macro accepts missing comma at the end of the list.
+ ];
+ let mut i = v.into_iter();
+ assert_eq!(i.next().unwrap().name(), "delete");
+ assert_eq!(i.next().unwrap().name(), "gen_unique_id");
+ assert_eq!(i.next().unwrap().name(), "get_info");
+ assert_eq!(i.next().unwrap().name(), "grant");
+ assert_eq!(i.next().unwrap().name(), "manage_blob");
+ assert_eq!(i.next().unwrap().name(), "rebind");
+ assert_eq!(i.next().unwrap().name(), "req_forced_op");
+ assert_eq!(i.next().unwrap().name(), "update");
+ assert_eq!(i.next().unwrap().name(), "use");
+ assert_eq!(i.next().unwrap().name(), "use_dev_id");
+ assert_eq!(None, i.next());
+}
+#[test]
+fn key_perm_set_sparse_test() {
+ let v = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::ReqForcedOp,
+ KeyPerm::GenUniqueId,
+ KeyPerm::Update,
+ KeyPerm::Use, // Test if macro accepts the comma at the end of the list.
+ ];
+ let mut i = v.into_iter();
+ assert_eq!(i.next().unwrap().name(), "gen_unique_id");
+ assert_eq!(i.next().unwrap().name(), "manage_blob");
+ assert_eq!(i.next().unwrap().name(), "req_forced_op");
+ assert_eq!(i.next().unwrap().name(), "update");
+ assert_eq!(i.next().unwrap().name(), "use");
+ assert_eq!(None, i.next());
+}
+#[test]
+fn key_perm_set_empty_test() {
+ let v = key_perm_set![];
+ let mut i = v.into_iter();
+ assert_eq!(None, i.next());
+}
+#[test]
+fn key_perm_set_include_subset_test() {
+ let v1 = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::UseDevId,
+ KeyPerm::ReqForcedOp,
+ KeyPerm::GenUniqueId,
+ KeyPerm::Grant,
+ KeyPerm::GetInfo,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ ];
+ assert!(v1.includes(v2));
+ assert!(!v2.includes(v1));
+}
+#[test]
+fn key_perm_set_include_equal_test() {
+ let v1 = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ ];
+ assert!(v1.includes(v2));
+ assert!(v2.includes(v1));
+}
+#[test]
+fn key_perm_set_include_overlap_test() {
+ let v1 = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::Grant, // only in v1
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::ReqForcedOp, // only in v2
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ ];
+ assert!(!v1.includes(v2));
+ assert!(!v2.includes(v1));
+}
+#[test]
+fn key_perm_set_include_no_overlap_test() {
+ let v1 = key_perm_set![KeyPerm::ManageBlob, KeyPerm::Delete, KeyPerm::Grant,];
+ let v2 = key_perm_set![KeyPerm::ReqForcedOp, KeyPerm::Rebind, KeyPerm::Update, KeyPerm::Use,];
+ assert!(!v1.includes(v2));
+ assert!(!v2.includes(v1));
+}
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index 44d805c3..bf1149c1 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -211,13 +211,10 @@ impl KeyMintDevice {
KeyBlob::NonSensitive(key_blob_vec),
|key_blob| {
map_km_error({
- let _wp = wd::watch_millis(
- concat!(
- "In KeyMintDevice::lookup_or_generate_key: ",
- "calling getKeyCharacteristics."
- ),
- 500,
- );
+ let _wp = wd::watch(concat!(
+ "KeyMintDevice::lookup_or_generate_key: ",
+ "calling IKeyMintDevice::getKeyCharacteristics."
+ ));
self.km_dev.getKeyCharacteristics(key_blob, &[], &[])
})
},
@@ -308,7 +305,9 @@ impl KeyMintDevice {
let (begin_result, _) = self
.upgrade_keyblob_if_required_with(db, key_id_guard, key_blob, |blob| {
map_km_error({
- let _wp = wd::watch_millis("In use_key_in_one_step: calling: begin", 500);
+ let _wp = wd::watch(
+ "KeyMintDevice::use_key_in_one_step: calling IKeyMintDevice::begin",
+ );
self.km_dev.begin(purpose, blob, operation_parameters, auth_token)
})
})
@@ -316,7 +315,8 @@ impl KeyMintDevice {
let operation: Strong<dyn IKeyMintOperation> =
begin_result.operation.ok_or_else(Error::sys).context(ks_err!("Operation missing"))?;
map_km_error({
- let _wp = wd::watch_millis("In use_key_in_one_step: calling: finish", 500);
+ let _wp =
+ wd::watch("KeyMintDevice::use_key_in_one_step: calling IKeyMintDevice::finish");
operation.finish(Some(input), None, None, None, None)
})
.context(ks_err!("Failed to finish operation."))
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index 0ef8c953..cda93b3f 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -31,7 +31,6 @@ use android_system_keystore2::aidl::android::system::keystore2::{
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 @@ use android_security_metrics::aidl::android::security::metrics::RkpError::RkpErr
#[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 {
@@ -136,6 +129,6 @@ fn get_rkpd_attestation_key(
// by the calling function and allow for natural fallback to the factory key.
let rpc_name = get_remotely_provisioned_component_name(security_level)
.context(ks_err!("Trying to get IRPC name."))?;
- let _wd = wd::watch_millis("Calling get_rkpd_attestation_key()", 500);
+ let _wd = wd::watch("Calling get_rkpd_attestation_key()");
rkpd_client::get_rkpd_attestation_key(&rpc_name, caller_uid)
}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 6fb0eb20..bd20afb7 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -20,7 +20,7 @@ use crate::audit_log::{
};
use crate::database::{BlobInfo, CertificateInfo, KeyIdGuard};
use crate::error::{
- self, map_km_error, map_or_log_err, wrapped_rkpd_error_to_ks_error, Error, ErrorCode,
+ self, into_logged_binder, map_km_error, wrapped_rkpd_error_to_ks_error, Error, ErrorCode,
};
use crate::globals::{
get_remotely_provisioned_component_name, DB, ENFORCEMENTS, LEGACY_IMPORTER, SUPER_KEY,
@@ -34,7 +34,8 @@ use crate::super_key::{KeyBlob, SuperKeyManager};
use crate::utils::{
check_device_attestation_permissions, check_key_permission,
check_unique_id_attestation_permissions, is_device_id_attestation_tag,
- key_characteristics_to_internal, uid_to_android_user, watchdog as wd, UNDEFINED_NOT_AFTER,
+ key_characteristics_to_internal, log_security_safe_params, uid_to_android_user, watchdog as wd,
+ UNDEFINED_NOT_AFTER,
};
use crate::{
database::{
@@ -99,7 +100,7 @@ impl KeystoreSecurityLevel {
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() },
@@ -109,7 +110,12 @@ impl KeystoreSecurityLevel {
fn watch_millis(&self, id: &'static str, millis: u64) -> Option<wd::WatchPoint> {
let sec_level = self.security_level;
- wd::watch_millis_with(id, millis, move || format!("SecurityLevel {:?}", sec_level))
+ wd::watch_millis_with(id, millis, sec_level)
+ }
+
+ fn watch(&self, id: &'static str) -> Option<wd::WatchPoint> {
+ let sec_level = self.security_level;
+ wd::watch_millis_with(id, wd::DEFAULT_TIMEOUT_MS, sec_level)
}
fn store_new_key(
@@ -323,9 +329,8 @@ impl KeystoreSecurityLevel {
operation_parameters,
|blob| loop {
match map_km_error({
- let _wp = self.watch_millis(
- "In KeystoreSecurityLevel::create_operation: calling begin",
- 500,
+ let _wp = self.watch(
+ "KeystoreSecurityLevel::create_operation: calling IKeyMintDevice::begin",
);
self.keymint.begin(
purpose,
@@ -439,19 +444,23 @@ impl KeystoreSecurityLevel {
// If there is an attestation challenge we need to get an application id.
if params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE) {
- let aaid = {
- let _wp = self.watch_millis(
- "In KeystoreSecurityLevel::add_required_parameters calling: get_aaid",
- 500,
- );
- keystore2_aaid::get_aaid(uid)
- .map_err(|e| anyhow!(ks_err!("get_aaid returned status {}.", e)))
- }?;
-
- result.push(KeyParameter {
- tag: Tag::ATTESTATION_APPLICATION_ID,
- value: KeyParameterValue::Blob(aaid),
- });
+ let _wp =
+ self.watch(" KeystoreSecurityLevel::add_required_parameters: calling get_aaid");
+ match keystore2_aaid::get_aaid(uid) {
+ Ok(aaid_ok) => {
+ result.push(KeyParameter {
+ tag: Tag::ATTESTATION_APPLICATION_ID,
+ value: KeyParameterValue::Blob(aaid_ok),
+ });
+ }
+ Err(e) if e == ResponseCode::GET_ATTESTATION_APPLICATION_ID_FAILED.0 as u32 => {
+ return Err(Error::Rc(ResponseCode::GET_ATTESTATION_APPLICATION_ID_FAILED))
+ .context(ks_err!("Attestation ID retrieval failed."));
+ }
+ Err(e) => {
+ return Err(anyhow!(e)).context(ks_err!("Attestation ID retrieval error."))
+ }
+ }
}
if params.iter().any(|kp| kp.tag == Tag::INCLUDE_UNIQUE_ID) {
@@ -573,8 +582,8 @@ impl KeystoreSecurityLevel {
map_km_error({
let _wp = self.watch_millis(
concat!(
- "In KeystoreSecurityLevel::generate_key (UserGenerated): ",
- "calling generate_key."
+ "KeystoreSecurityLevel::generate_key (UserGenerated): ",
+ "calling IKeyMintDevice::generate_key"
),
5000, // Generate can take a little longer.
);
@@ -582,15 +591,19 @@ impl KeystoreSecurityLevel {
})
},
)
- .context(ks_err!("Using user generated attestation key."))
+ .context(ks_err!(
+ "While generating with a user-generated \
+ attestation key, params: {:?}.",
+ log_security_safe_params(&params)
+ ))
.map(|(result, _)| result),
Some(AttestationKeyInfo::RkpdProvisioned { attestation_key, attestation_certs }) => {
self.upgrade_rkpd_keyblob_if_required_with(&attestation_key.keyBlob, &[], |blob| {
map_km_error({
let _wp = self.watch_millis(
concat!(
- "In KeystoreSecurityLevel::generate_key (RkpdProvisioned): ",
- "calling generate_key.",
+ "KeystoreSecurityLevel::generate_key (RkpdProvisioned): ",
+ "calling IKeyMintDevice::generate_key",
),
5000, // Generate can take a little longer.
);
@@ -602,7 +615,12 @@ impl KeystoreSecurityLevel {
self.keymint.generateKey(&params, dynamic_attest_key.as_ref())
})
})
- .context(ks_err!("While generating Key with remote provisioned attestation key."))
+ .context(ks_err!(
+ "While generating Key {:?} with remote \
+ provisioned attestation key and params: {:?}.",
+ key.alias,
+ log_security_safe_params(&params)
+ ))
.map(|(mut result, _)| {
result.certificateChain.push(attestation_certs);
result
@@ -611,14 +629,18 @@ impl KeystoreSecurityLevel {
None => map_km_error({
let _wp = self.watch_millis(
concat!(
- "In KeystoreSecurityLevel::generate_key (No attestation): ",
- "calling generate_key.",
+ "KeystoreSecurityLevel::generate_key (No attestation key): ",
+ "calling IKeyMintDevice::generate_key",
),
5000, // Generate can take a little longer.
);
self.keymint.generateKey(&params, None)
})
- .context(ks_err!("While generating Key without explicit attestation key.")),
+ .context(ks_err!(
+ "While generating without a provided \
+ attestation key and params: {:?}.",
+ log_security_safe_params(&params)
+ )),
}
.context(ks_err!())?;
@@ -676,7 +698,7 @@ impl KeystoreSecurityLevel {
let km_dev = &self.keymint;
let creation_result = map_km_error({
let _wp =
- self.watch_millis("In KeystoreSecurityLevel::import_key: calling importKey.", 500);
+ self.watch("KeystoreSecurityLevel::import_key: calling IKeyMintDevice::importKey.");
km_dev.importKey(&params, format, key_data, None /* attestKey */)
})
.context(ks_err!("Trying to call importKey"))?;
@@ -789,9 +811,8 @@ impl KeystoreSecurityLevel {
wrapping_blob_metadata.km_uuid().copied(),
&[],
|wrapping_blob| {
- let _wp = self.watch_millis(
- "In KeystoreSecurityLevel::import_wrapped_key: calling importWrappedKey.",
- 500,
+ let _wp = self.watch(
+ "KeystoreSecurityLevel::import_wrapped_key: calling IKeyMintDevice::importWrappedKey.",
);
let creation_result = map_km_error(self.keymint.importWrappedKey(
wrapped_data,
@@ -865,7 +886,7 @@ impl KeystoreSecurityLevel {
}
},
)
- .context(ks_err!())?;
+ .context(ks_err!("upgrade_keyblob_if_required_with(key_id={:?})", key_id_guard))?;
// If no upgrade was needed, use the opportunity to reencrypt the blob if required
// and if the a key_id_guard is held. Note: key_id_guard can only be Some if no
@@ -897,7 +918,7 @@ impl KeystoreSecurityLevel {
params,
f,
|upgraded_blob| {
- let _wp = wd::watch_millis("Calling store_rkpd_attestation_key()", 500);
+ let _wp = wd::watch("Calling store_rkpd_attestation_key()");
if let Err(e) = store_rkpd_attestation_key(&rpc_name, key_blob, upgraded_blob) {
Err(wrapped_rkpd_error_to_ks_error(&e)).context(format!("{e:?}"))
} else {
@@ -905,7 +926,10 @@ impl KeystoreSecurityLevel {
}
},
)
- .context(ks_err!())
+ .context(ks_err!(
+ "upgrade_rkpd_keyblob_if_required_with(params={:?})",
+ log_security_safe_params(params)
+ ))
}
fn convert_storage_key_to_ephemeral(
@@ -927,38 +951,31 @@ impl KeystoreSecurityLevel {
.context(ks_err!("Check permission"))?;
let km_dev = &self.keymint;
- match {
- let _wp = self.watch_millis(
- concat!(
- "In IKeystoreSecurityLevel::convert_storage_key_to_ephemeral: ",
- "calling convertStorageKeyToEphemeral (1)"
- ),
- 500,
- );
+ let res = {
+ let _wp = self.watch(concat!(
+ "IKeystoreSecurityLevel::convert_storage_key_to_ephemeral: ",
+ "calling IKeyMintDevice::convertStorageKeyToEphemeral (1)"
+ ));
map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
- } {
+ };
+ match res {
Ok(result) => {
Ok(EphemeralStorageKeyResponse { ephemeralKey: result, upgradedBlob: None })
}
Err(error::Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
let upgraded_blob = {
- let _wp = self.watch_millis(
- "In convert_storage_key_to_ephemeral: calling upgradeKey",
- 500,
- );
+ let _wp = self.watch("IKeystoreSecurityLevel::convert_storage_key_to_ephemeral: calling IKeyMintDevice::upgradeKey");
map_km_error(km_dev.upgradeKey(key_blob, &[]))
}
.context(ks_err!("Failed to upgrade key blob."))?;
let ephemeral_key = {
- let _wp = self.watch_millis(
- "In convert_storage_key_to_ephemeral: calling convertStorageKeyToEphemeral (2)",
- 500,
- );
+ let _wp = self.watch(concat!(
+ "IKeystoreSecurityLevel::convert_storage_key_to_ephemeral: ",
+ "calling IKeyMintDevice::convertStorageKeyToEphemeral (2)"
+ ));
map_km_error(km_dev.convertStorageKeyToEphemeral(&upgraded_blob))
}
- .context(ks_err!(
- "Failed to retrieve ephemeral key (after upgrade)."
- ))?;
+ .context(ks_err!("Failed to retrieve ephemeral key (after upgrade)."))?;
Ok(EphemeralStorageKeyResponse {
ephemeralKey: ephemeral_key,
upgradedBlob: Some(upgraded_blob),
@@ -986,7 +1003,7 @@ impl KeystoreSecurityLevel {
let km_dev = &self.keymint;
{
let _wp =
- self.watch_millis("In KeystoreSecuritylevel::delete_key: calling deleteKey", 500);
+ self.watch("KeystoreSecuritylevel::delete_key: calling IKeyMintDevice::deleteKey");
map_km_error(km_dev.deleteKey(key_blob)).context(ks_err!("keymint device deleteKey"))
}
}
@@ -1001,8 +1018,8 @@ impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
operation_parameters: &[KeyParameter],
forced: bool,
) -> binder::Result<CreateOperationResponse> {
- let _wp = self.watch_millis("IKeystoreSecurityLevel::createOperation", 500);
- map_or_log_err(self.create_operation(key, operation_parameters, forced), Ok)
+ let _wp = self.watch("IKeystoreSecurityLevel::createOperation");
+ self.create_operation(key, operation_parameters, forced).map_err(into_logged_binder)
}
fn generateKey(
&self,
@@ -1018,7 +1035,7 @@ impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
let result = self.generate_key(key, attestation_key, params, flags, entropy);
log_key_creation_event_stats(self.security_level, params, &result);
log_key_generated(key, ThreadState::get_calling_uid(), result.is_ok());
- map_or_log_err(result, Ok)
+ result.map_err(into_logged_binder)
}
fn importKey(
&self,
@@ -1028,11 +1045,11 @@ impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
flags: i32,
key_data: &[u8],
) -> binder::Result<KeyMetadata> {
- let _wp = self.watch_millis("IKeystoreSecurityLevel::importKey", 500);
+ let _wp = self.watch("IKeystoreSecurityLevel::importKey");
let result = self.import_key(key, attestation_key, params, flags, key_data);
log_key_creation_event_stats(self.security_level, params, &result);
log_key_imported(key, ThreadState::get_calling_uid(), result.is_ok());
- map_or_log_err(result, Ok)
+ result.map_err(into_logged_binder)
}
fn importWrappedKey(
&self,
@@ -1042,25 +1059,25 @@ impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
params: &[KeyParameter],
authenticators: &[AuthenticatorSpec],
) -> binder::Result<KeyMetadata> {
- let _wp = self.watch_millis("IKeystoreSecurityLevel::importWrappedKey", 500);
+ let _wp = self.watch("IKeystoreSecurityLevel::importWrappedKey");
let result =
self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators);
log_key_creation_event_stats(self.security_level, params, &result);
log_key_imported(key, ThreadState::get_calling_uid(), result.is_ok());
- map_or_log_err(result, Ok)
+ result.map_err(into_logged_binder)
}
fn convertStorageKeyToEphemeral(
&self,
storage_key: &KeyDescriptor,
) -> binder::Result<EphemeralStorageKeyResponse> {
- let _wp = self.watch_millis("IKeystoreSecurityLevel::convertStorageKeyToEphemeral", 500);
- map_or_log_err(self.convert_storage_key_to_ephemeral(storage_key), Ok)
+ let _wp = self.watch("IKeystoreSecurityLevel::convertStorageKeyToEphemeral");
+ self.convert_storage_key_to_ephemeral(storage_key).map_err(into_logged_binder)
}
fn deleteKey(&self, key: &KeyDescriptor) -> binder::Result<()> {
- let _wp = self.watch_millis("IKeystoreSecurityLevel::deleteKey", 500);
+ let _wp = self.watch("IKeystoreSecurityLevel::deleteKey");
let result = self.delete_key(key);
log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
- map_or_log_err(result, Ok)
+ result.map_err(into_logged_binder)
}
}
@@ -1129,7 +1146,7 @@ mod tests {
|new_blob| {
// This handler is only executed if a key upgrade was performed.
key_upgraded = true;
- let _wp = wd::watch_millis("Calling store_rkpd_attestation_key()", 500);
+ let _wp = wd::watch("Calling store_rkpd_attestation_key()");
store_rkpd_attestation_key(&rpc_name, &key.keyBlob, new_blob).unwrap();
Ok(())
},
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index 1459254e..95e17445 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -35,7 +35,7 @@ use crate::{
error::ResponseCode,
};
use crate::{
- error::{self, map_or_log_err, ErrorCode},
+ error::{self, into_logged_binder, ErrorCode},
id_rotation::IdRotationState,
};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
@@ -62,11 +62,18 @@ impl KeystoreService {
id_rotation_state: IdRotationState,
) -> Result<Strong<dyn IKeystoreService>> {
let mut result: Self = Default::default();
- let (dev, uuid) = KeystoreSecurityLevel::new_native_binder(
+ let (dev, uuid) = match KeystoreSecurityLevel::new_native_binder(
SecurityLevel::TRUSTED_ENVIRONMENT,
id_rotation_state.clone(),
- )
- .context(ks_err!("Trying to construct mandatory security level TEE."))?;
+ ) {
+ Ok(v) => v,
+ Err(e) => {
+ log::error!("Failed to construct mandatory security level TEE: {e:?}");
+ log::error!("Does the device have a /default Keymaster or KeyMint instance?");
+ return Err(e.context(ks_err!("Trying to construct mandatory security level TEE")));
+ }
+ };
+
result.i_sec_level_by_uuid.insert(uuid, dev);
result.uuid_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, uuid);
@@ -381,14 +388,12 @@ impl IKeystoreService for KeystoreService {
&self,
security_level: SecurityLevel,
) -> binder::Result<Strong<dyn IKeystoreSecurityLevel>> {
- let _wp = wd::watch_millis_with("IKeystoreService::getSecurityLevel", 500, move || {
- format!("security_level: {}", security_level.0)
- });
- map_or_log_err(self.get_security_level(security_level), Ok)
+ let _wp = wd::watch_millis_with("IKeystoreService::getSecurityLevel", 500, security_level);
+ self.get_security_level(security_level).map_err(into_logged_binder)
}
fn getKeyEntry(&self, key: &KeyDescriptor) -> binder::Result<KeyEntryResponse> {
- let _wp = wd::watch_millis("IKeystoreService::get_key_entry", 500);
- map_or_log_err(self.get_key_entry(key), Ok)
+ let _wp = wd::watch("IKeystoreService::get_key_entry");
+ self.get_key_entry(key).map_err(into_logged_binder)
}
fn updateSubcomponent(
&self,
@@ -396,18 +401,18 @@ impl IKeystoreService for KeystoreService {
public_cert: Option<&[u8]>,
certificate_chain: Option<&[u8]>,
) -> binder::Result<()> {
- let _wp = wd::watch_millis("IKeystoreService::updateSubcomponent", 500);
- map_or_log_err(self.update_subcomponent(key, public_cert, certificate_chain), Ok)
+ let _wp = wd::watch("IKeystoreService::updateSubcomponent");
+ self.update_subcomponent(key, public_cert, certificate_chain).map_err(into_logged_binder)
}
fn listEntries(&self, domain: Domain, namespace: i64) -> binder::Result<Vec<KeyDescriptor>> {
- let _wp = wd::watch_millis("IKeystoreService::listEntries", 500);
- map_or_log_err(self.list_entries(domain, namespace), Ok)
+ let _wp = wd::watch("IKeystoreService::listEntries");
+ self.list_entries(domain, namespace).map_err(into_logged_binder)
}
fn deleteKey(&self, key: &KeyDescriptor) -> binder::Result<()> {
- let _wp = wd::watch_millis("IKeystoreService::deleteKey", 500);
+ let _wp = wd::watch("IKeystoreService::deleteKey");
let result = self.delete_key(key);
log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
- map_or_log_err(result, Ok)
+ result.map_err(into_logged_binder)
}
fn grant(
&self,
@@ -415,12 +420,12 @@ impl IKeystoreService for KeystoreService {
grantee_uid: i32,
access_vector: i32,
) -> binder::Result<KeyDescriptor> {
- let _wp = wd::watch_millis("IKeystoreService::grant", 500);
- map_or_log_err(self.grant(key, grantee_uid, access_vector.into()), Ok)
+ let _wp = wd::watch("IKeystoreService::grant");
+ self.grant(key, grantee_uid, access_vector.into()).map_err(into_logged_binder)
}
fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> binder::Result<()> {
- let _wp = wd::watch_millis("IKeystoreService::ungrant", 500);
- map_or_log_err(self.ungrant(key, grantee_uid), Ok)
+ let _wp = wd::watch("IKeystoreService::ungrant");
+ self.ungrant(key, grantee_uid).map_err(into_logged_binder)
}
fn listEntriesBatched(
&self,
@@ -428,12 +433,12 @@ impl IKeystoreService for KeystoreService {
namespace: i64,
start_past_alias: Option<&str>,
) -> binder::Result<Vec<KeyDescriptor>> {
- let _wp = wd::watch_millis("IKeystoreService::listEntriesBatched", 500);
- map_or_log_err(self.list_entries_batched(domain, namespace, start_past_alias), Ok)
+ let _wp = wd::watch("IKeystoreService::listEntriesBatched");
+ self.list_entries_batched(domain, namespace, start_past_alias).map_err(into_logged_binder)
}
fn getNumberOfEntries(&self, domain: Domain, namespace: i64) -> binder::Result<i32> {
- let _wp = wd::watch_millis("IKeystoreService::getNumberOfEntries", 500);
- map_or_log_err(self.count_num_entries(domain, namespace), Ok)
+ let _wp = wd::watch("IKeystoreService::getNumberOfEntries");
+ self.count_num_entries(domain, namespace).map_err(into_logged_binder)
}
}
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 44ce9abf..42fd7645 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -52,6 +52,9 @@ use std::{
};
use std::{convert::TryFrom, ops::Deref};
+#[cfg(test)]
+mod tests;
+
const MAX_MAX_BOOT_LEVEL: usize = 1_000_000_000;
/// Allow up to 15 seconds between the user unlocking using a biometric, and the auth
/// token being used to unlock in [`SuperKeyManager::try_unlock_user_with_biometric`].
@@ -576,13 +579,9 @@ impl SuperKeyManager {
pw: &Password,
) -> Result<(Vec<u8>, BlobMetaData)> {
let salt = generate_salt().context("In encrypt_with_password: Failed to generate salt.")?;
- let derived_key = if android_security_flags::fix_unlocked_device_required_keys_v2() {
- pw.derive_key_hkdf(&salt, AES_256_KEY_LENGTH)
- .context(ks_err!("Failed to derive key from password."))?
- } else {
- pw.derive_key_pbkdf2(&salt, AES_256_KEY_LENGTH)
- .context(ks_err!("Failed to derive password."))?
- };
+ let derived_key = pw
+ .derive_key_hkdf(&salt, AES_256_KEY_LENGTH)
+ .context(ks_err!("Failed to derive key from password."))?;
let mut metadata = BlobMetaData::new();
metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
metadata.add(BlobMetaEntry::Salt(salt));
@@ -879,9 +878,7 @@ impl SuperKeyManager {
) {
let entry = self.data.user_keys.entry(user_id).or_default();
if unlocking_sids.is_empty() {
- if android_security_flags::fix_unlocked_device_required_keys_v2() {
- entry.biometric_unlock = None;
- }
+ entry.biometric_unlock = None;
} else if let (Some(aes), Some(ecdh)) = (
entry.unlocked_device_required_symmetric.as_ref().cloned(),
entry.unlocked_device_required_private.as_ref().cloned(),
@@ -919,10 +916,8 @@ impl SuperKeyManager {
&key_desc,
KeyType::Client, /* TODO Should be Super b/189470584 */
|dev| {
- let _wp = wd::watch_millis(
- "In lock_unlocked_device_required_keys: calling importKey.",
- 500,
- );
+ let _wp =
+ wd::watch("SKM::lock_unlocked_device_required_keys: calling IKeyMintDevice::importKey.");
dev.importKey(key_params.as_slice(), KeyFormat::RAW, &encrypting_key, None)
},
)?;
@@ -986,8 +981,7 @@ impl SuperKeyManager {
user_id: UserId,
) -> Result<()> {
let entry = self.data.user_keys.entry(user_id).or_default();
- if android_security_flags::fix_unlocked_device_required_keys_v2()
- && entry.unlocked_device_required_symmetric.is_some()
+ if entry.unlocked_device_required_symmetric.is_some()
&& entry.unlocked_device_required_private.is_some()
{
// If the keys are already cached in plaintext, then there is no need to decrypt the
@@ -1011,7 +1005,7 @@ impl SuperKeyManager {
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>)> = (|| {
@@ -1098,92 +1092,13 @@ impl SuperKeyManager {
legacy_importer
.bulk_delete_user(user_id, false)
.context(ks_err!("Trying to delete legacy keys."))?;
- db.unbind_keys_for_user(user_id, false).context(ks_err!("Error in unbinding keys."))?;
+ db.unbind_keys_for_user(user_id).context(ks_err!("Error in unbinding keys."))?;
// Delete super key in cache, if exists.
self.forget_all_keys_for_user(user_id);
Ok(())
}
- /// Deletes all authentication bound keys and super keys for the given user. The user must be
- /// unlocked before this function is called. This function is used to transition a user to
- /// swipe.
- pub fn reset_user(
- &mut self,
- db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- user_id: UserId,
- ) -> Result<()> {
- log::info!("reset_user(user={user_id})");
- match self.get_user_state(db, legacy_importer, user_id)? {
- UserState::Uninitialized => {
- Err(Error::sys()).context(ks_err!("Tried to reset an uninitialized user!"))
- }
- UserState::BeforeFirstUnlock => {
- Err(Error::sys()).context(ks_err!("Tried to reset a locked user's password!"))
- }
- UserState::AfterFirstUnlock(_) => {
- // Mark keys created on behalf of the user as unreferenced.
- legacy_importer
- .bulk_delete_user(user_id, true)
- .context(ks_err!("Trying to delete legacy keys."))?;
- db.unbind_keys_for_user(user_id, true)
- .context(ks_err!("Error in unbinding keys."))?;
-
- // Delete super key in cache, if exists.
- self.forget_all_keys_for_user(user_id);
- Ok(())
- }
- }
- }
-
- /// If the user hasn't been initialized yet, then this function generates the user's
- /// AfterFirstUnlock super key and sets the user's state to AfterFirstUnlock. Otherwise this
- /// function returns an error.
- pub fn init_user(
- &mut self,
- db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- user_id: UserId,
- password: &Password,
- ) -> Result<()> {
- log::info!("init_user(user={user_id})");
- match self.get_user_state(db, legacy_importer, user_id)? {
- UserState::AfterFirstUnlock(_) | UserState::BeforeFirstUnlock => {
- Err(Error::sys()).context(ks_err!("Tried to re-init an initialized user!"))
- }
- UserState::Uninitialized => {
- // Generate a new super key.
- let super_key =
- generate_aes256_key().context(ks_err!("Failed to generate AES 256 key."))?;
- // Derive an AES256 key from the password and re-encrypt the super key
- // before we insert it in the database.
- let (encrypted_super_key, blob_metadata) =
- Self::encrypt_with_password(&super_key, password)
- .context(ks_err!("Failed to encrypt super key with password!"))?;
-
- let key_entry = db
- .store_super_key(
- user_id,
- &USER_AFTER_FIRST_UNLOCK_SUPER_KEY,
- &encrypted_super_key,
- &blob_metadata,
- &KeyMetaData::new(),
- )
- .context(ks_err!("Failed to store super key."))?;
-
- self.populate_cache_from_super_key_blob(
- user_id,
- USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm,
- key_entry,
- password,
- )
- .context(ks_err!("Failed to initialize user!"))?;
- Ok(())
- }
- }
- }
-
/// Initializes the given user by creating their super keys, both AfterFirstUnlock and
/// UnlockedDeviceRequired. If allow_existing is true, then the user already being initialized
/// is not considered an error.
@@ -1325,390 +1240,3 @@ impl<'a> Deref for KeyBlob<'a> {
}
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::database::tests::make_bootlevel_key_entry;
- use crate::database::tests::make_test_key_entry;
- use crate::database::tests::new_test_db;
- use rand::prelude::*;
- const USER_ID: u32 = 0;
- const TEST_KEY_ALIAS: &str = "TEST_KEY";
- const TEST_BOOT_KEY_ALIAS: &str = "TEST_BOOT_KEY";
-
- pub fn generate_password_blob() -> Password<'static> {
- let mut rng = rand::thread_rng();
- let mut password = vec![0u8; 64];
- rng.fill_bytes(&mut password);
-
- let mut zvec = ZVec::new(64).expect("Failed to create ZVec");
- zvec[..].copy_from_slice(&password[..]);
-
- Password::Owned(zvec)
- }
-
- fn setup_test(pw: &Password) -> (Arc<RwLock<SuperKeyManager>>, KeystoreDB, LegacyImporter) {
- let mut keystore_db = new_test_db().unwrap();
- let mut legacy_importer = LegacyImporter::new(Arc::new(Default::default()));
- legacy_importer.set_empty();
- let skm: Arc<RwLock<SuperKeyManager>> = Default::default();
- assert!(skm
- .write()
- .unwrap()
- .init_user(&mut keystore_db, &legacy_importer, USER_ID, pw)
- .is_ok());
- (skm, keystore_db, legacy_importer)
- }
-
- fn assert_unlocked(
- skm: &Arc<RwLock<SuperKeyManager>>,
- keystore_db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- user_id: u32,
- err_msg: &str,
- ) {
- let user_state =
- skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
- match user_state {
- UserState::AfterFirstUnlock(_) => {}
- _ => panic!("{}", err_msg),
- }
- }
-
- fn assert_locked(
- skm: &Arc<RwLock<SuperKeyManager>>,
- keystore_db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- user_id: u32,
- err_msg: &str,
- ) {
- let user_state =
- skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
- match user_state {
- UserState::BeforeFirstUnlock => {}
- _ => panic!("{}", err_msg),
- }
- }
-
- fn assert_uninitialized(
- skm: &Arc<RwLock<SuperKeyManager>>,
- keystore_db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- user_id: u32,
- err_msg: &str,
- ) {
- let user_state =
- skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
- match user_state {
- UserState::Uninitialized => {}
- _ => panic!("{}", err_msg),
- }
- }
-
- #[test]
- fn test_init_user() {
- let pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
- }
-
- #[test]
- fn test_unlock_user() {
- let pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
-
- skm.write().unwrap().data.user_keys.clear();
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "Clearing the cache did not lock the user!",
- );
-
- assert!(skm
- .write()
- .unwrap()
- .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
- .is_ok());
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user did not unlock!",
- );
- }
-
- #[test]
- fn test_unlock_wrong_password() {
- let pw: Password = generate_password_blob();
- let wrong_pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
-
- skm.write().unwrap().data.user_keys.clear();
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "Clearing the cache did not lock the user!",
- );
-
- assert!(skm
- .write()
- .unwrap()
- .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &wrong_pw)
- .is_err());
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was unlocked with an incorrect password!",
- );
- }
-
- #[test]
- fn test_unlock_user_idempotent() {
- let pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
-
- skm.write().unwrap().data.user_keys.clear();
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "Clearing the cache did not lock the user!",
- );
-
- for _ in 0..5 {
- assert!(skm
- .write()
- .unwrap()
- .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
- .is_ok());
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user did not unlock!",
- );
- }
- }
-
- fn test_user_removal(locked: bool) {
- let pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
-
- assert!(make_test_key_entry(
- &mut keystore_db,
- Domain::APP,
- USER_ID.into(),
- TEST_KEY_ALIAS,
- None
- )
- .is_ok());
- assert!(make_bootlevel_key_entry(
- &mut keystore_db,
- Domain::APP,
- USER_ID.into(),
- TEST_BOOT_KEY_ALIAS,
- false
- )
- .is_ok());
-
- assert!(keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
- .unwrap());
- assert!(keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
- .unwrap());
-
- if locked {
- skm.write().unwrap().data.user_keys.clear();
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "Clearing the cache did not lock the user!",
- );
- }
-
- assert!(skm
- .write()
- .unwrap()
- .remove_user(&mut keystore_db, &legacy_importer, USER_ID)
- .is_ok());
- assert_uninitialized(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not removed!",
- );
-
- assert!(!skm
- .write()
- .unwrap()
- .super_key_exists_in_db_for_user(&mut keystore_db, &legacy_importer, USER_ID)
- .unwrap());
-
- assert!(!keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
- .unwrap());
- assert!(!keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
- .unwrap());
- }
-
- fn test_user_reset(locked: bool) {
- let pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
-
- assert!(make_test_key_entry(
- &mut keystore_db,
- Domain::APP,
- USER_ID.into(),
- TEST_KEY_ALIAS,
- None
- )
- .is_ok());
- assert!(make_bootlevel_key_entry(
- &mut keystore_db,
- Domain::APP,
- USER_ID.into(),
- TEST_BOOT_KEY_ALIAS,
- false
- )
- .is_ok());
- assert!(keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
- .unwrap());
- assert!(keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
- .unwrap());
-
- if locked {
- skm.write().unwrap().data.user_keys.clear();
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "Clearing the cache did not lock the user!",
- );
- assert!(skm
- .write()
- .unwrap()
- .reset_user(&mut keystore_db, &legacy_importer, USER_ID)
- .is_err());
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "User state should not have changed!",
- );
-
- // Keys should still exist.
- assert!(keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
- .unwrap());
- assert!(keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
- .unwrap());
- } else {
- assert!(skm
- .write()
- .unwrap()
- .reset_user(&mut keystore_db, &legacy_importer, USER_ID)
- .is_ok());
- assert_uninitialized(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not reset!",
- );
- assert!(!skm
- .write()
- .unwrap()
- .super_key_exists_in_db_for_user(&mut keystore_db, &legacy_importer, USER_ID)
- .unwrap());
-
- // Auth bound key should no longer exist.
- assert!(!keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
- .unwrap());
- assert!(keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
- .unwrap());
- }
- }
-
- #[test]
- fn test_remove_unlocked_user() {
- test_user_removal(false);
- }
-
- #[test]
- fn test_remove_locked_user() {
- test_user_removal(true);
- }
-
- #[test]
- fn test_reset_unlocked_user() {
- test_user_reset(false);
- }
-
- #[test]
- fn test_reset_locked_user() {
- test_user_reset(true);
- }
-}
diff --git a/keystore2/src/super_key/tests.rs b/keystore2/src/super_key/tests.rs
new file mode 100644
index 00000000..76a96a71
--- /dev/null
+++ b/keystore2/src/super_key/tests.rs
@@ -0,0 +1,287 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Super-key tests.
+
+use super::*;
+use crate::database::tests::make_bootlevel_key_entry;
+use crate::database::tests::make_test_key_entry;
+use crate::database::tests::new_test_db;
+use rand::prelude::*;
+const USER_ID: u32 = 0;
+const TEST_KEY_ALIAS: &str = "TEST_KEY";
+const TEST_BOOT_KEY_ALIAS: &str = "TEST_BOOT_KEY";
+
+pub fn generate_password_blob() -> Password<'static> {
+ let mut rng = rand::thread_rng();
+ let mut password = vec![0u8; 64];
+ rng.fill_bytes(&mut password);
+
+ let mut zvec = ZVec::new(64).expect("Failed to create ZVec");
+ zvec[..].copy_from_slice(&password[..]);
+
+ Password::Owned(zvec)
+}
+
+fn setup_test(pw: &Password) -> (Arc<RwLock<SuperKeyManager>>, KeystoreDB, LegacyImporter) {
+ let mut keystore_db = new_test_db().unwrap();
+ let mut legacy_importer = LegacyImporter::new(Arc::new(Default::default()));
+ legacy_importer.set_empty();
+ let skm: Arc<RwLock<SuperKeyManager>> = Default::default();
+ assert!(skm
+ .write()
+ .unwrap()
+ .initialize_user(&mut keystore_db, &legacy_importer, USER_ID, pw, false)
+ .is_ok());
+ (skm, keystore_db, legacy_importer)
+}
+
+fn assert_unlocked(
+ skm: &Arc<RwLock<SuperKeyManager>>,
+ keystore_db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: u32,
+ err_msg: &str,
+) {
+ let user_state =
+ skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+ match user_state {
+ UserState::AfterFirstUnlock(_) => {}
+ _ => panic!("{}", err_msg),
+ }
+}
+
+fn assert_locked(
+ skm: &Arc<RwLock<SuperKeyManager>>,
+ keystore_db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: u32,
+ err_msg: &str,
+) {
+ let user_state =
+ skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+ match user_state {
+ UserState::BeforeFirstUnlock => {}
+ _ => panic!("{}", err_msg),
+ }
+}
+
+fn assert_uninitialized(
+ skm: &Arc<RwLock<SuperKeyManager>>,
+ keystore_db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: u32,
+ err_msg: &str,
+) {
+ let user_state =
+ skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+ match user_state {
+ UserState::Uninitialized => {}
+ _ => panic!("{}", err_msg),
+ }
+}
+
+#[test]
+fn test_initialize_user() {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+}
+
+#[test]
+fn test_unlock_user() {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+
+ assert!(skm
+ .write()
+ .unwrap()
+ .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
+ .is_ok());
+ assert_unlocked(&skm, &mut keystore_db, &legacy_importer, USER_ID, "The user did not unlock!");
+}
+
+#[test]
+fn test_unlock_wrong_password() {
+ let pw: Password = generate_password_blob();
+ let wrong_pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+
+ assert!(skm
+ .write()
+ .unwrap()
+ .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &wrong_pw)
+ .is_err());
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was unlocked with an incorrect password!",
+ );
+}
+
+#[test]
+fn test_unlock_user_idempotent() {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+
+ for _ in 0..5 {
+ assert!(skm
+ .write()
+ .unwrap()
+ .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
+ .is_ok());
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user did not unlock!",
+ );
+ }
+}
+
+fn test_user_removal(locked: bool) {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ assert!(make_test_key_entry(
+ &mut keystore_db,
+ Domain::APP,
+ USER_ID.into(),
+ TEST_KEY_ALIAS,
+ None
+ )
+ .is_ok());
+ assert!(make_bootlevel_key_entry(
+ &mut keystore_db,
+ Domain::APP,
+ USER_ID.into(),
+ TEST_BOOT_KEY_ALIAS,
+ false
+ )
+ .is_ok());
+
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+
+ if locked {
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+ }
+
+ assert!(skm.write().unwrap().remove_user(&mut keystore_db, &legacy_importer, USER_ID).is_ok());
+ assert_uninitialized(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not removed!",
+ );
+
+ assert!(!skm
+ .write()
+ .unwrap()
+ .super_key_exists_in_db_for_user(&mut keystore_db, &legacy_importer, USER_ID)
+ .unwrap());
+
+ assert!(!keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ assert!(!keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+}
+
+#[test]
+fn test_remove_unlocked_user() {
+ test_user_removal(false);
+}
+
+#[test]
+fn test_remove_locked_user() {
+ test_user_removal(true);
+}
diff --git a/keystore2/src/sw_keyblob.rs b/keystore2/src/sw_keyblob.rs
index 47ab49fd..c0173b52 100644
--- a/keystore2/src/sw_keyblob.rs
+++ b/keystore2/src/sw_keyblob.rs
@@ -28,6 +28,9 @@ use anyhow::Result;
use keystore2_crypto::hmac_sha256;
use std::mem::size_of;
+#[cfg(test)]
+mod tests;
+
/// Root of trust value.
const SOFTWARE_ROOT_OF_TRUST: &[u8] = b"SW";
@@ -556,481 +559,3 @@ fn serialize_params(params: &[KeyParameter]) -> Result<Vec<u8>> {
.clone_from_slice(&serialized_size.to_ne_bytes());
Ok(result)
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
- KeyOrigin::KeyOrigin, KeyParameter::KeyParameter,
- KeyParameterValue::KeyParameterValue as KPV, KeyPurpose::KeyPurpose,
- PaddingMode::PaddingMode, Tag::Tag,
- };
-
- macro_rules! expect_err {
- ($result:expr, $err_msg:expr) => {
- assert!(
- $result.is_err(),
- "Expected error containing '{}', got success {:?}",
- $err_msg,
- $result
- );
- let err = $result.err();
- assert!(
- format!("{:?}", err).contains($err_msg),
- "Unexpected error {:?}, doesn't contain '{}'",
- err,
- $err_msg
- );
- };
- }
-
- #[test]
- fn test_consume_u8() {
- let buffer = [1, 2];
- let mut data = &buffer[..];
- assert_eq!(1u8, consume_u8(&mut data).unwrap());
- assert_eq!(2u8, consume_u8(&mut data).unwrap());
- let result = consume_u8(&mut data);
- expect_err!(result, "failed to find 1 byte");
- }
-
- #[test]
- fn test_consume_u32() {
- // All supported platforms are little-endian.
- let buffer = [
- 0x01, 0x02, 0x03, 0x04, // little-endian u32
- 0x04, 0x03, 0x02, 0x01, // little-endian u32
- 0x11, 0x12, 0x13,
- ];
- let mut data = &buffer[..];
- assert_eq!(0x04030201u32, consume_u32(&mut data).unwrap());
- assert_eq!(0x01020304u32, consume_u32(&mut data).unwrap());
- let result = consume_u32(&mut data);
- expect_err!(result, "failed to find 4 bytes");
- }
-
- #[test]
- fn test_consume_i64() {
- // All supported platforms are little-endian.
- let buffer = [
- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // little-endian i64
- 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // little-endian i64
- 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- ];
- let mut data = &buffer[..];
- assert_eq!(0x0807060504030201i64, consume_i64(&mut data).unwrap());
- assert_eq!(0x0102030405060708i64, consume_i64(&mut data).unwrap());
- let result = consume_i64(&mut data);
- expect_err!(result, "failed to find 8 bytes");
- }
-
- #[test]
- fn test_consume_vec() {
- let buffer = [
- 0x01, 0x00, 0x00, 0x00, 0xaa, //
- 0x00, 0x00, 0x00, 0x00, //
- 0x01, 0x00, 0x00, 0x00, 0xbb, //
- 0x07, 0x00, 0x00, 0x00, 0xbb, // not enough data
- ];
- let mut data = &buffer[..];
- assert_eq!(vec![0xaa], consume_vec(&mut data).unwrap());
- assert_eq!(Vec::<u8>::new(), consume_vec(&mut data).unwrap());
- assert_eq!(vec![0xbb], consume_vec(&mut data).unwrap());
- let result = consume_vec(&mut data);
- expect_err!(result, "failed to find 7 bytes");
-
- let buffer = [
- 0x01, 0x00, 0x00, //
- ];
- let mut data = &buffer[..];
- let result = consume_vec(&mut data);
- expect_err!(result, "failed to find 4 bytes");
- }
-
- #[test]
- fn test_key_new_from_serialized() {
- let hidden = hidden_params(&[], &[SOFTWARE_ROOT_OF_TRUST]);
- // Test data originally generated by instrumenting Cuttlefish C++ KeyMint while running VTS
- // tests.
- let tests = [
- (
- concat!(
- "0010000000d43c2f04f948521b81bdbf001310f5920000000000000000000000",
- "00000000000c0000006400000002000010200000000300003080000000010000",
- "2000000000010000200100000004000020020000000600002001000000be0200",
- "1000000000c1020030b0ad0100c20200307b150300bd020060a8bb52407b0100",
- "00ce02003011643401cf020030000000003b06b13ae6ae6671",
- ),
- KeyBlob {
- key_material: hex::decode("d43c2f04f948521b81bdbf001310f592").unwrap(),
- hw_enforced: vec![],
- sw_enforced: vec![
- KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::AES) },
- KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(128) },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::ENCRYPT),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::DECRYPT),
- },
- KeyParameter {
- tag: Tag::BLOCK_MODE,
- value: KPV::BlockMode(BlockMode::CBC),
- },
- KeyParameter {
- tag: Tag::PADDING,
- value: KPV::PaddingMode(PaddingMode::NONE),
- },
- KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
- KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
- KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
- KeyParameter {
- tag: Tag::CREATION_DATETIME,
- value: KPV::DateTime(1628871769000),
- },
- KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
- KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
- ],
- },
- Some(KeyFormat::RAW),
- ),
- (
- concat!(
- "00df0000003081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6b",
- "a14e46ab7ca532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654",
- "e9e79413cd503eae3d9cf68ed24f47a00706052b81040023a181890381860004",
- "006b840f0db0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d14",
- "0743bdd028db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b7",
- "45e30142e90685646661550344113aaf28bdee6cb02d19df1faab4398556a909",
- "7d6f64b95209601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3",
- "b877984d000000000000000000000000000000000c0000006400000002000010",
- "030000000a000010030000000100002002000000010000200300000005000020",
- "000000000300003009020000be02001000000000c1020030b0ad0100c2020030",
- "7b150300bd02006018d352407b010000ce02003011643401cf02003000000000",
- "2f69002e55e9b0a3"
- ),
- KeyBlob {
- key_material: hex::decode(concat!(
- "3081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6ba14e46ab7c",
- "a532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654e9e79413cd",
- "503eae3d9cf68ed24f47a00706052b81040023a181890381860004006b840f0d",
- "b0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d140743bdd028",
- "db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b745e30142e9",
- "0685646661550344113aaf28bdee6cb02d19df1faab4398556a9097d6f64b952",
- "09601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3b877984d",
- ))
- .unwrap(),
- hw_enforced: vec![],
- sw_enforced: vec![
- KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::EC) },
- KeyParameter { tag: Tag::EC_CURVE, value: KPV::EcCurve(EcCurve::P_521) },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::SIGN),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::VERIFY),
- },
- KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) },
- KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(521) },
- KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
- KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
- KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
- KeyParameter {
- tag: Tag::CREATION_DATETIME,
- value: KPV::DateTime(1628871775000),
- },
- KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
- KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
- ],
- },
- Some(KeyFormat::PKCS8),
- ),
- (
- concat!(
- "0037000000541d4c440223650d5f51753c1abd80c725034485551e874d62327c",
- "65f6247a057f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc00000000",
- "0000000000000000000000000c00000064000000020000108000000003000030",
- "b801000001000020020000000100002003000000050000200400000008000030",
- "00010000be02001000000000c1020030b0ad0100c20200307b150300bd020060",
- "00d752407b010000ce02003011643401cf0200300000000036e6986ffc45fbb0",
- ),
- KeyBlob {
- key_material: hex::decode(concat!(
- "541d4c440223650d5f51753c1abd80c725034485551e874d62327c65f6247a05",
- "7f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc"
- ))
- .unwrap(),
- hw_enforced: vec![],
- sw_enforced: vec![
- KeyParameter {
- tag: Tag::ALGORITHM,
- value: KPV::Algorithm(Algorithm::HMAC),
- },
- KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(440) },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::SIGN),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::VERIFY),
- },
- KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::SHA_2_256) },
- KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KPV::Integer(256) },
- KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
- KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
- KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
- KeyParameter {
- tag: Tag::CREATION_DATETIME,
- value: KPV::DateTime(1628871776000),
- },
- KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
- KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
- ],
- },
- Some(KeyFormat::RAW),
- ),
- (
- concat!(
- "00a8040000308204a40201000282010100bc47b5c71116766669b91fa747df87",
- "a1963df83956569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082",
- "825c7c6e482a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31",
- "c1ced84878edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c3973",
- "6838151642eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d",
- "5337c8bf9245d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0de",
- "b0957d61dbba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2",
- "540c84e45c4a99fb338b76bba7722856b5113341c349708937228f167d238ed8",
- "efb9cc19547dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af",
- "59fe87421af9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d3703",
- "8bf9f588ae20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178",
- "a848e06d558c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88e",
- "e610260e406c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30a",
- "e7a31f8262da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73a",
- "e6f8b29a9144eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b",
- "4217c8db50db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817",
- "d9380b190bd382aaffa37785759f285194c11a188bccde0e2e2902818100fb23",
- "3335770c9f3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a63",
- "3f7790d1011bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f2",
- "7a74852d6c7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd78009490",
- "4c2856d2b944fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f45990281",
- "8100bfecf2bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a7158458",
- "18e01181ff06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb",
- "0078b77fb5b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc84",
- "20fcbf48d1eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b57",
- "298902818100ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd",
- "3765559994a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55",
- "dad696d3821def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2",
- "d8480ed07a7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f6332",
- "38f521ba764902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6",
- "603c6265f70018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d46",
- "93fa48e3a9abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b",
- "2a57e18a2e131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc",
- "2af4ed75760858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1",
- "c0a258d882984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4",
- "340a9fae64a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c5526",
- "1941b3654533b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a2",
- "47f56a337e3b9845b4f2b61356000000000000000000000000000000000d0000",
- "007000000002000010010000000300003000080000c800005001000100000000",
- "0001000020020000000100002003000000050000200000000006000020010000",
- "00be02001000000000c1020030b0ad0100c20200307b150300bd020060a8bb52",
- "407b010000ce02003011643401cf02003000000000544862e9c961e857",
- ),
- KeyBlob {
- key_material: hex::decode(concat!(
- "308204a40201000282010100bc47b5c71116766669b91fa747df87a1963df839",
- "56569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082825c7c6e48",
- "2a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31c1ced84878",
- "edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c39736838151642",
- "eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d5337c8bf92",
- "45d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0deb0957d61db",
- "ba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2540c84e45c",
- "4a99fb338b76bba7722856b5113341c349708937228f167d238ed8efb9cc1954",
- "7dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af59fe87421a",
- "f9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d37038bf9f588ae",
- "20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178a848e06d55",
- "8c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88ee610260e40",
- "6c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30ae7a31f8262",
- "da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73ae6f8b29a91",
- "44eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b4217c8db50",
- "db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817d9380b190b",
- "d382aaffa37785759f285194c11a188bccde0e2e2902818100fb233335770c9f",
- "3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a633f7790d101",
- "1bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f27a74852d6c",
- "7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd780094904c2856d2b9",
- "44fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f459902818100bfecf2",
- "bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a715845818e01181ff",
- "06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb0078b77fb5",
- "b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc8420fcbf48d1",
- "eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b572989028181",
- "00ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd3765559994",
- "a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55dad696d382",
- "1def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2d8480ed07a",
- "7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f633238f521ba76",
- "4902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6603c6265f7",
- "0018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d4693fa48e3a9",
- "abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b2a57e18a2e",
- "131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc2af4ed7576",
- "0858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1c0a258d882",
- "984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4340a9fae64",
- "a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c55261941b36545",
- "33b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a247f56a337e",
- "3b9845b4f2b61356",
- ))
- .unwrap(),
- hw_enforced: vec![],
- sw_enforced: vec![
- KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::RSA) },
- KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(2048) },
- KeyParameter {
- tag: Tag::RSA_PUBLIC_EXPONENT,
- value: KPV::LongInteger(65537),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::SIGN),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::VERIFY),
- },
- KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) },
- KeyParameter {
- tag: Tag::PADDING,
- value: KPV::PaddingMode(PaddingMode::NONE),
- },
- KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
- KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
- KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
- KeyParameter {
- tag: Tag::CREATION_DATETIME,
- value: KPV::DateTime(1628871769000),
- },
- KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
- KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
- ],
- },
- // No support for RSA keys in export_key().
- None,
- ),
- ];
-
- for (input, want, want_format) in tests {
- let input = hex::decode(input).unwrap();
- let got = KeyBlob::new_from_serialized(&input, &hidden).expect("invalid keyblob!");
- assert!(got == want);
-
- if let Some(want_format) = want_format {
- let (got_format, _key_material, params) =
- export_key(&input, &[]).expect("invalid keyblob!");
- assert_eq!(got_format, want_format);
- // All the test cases are software-only keys.
- assert_eq!(params, got.sw_enforced);
- }
- }
- }
-
- #[test]
- fn test_add_der_len() {
- let tests = [
- (0, "00"),
- (1, "01"),
- (126, "7e"),
- (127, "7f"),
- (128, "8180"),
- (129, "8181"),
- (255, "81ff"),
- (256, "820100"),
- (257, "820101"),
- (65535, "82ffff"),
- ];
- for (input, want) in tests {
- let mut got = Vec::new();
- add_der_len(&mut got, input).unwrap();
- assert_eq!(hex::encode(got), want, " for input length {input}");
- }
- }
-
- #[test]
- fn test_pkcs8_wrap_key_p256() {
- // Key material taken from `ec_256_key` in
- // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
- let input = hex::decode(concat!(
- "3025", // SEQUENCE (ECPrivateKey)
- "020101", // INTEGER length 1 value 1 (version)
- "0420", // OCTET STRING (privateKey)
- "737c2ecd7b8d1940bf2930aa9b4ed3ff",
- "941eed09366bc03299986481f3a4d859",
- ))
- .unwrap();
- let want = hex::decode(concat!(
- // RFC 5208 s5
- "3041", // SEQUENCE (PrivateKeyInfo) {
- "020100", // INTEGER length 1 value 0 (version)
- "3013", // SEQUENCE length 0x13 (AlgorithmIdentifier) {
- "0607", // OBJECT IDENTIFIER length 7 (algorithm)
- "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
- "0608", // OBJECT IDENTIFIER length 8 (param)
- "2a8648ce3d030107", // 1.2.840.10045.3.1.7 (secp256r1)
- // } end SEQUENCE (AlgorithmIdentifier)
- "0427", // OCTET STRING (privateKey) holding...
- "3025", // SEQUENCE (ECPrivateKey)
- "020101", // INTEGER length 1 value 1 (version)
- "0420", // OCTET STRING length 0x20 (privateKey)
- "737c2ecd7b8d1940bf2930aa9b4ed3ff",
- "941eed09366bc03299986481f3a4d859",
- // } end SEQUENCE (ECPrivateKey)
- // } end SEQUENCE (PrivateKeyInfo)
- ))
- .unwrap();
- let got = pkcs8_wrap_nist_key(&input, EcCurve::P_256).unwrap();
- assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
- }
-
- #[test]
- fn test_pkcs8_wrap_key_p521() {
- // Key material taken from `ec_521_key` in
- // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
- let input = hex::decode(concat!(
- "3047", // SEQUENCE length 0xd3 (ECPrivateKey)
- "020101", // INTEGER length 1 value 1 (version)
- "0442", // OCTET STRING length 0x42 (privateKey)
- "0011458c586db5daa92afab03f4fe46a",
- "a9d9c3ce9a9b7a006a8384bec4c78e8e",
- "9d18d7d08b5bcfa0e53c75b064ad51c4",
- "49bae0258d54b94b1e885ded08ed4fb2",
- "5ce9",
- // } end SEQUENCE (ECPrivateKey)
- ))
- .unwrap();
- let want = hex::decode(concat!(
- // RFC 5208 s5
- "3060", // SEQUENCE (PrivateKeyInfo) {
- "020100", // INTEGER length 1 value 0 (version)
- "3010", // SEQUENCE length 0x10 (AlgorithmIdentifier) {
- "0607", // OBJECT IDENTIFIER length 7 (algorithm)
- "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
- "0605", // OBJECT IDENTIFIER length 5 (param)
- "2b81040023", // 1.3.132.0.35 (secp521r1)
- // } end SEQUENCE (AlgorithmIdentifier)
- "0449", // OCTET STRING (privateKey) holding...
- "3047", // SEQUENCE (ECPrivateKey)
- "020101", // INTEGER length 1 value 1 (version)
- "0442", // OCTET STRING length 0x42 (privateKey)
- "0011458c586db5daa92afab03f4fe46a",
- "a9d9c3ce9a9b7a006a8384bec4c78e8e",
- "9d18d7d08b5bcfa0e53c75b064ad51c4",
- "49bae0258d54b94b1e885ded08ed4fb2",
- "5ce9",
- // } end SEQUENCE (ECPrivateKey)
- // } end SEQUENCE (PrivateKeyInfo)
- ))
- .unwrap();
- let got = pkcs8_wrap_nist_key(&input, EcCurve::P_521).unwrap();
- assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
- }
-}
diff --git a/keystore2/src/sw_keyblob/tests.rs b/keystore2/src/sw_keyblob/tests.rs
new file mode 100644
index 00000000..fe01112c
--- /dev/null
+++ b/keystore2/src/sw_keyblob/tests.rs
@@ -0,0 +1,449 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Tests for software-backed keyblobs.
+use super::*;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ KeyOrigin::KeyOrigin, KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue as KPV,
+ KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, Tag::Tag,
+};
+
+macro_rules! expect_err {
+ ($result:expr, $err_msg:expr) => {
+ assert!(
+ $result.is_err(),
+ "Expected error containing '{}', got success {:?}",
+ $err_msg,
+ $result
+ );
+ let err = $result.err();
+ assert!(
+ format!("{:?}", err).contains($err_msg),
+ "Unexpected error {:?}, doesn't contain '{}'",
+ err,
+ $err_msg
+ );
+ };
+}
+
+#[test]
+fn test_consume_u8() {
+ let buffer = [1, 2];
+ let mut data = &buffer[..];
+ assert_eq!(1u8, consume_u8(&mut data).unwrap());
+ assert_eq!(2u8, consume_u8(&mut data).unwrap());
+ let result = consume_u8(&mut data);
+ expect_err!(result, "failed to find 1 byte");
+}
+
+#[test]
+fn test_consume_u32() {
+ // All supported platforms are little-endian.
+ let buffer = [
+ 0x01, 0x02, 0x03, 0x04, // little-endian u32
+ 0x04, 0x03, 0x02, 0x01, // little-endian u32
+ 0x11, 0x12, 0x13,
+ ];
+ let mut data = &buffer[..];
+ assert_eq!(0x04030201u32, consume_u32(&mut data).unwrap());
+ assert_eq!(0x01020304u32, consume_u32(&mut data).unwrap());
+ let result = consume_u32(&mut data);
+ expect_err!(result, "failed to find 4 bytes");
+}
+
+#[test]
+fn test_consume_i64() {
+ // All supported platforms are little-endian.
+ let buffer = [
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // little-endian i64
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // little-endian i64
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ ];
+ let mut data = &buffer[..];
+ assert_eq!(0x0807060504030201i64, consume_i64(&mut data).unwrap());
+ assert_eq!(0x0102030405060708i64, consume_i64(&mut data).unwrap());
+ let result = consume_i64(&mut data);
+ expect_err!(result, "failed to find 8 bytes");
+}
+
+#[test]
+fn test_consume_vec() {
+ let buffer = [
+ 0x01, 0x00, 0x00, 0x00, 0xaa, //
+ 0x00, 0x00, 0x00, 0x00, //
+ 0x01, 0x00, 0x00, 0x00, 0xbb, //
+ 0x07, 0x00, 0x00, 0x00, 0xbb, // not enough data
+ ];
+ let mut data = &buffer[..];
+ assert_eq!(vec![0xaa], consume_vec(&mut data).unwrap());
+ assert_eq!(Vec::<u8>::new(), consume_vec(&mut data).unwrap());
+ assert_eq!(vec![0xbb], consume_vec(&mut data).unwrap());
+ let result = consume_vec(&mut data);
+ expect_err!(result, "failed to find 7 bytes");
+
+ let buffer = [
+ 0x01, 0x00, 0x00, //
+ ];
+ let mut data = &buffer[..];
+ let result = consume_vec(&mut data);
+ expect_err!(result, "failed to find 4 bytes");
+}
+
+#[test]
+fn test_key_new_from_serialized() {
+ let hidden = hidden_params(&[], &[SOFTWARE_ROOT_OF_TRUST]);
+ // Test data originally generated by instrumenting Cuttlefish C++ KeyMint while running VTS
+ // tests.
+ let tests = [
+ (
+ concat!(
+ "0010000000d43c2f04f948521b81bdbf001310f5920000000000000000000000",
+ "00000000000c0000006400000002000010200000000300003080000000010000",
+ "2000000000010000200100000004000020020000000600002001000000be0200",
+ "1000000000c1020030b0ad0100c20200307b150300bd020060a8bb52407b0100",
+ "00ce02003011643401cf020030000000003b06b13ae6ae6671",
+ ),
+ KeyBlob {
+ key_material: hex::decode("d43c2f04f948521b81bdbf001310f592").unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::AES) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(128) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::ENCRYPT) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::DECRYPT) },
+ KeyParameter { tag: Tag::BLOCK_MODE, value: KPV::BlockMode(BlockMode::CBC) },
+ KeyParameter { tag: Tag::PADDING, value: KPV::PaddingMode(PaddingMode::NONE) },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871769000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ Some(KeyFormat::RAW),
+ ),
+ (
+ concat!(
+ "00df0000003081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6b",
+ "a14e46ab7ca532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654",
+ "e9e79413cd503eae3d9cf68ed24f47a00706052b81040023a181890381860004",
+ "006b840f0db0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d14",
+ "0743bdd028db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b7",
+ "45e30142e90685646661550344113aaf28bdee6cb02d19df1faab4398556a909",
+ "7d6f64b95209601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3",
+ "b877984d000000000000000000000000000000000c0000006400000002000010",
+ "030000000a000010030000000100002002000000010000200300000005000020",
+ "000000000300003009020000be02001000000000c1020030b0ad0100c2020030",
+ "7b150300bd02006018d352407b010000ce02003011643401cf02003000000000",
+ "2f69002e55e9b0a3"
+ ),
+ KeyBlob {
+ key_material: hex::decode(concat!(
+ "3081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6ba14e46ab7c",
+ "a532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654e9e79413cd",
+ "503eae3d9cf68ed24f47a00706052b81040023a181890381860004006b840f0d",
+ "b0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d140743bdd028",
+ "db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b745e30142e9",
+ "0685646661550344113aaf28bdee6cb02d19df1faab4398556a9097d6f64b952",
+ "09601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3b877984d",
+ ))
+ .unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::EC) },
+ KeyParameter { tag: Tag::EC_CURVE, value: KPV::EcCurve(EcCurve::P_521) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::SIGN) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::VERIFY) },
+ KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(521) },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871775000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ Some(KeyFormat::PKCS8),
+ ),
+ (
+ concat!(
+ "0037000000541d4c440223650d5f51753c1abd80c725034485551e874d62327c",
+ "65f6247a057f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc00000000",
+ "0000000000000000000000000c00000064000000020000108000000003000030",
+ "b801000001000020020000000100002003000000050000200400000008000030",
+ "00010000be02001000000000c1020030b0ad0100c20200307b150300bd020060",
+ "00d752407b010000ce02003011643401cf0200300000000036e6986ffc45fbb0",
+ ),
+ KeyBlob {
+ key_material: hex::decode(concat!(
+ "541d4c440223650d5f51753c1abd80c725034485551e874d62327c65f6247a05",
+ "7f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc"
+ ))
+ .unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::HMAC) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(440) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::SIGN) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::VERIFY) },
+ KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::SHA_2_256) },
+ KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KPV::Integer(256) },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871776000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ Some(KeyFormat::RAW),
+ ),
+ (
+ concat!(
+ "00a8040000308204a40201000282010100bc47b5c71116766669b91fa747df87",
+ "a1963df83956569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082",
+ "825c7c6e482a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31",
+ "c1ced84878edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c3973",
+ "6838151642eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d",
+ "5337c8bf9245d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0de",
+ "b0957d61dbba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2",
+ "540c84e45c4a99fb338b76bba7722856b5113341c349708937228f167d238ed8",
+ "efb9cc19547dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af",
+ "59fe87421af9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d3703",
+ "8bf9f588ae20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178",
+ "a848e06d558c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88e",
+ "e610260e406c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30a",
+ "e7a31f8262da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73a",
+ "e6f8b29a9144eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b",
+ "4217c8db50db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817",
+ "d9380b190bd382aaffa37785759f285194c11a188bccde0e2e2902818100fb23",
+ "3335770c9f3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a63",
+ "3f7790d1011bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f2",
+ "7a74852d6c7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd78009490",
+ "4c2856d2b944fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f45990281",
+ "8100bfecf2bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a7158458",
+ "18e01181ff06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb",
+ "0078b77fb5b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc84",
+ "20fcbf48d1eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b57",
+ "298902818100ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd",
+ "3765559994a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55",
+ "dad696d3821def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2",
+ "d8480ed07a7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f6332",
+ "38f521ba764902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6",
+ "603c6265f70018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d46",
+ "93fa48e3a9abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b",
+ "2a57e18a2e131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc",
+ "2af4ed75760858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1",
+ "c0a258d882984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4",
+ "340a9fae64a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c5526",
+ "1941b3654533b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a2",
+ "47f56a337e3b9845b4f2b61356000000000000000000000000000000000d0000",
+ "007000000002000010010000000300003000080000c800005001000100000000",
+ "0001000020020000000100002003000000050000200000000006000020010000",
+ "00be02001000000000c1020030b0ad0100c20200307b150300bd020060a8bb52",
+ "407b010000ce02003011643401cf02003000000000544862e9c961e857",
+ ),
+ KeyBlob {
+ key_material: hex::decode(concat!(
+ "308204a40201000282010100bc47b5c71116766669b91fa747df87a1963df839",
+ "56569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082825c7c6e48",
+ "2a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31c1ced84878",
+ "edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c39736838151642",
+ "eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d5337c8bf92",
+ "45d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0deb0957d61db",
+ "ba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2540c84e45c",
+ "4a99fb338b76bba7722856b5113341c349708937228f167d238ed8efb9cc1954",
+ "7dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af59fe87421a",
+ "f9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d37038bf9f588ae",
+ "20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178a848e06d55",
+ "8c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88ee610260e40",
+ "6c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30ae7a31f8262",
+ "da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73ae6f8b29a91",
+ "44eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b4217c8db50",
+ "db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817d9380b190b",
+ "d382aaffa37785759f285194c11a188bccde0e2e2902818100fb233335770c9f",
+ "3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a633f7790d101",
+ "1bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f27a74852d6c",
+ "7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd780094904c2856d2b9",
+ "44fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f459902818100bfecf2",
+ "bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a715845818e01181ff",
+ "06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb0078b77fb5",
+ "b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc8420fcbf48d1",
+ "eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b572989028181",
+ "00ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd3765559994",
+ "a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55dad696d382",
+ "1def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2d8480ed07a",
+ "7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f633238f521ba76",
+ "4902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6603c6265f7",
+ "0018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d4693fa48e3a9",
+ "abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b2a57e18a2e",
+ "131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc2af4ed7576",
+ "0858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1c0a258d882",
+ "984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4340a9fae64",
+ "a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c55261941b36545",
+ "33b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a247f56a337e",
+ "3b9845b4f2b61356",
+ ))
+ .unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::RSA) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(2048) },
+ KeyParameter { tag: Tag::RSA_PUBLIC_EXPONENT, value: KPV::LongInteger(65537) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::SIGN) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::VERIFY) },
+ KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) },
+ KeyParameter { tag: Tag::PADDING, value: KPV::PaddingMode(PaddingMode::NONE) },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871769000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ // No support for RSA keys in export_key().
+ None,
+ ),
+ ];
+
+ for (input, want, want_format) in tests {
+ let input = hex::decode(input).unwrap();
+ let got = KeyBlob::new_from_serialized(&input, &hidden).expect("invalid keyblob!");
+ assert!(got == want);
+
+ if let Some(want_format) = want_format {
+ let (got_format, _key_material, params) =
+ export_key(&input, &[]).expect("invalid keyblob!");
+ assert_eq!(got_format, want_format);
+ // All the test cases are software-only keys.
+ assert_eq!(params, got.sw_enforced);
+ }
+ }
+}
+
+#[test]
+fn test_add_der_len() {
+ let tests = [
+ (0, "00"),
+ (1, "01"),
+ (126, "7e"),
+ (127, "7f"),
+ (128, "8180"),
+ (129, "8181"),
+ (255, "81ff"),
+ (256, "820100"),
+ (257, "820101"),
+ (65535, "82ffff"),
+ ];
+ for (input, want) in tests {
+ let mut got = Vec::new();
+ add_der_len(&mut got, input).unwrap();
+ assert_eq!(hex::encode(got), want, " for input length {input}");
+ }
+}
+
+#[test]
+fn test_pkcs8_wrap_key_p256() {
+ // Key material taken from `ec_256_key` in
+ // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
+ let input = hex::decode(concat!(
+ "3025", // SEQUENCE (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0420", // OCTET STRING (privateKey)
+ "737c2ecd7b8d1940bf2930aa9b4ed3ff",
+ "941eed09366bc03299986481f3a4d859",
+ ))
+ .unwrap();
+ let want = hex::decode(concat!(
+ // RFC 5208 s5
+ "3041", // SEQUENCE (PrivateKeyInfo) {
+ "020100", // INTEGER length 1 value 0 (version)
+ "3013", // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+ "0607", // OBJECT IDENTIFIER length 7 (algorithm)
+ "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
+ "0608", // OBJECT IDENTIFIER length 8 (param)
+ "2a8648ce3d030107", // 1.2.840.10045.3.1.7 (secp256r1)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "0427", // OCTET STRING (privateKey) holding...
+ "3025", // SEQUENCE (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0420", // OCTET STRING length 0x20 (privateKey)
+ "737c2ecd7b8d1940bf2930aa9b4ed3ff",
+ "941eed09366bc03299986481f3a4d859",
+ // } end SEQUENCE (ECPrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+ ))
+ .unwrap();
+ let got = pkcs8_wrap_nist_key(&input, EcCurve::P_256).unwrap();
+ assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
+}
+
+#[test]
+fn test_pkcs8_wrap_key_p521() {
+ // Key material taken from `ec_521_key` in
+ // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
+ let input = hex::decode(concat!(
+ "3047", // SEQUENCE length 0xd3 (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0442", // OCTET STRING length 0x42 (privateKey)
+ "0011458c586db5daa92afab03f4fe46a",
+ "a9d9c3ce9a9b7a006a8384bec4c78e8e",
+ "9d18d7d08b5bcfa0e53c75b064ad51c4",
+ "49bae0258d54b94b1e885ded08ed4fb2",
+ "5ce9",
+ // } end SEQUENCE (ECPrivateKey)
+ ))
+ .unwrap();
+ let want = hex::decode(concat!(
+ // RFC 5208 s5
+ "3060", // SEQUENCE (PrivateKeyInfo) {
+ "020100", // INTEGER length 1 value 0 (version)
+ "3010", // SEQUENCE length 0x10 (AlgorithmIdentifier) {
+ "0607", // OBJECT IDENTIFIER length 7 (algorithm)
+ "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
+ "0605", // OBJECT IDENTIFIER length 5 (param)
+ "2b81040023", // 1.3.132.0.35 (secp521r1)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "0449", // OCTET STRING (privateKey) holding...
+ "3047", // SEQUENCE (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0442", // OCTET STRING length 0x42 (privateKey)
+ "0011458c586db5daa92afab03f4fe46a",
+ "a9d9c3ce9a9b7a006a8384bec4c78e8e",
+ "9d18d7d08b5bcfa0e53c75b064ad51c4",
+ "49bae0258d54b94b1e885ded08ed4fb2",
+ "5ce9",
+ // } end SEQUENCE (ECPrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+ ))
+ .unwrap();
+ let got = pkcs8_wrap_nist_key(&input, EcCurve::P_521).unwrap();
+ assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
+}
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index a3fd8824..0a545f76 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -49,6 +49,9 @@ use keystore2_apc_compat::{
use keystore2_crypto::{aes_gcm_decrypt, aes_gcm_encrypt, ZVec};
use std::iter::IntoIterator;
+#[cfg(test)]
+mod tests;
+
/// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to GeneralizedTime
/// 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
pub const UNDEFINED_NOT_AFTER: i64 = 253402300799000i64;
@@ -143,10 +146,7 @@ fn check_android_permission(permission: &str) -> anyhow::Result<()> {
binder::get_interface("permission")?;
let binder_result = {
- let _wp = watchdog::watch_millis(
- "In check_device_attestation_permissions: calling checkPermission.",
- 500,
- );
+ let _wp = watchdog::watch("check_android_permission: calling checkPermission");
permission_controller.checkPermission(
permission,
ThreadState::get_calling_pid(),
@@ -263,9 +263,8 @@ where
log::debug!("import parameters={import_params:?}");
let creation_result = {
- let _wp = watchdog::watch_millis(
- "In utils::import_keyblob_and_perform_op: calling importKey.",
- 500,
+ let _wp = watchdog::watch(
+ "utils::import_keyblob_and_perform_op: calling IKeyMintDevice::importKey",
);
map_km_error(km_dev.importKey(&import_params, format, &key_material, None))
}
@@ -306,9 +305,8 @@ where
NewBlobHandler: FnOnce(&[u8]) -> Result<()>,
{
let upgraded_blob = {
- let _wp = watchdog::watch_millis(
- "In utils::upgrade_keyblob_and_perform_op: calling upgradeKey.",
- 500,
+ let _wp = watchdog::watch(
+ "utils::upgrade_keyblob_and_perform_op: calling IKeyMintDevice::upgradeKey.",
);
map_km_error(km_dev.upgradeKey(key_blob, upgrade_params))
}
@@ -522,6 +520,8 @@ fn merge_and_filter_key_entry_lists(
}
fn estimate_safe_amount_to_return(
+ domain: Domain,
+ namespace: i64,
key_descriptors: &[KeyDescriptor],
response_size_limit: usize,
) -> usize {
@@ -546,11 +546,9 @@ fn estimate_safe_amount_to_return(
// 350KB and return a partial list.
if returned_bytes > response_size_limit {
log::warn!(
- "Key descriptors list ({} items) may exceed binder \
- size, returning {} items est {} bytes.",
+ "{domain:?}:{namespace}: Key descriptors list ({} items) may exceed binder \
+ size, returning {items_to_return} items est {returned_bytes} bytes.",
key_descriptors.len(),
- items_to_return,
- returned_bytes
);
break;
}
@@ -584,7 +582,7 @@ pub fn list_key_entries(
const RESPONSE_SIZE_LIMIT: usize = 358400;
let safe_amount_to_return =
- estimate_safe_amount_to_return(&merged_key_entries, RESPONSE_SIZE_LIMIT);
+ estimate_safe_amount_to_return(domain, namespace, &merged_key_entries, RESPONSE_SIZE_LIMIT);
Ok(merged_key_entries[..safe_amount_to_return].to_vec())
}
@@ -599,6 +597,15 @@ pub fn count_key_entries(db: &mut KeystoreDB, domain: Domain, namespace: i64) ->
Ok((legacy_keys.len() + num_keys_in_db) as i32)
}
+/// For params remove sensitive data before returning a string for logging
+pub fn log_security_safe_params(params: &[KmKeyParameter]) -> Vec<KmKeyParameter> {
+ params
+ .iter()
+ .filter(|kp| (kp.tag != Tag::APPLICATION_ID && kp.tag != Tag::APPLICATION_DATA))
+ .cloned()
+ .collect::<Vec<KmKeyParameter>>()
+}
+
/// Trait implemented by objects that can be used to decrypt cipher text using AES-GCM.
pub trait AesGcm {
/// Deciphers `data` using the initialization vector `iv` and AEAD tag `tag`
@@ -627,101 +634,3 @@ impl<T: AesGcmKey> AesGcm for T {
aes_gcm_encrypt(plaintext, self.key()).context(ks_err!("Encryption failed."))
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use anyhow::Result;
-
- #[test]
- fn check_device_attestation_permissions_test() -> Result<()> {
- check_device_attestation_permissions().or_else(|error| {
- match error.root_cause().downcast_ref::<Error>() {
- // Expected: the context for this test might not be allowed to attest device IDs.
- Some(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)) => Ok(()),
- // Other errors are unexpected
- _ => Err(error),
- }
- })
- }
-
- fn create_key_descriptors_from_aliases(key_aliases: &[&str]) -> Vec<KeyDescriptor> {
- key_aliases
- .iter()
- .map(|key_alias| KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(key_alias.to_string()),
- blob: None,
- })
- .collect::<Vec<KeyDescriptor>>()
- }
-
- fn aliases_from_key_descriptors(key_descriptors: &[KeyDescriptor]) -> Vec<String> {
- key_descriptors
- .iter()
- .map(
- |kd| {
- if let Some(alias) = &kd.alias {
- String::from(alias)
- } else {
- String::from("")
- }
- },
- )
- .collect::<Vec<String>>()
- }
-
- #[test]
- fn test_safe_amount_to_return() -> Result<()> {
- let key_aliases = vec!["key1", "key2", "key3"];
- let key_descriptors = create_key_descriptors_from_aliases(&key_aliases);
-
- assert_eq!(estimate_safe_amount_to_return(&key_descriptors, 20), 1);
- assert_eq!(estimate_safe_amount_to_return(&key_descriptors, 50), 2);
- assert_eq!(estimate_safe_amount_to_return(&key_descriptors, 100), 3);
- Ok(())
- }
-
- #[test]
- fn test_merge_and_sort_lists_without_filtering() -> Result<()> {
- let legacy_key_aliases = vec!["key_c", "key_a", "key_b"];
- let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
- let db_key_aliases = vec!["key_a", "key_d"];
- let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
- let result =
- merge_and_filter_key_entry_lists(&legacy_key_descriptors, &db_key_descriptors, None);
- assert_eq!(aliases_from_key_descriptors(&result), vec!["key_a", "key_b", "key_c", "key_d"]);
- Ok(())
- }
-
- #[test]
- fn test_merge_and_sort_lists_with_filtering() -> Result<()> {
- let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
- let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
- let db_key_aliases = vec!["key_c", "key_g"];
- let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
- let result = merge_and_filter_key_entry_lists(
- &legacy_key_descriptors,
- &db_key_descriptors,
- Some("key_b"),
- );
- assert_eq!(aliases_from_key_descriptors(&result), vec!["key_c", "key_e", "key_f", "key_g"]);
- Ok(())
- }
-
- #[test]
- fn test_merge_and_sort_lists_with_filtering_and_dups() -> Result<()> {
- let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
- let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
- let db_key_aliases = vec!["key_d", "key_e", "key_g"];
- let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
- let result = merge_and_filter_key_entry_lists(
- &legacy_key_descriptors,
- &db_key_descriptors,
- Some("key_c"),
- );
- assert_eq!(aliases_from_key_descriptors(&result), vec!["key_d", "key_e", "key_f", "key_g"]);
- Ok(())
- }
-}
diff --git a/keystore2/src/utils/tests.rs b/keystore2/src/utils/tests.rs
new file mode 100644
index 00000000..618ea472
--- /dev/null
+++ b/keystore2/src/utils/tests.rs
@@ -0,0 +1,125 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Utility functions tests.
+
+use super::*;
+use anyhow::Result;
+
+#[test]
+fn check_device_attestation_permissions_test() -> Result<()> {
+ check_device_attestation_permissions().or_else(|error| {
+ match error.root_cause().downcast_ref::<Error>() {
+ // Expected: the context for this test might not be allowed to attest device IDs.
+ Some(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)) => Ok(()),
+ // Other errors are unexpected
+ _ => Err(error),
+ }
+ })
+}
+
+fn create_key_descriptors_from_aliases(key_aliases: &[&str]) -> Vec<KeyDescriptor> {
+ key_aliases
+ .iter()
+ .map(|key_alias| KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(key_alias.to_string()),
+ blob: None,
+ })
+ .collect::<Vec<KeyDescriptor>>()
+}
+
+fn aliases_from_key_descriptors(key_descriptors: &[KeyDescriptor]) -> Vec<String> {
+ key_descriptors
+ .iter()
+ .map(|kd| if let Some(alias) = &kd.alias { String::from(alias) } else { String::from("") })
+ .collect::<Vec<String>>()
+}
+
+#[test]
+fn test_safe_amount_to_return() -> Result<()> {
+ let key_aliases = vec!["key1", "key2", "key3"];
+ let key_descriptors = create_key_descriptors_from_aliases(&key_aliases);
+
+ assert_eq!(estimate_safe_amount_to_return(Domain::APP, 1017, &key_descriptors, 20), 1);
+ assert_eq!(estimate_safe_amount_to_return(Domain::APP, 1017, &key_descriptors, 50), 2);
+ assert_eq!(estimate_safe_amount_to_return(Domain::APP, 1017, &key_descriptors, 100), 3);
+ Ok(())
+}
+
+#[test]
+fn test_merge_and_sort_lists_without_filtering() -> Result<()> {
+ let legacy_key_aliases = vec!["key_c", "key_a", "key_b"];
+ let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+ let db_key_aliases = vec!["key_a", "key_d"];
+ let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+ let result =
+ merge_and_filter_key_entry_lists(&legacy_key_descriptors, &db_key_descriptors, None);
+ assert_eq!(aliases_from_key_descriptors(&result), vec!["key_a", "key_b", "key_c", "key_d"]);
+ Ok(())
+}
+
+#[test]
+fn test_merge_and_sort_lists_with_filtering() -> Result<()> {
+ let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
+ let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+ let db_key_aliases = vec!["key_c", "key_g"];
+ let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+ let result = merge_and_filter_key_entry_lists(
+ &legacy_key_descriptors,
+ &db_key_descriptors,
+ Some("key_b"),
+ );
+ assert_eq!(aliases_from_key_descriptors(&result), vec!["key_c", "key_e", "key_f", "key_g"]);
+ Ok(())
+}
+
+#[test]
+fn test_merge_and_sort_lists_with_filtering_and_dups() -> Result<()> {
+ let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
+ let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+ let db_key_aliases = vec!["key_d", "key_e", "key_g"];
+ let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+ let result = merge_and_filter_key_entry_lists(
+ &legacy_key_descriptors,
+ &db_key_descriptors,
+ Some("key_c"),
+ );
+ assert_eq!(aliases_from_key_descriptors(&result), vec!["key_d", "key_e", "key_f", "key_g"]);
+ Ok(())
+}
+
+#[test]
+fn test_list_key_parameters_with_filter_on_security_sensitive_info() -> Result<()> {
+ let params = vec![
+ KmKeyParameter { tag: Tag::APPLICATION_ID, value: KeyParameterValue::Integer(0) },
+ KmKeyParameter { tag: Tag::APPLICATION_DATA, value: KeyParameterValue::Integer(0) },
+ KmKeyParameter {
+ tag: Tag::CERTIFICATE_NOT_AFTER,
+ value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER),
+ },
+ KmKeyParameter { tag: Tag::CERTIFICATE_NOT_BEFORE, value: KeyParameterValue::DateTime(0) },
+ ];
+ let wanted = vec![
+ KmKeyParameter {
+ tag: Tag::CERTIFICATE_NOT_AFTER,
+ value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER),
+ },
+ KmKeyParameter { tag: Tag::CERTIFICATE_NOT_BEFORE, value: KeyParameterValue::DateTime(0) },
+ ];
+
+ assert_eq!(log_security_safe_params(&params), wanted);
+ Ok(())
+}
diff --git a/keystore2/src/watchdog_helper.rs b/keystore2/src/watchdog_helper.rs
index 92a0abc1..1072ac07 100644
--- a/keystore2/src/watchdog_helper.rs
+++ b/keystore2/src/watchdog_helper.rs
@@ -23,6 +23,11 @@ pub mod watchdog {
pub use watchdog_rs::WatchPoint;
use watchdog_rs::Watchdog;
+ /// Default timeout interval, in milliseconds.
+ pub const DEFAULT_TIMEOUT_MS: u64 = 500;
+
+ const DEFAULT_TIMEOUT: Duration = Duration::from_millis(DEFAULT_TIMEOUT_MS);
+
lazy_static! {
/// A Watchdog thread, that can be used to create watch points.
static ref WD: Arc<Watchdog> = Watchdog::new(Duration::from_secs(10));
@@ -33,14 +38,19 @@ pub mod watchdog {
Watchdog::watch(&WD, id, Duration::from_millis(millis))
}
- /// Like `watch_millis` but with a callback that is called every time a report
- /// is printed about this watch point.
+ /// Sets a watch point with `id` and a default timeout of [`DEFAULT_TIMEOUT_MS`] milliseconds.
+ pub fn watch(id: &'static str) -> Option<WatchPoint> {
+ Watchdog::watch(&WD, id, DEFAULT_TIMEOUT)
+ }
+
+ /// Like `watch_millis` but with context that is included every time a report is printed about
+ /// this watch point.
pub fn watch_millis_with(
id: &'static str,
millis: u64,
- callback: impl Fn() -> String + Send + 'static,
+ context: impl std::fmt::Debug + Send + 'static,
) -> Option<WatchPoint> {
- Watchdog::watch_with(&WD, id, Duration::from_millis(millis), callback)
+ Watchdog::watch_with(&WD, id, Duration::from_millis(millis), context)
}
}
@@ -53,11 +63,15 @@ pub mod watchdog {
fn watch_millis(_: &'static str, _: u64) -> Option<WatchPoint> {
None
}
+ /// Sets a Noop watch point.
+ fn watch(_: &'static str) -> Option<WatchPoint> {
+ None
+ }
pub fn watch_millis_with(
_: &'static str,
_: u64,
- _: impl Fn() -> String + Send + 'static,
+ _: impl std::fmt::Debug + Send + 'static,
) -> Option<WatchPoint> {
None
}
diff --git a/keystore2/test_utils/authorizations.rs b/keystore2/test_utils/authorizations.rs
index 2cb2aaf6..a96d9946 100644
--- a/keystore2/test_utils/authorizations.rs
+++ b/keystore2/test_utils/authorizations.rs
@@ -360,6 +360,15 @@ impl AuthSetBuilder {
});
self
}
+
+ /// Set unlocked-device-required
+ pub fn unlocked_device_required(mut self) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::UNLOCKED_DEVICE_REQUIRED,
+ value: KeyParameterValue::BoolValue(true),
+ });
+ self
+ }
}
impl Deref for AuthSetBuilder {
diff --git a/keystore2/test_utils/key_generations.rs b/keystore2/test_utils/key_generations.rs
index a733be39..c845332d 100644
--- a/keystore2/test_utils/key_generations.rs
+++ b/keystore2/test_utils/key_generations.rs
@@ -14,14 +14,12 @@
//! This module implements test utils to generate various types of keys.
-use anyhow::Result;
-use core::ops::Range;
-use nix::unistd::getuid;
-use std::collections::HashSet;
-use std::fmt::Write;
-
-use binder::ThreadState;
-
+use crate::authorizations::AuthSetBuilder;
+use crate::ffi_test_utils::{
+ get_os_patchlevel, get_os_version, get_value_from_attest_record, get_vendor_patchlevel,
+ validate_certchain_with_strict_issuer_check,
+};
+use crate::SecLevel;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType,
@@ -30,18 +28,16 @@ use android_hardware_security_keymint::aidl::android::hardware::security::keymin
};
use android_system_keystore2::aidl::android::system::keystore2::{
AuthenticatorSpec::AuthenticatorSpec, Authorization::Authorization,
- CreateOperationResponse::CreateOperationResponse, Domain::Domain,
- IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+ CreateOperationResponse::CreateOperationResponse, Domain::Domain, KeyDescriptor::KeyDescriptor,
KeyMetadata::KeyMetadata, ResponseCode::ResponseCode,
};
-
-use crate::authorizations::AuthSetBuilder;
use android_system_keystore2::binder::{ExceptionCode, Result as BinderResult};
-
-use crate::ffi_test_utils::{
- get_os_patchlevel, get_os_version, get_value_from_attest_record, get_vendor_patchlevel,
- validate_certchain_with_strict_issuer_check,
-};
+use anyhow::Result;
+use binder::ThreadState;
+use core::ops::Range;
+use nix::unistd::getuid;
+use std::collections::HashSet;
+use std::fmt::Write;
/// Shell namespace.
pub const SELINUX_SHELL_NAMESPACE: i64 = 1;
@@ -391,12 +387,6 @@ pub fn map_ks_error<T>(r: BinderResult<T>) -> Result<T, Error> {
})
}
-/// Indicate whether the default device is KeyMint (rather than Keymaster).
-pub fn has_default_keymint() -> bool {
- binder::is_declared("android.hardware.security.keymint.IKeyMintDevice/default")
- .expect("Could not check for declared keymint interface")
-}
-
/// Verify that given key param is listed in given authorizations list.
pub fn check_key_param(authorizations: &[Authorization], key_param: &KeyParameter) -> bool {
authorizations.iter().any(|auth| &auth.keyParameter == key_param)
@@ -404,15 +394,15 @@ pub fn check_key_param(authorizations: &[Authorization], key_param: &KeyParamete
/// Verify the given key authorizations with the expected authorizations.
pub fn check_key_authorizations(
+ sl: &SecLevel,
authorizations: &[Authorization],
expected_params: &[KeyParameter],
expected_key_origin: KeyOrigin,
) {
// Make sure key authorizations contains only `ALLOWED_TAGS_IN_KEY_AUTHS`
authorizations.iter().all(|auth| {
- // Ignore `INVALID` tag if the backend is Keymaster and not KeyMint.
- // Keymaster allows INVALID tag for unsupported key parameters.
- if !has_default_keymint() && auth.keyParameter.tag == Tag::INVALID {
+ // Ignore `INVALID` tag
+ if auth.keyParameter.tag == Tag::INVALID {
return true;
}
assert!(
@@ -423,7 +413,7 @@ pub fn check_key_authorizations(
true
});
- //Check allowed-expected-key-parameters are present in given key authorizations list.
+ // Check allowed-expected-key-parameters are present in given key authorizations list.
expected_params.iter().all(|key_param| {
// `INCLUDE_UNIQUE_ID` is not strictly expected to be in key authorizations but has been
// put there by some implementations so cope with that.
@@ -433,13 +423,25 @@ pub fn check_key_authorizations(
return true;
}
- // Ignore below parameters if the backend is Keymaster and not KeyMint.
- // Keymaster does not support these parameters. These key parameters are introduced in
- // KeyMint1.0.
- if !has_default_keymint() {
- if matches!(key_param.tag, Tag::RSA_OAEP_MGF_DIGEST | Tag::USAGE_COUNT_LIMIT) {
+ // `Tag::RSA_OAEP_MGF_DIGEST` was added in KeyMint 1.0, but the KeyMint VTS tests didn't
+ // originally check for its presence and so some implementations of early versions (< 3) of
+ // the KeyMint HAL don't include it (cf. b/297306437 and aosp/2758513).
+ //
+ // Given that Keymaster implementations will also omit this tag, skip the check for it
+ // altogether (and rely on the updated KeyMint VTS tests to ensure that up-level KeyMint
+ // implementations correctly populate this tag).
+ if matches!(key_param.tag, Tag::RSA_OAEP_MGF_DIGEST) {
+ return true;
+ }
+
+ if sl.is_keymaster() {
+ // `Tag::USAGE_COUNT_LIMIT` was added in KeyMint 1.0, so don't check for it if the
+ // underlying device is a Keymaster implementation.
+ if matches!(key_param.tag, Tag::USAGE_COUNT_LIMIT) {
return true;
}
+ // `KeyPurpose::ATTEST_KEY` was added in KeyMint 1.0, so don't check for it if the
+ // underlying device is a Keymaster implementation.
if key_param.tag == Tag::PURPOSE
&& key_param.value == KeyParameterValue::KeyPurpose(KeyPurpose::ATTEST_KEY)
{
@@ -457,11 +459,15 @@ pub fn check_key_authorizations(
true
});
- check_common_auths(authorizations, expected_key_origin);
+ check_common_auths(sl, authorizations, expected_key_origin);
}
/// Verify common key authorizations.
-fn check_common_auths(authorizations: &[Authorization], expected_key_origin: KeyOrigin) {
+fn check_common_auths(
+ sl: &SecLevel,
+ authorizations: &[Authorization],
+ expected_key_origin: KeyOrigin,
+) {
assert!(check_key_param(
authorizations,
&KeyParameter {
@@ -505,7 +511,7 @@ fn check_common_auths(authorizations: &[Authorization], expected_key_origin: Key
}
));
- if has_default_keymint() {
+ if sl.is_keymint() {
assert!(authorizations
.iter()
.map(|auth| &auth.keyParameter)
@@ -532,7 +538,7 @@ pub fn get_key_auth(authorizations: &[Authorization], tag: Tag) -> Option<&Autho
/// Digest: SHA_2_256
/// Curve: P_256
pub fn generate_ec_p256_signing_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
@@ -552,7 +558,7 @@ pub fn generate_ec_p256_signing_key(
gen_params = gen_params.clone().attestation_challenge(challenge.to_vec());
}
- match sec_level.generateKey(
+ match sl.binder.generateKey(
&KeyDescriptor { domain, nspace, alias, blob: None },
None,
&gen_params,
@@ -569,6 +575,7 @@ pub fn generate_ec_p256_signing_key(
}
check_key_authorizations(
+ sl,
&key_metadata.authorizations,
&gen_params,
KeyOrigin::GENERATED,
@@ -581,7 +588,7 @@ pub fn generate_ec_p256_signing_key(
/// Generate EC signing key.
pub fn generate_ec_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
@@ -596,7 +603,7 @@ pub fn generate_ec_key(
.digest(digest)
.ec_curve(ec_curve);
- let key_metadata = sec_level.generateKey(
+ let key_metadata = sl.binder.generateKey(
&KeyDescriptor { domain, nspace, alias, blob: None },
None,
&gen_params,
@@ -615,13 +622,13 @@ pub fn generate_ec_key(
} else {
assert!(key_metadata.key.blob.is_none());
}
- check_key_authorizations(&key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
+ check_key_authorizations(sl, &key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
Ok(key_metadata)
}
/// Generate a RSA key with the given key parameters, alias, domain and namespace.
pub fn generate_rsa_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
@@ -653,7 +660,7 @@ pub fn generate_rsa_key(
gen_params = gen_params.attestation_challenge(value.to_vec())
}
- let key_metadata = sec_level.generateKey(
+ let key_metadata = sl.binder.generateKey(
&KeyDescriptor { domain, nspace, alias, blob: None },
attest_key,
&gen_params,
@@ -677,7 +684,7 @@ pub fn generate_rsa_key(
|| key_metadata.key.blob.is_none()
);
- check_key_authorizations(&key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
+ check_key_authorizations(sl, &key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
// If `RSA_OAEP_MGF_DIGEST` tag is not mentioned explicitly while generating/importing a key,
// then make sure `RSA_OAEP_MGF_DIGEST` tag with default value (SHA1) must not be included in
// key authorization list.
@@ -695,7 +702,7 @@ pub fn generate_rsa_key(
/// Generate AES/3DES key.
pub fn generate_sym_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
algorithm: Algorithm,
size: i32,
alias: &str,
@@ -716,7 +723,7 @@ pub fn generate_sym_key(
gen_params = gen_params.min_mac_length(val);
}
- let key_metadata = sec_level.generateKey(
+ let key_metadata = sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -734,13 +741,13 @@ pub fn generate_sym_key(
// Should not have an attestation record.
assert!(key_metadata.certificateChain.is_none());
- check_key_authorizations(&key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
+ check_key_authorizations(sl, &key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
Ok(key_metadata)
}
/// Generate HMAC key.
pub fn generate_hmac_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
alias: &str,
key_size: i32,
min_mac_len: i32,
@@ -755,7 +762,7 @@ pub fn generate_hmac_key(
.min_mac_length(min_mac_len)
.digest(digest);
- let key_metadata = sec_level.generateKey(
+ let key_metadata = sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -774,7 +781,7 @@ pub fn generate_hmac_key(
// Should not have an attestation record.
assert!(key_metadata.certificateChain.is_none());
- check_key_authorizations(&key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
+ check_key_authorizations(sl, &key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
Ok(key_metadata)
}
@@ -785,7 +792,7 @@ pub fn generate_hmac_key(
/// RSA-Key-Size: 2048
/// EC-Curve: EcCurve::P_256
pub fn generate_attestation_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
algorithm: Algorithm,
att_challenge: &[u8],
) -> binder::Result<KeyMetadata> {
@@ -794,7 +801,7 @@ pub fn generate_attestation_key(
if algorithm == Algorithm::RSA {
let alias = "ks_rsa_attest_test_key";
let metadata = generate_rsa_key(
- sec_level,
+ sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -812,13 +819,9 @@ pub fn generate_attestation_key(
.unwrap();
Ok(metadata)
} else {
- let metadata = generate_ec_attestation_key(
- sec_level,
- att_challenge,
- Digest::SHA_2_256,
- EcCurve::P_256,
- )
- .unwrap();
+ let metadata =
+ generate_ec_attestation_key(sl, att_challenge, Digest::SHA_2_256, EcCurve::P_256)
+ .unwrap();
Ok(metadata)
}
@@ -827,7 +830,7 @@ pub fn generate_attestation_key(
/// Generate EC attestation key with the given
/// curve, attestation-challenge and attestation-app-id.
pub fn generate_ec_attestation_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
att_challenge: &[u8],
digest: Digest,
ec_curve: EcCurve,
@@ -841,7 +844,7 @@ pub fn generate_ec_attestation_key(
.digest(digest)
.attestation_challenge(att_challenge.to_vec());
- let attestation_key_metadata = sec_level.generateKey(
+ let attestation_key_metadata = sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -860,6 +863,7 @@ pub fn generate_ec_attestation_key(
assert!(attestation_key_metadata.certificateChain.is_some());
check_key_authorizations(
+ sl,
&attestation_key_metadata.authorizations,
&gen_params,
KeyOrigin::GENERATED,
@@ -869,7 +873,7 @@ pub fn generate_ec_attestation_key(
/// Generate EC-P-256 key and attest it with given attestation key.
pub fn generate_ec_256_attested_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
alias: Option<String>,
att_challenge: &[u8],
attest_key: &KeyDescriptor,
@@ -883,7 +887,8 @@ pub fn generate_ec_256_attested_key(
.ec_curve(EcCurve::P_256)
.attestation_challenge(att_challenge.to_vec());
- let ec_key_metadata = sec_level
+ let ec_key_metadata = sl
+ .binder
.generateKey(
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias, blob: None },
Some(attest_key),
@@ -898,19 +903,25 @@ pub fn generate_ec_256_attested_key(
// Shouldn't have an attestation record.
assert!(ec_key_metadata.certificateChain.is_none());
- check_key_authorizations(&ec_key_metadata.authorizations, &ec_gen_params, KeyOrigin::GENERATED);
+ check_key_authorizations(
+ sl,
+ &ec_key_metadata.authorizations,
+ &ec_gen_params,
+ KeyOrigin::GENERATED,
+ );
Ok(ec_key_metadata)
}
/// Imports above defined RSA key - `RSA_2048_KEY` and validates imported key parameters.
pub fn import_rsa_2048_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
import_params: AuthSetBuilder,
) -> binder::Result<KeyMetadata> {
- let key_metadata = sec_level
+ let key_metadata = sl
+ .binder
.importKey(
&KeyDescriptor { domain, nspace, alias, blob: None },
None,
@@ -923,7 +934,7 @@ pub fn import_rsa_2048_key(
assert!(key_metadata.certificate.is_some());
assert!(key_metadata.certificateChain.is_none());
- check_key_authorizations(&key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
+ check_key_authorizations(sl, &key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
// Check below auths explicitly, they might not be addd in import parameters.
assert!(check_key_param(
@@ -967,13 +978,14 @@ pub fn import_rsa_2048_key(
/// Imports above defined EC key - `EC_P_256_KEY` and validates imported key parameters.
pub fn import_ec_p_256_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
import_params: AuthSetBuilder,
) -> binder::Result<KeyMetadata> {
- let key_metadata = sec_level
+ let key_metadata = sl
+ .binder
.importKey(
&KeyDescriptor { domain, nspace, alias, blob: None },
None,
@@ -986,7 +998,7 @@ pub fn import_ec_p_256_key(
assert!(key_metadata.certificate.is_some());
assert!(key_metadata.certificateChain.is_none());
- check_key_authorizations(&key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
+ check_key_authorizations(sl, &key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
// Check below auths explicitly, they might not be addd in import parameters.
assert!(check_key_param(
@@ -1013,7 +1025,7 @@ pub fn import_ec_p_256_key(
/// Import sample AES key and validate its key parameters.
pub fn import_aes_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
@@ -1030,7 +1042,7 @@ pub fn import_aes_key(
.purpose(KeyPurpose::DECRYPT)
.padding_mode(PaddingMode::PKCS7);
- let key_metadata = sec_level.importKey(
+ let key_metadata = sl.binder.importKey(
&KeyDescriptor { domain, nspace, alias, blob: None },
None,
&import_params,
@@ -1038,7 +1050,7 @@ pub fn import_aes_key(
AES_KEY,
)?;
- check_key_authorizations(&key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
+ check_key_authorizations(sl, &key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
// Check below auths explicitly, they might not be addd in import parameters.
assert!(check_key_param(
@@ -1070,7 +1082,7 @@ pub fn import_aes_key(
/// Import sample 3DES key and validate its key parameters.
pub fn import_3des_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
@@ -1089,7 +1101,7 @@ pub fn import_3des_key(
.purpose(KeyPurpose::DECRYPT)
.padding_mode(PaddingMode::PKCS7);
- let key_metadata = sec_level.importKey(
+ let key_metadata = sl.binder.importKey(
&KeyDescriptor { domain, nspace, alias, blob: None },
None,
&import_params,
@@ -1097,7 +1109,7 @@ pub fn import_3des_key(
TRIPLE_DES_KEY,
)?;
- check_key_authorizations(&key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
+ check_key_authorizations(sl, &key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
// Check below auths explicitly, they might not be addd in import parameters.
assert!(check_key_param(
@@ -1132,7 +1144,7 @@ pub fn import_3des_key(
/// Import sample HMAC key and validate its key parameters.
pub fn import_hmac_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
@@ -1149,7 +1161,7 @@ pub fn import_hmac_key(
.digest(Digest::SHA_2_256)
.min_mac_length(256);
- let key_metadata = sec_level.importKey(
+ let key_metadata = sl.binder.importKey(
&KeyDescriptor { domain, nspace, alias, blob: None },
None,
&import_params,
@@ -1157,7 +1169,7 @@ pub fn import_hmac_key(
HMAC_KEY,
)?;
- check_key_authorizations(&key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
+ check_key_authorizations(sl, &key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
// Check below auths explicitly, they might not be addd in import parameters.
assert!(check_key_param(
@@ -1182,7 +1194,7 @@ pub fn import_hmac_key(
/// Imports RSA encryption key with WRAP_KEY purpose.
pub fn import_wrapping_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
wrapping_key_data: &[u8],
wrapping_key_alias: Option<String>,
) -> binder::Result<KeyMetadata> {
@@ -1199,7 +1211,7 @@ pub fn import_wrapping_key(
.cert_not_before(0)
.cert_not_after(253402300799000);
- sec_level.importKey(
+ sl.binder.importKey(
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias: wrapping_key_alias, blob: None },
None,
&wrapping_key_params,
@@ -1210,7 +1222,7 @@ pub fn import_wrapping_key(
/// Import wrapped key using given wrapping key.
pub fn import_wrapped_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
alias: Option<String>,
wrapping_key_metadata: &KeyMetadata,
wrapped_key: Option<Vec<u8>>,
@@ -1223,7 +1235,7 @@ pub fn import_wrapped_key(
authenticatorId: 0,
}];
- let key_metadata = sec_level.importWrappedKey(
+ let key_metadata = sl.binder.importWrappedKey(
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias, blob: wrapped_key },
&wrapping_key_metadata.key,
None,
@@ -1236,14 +1248,14 @@ pub fn import_wrapped_key(
/// Import wrapping key and then import wrapped key using wrapping key.
pub fn import_wrapping_key_and_wrapped_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
wrapping_key_alias: Option<String>,
wrapping_key_params: AuthSetBuilder,
) -> binder::Result<KeyMetadata> {
- let wrapping_key_metadata = sec_level.importKey(
+ let wrapping_key_metadata = sl.binder.importKey(
&KeyDescriptor { domain, nspace, alias: wrapping_key_alias, blob: None },
None,
&wrapping_key_params,
@@ -1251,12 +1263,12 @@ pub fn import_wrapping_key_and_wrapped_key(
WRAPPING_KEY,
)?;
- import_wrapped_key(sec_level, alias, &wrapping_key_metadata, Some(WRAPPED_KEY.to_vec()))
+ import_wrapped_key(sl, alias, &wrapping_key_metadata, Some(WRAPPED_KEY.to_vec()))
}
/// Import given key material as AES-256-GCM-NONE transport key.
pub fn import_transport_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
transport_key_alias: Option<String>,
transport_key: &[u8],
) -> binder::Result<KeyMetadata> {
@@ -1271,7 +1283,7 @@ pub fn import_transport_key(
.purpose(KeyPurpose::ENCRYPT)
.purpose(KeyPurpose::DECRYPT);
- sec_level.importKey(
+ sl.binder.importKey(
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias: transport_key_alias, blob: None },
None,
&transport_key_params,
@@ -1282,7 +1294,7 @@ pub fn import_transport_key(
/// Generate EC key with purpose AGREE_KEY.
pub fn generate_ec_agree_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
ec_curve: EcCurve,
digest: Digest,
domain: Domain,
@@ -1296,7 +1308,7 @@ pub fn generate_ec_agree_key(
.digest(digest)
.ec_curve(ec_curve);
- match sec_level.generateKey(
+ match sl.binder.generateKey(
&KeyDescriptor { domain, nspace, alias, blob: None },
None,
&gen_params,
@@ -1310,6 +1322,7 @@ pub fn generate_ec_agree_key(
}
check_key_authorizations(
+ sl,
&key_metadata.authorizations,
&gen_params,
KeyOrigin::GENERATED,
@@ -1322,7 +1335,7 @@ pub fn generate_ec_agree_key(
/// Helper method to import AES keys `total_count` of times.
pub fn import_aes_keys(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
alias_prefix: String,
total_count: Range<i32>,
) -> binder::Result<HashSet<String>> {
@@ -1334,7 +1347,7 @@ pub fn import_aes_keys(
write!(alias, "{}_{}", alias_prefix, count).unwrap();
imported_key_aliases.insert(alias.clone());
- import_aes_key(sec_level, Domain::APP, -1, Some(alias))?;
+ import_aes_key(sl, Domain::APP, -1, Some(alias))?;
}
Ok(imported_key_aliases)
@@ -1342,7 +1355,7 @@ pub fn import_aes_keys(
/// Generate attested EC-P_256 key with device id attestation.
pub fn generate_key_with_attest_id(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
algorithm: Algorithm,
alias: Option<String>,
att_challenge: &[u8],
@@ -1405,7 +1418,7 @@ pub fn generate_key_with_attest_id(
}
}
- sec_level.generateKey(
+ sl.binder.generateKey(
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias, blob: None },
Some(attest_key),
&ec_gen_params,
@@ -1416,11 +1429,11 @@ pub fn generate_key_with_attest_id(
/// Generate Key and validate key characteristics.
pub fn generate_key(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
gen_params: &AuthSetBuilder,
alias: &str,
) -> binder::Result<KeyMetadata> {
- let key_metadata = sec_level.generateKey(
+ let key_metadata = sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -1474,19 +1487,19 @@ pub fn generate_key(
assert!(!att_app_id.is_empty());
}
}
- check_key_authorizations(&key_metadata.authorizations, gen_params, KeyOrigin::GENERATED);
+ check_key_authorizations(sl, &key_metadata.authorizations, gen_params, KeyOrigin::GENERATED);
Ok(key_metadata)
}
/// Generate a key using given authorizations and create an operation using the generated key.
pub fn create_key_and_operation(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
gen_params: &AuthSetBuilder,
op_params: &AuthSetBuilder,
alias: &str,
) -> binder::Result<CreateOperationResponse> {
- let key_metadata = generate_key(sec_level, gen_params, alias)?;
+ let key_metadata = generate_key(sl, gen_params, alias)?;
- sec_level.createOperation(&key_metadata.key, op_params, false)
+ sl.binder.createOperation(&key_metadata.key, op_params, false)
}
diff --git a/keystore2/test_utils/lib.rs b/keystore2/test_utils/lib.rs
index 8394ca1c..825657fd 100644
--- a/keystore2/test_utils/lib.rs
+++ b/keystore2/test_utils/lib.rs
@@ -19,7 +19,13 @@ use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use std::{env::temp_dir, ops::Deref};
-use android_system_keystore2::aidl::android::system::keystore2::IKeystoreService::IKeystoreService;
+use android_system_keystore2::aidl::android::system::keystore2::{
+ IKeystoreService::IKeystoreService,
+ IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice, SecurityLevel::SecurityLevel,
+};
use android_security_authorization::aidl::android::security::authorization::IKeystoreAuthorization::IKeystoreAuthorization;
pub mod authorizations;
@@ -123,3 +129,68 @@ pub fn get_keystore_service() -> binder::Strong<dyn IKeystoreService> {
pub fn get_keystore_auth_service() -> binder::Strong<dyn IKeystoreAuthorization> {
binder::get_interface(AUTH_SERVICE_NAME).unwrap()
}
+
+/// Security level-specific data.
+pub struct SecLevel {
+ /// Binder connection for the top-level service.
+ pub keystore2: binder::Strong<dyn IKeystoreService>,
+ /// Binder connection for the security level.
+ pub binder: binder::Strong<dyn IKeystoreSecurityLevel>,
+ /// Security level.
+ pub level: SecurityLevel,
+}
+
+impl SecLevel {
+ /// Return security level data for TEE.
+ pub fn tee() -> Self {
+ let level = SecurityLevel::TRUSTED_ENVIRONMENT;
+ let keystore2 = get_keystore_service();
+ let binder =
+ keystore2.getSecurityLevel(level).expect("TEE security level should always be present");
+ Self { keystore2, binder, level }
+ }
+ /// Return security level data for StrongBox, if present.
+ pub fn strongbox() -> Option<Self> {
+ let level = SecurityLevel::STRONGBOX;
+ let keystore2 = get_keystore_service();
+ match key_generations::map_ks_error(keystore2.getSecurityLevel(level)) {
+ Ok(binder) => Some(Self { keystore2, binder, level }),
+ Err(e) => {
+ assert_eq!(e, key_generations::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE));
+ None
+ }
+ }
+ }
+ /// Indicate whether this security level is a KeyMint implementation (not Keymaster).
+ pub fn is_keymint(&self) -> bool {
+ let instance = match self.level {
+ SecurityLevel::TRUSTED_ENVIRONMENT => "default",
+ SecurityLevel::STRONGBOX => "strongbox",
+ l => panic!("unexpected level {l:?}"),
+ };
+ let name = format!("android.hardware.security.keymint.IKeyMintDevice/{instance}");
+ binder::is_declared(&name).expect("Could not check for declared keymint interface")
+ }
+
+ /// Indicate whether this security level is a Keymaster implementation (not KeyMint).
+ pub fn is_keymaster(&self) -> bool {
+ !self.is_keymint()
+ }
+
+ /// Get KeyMint version.
+ /// Returns 0 if the underlying device is Keymaster not KeyMint.
+ pub fn get_keymint_version(&self) -> i32 {
+ let instance = match self.level {
+ SecurityLevel::TRUSTED_ENVIRONMENT => "default",
+ SecurityLevel::STRONGBOX => "strongbox",
+ l => panic!("unexpected level {l:?}"),
+ };
+ let name = format!("android.hardware.security.keymint.IKeyMintDevice/{instance}");
+ if binder::is_declared(&name).expect("Could not check for declared keymint interface") {
+ let km: binder::Strong<dyn IKeyMintDevice> = binder::get_interface(&name).unwrap();
+ km.getInterfaceVersion().unwrap()
+ } else {
+ 0
+ }
+ }
+}
diff --git a/keystore2/test_utils/run_as.rs b/keystore2/test_utils/run_as.rs
index be643b6b..d39d0697 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 @@ fn transition(se_context: selinux::Context, uid: Uid, gid: Gid) {
/// 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/Android.bp b/keystore2/tests/Android.bp
index 01ea7465..ca9f5e36 100644
--- a/keystore2/tests/Android.bp
+++ b/keystore2/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_hardware_backed_security",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "system_security_license"
@@ -38,9 +39,13 @@ rust_test {
rustlibs: [
"android.hardware.security.secureclock-V1-rust",
"android.security.authorization-rust",
+ "android.security.maintenance-rust",
"libaconfig_android_hardware_biometrics_rust",
+ "libandroid_logger",
+ "libandroid_security_flags_rust",
"libbinder_rs",
"libkeystore2_test_utils",
+ "liblog_rust",
"libnix",
"libopenssl",
"librustutils",
diff --git a/keystore2/tests/AndroidTest.xml b/keystore2/tests/AndroidTest.xml
index 7db36f7e..dde18a9b 100644
--- a/keystore2/tests/AndroidTest.xml
+++ b/keystore2/tests/AndroidTest.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<configuration description="Config to run keystore2_client_tests device tests.">
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
</target_preparer>
diff --git a/keystore2/tests/keystore2_client_3des_key_tests.rs b/keystore2/tests/keystore2_client_3des_key_tests.rs
index eda24db0..29f16177 100644
--- a/keystore2/tests/keystore2_client_3des_key_tests.rs
+++ b/keystore2/tests/keystore2_client_3des_key_tests.rs
@@ -12,26 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use crate::keystore2_client_test_utils::{
+ perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
- PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+ PaddingMode::PaddingMode,
};
-
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
-};
-
-use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error,
-};
-
-use crate::keystore2_client_test_utils::{
- perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
};
+use keystore2_test_utils::{authorizations, key_generations, key_generations::Error, SecLevel};
/// Generate a 3DES key. Create encryption and decryption operations using the generated key.
fn create_3des_key_and_operation(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
padding_mode: PaddingMode,
block_mode: BlockMode,
nonce: &mut Option<Vec<u8>>,
@@ -39,7 +34,7 @@ fn create_3des_key_and_operation(
let alias = format!("ks_3des_test_key_{}{}", block_mode.0, padding_mode.0);
let key_metadata = key_generations::generate_sym_key(
- sec_level,
+ sl,
Algorithm::TRIPLE_DES,
168,
&alias,
@@ -50,7 +45,7 @@ fn create_3des_key_and_operation(
// Encrypts `SAMPLE_PLAIN_TEXT` whose length is multiple of DES block size.
let cipher_text = perform_sample_sym_key_encrypt_op(
- sec_level,
+ &sl.binder,
padding_mode,
block_mode,
nonce,
@@ -60,7 +55,7 @@ fn create_3des_key_and_operation(
assert!(cipher_text.is_some());
let plain_text = perform_sample_sym_key_decrypt_op(
- sec_level,
+ &sl.binder,
&cipher_text.unwrap(),
padding_mode,
block_mode,
@@ -80,16 +75,15 @@ fn create_3des_key_and_operation(
/// Test should generate keys and perform operation successfully.
#[test]
fn keystore2_3des_ecb_cbc_generate_key_success() {
- let keystore2 = get_keystore_service();
let block_modes = [BlockMode::ECB, BlockMode::CBC];
let padding_modes = [PaddingMode::PKCS7, PaddingMode::NONE];
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
for block_mode in block_modes {
for padding_mode in padding_modes {
assert_eq!(
Ok(()),
- create_3des_key_and_operation(&sec_level, padding_mode, block_mode, &mut None)
+ create_3des_key_and_operation(&sl, padding_mode, block_mode, &mut None)
);
}
}
@@ -99,13 +93,12 @@ fn keystore2_3des_ecb_cbc_generate_key_success() {
/// an error code `UNSUPPORTED_KEY_SIZE`.
#[test]
fn keystore2_3des_key_fails_unsupported_key_size() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "3des_key_test_invalid_1";
let invalid_key_size = 128;
let result = key_generations::map_ks_error(key_generations::generate_sym_key(
- &sec_level,
+ &sl,
Algorithm::TRIPLE_DES,
invalid_key_size,
alias,
@@ -122,8 +115,7 @@ fn keystore2_3des_key_fails_unsupported_key_size() {
/// `UNSUPPORTED_PADDING_MODE`.
#[test]
fn keystore2_3des_key_fails_missing_padding() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "3des_key_test_missing_padding";
let gen_params = authorizations::AuthSetBuilder::new()
@@ -134,7 +126,8 @@ fn keystore2_3des_key_fails_missing_padding() {
.key_size(168)
.block_mode(BlockMode::ECB);
- let key_metadata = sec_level
+ let key_metadata = sl
+ .binder
.generateKey(
&KeyDescriptor {
domain: Domain::APP,
@@ -153,7 +146,7 @@ fn keystore2_3des_key_fails_missing_padding() {
.purpose(KeyPurpose::ENCRYPT)
.block_mode(BlockMode::ECB);
- let result = key_generations::map_ks_error(sec_level.createOperation(
+ let result = key_generations::map_ks_error(sl.binder.createOperation(
&key_metadata.key,
&op_params,
false,
@@ -166,12 +159,11 @@ fn keystore2_3des_key_fails_missing_padding() {
/// multiple of the DES block size.
#[test]
fn keystore2_3des_key_encrypt_fails_invalid_input_length() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "3des_key_test_invalid_input_len";
let key_metadata = key_generations::generate_sym_key(
- &sec_level,
+ &sl,
Algorithm::TRIPLE_DES,
168,
alias,
@@ -186,7 +178,8 @@ fn keystore2_3des_key_encrypt_fails_invalid_input_length() {
.padding_mode(PaddingMode::NONE)
.block_mode(BlockMode::ECB);
- let op_response = sec_level
+ let op_response = sl
+ .binder
.createOperation(&key_metadata.key, &op_params, false)
.expect("Error in creation of operation using rebound key.");
assert!(op_response.iOperation.is_some());
@@ -204,11 +197,10 @@ fn keystore2_3des_key_encrypt_fails_invalid_input_length() {
/// error code `UNSUPPORTED_BLOCK_MODE`.
#[test]
fn keystore2_3des_key_fails_unsupported_block_mode() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let result = key_generations::map_ks_error(create_3des_key_and_operation(
- &sec_level,
+ &sl,
PaddingMode::NONE,
BlockMode::CTR,
&mut None,
diff --git a/keystore2/tests/keystore2_client_aes_key_tests.rs b/keystore2/tests/keystore2_client_aes_key_tests.rs
index 313f596f..9f85c383 100644
--- a/keystore2/tests/keystore2_client_aes_key_tests.rs
+++ b/keystore2/tests/keystore2_client_aes_key_tests.rs
@@ -12,26 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use crate::keystore2_client_test_utils::{
+ perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
- PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+ PaddingMode::PaddingMode,
};
-
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
-};
-
-use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error,
-};
-
-use crate::keystore2_client_test_utils::{
- perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
};
+use keystore2_test_utils::{authorizations, key_generations, key_generations::Error, SecLevel};
/// Generate a AES key. Create encrypt and decrypt operations using the generated key.
fn create_aes_key_and_operation(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
key_size: i32,
padding_mode: PaddingMode,
block_mode: BlockMode,
@@ -42,7 +37,7 @@ fn create_aes_key_and_operation(
let alias = format!("ks_aes_test_key_{}{}{}", key_size, block_mode.0, padding_mode.0);
let key_metadata = key_generations::generate_sym_key(
- sec_level,
+ sl,
Algorithm::AES,
key_size,
&alias,
@@ -52,7 +47,7 @@ fn create_aes_key_and_operation(
)?;
let cipher_text = perform_sample_sym_key_encrypt_op(
- sec_level,
+ &sl.binder,
padding_mode,
block_mode,
nonce,
@@ -63,7 +58,7 @@ fn create_aes_key_and_operation(
assert!(cipher_text.is_some());
let plain_text = perform_sample_sym_key_decrypt_op(
- sec_level,
+ &sl.binder,
&cipher_text.unwrap(),
padding_mode,
block_mode,
@@ -83,19 +78,18 @@ fn create_aes_key_and_operation(
/// Test should generate keys and perform operation successfully.
#[test]
fn keystore2_aes_ecb_cbc_generate_key() {
- let keystore2 = get_keystore_service();
let key_sizes = [128, 256];
let block_modes = [BlockMode::ECB, BlockMode::CBC];
let padding_modes = [PaddingMode::PKCS7, PaddingMode::NONE];
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
for key_size in key_sizes {
for block_mode in block_modes {
for padding_mode in padding_modes {
assert_eq!(
Ok(()),
create_aes_key_and_operation(
- &sec_level,
+ &sl,
key_size,
padding_mode,
block_mode,
@@ -115,16 +109,14 @@ fn keystore2_aes_ecb_cbc_generate_key() {
/// Test should generate keys and perform operation successfully.
#[test]
fn keystore2_aes_ctr_gcm_generate_key_success() {
- let keystore2 = get_keystore_service();
let key_sizes = [128, 256];
let key_params = [(BlockMode::CTR, None, None), (BlockMode::GCM, Some(128), Some(128))];
-
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
for key_size in key_sizes {
for (block_mode, mac_len, min_mac_len) in key_params {
let result = key_generations::map_ks_error(create_aes_key_and_operation(
- &sec_level,
+ &sl,
key_size,
PaddingMode::NONE,
block_mode,
@@ -145,16 +137,14 @@ fn keystore2_aes_ctr_gcm_generate_key_success() {
/// with an error code `INCOMPATIBLE_PADDING_MODE`.
#[test]
fn keystore2_aes_ctr_gcm_generate_key_fails_incompatible() {
- let keystore2 = get_keystore_service();
let key_sizes = [128, 256];
let key_params = [(BlockMode::CTR, None, None), (BlockMode::GCM, Some(128), Some(128))];
-
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
for key_size in key_sizes {
for (block_mode, mac_len, min_mac_len) in key_params {
let result = key_generations::map_ks_error(create_aes_key_and_operation(
- &sec_level,
+ &sl,
key_size,
PaddingMode::PKCS7,
block_mode,
@@ -173,12 +163,11 @@ fn keystore2_aes_ctr_gcm_generate_key_fails_incompatible() {
/// an error code `UNSUPPORTED_KEY_SIZE`.
#[test]
fn keystore2_aes_key_fails_unsupported_key_size() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "aes_key_test_invalid_1";
let result = key_generations::map_ks_error(key_generations::generate_sym_key(
- &sec_level,
+ &sl,
Algorithm::AES,
1024,
alias,
@@ -194,12 +183,11 @@ fn keystore2_aes_key_fails_unsupported_key_size() {
/// Test should fail to generate a key with an error code `MISSING_MIN_MAC_LENGTH`.
#[test]
fn keystore2_aes_gcm_key_fails_missing_min_mac_len() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "aes_key_test_invalid_1";
let result = key_generations::map_ks_error(key_generations::generate_sym_key(
- &sec_level,
+ &sl,
Algorithm::AES,
128,
alias,
@@ -215,8 +203,7 @@ fn keystore2_aes_gcm_key_fails_missing_min_mac_len() {
/// an operation with `UNSUPPORTED_BLOCK_MODE` error code.
#[test]
fn keystore2_aes_key_op_fails_multi_block_modes() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "aes_key_test_invalid_1";
let gen_params = authorizations::AuthSetBuilder::new()
@@ -229,7 +216,8 @@ fn keystore2_aes_key_op_fails_multi_block_modes() {
.block_mode(BlockMode::CBC)
.padding_mode(PaddingMode::NONE);
- let key_metadata = sec_level
+ let key_metadata = sl
+ .binder
.generateKey(
&KeyDescriptor {
domain: Domain::APP,
@@ -250,7 +238,7 @@ fn keystore2_aes_key_op_fails_multi_block_modes() {
.block_mode(BlockMode::CBC)
.padding_mode(PaddingMode::NONE);
- let result = key_generations::map_ks_error(sec_level.createOperation(
+ let result = key_generations::map_ks_error(sl.binder.createOperation(
&key_metadata.key,
&op_params,
false,
@@ -263,8 +251,7 @@ fn keystore2_aes_key_op_fails_multi_block_modes() {
/// an operation with `UNSUPPORTED_PADDING_MODE` error code.
#[test]
fn keystore2_aes_key_op_fails_multi_padding_modes() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "aes_key_test_invalid_1";
let gen_params = authorizations::AuthSetBuilder::new()
@@ -277,7 +264,8 @@ fn keystore2_aes_key_op_fails_multi_padding_modes() {
.padding_mode(PaddingMode::PKCS7)
.padding_mode(PaddingMode::NONE);
- let key_metadata = sec_level
+ let key_metadata = sl
+ .binder
.generateKey(
&KeyDescriptor {
domain: Domain::APP,
@@ -298,7 +286,7 @@ fn keystore2_aes_key_op_fails_multi_padding_modes() {
.padding_mode(PaddingMode::PKCS7)
.padding_mode(PaddingMode::NONE);
- let result = key_generations::map_ks_error(sec_level.createOperation(
+ let result = key_generations::map_ks_error(sl.binder.createOperation(
&key_metadata.key,
&op_params,
false,
@@ -312,12 +300,11 @@ fn keystore2_aes_key_op_fails_multi_padding_modes() {
/// `INCOMPATIBLE_PADDING_MODE` error code.
#[test]
fn keystore2_aes_key_op_fails_incompatible_padding() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "aes_key_test_invalid_1";
let key_metadata = key_generations::generate_sym_key(
- &sec_level,
+ &sl,
Algorithm::AES,
128,
alias,
@@ -328,7 +315,7 @@ fn keystore2_aes_key_op_fails_incompatible_padding() {
.unwrap();
let result = key_generations::map_ks_error(perform_sample_sym_key_encrypt_op(
- &sec_level,
+ &sl.binder,
PaddingMode::PKCS7,
BlockMode::ECB,
&mut None,
@@ -344,12 +331,11 @@ fn keystore2_aes_key_op_fails_incompatible_padding() {
/// `INCOMPATIBLE_BLOCK_MODE` error code.
#[test]
fn keystore2_aes_key_op_fails_incompatible_blockmode() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "aes_key_test_invalid_1";
let key_metadata = key_generations::generate_sym_key(
- &sec_level,
+ &sl,
Algorithm::AES,
128,
alias,
@@ -360,7 +346,7 @@ fn keystore2_aes_key_op_fails_incompatible_blockmode() {
.unwrap();
let result = key_generations::map_ks_error(perform_sample_sym_key_encrypt_op(
- &sec_level,
+ &sl.binder,
PaddingMode::NONE,
BlockMode::CBC,
&mut None,
@@ -376,13 +362,12 @@ fn keystore2_aes_key_op_fails_incompatible_blockmode() {
/// `MISSING_MAC_LENGTH` error code.
#[test]
fn keystore2_aes_gcm_op_fails_missing_mac_len() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let mac_len = None;
let min_mac_len = Some(128);
let result = key_generations::map_ks_error(create_aes_key_and_operation(
- &sec_level,
+ &sl,
128,
PaddingMode::NONE,
BlockMode::GCM,
@@ -404,13 +389,12 @@ fn keystore2_aes_gcm_op_fails_missing_mac_len() {
/// an operation with `INVALID_MAC_LENGTH` error code.
#[test]
fn keystore2_aes_gcm_op_fails_invalid_mac_len() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let mac_len = Some(96);
let min_mac_len = Some(104);
let result = key_generations::map_ks_error(create_aes_key_and_operation(
- &sec_level,
+ &sl,
128,
PaddingMode::NONE,
BlockMode::GCM,
@@ -427,11 +411,10 @@ fn keystore2_aes_gcm_op_fails_invalid_mac_len() {
/// `UNSUPPORTED_MAC_LENGTH` error code.
#[test]
fn keystore2_aes_gcm_op_fails_unsupported_mac_len() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let result = key_generations::map_ks_error(create_aes_key_and_operation(
- &sec_level,
+ &sl,
128,
PaddingMode::NONE,
BlockMode::GCM,
@@ -448,13 +431,12 @@ fn keystore2_aes_gcm_op_fails_unsupported_mac_len() {
/// `CALLER_NONCE_PROHIBITED` error code.
#[test]
fn keystore2_aes_key_op_fails_nonce_prohibited() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "aes_key_test_nonce_1";
let mut nonce = Some(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let key_metadata = key_generations::generate_sym_key(
- &sec_level,
+ &sl,
Algorithm::AES,
128,
alias,
@@ -465,7 +447,7 @@ fn keystore2_aes_key_op_fails_nonce_prohibited() {
.unwrap();
let result = key_generations::map_ks_error(perform_sample_sym_key_encrypt_op(
- &sec_level,
+ &sl.binder,
PaddingMode::NONE,
BlockMode::CBC,
&mut nonce,
diff --git a/keystore2/tests/keystore2_client_attest_key_tests.rs b/keystore2/tests/keystore2_client_attest_key_tests.rs
index 454248a3..a303ee89 100644
--- a/keystore2/tests/keystore2_client_attest_key_tests.rs
+++ b/keystore2/tests/keystore2_client_attest_key_tests.rs
@@ -12,8 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::getuid;
-
+use crate::keystore2_client_test_utils::{
+ app_attest_key_feature_exists, device_id_attestation_feature_exists, get_attest_id_value,
+ is_second_imei_id_attestation_required, skip_device_id_attest_tests,
+};
+use crate::{
+ skip_device_id_attestation_tests, skip_test_if_no_app_attest_key_feature,
+ skip_test_if_no_device_id_attestation_feature,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
@@ -23,22 +29,12 @@ use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor,
ResponseCode::ResponseCode,
};
-
-use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error,
-};
-
use keystore2_test_utils::ffi_test_utils::{get_value_from_attest_record, validate_certchain};
-
-use crate::{
- skip_device_id_attestation_tests, skip_test_if_no_app_attest_key_feature,
- skip_test_if_no_device_id_attestation_feature,
-};
-
-use crate::keystore2_client_test_utils::{
- app_attest_key_feature_exists, device_id_attestation_feature_exists, get_attest_id_value,
- is_second_imei_id_attestation_required, skip_device_id_attest_tests,
+use keystore2_test_utils::{
+ authorizations, key_generations, key_generations::Error, run_as, SecLevel,
};
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
/// Generate RSA and EC attestation keys and use them for signing RSA-signing keys.
/// Test should be able to generate attestation keys and use them successfully.
@@ -46,14 +42,13 @@ use crate::keystore2_client_test_utils::{
fn keystore2_attest_rsa_signing_key_success() {
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let att_challenge: &[u8] = b"foo";
for algo in [Algorithm::RSA, Algorithm::EC] {
// Create attestation key.
let attestation_key_metadata =
- key_generations::generate_attestation_key(&sec_level, algo, att_challenge).unwrap();
+ key_generations::generate_attestation_key(&sl, algo, att_challenge).unwrap();
let mut cert_chain: Vec<u8> = Vec::new();
cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
@@ -63,7 +58,7 @@ fn keystore2_attest_rsa_signing_key_success() {
// Create RSA signing key and use attestation key to sign it.
let sign_key_alias = format!("ks_attest_rsa_signing_key_{}", getuid());
let sign_key_metadata = key_generations::generate_rsa_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(sign_key_alias),
@@ -94,14 +89,13 @@ fn keystore2_attest_rsa_signing_key_success() {
fn keystore2_attest_rsa_encrypt_key_success() {
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let att_challenge: &[u8] = b"foo";
for algo in [Algorithm::RSA, Algorithm::EC] {
// Create attestation key.
let attestation_key_metadata =
- key_generations::generate_attestation_key(&sec_level, algo, att_challenge).unwrap();
+ key_generations::generate_attestation_key(&sl, algo, att_challenge).unwrap();
let mut cert_chain: Vec<u8> = Vec::new();
cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
@@ -111,7 +105,7 @@ fn keystore2_attest_rsa_encrypt_key_success() {
// Create RSA encrypt/decrypt key and use attestation key to sign it.
let decrypt_key_alias = format!("ks_attest_rsa_encrypt_key_{}", getuid());
let decrypt_key_metadata = key_generations::generate_rsa_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(decrypt_key_alias),
@@ -143,14 +137,13 @@ fn keystore2_attest_rsa_encrypt_key_success() {
fn keystore2_attest_ec_key_success() {
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let att_challenge: &[u8] = b"foo";
for algo in [Algorithm::RSA, Algorithm::EC] {
// Create attestation key.
let attestation_key_metadata =
- key_generations::generate_attestation_key(&sec_level, algo, att_challenge).unwrap();
+ key_generations::generate_attestation_key(&sl, algo, att_challenge).unwrap();
let mut cert_chain: Vec<u8> = Vec::new();
cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
@@ -160,7 +153,7 @@ fn keystore2_attest_ec_key_success() {
// Create EC key and use attestation key to sign it.
let ec_key_alias = format!("ks_ec_attested_test_key_{}", getuid());
let ec_key_metadata = key_generations::generate_ec_256_attested_key(
- &sec_level,
+ &sl,
Some(ec_key_alias),
att_challenge,
&attestation_key_metadata.key,
@@ -183,13 +176,19 @@ fn keystore2_attest_ec_key_success() {
fn keystore2_attest_rsa_signing_key_with_ec_25519_key_success() {
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
+ if sl.get_keymint_version() < 2 {
+ // Curve 25519 was included in version 2 of the KeyMint interface.
+ // For device with KeyMint-V1 or Keymaster in backend, emulated Ed25519 key can't attest
+ // to a "real" RSA key.
+ return;
+ }
+
let att_challenge: &[u8] = b"foo";
// Create EcCurve::CURVE_25519 attestation key.
let attestation_key_metadata = key_generations::generate_ec_attestation_key(
- &sec_level,
+ &sl,
att_challenge,
Digest::NONE,
EcCurve::CURVE_25519,
@@ -204,7 +203,7 @@ fn keystore2_attest_rsa_signing_key_with_ec_25519_key_success() {
// Create RSA signing key and use attestation key to sign it.
let sign_key_alias = format!("ksrsa_attested_sign_test_key_{}", getuid());
let sign_key_metadata = key_generations::generate_rsa_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(sign_key_alias),
@@ -233,9 +232,14 @@ fn keystore2_attest_rsa_signing_key_with_ec_25519_key_success() {
#[test]
fn keystore2_generate_rsa_attest_key_with_multi_purpose_fail() {
skip_test_if_no_app_attest_key_feature!();
-
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
+ if sl.get_keymint_version() < 2 {
+ // The KeyMint v1 spec required that KeyPurpose::ATTEST_KEY not be combined
+ // with other key purposes. However, this was not checked at the time
+ // so we can only be strict about checking this for implementations of KeyMint
+ // version 2 and above.
+ return;
+ }
let digest = Digest::SHA_2_256;
let padding = PaddingMode::RSA_PKCS1_1_5_SIGN;
@@ -255,7 +259,7 @@ fn keystore2_generate_rsa_attest_key_with_multi_purpose_fail() {
.rsa_public_exponent(65537)
.padding_mode(padding);
- let result = key_generations::map_ks_error(sec_level.generateKey(
+ let result = key_generations::map_ks_error(sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -276,9 +280,14 @@ fn keystore2_generate_rsa_attest_key_with_multi_purpose_fail() {
#[test]
fn keystore2_ec_attest_key_with_multi_purpose_fail() {
skip_test_if_no_app_attest_key_feature!();
-
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
+ if sl.get_keymint_version() < 2 {
+ // The KeyMint v1 spec required that KeyPurpose::ATTEST_KEY not be combined
+ // with other key purposes. However, this was not checked at the time
+ // so we can only be strict about checking this for implementations of KeyMint
+ // version 2 and above.
+ return;
+ }
let attest_key_alias = format!("ks_ec_attest_multipurpose_key_{}", getuid());
@@ -291,7 +300,7 @@ fn keystore2_ec_attest_key_with_multi_purpose_fail() {
.digest(Digest::SHA_2_256)
.ec_curve(EcCurve::P_256);
- let result = key_generations::map_ks_error(sec_level.generateKey(
+ let result = key_generations::map_ks_error(sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -314,14 +323,12 @@ fn keystore2_ec_attest_key_with_multi_purpose_fail() {
fn keystore2_attest_key_fails_missing_challenge() {
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let att_challenge: &[u8] = b"foo";
// Create RSA attestation key.
let attestation_key_metadata =
- key_generations::generate_attestation_key(&sec_level, Algorithm::RSA, att_challenge)
- .unwrap();
+ key_generations::generate_attestation_key(&sl, Algorithm::RSA, att_challenge).unwrap();
let mut cert_chain: Vec<u8> = Vec::new();
cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
@@ -331,7 +338,7 @@ fn keystore2_attest_key_fails_missing_challenge() {
// Try to attest RSA signing key without providing attestation challenge.
let sign_key_alias = format!("ksrsa_attested_test_key_missing_challenge{}", getuid());
let result = key_generations::map_ks_error(key_generations::generate_rsa_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(sign_key_alias),
@@ -357,24 +364,18 @@ fn keystore2_attest_key_fails_missing_challenge() {
fn keystore2_attest_rsa_key_with_non_attest_key_fails_incompat_purpose_error() {
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let att_challenge: &[u8] = b"foo";
let alias = format!("non_attest_key_{}", getuid());
- let non_attest_key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
- Domain::APP,
- -1,
- Some(alias),
- None,
- )
- .unwrap();
+ let non_attest_key_metadata =
+ key_generations::generate_ec_p256_signing_key(&sl, Domain::APP, -1, Some(alias), None)
+ .unwrap();
// Try to generate RSA signing key with non-attestation key to sign it.
let sign_key_alias = format!("ksrsa_attested_sign_test_key_non_attest_{}", getuid());
let result = key_generations::map_ks_error(key_generations::generate_rsa_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(sign_key_alias),
@@ -399,13 +400,12 @@ fn keystore2_attest_rsa_key_with_non_attest_key_fails_incompat_purpose_error() {
fn keystore2_attest_rsa_key_with_symmetric_key_fails_sys_error() {
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let att_challenge: &[u8] = b"foo";
let alias = "aes_attest_key";
let sym_key_metadata = key_generations::generate_sym_key(
- &sec_level,
+ &sl,
Algorithm::AES,
128,
alias,
@@ -418,7 +418,7 @@ fn keystore2_attest_rsa_key_with_symmetric_key_fails_sys_error() {
// Try to generate RSA signing key with symmetric key as attestation key.
let sign_key_alias = format!("ksrsa_attested_sign_test_key_sym_attest_{}", getuid());
let result = key_generations::map_ks_error(key_generations::generate_rsa_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(sign_key_alias),
@@ -444,14 +444,12 @@ fn keystore2_attest_rsa_key_with_symmetric_key_fails_sys_error() {
fn keystore2_attest_symmetric_key_fail_sys_error() {
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let att_challenge: &[u8] = b"foo";
// Create attestation key.
let attestation_key_metadata =
- key_generations::generate_attestation_key(&sec_level, Algorithm::RSA, att_challenge)
- .unwrap();
+ key_generations::generate_attestation_key(&sl, Algorithm::RSA, att_challenge).unwrap();
let mut cert_chain: Vec<u8> = Vec::new();
cert_chain.extend(attestation_key_metadata.certificate.as_ref().unwrap());
@@ -470,7 +468,8 @@ fn keystore2_attest_symmetric_key_fail_sys_error() {
.attestation_challenge(att_challenge.to_vec());
let alias = format!("ks_test_sym_key_attest_{}", getuid());
- let aes_key_metadata = sec_level
+ let aes_key_metadata = sl
+ .binder
.generateKey(
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
Some(&attestation_key_metadata.key),
@@ -525,22 +524,21 @@ fn generate_attested_key_with_device_attest_ids(algorithm: Algorithm) {
skip_test_if_no_device_id_attestation_feature!();
skip_device_id_attestation_tests!();
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let att_challenge: &[u8] = b"foo";
let attest_key_metadata =
- key_generations::generate_attestation_key(&sec_level, algorithm, att_challenge).unwrap();
+ key_generations::generate_attestation_key(&sl, algorithm, att_challenge).unwrap();
- let attest_id_params = get_attestation_ids(&keystore2);
+ let attest_id_params = get_attestation_ids(&sl.keystore2);
for (attest_id, value) in attest_id_params {
// Create RSA/EC key and use attestation key to sign it.
let key_alias = format!("ks_attested_test_key_{}", getuid());
let key_metadata =
key_generations::map_ks_error(key_generations::generate_key_with_attest_id(
- &sec_level,
+ &sl,
algorithm,
Some(key_alias),
att_challenge,
@@ -585,20 +583,15 @@ fn keystore2_attest_rsa_attestation_id() {
fn keystore2_attest_key_fails_with_invalid_attestation_id() {
skip_test_if_no_device_id_attestation_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let digest = Digest::SHA_2_256;
let att_challenge: &[u8] = b"foo";
// Create EC-Attestation key.
- let attest_key_metadata = key_generations::generate_ec_attestation_key(
- &sec_level,
- att_challenge,
- digest,
- EcCurve::P_256,
- )
- .unwrap();
+ let attest_key_metadata =
+ key_generations::generate_ec_attestation_key(&sl, att_challenge, digest, EcCurve::P_256)
+ .unwrap();
let attest_id_params = vec![
(Tag::ATTESTATION_ID_BRAND, b"invalid-brand".to_vec()),
@@ -614,7 +607,7 @@ fn keystore2_attest_key_fails_with_invalid_attestation_id() {
// Create EC key and use attestation key to sign it.
let ec_key_alias = format!("ks_ec_attested_test_key_fail_{}{}", getuid(), digest.0);
let result = key_generations::map_ks_error(key_generations::generate_key_with_attest_id(
- &sec_level,
+ &sl,
Algorithm::EC,
Some(ec_key_alias),
att_challenge,
@@ -638,20 +631,18 @@ fn keystore2_attest_key_without_attestation_id_support_fails_with_cannot_attest_
return;
}
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let att_challenge: &[u8] = b"foo";
let attest_key_metadata =
- key_generations::generate_attestation_key(&sec_level, Algorithm::RSA, att_challenge)
- .unwrap();
+ key_generations::generate_attestation_key(&sl, Algorithm::RSA, att_challenge).unwrap();
- let attest_id_params = get_attestation_ids(&keystore2);
+ let attest_id_params = get_attestation_ids(&sl.keystore2);
for (attest_id, value) in attest_id_params {
// Create RSA/EC key and use attestation key to sign it.
let key_alias = format!("ks_attested_test_key_{}", getuid());
let result = key_generations::map_ks_error(key_generations::generate_key_with_attest_id(
- &sec_level,
+ &sl,
Algorithm::RSA,
Some(key_alias),
att_challenge,
@@ -666,3 +657,49 @@ fn keystore2_attest_key_without_attestation_id_support_fails_with_cannot_attest_
assert_eq!(result.unwrap_err(), Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
}
}
+
+/// Try to generate an attestation key from user context with UID other than AID_SYSTEM or AID_ROOT
+/// and also there is no package name associated with it. In such case key generation should fail
+/// while collecting Attestation Application ID (AAID) from AAID provider service and keystore
+/// should return error response code - `GET_ATTESTATION_APPLICATION_ID_FAILED`.
+#[test]
+fn keystore2_generate_attested_key_fail_to_get_aaid() {
+ static APP_USER_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static APP_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static APP_GID: u32 = APP_UID;
+
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(APP_USER_CTX, Uid::from_raw(APP_UID), Gid::from_raw(APP_GID), || {
+ skip_test_if_no_app_attest_key_feature!();
+ let sl = SecLevel::tee();
+ let att_challenge: &[u8] = b"foo";
+ let alias = format!("ks_attest_rsa_encrypt_key_aaid_fail{}", getuid());
+
+ let result = key_generations::map_ks_error(key_generations::generate_rsa_key(
+ &sl,
+ Domain::APP,
+ -1,
+ Some(alias),
+ &key_generations::KeyParams {
+ key_size: 2048,
+ purpose: vec![KeyPurpose::ATTEST_KEY],
+ padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+ digest: Some(Digest::SHA_2_256),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: Some(att_challenge.to_vec()),
+ },
+ None,
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(
+ result.unwrap_err(),
+ Error::Rc(ResponseCode::GET_ATTESTATION_APPLICATION_ID_FAILED)
+ );
+ })
+ };
+}
diff --git a/keystore2/tests/keystore2_client_authorizations_tests.rs b/keystore2/tests/keystore2_client_authorizations_tests.rs
index 0fde7aff..09817830 100644
--- a/keystore2/tests/keystore2_client_authorizations_tests.rs
+++ b/keystore2/tests/keystore2_client_authorizations_tests.rs
@@ -12,49 +12,39 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use std::time::SystemTime;
-
-use openssl::bn::{BigNum, MsbOption};
-use openssl::x509::X509NameBuilder;
-
+use crate::keystore2_client_test_utils::{
+ app_attest_key_feature_exists, delete_app_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, verify_certificate_serial_num,
+ verify_certificate_subject_name, SAMPLE_PLAIN_TEXT,
+};
+use crate::{require_keymint, skip_test_if_no_app_attest_key_feature};
+use aconfig_android_hardware_biometrics_rust;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
SecurityLevel::SecurityLevel, Tag::Tag,
};
-
-use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
- KeyMetadata::KeyMetadata, ResponseCode::ResponseCode,
-};
-
-use aconfig_android_hardware_biometrics_rust;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- HardwareAuthToken::HardwareAuthToken,
- HardwareAuthenticatorType::HardwareAuthenticatorType
+ HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
};
-use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::Timestamp::Timestamp;
-
-use keystore2_test_utils::{
- authorizations, get_keystore_auth_service, get_keystore_service, key_generations,
- key_generations::Error,
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+ Timestamp::Timestamp
};
-
-use crate::keystore2_client_test_utils::{
- app_attest_key_feature_exists, delete_app_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, verify_certificate_serial_num,
- verify_certificate_subject_name, SAMPLE_PLAIN_TEXT,
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, KeyMetadata::KeyMetadata,
+ ResponseCode::ResponseCode,
};
-
-use crate::{skip_test_if_no_app_attest_key_feature, skip_tests_if_keymaster_impl_present};
-
use keystore2_test_utils::ffi_test_utils::get_value_from_attest_record;
+use keystore2_test_utils::{
+ authorizations, get_keystore_auth_service, key_generations,
+ key_generations::Error, SecLevel,
+};
+use openssl::bn::{BigNum, MsbOption};
+use openssl::x509::X509NameBuilder;
+use std::time::SystemTime;
-fn gen_key_including_unique_id(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
- alias: &str,
-) -> Vec<u8> {
+fn gen_key_including_unique_id(sl: &SecLevel, alias: &str) -> Vec<u8> {
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
.algorithm(Algorithm::EC)
@@ -65,7 +55,7 @@ fn gen_key_including_unique_id(
.attestation_challenge(b"foo".to_vec())
.include_unique_id();
- let key_metadata = key_generations::generate_key(sec_level, &gen_params, alias).unwrap();
+ let key_metadata = key_generations::generate_key(sl, &gen_params, alias).unwrap();
let unique_id = get_value_from_attest_record(
key_metadata.certificate.as_ref().unwrap(),
@@ -78,16 +68,21 @@ fn gen_key_including_unique_id(
}
fn generate_key_and_perform_sign_verify_op_max_times(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
gen_params: &authorizations::AuthSetBuilder,
alias: &str,
max_usage_count: i32,
) -> binder::Result<KeyMetadata> {
- let key_metadata = key_generations::generate_key(sec_level, gen_params, alias)?;
+ let key_metadata = key_generations::generate_key(sl, gen_params, alias)?;
// Use above generated key `max_usage_count` times.
for _ in 0..max_usage_count {
- perform_sample_asym_sign_verify_op(sec_level, &key_metadata, None, Some(Digest::SHA_2_256));
+ perform_sample_asym_sign_verify_op(
+ &sl.binder,
+ &key_metadata,
+ None,
+ Some(Digest::SHA_2_256),
+ );
}
Ok(key_metadata)
@@ -98,24 +93,20 @@ fn generate_key_and_perform_sign_verify_op_max_times(
/// times subsequent attempts to use the key in test should fail with response code `KEY_NOT_FOUND`.
/// Test should also verify that the attest record includes `USAGE_COUNT_LIMIT` for attested keys.
fn generate_key_and_perform_op_with_max_usage_limit(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
gen_params: &authorizations::AuthSetBuilder,
alias: &str,
max_usage_count: i32,
check_attestation: bool,
) {
// Generate a key and use the key for `max_usage_count` times.
- let key_metadata = generate_key_and_perform_sign_verify_op_max_times(
- sec_level,
- gen_params,
- alias,
- max_usage_count,
- )
- .unwrap();
+ let key_metadata =
+ generate_key_and_perform_sign_verify_op_max_times(sl, gen_params, alias, max_usage_count)
+ .unwrap();
let auth = key_generations::get_key_auth(&key_metadata.authorizations, Tag::USAGE_COUNT_LIMIT)
.unwrap();
- if check_attestation && key_generations::has_default_keymint() {
+ if check_attestation && sl.is_keymint() {
// Check usage-count-limit is included in attest-record.
// `USAGE_COUNT_LIMIT` is supported from KeyMint1.0
assert_ne!(
@@ -142,7 +133,7 @@ fn generate_key_and_perform_op_with_max_usage_limit(
}
// Try to use the key one more time.
- let result = key_generations::map_ks_error(sec_level.createOperation(
+ let result = key_generations::map_ks_error(sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
false,
@@ -156,8 +147,7 @@ fn generate_key_and_perform_op_with_max_usage_limit(
/// the generated key successfully.
#[test]
fn keystore2_gen_key_auth_active_datetime_test_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let active_datetime = duration_since_epoch.as_millis();
@@ -173,13 +163,13 @@ fn keystore2_gen_key_auth_active_datetime_test_success() {
let alias = "ks_test_auth_tags_test";
let result = key_generations::create_key_and_operation(
- &sec_level,
+ &sl,
&gen_params,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
alias,
);
assert!(result.is_ok());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a key with `ACTIVE_DATETIME` set to future date and time. Test should successfully
@@ -188,8 +178,7 @@ fn keystore2_gen_key_auth_active_datetime_test_success() {
/// `KEY_NOT_YET_VALID`.
#[test]
fn keystore2_gen_key_auth_future_active_datetime_test_op_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let future_active_datetime = duration_since_epoch.as_millis() + (24 * 60 * 60 * 1000);
@@ -205,14 +194,14 @@ fn keystore2_gen_key_auth_future_active_datetime_test_op_fail() {
let alias = "ks_test_auth_tags_test";
let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
- &sec_level,
+ &sl,
&gen_params,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
alias,
));
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::KEY_NOT_YET_VALID), result.unwrap_err());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a key with `ORIGINATION_EXPIRE_DATETIME` set to future date and time. Test should
@@ -220,8 +209,7 @@ fn keystore2_gen_key_auth_future_active_datetime_test_op_fail() {
/// sign operation using the generated key successfully.
#[test]
fn keystore2_gen_key_auth_future_origination_expire_datetime_test_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let origination_expire_datetime = duration_since_epoch.as_millis() + (24 * 60 * 60 * 1000);
@@ -237,13 +225,13 @@ fn keystore2_gen_key_auth_future_origination_expire_datetime_test_success() {
let alias = "ks_test_auth_tags_test";
let result = key_generations::create_key_and_operation(
- &sec_level,
+ &sl,
&gen_params,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
alias,
);
assert!(result.is_ok());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a key with `ORIGINATION_EXPIRE_DATETIME` set to current date and time. Test should
@@ -252,8 +240,7 @@ fn keystore2_gen_key_auth_future_origination_expire_datetime_test_success() {
/// `KEY_EXPIRED`.
#[test]
fn keystore2_gen_key_auth_origination_expire_datetime_test_op_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let origination_expire_datetime = duration_since_epoch.as_millis();
@@ -269,14 +256,14 @@ fn keystore2_gen_key_auth_origination_expire_datetime_test_op_fail() {
let alias = "ks_test_auth_tags_test";
let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
- &sec_level,
+ &sl,
&gen_params,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
alias,
));
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::KEY_EXPIRED), result.unwrap_err());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a HMAC key with `USAGE_EXPIRE_DATETIME` set to future date and time. Test should
@@ -284,8 +271,7 @@ fn keystore2_gen_key_auth_origination_expire_datetime_test_op_fail() {
/// sign and verify operations using the generated key successfully.
#[test]
fn keystore2_gen_key_auth_future_usage_expire_datetime_hmac_verify_op_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let usage_expire_datetime = duration_since_epoch.as_millis() + (24 * 60 * 60 * 1000);
@@ -300,10 +286,10 @@ fn keystore2_gen_key_auth_future_usage_expire_datetime_hmac_verify_op_success()
.usage_expire_date_time(usage_expire_datetime.try_into().unwrap());
let alias = "ks_test_auth_tags_hmac_verify_success";
- let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+ let key_metadata = key_generations::generate_key(&sl, &gen_params, alias).unwrap();
- perform_sample_hmac_sign_verify_op(&sec_level, &key_metadata.key);
- delete_app_key(&keystore2, alias).unwrap();
+ perform_sample_hmac_sign_verify_op(&sl.binder, &key_metadata.key);
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a key with `USAGE_EXPIRE_DATETIME` set to current date and time. Test should
@@ -312,8 +298,7 @@ fn keystore2_gen_key_auth_future_usage_expire_datetime_hmac_verify_op_success()
/// `KEY_EXPIRED`.
#[test]
fn keystore2_gen_key_auth_usage_expire_datetime_hmac_verify_op_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let usage_expire_datetime = duration_since_epoch.as_millis();
@@ -328,10 +313,10 @@ fn keystore2_gen_key_auth_usage_expire_datetime_hmac_verify_op_fail() {
.usage_expire_date_time(usage_expire_datetime.try_into().unwrap());
let alias = "ks_test_auth_tags_hamc_verify_fail";
- let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+ let key_metadata = key_generations::generate_key(&sl, &gen_params, alias).unwrap();
let result = key_generations::map_ks_error(
- sec_level.createOperation(
+ sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new()
.purpose(KeyPurpose::VERIFY)
@@ -341,7 +326,7 @@ fn keystore2_gen_key_auth_usage_expire_datetime_hmac_verify_op_fail() {
);
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::KEY_EXPIRED), result.unwrap_err());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate AES key with `USAGE_EXPIRE_DATETIME` set to future date and time. Test should
@@ -349,8 +334,7 @@ fn keystore2_gen_key_auth_usage_expire_datetime_hmac_verify_op_fail() {
/// Encrypt and Decrypt operations successfully.
#[test]
fn keystore2_gen_key_auth_usage_future_expire_datetime_decrypt_op_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let usage_expire_datetime = duration_since_epoch.as_millis() + (24 * 60 * 60 * 1000);
@@ -365,9 +349,9 @@ fn keystore2_gen_key_auth_usage_future_expire_datetime_decrypt_op_success() {
.usage_expire_date_time(usage_expire_datetime.try_into().unwrap());
let alias = "ks_test_auth_tags_test";
- let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+ let key_metadata = key_generations::generate_key(&sl, &gen_params, alias).unwrap();
let cipher_text = perform_sample_sym_key_encrypt_op(
- &sec_level,
+ &sl.binder,
PaddingMode::PKCS7,
BlockMode::ECB,
&mut None,
@@ -379,7 +363,7 @@ fn keystore2_gen_key_auth_usage_future_expire_datetime_decrypt_op_success() {
assert!(cipher_text.is_some());
let plain_text = perform_sample_sym_key_decrypt_op(
- &sec_level,
+ &sl.binder,
&cipher_text.unwrap(),
PaddingMode::PKCS7,
BlockMode::ECB,
@@ -390,7 +374,7 @@ fn keystore2_gen_key_auth_usage_future_expire_datetime_decrypt_op_success() {
.unwrap();
assert!(plain_text.is_some());
assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate AES key with `USAGE_EXPIRE_DATETIME` set to current date and time. Test should
@@ -399,8 +383,7 @@ fn keystore2_gen_key_auth_usage_future_expire_datetime_decrypt_op_success() {
/// `KEY_EXPIRED`.
#[test]
fn keystore2_gen_key_auth_usage_expire_datetime_decrypt_op_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let usage_expire_datetime = duration_since_epoch.as_millis();
@@ -415,9 +398,9 @@ fn keystore2_gen_key_auth_usage_expire_datetime_decrypt_op_fail() {
.usage_expire_date_time(usage_expire_datetime.try_into().unwrap());
let alias = "ks_test_auth_tags_test";
- let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+ let key_metadata = key_generations::generate_key(&sl, &gen_params, alias).unwrap();
let cipher_text = perform_sample_sym_key_encrypt_op(
- &sec_level,
+ &sl.binder,
PaddingMode::PKCS7,
BlockMode::ECB,
&mut None,
@@ -429,7 +412,7 @@ fn keystore2_gen_key_auth_usage_expire_datetime_decrypt_op_fail() {
assert!(cipher_text.is_some());
let result = key_generations::map_ks_error(perform_sample_sym_key_decrypt_op(
- &sec_level,
+ &sl.binder,
&cipher_text.unwrap(),
PaddingMode::PKCS7,
BlockMode::ECB,
@@ -439,37 +422,7 @@ fn keystore2_gen_key_auth_usage_expire_datetime_decrypt_op_fail() {
));
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::KEY_EXPIRED), result.unwrap_err());
- 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());
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a key with `EARLY_BOOT_ONLY`. Test should successfully generate
@@ -477,9 +430,8 @@ fn keystore2_gen_key_auth_boot_loader_only_op_fail() {
/// during creation of an operation using this key.
#[test]
fn keystore2_gen_key_auth_early_boot_only_op_fail() {
- skip_tests_if_keymaster_impl_present!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
+ require_keymint!(sl);
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
@@ -493,14 +445,14 @@ fn keystore2_gen_key_auth_early_boot_only_op_fail() {
let alias = "ks_test_auth_tags_test";
let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
- &sec_level,
+ &sl,
&gen_params,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
alias,
));
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::EARLY_BOOT_ENDED), result.unwrap_err());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a key with `MAX_USES_PER_BOOT`. Test should successfully generate
@@ -509,8 +461,7 @@ fn keystore2_gen_key_auth_early_boot_only_op_fail() {
/// subsequent attempts to use the key in test should fail with error code MAX_OPS_EXCEEDED.
#[test]
fn keystore2_gen_key_auth_max_uses_per_boot() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
const MAX_USES_COUNT: i32 = 3;
let gen_params = authorizations::AuthSetBuilder::new()
@@ -525,23 +476,19 @@ fn keystore2_gen_key_auth_max_uses_per_boot() {
let alias = "ks_test_auth_tags_test";
// Generate a key and use the key for `MAX_USES_COUNT` times.
- let key_metadata = generate_key_and_perform_sign_verify_op_max_times(
- &sec_level,
- &gen_params,
- alias,
- MAX_USES_COUNT,
- )
- .unwrap();
+ let key_metadata =
+ generate_key_and_perform_sign_verify_op_max_times(&sl, &gen_params, alias, MAX_USES_COUNT)
+ .unwrap();
// Try to use the key one more time.
- let result = key_generations::map_ks_error(sec_level.createOperation(
+ let result = key_generations::map_ks_error(sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
false,
));
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::KEY_MAX_OPS_EXCEEDED), result.unwrap_err());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a key with `USAGE_COUNT_LIMIT`. Test should successfully generate
@@ -551,8 +498,7 @@ fn keystore2_gen_key_auth_max_uses_per_boot() {
/// Test should also verify that the attest record includes `USAGE_COUNT_LIMIT`.
#[test]
fn keystore2_gen_key_auth_usage_count_limit() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
const MAX_USES_COUNT: i32 = 3;
let gen_params = authorizations::AuthSetBuilder::new()
@@ -566,13 +512,7 @@ fn keystore2_gen_key_auth_usage_count_limit() {
.usage_count_limit(MAX_USES_COUNT);
let alias = "ks_test_auth_tags_test";
- generate_key_and_perform_op_with_max_usage_limit(
- &sec_level,
- &gen_params,
- alias,
- MAX_USES_COUNT,
- true,
- );
+ generate_key_and_perform_op_with_max_usage_limit(&sl, &gen_params, alias, MAX_USES_COUNT, true);
}
/// Generate a key with `USAGE_COUNT_LIMIT`. Test should successfully generate
@@ -582,8 +522,7 @@ fn keystore2_gen_key_auth_usage_count_limit() {
/// Test should also verify that the attest record includes `USAGE_COUNT_LIMIT`.
#[test]
fn keystore2_gen_key_auth_usage_count_limit_one() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
const MAX_USES_COUNT: i32 = 1;
let gen_params = authorizations::AuthSetBuilder::new()
@@ -597,13 +536,7 @@ fn keystore2_gen_key_auth_usage_count_limit_one() {
.usage_count_limit(MAX_USES_COUNT);
let alias = "ks_test_auth_tags_test";
- generate_key_and_perform_op_with_max_usage_limit(
- &sec_level,
- &gen_params,
- alias,
- MAX_USES_COUNT,
- true,
- );
+ generate_key_and_perform_op_with_max_usage_limit(&sl, &gen_params, alias, MAX_USES_COUNT, true);
}
/// Generate a non-attested key with `USAGE_COUNT_LIMIT`. Test should successfully generate
@@ -612,8 +545,7 @@ fn keystore2_gen_key_auth_usage_count_limit_one() {
/// subsequent attempts to use the key in test should fail with response code `KEY_NOT_FOUND`.
#[test]
fn keystore2_gen_non_attested_key_auth_usage_count_limit() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
const MAX_USES_COUNT: i32 = 2;
let gen_params = authorizations::AuthSetBuilder::new()
@@ -627,7 +559,7 @@ fn keystore2_gen_non_attested_key_auth_usage_count_limit() {
let alias = "ks_test_auth_tags_test";
generate_key_and_perform_op_with_max_usage_limit(
- &sec_level,
+ &sl,
&gen_params,
alias,
MAX_USES_COUNT,
@@ -640,8 +572,7 @@ fn keystore2_gen_non_attested_key_auth_usage_count_limit() {
/// specify `CREATION_DATETIME`.
#[test]
fn keystore2_gen_key_auth_creation_date_time_test_fail_with_invalid_arg_error() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let creation_datetime = duration_since_epoch.as_millis();
@@ -656,7 +587,7 @@ fn keystore2_gen_key_auth_creation_date_time_test_fail_with_invalid_arg_error()
.creation_date_time(creation_datetime.try_into().unwrap());
let alias = "ks_test_auth_tags_test";
- let result = key_generations::map_ks_error(sec_level.generateKey(
+ let result = key_generations::map_ks_error(sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -677,27 +608,25 @@ fn keystore2_gen_key_auth_creation_date_time_test_fail_with_invalid_arg_error()
/// included in attest record and it remains the same for new keys generated.
#[test]
fn keystore2_gen_key_auth_include_unique_id_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias_first = "ks_test_auth_tags_test_1";
- let unique_id_first = gen_key_including_unique_id(&sec_level, alias_first);
+ let unique_id_first = gen_key_including_unique_id(&sl, alias_first);
let alias_second = "ks_test_auth_tags_test_2";
- let unique_id_second = gen_key_including_unique_id(&sec_level, alias_second);
+ let unique_id_second = gen_key_including_unique_id(&sl, alias_second);
assert_eq!(unique_id_first, unique_id_second);
- delete_app_key(&keystore2, alias_first).unwrap();
- delete_app_key(&keystore2, alias_second).unwrap();
+ delete_app_key(&sl.keystore2, alias_first).unwrap();
+ delete_app_key(&sl.keystore2, alias_second).unwrap();
}
/// Generate a key with `APPLICATION_DATA`. Test should create an operation using the
/// same `APPLICATION_DATA` successfully.
#[test]
fn keystore2_gen_key_auth_app_data_test_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
@@ -710,7 +639,7 @@ fn keystore2_gen_key_auth_app_data_test_success() {
let alias = "ks_test_auth_tags_test";
let result = key_generations::create_key_and_operation(
- &sec_level,
+ &sl,
&gen_params,
&authorizations::AuthSetBuilder::new()
.purpose(KeyPurpose::SIGN)
@@ -719,7 +648,7 @@ fn keystore2_gen_key_auth_app_data_test_success() {
alias,
);
assert!(result.is_ok());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a key with `APPLICATION_DATA`. Try to create an operation using the
@@ -727,8 +656,7 @@ fn keystore2_gen_key_auth_app_data_test_success() {
/// `INVALID_KEY_BLOB`.
#[test]
fn keystore2_gen_key_auth_app_data_test_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
@@ -741,7 +669,7 @@ fn keystore2_gen_key_auth_app_data_test_fail() {
let alias = "ks_test_auth_tags_test";
let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
- &sec_level,
+ &sl,
&gen_params,
&authorizations::AuthSetBuilder::new()
.purpose(KeyPurpose::SIGN)
@@ -751,15 +679,14 @@ fn keystore2_gen_key_auth_app_data_test_fail() {
));
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::INVALID_KEY_BLOB), result.unwrap_err());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a key with `APPLICATION_ID`. Test should create an operation using the
/// same `APPLICATION_ID` successfully.
#[test]
fn keystore2_gen_key_auth_app_id_test_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
@@ -772,7 +699,7 @@ fn keystore2_gen_key_auth_app_id_test_success() {
let alias = "ks_test_auth_tags_test";
let result = key_generations::create_key_and_operation(
- &sec_level,
+ &sl,
&gen_params,
&authorizations::AuthSetBuilder::new()
.purpose(KeyPurpose::SIGN)
@@ -781,7 +708,7 @@ fn keystore2_gen_key_auth_app_id_test_success() {
alias,
);
assert!(result.is_ok());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate a key with `APPLICATION_ID`. Try to create an operation using the
@@ -789,8 +716,7 @@ fn keystore2_gen_key_auth_app_id_test_success() {
/// `INVALID_KEY_BLOB`.
#[test]
fn keystore2_gen_key_auth_app_id_test_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
@@ -803,7 +729,7 @@ fn keystore2_gen_key_auth_app_id_test_fail() {
let alias = "ks_test_auth_tags_test";
let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
- &sec_level,
+ &sl,
&gen_params,
&authorizations::AuthSetBuilder::new()
.purpose(KeyPurpose::SIGN)
@@ -813,7 +739,7 @@ fn keystore2_gen_key_auth_app_id_test_fail() {
));
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::INVALID_KEY_BLOB), result.unwrap_err());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate an attestation-key without specifying `APPLICATION_ID` and `APPLICATION_DATA`.
@@ -822,8 +748,7 @@ fn keystore2_gen_key_auth_app_id_test_fail() {
#[test]
fn keystore2_gen_attested_key_auth_app_id_app_data_test_success() {
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
// Generate attestation key.
let attest_gen_params = authorizations::AuthSetBuilder::new()
@@ -835,7 +760,7 @@ fn keystore2_gen_attested_key_auth_app_id_app_data_test_success() {
.attestation_challenge(b"foo".to_vec());
let attest_alias = "ks_test_auth_tags_attest_key";
let attest_key_metadata =
- key_generations::generate_key(&sec_level, &attest_gen_params, attest_alias).unwrap();
+ key_generations::generate_key(&sl, &attest_gen_params, attest_alias).unwrap();
// Generate attested key.
let alias = "ks_test_auth_tags_attested_key";
@@ -850,7 +775,7 @@ fn keystore2_gen_attested_key_auth_app_id_app_data_test_success() {
.app_id(b"app-id".to_vec())
.app_data(b"app-data".to_vec());
- let result = sec_level.generateKey(
+ let result = sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -864,8 +789,8 @@ fn keystore2_gen_attested_key_auth_app_id_app_data_test_success() {
);
assert!(result.is_ok());
- delete_app_key(&keystore2, alias).unwrap();
- delete_app_key(&keystore2, attest_alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, attest_alias).unwrap();
}
/// Generate an attestation-key with specifying `APPLICATION_ID` and `APPLICATION_DATA`.
@@ -877,8 +802,7 @@ fn keystore2_gen_attested_key_auth_app_id_app_data_test_success() {
#[test]
fn keystore2_gen_attestation_key_with_auth_app_id_app_data_test_fail() {
skip_test_if_no_app_attest_key_feature!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
// Generate attestation key.
let attest_gen_params = authorizations::AuthSetBuilder::new()
@@ -892,7 +816,7 @@ fn keystore2_gen_attestation_key_with_auth_app_id_app_data_test_fail() {
.app_data(b"app-data".to_vec());
let attest_alias = "ks_test_auth_tags_attest_key";
let attest_key_metadata =
- key_generations::generate_key(&sec_level, &attest_gen_params, attest_alias).unwrap();
+ key_generations::generate_key(&sl, &attest_gen_params, attest_alias).unwrap();
// Generate new key using above generated attestation key without providing app-id and app-data.
let alias = "ks_test_auth_tags_attested_key";
@@ -905,7 +829,7 @@ fn keystore2_gen_attestation_key_with_auth_app_id_app_data_test_fail() {
.ec_curve(EcCurve::P_256)
.attestation_challenge(b"foo".to_vec());
- let result = key_generations::map_ks_error(sec_level.generateKey(
+ let result = key_generations::map_ks_error(sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -920,7 +844,7 @@ fn keystore2_gen_attestation_key_with_auth_app_id_app_data_test_fail() {
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::INVALID_KEY_BLOB), result.unwrap_err());
- delete_app_key(&keystore2, attest_alias).unwrap();
+ delete_app_key(&sl.keystore2, attest_alias).unwrap();
}
fn add_hardware_token(auth_type: HardwareAuthenticatorType) {
@@ -981,9 +905,8 @@ fn keystore2_flagged_on_get_last_auth_fingerprint_success() {
/// generate a key successfully and verify the specified key parameters.
#[test]
fn keystore2_gen_key_auth_serial_number_subject_test_success() {
- skip_tests_if_keymaster_impl_present!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
+ require_keymint!(sl);
let cert_subject = "test cert subject";
let mut x509_name = X509NameBuilder::new().unwrap();
@@ -1005,11 +928,11 @@ fn keystore2_gen_key_auth_serial_number_subject_test_success() {
.cert_serial(serial.to_vec());
let alias = "ks_test_auth_tags_test";
- let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+ let key_metadata = key_generations::generate_key(&sl, &gen_params, alias).unwrap();
verify_certificate_subject_name(
key_metadata.certificate.as_ref().unwrap(),
cert_subject.as_bytes(),
);
verify_certificate_serial_num(key_metadata.certificate.as_ref().unwrap(), &serial);
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
diff --git a/keystore2/tests/keystore2_client_delete_key_tests.rs b/keystore2/tests/keystore2_client_delete_key_tests.rs
index 2a06edbc..a0fb9c2a 100644
--- a/keystore2/tests/keystore2_client_delete_key_tests.rs
+++ b/keystore2/tests/keystore2_client_delete_key_tests.rs
@@ -12,27 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::getuid;
-
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- ErrorCode::ErrorCode, SecurityLevel::SecurityLevel,
-};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
};
-
-use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error};
+use keystore2_test_utils::{
+ get_keystore_service, key_generations, key_generations::Error, SecLevel,
+};
+use nix::unistd::getuid;
/// Generate a key and delete it using keystore2 service `deleteKey` API. Test should successfully
/// delete the generated key.
#[test]
fn keystore2_delete_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "delete_key_success_key";
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -40,10 +37,10 @@ fn keystore2_delete_key_success() {
)
.unwrap();
- keystore2.deleteKey(&key_metadata.key).expect("Failed to delete a key.");
+ sl.keystore2.deleteKey(&key_metadata.key).expect("Failed to delete a key.");
// Check wehther deleted key is removed from keystore.
- let result = key_generations::map_ks_error(keystore2.getKeyEntry(&key_metadata.key));
+ let result = key_generations::map_ks_error(sl.keystore2.getKeyEntry(&key_metadata.key));
assert!(result.is_err());
assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
}
@@ -70,12 +67,11 @@ fn keystore2_delete_key_fail() {
/// `INVALID_ARGUMENT`.
#[test]
fn keystore2_delete_key_with_blob_domain_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "delete_key_blob_fail_key";
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::BLOB,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias.to_string()),
@@ -83,7 +79,7 @@ fn keystore2_delete_key_with_blob_domain_fail() {
)
.unwrap();
- let result = key_generations::map_ks_error(keystore2.deleteKey(&key_metadata.key));
+ let result = key_generations::map_ks_error(sl.keystore2.deleteKey(&key_metadata.key));
assert!(result.is_err());
assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
}
@@ -92,12 +88,11 @@ fn keystore2_delete_key_with_blob_domain_fail() {
/// security level `deleteKey` API. Test should delete the key successfully.
#[test]
fn keystore2_delete_key_blob_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "delete_key_blob_success_key";
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::BLOB,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias.to_string()),
@@ -105,7 +100,7 @@ fn keystore2_delete_key_blob_success() {
)
.unwrap();
- let result = sec_level.deleteKey(&key_metadata.key);
+ let result = sl.binder.deleteKey(&key_metadata.key);
assert!(result.is_ok());
}
@@ -113,10 +108,9 @@ fn keystore2_delete_key_blob_success() {
/// key with error code `INVALID_ARGUMENT`.
#[test]
fn keystore2_delete_key_fails_with_missing_key_blob() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
- let result = key_generations::map_ks_error(sec_level.deleteKey(&KeyDescriptor {
+ let result = key_generations::map_ks_error(sl.binder.deleteKey(&KeyDescriptor {
domain: Domain::BLOB,
nspace: key_generations::SELINUX_SHELL_NAMESPACE,
alias: None,
@@ -131,20 +125,14 @@ fn keystore2_delete_key_fails_with_missing_key_blob() {
/// with error code `INVALID_ARGUMENT`.
#[test]
fn keystore2_delete_key_blob_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_delete_keyblob_test_key_{}", getuid());
- let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
- Domain::APP,
- -1,
- Some(alias),
- None,
- )
- .unwrap();
+ let key_metadata =
+ key_generations::generate_ec_p256_signing_key(&sl, Domain::APP, -1, Some(alias), None)
+ .unwrap();
- let result = key_generations::map_ks_error(sec_level.deleteKey(&key_metadata.key));
+ let result = key_generations::map_ks_error(sl.binder.deleteKey(&key_metadata.key));
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::INVALID_ARGUMENT), result.unwrap_err());
}
diff --git a/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs b/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
index 4f881bcd..cc5d85cc 100644
--- a/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
+++ b/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
@@ -11,23 +11,18 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode,
- KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
-};
-
-use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error,
-};
-
-use keystore2_test_utils::ffi_test_utils::get_value_from_attest_record;
use crate::keystore2_client_test_utils::{
delete_app_key, get_attest_id_value, is_second_imei_id_attestation_required,
- perform_sample_asym_sign_verify_op,
+ perform_sample_asym_sign_verify_op, skip_device_unique_attestation_tests,
};
-
-use crate::skip_tests_if_keymaster_impl_present;
+use crate::require_keymint;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode,
+ KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, Tag::Tag,
+};
+use keystore2_test_utils::ffi_test_utils::get_value_from_attest_record;
+use keystore2_test_utils::{authorizations, key_generations, key_generations::Error, SecLevel};
/// This macro is used for generating device unique attested EC key with device id attestation.
macro_rules! test_ec_key_device_unique_attestation_id {
@@ -50,6 +45,9 @@ macro_rules! test_rsa_key_device_unique_attestation_id {
}
fn generate_ec_key_device_unique_attested_with_id_attest(attest_id_tag: Tag, prop_name: &str) {
+ if skip_device_unique_attestation_tests() {
+ return;
+ }
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
.algorithm(Algorithm::EC)
@@ -67,6 +65,9 @@ fn generate_ec_key_device_unique_attested_with_id_attest(attest_id_tag: Tag, pro
}
fn generate_rsa_key_device_unique_attested_with_id_attest(attest_id_tag: Tag, prop_name: &str) {
+ if skip_device_unique_attestation_tests() {
+ return;
+ }
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
.algorithm(Algorithm::RSA)
@@ -113,17 +114,10 @@ fn generate_device_unique_attested_key_with_device_attest_ids(
attest_id: Tag,
prop_name: &str,
) {
- let keystore2 = get_keystore_service();
- let result =
- key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
- if result.is_err() {
- assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
- return;
- }
- let sec_level = result.unwrap();
+ let Some(sl) = SecLevel::strongbox() else { return };
if attest_id == Tag::ATTESTATION_ID_SECOND_IMEI
- && !is_second_imei_id_attestation_required(&keystore2)
+ && !is_second_imei_id_attestation_required(&sl.keystore2)
{
return;
}
@@ -134,11 +128,8 @@ fn generate_device_unique_attested_key_with_device_attest_ids(
}
let gen_params = add_attest_id_auth(gen_params, attest_id, value.clone());
let alias = "ks_test_device_unique_attest_id_test";
- match key_generations::map_ks_error(key_generations::generate_key(
- &sec_level,
- &gen_params,
- alias,
- )) {
+ match key_generations::map_ks_error(key_generations::generate_key(&sl, &gen_params, alias))
+ {
Ok(key_metadata) => {
let attest_id_value = get_value_from_attest_record(
key_metadata.certificate.as_ref().unwrap(),
@@ -147,7 +138,7 @@ fn generate_device_unique_attested_key_with_device_attest_ids(
)
.expect("Attest id verification failed.");
assert_eq!(attest_id_value, value);
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
Err(e) => {
assert_eq!(e, Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
@@ -160,9 +151,8 @@ fn generate_device_unique_attested_key_with_device_attest_ids(
/// Test should fail to generate a key with error code `INVALID_ARGUMENT`
#[test]
fn keystore2_gen_key_device_unique_attest_with_default_sec_level_unimplemented() {
- skip_tests_if_keymaster_impl_present!();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
+ require_keymint!(sl);
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
@@ -175,13 +165,13 @@ fn keystore2_gen_key_device_unique_attest_with_default_sec_level_unimplemented()
.device_unique_attestation();
let alias = "ks_test_auth_tags_test";
- let result = key_generations::map_ks_error(key_generations::generate_key(
- &sec_level,
- &gen_params,
- alias,
- ));
+ let result =
+ key_generations::map_ks_error(key_generations::generate_key(&sl, &gen_params, 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.
@@ -189,15 +179,11 @@ fn keystore2_gen_key_device_unique_attest_with_default_sec_level_unimplemented()
/// use it for performing an operation.
#[test]
fn keystore2_gen_ec_key_device_unique_attest_with_strongbox_sec_level_test_success() {
- let keystore2 = get_keystore_service();
- let result =
- key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
- if result.is_err() {
- assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+ let Some(sl) = SecLevel::strongbox() else { return };
+ if skip_device_unique_attestation_tests() {
return;
}
- let sec_level = result.unwrap();
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
.algorithm(Algorithm::EC)
@@ -209,19 +195,15 @@ fn keystore2_gen_ec_key_device_unique_attest_with_strongbox_sec_level_test_succe
.device_unique_attestation();
let alias = "ks_device_unique_ec_key_attest_test";
- match key_generations::map_ks_error(key_generations::generate_key(
- &sec_level,
- &gen_params,
- alias,
- )) {
+ match key_generations::map_ks_error(key_generations::generate_key(&sl, &gen_params, alias)) {
Ok(key_metadata) => {
perform_sample_asym_sign_verify_op(
- &sec_level,
+ &sl.binder,
&key_metadata,
None,
Some(Digest::SHA_2_256),
);
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
Err(e) => {
assert_eq!(e, Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
@@ -234,15 +216,11 @@ fn keystore2_gen_ec_key_device_unique_attest_with_strongbox_sec_level_test_succe
/// use it for performing an operation.
#[test]
fn keystore2_gen_rsa_key_device_unique_attest_with_strongbox_sec_level_test_success() {
- let keystore2 = get_keystore_service();
- let result =
- key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
- if result.is_err() {
- assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+ let Some(sl) = SecLevel::strongbox() else { return };
+ if skip_device_unique_attestation_tests() {
return;
}
- let sec_level = result.unwrap();
let gen_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
.algorithm(Algorithm::RSA)
@@ -256,19 +234,15 @@ fn keystore2_gen_rsa_key_device_unique_attest_with_strongbox_sec_level_test_succ
.device_unique_attestation();
let alias = "ks_device_unique_rsa_key_attest_test";
- match key_generations::map_ks_error(key_generations::generate_key(
- &sec_level,
- &gen_params,
- alias,
- )) {
+ match key_generations::map_ks_error(key_generations::generate_key(&sl, &gen_params, alias)) {
Ok(key_metadata) => {
perform_sample_asym_sign_verify_op(
- &sec_level,
+ &sl.binder,
&key_metadata,
Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
Some(Digest::SHA_2_256),
);
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
Err(e) => {
assert_eq!(e, Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
@@ -280,15 +254,11 @@ fn keystore2_gen_rsa_key_device_unique_attest_with_strongbox_sec_level_test_succ
/// Test should fail with error response code `CANNOT_ATTEST_IDS`.
#[test]
fn keystore2_device_unique_attest_key_fails_with_invalid_attestation_id() {
- let keystore2 = get_keystore_service();
- let result =
- key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
- if result.is_err() {
- assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+ let Some(sl) = SecLevel::strongbox() else { return };
+ if skip_device_unique_attestation_tests() {
return;
}
- let sec_level = result.unwrap();
let attest_id_params = vec![
(Tag::ATTESTATION_ID_BRAND, b"invalid-brand".to_vec()),
(Tag::ATTESTATION_ID_DEVICE, b"invalid-device-name".to_vec()),
@@ -312,11 +282,8 @@ fn keystore2_device_unique_attest_key_fails_with_invalid_attestation_id() {
let alias = "ks_ec_device_unique_attested_test_key_fail";
let gen_params = add_attest_id_auth(gen_params, attest_id, value.clone());
- let result = key_generations::map_ks_error(key_generations::generate_key(
- &sec_level,
- &gen_params,
- alias,
- ));
+ let result =
+ key_generations::map_ks_error(key_generations::generate_key(&sl, &gen_params, alias));
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::Km(ErrorCode::CANNOT_ATTEST_IDS)));
}
diff --git a/keystore2/tests/keystore2_client_ec_key_tests.rs b/keystore2/tests/keystore2_client_ec_key_tests.rs
index 82671402..8aa9bc49 100644
--- a/keystore2/tests/keystore2_client_ec_key_tests.rs
+++ b/keystore2/tests/keystore2_client_ec_key_tests.rs
@@ -12,27 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::{getuid, Gid, Uid};
-use rustutils::users::AID_USER_OFFSET;
-
+use crate::keystore2_client_test_utils::{
+ delete_app_key, execute_op_run_as_child, get_vsr_api_level, perform_sample_sign_operation,
+ BarrierReached, ForcedOp, TestOutcome,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode,
- KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+ KeyPurpose::KeyPurpose,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- CreateOperationResponse::CreateOperationResponse, Domain::Domain,
- IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+ CreateOperationResponse::CreateOperationResponse, Domain::Domain, KeyDescriptor::KeyDescriptor,
ResponseCode::ResponseCode,
};
-
use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
-};
-
-use crate::keystore2_client_test_utils::{
- delete_app_key, execute_op_run_as_child, perform_sample_sign_operation, BarrierReached,
- ForcedOp, TestOutcome,
+ authorizations, get_keystore_service, key_generations, key_generations::Error, run_as, SecLevel,
};
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
macro_rules! test_ec_sign_key_op_success {
( $test_name:ident, $digest:expr, $ec_curve:expr ) => {
@@ -57,7 +53,7 @@ macro_rules! test_ec_sign_key_op_with_none_or_md5_digest {
}
fn create_ec_key_and_operation(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
@@ -65,9 +61,9 @@ fn create_ec_key_and_operation(
ec_curve: EcCurve,
) -> binder::Result<CreateOperationResponse> {
let key_metadata =
- key_generations::generate_ec_key(sec_level, domain, nspace, alias, ec_curve, digest)?;
+ key_generations::generate_ec_key(sl, domain, nspace, alias, ec_curve, digest)?;
- sec_level.createOperation(
+ sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
false,
@@ -75,11 +71,10 @@ fn create_ec_key_and_operation(
}
fn perform_ec_sign_key_op_success(alias: &str, digest: Digest, ec_curve: EcCurve) {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let op_response = create_ec_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -96,15 +91,14 @@ fn perform_ec_sign_key_op_success(alias: &str, digest: Digest, ec_curve: EcCurve
))
);
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
fn perform_ec_sign_key_op_with_none_or_md5_digest(alias: &str, digest: Digest, ec_curve: EcCurve) {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
match key_generations::map_ks_error(create_ec_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -126,7 +120,7 @@ fn perform_ec_sign_key_op_with_none_or_md5_digest(alias: &str, digest: Digest, e
}
}
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
// Below macros generate tests for generating EC keys with curves EcCurve::P_224, EcCurve::P_256,
@@ -199,12 +193,11 @@ test_ec_sign_key_op_success!(sign_ec_key_op_sha512_ec_p521, Digest::SHA_2_512, E
/// INVALID_ARGUMENT error is expected.
#[test]
fn keystore2_get_key_entry_blob_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
// Generate a key with domain as BLOB.
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::BLOB,
key_generations::SELINUX_SHELL_NAMESPACE,
None,
@@ -213,23 +206,22 @@ fn keystore2_get_key_entry_blob_fail() {
.unwrap();
// Try to load the key using above generated KeyDescriptor.
- let result = key_generations::map_ks_error(keystore2.getKeyEntry(&key_metadata.key));
+ let result = key_generations::map_ks_error(sl.keystore2.getKeyEntry(&key_metadata.key));
assert!(result.is_err());
assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
// Delete the generated key blob.
- sec_level.deleteKey(&key_metadata.key).unwrap();
+ sl.binder.deleteKey(&key_metadata.key).unwrap();
}
/// Try to generate a key with invalid Domain. `INVALID_ARGUMENT` error response is expected.
#[test]
fn keystore2_generate_key_invalid_domain() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_invalid_test_key_{}", getuid());
let result = key_generations::map_ks_error(key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain(99), // Invalid domain.
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias),
@@ -244,8 +236,7 @@ fn keystore2_generate_key_invalid_domain() {
/// `UNSUPPORTED_EC_CURVE or UNSUPPORTED_KEY_SIZE` error response is expected.
#[test]
fn keystore2_generate_ec_key_missing_curve() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_ec_no_curve_test_key_{}", getuid());
// Don't provide EC curve.
@@ -256,7 +247,7 @@ fn keystore2_generate_ec_key_missing_curve() {
.purpose(KeyPurpose::VERIFY)
.digest(Digest::SHA_2_256);
- let result = key_generations::map_ks_error(sec_level.generateKey(
+ let result = key_generations::map_ks_error(sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::SELINUX,
nspace: key_generations::SELINUX_SHELL_NAMESPACE,
@@ -280,8 +271,7 @@ fn keystore2_generate_ec_key_missing_curve() {
/// `INCOMPATIBLE_PURPOSE` error response is expected.
#[test]
fn keystore2_generate_ec_key_25519_multi_purpose() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_ec_no_curve_test_key_{}", getuid());
// Specify `SIGN and AGREE_KEY` purposes.
@@ -293,7 +283,7 @@ fn keystore2_generate_ec_key_25519_multi_purpose() {
.purpose(KeyPurpose::AGREE_KEY)
.digest(Digest::SHA_2_256);
- let result = key_generations::map_ks_error(sec_level.generateKey(
+ let result = key_generations::map_ks_error(sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::SELINUX,
nspace: key_generations::SELINUX_SHELL_NAMESPACE,
@@ -314,12 +304,11 @@ fn keystore2_generate_ec_key_25519_multi_purpose() {
/// able to create an operation successfully.
#[test]
fn keystore2_ec_25519_generate_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_ec_25519_none_test_key_gen_{}", getuid());
let key_metadata = key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias),
@@ -328,7 +317,8 @@ fn keystore2_ec_25519_generate_key_success() {
)
.unwrap();
- let op_response = sec_level
+ let op_response = sl
+ .binder
.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::NONE),
@@ -350,8 +340,7 @@ fn keystore2_ec_25519_generate_key_success() {
/// `UNSUPPORTED_DIGEST`.
#[test]
fn keystore2_ec_25519_generate_key_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let digests = [
Digest::MD5,
@@ -365,7 +354,7 @@ fn keystore2_ec_25519_generate_key_fail() {
for digest in digests {
let alias = format!("ks_ec_25519_test_key_gen_{}{}", getuid(), digest.0);
let key_metadata = key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -374,13 +363,18 @@ fn keystore2_ec_25519_generate_key_fail() {
)
.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(sl.binder.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());
+ }
}
}
@@ -389,12 +383,11 @@ fn keystore2_ec_25519_generate_key_fail() {
/// `INCOMPATIBLE_DIGEST` error as there is a mismatch of digest mode in key authorizations.
#[test]
fn keystore2_create_op_with_incompatible_key_digest() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_ec_test_incomp_key_digest";
let key_metadata = key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -407,7 +400,7 @@ fn keystore2_create_op_with_incompatible_key_digest() {
[Digest::NONE, Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_384, Digest::SHA_2_512];
for digest in digests {
- let result = key_generations::map_ks_error(sec_level.createOperation(
+ let result = key_generations::map_ks_error(sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
false,
@@ -483,11 +476,10 @@ fn keystore2_key_owner_validation() {
/// successfully.
#[test]
fn keystore2_generate_key_with_blob_domain() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let key_metadata = key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain::BLOB,
key_generations::SELINUX_SHELL_NAMESPACE,
None,
@@ -502,7 +494,7 @@ fn keystore2_generate_key_with_blob_domain() {
// Must have the key blob.
assert!(key_metadata.key.blob.is_some());
- let op_response = key_generations::map_ks_error(sec_level.createOperation(
+ let op_response = key_generations::map_ks_error(sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
false,
@@ -517,5 +509,5 @@ fn keystore2_generate_key_with_blob_domain() {
);
// Delete the generated key blob.
- sec_level.deleteKey(&key_metadata.key).unwrap();
+ sl.binder.deleteKey(&key_metadata.key).unwrap();
}
diff --git a/keystore2/tests/keystore2_client_grant_key_tests.rs b/keystore2/tests/keystore2_client_grant_key_tests.rs
index 516869a1..50b87b9a 100644
--- a/keystore2/tests/keystore2_client_grant_key_tests.rs
+++ b/keystore2/tests/keystore2_client_grant_key_tests.rs
@@ -12,37 +12,32 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::{getuid, Gid, Uid};
-use rustutils::users::AID_USER_OFFSET;
-
+use crate::keystore2_client_test_utils::{
+ generate_ec_key_and_grant_to_users, perform_sample_sign_operation,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Digest::Digest, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+ Digest::Digest, KeyPurpose::KeyPurpose,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
- IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+ Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
ResponseCode::ResponseCode,
};
-
use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
-};
-
-use crate::keystore2_client_test_utils::{
- generate_ec_key_and_grant_to_users, perform_sample_sign_operation,
+ authorizations, get_keystore_service, key_generations, key_generations::Error, run_as, SecLevel,
};
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
/// Generate an EC signing key and grant it to the user with given access vector.
fn generate_ec_key_and_grant_to_user(
grantee_uid: i32,
access_vector: i32,
) -> binder::Result<KeyDescriptor> {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("{}{}", "ks_grant_test_key_1", getuid());
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias),
@@ -50,15 +45,14 @@ fn generate_ec_key_and_grant_to_user(
)
.unwrap();
- keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
+ sl.keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
}
fn load_grant_key_and_perform_sign_operation(
- keystore2: &binder::Strong<dyn IKeystoreService>,
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
grant_key_nspace: i64,
) -> Result<(), binder::Status> {
- let key_entry_response = keystore2.getKeyEntry(&KeyDescriptor {
+ let key_entry_response = sl.keystore2.getKeyEntry(&KeyDescriptor {
domain: Domain::GRANT,
nspace: grant_key_nspace,
alias: None,
@@ -66,7 +60,7 @@ fn load_grant_key_and_perform_sign_operation(
})?;
// Perform sample crypto operation using granted key.
- let op_response = sec_level.createOperation(
+ let op_response = sl.binder.createOperation(
&key_entry_response.metadata.key,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
false,
@@ -195,12 +189,11 @@ fn keystore2_grant_get_info_use_key_perm() {
Uid::from_raw(GRANTEE_UID),
Gid::from_raw(GRANTEE_GID),
move || {
- let keystore2 = get_keystore_service();
- let sec_level =
- keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
// Load the granted key.
- let key_entry_response = keystore2
+ let key_entry_response = sl
+ .keystore2
.getKeyEntry(&KeyDescriptor {
domain: Domain::GRANT,
nspace: grant_key_nspace,
@@ -210,7 +203,8 @@ fn keystore2_grant_get_info_use_key_perm() {
.unwrap();
// Perform sample crypto operation using granted key.
- let op_response = sec_level
+ let op_response = sl
+ .binder
.createOperation(
&key_entry_response.metadata.key,
&authorizations::AuthSetBuilder::new()
@@ -228,12 +222,13 @@ fn keystore2_grant_get_info_use_key_perm() {
);
// Try to delete the key, it is expected to be fail with permission denied error.
- let result = key_generations::map_ks_error(keystore2.deleteKey(&KeyDescriptor {
- domain: Domain::GRANT,
- nspace: grant_key_nspace,
- alias: None,
- blob: None,
- }));
+ let result =
+ key_generations::map_ks_error(sl.keystore2.deleteKey(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ }));
assert!(result.is_err());
assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
},
@@ -258,12 +253,10 @@ fn keystore2_grant_delete_key_success() {
// SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let access_vector = KeyPermission::DELETE.0;
let mut grant_keys = generate_ec_key_and_grant_to_users(
- &keystore2,
- &sec_level,
+ &sl,
Some(ALIAS.to_string()),
vec![GRANTEE_UID.try_into().unwrap()],
access_vector,
@@ -335,13 +328,11 @@ fn keystore2_grant_key_fails_with_permission_denied() {
// SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let access_vector = KeyPermission::GET_INFO.0;
let alias = format!("ks_grant_perm_denied_key_{}", getuid());
let mut grant_keys = generate_ec_key_and_grant_to_users(
- &keystore2,
- &sec_level,
+ &sl,
Some(alias),
vec![GRANTEE_UID.try_into().unwrap()],
access_vector,
@@ -411,8 +402,7 @@ fn keystore2_grant_key_fails_with_permission_denied() {
/// `GRANT` access. Test should fail to grant a key with `PERMISSION_DENIED` error response code.
#[test]
fn keystore2_grant_key_fails_with_grant_perm_expect_perm_denied() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let access_vector = KeyPermission::GRANT.0;
let alias = format!("ks_grant_access_vec_key_{}", getuid());
let user_id = 98;
@@ -420,8 +410,7 @@ fn keystore2_grant_key_fails_with_grant_perm_expect_perm_denied() {
let grantee_uid = user_id * AID_USER_OFFSET + application_id;
let result = key_generations::map_ks_error(generate_ec_key_and_grant_to_users(
- &keystore2,
- &sec_level,
+ &sl,
Some(alias),
vec![grantee_uid.try_into().unwrap()],
access_vector,
@@ -470,13 +459,11 @@ fn keystore2_ungrant_key_success() {
// SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_ungrant_test_key_1{}", getuid());
let access_vector = KeyPermission::GET_INFO.0;
let mut grant_keys = generate_ec_key_and_grant_to_users(
- &keystore2,
- &sec_level,
+ &sl,
Some(alias.to_string()),
vec![GRANTEE_UID.try_into().unwrap()],
access_vector,
@@ -485,8 +472,8 @@ fn keystore2_ungrant_key_success() {
let grant_key_nspace = grant_keys.remove(0);
- //Ungrant above granted key.
- keystore2
+ // Ungrant above granted key.
+ sl.keystore2
.ungrant(
&KeyDescriptor {
domain: Domain::APP,
@@ -542,12 +529,11 @@ fn keystore2_ungrant_fails_with_non_existing_key_expect_key_not_found_error() {
// SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("{}{}", "ks_grant_delete_ungrant_test_key_1", getuid());
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias.to_string()),
@@ -556,17 +542,18 @@ fn keystore2_ungrant_fails_with_non_existing_key_expect_key_not_found_error() {
.unwrap();
let access_vector = KeyPermission::GET_INFO.0;
- let grant_key = keystore2
+ let grant_key = sl
+ .keystore2
.grant(&key_metadata.key, GRANTEE_UID.try_into().unwrap(), access_vector)
.unwrap();
assert_eq!(grant_key.domain, Domain::GRANT);
// Delete above granted key.
- keystore2.deleteKey(&key_metadata.key).unwrap();
+ sl.keystore2.deleteKey(&key_metadata.key).unwrap();
// Try to ungrant above granted key.
let result = key_generations::map_ks_error(
- keystore2.ungrant(&key_metadata.key, GRANTEE_UID.try_into().unwrap()),
+ sl.keystore2.ungrant(&key_metadata.key, GRANTEE_UID.try_into().unwrap()),
);
assert!(result.is_err());
assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
@@ -574,7 +561,7 @@ fn keystore2_ungrant_fails_with_non_existing_key_expect_key_not_found_error() {
// Generate a new key with the same alias and try to access the earlier granted key
// in grantee context.
let result = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias),
@@ -631,14 +618,12 @@ fn keystore2_grant_key_to_multi_users_success() {
// SAFETY: The test is run in a separate process with no other threads.
let mut grant_keys = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_grant_test_key_2{}", getuid());
let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0;
generate_ec_key_and_grant_to_users(
- &keystore2,
- &sec_level,
+ &sl,
Some(alias),
vec![GRANTEE_1_UID.try_into().unwrap(), GRANTEE_2_UID.try_into().unwrap()],
access_vector,
@@ -658,15 +643,12 @@ fn keystore2_grant_key_to_multi_users_success() {
Uid::from_raw(*grantee_uid),
Gid::from_raw(*grantee_gid),
move || {
- let keystore2 = get_keystore_service();
- let sec_level =
- keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
assert_eq!(
Ok(()),
key_generations::map_ks_error(load_grant_key_and_perform_sign_operation(
- &keystore2,
- &sec_level,
+ &sl,
grant_key_nspace
))
);
@@ -697,15 +679,13 @@ fn keystore2_grant_key_to_multi_users_delete_fails_with_key_not_found_error() {
// SAFETY: The test is run in a separate process with no other threads.
let mut grant_keys = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_grant_test_key_2{}", getuid());
let access_vector =
KeyPermission::GET_INFO.0 | KeyPermission::USE.0 | KeyPermission::DELETE.0;
generate_ec_key_and_grant_to_users(
- &keystore2,
- &sec_level,
+ &sl,
Some(alias),
vec![GRANTEE_1_UID.try_into().unwrap(), GRANTEE_2_UID.try_into().unwrap()],
access_vector,
@@ -723,21 +703,18 @@ fn keystore2_grant_key_to_multi_users_delete_fails_with_key_not_found_error() {
Uid::from_raw(GRANTEE_1_UID),
Gid::from_raw(GRANTEE_1_GID),
move || {
- let keystore2 = get_keystore_service();
- let sec_level =
- keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
assert_eq!(
Ok(()),
key_generations::map_ks_error(load_grant_key_and_perform_sign_operation(
- &keystore2,
- &sec_level,
+ &sl,
grant_key1_nspace
))
);
// Delete the granted key.
- keystore2
+ sl.keystore2
.deleteKey(&KeyDescriptor {
domain: Domain::GRANT,
nspace: grant_key1_nspace,
diff --git a/keystore2/tests/keystore2_client_hmac_key_tests.rs b/keystore2/tests/keystore2_client_hmac_key_tests.rs
index 6bb80017..76780a0b 100644
--- a/keystore2/tests/keystore2_client_hmac_key_tests.rs
+++ b/keystore2/tests/keystore2_client_hmac_key_tests.rs
@@ -12,23 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use crate::keystore2_client_test_utils::perform_sample_sign_operation;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, Digest::Digest, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
- SecurityLevel::SecurityLevel,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
-};
-
-use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error,
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
};
-
-use crate::keystore2_client_test_utils::perform_sample_sign_operation;
+use keystore2_test_utils::{authorizations, key_generations, key_generations::Error, SecLevel};
/// Generate HMAC key with given parameters and perform a sample operation using generated key.
fn create_hmac_key_and_operation(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
alias: &str,
key_size: i32,
mac_len: i32,
@@ -36,9 +31,9 @@ fn create_hmac_key_and_operation(
digest: Digest,
) -> Result<(), binder::Status> {
let key_metadata =
- key_generations::generate_hmac_key(sec_level, alias, key_size, min_mac_len, digest)?;
+ key_generations::generate_hmac_key(sl, alias, key_size, min_mac_len, digest)?;
- let op_response = sec_level.createOperation(
+ let op_response = sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new()
.purpose(KeyPurpose::SIGN)
@@ -69,22 +64,14 @@ fn keystore2_hmac_key_op_success() {
let mac_len = 128;
let key_size = 128;
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
for digest in digests {
let alias = format!("ks_hmac_test_key_{}", digest.0);
assert_eq!(
Ok(()),
- create_hmac_key_and_operation(
- &sec_level,
- &alias,
- key_size,
- mac_len,
- min_mac_len,
- digest,
- )
+ create_hmac_key_and_operation(&sl, &alias, key_size, mac_len, min_mac_len, digest,)
);
}
}
@@ -96,13 +83,12 @@ fn keystore2_hmac_gen_keys_fails_expect_unsupported_key_size() {
let min_mac_len = 256;
let digest = Digest::SHA_2_256;
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
for key_size in 0..513 {
let alias = format!("ks_hmac_test_key_{}", key_size);
let result = key_generations::map_ks_error(key_generations::generate_hmac_key(
- &sec_level,
+ &sl,
&alias,
key_size,
min_mac_len,
@@ -128,13 +114,12 @@ fn keystore2_hmac_gen_keys_fails_expect_unsupported_min_mac_length() {
let digest = Digest::SHA_2_256;
let key_size = 128;
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
for min_mac_len in 0..257 {
let alias = format!("ks_hmac_test_key_mml_{}", min_mac_len);
match key_generations::map_ks_error(key_generations::generate_hmac_key(
- &sec_level,
+ &sl,
&alias,
key_size,
min_mac_len,
@@ -159,8 +144,7 @@ fn keystore2_hmac_gen_keys_fails_expect_unsupported_min_mac_length() {
/// Test fails to generate a key with multiple digests with an error code `UNSUPPORTED_DIGEST`.
#[test]
fn keystore2_hmac_gen_key_multi_digests_fails_expect_unsupported_digest() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_hmac_test_key_multi_dig";
let gen_params = authorizations::AuthSetBuilder::new()
@@ -173,7 +157,7 @@ fn keystore2_hmac_gen_key_multi_digests_fails_expect_unsupported_digest() {
.digest(Digest::SHA1)
.digest(Digest::SHA_2_256);
- let result = key_generations::map_ks_error(sec_level.generateKey(
+ let result = key_generations::map_ks_error(sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -193,8 +177,7 @@ fn keystore2_hmac_gen_key_multi_digests_fails_expect_unsupported_digest() {
/// no digest should fail with an error code `UNSUPPORTED_DIGEST`.
#[test]
fn keystore2_hmac_gen_key_no_digests_fails_expect_unsupported_digest() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_hmac_test_key_no_dig";
let gen_params = authorizations::AuthSetBuilder::new()
@@ -205,7 +188,7 @@ fn keystore2_hmac_gen_key_no_digests_fails_expect_unsupported_digest() {
.key_size(128)
.min_mac_length(128);
- let result = key_generations::map_ks_error(sec_level.generateKey(
+ let result = key_generations::map_ks_error(sl.binder.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -227,12 +210,11 @@ fn keystore2_hmac_gen_key_no_digests_fails_expect_unsupported_digest() {
fn keystore2_hmac_gen_key_with_none_digest_fails_expect_unsupported_digest() {
let min_mac_len = 128;
let key_size = 128;
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_hmac_test_key_fail";
let result = key_generations::map_ks_error(key_generations::generate_hmac_key(
- &sec_level,
+ &sl,
alias,
key_size,
min_mac_len,
@@ -253,14 +235,13 @@ fn keystore2_hmac_key_op_with_mac_len_greater_than_digest_len_fail() {
let mac_len = 256;
let key_size = 128;
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
for digest in digests {
let alias = format!("ks_hmac_test_key_{}", digest.0);
let result = key_generations::map_ks_error(create_hmac_key_and_operation(
- &sec_level,
+ &sl,
&alias,
key_size,
mac_len,
@@ -284,14 +265,13 @@ fn keystore2_hmac_key_op_with_mac_len_less_than_min_mac_len_fail() {
let mac_len = 64;
let key_size = 128;
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
for digest in digests {
let alias = format!("ks_hmac_test_key_{}", digest.0);
let result = key_generations::map_ks_error(create_hmac_key_and_operation(
- &sec_level,
+ &sl,
&alias,
key_size,
mac_len,
diff --git a/keystore2/tests/keystore2_client_import_keys_tests.rs b/keystore2/tests/keystore2_client_import_keys_tests.rs
index 31d57a2f..f3a267bb 100644
--- a/keystore2/tests/keystore2_client_import_keys_tests.rs
+++ b/keystore2/tests/keystore2_client_import_keys_tests.rs
@@ -12,49 +12,41 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::getuid;
-
-use openssl::rand::rand_bytes;
-use openssl::x509::X509;
-
+use crate::keystore2_client_test_utils::{
+ 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,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType,
- KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+ KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
};
use android_system_keystore2::aidl::android::system::keystore2::{
AuthenticatorSpec::AuthenticatorSpec, Domain::Domain,
IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
KeyMetadata::KeyMetadata, ResponseCode::ResponseCode,
};
-
-use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error,
-};
-
use keystore2_test_utils::ffi_test_utils::{
create_wrapped_key, create_wrapped_key_additional_auth_data,
};
-
-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,
-};
+use keystore2_test_utils::{authorizations, key_generations, key_generations::Error, SecLevel};
+use nix::unistd::getuid;
+use openssl::rand::rand_bytes;
+use openssl::x509::X509;
pub fn import_rsa_sign_key_and_perform_sample_operation(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
import_params: authorizations::AuthSetBuilder,
) {
let key_metadata =
- key_generations::import_rsa_2048_key(sec_level, domain, nspace, alias, import_params)
- .unwrap();
+ key_generations::import_rsa_2048_key(sl, domain, nspace, alias, import_params).unwrap();
perform_sample_asym_sign_verify_op(
- sec_level,
+ &sl.binder,
&key_metadata,
Some(PaddingMode::RSA_PSS),
Some(Digest::SHA_2_256),
@@ -93,7 +85,7 @@ fn perform_sym_key_encrypt_decrypt_op(
}
fn build_secure_key_wrapper(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
secure_key: &[u8],
transport_key: &[u8],
nonce: &[u8],
@@ -103,10 +95,10 @@ fn build_secure_key_wrapper(
// Encrypt secure key with transport key.
let transport_key_alias = format!("ks_transport_key_aes_256_key_test_{}", getuid());
let transport_key_metadata =
- key_generations::import_transport_key(sec_level, Some(transport_key_alias), transport_key)
+ key_generations::import_transport_key(sl, Some(transport_key_alias), transport_key)
.unwrap();
let encrypted_secure_key = encrypt_secure_key(
- sec_level,
+ &sl.binder,
secure_key,
aad,
nonce.to_vec(),
@@ -135,8 +127,7 @@ fn build_secure_key_wrapper(
/// imported key. Test should be able to create an operation successfully.
#[test]
fn keystore2_rsa_import_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_rsa_key_test_import_1_{}{}", getuid(), 2048);
@@ -153,7 +144,7 @@ fn keystore2_rsa_import_key_success() {
.cert_not_after(253402300799000);
import_rsa_sign_key_and_perform_sample_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias),
@@ -167,8 +158,7 @@ fn keystore2_rsa_import_key_success() {
/// able to create an operation successfully.
#[test]
fn keystore2_rsa_import_key_determine_key_size_and_pub_exponent() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_rsa_key_test_import_2_{}{}", getuid(), 2048);
@@ -184,7 +174,7 @@ fn keystore2_rsa_import_key_determine_key_size_and_pub_exponent() {
.cert_not_after(253402300799000);
import_rsa_sign_key_and_perform_sample_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias),
@@ -196,8 +186,7 @@ fn keystore2_rsa_import_key_determine_key_size_and_pub_exponent() {
/// a key with `IMPORT_PARAMETER_MISMATCH` error code.
#[test]
fn keystore2_rsa_import_key_fails_with_keysize_param_mismatch_error() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_rsa_key_test_import_3_{}{}", getuid(), 2048);
@@ -213,7 +202,7 @@ fn keystore2_rsa_import_key_fails_with_keysize_param_mismatch_error() {
.cert_not_before(0)
.cert_not_after(253402300799000);
- let result = key_generations::map_ks_error(sec_level.importKey(
+ let result = key_generations::map_ks_error(sl.binder.importKey(
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
None,
&import_params,
@@ -229,8 +218,7 @@ fn keystore2_rsa_import_key_fails_with_keysize_param_mismatch_error() {
/// Test should fail to import a key with `IMPORT_PARAMETER_MISMATCH` error code.
#[test]
fn keystore2_rsa_import_key_fails_with_public_exponent_param_mismatch_error() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_rsa_key_test_import_4_{}{}", getuid(), 2048);
@@ -246,7 +234,7 @@ fn keystore2_rsa_import_key_fails_with_public_exponent_param_mismatch_error() {
.cert_not_before(0)
.cert_not_after(253402300799000);
- let result = key_generations::map_ks_error(sec_level.importKey(
+ let result = key_generations::map_ks_error(sl.binder.importKey(
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
None,
&import_params,
@@ -259,12 +247,11 @@ fn keystore2_rsa_import_key_fails_with_public_exponent_param_mismatch_error() {
}
/// Try to import a key with multiple purposes. Test should fail to import a key with
-/// `INCOMPATIBLE_PURPOSE` error code. If the backend is `keymaster` then `importKey` shall be
-/// successful.
+/// `INCOMPATIBLE_PURPOSE` error code. If the backend is `keymaster` or KeyMint-version-1 then
+/// `importKey` shall be successful.
#[test]
fn keystore2_rsa_import_key_with_multipurpose_fails_incompt_purpose_error() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_rsa_key_test_import_5_{}{}", getuid(), 2048);
@@ -280,7 +267,7 @@ fn keystore2_rsa_import_key_with_multipurpose_fails_incompt_purpose_error() {
.cert_not_before(0)
.cert_not_after(253402300799000);
- let result = key_generations::map_ks_error(sec_level.importKey(
+ let result = key_generations::map_ks_error(sl.binder.importKey(
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
None,
&import_params,
@@ -288,9 +275,15 @@ fn keystore2_rsa_import_key_with_multipurpose_fails_incompt_purpose_error() {
key_generations::RSA_2048_KEY,
));
- if key_generations::has_default_keymint() {
- assert!(result.is_err());
- assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+ if sl.is_keymint() {
+ if sl.get_keymint_version() >= 2 {
+ // The KeyMint v1 spec required that KeyPurpose::ATTEST_KEY not be combined
+ // with other key purposes. However, this was not checked at the time
+ // so we can only be strict about checking this for implementations of KeyMint
+ // version 2 and above.
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+ }
} else {
assert!(result.is_ok());
}
@@ -301,11 +294,17 @@ fn keystore2_rsa_import_key_with_multipurpose_fails_incompt_purpose_error() {
/// able to create an operation successfully.
#[test]
fn keystore2_import_ec_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
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()
@@ -316,24 +315,18 @@ fn keystore2_import_ec_key_success() {
.cert_not_before(0)
.cert_not_after(253402300799000);
- let key_metadata = key_generations::import_ec_p_256_key(
- &sec_level,
- Domain::APP,
- -1,
- Some(alias),
- import_params,
- )
- .expect("Failed to import EC key.");
+ let key_metadata =
+ key_generations::import_ec_p_256_key(&sl, Domain::APP, -1, Some(alias), import_params)
+ .expect("Failed to import EC key.");
- perform_sample_asym_sign_verify_op(&sec_level, &key_metadata, None, Some(Digest::SHA_2_256));
+ perform_sample_asym_sign_verify_op(&sl.binder, &key_metadata, None, Some(Digest::SHA_2_256));
}
/// Try to import EC key with wrong ec-curve as import-key-parameter. Test should fail to import a
/// key with `IMPORT_PARAMETER_MISMATCH` error code.
#[test]
fn keystore2_ec_import_key_fails_with_mismatch_curve_error() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_ec_key_test_import_1_{}{}", getuid(), 256);
@@ -347,7 +340,7 @@ fn keystore2_ec_import_key_fails_with_mismatch_curve_error() {
.cert_not_before(0)
.cert_not_after(253402300799000);
- let result = key_generations::map_ks_error(sec_level.importKey(
+ let result = key_generations::map_ks_error(sl.binder.importKey(
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
None,
&import_params,
@@ -362,47 +355,41 @@ fn keystore2_ec_import_key_fails_with_mismatch_curve_error() {
/// Test should be able to create an operation successfully.
#[test]
fn keystore2_import_aes_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_aes_key_test_import_1_{}{}", getuid(), 256);
- let key_metadata = key_generations::import_aes_key(&sec_level, Domain::APP, -1, Some(alias))
+ let key_metadata = key_generations::import_aes_key(&sl, Domain::APP, -1, Some(alias))
.expect("Failed to import AES key.");
- perform_sym_key_encrypt_decrypt_op(&sec_level, &key_metadata);
+ perform_sym_key_encrypt_decrypt_op(&sl.binder, &key_metadata);
}
/// Import 3DES key and verify key parameters. Try to create an operation using the imported key.
/// Test should be able to create an operation successfully.
#[test]
fn keystore2_import_3des_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = key_generations::map_ks_error(
- keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT),
- )
- .unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_3des_key_test_import_1_{}{}", getuid(), 168);
- let key_metadata = key_generations::import_3des_key(&sec_level, Domain::APP, -1, Some(alias))
+ let key_metadata = key_generations::import_3des_key(&sl, Domain::APP, -1, Some(alias))
.expect("Failed to import 3DES key.");
- perform_sym_key_encrypt_decrypt_op(&sec_level, &key_metadata);
+ perform_sym_key_encrypt_decrypt_op(&sl.binder, &key_metadata);
}
/// Import HMAC key and verify key parameters. Try to create an operation using the imported key.
/// Test should be able to create an operation successfully.
#[test]
fn keystore2_import_hmac_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_hmac_key_test_import_1_{}", getuid());
- let key_metadata = key_generations::import_hmac_key(&sec_level, Domain::APP, -1, Some(alias))
+ let key_metadata = key_generations::import_hmac_key(&sl, Domain::APP, -1, Some(alias))
.expect("Failed to import HMAC key.");
- perform_sample_hmac_sign_verify_op(&sec_level, &key_metadata.key);
+ perform_sample_hmac_sign_verify_op(&sl.binder, &key_metadata.key);
}
/// This test creates a wrapped key data and imports it. Validates the imported wrapped key.
@@ -412,8 +399,7 @@ fn keystore2_import_hmac_key_success() {
/// Test should successfully import the wrapped key and perform crypto operations.
#[test]
fn keystore2_create_wrapped_key_and_import_wrapped_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let mut secure_key = [0; 32];
rand_bytes(&mut secure_key).unwrap();
@@ -427,7 +413,7 @@ fn keystore2_create_wrapped_key_and_import_wrapped_key_success() {
// Import wrapping key.
let wrapping_key_alias = format!("ks_wrapping_key_test_import_2_{}_2048", getuid());
let wrapping_key_metadata = key_generations::import_wrapping_key(
- &sec_level,
+ &sl,
key_generations::RSA_2048_KEY,
Some(wrapping_key_alias),
)
@@ -439,7 +425,7 @@ fn keystore2_create_wrapped_key_and_import_wrapped_key_success() {
// Build ASN.1 DER-encoded wrapped key material as described in `SecureKeyWrapper` schema.
let wrapped_key_data = build_secure_key_wrapper(
- &sec_level,
+ &sl,
&secure_key,
&transport_key,
&nonce,
@@ -451,14 +437,14 @@ fn keystore2_create_wrapped_key_and_import_wrapped_key_success() {
// Unwrap the key. Import wrapped key.
let secured_key_alias = format!("ks_wrapped_aes_key_{}", getuid());
let secured_key_metadata = key_generations::import_wrapped_key(
- &sec_level,
+ &sl,
Some(secured_key_alias),
&wrapping_key_metadata,
Some(wrapped_key_data.to_vec()),
)
.unwrap();
- perform_sym_key_encrypt_decrypt_op(&sec_level, &secured_key_metadata);
+ perform_sym_key_encrypt_decrypt_op(&sl.binder, &secured_key_metadata);
}
/// Create a wrapped key data with invalid Additional Authenticated Data (AAD) and
@@ -469,8 +455,7 @@ fn keystore2_create_wrapped_key_and_import_wrapped_key_success() {
/// Test should fail to import the wrapped key with error code `VERIFICATION_FAILED`.
#[test]
fn keystore2_create_wrapped_key_with_invalid_aad_and_import_wrapped_key_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let mut secure_key = [0; 32];
rand_bytes(&mut secure_key).unwrap();
@@ -484,7 +469,7 @@ fn keystore2_create_wrapped_key_with_invalid_aad_and_import_wrapped_key_fail() {
// Import wrapping key.
let wrapping_key_alias = format!("ks_wrapping_key_test_import_2_{}_2048", getuid());
let wrapping_key_metadata = key_generations::import_wrapping_key(
- &sec_level,
+ &sl,
key_generations::RSA_2048_KEY,
Some(wrapping_key_alias),
)
@@ -495,7 +480,7 @@ fn keystore2_create_wrapped_key_with_invalid_aad_and_import_wrapped_key_fail() {
// Build ASN.1 DER-encoded wrapped key material as described in `SecureKeyWrapper` schema.
let wrapped_key_data = build_secure_key_wrapper(
- &sec_level,
+ &sl,
&secure_key,
&transport_key,
&nonce,
@@ -507,7 +492,7 @@ fn keystore2_create_wrapped_key_with_invalid_aad_and_import_wrapped_key_fail() {
// Unwrap the key. Import wrapped key.
let secured_key_alias = format!("ks_wrapped_aes_key_{}", getuid());
let result = key_generations::map_ks_error(key_generations::import_wrapped_key(
- &sec_level,
+ &sl,
Some(secured_key_alias),
&wrapping_key_metadata,
Some(wrapped_key_data.to_vec()),
@@ -521,8 +506,7 @@ fn keystore2_create_wrapped_key_with_invalid_aad_and_import_wrapped_key_fail() {
/// perform crypto operations successfully.
#[test]
fn keystore2_import_wrapped_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_wrapped_key_test_import_1_{}_256", getuid());
let wrapping_key_alias = format!("ks_wrapping_key_test_import_1_{}_2048", getuid());
@@ -541,7 +525,7 @@ fn keystore2_import_wrapped_key_success() {
.cert_not_after(253402300799000);
let key_metadata = key_generations::import_wrapping_key_and_wrapped_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias),
@@ -551,7 +535,7 @@ fn keystore2_import_wrapped_key_success() {
.expect("Failed to import wrapped key.");
// Try to perform operations using wrapped key.
- perform_sym_key_encrypt_decrypt_op(&sec_level, &key_metadata);
+ perform_sym_key_encrypt_decrypt_op(&sl.binder, &key_metadata);
}
/// Import wrapping-key without specifying KeyPurpose::WRAP_KEY in import key parameters. Try to
@@ -560,8 +544,7 @@ fn keystore2_import_wrapped_key_success() {
/// `WRAP_KEY` purpose.
#[test]
fn keystore2_import_wrapped_key_fails_with_wrong_purpose() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let wrapping_key_alias = format!("ks_wrapping_key_test_import_2_{}_2048", getuid());
let alias = format!("ks_wrapped_key_test_import_2_{}_256", getuid());
@@ -581,7 +564,7 @@ fn keystore2_import_wrapped_key_fails_with_wrong_purpose() {
let result =
key_generations::map_ks_error(key_generations::import_wrapping_key_and_wrapped_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias),
@@ -597,8 +580,7 @@ fn keystore2_import_wrapped_key_fails_with_wrong_purpose() {
/// Test should fail to import wrapped key with `ResponseCode::KEY_NOT_FOUND`.
#[test]
fn keystore2_import_wrapped_key_fails_with_missing_wrapping_key() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let unwrap_params = authorizations::AuthSetBuilder::new()
.digest(Digest::SHA_2_256)
@@ -614,7 +596,7 @@ fn keystore2_import_wrapped_key_fails_with_missing_wrapping_key() {
// Wrapping key with this alias doesn't exist.
let wrapping_key_alias = format!("ks_wrapping_key_not_exist_{}_2048", getuid());
- let result = key_generations::map_ks_error(sec_level.importWrappedKey(
+ let result = key_generations::map_ks_error(sl.binder.importWrappedKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
diff --git a/keystore2/tests/keystore2_client_key_agreement_tests.rs b/keystore2/tests/keystore2_client_key_agreement_tests.rs
index 6b2e3c2d..6744b60d 100644
--- a/keystore2/tests/keystore2_client_key_agreement_tests.rs
+++ b/keystore2/tests/keystore2_client_key_agreement_tests.rs
@@ -12,27 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::getuid;
-
-use openssl::ec::{EcGroup, EcKey};
-use openssl::error::ErrorStack;
-use openssl::nid::Nid;
-use openssl::pkey::{PKey, PKeyRef, Private, Public};
-use openssl::pkey_ctx::PkeyCtx;
-use openssl::x509::X509;
-
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
- SecurityLevel::SecurityLevel,
};
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
KeyMetadata::KeyMetadata,
};
-
-use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error,
-};
+use keystore2_test_utils::{authorizations, key_generations, key_generations::Error, SecLevel};
+use nix::unistd::getuid;
+use openssl::ec::{EcGroup, EcKey};
+use openssl::error::ErrorStack;
+use openssl::nid::Nid;
+use openssl::pkey::{PKey, PKeyRef, Private, Public};
+use openssl::pkey_ctx::PkeyCtx;
+use openssl::x509::X509;
/// This macro is used to verify that the key agreement works for the given curve.
macro_rules! test_ec_key_agree {
@@ -89,13 +83,12 @@ fn ec_curve_to_openrssl_curve_name(ec_curve: &EcCurve) -> Nid {
/// Generate two EC keys with given curve from KeyMint and OpeanSSL. Perform local ECDH between
/// them and verify that the derived secrets are the same.
fn perform_ec_key_agreement(ec_curve: EcCurve) {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let openssl_ec_curve = ec_curve_to_openrssl_curve_name(&ec_curve);
let alias = format!("ks_ec_test_key_agree_{}", getuid());
let keymint_key = key_generations::generate_ec_agree_key(
- &sec_level,
+ &sl,
ec_curve,
Digest::SHA_2_256,
Domain::APP,
@@ -111,7 +104,7 @@ fn perform_ec_key_agreement(ec_curve: EcCurve) {
let local_key = PKey::from_ec_key(ec_key).unwrap();
let local_pub_key = local_key.public_key_to_der().unwrap();
- check_agreement(&sec_level, &keymint_key.key, &keymint_pub_key, &local_key, &local_pub_key);
+ check_agreement(&sl.binder, &keymint_key.key, &keymint_pub_key, &local_key, &local_pub_key);
}
test_ec_key_agree!(test_ec_p224_key_agreement, EcCurve::P_224);
@@ -119,16 +112,15 @@ test_ec_key_agree!(test_ec_p256_key_agreement, EcCurve::P_256);
test_ec_key_agree!(test_ec_p384_key_agreement, EcCurve::P_384);
test_ec_key_agree!(test_ec_p521_key_agreement, EcCurve::P_521);
-/// Generate two EC keys with curve `CURVE_25519` from KeyMint and OpeanSSL.
+/// Generate two EC keys with curve `CURVE_25519` from KeyMint and OpenSSL.
/// Perform local ECDH between them and verify that the derived secrets are the same.
#[test]
fn keystore2_ec_25519_agree_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_ec_25519_test_key_agree_{}", getuid());
let keymint_key = key_generations::generate_ec_agree_key(
- &sec_level,
+ &sl,
EcCurve::CURVE_25519,
Digest::NONE,
Domain::APP,
@@ -142,19 +134,18 @@ fn keystore2_ec_25519_agree_key_success() {
let local_key = PKey::generate_x25519().unwrap();
let local_pub_key = local_key.public_key_to_der().unwrap();
- check_agreement(&sec_level, &keymint_key.key, &keymint_pub_key, &local_key, &local_pub_key);
+ check_agreement(&sl.binder, &keymint_key.key, &keymint_pub_key, &local_key, &local_pub_key);
}
/// Generate two EC keys with different curves and try to perform local ECDH. Since keys are using
/// different curves operation should fail with `ErrorCode:INVALID_ARGUMENT`.
#[test]
fn keystore2_ec_agree_key_with_different_curves_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_test_key_agree_fail{}", getuid());
let keymint_key = key_generations::generate_ec_agree_key(
- &sec_level,
+ &sl,
EcCurve::P_256,
Digest::SHA_2_256,
Domain::APP,
@@ -169,7 +160,7 @@ fn keystore2_ec_agree_key_with_different_curves_fail() {
// If the keys are using different curves KeyMint should fail with
// ErrorCode:INVALID_ARGUMENT.
let authorizations = authorizations::AuthSetBuilder::new().purpose(KeyPurpose::AGREE_KEY);
- let key_agree_op = sec_level.createOperation(&keymint_key.key, &authorizations, false).unwrap();
+ let key_agree_op = sl.binder.createOperation(&keymint_key.key, &authorizations, false).unwrap();
assert!(key_agree_op.iOperation.is_some());
let op = key_agree_op.iOperation.unwrap();
diff --git a/keystore2/tests/keystore2_client_key_id_domain_tests.rs b/keystore2/tests/keystore2_client_key_id_domain_tests.rs
index 09b13784..8f9191f3 100644
--- a/keystore2/tests/keystore2_client_key_id_domain_tests.rs
+++ b/keystore2/tests/keystore2_client_key_id_domain_tests.rs
@@ -12,20 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::getuid;
-
+use crate::keystore2_client_test_utils::perform_sample_sign_operation;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Digest::Digest, EcCurve::EcCurve, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+ Digest::Digest, EcCurve::EcCurve, KeyPurpose::KeyPurpose,
};
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
};
-
-use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error,
-};
-
-use crate::keystore2_client_test_utils::perform_sample_sign_operation;
+use keystore2_test_utils::{authorizations, key_generations, key_generations::Error, SecLevel};
+use nix::unistd::getuid;
/// Try to generate a key with `Domain::KEY_ID`, test should fail with an error code
/// `SYSTEM_ERROR`. `Domain::KEY_ID` is not allowed to use for generating a key. Key id is returned
@@ -33,11 +28,10 @@ use crate::keystore2_client_test_utils::perform_sample_sign_operation;
#[test]
fn keystore2_generate_key_with_key_id_domain_expect_sys_error() {
let alias = "ks_gen_key_id_test_key";
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let result = key_generations::map_ks_error(key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain::KEY_ID,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias.to_string()),
@@ -53,12 +47,11 @@ fn keystore2_generate_key_with_key_id_domain_expect_sys_error() {
/// successfully.
#[test]
fn keystore2_find_key_with_key_id_as_domain() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_key_id_test_key";
let key_metadata = key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -68,7 +61,8 @@ fn keystore2_find_key_with_key_id_as_domain() {
.expect("Failed to generate a EC key.");
// Try to load the above generated key with KEY_ID as domain.
- let key_entry_response = keystore2
+ let key_entry_response = sl
+ .keystore2
.getKeyEntry(&KeyDescriptor {
domain: Domain::KEY_ID,
nspace: key_metadata.key.nspace,
@@ -85,7 +79,8 @@ fn keystore2_find_key_with_key_id_as_domain() {
// Try to create an operation using above loaded key, operation should be created
// successfully.
- let op_response = sec_level
+ let op_response = sl
+ .binder
.createOperation(
&key_entry_response.metadata.key,
&authorizations::AuthSetBuilder::new()
@@ -110,12 +105,11 @@ fn keystore2_find_key_with_key_id_as_domain() {
/// create an operation using the rebound key.
#[test]
fn keystore2_key_id_alias_rebind_verify_by_alias() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_key_id_test_alias_rebind_1_{}", getuid());
let key_metadata = key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -127,7 +121,7 @@ fn keystore2_key_id_alias_rebind_verify_by_alias() {
// Generate a key with same alias as above generated key, so that alias will be rebound
// to this key.
let new_key_metadata = key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias),
@@ -142,7 +136,7 @@ fn keystore2_key_id_alias_rebind_verify_by_alias() {
// Try to create an operation using previously generated key_metadata.
// It should fail as previously generated key material is no longer remains valid.
- let result = key_generations::map_ks_error(sec_level.createOperation(
+ let result = key_generations::map_ks_error(sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
false,
@@ -152,7 +146,8 @@ fn keystore2_key_id_alias_rebind_verify_by_alias() {
// Try to create an operation using rebound key, operation should be created
// successfully.
- let op_response = sec_level
+ let op_response = sl
+ .binder
.createOperation(
&new_key_metadata.key,
&authorizations::AuthSetBuilder::new()
@@ -177,12 +172,11 @@ fn keystore2_key_id_alias_rebind_verify_by_alias() {
/// Test should successfully create an operation using the rebound key.
#[test]
fn keystore2_key_id_alias_rebind_verify_by_key_id() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_key_id_test_alias_rebind_2_{}", getuid());
let key_metadata = key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -192,7 +186,8 @@ fn keystore2_key_id_alias_rebind_verify_by_key_id() {
.expect("Failed to generate a EC key.");
// Load the above generated key with KEY_ID as domain.
- let key_entry_response = keystore2
+ let key_entry_response = sl
+ .keystore2
.getKeyEntry(&KeyDescriptor {
domain: Domain::KEY_ID,
nspace: key_metadata.key.nspace,
@@ -210,7 +205,7 @@ fn keystore2_key_id_alias_rebind_verify_by_key_id() {
// Generate another key with same alias as above generated key, so that alias will be rebound
// to this key.
let new_key_metadata = key_generations::generate_ec_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias),
@@ -227,7 +222,7 @@ fn keystore2_key_id_alias_rebind_verify_by_key_id() {
// Try to create an operation using previously loaded key_entry_response.
// It should fail as previously generated key material is no longer valid.
- let result = key_generations::map_ks_error(sec_level.createOperation(
+ let result = key_generations::map_ks_error(sl.binder.createOperation(
&key_entry_response.metadata.key,
&authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
false,
@@ -237,7 +232,8 @@ fn keystore2_key_id_alias_rebind_verify_by_key_id() {
// Try to create an operation using rebound key, operation should be created
// successfully.
- let op_response = sec_level
+ let op_response = sl
+ .binder
.createOperation(
&new_key_metadata.key,
&authorizations::AuthSetBuilder::new()
diff --git a/keystore2/tests/keystore2_client_keystore_engine_tests.rs b/keystore2/tests/keystore2_client_keystore_engine_tests.rs
index 4651931b..01f8917e 100644
--- a/keystore2/tests/keystore2_client_keystore_engine_tests.rs
+++ b/keystore2/tests/keystore2_client_keystore_engine_tests.rs
@@ -12,27 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::{Gid, Uid};
-use rustutils::users::AID_USER_OFFSET;
-
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, KeyPurpose::KeyPurpose,
- PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+ PaddingMode::PaddingMode,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
- IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+ Domain::Domain, IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor,
+ KeyPermission::KeyPermission,
};
-
-use keystore2_test_utils::{authorizations::AuthSetBuilder, get_keystore_service, run_as};
-
use keystore2_test_utils::ffi_test_utils::perform_crypto_op_using_keystore_engine;
-
+use keystore2_test_utils::{
+ authorizations::AuthSetBuilder, get_keystore_service, run_as, SecLevel,
+};
+use nix::unistd::{Gid, Uid};
use openssl::x509::X509;
+use rustutils::users::AID_USER_OFFSET;
fn generate_rsa_key_and_grant_to_user(
- keystore2: &binder::Strong<dyn IKeystoreService>,
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
alias: &str,
grantee_uid: i32,
access_vector: i32,
@@ -47,7 +44,8 @@ fn generate_rsa_key_and_grant_to_user(
.padding_mode(PaddingMode::NONE)
.digest(Digest::NONE);
- let key_metadata = sec_level
+ let key_metadata = sl
+ .binder
.generateKey(
&KeyDescriptor {
domain: Domain::APP,
@@ -64,12 +62,11 @@ fn generate_rsa_key_and_grant_to_user(
assert!(key_metadata.certificate.is_some());
- keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
+ sl.keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
}
fn generate_ec_key_and_grant_to_user(
- keystore2: &binder::Strong<dyn IKeystoreService>,
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
alias: &str,
grantee_uid: i32,
access_vector: i32,
@@ -82,7 +79,8 @@ fn generate_ec_key_and_grant_to_user(
.digest(Digest::NONE)
.ec_curve(EcCurve::P_256);
- let key_metadata = sec_level
+ let key_metadata = sl
+ .binder
.generateKey(
&KeyDescriptor {
domain: Domain::APP,
@@ -99,12 +97,11 @@ fn generate_ec_key_and_grant_to_user(
assert!(key_metadata.certificate.is_some());
- keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
+ sl.keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
}
fn generate_key_and_grant_to_user(
- keystore2: &binder::Strong<dyn IKeystoreService>,
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
alias: &str,
grantee_uid: u32,
algo: Algorithm,
@@ -115,16 +112,14 @@ fn generate_key_and_grant_to_user(
let grant_key = match algo {
Algorithm::RSA => generate_rsa_key_and_grant_to_user(
- keystore2,
- sec_level,
+ sl,
alias,
grantee_uid.try_into().unwrap(),
access_vector,
)
.unwrap(),
Algorithm::EC => generate_ec_key_and_grant_to_user(
- keystore2,
- sec_level,
+ sl,
alias,
grantee_uid.try_into().unwrap(),
access_vector,
@@ -170,17 +165,9 @@ fn keystore2_perofrm_crypto_op_using_keystore2_engine_rsa_key_success() {
// SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "keystore2_engine_rsa_key";
- generate_key_and_grant_to_user(
- &keystore2,
- &sec_level,
- alias,
- GRANTEE_UID,
- Algorithm::RSA,
- )
- .unwrap()
+ generate_key_and_grant_to_user(&sl, alias, GRANTEE_UID, Algorithm::RSA).unwrap()
})
};
@@ -213,17 +200,9 @@ fn keystore2_perofrm_crypto_op_using_keystore2_engine_ec_key_success() {
// SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "keystore2_engine_ec_test_key";
- generate_key_and_grant_to_user(
- &keystore2,
- &sec_level,
- alias,
- GRANTEE_UID,
- Algorithm::EC,
- )
- .unwrap()
+ generate_key_and_grant_to_user(&sl, alias, GRANTEE_UID, Algorithm::EC).unwrap()
})
};
@@ -257,20 +236,14 @@ fn keystore2_perofrm_crypto_op_using_keystore2_engine_pem_pub_key_success() {
// SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "keystore2_engine_rsa_pem_pub_key";
- let grant_key_nspace = generate_key_and_grant_to_user(
- &keystore2,
- &sec_level,
- alias,
- GRANTEE_UID,
- Algorithm::RSA,
- )
- .unwrap();
+ let grant_key_nspace =
+ generate_key_and_grant_to_user(&sl, alias, GRANTEE_UID, Algorithm::RSA).unwrap();
// Update certificate with encodeed PEM data.
- let key_entry_response = keystore2
+ let key_entry_response = sl
+ .keystore2
.getKeyEntry(&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
@@ -281,7 +254,7 @@ fn keystore2_perofrm_crypto_op_using_keystore2_engine_pem_pub_key_success() {
let cert_bytes = key_entry_response.metadata.certificate.as_ref().unwrap();
let cert = X509::from_der(cert_bytes.as_ref()).unwrap();
let cert_pem = cert.to_pem().unwrap();
- keystore2
+ sl.keystore2
.updateSubcomponent(&key_entry_response.metadata.key, Some(&cert_pem), None)
.expect("updateSubcomponent failed.");
diff --git a/keystore2/tests/keystore2_client_list_entries_tests.rs b/keystore2/tests/keystore2_client_list_entries_tests.rs
index 8b3f7001..539dac2d 100644
--- a/keystore2/tests/keystore2_client_list_entries_tests.rs
+++ b/keystore2/tests/keystore2_client_list_entries_tests.rs
@@ -12,19 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::{getuid, Gid, Uid};
-use rustutils::users::AID_USER_OFFSET;
-use std::collections::HashSet;
-use std::fmt::Write;
-
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use crate::keystore2_client_test_utils::{delete_all_entries, delete_app_key, verify_aliases};
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor,
KeyPermission::KeyPermission, ResponseCode::ResponseCode,
};
-
-use crate::keystore2_client_test_utils::{delete_all_entries, delete_app_key, verify_aliases};
-use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error, run_as};
+use keystore2_test_utils::{
+ get_keystore_service, key_generations, key_generations::Error, run_as, SecLevel,
+};
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+use std::collections::HashSet;
+use std::fmt::Write;
/// Try to find a key with given key parameters using `listEntries` API.
fn key_alias_exists(
@@ -63,20 +62,19 @@ fn keystore2_list_entries_success() {
// SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("list_entries_grant_key1_{}", getuid());
// Make sure there is no key exist with this `alias` in `SELINUX` domain and
// `SELINUX_SHELL_NAMESPACE` namespace.
if key_alias_exists(
- &keystore2,
+ &sl.keystore2,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
alias.to_string(),
) {
- keystore2
+ sl.keystore2
.deleteKey(&KeyDescriptor {
domain: Domain::SELINUX,
nspace: key_generations::SELINUX_SHELL_NAMESPACE,
@@ -88,7 +86,7 @@ fn keystore2_list_entries_success() {
// Generate a key with above defined `alias`.
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias.to_string()),
@@ -99,7 +97,7 @@ fn keystore2_list_entries_success() {
// Verify that above generated key entry is listed with domain SELINUX and
// namespace SELINUX_SHELL_NAMESPACE
assert!(key_alias_exists(
- &keystore2,
+ &sl.keystore2,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
alias,
@@ -107,7 +105,7 @@ fn keystore2_list_entries_success() {
// Grant a key with GET_INFO permission.
let access_vector = KeyPermission::GET_INFO.0;
- keystore2
+ sl.keystore2
.grant(&key_metadata.key, GRANTEE_UID.try_into().unwrap(), access_vector)
.unwrap();
})
@@ -121,13 +119,11 @@ fn keystore2_list_entries_success() {
Uid::from_raw(GRANTEE_UID),
Gid::from_raw(GRANTEE_GID),
move || {
- let keystore2 = get_keystore_service();
- let sec_level =
- keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("list_entries_success_key{}", getuid());
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -137,7 +133,7 @@ fn keystore2_list_entries_success() {
// Make sure there is only one key entry exist and that should be the same key
// generated in this user context. Granted key shouldn't be included in this list.
- let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+ let key_descriptors = sl.keystore2.listEntries(Domain::APP, -1).unwrap();
assert_eq!(1, key_descriptors.len());
let key = key_descriptors.first().unwrap();
@@ -145,9 +141,9 @@ fn keystore2_list_entries_success() {
assert_eq!(key.nspace, GRANTEE_UID.try_into().unwrap());
assert_eq!(key.domain, Domain::APP);
- keystore2.deleteKey(&key_metadata.key).unwrap();
+ sl.keystore2.deleteKey(&key_metadata.key).unwrap();
- let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+ let key_descriptors = sl.keystore2.listEntries(Domain::APP, -1).unwrap();
assert_eq!(0, key_descriptors.len());
},
)
@@ -204,14 +200,13 @@ fn keystore2_list_entries_with_long_aliases_success() {
// SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
// Make sure there are no keystore entries exist before adding new entries.
- let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+ let key_descriptors = sl.keystore2.listEntries(Domain::APP, -1).unwrap();
if !key_descriptors.is_empty() {
key_descriptors.into_iter().map(|key| key.alias.unwrap()).for_each(|alias| {
- delete_app_key(&keystore2, &alias).unwrap();
+ delete_app_key(&sl.keystore2, &alias).unwrap();
});
}
@@ -223,8 +218,7 @@ fn keystore2_list_entries_with_long_aliases_success() {
write!(alias, "{}_{}", "X".repeat(6000), count).unwrap();
imported_key_aliases.insert(alias.clone());
- let result =
- key_generations::import_aes_key(&sec_level, Domain::APP, -1, Some(alias));
+ let result = key_generations::import_aes_key(&sl, Domain::APP, -1, Some(alias));
assert!(result.is_ok());
}
@@ -237,7 +231,7 @@ fn keystore2_list_entries_with_long_aliases_success() {
// list of key aliases
// - continue above steps till it cleanup all the imported keystore entries.
while !imported_key_aliases.is_empty() {
- let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+ let key_descriptors = sl.keystore2.listEntries(Domain::APP, -1).unwrap();
// Check retrieved key entries list is a subset of imported keys list.
assert!(key_descriptors
@@ -246,7 +240,7 @@ fn keystore2_list_entries_with_long_aliases_success() {
// Delete the listed key entries from Keystore as well as from imported keys list.
key_descriptors.into_iter().map(|key| key.alias.unwrap()).for_each(|alias| {
- delete_app_key(&keystore2, &alias).unwrap();
+ delete_app_key(&sl.keystore2, &alias).unwrap();
assert!(imported_key_aliases.remove(&alias));
});
}
@@ -271,17 +265,16 @@ fn keystore2_list_entries_batched_with_long_aliases_success() {
// SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
// Make sure there are no keystore entries exist before adding new entries.
- delete_all_entries(&keystore2);
+ delete_all_entries(&sl.keystore2);
// Import 100 keys with aliases of length 6000.
let mut imported_key_aliases =
- key_generations::import_aes_keys(&sec_level, "X".repeat(6000), 1..101).unwrap();
+ key_generations::import_aes_keys(&sl, "X".repeat(6000), 1..101).unwrap();
assert_eq!(
- keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ sl.keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
100,
"Error while importing keys"
);
@@ -290,7 +283,7 @@ fn keystore2_list_entries_batched_with_long_aliases_success() {
let mut alias;
while !imported_key_aliases.is_empty() {
let key_descriptors =
- keystore2.listEntriesBatched(Domain::APP, -1, start_past_alias).unwrap();
+ sl.keystore2.listEntriesBatched(Domain::APP, -1, start_past_alias).unwrap();
// Check retrieved key entries list is a subset of imported keys list.
assert!(key_descriptors
@@ -306,9 +299,9 @@ fn keystore2_list_entries_batched_with_long_aliases_success() {
}
assert!(imported_key_aliases.is_empty());
- delete_all_entries(&keystore2);
+ delete_all_entries(&sl.keystore2);
assert_eq!(
- keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ sl.keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
0,
"Error while doing cleanup"
);
@@ -339,25 +332,23 @@ fn keystore2_list_entries_batched_with_multi_procs_success() {
// SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
// Make sure there are no keystore entries exist before adding new entries.
- delete_all_entries(&keystore2);
+ delete_all_entries(&sl.keystore2);
// Import 3 keys with below aliases -
// [key_test_batch_list_1, key_test_batch_list_2, key_test_batch_list_3]
let imported_key_aliases =
- key_generations::import_aes_keys(&sec_level, ALIAS_PREFIX.to_string(), 1..4)
- .unwrap();
+ key_generations::import_aes_keys(&sl, ALIAS_PREFIX.to_string(), 1..4).unwrap();
assert_eq!(
- keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ sl.keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
3,
"Error while importing keys"
);
// List all entries in keystore for this user-id.
- let key_descriptors = keystore2.listEntriesBatched(Domain::APP, -1, None).unwrap();
+ let key_descriptors = sl.keystore2.listEntriesBatched(Domain::APP, -1, None).unwrap();
assert_eq!(key_descriptors.len(), 3);
// Makes sure all listed aliases are matching with imported keys aliases.
@@ -370,20 +361,18 @@ fn keystore2_list_entries_batched_with_multi_procs_success() {
// SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
// Import another 5 keys with below aliases -
// [ key_test_batch_list_4, key_test_batch_list_5, key_test_batch_list_6,
// key_test_batch_list_7, key_test_batch_list_8 ]
let mut imported_key_aliases =
- key_generations::import_aes_keys(&sec_level, ALIAS_PREFIX.to_string(), 4..9)
- .unwrap();
+ key_generations::import_aes_keys(&sl, ALIAS_PREFIX.to_string(), 4..9).unwrap();
// Above context already 3 keys are imported, in this context 5 keys are imported,
// total 8 keystore entries are expected to be present in Keystore for this user-id.
assert_eq!(
- keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ sl.keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
8,
"Error while importing keys"
);
@@ -391,7 +380,8 @@ fn keystore2_list_entries_batched_with_multi_procs_success() {
// List keystore entries with `start_past_alias` as "key_test_batch_list_3".
// `listEntriesBatched` should list all the keystore entries with
// alias > "key_test_batch_list_3".
- let key_descriptors = keystore2
+ let key_descriptors = sl
+ .keystore2
.listEntriesBatched(Domain::APP, -1, Some("key_test_batch_list_3"))
.unwrap();
assert_eq!(key_descriptors.len(), 5);
@@ -403,7 +393,7 @@ fn keystore2_list_entries_batched_with_multi_procs_success() {
// List all keystore entries with `start_past_alias` as `None`.
// `listEntriesBatched` should list all the keystore entries.
- let key_descriptors = keystore2.listEntriesBatched(Domain::APP, -1, None).unwrap();
+ let key_descriptors = sl.keystore2.listEntriesBatched(Domain::APP, -1, None).unwrap();
assert_eq!(key_descriptors.len(), 8);
// Include previously imported keys aliases as well
@@ -416,9 +406,9 @@ fn keystore2_list_entries_batched_with_multi_procs_success() {
.iter()
.all(|key| imported_key_aliases.contains(key.alias.as_ref().unwrap())));
- delete_all_entries(&keystore2);
+ delete_all_entries(&sl.keystore2);
assert_eq!(
- keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ sl.keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
0,
"Error while doing cleanup"
);
@@ -459,23 +449,23 @@ fn keystore2_list_entries_batched_with_empty_keystore_success() {
/// Test should successfully list the imported key.
#[test]
fn keystore2_list_entries_batched_with_selinux_domain_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "test_selinux_key_list_alias_batched";
- let _result = keystore2.deleteKey(&KeyDescriptor {
+ let _result = sl.keystore2.deleteKey(&KeyDescriptor {
domain: Domain::SELINUX,
nspace: key_generations::SELINUX_SHELL_NAMESPACE,
alias: Some(alias.to_string()),
blob: None,
});
- let initial_count = keystore2
+ let initial_count = sl
+ .keystore2
.getNumberOfEntries(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE)
.unwrap();
key_generations::import_aes_key(
- &sec_level,
+ &sl,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias.to_string()),
@@ -483,14 +473,15 @@ fn keystore2_list_entries_batched_with_selinux_domain_success() {
.unwrap();
assert_eq!(
- keystore2
+ sl.keystore2
.getNumberOfEntries(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE)
.unwrap(),
initial_count + 1,
"Error while getting number of keystore entries accessible."
);
- let key_descriptors = keystore2
+ let key_descriptors = sl
+ .keystore2
.listEntriesBatched(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE, None)
.unwrap();
assert_eq!(key_descriptors.len(), (initial_count + 1) as usize);
@@ -499,7 +490,7 @@ fn keystore2_list_entries_batched_with_selinux_domain_success() {
key_descriptors.into_iter().map(|key| key.alias.unwrap()).filter(|a| a == alias).count();
assert_eq!(count, 1);
- keystore2
+ sl.keystore2
.deleteKey(&KeyDescriptor {
domain: Domain::SELINUX,
nspace: key_generations::SELINUX_SHELL_NAMESPACE,
@@ -522,11 +513,10 @@ fn keystore2_list_entries_batched_validate_count_and_order_success() {
// SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
// Make sure there are no keystore entries exist before adding new entries.
- delete_all_entries(&keystore2);
+ delete_all_entries(&sl.keystore2);
// Import keys with below mentioned aliases -
// [
@@ -542,48 +532,49 @@ fn keystore2_list_entries_batched_validate_count_and_order_success() {
// key_test_batch_list_22,
// ]
let _imported_key_aliases =
- key_generations::import_aes_keys(&sec_level, ALIAS_PREFIX.to_string(), 1..6)
- .unwrap();
+ key_generations::import_aes_keys(&sl, ALIAS_PREFIX.to_string(), 1..6).unwrap();
assert_eq!(
- keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ sl.keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
5,
"Error while importing keys"
);
let _imported_key_aliases =
- key_generations::import_aes_keys(&sec_level, ALIAS_PREFIX.to_string(), 10..13)
- .unwrap();
+ key_generations::import_aes_keys(&sl, ALIAS_PREFIX.to_string(), 10..13).unwrap();
assert_eq!(
- keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ sl.keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
8,
"Error while importing keys"
);
let _imported_key_aliases =
- key_generations::import_aes_keys(&sec_level, ALIAS_PREFIX.to_string(), 21..23)
- .unwrap();
+ key_generations::import_aes_keys(&sl, ALIAS_PREFIX.to_string(), 21..23).unwrap();
assert_eq!(
- keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ sl.keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
10,
"Error while importing keys"
);
// List the aliases using given `startingPastAlias` and verify the listed
// aliases with the expected list of aliases.
- verify_aliases(&keystore2, Some(format!("{}{}", ALIAS_PREFIX, "_5").as_str()), vec![]);
+ verify_aliases(
+ &sl.keystore2,
+ Some(format!("{}{}", ALIAS_PREFIX, "_5").as_str()),
+ vec![],
+ );
verify_aliases(
- &keystore2,
+ &sl.keystore2,
Some(format!("{}{}", ALIAS_PREFIX, "_4").as_str()),
vec![ALIAS_PREFIX.to_owned() + "_5"],
);
verify_aliases(
- &keystore2,
+ &sl.keystore2,
Some(format!("{}{}", ALIAS_PREFIX, "_3").as_str()),
vec![ALIAS_PREFIX.to_owned() + "_4", ALIAS_PREFIX.to_owned() + "_5"],
);
verify_aliases(
- &keystore2,
+ &sl.keystore2,
Some(format!("{}{}", ALIAS_PREFIX, "_2").as_str()),
vec![
ALIAS_PREFIX.to_owned() + "_21",
@@ -595,7 +586,7 @@ fn keystore2_list_entries_batched_validate_count_and_order_success() {
);
verify_aliases(
- &keystore2,
+ &sl.keystore2,
Some(format!("{}{}", ALIAS_PREFIX, "_1").as_str()),
vec![
ALIAS_PREFIX.to_owned() + "_10",
@@ -611,7 +602,7 @@ fn keystore2_list_entries_batched_validate_count_and_order_success() {
);
verify_aliases(
- &keystore2,
+ &sl.keystore2,
Some(ALIAS_PREFIX),
vec![
ALIAS_PREFIX.to_owned() + "_1",
@@ -628,7 +619,7 @@ fn keystore2_list_entries_batched_validate_count_and_order_success() {
);
verify_aliases(
- &keystore2,
+ &sl.keystore2,
None,
vec![
ALIAS_PREFIX.to_owned() + "_1",
diff --git a/keystore2/tests/keystore2_client_operation_tests.rs b/keystore2/tests/keystore2_client_operation_tests.rs
index 89b5a319..95888a66 100644
--- a/keystore2/tests/keystore2_client_operation_tests.rs
+++ b/keystore2/tests/keystore2_client_operation_tests.rs
@@ -12,27 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::{getuid, Gid, Uid};
-use rustutils::users::AID_USER_OFFSET;
-use std::thread;
-use std::thread::JoinHandle;
-
+use crate::keystore2_client_test_utils::{
+ create_signing_operation, execute_op_run_as_child, perform_sample_sign_operation,
+ BarrierReached, ForcedOp, TestOutcome,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Digest::Digest, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+ Digest::Digest, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
};
use android_system_keystore2::aidl::android::system::keystore2::{
CreateOperationResponse::CreateOperationResponse, Domain::Domain,
IKeystoreOperation::IKeystoreOperation, ResponseCode::ResponseCode,
};
-
use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
-};
-
-use crate::keystore2_client_test_utils::{
- create_signing_operation, execute_op_run_as_child, perform_sample_sign_operation,
- BarrierReached, ForcedOp, TestOutcome,
+ authorizations, key_generations, key_generations::Error, run_as, SecLevel,
};
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+use std::thread;
+use std::thread::JoinHandle;
/// Create `max_ops` number child processes with the given context and perform an operation under each
/// child process.
@@ -312,11 +309,10 @@ fn keystore2_ops_prune_test() {
child_handle.recv();
// Generate a key to use in below operations.
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_prune_op_test_key_{}", getuid());
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias),
@@ -327,7 +323,7 @@ fn keystore2_ops_prune_test() {
// Create multiple operations in this process to trigger cannibalizing sibling operations.
let mut ops: Vec<binder::Result<CreateOperationResponse>> = (0..MAX_OPS)
.map(|_| {
- sec_level.createOperation(
+ sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new()
.purpose(KeyPurpose::SIGN)
@@ -353,7 +349,7 @@ fn keystore2_ops_prune_test() {
// Create a new operation, it should trigger to cannibalize one of their own sibling
// operations.
ops.push(
- sec_level.createOperation(
+ sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new()
.purpose(KeyPurpose::SIGN)
diff --git a/keystore2/tests/keystore2_client_rsa_key_tests.rs b/keystore2/tests/keystore2_client_rsa_key_tests.rs
index ad176a48..15590087 100644
--- a/keystore2/tests/keystore2_client_rsa_key_tests.rs
+++ b/keystore2/tests/keystore2_client_rsa_key_tests.rs
@@ -12,20 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use crate::keystore2_client_test_utils::{delete_app_key, perform_sample_sign_operation, ForcedOp};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Digest::Digest, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
- SecurityLevel::SecurityLevel,
};
use android_system_keystore2::aidl::android::system::keystore2::{
CreateOperationResponse::CreateOperationResponse, Domain::Domain,
- IKeystoreSecurityLevel::IKeystoreSecurityLevel,
-};
-
-use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error,
};
-
-use crate::keystore2_client_test_utils::{delete_app_key, perform_sample_sign_operation, ForcedOp};
+use keystore2_test_utils::{authorizations, key_generations, key_generations::Error, SecLevel};
/// This macro is used for creating signing key operation tests using digests and paddings
/// for various key sizes.
@@ -77,7 +71,7 @@ macro_rules! test_rsa_encrypt_key_op {
/// Generate a RSA key and create an operation using the generated key.
fn create_rsa_key_and_operation(
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
domain: Domain,
nspace: i64,
alias: Option<String>,
@@ -86,7 +80,7 @@ fn create_rsa_key_and_operation(
forced_op: ForcedOp,
) -> binder::Result<CreateOperationResponse> {
let key_metadata =
- key_generations::generate_rsa_key(sec_level, domain, nspace, alias, key_params, None)?;
+ key_generations::generate_rsa_key(sl, domain, nspace, alias, key_params, None)?;
let mut op_params = authorizations::AuthSetBuilder::new().purpose(op_purpose);
@@ -103,7 +97,7 @@ fn create_rsa_key_and_operation(
op_params = op_params.block_mode(value)
}
- sec_level.createOperation(&key_metadata.key, &op_params, forced_op.0)
+ sl.binder.createOperation(&key_metadata.key, &op_params, forced_op.0)
}
/// Generate RSA signing key with given parameters and perform signing operation.
@@ -113,11 +107,10 @@ fn perform_rsa_sign_key_op_success(
alias: &str,
padding: PaddingMode,
) {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let op_response = create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -143,17 +136,16 @@ fn perform_rsa_sign_key_op_success(
))
);
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate RSA signing key with given parameters and try to perform signing operation.
/// Error `INCOMPATIBLE_DIGEST | UNKNOWN_ERROR` is expected while creating an opearation.
fn perform_rsa_sign_key_op_failure(digest: Digest, alias: &str, padding: PaddingMode) {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -176,7 +168,7 @@ fn perform_rsa_sign_key_op_failure(digest: Digest, alias: &str, padding: Padding
e == Error::Km(ErrorCode::UNKNOWN_ERROR) || e == Error::Km(ErrorCode::INCOMPATIBLE_DIGEST)
);
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
/// Generate RSA encrypt/decrypt key with given parameters and perform decrypt operation.
@@ -187,11 +179,10 @@ fn create_rsa_encrypt_decrypt_key_op_success(
padding: PaddingMode,
mgf_digest: Option<Digest>,
) {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let result = create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -210,7 +201,7 @@ fn create_rsa_encrypt_decrypt_key_op_success(
assert!(result.is_ok());
- delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&sl.keystore2, alias).unwrap();
}
// Below macros generate tests for generating RSA signing keys with -
@@ -1533,12 +1524,11 @@ test_rsa_encrypt_key_op!(
/// `INCOMPATIBLE_DIGEST` error code.
#[test]
fn keystore2_rsa_generate_signing_key_padding_pss_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_rsa_pss_none_key_op_test";
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1565,12 +1555,11 @@ fn keystore2_rsa_generate_signing_key_padding_pss_fail() {
/// with an error code `INCOMPATIBLE_DIGEST`.
#[test]
fn keystore2_rsa_generate_key_with_oaep_padding_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_rsa_key_oaep_padding_fail_test";
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1596,12 +1585,11 @@ fn keystore2_rsa_generate_key_with_oaep_padding_fail() {
/// `UNSUPPORTED_PADDING_MODE`.
#[test]
fn keystore2_rsa_generate_keys() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_rsa_key_unsupport_padding_test";
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1625,12 +1613,11 @@ fn keystore2_rsa_generate_keys() {
/// `INCOMPATIBLE_PURPOSE` is expected as the generated key doesn't support sign operation.
#[test]
fn keystore2_rsa_encrypt_key_op_invalid_purpose() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_rsa_test_key_1";
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1654,12 +1641,11 @@ fn keystore2_rsa_encrypt_key_op_invalid_purpose() {
/// `INCOMPATIBLE_PURPOSE` is expected as the generated key doesn't support decrypt operation.
#[test]
fn keystore2_rsa_sign_key_op_invalid_purpose() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_rsa_test_key_2";
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1683,12 +1669,11 @@ fn keystore2_rsa_sign_key_op_invalid_purpose() {
/// generated key, an error `UNSUPPORTED_PURPOSE` is expected as RSA doesn't support AGREE_KEY.
#[test]
fn keystore2_rsa_key_unsupported_purpose() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_rsa_key_test_3";
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1713,14 +1698,13 @@ fn keystore2_rsa_key_unsupported_purpose() {
/// mode.
#[test]
fn keystore2_rsa_encrypt_key_unsupported_padding() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let paddings = [PaddingMode::RSA_PKCS1_1_5_SIGN, PaddingMode::RSA_PSS];
for padding in paddings {
let alias = format!("ks_rsa_encrypt_key_unsupported_pad_test{}", padding.0);
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1746,14 +1730,13 @@ fn keystore2_rsa_encrypt_key_unsupported_padding() {
/// unsupported padding mode.
#[test]
fn keystore2_rsa_signing_key_unsupported_padding() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let paddings = [PaddingMode::RSA_PKCS1_1_5_ENCRYPT, PaddingMode::RSA_OAEP];
for padding in paddings {
let alias = format!("ks_rsa_sign_key_unsupported_pad_test_4_{}", padding.0);
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1779,12 +1762,11 @@ fn keystore2_rsa_signing_key_unsupported_padding() {
/// with RSA key.
#[test]
fn keystore2_rsa_key_unsupported_op() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_rsa_key_test_5";
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1810,12 +1792,11 @@ fn keystore2_rsa_key_unsupported_op() {
/// generated with decrypt purpose.
#[test]
fn keystore2_rsa_key_missing_purpose() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_rsa_key_test_6";
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1840,12 +1821,11 @@ fn keystore2_rsa_key_missing_purpose() {
/// operation with generated key, unsupported digest error is expected.
#[test]
fn keystore2_rsa_gen_keys_with_oaep_paddings_without_digest() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_rsa_key_padding_fail";
let result = key_generations::map_ks_error(create_rsa_key_and_operation(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
@@ -1869,12 +1849,11 @@ fn keystore2_rsa_gen_keys_with_oaep_paddings_without_digest() {
/// Generate RSA keys with unsupported key size, an error `UNSUPPORTED_KEY_SIZE` is expected.
#[test]
fn keystore2_rsa_gen_keys_unsupported_size() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = "ks_rsa_key_padding_fail";
let result = key_generations::map_ks_error(key_generations::generate_rsa_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias.to_string()),
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
index f270297c..d4ca2ae9 100644
--- a/keystore2/tests/keystore2_client_test_utils.rs
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -12,29 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::{Gid, Uid};
-use serde::{Deserialize, Serialize};
-
-use std::path::PathBuf;
-use std::process::{Command, Output};
-
-use openssl::bn::BigNum;
-use openssl::encrypt::Encrypter;
-use openssl::error::ErrorStack;
-use openssl::hash::MessageDigest;
-use openssl::nid::Nid;
-use openssl::pkey::PKey;
-use openssl::pkey::Public;
-use openssl::rsa::Padding;
-use openssl::sign::Verifier;
-use openssl::x509::X509;
-
-use binder::wait_for_interface;
-
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
BlockMode::BlockMode, Digest::Digest, ErrorCode::ErrorCode,
KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
- SecurityLevel::SecurityLevel, Tag::Tag,
+ Tag::Tag,
};
use android_system_keystore2::aidl::android::system::keystore2::{
CreateOperationResponse::CreateOperationResponse, Domain::Domain,
@@ -42,12 +23,25 @@ use android_system_keystore2::aidl::android::system::keystore2::{
IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyMetadata::KeyMetadata,
KeyParameters::KeyParameters, ResponseCode::ResponseCode,
};
-
-use packagemanager_aidl::aidl::android::content::pm::IPackageManagerNative::IPackageManagerNative;
-
+use binder::wait_for_interface;
use keystore2_test_utils::{
- authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
+ authorizations, key_generations, key_generations::Error, run_as, SecLevel,
};
+use nix::unistd::{Gid, Uid};
+use openssl::bn::BigNum;
+use openssl::encrypt::Encrypter;
+use openssl::error::ErrorStack;
+use openssl::hash::MessageDigest;
+use openssl::nid::Nid;
+use openssl::pkey::PKey;
+use openssl::pkey::Public;
+use openssl::rsa::Padding;
+use openssl::sign::Verifier;
+use openssl::x509::X509;
+use packagemanager_aidl::aidl::android::content::pm::IPackageManagerNative::IPackageManagerNative;
+use serde::{Deserialize, Serialize};
+use std::path::PathBuf;
+use std::process::{Command, Output};
/// This enum is used to communicate between parent and child processes.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
@@ -70,8 +64,9 @@ pub struct ForcedOp(pub bool);
pub const SAMPLE_PLAIN_TEXT: &[u8] = b"my message 11111";
pub const PACKAGE_MANAGER_NATIVE_SERVICE: &str = "package_native";
-pub const APP_ATTEST_KEY_FEATURE: &str = "android.hardware.keystore.app_attest_key";
-pub const DEVICE_ID_ATTESTATION_FEATURE: &str = "android.software.device_id_attestation";
+const APP_ATTEST_KEY_FEATURE: &str = "android.hardware.keystore.app_attest_key";
+const DEVICE_ID_ATTESTATION_FEATURE: &str = "android.software.device_id_attestation";
+const STRONGBOX_KEYSTORE_FEATURE: &str = "android.hardware.strongbox_keystore";
/// Determines whether app_attest_key_feature is supported or not.
pub fn app_attest_key_feature_exists() -> bool {
@@ -89,20 +84,26 @@ pub fn device_id_attestation_feature_exists() -> bool {
pm.hasSystemFeature(DEVICE_ID_ATTESTATION_FEATURE, 0).expect("hasSystemFeature failed.")
}
+/// Determines whether device-unique attestation might be supported by StrongBox.
+pub fn skip_device_unique_attestation_tests() -> bool {
+ let pm = wait_for_interface::<dyn IPackageManagerNative>(PACKAGE_MANAGER_NATIVE_SERVICE)
+ .expect("Failed to get package manager native service.");
+
+ // Device unique attestation was first included in Keymaster 4.1.
+ !pm.hasSystemFeature(STRONGBOX_KEYSTORE_FEATURE, 41).expect("hasSystemFeature failed.")
+}
+
/// Determines whether to skip device id attestation tests on GSI build with API level < 34.
pub fn skip_device_id_attest_tests() -> bool {
// b/298586194, there are some devices launched with Android T, and they will be receiving
// only system update and not vendor update, newly added attestation properties
// (ro.product.*_for_attestation) reading logic would not be available for such devices
// hence skipping this test for such scenario.
- let api_level = std::str::from_utf8(&get_system_prop("ro.board.first_api_level"))
- .unwrap()
- .parse::<i32>()
- .unwrap();
+
// This file is only present on GSI builds.
- let path_buf = PathBuf::from("/system/system_ext/etc/init/init.gsi.rc");
+ let gsi_marker = PathBuf::from("/system/system_ext/etc/init/init.gsi.rc");
- api_level < 34 && path_buf.as_path().is_file()
+ get_vsr_api_level() < 34 && gsi_marker.as_path().is_file()
}
#[macro_export]
@@ -133,9 +134,9 @@ macro_rules! skip_device_id_attestation_tests {
}
#[macro_export]
-macro_rules! skip_tests_if_keymaster_impl_present {
- () => {
- if !key_generations::has_default_keymint() {
+macro_rules! require_keymint {
+ ($sl:ident) => {
+ if !$sl.is_keymint() {
return;
}
};
@@ -144,19 +145,18 @@ macro_rules! skip_tests_if_keymaster_impl_present {
/// Generate EC key and grant it to the list of users with given access vector.
/// Returns the list of granted keys `nspace` values in the order of given grantee uids.
pub fn generate_ec_key_and_grant_to_users(
- keystore2: &binder::Strong<dyn IKeystoreService>,
- sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ sl: &SecLevel,
alias: Option<String>,
grantee_uids: Vec<i32>,
access_vector: i32,
) -> Result<Vec<i64>, binder::Status> {
let key_metadata =
- key_generations::generate_ec_p256_signing_key(sec_level, Domain::APP, -1, alias, None)?;
+ key_generations::generate_ec_p256_signing_key(sl, Domain::APP, -1, alias, None)?;
let mut granted_keys = Vec::new();
for uid in grantee_uids {
- let granted_key = keystore2.grant(&key_metadata.key, uid, access_vector)?;
+ let granted_key = sl.keystore2.grant(&key_metadata.key, uid, access_vector)?;
assert_eq!(granted_key.domain, Domain::GRANT);
granted_keys.push(granted_key.nspace);
}
@@ -174,14 +174,12 @@ pub fn create_signing_operation(
nspace: i64,
alias: Option<String>,
) -> binder::Result<CreateOperationResponse> {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let key_metadata =
- key_generations::generate_ec_p256_signing_key(&sec_level, domain, nspace, alias, None)
- .unwrap();
+ key_generations::generate_ec_p256_signing_key(&sl, domain, nspace, alias, None).unwrap();
- sec_level.createOperation(
+ sl.binder.createOperation(
&key_metadata.key,
&authorizations::AuthSetBuilder::new().purpose(op_purpose).digest(op_digest),
forced_op.0,
@@ -514,15 +512,38 @@ pub fn get_system_prop(name: &str) -> Vec<u8> {
}
}
+fn get_integer_system_prop(name: &str) -> Option<i32> {
+ let val = get_system_prop(name);
+ if val.is_empty() {
+ return None;
+ }
+ let val = std::str::from_utf8(&val).ok()?;
+ val.parse::<i32>().ok()
+}
+
+pub fn get_vsr_api_level() -> i32 {
+ if let Some(api_level) = get_integer_system_prop("ro.vendor.api_level") {
+ return api_level;
+ }
+
+ let vendor_api_level = get_integer_system_prop("ro.board.api_level")
+ .or_else(|| get_integer_system_prop("ro.board.first_api_level"));
+ let product_api_level = get_integer_system_prop("ro.product.first_api_level")
+ .or_else(|| get_integer_system_prop("ro.build.version.sdk"));
+
+ match (vendor_api_level, product_api_level) {
+ (Some(v), Some(p)) => std::cmp::min(v, p),
+ (Some(v), None) => v,
+ (None, Some(p)) => p,
+ _ => panic!("Could not determine VSR API level"),
+ }
+}
+
/// Determines whether the SECOND-IMEI can be used as device attest-id.
pub fn is_second_imei_id_attestation_required(
keystore2: &binder::Strong<dyn IKeystoreService>,
) -> bool {
- let api_level = std::str::from_utf8(&get_system_prop("ro.vendor.api_level"))
- .unwrap()
- .parse::<i32>()
- .unwrap();
- keystore2.getInterfaceVersion().unwrap() >= 3 && api_level > 33
+ keystore2.getInterfaceVersion().unwrap() >= 3 && get_vsr_api_level() > 33
}
/// Run a service command and collect the output.
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index a0c140a0..34ba81f7 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// TODO: rename modules to strip text repeated from crate name ("keystore2_client_" and "_tests").
pub mod keystore2_client_3des_key_tests;
pub mod keystore2_client_aes_key_tests;
pub mod keystore2_client_attest_key_tests;
@@ -30,3 +31,5 @@ pub mod keystore2_client_operation_tests;
pub mod keystore2_client_rsa_key_tests;
pub mod keystore2_client_test_utils;
pub mod keystore2_client_update_subcomponent_tests;
+
+pub mod user_auth;
diff --git a/keystore2/tests/keystore2_client_update_subcomponent_tests.rs b/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
index d9576a84..e25e52a2 100644
--- a/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
+++ b/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
@@ -12,9 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::{getuid, Gid, Uid};
-use rustutils::users::AID_USER_OFFSET;
-
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
ErrorCode::ErrorCode, SecurityLevel::SecurityLevel,
};
@@ -22,8 +19,11 @@ use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
ResponseCode::ResponseCode,
};
-
-use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error, run_as};
+use keystore2_test_utils::{
+ get_keystore_service, key_generations, key_generations::Error, run_as, SecLevel,
+};
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
/// Generate a key and update its public certificate and certificate chain. Test should be able to
/// load the key and able to verify whether its certificate and cert-chain are updated successfully.
@@ -31,11 +31,10 @@ use keystore2_test_utils::{get_keystore_service, key_generations, key_generation
fn keystore2_update_subcomponent_success() {
let alias = "update_subcomponent_success_key";
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias.to_string()),
@@ -46,11 +45,11 @@ fn keystore2_update_subcomponent_success() {
let other_cert: [u8; 32] = [123; 32];
let other_cert_chain: [u8; 32] = [12; 32];
- keystore2
+ sl.keystore2
.updateSubcomponent(&key_metadata.key, Some(&other_cert), Some(&other_cert_chain))
.expect("updateSubcomponent should have succeeded.");
- let key_entry_response = keystore2.getKeyEntry(&key_metadata.key).unwrap();
+ let key_entry_response = sl.keystore2.getKeyEntry(&key_metadata.key).unwrap();
assert_eq!(Some(other_cert.to_vec()), key_entry_response.metadata.certificate);
assert_eq!(Some(other_cert_chain.to_vec()), key_entry_response.metadata.certificateChain);
}
@@ -170,13 +169,12 @@ fn keystore2_update_subcomponent_fails_permission_denied() {
// SAFETY: The test is run in a separate process with no other threads.
let mut granted_keys = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let sl = SecLevel::tee();
let alias = format!("ks_update_subcompo_test_1_{}", getuid());
let mut granted_keys = Vec::new();
let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
+ &sl,
Domain::APP,
-1,
Some(alias),
@@ -186,7 +184,8 @@ fn keystore2_update_subcomponent_fails_permission_denied() {
// Grant a key without update permission.
let access_vector = KeyPermission::GET_INFO.0;
- let granted_key = keystore2
+ let granted_key = sl
+ .keystore2
.grant(&key_metadata.key, GRANTEE_1_UID.try_into().unwrap(), access_vector)
.unwrap();
assert_eq!(granted_key.domain, Domain::GRANT);
@@ -194,7 +193,8 @@ fn keystore2_update_subcomponent_fails_permission_denied() {
// Grant a key with update permission.
let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::UPDATE.0;
- let granted_key = keystore2
+ let granted_key = sl
+ .keystore2
.grant(&key_metadata.key, GRANTEE_2_UID.try_into().unwrap(), access_vector)
.unwrap();
assert_eq!(granted_key.domain, Domain::GRANT);
diff --git a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
index 0335159a..11a4c0b1 100644
--- a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
+++ b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
@@ -12,40 +12,35 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::{getuid, Gid, Uid};
-use rustutils::users::AID_USER_OFFSET;
-use serde::{Deserialize, Serialize};
-
-use std::ops::Deref;
-use std::path::PathBuf;
-
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel;
-
-use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, KeyDescriptor::KeyDescriptor,
-};
-
-use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::IKeystoreMaintenance;
-
use android_security_authorization::aidl::android::security::authorization::{
IKeystoreAuthorization::IKeystoreAuthorization,
};
-
+use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::IKeystoreMaintenance;
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
use keystore2::key_parameter::KeyParameter as KsKeyparameter;
use keystore2::legacy_blob::test_utils::legacy_blob_test_vectors::*;
use keystore2::legacy_blob::test_utils::*;
use keystore2::legacy_blob::LegacyKeyCharacteristics;
use keystore2::utils::AesGcm;
use keystore2_crypto::{Password, ZVec};
-
-use keystore2_test_utils::get_keystore_service;
-use keystore2_test_utils::key_generations;
-use keystore2_test_utils::run_as;
+use keystore2_test_utils::{get_keystore_service, key_generations, run_as, SecLevel};
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+use serde::{Deserialize, Serialize};
+use std::ops::Deref;
+use std::path::PathBuf;
static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
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()
}
@@ -156,19 +151,16 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
println!("onUserRemoved error: {:#?}", e);
}
}
+ let sl = SecLevel::tee();
- let keystore2 = get_keystore_service();
- let sec_level = keystore2
- .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,
+ &sl,
Domain::BLOB,
SELINUX_SHELL_NAMESPACE,
None,
- Some(att_challenge),
+ att_challenge,
)
.expect("Failed to generate key blob");
@@ -212,14 +204,12 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
.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 +236,7 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
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 +265,7 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
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);
@@ -410,18 +400,15 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
}
}
- let keystore2 = get_keystore_service();
- let sec_level = keystore2
- .getSecurityLevel(SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT)
- .unwrap();
+ let sl = SecLevel::tee();
// 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,
+ &sl,
Domain::BLOB,
SELINUX_SHELL_NAMESPACE,
None,
- Some(att_challenge),
+ att_challenge,
)
.expect("Failed to generate key blob");
@@ -468,15 +455,12 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
.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 +487,7 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
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 +516,7 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
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/keystore2/tests/user_auth.rs b/keystore2/tests/user_auth.rs
new file mode 100644
index 00000000..263ad565
--- /dev/null
+++ b/keystore2/tests/user_auth.rs
@@ -0,0 +1,245 @@
+// Copyright 2024, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Tests for user authentication interactions (via `IKeystoreAuthorization`).
+
+use crate::keystore2_client_test_utils::BarrierReached;
+use android_security_authorization::aidl::android::security::authorization::{
+ IKeystoreAuthorization::IKeystoreAuthorization
+};
+use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::{
+ IKeystoreMaintenance,
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel,
+ KeyPurpose::KeyPurpose
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ CreateOperationResponse::CreateOperationResponse, Domain::Domain, KeyDescriptor::KeyDescriptor,
+ KeyMetadata::KeyMetadata,
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+ Timestamp::Timestamp,
+};
+use keystore2_test_utils::{
+ get_keystore_service, run_as, authorizations::AuthSetBuilder,
+};
+use log::{warn, info};
+use nix::unistd::{Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+/// Test user ID.
+const TEST_USER_ID: i32 = 100;
+/// Fake password blob.
+static PASSWORD: &[u8] = &[
+ 0x42, 0x39, 0x30, 0x37, 0x44, 0x37, 0x32, 0x37, 0x39, 0x39, 0x43, 0x42, 0x39, 0x41, 0x42, 0x30,
+ 0x34, 0x31, 0x30, 0x38, 0x46, 0x44, 0x33, 0x45, 0x39, 0x42, 0x32, 0x38, 0x36, 0x35, 0x41, 0x36,
+ 0x33, 0x44, 0x42, 0x42, 0x43, 0x36, 0x33, 0x42, 0x34, 0x39, 0x37, 0x33, 0x35, 0x45, 0x41, 0x41,
+ 0x32, 0x45, 0x31, 0x35, 0x43, 0x43, 0x46, 0x32, 0x39, 0x36, 0x33, 0x34, 0x31, 0x32, 0x41, 0x39,
+];
+/// Fake SID value corresponding to Gatekeeper.
+static GK_SID: i64 = 123456;
+/// Fake SID value corresponding to a biometric authenticator.
+static BIO_SID1: i64 = 345678;
+/// Fake SID value corresponding to a biometric authenticator.
+static BIO_SID2: i64 = 456789;
+
+const WEAK_UNLOCK_ENABLED: bool = true;
+const WEAK_UNLOCK_DISABLED: bool = false;
+const UNFORCED: bool = false;
+
+fn get_authorization() -> binder::Strong<dyn IKeystoreAuthorization> {
+ binder::get_interface("android.security.authorization").unwrap()
+}
+
+fn get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance> {
+ binder::get_interface("android.security.maintenance").unwrap()
+}
+
+fn abort_op(result: binder::Result<CreateOperationResponse>) {
+ if let Ok(rsp) = result {
+ if let Some(op) = rsp.iOperation {
+ if let Err(e) = op.abort() {
+ warn!("abort op failed: {e:?}");
+ }
+ } else {
+ warn!("can't abort op with missing iOperation");
+ }
+ } else {
+ warn!("can't abort failed op: {result:?}");
+ }
+}
+
+/// RAII structure to ensure that test users are removed at the end of a test.
+struct TestUser {
+ id: i32,
+ maint: binder::Strong<dyn IKeystoreMaintenance>,
+}
+
+impl TestUser {
+ fn new() -> Self {
+ Self::new_user(TEST_USER_ID, PASSWORD)
+ }
+ fn new_user(user_id: i32, password: &[u8]) -> Self {
+ let maint = get_maintenance();
+ maint.onUserAdded(user_id).expect("failed to add test user");
+ maint
+ .initUserSuperKeys(user_id, password, /* allowExisting= */ false)
+ .expect("failed to init test user");
+ Self { id: user_id, maint }
+ }
+}
+
+impl Drop for TestUser {
+ fn drop(&mut self) {
+ let _ = self.maint.onUserRemoved(self.id);
+ }
+}
+
+#[test]
+fn keystore2_test_unlocked_device_required() {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore2_client_tests")
+ .with_max_level(log::LevelFilter::Debug),
+ );
+ static CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const UID: u32 = TEST_USER_ID as u32 * AID_USER_OFFSET + 1001;
+
+ // Safety: only one thread at this point, and nothing yet done with binder.
+ let mut child_handle = unsafe {
+ // Perform keystore actions while running as the test user.
+ run_as::run_as_child(
+ CTX,
+ Uid::from_raw(UID),
+ Gid::from_raw(UID),
+ move |reader, writer| -> Result<(), String> {
+ // Now we're in a new process, wait to be notified before starting.
+ reader.recv();
+
+ // Action A: create a new unlocked-device-required key (which thus requires
+ // super-encryption), while the device is unlocked.
+ let ks2 = get_keystore_service();
+ let sec_level = ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let params = AuthSetBuilder::new()
+ .unlocked_device_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256);
+
+ let KeyMetadata { key, .. } = sec_level
+ .generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some("unlocked-device-required".to_string()),
+ blob: None,
+ },
+ None,
+ &params,
+ 0,
+ b"entropy",
+ )
+ .expect("key generation failed");
+ info!("A: created unlocked-device-required key while unlocked {key:?}");
+ writer.send(&BarrierReached {}); // A done.
+
+ // Action B: fail to use the unlocked-device-required key while locked.
+ reader.recv();
+ let params =
+ AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
+ let result = sec_level.createOperation(&key, &params, UNFORCED);
+ info!("B: use unlocked-device-required key while locked => {result:?}");
+ assert!(result.is_err());
+ writer.send(&BarrierReached {}); // B done.
+
+ // Action C: try to use the unlocked-device-required key while unlocked with a
+ // password.
+ reader.recv();
+ let result = sec_level.createOperation(&key, &params, UNFORCED);
+ info!("C: use unlocked-device-required key while lskf-unlocked => {result:?}");
+ assert!(result.is_ok(), "failed with {result:?}");
+ abort_op(result);
+ writer.send(&BarrierReached {}); // C done.
+
+ // Action D: try to use the unlocked-device-required key while unlocked with a weak
+ // biometric.
+ reader.recv();
+ let result = sec_level.createOperation(&key, &params, UNFORCED);
+ info!("D: use unlocked-device-required key while weak-locked => {result:?}");
+ assert!(result.is_ok(), "createOperation failed: {result:?}");
+ abort_op(result);
+ writer.send(&BarrierReached {}); // D done.
+
+ let _ = sec_level.deleteKey(&key);
+ Ok(())
+ },
+ )
+ }
+ .unwrap();
+
+ // Now that the separate process has been forked off, it's safe to use binder.
+ let user = TestUser::new();
+ let user_id = user.id;
+ let auth_service = get_authorization();
+
+ // Lock and unlock to ensure super keys are already created.
+ auth_service.onDeviceLocked(user_id, &[BIO_SID1, BIO_SID2], WEAK_UNLOCK_DISABLED).unwrap();
+ auth_service.onDeviceUnlocked(user_id, Some(PASSWORD)).unwrap();
+ auth_service.addAuthToken(&fake_lskf_token(GK_SID)).unwrap();
+
+ info!("trigger child process action A while unlocked and wait for completion");
+ child_handle.send(&BarrierReached {});
+ child_handle.recv();
+
+ // Move to locked and don't allow weak unlock, so super keys are wiped.
+ auth_service.onDeviceLocked(user_id, &[BIO_SID1, BIO_SID2], WEAK_UNLOCK_DISABLED).unwrap();
+
+ info!("trigger child process action B while locked and wait for completion");
+ child_handle.send(&BarrierReached {});
+ child_handle.recv();
+
+ // Unlock with password => loads super key from database.
+ auth_service.onDeviceUnlocked(user_id, Some(PASSWORD)).unwrap();
+ auth_service.addAuthToken(&fake_lskf_token(GK_SID)).unwrap();
+
+ info!("trigger child process action C while lskf-unlocked and wait for completion");
+ child_handle.send(&BarrierReached {});
+ child_handle.recv();
+
+ // Move to locked and allow weak unlock, then do a weak unlock.
+ auth_service.onDeviceLocked(user_id, &[BIO_SID1, BIO_SID2], WEAK_UNLOCK_ENABLED).unwrap();
+ auth_service.onDeviceUnlocked(user_id, None).unwrap();
+
+ info!("trigger child process action D while weak-unlocked and wait for completion");
+ child_handle.send(&BarrierReached {});
+ child_handle.recv();
+
+ assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
+}
+
+/// Generate a fake [`HardwareAuthToken`] for the given sid.
+fn fake_lskf_token(gk_sid: i64) -> HardwareAuthToken {
+ HardwareAuthToken {
+ challenge: 0,
+ userId: gk_sid,
+ authenticatorId: 0,
+ authenticatorType: HardwareAuthenticatorType::PASSWORD,
+ timestamp: Timestamp { milliSeconds: 123 },
+ mac: vec![1, 2, 3],
+ }
+}
diff --git a/keystore2/watchdog/src/lib.rs b/keystore2/watchdog/src/lib.rs
index fa4620a8..ff05783a 100644
--- a/keystore2/watchdog/src/lib.rs
+++ b/keystore2/watchdog/src/lib.rs
@@ -29,6 +29,9 @@ use std::{
time::{Duration, Instant},
};
+#[cfg(test)]
+mod tests;
+
/// Represents a Watchdog record. It can be created with `Watchdog::watch` or
/// `Watchdog::watch_with`. It disarms the record when dropped.
pub struct WatchPoint {
@@ -58,59 +61,73 @@ struct Index {
struct Record {
started: Instant,
deadline: Instant,
- callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+ context: Option<Box<dyn std::fmt::Debug + Send + 'static>>,
}
struct WatchdogState {
state: State,
thread: Option<thread::JoinHandle<()>>,
- timeout: Duration,
+ /// How long to wait before dropping the watchdog thread when idle.
+ idle_timeout: Duration,
records: HashMap<Index, Record>,
- last_report: Instant,
- has_overdue: bool,
+ last_report: Option<Instant>,
+ noisy_timeout: Duration,
}
impl WatchdogState {
- fn update_overdue_and_find_next_timeout(&mut self) -> (bool, Option<Duration>) {
+ /// If we have overdue records, we want to log them but slowly backoff
+ /// so that we do not clog the logs. We start with logs every
+ /// `MIN_REPORT_TIMEOUT` sec then increment the timeout by 5 up
+ /// to a maximum of `MAX_REPORT_TIMEOUT`.
+ const MIN_REPORT_TIMEOUT: Duration = Duration::from_secs(1);
+ const MAX_REPORT_TIMEOUT: Duration = Duration::from_secs(30);
+
+ fn reset_noisy_timeout(&mut self) {
+ self.noisy_timeout = Self::MIN_REPORT_TIMEOUT;
+ }
+
+ fn update_noisy_timeout(&mut self) {
+ let noisy_update = self.noisy_timeout + Duration::from_secs(5);
+ self.noisy_timeout = min(Self::MAX_REPORT_TIMEOUT, noisy_update);
+ }
+
+ fn overdue_and_next_timeout(&self) -> (bool, Option<Duration>) {
let now = Instant::now();
let mut next_timeout: Option<Duration> = None;
let mut has_overdue = false;
for (_, r) in self.records.iter() {
let timeout = r.deadline.saturating_duration_since(now);
if timeout == Duration::new(0, 0) {
+ // This timeout has passed.
has_overdue = true;
- continue;
+ } else {
+ // This timeout is still to come; see if it's the closest one to now.
+ next_timeout = match next_timeout {
+ Some(nt) if timeout < nt => Some(timeout),
+ Some(nt) => Some(nt),
+ None => Some(timeout),
+ };
}
- next_timeout = match next_timeout {
- Some(nt) => {
- if timeout < nt {
- Some(timeout)
- } else {
- Some(nt)
- }
- }
- None => Some(timeout),
- };
}
(has_overdue, next_timeout)
}
- fn log_report(&mut self, has_overdue: bool) -> bool {
- match (self.has_overdue, has_overdue) {
- (true, true) => {
- if self.last_report.elapsed() < Watchdog::NOISY_REPORT_TIMEOUT {
- self.has_overdue = false;
- return false;
- }
- }
- (_, false) => {
- self.has_overdue = false;
- return false;
+ fn log_report(&mut self, has_overdue: bool) {
+ if !has_overdue {
+ // Nothing to report.
+ self.last_report = None;
+ return;
+ }
+ // Something to report...
+ if let Some(reported_at) = self.last_report {
+ if reported_at.elapsed() < self.noisy_timeout {
+ // .. but it's too soon since the last report.
+ self.last_report = None;
+ return;
}
- (false, true) => {}
}
- self.last_report = Instant::now();
- self.has_overdue = has_overdue;
+ self.update_noisy_timeout();
+ self.last_report = Some(Instant::now());
log::warn!("### Keystore Watchdog report - BEGIN ###");
let now = Instant::now();
@@ -149,15 +166,15 @@ impl WatchdogState {
for g in groups.iter() {
for (i, r) in g.iter() {
- match &r.callback {
- Some(cb) => {
+ match &r.context {
+ Some(ctx) => {
log::warn!(
- "{:?} {} Pending: {:?} Overdue {:?}: {}",
+ "{:?} {} Pending: {:?} Overdue {:?} for {:?}",
i.tid,
i.id,
r.started.elapsed(),
r.deadline.elapsed(),
- (cb)()
+ ctx
);
}
None => {
@@ -173,7 +190,6 @@ impl WatchdogState {
}
}
log::warn!("### Keystore Watchdog report - END ###");
- true
}
fn disarm(&mut self, index: Index) {
@@ -195,71 +211,66 @@ pub struct Watchdog {
}
impl Watchdog {
- /// If we have overdue records, we want to be noisy about it and log a report
- /// at least every `NOISY_REPORT_TIMEOUT` interval.
- const NOISY_REPORT_TIMEOUT: Duration = Duration::from_secs(1);
-
- /// Construct a [`Watchdog`]. When `timeout` has elapsed since the watchdog thread became
+ /// Construct a [`Watchdog`]. When `idle_timeout` has elapsed since the watchdog thread became
/// idle, i.e., there are no more active or overdue watch points, the watchdog thread
/// terminates.
- pub fn new(timeout: Duration) -> Arc<Self> {
+ pub fn new(idle_timeout: Duration) -> Arc<Self> {
Arc::new(Self {
state: Arc::new((
Condvar::new(),
Mutex::new(WatchdogState {
state: State::NotRunning,
thread: None,
- timeout,
+ idle_timeout,
records: HashMap::new(),
- last_report: Instant::now(),
- has_overdue: false,
+ last_report: None,
+ noisy_timeout: WatchdogState::MIN_REPORT_TIMEOUT,
}),
)),
})
}
fn watch_with_optional(
- wd: &Arc<Self>,
- callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+ wd: Arc<Self>,
+ context: Option<Box<dyn std::fmt::Debug + Send + 'static>>,
id: &'static str,
timeout: Duration,
) -> Option<WatchPoint> {
- let deadline = Instant::now().checked_add(timeout);
- if deadline.is_none() {
+ let Some(deadline) = Instant::now().checked_add(timeout) else {
log::warn!("Deadline computation failed for WatchPoint \"{}\"", id);
log::warn!("WatchPoint not armed.");
return None;
- }
- wd.arm(callback, id, deadline.unwrap());
- Some(WatchPoint { id, wd: wd.clone(), not_send: Default::default() })
+ };
+ wd.arm(context, id, deadline);
+ Some(WatchPoint { id, wd, not_send: Default::default() })
}
/// Create a new watch point. If the WatchPoint is not dropped before the timeout
/// expires, a report is logged at least every second, which includes the id string
- /// and whatever string the callback returns.
+ /// and any provided context.
pub fn watch_with(
wd: &Arc<Self>,
id: &'static str,
timeout: Duration,
- callback: impl Fn() -> String + Send + 'static,
+ context: impl std::fmt::Debug + Send + 'static,
) -> Option<WatchPoint> {
- Self::watch_with_optional(wd, Some(Box::new(callback)), id, timeout)
+ Self::watch_with_optional(wd.clone(), Some(Box::new(context)), id, timeout)
}
- /// Like `watch_with`, but without a callback.
+ /// Like `watch_with`, but without context.
pub fn watch(wd: &Arc<Self>, id: &'static str, timeout: Duration) -> Option<WatchPoint> {
- Self::watch_with_optional(wd, None, id, timeout)
+ Self::watch_with_optional(wd.clone(), None, id, timeout)
}
fn arm(
&self,
- callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+ context: Option<Box<dyn std::fmt::Debug + Send + 'static>>,
id: &'static str,
deadline: Instant,
) {
let tid = thread::current().id();
let index = Index { tid, id };
- let record = Record { started: Instant::now(), deadline, callback };
+ let record = Record { started: Instant::now(), deadline, context };
let (ref condvar, ref state) = *self.state;
@@ -297,21 +308,24 @@ impl Watchdog {
let mut state = state.lock().unwrap();
loop {
- let (has_overdue, next_timeout) = state.update_overdue_and_find_next_timeout();
+ let (has_overdue, next_timeout) = state.overdue_and_next_timeout();
state.log_report(has_overdue);
+
let (next_timeout, idle) = match (has_overdue, next_timeout) {
- (true, Some(next_timeout)) => {
- (min(next_timeout, Self::NOISY_REPORT_TIMEOUT), false)
- }
+ (true, Some(next_timeout)) => (min(next_timeout, state.noisy_timeout), false),
+ (true, None) => (state.noisy_timeout, false),
(false, Some(next_timeout)) => (next_timeout, false),
- (true, None) => (Self::NOISY_REPORT_TIMEOUT, false),
- (false, None) => (state.timeout, true),
+ (false, None) => (state.idle_timeout, true),
};
+ // Wait until the closest timeout pops, but use a condition variable so that if a
+ // new watchpoint is started in the meanwhile it will interrupt the wait so we can
+ // recalculate.
let (s, timeout) = condvar.wait_timeout(state, next_timeout).unwrap();
state = s;
if idle && timeout.timed_out() && state.records.is_empty() {
+ state.reset_noisy_timeout();
state.state = State::NotRunning;
break;
}
@@ -321,40 +335,3 @@ impl Watchdog {
state.state = State::Running;
}
}
-
-#[cfg(test)]
-mod tests {
-
- use super::*;
- use std::sync::atomic;
- use std::thread;
- use std::time::Duration;
-
- #[test]
- fn test_watchdog() {
- android_logger::init_once(
- android_logger::Config::default()
- .with_tag("keystore2_watchdog_tests")
- .with_max_level(log::LevelFilter::Debug),
- );
-
- let wd = Watchdog::new(Watchdog::NOISY_REPORT_TIMEOUT.checked_mul(3).unwrap());
- let hit_count = Arc::new(atomic::AtomicU8::new(0));
- let hit_count_clone = hit_count.clone();
- let wp =
- Watchdog::watch_with(&wd, "test_watchdog", Duration::from_millis(100), move || {
- format!("hit_count: {}", hit_count_clone.fetch_add(1, atomic::Ordering::Relaxed))
- });
- assert_eq!(0, hit_count.load(atomic::Ordering::Relaxed));
- thread::sleep(Duration::from_millis(500));
- assert_eq!(1, hit_count.load(atomic::Ordering::Relaxed));
- thread::sleep(Watchdog::NOISY_REPORT_TIMEOUT);
- assert_eq!(2, hit_count.load(atomic::Ordering::Relaxed));
- drop(wp);
- thread::sleep(Watchdog::NOISY_REPORT_TIMEOUT.checked_mul(4).unwrap());
- assert_eq!(2, hit_count.load(atomic::Ordering::Relaxed));
- let (_, ref state) = *wd.state;
- let state = state.lock().unwrap();
- assert_eq!(state.state, State::NotRunning);
- }
-}
diff --git a/keystore2/watchdog/src/tests.rs b/keystore2/watchdog/src/tests.rs
new file mode 100644
index 00000000..d35c0dde
--- /dev/null
+++ b/keystore2/watchdog/src/tests.rs
@@ -0,0 +1,86 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Watchdog tests.
+
+use super::*;
+use std::sync::atomic;
+use std::thread;
+use std::time::Duration;
+
+/// Count the number of times `Debug::fmt` is invoked.
+#[derive(Default, Clone)]
+struct DebugCounter(Arc<atomic::AtomicU8>);
+impl DebugCounter {
+ fn value(&self) -> u8 {
+ self.0.load(atomic::Ordering::Relaxed)
+ }
+}
+impl std::fmt::Debug for DebugCounter {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ let count = self.0.fetch_add(1, atomic::Ordering::Relaxed);
+ write!(f, "hit_count: {count}")
+ }
+}
+
+#[test]
+fn test_watchdog() {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore2_watchdog_tests")
+ .with_max_level(log::LevelFilter::Debug),
+ );
+
+ let wd = Watchdog::new(Duration::from_secs(3));
+ let hit_counter = DebugCounter::default();
+ let wp =
+ Watchdog::watch_with(&wd, "test_watchdog", Duration::from_millis(100), hit_counter.clone());
+ assert_eq!(0, hit_counter.value());
+ thread::sleep(Duration::from_millis(500));
+ assert_eq!(1, hit_counter.value());
+ thread::sleep(Duration::from_secs(1));
+ assert_eq!(1, hit_counter.value());
+
+ drop(wp);
+ thread::sleep(Duration::from_secs(10));
+ assert_eq!(1, hit_counter.value());
+ let (_, ref state) = *wd.state;
+ let state = state.lock().unwrap();
+ assert_eq!(state.state, State::NotRunning);
+}
+
+#[test]
+fn test_watchdog_backoff() {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore2_watchdog_tests")
+ .with_max_level(log::LevelFilter::Debug),
+ );
+
+ let wd = Watchdog::new(Duration::from_secs(3));
+ let hit_counter = DebugCounter::default();
+ let wp =
+ Watchdog::watch_with(&wd, "test_watchdog", Duration::from_millis(100), hit_counter.clone());
+ assert_eq!(0, hit_counter.value());
+ thread::sleep(Duration::from_millis(500));
+ assert_eq!(1, hit_counter.value());
+ thread::sleep(Duration::from_secs(6));
+ assert_eq!(2, hit_counter.value());
+ thread::sleep(Duration::from_secs(11));
+ assert_eq!(3, hit_counter.value());
+
+ drop(wp);
+ thread::sleep(Duration::from_secs(4));
+ assert_eq!(3, hit_counter.value());
+}