Merge changes Ic719fcaa,I34999d76,Ia7563394,Id4abe50c,I9e141674, ...

* changes:
  Keystore 2.0: Boot level key: Add missing digest to operation params
  Keystore 2.0: Delete unencrypted boot level keys.
  Keystore 2.0: Add database versioning.
  Keystore 2.0: Move db_utils to database submodule.
  Keystore 2.0: Make key type an explict argument.
  Keystore 2.0: Boot level keys: Check key characteristics.
  Keystore 2.0: Use preferred KM instance for level zero key.
  Keystore 2.0: km_compat: Implement getKeyCharacteristics.
diff --git a/keystore2/src/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
index 3084195..1110caf 100644
--- a/keystore2/src/boot_level_keys.rs
+++ b/keystore2/src/boot_level_keys.rs
@@ -14,38 +14,95 @@
 
 //! Offer keys based on the "boot level" for superencryption.
 
+use crate::{
+    database::{KeyType, KeystoreDB},
+    key_parameter::KeyParameterValue,
+    raw_device::KeyMintDevice,
+};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    Algorithm::Algorithm, Digest::Digest, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+    Algorithm::Algorithm, Digest::Digest, KeyParameter::KeyParameter as KmKeyParameter,
+    KeyParameterValue::KeyParameterValue as KmKeyParameterValue, KeyPurpose::KeyPurpose,
+    SecurityLevel::SecurityLevel, Tag::Tag,
 };
 use anyhow::{Context, Result};
 use keystore2_crypto::{hkdf_expand, ZVec, AES_256_KEY_LENGTH};
 use std::{collections::VecDeque, convert::TryFrom};
 
-use crate::{database::KeystoreDB, key_parameter::KeyParameterValue, raw_device::KeyMintDevice};
+fn get_preferred_km_instance_for_level_zero_key() -> Result<KeyMintDevice> {
+    let tee = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+        .context("In get_preferred_km_instance_for_level_zero_key: Get TEE instance failed.")?;
+    if tee.version() >= KeyMintDevice::KEY_MASTER_V4_1 {
+        Ok(tee)
+    } else {
+        match KeyMintDevice::get_or_none(SecurityLevel::STRONGBOX).context(
+            "In get_preferred_km_instance_for_level_zero_key: Get Strongbox instance failed.",
+        )? {
+            Some(strongbox) if strongbox.version() >= KeyMintDevice::KEY_MASTER_V4_1 => {
+                Ok(strongbox)
+            }
+            _ => Ok(tee),
+        }
+    }
+}
 
 /// This is not thread safe; caller must hold a lock before calling.
 /// In practice the caller is SuperKeyManager and the lock is the
 /// Mutex on its internal state.
 pub fn get_level_zero_key(db: &mut KeystoreDB) -> Result<ZVec> {
+    let km_dev = get_preferred_km_instance_for_level_zero_key()
+        .context("In get_level_zero_key: get preferred KM instance failed")?;
+
     let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string());
-    let params = [
+    let mut params = vec![
         KeyParameterValue::Algorithm(Algorithm::HMAC).into(),
         KeyParameterValue::Digest(Digest::SHA_2_256).into(),
         KeyParameterValue::KeySize(256).into(),
         KeyParameterValue::MinMacLength(256).into(),
         KeyParameterValue::KeyPurpose(KeyPurpose::SIGN).into(),
         KeyParameterValue::NoAuthRequired.into(),
-        KeyParameterValue::MaxUsesPerBoot(1).into(),
     ];
-    // We use TRUSTED_ENVIRONMENT here because it is the authority on when
-    // the device has rebooted.
-    let km_dev: KeyMintDevice = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
-        .context("In get_level_zero_key: KeyMintDevice::get failed")?;
+
+    let has_early_boot_only = km_dev.version() >= KeyMintDevice::KEY_MASTER_V4_1;
+
+    if has_early_boot_only {
+        params.push(KeyParameterValue::EarlyBootOnly.into());
+    } else {
+        params.push(KeyParameterValue::MaxUsesPerBoot(1).into())
+    }
+
     let (key_id_guard, key_entry) = km_dev
-        .lookup_or_generate_key(db, &key_desc, &params)
+        .lookup_or_generate_key(db, &key_desc, KeyType::Client, &params, |key_characteristics| {
+            key_characteristics.iter().any(|kc| {
+                if kc.securityLevel == km_dev.security_level() {
+                    kc.authorizations.iter().any(|a| {
+                        matches!(
+                            (has_early_boot_only, a),
+                            (
+                                true,
+                                KmKeyParameter {
+                                    tag: Tag::EARLY_BOOT_ONLY,
+                                    value: KmKeyParameterValue::BoolValue(true)
+                                }
+                            ) | (
+                                false,
+                                KmKeyParameter {
+                                    tag: Tag::MAX_USES_PER_BOOT,
+                                    value: KmKeyParameterValue::Integer(1)
+                                }
+                            )
+                        )
+                    })
+                } else {
+                    false
+                }
+            })
+        })
         .context("In get_level_zero_key: lookup_or_generate_key failed")?;
 
-    let params = [KeyParameterValue::MacLength(256).into()];
+    let params = [
+        KeyParameterValue::MacLength(256).into(),
+        KeyParameterValue::Digest(Digest::SHA_2_256).into(),
+    ];
     let level_zero_key = km_dev
         .use_key_in_one_step(
             db,
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 3b7ed38..b4122bb 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -42,22 +42,22 @@
 //! callbacks.
 
 mod perboot;
+pub(crate) mod utils;
+mod versioning;
 
 use crate::impl_metadata; // This is in db_utils.rs
 use crate::key_parameter::{KeyParameter, Tag};
 use crate::permission::KeyPermSet;
 use crate::utils::{get_current_time_in_milliseconds, watchdog as wd, AID_USER_OFFSET};
 use crate::{
-    db_utils::{self, SqlField},
-    gc::Gc,
-    super_key::USER_SUPER_KEY,
-};
-use crate::{
     error::{Error as KsError, ErrorCode, ResponseCode},
     super_key::SuperKeyType,
 };
+use crate::{gc::Gc, super_key::USER_SUPER_KEY};
 use anyhow::{anyhow, Context, Result};
 use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
+use utils as db_utils;
+use utils::SqlField;
 
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     HardwareAuthToken::HardwareAuthToken,
@@ -825,6 +825,8 @@
 
 impl KeystoreDB {
     const UNASSIGNED_KEY_ID: i64 = -1i64;
+    const CURRENT_DB_VERSION: u32 = 1;
+    const UPGRADERS: &'static [fn(&Transaction) -> Result<u32>] = &[Self::from_0_to_1];
 
     /// Name of the file that holds the cross-boot persistent database.
     pub const PERSISTENT_DB_FILENAME: &'static str = &"persistent.sqlite";
@@ -856,11 +858,33 @@
 
         let mut db = Self { conn, gc, perboot: perboot::PERBOOT_DB.clone() };
         db.with_transaction(TransactionBehavior::Immediate, |tx| {
+            versioning::upgrade_database(tx, Self::CURRENT_DB_VERSION, Self::UPGRADERS)
+                .context("In KeystoreDB::new: trying to upgrade database.")?;
             Self::init_tables(tx).context("Trying to initialize tables.").no_gc()
         })?;
         Ok(db)
     }
 
+    // This upgrade function deletes all MAX_BOOT_LEVEL keys, that were generated before
+    // cryptographic binding to the boot level keys was implemented.
+    fn from_0_to_1(tx: &Transaction) -> Result<u32> {
+        tx.execute(
+            "UPDATE persistent.keyentry SET state = ?
+             WHERE
+                 id IN (SELECT keyentryid FROM persistent.keyparameter WHERE tag = ?)
+             AND
+                 id NOT IN (
+                     SELECT keyentryid FROM persistent.blobentry
+                     WHERE id IN (
+                         SELECT blobentryid FROM persistent.blobmetadata WHERE tag = ?
+                     )
+                 );",
+            params![KeyLifeCycle::Unreferenced, Tag::MAX_BOOT_LEVEL.0, BlobMetaData::MaxBootLevel],
+        )
+        .context("In from_0_to_1: Failed to delete logical boot level keys.")?;
+        Ok(1)
+    }
+
     fn init_tables(tx: &Transaction) -> Result<()> {
         tx.execute(
             "CREATE TABLE IF NOT EXISTS persistent.keyentry (
@@ -1481,12 +1505,13 @@
         &mut self,
         domain: &Domain,
         namespace: &i64,
+        key_type: KeyType,
         km_uuid: &Uuid,
     ) -> Result<KeyIdGuard> {
         let _wp = wd::watch_millis("KeystoreDB::create_key_entry", 500);
 
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
-            Self::create_key_entry_internal(tx, domain, namespace, km_uuid).no_gc()
+            Self::create_key_entry_internal(tx, domain, namespace, key_type, km_uuid).no_gc()
         })
         .context("In create_key_entry.")
     }
@@ -1495,6 +1520,7 @@
         tx: &Transaction,
         domain: &Domain,
         namespace: &i64,
+        key_type: KeyType,
         km_uuid: &Uuid,
     ) -> Result<KeyIdGuard> {
         match *domain {
@@ -1512,7 +1538,7 @@
                      VALUES(?, ?, ?, ?, NULL, ?, ?);",
                     params![
                         id,
-                        KeyType::Client,
+                        key_type,
                         domain.0 as u32,
                         *namespace,
                         KeyLifeCycle::Existing,
@@ -2103,6 +2129,7 @@
         alias: &str,
         domain: &Domain,
         namespace: &i64,
+        key_type: KeyType,
     ) -> Result<bool> {
         match *domain {
             Domain::APP | Domain::SELINUX => {}
@@ -2117,15 +2144,15 @@
             .execute(
                 "UPDATE persistent.keyentry
                  SET alias = NULL, domain = NULL, namespace = NULL, state = ?
-                 WHERE alias = ? AND domain = ? AND namespace = ?;",
-                params![KeyLifeCycle::Unreferenced, alias, domain.0 as u32, namespace],
+                 WHERE alias = ? AND domain = ? AND namespace = ? AND key_type = ?;",
+                params![KeyLifeCycle::Unreferenced, alias, domain.0 as u32, namespace, key_type],
             )
             .context("In rebind_alias: Failed to rebind existing entry.")?;
         let result = tx
             .execute(
                 "UPDATE persistent.keyentry
                     SET alias = ?, state = ?
-                    WHERE id = ? AND domain = ? AND namespace = ? AND state = ?;",
+                    WHERE id = ? AND domain = ? AND namespace = ? AND state = ? AND key_type = ?;",
                 params![
                     alias,
                     KeyLifeCycle::Live,
@@ -2133,6 +2160,7 @@
                     domain.0 as u32,
                     *namespace,
                     KeyLifeCycle::Existing,
+                    key_type,
                 ],
             )
             .context("In rebind_alias: Failed to set alias.")?;
@@ -2215,9 +2243,11 @@
     /// fields, and rebinds the given alias to the new key.
     /// The boolean returned is a hint for the garbage collector. If true, a key was replaced,
     /// is now unreferenced and needs to be collected.
+    #[allow(clippy::clippy::too_many_arguments)]
     pub fn store_new_key(
         &mut self,
         key: &KeyDescriptor,
+        key_type: KeyType,
         params: &[KeyParameter],
         blob_info: &(&[u8], &BlobMetaData),
         cert_info: &CertificateInfo,
@@ -2237,7 +2267,7 @@
             }
         };
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
-            let key_id = Self::create_key_entry_internal(tx, &domain, namespace, km_uuid)
+            let key_id = Self::create_key_entry_internal(tx, &domain, namespace, key_type, km_uuid)
                 .context("Trying to create new key entry.")?;
             let (blob, blob_metadata) = *blob_info;
             Self::set_blob_internal(
@@ -2265,7 +2295,7 @@
             Self::insert_keyparameter_internal(tx, &key_id, params)
                 .context("Trying to insert key parameters.")?;
             metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
-            let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace)
+            let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace, key_type)
                 .context("Trying to rebind alias.")?;
             Ok(key_id).do_gc(need_gc)
         })
@@ -2278,6 +2308,7 @@
     pub fn store_new_certificate(
         &mut self,
         key: &KeyDescriptor,
+        key_type: KeyType,
         cert: &[u8],
         km_uuid: &Uuid,
     ) -> Result<KeyIdGuard> {
@@ -2295,7 +2326,7 @@
             }
         };
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
-            let key_id = Self::create_key_entry_internal(tx, &domain, namespace, km_uuid)
+            let key_id = Self::create_key_entry_internal(tx, &domain, namespace, key_type, km_uuid)
                 .context("Trying to create new key entry.")?;
 
             Self::set_blob_internal(
@@ -2314,7 +2345,7 @@
 
             metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
 
-            let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace)
+            let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace, key_type)
                 .context("Trying to rebind alias.")?;
             Ok(key_id).do_gc(need_gc)
         })
@@ -3256,7 +3287,7 @@
         namespace: i64,
     ) -> Result<bool> {
         db.with_transaction(TransactionBehavior::Immediate, |tx| {
-            KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace).no_gc()
+            KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace, KeyType::Client).no_gc()
         })
         .context("In rebind_alias.")
     }
@@ -3380,7 +3411,7 @@
         let temp_dir = TempDir::new("persistent_db_test")?;
         let mut db = KeystoreDB::new(temp_dir.path(), None)?;
 
-        db.create_key_entry(&Domain::APP, &100, &KEYSTORE_UUID)?;
+        db.create_key_entry(&Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
         let entries = get_keyentry(&db)?;
         assert_eq!(entries.len(), 1);
 
@@ -3399,8 +3430,8 @@
 
         let mut db = new_test_db()?;
 
-        db.create_key_entry(&Domain::APP, &100, &KEYSTORE_UUID)?;
-        db.create_key_entry(&Domain::SELINUX, &101, &KEYSTORE_UUID)?;
+        db.create_key_entry(&Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
+        db.create_key_entry(&Domain::SELINUX, &101, KeyType::Client, &KEYSTORE_UUID)?;
 
         let entries = get_keyentry(&db)?;
         assert_eq!(entries.len(), 2);
@@ -3409,15 +3440,15 @@
 
         // Test that we must pass in a valid Domain.
         check_result_is_error_containing_string(
-            db.create_key_entry(&Domain::GRANT, &102, &KEYSTORE_UUID),
+            db.create_key_entry(&Domain::GRANT, &102, KeyType::Client, &KEYSTORE_UUID),
             "Domain Domain(1) must be either App or SELinux.",
         );
         check_result_is_error_containing_string(
-            db.create_key_entry(&Domain::BLOB, &103, &KEYSTORE_UUID),
+            db.create_key_entry(&Domain::BLOB, &103, KeyType::Client, &KEYSTORE_UUID),
             "Domain Domain(3) must be either App or SELinux.",
         );
         check_result_is_error_containing_string(
-            db.create_key_entry(&Domain::KEY_ID, &104, &KEYSTORE_UUID),
+            db.create_key_entry(&Domain::KEY_ID, &104, KeyType::Client, &KEYSTORE_UUID),
             "Domain Domain(4) must be either App or SELinux.",
         );
 
@@ -3585,7 +3616,7 @@
         let mut db = new_test_db()?;
         load_attestation_key_pool(&mut db, 45 /* expiration */, 1 /* namespace */, 0x02)?;
         load_attestation_key_pool(&mut db, 80 /* expiration */, 2 /* namespace */, 0x03)?;
-        db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
+        db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
         let result = db.delete_all_attestation_keys()?;
 
         // Give the garbage collector half a second to catch up.
@@ -3606,8 +3637,8 @@
         }
 
         let mut db = new_test_db()?;
-        db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
-        db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
+        db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+        db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
         let entries = get_keyentry(&db)?;
         assert_eq!(entries.len(), 2);
         assert_eq!(
@@ -3943,6 +3974,7 @@
                 alias: Some(TEST_ALIAS.to_string()),
                 blob: None,
             },
+            KeyType::Client,
             TEST_CERT_BLOB,
             &KEYSTORE_UUID,
         )
@@ -4462,6 +4494,152 @@
         Ok(())
     }
 
+    #[test]
+    fn test_upgrade_0_to_1() {
+        const ALIAS1: &str = &"test_upgrade_0_to_1_1";
+        const ALIAS2: &str = &"test_upgrade_0_to_1_2";
+        const ALIAS3: &str = &"test_upgrade_0_to_1_3";
+        const UID: u32 = 33;
+        let temp_dir = Arc::new(TempDir::new("test_upgrade_0_to_1").unwrap());
+        let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+        let key_id_untouched1 =
+            make_test_key_entry(&mut db, Domain::APP, UID as i64, ALIAS1, None).unwrap().id();
+        let key_id_untouched2 =
+            make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS2, false).unwrap().id();
+        let key_id_deleted =
+            make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS3, true).unwrap().id();
+
+        let (_, key_entry) = db
+            .load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(ALIAS1.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(UID as i64, k.nspace);
+                    assert!(av.is_none());
+                    Ok(())
+                },
+            )
+            .unwrap();
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
+        let (_, key_entry) = db
+            .load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(ALIAS2.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(UID as i64, k.nspace);
+                    assert!(av.is_none());
+                    Ok(())
+                },
+            )
+            .unwrap();
+        assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
+        let (_, key_entry) = db
+            .load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(ALIAS3.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(UID as i64, k.nspace);
+                    assert!(av.is_none());
+                    Ok(())
+                },
+            )
+            .unwrap();
+        assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_deleted, true));
+
+        db.with_transaction(TransactionBehavior::Immediate, |tx| {
+            KeystoreDB::from_0_to_1(tx).no_gc()
+        })
+        .unwrap();
+
+        let (_, key_entry) = db
+            .load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(ALIAS1.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(UID as i64, k.nspace);
+                    assert!(av.is_none());
+                    Ok(())
+                },
+            )
+            .unwrap();
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
+        let (_, key_entry) = db
+            .load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(ALIAS2.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(UID as i64, k.nspace);
+                    assert!(av.is_none());
+                    Ok(())
+                },
+            )
+            .unwrap();
+        assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+            db.load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(ALIAS3.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(UID as i64, k.nspace);
+                    assert!(av.is_none());
+                    Ok(())
+                },
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+    }
+
     static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
 
     #[test]
@@ -5104,7 +5282,7 @@
         alias: &str,
         max_usage_count: Option<i32>,
     ) -> Result<KeyIdGuard> {
-        let key_id = db.create_key_entry(&domain, &namespace, &KEYSTORE_UUID)?;
+        let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
         let mut blob_metadata = BlobMetaData::new();
         blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
         blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
@@ -5156,6 +5334,66 @@
         }
     }
 
+    fn make_bootlevel_key_entry(
+        db: &mut KeystoreDB,
+        domain: Domain,
+        namespace: i64,
+        alias: &str,
+        logical_only: bool,
+    ) -> Result<KeyIdGuard> {
+        let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+        let mut blob_metadata = BlobMetaData::new();
+        if !logical_only {
+            blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
+        }
+        blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+        db.set_blob(
+            &key_id,
+            SubComponentType::KEY_BLOB,
+            Some(TEST_KEY_BLOB),
+            Some(&blob_metadata),
+        )?;
+        db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+        db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+
+        let mut params = make_test_params(None);
+        params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
+
+        db.insert_keyparameter(&key_id, &params)?;
+
+        let mut metadata = KeyMetaData::new();
+        metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+        db.insert_key_metadata(&key_id, &metadata)?;
+        rebind_alias(db, &key_id, alias, domain, namespace)?;
+        Ok(key_id)
+    }
+
+    fn make_bootlevel_test_key_entry_test_vector(key_id: i64, logical_only: bool) -> KeyEntry {
+        let mut params = make_test_params(None);
+        params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
+
+        let mut blob_metadata = BlobMetaData::new();
+        if !logical_only {
+            blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
+        }
+        blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+        let mut metadata = KeyMetaData::new();
+        metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+
+        KeyEntry {
+            id: key_id,
+            key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
+            cert: Some(TEST_CERT_BLOB.to_vec()),
+            cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+            km_uuid: KEYSTORE_UUID,
+            parameters: params,
+            metadata,
+            pure_cert: false,
+        }
+    }
+
     fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
         let mut stmt = db.conn.prepare(
             "SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
@@ -5394,7 +5632,7 @@
         let mut db = new_test_db()?;
         let mut working_stats = get_storage_stats_map(&mut db);
 
-        let key_id = db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
+        let key_id = db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
         assert_storage_increased(
             &mut db,
             vec![
diff --git a/keystore2/src/db_utils.rs b/keystore2/src/database/utils.rs
similarity index 100%
rename from keystore2/src/db_utils.rs
rename to keystore2/src/database/utils.rs
diff --git a/keystore2/src/database/versioning.rs b/keystore2/src/database/versioning.rs
new file mode 100644
index 0000000..e3a95c8
--- /dev/null
+++ b/keystore2/src/database/versioning.rs
@@ -0,0 +1,379 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use anyhow::{anyhow, Context, Result};
+use rusqlite::{params, OptionalExtension, Transaction, NO_PARAMS};
+
+pub fn create_or_get_version(tx: &Transaction, current_version: u32) -> Result<u32> {
+    tx.execute(
+        "CREATE TABLE IF NOT EXISTS persistent.version (
+                id INTEGER PRIMARY KEY,
+                version INTEGER);",
+        NO_PARAMS,
+    )
+    .context("In create_or_get_version: Failed to create version table.")?;
+
+    let version = tx
+        .query_row("SELECT version FROM persistent.version WHERE id = 0;", NO_PARAMS, |row| {
+            row.get(0)
+        })
+        .optional()
+        .context("In create_or_get_version: Failed to read version.")?;
+
+    let version = if let Some(version) = version {
+        version
+    } else {
+        // If no version table existed it could mean one of two things:
+        // 1) This database is completely new. In this case the version has to be set
+        //    to the current version and the current version which also needs to be
+        //    returned.
+        // 2) The database predates db versioning. In this case the version needs to be
+        //    set to 0, and 0 needs to be returned.
+        let version = if tx
+            .query_row(
+                "SELECT name FROM persistent.sqlite_master
+                 WHERE type = 'table' AND name = 'keyentry';",
+                NO_PARAMS,
+                |_| Ok(()),
+            )
+            .optional()
+            .context("In create_or_get_version: Failed to check for keyentry table.")?
+            .is_none()
+        {
+            current_version
+        } else {
+            0
+        };
+
+        tx.execute("INSERT INTO persistent.version (id, version) VALUES(0, ?);", params![version])
+            .context("In create_or_get_version: Failed to insert initial version.")?;
+        version
+    };
+    Ok(version)
+}
+
+pub fn update_version(tx: &Transaction, new_version: u32) -> Result<()> {
+    let updated = tx
+        .execute("UPDATE persistent.version SET version = ? WHERE id = 0;", params![new_version])
+        .context("In update_version: Failed to update row.")?;
+    if updated == 1 {
+        Ok(())
+    } else {
+        Err(anyhow!("In update_version: No rows were updated."))
+    }
+}
+
+pub fn upgrade_database<F>(tx: &Transaction, current_version: u32, upgraders: &[F]) -> Result<()>
+where
+    F: Fn(&Transaction) -> Result<u32> + 'static,
+{
+    if upgraders.len() < current_version as usize {
+        return Err(anyhow!("In upgrade_database: Insufficient upgraders provided."));
+    }
+    let mut db_version = create_or_get_version(tx, current_version)
+        .context("In upgrade_database: Failed to get database version.")?;
+    while db_version < current_version {
+        db_version = upgraders[db_version as usize](tx).with_context(|| {
+            format!("In upgrade_database: Trying to upgrade from db version {}.", db_version)
+        })?;
+    }
+    update_version(tx, db_version).context("In upgrade_database.")
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use rusqlite::{Connection, TransactionBehavior, NO_PARAMS};
+
+    #[test]
+    fn upgrade_database_test() {
+        let mut conn = Connection::open_in_memory().unwrap();
+        conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", NO_PARAMS).unwrap();
+
+        let upgraders: Vec<_> = (0..30_u32)
+            .map(move |i| {
+                move |tx: &Transaction| {
+                    tx.execute(
+                        "INSERT INTO persistent.test (test_field) VALUES(?);",
+                        params![i + 1],
+                    )
+                    .with_context(|| format!("In upgrade_from_{}_to_{}.", i, i + 1))?;
+                    Ok(i + 1)
+                }
+            })
+            .collect();
+
+        for legacy in &[false, true] {
+            if *legacy {
+                conn.execute(
+                    "CREATE TABLE IF NOT EXISTS persistent.keyentry (
+                        id INTEGER UNIQUE,
+                        key_type INTEGER,
+                        domain INTEGER,
+                        namespace INTEGER,
+                        alias BLOB,
+                        state INTEGER,
+                        km_uuid BLOB);",
+                    NO_PARAMS,
+                )
+                .unwrap();
+            }
+            for from in 1..29 {
+                for to in from..30 {
+                    conn.execute("DROP TABLE IF EXISTS persistent.version;", NO_PARAMS).unwrap();
+                    conn.execute("DROP TABLE IF EXISTS persistent.test;", NO_PARAMS).unwrap();
+                    conn.execute(
+                        "CREATE TABLE IF NOT EXISTS persistent.test (
+                            id INTEGER PRIMARY KEY,
+                            test_field INTEGER);",
+                        NO_PARAMS,
+                    )
+                    .unwrap();
+
+                    {
+                        let tx =
+                            conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+                        create_or_get_version(&tx, from).unwrap();
+                        tx.commit().unwrap();
+                    }
+                    {
+                        let tx =
+                            conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+                        upgrade_database(&tx, to, &upgraders).unwrap();
+                        tx.commit().unwrap();
+                    }
+
+                    // In the legacy database case all upgraders starting from 0 have to run. So
+                    // after the upgrade step, the expectations need to be adjusted.
+                    let from = if *legacy { 0 } else { from };
+
+                    // There must be exactly to - from rows.
+                    assert_eq!(
+                        to - from,
+                        conn.query_row(
+                            "SELECT COUNT(test_field) FROM persistent.test;",
+                            NO_PARAMS,
+                            |row| row.get(0)
+                        )
+                        .unwrap()
+                    );
+                    // Each row must have the correct relation between id and test_field. If this
+                    // is not the case, the upgraders were not executed in the correct order.
+                    assert_eq!(
+                        to - from,
+                        conn.query_row(
+                            "SELECT COUNT(test_field) FROM persistent.test
+                             WHERE id = test_field - ?;",
+                            params![from],
+                            |row| row.get(0)
+                        )
+                        .unwrap()
+                    );
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn create_or_get_version_new_database() {
+        let mut conn = Connection::open_in_memory().unwrap();
+        conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", NO_PARAMS).unwrap();
+        {
+            let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+            let version = create_or_get_version(&tx, 3).unwrap();
+            tx.commit().unwrap();
+            assert_eq!(version, 3);
+        }
+
+        // Was the version table created as expected?
+        assert_eq!(
+            Ok("version".to_owned()),
+            conn.query_row(
+                "SELECT name FROM persistent.sqlite_master
+                 WHERE type = 'table' AND name = 'version';",
+                NO_PARAMS,
+                |row| row.get(0),
+            )
+        );
+
+        // There is exactly one row in the version table.
+        assert_eq!(
+            Ok(1),
+            conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+                .get(0))
+        );
+
+        // The version must be set to 3
+        assert_eq!(
+            Ok(3),
+            conn.query_row(
+                "SELECT version from persistent.version WHERE id = 0;",
+                NO_PARAMS,
+                |row| row.get(0)
+            )
+        );
+
+        // Will subsequent calls to create_or_get_version still return the same version even
+        // if the current version changes.
+        {
+            let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+            let version = create_or_get_version(&tx, 5).unwrap();
+            tx.commit().unwrap();
+            assert_eq!(version, 3);
+        }
+
+        // There is still exactly one row in the version table.
+        assert_eq!(
+            Ok(1),
+            conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+                .get(0))
+        );
+
+        // Bump the version.
+        {
+            let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+            update_version(&tx, 5).unwrap();
+            tx.commit().unwrap();
+        }
+
+        // Now the version should have changed.
+        {
+            let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+            let version = create_or_get_version(&tx, 7).unwrap();
+            tx.commit().unwrap();
+            assert_eq!(version, 5);
+        }
+
+        // There is still exactly one row in the version table.
+        assert_eq!(
+            Ok(1),
+            conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+                .get(0))
+        );
+
+        // The version must be set to 5
+        assert_eq!(
+            Ok(5),
+            conn.query_row(
+                "SELECT version from persistent.version WHERE id = 0;",
+                NO_PARAMS,
+                |row| row.get(0)
+            )
+        );
+    }
+
+    #[test]
+    fn create_or_get_version_legacy_database() {
+        let mut conn = Connection::open_in_memory().unwrap();
+        conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", NO_PARAMS).unwrap();
+        // A legacy (version 0) database is detected if the keyentry table exists but no
+        // version table.
+        conn.execute(
+            "CREATE TABLE IF NOT EXISTS persistent.keyentry (
+             id INTEGER UNIQUE,
+             key_type INTEGER,
+             domain INTEGER,
+             namespace INTEGER,
+             alias BLOB,
+             state INTEGER,
+             km_uuid BLOB);",
+            NO_PARAMS,
+        )
+        .unwrap();
+
+        {
+            let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+            let version = create_or_get_version(&tx, 3).unwrap();
+            tx.commit().unwrap();
+            // In the legacy case, version 0 must be returned.
+            assert_eq!(version, 0);
+        }
+
+        // Was the version table created as expected?
+        assert_eq!(
+            Ok("version".to_owned()),
+            conn.query_row(
+                "SELECT name FROM persistent.sqlite_master
+                 WHERE type = 'table' AND name = 'version';",
+                NO_PARAMS,
+                |row| row.get(0),
+            )
+        );
+
+        // There is exactly one row in the version table.
+        assert_eq!(
+            Ok(1),
+            conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+                .get(0))
+        );
+
+        // The version must be set to 0
+        assert_eq!(
+            Ok(0),
+            conn.query_row(
+                "SELECT version from persistent.version WHERE id = 0;",
+                NO_PARAMS,
+                |row| row.get(0)
+            )
+        );
+
+        // Will subsequent calls to create_or_get_version still return the same version even
+        // if the current version changes.
+        {
+            let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+            let version = create_or_get_version(&tx, 5).unwrap();
+            tx.commit().unwrap();
+            assert_eq!(version, 0);
+        }
+
+        // There is still exactly one row in the version table.
+        assert_eq!(
+            Ok(1),
+            conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+                .get(0))
+        );
+
+        // Bump the version.
+        {
+            let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+            update_version(&tx, 5).unwrap();
+            tx.commit().unwrap();
+        }
+
+        // Now the version should have changed.
+        {
+            let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+            let version = create_or_get_version(&tx, 7).unwrap();
+            tx.commit().unwrap();
+            assert_eq!(version, 5);
+        }
+
+        // There is still exactly one row in the version table.
+        assert_eq!(
+            Ok(1),
+            conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+                .get(0))
+        );
+
+        // The version must be set to 5
+        assert_eq!(
+            Ok(5),
+            conn.query_row(
+                "SELECT version from persistent.version WHERE id = 0;",
+                NO_PARAMS,
+                |row| row.get(0)
+            )
+        );
+    }
+}
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index 549f574..771d609 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -92,7 +92,7 @@
 
 use std::convert::TryInto;
 
-use crate::db_utils::SqlField;
+use crate::database::utils::SqlField;
 use crate::error::Error as KeystoreError;
 use crate::error::ResponseCode;
 
@@ -825,6 +825,9 @@
     /// When deleted, the key is guaranteed to be permanently deleted and unusable
     #[key_param(tag = ROLLBACK_RESISTANCE, field = BoolValue)]
     RollbackResistance,
+    /// The Key shall only be used during the early boot stage
+    #[key_param(tag = EARLY_BOOT_ONLY, field = BoolValue)]
+    EarlyBootOnly,
     /// The date and time at which the key becomes active
     #[key_param(tag = ACTIVE_DATETIME, field = DateTime)]
     ActiveDateTime(i64),
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index bdc3f2a..19576aa 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -188,7 +188,7 @@
     return std::make_pair(true, isSoftKeyMint);
 }
 
-// Returns the prefix from a blob. If there's no prefix, returns the passed-in blob.
+// Removes the prefix from a blob. If there's no prefix, returns the passed-in blob.
 //
 std::vector<uint8_t> prefixedKeyBlobRemovePrefix(const std::vector<uint8_t>& prefixedBlob) {
     auto parsed = prefixedKeyBlobParsePrefix(prefixedBlob);
@@ -208,6 +208,21 @@
     return parsed.second;
 }
 
+// Inspects the given blob for prefixes.
+// Returns the blob stripped of the prefix if present. The boolean argument is true if the blob was
+// a software blob.
+std::pair<std::vector<uint8_t>, bool>
+dissectPrefixedKeyBlob(const std::vector<uint8_t>& prefixedBlob) {
+    auto [hasPrefix, isSoftware] = prefixedKeyBlobParsePrefix(prefixedBlob);
+    if (!hasPrefix) {
+        // Not actually prefixed, blob was probably persisted to disk prior to the
+        // prefixing code being introduced.
+        return {prefixedBlob, false};
+    }
+    return {std::vector<uint8_t>(prefixedBlob.begin() + kKeyBlobPrefixSize, prefixedBlob.end()),
+            isSoftware};
+}
+
 /*
  * Returns true if the parameter is not understood by KM 4.1 and older but can be enforced by
  * Keystore. These parameters need to be included in the returned KeyCharacteristics, but will not
@@ -289,7 +304,14 @@
 static std::vector<KeyCharacteristics>
 processLegacyCharacteristics(KeyMintSecurityLevel securityLevel,
                              const std::vector<KeyParameter>& genParams,
-                             const V4_0_KeyCharacteristics& legacyKc) {
+                             const V4_0_KeyCharacteristics& legacyKc, bool hwEnforcedOnly = false) {
+
+    KeyCharacteristics hwEnforced{securityLevel,
+                                  convertKeyParametersFromLegacy(legacyKc.hardwareEnforced)};
+
+    if (hwEnforcedOnly) {
+        return {hwEnforced};
+    }
 
     KeyCharacteristics keystoreEnforced{KeyMintSecurityLevel::KEYSTORE,
                                         convertKeyParametersFromLegacy(legacyKc.softwareEnforced)};
@@ -308,8 +330,6 @@
         return {keystoreEnforced};
     }
 
-    KeyCharacteristics hwEnforced{securityLevel,
-                                  convertKeyParametersFromLegacy(legacyKc.hardwareEnforced)};
     return {hwEnforced, keystoreEnforced};
 }
 
@@ -687,12 +707,35 @@
     return convertErrorCode(km_error);
 }
 
-ScopedAStatus
-KeyMintDevice::getKeyCharacteristics(const std::vector<uint8_t>& /* storageKeyBlob */,
-                                     const std::vector<uint8_t>& /* appId */,
-                                     const std::vector<uint8_t>& /* appData */,
-                                     std::vector<KeyCharacteristics>* /* keyCharacteristics */) {
-    return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
+ScopedAStatus KeyMintDevice::getKeyCharacteristics(
+    const std::vector<uint8_t>& prefixedKeyBlob, const std::vector<uint8_t>& appId,
+    const std::vector<uint8_t>& appData, std::vector<KeyCharacteristics>* keyCharacteristics) {
+    auto [strippedKeyBlob, isSoftware] = dissectPrefixedKeyBlob(prefixedKeyBlob);
+    if (isSoftware) {
+        return softKeyMintDevice_->getKeyCharacteristics(strippedKeyBlob, appId, appData,
+                                                         keyCharacteristics);
+    } else {
+        KMV1::ErrorCode km_error;
+        auto ret = mDevice->getKeyCharacteristics(
+            strippedKeyBlob, appId, appData,
+            [&](V4_0_ErrorCode errorCode, const V4_0_KeyCharacteristics& v40KeyCharacteristics) {
+                km_error = convert(errorCode);
+                *keyCharacteristics =
+                    processLegacyCharacteristics(securityLevel_, {} /* getParams */,
+                                                 v40KeyCharacteristics, true /* hwEnforcedOnly */);
+            });
+
+        if (!ret.isOk()) {
+            LOG(ERROR) << __func__ << " getKeyCharacteristics failed: " << ret.description();
+            return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+        }
+        if (km_error != KMV1::ErrorCode::OK) {
+            LOG(ERROR) << __func__
+                       << " getKeyCharacteristics failed with code: " << toString(km_error);
+        }
+
+        return convertErrorCode(km_error);
+    }
 }
 
 ScopedAStatus KeyMintOperation::updateAad(const std::vector<uint8_t>& input,
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index eddd684..56c35bf 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -31,8 +31,9 @@
     use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
         Algorithm::Algorithm, BeginResult::BeginResult, BlockMode::BlockMode, Digest::Digest,
         ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice, KeyCreationResult::KeyCreationResult,
-        KeyFormat::KeyFormat, KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
-        KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
+        KeyFormat::KeyFormat, KeyOrigin::KeyOrigin, KeyParameter::KeyParameter,
+        KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+        SecurityLevel::SecurityLevel, Tag::Tag,
     };
     use android_hardware_security_keymint::binder::{self, Strong};
     use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
@@ -375,4 +376,85 @@
         assert!(result.is_ok(), "{:?}", result);
         assert_ne!(result.unwrap().len(), 0);
     }
+
+    #[test]
+    fn test_get_key_characteristics() {
+        let legacy = get_device_or_skip_test!();
+        let hw_info = legacy.getHardwareInfo().expect("GetHardwareInfo");
+
+        let blob = generate_rsa_key(legacy.as_ref(), false, false);
+        let characteristics =
+            legacy.getKeyCharacteristics(&blob, &[], &[]).expect("GetKeyCharacteristics.");
+
+        assert!(characteristics.iter().any(|kc| kc.securityLevel == hw_info.securityLevel));
+        let sec_level_enforced = &characteristics
+            .iter()
+            .find(|kc| kc.securityLevel == hw_info.securityLevel)
+            .expect("There should be characteristics matching the device's security level.")
+            .authorizations;
+
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter {
+                tag: Tag::PURPOSE,
+                value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN)
+            }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter {
+                tag: Tag::PADDING,
+                value: KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS)
+            }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter {
+                tag: Tag::ALGORITHM,
+                value: KeyParameterValue::Algorithm(Algorithm::RSA)
+            }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(2048) }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter {
+                tag: Tag::RSA_PUBLIC_EXPONENT,
+                value: KeyParameterValue::LongInteger(65537)
+            }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter {
+                tag: Tag::ORIGIN,
+                value: KeyParameterValue::Origin(KeyOrigin::GENERATED)
+            }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter { tag: Tag::OS_VERSION, value: KeyParameterValue::Integer(_) }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KeyParameterValue::Integer(_) }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KeyParameterValue::Integer(_) }
+        )));
+        assert!(sec_level_enforced.iter().any(|kp| matches!(
+            kp,
+            KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KeyParameterValue::Integer(_) }
+        )));
+    }
 }
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
index d5647cd..f92fd45 100644
--- a/keystore2/src/legacy_migrator.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -14,11 +14,11 @@
 
 //! This module acts as a bridge between the legacy key database and the keystore2 database.
 
-use crate::error::Error;
 use crate::key_parameter::KeyParameterValue;
 use crate::legacy_blob::BlobValue;
 use crate::utils::{uid_to_android_user, watchdog as wd};
 use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
+use crate::{database::KeyType, error::Error};
 use crate::{
     database::{
         BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
@@ -523,6 +523,7 @@
                 self.db
                     .store_new_key(
                         &key,
+                        KeyType::Client,
                         &params,
                         &(&blob, &blob_metadata),
                         &CertificateInfo::new(user_cert, ca_cert),
@@ -535,7 +536,7 @@
             None => {
                 if let Some(ca_cert) = ca_cert {
                     self.db
-                        .store_new_certificate(&key, &ca_cert, &KEYSTORE_UUID)
+                        .store_new_certificate(&key, KeyType::Client, &ca_cert, &KEYSTORE_UUID)
                         .context("In check_and_migrate: Failed to insert new certificate.")?;
                     Ok(())
                 } else {
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index c04c4b0..51316d7 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -44,7 +44,6 @@
 
 mod attestation_key_utils;
 mod audit_log;
-mod db_utils;
 mod gc;
 mod super_key;
 
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index 9e6ef41..cd54915 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -19,14 +19,14 @@
         BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, KeyEntry, KeyEntryLoadBits,
         KeyIdGuard, KeyMetaData, KeyMetaEntry, KeyType, KeystoreDB, SubComponentType, Uuid,
     },
-    error::{map_km_error, Error},
+    error::{map_km_error, Error, ErrorCode},
     globals::get_keymint_device,
     super_key::KeyBlob,
-    utils::{key_characteristics_to_internal, watchdog as wd, Asp, AID_KEYSTORE},
+    utils::{key_characteristics_to_internal, watchdog as wd, AID_KEYSTORE},
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    BeginResult::BeginResult, ErrorCode::ErrorCode, HardwareAuthToken::HardwareAuthToken,
-    IKeyMintDevice::IKeyMintDevice, IKeyMintOperation::IKeyMintOperation,
+    HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
+    IKeyMintOperation::IKeyMintOperation, KeyCharacteristics::KeyCharacteristics,
     KeyCreationResult::KeyCreationResult, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
     SecurityLevel::SecurityLevel,
 };
@@ -46,16 +46,54 @@
 /// Because these methods run very early, we don't even try to cooperate with
 /// the operation slot database; we assume there will be plenty of slots.
 pub struct KeyMintDevice {
-    asp: Asp,
+    km_dev: Strong<dyn IKeyMintDevice>,
     km_uuid: Uuid,
+    version: i32,
+    security_level: SecurityLevel,
 }
 
 impl KeyMintDevice {
+    /// Version number of KeyMasterDevice@V4_0
+    pub const KEY_MASTER_V4_0: i32 = 40;
+    /// Version number of KeyMasterDevice@V4_1
+    pub const KEY_MASTER_V4_1: i32 = 41;
+    /// Version number of KeyMintDevice@V1
+    pub const KEY_MINT_V1: i32 = 100;
+
     /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`]
     pub fn get(security_level: SecurityLevel) -> Result<KeyMintDevice> {
-        let (asp, _hw_info, km_uuid) = get_keymint_device(&security_level)
+        let (asp, hw_info, km_uuid) = get_keymint_device(&security_level)
             .context("In KeyMintDevice::get: get_keymint_device failed")?;
-        Ok(KeyMintDevice { asp, km_uuid })
+
+        Ok(KeyMintDevice {
+            km_dev: asp.get_interface()?,
+            km_uuid,
+            version: hw_info.versionNumber,
+            security_level: hw_info.securityLevel,
+        })
+    }
+
+    /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`], return
+    /// [`None`] if the error `HARDWARE_TYPE_UNAVAILABLE` is returned
+    pub fn get_or_none(security_level: SecurityLevel) -> Result<Option<KeyMintDevice>> {
+        KeyMintDevice::get(security_level).map(Some).or_else(|e| {
+            match e.root_cause().downcast_ref::<Error>() {
+                Some(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)) => Ok(None),
+                _ => Err(e),
+            }
+        })
+    }
+
+    /// Returns the version of the underlying KeyMint/KeyMaster device.
+    pub fn version(&self) -> i32 {
+        self.version
+    }
+
+    /// Returns the self advertised security level of the KeyMint device.
+    /// This may differ from the requested security level if the best security level
+    /// on the device is Software.
+    pub fn security_level(&self) -> SecurityLevel {
+        self.security_level
     }
 
     /// Create a KM key and store in the database.
@@ -63,17 +101,14 @@
         &self,
         db: &mut KeystoreDB,
         key_desc: &KeyDescriptor,
+        key_type: KeyType,
         creator: F,
     ) -> Result<()>
     where
-        F: FnOnce(Strong<dyn IKeyMintDevice>) -> Result<KeyCreationResult, binder::Status>,
+        F: FnOnce(&Strong<dyn IKeyMintDevice>) -> Result<KeyCreationResult, binder::Status>,
     {
-        let km_dev: Strong<dyn IKeyMintDevice> = self
-            .asp
-            .get_interface()
-            .context("In create_and_store_key: Failed to get KeyMint device")?;
-        let creation_result =
-            map_km_error(creator(km_dev)).context("In create_and_store_key: creator failed")?;
+        let creation_result = map_km_error(creator(&self.km_dev))
+            .context("In create_and_store_key: creator failed")?;
         let key_parameters = key_characteristics_to_internal(creation_result.keyCharacteristics);
 
         let creation_date =
@@ -86,6 +121,7 @@
 
         db.store_new_key(
             &key_desc,
+            key_type,
             &key_parameters,
             &(&creation_result.keyBlob, &blob_metadata),
             &CertificateInfo::new(None, None),
@@ -107,18 +143,17 @@
     }
 
     /// Look up an internal-use key in the database given a key descriptor.
-    pub fn lookup_from_desc(
+    fn lookup_from_desc(
         db: &mut KeystoreDB,
         key_desc: &KeyDescriptor,
+        key_type: KeyType,
     ) -> Result<(KeyIdGuard, KeyEntry)> {
-        db.load_key_entry(&key_desc, KeyType::Client, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| {
-            Ok(())
-        })
-        .context("In lookup_from_desc: load_key_entry failed")
+        db.load_key_entry(&key_desc, key_type, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| Ok(()))
+            .context("In lookup_from_desc: load_key_entry failed.")
     }
 
     /// Look up the key in the database, and return None if it is absent.
-    pub fn not_found_is_none(
+    fn not_found_is_none(
         lookup: Result<(KeyIdGuard, KeyEntry)>,
     ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
         match lookup {
@@ -132,50 +167,105 @@
 
     /// This does the lookup and store in separate transactions; caller must
     /// hold a lock before calling.
-    pub fn lookup_or_generate_key(
+    pub fn lookup_or_generate_key<F>(
         &self,
         db: &mut KeystoreDB,
         key_desc: &KeyDescriptor,
+        key_type: KeyType,
         params: &[KeyParameter],
-    ) -> Result<(KeyIdGuard, KeyEntry)> {
+        validate_characteristics: F,
+    ) -> Result<(KeyIdGuard, KeyBlob)>
+    where
+        F: FnOnce(&[KeyCharacteristics]) -> bool,
+    {
         // We use a separate transaction for the lookup than for the store
         // - to keep the code simple
         // - because the caller needs to hold a lock in any case
         // - because it avoids holding database locks during slow
         //   KeyMint operations
-        let lookup = Self::not_found_is_none(Self::lookup_from_desc(db, key_desc))
+        let lookup = Self::not_found_is_none(Self::lookup_from_desc(db, key_desc, key_type))
             .context("In lookup_or_generate_key: first lookup failed")?;
-        if let Some(result) = lookup {
-            Ok(result)
-        } else {
-            self.create_and_store_key(db, &key_desc, |km_dev| km_dev.generateKey(&params, None))
-                .context("In lookup_or_generate_key: generate_and_store_key failed")?;
-            Self::lookup_from_desc(db, key_desc)
-                .context("In lookup_or_generate_key: second lookup failed")
+
+        if let Some((key_id_guard, mut key_entry)) = lookup {
+            // If the key is associated with a different km instance
+            // or if there is no blob metadata for some reason the key entry
+            // is considered corrupted and needs to be replaced with a new one.
+            let key_blob = key_entry.take_key_blob_info().and_then(|(key_blob, blob_metadata)| {
+                if Some(&self.km_uuid) == blob_metadata.km_uuid() {
+                    Some(key_blob)
+                } else {
+                    None
+                }
+            });
+
+            if let Some(key_blob_vec) = key_blob {
+                let (key_characteristics, key_blob) = self
+                    .upgrade_keyblob_if_required_with(
+                        db,
+                        &key_id_guard,
+                        KeyBlob::NonSensitive(key_blob_vec),
+                        |key_blob| {
+                            map_km_error({
+                                let _wp = wd::watch_millis(
+                                    concat!(
+                                        "In KeyMintDevice::lookup_or_generate_key: ",
+                                        "calling getKeyCharacteristics."
+                                    ),
+                                    500,
+                                );
+                                self.km_dev.getKeyCharacteristics(key_blob, &[], &[])
+                            })
+                        },
+                    )
+                    .context("In lookup_or_generate_key: calling getKeyCharacteristics")?;
+
+                if validate_characteristics(&key_characteristics) {
+                    return Ok((key_id_guard, key_blob));
+                }
+
+                // If this point is reached the existing key is considered outdated or corrupted
+                // in some way. It will be replaced with a new key below.
+            };
         }
+
+        self.create_and_store_key(db, &key_desc, key_type, |km_dev| {
+            km_dev.generateKey(&params, None)
+        })
+        .context("In lookup_or_generate_key: generate_and_store_key failed")?;
+        Self::lookup_from_desc(db, key_desc, key_type)
+            .and_then(|(key_id_guard, mut key_entry)| {
+                Ok((
+                    key_id_guard,
+                    key_entry
+                        .take_key_blob_info()
+                        .ok_or(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                        .map(|(key_blob, _)| KeyBlob::NonSensitive(key_blob))
+                        .context("Missing key blob info.")?,
+                ))
+            })
+            .context("In lookup_or_generate_key: second lookup failed")
     }
 
     /// Call the passed closure; if it returns `KEY_REQUIRES_UPGRADE`, call upgradeKey, and
     /// write the upgraded key to the database.
-    fn upgrade_keyblob_if_required_with<T, F>(
+    fn upgrade_keyblob_if_required_with<'a, T, F>(
         &self,
         db: &mut KeystoreDB,
-        km_dev: &Strong<dyn IKeyMintDevice>,
         key_id_guard: &KeyIdGuard,
-        key_blob: &KeyBlob,
+        key_blob: KeyBlob<'a>,
         f: F,
-    ) -> Result<T>
+    ) -> Result<(T, KeyBlob<'a>)>
     where
         F: Fn(&[u8]) -> Result<T, Error>,
     {
-        match f(key_blob) {
+        match f(&key_blob) {
             Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
                 let upgraded_blob = map_km_error({
                     let _wp = wd::watch_millis(
                         "In KeyMintDevice::upgrade_keyblob_if_required_with: calling upgradeKey.",
                         500,
                     );
-                    km_dev.upgradeKey(key_blob, &[])
+                    self.km_dev.upgradeKey(&key_blob, &[])
                 })
                 .context("In upgrade_keyblob_if_required_with: Upgrade failed")?;
 
@@ -193,12 +283,17 @@
                     "Failed to insert upgraded blob into the database"
                 ))?;
 
-                Ok(f(&upgraded_blob).context(concat!(
-                    "In upgrade_keyblob_if_required_with: ",
-                    "Closure failed after upgrade"
-                ))?)
+                Ok((
+                    f(&upgraded_blob).context(
+                        "In upgrade_keyblob_if_required_with: Closure failed after upgrade",
+                    )?,
+                    KeyBlob::NonSensitive(upgraded_blob),
+                ))
             }
-            result => Ok(result.context("In upgrade_keyblob_if_required_with: Closure failed")?),
+            result => Ok((
+                result.context("In upgrade_keyblob_if_required_with: Closure failed")?,
+                key_blob,
+            )),
         }
     }
 
@@ -209,29 +304,19 @@
         &self,
         db: &mut KeystoreDB,
         key_id_guard: &KeyIdGuard,
-        key_entry: &KeyEntry,
+        key_blob: &[u8],
         purpose: KeyPurpose,
         operation_parameters: &[KeyParameter],
         auth_token: Option<&HardwareAuthToken>,
         input: &[u8],
     ) -> Result<Vec<u8>> {
-        let km_dev: Strong<dyn IKeyMintDevice> = self
-            .asp
-            .get_interface()
-            .context("In use_key_in_one_step: Failed to get KeyMint device")?;
+        let key_blob = KeyBlob::Ref(key_blob);
 
-        let (key_blob, _blob_metadata) = key_entry
-            .key_blob_info()
-            .as_ref()
-            .ok_or_else(Error::sys)
-            .context("use_key_in_one_step: Keyblob missing")?;
-        let key_blob = KeyBlob::Ref(&key_blob);
-
-        let begin_result: BeginResult = self
-            .upgrade_keyblob_if_required_with(db, &km_dev, key_id_guard, &key_blob, |blob| {
+        let (begin_result, _) = self
+            .upgrade_keyblob_if_required_with(db, key_id_guard, key_blob, |blob| {
                 map_km_error({
                     let _wp = wd::watch_millis("In use_key_in_one_step: calling: begin", 500);
-                    km_dev.begin(purpose, blob, operation_parameters, auth_token)
+                    self.km_dev.begin(purpose, blob, operation_parameters, auth_token)
                 })
             })
             .context("In use_key_in_one_step: Failed to begin operation.")?;
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 00b26e4..f78d98b 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -176,6 +176,7 @@
                     let key_id = db
                         .store_new_key(
                             &key,
+                            KeyType::Client,
                             &key_parameters,
                             &(&key_blob, &blob_metadata),
                             &cert_info,
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index 1f61729..d65743d 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -241,8 +241,13 @@
             check_key_permission(KeyPerm::rebind(), &key, &None)
                 .context("Caller does not have permission to insert this certificate.")?;
 
-            db.store_new_certificate(&key, certificate_chain.unwrap(), &KEYSTORE_UUID)
-                .context("Failed to insert new certificate.")?;
+            db.store_new_certificate(
+                &key,
+                KeyType::Client,
+                certificate_chain.unwrap(),
+                &KEYSTORE_UUID,
+            )
+            .context("Failed to insert new certificate.")?;
             Ok(())
         })
         .context("In update_subcomponent.")
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 7a8b9be..9fb267a 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -19,7 +19,7 @@
     database::EncryptedBy,
     database::KeyEntry,
     database::KeyType,
-    database::{KeyIdGuard, KeyMetaData, KeyMetaEntry, KeystoreDB},
+    database::{KeyEntryLoadBits, KeyIdGuard, KeyMetaData, KeyMetaEntry, KeystoreDB},
     ec_crypto::ECDHPrivateKey,
     enforcements::Enforcements,
     error::Error,
@@ -30,6 +30,7 @@
     raw_device::KeyMintDevice,
     try_insert::TryInsert,
     utils::watchdog as wd,
+    utils::AID_KEYSTORE,
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, BlockMode::BlockMode, HardwareAuthToken::HardwareAuthToken,
@@ -194,6 +195,12 @@
         auth_token: &HardwareAuthToken,
         reencrypt_with: Option<Arc<SuperKey>>,
     ) -> Result<Arc<SuperKey>> {
+        let key_blob = key_entry
+            .key_blob_info()
+            .as_ref()
+            .map(|(key_blob, _)| KeyBlob::Ref(key_blob))
+            .ok_or(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+            .context("In LockedKey::decrypt: Missing key blob info.")?;
         let key_params = vec![
             KeyParameterValue::Algorithm(Algorithm::AES),
             KeyParameterValue::KeySize(256),
@@ -206,7 +213,7 @@
         let key = ZVec::try_from(km_dev.use_key_in_one_step(
             db,
             key_id_guard,
-            key_entry,
+            &key_blob,
             KeyPurpose::DECRYPT,
             &key_params,
             Some(auth_token),
@@ -949,13 +956,23 @@
                     }
                     let key_params: Vec<KmKeyParameter> =
                         key_params.into_iter().map(|x| x.into()).collect();
-                    km_dev.create_and_store_key(db, &key_desc, |dev| {
-                        let _wp = wd::watch_millis(
-                            "In lock_screen_lock_bound_key: calling importKey.",
-                            500,
-                        );
-                        dev.importKey(key_params.as_slice(), KeyFormat::RAW, &encrypting_key, None)
-                    })?;
+                    km_dev.create_and_store_key(
+                        db,
+                        &key_desc,
+                        KeyType::Client, /* TODO Should be Super b/189470584 */
+                        |dev| {
+                            let _wp = wd::watch_millis(
+                                "In lock_screen_lock_bound_key: calling importKey.",
+                                500,
+                            );
+                            dev.importKey(
+                                key_params.as_slice(),
+                                KeyFormat::RAW,
+                                &encrypting_key,
+                                None,
+                            )
+                        },
+                    )?;
                     entry.biometric_unlock = Some(BiometricUnlock {
                         sids: unlocking_sids.into(),
                         key_desc,
@@ -985,8 +1002,15 @@
         let mut data = self.data.lock().unwrap();
         let mut entry = data.user_keys.entry(user_id).or_default();
         if let Some(biometric) = entry.biometric_unlock.as_ref() {
-            let (key_id_guard, key_entry) =
-                KeyMintDevice::lookup_from_desc(db, &biometric.key_desc)?;
+            let (key_id_guard, key_entry) = db
+                .load_key_entry(
+                    &biometric.key_desc,
+                    KeyType::Client, // This should not be a Client key.
+                    KeyEntryLoadBits::KM,
+                    AID_KEYSTORE,
+                    |_, _| Ok(()),
+                )
+                .context("In try_unlock_user_with_biometric: load_key_entry failed")?;
             let km_dev: KeyMintDevice = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
                 .context("In try_unlock_user_with_biometric: KeyMintDevice::get failed")?;
             for sid in &biometric.sids {