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, ¶ms)
+ .lookup_or_generate_key(db, &key_desc, KeyType::Client, ¶ms, |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, ¶ms)?;
+
+ 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,
¶ms,
&(&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(¶ms, 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(¶ms, 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 {