• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
22 use std::collections::HashMap;
23 
24 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
25     Algorithm::Algorithm, AttestationKey::AttestationKey, Certificate::Certificate,
26     DeviceInfo::DeviceInfo, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
27     KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
28     MacedPublicKey::MacedPublicKey, ProtectedData::ProtectedData, SecurityLevel::SecurityLevel,
29     Tag::Tag,
30 };
31 use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
32     AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
33     IRemoteProvisioning::IRemoteProvisioning, ImplInfo::ImplInfo,
34 };
35 use android_security_remoteprovisioning::binder::{BinderFeatures, Strong};
36 use android_system_keystore2::aidl::android::system::keystore2::{
37     Domain::Domain, KeyDescriptor::KeyDescriptor,
38 };
39 use anyhow::{Context, Result};
40 use keystore2_crypto::parse_subject_from_certificate;
41 use std::sync::atomic::{AtomicBool, Ordering};
42 
43 use crate::database::{CertificateChain, KeystoreDB, Uuid};
44 use crate::error::{self, map_or_log_err, map_rem_prov_error, Error};
45 use crate::globals::{get_keymint_device, get_remotely_provisioned_component, DB};
46 use crate::metrics_store::log_rkp_error_stats;
47 use crate::utils::{watchdog as wd, Asp};
48 use android_security_metrics::aidl::android::security::metrics::RkpError::RkpError as MetricsRkpError;
49 
50 /// 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)]
53 pub struct RemProvState {
54     security_level: SecurityLevel,
55     km_uuid: Uuid,
56     is_hal_present: AtomicBool,
57 }
58 
59 impl RemProvState {
60     /// Creates a RemProvState struct.
new(security_level: SecurityLevel, km_uuid: Uuid) -> Self61     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.
check_rem_prov_enabled(&self, db: &mut KeystoreDB) -> Result<bool>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`
get_rem_prov_attest_key( &self, key: &KeyDescriptor, caller_uid: u32, db: &mut KeystoreDB, ) -> Result<Option<CertificateChain>>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.
get_rem_prov_attest_key_helper( &self, key: &KeyDescriptor, caller_uid: u32, db: &mut KeystoreDB, ) -> Result<Option<CertificateChain>>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 
is_asymmetric_key(&self, params: &[KeyParameter]) -> bool150     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.
get_remotely_provisioned_attestation_key_and_certs( &self, key: &KeyDescriptor, caller_uid: u32, params: &[KeyParameter], db: &mut KeystoreDB, ) -> Result<Option<(AttestationKey, Certificate)>>171     pub fn get_remotely_provisioned_attestation_key_and_certs(
172         &self,
173         key: &KeyDescriptor,
174         caller_uid: u32,
175         params: &[KeyParameter],
176         db: &mut KeystoreDB,
177     ) -> Result<Option<(AttestationKey, Certificate)>> {
178         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.
183             Ok(None)
184         } else {
185             match self.get_rem_prov_attest_key(&key, caller_uid, db) {
186                 Err(e) => {
187                     log::error!(
188                         concat!(
189                             "In get_remote_provisioning_key_and_certs: Failed to get ",
190                             "attestation key. {:?}"
191                         ),
192                         e
193                     );
194                     log_rkp_error_stats(MetricsRkpError::FALL_BACK_DURING_HYBRID);
195                     Ok(None)
196                 }
197                 Ok(v) => match v {
198                     Some(cert_chain) => Ok(Some((
199                         AttestationKey {
200                             keyBlob: cert_chain.private_key.to_vec(),
201                             attestKeyParams: vec![],
202                             issuerSubjectName: parse_subject_from_certificate(
203                                 &cert_chain.batch_cert,
204                             )
205                             .context(concat!(
206                                 "In get_remote_provisioning_key_and_certs: Failed to ",
207                                 "parse subject."
208                             ))?,
209                         },
210                         Certificate { encodedCertificate: cert_chain.cert_chain },
211                     ))),
212                     None => Ok(None),
213                 },
214             }
215         }
216     }
217 }
218 /// Implementation of the IRemoteProvisioning service.
219 #[derive(Default)]
220 pub struct RemoteProvisioningService {
221     device_by_sec_level: HashMap<SecurityLevel, Asp>,
222     curve_by_sec_level: HashMap<SecurityLevel, i32>,
223 }
224 
225 impl RemoteProvisioningService {
get_dev_by_sec_level( &self, sec_level: &SecurityLevel, ) -> Result<Strong<dyn IRemotelyProvisionedComponent>>226     fn get_dev_by_sec_level(
227         &self,
228         sec_level: &SecurityLevel,
229     ) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
230         if let Some(dev) = self.device_by_sec_level.get(sec_level) {
231             dev.get_interface().context("In get_dev_by_sec_level.")
232         } else {
233             Err(error::Error::sys()).context(concat!(
234                 "In get_dev_by_sec_level: Remote instance for requested security level",
235                 " not found."
236             ))
237         }
238     }
239 
240     /// Creates a new instance of the remote provisioning service
new_native_binder() -> Result<Strong<dyn IRemoteProvisioning>>241     pub fn new_native_binder() -> Result<Strong<dyn IRemoteProvisioning>> {
242         let mut result: Self = Default::default();
243         let dev = get_remotely_provisioned_component(&SecurityLevel::TRUSTED_ENVIRONMENT)
244             .context("In new_native_binder: Failed to get TEE Remote Provisioner instance.")?;
245         let rkp_tee_dev: Strong<dyn IRemotelyProvisionedComponent> = dev.get_interface()?;
246         result.curve_by_sec_level.insert(
247             SecurityLevel::TRUSTED_ENVIRONMENT,
248             rkp_tee_dev
249                 .getHardwareInfo()
250                 .context("In new_native_binder: Failed to get hardware info for the TEE.")?
251                 .supportedEekCurve,
252         );
253         result.device_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, dev);
254         if let Ok(dev) = get_remotely_provisioned_component(&SecurityLevel::STRONGBOX) {
255             let rkp_sb_dev: Strong<dyn IRemotelyProvisionedComponent> = dev.get_interface()?;
256             result.curve_by_sec_level.insert(
257                 SecurityLevel::STRONGBOX,
258                 rkp_sb_dev
259                     .getHardwareInfo()
260                     .context("In new_native_binder: Failed to get hardware info for StrongBox.")?
261                     .supportedEekCurve,
262             );
263             result.device_by_sec_level.insert(SecurityLevel::STRONGBOX, dev);
264         }
265         Ok(BnRemoteProvisioning::new_binder(result, BinderFeatures::default()))
266     }
267 
268     /// Generates a CBOR blob which will be assembled by the calling code into a larger
269     /// CBOR blob intended for delivery to a provisioning serever. This blob will contain
270     /// `num_csr` certificate signing requests for attestation keys generated in the TEE,
271     /// along with a server provided `eek` and `challenge`. The endpoint encryption key will
272     /// be used to encrypt the sensitive contents being transmitted to the server, and the
273     /// challenge will ensure freshness. A `test_mode` flag will instruct the remote provisioning
274     /// HAL if it is okay to accept EEKs that aren't signed by something that chains back to the
275     /// baked in root of trust in the underlying IRemotelyProvisionedComponent instance.
276     #[allow(clippy::too_many_arguments)]
generate_csr( &self, test_mode: bool, num_csr: i32, eek: &[u8], challenge: &[u8], sec_level: SecurityLevel, protected_data: &mut ProtectedData, device_info: &mut DeviceInfo, ) -> Result<Vec<u8>>277     pub fn generate_csr(
278         &self,
279         test_mode: bool,
280         num_csr: i32,
281         eek: &[u8],
282         challenge: &[u8],
283         sec_level: SecurityLevel,
284         protected_data: &mut ProtectedData,
285         device_info: &mut DeviceInfo,
286     ) -> Result<Vec<u8>> {
287         let dev = self.get_dev_by_sec_level(&sec_level)?;
288         let (_, _, uuid) = get_keymint_device(&sec_level)?;
289         let keys_to_sign = DB.with::<_, Result<Vec<MacedPublicKey>>>(|db| {
290             let mut db = db.borrow_mut();
291             Ok(db
292                 .fetch_unsigned_attestation_keys(num_csr, &uuid)?
293                 .iter()
294                 .map(|key| MacedPublicKey { macedKey: key.to_vec() })
295                 .collect())
296         })?;
297         let mut mac = map_rem_prov_error(dev.generateCertificateRequest(
298             test_mode,
299             &keys_to_sign,
300             eek,
301             challenge,
302             device_info,
303             protected_data,
304         ))
305         .context("In generate_csr: Failed to generate csr")?;
306         // TODO(b/180392379): Replace this manual CBOR generation with the cbor-serde crate as well.
307         //                    This generates an array consisting of the mac and the public key Maps.
308         //                    Just generate the actual MacedPublicKeys structure when the crate is
309         //                    available.
310         let mut cose_mac_0: Vec<u8> = vec![
311             (0b100_00000 | (keys_to_sign.len() + 1)) as u8,
312             0b010_11000, // mac
313             (mac.len() as u8),
314         ];
315         cose_mac_0.append(&mut mac);
316         // If this is a test mode key, there is an extra 6 bytes added as an additional entry in
317         // the COSE_Key struct to denote that.
318         let test_mode_entry_shift = if test_mode { 0 } else { 6 };
319         let byte_dist_mac0_payload = 8;
320         let cose_key_size = 83 - test_mode_entry_shift;
321         for maced_public_key in keys_to_sign {
322             if maced_public_key.macedKey.len() > cose_key_size + byte_dist_mac0_payload {
323                 cose_mac_0.extend_from_slice(
324                     &maced_public_key.macedKey
325                         [byte_dist_mac0_payload..cose_key_size + byte_dist_mac0_payload],
326                 );
327             }
328         }
329         Ok(cose_mac_0)
330     }
331 
332     /// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
333     /// `public_key` is used to index into the SQL database in order to insert the `certs` blob
334     /// which represents a PEM encoded X.509 certificate chain. The `expiration_date` is provided
335     /// as a convenience from the caller to avoid having to parse the certificates semantically
336     /// here.
provision_cert_chain( &self, public_key: &[u8], batch_cert: &[u8], certs: &[u8], expiration_date: i64, sec_level: SecurityLevel, ) -> Result<()>337     pub fn provision_cert_chain(
338         &self,
339         public_key: &[u8],
340         batch_cert: &[u8],
341         certs: &[u8],
342         expiration_date: i64,
343         sec_level: SecurityLevel,
344     ) -> Result<()> {
345         DB.with::<_, Result<()>>(|db| {
346             let mut db = db.borrow_mut();
347             let (_, _, uuid) = get_keymint_device(&sec_level)?;
348             db.store_signed_attestation_certificate_chain(
349                 public_key,
350                 batch_cert,
351                 certs, /* DER encoded certificate chain */
352                 expiration_date,
353                 &uuid,
354             )
355         })
356     }
357 
358     /// Submits a request to the Remote Provisioner HAL to generate a signing key pair.
359     /// `is_test_mode` indicates whether or not the returned public key should be marked as being
360     /// for testing in order to differentiate them from private keys. If the call is successful,
361     /// the key pair is then added to the database.
generate_key_pair(&self, is_test_mode: bool, sec_level: SecurityLevel) -> Result<()>362     pub fn generate_key_pair(&self, is_test_mode: bool, sec_level: SecurityLevel) -> Result<()> {
363         let (_, _, uuid) = get_keymint_device(&sec_level)?;
364         let dev = self.get_dev_by_sec_level(&sec_level)?;
365         let mut maced_key = MacedPublicKey { macedKey: Vec::new() };
366         let priv_key =
367             map_rem_prov_error(dev.generateEcdsaP256KeyPair(is_test_mode, &mut maced_key))
368                 .context("In generate_key_pair: Failed to generated ECDSA keypair.")?;
369         // TODO(b/180392379): This is a brittle hack that relies on the consistent formatting of
370         //                    the returned CBOR blob in order to extract the public key.
371         let data = &maced_key.macedKey;
372         if data.len() < 85 {
373             return Err(error::Error::sys()).context(concat!(
374                 "In generate_key_pair: CBOR blob returned from",
375                 "RemotelyProvisionedComponent is definitely malformatted or empty."
376             ));
377         }
378         let mut raw_key: Vec<u8> = vec![0; 64];
379         raw_key[0..32].clone_from_slice(&data[18..18 + 32]);
380         raw_key[32..64].clone_from_slice(&data[53..53 + 32]);
381         DB.with::<_, Result<()>>(|db| {
382             let mut db = db.borrow_mut();
383             db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)
384         })
385     }
386 
387     /// Checks the security level of each available IRemotelyProvisionedComponent hal and returns
388     /// all levels in an array to the caller.
get_implementation_info(&self) -> Result<Vec<ImplInfo>>389     pub fn get_implementation_info(&self) -> Result<Vec<ImplInfo>> {
390         Ok(self
391             .curve_by_sec_level
392             .iter()
393             .map(|(sec_level, curve)| ImplInfo { secLevel: *sec_level, supportedCurve: *curve })
394             .collect())
395     }
396 
397     /// Deletes all attestation keys generated by the IRemotelyProvisionedComponent from the device,
398     /// regardless of what state of the attestation key lifecycle they were in.
delete_all_keys(&self) -> Result<i64>399     pub fn delete_all_keys(&self) -> Result<i64> {
400         DB.with::<_, Result<i64>>(|db| {
401             let mut db = db.borrow_mut();
402             db.delete_all_attestation_keys()
403         })
404     }
405 }
406 
407 /// Populates the AttestationPoolStatus parcelable with information about how many
408 /// certs will be expiring by the date provided in `expired_by` along with how many
409 /// keys have not yet been assigned.
get_pool_status(expired_by: i64, sec_level: SecurityLevel) -> Result<AttestationPoolStatus>410 pub fn get_pool_status(expired_by: i64, sec_level: SecurityLevel) -> Result<AttestationPoolStatus> {
411     let (_, _, uuid) = get_keymint_device(&sec_level)?;
412     DB.with::<_, Result<AttestationPoolStatus>>(|db| {
413         let mut db = db.borrow_mut();
414         // delete_expired_attestation_keys is always safe to call, and will remove anything
415         // older than the date at the time of calling. No work should be done on the
416         // attestation keys unless the pool status is checked first, so this call should be
417         // enough to routinely clean out expired keys.
418         db.delete_expired_attestation_keys()?;
419         db.get_attestation_pool_status(expired_by, &uuid)
420     })
421 }
422 
423 impl binder::Interface for RemoteProvisioningService {}
424 
425 // Implementation of IRemoteProvisioning. See AIDL spec at
426 // :aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
427 impl IRemoteProvisioning for RemoteProvisioningService {
getPoolStatus( &self, expired_by: i64, sec_level: SecurityLevel, ) -> binder::public_api::Result<AttestationPoolStatus>428     fn getPoolStatus(
429         &self,
430         expired_by: i64,
431         sec_level: SecurityLevel,
432     ) -> binder::public_api::Result<AttestationPoolStatus> {
433         let _wp = wd::watch_millis("IRemoteProvisioning::getPoolStatus", 500);
434         map_or_log_err(get_pool_status(expired_by, sec_level), Ok)
435     }
436 
generateCsr( &self, test_mode: bool, num_csr: i32, eek: &[u8], challenge: &[u8], sec_level: SecurityLevel, protected_data: &mut ProtectedData, device_info: &mut DeviceInfo, ) -> binder::public_api::Result<Vec<u8>>437     fn generateCsr(
438         &self,
439         test_mode: bool,
440         num_csr: i32,
441         eek: &[u8],
442         challenge: &[u8],
443         sec_level: SecurityLevel,
444         protected_data: &mut ProtectedData,
445         device_info: &mut DeviceInfo,
446     ) -> binder::public_api::Result<Vec<u8>> {
447         let _wp = wd::watch_millis("IRemoteProvisioning::generateCsr", 500);
448         map_or_log_err(
449             self.generate_csr(
450                 test_mode,
451                 num_csr,
452                 eek,
453                 challenge,
454                 sec_level,
455                 protected_data,
456                 device_info,
457             ),
458             Ok,
459         )
460     }
461 
provisionCertChain( &self, public_key: &[u8], batch_cert: &[u8], certs: &[u8], expiration_date: i64, sec_level: SecurityLevel, ) -> binder::public_api::Result<()>462     fn provisionCertChain(
463         &self,
464         public_key: &[u8],
465         batch_cert: &[u8],
466         certs: &[u8],
467         expiration_date: i64,
468         sec_level: SecurityLevel,
469     ) -> binder::public_api::Result<()> {
470         let _wp = wd::watch_millis("IRemoteProvisioning::provisionCertChain", 500);
471         map_or_log_err(
472             self.provision_cert_chain(public_key, batch_cert, certs, expiration_date, sec_level),
473             Ok,
474         )
475     }
476 
generateKeyPair( &self, is_test_mode: bool, sec_level: SecurityLevel, ) -> binder::public_api::Result<()>477     fn generateKeyPair(
478         &self,
479         is_test_mode: bool,
480         sec_level: SecurityLevel,
481     ) -> binder::public_api::Result<()> {
482         let _wp = wd::watch_millis("IRemoteProvisioning::generateKeyPair", 500);
483         map_or_log_err(self.generate_key_pair(is_test_mode, sec_level), Ok)
484     }
485 
getImplementationInfo(&self) -> binder::public_api::Result<Vec<ImplInfo>>486     fn getImplementationInfo(&self) -> binder::public_api::Result<Vec<ImplInfo>> {
487         let _wp = wd::watch_millis("IRemoteProvisioning::getSecurityLevels", 500);
488         map_or_log_err(self.get_implementation_info(), Ok)
489     }
490 
deleteAllKeys(&self) -> binder::public_api::Result<i64>491     fn deleteAllKeys(&self) -> binder::public_api::Result<i64> {
492         let _wp = wd::watch_millis("IRemoteProvisioning::deleteAllKeys", 500);
493         map_or_log_err(self.delete_all_keys(), Ok)
494     }
495 }
496