blob: fc1a6adae63c29d476be1df67fc138f92ecac02b [file] [log] [blame]
Max Bires148c08e2020-10-13 13:41:41 -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 is the implementation for the remote provisioning AIDL interface between
16//! the network providers for remote provisioning and the system. This interface
17//! allows the caller to prompt the Remote Provisioning HAL to generate keys and
18//! CBOR blobs that can be ferried to a provisioning server that will return
19//! certificate chains signed by some root authority and stored in a keystore SQLite
20//! DB.
21
Jeff Vander Stoep46bbc612021-04-09 08:55:21 +020022#![allow(clippy::from_over_into, clippy::needless_question_mark, clippy::vec_init_then_push)]
23
Max Biresb2e1d032021-02-08 21:35:05 -080024use std::collections::HashMap;
Max Bires148c08e2020-10-13 13:41:41 -070025
Max Biresb2e1d032021-02-08 21:35:05 -080026use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Max Bires97f96812021-02-23 23:44:57 -080027 Algorithm::Algorithm, AttestationKey::AttestationKey, Certificate::Certificate,
Max Bires834dd362021-03-23 13:01:57 -070028 DeviceInfo::DeviceInfo, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
29 KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
30 MacedPublicKey::MacedPublicKey, ProtectedData::ProtectedData, SecurityLevel::SecurityLevel,
31 Tag::Tag,
Max Biresb2e1d032021-02-08 21:35:05 -080032};
Max Bires148c08e2020-10-13 13:41:41 -070033use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
34 AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
35 IRemoteProvisioning::IRemoteProvisioning,
36};
Andrew Walbrande45c8b2021-04-13 14:42:38 +000037use android_security_remoteprovisioning::binder::{BinderFeatures, Strong};
Max Bires97f96812021-02-23 23:44:57 -080038use android_system_keystore2::aidl::android::system::keystore2::{
39 Domain::Domain, KeyDescriptor::KeyDescriptor,
40};
Max Biresb2e1d032021-02-08 21:35:05 -080041use anyhow::{Context, Result};
Max Bires97f96812021-02-23 23:44:57 -080042use keystore2_crypto::parse_subject_from_certificate;
43use std::sync::atomic::{AtomicBool, Ordering};
Max Bires148c08e2020-10-13 13:41:41 -070044
Max Bires97f96812021-02-23 23:44:57 -080045use crate::database::{CertificateChain, KeystoreDB, Uuid};
46use crate::error::{self, map_or_log_err, map_rem_prov_error, Error};
Max Biresb2e1d032021-02-08 21:35:05 -080047use crate::globals::{get_keymint_device, get_remotely_provisioned_component, DB};
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +000048use crate::utils::{watchdog as wd, Asp};
Max Bires148c08e2020-10-13 13:41:41 -070049
Max Bires97f96812021-02-23 23:44:57 -080050/// Contains helper functions to check if remote provisioning is enabled on the system and, if so,
51/// to assign and retrieve attestation keys and certificate chains.
52#[derive(Default)]
53pub struct RemProvState {
54 security_level: SecurityLevel,
55 km_uuid: Uuid,
56 is_hal_present: AtomicBool,
57}
58
59impl RemProvState {
60 /// Creates a RemProvState struct.
61 pub fn new(security_level: SecurityLevel, km_uuid: Uuid) -> Self {
62 Self { security_level, km_uuid, is_hal_present: AtomicBool::new(true) }
63 }
64
65 /// Checks if remote provisioning is enabled and partially caches the result. On a hybrid system
66 /// remote provisioning can flip from being disabled to enabled depending on responses from the
67 /// server, so unfortunately caching the presence or absence of the HAL is not enough to fully
68 /// make decisions about the state of remote provisioning during runtime.
69 fn check_rem_prov_enabled(&self, db: &mut KeystoreDB) -> Result<bool> {
70 if !self.is_hal_present.load(Ordering::Relaxed)
71 || get_remotely_provisioned_component(&self.security_level).is_err()
72 {
73 self.is_hal_present.store(false, Ordering::Relaxed);
74 return Ok(false);
75 }
76 // To check if remote provisioning is enabled on a system that supports both remote
77 // provisioning and factory provisioned keys, we only need to check if there are any
78 // keys at all generated to indicate if the app has gotten the signal to begin filling
79 // the key pool from the server.
80 let pool_status = db
81 .get_attestation_pool_status(0 /* date */, &self.km_uuid)
82 .context("In check_rem_prov_enabled: failed to get attestation pool status.")?;
83 Ok(pool_status.total != 0)
84 }
85
86 /// Fetches a remote provisioning attestation key and certificate chain inside of the
87 /// returned `CertificateChain` struct if one exists for the given caller_uid. If one has not
88 /// been assigned, this function will assign it. If there are no signed attestation keys
89 /// available to be assigned, it will return the ResponseCode `OUT_OF_KEYS`
90 fn get_rem_prov_attest_key(
91 &self,
92 key: &KeyDescriptor,
93 caller_uid: u32,
94 db: &mut KeystoreDB,
95 ) -> Result<Option<CertificateChain>> {
96 match key.domain {
97 Domain::APP => {
98 // Attempt to get an Attestation Key once. If it fails, then the app doesn't
99 // have a valid chain assigned to it. The helper function will return None after
100 // attempting to assign a key. An error will be thrown if the pool is simply out
101 // of usable keys. Then another attempt to fetch the just-assigned key will be
102 // made. If this fails too, something is very wrong.
103 self.get_rem_prov_attest_key_helper(key, caller_uid, db)
104 .context("In get_rem_prov_attest_key: Failed to get a key")?
105 .map_or_else(
106 || self.get_rem_prov_attest_key_helper(key, caller_uid, db),
107 |v| Ok(Some(v)),
108 )
109 .context(concat!(
110 "In get_rem_prov_attest_key: Failed to get a key after",
111 "attempting to assign one."
112 ))?
113 .map_or_else(
114 || {
115 Err(Error::sys()).context(concat!(
116 "In get_rem_prov_attest_key: Attempted to assign a ",
117 "key and failed silently. Something is very wrong."
118 ))
119 },
120 |cert_chain| Ok(Some(cert_chain)),
121 )
122 }
123 _ => Ok(None),
124 }
125 }
126
127 /// Returns None if an AttestationKey fails to be assigned. Errors if no keys are available.
128 fn get_rem_prov_attest_key_helper(
129 &self,
130 key: &KeyDescriptor,
131 caller_uid: u32,
132 db: &mut KeystoreDB,
133 ) -> Result<Option<CertificateChain>> {
134 let cert_chain = db
135 .retrieve_attestation_key_and_cert_chain(key.domain, caller_uid as i64, &self.km_uuid)
136 .context("In get_rem_prov_attest_key_helper: Failed to retrieve a key + cert chain")?;
137 match cert_chain {
138 Some(cert_chain) => Ok(Some(cert_chain)),
139 // Either this app needs to be assigned a key, or the pool is empty. An error will
140 // be thrown if there is no key available to assign. This will indicate that the app
141 // should be nudged to provision more keys so keystore can retry.
142 None => {
143 db.assign_attestation_key(key.domain, caller_uid as i64, &self.km_uuid)
144 .context("In get_rem_prov_attest_key_helper: Failed to assign a key")?;
145 Ok(None)
146 }
147 }
148 }
149
150 fn is_asymmetric_key(&self, params: &[KeyParameter]) -> bool {
151 params.iter().any(|kp| {
152 matches!(
153 kp,
154 KeyParameter {
155 tag: Tag::ALGORITHM,
156 value: KeyParameterValue::Algorithm(Algorithm::RSA)
157 } | KeyParameter {
158 tag: Tag::ALGORITHM,
159 value: KeyParameterValue::Algorithm(Algorithm::EC)
160 }
161 )
162 })
163 }
164
165 /// Checks to see (1) if the key in question should be attested to based on the algorithm and
166 /// (2) if remote provisioning is present and enabled on the system. If these conditions are
167 /// met, it makes an attempt to fetch the attestation key assigned to the `caller_uid`.
168 ///
169 /// It returns the ResponseCode `OUT_OF_KEYS` if there is not one key currently assigned to the
170 /// `caller_uid` and there are none available to assign.
Janis Danisevskis3541f3e2021-03-20 14:18:52 -0700171 pub fn get_remotely_provisioned_attestation_key_and_certs(
Max Bires97f96812021-02-23 23:44:57 -0800172 &self,
173 key: &KeyDescriptor,
174 caller_uid: u32,
175 params: &[KeyParameter],
176 db: &mut KeystoreDB,
Janis Danisevskis3541f3e2021-03-20 14:18:52 -0700177 ) -> Result<Option<(AttestationKey, Certificate)>> {
Max Bires97f96812021-02-23 23:44:57 -0800178 if !self.is_asymmetric_key(params) || !self.check_rem_prov_enabled(db)? {
179 // There is no remote provisioning component for this security level on the
180 // device. Return None so the underlying KM instance knows to use its
181 // factory provisioned key instead. Alternatively, it's not an asymmetric key
182 // and therefore will not be attested.
Janis Danisevskis3541f3e2021-03-20 14:18:52 -0700183 Ok(None)
Max Bires97f96812021-02-23 23:44:57 -0800184 } else {
185 match self.get_rem_prov_attest_key(&key, caller_uid, db).context(concat!(
186 "In get_remote_provisioning_key_and_certs: Failed to get ",
187 "attestation key"
188 ))? {
Janis Danisevskis3541f3e2021-03-20 14:18:52 -0700189 Some(cert_chain) => Ok(Some((
190 AttestationKey {
Max Bires97f96812021-02-23 23:44:57 -0800191 keyBlob: cert_chain.private_key.to_vec(),
192 attestKeyParams: vec![],
193 issuerSubjectName: parse_subject_from_certificate(&cert_chain.batch_cert)
194 .context(concat!(
195 "In get_remote_provisioning_key_and_certs: Failed to ",
196 "parse subject."
197 ))?,
Janis Danisevskis3541f3e2021-03-20 14:18:52 -0700198 },
199 Certificate { encodedCertificate: cert_chain.cert_chain },
200 ))),
201 None => Ok(None),
Max Bires97f96812021-02-23 23:44:57 -0800202 }
203 }
204 }
205}
Max Bires148c08e2020-10-13 13:41:41 -0700206/// Implementation of the IRemoteProvisioning service.
Max Biresb2e1d032021-02-08 21:35:05 -0800207#[derive(Default)]
Max Bires148c08e2020-10-13 13:41:41 -0700208pub struct RemoteProvisioningService {
Max Biresb2e1d032021-02-08 21:35:05 -0800209 device_by_sec_level: HashMap<SecurityLevel, Asp>,
Max Bires148c08e2020-10-13 13:41:41 -0700210}
211
212impl RemoteProvisioningService {
Max Biresb2e1d032021-02-08 21:35:05 -0800213 fn get_dev_by_sec_level(
214 &self,
215 sec_level: &SecurityLevel,
216 ) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
217 if let Some(dev) = self.device_by_sec_level.get(sec_level) {
218 dev.get_interface().context("In get_dev_by_sec_level.")
219 } else {
220 Err(error::Error::sys()).context(concat!(
221 "In get_dev_by_sec_level: Remote instance for requested security level",
222 " not found."
223 ))
224 }
225 }
226
Max Bires148c08e2020-10-13 13:41:41 -0700227 /// Creates a new instance of the remote provisioning service
Stephen Crane221bbb52020-12-16 15:52:10 -0800228 pub fn new_native_binder() -> Result<Strong<dyn IRemoteProvisioning>> {
Max Biresb2e1d032021-02-08 21:35:05 -0800229 let mut result: Self = Default::default();
230 let dev = get_remotely_provisioned_component(&SecurityLevel::TRUSTED_ENVIRONMENT)
231 .context("In new_native_binder: Failed to get TEE Remote Provisioner instance.")?;
232 result.device_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, dev);
233 if let Ok(dev) = get_remotely_provisioned_component(&SecurityLevel::STRONGBOX) {
234 result.device_by_sec_level.insert(SecurityLevel::STRONGBOX, dev);
235 }
Andrew Walbrande45c8b2021-04-13 14:42:38 +0000236 Ok(BnRemoteProvisioning::new_binder(result, BinderFeatures::default()))
Max Bires148c08e2020-10-13 13:41:41 -0700237 }
238
239 /// Populates the AttestationPoolStatus parcelable with information about how many
240 /// certs will be expiring by the date provided in `expired_by` along with how many
241 /// keys have not yet been assigned.
242 pub fn get_pool_status(
243 &self,
244 expired_by: i64,
245 sec_level: SecurityLevel,
246 ) -> Result<AttestationPoolStatus> {
247 let (_, _, uuid) = get_keymint_device(&sec_level)?;
248 DB.with::<_, Result<AttestationPoolStatus>>(|db| {
249 let mut db = db.borrow_mut();
Max Biresb2e1d032021-02-08 21:35:05 -0800250 // delete_expired_attestation_keys is always safe to call, and will remove anything
251 // older than the date at the time of calling. No work should be done on the
252 // attestation keys unless the pool status is checked first, so this call should be
253 // enough to routinely clean out expired keys.
254 db.delete_expired_attestation_keys()?;
Max Bires148c08e2020-10-13 13:41:41 -0700255 Ok(db.get_attestation_pool_status(expired_by, &uuid)?)
256 })
257 }
258
259 /// Generates a CBOR blob which will be assembled by the calling code into a larger
260 /// CBOR blob intended for delivery to a provisioning serever. This blob will contain
261 /// `num_csr` certificate signing requests for attestation keys generated in the TEE,
262 /// along with a server provided `eek` and `challenge`. The endpoint encryption key will
263 /// be used to encrypt the sensitive contents being transmitted to the server, and the
264 /// challenge will ensure freshness. A `test_mode` flag will instruct the remote provisioning
265 /// HAL if it is okay to accept EEKs that aren't signed by something that chains back to the
266 /// baked in root of trust in the underlying IRemotelyProvisionedComponent instance.
Max Bires834dd362021-03-23 13:01:57 -0700267 #[allow(clippy::too_many_arguments)]
Max Bires148c08e2020-10-13 13:41:41 -0700268 pub fn generate_csr(
269 &self,
Max Biresb2e1d032021-02-08 21:35:05 -0800270 test_mode: bool,
271 num_csr: i32,
272 eek: &[u8],
273 challenge: &[u8],
274 sec_level: SecurityLevel,
275 protected_data: &mut ProtectedData,
Max Bires834dd362021-03-23 13:01:57 -0700276 device_info: &mut DeviceInfo,
Max Bires148c08e2020-10-13 13:41:41 -0700277 ) -> Result<Vec<u8>> {
Max Biresb2e1d032021-02-08 21:35:05 -0800278 let dev = self.get_dev_by_sec_level(&sec_level)?;
279 let (_, _, uuid) = get_keymint_device(&sec_level)?;
280 let keys_to_sign = DB.with::<_, Result<Vec<MacedPublicKey>>>(|db| {
281 let mut db = db.borrow_mut();
282 Ok(db
283 .fetch_unsigned_attestation_keys(num_csr, &uuid)?
284 .iter()
285 .map(|key| MacedPublicKey { macedKey: key.to_vec() })
286 .collect())
287 })?;
Max Bires834dd362021-03-23 13:01:57 -0700288 let mut mac = map_rem_prov_error(dev.generateCertificateRequest(
Max Biresb2e1d032021-02-08 21:35:05 -0800289 test_mode,
290 &keys_to_sign,
291 eek,
292 challenge,
Max Bires834dd362021-03-23 13:01:57 -0700293 device_info,
Max Biresb2e1d032021-02-08 21:35:05 -0800294 protected_data,
295 ))
296 .context("In generate_csr: Failed to generate csr")?;
Max Bires97f96812021-02-23 23:44:57 -0800297 let mut cose_mac_0 = Vec::<u8>::new();
298 // TODO(b/180392379): Replace this manual CBOR generation with the cbor-serde crate as well.
299 // This generates an array consisting of the mac and the public key Maps.
300 // Just generate the actual MacedPublicKeys structure when the crate is
301 // available.
302 cose_mac_0.push((0b100_00000 | (keys_to_sign.len() + 1)) as u8);
303 cose_mac_0.push(0b010_11000); //push mac
304 cose_mac_0.push(mac.len() as u8);
305 cose_mac_0.append(&mut mac);
306 for maced_public_key in keys_to_sign {
307 if maced_public_key.macedKey.len() > 83 + 8 {
308 cose_mac_0.extend_from_slice(&maced_public_key.macedKey[8..83 + 8]);
309 }
310 }
311 Ok(cose_mac_0)
Max Bires148c08e2020-10-13 13:41:41 -0700312 }
313
314 /// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
315 /// `public_key` is used to index into the SQL database in order to insert the `certs` blob
316 /// which represents a PEM encoded X.509 certificate chain. The `expiration_date` is provided
317 /// as a convenience from the caller to avoid having to parse the certificates semantically
318 /// here.
319 pub fn provision_cert_chain(
320 &self,
321 public_key: &[u8],
Max Biresb2e1d032021-02-08 21:35:05 -0800322 batch_cert: &[u8],
Max Bires148c08e2020-10-13 13:41:41 -0700323 certs: &[u8],
324 expiration_date: i64,
325 sec_level: SecurityLevel,
326 ) -> Result<()> {
327 DB.with::<_, Result<()>>(|db| {
328 let mut db = db.borrow_mut();
329 let (_, _, uuid) = get_keymint_device(&sec_level)?;
330 Ok(db.store_signed_attestation_certificate_chain(
331 public_key,
Max Biresb2e1d032021-02-08 21:35:05 -0800332 batch_cert,
Max Bires148c08e2020-10-13 13:41:41 -0700333 certs, /* DER encoded certificate chain */
334 expiration_date,
335 &uuid,
336 )?)
337 })
338 }
339
340 /// Submits a request to the Remote Provisioner HAL to generate a signing key pair.
341 /// `is_test_mode` indicates whether or not the returned public key should be marked as being
342 /// for testing in order to differentiate them from private keys. If the call is successful,
343 /// the key pair is then added to the database.
Max Biresb2e1d032021-02-08 21:35:05 -0800344 pub fn generate_key_pair(&self, is_test_mode: bool, sec_level: SecurityLevel) -> Result<()> {
345 let (_, _, uuid) = get_keymint_device(&sec_level)?;
346 let dev = self.get_dev_by_sec_level(&sec_level)?;
347 let mut maced_key = MacedPublicKey { macedKey: Vec::new() };
348 let priv_key =
349 map_rem_prov_error(dev.generateEcdsaP256KeyPair(is_test_mode, &mut maced_key))
350 .context("In generate_key_pair: Failed to generated ECDSA keypair.")?;
351 // TODO(b/180392379): This is a brittle hack that relies on the consistent formatting of
352 // the returned CBOR blob in order to extract the public key.
353 let data = &maced_key.macedKey;
354 if data.len() < 85 {
355 return Err(error::Error::sys()).context(concat!(
356 "In generate_key_pair: CBOR blob returned from",
357 "RemotelyProvisionedComponent is definitely malformatted or empty."
358 ));
359 }
360 let mut raw_key: Vec<u8> = vec![0; 64];
361 raw_key[0..32].clone_from_slice(&data[18..18 + 32]);
362 raw_key[32..64].clone_from_slice(&data[53..53 + 32]);
363 DB.with::<_, Result<()>>(|db| {
364 let mut db = db.borrow_mut();
365 Ok(db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)?)
366 })
367 }
368
369 /// Checks the security level of each available IRemotelyProvisionedComponent hal and returns
370 /// all levels in an array to the caller.
371 pub fn get_security_levels(&self) -> Result<Vec<SecurityLevel>> {
372 Ok(self.device_by_sec_level.keys().cloned().collect())
Max Bires148c08e2020-10-13 13:41:41 -0700373 }
Max Bires60d7ed12021-03-05 15:59:22 -0800374
375 /// Deletes all attestation keys generated by the IRemotelyProvisionedComponent from the device,
376 /// regardless of what state of the attestation key lifecycle they were in.
377 pub fn delete_all_keys(&self) -> Result<i64> {
378 DB.with::<_, Result<i64>>(|db| {
379 let mut db = db.borrow_mut();
380 Ok(db.delete_all_attestation_keys()?)
381 })
382 }
Max Bires148c08e2020-10-13 13:41:41 -0700383}
384
385impl binder::Interface for RemoteProvisioningService {}
386
387// Implementation of IRemoteProvisioning. See AIDL spec at
388// :aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
389impl IRemoteProvisioning for RemoteProvisioningService {
390 fn getPoolStatus(
391 &self,
392 expired_by: i64,
393 sec_level: SecurityLevel,
394 ) -> binder::public_api::Result<AttestationPoolStatus> {
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +0000395 let _wp = wd::watch_millis("IRemoteProvisioning::getPoolStatus", 500);
Max Bires148c08e2020-10-13 13:41:41 -0700396 map_or_log_err(self.get_pool_status(expired_by, sec_level), Ok)
397 }
398
399 fn generateCsr(
400 &self,
401 test_mode: bool,
402 num_csr: i32,
403 eek: &[u8],
404 challenge: &[u8],
405 sec_level: SecurityLevel,
Max Biresb2e1d032021-02-08 21:35:05 -0800406 protected_data: &mut ProtectedData,
Max Bires834dd362021-03-23 13:01:57 -0700407 device_info: &mut DeviceInfo,
Max Bires148c08e2020-10-13 13:41:41 -0700408 ) -> binder::public_api::Result<Vec<u8>> {
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +0000409 let _wp = wd::watch_millis("IRemoteProvisioning::generateCsr", 500);
Max Biresb2e1d032021-02-08 21:35:05 -0800410 map_or_log_err(
Max Bires834dd362021-03-23 13:01:57 -0700411 self.generate_csr(
412 test_mode,
413 num_csr,
414 eek,
415 challenge,
416 sec_level,
417 protected_data,
418 device_info,
419 ),
Max Biresb2e1d032021-02-08 21:35:05 -0800420 Ok,
421 )
Max Bires148c08e2020-10-13 13:41:41 -0700422 }
423
424 fn provisionCertChain(
425 &self,
426 public_key: &[u8],
Max Biresb2e1d032021-02-08 21:35:05 -0800427 batch_cert: &[u8],
Max Bires148c08e2020-10-13 13:41:41 -0700428 certs: &[u8],
429 expiration_date: i64,
430 sec_level: SecurityLevel,
431 ) -> binder::public_api::Result<()> {
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +0000432 let _wp = wd::watch_millis("IRemoteProvisioning::provisionCertChain", 500);
Max Biresb2e1d032021-02-08 21:35:05 -0800433 map_or_log_err(
434 self.provision_cert_chain(public_key, batch_cert, certs, expiration_date, sec_level),
435 Ok,
436 )
Max Bires148c08e2020-10-13 13:41:41 -0700437 }
438
439 fn generateKeyPair(
440 &self,
441 is_test_mode: bool,
442 sec_level: SecurityLevel,
443 ) -> binder::public_api::Result<()> {
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +0000444 let _wp = wd::watch_millis("IRemoteProvisioning::generateKeyPair", 500);
Max Bires148c08e2020-10-13 13:41:41 -0700445 map_or_log_err(self.generate_key_pair(is_test_mode, sec_level), Ok)
446 }
Max Biresb2e1d032021-02-08 21:35:05 -0800447
448 fn getSecurityLevels(&self) -> binder::public_api::Result<Vec<SecurityLevel>> {
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +0000449 let _wp = wd::watch_millis("IRemoteProvisioning::getSecurityLevels", 500);
Max Biresb2e1d032021-02-08 21:35:05 -0800450 map_or_log_err(self.get_security_levels(), Ok)
451 }
Max Bires60d7ed12021-03-05 15:59:22 -0800452
453 fn deleteAllKeys(&self) -> binder::public_api::Result<i64> {
Hasini Gunasinghe5a893e82021-05-05 14:32:32 +0000454 let _wp = wd::watch_millis("IRemoteProvisioning::deleteAllKeys", 500);
Max Bires60d7ed12021-03-05 15:59:22 -0800455 map_or_log_err(self.delete_all_keys(), Ok)
456 }
Max Bires148c08e2020-10-13 13:41:41 -0700457}