Merge "Revert^2 "Cryptographic security for MAX_BOOT_LEVEL""
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index aff824b..af177be 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -86,6 +86,7 @@
     rustlibs: [
         "libandroid_logger",
         "libkeystore2_test_utils",
+        "libnix",
     ],
 }
 
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index f8259ea..46b71dd 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -268,7 +268,7 @@
 
     fn present_prompt(
         &self,
-        listener: &dyn IConfirmationCallback,
+        listener: &binder::Strong<dyn IConfirmationCallback>,
         prompt_text: &str,
         extra_data: &[u8],
         locale: &str,
@@ -327,7 +327,7 @@
         Ok(())
     }
 
-    fn cancel_prompt(&self, listener: &dyn IConfirmationCallback) -> Result<()> {
+    fn cancel_prompt(&self, listener: &binder::Strong<dyn IConfirmationCallback>) -> Result<()> {
         let mut state = self.state.lock().unwrap();
         let hal = match &mut state.session {
             None => {
@@ -358,7 +358,7 @@
 impl IProtectedConfirmation for ApcManager {
     fn presentPrompt(
         &self,
-        listener: &dyn IConfirmationCallback,
+        listener: &binder::Strong<dyn IConfirmationCallback>,
         prompt_text: &str,
         extra_data: &[u8],
         locale: &str,
@@ -369,7 +369,10 @@
             Ok,
         )
     }
-    fn cancelPrompt(&self, listener: &dyn IConfirmationCallback) -> BinderResult<()> {
+    fn cancelPrompt(
+        &self,
+        listener: &binder::Strong<dyn IConfirmationCallback>,
+    ) -> BinderResult<()> {
         map_or_log_err(self.cancel_prompt(listener), Ok)
     }
     fn isSupported(&self) -> BinderResult<bool> {
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
index 20a7458..4d0034a 100644
--- a/keystore2/src/async_task.rs
+++ b/keystore2/src/async_task.rs
@@ -197,7 +197,7 @@
             enum Action {
                 QueuedFn(Box<dyn FnOnce(&mut Shelf) + Send>),
                 IdleFns(Vec<Arc<dyn Fn(&mut Shelf) + Send + Sync>>),
-            };
+            }
             let mut done_idle = false;
 
             // When the worker starts, it takes the shelf and puts it on the stack.
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index f673d17..7a94600 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -41,6 +41,8 @@
 //! from the database module these functions take permission check
 //! callbacks.
 
+#![allow(clippy::needless_question_mark)]
+
 use crate::impl_metadata; // This is in db_utils.rs
 use crate::key_parameter::{KeyParameter, Tag};
 use crate::permission::KeyPermSet;
diff --git a/keystore2/src/id_rotation.rs b/keystore2/src/id_rotation.rs
new file mode 100644
index 0000000..dbf0fc9
--- /dev/null
+++ b/keystore2/src/id_rotation.rs
@@ -0,0 +1,122 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module implements the unique id rotation privacy feature. Certain system components
+//! have the ability to include a per-app unique id into the key attestation. The key rotation
+//! feature assures that the unique id is rotated on factory reset at least once in a 30 day
+//! key rotation period.
+//!
+//! It is assumed that the timestamp file does not exist after a factory reset. So the creation
+//! time of the timestamp file provides a lower bound for the time since factory reset.
+
+use anyhow::{Context, Result};
+use std::fs;
+use std::io::ErrorKind;
+use std::path::{Path, PathBuf};
+use std::time::Duration;
+
+const ID_ROTATION_PERIOD: Duration = Duration::from_secs(30 * 24 * 60 * 60); // Thirty days.
+static TIMESTAMP_FILE_NAME: &str = &"timestamp";
+
+/// The IdRotationState stores the path to the timestamp file for deferred usage. The data
+/// partition is usually not available when Keystore 2.0 starts up. So this object is created
+/// and passed down to the users of the feature which can then query the timestamp on demand.
+#[derive(Debug, Clone)]
+pub struct IdRotationState {
+    timestamp_path: PathBuf,
+}
+
+impl IdRotationState {
+    /// Creates a new IdRotationState. It holds the path to the timestamp file for deferred usage.
+    pub fn new(keystore_db_path: &Path) -> Self {
+        let mut timestamp_path = keystore_db_path.to_owned();
+        timestamp_path.push(TIMESTAMP_FILE_NAME);
+        Self { timestamp_path }
+    }
+
+    /// Reads the metadata of or creates the timestamp file. It returns true if the timestamp
+    /// file is younger than `ID_ROTATION_PERIOD`, i.e., 30 days.
+    pub fn had_factory_reset_since_id_rotation(&self) -> Result<bool> {
+        match fs::metadata(&self.timestamp_path) {
+            Ok(metadata) => {
+                let duration_since_factory_reset = metadata
+                    .modified()
+                    .context("File creation time not supported.")?
+                    .elapsed()
+                    .context("Failed to compute time elapsed since factory reset.")?;
+                Ok(duration_since_factory_reset < ID_ROTATION_PERIOD)
+            }
+            Err(e) => match e.kind() {
+                ErrorKind::NotFound => {
+                    fs::File::create(&self.timestamp_path)
+                        .context("Failed to create timestamp file.")?;
+                    Ok(true)
+                }
+                _ => Err(e).context("Failed to open timestamp file."),
+            },
+        }
+        .context("In had_factory_reset_since_id_rotation:")
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use keystore2_test_utils::TempDir;
+    use nix::sys::stat::utimes;
+    use nix::sys::time::{TimeVal, TimeValLike};
+    use std::convert::TryInto;
+    use std::time::UNIX_EPOCH;
+
+    #[test]
+    fn test_had_factory_reset_since_id_rotation() -> Result<()> {
+        let temp_dir = TempDir::new("test_had_factory_reset_since_id_rotation_")
+            .expect("Failed to create temp dir.");
+        let id_rotation_state = IdRotationState::new(&temp_dir.path());
+
+        let mut temp_file_path = temp_dir.path().to_owned();
+        temp_file_path.push(TIMESTAMP_FILE_NAME);
+
+        // The timestamp file should not exist.
+        assert!(!temp_file_path.exists());
+
+        // This should return true.
+        assert!(id_rotation_state.had_factory_reset_since_id_rotation()?);
+
+        // Now the timestamp file should exist.
+        assert!(temp_file_path.exists());
+
+        // We should still return true because the timestamp file is young.
+        assert!(id_rotation_state.had_factory_reset_since_id_rotation()?);
+
+        // Now let's age the timestamp file by backdating the modification time.
+        let metadata = fs::metadata(&temp_file_path)?;
+        let mtime = metadata.modified()?;
+        let mtime = mtime.duration_since(UNIX_EPOCH)?;
+        let mtime =
+            mtime.checked_sub(ID_ROTATION_PERIOD).expect("Failed to subtract id rotation period");
+        let mtime = TimeVal::seconds(mtime.as_secs().try_into().unwrap());
+
+        let atime = metadata.accessed()?;
+        let atime = atime.duration_since(UNIX_EPOCH)?;
+        let atime = TimeVal::seconds(atime.as_secs().try_into().unwrap());
+
+        utimes(&temp_file_path, &atime, &mtime)?;
+
+        // Now that the file has aged we should see false.
+        assert!(!id_rotation_state.had_factory_reset_since_id_rotation()?);
+
+        Ok(())
+    }
+}
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index e536e45..74a9b23 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -90,6 +90,8 @@
 //!  * The termination condition which has an empty in list.
 //!  * The public interface, which does not have @marker and calls itself with an empty out list.
 
+#![allow(clippy::from_over_into, clippy::needless_question_mark)]
+
 use std::convert::TryInto;
 
 use crate::db_utils::SqlField;
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index a6bbb2d..b95add2 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -14,13 +14,13 @@
 
 //! This crate implements the Keystore 2.0 service entry point.
 
-use keystore2::authorization::AuthorizationManager;
 use keystore2::entropy;
 use keystore2::globals::ENFORCEMENTS;
 use keystore2::maintenance::Maintenance;
 use keystore2::remote_provisioning::RemoteProvisioningService;
 use keystore2::service::KeystoreService;
 use keystore2::{apc::ApcManager, shared_secret_negotiation};
+use keystore2::{authorization::AuthorizationManager, id_rotation::IdRotationState};
 use log::{error, info};
 use std::{panic, path::Path, sync::mpsc::channel};
 use vpnprofilestore::VpnProfileStore;
@@ -57,12 +57,14 @@
     // startup as Keystore 1.0 did because Keystore 2.0 is intended to run much earlier than
     // Keystore 1.0. Instead we set a global variable to the database path.
     // For the ground truth check the service startup rule for init (typically in keystore2.rc).
-    if let Some(dir) = args.next() {
+    let id_rotation_state = if let Some(dir) = args.next() {
+        let db_path = Path::new(&dir);
         *keystore2::globals::DB_PATH.lock().expect("Could not lock DB_PATH.") =
-            Path::new(&dir).to_path_buf();
+            db_path.to_path_buf();
+        IdRotationState::new(&db_path)
     } else {
-        panic!("Must specify a working directory.");
-    }
+        panic!("Must specify a database directory.");
+    };
 
     let (confirmation_token_sender, confirmation_token_receiver) = channel();
 
@@ -74,7 +76,7 @@
     info!("Starting thread pool now.");
     binder::ProcessState::start_thread_pool();
 
-    let ks_service = KeystoreService::new_native_binder().unwrap_or_else(|e| {
+    let ks_service = KeystoreService::new_native_binder(id_rotation_state).unwrap_or_else(|e| {
         panic!("Failed to create service {} because of {:?}.", KS2_SERVICE_NAME, e);
     });
     binder::add_service(KS2_SERVICE_NAME, ks_service.as_binder()).unwrap_or_else(|e| {
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index c108b32..a3e440b 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -14,6 +14,8 @@
 
 //! This module implements methods to load legacy keystore key blob files.
 
+#![allow(clippy::redundant_slicing)]
+
 use crate::{
     error::{Error as KsError, ResponseCode},
     key_parameter::{KeyParameter, KeyParameterValue},
@@ -799,10 +801,18 @@
     /// encoded with UID prefix.
     fn list_user(&self, user_id: u32) -> Result<Vec<String>> {
         let path = self.make_user_path_name(user_id);
-        let dir =
-            Self::with_retry_interrupted(|| fs::read_dir(path.as_path())).with_context(|| {
-                format!("In list_user: Failed to open legacy blob database. {:?}", path)
-            })?;
+        let dir = match Self::with_retry_interrupted(|| fs::read_dir(path.as_path())) {
+            Ok(dir) => dir,
+            Err(e) => match e.kind() {
+                ErrorKind::NotFound => return Ok(Default::default()),
+                _ => {
+                    return Err(e).context(format!(
+                        "In list_user: Failed to open legacy blob database. {:?}",
+                        path
+                    ))
+                }
+            },
+        };
         let mut result: Vec<String> = Vec::new();
         for entry in dir {
             let file_name = entry.context("In list_user: Trying to access dir entry")?.file_name();
@@ -1138,7 +1148,7 @@
             let encoded = LegacyBlobLoader::encode_alias(&alias_str);
             let decoded = match LegacyBlobLoader::decode_alias(&encoded) {
                 Ok(d) => d,
-                Err(_) => panic!(format!("random_alias: {:x?}\nencoded {}", random_alias, encoded)),
+                Err(_) => panic!("random_alias: {:x?}\nencoded {}", random_alias, encoded),
             };
             assert_eq!(random_alias.to_vec(), decoded.bytes().collect::<Vec<u8>>());
         }
@@ -1350,4 +1360,14 @@
 
         Ok(())
     }
+
+    #[test]
+    fn list_non_existing_user() -> Result<()> {
+        let temp_dir = TempDir::new("list_non_existing_user")?;
+        let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+        assert!(legacy_blob_loader.list_user(20)?.is_empty());
+
+        Ok(())
+    }
 }
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 0916af1..f851d3a 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -25,6 +25,7 @@
 pub mod entropy;
 pub mod error;
 pub mod globals;
+pub mod id_rotation;
 /// Internal Representation of Key Parameter and convenience functions.
 pub mod key_parameter;
 pub mod legacy_blob;
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 45c4dc1..726c2ec 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -18,6 +18,8 @@
 //! It also provides KeystorePerm and KeyPerm as convenience wrappers for the SELinux permission
 //! defined by keystore2 and keystore2_key respectively.
 
+#![allow(clippy::from_over_into)]
+
 use android_system_keystore2::aidl::android::system::keystore2::{
     Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
 };
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index 1c757c9..f99805d 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -19,6 +19,8 @@
 //! certificate chains signed by some root authority and stored in a keystore SQLite
 //! DB.
 
+#![allow(clippy::from_over_into, clippy::needless_question_mark, clippy::vec_init_then_push)]
+
 use std::collections::HashMap;
 
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 65512f1..c654c02 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -14,7 +14,7 @@
 
 //! This crate implements the IKeystoreSecurityLevel interface.
 
-use crate::globals::get_keymint_device;
+use crate::{globals::get_keymint_device, id_rotation::IdRotationState};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, AttestationKey::AttestationKey,
     HardwareAuthenticatorType::HardwareAuthenticatorType, IKeyMintDevice::IKeyMintDevice,
@@ -67,6 +67,7 @@
     km_uuid: Uuid,
     operation_db: OperationDb,
     rem_prov_state: RemProvState,
+    id_rotation_state: IdRotationState,
 }
 
 // Blob of 32 zeroes used as empty masking key.
@@ -83,6 +84,7 @@
     /// we need it for checking keystore permissions.
     pub fn new_native_binder(
         security_level: SecurityLevel,
+        id_rotation_state: IdRotationState,
     ) -> Result<(Strong<dyn IKeystoreSecurityLevel>, Uuid)> {
         let (dev, hw_info, km_uuid) = get_keymint_device(&security_level)
             .context("In KeystoreSecurityLevel::new_native_binder.")?;
@@ -93,6 +95,7 @@
             km_uuid,
             operation_db: OperationDb::new(),
             rem_prov_state: RemProvState::new(security_level, km_uuid),
+            id_rotation_state,
         });
         result.as_binder().set_requesting_sid(true);
         Ok((result, km_uuid))
@@ -275,6 +278,12 @@
             },
         )?;
 
+        // Remove Tag::PURPOSE from the operation_parameters, since some keymaster devices return
+        // an error on begin() if Tag::PURPOSE is in the operation_parameters.
+        let op_params: Vec<KeyParameter> =
+            operation_parameters.iter().filter(|p| p.tag != Tag::PURPOSE).cloned().collect();
+        let operation_parameters = op_params.as_slice();
+
         let (immediate_hat, mut auth_info) = ENFORCEMENTS
             .authorize_create(
                 purpose,
@@ -353,6 +362,7 @@
     }
 
     fn add_certificate_parameters(
+        &self,
         uid: u32,
         params: &[KeyParameter],
         key: &KeyDescriptor,
@@ -374,6 +384,14 @@
                 "In add_certificate_parameters: ",
                 "Caller does not have the permission to generate a unique ID"
             ))?;
+            if self.id_rotation_state.had_factory_reset_since_id_rotation().context(
+                "In add_certificate_parameters: Call to had_factory_reset_since_id_rotation failed."
+            )? {
+                result.push(KeyParameter{
+                    tag: Tag::RESET_SINCE_ID_ROTATION,
+                    value: KeyParameterValue::BoolValue(true),
+                })
+            }
         }
 
         // If the caller requests any device identifier attestation tag, check that they hold the
@@ -451,7 +469,8 @@
                 })
                 .context("In generate_key: Trying to get an attestation key")?,
         };
-        let params = Self::add_certificate_parameters(caller_uid, params, &key)
+        let params = self
+            .add_certificate_parameters(caller_uid, params, &key)
             .context("In generate_key: Trying to get aaid.")?;
 
         let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
@@ -524,7 +543,8 @@
         // import_key requires the rebind permission.
         check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_key.")?;
 
-        let params = Self::add_certificate_parameters(caller_uid, params, &key)
+        let params = self
+            .add_certificate_parameters(caller_uid, params, &key)
             .context("In import_key: Trying to get aaid.")?;
 
         let format = params
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index 73bd526..1debe1b 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -17,7 +17,6 @@
 
 use std::collections::HashMap;
 
-use crate::error::{self, map_or_log_err, ErrorCode};
 use crate::permission::{KeyPerm, KeystorePerm};
 use crate::security_level::KeystoreSecurityLevel;
 use crate::utils::{
@@ -33,6 +32,10 @@
     database::{KeyEntryLoadBits, KeyType, SubComponentType},
     error::ResponseCode,
 };
+use crate::{
+    error::{self, map_or_log_err, ErrorCode},
+    id_rotation::IdRotationState,
+};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
 use android_system_keystore2::aidl::android::system::keystore2::{
     Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
@@ -53,21 +56,26 @@
 
 impl KeystoreService {
     /// Create a new instance of the Keystore 2.0 service.
-    pub fn new_native_binder() -> Result<Strong<dyn IKeystoreService>> {
+    pub fn new_native_binder(
+        id_rotation_state: IdRotationState,
+    ) -> Result<Strong<dyn IKeystoreService>> {
         let mut result: Self = Default::default();
-        let (dev, uuid) =
-            KeystoreSecurityLevel::new_native_binder(SecurityLevel::TRUSTED_ENVIRONMENT)
-                .context(concat!(
-                    "In KeystoreService::new_native_binder: ",
-                    "Trying to construct mandatory security level TEE."
-                ))
-                .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))?;
+        let (dev, uuid) = KeystoreSecurityLevel::new_native_binder(
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+            id_rotation_state.clone(),
+        )
+        .context(concat!(
+            "In KeystoreService::new_native_binder: ",
+            "Trying to construct mandatory security level TEE."
+        ))
+        .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))?;
         result.i_sec_level_by_uuid.insert(uuid, dev);
         result.uuid_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, uuid);
 
         // Strongbox is optional, so we ignore errors and turn the result into an Option.
-        if let Ok((dev, uuid)) = KeystoreSecurityLevel::new_native_binder(SecurityLevel::STRONGBOX)
-            .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))
+        if let Ok((dev, uuid)) =
+            KeystoreSecurityLevel::new_native_binder(SecurityLevel::STRONGBOX, id_rotation_state)
+                .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))
         {
             result.i_sec_level_by_uuid.insert(uuid, dev);
             result.uuid_by_sec_level.insert(SecurityLevel::STRONGBOX, uuid);
diff --git a/keystore2/vpnprofilestore/lib.rs b/keystore2/vpnprofilestore/lib.rs
index f92eacd..f5adc1b 100644
--- a/keystore2/vpnprofilestore/lib.rs
+++ b/keystore2/vpnprofilestore/lib.rs
@@ -39,6 +39,10 @@
         let mut db = Self {
             conn: Connection::open(db_file).context("Failed to initialize SQLite connection.")?,
         };
+
+        // On busy fail Immediately. It is unlikely to succeed given a bug in sqlite.
+        db.conn.busy_handler(None).context("Failed to set busy handler.")?;
+
         db.init_tables().context("Trying to initialize vpnstore db.")?;
         Ok(db)
     }
@@ -377,7 +381,12 @@
 mod db_test {
     use super::*;
     use keystore2_test_utils::TempDir;
+    use std::sync::Arc;
+    use std::thread;
+    use std::time::Duration;
+    use std::time::Instant;
 
+    static TEST_ALIAS: &str = &"test_alias";
     static TEST_BLOB1: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
     static TEST_BLOB2: &[u8] = &[2, 2, 3, 4, 5, 6, 7, 8, 9, 0];
     static TEST_BLOB3: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
@@ -440,4 +449,104 @@
             db.get(2, "test1").expect("Failed to get profile.").as_deref()
         );
     }
+
+    #[test]
+    fn concurrent_vpn_profile_test() -> Result<()> {
+        let temp_dir = Arc::new(
+            TempDir::new("concurrent_vpn_profile_test_").expect("Failed to create temp dir."),
+        );
+
+        let db_path = temp_dir.build().push("vpnprofile.sqlite").to_owned();
+
+        let test_begin = Instant::now();
+
+        let mut db = DB::new(&db_path).expect("Failed to open database.");
+        const PROFILE_COUNT: u32 = 5000u32;
+        const PROFILE_DB_COUNT: u32 = 5000u32;
+
+        let mut actual_profile_count = PROFILE_COUNT;
+        // First insert PROFILE_COUNT profiles.
+        for count in 0..PROFILE_COUNT {
+            if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
+                actual_profile_count = count;
+                break;
+            }
+            let alias = format!("test_alias_{}", count);
+            db.put(1, &alias, TEST_BLOB1).expect("Failed to add profile (1).");
+        }
+
+        // Insert more keys from a different thread and into a different namespace.
+        let db_path1 = db_path.clone();
+        let handle1 = thread::spawn(move || {
+            let mut db = DB::new(&db_path1).expect("Failed to open database.");
+
+            for count in 0..actual_profile_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let alias = format!("test_alias_{}", count);
+                db.put(2, &alias, TEST_BLOB2).expect("Failed to add profile (2).");
+            }
+
+            // Then delete them again.
+            for count in 0..actual_profile_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let alias = format!("test_alias_{}", count);
+                db.remove(2, &alias).expect("Remove Failed (2).");
+            }
+        });
+
+        // And start deleting the first set of profiles.
+        let db_path2 = db_path.clone();
+        let handle2 = thread::spawn(move || {
+            let mut db = DB::new(&db_path2).expect("Failed to open database.");
+
+            for count in 0..actual_profile_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let alias = format!("test_alias_{}", count);
+                db.remove(1, &alias).expect("Remove Failed (1)).");
+            }
+        });
+
+        // While a lot of inserting and deleting is going on we have to open database connections
+        // successfully and then insert and delete a specific profile.
+        let db_path3 = db_path.clone();
+        let handle3 = thread::spawn(move || {
+            for _count in 0..PROFILE_DB_COUNT {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let mut db = DB::new(&db_path3).expect("Failed to open database.");
+
+                db.put(3, &TEST_ALIAS, TEST_BLOB3).expect("Failed to add profile (3).");
+
+                db.remove(3, &TEST_ALIAS).expect("Remove failed (3).");
+            }
+        });
+
+        // While thread 3 is inserting and deleting TEST_ALIAS, we try to get the alias.
+        // This may yield an entry or none, but it must not fail.
+        let handle4 = thread::spawn(move || {
+            for _count in 0..PROFILE_DB_COUNT {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let mut db = DB::new(&db_path).expect("Failed to open database.");
+
+                // This may return Some or None but it must not fail.
+                db.get(3, &TEST_ALIAS).expect("Failed to get profile (4).");
+            }
+        });
+
+        handle1.join().expect("Thread 1 panicked.");
+        handle2.join().expect("Thread 2 panicked.");
+        handle3.join().expect("Thread 3 panicked.");
+        handle4.join().expect("Thread 4 panicked.");
+
+        Ok(())
+    }
 }
diff --git a/ondevice-signing/KeystoreKey.cpp b/ondevice-signing/KeystoreKey.cpp
index 840b683..9b5e505 100644
--- a/ondevice-signing/KeystoreKey.cpp
+++ b/ondevice-signing/KeystoreKey.cpp
@@ -239,5 +239,10 @@
 }
 
 Result<std::vector<uint8_t>> KeystoreKey::getPublicKey() const {
-    return extractPublicKeyFromX509(mKeyMetadata.certificate.value());
+    auto cert = mKeyMetadata.certificate;
+    if (cert) {
+        return extractPublicKeyFromX509(cert.value());
+    } else {
+        return Error() << "Key did not have a certificate";
+    }
 }