blob: 473686c1f48e04602d36aef0e403cb94b1f2202a [file] [log] [blame]
Hasini Gunasinghe3410f792020-09-14 17:55:21 +00001// 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 Gunasinghe52333ba2020-11-06 01:24:16 +000020use crate::auth_token_handler::AuthTokenHandler;
21use crate::database::AuthTokenEntry;
22use crate::error::Error as KeystoreError;
Hasini Gunasinghe557b1032020-11-10 01:35:30 +000023use crate::globals::DB;
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000024use crate::key_parameter::{KeyParameter, KeyParameterValue};
25use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Hasini Gunasinghe5112c702020-11-09 22:13:25 +000026 Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
27 HardwareAuthenticatorType::HardwareAuthenticatorType, KeyPurpose::KeyPurpose,
Hasini Gunasinghe557b1032020-11-10 01:35:30 +000028 SecurityLevel::SecurityLevel, Tag::Tag, Timestamp::Timestamp,
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000029};
30use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
31use anyhow::{Context, Result};
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000032use std::collections::{HashMap, HashSet};
33use std::sync::Mutex;
Hasini Gunasinghe5112c702020-11-09 22:13:25 +000034use std::time::SystemTime;
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000035
36/// Enforcements data structure
37pub struct Enforcements {
38 // This hash set contains the user ids for whom the device is currently unlocked. If a user id
39 // is not in the set, it implies that the device is locked for the user.
40 device_unlocked_set: Mutex<HashSet<i32>>,
41 // This maps the operation challenge to an optional auth token, to maintain op-auth tokens
42 // in-memory, until they are picked up and given to the operation by authorise_update_finish().
43 op_auth_map: Mutex<HashMap<i64, Option<HardwareAuthToken>>>,
44}
45
46impl Enforcements {
47 /// Creates an enforcement object with the two data structures it holds.
48 pub fn new() -> Self {
49 Enforcements {
50 device_unlocked_set: Mutex::new(HashSet::new()),
51 op_auth_map: Mutex::new(HashMap::new()),
52 }
53 }
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000054
55 /// Checks if update or finish calls are authorized. If the operation is based on per-op key,
56 /// try to receive the auth token from the op_auth_map. We assume that by the time update/finish
57 /// is called, the auth token has been delivered to keystore. Therefore, we do not wait for it
58 /// and if the auth token is not found in the map, an error is returned.
59 pub fn authorize_update_or_finish(
60 &self,
61 key_params: &[KeyParameter],
62 op_challenge: Option<OperationChallenge>,
63 ) -> Result<AuthTokenHandler> {
64 let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
65 let mut user_secure_ids = Vec::<i64>::new();
66 let mut is_timeout_key = false;
67
68 for key_param in key_params.iter() {
69 match key_param.key_parameter_value() {
70 KeyParameterValue::NoAuthRequired => {
71 // unlike in authorize_create, we do not check if both NoAuthRequired and user
72 // secure id are present, because that is already checked in authorize_create.
73 return Ok(AuthTokenHandler::NoAuthRequired);
74 }
75 KeyParameterValue::AuthTimeout(_) => {
76 is_timeout_key = true;
77 }
78 KeyParameterValue::HardwareAuthenticatorType(a) => {
79 user_auth_type = Some(*a);
80 }
81 KeyParameterValue::UserSecureID(u) => {
82 user_secure_ids.push(*u);
83 }
84 _ => {}
85 }
86 }
87
88 // If either of auth_type or secure_id is present and the other is not present,
89 // authorize_create would have already returned error.
90 // At this point, if UserSecureID is present and AuthTimeout is not present in
91 // key parameters, per-op auth is required.
92 // Obtain and validate the auth token.
93 if !is_timeout_key && !user_secure_ids.is_empty() {
94 let challenge =
95 op_challenge.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
96 "In authorize_update_or_finish: Auth required, but operation challenge is not
97 present.",
98 )?;
99 let auth_type =
100 user_auth_type.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
101 "In authorize_update_or_finish: Auth required, but authenticator type is not
102 present.",
103 )?;
104 // It is ok to unwrap here, because there is no way this lock can get poisoned and
105 // and there is no way to recover if it is poisoned.
106 let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
107 let auth_entry = op_auth_map_guard.remove(&(challenge.challenge));
108
109 match auth_entry {
110 Some(Some(auth_token)) => {
111 if AuthTokenEntry::satisfies_auth(&auth_token, &user_secure_ids, auth_type) {
112 return Ok(AuthTokenHandler::Token(auth_token, None));
113 } else {
114 return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
115 .context("In authorize_update_or_finish: Auth token does not match.");
116 }
117 }
118 _ => {
119 // there was no auth token
120 return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
121 "In authorize_update_or_finish: Auth required, but an auth token
122 is not found for the given operation challenge, in the op_auth_map.",
123 );
124 }
125 }
126 }
127
128 // If we don't find HardwareAuthenticatorType and UserSecureID, we assume that
129 // authentication is not required, because in legacy keys, authentication related
130 // key parameters may not present.
131 // TODO: METRICS: count how many times (if any) this code path is executed, in order
132 // to identify if any such keys are in use
133 Ok(AuthTokenHandler::NoAuthRequired)
134 }
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000135
136 /// Checks if a create call is authorized, given key parameters and operation parameters.
137 /// With regard to auth tokens, the following steps are taken:
138 /// If the key is time-bound, find a matching auth token from the database.
139 /// If the above step is successful, and if the security level is STRONGBOX, return a
140 /// VerificationRequired variant of the AuthTokenHandler with the found auth token to signal
141 /// the operation that it may need to obtain a verification token from TEE KeyMint.
142 /// If the security level is not STRONGBOX, return a Token variant of the AuthTokenHandler with
143 /// the found auth token to signal the operation that no more authorization required.
144 /// If the key is per-op, return an OpAuthRequired variant of the AuthTokenHandler to signal
145 /// create_operation() that it needs to add the operation challenge to the op_auth_map, once it
146 /// is received from the keymint, and that operation needs to be authorized before update/finish
147 /// is called.
148 pub fn authorize_create(
149 &self,
150 purpose: KeyPurpose,
151 key_params: &[KeyParameter],
152 op_params: &[KeyParameter],
153 // security_level will be used in the next CL
154 _security_level: SecurityLevel,
155 ) -> Result<AuthTokenHandler> {
156 match purpose {
157 // Allow SIGN, DECRYPT for both symmetric and asymmetric keys.
158 KeyPurpose::SIGN | KeyPurpose::DECRYPT => {}
159 // Rule out WRAP_KEY purpose
160 KeyPurpose::WRAP_KEY => {
161 return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE))
162 .context("In authorize_create: WRAP_KEY purpose is not allowed here.");
163 }
164 KeyPurpose::VERIFY | KeyPurpose::ENCRYPT => {
165 // We do not support ENCRYPT and VERIFY (the remaining two options of purpose) for
166 // asymmetric keys.
167 for kp in key_params.iter() {
168 match *kp.key_parameter_value() {
169 KeyParameterValue::Algorithm(Algorithm::RSA)
170 | KeyParameterValue::Algorithm(Algorithm::EC) => {
171 return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE)).context(
172 "In authorize_create: public operations on asymmetric keys are not
173 supported.",
174 );
175 }
176 _ => {}
177 }
178 }
179 }
180 _ => {
181 return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE))
182 .context("In authorize_create: specified purpose is not supported.");
183 }
184 }
185 // The following variables are to record information from key parameters to be used in
186 // enforcements, when two or more such pieces of information are required for enforcements.
187 // There is only one additional variable than what legacy keystore has, but this helps
188 // reduce the number of for loops on key parameters from 3 to 1, compared to legacy keystore
189 let mut key_purpose_authorized: bool = false;
190 let mut is_time_out_key: bool = false;
191 let mut auth_type: Option<HardwareAuthenticatorType> = None;
192 let mut no_auth_required: bool = false;
193 let mut caller_nonce_allowed = false;
194 let mut user_id: i32 = -1;
195 let mut user_secure_ids = Vec::<i64>::new();
196
197 // iterate through key parameters, recording information we need for authorization
198 // enforcements later, or enforcing authorizations in place, where applicable
199 for key_param in key_params.iter() {
200 match key_param.key_parameter_value() {
201 KeyParameterValue::NoAuthRequired => {
202 no_auth_required = true;
203 }
204 KeyParameterValue::AuthTimeout(_) => {
205 is_time_out_key = true;
206 }
207 KeyParameterValue::HardwareAuthenticatorType(a) => {
208 auth_type = Some(*a);
209 }
210 KeyParameterValue::KeyPurpose(p) => {
211 // Note: if there can be multiple KeyPurpose key parameters (TODO: confirm this),
212 // following check has the effect of key_params.contains(purpose)
213 // Also, authorizing purpose can not be completed here, if there can be multiple
214 // key parameters for KeyPurpose
215 if !key_purpose_authorized && *p == purpose {
216 key_purpose_authorized = true;
217 }
218 }
219 KeyParameterValue::CallerNonce => {
220 caller_nonce_allowed = true;
221 }
222 KeyParameterValue::ActiveDateTime(a) => {
223 if !Enforcements::is_given_time_passed(*a, true) {
224 return Err(KeystoreError::Km(Ec::KEY_NOT_YET_VALID))
225 .context("In authorize_create: key is not yet active.");
226 }
227 }
228 KeyParameterValue::OriginationExpireDateTime(o) => {
229 if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
230 && Enforcements::is_given_time_passed(*o, false)
231 {
232 return Err(KeystoreError::Km(Ec::KEY_EXPIRED))
233 .context("In authorize_create: key is expired.");
234 }
235 }
236 KeyParameterValue::UsageExpireDateTime(u) => {
237 if (purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY)
238 && Enforcements::is_given_time_passed(*u, false)
239 {
240 return Err(KeystoreError::Km(Ec::KEY_EXPIRED))
241 .context("In authorize_create: key is expired.");
242 }
243 }
244 KeyParameterValue::UserSecureID(s) => {
245 user_secure_ids.push(*s);
246 }
247 KeyParameterValue::UserID(u) => {
248 user_id = *u;
249 }
250 KeyParameterValue::UnlockedDeviceRequired => {
251 // check the device locked status. If locked, operations on the key are not
252 // allowed.
253 if self.is_device_locked(user_id) {
254 return Err(KeystoreError::Km(Ec::DEVICE_LOCKED))
255 .context("In authorize_create: device is locked.");
256 }
257 }
258 // NOTE: as per offline discussion, sanitizing key parameters and rejecting
259 // create operation if any non-allowed tags are present, is not done in
260 // authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
261 // a subset of non-allowed tags are present). Because santizing key parameters
262 // should have been done during generate/import key, by KeyMint.
263 _ => { /*Do nothing on all the other key parameters, as in legacy keystore*/ }
264 }
265 }
266
267 // authorize the purpose
268 if !key_purpose_authorized {
269 return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE))
270 .context("In authorize_create: the purpose is not authorized.");
271 }
272
273 // if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error
274 if !user_secure_ids.is_empty() && no_auth_required {
275 return Err(KeystoreError::Km(Ec::INVALID_KEY_BLOB)).context(
276 "In authorize_create: key has both NO_AUTH_REQUIRED
277 and USER_SECURE_ID tags.",
278 );
279 }
280
281 // if either of auth_type or secure_id is present and the other is not present, return error
282 if (auth_type.is_some() && user_secure_ids.is_empty())
283 || (auth_type.is_none() && !user_secure_ids.is_empty())
284 {
285 return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
286 "In authorize_create: Auth required, but either auth type or secure ids
287 are not present.",
288 );
289 }
290 // validate caller nonce for origination purposes
291 if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
292 && !caller_nonce_allowed
293 && op_params.iter().any(|kp| kp.get_tag() == Tag::NONCE)
294 {
295 return Err(KeystoreError::Km(Ec::CALLER_NONCE_PROHIBITED)).context(
296 "In authorize_create, NONCE is present,
297 although CALLER_NONCE is not present",
298 );
299 }
300
301 if !user_secure_ids.is_empty() {
302 // per op auth token
303 if !is_time_out_key {
304 return Ok(AuthTokenHandler::OpAuthRequired);
305 } else {
306 //time out token
307 // TODO: retrieve it from the database
308 // - in an upcoming CL
309 }
310 }
311
312 // If we reach here, all authorization enforcements have passed and no auth token required.
313 Ok(AuthTokenHandler::NoAuthRequired)
314 }
315
316 /// Checks if the time now since epoch is greater than (or equal, if is_given_time_inclusive is
317 /// set) the given time (in milliseconds)
318 fn is_given_time_passed(given_time: i64, is_given_time_inclusive: bool) -> bool {
319 let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
320
321 let time_since_epoch = match duration_since_epoch {
322 Ok(duration) => duration.as_millis(),
323 Err(_) => return false,
324 };
325
326 if is_given_time_inclusive {
327 time_since_epoch >= (given_time as u128)
328 } else {
329 time_since_epoch > (given_time as u128)
330 }
331 }
332
333 /// Check if the device is locked for the given user. If there's no entry yet for the user,
334 /// we assume that the device is locked
335 fn is_device_locked(&self, user_id: i32) -> bool {
336 // unwrap here because there's no way this mutex guard can be poisoned and
337 // because there's no way to recover, even if it is poisoned.
338 let set = self.device_unlocked_set.lock().unwrap();
339 !set.contains(&user_id)
340 }
Hasini Gunasinghe557b1032020-11-10 01:35:30 +0000341
342 /// Sets the device locked status for the user. This method is called externally.
343 pub fn set_device_locked(&self, user_id: i32, device_locked_status: bool) {
344 // unwrap here because there's no way this mutex guard can be poisoned and
345 // because there's no way to recover, even if it is poisoned.
346 let mut set = self.device_unlocked_set.lock().unwrap();
347 if device_locked_status {
348 set.remove(&user_id);
349 } else {
350 set.insert(user_id);
351 }
352 }
353
354 /// Add this auth token to the database.
355 /// Then check if there is an entry in the op_auth_map, indexed by the challenge of this
356 /// auth token (which could have been inserted during create_operation of an operation on a
357 /// per-op-auth key). If so, add a copy of this auth token to the map indexed by the
358 /// challenge.
359 pub fn add_auth_token(&self, auth_token: HardwareAuthToken) -> Result<()> {
360 //it is ok to unwrap here, because there is no way this lock can get poisoned and
361 //and there is no way to recover if it is poisoned.
362 let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
363
364 if op_auth_map_guard.contains_key(&auth_token.challenge) {
365 let auth_token_copy = HardwareAuthToken {
366 challenge: auth_token.challenge,
367 userId: auth_token.userId,
368 authenticatorId: auth_token.authenticatorId,
369 authenticatorType: HardwareAuthenticatorType(auth_token.authenticatorType.0),
370 timestamp: Timestamp { milliSeconds: auth_token.timestamp.milliSeconds },
371 mac: auth_token.mac.clone(),
372 };
373 op_auth_map_guard.insert(auth_token.challenge, Some(auth_token_copy));
374 }
375
376 DB.with(|db| db.borrow_mut().insert_auth_token(&auth_token))
377 .context("In add_auth_token.")?;
378 Ok(())
379 }
Hasini Gunasinghe3410f792020-09-14 17:55:21 +0000380}
381
382impl Default for Enforcements {
383 fn default() -> Self {
384 Self::new()
385 }
386}
387
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +0000388// TODO: Add tests to enforcement module (b/175578618).