Keystore 2.0: Adding uuid field to persistent.keyentry
This change adds a uuid field to map keys to KM devices to the keyentry
table. For now, the security level reported by the KeyMint instance's
hardware info is uased as uuid until the hardware info returns an
actual uuid. This security level may differ from the security level
requested by keystore clients in some situations, e.g., when running a
pure software implementation or on chrome os.
Test: atest keystore2_test
Change-Id: I4b9556804eb6a435ac48d5929fc238e22c23d94d
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 840fbc6..8e5507e 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -48,7 +48,7 @@
use crate::permission::KeyPermSet;
use crate::utils::get_current_time_in_seconds;
use anyhow::{anyhow, Context, Result};
-use std::{convert::TryFrom, convert::TryInto, time::SystemTimeError};
+use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
HardwareAuthToken::HardwareAuthToken,
@@ -187,6 +187,50 @@
}
}
+/// Uuid representation that can be stored in the database.
+/// Right now it can only be initialized from SecurityLevel.
+/// Once KeyMint provides a UUID type a corresponding From impl shall be added.
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Uuid([u8; 16]);
+
+impl Deref for Uuid {
+ type Target = [u8; 16];
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl From<SecurityLevel> for Uuid {
+ fn from(sec_level: SecurityLevel) -> Self {
+ Self((sec_level.0 as u128).to_be_bytes())
+ }
+}
+
+impl ToSql for Uuid {
+ fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+ self.0.to_sql()
+ }
+}
+
+impl FromSql for Uuid {
+ fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
+ let blob = Vec::<u8>::column_result(value)?;
+ if blob.len() != 16 {
+ return Err(FromSqlError::OutOfRange(blob.len() as i64));
+ }
+ let mut arr = [0u8; 16];
+ arr.copy_from_slice(&blob);
+ Ok(Self(arr))
+ }
+}
+
+/// Key entries that are not associated with any KeyMint instance, such as pure certificate
+/// entries are associated with this UUID.
+pub static KEYSTORE_UUID: Uuid = Uuid([
+ 0x41, 0xe3, 0xb9, 0xce, 0x27, 0x58, 0x4e, 0x91, 0xbc, 0xfd, 0xa5, 0x5d, 0x91, 0x85, 0xab, 0x11,
+]);
+
/// Indicates how the sensitive part of this key blob is encrypted.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum EncryptedBy {
@@ -435,6 +479,30 @@
}
}
+/// This type represents a certificate and certificate chain entry for a key.
+#[derive(Debug)]
+pub struct CertificateInfo {
+ cert: Option<Vec<u8>>,
+ cert_chain: Option<Vec<u8>>,
+}
+
+impl CertificateInfo {
+ /// Constructs a new CertificateInfo object from `cert` and `cert_chain`
+ pub fn new(cert: Option<Vec<u8>>, cert_chain: Option<Vec<u8>>) -> Self {
+ Self { cert, cert_chain }
+ }
+
+ /// Take the cert
+ pub fn take_cert(&mut self) -> Option<Vec<u8>> {
+ self.cert.take()
+ }
+
+ /// Take the cert chain
+ pub fn take_cert_chain(&mut self) -> Option<Vec<u8>> {
+ self.cert_chain.take()
+ }
+}
+
/// This type represents a Keystore 2.0 key entry.
/// An entry has a unique `id` by which it can be found in the database.
/// It has a security level field, key parameters, and three optional fields
@@ -445,7 +513,7 @@
km_blob: Option<Vec<u8>>,
cert: Option<Vec<u8>>,
cert_chain: Option<Vec<u8>>,
- sec_level: SecurityLevel,
+ km_uuid: Uuid,
parameters: Vec<KeyParameter>,
metadata: KeyMetaData,
pure_cert: bool,
@@ -480,9 +548,9 @@
pub fn take_cert_chain(&mut self) -> Option<Vec<u8>> {
self.cert_chain.take()
}
- /// Returns the security level of the key entry.
- pub fn sec_level(&self) -> SecurityLevel {
- self.sec_level
+ /// Returns the uuid of the owning KeyMint instance.
+ pub fn km_uuid(&self) -> &Uuid {
+ &self.km_uuid
}
/// Exposes the key parameters of this key entry.
pub fn key_parameters(&self) -> &Vec<KeyParameter> {
@@ -641,7 +709,8 @@
domain INTEGER,
namespace INTEGER,
alias BLOB,
- state INTEGER);",
+ state INTEGER,
+ km_uuid BLOB);",
NO_PARAMS,
)
.context("Failed to initialize \"keyentry\" table.")?;
@@ -830,6 +899,7 @@
domain: Domain,
namespace: i64,
alias: &str,
+ km_uuid: Uuid,
create_new_key: F,
) -> Result<(KeyIdGuard, KeyEntry)>
where
@@ -876,9 +946,17 @@
let id = Self::insert_with_retry(|id| {
tx.execute(
"INSERT into persistent.keyentry
- (id, key_type, domain, namespace, alias, state)
- VALUES(?, ?, ?, ?, ?, ?);",
- params![id, KeyType::Super, domain.0, namespace, alias, KeyLifeCycle::Live],
+ (id, key_type, domain, namespace, alias, state, km_uuid)
+ VALUES(?, ?, ?, ?, ?, ?, ?);",
+ params![
+ id,
+ KeyType::Super,
+ domain.0,
+ namespace,
+ alias,
+ KeyLifeCycle::Live,
+ km_uuid,
+ ],
)
})
.context("In get_or_create_key_with.")?;
@@ -925,9 +1003,14 @@
/// key artifacts, i.e., blobs and parameters have been associated with the new
/// key id. Finalizing with `rebind_alias` makes the creation of a new key entry
/// atomic even if key generation is not.
- pub fn create_key_entry(&mut self, domain: Domain, namespace: i64) -> Result<KeyIdGuard> {
+ pub fn create_key_entry(
+ &mut self,
+ domain: Domain,
+ namespace: i64,
+ km_uuid: &Uuid,
+ ) -> Result<KeyIdGuard> {
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- Self::create_key_entry_internal(tx, domain, namespace)
+ Self::create_key_entry_internal(tx, domain, namespace, km_uuid)
})
.context("In create_key_entry.")
}
@@ -936,6 +1019,7 @@
tx: &Transaction,
domain: Domain,
namespace: i64,
+ km_uuid: &Uuid,
) -> Result<KeyIdGuard> {
match domain {
Domain::APP | Domain::SELINUX => {}
@@ -948,14 +1032,15 @@
Self::insert_with_retry(|id| {
tx.execute(
"INSERT into persistent.keyentry
- (id, key_type, domain, namespace, alias, state)
- VALUES(?, ?, ?, ?, NULL, ?);",
+ (id, key_type, domain, namespace, alias, state, km_uuid)
+ VALUES(?, ?, ?, ?, NULL, ?, ?);",
params![
id,
KeyType::Client,
domain.0 as u32,
namespace,
- KeyLifeCycle::Existing
+ KeyLifeCycle::Existing,
+ km_uuid,
],
)
})
@@ -1105,7 +1190,7 @@
newid.0,
domain.0 as u32,
namespace,
- KeyLifeCycle::Existing
+ KeyLifeCycle::Existing,
],
)
.context("In rebind_alias: Failed to set alias.")?;
@@ -1128,9 +1213,9 @@
key: KeyDescriptor,
params: impl IntoIterator<Item = &'a KeyParameter>,
blob: &[u8],
- cert: Option<&[u8]>,
- cert_chain: Option<&[u8]>,
+ cert_info: &CertificateInfo,
metadata: &KeyMetaData,
+ km_uuid: &Uuid,
) -> Result<(bool, KeyIdGuard)> {
let (alias, domain, namespace) = match key {
KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
@@ -1143,15 +1228,15 @@
}
};
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let key_id = Self::create_key_entry_internal(tx, domain, namespace)
+ let key_id = Self::create_key_entry_internal(tx, domain, namespace, km_uuid)
.context("Trying to create new key entry.")?;
Self::set_blob_internal(tx, key_id.id(), SubComponentType::KEY_BLOB, Some(blob))
.context("Trying to insert the key blob.")?;
- if let Some(cert) = cert {
+ if let Some(cert) = &cert_info.cert {
Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT, Some(&cert))
.context("Trying to insert the certificate.")?;
}
- if let Some(cert_chain) = cert_chain {
+ if let Some(cert_chain) = &cert_info.cert_chain {
Self::set_blob_internal(
tx,
key_id.id(),
@@ -1173,7 +1258,12 @@
/// Store a new certificate
/// The function creates a new key entry, populates the blob field and metadata, and rebinds
/// the given alias to the new cert.
- pub fn store_new_certificate(&mut self, key: KeyDescriptor, cert: &[u8]) -> Result<KeyIdGuard> {
+ pub fn store_new_certificate(
+ &mut self,
+ key: KeyDescriptor,
+ cert: &[u8],
+ km_uuid: &Uuid,
+ ) -> Result<KeyIdGuard> {
let (alias, domain, namespace) = match key {
KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
| KeyDescriptor { alias: Some(alias), domain: Domain::SELINUX, nspace, blob: None } => {
@@ -1186,7 +1276,7 @@
}
};
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let key_id = Self::create_key_entry_internal(tx, domain, namespace)
+ let key_id = Self::create_key_entry_internal(tx, domain, namespace, km_uuid)
.context("Trying to create new key entry.")?;
Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT_CHAIN, Some(cert))
@@ -1592,6 +1682,15 @@
.context("In unbind_key.")
}
+ fn get_key_km_uuid(tx: &Transaction, key_id: i64) -> Result<Uuid> {
+ tx.query_row(
+ "SELECT km_uuid FROM persistent.keyentry WHERE id = ?",
+ params![key_id],
+ |row| row.get(0),
+ )
+ .context("In get_key_km_uuid.")
+ }
+
fn load_key_components(
tx: &Transaction,
load_bits: KeyEntryLoadBits,
@@ -1603,25 +1702,18 @@
Self::load_blob_components(key_id, load_bits, &tx)
.context("In load_key_components.")?;
- let parameters =
- Self::load_key_parameters(key_id, &tx).context("In load_key_components.")?;
+ let parameters = Self::load_key_parameters(key_id, &tx)
+ .context("In load_key_components: Trying to load key parameters.")?;
- // Extract the security level by checking the security level of the origin tag.
- // Super keys don't have key parameters so we use security_level software by default.
- let sec_level = parameters
- .iter()
- .find_map(|k| match k.get_tag() {
- Tag::ORIGIN => Some(*k.security_level()),
- _ => None,
- })
- .unwrap_or(SecurityLevel::SOFTWARE);
+ let km_uuid = Self::get_key_km_uuid(&tx, key_id)
+ .context("In load_key_components: Trying to get KM uuid.")?;
Ok(KeyEntry {
id: key_id,
km_blob,
cert: cert_blob,
cert_chain: cert_chain_blob,
- sec_level,
+ km_uuid,
parameters,
metadata,
pure_cert: !has_km_blob,
@@ -2072,7 +2164,7 @@
let temp_dir = TempDir::new("persistent_db_test")?;
let mut db = KeystoreDB::new(temp_dir.path())?;
- db.create_key_entry(Domain::APP, 100)?;
+ db.create_key_entry(Domain::APP, 100, &KEYSTORE_UUID)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 1);
@@ -2085,31 +2177,31 @@
#[test]
fn test_create_key_entry() -> Result<()> {
- fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>) {
- (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref())
+ fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>, Uuid) {
+ (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref(), ke.km_uuid.unwrap())
}
let mut db = new_test_db()?;
- db.create_key_entry(Domain::APP, 100)?;
- db.create_key_entry(Domain::SELINUX, 101)?;
+ db.create_key_entry(Domain::APP, 100, &KEYSTORE_UUID)?;
+ db.create_key_entry(Domain::SELINUX, 101, &KEYSTORE_UUID)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None));
- assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None));
+ assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None, KEYSTORE_UUID));
+ assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None, KEYSTORE_UUID));
// Test that we must pass in a valid Domain.
check_result_is_error_containing_string(
- db.create_key_entry(Domain::GRANT, 102),
+ db.create_key_entry(Domain::GRANT, 102, &KEYSTORE_UUID),
"Domain Domain(1) must be either App or SELinux.",
);
check_result_is_error_containing_string(
- db.create_key_entry(Domain::BLOB, 103),
+ db.create_key_entry(Domain::BLOB, 103, &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),
+ db.create_key_entry(Domain::KEY_ID, 104, &KEYSTORE_UUID),
"Domain Domain(4) must be either App or SELinux.",
);
@@ -2118,31 +2210,48 @@
#[test]
fn test_rebind_alias() -> Result<()> {
- fn extractor(ke: &KeyEntryRow) -> (Option<Domain>, Option<i64>, Option<&str>) {
- (ke.domain, ke.namespace, ke.alias.as_deref())
+ fn extractor(
+ ke: &KeyEntryRow,
+ ) -> (Option<Domain>, Option<i64>, Option<&str>, Option<Uuid>) {
+ (ke.domain, ke.namespace, ke.alias.as_deref(), ke.km_uuid)
}
let mut db = new_test_db()?;
- db.create_key_entry(Domain::APP, 42)?;
- db.create_key_entry(Domain::APP, 42)?;
+ db.create_key_entry(Domain::APP, 42, &KEYSTORE_UUID)?;
+ db.create_key_entry(Domain::APP, 42, &KEYSTORE_UUID)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), None));
- assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None));
+ assert_eq!(
+ extractor(&entries[0]),
+ (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
+ );
+ assert_eq!(
+ extractor(&entries[1]),
+ (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
+ );
// Test that the first call to rebind_alias sets the alias.
rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[0].id), "foo", Domain::APP, 42)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), Some("foo")));
- assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None));
+ assert_eq!(
+ extractor(&entries[0]),
+ (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+ );
+ assert_eq!(
+ extractor(&entries[1]),
+ (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
+ );
// Test that the second call to rebind_alias also empties the old one.
rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[1].id), "foo", Domain::APP, 42)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (None, None, None));
- assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), Some("foo")));
+ assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
+ assert_eq!(
+ extractor(&entries[1]),
+ (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+ );
// Test that we must pass in a valid Domain.
check_result_is_error_containing_string(
@@ -2166,8 +2275,11 @@
// Test that we correctly abort the transaction in this case.
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (None, None, None));
- assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), Some("foo")));
+ assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
+ assert_eq!(
+ extractor(&entries[1]),
+ (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+ );
Ok(())
}
@@ -2180,9 +2292,9 @@
let mut db = new_test_db()?;
db.conn.execute(
- "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias, state)
- VALUES (1, 0, 0, 15, 'key', 1), (2, 0, 2, 7, 'yek', 1);",
- NO_PARAMS,
+ "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias, state, km_uuid)
+ VALUES (1, 0, 0, 15, 'key', 1, ?), (2, 0, 2, 7, 'yek', 1, ?);",
+ params![KEYSTORE_UUID, KEYSTORE_UUID],
)?;
let app_key = KeyDescriptor {
domain: super::Domain::APP,
@@ -2426,6 +2538,7 @@
blob: None,
},
TEST_CERT_BLOB,
+ &KEYSTORE_UUID,
)
.expect("Trying to insert cert.");
@@ -2965,6 +3078,7 @@
namespace: Option<i64>,
alias: Option<String>,
state: KeyLifeCycle,
+ km_uuid: Option<Uuid>,
}
fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
@@ -2981,6 +3095,7 @@
namespace: row.get(3)?,
alias: row.get(4)?,
state: row.get(5)?,
+ km_uuid: row.get(6)?,
})
})?
.map(|r| r.context("Could not read keyentry row."))
@@ -3223,7 +3338,7 @@
alias: &str,
max_usage_count: Option<i32>,
) -> Result<KeyIdGuard> {
- let key_id = db.create_key_entry(domain, namespace)?;
+ let key_id = db.create_key_entry(domain, namespace, &KEYSTORE_UUID)?;
db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB))?;
db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB))?;
db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB))?;
@@ -3255,7 +3370,7 @@
km_blob: Some(TEST_KEY_BLOB.to_vec()),
cert: Some(TEST_CERT_BLOB.to_vec()),
cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
- sec_level: SecurityLevel::TRUSTED_ENVIRONMENT,
+ km_uuid: KEYSTORE_UUID,
parameters: params,
metadata,
pure_cert: false,
@@ -3264,21 +3379,29 @@
fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
let mut stmt = db.conn.prepare(
- "SELECT id, key_type, domain, namespace, alias, state FROM persistent.keyentry;",
+ "SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
)?;
- let rows = stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle), _, _>(
+ let rows = stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle, Uuid), _, _>(
NO_PARAMS,
|row| {
- Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?, row.get(5)?))
+ Ok((
+ row.get(0)?,
+ row.get(1)?,
+ row.get(2)?,
+ row.get(3)?,
+ row.get(4)?,
+ row.get(5)?,
+ row.get(6)?,
+ ))
},
)?;
println!("Key entry table rows:");
for r in rows {
- let (id, key_type, domain, namespace, alias, state) = r.unwrap();
+ let (id, key_type, domain, namespace, alias, state, km_uuid) = r.unwrap();
println!(
- " id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?}",
- id, key_type, domain, namespace, alias, state
+ " id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?} KmUuid: {:?}",
+ id, key_type, domain, namespace, alias, state, km_uuid
);
}
Ok(())
diff --git a/keystore2/src/gc.rs b/keystore2/src/gc.rs
index b5bdd98..692cb7e 100644
--- a/keystore2/src/gc.rs
+++ b/keystore2/src/gc.rs
@@ -18,7 +18,7 @@
//! optionally dispose of sensitive key material appropriately, and then delete
//! the key entry from the database.
-use crate::globals::{get_keymint_device, DB};
+use crate::globals::{get_keymint_dev_by_uuid, DB};
use crate::{error::map_km_error, globals::ASYNC_TASK};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::IKeyMintDevice::IKeyMintDevice;
use anyhow::Result;
@@ -42,7 +42,9 @@
if let Some((key_id, mut key_entry)) = db.get_unreferenced_key()? {
if let Some(blob) = key_entry.take_km_blob() {
let km_dev: Box<dyn IKeyMintDevice> =
- get_keymint_device(key_entry.sec_level())?.get_interface()?;
+ get_keymint_dev_by_uuid(key_entry.km_uuid())
+ .map(|(dev, _)| dev)?
+ .get_interface()?;
if let Err(e) = map_km_error(km_dev.deleteKey(&blob)) {
// Log but ignore error.
log::error!("Error trying to delete key. {:?}", e);
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 50ec26c..c488a18 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -16,7 +16,6 @@
//! database connections and connections to services that Keystore needs
//! to talk to.
-use crate::enforcements::Enforcements;
use crate::gc::Gc;
use crate::legacy_blob::LegacyBlobLoader;
use crate::super_key::SuperKeyManager;
@@ -24,9 +23,13 @@
use crate::{async_task::AsyncTask, database::MonotonicRawTime};
use crate::{
database::KeystoreDB,
+ database::Uuid,
error::{map_binder_status, map_binder_status_code, Error, ErrorCode},
};
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use crate::{enforcements::Enforcements, error::map_km_error};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ KeyMintHardwareInfo::KeyMintHardwareInfo, SecurityLevel::SecurityLevel,
+};
use android_hardware_security_keymint::binder::StatusCode;
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
use anyhow::{Context, Result};
@@ -77,11 +80,43 @@
RefCell::new(create_thread_local_db());
}
+#[derive(Default)]
+struct DevicesMap {
+ devices_by_uuid: HashMap<Uuid, (Asp, KeyMintHardwareInfo)>,
+ uuid_by_sec_level: HashMap<SecurityLevel, Uuid>,
+}
+
+impl DevicesMap {
+ fn dev_by_sec_level(
+ &self,
+ sec_level: &SecurityLevel,
+ ) -> Option<(Asp, KeyMintHardwareInfo, Uuid)> {
+ self.uuid_by_sec_level.get(sec_level).and_then(|uuid| self.dev_by_uuid(uuid))
+ }
+
+ fn dev_by_uuid(&self, uuid: &Uuid) -> Option<(Asp, KeyMintHardwareInfo, Uuid)> {
+ self.devices_by_uuid
+ .get(uuid)
+ .map(|(dev, hw_info)| ((*dev).clone(), (*hw_info).clone(), *uuid))
+ }
+
+ /// The requested security level and the security level of the actual implementation may
+ /// differ. So we map the requested security level to the uuid of the implementation
+ /// so that there cannot be any confusion as to which KeyMint instance is requested.
+ fn insert(&mut self, sec_level: SecurityLevel, dev: Asp, hw_info: KeyMintHardwareInfo) {
+ // For now we use the reported security level of the KM instance as UUID.
+ // TODO update this section once UUID was added to the KM hardware info.
+ let uuid: Uuid = sec_level.into();
+ self.devices_by_uuid.insert(uuid, (dev, hw_info));
+ self.uuid_by_sec_level.insert(sec_level, uuid);
+ }
+}
+
lazy_static! {
/// Runtime database of unwrapped super keys.
pub static ref SUPER_KEY: SuperKeyManager = Default::default();
/// Map of KeyMint devices.
- static ref KEY_MINT_DEVICES: Mutex<HashMap<SecurityLevel, Asp>> = Default::default();
+ static ref KEY_MINT_DEVICES: Mutex<DevicesMap> = Default::default();
/// Timestamp service.
static ref TIME_STAMP_DEVICE: Mutex<Option<Asp>> = Default::default();
/// A single on-demand worker thread that handles deferred tasks with two different
@@ -100,8 +135,8 @@
/// Make a new connection to a KeyMint device of the given security level.
/// If no native KeyMint device can be found this function also brings
/// up the compatibility service and attempts to connect to the legacy wrapper.
-fn connect_keymint(security_level: SecurityLevel) -> Result<Asp> {
- let service_name = match security_level {
+fn connect_keymint(security_level: &SecurityLevel) -> Result<(Asp, KeyMintHardwareInfo)> {
+ let service_name = match *security_level {
SecurityLevel::TRUSTED_ENVIRONMENT => format!("{}/default", KEYMINT_SERVICE_NAME),
SecurityLevel::STRONGBOX => format!("{}/strongbox", KEYMINT_SERVICE_NAME),
_ => {
@@ -121,32 +156,52 @@
let keystore_compat_service: Box<dyn IKeystoreCompatService> =
map_binder_status_code(binder::get_interface("android.security.compat"))
.context("In connect_keymint: Trying to connect to compat service.")?;
- map_binder_status(keystore_compat_service.getKeyMintDevice(security_level))
+ map_binder_status(keystore_compat_service.getKeyMintDevice(*security_level))
.map_err(|e| match e {
Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
}
e => e,
})
- .context("In connext_keymint: Trying to get Legacy wrapper.")
+ .context("In connect_keymint: Trying to get Legacy wrapper.")
}
_ => Err(e),
}
})?;
- Ok(Asp::new(keymint.as_binder()))
+ let hw_info = map_km_error(keymint.getHardwareInfo())
+ .context("In connect_keymint: Failed to get hardware info.")?;
+
+ Ok((Asp::new(keymint.as_binder()), hw_info))
}
/// Get a keymint device for the given security level either from our cache or
-/// by making a new connection.
-pub fn get_keymint_device(security_level: SecurityLevel) -> Result<Asp> {
+/// by making a new connection. Returns the device, the hardware info and the uuid.
+/// TODO the latter can be removed when the uuid is part of the hardware info.
+pub fn get_keymint_device(
+ security_level: &SecurityLevel,
+) -> Result<(Asp, KeyMintHardwareInfo, Uuid)> {
let mut devices_map = KEY_MINT_DEVICES.lock().unwrap();
- if let Some(dev) = devices_map.get(&security_level) {
- Ok(dev.clone())
+ if let Some((dev, hw_info, uuid)) = devices_map.dev_by_sec_level(&security_level) {
+ Ok((dev, hw_info, uuid))
} else {
- let dev = connect_keymint(security_level).context("In get_keymint_device.")?;
- devices_map.insert(security_level, dev.clone());
- Ok(dev)
+ let (dev, hw_info) = connect_keymint(security_level).context("In get_keymint_device.")?;
+ devices_map.insert(*security_level, dev, hw_info);
+ // Unwrap must succeed because we just inserted it.
+ Ok(devices_map.dev_by_sec_level(security_level).unwrap())
+ }
+}
+
+/// Get a keymint device for the given uuid. This will only access the cache, but will not
+/// attempt to establish a new connection. It is assumed that the cache is already populated
+/// when this is called. This is a fair assumption, because service.rs iterates through all
+/// security levels when it gets instantiated.
+pub fn get_keymint_dev_by_uuid(uuid: &Uuid) -> Result<(Asp, KeyMintHardwareInfo)> {
+ let devices_map = KEY_MINT_DEVICES.lock().unwrap();
+ if let Some((dev, hw_info, _)) = devices_map.dev_by_uuid(uuid) {
+ Ok((dev, hw_info))
+ } else {
+ Err(Error::sys()).context("In get_keymint_dev_by_uuid: No KeyMint instance found.")
}
}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 994b2c8..f6d8108 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -16,12 +16,12 @@
//! This crate implements the IKeystoreSecurityLevel interface.
-use crate::gc::Gc;
+use crate::{database::Uuid, gc::Gc, globals::get_keymint_device};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, HardwareAuthenticatorType::HardwareAuthenticatorType,
IKeyMintDevice::IKeyMintDevice, KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat,
- KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel,
- Tag::Tag,
+ KeyMintHardwareInfo::KeyMintHardwareInfo, KeyParameter::KeyParameter,
+ KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel, Tag::Tag,
};
use android_system_keystore2::aidl::android::system::keystore2::{
AuthenticatorSpec::AuthenticatorSpec, CreateOperationResponse::CreateOperationResponse,
@@ -35,7 +35,10 @@
use crate::key_parameter::KeyParameter as KsKeyParam;
use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
use crate::utils::{check_key_permission, uid_to_android_user, Asp};
-use crate::{database::KeyIdGuard, globals::DB};
+use crate::{
+ database::{CertificateInfo, KeyIdGuard},
+ globals::DB,
+};
use crate::{
database::{DateTime, KeyMetaData, KeyMetaEntry, KeyType},
permission::KeyPerm,
@@ -56,6 +59,9 @@
pub struct KeystoreSecurityLevel {
security_level: SecurityLevel,
keymint: Asp,
+ #[allow(dead_code)]
+ hw_info: KeyMintHardwareInfo,
+ km_uuid: Uuid,
operation_db: OperationDb,
}
@@ -69,15 +75,18 @@
/// we need it for checking keystore permissions.
pub fn new_native_binder(
security_level: SecurityLevel,
- ) -> Result<impl IKeystoreSecurityLevel + Send> {
+ ) -> Result<(impl IKeystoreSecurityLevel + Send, Uuid)> {
+ let (dev, hw_info, km_uuid) = get_keymint_device(&security_level)
+ .context("In KeystoreSecurityLevel::new_native_binder.")?;
let result = BnKeystoreSecurityLevel::new_binder(Self {
security_level,
- keymint: crate::globals::get_keymint_device(security_level)
- .context("In KeystoreSecurityLevel::new_native_binder.")?,
+ keymint: dev,
+ hw_info,
+ km_uuid,
operation_db: OperationDb::new(),
});
result.as_binder().set_requesting_sid(true);
- Ok(result)
+ Ok((result, km_uuid))
}
fn store_new_key(
@@ -92,7 +101,7 @@
certificateChain: mut certificate_chain,
} = creation_result;
- let (cert, cert_chain): (Option<Vec<u8>>, Option<Vec<u8>>) = (
+ let mut cert_info: CertificateInfo = CertificateInfo::new(
match certificate_chain.len() {
0 => None,
_ => Some(certificate_chain.remove(0).encodedCertificate),
@@ -134,9 +143,9 @@
key,
&key_parameters,
&key_blob,
- cert.as_deref(),
- cert_chain.as_deref(),
+ &cert_info,
&metadata,
+ &self.km_uuid,
)
.context("In store_new_key.")?;
if need_gc {
@@ -154,8 +163,8 @@
Ok(KeyMetadata {
key,
keySecurityLevel: self.security_level,
- certificate: cert,
- certificateChain: cert_chain,
+ certificate: cert_info.take_cert(),
+ certificateChain: cert_info.take_cert_chain(),
authorizations: crate::utils::key_parameters_to_authorizations(key_parameters),
modificationTimeMs: creation_date.to_millis_epoch(),
})
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index e57a9b7..72671c6 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -18,14 +18,16 @@
//! This crate implement the core Keystore 2.0 service API as defined by the Keystore 2.0
//! AIDL spec.
-use crate::globals::DB;
-use crate::permission;
+use std::collections::HashMap;
+
use crate::permission::{KeyPerm, KeystorePerm};
use crate::security_level::KeystoreSecurityLevel;
use crate::utils::{
check_grant_permission, check_key_permission, check_keystore_permission,
key_parameters_to_authorizations, Asp,
};
+use crate::{database::Uuid, globals::DB};
+use crate::{database::KEYSTORE_UUID, permission};
use crate::{
database::{KeyEntryLoadBits, KeyType, SubComponentType},
error::ResponseCode,
@@ -40,62 +42,76 @@
IKeystoreService::BnKeystoreService, IKeystoreService::IKeystoreService,
KeyDescriptor::KeyDescriptor, KeyEntryResponse::KeyEntryResponse, KeyMetadata::KeyMetadata,
};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{Context, Result};
use binder::{IBinder, Interface, ThreadState};
use error::Error;
use keystore2_selinux as selinux;
/// Implementation of the IKeystoreService.
+#[derive(Default)]
pub struct KeystoreService {
- sec_level_tee: Asp,
- sec_level_strongbox: Option<Asp>,
+ i_sec_level_by_uuid: HashMap<Uuid, Asp>,
+ uuid_by_sec_level: HashMap<SecurityLevel, Uuid>,
}
impl KeystoreService {
/// Create a new instance of the Keystore 2.0 service.
pub fn new_native_binder() -> Result<impl IKeystoreService> {
- let tee = KeystoreSecurityLevel::new_native_binder(SecurityLevel::TRUSTED_ENVIRONMENT)
- .map(|tee| Asp::new(tee.as_binder()))
- .context(concat!(
- "In KeystoreService::new_native_binder: ",
- "Trying to construct mendatory security level TEE."
- ))?;
- // Strongbox is optional, so we ignore errors and turn the result into an Option.
- let strongbox =
+ let mut result: Self = Default::default();
+ let (dev, uuid) =
KeystoreSecurityLevel::new_native_binder(SecurityLevel::TRUSTED_ENVIRONMENT)
- .map(|tee| Asp::new(tee.as_binder()))
- .ok();
+ .context(concat!(
+ "In KeystoreService::new_native_binder: ",
+ "Trying to construct mandatory security level TEE."
+ ))
+ .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))?;
+ result.i_sec_level_by_uuid.insert(uuid, dev);
+ result.uuid_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, uuid);
- let result = BnKeystoreService::new_binder(Self {
- sec_level_tee: tee,
- sec_level_strongbox: strongbox,
- });
+ // Strongbox is optional, so we ignore errors and turn the result into an Option.
+ if let Ok((dev, uuid)) = KeystoreSecurityLevel::new_native_binder(SecurityLevel::STRONGBOX)
+ .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))
+ {
+ result.i_sec_level_by_uuid.insert(uuid, dev);
+ result.uuid_by_sec_level.insert(SecurityLevel::STRONGBOX, uuid);
+ }
+
+ let result = BnKeystoreService::new_binder(result);
result.as_binder().set_requesting_sid(true);
Ok(result)
}
- fn get_security_level_internal(
- &self,
- security_level: SecurityLevel,
- ) -> Result<Option<Box<dyn IKeystoreSecurityLevel>>> {
- Ok(match (security_level, &self.sec_level_strongbox) {
- (SecurityLevel::TRUSTED_ENVIRONMENT, _) => Some(self.sec_level_tee.get_interface().context(
- "In get_security_level_internal: Failed to get IKeystoreSecurityLevel (TEE).",
- )?),
- (SecurityLevel::STRONGBOX, Some(strongbox)) => Some(strongbox.get_interface().context(
- "In get_security_level_internal: Failed to get IKeystoreSecurityLevel (Strongbox).",
- )?),
- _ => None,
- })
+ fn uuid_to_sec_level(&self, uuid: &Uuid) -> SecurityLevel {
+ self.uuid_by_sec_level
+ .iter()
+ .find(|(_, v)| **v == *uuid)
+ .map(|(s, _)| *s)
+ .unwrap_or(SecurityLevel::SOFTWARE)
+ }
+
+ fn get_i_sec_level_by_uuid(&self, uuid: &Uuid) -> Result<Box<dyn IKeystoreSecurityLevel>> {
+ if let Some(dev) = self.i_sec_level_by_uuid.get(uuid) {
+ dev.get_interface().context("In get_i_sec_level_by_uuid.")
+ } else {
+ Err(error::Error::sys())
+ .context("In get_i_sec_level_by_uuid: KeyMint instance for key not found.")
+ }
}
fn get_security_level(
&self,
- security_level: SecurityLevel,
+ sec_level: SecurityLevel,
) -> Result<Box<dyn IKeystoreSecurityLevel>> {
- self.get_security_level_internal(security_level)
- .context("In get_security_level.")?
- .ok_or_else(|| anyhow!(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)))
+ if let Some(dev) = self
+ .uuid_by_sec_level
+ .get(&sec_level)
+ .and_then(|uuid| self.i_sec_level_by_uuid.get(uuid))
+ {
+ dev.get_interface().context("In get_security_level.")
+ } else {
+ Err(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+ .context("In get_security_level: No such security level.")
+ }
}
fn get_key_entry(&self, key: &KeyDescriptor) -> Result<KeyEntryResponse> {
@@ -113,17 +129,8 @@
let i_sec_level = if !key_entry.pure_cert() {
Some(
- self.get_security_level_internal(key_entry.sec_level())
- .context("In get_key_entry.")?
- .ok_or_else(|| {
- anyhow!(error::Error::sys()).context(format!(
- concat!(
- "Found key with security level {:?} ",
- "but no KeyMint instance of that security level."
- ),
- key_entry.sec_level()
- ))
- })?,
+ self.get_i_sec_level_by_uuid(key_entry.km_uuid())
+ .context("In get_key_entry: Trying to get security level proxy.")?,
)
} else {
None
@@ -137,7 +144,7 @@
nspace: key_id_guard.id(),
..Default::default()
},
- keySecurityLevel: key_entry.sec_level(),
+ keySecurityLevel: self.uuid_to_sec_level(key_entry.km_uuid()),
certificate: key_entry.take_cert(),
certificateChain: key_entry.take_cert_chain(),
modificationTimeMs: key_entry
@@ -212,7 +219,7 @@
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())
+ db.store_new_certificate(key, certificate_chain.unwrap(), &KEYSTORE_UUID)
.context("Failed to insert new certificate.")?;
Ok(())
})
@@ -315,7 +322,7 @@
&self,
security_level: SecurityLevel,
) -> binder::public_api::Result<Box<dyn IKeystoreSecurityLevel>> {
- map_or_log_err(self.get_security_level(SecurityLevel(security_level.0)), Ok)
+ map_or_log_err(self.get_security_level(security_level), Ok)
}
fn getKeyEntry(&self, key: &KeyDescriptor) -> binder::public_api::Result<KeyEntryResponse> {
map_or_log_err(self.get_key_entry(key), Ok)
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 4ffe897..9872513 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -115,36 +115,42 @@
legacy_blob_loader: &LegacyBlobLoader,
) -> Result<()> {
let (_, entry) = db
- .get_or_create_key_with(Domain::APP, user as u64 as i64, &"USER_SUPER_KEY", || {
- // For backward compatibility we need to check if there is a super key present.
- let super_key = legacy_blob_loader
- .load_super_key(user, pw)
- .context("In create_new_key: Failed to load legacy key blob.")?;
- let super_key = match super_key {
- None => {
- // No legacy file was found. So we generate a new key.
- keystore2_crypto::generate_aes256_key()
- .context("In create_new_key: Failed to generate AES 256 key.")?
- }
- Some(key) => key,
- };
- // Regardless of whether we loaded an old AES128 key or a new AES256 key,
- // we derive a AES256 key and re-encrypt the key before we insert it in the
- // database. The length of the key is preserved by the encryption so we don't
- // need any extra flags to inform us which algorithm to use it with.
- let salt =
- generate_salt().context("In create_new_key: Failed to generate salt.")?;
- let derived_key = derive_key_from_password(pw, Some(&salt), AES_256_KEY_LENGTH)
- .context("In create_new_key: Failed to derive password.")?;
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password));
- metadata.add(KeyMetaEntry::Salt(salt));
- let (encrypted_key, iv, tag) = aes_gcm_encrypt(&super_key, &derived_key)
- .context("In create_new_key: Failed to encrypt new super key.")?;
- metadata.add(KeyMetaEntry::Iv(iv));
- metadata.add(KeyMetaEntry::AeadTag(tag));
- Ok((encrypted_key, metadata))
- })
+ .get_or_create_key_with(
+ Domain::APP,
+ user as u64 as i64,
+ &"USER_SUPER_KEY",
+ crate::database::KEYSTORE_UUID,
+ || {
+ // For backward compatibility we need to check if there is a super key present.
+ let super_key = legacy_blob_loader
+ .load_super_key(user, pw)
+ .context("In create_new_key: Failed to load legacy key blob.")?;
+ let super_key = match super_key {
+ None => {
+ // No legacy file was found. So we generate a new key.
+ keystore2_crypto::generate_aes256_key()
+ .context("In create_new_key: Failed to generate AES 256 key.")?
+ }
+ Some(key) => key,
+ };
+ // Regardless of whether we loaded an old AES128 key or a new AES256 key,
+ // we derive a AES256 key and re-encrypt the key before we insert it in the
+ // database. The length of the key is preserved by the encryption so we don't
+ // need any extra flags to inform us which algorithm to use it with.
+ let salt =
+ generate_salt().context("In create_new_key: Failed to generate salt.")?;
+ let derived_key = derive_key_from_password(pw, Some(&salt), AES_256_KEY_LENGTH)
+ .context("In create_new_key: Failed to derive password.")?;
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password));
+ metadata.add(KeyMetaEntry::Salt(salt));
+ let (encrypted_key, iv, tag) = aes_gcm_encrypt(&super_key, &derived_key)
+ .context("In create_new_key: Failed to encrypt new super key.")?;
+ metadata.add(KeyMetaEntry::Iv(iv));
+ metadata.add(KeyMetaEntry::AeadTag(tag));
+ Ok((encrypted_key, metadata))
+ },
+ )
.context("In unlock_user_key: Failed to get key id.")?;
let metadata = entry.metadata();
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 870b7fc..bada2c9 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -19,7 +19,7 @@
use crate::permission;
use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- KeyCharacteristics::KeyCharacteristics, SecurityLevel::SecurityLevel, Tag::Tag,
+ KeyCharacteristics::KeyCharacteristics,
};
use android_security_apc::aidl::android::security::apc::{
IProtectedConfirmation::{FLAG_UI_OPTION_INVERTED, FLAG_UI_OPTION_MAGNIFIED},
@@ -131,10 +131,6 @@
.flat_map(|aidl_key_char| {
let sec_level = aidl_key_char.securityLevel;
aidl_key_char.authorizations.into_iter().map(move |aidl_kp| {
- let sec_level = match (aidl_kp.tag, sec_level) {
- (Tag::ORIGIN, SecurityLevel::SOFTWARE) => SecurityLevel::TRUSTED_ENVIRONMENT,
- _ => sec_level,
- };
crate::key_parameter::KeyParameter::new(aidl_kp.into(), sec_level)
})
})