Merge "Specify version for aidl_interface explicitly"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..99cf5ff
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.*~
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 2ed2a60..f9295ca 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -26,6 +26,7 @@
         "android.system.keystore2-V1-rust",
         "libanyhow",
         "libbinder_rs",
+        "libkeystore2_aaid-rust",
         "libkeystore2_apc_compat-rust",
         "libkeystore2_crypto_rust",
         "libkeystore2_km_compat",
@@ -40,6 +41,16 @@
     ],
 }
 
+rust_library {
+    name: "libkeystore2_test_utils",
+    crate_name: "keystore2_test_utils",
+    srcs: ["test_utils/lib.rs"],
+    rustlibs: [
+        "liblog_rust",
+        "librand",
+    ]
+}
+
 rust_test {
     name: "keystore2_test",
     crate_name: "keystore2",
@@ -56,10 +67,12 @@
         "libandroid_logger",
         "libanyhow",
         "libbinder_rs",
+        "libkeystore2_aaid-rust",
         "libkeystore2_apc_compat-rust",
         "libkeystore2_crypto_rust",
         "libkeystore2_km_compat",
         "libkeystore2_selinux",
+        "libkeystore2_test_utils",
         "liblazy_static",
         "liblibc",
         "liblibsqlite3_sys",
diff --git a/keystore2/aaid/Android.bp b/keystore2/aaid/Android.bp
new file mode 100644
index 0000000..2329400
--- /dev/null
+++ b/keystore2/aaid/Android.bp
@@ -0,0 +1,50 @@
+// 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.
+
+cc_library {
+    name: "libkeystore2_aaid",
+    srcs: [
+        "aaid.cpp",
+    ],
+    shared_libs: [
+        "libkeystore-attestation-application-id"
+    ],
+}
+
+rust_bindgen {
+    name: "libkeystore2_aaid_bindgen",
+    wrapper_src: "aaid.hpp",
+    crate_name: "keystore2_aaid_bindgen",
+    source_stem: "bindings",
+
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--whitelist-function=aaid_keystore_attestation_id",
+        "--whitelist-var=KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE",
+    ],
+}
+
+rust_library {
+    name: "libkeystore2_aaid-rust",
+    crate_name: "keystore2_aaid",
+    srcs: [
+        "lib.rs",
+    ],
+    rustlibs: [
+        "libkeystore2_aaid_bindgen",
+    ],
+    shared_libs: [
+        "libkeystore2_aaid",
+    ],
+}
diff --git a/keystore2/aaid/aaid.cpp b/keystore2/aaid/aaid.cpp
new file mode 100644
index 0000000..fd3faf6
--- /dev/null
+++ b/keystore2/aaid/aaid.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "aaid.hpp"
+
+#include <keystore/keystore_attestation_id.h>
+
+using android::security::gather_attestation_application_id;
+
+uint32_t aaid_keystore_attestation_id(uint32_t uid, uint8_t* aaid, size_t* aaid_size) {
+    static_assert(sizeof(uint32_t) == sizeof(uid_t), "uid_t has unexpected size");
+    static_assert(sizeof(uint32_t) == sizeof(android::status_t), "status_t has unexpected size");
+    static_assert(KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE ==
+                      android::security::KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE,
+                  "KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE sizes don't match.");
+    auto result = gather_attestation_application_id(uid);
+    if (!result.isOk()) {
+        return result.status();
+    }
+    if (result.value().size() > KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE) {
+        return ::android::NO_MEMORY;
+    }
+    if (*aaid_size != KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE) {
+        return ::android::BAD_VALUE;
+    }
+    std::copy(result.value().begin(), result.value().end(), aaid);
+    *aaid_size = result.value().size();
+    return ::android::OK;
+}
diff --git a/keystore2/aaid/aaid.hpp b/keystore2/aaid/aaid.hpp
new file mode 100644
index 0000000..9e2c148
--- /dev/null
+++ b/keystore2/aaid/aaid.hpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 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.
+ */
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+ * This is a redefinition of KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE in
+ * system/security/keystore/keystore_attestation_id.h and must be kept in sync.
+ * There is a static assert in aaid.cpp to assure that they are in sync.
+ * We redefine this here to avoid unnecessary build dependencies for
+ * the rust bindgen target.
+ */
+constexpr const size_t KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE = 1024;
+
+extern "C" {
+    /**
+     * Fills the buffer at aaid with the attestation application id of the app uid.
+     * The buffer must be exactly KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE bytes in size.
+     * *aaid_size is set to the number of bytes written to aaid.
+     *
+     * @param uid the uid of the app to retrieve the aaid for.
+     * @param aaid output buffer for the attestation id.
+     * @param aaid_size must be set to the size of the output buffer, which must be exactly
+     *          KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE bytes in size, by the caller. On success
+     *          it is set to the number of bytes written.
+     * @return OK on success.
+     */
+    uint32_t aaid_keystore_attestation_id(uint32_t uid, uint8_t* aaid, size_t* aaid_size);
+}
diff --git a/keystore2/aaid/lib.rs b/keystore2/aaid/lib.rs
new file mode 100644
index 0000000..3187198
--- /dev/null
+++ b/keystore2/aaid/lib.rs
@@ -0,0 +1,35 @@
+// 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.
+
+//! Rust binding for getting the attestation application id.
+
+use keystore2_aaid_bindgen::{
+    aaid_keystore_attestation_id, KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE,
+};
+
+/// Returns the attestation application id for the given uid or an error code
+/// corresponding to ::android::status_t.
+pub fn get_aaid(uid: u32) -> Result<Vec<u8>, u32> {
+    let mut buffer = [0u8; KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE];
+    let mut size = KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE;
+    // Safety:
+    // aaid_keystore_attestation_id expects a buffer of exactly
+    // KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE bytes and returns the number of bytes written
+    // in the second pointer argument.
+    let status = unsafe { aaid_keystore_attestation_id(uid, buffer.as_mut_ptr(), &mut size) };
+    match status {
+        0 => Ok(buffer[0..size as usize].to_vec()),
+        status => Err(status),
+    }
+}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index ffec931..4e12c64 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -42,7 +42,7 @@
 //! callbacks.
 
 use crate::db_utils::{self, SqlField};
-use crate::error::{Error as KsError, ResponseCode};
+use crate::error::{Error as KsError, ErrorCode, ResponseCode};
 use crate::impl_metadata; // This is in db_utils.rs
 use crate::key_parameter::{KeyParameter, Tag};
 use crate::permission::KeyPermSet;
@@ -617,6 +617,11 @@
         perboot_path_str.push_str(&perboot_path.to_string_lossy());
 
         let conn = Self::make_connection(&persistent_path_str, &perboot_path_str)?;
+        conn.busy_handler(Some(|_| {
+            std::thread::sleep(std::time::Duration::from_micros(50));
+            true
+        }))
+        .context("In KeystoreDB::new: Failed to set busy handler.")?;
 
         Self::init_tables(&conn)?;
         Ok(Self { conn })
@@ -1229,18 +1234,18 @@
             // Domain::KEY_ID. In this case we load the domain and namespace from the
             // keyentry database because we need them for access control.
             Domain::KEY_ID => {
-                let mut stmt = tx
-                    .prepare(
-                        "SELECT domain, namespace FROM persistent.keyentry
-                            WHERE
-                            id = ?
-                            AND state = ?;",
-                    )
-                    .context("Domain::KEY_ID: prepare statement failed")?;
-                let mut rows = stmt
-                    .query(params![key.nspace, KeyLifeCycle::Live])
-                    .context("Domain::KEY_ID: query failed.")?;
-                let (domain, namespace): (Domain, i64) =
+                let (domain, namespace): (Domain, i64) = {
+                    let mut stmt = tx
+                        .prepare(
+                            "SELECT domain, namespace FROM persistent.keyentry
+                                WHERE
+                                id = ?
+                                AND state = ?;",
+                        )
+                        .context("Domain::KEY_ID: prepare statement failed")?;
+                    let mut rows = stmt
+                        .query(params![key.nspace, KeyLifeCycle::Live])
+                        .context("Domain::KEY_ID: query failed.")?;
                     db_utils::with_rows_extract_one(&mut rows, |row| {
                         let r =
                             row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?;
@@ -1249,13 +1254,37 @@
                             r.get(1).context("Failed to unpack namespace.")?,
                         ))
                     })
-                    .context("Domain::KEY_ID.")?;
+                    .context("Domain::KEY_ID.")?
+                };
+
+                // We may use a key by id after loading it by grant.
+                // In this case we have to check if the caller has a grant for this particular
+                // key. We can skip this if we already know that the caller is the owner.
+                // But we cannot know this if domain is anything but App. E.g. in the case
+                // of Domain::SELINUX we have to speculatively check for grants because we have to
+                // consult the SEPolicy before we know if the caller is the owner.
+                let access_vector: Option<KeyPermSet> =
+                    if domain != Domain::APP || namespace != caller_uid as i64 {
+                        let access_vector: Option<i32> = tx
+                            .query_row(
+                                "SELECT access_vector FROM persistent.grant
+                                WHERE grantee = ? AND keyentryid = ?;",
+                                params![caller_uid as i64, key.nspace],
+                                |row| row.get(0),
+                            )
+                            .optional()
+                            .context("Domain::KEY_ID: query grant failed.")?;
+                        access_vector.map(|p| p.into())
+                    } else {
+                        None
+                    };
+
                 let key_id = key.nspace;
                 let mut access_key = key;
                 access_key.domain = domain;
                 access_key.nspace = namespace;
 
-                Ok((key_id, access_key, None))
+                Ok((key_id, access_key, access_vector))
             }
             _ => Err(anyhow!(KsError::sys())),
         }
@@ -1332,6 +1361,43 @@
         Ok(parameters)
     }
 
+    /// Decrements the usage count of a limited use key. This function first checks whether the
+    /// usage has been exhausted, if not, decreases the usage count. If the usage count reaches
+    /// zero, the key also gets marked unreferenced and scheduled for deletion.
+    /// Returns Ok(true) if the key was marked unreferenced as a hint to the garbage collector.
+    pub fn check_and_update_key_usage_count(&mut self, key_id: i64) -> Result<bool> {
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let limit: Option<i32> = tx
+                .query_row(
+                    "SELECT data FROM persistent.keyparameter WHERE keyentryid = ? AND tag = ?;",
+                    params![key_id, Tag::USAGE_COUNT_LIMIT.0],
+                    |row| row.get(0),
+                )
+                .optional()
+                .context("Trying to load usage count")?;
+
+            let limit = limit
+                .ok_or(KsError::Km(ErrorCode::INVALID_KEY_BLOB))
+                .context("The Key no longer exists. Key is exhausted.")?;
+
+            tx.execute(
+                "UPDATE persistent.keyparameter
+                 SET data = data - 1
+                 WHERE keyentryid = ? AND tag = ? AND data > 0;",
+                params![key_id, Tag::USAGE_COUNT_LIMIT.0],
+            )
+            .context("Failed to update key usage count.")?;
+
+            match limit {
+                1 => Self::mark_unreferenced(tx, key_id)
+                    .context("Trying to mark limited use key for deletion."),
+                0 => Err(KsError::Km(ErrorCode::INVALID_KEY_BLOB)).context("Key is exhausted."),
+                _ => Ok(false),
+            }
+        })
+        .context("In check_and_update_key_usage_count.")
+    }
+
     /// Load a key entry by the given key descriptor.
     /// It uses the `check_permission` callback to verify if the access is allowed
     /// given the key access tuple read from the database using `load_access_tuple`.
@@ -1755,7 +1821,7 @@
     };
     use crate::key_perm_set;
     use crate::permission::{KeyPerm, KeyPermSet};
-    use crate::test::utils::TempDir;
+    use keystore2_test_utils::TempDir;
     use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
         HardwareAuthToken::HardwareAuthToken,
         HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
@@ -2223,7 +2289,7 @@
     #[test]
     fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> {
         let mut db = new_test_db()?;
-        let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS)
+        let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
             .context("test_insert_and_load_full_keyentry_domain_app")?
             .0;
         let (_key_guard, key_entry) = db
@@ -2240,7 +2306,7 @@
                 |_k, _av| Ok(()),
             )
             .unwrap();
-        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
 
         db.unbind_key(
             KeyDescriptor {
@@ -2280,7 +2346,7 @@
     #[test]
     fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> {
         let mut db = new_test_db()?;
-        let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS)
+        let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
             .context("test_insert_and_load_full_keyentry_domain_selinux")?
             .0;
         let (_key_guard, key_entry) = db
@@ -2297,7 +2363,7 @@
                 |_k, _av| Ok(()),
             )
             .unwrap();
-        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
 
         db.unbind_key(
             KeyDescriptor {
@@ -2337,7 +2403,7 @@
     #[test]
     fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> {
         let mut db = new_test_db()?;
-        let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS)
+        let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
             .context("test_insert_and_load_full_keyentry_domain_key_id")?
             .0;
         let (_, key_entry) = db
@@ -2350,7 +2416,7 @@
             )
             .unwrap();
 
-        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
 
         db.unbind_key(
             KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
@@ -2378,9 +2444,57 @@
     }
 
     #[test]
+    fn test_check_and_update_key_usage_count_with_limited_use_key() -> Result<()> {
+        let mut db = new_test_db()?;
+        let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(123))
+            .context("test_check_and_update_key_usage_count_with_limited_use_key")?
+            .0;
+        // Update the usage count of the limited use key.
+        db.check_and_update_key_usage_count(key_id)?;
+
+        let (_key_guard, key_entry) = db.load_key_entry(
+            KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            1,
+            |_k, _av| Ok(()),
+        )?;
+
+        // The usage count is decremented now.
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, Some(122)));
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_check_and_update_key_usage_count_with_exhausted_limited_use_key() -> Result<()> {
+        let mut db = new_test_db()?;
+        let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(1))
+            .context("test_check_and_update_key_usage_count_with_exhausted_limited_use_key")?
+            .0;
+        // Update the usage count of the limited use key.
+        db.check_and_update_key_usage_count(key_id).expect(concat!(
+            "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
+            "This should succeed."
+        ));
+
+        // Try to update the exhausted limited use key.
+        let e = db.check_and_update_key_usage_count(key_id).expect_err(concat!(
+            "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
+            "This should fail."
+        ));
+        assert_eq!(
+            &KsError::Km(ErrorCode::INVALID_KEY_BLOB),
+            e.root_cause().downcast_ref::<KsError>().unwrap()
+        );
+
+        Ok(())
+    }
+
+    #[test]
     fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> {
         let mut db = new_test_db()?;
-        let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS)
+        let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
             .context("test_insert_and_load_full_keyentry_from_grant")?
             .0;
 
@@ -2415,7 +2529,7 @@
             )
             .unwrap();
 
-        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
 
         db.unbind_key(granted_key.clone(), KeyType::Client, 2, |_, _| Ok(())).unwrap();
 
@@ -2436,6 +2550,90 @@
         Ok(())
     }
 
+    // This test attempts to load a key by key id while the caller is not the owner
+    // but a grant exists for the given key and the caller.
+    #[test]
+    fn test_insert_and_load_full_keyentry_from_grant_by_key_id() -> Result<()> {
+        let mut db = new_test_db()?;
+        const OWNER_UID: u32 = 1u32;
+        const GRANTEE_UID: u32 = 2u32;
+        const SOMEONE_ELSE_UID: u32 = 3u32;
+        let key_id = make_test_key_entry(&mut db, Domain::APP, OWNER_UID as i64, TEST_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?
+            .0;
+
+        db.grant(
+            KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 0,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            OWNER_UID,
+            GRANTEE_UID,
+            key_perm_set![KeyPerm::use_()],
+            |_k, _av| Ok(()),
+        )
+        .unwrap();
+
+        debug_dump_grant_table(&mut db)?;
+
+        let id_descriptor =
+            KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, ..Default::default() };
+
+        let (_, key_entry) = db
+            .load_key_entry(
+                id_descriptor.clone(),
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                GRANTEE_UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(OWNER_UID as i64, k.nspace);
+                    assert!(av.unwrap().includes(KeyPerm::use_()));
+                    Ok(())
+                },
+            )
+            .unwrap();
+
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+        let (_, key_entry) = db
+            .load_key_entry(
+                id_descriptor.clone(),
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                SOMEONE_ELSE_UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(OWNER_UID as i64, k.nspace);
+                    assert!(av.is_none());
+                    Ok(())
+                },
+            )
+            .unwrap();
+
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+        db.unbind_key(id_descriptor.clone(), KeyType::Client, OWNER_UID, |_, _| Ok(())).unwrap();
+
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+            db.load_key_entry(
+                id_descriptor,
+                KeyType::Client,
+                KeyEntryLoadBits::NONE,
+                GRANTEE_UID,
+                |_k, _av| Ok(()),
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+
+        Ok(())
+    }
+
     static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
 
     #[test]
@@ -2444,7 +2642,7 @@
             let temp_dir = Arc::new(TempDir::new("id_lock_test")?);
             let temp_dir_clone = temp_dir.clone();
             let mut db = KeystoreDB::new(temp_dir.path())?;
-            let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS)
+            let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS, None)
                 .context("test_insert_and_load_full_keyentry_domain_app")?
                 .0;
             let (_key_guard, key_entry) = db
@@ -2461,7 +2659,7 @@
                     |_k, _av| Ok(()),
                 )
                 .unwrap();
-            assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+            assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
             let state = Arc::new(AtomicU8::new(1));
             let state2 = state.clone();
 
@@ -2543,8 +2741,8 @@
         let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES
             .iter()
             .map(|(domain, ns, alias)| {
-                let entry =
-                    make_test_key_entry(&mut db, *domain, *ns, *alias).unwrap_or_else(|e| {
+                let entry = make_test_key_entry(&mut db, *domain, *ns, *alias, None)
+                    .unwrap_or_else(|e| {
                         panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e)
                     });
                 (entry.id(), *ns)
@@ -2652,8 +2850,8 @@
     // Note: The parameters and SecurityLevel associations are nonsensical. This
     // collection is only used to check if the parameters are preserved as expected by the
     // database.
-    fn make_test_params() -> Vec<KeyParameter> {
-        vec![
+    fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
+        let mut params = vec![
             KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
             KeyParameter::new(
                 KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
@@ -2868,7 +3066,14 @@
                 KeyParameterValue::ConfirmationToken(vec![5u8, 5u8, 5u8, 5u8]),
                 SecurityLevel::TRUSTED_ENVIRONMENT,
             ),
-        ]
+        ];
+        if let Some(value) = max_usage_count {
+            params.push(KeyParameter::new(
+                KeyParameterValue::UsageCountLimit(value),
+                SecurityLevel::SOFTWARE,
+            ));
+        }
+        params
     }
 
     fn make_test_key_entry(
@@ -2876,12 +3081,16 @@
         domain: Domain,
         namespace: i64,
         alias: &str,
+        max_usage_count: Option<i32>,
     ) -> Result<KeyIdGuard> {
         let key_id = db.create_key_entry(domain, namespace)?;
         db.insert_blob(&key_id, SubComponentType::KEY_BLOB, TEST_KEY_BLOB)?;
         db.insert_blob(&key_id, SubComponentType::CERT, TEST_CERT_BLOB)?;
         db.insert_blob(&key_id, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB)?;
-        db.insert_keyparameter(&key_id, &make_test_params())?;
+
+        let params = make_test_params(max_usage_count);
+        db.insert_keyparameter(&key_id, &params)?;
+
         let mut metadata = KeyMetaData::new();
         metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password));
         metadata.add(KeyMetaEntry::Salt(vec![1, 2, 3]));
@@ -2892,7 +3101,9 @@
         Ok(key_id)
     }
 
-    fn make_test_key_entry_test_vector(key_id: i64) -> KeyEntry {
+    fn make_test_key_entry_test_vector(key_id: i64, max_usage_count: Option<i32>) -> KeyEntry {
+        let params = make_test_params(max_usage_count);
+
         let mut metadata = KeyMetaData::new();
         metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password));
         metadata.add(KeyMetaEntry::Salt(vec![1, 2, 3]));
@@ -2905,7 +3116,7 @@
             cert: Some(TEST_CERT_BLOB.to_vec()),
             cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
             sec_level: SecurityLevel::TRUSTED_ENVIRONMENT,
-            parameters: make_test_params(),
+            parameters: params,
             metadata,
         }
     }
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 387604e..3195ee0 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -14,10 +14,13 @@
 
 //! This is the Keystore 2.0 Enforcements module.
 // TODO: more description to follow.
-use crate::database::{AuthTokenEntry, MonotonicRawTime};
 use crate::error::{map_binder_status, Error, ErrorCode};
 use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS};
 use crate::key_parameter::{KeyParameter, KeyParameterValue};
+use crate::{
+    database::{AuthTokenEntry, MonotonicRawTime},
+    gc::Gc,
+};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
     HardwareAuthenticatorType::HardwareAuthenticatorType,
@@ -128,6 +131,8 @@
 #[derive(Debug)]
 pub struct AuthInfo {
     state: DeferredAuthState,
+    /// An optional key id required to update the usage count if the key usage is limited.
+    key_usage_limited: Option<i64>,
 }
 
 struct TokenReceiverMap {
@@ -251,14 +256,46 @@
         }
     }
 
+    /// This function is the authorization hook called before operation update.
+    /// It returns the auth tokens required by the operation to commence update.
+    pub fn before_update(&mut self) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
+        self.get_auth_tokens()
+    }
+
+    /// This function is the authorization hook called before operation finish.
+    /// It returns the auth tokens required by the operation to commence finish.
+    pub fn before_finish(&mut self) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
+        self.get_auth_tokens()
+    }
+
+    /// This function is the authorization hook called after finish succeeded.
+    /// As of this writing it checks if the key was a limited use key. If so it updates the
+    /// use counter of the key in the database. When the use counter is depleted, the key gets
+    /// marked for deletion and the garbage collector is notified.
+    pub fn after_finish(&self) -> Result<()> {
+        if let Some(key_id) = self.key_usage_limited {
+            // On the last successful use, the key gets deleted. In this case we
+            // have to notify the garbage collector.
+            let need_gc = DB
+                .with(|db| {
+                    db.borrow_mut()
+                        .check_and_update_key_usage_count(key_id)
+                        .context("Trying to update key usage count.")
+                })
+                .context("In after_finish.")?;
+            if need_gc {
+                Gc::notify_gc();
+            }
+        }
+        Ok(())
+    }
+
     /// This function returns the auth tokens as needed by the ongoing operation or fails
     /// with ErrorCode::KEY_USER_NOT_AUTHENTICATED. If this was called for the first time
     /// after a deferred authorization was requested by finalize_create_authorization, this
     /// function may block on the generation of a time stamp token. It then moves the
     /// tokens into the DeferredAuthState::Token state for future use.
-    pub fn get_auth_tokens(
-        &mut self,
-    ) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
+    fn get_auth_tokens(&mut self) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
         let deferred_tokens = if let DeferredAuthState::Waiting(ref auth_request) = self.state {
             let mut state = auth_request.lock().unwrap();
             Some(state.get_auth_tokens().context("In AuthInfo::get_auth_tokens.")?)
@@ -325,14 +362,18 @@
     pub fn authorize_create(
         &self,
         purpose: KeyPurpose,
-        key_params: Option<&[KeyParameter]>,
+        key_properties: Option<&(i64, Vec<KeyParameter>)>,
         op_params: &[KmKeyParameter],
         requires_timestamp: bool,
     ) -> Result<(Option<HardwareAuthToken>, AuthInfo)> {
-        let key_params = if let Some(k) = key_params {
-            k
-        } else {
-            return Ok((None, AuthInfo { state: DeferredAuthState::NoAuthRequired }));
+        let (key_id, key_params) = match key_properties {
+            Some((key_id, key_params)) => (*key_id, key_params),
+            None => {
+                return Ok((
+                    None,
+                    AuthInfo { state: DeferredAuthState::NoAuthRequired, key_usage_limited: None },
+                ))
+            }
         };
 
         match purpose {
@@ -343,6 +384,18 @@
                 return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE))
                     .context("In authorize_create: WRAP_KEY purpose is not allowed here.");
             }
+            // Allow AGREE_KEY for EC keys only.
+            KeyPurpose::AGREE_KEY => {
+                for kp in key_params.iter() {
+                    if kp.get_tag() == Tag::ALGORITHM
+                        && *kp.key_parameter_value() != KeyParameterValue::Algorithm(Algorithm::EC)
+                    {
+                        return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)).context(
+                            "In authorize_create: key agreement is only supported for EC keys.",
+                        );
+                    }
+                }
+            }
             KeyPurpose::VERIFY | KeyPurpose::ENCRYPT => {
                 // We do not support ENCRYPT and VERIFY (the remaining two options of purpose) for
                 // asymmetric keys.
@@ -377,6 +430,7 @@
         let mut key_time_out: Option<i64> = None;
         let mut allow_while_on_body = false;
         let mut unlocked_device_required = false;
+        let mut key_usage_limited: Option<i64> = None;
 
         // iterate through key parameters, recording information we need for authorization
         // enforcements later, or enforcing authorizations in place, where applicable
@@ -434,6 +488,12 @@
                 KeyParameterValue::AllowWhileOnBody => {
                     allow_while_on_body = true;
                 }
+                KeyParameterValue::UsageCountLimit(_) => {
+                    // We don't examine the limit here because this is enforced on finish.
+                    // Instead, we store the key_id so that finish can look up the key
+                    // in the database again and check and update the counter.
+                    key_usage_limited = Some(key_id);
+                }
                 // NOTE: as per offline discussion, sanitizing key parameters and rejecting
                 // create operation if any non-allowed tags are present, is not done in
                 // authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
@@ -488,7 +548,10 @@
         }
 
         if !unlocked_device_required && no_auth_required {
-            return Ok((None, AuthInfo { state: DeferredAuthState::NoAuthRequired }));
+            return Ok((
+                None,
+                AuthInfo { state: DeferredAuthState::NoAuthRequired, key_usage_limited },
+            ));
         }
 
         let has_sids = !user_secure_ids.is_empty();
@@ -562,7 +625,7 @@
             (None, _, true) => (None, DeferredAuthState::OpAuthRequired),
             (None, _, false) => (None, DeferredAuthState::NoAuthRequired),
         })
-        .map(|(hat, state)| (hat, AuthInfo { state }))
+        .map(|(hat, state)| (hat, AuthInfo { state, key_usage_limited }))
     }
 
     fn find_auth_token<F>(p: F) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>>
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index 71a17fe..93de6f2 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -840,6 +840,9 @@
     /// Maximum number of times that a key may be used between system reboots
     #[key_param(tag = MAX_USES_PER_BOOT, field = Integer)]
     MaxUsesPerBoot(i32),
+    /// The number of times that a limited use key can be used
+    #[key_param(tag = USAGE_COUNT_LIMIT, field = Integer)]
+    UsageCountLimit(i32),
     /// ID of the Android user that is permitted to use the key
     #[key_param(tag = USER_ID, field = Integer)]
     UserID(i32),
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index a27bfd1..2273d73 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -52,19 +52,23 @@
 
 // Utility functions
 
-// Converts a V4 error code into a ScopedAStatus
-ScopedAStatus convertErrorCode(V4_0_ErrorCode result) {
-    if (result == V4_0_ErrorCode::OK) {
+ScopedAStatus convertErrorCode(KMV1::ErrorCode result) {
+    if (result == KMV1::ErrorCode::OK) {
         return ScopedAStatus::ok();
     }
     return ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(result));
 }
 
-static V4_0_ErrorCode toErrorCode(const ScopedAStatus& status) {
+// Converts a V4 error code into a ScopedAStatus
+ScopedAStatus convertErrorCode(V4_0_ErrorCode result) {
+    return convertErrorCode(convert(result));
+}
+
+static KMV1::ErrorCode toErrorCode(const ScopedAStatus& status) {
     if (status.getExceptionCode() == EX_SERVICE_SPECIFIC) {
-        return static_cast<V4_0_ErrorCode>(status.getServiceSpecificError());
+        return static_cast<KMV1::ErrorCode>(status.getServiceSpecificError());
     } else {
-        return V4_0_ErrorCode::UNKNOWN_ERROR;
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
     }
 }
 
@@ -174,25 +178,27 @@
         _aidl_return->keyMintAuthorName = keymasterAuthorName;
     });
     if (!result.isOk()) {
-        return ScopedAStatus::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
     }
     return ScopedAStatus::ok();
 }
 
 ScopedAStatus KeyMintDevice::addRngEntropy(const std::vector<uint8_t>& in_data) {
-    V4_0_ErrorCode errorCode = mDevice->addRngEntropy(in_data);
-    return convertErrorCode(errorCode);
+    auto result = mDevice->addRngEntropy(in_data);
+    if (!result.isOk()) {
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    return convertErrorCode(result);
 }
 
 ScopedAStatus KeyMintDevice::generateKey(const std::vector<KeyParameter>& in_keyParams,
                                          KeyCreationResult* out_creationResult) {
     auto legacyKeyParams = convertKeyParametersToLegacy(in_keyParams);
-    V4_0_ErrorCode errorCode;
+    KMV1::ErrorCode errorCode;
     auto result = mDevice->generateKey(
         legacyKeyParams, [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                              const V4_0_KeyCharacteristics& keyCharacteristics) {
-            errorCode = error;
+            errorCode = convert(error);
             out_creationResult->keyBlob = keyBlob;
             out_creationResult->keyCharacteristics =
                 convertKeyCharacteristicsFromLegacy(securityLevel_, keyCharacteristics);
@@ -201,12 +207,12 @@
         return ScopedAStatus::fromServiceSpecificError(
             static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
     }
-    if (errorCode == V4_0_ErrorCode::OK) {
+    if (errorCode == KMV1::ErrorCode::OK) {
         auto cert = getCertificate(in_keyParams, out_creationResult->keyBlob);
-        if (std::holds_alternative<V4_0_ErrorCode>(cert)) {
-            auto code = std::get<V4_0_ErrorCode>(cert);
+        if (std::holds_alternative<KMV1::ErrorCode>(cert)) {
+            auto code = std::get<KMV1::ErrorCode>(cert);
             // We return OK in successful cases that do not generate a certificate.
-            if (code != V4_0_ErrorCode::OK) {
+            if (code != KMV1::ErrorCode::OK) {
                 errorCode = code;
                 deleteKey(out_creationResult->keyBlob);
             }
@@ -223,26 +229,25 @@
                                        KeyCreationResult* out_creationResult) {
     auto legacyKeyParams = convertKeyParametersToLegacy(in_inKeyParams);
     auto legacyKeyFormat = convertKeyFormatToLegacy(in_inKeyFormat);
-    V4_0_ErrorCode errorCode;
+    KMV1::ErrorCode errorCode;
     auto result = mDevice->importKey(legacyKeyParams, legacyKeyFormat, in_inKeyData,
                                      [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                                          const V4_0_KeyCharacteristics& keyCharacteristics) {
-                                         errorCode = error;
+                                         errorCode = convert(error);
                                          out_creationResult->keyBlob = keyBlob;
                                          out_creationResult->keyCharacteristics =
                                              convertKeyCharacteristicsFromLegacy(
                                                  securityLevel_, keyCharacteristics);
                                      });
     if (!result.isOk()) {
-        return ScopedAStatus::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
     }
-    if (errorCode == V4_0_ErrorCode::OK) {
+    if (errorCode == KMV1::ErrorCode::OK) {
         auto cert = getCertificate(in_inKeyParams, out_creationResult->keyBlob);
-        if (std::holds_alternative<V4_0_ErrorCode>(cert)) {
-            auto code = std::get<V4_0_ErrorCode>(cert);
+        if (std::holds_alternative<KMV1::ErrorCode>(cert)) {
+            auto code = std::get<KMV1::ErrorCode>(cert);
             // We return OK in successful cases that do not generate a certificate.
-            if (code != V4_0_ErrorCode::OK) {
+            if (code != KMV1::ErrorCode::OK) {
                 errorCode = code;
                 deleteKey(out_creationResult->keyBlob);
             }
@@ -259,20 +264,19 @@
     const std::vector<KeyParameter>& in_inUnwrappingParams, int64_t in_inPasswordSid,
     int64_t in_inBiometricSid, KeyCreationResult* out_creationResult) {
     auto legacyUnwrappingParams = convertKeyParametersToLegacy(in_inUnwrappingParams);
-    V4_0_ErrorCode errorCode;
+    KMV1::ErrorCode errorCode;
     auto result = mDevice->importWrappedKey(
         in_inWrappedKeyData, in_inWrappingKeyBlob, in_inMaskingKey, legacyUnwrappingParams,
         in_inPasswordSid, in_inBiometricSid,
         [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
             const V4_0_KeyCharacteristics& keyCharacteristics) {
-            errorCode = error;
+            errorCode = convert(error);
             out_creationResult->keyBlob = keyBlob;
             out_creationResult->keyCharacteristics =
                 convertKeyCharacteristicsFromLegacy(securityLevel_, keyCharacteristics);
         });
     if (!result.isOk()) {
-        return ScopedAStatus::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
     }
     return convertErrorCode(errorCode);
 }
@@ -289,20 +293,25 @@
                                 *_aidl_return = upgradedKeyBlob;
                             });
     if (!result.isOk()) {
-        return ScopedAStatus::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
     }
     return convertErrorCode(errorCode);
 }
 
 ScopedAStatus KeyMintDevice::deleteKey(const std::vector<uint8_t>& in_inKeyBlob) {
-    V4_0_ErrorCode errorCode = mDevice->deleteKey(in_inKeyBlob);
-    return convertErrorCode(errorCode);
+    auto result = mDevice->deleteKey(in_inKeyBlob);
+    if (!result.isOk()) {
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    return convertErrorCode(result);
 }
 
 ScopedAStatus KeyMintDevice::deleteAllKeys() {
-    V4_0_ErrorCode errorCode = mDevice->deleteAllKeys();
-    return convertErrorCode(errorCode);
+    auto result = mDevice->deleteAllKeys();
+    if (!result.isOk()) {
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    return convertErrorCode(result);
 }
 
 // We're not implementing this.
@@ -323,24 +332,21 @@
         static_cast<::android::hardware::keymaster::V4_0::KeyPurpose>(in_inPurpose);
     auto legacyParams = convertKeyParametersToLegacy(in_inParams);
     auto legacyAuthToken = convertAuthTokenToLegacy(in_inAuthToken);
-    V4_0_ErrorCode errorCode;
+    KMV1::ErrorCode errorCode;
     auto result = mDevice->begin(
         legacyPurpose, in_inKeyBlob, legacyParams, legacyAuthToken,
         [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
             uint64_t operationHandle) {
-            errorCode = error;
-            _aidl_return->challenge = operationHandle;  // TODO: Is this right?
+            errorCode = convert(error);
+            _aidl_return->challenge = operationHandle;
             _aidl_return->params = convertKeyParametersFromLegacy(outParams);
             _aidl_return->operation = ndk::SharedRefBase::make<KeyMintOperation>(
                 mDevice, operationHandle, &mOperationSlots, error == V4_0_ErrorCode::OK);
         });
     if (!result.isOk()) {
-        // TODO: In this case we're guaranteed that _aidl_return was not initialized, right?
-        mOperationSlots.freeSlot();
-        return ScopedAStatus::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
     }
-    if (errorCode != V4_0_ErrorCode::OK) {
+    if (errorCode != KMV1::ErrorCode::OK) {
         mOperationSlots.freeSlot();
     }
     return convertErrorCode(errorCode);
@@ -366,12 +372,12 @@
     if (in_inTimeStampToken.has_value()) {
         verificationToken = convertTimestampTokenToLegacy(in_inTimeStampToken.value());
     }
-    V4_0_ErrorCode errorCode;
+    KMV1::ErrorCode errorCode;
     auto result = mDevice->update(
         mOperationHandle, legacyParams, input, authToken, verificationToken,
         [&](V4_0_ErrorCode error, uint32_t inputConsumed,
             const hidl_vec<V4_0_KeyParameter>& outParams, const hidl_vec<uint8_t>& output) {
-            errorCode = error;
+            errorCode = convert(error);
             out_outParams->emplace();
             out_outParams->value().params = convertKeyParametersFromLegacy(outParams);
             out_output->emplace();
@@ -379,11 +385,9 @@
             *_aidl_return = inputConsumed;
         });
     if (!result.isOk()) {
-        mOperationSlot.freeSlot();
-        return ScopedAStatus::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
     }
-    if (errorCode != V4_0_ErrorCode::OK) {
+    if (errorCode != KMV1::ErrorCode::OK) {
         mOperationSlot.freeSlot();
     }
     return convertErrorCode(errorCode);
@@ -396,7 +400,7 @@
                                        const std::optional<TimeStampToken>& in_inTimeStampToken,
                                        std::optional<KeyParameterArray>* out_outParams,
                                        std::vector<uint8_t>* _aidl_return) {
-    V4_0_ErrorCode errorCode;
+    KMV1::ErrorCode errorCode;
     std::vector<V4_0_KeyParameter> legacyParams;
     if (in_inParams.has_value()) {
         legacyParams = convertKeyParametersToLegacy(in_inParams.value().params);
@@ -415,23 +419,25 @@
         mOperationHandle, legacyParams, input, signature, authToken, verificationToken,
         [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
             const hidl_vec<uint8_t>& output) {
-            errorCode = error;
+            errorCode = convert(error);
             out_outParams->emplace();
             out_outParams->value().params = convertKeyParametersFromLegacy(outParams);
             *_aidl_return = output;
         });
     mOperationSlot.freeSlot();
     if (!result.isOk()) {
-        return ScopedAStatus::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
     }
     return convertErrorCode(errorCode);
 }
 
 ScopedAStatus KeyMintOperation::abort() {
-    V4_0_ErrorCode errorCode = mDevice->abort(mOperationHandle);
+    auto result = mDevice->abort(mOperationHandle);
     mOperationSlot.freeSlot();
-    return convertErrorCode(errorCode);
+    if (!result.isOk()) {
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    return convertErrorCode(result);
 }
 
 KeyMintOperation::~KeyMintOperation() {
@@ -446,18 +452,17 @@
 // SecureClock implementation
 
 ScopedAStatus SecureClock::generateTimeStamp(int64_t in_challenge, TimeStampToken* _aidl_return) {
-    V4_0_ErrorCode errorCode;
+    KMV1::ErrorCode errorCode;
     auto result = mDevice->verifyAuthorization(
         in_challenge, {}, V4_0_HardwareAuthToken(),
         [&](V4_0_ErrorCode error, const V4_0_VerificationToken& token) {
-            errorCode = error;
+            errorCode = convert(error);
             _aidl_return->challenge = token.challenge;
             _aidl_return->timestamp.milliSeconds = token.timestamp;
             _aidl_return->mac = token.mac;
         });
     if (!result.isOk()) {
-        return ScopedAStatus::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
     }
     return convertErrorCode(errorCode);
 }
@@ -465,17 +470,16 @@
 // SharedSecret implementation
 
 ScopedAStatus SharedSecret::getSharedSecretParameters(SharedSecretParameters* _aidl_return) {
-    V4_0_ErrorCode errorCode;
+    KMV1::ErrorCode errorCode;
     auto result = mDevice->getHmacSharingParameters(
         [&](V4_0_ErrorCode error, const V4_0_HmacSharingParameters& params) {
-            errorCode = error;
+            errorCode = convert(error);
             _aidl_return->seed = params.seed;
             std::copy(params.nonce.data(), params.nonce.data() + params.nonce.elementCount(),
                       std::back_inserter(_aidl_return->nonce));
         });
     if (!result.isOk()) {
-        return ScopedAStatus::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
     }
     return convertErrorCode(errorCode);
 }
@@ -483,16 +487,15 @@
 ScopedAStatus
 SharedSecret::computeSharedSecret(const std::vector<SharedSecretParameters>& in_params,
                                   std::vector<uint8_t>* _aidl_return) {
-    V4_0_ErrorCode errorCode;
+    KMV1::ErrorCode errorCode;
     auto legacyParams = convertSharedSecretParametersToLegacy(in_params);
     auto result = mDevice->computeSharedHmac(
         legacyParams, [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& sharingCheck) {
-            errorCode = error;
+            errorCode = convert(error);
             *_aidl_return = sharingCheck;
         });
     if (!result.isOk()) {
-        return ScopedAStatus::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
     }
     return convertErrorCode(errorCode);
 }
@@ -536,12 +539,12 @@
     return *bestSoFar;
 }
 
-static std::variant<keystore::X509_Ptr, V4_0_ErrorCode>
+static std::variant<keystore::X509_Ptr, KMV1::ErrorCode>
 makeCert(::android::sp<Keymaster> mDevice, const std::vector<KeyParameter>& keyParams,
          const std::vector<uint8_t>& keyBlob) {
     // Start generating the certificate.
     // Get public key for makeCert.
-    V4_0_ErrorCode errorCode;
+    KMV1::ErrorCode errorCode;
     std::vector<uint8_t> key;
     static std::vector<uint8_t> empty_vector;
     auto unwrapBlob = [&](auto b) -> const std::vector<uint8_t>& {
@@ -554,13 +557,13 @@
         V4_0_KeyFormat::X509, keyBlob, unwrapBlob(getParam(keyParams, KMV1::TAG_APPLICATION_ID)),
         unwrapBlob(getParam(keyParams, KMV1::TAG_APPLICATION_DATA)),
         [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyMaterial) {
-            errorCode = error;
+            errorCode = convert(error);
             key = keyMaterial;
         });
     if (!result.isOk()) {
-        return V4_0_ErrorCode::UNKNOWN_ERROR;
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
     }
-    if (errorCode != V4_0_ErrorCode::OK) {
+    if (errorCode != KMV1::ErrorCode::OK) {
         return errorCode;
     }
     // Get pkey for makeCert.
@@ -585,12 +588,12 @@
         std::nullopt /* intentionally left blank */, std::nullopt /* intentionally left blank */);
     if (std::holds_alternative<keystore::CertUtilsError>(certOrError)) {
         LOG(ERROR) << __func__ << ": Failed to make certificate";
-        return V4_0_ErrorCode::UNKNOWN_ERROR;
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
     }
     return std::move(std::get<keystore::X509_Ptr>(certOrError));
 }
 
-static std::variant<keystore::Algo, V4_0_ErrorCode> getKeystoreAlgorithm(Algorithm algorithm) {
+static std::variant<keystore::Algo, KMV1::ErrorCode> getKeystoreAlgorithm(Algorithm algorithm) {
     switch (algorithm) {
     case Algorithm::RSA:
         return keystore::Algo::RSA;
@@ -598,11 +601,11 @@
         return keystore::Algo::ECDSA;
     default:
         LOG(ERROR) << __func__ << ": This should not be called with symmetric algorithm.";
-        return V4_0_ErrorCode::UNKNOWN_ERROR;
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
     }
 }
 
-static std::variant<keystore::Padding, V4_0_ErrorCode> getKeystorePadding(PaddingMode padding) {
+static std::variant<keystore::Padding, KMV1::ErrorCode> getKeystorePadding(PaddingMode padding) {
     switch (padding) {
     case PaddingMode::RSA_PKCS1_1_5_SIGN:
         return keystore::Padding::PKCS1_5;
@@ -613,7 +616,7 @@
     }
 }
 
-static std::variant<keystore::Digest, V4_0_ErrorCode> getKeystoreDigest(Digest digest) {
+static std::variant<keystore::Digest, KMV1::ErrorCode> getKeystoreDigest(Digest digest) {
     switch (digest) {
     case Digest::SHA1:
         return keystore::Digest::SHA1;
@@ -628,36 +631,36 @@
         return keystore::Digest::SHA512;
     default:
         LOG(ERROR) << __func__ << ": Unknown digest.";
-        return V4_0_ErrorCode::UNKNOWN_ERROR;
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
     }
 }
 
-std::optional<V4_0_ErrorCode>
+std::optional<KMV1::ErrorCode>
 KeyMintDevice::signCertificate(const std::vector<KeyParameter>& keyParams,
                                const std::vector<uint8_t>& keyBlob, X509* cert) {
     auto algorithm = getParam(keyParams, KMV1::TAG_ALGORITHM);
     auto algoOrError = getKeystoreAlgorithm(*algorithm);
-    if (std::holds_alternative<V4_0_ErrorCode>(algoOrError)) {
-        return std::get<V4_0_ErrorCode>(algoOrError);
+    if (std::holds_alternative<KMV1::ErrorCode>(algoOrError)) {
+        return std::get<KMV1::ErrorCode>(algoOrError);
     }
     auto algo = std::get<keystore::Algo>(algoOrError);
     auto origPadding = getMaximum(keyParams, KMV1::TAG_PADDING,
                                   {PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN});
     auto paddingOrError = getKeystorePadding(origPadding);
-    if (std::holds_alternative<V4_0_ErrorCode>(paddingOrError)) {
-        return std::get<V4_0_ErrorCode>(paddingOrError);
+    if (std::holds_alternative<KMV1::ErrorCode>(paddingOrError)) {
+        return std::get<KMV1::ErrorCode>(paddingOrError);
     }
     auto padding = std::get<keystore::Padding>(paddingOrError);
     auto origDigest = getMaximum(
         keyParams, KMV1::TAG_DIGEST,
         {Digest::SHA_2_256, Digest::SHA_2_512, Digest::SHA_2_384, Digest::SHA_2_224, Digest::SHA1});
     auto digestOrError = getKeystoreDigest(origDigest);
-    if (std::holds_alternative<V4_0_ErrorCode>(digestOrError)) {
-        return std::get<V4_0_ErrorCode>(digestOrError);
+    if (std::holds_alternative<KMV1::ErrorCode>(digestOrError)) {
+        return std::get<KMV1::ErrorCode>(digestOrError);
     }
     auto digest = std::get<keystore::Digest>(digestOrError);
 
-    V4_0_ErrorCode errorCode = V4_0_ErrorCode::OK;
+    KMV1::ErrorCode errorCode = KMV1::ErrorCode::OK;
     auto error = keystore::signCertWith(
         &*cert,
         [&](const uint8_t* data, size_t len) {
@@ -694,40 +697,40 @@
     if (error) {
         LOG(ERROR) << __func__
                    << ": signCertWith failed. (Callback diagnosed: " << toString(errorCode) << ")";
-        return V4_0_ErrorCode::UNKNOWN_ERROR;
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
     }
-    if (errorCode != V4_0_ErrorCode::OK) {
+    if (errorCode != KMV1::ErrorCode::OK) {
         return errorCode;
     }
     return std::nullopt;
 }
 
-std::variant<std::vector<Certificate>, V4_0_ErrorCode>
+std::variant<std::vector<Certificate>, KMV1::ErrorCode>
 KeyMintDevice::getCertificate(const std::vector<KeyParameter>& keyParams,
                               const std::vector<uint8_t>& keyBlob) {
     // There are no certificates for symmetric keys.
     auto algorithm = getParam(keyParams, KMV1::TAG_ALGORITHM);
     if (!algorithm) {
         LOG(ERROR) << __func__ << ": Unable to determine key algorithm.";
-        return V4_0_ErrorCode::UNKNOWN_ERROR;
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
     }
     switch (*algorithm) {
     case Algorithm::RSA:
     case Algorithm::EC:
         break;
     default:
-        return V4_0_ErrorCode::OK;
+        return KMV1::ErrorCode::OK;
     }
 
     // If attestation was requested, call and use attestKey.
     if (containsParam(keyParams, KMV1::TAG_ATTESTATION_CHALLENGE)) {
         auto legacyParams = convertKeyParametersToLegacy(keyParams);
         std::vector<Certificate> certs;
-        V4_0_ErrorCode errorCode = V4_0_ErrorCode::OK;
+        KMV1::ErrorCode errorCode = KMV1::ErrorCode::OK;
         auto result = mDevice->attestKey(
             keyBlob, legacyParams,
-            [&](V4_0_ErrorCode error, const hidl_vec<hidl_vec<uint8_t>>& certChain) {
-                errorCode = error;
+            [&](V4_0::ErrorCode error, const hidl_vec<hidl_vec<uint8_t>>& certChain) {
+                errorCode = convert(error);
                 for (const auto& cert : certChain) {
                     Certificate certificate;
                     certificate.encodedCertificate = cert;
@@ -736,9 +739,9 @@
             });
         if (!result.isOk()) {
             LOG(ERROR) << __func__ << ": Call to attestKey failed.";
-            return V4_0_ErrorCode::UNKNOWN_ERROR;
+            return KMV1::ErrorCode::UNKNOWN_ERROR;
         }
-        if (errorCode != V4_0_ErrorCode::OK) {
+        if (errorCode != KMV1::ErrorCode::OK) {
             return errorCode;
         }
         return certs;
@@ -746,8 +749,8 @@
 
     // makeCert
     auto certOrError = makeCert(mDevice, keyParams, keyBlob);
-    if (std::holds_alternative<V4_0_ErrorCode>(certOrError)) {
-        return std::get<V4_0_ErrorCode>(certOrError);
+    if (std::holds_alternative<KMV1::ErrorCode>(certOrError)) {
+        return std::get<KMV1::ErrorCode>(certOrError);
     }
     auto cert = std::move(std::get<keystore::X509_Ptr>(certOrError));
 
@@ -755,7 +758,7 @@
     auto error = keystore::setIssuer(&*cert, &*cert, false);
     if (error) {
         LOG(ERROR) << __func__ << ": Set issuer failed.";
-        return V4_0_ErrorCode::UNKNOWN_ERROR;
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
     }
 
     // Signing
@@ -780,7 +783,7 @@
         error = keystore::signCert(&*cert, pkey_ptr);
         if (error) {
             LOG(ERROR) << __func__ << ": signCert failed.";
-            return V4_0_ErrorCode::UNKNOWN_ERROR;
+            return KMV1::ErrorCode::UNKNOWN_ERROR;
         }
     }
 
@@ -788,7 +791,7 @@
     auto encodedCertOrError = keystore::encodeCert(&*cert);
     if (std::holds_alternative<keystore::CertUtilsError>(encodedCertOrError)) {
         LOG(ERROR) << __func__ << ": encodeCert failed.";
-        return V4_0_ErrorCode::UNKNOWN_ERROR;
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
     }
 
     Certificate certificate{.encodedCertificate =
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 481481a..5637b58 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -18,6 +18,7 @@
 
 #include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
 #include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
 #include <aidl/android/hardware/security/secureclock/BnSecureClock.h>
 #include <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
 #include <aidl/android/security/compat/BnKeystoreCompatService.h>
@@ -41,6 +42,7 @@
 using KeyMintSecurityLevel = ::aidl::android::hardware::security::keymint::SecurityLevel;
 using V4_0_ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
 using ::aidl::android::hardware::security::keymint::IKeyMintDevice;
+using KMV1_ErrorCode = ::aidl::android::hardware::security::keymint::ErrorCode;
 using ::aidl::android::hardware::security::secureclock::ISecureClock;
 using ::aidl::android::hardware::security::secureclock::TimeStampToken;
 using ::aidl::android::hardware::security::sharedsecret::ISharedSecret;
@@ -112,13 +114,13 @@
     // These are public to allow testing code to use them directly.
     // This class should not be used publicly anyway.
 
-    std::variant<std::vector<Certificate>, V4_0_ErrorCode>
+    std::variant<std::vector<Certificate>, KMV1_ErrorCode>
     getCertificate(const std::vector<KeyParameter>& keyParams, const std::vector<uint8_t>& keyBlob);
 
     void setNumFreeSlots(uint8_t numFreeSlots);
 
   private:
-    std::optional<V4_0_ErrorCode> signCertificate(const std::vector<KeyParameter>& keyParams,
+    std::optional<KMV1_ErrorCode> signCertificate(const std::vector<KeyParameter>& keyParams,
                                                   const std::vector<uint8_t>& keyBlob, X509* cert);
     KeyMintSecurityLevel securityLevel_;
 };
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index 57c837b..df9c862 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
 #include <keymasterV4_1/keymaster_tags.h>
 #include <keymint_support/keymint_tags.h>
 
@@ -23,6 +24,159 @@
 namespace V4_1 = ::android::hardware::keymaster::V4_1;
 namespace KMV1 = ::aidl::android::hardware::security::keymint;
 
+static KMV1::ErrorCode convert(V4_0::ErrorCode error) {
+    switch (error) {
+    case V4_0::ErrorCode::OK:
+        return KMV1::ErrorCode::OK;
+    case V4_0::ErrorCode::ROOT_OF_TRUST_ALREADY_SET:
+        return KMV1::ErrorCode::ROOT_OF_TRUST_ALREADY_SET;
+    case V4_0::ErrorCode::UNSUPPORTED_PURPOSE:
+        return KMV1::ErrorCode::UNSUPPORTED_PURPOSE;
+    case V4_0::ErrorCode::INCOMPATIBLE_PURPOSE:
+        return KMV1::ErrorCode::INCOMPATIBLE_PURPOSE;
+    case V4_0::ErrorCode::UNSUPPORTED_ALGORITHM:
+        return KMV1::ErrorCode::UNSUPPORTED_ALGORITHM;
+    case V4_0::ErrorCode::INCOMPATIBLE_ALGORITHM:
+        return KMV1::ErrorCode::INCOMPATIBLE_ALGORITHM;
+    case V4_0::ErrorCode::UNSUPPORTED_KEY_SIZE:
+        return KMV1::ErrorCode::UNSUPPORTED_KEY_SIZE;
+    case V4_0::ErrorCode::UNSUPPORTED_BLOCK_MODE:
+        return KMV1::ErrorCode::UNSUPPORTED_BLOCK_MODE;
+    case V4_0::ErrorCode::INCOMPATIBLE_BLOCK_MODE:
+        return KMV1::ErrorCode::INCOMPATIBLE_BLOCK_MODE;
+    case V4_0::ErrorCode::UNSUPPORTED_MAC_LENGTH:
+        return KMV1::ErrorCode::UNSUPPORTED_MAC_LENGTH;
+    case V4_0::ErrorCode::UNSUPPORTED_PADDING_MODE:
+        return KMV1::ErrorCode::UNSUPPORTED_PADDING_MODE;
+    case V4_0::ErrorCode::INCOMPATIBLE_PADDING_MODE:
+        return KMV1::ErrorCode::INCOMPATIBLE_PADDING_MODE;
+    case V4_0::ErrorCode::UNSUPPORTED_DIGEST:
+        return KMV1::ErrorCode::UNSUPPORTED_DIGEST;
+    case V4_0::ErrorCode::INCOMPATIBLE_DIGEST:
+        return KMV1::ErrorCode::INCOMPATIBLE_DIGEST;
+    case V4_0::ErrorCode::INVALID_EXPIRATION_TIME:
+        return KMV1::ErrorCode::INVALID_EXPIRATION_TIME;
+    case V4_0::ErrorCode::INVALID_USER_ID:
+        return KMV1::ErrorCode::INVALID_USER_ID;
+    case V4_0::ErrorCode::INVALID_AUTHORIZATION_TIMEOUT:
+        return KMV1::ErrorCode::INVALID_AUTHORIZATION_TIMEOUT;
+    case V4_0::ErrorCode::UNSUPPORTED_KEY_FORMAT:
+        return KMV1::ErrorCode::UNSUPPORTED_KEY_FORMAT;
+    case V4_0::ErrorCode::INCOMPATIBLE_KEY_FORMAT:
+        return KMV1::ErrorCode::INCOMPATIBLE_KEY_FORMAT;
+    case V4_0::ErrorCode::UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM:
+        return KMV1::ErrorCode::UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM;
+    case V4_0::ErrorCode::UNSUPPORTED_KEY_VERIFICATION_ALGORITHM:
+        return KMV1::ErrorCode::UNSUPPORTED_KEY_VERIFICATION_ALGORITHM;
+    case V4_0::ErrorCode::INVALID_INPUT_LENGTH:
+        return KMV1::ErrorCode::INVALID_INPUT_LENGTH;
+    case V4_0::ErrorCode::KEY_EXPORT_OPTIONS_INVALID:
+        return KMV1::ErrorCode::KEY_EXPORT_OPTIONS_INVALID;
+    case V4_0::ErrorCode::DELEGATION_NOT_ALLOWED:
+        return KMV1::ErrorCode::DELEGATION_NOT_ALLOWED;
+    case V4_0::ErrorCode::KEY_NOT_YET_VALID:
+        return KMV1::ErrorCode::KEY_NOT_YET_VALID;
+    case V4_0::ErrorCode::KEY_EXPIRED:
+        return KMV1::ErrorCode::KEY_EXPIRED;
+    case V4_0::ErrorCode::KEY_USER_NOT_AUTHENTICATED:
+        return KMV1::ErrorCode::KEY_USER_NOT_AUTHENTICATED;
+    case V4_0::ErrorCode::OUTPUT_PARAMETER_NULL:
+        return KMV1::ErrorCode::OUTPUT_PARAMETER_NULL;
+    case V4_0::ErrorCode::INVALID_OPERATION_HANDLE:
+        return KMV1::ErrorCode::INVALID_OPERATION_HANDLE;
+    case V4_0::ErrorCode::INSUFFICIENT_BUFFER_SPACE:
+        return KMV1::ErrorCode::INSUFFICIENT_BUFFER_SPACE;
+    case V4_0::ErrorCode::VERIFICATION_FAILED:
+        return KMV1::ErrorCode::VERIFICATION_FAILED;
+    case V4_0::ErrorCode::TOO_MANY_OPERATIONS:
+        return KMV1::ErrorCode::TOO_MANY_OPERATIONS;
+    case V4_0::ErrorCode::UNEXPECTED_NULL_POINTER:
+        return KMV1::ErrorCode::UNEXPECTED_NULL_POINTER;
+    case V4_0::ErrorCode::INVALID_KEY_BLOB:
+        return KMV1::ErrorCode::INVALID_KEY_BLOB;
+    case V4_0::ErrorCode::IMPORTED_KEY_NOT_ENCRYPTED:
+        return KMV1::ErrorCode::IMPORTED_KEY_NOT_ENCRYPTED;
+    case V4_0::ErrorCode::IMPORTED_KEY_DECRYPTION_FAILED:
+        return KMV1::ErrorCode::IMPORTED_KEY_DECRYPTION_FAILED;
+    case V4_0::ErrorCode::IMPORTED_KEY_NOT_SIGNED:
+        return KMV1::ErrorCode::IMPORTED_KEY_NOT_SIGNED;
+    case V4_0::ErrorCode::IMPORTED_KEY_VERIFICATION_FAILED:
+        return KMV1::ErrorCode::IMPORTED_KEY_VERIFICATION_FAILED;
+    case V4_0::ErrorCode::INVALID_ARGUMENT:
+        return KMV1::ErrorCode::INVALID_ARGUMENT;
+    case V4_0::ErrorCode::UNSUPPORTED_TAG:
+        return KMV1::ErrorCode::UNSUPPORTED_TAG;
+    case V4_0::ErrorCode::INVALID_TAG:
+        return KMV1::ErrorCode::INVALID_TAG;
+    case V4_0::ErrorCode::MEMORY_ALLOCATION_FAILED:
+        return KMV1::ErrorCode::MEMORY_ALLOCATION_FAILED;
+    case V4_0::ErrorCode::IMPORT_PARAMETER_MISMATCH:
+        return KMV1::ErrorCode::IMPORT_PARAMETER_MISMATCH;
+    case V4_0::ErrorCode::SECURE_HW_ACCESS_DENIED:
+        return KMV1::ErrorCode::SECURE_HW_ACCESS_DENIED;
+    case V4_0::ErrorCode::OPERATION_CANCELLED:
+        return KMV1::ErrorCode::OPERATION_CANCELLED;
+    case V4_0::ErrorCode::CONCURRENT_ACCESS_CONFLICT:
+        return KMV1::ErrorCode::CONCURRENT_ACCESS_CONFLICT;
+    case V4_0::ErrorCode::SECURE_HW_BUSY:
+        return KMV1::ErrorCode::SECURE_HW_BUSY;
+    case V4_0::ErrorCode::SECURE_HW_COMMUNICATION_FAILED:
+        return KMV1::ErrorCode::SECURE_HW_COMMUNICATION_FAILED;
+    case V4_0::ErrorCode::UNSUPPORTED_EC_FIELD:
+        return KMV1::ErrorCode::UNSUPPORTED_EC_FIELD;
+    case V4_0::ErrorCode::MISSING_NONCE:
+        return KMV1::ErrorCode::MISSING_NONCE;
+    case V4_0::ErrorCode::INVALID_NONCE:
+        return KMV1::ErrorCode::INVALID_NONCE;
+    case V4_0::ErrorCode::MISSING_MAC_LENGTH:
+        return KMV1::ErrorCode::MISSING_MAC_LENGTH;
+    case V4_0::ErrorCode::KEY_RATE_LIMIT_EXCEEDED:
+        return KMV1::ErrorCode::KEY_RATE_LIMIT_EXCEEDED;
+    case V4_0::ErrorCode::CALLER_NONCE_PROHIBITED:
+        return KMV1::ErrorCode::CALLER_NONCE_PROHIBITED;
+    case V4_0::ErrorCode::KEY_MAX_OPS_EXCEEDED:
+        return KMV1::ErrorCode::KEY_MAX_OPS_EXCEEDED;
+    case V4_0::ErrorCode::INVALID_MAC_LENGTH:
+        return KMV1::ErrorCode::INVALID_MAC_LENGTH;
+    case V4_0::ErrorCode::MISSING_MIN_MAC_LENGTH:
+        return KMV1::ErrorCode::MISSING_MIN_MAC_LENGTH;
+    case V4_0::ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH:
+        return KMV1::ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH;
+    case V4_0::ErrorCode::UNSUPPORTED_KDF:
+        return KMV1::ErrorCode::UNSUPPORTED_KDF;
+    case V4_0::ErrorCode::UNSUPPORTED_EC_CURVE:
+        return KMV1::ErrorCode::UNSUPPORTED_EC_CURVE;
+    case V4_0::ErrorCode::KEY_REQUIRES_UPGRADE:
+        return KMV1::ErrorCode::KEY_REQUIRES_UPGRADE;
+    case V4_0::ErrorCode::ATTESTATION_CHALLENGE_MISSING:
+        return KMV1::ErrorCode::ATTESTATION_CHALLENGE_MISSING;
+    case V4_0::ErrorCode::KEYMASTER_NOT_CONFIGURED:
+        return KMV1::ErrorCode::KEYMINT_NOT_CONFIGURED;
+    case V4_0::ErrorCode::ATTESTATION_APPLICATION_ID_MISSING:
+        return KMV1::ErrorCode::ATTESTATION_APPLICATION_ID_MISSING;
+    case V4_0::ErrorCode::CANNOT_ATTEST_IDS:
+        return KMV1::ErrorCode::CANNOT_ATTEST_IDS;
+    case V4_0::ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE:
+        return KMV1::ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE;
+    case V4_0::ErrorCode::HARDWARE_TYPE_UNAVAILABLE:
+        return KMV1::ErrorCode::HARDWARE_TYPE_UNAVAILABLE;
+    case V4_0::ErrorCode::PROOF_OF_PRESENCE_REQUIRED:
+        return KMV1::ErrorCode::PROOF_OF_PRESENCE_REQUIRED;
+    case V4_0::ErrorCode::CONCURRENT_PROOF_OF_PRESENCE_REQUESTED:
+        return KMV1::ErrorCode::CONCURRENT_PROOF_OF_PRESENCE_REQUESTED;
+    case V4_0::ErrorCode::NO_USER_CONFIRMATION:
+        return KMV1::ErrorCode::NO_USER_CONFIRMATION;
+    case V4_0::ErrorCode::DEVICE_LOCKED:
+        return KMV1::ErrorCode::DEVICE_LOCKED;
+    case V4_0::ErrorCode::UNIMPLEMENTED:
+        return KMV1::ErrorCode::UNIMPLEMENTED;
+    case V4_0::ErrorCode::VERSION_MISMATCH:
+        return KMV1::ErrorCode::VERSION_MISMATCH;
+    case V4_0::ErrorCode::UNKNOWN_ERROR:
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+}
+
 static std::optional<V4_0::KeyPurpose> convert(KMV1::KeyPurpose p) {
     switch (p) {
     case KMV1::KeyPurpose::ENCRYPT:
@@ -395,6 +549,9 @@
             return V4_0::makeKeyParameter(V4_0::TAG_MAX_USES_PER_BOOT, v->get());
         }
         break;
+    case KMV1::Tag::USAGE_COUNT_LIMIT:
+        // Does not exist in KM < KeyMint 1.0.
+        break;
     case KMV1::Tag::USER_ID:
         if (auto v = KMV1::authorizationValue(KMV1::TAG_USER_ID, kp)) {
             return V4_0::makeKeyParameter(V4_0::TAG_USER_ID, v->get());
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 7814364..d264e7a 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -39,23 +39,32 @@
 
     static COMPAT_NAME: &str = "android.security.compat";
 
-    fn get_device() -> Box<dyn IKeyMintDevice> {
+    fn get_device() -> Option<Box<dyn IKeyMintDevice>> {
         add_keymint_device_service();
         let compat_service: Box<dyn IKeystoreCompatService> =
-            binder::get_interface(COMPAT_NAME).unwrap();
-        compat_service.getKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap()
+            binder::get_interface(COMPAT_NAME).ok()?;
+        compat_service.getKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT).ok()
+    }
+
+    macro_rules! get_device_or_skip_test {
+        () => {
+            match get_device() {
+                Some(dev) => dev,
+                None => return,
+            }
+        };
     }
 
     #[test]
     fn test_get_hardware_info() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         let hinfo = legacy.getHardwareInfo();
         assert!(hinfo.is_ok());
     }
 
     #[test]
     fn test_add_rng_entropy() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         let result = legacy.addRngEntropy(&[42; 16]);
         assert!(result.is_ok(), "{:?}", result);
     }
@@ -117,25 +126,25 @@
 
     #[test]
     fn test_generate_key_no_encrypt() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         generate_rsa_key(legacy.as_ref(), false, false);
     }
 
     #[test]
     fn test_generate_key_encrypt() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         generate_rsa_key(legacy.as_ref(), true, false);
     }
 
     #[test]
     fn test_generate_key_attested() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         generate_rsa_key(legacy.as_ref(), false, true);
     }
 
     #[test]
     fn test_import_key() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         let kps = [KeyParameter {
             tag: Tag::ALGORITHM,
             value: KeyParameterValue::Algorithm(Algorithm::AES),
@@ -149,7 +158,7 @@
 
     #[test]
     fn test_import_wrapped_key() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         let result = legacy.importWrappedKey(&[], &[], &[], &[], 0, 0);
         // For this test we only care that there was no crash.
         assert!(result.is_ok() || result.is_err());
@@ -157,7 +166,7 @@
 
     #[test]
     fn test_upgrade_key() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         let blob = generate_rsa_key(legacy.as_ref(), false, false);
         let result = legacy.upgradeKey(&blob, &[]);
         // For this test we only care that there was no crash.
@@ -166,7 +175,7 @@
 
     #[test]
     fn test_delete_key() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         let blob = generate_rsa_key(legacy.as_ref(), false, false);
         let result = legacy.deleteKey(&blob);
         assert!(result.is_ok(), "{:?}", result);
@@ -174,14 +183,14 @@
 
     #[test]
     fn test_delete_all_keys() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         let result = legacy.deleteAllKeys();
         assert!(result.is_ok(), "{:?}", result);
     }
 
     #[test]
     fn test_destroy_attestation_ids() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         let result = legacy.destroyAttestationIds();
         assert!(result.is_err());
         assert_eq!(result.unwrap_err().service_specific_error(), ErrorCode::UNIMPLEMENTED.0,);
@@ -243,7 +252,7 @@
 
     #[test]
     fn test_begin_abort() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         let blob = generate_aes_key(legacy.as_ref());
         let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::ENCRYPT, None);
         let operation = begin_result.operation.unwrap();
@@ -255,7 +264,7 @@
 
     #[test]
     fn test_begin_update_finish() {
-        let legacy = get_device();
+        let legacy = get_device_or_skip_test!();
         let blob = generate_aes_key(legacy.as_ref());
 
         let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::ENCRYPT, None);
diff --git a/keystore2/src/km_compat/parameter_conversion_test.cpp b/keystore2/src/km_compat/parameter_conversion_test.cpp
index 41be067..48af20c 100644
--- a/keystore2/src/km_compat/parameter_conversion_test.cpp
+++ b/keystore2/src/km_compat/parameter_conversion_test.cpp
@@ -150,3 +150,84 @@
     TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_USER_SECURE_ID);
     TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_VENDOR_PATCHLEVEL);
 }
+
+#define TEST_ERROR_CODE_CONVERSION(variant)                                                        \
+    ASSERT_EQ(KMV1::ErrorCode::variant, convert(V4_0::ErrorCode::variant))
+
+TEST(KmCompatTypeConversionTest, testErrorCodeConversion) {
+    TEST_ERROR_CODE_CONVERSION(OK);
+    TEST_ERROR_CODE_CONVERSION(ROOT_OF_TRUST_ALREADY_SET);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_PURPOSE);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_PURPOSE);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_ALGORITHM);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_ALGORITHM);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_KEY_SIZE);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_BLOCK_MODE);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_BLOCK_MODE);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_MAC_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_PADDING_MODE);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_PADDING_MODE);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_DIGEST);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_DIGEST);
+    TEST_ERROR_CODE_CONVERSION(INVALID_EXPIRATION_TIME);
+    TEST_ERROR_CODE_CONVERSION(INVALID_USER_ID);
+    TEST_ERROR_CODE_CONVERSION(INVALID_AUTHORIZATION_TIMEOUT);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_KEY_FORMAT);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_KEY_FORMAT);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_KEY_VERIFICATION_ALGORITHM);
+    TEST_ERROR_CODE_CONVERSION(INVALID_INPUT_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(KEY_EXPORT_OPTIONS_INVALID);
+    TEST_ERROR_CODE_CONVERSION(DELEGATION_NOT_ALLOWED);
+    TEST_ERROR_CODE_CONVERSION(KEY_NOT_YET_VALID);
+    TEST_ERROR_CODE_CONVERSION(KEY_EXPIRED);
+    TEST_ERROR_CODE_CONVERSION(KEY_USER_NOT_AUTHENTICATED);
+    TEST_ERROR_CODE_CONVERSION(OUTPUT_PARAMETER_NULL);
+    TEST_ERROR_CODE_CONVERSION(INVALID_OPERATION_HANDLE);
+    TEST_ERROR_CODE_CONVERSION(INSUFFICIENT_BUFFER_SPACE);
+    TEST_ERROR_CODE_CONVERSION(VERIFICATION_FAILED);
+    TEST_ERROR_CODE_CONVERSION(TOO_MANY_OPERATIONS);
+    TEST_ERROR_CODE_CONVERSION(UNEXPECTED_NULL_POINTER);
+    TEST_ERROR_CODE_CONVERSION(INVALID_KEY_BLOB);
+    TEST_ERROR_CODE_CONVERSION(IMPORTED_KEY_NOT_ENCRYPTED);
+    TEST_ERROR_CODE_CONVERSION(IMPORTED_KEY_DECRYPTION_FAILED);
+    TEST_ERROR_CODE_CONVERSION(IMPORTED_KEY_NOT_SIGNED);
+    TEST_ERROR_CODE_CONVERSION(IMPORTED_KEY_VERIFICATION_FAILED);
+    TEST_ERROR_CODE_CONVERSION(INVALID_ARGUMENT);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_TAG);
+    TEST_ERROR_CODE_CONVERSION(INVALID_TAG);
+    TEST_ERROR_CODE_CONVERSION(MEMORY_ALLOCATION_FAILED);
+    TEST_ERROR_CODE_CONVERSION(IMPORT_PARAMETER_MISMATCH);
+    TEST_ERROR_CODE_CONVERSION(SECURE_HW_ACCESS_DENIED);
+    TEST_ERROR_CODE_CONVERSION(OPERATION_CANCELLED);
+    TEST_ERROR_CODE_CONVERSION(CONCURRENT_ACCESS_CONFLICT);
+    TEST_ERROR_CODE_CONVERSION(SECURE_HW_BUSY);
+    TEST_ERROR_CODE_CONVERSION(SECURE_HW_COMMUNICATION_FAILED);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_EC_FIELD);
+    TEST_ERROR_CODE_CONVERSION(MISSING_NONCE);
+    TEST_ERROR_CODE_CONVERSION(INVALID_NONCE);
+    TEST_ERROR_CODE_CONVERSION(MISSING_MAC_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(KEY_RATE_LIMIT_EXCEEDED);
+    TEST_ERROR_CODE_CONVERSION(CALLER_NONCE_PROHIBITED);
+    TEST_ERROR_CODE_CONVERSION(KEY_MAX_OPS_EXCEEDED);
+    TEST_ERROR_CODE_CONVERSION(INVALID_MAC_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(MISSING_MIN_MAC_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_MIN_MAC_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_KDF);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_EC_CURVE);
+    TEST_ERROR_CODE_CONVERSION(KEY_REQUIRES_UPGRADE);
+    TEST_ERROR_CODE_CONVERSION(ATTESTATION_CHALLENGE_MISSING);
+    ASSERT_EQ(KMV1::ErrorCode::KEYMINT_NOT_CONFIGURED,
+              convert(V4_0::ErrorCode::KEYMASTER_NOT_CONFIGURED));
+    TEST_ERROR_CODE_CONVERSION(ATTESTATION_APPLICATION_ID_MISSING);
+    TEST_ERROR_CODE_CONVERSION(CANNOT_ATTEST_IDS);
+    TEST_ERROR_CODE_CONVERSION(ROLLBACK_RESISTANCE_UNAVAILABLE);
+    TEST_ERROR_CODE_CONVERSION(HARDWARE_TYPE_UNAVAILABLE);
+    TEST_ERROR_CODE_CONVERSION(PROOF_OF_PRESENCE_REQUIRED);
+    TEST_ERROR_CODE_CONVERSION(CONCURRENT_PROOF_OF_PRESENCE_REQUESTED);
+    TEST_ERROR_CODE_CONVERSION(NO_USER_CONFIRMATION);
+    TEST_ERROR_CODE_CONVERSION(DEVICE_LOCKED);
+    TEST_ERROR_CODE_CONVERSION(UNIMPLEMENTED);
+    TEST_ERROR_CODE_CONVERSION(VERSION_MISMATCH);
+    TEST_ERROR_CODE_CONVERSION(UNKNOWN_ERROR);
+}
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 34a0eca..230a82c 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -775,7 +775,7 @@
     mod legacy_blob_test_vectors;
     use crate::error;
     use crate::legacy_blob::test::legacy_blob_test_vectors::*;
-    use crate::test::utils::TempDir;
+    use keystore2_test_utils::TempDir;
 
     #[test]
     fn decode_encode_alias_test() {
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 0475d6f..811db91 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -34,8 +34,3 @@
 mod db_utils;
 mod gc;
 mod super_key;
-
-#[cfg(test)]
-mod test {
-    pub mod utils;
-}
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 30e6d55..829987d 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -341,7 +341,7 @@
             .auth_info
             .lock()
             .unwrap()
-            .get_auth_tokens()
+            .before_update()
             .context("In update_aad: Trying to get auth tokens.")?;
 
         self.update_outcome(
@@ -377,7 +377,7 @@
             .auth_info
             .lock()
             .unwrap()
-            .get_auth_tokens()
+            .before_update()
             .context("In update: Trying to get auth tokens.")?;
 
         self.update_outcome(
@@ -423,7 +423,7 @@
             .auth_info
             .lock()
             .unwrap()
-            .get_auth_tokens()
+            .before_finish()
             .context("In finish: Trying to get auth tokens.")?;
 
         let output = self
@@ -440,6 +440,8 @@
             )
             .context("In finish: KeyMint::finish failed.")?;
 
+        self.auth_info.lock().unwrap().after_finish().context("In finish.")?;
+
         // At this point the operation concluded successfully.
         *outcome = Outcome::Success;
 
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 0917256..0f0ca04 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -454,9 +454,12 @@
 
     for p in access_vec.into_iter() {
         selinux::check_access(caller_ctx, &target_context, "keystore2_key", p.to_selinux())
-            .context(concat!(
-                "check_grant_permission: check_access failed. ",
-                "The caller may have tried to grant a permission that they don't possess."
+            .context(format!(
+                concat!(
+                    "check_grant_permission: check_access failed. ",
+                    "The caller may have tried to grant a permission that they don't possess. {:?}"
+                ),
+                p
             ))?
     }
     Ok(())
@@ -482,25 +485,41 @@
 ///                      was supplied. It is also produced if `Domain::KEY_ID` was selected, and
 ///                      on various unexpected backend failures.
 pub fn check_key_permission(
+    caller_uid: u32,
     caller_ctx: &CStr,
     perm: KeyPerm,
     key: &KeyDescriptor,
     access_vector: &Option<KeyPermSet>,
 ) -> anyhow::Result<()> {
+    // If an access vector was supplied, the key is either accessed by GRANT or by KEY_ID.
+    // In the former case, key.domain was set to GRANT and we check the failure cases
+    // further below. If the access is requested by KEY_ID, key.domain would have been
+    // resolved to APP or SELINUX depending on where the key actually resides.
+    // Either way we can return here immediately if the access vector covers the requested
+    // permission. If it does not, we can still check if the caller has access by means of
+    // ownership.
+    if let Some(access_vector) = access_vector {
+        if access_vector.includes(perm) {
+            return Ok(());
+        }
+    }
+
     let target_context = match key.domain {
         // apps get the default keystore context
-        Domain::APP => getcon().context("check_key_permission: getcon failed.")?,
+        Domain::APP => {
+            if caller_uid as i64 != key.nspace {
+                return Err(selinux::Error::perm())
+                    .context("Trying to access key without ownership.");
+            }
+            getcon().context("check_key_permission: getcon failed.")?
+        }
         Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
             .context("check_key_permission: Domain::SELINUX: Failed to lookup namespace.")?,
         Domain::GRANT => {
             match access_vector {
-                Some(pv) => {
-                    if pv.includes(perm) {
-                        return Ok(());
-                    } else {
-                        return Err(selinux::Error::perm())
-                            .context(format!("\"{}\" not granted", perm.to_selinux()));
-                    }
+                Some(_) => {
+                    return Err(selinux::Error::perm())
+                        .context(format!("\"{}\" not granted", perm.to_selinux()));
                 }
                 None => {
                     // If DOMAIN_GRANT was selected an access vector must be supplied.
@@ -559,6 +578,16 @@
         KeyPerm::use_(),
     ];
 
+    const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
+        KeyPerm::delete(),
+        KeyPerm::use_dev_id(),
+        // No KeyPerm::grant()
+        KeyPerm::get_info(),
+        KeyPerm::rebind(),
+        KeyPerm::update(),
+        KeyPerm::use_(),
+    ];
+
     const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
         KeyPerm::manage_blob(),
         KeyPerm::delete(),
@@ -627,7 +656,6 @@
         assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::add_auth()).is_ok());
         assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::clear_ns()).is_ok());
         assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::get_state()).is_ok());
-        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::list()).is_ok());
         assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::lock()).is_ok());
         assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::reset()).is_ok());
         assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::unlock()).is_ok());
@@ -647,9 +675,10 @@
         let system_server_ctx = Context::new("u:r:system_server:s0")?;
         let shell_ctx = Context::new("u:r:shell:s0")?;
         let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
-        assert!(check_grant_permission(&system_server_ctx, NOT_GRANT_PERMS, &key).is_ok());
-        // attempts to grant the grant permission must always fail even when privileged.
+        check_grant_permission(&system_server_ctx, SYSTEM_SERVER_PERMISSIONS_NO_GRANT, &key)
+            .expect("Grant permission check failed.");
 
+        // attempts to grant the grant permission must always fail even when privileged.
         assert_perm_failed!(check_grant_permission(
             &system_server_ctx,
             KeyPerm::grant().into(),
@@ -685,6 +714,7 @@
         let key = KeyDescriptor { domain: Domain::GRANT, nspace: 0, alias: None, blob: None };
 
         assert_perm_failed!(check_key_permission(
+            0,
             &selinux::Context::new("ignored").unwrap(),
             KeyPerm::grant(),
             &key,
@@ -692,6 +722,7 @@
         ));
 
         check_key_permission(
+            0,
             &selinux::Context::new("ignored").unwrap(),
             KeyPerm::use_(),
             &key,
@@ -707,38 +738,82 @@
 
         let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
 
-        assert!(check_key_permission(&system_server_ctx, KeyPerm::use_(), &key, &None).is_ok());
-        assert!(check_key_permission(&system_server_ctx, KeyPerm::delete(), &key, &None).is_ok());
-        assert!(check_key_permission(&system_server_ctx, KeyPerm::get_info(), &key, &None).is_ok());
-        assert!(check_key_permission(&system_server_ctx, KeyPerm::rebind(), &key, &None).is_ok());
-        assert!(check_key_permission(&system_server_ctx, KeyPerm::update(), &key, &None).is_ok());
-        assert!(check_key_permission(&system_server_ctx, KeyPerm::grant(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::use_(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::delete(), &key, &None).is_ok());
         assert!(
-            check_key_permission(&system_server_ctx, KeyPerm::use_dev_id(), &key, &None).is_ok()
+            check_key_permission(0, &system_server_ctx, KeyPerm::get_info(), &key, &None).is_ok()
         );
-        assert!(check_key_permission(&gmscore_app, KeyPerm::gen_unique_id(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::rebind(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::update(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::grant(), &key, &None).is_ok());
+        assert!(
+            check_key_permission(0, &system_server_ctx, KeyPerm::use_dev_id(), &key, &None).is_ok()
+        );
+        assert!(
+            check_key_permission(0, &gmscore_app, KeyPerm::gen_unique_id(), &key, &None).is_ok()
+        );
 
-        assert!(check_key_permission(&shell_ctx, KeyPerm::use_(), &key, &None).is_ok());
-        assert!(check_key_permission(&shell_ctx, KeyPerm::delete(), &key, &None).is_ok());
-        assert!(check_key_permission(&shell_ctx, KeyPerm::get_info(), &key, &None).is_ok());
-        assert!(check_key_permission(&shell_ctx, KeyPerm::rebind(), &key, &None).is_ok());
-        assert!(check_key_permission(&shell_ctx, KeyPerm::update(), &key, &None).is_ok());
-        assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::grant(), &key, &None));
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::use_(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::delete(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::get_info(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::rebind(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::update(), &key, &None).is_ok());
+        assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::grant(), &key, &None));
         assert_perm_failed!(check_key_permission(
+            0,
             &shell_ctx,
             KeyPerm::req_forced_op(),
             &key,
             &None
         ));
-        assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::manage_blob(), &key, &None));
-        assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::use_dev_id(), &key, &None));
         assert_perm_failed!(check_key_permission(
+            0,
+            &shell_ctx,
+            KeyPerm::manage_blob(),
+            &key,
+            &None
+        ));
+        assert_perm_failed!(check_key_permission(
+            0,
+            &shell_ctx,
+            KeyPerm::use_dev_id(),
+            &key,
+            &None
+        ));
+        assert_perm_failed!(check_key_permission(
+            0,
             &shell_ctx,
             KeyPerm::gen_unique_id(),
             &key,
             &None
         ));
 
+        // Also make sure that the permission fails if the caller is not the owner.
+        assert_perm_failed!(check_key_permission(
+            1, // the owner is 0
+            &system_server_ctx,
+            KeyPerm::use_(),
+            &key,
+            &None
+        ));
+        // Unless there was a grant.
+        assert!(check_key_permission(
+            1,
+            &system_server_ctx,
+            KeyPerm::use_(),
+            &key,
+            &Some(key_perm_set![KeyPerm::use_()])
+        )
+        .is_ok());
+        // But fail if the grant did not cover the requested permission.
+        assert_perm_failed!(check_key_permission(
+            1,
+            &system_server_ctx,
+            KeyPerm::use_(),
+            &key,
+            &Some(key_perm_set![KeyPerm::get_info()])
+        ));
+
         Ok(())
     }
 
@@ -753,27 +828,45 @@
         };
 
         if is_su {
-            assert!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::delete(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::get_info(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::rebind(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::update(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::grant(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::manage_blob(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::use_dev_id(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::gen_unique_id(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::req_forced_op(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::manage_blob(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::gen_unique_id(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::req_forced_op(), &key, &None).is_ok());
         } else {
-            assert!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::delete(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::get_info(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::rebind(), &key, &None).is_ok());
-            assert!(check_key_permission(&sctx, KeyPerm::update(), &key, &None).is_ok());
-            assert_perm_failed!(check_key_permission(&sctx, KeyPerm::grant(), &key, &None));
-            assert_perm_failed!(check_key_permission(&sctx, KeyPerm::req_forced_op(), &key, &None));
-            assert_perm_failed!(check_key_permission(&sctx, KeyPerm::manage_blob(), &key, &None));
-            assert_perm_failed!(check_key_permission(&sctx, KeyPerm::use_dev_id(), &key, &None));
-            assert_perm_failed!(check_key_permission(&sctx, KeyPerm::gen_unique_id(), &key, &None));
+            assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None));
+            assert_perm_failed!(check_key_permission(
+                0,
+                &sctx,
+                KeyPerm::req_forced_op(),
+                &key,
+                &None
+            ));
+            assert_perm_failed!(check_key_permission(
+                0,
+                &sctx,
+                KeyPerm::manage_blob(),
+                &key,
+                &None
+            ));
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None));
+            assert_perm_failed!(check_key_permission(
+                0,
+                &sctx,
+                KeyPerm::gen_unique_id(),
+                &key,
+                &None
+            ));
         }
         Ok(())
     }
@@ -789,9 +882,9 @@
         };
 
         if is_su {
-            check_key_permission(&sctx, KeyPerm::use_(), &key, &None)
+            check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None)
         } else {
-            assert_perm_failed!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None));
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None));
             Ok(())
         }
     }
@@ -803,6 +896,7 @@
         assert_eq!(
             Some(&KsError::sys()),
             check_key_permission(
+                0,
                 &selinux::Context::new("ignored").unwrap(),
                 KeyPerm::use_(),
                 &key,
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index d0972d1..12b75bf 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -34,7 +34,7 @@
 use crate::globals::ENFORCEMENTS;
 use crate::key_parameter::KeyParameter as KsKeyParam;
 use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
-use crate::utils::{check_key_permission, Asp};
+use crate::utils::{check_key_permission, uid_to_android_user, Asp};
 use crate::{database::KeyIdGuard, globals::DB};
 use crate::{
     database::{DateTime, KeyMetaData, KeyMetaEntry, KeyType},
@@ -48,9 +48,8 @@
 use crate::{
     error::{self, map_km_error, map_or_log_err, Error, ErrorCode},
     utils::key_characteristics_to_internal,
-    utils::uid_to_android_user,
 };
-use anyhow::{Context, Result};
+use anyhow::{anyhow, Context, Result};
 use binder::{IBinder, Interface, ThreadState};
 
 /// Implementation of the IKeystoreSecurityLevel Interface.
@@ -173,7 +172,7 @@
         // so that we can use it by reference like the blob provided by the key descriptor.
         // Otherwise, we would have to clone the blob from the key descriptor.
         let scoping_blob: Vec<u8>;
-        let (km_blob, key_id_guard, key_parameters) = match key.domain {
+        let (km_blob, key_properties, key_id_guard) = match key.domain {
             Domain::BLOB => {
                 check_key_permission(KeyPerm::use_(), key, &None)
                     .context("In create_operation: checking use permission for Domain::BLOB.")?;
@@ -212,7 +211,11 @@
                         ))
                     }
                 };
-                (&scoping_blob, Some(key_id_guard), Some(key_entry.into_key_parameters()))
+                (
+                    &scoping_blob,
+                    Some((key_id_guard.id(), key_entry.into_key_parameters())),
+                    Some(key_id_guard),
+                )
             }
         };
 
@@ -229,8 +232,8 @@
         let (immediate_hat, mut auth_info) = ENFORCEMENTS
             .authorize_create(
                 purpose,
-                key_parameters.as_deref(),
-                operation_parameters,
+                key_properties.as_ref(),
+                operation_parameters.as_ref(),
                 // TODO b/178222844 Replace this with the configuration returned by
                 //      KeyMintDevice::getHardwareInfo.
                 //      For now we assume that strongbox implementations need secure timestamps.
@@ -293,6 +296,20 @@
         })
     }
 
+    fn add_attestation_parameters(uid: u32, params: &[KeyParameter]) -> Result<Vec<KeyParameter>> {
+        let mut result = params.to_vec();
+        if params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE) {
+            let aaid = keystore2_aaid::get_aaid(uid).map_err(|e| {
+                anyhow!(format!("In add_attestation_parameters: get_aaid returned status {}.", e))
+            })?;
+            result.push(KeyParameter {
+                tag: Tag::ATTESTATION_APPLICATION_ID,
+                value: KeyParameterValue::Blob(aaid),
+            });
+        }
+        Ok(result)
+    }
+
     fn generate_key(
         &self,
         key: &KeyDescriptor,
@@ -320,6 +337,9 @@
         // generate_key requires the rebind permission.
         check_key_permission(KeyPerm::rebind(), &key, &None).context("In generate_key.")?;
 
+        let params = Self::add_attestation_parameters(caller_uid, params)
+            .context("In generate_key: Trying to get aaid.")?;
+
         let km_dev: Box<dyn IKeyMintDevice> = self.keymint.get_interface()?;
         map_km_error(km_dev.addRngEntropy(entropy))
             .context("In generate_key: Trying to add entropy.")?;
@@ -357,6 +377,9 @@
         // import_key requires the rebind permission.
         check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_key.")?;
 
+        let params = Self::add_attestation_parameters(caller_uid, params)
+            .context("In import_key: Trying to get aaid.")?;
+
         let format = params
             .iter()
             .find(|p| p.tag == Tag::ALGORITHM)
@@ -390,7 +413,7 @@
         params: &[KeyParameter],
         authenticators: &[AuthenticatorSpec],
     ) -> Result<KeyMetadata> {
-        if key.domain != Domain::BLOB && key.alias.is_none() {
+        if !(key.domain == Domain::BLOB && key.alias.is_some()) {
             return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
                 .context("In import_wrapped_key: Alias must be specified.");
         }
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 080348c..870b7fc 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -77,6 +77,7 @@
 ) -> anyhow::Result<()> {
     ThreadState::with_calling_sid(|calling_sid| {
         permission::check_key_permission(
+            ThreadState::get_calling_uid(),
             &calling_sid
                 .ok_or_else(Error::sys)
                 .context("In check_key_permission: Cannot check permission without calling_sid.")?,
diff --git a/keystore2/src/test/utils.rs b/keystore2/test_utils/lib.rs
similarity index 67%
rename from keystore2/src/test/utils.rs
rename to keystore2/test_utils/lib.rs
index 8c93859..627af20 100644
--- a/keystore2/src/test/utils.rs
+++ b/keystore2/test_utils/lib.rs
@@ -12,11 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+//! Implements TempDir which aids in creating an cleaning up temporary directories for testing.
+
 use std::fs::{create_dir, remove_dir_all};
 use std::io::ErrorKind;
 use std::path::{Path, PathBuf};
 use std::{env::temp_dir, ops::Deref};
 
+/// Represents the lifecycle of a temporary directory for testing.
 #[derive(Debug)]
 pub struct TempDir {
     path: std::path::PathBuf,
@@ -24,6 +27,11 @@
 }
 
 impl TempDir {
+    /// Creates a temporary directory with a name of the form <prefix>_NNNNN where NNNNN is a zero
+    /// padded random number with 5 figures. The prefix must not contain file system separators.
+    /// The location of the directory cannot be chosen.
+    /// The directory with all of its content is removed from the file system when the resulting
+    /// object gets dropped.
     pub fn new(prefix: &str) -> std::io::Result<Self> {
         let tmp = loop {
             let mut tmp = temp_dir();
@@ -40,10 +48,20 @@
         Ok(Self { path: tmp, do_drop: true })
     }
 
+    /// Returns the absolute path of the temporary directory.
     pub fn path(&self) -> &Path {
         &self.path
     }
 
+    /// Returns a path builder for convenient extension of the path.
+    ///
+    /// ## Example:
+    ///
+    /// ```
+    /// let tdir = TempDir::new("my_test")?;
+    /// let temp_foo_bar = tdir.build().push("foo").push("bar");
+    /// ```
+    /// `temp_foo_bar` derefs to a Path that represents "<tdir.path()>/foo/bar"
     pub fn build(&self) -> PathBuilder {
         PathBuilder(self.path.clone())
     }
@@ -66,9 +84,11 @@
     }
 }
 
+/// Allows for convenient building of paths from a TempDir. See TempDir.build() for more details.
 pub struct PathBuilder(PathBuf);
 
 impl PathBuilder {
+    /// Adds another segment to the end of the path. Consumes, modifies and returns self.
     pub fn push(mut self, segment: &str) -> Self {
         self.0.push(segment);
         self