Refactor RKP key pool in keystore
Split the IRemotelyProvisionedKeyPool binder implementation to its own
struct, as we cannot have two rust objects backing the same native
binder.
Test: keystore2_test
Test: keystore2_test --ignored
Bug: 194696876
Change-Id: I188bc2e2daf277f4a3543c7ec8320002d57f60ba
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index abab4b6..bea5f08 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -19,7 +19,9 @@
use keystore2::maintenance::Maintenance;
use keystore2::metrics::Metrics;
use keystore2::metrics_store;
-use keystore2::remote_provisioning::RemoteProvisioningService;
+use keystore2::remote_provisioning::{
+ RemoteProvisioningService, RemotelyProvisionedKeyPoolService,
+};
use keystore2::service::KeystoreService;
use keystore2::{apc::ApcManager, shared_secret_negotiation};
use keystore2::{authorization::AuthorizationManager, id_rotation::IdRotationState};
@@ -33,6 +35,8 @@
static AUTHORIZATION_SERVICE_NAME: &str = "android.security.authorization";
static METRICS_SERVICE_NAME: &str = "android.security.metrics";
static REMOTE_PROVISIONING_SERVICE_NAME: &str = "android.security.remoteprovisioning";
+static REMOTELY_PROVISIONED_KEY_POOL_SERVICE_NAME: &str =
+ "android.security.remoteprovisioning.IRemotelyProvisionedKeyPool";
static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
static LEGACY_KEYSTORE_SERVICE_NAME: &str = "android.security.legacykeystore";
@@ -145,6 +149,22 @@
});
}
+ // Even if the IRemotelyProvisionedComponent HAL is implemented, it doesn't mean that the keys
+ // may be fetched via the key pool. The HAL must be a new version that exports a unique id. If
+ // none of the HALs support this, then the key pool service is not published.
+ if let Ok(key_pool_service) = RemotelyProvisionedKeyPoolService::new_native_binder() {
+ binder::add_service(
+ REMOTELY_PROVISIONED_KEY_POOL_SERVICE_NAME,
+ key_pool_service.as_binder(),
+ )
+ .unwrap_or_else(|e| {
+ panic!(
+ "Failed to register service {} because of {:?}.",
+ REMOTELY_PROVISIONED_KEY_POOL_SERVICE_NAME, e
+ );
+ });
+ }
+
binder::add_service(LEGACY_KEYSTORE_SERVICE_NAME, legacykeystore.as_binder()).unwrap_or_else(
|e| {
panic!(
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index fadd252..639fe1e 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -31,6 +31,7 @@
use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
IRemoteProvisioning::IRemoteProvisioning,
+ IRemotelyProvisionedKeyPool::BnRemotelyProvisionedKeyPool,
IRemotelyProvisionedKeyPool::IRemotelyProvisionedKeyPool, ImplInfo::ImplInfo,
RemotelyProvisionedKey::RemotelyProvisionedKey,
};
@@ -183,22 +184,6 @@
}
}
- fn get_dev_by_unique_id(
- &self,
- unique_id: &str,
- ) -> Result<(SecurityLevel, &dyn IRemotelyProvisionedComponent)> {
- for (sec_level, dev) in &self.device_by_sec_level {
- if dev.getHardwareInfo()?.uniqueId == Some(unique_id.to_string()) {
- return Ok((*sec_level, dev.as_ref()));
- }
- }
-
- Err(error::Error::sys()).context(format!(
- "In get_dev_by_unique_id: Instance for requested unique id '{}' not found",
- unique_id
- ))
- }
-
/// Creates a new instance of the remote provisioning service
pub fn new_native_binder() -> Result<Strong<dyn IRemoteProvisioning>> {
let mut result: Self = Default::default();
@@ -421,35 +406,6 @@
db.delete_all_attestation_keys()
})
}
-
- /// Fetches a remotely provisioned certificate chain and key for the given client uid that
- /// was provisioned using the IRemotelyProvisionedComponent with the given id. The same key
- /// will be returned for a given caller_uid on every request. If there are no attestation keys
- /// available, `OUT_OF_KEYS` is returned.
- fn get_attestation_key(
- &self,
- db: &mut KeystoreDB,
- caller_uid: i32,
- irpc_id: &str,
- ) -> Result<RemotelyProvisionedKey> {
- log::info!("get_attestation_key(self, {}, {}", caller_uid, irpc_id);
-
- let (sec_level, _) = self.get_dev_by_unique_id(irpc_id)?;
- let (_, _, km_uuid) = get_keymint_device(&sec_level)?;
-
- let cert_chain = get_rem_prov_attest_key(Domain::APP, caller_uid as u32, db, &km_uuid)
- .context("In get_attestation_key")?;
- match cert_chain {
- Some(chain) => Ok(RemotelyProvisionedKey {
- keyBlob: chain.private_key.to_vec(),
- encodedCertChain: chain.cert_chain,
- }),
- // It should be impossible to get `None`, but handle it just in case as a
- // precaution against future behavioral changes in `get_rem_prov_attest_key`.
- None => Err(error::Error::Rc(ResponseCode::OUT_OF_KEYS))
- .context("In get_attestation_key: No available attestation keys"),
- }
- }
}
/// Populates the AttestationPoolStatus parcelable with information about how many
@@ -616,9 +572,86 @@
}
}
+/// Implementation of the IRemotelyProvisionedKeyPool service.
+#[derive(Default)]
+pub struct RemotelyProvisionedKeyPoolService {
+ unique_id_to_sec_level: HashMap<String, SecurityLevel>,
+}
+
+impl RemotelyProvisionedKeyPoolService {
+ /// Fetches a remotely provisioned certificate chain and key for the given client uid that
+ /// was provisioned using the IRemotelyProvisionedComponent with the given id. The same key
+ /// will be returned for a given caller_uid on every request. If there are no attestation keys
+ /// available, `OUT_OF_KEYS` is returned.
+ fn get_attestation_key(
+ &self,
+ db: &mut KeystoreDB,
+ caller_uid: i32,
+ irpc_id: &str,
+ ) -> Result<RemotelyProvisionedKey> {
+ log::info!("get_attestation_key(self, {}, {}", caller_uid, irpc_id);
+
+ let sec_level = self
+ .unique_id_to_sec_level
+ .get(irpc_id)
+ .ok_or(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+ .context(format!("In get_attestation_key: unknown irpc id '{}'", irpc_id))?;
+ let (_, _, km_uuid) = get_keymint_device(sec_level)?;
+
+ let cert_chain = get_rem_prov_attest_key(Domain::APP, caller_uid as u32, db, &km_uuid)
+ .context("In get_attestation_key")?;
+ match cert_chain {
+ Some(chain) => Ok(RemotelyProvisionedKey {
+ keyBlob: chain.private_key.to_vec(),
+ encodedCertChain: chain.cert_chain,
+ }),
+ // It should be impossible to get `None`, but handle it just in case as a
+ // precaution against future behavioral changes in `get_rem_prov_attest_key`.
+ None => Err(error::Error::Rc(ResponseCode::OUT_OF_KEYS))
+ .context("In get_attestation_key: No available attestation keys"),
+ }
+ }
+
+ /// Creates a new instance of the remotely provisioned key pool service, used for fetching
+ /// remotely provisioned attestation keys.
+ pub fn new_native_binder() -> Result<Strong<dyn IRemotelyProvisionedKeyPool>> {
+ let mut result: Self = Default::default();
+
+ let dev = get_remotely_provisioned_component(&SecurityLevel::TRUSTED_ENVIRONMENT)
+ .context("In new_native_binder: Failed to get TEE Remote Provisioner instance.")?;
+ if let Some(id) = dev.getHardwareInfo()?.uniqueId {
+ result.unique_id_to_sec_level.insert(id, SecurityLevel::TRUSTED_ENVIRONMENT);
+ }
+
+ if let Ok(dev) = get_remotely_provisioned_component(&SecurityLevel::STRONGBOX) {
+ if let Some(id) = dev.getHardwareInfo()?.uniqueId {
+ if result.unique_id_to_sec_level.contains_key(&id) {
+ anyhow::bail!("In new_native_binder: duplicate irpc id found: '{}'", id)
+ }
+ result.unique_id_to_sec_level.insert(id, SecurityLevel::STRONGBOX);
+ }
+ }
+
+ // If none of the remotely provisioned components have unique ids, then we shouldn't
+ // bother publishing the service, as it's impossible to match keys with their backends.
+ if result.unique_id_to_sec_level.is_empty() {
+ anyhow::bail!(
+ "In new_native_binder: No remotely provisioned components have unique ids"
+ )
+ }
+
+ Ok(BnRemotelyProvisionedKeyPool::new_binder(
+ result,
+ BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+ ))
+ }
+}
+
+impl binder::Interface for RemotelyProvisionedKeyPoolService {}
+
// Implementation of IRemotelyProvisionedKeyPool. See AIDL spec at
// :aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl
-impl IRemotelyProvisionedKeyPool for RemoteProvisioningService {
+impl IRemotelyProvisionedKeyPool for RemotelyProvisionedKeyPoolService {
fn getAttestationKey(
&self,
caller_uid: i32,
@@ -842,10 +875,10 @@
let mock_rpc = Box::<MockRemotelyProvisionedComponent>::default();
mock_rpc.0.lock().unwrap().hw_info.uniqueId = Some(String::from("mallory"));
- let mut service: RemoteProvisioningService = Default::default();
+ let mut service: RemotelyProvisionedKeyPoolService = Default::default();
service
- .device_by_sec_level
- .insert(SecurityLevel::TRUSTED_ENVIRONMENT, Strong::new(mock_rpc));
+ .unique_id_to_sec_level
+ .insert(String::from("mallory"), SecurityLevel::TRUSTED_ENVIRONMENT);
assert_eq!(
service
@@ -867,13 +900,15 @@
let mock_rpc = Box::<MockRemotelyProvisionedComponent>::default();
let mock_values = mock_rpc.0.clone();
- let mut service: RemoteProvisioningService = Default::default();
- service.device_by_sec_level.insert(sec_level, Strong::new(mock_rpc));
+ let mut remote_provisioning: RemoteProvisioningService = Default::default();
+ remote_provisioning.device_by_sec_level.insert(sec_level, Strong::new(mock_rpc));
+ let mut key_pool: RemotelyProvisionedKeyPoolService = Default::default();
+ key_pool.unique_id_to_sec_level.insert(String::from(irpc_id), sec_level);
mock_values.lock().unwrap().hw_info.uniqueId = Some(String::from(irpc_id));
mock_values.lock().unwrap().private_key = vec![8, 6, 7, 5, 3, 0, 9];
mock_values.lock().unwrap().maced_public_key = generate_maced_pubkey(0x11);
- service.generate_key_pair(&mut db, true, sec_level).unwrap();
+ remote_provisioning.generate_key_pair(&mut db, true, sec_level).unwrap();
let public_key = RemoteProvisioningService::parse_cose_mac0_for_coords(
mock_values.lock().unwrap().maced_public_key.as_slice(),
@@ -881,7 +916,7 @@
.unwrap();
let batch_cert = get_fake_cert();
let certs = &[5, 6, 7, 8];
- assert!(service
+ assert!(remote_provisioning
.provision_cert_chain(
&mut db,
public_key.as_slice(),
@@ -893,7 +928,7 @@
.is_ok());
// ensure we got the key we expected
- let first_key = service
+ let first_key = key_pool
.get_attestation_key(&mut db, caller_uid, irpc_id)
.context("get first key")
.unwrap();
@@ -903,7 +938,7 @@
// ensure that multiple calls get the same key
assert_eq!(
first_key,
- service
+ key_pool
.get_attestation_key(&mut db, caller_uid, irpc_id)
.context("get second key")
.unwrap()
@@ -911,7 +946,7 @@
// no more keys for new clients
assert_eq!(
- service
+ key_pool
.get_attestation_key(&mut db, caller_uid + 1, irpc_id)
.unwrap_err()
.downcast::<error::Error>()
@@ -931,19 +966,21 @@
let mock_rpc = Box::<MockRemotelyProvisionedComponent>::default();
let mock_values = mock_rpc.0.clone();
- let mut service: RemoteProvisioningService = Default::default();
- service.device_by_sec_level.insert(sec_level, Strong::new(mock_rpc));
+ let mut remote_provisioning: RemoteProvisioningService = Default::default();
+ remote_provisioning.device_by_sec_level.insert(sec_level, Strong::new(mock_rpc));
+ let mut key_pool: RemotelyProvisionedKeyPoolService = Default::default();
+ key_pool.unique_id_to_sec_level.insert(String::from(irpc_id), sec_level);
// generate two distinct keys and provision them with certs
mock_values.lock().unwrap().hw_info.uniqueId = Some(String::from(irpc_id));
mock_values.lock().unwrap().private_key = vec![3, 1, 4, 1, 5];
mock_values.lock().unwrap().maced_public_key = generate_maced_pubkey(0x11);
- assert!(service.generate_key_pair(&mut db, true, sec_level).is_ok());
+ assert!(remote_provisioning.generate_key_pair(&mut db, true, sec_level).is_ok());
let public_key = RemoteProvisioningService::parse_cose_mac0_for_coords(
mock_values.lock().unwrap().maced_public_key.as_slice(),
)
.unwrap();
- assert!(service
+ assert!(remote_provisioning
.provision_cert_chain(
&mut db,
public_key.as_slice(),
@@ -957,12 +994,12 @@
mock_values.lock().unwrap().hw_info.uniqueId = Some(String::from(irpc_id));
mock_values.lock().unwrap().private_key = vec![9, 0, 2, 1, 0];
mock_values.lock().unwrap().maced_public_key = generate_maced_pubkey(0x22);
- assert!(service.generate_key_pair(&mut db, true, sec_level).is_ok());
+ assert!(remote_provisioning.generate_key_pair(&mut db, true, sec_level).is_ok());
let public_key = RemoteProvisioningService::parse_cose_mac0_for_coords(
mock_values.lock().unwrap().maced_public_key.as_slice(),
)
.unwrap();
- assert!(service
+ assert!(remote_provisioning
.provision_cert_chain(
&mut db,
public_key.as_slice(),
@@ -975,11 +1012,11 @@
// make sure each caller gets a distinct key
assert_ne!(
- service
+ key_pool
.get_attestation_key(&mut db, first_caller, irpc_id)
.context("get first key")
.unwrap(),
- service
+ key_pool
.get_attestation_key(&mut db, second_caller, irpc_id)
.context("get second key")
.unwrap()
@@ -987,22 +1024,22 @@
// repeated calls should return the same key for a given caller
assert_eq!(
- service
+ key_pool
.get_attestation_key(&mut db, first_caller, irpc_id)
.context("first caller a")
.unwrap(),
- service
+ key_pool
.get_attestation_key(&mut db, first_caller, irpc_id)
.context("first caller b")
.unwrap(),
);
assert_eq!(
- service
+ key_pool
.get_attestation_key(&mut db, second_caller, irpc_id)
.context("second caller a")
.unwrap(),
- service
+ key_pool
.get_attestation_key(&mut db, second_caller, irpc_id)
.context("second caller b")
.unwrap()