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 | |
| 15 | //TODO: remove this after implementing the methods. |
| 16 | #![allow(dead_code)] |
| 17 | |
| 18 | //! This is the Keystore 2.0 Enforcements module. |
| 19 | // TODO: more description to follow. |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 20 | use crate::auth_token_handler::AuthTokenHandler; |
| 21 | use crate::database::AuthTokenEntry; |
| 22 | use crate::error::Error as KeystoreError; |
| 23 | use crate::key_parameter::{KeyParameter, KeyParameterValue}; |
| 24 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ |
| 25 | ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken, |
| 26 | HardwareAuthenticatorType::HardwareAuthenticatorType, |
| 27 | }; |
| 28 | use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge; |
| 29 | use anyhow::{Context, Result}; |
Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 30 | use std::collections::{HashMap, HashSet}; |
| 31 | use std::sync::Mutex; |
| 32 | |
| 33 | /// Enforcements data structure |
| 34 | pub struct Enforcements { |
| 35 | // This hash set contains the user ids for whom the device is currently unlocked. If a user id |
| 36 | // is not in the set, it implies that the device is locked for the user. |
| 37 | device_unlocked_set: Mutex<HashSet<i32>>, |
| 38 | // This maps the operation challenge to an optional auth token, to maintain op-auth tokens |
| 39 | // in-memory, until they are picked up and given to the operation by authorise_update_finish(). |
| 40 | op_auth_map: Mutex<HashMap<i64, Option<HardwareAuthToken>>>, |
| 41 | } |
| 42 | |
| 43 | impl Enforcements { |
| 44 | /// Creates an enforcement object with the two data structures it holds. |
| 45 | pub fn new() -> Self { |
| 46 | Enforcements { |
| 47 | device_unlocked_set: Mutex::new(HashSet::new()), |
| 48 | op_auth_map: Mutex::new(HashMap::new()), |
| 49 | } |
| 50 | } |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 51 | |
| 52 | /// Checks if update or finish calls are authorized. If the operation is based on per-op key, |
| 53 | /// try to receive the auth token from the op_auth_map. We assume that by the time update/finish |
| 54 | /// is called, the auth token has been delivered to keystore. Therefore, we do not wait for it |
| 55 | /// and if the auth token is not found in the map, an error is returned. |
| 56 | pub fn authorize_update_or_finish( |
| 57 | &self, |
| 58 | key_params: &[KeyParameter], |
| 59 | op_challenge: Option<OperationChallenge>, |
| 60 | ) -> Result<AuthTokenHandler> { |
| 61 | let mut user_auth_type: Option<HardwareAuthenticatorType> = None; |
| 62 | let mut user_secure_ids = Vec::<i64>::new(); |
| 63 | let mut is_timeout_key = false; |
| 64 | |
| 65 | for key_param in key_params.iter() { |
| 66 | match key_param.key_parameter_value() { |
| 67 | KeyParameterValue::NoAuthRequired => { |
| 68 | // unlike in authorize_create, we do not check if both NoAuthRequired and user |
| 69 | // secure id are present, because that is already checked in authorize_create. |
| 70 | return Ok(AuthTokenHandler::NoAuthRequired); |
| 71 | } |
| 72 | KeyParameterValue::AuthTimeout(_) => { |
| 73 | is_timeout_key = true; |
| 74 | } |
| 75 | KeyParameterValue::HardwareAuthenticatorType(a) => { |
| 76 | user_auth_type = Some(*a); |
| 77 | } |
| 78 | KeyParameterValue::UserSecureID(u) => { |
| 79 | user_secure_ids.push(*u); |
| 80 | } |
| 81 | _ => {} |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | // If either of auth_type or secure_id is present and the other is not present, |
| 86 | // authorize_create would have already returned error. |
| 87 | // At this point, if UserSecureID is present and AuthTimeout is not present in |
| 88 | // key parameters, per-op auth is required. |
| 89 | // Obtain and validate the auth token. |
| 90 | if !is_timeout_key && !user_secure_ids.is_empty() { |
| 91 | let challenge = |
| 92 | op_challenge.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context( |
| 93 | "In authorize_update_or_finish: Auth required, but operation challenge is not |
| 94 | present.", |
| 95 | )?; |
| 96 | let auth_type = |
| 97 | user_auth_type.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context( |
| 98 | "In authorize_update_or_finish: Auth required, but authenticator type is not |
| 99 | present.", |
| 100 | )?; |
| 101 | // It is ok to unwrap here, because there is no way this lock can get poisoned and |
| 102 | // and there is no way to recover if it is poisoned. |
| 103 | let mut op_auth_map_guard = self.op_auth_map.lock().unwrap(); |
| 104 | let auth_entry = op_auth_map_guard.remove(&(challenge.challenge)); |
| 105 | |
| 106 | match auth_entry { |
| 107 | Some(Some(auth_token)) => { |
| 108 | if AuthTokenEntry::satisfies_auth(&auth_token, &user_secure_ids, auth_type) { |
| 109 | return Ok(AuthTokenHandler::Token(auth_token, None)); |
| 110 | } else { |
| 111 | return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)) |
| 112 | .context("In authorize_update_or_finish: Auth token does not match."); |
| 113 | } |
| 114 | } |
| 115 | _ => { |
| 116 | // there was no auth token |
| 117 | return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context( |
| 118 | "In authorize_update_or_finish: Auth required, but an auth token |
| 119 | is not found for the given operation challenge, in the op_auth_map.", |
| 120 | ); |
| 121 | } |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | // If we don't find HardwareAuthenticatorType and UserSecureID, we assume that |
| 126 | // authentication is not required, because in legacy keys, authentication related |
| 127 | // key parameters may not present. |
| 128 | // TODO: METRICS: count how many times (if any) this code path is executed, in order |
| 129 | // to identify if any such keys are in use |
| 130 | Ok(AuthTokenHandler::NoAuthRequired) |
| 131 | } |
Hasini Gunasinghe | 3410f79 | 2020-09-14 17:55:21 +0000 | [diff] [blame] | 132 | } |
| 133 | |
| 134 | impl Default for Enforcements { |
| 135 | fn default() -> Self { |
| 136 | Self::new() |
| 137 | } |
| 138 | } |
| 139 | |
Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 140 | // TODO: Add tests to enforcement module (b/175578618). |