Merge "Keystore 2.0: Provide confirmation token to operation."
diff --git a/keystore2/apc_compat/apc_compat.rs b/keystore2/apc_compat/apc_compat.rs
index 4391f5b..57f8710 100644
--- a/keystore2/apc_compat/apc_compat.rs
+++ b/keystore2/apc_compat/apc_compat.rs
@@ -141,6 +141,10 @@
/// data_confirmed: Option<&[u8]> and
/// confirmation_token: Option<&[u8]> hold the confirmed message and the confirmation token
/// respectively. They must be `Some()` if `rc == APC_COMPAT_ERROR_OK` and `None` otherwise.
+ ///
+ /// `cb` does not get called if this function returns an error.
+ /// (Thus the allow(unused_must_use))
+ #[allow(unused_must_use)]
pub fn prompt_user_confirmation<F>(
&self,
prompt_text: &str,
@@ -150,9 +154,9 @@
cb: F,
) -> Result<(), u32>
where
- F: FnOnce(u32, Option<&[u8]>, Option<&[u8]>),
+ F: FnOnce(u32, Option<&[u8]>, Option<&[u8]>) + 'static,
{
- let cb_data_ptr = Box::into_raw(Box::new(Box::new(cb)));
+ let cb_data_ptr = Box::into_raw(Box::new(Box::new(cb) as Box<Callback>));
let cb = ApcCompatCallback {
data: cb_data_ptr as *mut std::ffi::c_void,
result: Some(confirmation_result_callback),
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index 105e071..bdfec4e 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -12,16 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// TODO The confirmation token is yet unused.
-#![allow(unused_variables)]
-
//! This module implements the Android Protected Confirmation (APC) service as defined
//! in the android.security.apc AIDL spec.
use std::{
cmp::PartialEq,
collections::HashMap,
- sync::{Arc, Mutex},
+ sync::{mpsc::Sender, Arc, Mutex},
};
use crate::utils::{compat_2_response_code, ui_opts_2_compat};
@@ -182,10 +179,16 @@
client_aborted: bool,
}
-#[derive(Default)]
struct ApcState {
session: Option<ApcSessionState>,
rate_limiting: HashMap<u32, RateInfo>,
+ confirmation_token_sender: Sender<Vec<u8>>,
+}
+
+impl ApcState {
+ fn new(confirmation_token_sender: Sender<Vec<u8>>) -> Self {
+ Self { session: None, rate_limiting: Default::default(), confirmation_token_sender }
+ }
}
/// Implementation of the APC service.
@@ -197,9 +200,11 @@
impl ApcManager {
/// Create a new instance of the Android Protected Confirmation service.
- pub fn new_native_binder() -> Result<impl IProtectedConfirmation> {
+ pub fn new_native_binder(
+ confirmation_token_sender: Sender<Vec<u8>>,
+ ) -> Result<impl IProtectedConfirmation> {
let result = BnProtectedConfirmation::new_binder(Self {
- state: Arc::new(Mutex::new(Default::default())),
+ state: Arc::new(Mutex::new(ApcState::new(confirmation_token_sender))),
});
result.as_binder().set_requesting_sid(true);
Ok(result)
@@ -222,21 +227,28 @@
let rc = compat_2_response_code(rc);
// Update rate limiting information.
- match (rc, client_aborted) {
+ match (rc, client_aborted, confirmation_token) {
// If the user confirmed the dialog.
- (ResponseCode::OK, _) => {
+ (ResponseCode::OK, _, Some(confirmation_token)) => {
// Reset counter.
state.rate_limiting.remove(&uid);
- // TODO at this point we need to send the confirmation token to where keystore can
- // use it.
+ // Send confirmation token to the enforcement module.
+ if let Err(e) = state.confirmation_token_sender.send(confirmation_token.to_vec()) {
+ log::error!("Got confirmation token, but receiver would not have it. {:?}", e);
+ }
}
// If cancelled by the user or if aborted by the client.
- (ResponseCode::CANCELLED, _) | (ResponseCode::ABORTED, true) => {
+ (ResponseCode::CANCELLED, _, _) | (ResponseCode::ABORTED, true, _) => {
// Penalize.
let mut rate_info = state.rate_limiting.entry(uid).or_default();
rate_info.counter += 1;
rate_info.timestamp = start;
}
+ (ResponseCode::OK, _, None) => {
+ log::error!(
+ "Confirmation prompt was successful but no confirmation token was returned."
+ );
+ }
// In any other case this try does not count at all.
_ => {}
}
@@ -299,7 +311,7 @@
extra_data,
locale,
ui_opts,
- |rc, data_confirmed, confirmation_token| {
+ move |rc, data_confirmed, confirmation_token| {
Self::result(state_clone, rc, data_confirmed, confirmation_token)
},
)
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 3195ee0..bee0e4b 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -31,12 +31,15 @@
};
use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
use anyhow::{Context, Result};
-use std::collections::{HashMap, HashSet};
use std::sync::{
mpsc::{channel, Receiver, Sender},
Arc, Mutex, Weak,
};
use std::time::SystemTime;
+use std::{
+ collections::{HashMap, HashSet},
+ sync::mpsc::TryRecvError,
+};
#[derive(Debug)]
enum AuthRequestState {
@@ -133,6 +136,7 @@
state: DeferredAuthState,
/// An optional key id required to update the usage count if the key usage is limited.
key_usage_limited: Option<i64>,
+ confirmation_token_receiver: Option<Arc<Mutex<Option<Receiver<Vec<u8>>>>>>,
}
struct TokenReceiverMap {
@@ -264,8 +268,32 @@
/// This function is the authorization hook called before operation finish.
/// It returns the auth tokens required by the operation to commence finish.
- pub fn before_finish(&mut self) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
- self.get_auth_tokens()
+ /// The third token is a confirmation token.
+ pub fn before_finish(
+ &mut self,
+ ) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>, Option<Vec<u8>>)> {
+ let mut confirmation_token: Option<Vec<u8>> = None;
+ if let Some(ref confirmation_token_receiver) = self.confirmation_token_receiver {
+ let locked_receiver = confirmation_token_receiver.lock().unwrap();
+ if let Some(ref receiver) = *locked_receiver {
+ loop {
+ match receiver.try_recv() {
+ // As long as we get tokens we loop and discard all but the most
+ // recent one.
+ Ok(t) => confirmation_token = Some(t),
+ Err(TryRecvError::Empty) => break,
+ Err(TryRecvError::Disconnected) => {
+ log::error!(concat!(
+ "We got disconnected from the APC service, ",
+ "this should never happen."
+ ));
+ break;
+ }
+ }
+ }
+ }
+ }
+ self.get_auth_tokens().map(|(hat, tst)| (hat, tst, confirmation_token))
}
/// This function is the authorization hook called after finish succeeded.
@@ -337,6 +365,9 @@
/// stale, because the operation gets dropped before an auth token is received, the map
/// is cleaned up in regular intervals.
op_auth_map: TokenReceiverMap,
+ /// The enforcement module will try to get a confirmation token from this channel whenever
+ /// an operation that requires confirmation finishes.
+ confirmation_token_receiver: Arc<Mutex<Option<Receiver<Vec<u8>>>>>,
}
impl Enforcements {
@@ -345,9 +376,20 @@
Enforcements {
device_unlocked_set: Mutex::new(HashSet::new()),
op_auth_map: Default::default(),
+ confirmation_token_receiver: Default::default(),
}
}
+ /// Install the confirmation token receiver. The enforcement module will try to get a
+ /// confirmation token from this channel whenever an operation that requires confirmation
+ /// finishes.
+ pub fn install_confirmation_token_receiver(
+ &self,
+ confirmation_token_receiver: Receiver<Vec<u8>>,
+ ) {
+ *self.confirmation_token_receiver.lock().unwrap() = Some(confirmation_token_receiver);
+ }
+
/// Checks if a create call is authorized, given key parameters and operation parameters.
/// It returns an optional immediate auth token which can be presented to begin, and an
/// AuthInfo object which stays with the authorized operation and is used to obtain
@@ -371,7 +413,11 @@
None => {
return Ok((
None,
- AuthInfo { state: DeferredAuthState::NoAuthRequired, key_usage_limited: None },
+ AuthInfo {
+ state: DeferredAuthState::NoAuthRequired,
+ key_usage_limited: None,
+ confirmation_token_receiver: None,
+ },
))
}
};
@@ -431,6 +477,7 @@
let mut allow_while_on_body = false;
let mut unlocked_device_required = false;
let mut key_usage_limited: Option<i64> = None;
+ let mut confirmation_token_receiver: Option<Arc<Mutex<Option<Receiver<Vec<u8>>>>>> = None;
// iterate through key parameters, recording information we need for authorization
// enforcements later, or enforcing authorizations in place, where applicable
@@ -494,6 +541,9 @@
// in the database again and check and update the counter.
key_usage_limited = Some(key_id);
}
+ KeyParameterValue::TrustedConfirmationRequired => {
+ confirmation_token_receiver = Some(self.confirmation_token_receiver.clone());
+ }
// NOTE: as per offline discussion, sanitizing key parameters and rejecting
// create operation if any non-allowed tags are present, is not done in
// authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
@@ -550,7 +600,11 @@
if !unlocked_device_required && no_auth_required {
return Ok((
None,
- AuthInfo { state: DeferredAuthState::NoAuthRequired, key_usage_limited },
+ AuthInfo {
+ state: DeferredAuthState::NoAuthRequired,
+ key_usage_limited,
+ confirmation_token_receiver,
+ },
));
}
@@ -625,7 +679,9 @@
(None, _, true) => (None, DeferredAuthState::OpAuthRequired),
(None, _, false) => (None, DeferredAuthState::NoAuthRequired),
})
- .map(|(hat, state)| (hat, AuthInfo { state, key_usage_limited }))
+ .map(|(hat, state)| {
+ (hat, AuthInfo { state, key_usage_limited, confirmation_token_receiver })
+ })
}
fn find_auth_token<F>(p: F) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>>
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index f8dba07..0fd515c 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -17,9 +17,10 @@
use binder::Interface;
use keystore2::apc::ApcManager;
use keystore2::authorization::AuthorizationManager;
+use keystore2::globals::ENFORCEMENTS;
use keystore2::service::KeystoreService;
use log::{error, info};
-use std::{panic, path::Path};
+use std::{panic, path::Path, sync::mpsc::channel};
static KS2_SERVICE_NAME: &str = "android.system.keystore2";
static APC_SERVICE_NAME: &str = "android.security.apc";
@@ -57,6 +58,10 @@
panic!("Must specify a working directory.");
}
+ let (confirmation_token_sender, confirmation_token_receiver) = channel();
+
+ ENFORCEMENTS.install_confirmation_token_receiver(confirmation_token_receiver);
+
info!("Starting thread pool now.");
binder::ProcessState::start_thread_pool();
@@ -67,9 +72,10 @@
panic!("Failed to register service {} because of {:?}.", KS2_SERVICE_NAME, e);
});
- let apc_service = ApcManager::new_native_binder().unwrap_or_else(|e| {
- panic!("Failed to create service {} because of {:?}.", APC_SERVICE_NAME, e);
- });
+ let apc_service =
+ ApcManager::new_native_binder(confirmation_token_sender).unwrap_or_else(|e| {
+ panic!("Failed to create service {} because of {:?}.", APC_SERVICE_NAME, e);
+ });
binder::add_service(APC_SERVICE_NAME, apc_service.as_binder()).unwrap_or_else(|e| {
panic!("Failed to register service {} because of {:?}.", APC_SERVICE_NAME, e);
});
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 97bab77..18ea19f 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -429,18 +429,25 @@
let km_op: Box<dyn IKeyMintOperation> =
self.km_op.get_interface().context("In finish: Failed to get KeyMintOperation.")?;
- let (hat, tst) = self
+ let (hat, tst, confirmation_token) = self
.auth_info
.lock()
.unwrap()
.before_finish()
.context("In finish: Trying to get auth tokens.")?;
+ let in_params = confirmation_token.map(|token| KeyParameterArray {
+ params: vec![KmParam {
+ tag: Tag::CONFIRMATION_TOKEN,
+ value: KmParamValue::Blob(token),
+ }],
+ });
+
let output = self
.update_outcome(
&mut *outcome,
map_km_error(km_op.finish(
- None,
+ in_params.as_ref(),
input,
signature,
hat.as_ref(),