blob: dfd89b5693794c33b080329b2adb10c75d7732d5 [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;
23use crate::key_parameter::{KeyParameter, KeyParameterValue};
24use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
25 ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
26 HardwareAuthenticatorType::HardwareAuthenticatorType,
27};
28use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
29use anyhow::{Context, Result};
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000030use std::collections::{HashMap, HashSet};
31use std::sync::Mutex;
32
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 Gunasinghe3410f792020-09-14 17:55:21 +0000132}
133
134impl Default for Enforcements {
135 fn default() -> Self {
136 Self::new()
137 }
138}
139
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +0000140// TODO: Add tests to enforcement module (b/175578618).