Keystore 2.0: Initialize all KeyMint instances.
Test: Strongbox CTS tests on Devices sporting Strongbox.
Bug: 173546020
Change-Id: Ief1983571c8a16979ed1e0ed7e4f636ab269ce8d
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index b4cf913..7227f62 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -38,7 +38,7 @@
use keystore2_selinux as selinux;
use android_system_keystore2::binder::{
- ExceptionCode, Result as BinderResult, Status as BinderStatus,
+ ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
};
/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
@@ -54,6 +54,9 @@
/// Wraps a Binder exception code other than a service specific exception.
#[error("Binder exception code {0:?}, {1:?}")]
Binder(ExceptionCode, i32),
+ /// Wraps a Binder status code.
+ #[error("Binder transaction error {0:?}")]
+ BinderTransaction(StatusCode),
}
impl Error {
@@ -98,6 +101,28 @@
})
}
+/// This function is similar to map_km_error only that we don't expect
+/// any KeyMint error codes, we simply preserve the exception code and optional
+/// service specific exception.
+pub fn map_binder_status<T>(r: BinderResult<T>) -> Result<T, Error> {
+ r.map_err(|s| match s.exception_code() {
+ ExceptionCode::SERVICE_SPECIFIC => {
+ let se = s.service_specific_error();
+ Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
+ }
+ ExceptionCode::TRANSACTION_FAILED => {
+ let e = s.transaction_error();
+ Error::BinderTransaction(e)
+ }
+ e_code => Error::Binder(e_code, 0),
+ })
+}
+
+/// This function maps a status code onto a Keystore Error.
+pub fn map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error> {
+ r.map_err(Error::BinderTransaction)
+}
+
/// This function should be used by Keystore service calls to translate error conditions
/// into service specific exceptions.
///
@@ -142,7 +167,9 @@
// If an Error::Binder reaches this stage we report a system error.
// The exception code and possible service specific error will be
// printed in the error log above.
- Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
+ Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
+ ResponseCode::SYSTEM_ERROR.0
+ }
None => match root_cause.downcast_ref::<selinux::Error>() {
Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
_ => ResponseCode::SYSTEM_ERROR.0,
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index eff3196..d6c2ba4 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -16,10 +16,20 @@
//! database connections and connections to services that Keystore needs
//! to talk to.
-use crate::database::KeystoreDB;
use crate::super_key::SuperKeyManager;
+use crate::utils::Asp;
+use crate::{
+ database::KeystoreDB,
+ error::{map_binder_status_code, Error, ErrorCode},
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ IKeyMintDevice::IKeyMintDevice, SecurityLevel::SecurityLevel,
+};
+use anyhow::{Context, Result};
use lazy_static::lazy_static;
use std::cell::RefCell;
+use std::collections::HashMap;
+use std::sync::Mutex;
thread_local! {
/// Database connections are not thread safe, but connecting to the
@@ -40,4 +50,42 @@
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 KEYMINT_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
+
+/// Make a new connection to a KeyMint device of the given security level.
+fn connect_keymint(security_level: SecurityLevel) -> Result<Asp> {
+ let service_name = match security_level {
+ SecurityLevel::TRUSTED_ENVIRONMENT => format!("{}/default", KEYMINT_SERVICE_NAME),
+ SecurityLevel::STRONGBOX => format!("{}/strongbox", KEYMINT_SERVICE_NAME),
+ _ => {
+ return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+ .context("In connect_keymint.")
+ }
+ };
+
+ let keymint: Box<dyn IKeyMintDevice> =
+ map_binder_status_code(binder::get_interface(&service_name))
+ .context("In connect_keymint: Trying to connect to genuine KeyMint service.")?;
+
+ Ok(Asp::new(keymint.as_binder()))
+}
+
+/// 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> {
+ let mut devices_map = KEY_MINT_DEVICES.lock().unwrap();
+ if let Some(dev) = devices_map.get(&security_level) {
+ Ok(dev.clone())
+ } else {
+ let dev = connect_keymint(security_level).map_err(|e| {
+ anyhow::anyhow!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+ .context(format!("In get_keymint_device: {:?}", e))
+ })?;
+ devices_map.insert(security_level, dev.clone());
+ Ok(dev)
+ }
}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index a89f309..29bb9b2 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -45,7 +45,7 @@
error::{self, map_km_error, map_or_log_err, Error, ErrorCode},
utils::key_characteristics_to_internal,
};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{Context, Result};
use binder::{IBinder, Interface, ThreadState};
/// Implementation of the IKeystoreSecurityLevel Interface.
@@ -55,8 +55,6 @@
operation_db: OperationDb,
}
-static KEYMINT_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
-
// Blob of 32 zeroes used as empty masking key.
static ZERO_BLOB_32: &[u8] = &[0; 32];
@@ -68,18 +66,10 @@
pub fn new_native_binder(
security_level: SecurityLevel,
) -> Result<impl IKeystoreSecurityLevel + Send> {
- let service_name = format!("{}/default", KEYMINT_SERVICE_NAME);
- let keymint: Box<dyn IKeyMintDevice> =
- binder::get_interface(&service_name).map_err(|e| {
- anyhow!(format!(
- "Could not get KeyMint instance: {} failed with error code {:?}",
- service_name, e
- ))
- })?;
-
let result = BnKeystoreSecurityLevel::new_binder(Self {
security_level,
- keymint: Asp::new(keymint.as_binder()),
+ keymint: crate::globals::get_keymint_device(security_level)
+ .context("In KeystoreSecurityLevel::new_native_binder.")?,
operation_db: OperationDb::new(),
});
result.as_binder().set_requesting_sid(true);
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index d475b7e..d185025 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -44,35 +44,55 @@
/// Implementation of the IKeystoreService.
pub struct KeystoreService {
- sec_level: Asp,
+ sec_level_tee: Asp,
+ sec_level_strongbox: Option<Asp>,
}
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 =
+ KeystoreSecurityLevel::new_native_binder(SecurityLevel::TRUSTED_ENVIRONMENT)
+ .map(|tee| Asp::new(tee.as_binder()))
+ .ok();
+
let result = BnKeystoreService::new_binder(Self {
- sec_level: Asp::new({
- let sec_level =
- KeystoreSecurityLevel::new_native_binder(SecurityLevel::TRUSTED_ENVIRONMENT)
- .context("While trying to create IKeystoreSecurityLevel")?;
- sec_level.as_binder()
- }),
+ sec_level_tee: tee,
+ sec_level_strongbox: strongbox,
});
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 get_security_level(
&self,
security_level: SecurityLevel,
) -> Result<Box<dyn IKeystoreSecurityLevel>> {
- match security_level {
- SecurityLevel::TRUSTED_ENVIRONMENT => self
- .sec_level
- .get_interface()
- .context("In get_security_level: Failed to get IKeystoreSecurityLevel."),
- _ => Err(anyhow!(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))),
- }
+ self.get_security_level_internal(security_level)
+ .context("In get_security_level.")?
+ .ok_or_else(|| anyhow!(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)))
}
fn get_key_entry(&self, key: &KeyDescriptor) -> Result<KeyEntryResponse> {
@@ -88,13 +108,18 @@
})
.context("In get_key_entry, while trying to load key info.")?;
- let i_sec_level = match key_entry.sec_level() {
- SecurityLevel::TRUSTED_ENVIRONMENT => self
- .sec_level
- .get_interface()
- .context("In get_key_entry: Failed to get IKeystoreSecurityLevel.")?,
- _ => return Err(anyhow!(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))),
- };
+ let i_sec_level = 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()
+ ))
+ })?;
Ok(KeyEntryResponse {
iSecurityLevel: Some(i_sec_level),
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index eab9b4d..86a86dd 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -117,6 +117,13 @@
}
}
+impl Clone for Asp {
+ fn clone(&self) -> Self {
+ let lock = self.0.lock().unwrap();
+ Self(Mutex::new((*lock).clone()))
+ }
+}
+
/// Converts a set of key characteristics as returned from KeyMint into the internal
/// representation of the keystore service.
/// The parameter `hw_security_level` indicates which security level shall be used for