Adding tests to validate support for encrypted legacy blobs.

Bug: 213173772
Bug: 213172664
Bug: 203101472
Test: atest keystore2_legacy_blobs_test
Change-Id: I3c898c4447c114dd6a9481615a3366c76ad3a2bb
diff --git a/keystore2/tests/legacy_blobs/Android.bp b/keystore2/tests/legacy_blobs/Android.bp
new file mode 100644
index 0000000..2f48d7f
--- /dev/null
+++ b/keystore2/tests/legacy_blobs/Android.bp
@@ -0,0 +1,42 @@
+// 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.
+
+rust_test {
+    name: "keystore2_legacy_blobs_test",
+    srcs: ["keystore2_legacy_blob_tests.rs"],
+    test_suites: [
+        "general-tests",
+    ],
+    // auto_gen_config: true,
+    test_config: "AndroidTest.xml",
+
+    rustlibs: [
+        "libkeystore2_with_test_utils",
+        "libkeystore2_crypto_rust",
+        "android.system.keystore2-V2-rust",
+        "android.hardware.security.keymint-V2-rust",
+        "android.security.maintenance-rust",
+        "android.security.authorization-rust",
+        "librustutils",
+        "libkeystore2_test_utils",
+        "libnix",
+        "libanyhow",
+        "libbinder_rs",
+        "liblazy_static",
+        "liblibc",
+        "libserde",
+        "libthiserror",
+    ],
+    require_root: true,
+}
diff --git a/keystore2/tests/legacy_blobs/AndroidTest.xml b/keystore2/tests/legacy_blobs/AndroidTest.xml
new file mode 100644
index 0000000..ea83fbf
--- /dev/null
+++ b/keystore2/tests/legacy_blobs/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+<configuration description="Config to run keystore2_legacy_blobs_test device tests.">
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option
+            name="push"
+            value="keystore2_legacy_blobs_test->/data/local/tmp/keystore2_legacy_blobs_test"
+        />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+        <option name="test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="keystore2_legacy_blobs_test" />
+        <option name="native-test-flag" value="--test-threads=1" />
+    </test>
+</configuration>
diff --git a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
new file mode 100644
index 0000000..6def39e
--- /dev/null
+++ b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
@@ -0,0 +1,579 @@
+// 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.
+
+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, UserState::UserState,
+};
+
+use android_security_authorization::aidl::android::security::authorization::{
+    IKeystoreAuthorization::IKeystoreAuthorization, LockScreenEvent::LockScreenEvent,
+};
+
+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;
+
+static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
+static AUTH_SERVICE_NAME: &str = "android.security.authorization";
+const SELINUX_SHELL_NAMESPACE: i64 = 1;
+
+fn get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance> {
+    binder::get_interface(USER_MANAGER_SERVICE_NAME).unwrap()
+}
+
+fn get_authorization() -> binder::Strong<dyn IKeystoreAuthorization> {
+    binder::get_interface(AUTH_SERVICE_NAME).unwrap()
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+struct KeygenResult {
+    cert: Vec<u8>,
+    cert_chain: Vec<u8>,
+    key_parameters: Vec<KsKeyparameter>,
+}
+
+struct TestKey(ZVec);
+
+impl keystore2::utils::AesGcmKey for TestKey {
+    fn key(&self) -> &[u8] {
+        &self.0
+    }
+}
+
+impl Deref for TestKey {
+    type Target = [u8];
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+fn keystore2_restart_service() {
+    let output = std::process::Command::new("pidof")
+        .arg("keystore2")
+        .output()
+        .expect("failed to execute pidof keystore2");
+
+    let id = String::from_utf8(output.stdout).unwrap();
+    let id: String = id.chars().filter(|c| c.is_digit(10)).collect();
+
+    let _status = std::process::Command::new("kill").arg("-9").arg(id).status().unwrap();
+
+    // Loop till we find keystore2 service up and running.
+    loop {
+        let output = std::process::Command::new("pidof")
+            .arg("keystore2")
+            .output()
+            .expect("failed to execute pidof keystore2");
+
+        if output.status.code() == Some(0) {
+            break;
+        }
+    }
+}
+
+/// Create legacy blobs file layout for a user with user-id 99 and app-id 10001 with
+/// user-cert, ca-certs and encrypted key-characteristics files and tries to import
+/// these legacy blobs under user context.
+///
+/// Expected File layout for user with user-id "98" and app-id "10001" and key-alias
+/// "authbound":
+///     /data/misc/keystore/user_99/.masterkey
+///     /data/misc/keystore/user_99/9910001_USRPKEY_authbound
+///     /data/misc/keystore/user_99/.9910001_chr_USRPKEY_authbound
+///     /data/misc/keystore/user_99/9910001_USRCERT_authbound
+///     /data/misc/keystore/user_99/9910001_CACERT_authbound
+///
+/// Test performs below tasks -
+/// With su context it performs following tasks -
+///     1. Remove this user if already exist.
+///     2. Generate a key-blob, user cert-blob and ca-cert-blob to store it in legacy blobs file
+///        layout.
+///     3. Prepare file layout using generated key-blob, user cert and ca certs.
+///     4. Restart the keystore2 service to make it detect the populated legacy blobs.
+///     5. Inform the keystore2 service about the user and unlock the user.
+/// With user-99 context it performs following tasks -
+///     6. To load and import the legacy key using its alias.
+///     7. After successful key import validate the user cert and cert-chain with initially
+///        generated blobs.
+///     8. Validate imported key perameters. Imported key parameters list should be the combination
+///        of the key-parameters in characteristics file and the characteristics according to
+///        the augmentation rules. There might be duplicate entries with different values for the
+///        parameters like OS_VERSION, OS_VERSION, BOOT_PATCHLEVEL, VENDOR_PATCHLEVEL etc.
+///     9. Confirm keystore2 service cleanup the legacy blobs after successful import.
+#[test]
+fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
+    let auid = 99 * AID_USER_OFFSET + 10001;
+    let agid = 99 * AID_USER_OFFSET + 10001;
+    static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+    // Cleanup user directory if it exists
+    let path_buf = PathBuf::from("/data/misc/keystore/user_99");
+    if path_buf.as_path().is_dir() {
+        std::fs::remove_dir_all(path_buf.as_path()).unwrap();
+    }
+
+    // Safety: run_as must be called from a single threaded process.
+    // This device test is run as a separate single threaded process.
+    let mut gen_key_result = unsafe {
+        run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            // Remove user if already exist.
+            let maint_service = get_maintenance();
+            match maint_service.onUserRemoved(99) {
+                Ok(_) => {
+                    println!("User was existed, deleted successfully");
+                }
+                Err(e) => {
+                    println!("onUserRemoved error: {:#?}", e);
+                }
+            }
+
+            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 key_metadata =
+                key_generations::generate_ec_p256_signing_key_with_attestation(&sec_level)
+                    .expect("Failed to generate key blob");
+
+            // Create keystore file layout for user_99.
+            let pw: Password = PASSWORD.into();
+            let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
+            let super_key =
+                TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap());
+
+            let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
+            if !path_buf.as_path().is_dir() {
+                std::fs::create_dir(path_buf.as_path()).unwrap();
+            }
+            path_buf.push(".masterkey");
+            if !path_buf.as_path().is_file() {
+                std::fs::write(path_buf.as_path(), SUPERKEY).unwrap();
+            }
+
+            let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
+            path_buf.push("9910001_USRPKEY_authbound");
+            if !path_buf.as_path().is_file() {
+                make_encrypted_key_file(
+                    path_buf.as_path(),
+                    &super_key,
+                    &key_metadata.key.blob.unwrap(),
+                )
+                .unwrap();
+            }
+
+            let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
+            path_buf.push(".9910001_chr_USRPKEY_authbound");
+            if !path_buf.as_path().is_file() {
+                make_encrypted_characteristics_file(path_buf.as_path(), &super_key, KEY_PARAMETERS)
+                    .unwrap();
+            }
+
+            let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
+            path_buf.push("9910001_USRCERT_authbound");
+            if !path_buf.as_path().is_file() {
+                make_cert_blob_file(path_buf.as_path(), key_metadata.certificate.as_ref().unwrap())
+                    .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();
+            }
+
+            // Keystore2 disables the legacy importer when it finds the legacy database empty.
+            // However, if the device boots with an empty legacy database, the optimization kicks in
+            // and keystore2 never checks the legacy file system layout.
+            // So, restart keystore2 service to detect populated legacy database.
+            keystore2_restart_service();
+
+            let auth_service = get_authorization();
+            match auth_service.onLockScreenEvent(LockScreenEvent::UNLOCK, 99, Some(PASSWORD), None)
+            {
+                Ok(result) => {
+                    println!("Unlock Result: {:?}", result);
+                }
+                Err(e) => {
+                    panic!("Unlock should have succeeded: {:?}", e);
+                }
+            }
+
+            let maint_service = get_maintenance();
+            assert_eq!(Ok(UserState(1)), maint_service.getState(99));
+
+            let mut key_params: Vec<KsKeyparameter> = Vec::new();
+            for param in key_metadata.authorizations {
+                let key_param = KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
+                key_params.push(key_param);
+            }
+
+            KeygenResult {
+                cert: key_metadata.certificate.unwrap(),
+                cert_chain: key_metadata.certificateChain.unwrap(),
+                key_parameters: key_params,
+            }
+        })
+    };
+
+    // Safety: run_as must be called from a single threaded process.
+    // This device test is run as a separate single threaded process.
+    unsafe {
+        run_as::run_as(TARGET_CTX, Uid::from_raw(auid), Gid::from_raw(agid), move || {
+            println!("UID: {}", getuid());
+            println!("Android User ID: {}", rustutils::users::multiuser_get_user_id(9910001));
+            println!("Android app ID: {}", rustutils::users::multiuser_get_app_id(9910001));
+
+            let test_alias = "authbound";
+            let keystore2 = get_keystore_service();
+
+            match keystore2.getKeyEntry(&KeyDescriptor {
+                domain: Domain::APP,
+                nspace: SELINUX_SHELL_NAMESPACE,
+                alias: Some(test_alias.to_string()),
+                blob: None,
+            }) {
+                Ok(key_entry_response) => {
+                    assert_eq!(
+                        key_entry_response.metadata.certificate.unwrap(),
+                        gen_key_result.cert
+                    );
+                    assert_eq!(
+                        key_entry_response.metadata.certificateChain.unwrap(),
+                        gen_key_result.cert_chain
+                    );
+                    assert_eq!(key_entry_response.metadata.key.domain, Domain::KEY_ID);
+                    assert_ne!(key_entry_response.metadata.key.nspace, 0);
+                    assert_eq!(
+                        key_entry_response.metadata.keySecurityLevel,
+                        SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT
+                    );
+
+                    // Preapare KsKeyParameter list from getKeEntry response Authorizations.
+                    let mut key_params: Vec<KsKeyparameter> = Vec::new();
+                    for param in key_entry_response.metadata.authorizations {
+                        let key_param =
+                            KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
+                        key_params.push(key_param);
+                    }
+
+                    // Combine keyparameters from gen_key_result and keyparameters
+                    // from legacy key-char file.
+                    let mut legacy_file_key_params: Vec<KsKeyparameter> = Vec::new();
+                    match structured_test_params() {
+                        LegacyKeyCharacteristics::File(legacy_key_params) => {
+                            for param in &legacy_key_params {
+                                let mut present_in_gen_params = false;
+                                for gen_param in &gen_key_result.key_parameters {
+                                    if param.get_tag() == gen_param.get_tag() {
+                                        present_in_gen_params = true;
+                                    }
+                                }
+                                if !present_in_gen_params {
+                                    legacy_file_key_params.push(param.clone());
+                                }
+                            }
+                        }
+                        _ => {
+                            panic!("Expecting file characteristics");
+                        }
+                    }
+
+                    // Remove Key-Params which have security levels other than TRUSTED_ENVIRONMENT
+                    gen_key_result.key_parameters.retain(|in_element| {
+                        *in_element.security_level()
+                            == SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT
+                    });
+
+                    println!("GetKeyEntry response key params: {:#?}", key_params);
+                    println!("Generated key params: {:#?}", gen_key_result.key_parameters);
+
+                    gen_key_result.key_parameters.append(&mut legacy_file_key_params);
+
+                    println!("Combined key params: {:#?}", gen_key_result.key_parameters);
+
+                    // Validate all keyparameters present in getKeyEntry response.
+                    for param in &key_params {
+                        gen_key_result.key_parameters.retain(|in_element| *in_element != *param);
+                    }
+
+                    println!(
+                        "GetKeyEntry response unmatched key params: {:#?}",
+                        gen_key_result.key_parameters
+                    );
+                    assert_eq!(gen_key_result.key_parameters.len(), 0);
+                }
+                Err(s) => {
+                    panic!("getKeyEntry should have succeeded. {:?}", s);
+                }
+            };
+        })
+    };
+
+    // Make sure keystore2 clean up imported legacy db.
+    let path_buf = PathBuf::from("/data/misc/keystore/user_99");
+    if path_buf.as_path().is_dir() {
+        panic!("Keystore service should have deleted this dir {:?}", path_buf);
+    }
+    Ok(())
+}
+
+/// Create legacy blobs file layout for a user with user-id 98 and app-id 10001 with encrypted
+/// user-cert and ca-certs files and tries to import these legacy blobs under user context.
+///
+/// Expected File layout for user with user-id "98" and app-id "10001" and key-alias
+/// "authboundcertenc":
+///     /data/misc/keystore/user_98/.masterkey
+///     /data/misc/keystore/user_98/9810001_USRPKEY_authboundcertenc
+///     /data/misc/keystore/user_98/.9810001_chr_USRPKEY_authboundcertenc
+///     /data/misc/keystore/user_98/9810001_USRCERT_authboundcertenc
+///     /data/misc/keystore/user_98/9810001_CACERT_authboundcertenc
+///
+/// Test performs below tasks -
+/// With su context it performs following tasks -
+///     1. Remove this user if already exist.
+///     2. Generate a key-blob, user cert-blob and ca-cert-blob to store it in legacy blobs file
+///        layout.
+///     3. Prepare file layout using generated key-blob, user cert and ca certs.
+///     4. Restart the keystore2 service to make it detect the populated legacy blobs.
+///     5. Inform the keystore2 service about the user and unlock the user.
+/// With user-98 context it performs following tasks -
+///     6. To load and import the legacy key using its alias.
+///     7. After successful key import validate the user cert and cert-chain with initially
+///        generated blobs.
+///     8. Validate imported key perameters. Imported key parameters list should be the combination
+///        of the key-parameters in characteristics file and the characteristics according to
+///        the augmentation rules. There might be duplicate entries with different values for the
+///        parameters like OS_VERSION, OS_VERSION, BOOT_PATCHLEVEL, VENDOR_PATCHLEVEL etc.
+///     9. Confirm keystore2 service cleanup the legacy blobs after successful import.
+#[test]
+fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
+    let auid = 98 * AID_USER_OFFSET + 10001;
+    let agid = 98 * AID_USER_OFFSET + 10001;
+    static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+    static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+    // Cleanup user directory if it exists
+    let path_buf = PathBuf::from("/data/misc/keystore/user_98");
+    if path_buf.as_path().is_dir() {
+        std::fs::remove_dir_all(path_buf.as_path()).unwrap();
+    }
+
+    // Safety: run_as must be called from a single threaded process.
+    // This device test is run as a separate single threaded process.
+    let gen_key_result = unsafe {
+        run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+            // Remove user if already exist.
+            let maint_service = get_maintenance();
+            match maint_service.onUserRemoved(98) {
+                Ok(_) => {
+                    println!("User was existed, deleted successfully");
+                }
+                Err(e) => {
+                    println!("onUserRemoved error: {:#?}", e);
+                }
+            }
+
+            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 key_metadata =
+                key_generations::generate_ec_p256_signing_key_with_attestation(&sec_level)
+                    .expect("Failed to generate key blob");
+
+            // Create keystore file layout for user_98.
+            let pw: Password = PASSWORD.into();
+            let pw_key = TestKey(pw.derive_key(Some(SUPERKEY_SALT), 32).unwrap());
+            let super_key =
+                TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap());
+
+            let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
+            if !path_buf.as_path().is_dir() {
+                std::fs::create_dir(path_buf.as_path()).unwrap();
+            }
+            path_buf.push(".masterkey");
+            if !path_buf.as_path().is_file() {
+                std::fs::write(path_buf.as_path(), SUPERKEY).unwrap();
+            }
+
+            let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
+            path_buf.push("9810001_USRPKEY_authboundcertenc");
+            if !path_buf.as_path().is_file() {
+                make_encrypted_key_file(
+                    path_buf.as_path(),
+                    &super_key,
+                    &key_metadata.key.blob.unwrap(),
+                )
+                .unwrap();
+            }
+
+            let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
+            path_buf.push(".9810001_chr_USRPKEY_authboundcertenc");
+            if !path_buf.as_path().is_file() {
+                std::fs::write(path_buf.as_path(), USRPKEY_AUTHBOUND_CHR).unwrap();
+            }
+
+            let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
+            path_buf.push("9810001_USRCERT_authboundcertenc");
+            if !path_buf.as_path().is_file() {
+                make_encrypted_usr_cert_file(
+                    path_buf.as_path(),
+                    &super_key,
+                    key_metadata.certificate.as_ref().unwrap(),
+                )
+                .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();
+            }
+
+            // Keystore2 disables the legacy importer when it finds the legacy database empty.
+            // However, if the device boots with an empty legacy database, the optimization kicks in
+            // and keystore2 never checks the legacy file system layout.
+            // So, restart keystore2 service to detect populated legacy database.
+            keystore2_restart_service();
+
+            let auth_service = get_authorization();
+            match auth_service.onLockScreenEvent(LockScreenEvent::UNLOCK, 98, Some(PASSWORD), None)
+            {
+                Ok(result) => {
+                    println!("Unlock Result: {:?}", result);
+                }
+                Err(e) => {
+                    panic!("Unlock should have succeeded: {:?}", e);
+                }
+            }
+
+            let maint_service = get_maintenance();
+            assert_eq!(Ok(UserState(1)), maint_service.getState(98));
+
+            let mut key_params: Vec<KsKeyparameter> = Vec::new();
+            for param in key_metadata.authorizations {
+                let key_param = KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
+                key_params.push(key_param);
+            }
+
+            KeygenResult {
+                cert: key_metadata.certificate.unwrap(),
+                cert_chain: key_metadata.certificateChain.unwrap(),
+                key_parameters: key_params,
+            }
+        })
+    };
+
+    // Safety: run_as must be called from a single threaded process.
+    // This device test is run as a separate single threaded process.
+    unsafe {
+        run_as::run_as(TARGET_CTX, Uid::from_raw(auid), Gid::from_raw(agid), move || {
+            println!("UID: {}", getuid());
+            println!("Android User ID: {}", rustutils::users::multiuser_get_user_id(9810001));
+            println!("Android app ID: {}", rustutils::users::multiuser_get_app_id(9810001));
+
+            let test_alias = "authboundcertenc";
+            let keystore2 = get_keystore_service();
+
+            match keystore2.getKeyEntry(&KeyDescriptor {
+                domain: Domain::APP,
+                nspace: SELINUX_SHELL_NAMESPACE,
+                alias: Some(test_alias.to_string()),
+                blob: None,
+            }) {
+                Ok(key_entry_response) => {
+                    assert_eq!(
+                        key_entry_response.metadata.certificate.unwrap(),
+                        gen_key_result.cert
+                    );
+                    assert_eq!(
+                        key_entry_response.metadata.certificateChain.unwrap(),
+                        gen_key_result.cert_chain
+                    );
+
+                    // Preapare KsKeyParameter list from getKeEntry response Authorizations.
+                    let mut key_params: Vec<KsKeyparameter> = Vec::new();
+                    for param in key_entry_response.metadata.authorizations {
+                        let key_param =
+                            KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
+                        key_params.push(key_param);
+                    }
+
+                    println!("GetKeyEntry response key params: {:#?}", key_params);
+                    println!("Generated key params: {:#?}", gen_key_result.key_parameters);
+                    match structured_test_params_cache() {
+                        LegacyKeyCharacteristics::Cache(legacy_key_params) => {
+                            println!("Legacy key-char cache: {:#?}", legacy_key_params);
+                            // Validate all keyparameters present in getKeyEntry response.
+                            for param in &legacy_key_params {
+                                key_params.retain(|in_element| *in_element != *param);
+                            }
+
+                            println!(
+                                "GetKeyEntry response unmatched key params: {:#?}",
+                                key_params
+                            );
+                            assert_eq!(key_params.len(), 0);
+                        }
+                        _ => {
+                            panic!("Expecting file characteristics");
+                        }
+                    }
+                }
+                Err(s) => {
+                    panic!("getKeyEntry should have succeeded. {:?}", s);
+                }
+            };
+        })
+    };
+
+    // Make sure keystore2 clean up imported legacy db.
+    let path_buf = PathBuf::from("/data/misc/keystore/user_98");
+    if path_buf.as_path().is_dir() {
+        panic!("Keystore service should have deleted this dir {:?}", path_buf);
+    }
+    Ok(())
+}