// Copyright 2022, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Helper wrapper around RKPD interface. use android_security_rkp_aidl::aidl::android::security::rkp::{ IGetKeyCallback::BnGetKeyCallback, IGetKeyCallback::ErrorCode::ErrorCode as GetKeyErrorCode, IGetKeyCallback::IGetKeyCallback, IGetRegistrationCallback::BnGetRegistrationCallback, IGetRegistrationCallback::IGetRegistrationCallback, IRegistration::IRegistration, IRemoteProvisioning::IRemoteProvisioning, IStoreUpgradedKeyCallback::BnStoreUpgradedKeyCallback, IStoreUpgradedKeyCallback::IStoreUpgradedKeyCallback, RemotelyProvisionedKey::RemotelyProvisionedKey, }; use anyhow::{Context, Result}; use binder::{BinderFeatures, Interface, StatusCode, Strong}; use message_macro::source_location_msg; use std::sync::Mutex; use std::time::Duration; use tokio::sync::oneshot; use tokio::time::timeout; // Normally, we block indefinitely when making calls outside of keystore and rely on watchdog to // report deadlocks. However, RKPD is mainline updatable. Also, calls to RKPD may wait on network // for certificates. So, we err on the side of caution and timeout instead. static RKPD_TIMEOUT: Duration = Duration::from_secs(10); fn tokio_rt() -> tokio::runtime::Runtime { tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap() } /// Errors occurred during the interaction with RKPD. #[derive(Debug, Clone, Copy, thiserror::Error, PartialEq, Eq)] pub enum Error { /// An RKPD request gets cancelled. #[error("An RKPD request gets cancelled")] RequestCancelled, /// Failed to get registration. #[error("Failed to get registration")] GetRegistrationFailed, /// Failed to get key. #[error("Failed to get key: {0:?}")] GetKeyFailed(GetKeyErrorCode), /// Failed to store upgraded key. #[error("Failed to store upgraded key")] StoreUpgradedKeyFailed, /// Retryable timeout when waiting for a callback. #[error("Retryable timeout when waiting for a callback")] RetryableTimeout, /// Timeout when waiting for a callback. #[error("Timeout when waiting for a callback")] Timeout, /// Wraps a Binder status code. #[error("Binder transaction error {0:?}")] BinderTransaction(StatusCode), } impl From for Error { fn from(s: StatusCode) -> Self { Self::BinderTransaction(s) } } /// Thread-safe channel for sending a value once and only once. If a value has /// already been send, subsequent calls to send will noop. struct SafeSender { inner: Mutex>>, } impl SafeSender { fn new(sender: oneshot::Sender) -> Self { Self { inner: Mutex::new(Some(sender)) } } fn send(&self, value: T) { if let Some(inner) = self.inner.lock().unwrap().take() { // It's possible for the corresponding receiver to time out and be dropped. In this // case send() will fail. This error is not actionable though, so only log the error. if inner.send(value).is_err() { log::error!("SafeSender::send() failed"); } } } } struct GetRegistrationCallback { registration_tx: SafeSender>>, } impl GetRegistrationCallback { pub fn new_native_binder( registration_tx: oneshot::Sender>>, ) -> Strong { let result: Self = GetRegistrationCallback { registration_tx: SafeSender::new(registration_tx) }; BnGetRegistrationCallback::new_binder(result, BinderFeatures::default()) } } impl Interface for GetRegistrationCallback {} impl IGetRegistrationCallback for GetRegistrationCallback { fn onSuccess(&self, registration: &Strong) -> binder::Result<()> { self.registration_tx.send(Ok(registration.clone())); Ok(()) } fn onCancel(&self) -> binder::Result<()> { log::warn!("IGetRegistrationCallback cancelled"); self.registration_tx.send( Err(Error::RequestCancelled) .context(source_location_msg!("GetRegistrationCallback cancelled.")), ); Ok(()) } fn onError(&self, description: &str) -> binder::Result<()> { log::error!("IGetRegistrationCallback failed: '{description}'"); self.registration_tx.send( Err(Error::GetRegistrationFailed) .context(source_location_msg!("GetRegistrationCallback failed: {:?}", description)), ); Ok(()) } } /// Make a new connection to a IRegistration service. async fn get_rkpd_registration(rpc_name: &str) -> Result> { let remote_provisioning: Strong = binder::get_interface("remote_provisioning") .map_err(Error::from) .context(source_location_msg!("Trying to connect to IRemoteProvisioning service."))?; let (tx, rx) = oneshot::channel(); let cb = GetRegistrationCallback::new_native_binder(tx); remote_provisioning .getRegistration(rpc_name, &cb) .context(source_location_msg!("Trying to get registration."))?; match timeout(RKPD_TIMEOUT, rx).await { Err(e) => Err(Error::Timeout).context(source_location_msg!("Waiting for RKPD: {:?}", e)), Ok(v) => v.unwrap(), } } struct GetKeyCallback { key_tx: SafeSender>, } impl GetKeyCallback { pub fn new_native_binder( key_tx: oneshot::Sender>, ) -> Strong { let result: Self = GetKeyCallback { key_tx: SafeSender::new(key_tx) }; BnGetKeyCallback::new_binder(result, BinderFeatures::default()) } } impl Interface for GetKeyCallback {} impl IGetKeyCallback for GetKeyCallback { fn onSuccess(&self, key: &RemotelyProvisionedKey) -> binder::Result<()> { self.key_tx.send(Ok(RemotelyProvisionedKey { keyBlob: key.keyBlob.clone(), encodedCertChain: key.encodedCertChain.clone(), })); Ok(()) } fn onCancel(&self) -> binder::Result<()> { log::warn!("IGetKeyCallback cancelled"); self.key_tx.send( Err(Error::RequestCancelled).context(source_location_msg!("GetKeyCallback cancelled.")), ); Ok(()) } fn onError(&self, error: GetKeyErrorCode, description: &str) -> binder::Result<()> { log::error!("IGetKeyCallback failed: {description}"); self.key_tx.send(Err(Error::GetKeyFailed(error)).context(source_location_msg!( "GetKeyCallback failed: {:?} {:?}", error, description ))); Ok(()) } } async fn get_rkpd_attestation_key_from_registration_async( registration: &Strong, caller_uid: u32, ) -> Result { let (tx, rx) = oneshot::channel(); let cb = GetKeyCallback::new_native_binder(tx); registration .getKey(caller_uid.try_into().unwrap(), &cb) .context(source_location_msg!("Trying to get key."))?; match timeout(RKPD_TIMEOUT, rx).await { Err(e) => { // Make a best effort attempt to cancel the timed out request. if let Err(e) = registration.cancelGetKey(&cb) { log::error!("IRegistration::cancelGetKey failed: {:?}", e); } Err(Error::RetryableTimeout) .context(source_location_msg!("Waiting for RKPD key timed out: {:?}", e)) } Ok(v) => v.unwrap(), } } async fn get_rkpd_attestation_key_async( rpc_name: &str, caller_uid: u32, ) -> Result { let registration = get_rkpd_registration(rpc_name) .await .context(source_location_msg!("Trying to get to IRegistration service."))?; get_rkpd_attestation_key_from_registration_async(®istration, caller_uid).await } struct StoreUpgradedKeyCallback { completer: SafeSender>, } impl StoreUpgradedKeyCallback { pub fn new_native_binder( completer: oneshot::Sender>, ) -> Strong { let result: Self = StoreUpgradedKeyCallback { completer: SafeSender::new(completer) }; BnStoreUpgradedKeyCallback::new_binder(result, BinderFeatures::default()) } } impl Interface for StoreUpgradedKeyCallback {} impl IStoreUpgradedKeyCallback for StoreUpgradedKeyCallback { fn onSuccess(&self) -> binder::Result<()> { self.completer.send(Ok(())); Ok(()) } fn onError(&self, error: &str) -> binder::Result<()> { log::error!("IStoreUpgradedKeyCallback failed: {error}"); self.completer.send( Err(Error::StoreUpgradedKeyFailed) .context(source_location_msg!("Failed to store upgraded key: {:?}", error)), ); Ok(()) } } async fn store_rkpd_attestation_key_with_registration_async( registration: &Strong, key_blob: &[u8], upgraded_blob: &[u8], ) -> Result<()> { let (tx, rx) = oneshot::channel(); let cb = StoreUpgradedKeyCallback::new_native_binder(tx); registration .storeUpgradedKeyAsync(key_blob, upgraded_blob, &cb) .context(source_location_msg!("Failed to store upgraded blob with RKPD."))?; match timeout(RKPD_TIMEOUT, rx).await { Err(e) => Err(Error::Timeout) .context(source_location_msg!("Waiting for RKPD to complete storing key: {:?}", e)), Ok(v) => v.unwrap(), } } async fn store_rkpd_attestation_key_async( rpc_name: &str, key_blob: &[u8], upgraded_blob: &[u8], ) -> Result<()> { let registration = get_rkpd_registration(rpc_name) .await .context(source_location_msg!("Trying to get to IRegistration service."))?; store_rkpd_attestation_key_with_registration_async(®istration, key_blob, upgraded_blob).await } /// Get attestation key from RKPD. pub fn get_rkpd_attestation_key(rpc_name: &str, caller_uid: u32) -> Result { tokio_rt().block_on(get_rkpd_attestation_key_async(rpc_name, caller_uid)) } /// Store attestation key in RKPD. pub fn store_rkpd_attestation_key( rpc_name: &str, key_blob: &[u8], upgraded_blob: &[u8], ) -> Result<()> { tokio_rt().block_on(store_rkpd_attestation_key_async(rpc_name, key_blob, upgraded_blob)) } #[cfg(test)] mod tests;