Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 1 | // Copyright 2020, The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 15 | //! This is the Keystore 2.0 Enforcements module. |
| 16 | // TODO: more description to follow. |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 17 | use crate::auth_token_handler::AuthTokenHandler; |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 18 | use crate::background_task_handler::Message; |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 19 | use crate::database::AuthTokenEntry; |
| 20 | use crate::error::Error as KeystoreError; |
Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 21 | use crate::globals::DB; |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 22 | use crate::key_parameter::{KeyParameter, KeyParameterValue}; |
| 23 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 24 | Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken, |
| 25 | HardwareAuthenticatorType::HardwareAuthenticatorType, KeyPurpose::KeyPurpose, |
Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 26 | SecurityLevel::SecurityLevel, Tag::Tag, |
| 27 | }; |
| 28 | use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{ |
| 29 | TimeStampToken::TimeStampToken, Timestamp::Timestamp, |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 30 | }; |
| 31 | use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge; |
| 32 | use anyhow::{Context, Result}; |
Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 33 | use std::collections::{HashMap, HashSet}; |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 34 | use std::sync::mpsc::{channel, Sender}; |
Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 35 | use std::sync::Mutex; |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 36 | use std::time::SystemTime; |
Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 37 | |
| 38 | /// Enforcements data structure |
| 39 | pub struct Enforcements { |
| 40 | // This hash set contains the user ids for whom the device is currently unlocked. If a user id |
| 41 | // is not in the set, it implies that the device is locked for the user. |
| 42 | device_unlocked_set: Mutex<HashSet<i32>>, |
| 43 | // This maps the operation challenge to an optional auth token, to maintain op-auth tokens |
| 44 | // in-memory, until they are picked up and given to the operation by authorise_update_finish(). |
| 45 | op_auth_map: Mutex<HashMap<i64, Option<HardwareAuthToken>>>, |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 46 | // sender end of the channel via which the enforcement module communicates with the |
| 47 | // background task handler (bth). This is of type Mutex in an Option because it is initialized |
| 48 | // after the global enforcement object is created. |
| 49 | sender_to_bth: Mutex<Option<Sender<Message>>>, |
Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | impl Enforcements { |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 53 | /// Creates an enforcement object with the two data structures it holds and the sender as None. |
Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 54 | pub fn new() -> Self { |
| 55 | Enforcements { |
| 56 | device_unlocked_set: Mutex::new(HashSet::new()), |
| 57 | op_auth_map: Mutex::new(HashMap::new()), |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 58 | sender_to_bth: Mutex::new(None), |
Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 59 | } |
| 60 | } |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 61 | |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 62 | /// Initialize the sender_to_bth field, using the given sender end of a channel. |
| 63 | pub fn set_sender_to_bth(&self, sender: Sender<Message>) { |
| 64 | // It is ok to unwrap here because there is no chance of poisoning this mutex. |
| 65 | let mut sender_guard = self.sender_to_bth.lock().unwrap(); |
| 66 | *sender_guard = Some(sender); |
| 67 | } |
| 68 | |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 69 | /// Checks if update or finish calls are authorized. If the operation is based on per-op key, |
| 70 | /// try to receive the auth token from the op_auth_map. We assume that by the time update/finish |
| 71 | /// is called, the auth token has been delivered to keystore. Therefore, we do not wait for it |
| 72 | /// and if the auth token is not found in the map, an error is returned. |
Hasini Gunasinghe | 888dd35 | 2020-11-17 23:08:39 +0000 | [diff] [blame] | 73 | /// This method is called only during the first call to update or if finish is called right |
| 74 | /// after create operation, because the operation caches the authorization decisions and tokens |
| 75 | /// from previous calls to enforcement module. |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 76 | pub fn authorize_update_or_finish( |
| 77 | &self, |
| 78 | key_params: &[KeyParameter], |
Hasini Gunasinghe | 888dd35 | 2020-11-17 23:08:39 +0000 | [diff] [blame] | 79 | op_challenge: Option<&OperationChallenge>, |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 80 | ) -> Result<AuthTokenHandler> { |
| 81 | let mut user_auth_type: Option<HardwareAuthenticatorType> = None; |
| 82 | let mut user_secure_ids = Vec::<i64>::new(); |
| 83 | let mut is_timeout_key = false; |
| 84 | |
| 85 | for key_param in key_params.iter() { |
| 86 | match key_param.key_parameter_value() { |
| 87 | KeyParameterValue::NoAuthRequired => { |
| 88 | // unlike in authorize_create, we do not check if both NoAuthRequired and user |
| 89 | // secure id are present, because that is already checked in authorize_create. |
| 90 | return Ok(AuthTokenHandler::NoAuthRequired); |
| 91 | } |
| 92 | KeyParameterValue::AuthTimeout(_) => { |
| 93 | is_timeout_key = true; |
| 94 | } |
| 95 | KeyParameterValue::HardwareAuthenticatorType(a) => { |
| 96 | user_auth_type = Some(*a); |
| 97 | } |
| 98 | KeyParameterValue::UserSecureID(u) => { |
| 99 | user_secure_ids.push(*u); |
| 100 | } |
| 101 | _ => {} |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | // If either of auth_type or secure_id is present and the other is not present, |
| 106 | // authorize_create would have already returned error. |
| 107 | // At this point, if UserSecureID is present and AuthTimeout is not present in |
| 108 | // key parameters, per-op auth is required. |
| 109 | // Obtain and validate the auth token. |
| 110 | if !is_timeout_key && !user_secure_ids.is_empty() { |
| 111 | let challenge = |
| 112 | op_challenge.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context( |
| 113 | "In authorize_update_or_finish: Auth required, but operation challenge is not |
| 114 | present.", |
| 115 | )?; |
| 116 | let auth_type = |
| 117 | user_auth_type.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context( |
| 118 | "In authorize_update_or_finish: Auth required, but authenticator type is not |
| 119 | present.", |
| 120 | )?; |
| 121 | // It is ok to unwrap here, because there is no way this lock can get poisoned and |
| 122 | // and there is no way to recover if it is poisoned. |
| 123 | let mut op_auth_map_guard = self.op_auth_map.lock().unwrap(); |
| 124 | let auth_entry = op_auth_map_guard.remove(&(challenge.challenge)); |
| 125 | |
| 126 | match auth_entry { |
| 127 | Some(Some(auth_token)) => { |
| 128 | if AuthTokenEntry::satisfies_auth(&auth_token, &user_secure_ids, auth_type) { |
| 129 | return Ok(AuthTokenHandler::Token(auth_token, None)); |
| 130 | } else { |
| 131 | return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)) |
| 132 | .context("In authorize_update_or_finish: Auth token does not match."); |
| 133 | } |
| 134 | } |
| 135 | _ => { |
| 136 | // there was no auth token |
| 137 | return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context( |
| 138 | "In authorize_update_or_finish: Auth required, but an auth token |
| 139 | is not found for the given operation challenge, in the op_auth_map.", |
| 140 | ); |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | // If we don't find HardwareAuthenticatorType and UserSecureID, we assume that |
| 146 | // authentication is not required, because in legacy keys, authentication related |
| 147 | // key parameters may not present. |
| 148 | // TODO: METRICS: count how many times (if any) this code path is executed, in order |
| 149 | // to identify if any such keys are in use |
| 150 | Ok(AuthTokenHandler::NoAuthRequired) |
| 151 | } |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 152 | |
| 153 | /// Checks if a create call is authorized, given key parameters and operation parameters. |
| 154 | /// With regard to auth tokens, the following steps are taken: |
| 155 | /// If the key is time-bound, find a matching auth token from the database. |
| 156 | /// If the above step is successful, and if the security level is STRONGBOX, return a |
Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 157 | /// TimestampRequired variant of the AuthTokenHandler with the found auth token to signal |
| 158 | /// the operation that it may need to obtain a timestamp token from TEE KeyMint. |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 159 | /// If the security level is not STRONGBOX, return a Token variant of the AuthTokenHandler with |
| 160 | /// the found auth token to signal the operation that no more authorization required. |
| 161 | /// If the key is per-op, return an OpAuthRequired variant of the AuthTokenHandler to signal |
| 162 | /// create_operation() that it needs to add the operation challenge to the op_auth_map, once it |
| 163 | /// is received from the keymint, and that operation needs to be authorized before update/finish |
| 164 | /// is called. |
| 165 | pub fn authorize_create( |
| 166 | &self, |
| 167 | purpose: KeyPurpose, |
| 168 | key_params: &[KeyParameter], |
| 169 | op_params: &[KeyParameter], |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 170 | security_level: SecurityLevel, |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 171 | ) -> Result<AuthTokenHandler> { |
| 172 | match purpose { |
| 173 | // Allow SIGN, DECRYPT for both symmetric and asymmetric keys. |
| 174 | KeyPurpose::SIGN | KeyPurpose::DECRYPT => {} |
| 175 | // Rule out WRAP_KEY purpose |
| 176 | KeyPurpose::WRAP_KEY => { |
| 177 | return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE)) |
| 178 | .context("In authorize_create: WRAP_KEY purpose is not allowed here."); |
| 179 | } |
| 180 | KeyPurpose::VERIFY | KeyPurpose::ENCRYPT => { |
| 181 | // We do not support ENCRYPT and VERIFY (the remaining two options of purpose) for |
| 182 | // asymmetric keys. |
| 183 | for kp in key_params.iter() { |
| 184 | match *kp.key_parameter_value() { |
| 185 | KeyParameterValue::Algorithm(Algorithm::RSA) |
| 186 | | KeyParameterValue::Algorithm(Algorithm::EC) => { |
| 187 | return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE)).context( |
| 188 | "In authorize_create: public operations on asymmetric keys are not |
| 189 | supported.", |
| 190 | ); |
| 191 | } |
| 192 | _ => {} |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | _ => { |
| 197 | return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE)) |
| 198 | .context("In authorize_create: specified purpose is not supported."); |
| 199 | } |
| 200 | } |
| 201 | // The following variables are to record information from key parameters to be used in |
| 202 | // enforcements, when two or more such pieces of information are required for enforcements. |
| 203 | // There is only one additional variable than what legacy keystore has, but this helps |
| 204 | // reduce the number of for loops on key parameters from 3 to 1, compared to legacy keystore |
| 205 | let mut key_purpose_authorized: bool = false; |
| 206 | let mut is_time_out_key: bool = false; |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 207 | let mut user_auth_type: Option<HardwareAuthenticatorType> = None; |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 208 | let mut no_auth_required: bool = false; |
| 209 | let mut caller_nonce_allowed = false; |
| 210 | let mut user_id: i32 = -1; |
| 211 | let mut user_secure_ids = Vec::<i64>::new(); |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 212 | let mut key_time_out: Option<i64> = None; |
| 213 | let mut allow_while_on_body = false; |
Hasini Gunasinghe | a020b53 | 2021-01-07 21:42:35 +0000 | [diff] [blame^] | 214 | let mut unlocked_device_required = false; |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 215 | |
| 216 | // iterate through key parameters, recording information we need for authorization |
| 217 | // enforcements later, or enforcing authorizations in place, where applicable |
| 218 | for key_param in key_params.iter() { |
| 219 | match key_param.key_parameter_value() { |
| 220 | KeyParameterValue::NoAuthRequired => { |
| 221 | no_auth_required = true; |
| 222 | } |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 223 | KeyParameterValue::AuthTimeout(t) => { |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 224 | is_time_out_key = true; |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 225 | key_time_out = Some(*t as i64); |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 226 | } |
| 227 | KeyParameterValue::HardwareAuthenticatorType(a) => { |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 228 | user_auth_type = Some(*a); |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 229 | } |
| 230 | KeyParameterValue::KeyPurpose(p) => { |
| 231 | // Note: if there can be multiple KeyPurpose key parameters (TODO: confirm this), |
| 232 | // following check has the effect of key_params.contains(purpose) |
| 233 | // Also, authorizing purpose can not be completed here, if there can be multiple |
| 234 | // key parameters for KeyPurpose |
| 235 | if !key_purpose_authorized && *p == purpose { |
| 236 | key_purpose_authorized = true; |
| 237 | } |
| 238 | } |
| 239 | KeyParameterValue::CallerNonce => { |
| 240 | caller_nonce_allowed = true; |
| 241 | } |
| 242 | KeyParameterValue::ActiveDateTime(a) => { |
| 243 | if !Enforcements::is_given_time_passed(*a, true) { |
| 244 | return Err(KeystoreError::Km(Ec::KEY_NOT_YET_VALID)) |
| 245 | .context("In authorize_create: key is not yet active."); |
| 246 | } |
| 247 | } |
| 248 | KeyParameterValue::OriginationExpireDateTime(o) => { |
| 249 | if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN) |
| 250 | && Enforcements::is_given_time_passed(*o, false) |
| 251 | { |
| 252 | return Err(KeystoreError::Km(Ec::KEY_EXPIRED)) |
| 253 | .context("In authorize_create: key is expired."); |
| 254 | } |
| 255 | } |
| 256 | KeyParameterValue::UsageExpireDateTime(u) => { |
| 257 | if (purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY) |
| 258 | && Enforcements::is_given_time_passed(*u, false) |
| 259 | { |
| 260 | return Err(KeystoreError::Km(Ec::KEY_EXPIRED)) |
| 261 | .context("In authorize_create: key is expired."); |
| 262 | } |
| 263 | } |
| 264 | KeyParameterValue::UserSecureID(s) => { |
| 265 | user_secure_ids.push(*s); |
| 266 | } |
| 267 | KeyParameterValue::UserID(u) => { |
| 268 | user_id = *u; |
| 269 | } |
| 270 | KeyParameterValue::UnlockedDeviceRequired => { |
Hasini Gunasinghe | a020b53 | 2021-01-07 21:42:35 +0000 | [diff] [blame^] | 271 | unlocked_device_required = true; |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 272 | } |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 273 | KeyParameterValue::AllowWhileOnBody => { |
| 274 | allow_while_on_body = true; |
| 275 | } |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 276 | // NOTE: as per offline discussion, sanitizing key parameters and rejecting |
| 277 | // create operation if any non-allowed tags are present, is not done in |
| 278 | // authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if |
| 279 | // a subset of non-allowed tags are present). Because santizing key parameters |
| 280 | // should have been done during generate/import key, by KeyMint. |
| 281 | _ => { /*Do nothing on all the other key parameters, as in legacy keystore*/ } |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | // authorize the purpose |
| 286 | if !key_purpose_authorized { |
| 287 | return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE)) |
| 288 | .context("In authorize_create: the purpose is not authorized."); |
| 289 | } |
| 290 | |
| 291 | // if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error |
| 292 | if !user_secure_ids.is_empty() && no_auth_required { |
| 293 | return Err(KeystoreError::Km(Ec::INVALID_KEY_BLOB)).context( |
| 294 | "In authorize_create: key has both NO_AUTH_REQUIRED |
| 295 | and USER_SECURE_ID tags.", |
| 296 | ); |
| 297 | } |
| 298 | |
| 299 | // if either of auth_type or secure_id is present and the other is not present, return error |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 300 | if (user_auth_type.is_some() && user_secure_ids.is_empty()) |
| 301 | || (user_auth_type.is_none() && !user_secure_ids.is_empty()) |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 302 | { |
| 303 | return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context( |
| 304 | "In authorize_create: Auth required, but either auth type or secure ids |
| 305 | are not present.", |
| 306 | ); |
| 307 | } |
| 308 | // validate caller nonce for origination purposes |
| 309 | if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN) |
| 310 | && !caller_nonce_allowed |
| 311 | && op_params.iter().any(|kp| kp.get_tag() == Tag::NONCE) |
| 312 | { |
| 313 | return Err(KeystoreError::Km(Ec::CALLER_NONCE_PROHIBITED)).context( |
| 314 | "In authorize_create, NONCE is present, |
| 315 | although CALLER_NONCE is not present", |
| 316 | ); |
| 317 | } |
| 318 | |
Hasini Gunasinghe | a020b53 | 2021-01-07 21:42:35 +0000 | [diff] [blame^] | 319 | if unlocked_device_required { |
| 320 | // check the device locked status. If locked, operations on the key are not |
| 321 | // allowed. |
| 322 | log::info!("Checking for lockd device of user {}.", user_id); |
| 323 | if self.is_device_locked(user_id) { |
| 324 | return Err(KeystoreError::Km(Ec::DEVICE_LOCKED)) |
| 325 | .context("In authorize_create: device is locked."); |
| 326 | } |
| 327 | } |
| 328 | |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 329 | if !user_secure_ids.is_empty() { |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 330 | // key requiring authentication per operation |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 331 | if !is_time_out_key { |
| 332 | return Ok(AuthTokenHandler::OpAuthRequired); |
| 333 | } else { |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 334 | // key requiring time-out based authentication |
| 335 | let auth_token = DB |
| 336 | .with::<_, Result<HardwareAuthToken>>(|db| { |
| 337 | let mut db = db.borrow_mut(); |
| 338 | match (user_auth_type, key_time_out) { |
| 339 | (Some(auth_type), Some(key_time_out)) => { |
| 340 | let matching_entry = db |
| 341 | .find_timed_auth_token_entry( |
| 342 | &user_secure_ids, |
| 343 | auth_type, |
| 344 | key_time_out, |
| 345 | allow_while_on_body, |
| 346 | ) |
| 347 | .context("Failed to find timed auth token.")?; |
| 348 | Ok(matching_entry.get_auth_token()) |
| 349 | } |
| 350 | (_, _) => Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)) |
| 351 | .context("Authenticator type and/or key time out is not given."), |
| 352 | } |
| 353 | }) |
| 354 | .context("In authorize_create.")?; |
| 355 | |
| 356 | if security_level == SecurityLevel::STRONGBOX { |
Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 357 | return Ok(AuthTokenHandler::TimestampRequired(auth_token)); |
Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 358 | } else { |
| 359 | return Ok(AuthTokenHandler::Token(auth_token, None)); |
| 360 | } |
Hasini Gunasinghe | 5112c70 | 2020-11-09 22:13:25 +0000 | [diff] [blame] | 361 | } |
| 362 | } |
| 363 | |
| 364 | // If we reach here, all authorization enforcements have passed and no auth token required. |
| 365 | Ok(AuthTokenHandler::NoAuthRequired) |
| 366 | } |
| 367 | |
| 368 | /// Checks if the time now since epoch is greater than (or equal, if is_given_time_inclusive is |
| 369 | /// set) the given time (in milliseconds) |
| 370 | fn is_given_time_passed(given_time: i64, is_given_time_inclusive: bool) -> bool { |
| 371 | let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH); |
| 372 | |
| 373 | let time_since_epoch = match duration_since_epoch { |
| 374 | Ok(duration) => duration.as_millis(), |
| 375 | Err(_) => return false, |
| 376 | }; |
| 377 | |
| 378 | if is_given_time_inclusive { |
| 379 | time_since_epoch >= (given_time as u128) |
| 380 | } else { |
| 381 | time_since_epoch > (given_time as u128) |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | /// Check if the device is locked for the given user. If there's no entry yet for the user, |
| 386 | /// we assume that the device is locked |
| 387 | fn is_device_locked(&self, user_id: i32) -> bool { |
| 388 | // unwrap here because there's no way this mutex guard can be poisoned and |
| 389 | // because there's no way to recover, even if it is poisoned. |
| 390 | let set = self.device_unlocked_set.lock().unwrap(); |
| 391 | !set.contains(&user_id) |
| 392 | } |
Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 393 | |
| 394 | /// Sets the device locked status for the user. This method is called externally. |
| 395 | pub fn set_device_locked(&self, user_id: i32, device_locked_status: bool) { |
| 396 | // unwrap here because there's no way this mutex guard can be poisoned and |
| 397 | // because there's no way to recover, even if it is poisoned. |
| 398 | let mut set = self.device_unlocked_set.lock().unwrap(); |
| 399 | if device_locked_status { |
| 400 | set.remove(&user_id); |
| 401 | } else { |
| 402 | set.insert(user_id); |
| 403 | } |
| 404 | } |
| 405 | |
| 406 | /// Add this auth token to the database. |
| 407 | /// Then check if there is an entry in the op_auth_map, indexed by the challenge of this |
| 408 | /// auth token (which could have been inserted during create_operation of an operation on a |
| 409 | /// per-op-auth key). If so, add a copy of this auth token to the map indexed by the |
| 410 | /// challenge. |
| 411 | pub fn add_auth_token(&self, auth_token: HardwareAuthToken) -> Result<()> { |
| 412 | //it is ok to unwrap here, because there is no way this lock can get poisoned and |
| 413 | //and there is no way to recover if it is poisoned. |
| 414 | let mut op_auth_map_guard = self.op_auth_map.lock().unwrap(); |
| 415 | |
| 416 | if op_auth_map_guard.contains_key(&auth_token.challenge) { |
| 417 | let auth_token_copy = HardwareAuthToken { |
| 418 | challenge: auth_token.challenge, |
| 419 | userId: auth_token.userId, |
| 420 | authenticatorId: auth_token.authenticatorId, |
| 421 | authenticatorType: HardwareAuthenticatorType(auth_token.authenticatorType.0), |
| 422 | timestamp: Timestamp { milliSeconds: auth_token.timestamp.milliSeconds }, |
| 423 | mac: auth_token.mac.clone(), |
| 424 | }; |
| 425 | op_auth_map_guard.insert(auth_token.challenge, Some(auth_token_copy)); |
| 426 | } |
| 427 | |
| 428 | DB.with(|db| db.borrow_mut().insert_auth_token(&auth_token)) |
| 429 | .context("In add_auth_token.")?; |
| 430 | Ok(()) |
| 431 | } |
Hasini Gunasinghe | 888dd35 | 2020-11-17 23:08:39 +0000 | [diff] [blame] | 432 | |
| 433 | /// This allows adding an entry to the op_auth_map, indexed by the operation challenge. |
| 434 | /// This is to be called by create_operation, once it has received the operation challenge |
| 435 | /// from keymint for an operation whose authorization decision is OpAuthRequired, as signalled |
| 436 | /// by the AuthTokenHandler. |
| 437 | pub fn insert_to_op_auth_map(&self, op_challenge: i64) { |
| 438 | let mut op_auth_map_guard = self.op_auth_map.lock().unwrap(); |
| 439 | op_auth_map_guard.insert(op_challenge, None); |
| 440 | } |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 441 | |
Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 442 | /// Requests a timestamp token from the background task handler which will retrieve it from |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 443 | /// Timestamp Service or TEE KeyMint. |
| 444 | /// Once the create_operation receives an operation challenge from KeyMint, if it has |
Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 445 | /// previously received a TimestampRequired variant of AuthTokenHandler during |
| 446 | /// authorize_create_operation, it calls this method to obtain a TimeStampToken. |
| 447 | pub fn request_timestamp_token( |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 448 | &self, |
| 449 | auth_token: HardwareAuthToken, |
| 450 | op_challenge: OperationChallenge, |
| 451 | ) -> Result<AuthTokenHandler> { |
| 452 | // create a channel for this particular operation |
Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 453 | let (op_sender, op_receiver) = channel::<(HardwareAuthToken, TimeStampToken)>(); |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 454 | // it is ok to unwrap here because there is no way this mutex gets poisoned. |
| 455 | let sender_guard = self.sender_to_bth.lock().unwrap(); |
| 456 | if let Some(sender) = &*sender_guard { |
| 457 | let sender_cloned = sender.clone(); |
| 458 | drop(sender_guard); |
| 459 | sender_cloned |
| 460 | .send(Message::Inputs((auth_token, op_challenge, op_sender))) |
| 461 | .map_err(|_| KeystoreError::sys()) |
| 462 | .context( |
Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 463 | "In request_timestamp_token. Sending a request for a timestamp token |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 464 | failed.", |
| 465 | )?; |
| 466 | } |
| 467 | Ok(AuthTokenHandler::Channel(op_receiver)) |
| 468 | } |
Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 469 | } |
| 470 | |
| 471 | impl Default for Enforcements { |
| 472 | fn default() -> Self { |
| 473 | Self::new() |
| 474 | } |
| 475 | } |
| 476 | |
Hasini Gunasinghe | f04d07a | 2020-11-25 22:41:35 +0000 | [diff] [blame] | 477 | impl Drop for Enforcements { |
| 478 | fn drop(&mut self) { |
| 479 | let sender_guard = self.sender_to_bth.lock().unwrap(); |
| 480 | if let Some(sender) = &*sender_guard { |
| 481 | let sender_cloned = sender.clone(); |
| 482 | drop(sender_guard); |
| 483 | // TODO: Verify how best to handle the error in this case. |
| 484 | sender_cloned.send(Message::Shutdown).unwrap_or_else(|e| { |
| 485 | panic!( |
| 486 | "Failed to send shutdown message to background task handler because of {:?}.", |
| 487 | e |
| 488 | ); |
| 489 | }); |
| 490 | } |
| 491 | } |
| 492 | } |
| 493 | |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 494 | // TODO: Add tests to enforcement module (b/175578618). |