[automerger skipped] Use static libraries for multilib VTS am: 906fc75e3e -s ours
am skip reason: Merged-In I4bae1255764b1687e5db6aec799057ac5612c200 with SHA-1 76311aab2a is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/security/+/29069576
Change-Id: I6a58ffbdab0c36ed69574ce24f2dead7337ca2fa
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/keystore/keystore_attestation_id.cpp b/keystore/keystore_attestation_id.cpp
index bcd3318..c91f86f 100644
--- a/keystore/keystore_attestation_id.cpp
+++ b/keystore/keystore_attestation_id.cpp
@@ -21,6 +21,7 @@
#include <log/log.h>
#include <memory>
+#include <mutex>
#include <string>
#include <vector>
@@ -51,6 +52,7 @@
constexpr const char* kAttestationSystemPackageName = "AndroidSystem";
constexpr const size_t kMaxAttempts = 3;
constexpr const unsigned long kRetryIntervalUsecs = 500000; // sleep for 500 ms
+constexpr const char* kProviderServiceName = "sec_key_att_app_id_provider";
std::vector<uint8_t> signature2SHA256(const security::keystore::Signature& sig) {
std::vector<uint8_t> digest_buffer(SHA256_DIGEST_LENGTH);
@@ -61,24 +63,24 @@
using ::aidl::android::system::keystore2::ResponseCode;
using ::android::security::keystore::BpKeyAttestationApplicationIdProvider;
-class KeyAttestationApplicationIdProvider : public BpKeyAttestationApplicationIdProvider {
- public:
- KeyAttestationApplicationIdProvider();
+[[clang::no_destroy]] std::mutex gServiceMu;
+[[clang::no_destroy]] std::shared_ptr<BpKeyAttestationApplicationIdProvider>
+ gService; // GUARDED_BY gServiceMu
- static KeyAttestationApplicationIdProvider& get();
-
- private:
- android::sp<android::IServiceManager> service_manager_;
-};
-
-KeyAttestationApplicationIdProvider& KeyAttestationApplicationIdProvider::get() {
- static KeyAttestationApplicationIdProvider mpm;
- return mpm;
+std::shared_ptr<BpKeyAttestationApplicationIdProvider> get_service() {
+ std::lock_guard<std::mutex> guard(gServiceMu);
+ if (gService.get() == nullptr) {
+ gService = std::make_shared<BpKeyAttestationApplicationIdProvider>(
+ android::defaultServiceManager()->waitForService(String16(kProviderServiceName)));
+ }
+ return gService;
}
-KeyAttestationApplicationIdProvider::KeyAttestationApplicationIdProvider()
- : BpKeyAttestationApplicationIdProvider(android::defaultServiceManager()->waitForService(
- String16("sec_key_att_app_id_provider"))) {}
+void reset_service() {
+ std::lock_guard<std::mutex> guard(gServiceMu);
+ // Drop the global reference; any thread that already has a reference can keep using it.
+ gService.reset();
+}
DECLARE_STACK_OF(ASN1_OCTET_STRING);
@@ -281,23 +283,31 @@
key_attestation_id.packageInfos.push_back(std::move(pinfo));
} else {
/* Get the attestation application ID from package manager */
- auto& pm = KeyAttestationApplicationIdProvider::get();
::android::binder::Status status;
- // Retry on failure if a service specific error code.
+ // Retry on failure.
for (size_t attempt{0}; attempt < kMaxAttempts; ++attempt) {
- status = pm.getKeyAttestationApplicationId(uid, &key_attestation_id);
+ auto pm = get_service();
+ status = pm->getKeyAttestationApplicationId(uid, &key_attestation_id);
if (status.isOk()) {
break;
- } else if (status.exceptionCode() != binder::Status::EX_SERVICE_SPECIFIC) {
- ALOGW("Retry: key attestation ID failed with service specific error: %s %d",
- status.exceptionMessage().c_str(), status.serviceSpecificErrorCode());
- usleep(kRetryIntervalUsecs);
- } else {
- ALOGW("Retry: key attestation ID failed with error: %s %d",
- status.exceptionMessage().c_str(), status.exceptionCode());
- usleep(kRetryIntervalUsecs);
}
+
+ if (status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
+ ALOGW("Retry: get attestation ID for %d failed with service specific error: %s %d",
+ uid, status.exceptionMessage().c_str(), status.serviceSpecificErrorCode());
+ } else if (status.exceptionCode() == binder::Status::EX_TRANSACTION_FAILED) {
+ // If the transaction failed, drop the package manager connection so that the next
+ // attempt will try again.
+ ALOGW(
+ "Retry: get attestation ID for %d transaction failed, reset connection: %s %d",
+ uid, status.exceptionMessage().c_str(), status.exceptionCode());
+ reset_service();
+ } else {
+ ALOGW("Retry: get attestation ID for %d failed with error: %s %d", uid,
+ status.exceptionMessage().c_str(), status.exceptionCode());
+ }
+ usleep(kRetryIntervalUsecs);
}
if (!status.isOk()) {
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 28bdfea..c482e84 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -30,7 +30,10 @@
"keystore2_use_latest_aidl_rust",
"structured_log_rust_defaults",
],
-
+ cfgs: select(release_flag("RELEASE_AVF_ENABLE_EARLY_VM"), {
+ true: ["early_vm"],
+ default: [],
+ }),
rustlibs: [
"android.hardware.security.rkp-V3-rust",
"android.hardware.security.secureclock-V1-rust",
diff --git a/keystore2/aconfig/flags.aconfig b/keystore2/aconfig/flags.aconfig
index 65f0857..05dae46 100644
--- a/keystore2/aconfig/flags.aconfig
+++ b/keystore2/aconfig/flags.aconfig
@@ -32,11 +32,3 @@
bug: "283077822"
is_fixed_read_only: true
}
-
-flag {
- name: "database_loop_timeout"
- namespace: "hardware_backed_security"
- description: "Abandon Keystore database retry loop after an interval"
- bug: "319563050"
- is_fixed_read_only: true
-}
\ No newline at end of file
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index 695e029..d7596a0 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -18,6 +18,7 @@
//! * getcon
//! * selinux_check_access
//! * selabel_lookup for the keystore2_key backend.
+//!
//! And it provides an owning wrapper around context strings `Context`.
// TODO(b/290018030): Remove this and add proper safety comments.
diff --git a/keystore2/src/audit_log.rs b/keystore2/src/audit_log.rs
index 8d9735e..4952b3b 100644
--- a/keystore2/src/audit_log.rs
+++ b/keystore2/src/audit_log.rs
@@ -34,8 +34,8 @@
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
}
}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 754dd9c..03bf401 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -1200,7 +1200,7 @@
// 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("handle_next_superseded_blob find_next");
+ let _wp = wd::watch("KeystoreDB::handle_next_superseded_blob find_next");
let mut stmt = tx
.prepare(
"SELECT id, blob FROM persistent.blobentry
@@ -1231,7 +1231,7 @@
.context("Trying to extract superseded blobs.")?
};
- let _wp = wd::watch("handle_next_superseded_blob load_metadata");
+ let _wp = wd::watch("KeystoreDB::handle_next_superseded_blob load_metadata");
let result = result
.into_iter()
.map(|(blob_id, blob)| {
@@ -1249,7 +1249,7 @@
// We did not find any superseded key blob, so let's remove other superseded blob in
// one transaction.
- let _wp = wd::watch("handle_next_superseded_blob delete");
+ let _wp = wd::watch("KeystoreDB::handle_next_superseded_blob delete");
tx.execute(
"DELETE FROM persistent.blobentry
WHERE NOT subcomponent_type = ?
@@ -1902,6 +1902,7 @@
/// `access_vector`.
/// * Domain::KEY_ID: The keyentry table is queried for the owning `domain` and
/// `namespace`.
+ ///
/// In each case the information returned is sufficient to perform the access
/// check and the key id can be used to load further key artifacts.
fn load_access_tuple(
diff --git a/keystore2/src/database/versioning.rs b/keystore2/src/database/versioning.rs
index 2c816f4..bc68f15 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 @@
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/globals.rs b/keystore2/src/globals.rs
index c7b495d..bde83fd 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -23,7 +23,7 @@
use crate::legacy_blob::LegacyBlobLoader;
use crate::legacy_importer::LegacyImporter;
use crate::super_key::SuperKeyManager;
-use crate::utils::watchdog as wd;
+use crate::utils::{retry_get_interface, watchdog as wd};
use crate::{
database::KeystoreDB,
database::Uuid,
@@ -62,21 +62,25 @@
/// 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 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 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.");
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"
);
}
});
@@ -88,8 +92,7 @@
/// 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> {
@@ -154,7 +157,7 @@
/// 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())));
@@ -165,12 +168,12 @@
(
Box::new(|uuid, blob| {
let km_dev = get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?;
- let _wp = wd::watch("In invalidate key closure: calling deleteKey");
+ 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(),
)
}));
@@ -222,8 +225,12 @@
let (keymint, hal_version) = if let Some(service_name) = service_name {
let km: Strong<dyn IKeyMintDevice> =
- map_binder_status_code(binder::get_interface(&service_name))
- .context(ks_err!("Trying to connect to genuine KeyMint service."))?;
+ if SecurityLevel::TRUSTED_ENVIRONMENT == *security_level {
+ map_binder_status_code(retry_get_interface(&service_name))
+ } else {
+ map_binder_status_code(binder::get_interface(&service_name))
+ }
+ .context(ks_err!("Trying to connect to genuine KeyMint service."))?;
// Map the HAL version code for KeyMint to be <AIDL version> * 100, so
// - V1 is 100
// - V2 is 200
@@ -306,7 +313,7 @@
}
};
- let wp = wd::watch("In connect_keymint: calling getHardwareInfo()");
+ 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);
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 2bb7f27..c27a050 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -36,6 +36,9 @@
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 @@
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 0000000..53fe03f
--- /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 045f848..24f3263 100644
--- a/keystore2/src/legacy_importer.rs
+++ b/keystore2/src/legacy_importer.rs
@@ -923,7 +923,7 @@
blob,
&[],
|blob| {
- let _wd = wd::watch("Calling GetKeyCharacteristics.");
+ let _wd = wd::watch("get_key_characteristics_without_app_data: calling IKeyMintDevice::getKeyCharacteristics");
map_km_error(km_dev.getKeyCharacteristics(blob, &[], &[]))
},
|_| Ok(()),
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 43d99d1..61277f1 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -28,7 +28,7 @@
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,
@@ -143,7 +143,7 @@
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, (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(())
}
@@ -164,12 +164,21 @@
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
})
@@ -313,7 +322,7 @@
}
fn deleteAllKeys(&self) -> BinderResult<()> {
- log::warn!("deleteAllKeys()");
+ 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)
}
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 7d988e1..9ae8ccf 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -31,6 +31,7 @@
//! * `abort` is called.
//! * The operation gets dropped.
//! * The operation gets pruned.
+//!
//! `Operation` has an `Outcome` member. While the outcome is `Outcome::Unknown`,
//! the operation is active and in a good state. Any of the above conditions may
//! change the outcome to one of the defined outcomes Success, Abort, Dropped,
@@ -286,7 +287,7 @@
}
*locked_outcome = Outcome::Pruned;
- let _wp = wd::watch("In Operation::prune: calling abort()");
+ 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()) {
@@ -362,7 +363,7 @@
.context(ks_err!("Trying to get auth tokens."))?;
self.update_outcome(&mut outcome, {
- let _wp = wd::watch("Operation::update_aad: calling updateAad");
+ 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 +387,7 @@
let output = self
.update_outcome(&mut outcome, {
- let _wp = wd::watch("Operation::update: calling update");
+ 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 +417,7 @@
let output = self
.update_outcome(&mut outcome, {
- let _wp = wd::watch("Operation::finish: calling finish");
+ let _wp = wd::watch("Operation::finish: calling IKeyMintOperation::finish");
map_km_error(self.km_op.finish(
input,
signature,
@@ -447,7 +448,7 @@
*locked_outcome = outcome;
{
- let _wp = wd::watch("Operation::abort: calling abort");
+ let _wp = wd::watch("Operation::abort: calling IKeyMintOperation::abort");
map_km_error(self.km_op.abort()).context(ks_err!("KeyMint::abort failed."))
}
}
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index a8a88d2..bf1149c 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -212,8 +212,8 @@
|key_blob| {
map_km_error({
let _wp = wd::watch(concat!(
- "In KeyMintDevice::lookup_or_generate_key: ",
- "calling getKeyCharacteristics."
+ "KeyMintDevice::lookup_or_generate_key: ",
+ "calling IKeyMintDevice::getKeyCharacteristics."
));
self.km_dev.getKeyCharacteristics(key_blob, &[], &[])
})
@@ -305,7 +305,9 @@
let (begin_result, _) = self
.upgrade_keyblob_if_required_with(db, key_id_guard, key_blob, |blob| {
map_km_error({
- let _wp = wd::watch("In use_key_in_one_step: calling: begin");
+ let _wp = wd::watch(
+ "KeyMintDevice::use_key_in_one_step: calling IKeyMintDevice::begin",
+ );
self.km_dev.begin(purpose, blob, operation_parameters, auth_token)
})
})
@@ -313,7 +315,8 @@
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("In use_key_in_one_step: calling: finish");
+ 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/security_level.rs b/keystore2/src/security_level.rs
index 8412397..bd20afb 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -329,8 +329,9 @@
operation_parameters,
|blob| loop {
match map_km_error({
- let _wp =
- self.watch("In KeystoreSecurityLevel::create_operation: calling begin");
+ let _wp = self.watch(
+ "KeystoreSecurityLevel::create_operation: calling IKeyMintDevice::begin",
+ );
self.keymint.begin(
purpose,
blob,
@@ -444,7 +445,7 @@
// If there is an attestation challenge we need to get an application id.
if params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE) {
let _wp =
- self.watch("In KeystoreSecurityLevel::add_required_parameters calling: get_aaid");
+ self.watch(" KeystoreSecurityLevel::add_required_parameters: calling get_aaid");
match keystore2_aaid::get_aaid(uid) {
Ok(aaid_ok) => {
result.push(KeyParameter {
@@ -581,8 +582,8 @@
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.
);
@@ -601,8 +602,8 @@
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.
);
@@ -628,8 +629,8 @@
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.
);
@@ -696,7 +697,8 @@
let km_dev = &self.keymint;
let creation_result = map_km_error({
- let _wp = self.watch("In KeystoreSecurityLevel::import_key: calling importKey.");
+ let _wp =
+ self.watch("KeystoreSecurityLevel::import_key: calling IKeyMintDevice::importKey.");
km_dev.importKey(¶ms, format, key_data, None /* attestKey */)
})
.context(ks_err!("Trying to call importKey"))?;
@@ -810,7 +812,7 @@
&[],
|wrapping_blob| {
let _wp = self.watch(
- "In KeystoreSecurityLevel::import_wrapped_key: calling importWrappedKey.",
+ "KeystoreSecurityLevel::import_wrapped_key: calling IKeyMintDevice::importWrappedKey.",
);
let creation_result = map_km_error(self.keymint.importWrappedKey(
wrapped_data,
@@ -951,8 +953,8 @@
let km_dev = &self.keymint;
let res = {
let _wp = self.watch(concat!(
- "In IKeystoreSecurityLevel::convert_storage_key_to_ephemeral: ",
- "calling convertStorageKeyToEphemeral (1)"
+ "IKeystoreSecurityLevel::convert_storage_key_to_ephemeral: ",
+ "calling IKeyMintDevice::convertStorageKeyToEphemeral (1)"
));
map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
};
@@ -962,19 +964,18 @@
}
Err(error::Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
let upgraded_blob = {
- let _wp = self.watch("In convert_storage_key_to_ephemeral: calling upgradeKey");
+ 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(
- "In convert_storage_key_to_ephemeral: calling convertStorageKeyToEphemeral (2)",
- );
+ 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),
@@ -1001,7 +1002,8 @@
let km_dev = &self.keymint;
{
- let _wp = self.watch("In KeystoreSecuritylevel::delete_key: calling deleteKey");
+ let _wp =
+ self.watch("KeystoreSecuritylevel::delete_key: calling IKeyMintDevice::deleteKey");
map_km_error(km_dev.deleteKey(key_blob)).context(ks_err!("keymint device deleteKey"))
}
}
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index b760a56..95e1744 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -62,11 +62,18 @@
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);
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index fa1de98..42fd764 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -917,7 +917,7 @@
KeyType::Client, /* TODO Should be Super b/189470584 */
|dev| {
let _wp =
- wd::watch("In lock_unlocked_device_required_keys: calling importKey.");
+ wd::watch("SKM::lock_unlocked_device_required_keys: calling IKeyMintDevice::importKey.");
dev.importKey(key_params.as_slice(), KeyFormat::RAW, &encrypting_key, None)
},
)?;
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 22c0522..03c0626 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -40,14 +40,17 @@
Authorization::Authorization, Domain::Domain, KeyDescriptor::KeyDescriptor,
};
use anyhow::{Context, Result};
-use binder::{Strong, ThreadState};
+use binder::{FromIBinder, StatusCode, Strong, ThreadState};
use keystore2_apc_compat::{
ApcCompatUiOptions, APC_COMPAT_ERROR_ABORTED, APC_COMPAT_ERROR_CANCELLED,
APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
APC_COMPAT_ERROR_SYSTEM_ERROR,
};
use keystore2_crypto::{aes_gcm_decrypt, aes_gcm_encrypt, ZVec};
+use log::{info, warn};
use std::iter::IntoIterator;
+use std::thread::sleep;
+use std::time::Duration;
#[cfg(test)]
mod tests;
@@ -146,8 +149,7 @@
binder::get_interface("permission")?;
let binder_result = {
- let _wp =
- watchdog::watch("In check_device_attestation_permissions: calling checkPermission.");
+ let _wp = watchdog::watch("check_android_permission: calling checkPermission");
permission_controller.checkPermission(
permission,
ThreadState::get_calling_pid(),
@@ -264,7 +266,9 @@
log::debug!("import parameters={import_params:?}");
let creation_result = {
- let _wp = watchdog::watch("In utils::import_keyblob_and_perform_op: calling importKey.");
+ 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))
}
.context(ks_err!("Upgrade failed."))?;
@@ -304,7 +308,9 @@
NewBlobHandler: FnOnce(&[u8]) -> Result<()>,
{
let upgraded_blob = {
- let _wp = watchdog::watch("In utils::upgrade_keyblob_and_perform_op: calling upgradeKey.");
+ let _wp = watchdog::watch(
+ "utils::upgrade_keyblob_and_perform_op: calling IKeyMintDevice::upgradeKey.",
+ );
map_km_error(km_dev.upgradeKey(key_blob, upgrade_params))
}
.context(ks_err!("Upgrade failed."))?;
@@ -631,3 +637,25 @@
aes_gcm_encrypt(plaintext, self.key()).context(ks_err!("Encryption failed."))
}
}
+
+pub(crate) fn retry_get_interface<T: FromIBinder + ?Sized>(
+ name: &str,
+) -> Result<Strong<T>, StatusCode> {
+ let retry_count = if cfg!(early_vm) { 5 } else { 1 };
+
+ let mut wait_time = Duration::from_secs(5);
+ for i in 1..retry_count {
+ match binder::get_interface(name) {
+ Ok(res) => return Ok(res),
+ Err(e) => {
+ warn!("failed to get interface {name}. Retry {i}/{retry_count}: {e:?}");
+ sleep(wait_time);
+ wait_time *= 2;
+ }
+ }
+ }
+ if retry_count > 1 {
+ info!("{retry_count}-th (last) retry to get interface: {name}");
+ }
+ binder::get_interface(name)
+}
diff --git a/keystore2/tests/keystore2_client_3des_key_tests.rs b/keystore2/tests/keystore2_client_3des_key_tests.rs
index 29f1617..4cb81d1 100644
--- a/keystore2/tests/keystore2_client_3des_key_tests.rs
+++ b/keystore2/tests/keystore2_client_3des_key_tests.rs
@@ -72,6 +72,7 @@
/// Generate 3DES keys with various block modes and paddings.
/// - Block Modes: ECB, CBC
/// - Padding Modes: NONE, PKCS7
+///
/// Test should generate keys and perform operation successfully.
#[test]
fn keystore2_3des_ecb_cbc_generate_key_success() {
diff --git a/keystore2/tests/keystore2_client_aes_key_tests.rs b/keystore2/tests/keystore2_client_aes_key_tests.rs
index 4e3e606..3c5fda5 100644
--- a/keystore2/tests/keystore2_client_aes_key_tests.rs
+++ b/keystore2/tests/keystore2_client_aes_key_tests.rs
@@ -75,6 +75,7 @@
/// Generate AES keys with various block modes and paddings.
/// - Block Modes: ECB, CBC
/// - Padding Modes: NONE, PKCS7
+///
/// Test should generate keys and perform operation successfully.
#[test]
fn keystore2_aes_ecb_cbc_generate_key() {
@@ -106,6 +107,7 @@
/// Generate AES keys with -
/// - Block Modes: `CTR, GCM`
/// - Padding Modes: `NONE`
+///
/// Test should generate keys and perform operation successfully.
#[test]
fn keystore2_aes_ctr_gcm_generate_key_success() {
@@ -133,6 +135,7 @@
/// Generate AES keys with -
/// - Block Modes: `CTR, GCM`
/// - Padding Modes: `PKCS7`
+///
/// Try to create an operation using generated keys, test should fail to create an operation
/// with an error code `INCOMPATIBLE_PADDING_MODE`.
#[test]
diff --git a/keystore2/tests/keystore2_client_operation_tests.rs b/keystore2/tests/keystore2_client_operation_tests.rs
index 95888a6..02cf260 100644
--- a/keystore2/tests/keystore2_client_operation_tests.rs
+++ b/keystore2/tests/keystore2_client_operation_tests.rs
@@ -376,6 +376,7 @@
/// - untrusted_app
/// - system_server
/// - priv_app
+///
/// `PERMISSION_DENIED` error response is expected.
#[test]
fn keystore2_forced_op_perm_denied_test() {
diff --git a/keystore2/watchdog/src/lib.rs b/keystore2/watchdog/src/lib.rs
index ff05783..b4a1e0f 100644
--- a/keystore2/watchdog/src/lib.rs
+++ b/keystore2/watchdog/src/lib.rs
@@ -193,7 +193,30 @@
}
fn disarm(&mut self, index: Index) {
- self.records.remove(&index);
+ let result = self.records.remove(&index);
+ if let Some(record) = result {
+ let now = Instant::now();
+ let timeout_left = record.deadline.saturating_duration_since(now);
+ if timeout_left == Duration::new(0, 0) {
+ match &record.context {
+ Some(ctx) => log::info!(
+ "Watchdog complete for: {:?} {} Pending: {:?} Overdue {:?} for {:?}",
+ index.tid,
+ index.id,
+ record.started.elapsed(),
+ record.deadline.elapsed(),
+ ctx
+ ),
+ None => log::info!(
+ "Watchdog complete for: {:?} {} Pending: {:?} Overdue {:?}",
+ index.tid,
+ index.id,
+ record.started.elapsed(),
+ record.deadline.elapsed()
+ ),
+ }
+ }
+ }
}
fn arm(&mut self, index: Index, record: Record) {