// Copyright 2020, 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. //! This is the Keystore 2.0 Enforcements module. // TODO: more description to follow. use crate::ks_err; use crate::error::{map_binder_status, Error, ErrorCode}; use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS}; use crate::key_parameter::{KeyParameter, KeyParameterValue}; use crate::{authorization::Error as AuthzError, super_key::SuperEncryptionType}; use crate::{ database::{AuthTokenEntry, MonotonicRawTime}, globals::SUPER_KEY, }; use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType, KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, Tag::Tag, }; use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{ TimeStampToken::TimeStampToken, }; use android_security_authorization::aidl::android::security::authorization::ResponseCode::ResponseCode as AuthzResponseCode; use android_system_keystore2::aidl::android::system::keystore2::{ Domain::Domain, IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING, OperationChallenge::OperationChallenge, }; use anyhow::{Context, Result}; use std::{ collections::{HashMap, HashSet}, sync::{ mpsc::{channel, Receiver, Sender, TryRecvError}, Arc, Mutex, Weak, }, time::SystemTime, }; #[derive(Debug)] enum AuthRequestState { /// An outstanding per operation authorization request. OpAuth, /// An outstanding request for per operation authorization and secure timestamp. TimeStampedOpAuth(Mutex>>), /// An outstanding request for a timestamp token. TimeStamp(Mutex>>), } #[derive(Debug)] struct AuthRequest { state: AuthRequestState, /// This need to be set to Some to fulfill a AuthRequestState::OpAuth or /// AuthRequestState::TimeStampedOpAuth. hat: Mutex>, } impl AuthRequest { fn op_auth() -> Arc { Arc::new(Self { state: AuthRequestState::OpAuth, hat: Mutex::new(None) }) } fn timestamped_op_auth(receiver: Receiver>) -> Arc { Arc::new(Self { state: AuthRequestState::TimeStampedOpAuth(Mutex::new(receiver)), hat: Mutex::new(None), }) } fn timestamp( hat: HardwareAuthToken, receiver: Receiver>, ) -> Arc { Arc::new(Self { state: AuthRequestState::TimeStamp(Mutex::new(receiver)), hat: Mutex::new(Some(hat)), }) } fn add_auth_token(&self, hat: HardwareAuthToken) { *self.hat.lock().unwrap() = Some(hat) } fn get_auth_tokens(&self) -> Result<(HardwareAuthToken, Option)> { let hat = self .hat .lock() .unwrap() .take() .ok_or(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED)) .context(ks_err!("No operation auth token received."))?; let tst = match &self.state { AuthRequestState::TimeStampedOpAuth(recv) | AuthRequestState::TimeStamp(recv) => { let result = recv .lock() .unwrap() .recv() .context("In get_auth_tokens: Sender disconnected.")?; Some(result.context(ks_err!( "Worker responded with error \ from generating timestamp token.", ))?) } AuthRequestState::OpAuth => None, }; Ok((hat, tst)) } } /// DeferredAuthState describes how auth tokens and timestamp tokens need to be provided when /// updating and finishing an operation. #[derive(Debug)] enum DeferredAuthState { /// Used when an operation does not require further authorization. NoAuthRequired, /// Indicates that the operation requires an operation specific token. This means we have /// to return an operation challenge to the client which should reward us with an /// operation specific auth token. If it is not provided before the client calls update /// or finish, the operation fails as not authorized. OpAuthRequired, /// Indicates that the operation requires a time stamp token. The auth token was already /// loaded from the database, but it has to be accompanied by a time stamp token to inform /// the target KM with a different clock about the time on the authenticators. TimeStampRequired(HardwareAuthToken), /// Indicates that both an operation bound auth token and a verification token are /// before the operation can commence. TimeStampedOpAuthRequired, /// In this state the auth info is waiting for the deferred authorizations to come in. /// We block on timestamp tokens, because we can always make progress on these requests. /// The per-op auth tokens might never come, which means we fail if the client calls /// update or finish before we got a per-op auth token. Waiting(Arc), /// In this state we have gotten all of the required tokens, we just cache them to /// be used when the operation progresses. Token(HardwareAuthToken, Option), } /// Auth info hold all of the authorization related information of an operation. It is stored /// in and owned by the operation. It is constructed by authorize_create and stays with the /// operation until it completes. #[derive(Debug)] pub struct AuthInfo { state: DeferredAuthState, /// An optional key id required to update the usage count if the key usage is limited. key_usage_limited: Option, confirmation_token_receiver: Option>>>>>, } struct TokenReceiverMap { /// The map maps an outstanding challenge to a TokenReceiver. If an incoming Hardware Auth /// Token (HAT) has the map key in its challenge field, it gets passed to the TokenReceiver /// and the entry is removed from the map. In the case where no HAT is received before the /// corresponding operation gets dropped, the entry goes stale. So every time the cleanup /// counter (second field in the tuple) turns 0, the map is cleaned from stale entries. /// The cleanup counter is decremented every time a new receiver is added. /// and reset to TokenReceiverMap::CLEANUP_PERIOD + 1 after each cleanup. map_and_cleanup_counter: Mutex<(HashMap, u8)>, } impl Default for TokenReceiverMap { fn default() -> Self { Self { map_and_cleanup_counter: Mutex::new((HashMap::new(), Self::CLEANUP_PERIOD + 1)) } } } impl TokenReceiverMap { /// There is a chance that receivers may become stale because their operation is dropped /// without ever being authorized. So occasionally we iterate through the map and throw /// out obsolete entries. /// This is the number of calls to add_receiver between cleanups. const CLEANUP_PERIOD: u8 = 25; pub fn add_auth_token(&self, hat: HardwareAuthToken) { let recv = { // Limit the scope of the mutex guard, so that it is not held while the auth token is // added. let mut map = self.map_and_cleanup_counter.lock().unwrap(); let (ref mut map, _) = *map; map.remove_entry(&hat.challenge) }; if let Some((_, recv)) = recv { recv.add_auth_token(hat); } } pub fn add_receiver(&self, challenge: i64, recv: TokenReceiver) { let mut map = self.map_and_cleanup_counter.lock().unwrap(); let (ref mut map, ref mut cleanup_counter) = *map; map.insert(challenge, recv); *cleanup_counter -= 1; if *cleanup_counter == 0 { map.retain(|_, v| !v.is_obsolete()); map.shrink_to_fit(); *cleanup_counter = Self::CLEANUP_PERIOD + 1; } } } #[derive(Debug)] struct TokenReceiver(Weak); impl TokenReceiver { fn is_obsolete(&self) -> bool { self.0.upgrade().is_none() } fn add_auth_token(&self, hat: HardwareAuthToken) { if let Some(state_arc) = self.0.upgrade() { state_arc.add_auth_token(hat); } } } fn get_timestamp_token(challenge: i64) -> Result { let dev = get_timestamp_service().expect(concat!( "Secure Clock service must be present ", "if TimeStampTokens are required." )); map_binder_status(dev.generateTimeStamp(challenge)) } fn timestamp_token_request(challenge: i64, sender: Sender>) { if let Err(e) = sender.send(get_timestamp_token(challenge)) { log::info!( concat!("Receiver hung up ", "before timestamp token could be delivered. {:?}"), e ); } } impl AuthInfo { /// This function gets called after an operation was successfully created. /// It makes all the preparations required, so that the operation has all the authentication /// related artifacts to advance on update and finish. pub fn finalize_create_authorization(&mut self, challenge: i64) -> Option { match &self.state { DeferredAuthState::OpAuthRequired => { let auth_request = AuthRequest::op_auth(); let token_receiver = TokenReceiver(Arc::downgrade(&auth_request)); ENFORCEMENTS.register_op_auth_receiver(challenge, token_receiver); self.state = DeferredAuthState::Waiting(auth_request); Some(OperationChallenge { challenge }) } DeferredAuthState::TimeStampedOpAuthRequired => { let (sender, receiver) = channel::>(); let auth_request = AuthRequest::timestamped_op_auth(receiver); let token_receiver = TokenReceiver(Arc::downgrade(&auth_request)); ENFORCEMENTS.register_op_auth_receiver(challenge, token_receiver); ASYNC_TASK.queue_hi(move |_| timestamp_token_request(challenge, sender)); self.state = DeferredAuthState::Waiting(auth_request); Some(OperationChallenge { challenge }) } DeferredAuthState::TimeStampRequired(hat) => { let hat = (*hat).clone(); let (sender, receiver) = channel::>(); let auth_request = AuthRequest::timestamp(hat, receiver); ASYNC_TASK.queue_hi(move |_| timestamp_token_request(challenge, sender)); self.state = DeferredAuthState::Waiting(auth_request); None } _ => None, } } /// This function is the authorization hook called before operation update. /// It returns the auth tokens required by the operation to commence update. pub fn before_update(&mut self) -> Result<(Option, Option)> { self.get_auth_tokens() } /// This function is the authorization hook called before operation finish. /// It returns the auth tokens required by the operation to commence finish. /// The third token is a confirmation token. pub fn before_finish( &mut self, ) -> Result<(Option, Option, Option>)> { let mut confirmation_token: Option> = 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. /// As of this writing it checks if the key was a limited use key. If so it updates the /// use counter of the key in the database. When the use counter is depleted, the key gets /// marked for deletion and the garbage collector is notified. pub fn after_finish(&self) -> Result<()> { if let Some(key_id) = self.key_usage_limited { // On the last successful use, the key gets deleted. In this case we // have to notify the garbage collector. DB.with(|db| { db.borrow_mut() .check_and_update_key_usage_count(key_id) .context("Trying to update key usage count.") }) .context(ks_err!())?; } Ok(()) } /// This function returns the auth tokens as needed by the ongoing operation or fails /// with ErrorCode::KEY_USER_NOT_AUTHENTICATED. If this was called for the first time /// after a deferred authorization was requested by finalize_create_authorization, this /// function may block on the generation of a time stamp token. It then moves the /// tokens into the DeferredAuthState::Token state for future use. fn get_auth_tokens(&mut self) -> Result<(Option, Option)> { let deferred_tokens = if let DeferredAuthState::Waiting(ref auth_request) = self.state { Some(auth_request.get_auth_tokens().context("In AuthInfo::get_auth_tokens.")?) } else { None }; if let Some((hat, tst)) = deferred_tokens { self.state = DeferredAuthState::Token(hat, tst); } match &self.state { DeferredAuthState::NoAuthRequired => Ok((None, None)), DeferredAuthState::Token(hat, tst) => Ok((Some((*hat).clone()), (*tst).clone())), DeferredAuthState::OpAuthRequired | DeferredAuthState::TimeStampedOpAuthRequired | DeferredAuthState::TimeStampRequired(_) => { Err(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED)).context(ks_err!( "No operation auth token requested??? \ This should not happen." )) } // This should not be reachable, because it should have been handled above. DeferredAuthState::Waiting(_) => { Err(Error::sys()).context(ks_err!("AuthInfo::get_auth_tokens: Cannot be reached.",)) } } } } /// Enforcements data structure #[derive(Default)] pub struct Enforcements { /// This hash set contains the user ids for whom the device is currently unlocked. If a user id /// is not in the set, it implies that the device is locked for the user. device_unlocked_set: Mutex>, /// This field maps outstanding auth challenges to their operations. When an auth token /// with the right challenge is received it is passed to the map using /// TokenReceiverMap::add_auth_token() which removes the entry from the map. If an entry goes /// 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>>>>, } impl Enforcements { /// 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>, ) { *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 /// auth tokens and timestamp tokens as required by the operation. /// With regard to auth tokens, the following steps are taken: /// /// If no key parameters are given (typically when the client is self managed /// (see Domain.Blob)) nothing is enforced. /// If the key is time-bound, find a matching auth token from the database. /// If the above step is successful, and if requires_timestamp is given, the returned /// AuthInfo will provide a Timestamp token as appropriate. pub fn authorize_create( &self, purpose: KeyPurpose, key_properties: Option<&(i64, Vec)>, op_params: &[KmKeyParameter], requires_timestamp: bool, ) -> Result<(Option, AuthInfo)> { let (key_id, key_params) = match key_properties { Some((key_id, key_params)) => (*key_id, key_params), None => { return Ok(( None, AuthInfo { state: DeferredAuthState::NoAuthRequired, key_usage_limited: None, confirmation_token_receiver: None, }, )); } }; match purpose { // Allow SIGN, DECRYPT for both symmetric and asymmetric keys. KeyPurpose::SIGN | KeyPurpose::DECRYPT => {} // Rule out WRAP_KEY purpose KeyPurpose::WRAP_KEY => { return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE)) .context(ks_err!("WRAP_KEY purpose is not allowed here.",)); } // Allow AGREE_KEY for EC keys only. KeyPurpose::AGREE_KEY => { for kp in key_params.iter() { if kp.get_tag() == Tag::ALGORITHM && *kp.key_parameter_value() != KeyParameterValue::Algorithm(Algorithm::EC) { return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)) .context(ks_err!("key agreement is only supported for EC keys.",)); } } } KeyPurpose::VERIFY | KeyPurpose::ENCRYPT => { // We do not support ENCRYPT and VERIFY (the remaining two options of purpose) for // asymmetric keys. for kp in key_params.iter() { match *kp.key_parameter_value() { KeyParameterValue::Algorithm(Algorithm::RSA) | KeyParameterValue::Algorithm(Algorithm::EC) => { return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)).context(ks_err!( "public operations on asymmetric keys are \ not supported." )); } _ => {} } } } _ => { return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)) .context(ks_err!("authorize_create: specified purpose is not supported.")); } } // The following variables are to record information from key parameters to be used in // enforcements, when two or more such pieces of information are required for enforcements. // There is only one additional variable than what legacy keystore has, but this helps // reduce the number of for loops on key parameters from 3 to 1, compared to legacy keystore let mut key_purpose_authorized: bool = false; let mut user_auth_type: Option = None; let mut no_auth_required: bool = false; let mut caller_nonce_allowed = false; let mut user_id: i32 = -1; let mut user_secure_ids = Vec::::new(); let mut key_time_out: Option = None; let mut allow_while_on_body = false; let mut unlocked_device_required = false; let mut key_usage_limited: Option = None; let mut confirmation_token_receiver: Option>>>>> = None; let mut max_boot_level: Option = None; // iterate through key parameters, recording information we need for authorization // enforcements later, or enforcing authorizations in place, where applicable for key_param in key_params.iter() { match key_param.key_parameter_value() { KeyParameterValue::NoAuthRequired => { no_auth_required = true; } KeyParameterValue::AuthTimeout(t) => { key_time_out = Some(*t as i64); } KeyParameterValue::HardwareAuthenticatorType(a) => { user_auth_type = Some(*a); } KeyParameterValue::KeyPurpose(p) => { // The following check has the effect of key_params.contains(purpose) // Also, authorizing purpose can not be completed here, if there can be multiple // key parameters for KeyPurpose. key_purpose_authorized = key_purpose_authorized || *p == purpose; } KeyParameterValue::CallerNonce => { caller_nonce_allowed = true; } KeyParameterValue::ActiveDateTime(a) => { if !Enforcements::is_given_time_passed(*a, true) { return Err(Error::Km(Ec::KEY_NOT_YET_VALID)) .context(ks_err!("key is not yet active.")); } } KeyParameterValue::OriginationExpireDateTime(o) => { if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN) && Enforcements::is_given_time_passed(*o, false) { return Err(Error::Km(Ec::KEY_EXPIRED)).context(ks_err!("key is expired.")); } } KeyParameterValue::UsageExpireDateTime(u) => { if (purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY) && Enforcements::is_given_time_passed(*u, false) { return Err(Error::Km(Ec::KEY_EXPIRED)).context(ks_err!("key is expired.")); } } KeyParameterValue::UserSecureID(s) => { user_secure_ids.push(*s); } KeyParameterValue::UserID(u) => { user_id = *u; } KeyParameterValue::UnlockedDeviceRequired => { unlocked_device_required = true; } KeyParameterValue::AllowWhileOnBody => { allow_while_on_body = true; } KeyParameterValue::UsageCountLimit(_) => { // We don't examine the limit here because this is enforced on finish. // Instead, we store the key_id so that finish can look up the key // 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()); } KeyParameterValue::MaxBootLevel(level) => { max_boot_level = Some(*level); } // 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 // a subset of non-allowed tags are present). Because sanitizing key parameters // should have been done during generate/import key, by KeyMint. _ => { /*Do nothing on all the other key parameters, as in legacy keystore*/ } } } // authorize the purpose if !key_purpose_authorized { return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE)) .context(ks_err!("the purpose is not authorized.")); } // if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error if !user_secure_ids.is_empty() && no_auth_required { return Err(Error::Km(Ec::INVALID_KEY_BLOB)) .context(ks_err!("key has both NO_AUTH_REQUIRED and USER_SECURE_ID tags.")); } // if either of auth_type or secure_id is present and the other is not present, return error if (user_auth_type.is_some() && user_secure_ids.is_empty()) || (user_auth_type.is_none() && !user_secure_ids.is_empty()) { return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(ks_err!( "Auth required, but either auth type or secure ids \ are not present." )); } // validate caller nonce for origination purposes if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN) && !caller_nonce_allowed && op_params.iter().any(|kp| kp.tag == Tag::NONCE) { return Err(Error::Km(Ec::CALLER_NONCE_PROHIBITED)) .context(ks_err!("NONCE is present, although CALLER_NONCE is not present")); } if unlocked_device_required { // check the device locked status. If locked, operations on the key are not // allowed. if self.is_device_locked(user_id) { return Err(Error::Km(Ec::DEVICE_LOCKED)).context(ks_err!("device is locked.")); } } if let Some(level) = max_boot_level { if !SUPER_KEY.read().unwrap().level_accessible(level) { return Err(Error::Km(Ec::BOOT_LEVEL_EXCEEDED)) .context(ks_err!("boot level is too late.")); } } if android_security_flags::fix_unlocked_device_required_keys() { let (hat, state) = if user_secure_ids.is_empty() { (None, DeferredAuthState::NoAuthRequired) } else if let Some(key_time_out) = key_time_out { let (hat, last_off_body) = Self::find_auth_token(|hat: &AuthTokenEntry| match user_auth_type { Some(auth_type) => hat.satisfies(&user_secure_ids, auth_type), None => false, // not reachable due to earlier check }) .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)) .context(ks_err!("No suitable auth token found."))?; let now = MonotonicRawTime::now(); let token_age = now .checked_sub(&hat.time_received()) .ok_or_else(Error::sys) .context(ks_err!( "Overflow while computing Auth token validity. \ Validity cannot be established." ))?; let on_body_extended = allow_while_on_body && last_off_body < hat.time_received(); if token_age.seconds() > key_time_out && !on_body_extended { return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)) .context(ks_err!("matching auth token is expired.")); } let state = if requires_timestamp { DeferredAuthState::TimeStampRequired(hat.auth_token().clone()) } else { DeferredAuthState::NoAuthRequired }; (Some(hat.take_auth_token()), state) } else { (None, DeferredAuthState::OpAuthRequired) }; return Ok((hat, AuthInfo { state, key_usage_limited, confirmation_token_receiver })); } if !unlocked_device_required && no_auth_required { return Ok(( None, AuthInfo { state: DeferredAuthState::NoAuthRequired, key_usage_limited, confirmation_token_receiver, }, )); } let has_sids = !user_secure_ids.is_empty(); let timeout_bound = key_time_out.is_some() && has_sids; let per_op_bound = key_time_out.is_none() && has_sids; let need_auth_token = timeout_bound || unlocked_device_required; let hat_and_last_off_body = if need_auth_token { let hat_and_last_off_body = Self::find_auth_token(|hat: &AuthTokenEntry| { if let (Some(auth_type), true) = (user_auth_type, timeout_bound) { hat.satisfies(&user_secure_ids, auth_type) } else { unlocked_device_required } }); Some( hat_and_last_off_body .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)) .context(ks_err!("No suitable auth token found."))?, ) } else { None }; // Now check the validity of the auth token if the key is timeout bound. let hat = match (hat_and_last_off_body, key_time_out) { (Some((hat, last_off_body)), Some(key_time_out)) => { let now = MonotonicRawTime::now(); let token_age = now .checked_sub(&hat.time_received()) .ok_or_else(Error::sys) .context(ks_err!( "Overflow while computing Auth token validity. \ Validity cannot be established." ))?; let on_body_extended = allow_while_on_body && last_off_body < hat.time_received(); if token_age.seconds() > key_time_out && !on_body_extended { return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)) .context(ks_err!("matching auth token is expired.")); } Some(hat) } (Some((hat, _)), None) => Some(hat), // If timeout_bound is true, above code must have retrieved a HAT or returned with // KEY_USER_NOT_AUTHENTICATED. This arm should not be reachable. (None, Some(_)) => panic!("Logical error."), _ => None, }; Ok(match (hat, requires_timestamp, per_op_bound) { // Per-op-bound and Some(hat) can only happen if we are both per-op bound and unlocked // device required. In addition, this KM instance needs a timestamp token. // So the HAT cannot be presented on create. So on update/finish we present both // an per-op-bound auth token and a timestamp token. (Some(_), true, true) => (None, DeferredAuthState::TimeStampedOpAuthRequired), (Some(hat), true, false) => ( Some(hat.auth_token().clone()), DeferredAuthState::TimeStampRequired(hat.take_auth_token()), ), (Some(hat), false, true) => { (Some(hat.take_auth_token()), DeferredAuthState::OpAuthRequired) } (Some(hat), false, false) => { (Some(hat.take_auth_token()), DeferredAuthState::NoAuthRequired) } (None, _, true) => (None, DeferredAuthState::OpAuthRequired), (None, _, false) => (None, DeferredAuthState::NoAuthRequired), }) .map(|(hat, state)| { (hat, AuthInfo { state, key_usage_limited, confirmation_token_receiver }) }) } fn find_auth_token(p: F) -> Option<(AuthTokenEntry, MonotonicRawTime)> where F: Fn(&AuthTokenEntry) -> bool, { DB.with(|db| db.borrow().find_auth_token_entry(p)) } /// Checks if the time now since epoch is greater than (or equal, if is_given_time_inclusive is /// set) the given time (in milliseconds) fn is_given_time_passed(given_time: i64, is_given_time_inclusive: bool) -> bool { let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH); let time_since_epoch = match duration_since_epoch { Ok(duration) => duration.as_millis(), Err(_) => return false, }; if is_given_time_inclusive { time_since_epoch >= (given_time as u128) } else { time_since_epoch > (given_time as u128) } } /// Check if the device is locked for the given user. If there's no entry yet for the user, /// we assume that the device is locked fn is_device_locked(&self, user_id: i32) -> bool { // unwrap here because there's no way this mutex guard can be poisoned and // because there's no way to recover, even if it is poisoned. let set = self.device_unlocked_set.lock().unwrap(); !set.contains(&user_id) } /// Sets the device locked status for the user. This method is called externally. pub fn set_device_locked(&self, user_id: i32, device_locked_status: bool) { // unwrap here because there's no way this mutex guard can be poisoned and // because there's no way to recover, even if it is poisoned. let mut set = self.device_unlocked_set.lock().unwrap(); if device_locked_status { set.remove(&user_id); } else { set.insert(user_id); } } /// Add this auth token to the database. /// Then present the auth token to the op auth map. If an operation is waiting for this /// auth token this fulfills the request and removes the receiver from the map. pub fn add_auth_token(&self, hat: HardwareAuthToken) { DB.with(|db| db.borrow_mut().insert_auth_token(&hat)); self.op_auth_map.add_auth_token(hat); } /// This allows adding an entry to the op_auth_map, indexed by the operation challenge. /// This is to be called by create_operation, once it has received the operation challenge /// from keymint for an operation whose authorization decision is OpAuthRequired, as signalled /// by the DeferredAuthState. fn register_op_auth_receiver(&self, challenge: i64, recv: TokenReceiver) { self.op_auth_map.add_receiver(challenge, recv); } /// Given the set of key parameters and flags, check if super encryption is required. pub fn super_encryption_required( domain: &Domain, key_parameters: &[KeyParameter], flags: Option, ) -> SuperEncryptionType { if let Some(flags) = flags { if (flags & KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING) != 0 { return SuperEncryptionType::None; } } // Each answer has a priority, numerically largest priority wins. struct Candidate { priority: u32, enc_type: SuperEncryptionType, } let mut result = Candidate { priority: 0, enc_type: SuperEncryptionType::None }; for kp in key_parameters { let t = match kp.key_parameter_value() { KeyParameterValue::MaxBootLevel(level) => { Candidate { priority: 3, enc_type: SuperEncryptionType::BootLevel(*level) } } KeyParameterValue::UnlockedDeviceRequired if *domain == Domain::APP => { Candidate { priority: 2, enc_type: SuperEncryptionType::UnlockedDeviceRequired } } KeyParameterValue::UserSecureID(_) if *domain == Domain::APP => { Candidate { priority: 1, enc_type: SuperEncryptionType::AfterFirstUnlock } } _ => Candidate { priority: 0, enc_type: SuperEncryptionType::None }, }; if t.priority > result.priority { result = t; } } result.enc_type } /// Finds a matching auth token along with a timestamp token. /// This method looks through auth-tokens cached by keystore which satisfy the given /// authentication information (i.e. |secureUserId|). /// The most recent matching auth token which has a |challenge| field which matches /// the passed-in |challenge| parameter is returned. /// In this case the |authTokenMaxAgeMillis| parameter is not used. /// /// Otherwise, the most recent matching auth token which is younger than |authTokenMaxAgeMillis| /// is returned. pub fn get_auth_tokens( &self, challenge: i64, secure_user_id: i64, auth_token_max_age_millis: i64, ) -> Result<(HardwareAuthToken, TimeStampToken)> { let auth_type = HardwareAuthenticatorType::ANY; let sids: Vec = vec![secure_user_id]; // Filter the matching auth tokens by challenge let result = Self::find_auth_token(|hat: &AuthTokenEntry| { (challenge == hat.challenge()) && hat.satisfies(&sids, auth_type) }); let auth_token = if let Some((auth_token_entry, _)) = result { auth_token_entry.take_auth_token() } else { // Filter the matching auth tokens by age. if auth_token_max_age_millis != 0 { let now_in_millis = MonotonicRawTime::now(); let result = Self::find_auth_token(|auth_token_entry: &AuthTokenEntry| { let token_valid = now_in_millis .checked_sub(&auth_token_entry.time_received()) .map_or(false, |token_age_in_millis| { auth_token_max_age_millis > token_age_in_millis.milliseconds() }); token_valid && auth_token_entry.satisfies(&sids, auth_type) }); if let Some((auth_token_entry, _)) = result { auth_token_entry.take_auth_token() } else { return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND)) .context(ks_err!("No auth token found.")); } } else { return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND)).context( ks_err!( "No auth token found for \ the given challenge and passed-in auth token max age is zero." ), ); } }; // Wait and obtain the timestamp token from secure clock service. let tst = get_timestamp_token(challenge).context(ks_err!("Error in getting timestamp token."))?; Ok((auth_token, tst)) } /// Finds the most recent received time for an auth token that matches the given secure user id and authenticator pub fn get_last_auth_time( &self, secure_user_id: i64, auth_type: HardwareAuthenticatorType, ) -> Option { let sids: Vec = vec![secure_user_id]; let result = Self::find_auth_token(|entry: &AuthTokenEntry| entry.satisfies(&sids, auth_type)); if let Some((auth_token_entry, _)) = result { Some(auth_token_entry.time_received()) } else { None } } } // TODO: Add tests to enforcement module (b/175578618).