blob: 1f82e577e3b4a93ac344186daa67f1a68390801c [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
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000015//! This is the Keystore 2.0 Enforcements module.
16// TODO: more description to follow.
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000017use crate::auth_token_handler::AuthTokenHandler;
18use crate::database::AuthTokenEntry;
19use crate::error::Error as KeystoreError;
Hasini Gunasinghe557b1032020-11-10 01:35:30 +000020use crate::globals::DB;
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000021use crate::key_parameter::{KeyParameter, KeyParameterValue};
22use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Hasini Gunasinghe5112c702020-11-09 22:13:25 +000023 Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
24 HardwareAuthenticatorType::HardwareAuthenticatorType, KeyPurpose::KeyPurpose,
Hasini Gunasinghe557b1032020-11-10 01:35:30 +000025 SecurityLevel::SecurityLevel, Tag::Tag, Timestamp::Timestamp,
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000026};
27use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
28use anyhow::{Context, Result};
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000029use std::collections::{HashMap, HashSet};
30use std::sync::Mutex;
Hasini Gunasinghe5112c702020-11-09 22:13:25 +000031use std::time::SystemTime;
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000032
33/// Enforcements data structure
34pub 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
43impl 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 Gunasinghe52333ba2020-11-06 01:24:16 +000051
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 Gunasinghe5112c702020-11-09 22:13:25 +0000132
133 /// Checks if a create call is authorized, given key parameters and operation parameters.
134 /// With regard to auth tokens, the following steps are taken:
135 /// If the key is time-bound, find a matching auth token from the database.
136 /// If the above step is successful, and if the security level is STRONGBOX, return a
137 /// VerificationRequired variant of the AuthTokenHandler with the found auth token to signal
138 /// the operation that it may need to obtain a verification token from TEE KeyMint.
139 /// If the security level is not STRONGBOX, return a Token variant of the AuthTokenHandler with
140 /// the found auth token to signal the operation that no more authorization required.
141 /// If the key is per-op, return an OpAuthRequired variant of the AuthTokenHandler to signal
142 /// create_operation() that it needs to add the operation challenge to the op_auth_map, once it
143 /// is received from the keymint, and that operation needs to be authorized before update/finish
144 /// is called.
145 pub fn authorize_create(
146 &self,
147 purpose: KeyPurpose,
148 key_params: &[KeyParameter],
149 op_params: &[KeyParameter],
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000150 security_level: SecurityLevel,
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000151 ) -> Result<AuthTokenHandler> {
152 match purpose {
153 // Allow SIGN, DECRYPT for both symmetric and asymmetric keys.
154 KeyPurpose::SIGN | KeyPurpose::DECRYPT => {}
155 // Rule out WRAP_KEY purpose
156 KeyPurpose::WRAP_KEY => {
157 return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE))
158 .context("In authorize_create: WRAP_KEY purpose is not allowed here.");
159 }
160 KeyPurpose::VERIFY | KeyPurpose::ENCRYPT => {
161 // We do not support ENCRYPT and VERIFY (the remaining two options of purpose) for
162 // asymmetric keys.
163 for kp in key_params.iter() {
164 match *kp.key_parameter_value() {
165 KeyParameterValue::Algorithm(Algorithm::RSA)
166 | KeyParameterValue::Algorithm(Algorithm::EC) => {
167 return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE)).context(
168 "In authorize_create: public operations on asymmetric keys are not
169 supported.",
170 );
171 }
172 _ => {}
173 }
174 }
175 }
176 _ => {
177 return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE))
178 .context("In authorize_create: specified purpose is not supported.");
179 }
180 }
181 // The following variables are to record information from key parameters to be used in
182 // enforcements, when two or more such pieces of information are required for enforcements.
183 // There is only one additional variable than what legacy keystore has, but this helps
184 // reduce the number of for loops on key parameters from 3 to 1, compared to legacy keystore
185 let mut key_purpose_authorized: bool = false;
186 let mut is_time_out_key: bool = false;
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000187 let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000188 let mut no_auth_required: bool = false;
189 let mut caller_nonce_allowed = false;
190 let mut user_id: i32 = -1;
191 let mut user_secure_ids = Vec::<i64>::new();
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000192 let mut key_time_out: Option<i64> = None;
193 let mut allow_while_on_body = false;
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000194
195 // iterate through key parameters, recording information we need for authorization
196 // enforcements later, or enforcing authorizations in place, where applicable
197 for key_param in key_params.iter() {
198 match key_param.key_parameter_value() {
199 KeyParameterValue::NoAuthRequired => {
200 no_auth_required = true;
201 }
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000202 KeyParameterValue::AuthTimeout(t) => {
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000203 is_time_out_key = true;
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000204 key_time_out = Some(*t as i64);
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000205 }
206 KeyParameterValue::HardwareAuthenticatorType(a) => {
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000207 user_auth_type = Some(*a);
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000208 }
209 KeyParameterValue::KeyPurpose(p) => {
210 // Note: if there can be multiple KeyPurpose key parameters (TODO: confirm this),
211 // following check has the effect of key_params.contains(purpose)
212 // Also, authorizing purpose can not be completed here, if there can be multiple
213 // key parameters for KeyPurpose
214 if !key_purpose_authorized && *p == purpose {
215 key_purpose_authorized = true;
216 }
217 }
218 KeyParameterValue::CallerNonce => {
219 caller_nonce_allowed = true;
220 }
221 KeyParameterValue::ActiveDateTime(a) => {
222 if !Enforcements::is_given_time_passed(*a, true) {
223 return Err(KeystoreError::Km(Ec::KEY_NOT_YET_VALID))
224 .context("In authorize_create: key is not yet active.");
225 }
226 }
227 KeyParameterValue::OriginationExpireDateTime(o) => {
228 if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
229 && Enforcements::is_given_time_passed(*o, false)
230 {
231 return Err(KeystoreError::Km(Ec::KEY_EXPIRED))
232 .context("In authorize_create: key is expired.");
233 }
234 }
235 KeyParameterValue::UsageExpireDateTime(u) => {
236 if (purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY)
237 && Enforcements::is_given_time_passed(*u, false)
238 {
239 return Err(KeystoreError::Km(Ec::KEY_EXPIRED))
240 .context("In authorize_create: key is expired.");
241 }
242 }
243 KeyParameterValue::UserSecureID(s) => {
244 user_secure_ids.push(*s);
245 }
246 KeyParameterValue::UserID(u) => {
247 user_id = *u;
248 }
249 KeyParameterValue::UnlockedDeviceRequired => {
250 // check the device locked status. If locked, operations on the key are not
251 // allowed.
252 if self.is_device_locked(user_id) {
253 return Err(KeystoreError::Km(Ec::DEVICE_LOCKED))
254 .context("In authorize_create: device is locked.");
255 }
256 }
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000257 KeyParameterValue::AllowWhileOnBody => {
258 allow_while_on_body = true;
259 }
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000260 // NOTE: as per offline discussion, sanitizing key parameters and rejecting
261 // create operation if any non-allowed tags are present, is not done in
262 // authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
263 // a subset of non-allowed tags are present). Because santizing key parameters
264 // should have been done during generate/import key, by KeyMint.
265 _ => { /*Do nothing on all the other key parameters, as in legacy keystore*/ }
266 }
267 }
268
269 // authorize the purpose
270 if !key_purpose_authorized {
271 return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE))
272 .context("In authorize_create: the purpose is not authorized.");
273 }
274
275 // if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error
276 if !user_secure_ids.is_empty() && no_auth_required {
277 return Err(KeystoreError::Km(Ec::INVALID_KEY_BLOB)).context(
278 "In authorize_create: key has both NO_AUTH_REQUIRED
279 and USER_SECURE_ID tags.",
280 );
281 }
282
283 // if either of auth_type or secure_id is present and the other is not present, return error
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000284 if (user_auth_type.is_some() && user_secure_ids.is_empty())
285 || (user_auth_type.is_none() && !user_secure_ids.is_empty())
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000286 {
287 return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
288 "In authorize_create: Auth required, but either auth type or secure ids
289 are not present.",
290 );
291 }
292 // validate caller nonce for origination purposes
293 if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
294 && !caller_nonce_allowed
295 && op_params.iter().any(|kp| kp.get_tag() == Tag::NONCE)
296 {
297 return Err(KeystoreError::Km(Ec::CALLER_NONCE_PROHIBITED)).context(
298 "In authorize_create, NONCE is present,
299 although CALLER_NONCE is not present",
300 );
301 }
302
303 if !user_secure_ids.is_empty() {
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000304 // key requiring authentication per operation
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000305 if !is_time_out_key {
306 return Ok(AuthTokenHandler::OpAuthRequired);
307 } else {
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000308 // key requiring time-out based authentication
309 let auth_token = DB
310 .with::<_, Result<HardwareAuthToken>>(|db| {
311 let mut db = db.borrow_mut();
312 match (user_auth_type, key_time_out) {
313 (Some(auth_type), Some(key_time_out)) => {
314 let matching_entry = db
315 .find_timed_auth_token_entry(
316 &user_secure_ids,
317 auth_type,
318 key_time_out,
319 allow_while_on_body,
320 )
321 .context("Failed to find timed auth token.")?;
322 Ok(matching_entry.get_auth_token())
323 }
324 (_, _) => Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
325 .context("Authenticator type and/or key time out is not given."),
326 }
327 })
328 .context("In authorize_create.")?;
329
330 if security_level == SecurityLevel::STRONGBOX {
331 return Ok(AuthTokenHandler::VerificationRequired(auth_token));
332 } else {
333 return Ok(AuthTokenHandler::Token(auth_token, None));
334 }
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000335 }
336 }
337
338 // If we reach here, all authorization enforcements have passed and no auth token required.
339 Ok(AuthTokenHandler::NoAuthRequired)
340 }
341
342 /// Checks if the time now since epoch is greater than (or equal, if is_given_time_inclusive is
343 /// set) the given time (in milliseconds)
344 fn is_given_time_passed(given_time: i64, is_given_time_inclusive: bool) -> bool {
345 let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
346
347 let time_since_epoch = match duration_since_epoch {
348 Ok(duration) => duration.as_millis(),
349 Err(_) => return false,
350 };
351
352 if is_given_time_inclusive {
353 time_since_epoch >= (given_time as u128)
354 } else {
355 time_since_epoch > (given_time as u128)
356 }
357 }
358
359 /// Check if the device is locked for the given user. If there's no entry yet for the user,
360 /// we assume that the device is locked
361 fn is_device_locked(&self, user_id: i32) -> bool {
362 // unwrap here because there's no way this mutex guard can be poisoned and
363 // because there's no way to recover, even if it is poisoned.
364 let set = self.device_unlocked_set.lock().unwrap();
365 !set.contains(&user_id)
366 }
Hasini Gunasinghe557b1032020-11-10 01:35:30 +0000367
368 /// Sets the device locked status for the user. This method is called externally.
369 pub fn set_device_locked(&self, user_id: i32, device_locked_status: bool) {
370 // unwrap here because there's no way this mutex guard can be poisoned and
371 // because there's no way to recover, even if it is poisoned.
372 let mut set = self.device_unlocked_set.lock().unwrap();
373 if device_locked_status {
374 set.remove(&user_id);
375 } else {
376 set.insert(user_id);
377 }
378 }
379
380 /// Add this auth token to the database.
381 /// Then check if there is an entry in the op_auth_map, indexed by the challenge of this
382 /// auth token (which could have been inserted during create_operation of an operation on a
383 /// per-op-auth key). If so, add a copy of this auth token to the map indexed by the
384 /// challenge.
385 pub fn add_auth_token(&self, auth_token: HardwareAuthToken) -> Result<()> {
386 //it is ok to unwrap here, because there is no way this lock can get poisoned and
387 //and there is no way to recover if it is poisoned.
388 let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
389
390 if op_auth_map_guard.contains_key(&auth_token.challenge) {
391 let auth_token_copy = HardwareAuthToken {
392 challenge: auth_token.challenge,
393 userId: auth_token.userId,
394 authenticatorId: auth_token.authenticatorId,
395 authenticatorType: HardwareAuthenticatorType(auth_token.authenticatorType.0),
396 timestamp: Timestamp { milliSeconds: auth_token.timestamp.milliSeconds },
397 mac: auth_token.mac.clone(),
398 };
399 op_auth_map_guard.insert(auth_token.challenge, Some(auth_token_copy));
400 }
401
402 DB.with(|db| db.borrow_mut().insert_auth_token(&auth_token))
403 .context("In add_auth_token.")?;
404 Ok(())
405 }
Hasini Gunasinghe3410f792020-09-14 17:55:21 +0000406}
407
408impl Default for Enforcements {
409 fn default() -> Self {
410 Self::new()
411 }
412}
413
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +0000414// TODO: Add tests to enforcement module (b/175578618).