Keystore 2.0: Provide confirmation token to operation.
This patch connects the APC manager to the enforment module and
ultimately to the operation, so that the confirmation token can be
delivered to operations that require it.
Also fix a bug in apc_compat.rs.
Test: CtsVerifier
Change-Id: I69b6b08b10f51d255c2d70da3a6354b04f0ce801
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(),