blob: 58f39859680338cdd8f405824328520d5a706c0b [file] [log] [blame]
Janis Danisevskis1af91262020-08-10 14:58:08 -07001// 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//! This crate implements the `IKeystoreOperation` AIDL interface, which represents
16//! an ongoing key operation, as well as the operation database, which is mainly
17//! required for tracking operations for the purpose of pruning.
18//! This crate also implements an operation pruning strategy.
19//!
20//! Operations implement the API calls update, finish, and abort.
21//! Additionally, an operation can be dropped and pruned. The former
22//! happens if the client deletes a binder to the operation object.
23//! An existing operation may get pruned when running out of operation
24//! slots and a new operation takes precedence.
25//!
26//! ## Operation Lifecycle
27//! An operation gets created when the client calls `IKeystoreSecurityLevel::create`.
28//! It may receive zero or more update request. The lifecycle ends when:
29//! * `update` yields an error.
30//! * `finish` is called.
31//! * `abort` is called.
32//! * The operation gets dropped.
33//! * The operation gets pruned.
34//! `Operation` has an `Outcome` member. While the outcome is `Outcome::Unknown`,
35//! the operation is active and in a good state. Any of the above conditions may
36//! change the outcome to one of the defined outcomes Success, Abort, Dropped,
37//! Pruned, or ErrorCode. The latter is chosen in the case of an unexpected error, during
38//! `update` or `finish`. `Success` is chosen iff `finish` completes without error.
39//! Note that all operations get dropped eventually in the sense that they lose
40//! their last reference and get destroyed. At that point, the fate of the operation
41//! gets logged. However, an operation will transition to `Outcome::Dropped` iff
42//! the operation was still active (`Outcome::Unknown`) at that time.
43//!
44//! ## Operation Dropping
45//! To observe the dropping of an operation, we have to make sure that there
46//! are no strong references to the IBinder representing this operation.
47//! This would be simple enough if the operation object would need to be accessed
48//! only by transactions. But to perform pruning, we have to retain a reference to the
49//! original operation object.
50//!
51//! ## Operation Pruning
52//! Pruning an operation happens during the creation of a new operation.
53//! We have to iterate through the operation database to find a suitable
54//! candidate. Then we abort and finalize this operation setting its outcome to
55//! `Outcome::Pruned`. The corresponding KeyMint operation slot will have been freed
56//! up at this point, but the `Operation` object lingers. When the client
57//! attempts to use the operation again they will receive
58//! ErrorCode::INVALID_OPERATION_HANDLE indicating that the operation no longer
59//! exits. This should be the cue for the client to destroy its binder.
60//! At that point the operation gets dropped.
61//!
62//! ## Architecture
63//! The `IKeystoreOperation` trait is implemented by `KeystoreOperation`.
64//! This acts as a proxy object holding a strong reference to actual operation
65//! implementation `Operation`.
66//!
67//! ```
68//! struct KeystoreOperation {
69//! operation: Mutex<Option<Arc<Operation>>>,
70//! }
71//! ```
72//!
73//! The `Mutex` serves two purposes. It provides interior mutability allowing
74//! us to set the Option to None. We do this when the life cycle ends during
75//! a call to `update`, `finish`, or `abort`. As a result most of the Operation
76//! related resources are freed. The `KeystoreOperation` proxy object still
77//! lingers until dropped by the client.
78//! The second purpose is to protect operations against concurrent usage.
79//! Failing to lock this mutex yields `ResponseCode::OPERATION_BUSY` and indicates
80//! a programming error in the client.
81//!
82//! Note that the Mutex only protects the operation against concurrent client calls.
83//! We still retain weak references to the operation in the operation database:
84//!
85//! ```
86//! struct OperationDb {
87//! operations: Mutex<Vec<Weak<Operation>>>
88//! }
89//! ```
90//!
91//! This allows us to access the operations for the purpose of pruning.
92//! We do this in three phases.
93//! 1. We gather the pruning information. Besides non mutable information,
94//! we access `last_usage` which is protected by a mutex.
95//! We only lock this mutex for single statements at a time. During
96//! this phase we hold the operation db lock.
97//! 2. We choose a pruning candidate by computing the pruning resistance
98//! of each operation. We do this entirely with information we now
99//! have on the stack without holding any locks.
100//! (See `OperationDb::prune` for more details on the pruning strategy.)
101//! 3. During pruning we briefly lock the operation database again to get the
102//! the pruning candidate by index. We then attempt to abort the candidate.
103//! If the candidate was touched in the meantime or is currently fulfilling
104//! a request (i.e., the client calls update, finish, or abort),
105//! we go back to 1 and try again.
106//!
107//! So the outer Mutex in `KeystoreOperation::operation` only protects
108//! operations against concurrent client calls but not against concurrent
109//! pruning attempts. This is what the `Operation::outcome` mutex is used for.
110//!
111//! ```
112//! struct Operation {
113//! ...
114//! outcome: Mutex<Outcome>,
115//! ...
116//! }
117//! ```
118//!
119//! Any request that can change the outcome, i.e., `update`, `finish`, `abort`,
120//! `drop`, and `prune` has to take the outcome lock and check if the outcome
121//! is still `Outcome::Unknown` before entering. `prune` is special in that
122//! it will `try_lock`, because we don't want to be blocked on a potentially
123//! long running request at another operation. If it fails to get the lock
124//! the operation is either being touched, which changes its pruning resistance,
125//! or it transitions to its end-of-life, which means we may get a free slot.
126//! Either way, we have to revaluate the pruning scores.
127
Janis Danisevskis5ed8c532021-01-11 14:19:42 -0800128use crate::enforcements::AuthInfo;
Janis Danisevskis778245c2021-03-04 15:40:23 -0800129use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000130use crate::ks_err;
Hasini Gunasinghe15891e62021-06-10 16:23:27 +0000131use crate::metrics_store::log_key_operation_event_stats;
Janis Danisevskis5f3a0572021-06-18 11:26:42 -0700132use crate::utils::watchdog as wd;
Hasini Gunasinghe888dd352020-11-17 23:08:39 +0000133use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000134 IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
Hasini Gunasinghe9617fd92021-04-01 22:27:07 +0000135 SecurityLevel::SecurityLevel,
Hasini Gunasinghe888dd352020-11-17 23:08:39 +0000136};
Janis Danisevskis5f3a0572021-06-18 11:26:42 -0700137use android_hardware_security_keymint::binder::{BinderFeatures, Strong};
Hasini Gunasinghe888dd352020-11-17 23:08:39 +0000138use android_system_keystore2::aidl::android::system::keystore2::{
139 IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
Hasini Gunasinghe888dd352020-11-17 23:08:39 +0000140};
141use anyhow::{anyhow, Context, Result};
Janis Danisevskis1af91262020-08-10 14:58:08 -0700142use std::{
143 collections::HashMap,
144 sync::{Arc, Mutex, MutexGuard, Weak},
145 time::Duration,
146 time::Instant,
147};
148
Janis Danisevskis1af91262020-08-10 14:58:08 -0700149/// Operations have `Outcome::Unknown` as long as they are active. They transition
150/// to one of the other variants exactly once. The distinction in outcome is mainly
151/// for the statistic.
152#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000153pub enum Outcome {
154 /// Operations have `Outcome::Unknown` as long as they are active.
Janis Danisevskis1af91262020-08-10 14:58:08 -0700155 Unknown,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000156 /// Operation is successful.
Janis Danisevskis1af91262020-08-10 14:58:08 -0700157 Success,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000158 /// Operation is aborted.
Janis Danisevskis1af91262020-08-10 14:58:08 -0700159 Abort,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000160 /// Operation is dropped.
Janis Danisevskis1af91262020-08-10 14:58:08 -0700161 Dropped,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000162 /// Operation is pruned.
Janis Danisevskis1af91262020-08-10 14:58:08 -0700163 Pruned,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000164 /// Operation is failed with the error code.
Janis Danisevskis1af91262020-08-10 14:58:08 -0700165 ErrorCode(ErrorCode),
166}
167
168/// Operation bundles all of the operation related resources and tracks the operation's
169/// outcome.
170#[derive(Debug)]
171pub struct Operation {
172 // The index of this operation in the OperationDb.
173 index: usize,
Janis Danisevskis5f3a0572021-06-18 11:26:42 -0700174 km_op: Strong<dyn IKeyMintOperation>,
Janis Danisevskis1af91262020-08-10 14:58:08 -0700175 last_usage: Mutex<Instant>,
176 outcome: Mutex<Outcome>,
177 owner: u32, // Uid of the operation's owner.
Janis Danisevskis5ed8c532021-01-11 14:19:42 -0800178 auth_info: Mutex<AuthInfo>,
Janis Danisevskis186d9f42021-03-03 14:40:52 -0800179 forced: bool,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000180 logging_info: LoggingInfo,
181}
182
183/// Keeps track of the information required for logging operations.
184#[derive(Debug)]
185pub struct LoggingInfo {
Hasini Gunasinghe9617fd92021-04-01 22:27:07 +0000186 sec_level: SecurityLevel,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000187 purpose: KeyPurpose,
188 op_params: Vec<KeyParameter>,
189 key_upgraded: bool,
190}
191
192impl LoggingInfo {
193 /// Constructor
194 pub fn new(
Hasini Gunasinghe9617fd92021-04-01 22:27:07 +0000195 sec_level: SecurityLevel,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000196 purpose: KeyPurpose,
197 op_params: Vec<KeyParameter>,
198 key_upgraded: bool,
199 ) -> LoggingInfo {
Hasini Gunasinghe9617fd92021-04-01 22:27:07 +0000200 Self { sec_level, purpose, op_params, key_upgraded }
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000201 }
Janis Danisevskis1af91262020-08-10 14:58:08 -0700202}
203
204struct PruningInfo {
205 last_usage: Instant,
206 owner: u32,
207 index: usize,
Janis Danisevskis186d9f42021-03-03 14:40:52 -0800208 forced: bool,
Janis Danisevskis1af91262020-08-10 14:58:08 -0700209}
210
Janis Danisevskis1af91262020-08-10 14:58:08 -0700211// We don't except more than 32KiB of data in `update`, `updateAad`, and `finish`.
212const MAX_RECEIVE_DATA: usize = 0x8000;
213
214impl Operation {
215 /// Constructor
Hasini Gunasinghe888dd352020-11-17 23:08:39 +0000216 pub fn new(
217 index: usize,
Stephen Crane221bbb52020-12-16 15:52:10 -0800218 km_op: binder::Strong<dyn IKeyMintOperation>,
Hasini Gunasinghe888dd352020-11-17 23:08:39 +0000219 owner: u32,
Janis Danisevskis5ed8c532021-01-11 14:19:42 -0800220 auth_info: AuthInfo,
Janis Danisevskis186d9f42021-03-03 14:40:52 -0800221 forced: bool,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000222 logging_info: LoggingInfo,
Hasini Gunasinghe888dd352020-11-17 23:08:39 +0000223 ) -> Self {
Janis Danisevskis1af91262020-08-10 14:58:08 -0700224 Self {
225 index,
Janis Danisevskis5f3a0572021-06-18 11:26:42 -0700226 km_op,
Janis Danisevskis1af91262020-08-10 14:58:08 -0700227 last_usage: Mutex::new(Instant::now()),
228 outcome: Mutex::new(Outcome::Unknown),
229 owner,
Janis Danisevskis5ed8c532021-01-11 14:19:42 -0800230 auth_info: Mutex::new(auth_info),
Janis Danisevskis186d9f42021-03-03 14:40:52 -0800231 forced,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000232 logging_info,
Janis Danisevskis1af91262020-08-10 14:58:08 -0700233 }
234 }
235
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700236 fn get_pruning_info(&self) -> Option<PruningInfo> {
237 // An operation may be finalized.
238 if let Ok(guard) = self.outcome.try_lock() {
239 match *guard {
240 Outcome::Unknown => {}
241 // If the outcome is any other than unknown, it has been finalized,
242 // and we can no longer consider it for pruning.
243 _ => return None,
244 }
245 }
246 // Else: If we could not grab the lock, this means that the operation is currently
247 // being used and it may be transitioning to finalized or it was simply updated.
248 // In any case it is fair game to consider it for pruning. If the operation
249 // transitioned to a final state, we will notice when we attempt to prune, and
250 // a subsequent attempt to create a new operation will succeed.
251 Some(PruningInfo {
252 // Expect safety:
253 // `last_usage` is locked only for primitive single line statements.
254 // There is no chance to panic and poison the mutex.
Janis Danisevskis1af91262020-08-10 14:58:08 -0700255 last_usage: *self.last_usage.lock().expect("In get_pruning_info."),
256 owner: self.owner,
257 index: self.index,
Janis Danisevskis186d9f42021-03-03 14:40:52 -0800258 forced: self.forced,
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700259 })
Janis Danisevskis1af91262020-08-10 14:58:08 -0700260 }
261
262 fn prune(&self, last_usage: Instant) -> Result<(), Error> {
263 let mut locked_outcome = match self.outcome.try_lock() {
264 Ok(guard) => match *guard {
265 Outcome::Unknown => guard,
266 _ => return Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)),
267 },
268 Err(_) => return Err(Error::Rc(ResponseCode::OPERATION_BUSY)),
269 };
270
271 // In `OperationDb::prune`, which is our caller, we first gather the pruning
272 // information including the last usage. When we select a candidate
273 // we call `prune` on that candidate passing the last_usage
274 // that we gathered earlier. If the actual last usage
275 // has changed since than, it means the operation was busy in the
276 // meantime, which means that we have to reevaluate the pruning score.
277 //
278 // Expect safety:
279 // `last_usage` is locked only for primitive single line statements.
280 // There is no chance to panic and poison the mutex.
281 if *self.last_usage.lock().expect("In Operation::prune()") != last_usage {
282 return Err(Error::Rc(ResponseCode::OPERATION_BUSY));
283 }
284 *locked_outcome = Outcome::Pruned;
285
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700286 let _wp = wd::watch_millis("In Operation::prune: calling abort()", 500);
287
Janis Danisevskis1af91262020-08-10 14:58:08 -0700288 // We abort the operation. If there was an error we log it but ignore it.
Janis Danisevskis5f3a0572021-06-18 11:26:42 -0700289 if let Err(e) = map_km_error(self.km_op.abort()) {
Janis Danisevskis1af91262020-08-10 14:58:08 -0700290 log::error!("In prune: KeyMint::abort failed with {:?}.", e);
291 }
292
293 Ok(())
294 }
295
296 // This function takes a Result from a KeyMint call and inspects it for errors.
297 // If an error was found it updates the given `locked_outcome` accordingly.
298 // It forwards the Result unmodified.
299 // The precondition to this call must be *locked_outcome == Outcome::Unknown.
300 // Ideally the `locked_outcome` came from a successful call to `check_active`
301 // see below.
302 fn update_outcome<T>(
303 &self,
304 locked_outcome: &mut Outcome,
305 err: Result<T, Error>,
306 ) -> Result<T, Error> {
307 match &err {
308 Err(Error::Km(e)) => *locked_outcome = Outcome::ErrorCode(*e),
309 Err(_) => *locked_outcome = Outcome::ErrorCode(ErrorCode::UNKNOWN_ERROR),
310 Ok(_) => (),
311 }
312 err
313 }
314
315 // This function grabs the outcome lock and checks the current outcome state.
316 // If the outcome is still `Outcome::Unknown`, this function returns
317 // the locked outcome for further updates. In any other case it returns
318 // ErrorCode::INVALID_OPERATION_HANDLE indicating that this operation has
319 // been finalized and is no longer active.
320 fn check_active(&self) -> Result<MutexGuard<Outcome>> {
321 let guard = self.outcome.lock().expect("In check_active.");
322 match *guard {
323 Outcome::Unknown => Ok(guard),
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000324 _ => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE))
325 .context(ks_err!("Call on finalized operation with outcome: {:?}.", *guard)),
Janis Danisevskis1af91262020-08-10 14:58:08 -0700326 }
327 }
328
329 // This function checks the amount of input data sent to us. We reject any buffer
330 // exceeding MAX_RECEIVE_DATA bytes as input to `update`, `update_aad`, and `finish`
331 // in order to force clients into using reasonable limits.
332 fn check_input_length(data: &[u8]) -> Result<()> {
333 if data.len() > MAX_RECEIVE_DATA {
334 // This error code is unique, no context required here.
335 return Err(anyhow!(Error::Rc(ResponseCode::TOO_MUCH_DATA)));
336 }
337 Ok(())
338 }
339
340 // Update the last usage to now.
341 fn touch(&self) {
342 // Expect safety:
343 // `last_usage` is locked only for primitive single line statements.
344 // There is no chance to panic and poison the mutex.
345 *self.last_usage.lock().expect("In touch.") = Instant::now();
346 }
347
348 /// Implementation of `IKeystoreOperation::updateAad`.
349 /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
350 fn update_aad(&self, aad_input: &[u8]) -> Result<()> {
351 let mut outcome = self.check_active().context("In update_aad")?;
352 Self::check_input_length(aad_input).context("In update_aad")?;
353 self.touch();
354
Janis Danisevskis5ed8c532021-01-11 14:19:42 -0800355 let (hat, tst) = self
356 .auth_info
357 .lock()
358 .unwrap()
Qi Wub9433b52020-12-01 14:52:46 +0800359 .before_update()
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000360 .context(ks_err!("Trying to get auth tokens."))?;
Janis Danisevskis5ed8c532021-01-11 14:19:42 -0800361
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700362 self.update_outcome(&mut *outcome, {
363 let _wp = wd::watch_millis("Operation::update_aad: calling updateAad", 500);
Janis Danisevskis5f3a0572021-06-18 11:26:42 -0700364 map_km_error(self.km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref()))
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700365 })
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000366 .context(ks_err!("Update failed."))?;
Janis Danisevskis1af91262020-08-10 14:58:08 -0700367
368 Ok(())
369 }
370
371 /// Implementation of `IKeystoreOperation::update`.
372 /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
373 fn update(&self, input: &[u8]) -> Result<Option<Vec<u8>>> {
374 let mut outcome = self.check_active().context("In update")?;
375 Self::check_input_length(input).context("In update")?;
376 self.touch();
377
Janis Danisevskis5ed8c532021-01-11 14:19:42 -0800378 let (hat, tst) = self
379 .auth_info
380 .lock()
381 .unwrap()
Qi Wub9433b52020-12-01 14:52:46 +0800382 .before_update()
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000383 .context(ks_err!("Trying to get auth tokens."))?;
Hasini Gunasinghe888dd352020-11-17 23:08:39 +0000384
Shawn Willden44cc03d2021-02-19 10:53:49 -0700385 let output = self
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700386 .update_outcome(&mut *outcome, {
387 let _wp = wd::watch_millis("Operation::update: calling update", 500);
Janis Danisevskis5f3a0572021-06-18 11:26:42 -0700388 map_km_error(self.km_op.update(input, hat.as_ref(), tst.as_ref()))
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700389 })
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000390 .context(ks_err!("Update failed."))?;
Janis Danisevskis1af91262020-08-10 14:58:08 -0700391
Shawn Willden44cc03d2021-02-19 10:53:49 -0700392 if output.is_empty() {
393 Ok(None)
394 } else {
395 Ok(Some(output))
Janis Danisevskis1af91262020-08-10 14:58:08 -0700396 }
397 }
398
399 /// Implementation of `IKeystoreOperation::finish`.
400 /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
401 fn finish(&self, input: Option<&[u8]>, signature: Option<&[u8]>) -> Result<Option<Vec<u8>>> {
402 let mut outcome = self.check_active().context("In finish")?;
403 if let Some(input) = input {
404 Self::check_input_length(input).context("In finish")?;
405 }
406 self.touch();
Janis Danisevskis1af91262020-08-10 14:58:08 -0700407
Janis Danisevskisb1673db2021-02-08 18:11:57 -0800408 let (hat, tst, confirmation_token) = self
Janis Danisevskis5ed8c532021-01-11 14:19:42 -0800409 .auth_info
410 .lock()
411 .unwrap()
Qi Wub9433b52020-12-01 14:52:46 +0800412 .before_finish()
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000413 .context(ks_err!("Trying to get auth tokens."))?;
Hasini Gunasinghe888dd352020-11-17 23:08:39 +0000414
Janis Danisevskis85d47932020-10-23 16:12:59 -0700415 let output = self
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700416 .update_outcome(&mut *outcome, {
417 let _wp = wd::watch_millis("Operation::finish: calling finish", 500);
Janis Danisevskis5f3a0572021-06-18 11:26:42 -0700418 map_km_error(self.km_op.finish(
Janis Danisevskis85d47932020-10-23 16:12:59 -0700419 input,
420 signature,
Janis Danisevskis5ed8c532021-01-11 14:19:42 -0800421 hat.as_ref(),
422 tst.as_ref(),
Shawn Willden44cc03d2021-02-19 10:53:49 -0700423 confirmation_token.as_deref(),
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700424 ))
425 })
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000426 .context(ks_err!("Finish failed."))?;
Janis Danisevskis1af91262020-08-10 14:58:08 -0700427
Qi Wub9433b52020-12-01 14:52:46 +0800428 self.auth_info.lock().unwrap().after_finish().context("In finish.")?;
429
Janis Danisevskis1af91262020-08-10 14:58:08 -0700430 // At this point the operation concluded successfully.
431 *outcome = Outcome::Success;
432
433 if output.is_empty() {
434 Ok(None)
435 } else {
436 Ok(Some(output))
437 }
438 }
439
440 /// Aborts the operation if it is active. IFF the operation is aborted the outcome is
441 /// set to `outcome`. `outcome` must reflect the reason for the abort. Since the operation
442 /// gets aborted `outcome` must not be `Operation::Success` or `Operation::Unknown`.
443 fn abort(&self, outcome: Outcome) -> Result<()> {
444 let mut locked_outcome = self.check_active().context("In abort")?;
445 *locked_outcome = outcome;
Janis Danisevskis1af91262020-08-10 14:58:08 -0700446
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700447 {
448 let _wp = wd::watch_millis("Operation::abort: calling abort", 500);
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000449 map_km_error(self.km_op.abort()).context(ks_err!("KeyMint::abort failed."))
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700450 }
Janis Danisevskis1af91262020-08-10 14:58:08 -0700451 }
452}
453
454impl Drop for Operation {
455 fn drop(&mut self) {
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000456 let guard = self.outcome.lock().expect("In drop.");
457 log_key_operation_event_stats(
Hasini Gunasinghe9617fd92021-04-01 22:27:07 +0000458 self.logging_info.sec_level,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000459 self.logging_info.purpose,
460 &(self.logging_info.op_params),
461 &guard,
462 self.logging_info.key_upgraded,
463 );
464 if let Outcome::Unknown = *guard {
465 drop(guard);
Janis Danisevskis1af91262020-08-10 14:58:08 -0700466 // If the operation was still active we call abort, setting
467 // the outcome to `Outcome::Dropped`
468 if let Err(e) = self.abort(Outcome::Dropped) {
469 log::error!("While dropping Operation: abort failed:\n {:?}", e);
470 }
471 }
472 }
473}
474
475/// The OperationDb holds weak references to all ongoing operations.
476/// Its main purpose is to facilitate operation pruning.
477#[derive(Debug, Default)]
478pub struct OperationDb {
479 // TODO replace Vec with WeakTable when the weak_table crate becomes
480 // available.
481 operations: Mutex<Vec<Weak<Operation>>>,
482}
483
484impl OperationDb {
485 /// Creates a new OperationDb.
486 pub fn new() -> Self {
487 Self { operations: Mutex::new(Vec::new()) }
488 }
489
490 /// Creates a new operation.
491 /// This function takes a KeyMint operation and an associated
492 /// owner uid and returns a new Operation wrapped in a `std::sync::Arc`.
493 pub fn create_operation(
494 &self,
Stephen Crane23cf7242022-01-19 17:49:46 +0000495 km_op: binder::Strong<dyn IKeyMintOperation>,
Janis Danisevskis1af91262020-08-10 14:58:08 -0700496 owner: u32,
Janis Danisevskis5ed8c532021-01-11 14:19:42 -0800497 auth_info: AuthInfo,
Janis Danisevskis186d9f42021-03-03 14:40:52 -0800498 forced: bool,
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000499 logging_info: LoggingInfo,
Janis Danisevskis1af91262020-08-10 14:58:08 -0700500 ) -> Arc<Operation> {
501 // We use unwrap because we don't allow code that can panic while locked.
502 let mut operations = self.operations.lock().expect("In create_operation.");
503
504 let mut index: usize = 0;
505 // First we iterate through the operation slots to try and find an unused
506 // slot. If we don't find one, we append the new entry instead.
507 match (*operations).iter_mut().find(|s| {
508 index += 1;
509 s.upgrade().is_none()
510 }) {
511 Some(free_slot) => {
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000512 let new_op = Arc::new(Operation::new(
513 index - 1,
514 km_op,
515 owner,
516 auth_info,
517 forced,
518 logging_info,
519 ));
Janis Danisevskis1af91262020-08-10 14:58:08 -0700520 *free_slot = Arc::downgrade(&new_op);
521 new_op
522 }
523 None => {
Hasini Gunasinghe0aba68a2021-03-19 00:43:52 +0000524 let new_op = Arc::new(Operation::new(
525 operations.len(),
526 km_op,
527 owner,
528 auth_info,
529 forced,
530 logging_info,
531 ));
Janis Danisevskis1af91262020-08-10 14:58:08 -0700532 operations.push(Arc::downgrade(&new_op));
533 new_op
534 }
535 }
536 }
537
538 fn get(&self, index: usize) -> Option<Arc<Operation>> {
539 self.operations.lock().expect("In OperationDb::get.").get(index).and_then(|op| op.upgrade())
540 }
541
542 /// Attempts to prune an operation.
543 ///
544 /// This function is used during operation creation, i.e., by
545 /// `KeystoreSecurityLevel::create_operation`, to try and free up an operation slot
546 /// if it got `ErrorCode::TOO_MANY_OPERATIONS` from the KeyMint backend. It is not
547 /// guaranteed that an operation slot is available after this call successfully
548 /// returned for various reasons. E.g., another thread may have snatched up the newly
549 /// available slot. Callers may have to call prune multiple times before they get a
550 /// free operation slot. Prune may also return `Err(Error::Rc(ResponseCode::BACKEND_BUSY))`
551 /// which indicates that no prunable operation was found.
552 ///
553 /// To find a suitable candidate we compute the malus for the caller and each existing
554 /// operation. The malus is the inverse of the pruning power (caller) or pruning
555 /// resistance (existing operation).
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700556 ///
Janis Danisevskis1af91262020-08-10 14:58:08 -0700557 /// The malus is based on the number of sibling operations and age. Sibling
558 /// operations are operations that have the same owner (UID).
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700559 ///
Janis Danisevskis1af91262020-08-10 14:58:08 -0700560 /// Every operation, existing or new, starts with a malus of 1. Every sibling
561 /// increases the malus by one. The age is the time since an operation was last touched.
562 /// It increases the malus by log6(<age in seconds> + 1) rounded down to the next
563 /// integer. So the malus increases stepwise after 5s, 35s, 215s, ...
564 /// Of two operations with the same malus the least recently used one is considered
565 /// weaker.
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700566 ///
Janis Danisevskis1af91262020-08-10 14:58:08 -0700567 /// For the caller to be able to prune an operation it must find an operation
568 /// with a malus higher than its own.
569 ///
570 /// The malus can be expressed as
571 /// ```
572 /// malus = 1 + no_of_siblings + floor(log6(age_in_seconds + 1))
573 /// ```
574 /// where the constant `1` accounts for the operation under consideration.
575 /// In reality we compute it as
576 /// ```
577 /// caller_malus = 1 + running_siblings
578 /// ```
579 /// because the new operation has no age and is not included in the `running_siblings`,
580 /// and
581 /// ```
582 /// running_malus = running_siblings + floor(log6(age_in_seconds + 1))
583 /// ```
584 /// because a running operation is included in the `running_siblings` and it has
585 /// an age.
586 ///
587 /// ## Example
588 /// A caller with no running operations has a malus of 1. Young (age < 5s) operations
589 /// also with no siblings have a malus of one and cannot be pruned by the caller.
590 /// We have to find an operation that has at least one sibling or is older than 5s.
591 ///
592 /// A caller with one running operation has a malus of 2. Now even young siblings
593 /// or single child aging (5s <= age < 35s) operations are off limit. An aging
594 /// sibling of two, however, would have a malus of 3 and would be fair game.
595 ///
596 /// ## Rationale
597 /// Due to the limitation of KeyMint operation slots, we cannot get around pruning or
598 /// a single app could easily DoS KeyMint.
599 /// Keystore 1.0 used to always prune the least recently used operation. This at least
600 /// guaranteed that new operations can always be started. With the increased usage
601 /// of Keystore we saw increased pruning activity which can lead to a livelock
602 /// situation in the worst case.
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700603 ///
Janis Danisevskis1af91262020-08-10 14:58:08 -0700604 /// With the new pruning strategy we want to provide well behaved clients with
605 /// progress assurances while punishing DoS attempts. As a result of this
606 /// strategy we can be in the situation where no operation can be pruned and the
607 /// creation of a new operation fails. This allows single child operations which
608 /// are frequently updated to complete, thereby breaking up livelock situations
609 /// and facilitating system wide progress.
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700610 ///
611 /// ## Update
612 /// We also allow callers to cannibalize their own sibling operations if no other
613 /// slot can be found. In this case the least recently used sibling is pruned.
Janis Danisevskis186d9f42021-03-03 14:40:52 -0800614 pub fn prune(&self, caller: u32, forced: bool) -> Result<(), Error> {
Janis Danisevskis1af91262020-08-10 14:58:08 -0700615 loop {
616 // Maps the uid of the owner to the number of operations that owner has
617 // (running_siblings). More operations per owner lowers the pruning
618 // resistance of the operations of that owner. Whereas the number of
619 // ongoing operations of the caller lowers the pruning power of the caller.
620 let mut owners: HashMap<u32, u64> = HashMap::new();
621 let mut pruning_info: Vec<PruningInfo> = Vec::new();
622
623 let now = Instant::now();
624 self.operations
625 .lock()
626 .expect("In OperationDb::prune: Trying to lock self.operations.")
627 .iter()
628 .for_each(|op| {
629 if let Some(op) = op.upgrade() {
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700630 if let Some(p_info) = op.get_pruning_info() {
631 let owner = p_info.owner;
632 pruning_info.push(p_info);
633 // Count operations per owner.
634 *owners.entry(owner).or_insert(0) += 1;
635 }
Janis Danisevskis1af91262020-08-10 14:58:08 -0700636 }
637 });
638
Janis Danisevskis186d9f42021-03-03 14:40:52 -0800639 // If the operation is forced, the caller has a malus of 0.
640 let caller_malus = if forced { 0 } else { 1u64 + *owners.entry(caller).or_default() };
Janis Danisevskis1af91262020-08-10 14:58:08 -0700641
642 // We iterate through all operations computing the malus and finding
643 // the candidate with the highest malus which must also be higher
644 // than the caller_malus.
645 struct CandidateInfo {
646 index: usize,
647 malus: u64,
648 last_usage: Instant,
649 age: Duration,
650 }
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700651 let mut oldest_caller_op: Option<CandidateInfo> = None;
Janis Danisevskis1af91262020-08-10 14:58:08 -0700652 let candidate = pruning_info.iter().fold(
653 None,
Janis Danisevskis186d9f42021-03-03 14:40:52 -0800654 |acc: Option<CandidateInfo>, &PruningInfo { last_usage, owner, index, forced }| {
Janis Danisevskis1af91262020-08-10 14:58:08 -0700655 // Compute the age of the current operation.
656 let age = now
657 .checked_duration_since(last_usage)
658 .unwrap_or_else(|| Duration::new(0, 0));
659
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700660 // Find the least recently used sibling as an alternative pruning candidate.
661 if owner == caller {
662 if let Some(CandidateInfo { age: a, .. }) = oldest_caller_op {
663 if age > a {
664 oldest_caller_op =
665 Some(CandidateInfo { index, malus: 0, last_usage, age });
666 }
667 } else {
668 oldest_caller_op =
669 Some(CandidateInfo { index, malus: 0, last_usage, age });
670 }
671 }
672
Janis Danisevskis1af91262020-08-10 14:58:08 -0700673 // Compute the malus of the current operation.
Janis Danisevskis186d9f42021-03-03 14:40:52 -0800674 let malus = if forced {
675 // Forced operations have a malus of 0. And cannot even be pruned
676 // by other forced operations.
677 0
678 } else {
679 // Expect safety: Every owner in pruning_info was counted in
680 // the owners map. So this unwrap cannot panic.
681 *owners.get(&owner).expect(
682 "This is odd. We should have counted every owner in pruning_info.",
683 ) + ((age.as_secs() + 1) as f64).log(6.0).floor() as u64
684 };
Janis Danisevskis1af91262020-08-10 14:58:08 -0700685
686 // Now check if the current operation is a viable/better candidate
687 // the one currently stored in the accumulator.
688 match acc {
689 // First we have to find any operation that is prunable by the caller.
690 None => {
691 if caller_malus < malus {
692 Some(CandidateInfo { index, malus, last_usage, age })
693 } else {
694 None
695 }
696 }
697 // If we have found one we look for the operation with the worst score.
698 // If there is a tie, the older operation is considered weaker.
699 Some(CandidateInfo { index: i, malus: m, last_usage: l, age: a }) => {
700 if malus > m || (malus == m && age > a) {
701 Some(CandidateInfo { index, malus, last_usage, age })
702 } else {
703 Some(CandidateInfo { index: i, malus: m, last_usage: l, age: a })
704 }
705 }
706 }
707 },
708 );
709
Janis Danisevskis45c5c972020-10-26 09:35:16 -0700710 // If we did not find a suitable candidate we may cannibalize our oldest sibling.
711 let candidate = candidate.or(oldest_caller_op);
712
Janis Danisevskis1af91262020-08-10 14:58:08 -0700713 match candidate {
714 Some(CandidateInfo { index, malus: _, last_usage, age: _ }) => {
715 match self.get(index) {
716 Some(op) => {
717 match op.prune(last_usage) {
718 // We successfully freed up a slot.
719 Ok(()) => break Ok(()),
720 // This means the operation we tried to prune was on its way
721 // out. It also means that the slot it had occupied was freed up.
722 Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => break Ok(()),
723 // This means the operation we tried to prune was currently
724 // servicing a request. There are two options.
725 // * Assume that it was touched, which means that its
726 // pruning resistance increased. In that case we have
727 // to start over and find another candidate.
728 // * Assume that the operation is transitioning to end-of-life.
729 // which means that we got a free slot for free.
730 // If we assume the first but the second is true, we prune
731 // a good operation without need (aggressive approach).
732 // If we assume the second but the first is true, our
733 // caller will attempt to create a new KeyMint operation,
734 // fail with `ErrorCode::TOO_MANY_OPERATIONS`, and call
735 // us again (conservative approach).
736 Err(Error::Rc(ResponseCode::OPERATION_BUSY)) => {
737 // We choose the conservative approach, because
738 // every needlessly pruned operation can impact
739 // the user experience.
740 // To switch to the aggressive approach replace
741 // the following line with `continue`.
742 break Ok(());
743 }
744
745 // The candidate may have been touched so the score
746 // has changed since our evaluation.
747 _ => continue,
748 }
749 }
750 // This index does not exist any more. The operation
751 // in this slot was dropped. Good news, a slot
752 // has freed up.
753 None => break Ok(()),
754 }
755 }
756 // We did not get a pruning candidate.
757 None => break Err(Error::Rc(ResponseCode::BACKEND_BUSY)),
758 }
759 }
760 }
761}
762
763/// Implementation of IKeystoreOperation.
764pub struct KeystoreOperation {
765 operation: Mutex<Option<Arc<Operation>>>,
766}
767
768impl KeystoreOperation {
769 /// Creates a new operation instance wrapped in a
Andrew Walbrande45c8b2021-04-13 14:42:38 +0000770 /// BnKeystoreOperation proxy object. It also enables
771 /// `BinderFeatures::set_requesting_sid` on the new interface, because
Janis Danisevskis1af91262020-08-10 14:58:08 -0700772 /// we need it for checking Keystore permissions.
Stephen Crane23cf7242022-01-19 17:49:46 +0000773 pub fn new_native_binder(operation: Arc<Operation>) -> binder::Strong<dyn IKeystoreOperation> {
Andrew Walbrande45c8b2021-04-13 14:42:38 +0000774 BnKeystoreOperation::new_binder(
775 Self { operation: Mutex::new(Some(operation)) },
776 BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
777 )
Janis Danisevskis1af91262020-08-10 14:58:08 -0700778 }
779
780 /// Grabs the outer operation mutex and calls `f` on the locked operation.
781 /// The function also deletes the operation if it returns with an error or if
782 /// `delete_op` is true.
783 fn with_locked_operation<T, F>(&self, f: F, delete_op: bool) -> Result<T>
784 where
785 for<'a> F: FnOnce(&'a Operation) -> Result<T>,
786 {
787 let mut delete_op: bool = delete_op;
788 match self.operation.try_lock() {
789 Ok(mut mutex_guard) => {
790 let result = match &*mutex_guard {
791 Some(op) => {
Chris Wailes263de9f2022-08-11 15:00:51 -0700792 let result = f(op);
Janis Danisevskis1af91262020-08-10 14:58:08 -0700793 // Any error here means we can discard the operation.
794 if result.is_err() {
795 delete_op = true;
796 }
797 result
798 }
799 None => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE))
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000800 .context(ks_err!("KeystoreOperation::with_locked_operation")),
Janis Danisevskis1af91262020-08-10 14:58:08 -0700801 };
802
803 if delete_op {
804 // We give up our reference to the Operation, thereby freeing up our
805 // internal resources and ending the wrapped KeyMint operation.
806 // This KeystoreOperation object will still be owned by an SpIBinder
807 // until the client drops its remote reference.
808 *mutex_guard = None;
809 }
810 result
811 }
812 Err(_) => Err(Error::Rc(ResponseCode::OPERATION_BUSY))
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000813 .context(ks_err!("KeystoreOperation::with_locked_operation")),
Janis Danisevskis1af91262020-08-10 14:58:08 -0700814 }
815 }
816}
817
818impl binder::Interface for KeystoreOperation {}
819
820impl IKeystoreOperation for KeystoreOperation {
Stephen Crane23cf7242022-01-19 17:49:46 +0000821 fn updateAad(&self, aad_input: &[u8]) -> binder::Result<()> {
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +0000822 let _wp = wd::watch_millis("IKeystoreOperation::updateAad", 500);
Janis Danisevskis1af91262020-08-10 14:58:08 -0700823 map_or_log_err(
824 self.with_locked_operation(
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000825 |op| op.update_aad(aad_input).context(ks_err!("KeystoreOperation::updateAad")),
Janis Danisevskis1af91262020-08-10 14:58:08 -0700826 false,
827 ),
828 Ok,
829 )
830 }
831
Stephen Crane23cf7242022-01-19 17:49:46 +0000832 fn update(&self, input: &[u8]) -> binder::Result<Option<Vec<u8>>> {
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +0000833 let _wp = wd::watch_millis("IKeystoreOperation::update", 500);
Janis Danisevskis1af91262020-08-10 14:58:08 -0700834 map_or_log_err(
835 self.with_locked_operation(
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000836 |op| op.update(input).context(ks_err!("KeystoreOperation::update")),
Janis Danisevskis1af91262020-08-10 14:58:08 -0700837 false,
838 ),
839 Ok,
840 )
841 }
842 fn finish(
843 &self,
844 input: Option<&[u8]>,
845 signature: Option<&[u8]>,
Stephen Crane23cf7242022-01-19 17:49:46 +0000846 ) -> binder::Result<Option<Vec<u8>>> {
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +0000847 let _wp = wd::watch_millis("IKeystoreOperation::finish", 500);
Janis Danisevskis1af91262020-08-10 14:58:08 -0700848 map_or_log_err(
849 self.with_locked_operation(
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000850 |op| op.finish(input, signature).context(ks_err!("KeystoreOperation::finish")),
Janis Danisevskis1af91262020-08-10 14:58:08 -0700851 true,
852 ),
853 Ok,
854 )
855 }
856
Stephen Crane23cf7242022-01-19 17:49:46 +0000857 fn abort(&self) -> binder::Result<()> {
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +0000858 let _wp = wd::watch_millis("IKeystoreOperation::abort", 500);
Janis Danisevskis778245c2021-03-04 15:40:23 -0800859 map_err_with(
Janis Danisevskis1af91262020-08-10 14:58:08 -0700860 self.with_locked_operation(
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000861 |op| op.abort(Outcome::Abort).context(ks_err!("KeystoreOperation::abort")),
Janis Danisevskis1af91262020-08-10 14:58:08 -0700862 true,
863 ),
Janis Danisevskis778245c2021-03-04 15:40:23 -0800864 |e| {
865 match e.root_cause().downcast_ref::<Error>() {
866 // Calling abort on expired operations is something very common.
867 // There is no reason to clutter the log with it. It is never the cause
868 // for a true problem.
869 Some(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => {}
870 _ => log::error!("{:?}", e),
871 };
872 e
873 },
Janis Danisevskis1af91262020-08-10 14:58:08 -0700874 Ok,
875 )
876 }
877}