Merge "Keystore 2.0: Adding uuid field to persistent.keyentry"
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)
})
})