Move KeyMintDevice into its own module
Biometric unlock will also need raw access to the device,
so create a separate module to that end.
Bug: 163866361
Test: keystore2_test
Change-Id: Ia3f10541fa668f606bbcef6df46adfc1ddda5319
diff --git a/keystore2/src/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
index 686f5c4..a658c02 100644
--- a/keystore2/src/boot_level_keys.rs
+++ b/keystore2/src/boot_level_keys.rs
@@ -15,199 +15,20 @@
//! Offer keys based on the "boot level" for superencryption.
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, BeginResult::BeginResult, Digest::Digest, ErrorCode::ErrorCode,
- IKeyMintDevice::IKeyMintDevice, IKeyMintOperation::IKeyMintOperation,
- KeyParameter::KeyParameter, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+ Algorithm::Algorithm, Digest::Digest, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
};
use anyhow::{Context, Result};
-use binder::Strong;
use keystore2_crypto::{hkdf_expand, ZVec, AES_256_KEY_LENGTH};
use std::{collections::VecDeque, convert::TryFrom};
use crate::{
- database::{
- BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, KeyEntry, KeyEntryLoadBits,
- KeyIdGuard, KeyMetaData, KeyMetaEntry, KeyType, KeystoreDB, SubComponentType, Uuid,
- },
- error::{map_km_error, Error},
- globals::get_keymint_device,
- key_parameter::KeyParameterValue,
- super_key::KeyBlob,
- utils::{key_characteristics_to_internal, Asp, AID_KEYSTORE},
+ database::KeystoreDB, key_parameter::KeyParameterValue, raw_device::KeyMintDevice,
+ utils::AID_KEYSTORE,
};
-/// Wrapper for operating directly on a KeyMint device.
-/// These methods often mirror methods in [`crate::security_level`]. However
-/// the functions in [`crate::security_level`] make assumptions that hold, and has side effects
-/// that make sense, only if called by an external client through binder.
-/// In addition we are trying to maintain a separation between interface services
-/// so that the architecture is compatible with a future move to multiple thread pools.
-/// So the simplest approach today is to write new implementations of them for internal use.
-/// Because these methods run very early, we don't even try to cooperate with
-/// the operation slot database; we assume there will be plenty of slots.
-struct KeyMintDevice {
- asp: Asp,
- km_uuid: Uuid,
-}
-
-impl KeyMintDevice {
- fn get(security_level: SecurityLevel) -> Result<KeyMintDevice> {
- let (asp, _hw_info, km_uuid) = get_keymint_device(&security_level)
- .context("In KeyMintDevice::get: get_keymint_device failed")?;
- Ok(KeyMintDevice { asp, km_uuid })
- }
-
- /// Generate a KM key and store in the database.
- fn generate_and_store_key(
- &self,
- db: &mut KeystoreDB,
- key_desc: &KeyDescriptor,
- params: &[KeyParameter],
- ) -> Result<()> {
- let km_dev: Strong<dyn IKeyMintDevice> = self
- .asp
- .get_interface()
- .context("In generate_and_store_key: Failed to get KeyMint device")?;
- let creation_result = map_km_error(km_dev.generateKey(params, None))
- .context("In generate_and_store_key: generateKey failed")?;
- let key_parameters = key_characteristics_to_internal(creation_result.keyCharacteristics);
-
- let creation_date =
- DateTime::now().context("In generate_and_store_key: DateTime::now() failed")?;
-
- let mut key_metadata = KeyMetaData::new();
- key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
-
- db.store_new_key(
- &key_desc,
- &key_parameters,
- &(&creation_result.keyBlob, &blob_metadata),
- &CertificateInfo::new(None, None),
- &key_metadata,
- &self.km_uuid,
- )
- .context("In generate_and_store_key: store_new_key failed")?;
- Ok(())
- }
-
- /// This does the lookup and store in separate transactions; caller must
- /// hold a lock before calling.
- fn lookup_or_generate_key(
- &self,
- db: &mut KeystoreDB,
- key_desc: &KeyDescriptor,
- params: &[KeyParameter],
- ) -> Result<(KeyIdGuard, KeyEntry)> {
- // We use a separate transaction for the lookup than for the store
- // - to keep the code simple
- // - because the caller needs to hold a lock in any case
- // - because it avoids holding database locks during slow
- // KeyMint operations
- let lookup = db.load_key_entry(
- &key_desc,
- KeyType::Client,
- KeyEntryLoadBits::KM,
- AID_KEYSTORE,
- |_, _| Ok(()),
- );
- match lookup {
- Ok(result) => return Ok(result),
- Err(e) => match e.root_cause().downcast_ref::<Error>() {
- Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => {}
- _ => return Err(e),
- },
- }
- self.generate_and_store_key(db, &key_desc, ¶ms)
- .context("In lookup_or_generate_key: generate_and_store_key failed")?;
- db.load_key_entry(&key_desc, KeyType::Client, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| {
- Ok(())
- })
- .context("In lookup_or_generate_key: load_key_entry failed")
- }
-
- /// Call the passed closure; if it returns `KEY_REQUIRES_UPGRADE`, call upgradeKey, and
- /// write the upgraded key to the database.
- fn upgrade_keyblob_if_required_with<T, F>(
- &self,
- db: &mut KeystoreDB,
- km_dev: &Strong<dyn IKeyMintDevice>,
- key_id_guard: KeyIdGuard,
- key_blob: &KeyBlob,
- f: F,
- ) -> Result<T>
- where
- F: Fn(&[u8]) -> Result<T, Error>,
- {
- match f(key_blob) {
- Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
- let upgraded_blob = map_km_error(km_dev.upgradeKey(key_blob, &[]))
- .context("In upgrade_keyblob_if_required_with: Upgrade failed")?;
-
- let mut new_blob_metadata = BlobMetaData::new();
- new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
-
- db.set_blob(
- &key_id_guard,
- SubComponentType::KEY_BLOB,
- Some(&upgraded_blob),
- Some(&new_blob_metadata),
- )
- .context(concat!(
- "In upgrade_keyblob_if_required_with: ",
- "Failed to insert upgraded blob into the database"
- ))?;
-
- Ok(f(&upgraded_blob).context(concat!(
- "In upgrade_keyblob_if_required_with: ",
- "Closure failed after upgrade"
- ))?)
- }
- result => Ok(result.context("In upgrade_keyblob_if_required_with: Closure failed")?),
- }
- }
-
- /// Use the created key in an operation that can be done with
- /// a call to begin followed by a call to finish.
- fn use_key_in_one_step(
- &self,
- db: &mut KeystoreDB,
- key_id_guard: KeyIdGuard,
- key_entry: &KeyEntry,
- purpose: KeyPurpose,
- operation_parameters: &[KeyParameter],
- input: &[u8],
- ) -> Result<Vec<u8>> {
- let km_dev: Strong<dyn IKeyMintDevice> = self
- .asp
- .get_interface()
- .context("In use_key_in_one_step: Failed to get KeyMint device")?;
-
- let (key_blob, _blob_metadata) = key_entry
- .key_blob_info()
- .as_ref()
- .ok_or_else(Error::sys)
- .context("use_key_in_one_step: Keyblob missing")?;
- let key_blob = KeyBlob::Ref(&key_blob);
-
- let begin_result: BeginResult = self
- .upgrade_keyblob_if_required_with(db, &km_dev, key_id_guard, &key_blob, |blob| {
- map_km_error(km_dev.begin(purpose, blob, operation_parameters, None))
- })
- .context("In use_key_in_one_step: Failed to begin operation.")?;
- let operation: Strong<dyn IKeyMintOperation> = begin_result
- .operation
- .ok_or_else(Error::sys)
- .context("In use_key_in_one_step: Operation missing")?;
- map_km_error(operation.finish(Some(input), None, None, None, None))
- .context("In use_key_in_one_step: Failed to finish operation.")
- }
-}
-
/// This is not thread safe; caller must hold a lock before calling.
/// In practice the caller is SuperKeyManager and the lock is the
/// Mutex on its internal state.
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index f851d3a..4000b7f 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -34,6 +34,7 @@
pub mod metrics;
pub mod operation;
pub mod permission;
+pub mod raw_device;
pub mod remote_provisioning;
pub mod security_level;
pub mod service;
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
new file mode 100644
index 0000000..cdec79b
--- /dev/null
+++ b/keystore2/src/raw_device.rs
@@ -0,0 +1,206 @@
+// 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.
+
+//! Provide the [`KeyMintDevice`] wrapper for operating directly on a KeyMint device.
+
+use crate::{
+ database::{
+ BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, KeyEntry, KeyEntryLoadBits,
+ KeyIdGuard, KeyMetaData, KeyMetaEntry, KeyType, KeystoreDB, SubComponentType, Uuid,
+ },
+ error::{map_km_error, Error},
+ globals::get_keymint_device,
+ super_key::KeyBlob,
+ utils::{key_characteristics_to_internal, Asp, AID_KEYSTORE},
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ BeginResult::BeginResult, ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice,
+ IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
+ SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+use anyhow::{Context, Result};
+use binder::Strong;
+
+/// Wrapper for operating directly on a KeyMint device.
+/// These methods often mirror methods in [`crate::security_level`]. However
+/// the functions in [`crate::security_level`] make assumptions that hold, and has side effects
+/// that make sense, only if called by an external client through binder.
+/// In addition we are trying to maintain a separation between interface services
+/// so that the architecture is compatible with a future move to multiple thread pools.
+/// So the simplest approach today is to write new implementations of them for internal use.
+/// Because these methods run very early, we don't even try to cooperate with
+/// the operation slot database; we assume there will be plenty of slots.
+pub struct KeyMintDevice {
+ asp: Asp,
+ km_uuid: Uuid,
+}
+
+impl KeyMintDevice {
+ /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`]
+ pub fn get(security_level: SecurityLevel) -> Result<KeyMintDevice> {
+ let (asp, _hw_info, km_uuid) = get_keymint_device(&security_level)
+ .context("In KeyMintDevice::get: get_keymint_device failed")?;
+ Ok(KeyMintDevice { asp, km_uuid })
+ }
+
+ /// Generate a KM key and store in the database.
+ fn generate_and_store_key(
+ &self,
+ db: &mut KeystoreDB,
+ key_desc: &KeyDescriptor,
+ params: &[KeyParameter],
+ ) -> Result<()> {
+ let km_dev: Strong<dyn IKeyMintDevice> = self
+ .asp
+ .get_interface()
+ .context("In generate_and_store_key: Failed to get KeyMint device")?;
+ let creation_result = map_km_error(km_dev.generateKey(params, None))
+ .context("In generate_and_store_key: generateKey failed")?;
+ let key_parameters = key_characteristics_to_internal(creation_result.keyCharacteristics);
+
+ let creation_date =
+ DateTime::now().context("In generate_and_store_key: DateTime::now() failed")?;
+
+ let mut key_metadata = KeyMetaData::new();
+ key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
+
+ db.store_new_key(
+ &key_desc,
+ &key_parameters,
+ &(&creation_result.keyBlob, &blob_metadata),
+ &CertificateInfo::new(None, None),
+ &key_metadata,
+ &self.km_uuid,
+ )
+ .context("In generate_and_store_key: store_new_key failed")?;
+ Ok(())
+ }
+
+ /// This does the lookup and store in separate transactions; caller must
+ /// hold a lock before calling.
+ pub fn lookup_or_generate_key(
+ &self,
+ db: &mut KeystoreDB,
+ key_desc: &KeyDescriptor,
+ params: &[KeyParameter],
+ ) -> Result<(KeyIdGuard, KeyEntry)> {
+ // We use a separate transaction for the lookup than for the store
+ // - to keep the code simple
+ // - because the caller needs to hold a lock in any case
+ // - because it avoids holding database locks during slow
+ // KeyMint operations
+ let lookup = db.load_key_entry(
+ &key_desc,
+ KeyType::Client,
+ KeyEntryLoadBits::KM,
+ AID_KEYSTORE,
+ |_, _| Ok(()),
+ );
+ match lookup {
+ Ok(result) => return Ok(result),
+ Err(e) => match e.root_cause().downcast_ref::<Error>() {
+ Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => {}
+ _ => return Err(e),
+ },
+ }
+ self.generate_and_store_key(db, &key_desc, ¶ms)
+ .context("In lookup_or_generate_key: generate_and_store_key failed")?;
+ db.load_key_entry(&key_desc, KeyType::Client, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| {
+ Ok(())
+ })
+ .context("In lookup_or_generate_key: load_key_entry failed")
+ }
+
+ /// Call the passed closure; if it returns `KEY_REQUIRES_UPGRADE`, call upgradeKey, and
+ /// write the upgraded key to the database.
+ fn upgrade_keyblob_if_required_with<T, F>(
+ &self,
+ db: &mut KeystoreDB,
+ km_dev: &Strong<dyn IKeyMintDevice>,
+ key_id_guard: KeyIdGuard,
+ key_blob: &KeyBlob,
+ f: F,
+ ) -> Result<T>
+ where
+ F: Fn(&[u8]) -> Result<T, Error>,
+ {
+ match f(key_blob) {
+ Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+ let upgraded_blob = map_km_error(km_dev.upgradeKey(key_blob, &[]))
+ .context("In upgrade_keyblob_if_required_with: Upgrade failed")?;
+
+ let mut new_blob_metadata = BlobMetaData::new();
+ new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
+
+ db.set_blob(
+ &key_id_guard,
+ SubComponentType::KEY_BLOB,
+ Some(&upgraded_blob),
+ Some(&new_blob_metadata),
+ )
+ .context(concat!(
+ "In upgrade_keyblob_if_required_with: ",
+ "Failed to insert upgraded blob into the database"
+ ))?;
+
+ Ok(f(&upgraded_blob).context(concat!(
+ "In upgrade_keyblob_if_required_with: ",
+ "Closure failed after upgrade"
+ ))?)
+ }
+ result => Ok(result.context("In upgrade_keyblob_if_required_with: Closure failed")?),
+ }
+ }
+
+ /// Use the created key in an operation that can be done with
+ /// a call to begin followed by a call to finish.
+ pub fn use_key_in_one_step(
+ &self,
+ db: &mut KeystoreDB,
+ key_id_guard: KeyIdGuard,
+ key_entry: &KeyEntry,
+ purpose: KeyPurpose,
+ operation_parameters: &[KeyParameter],
+ input: &[u8],
+ ) -> Result<Vec<u8>> {
+ let km_dev: Strong<dyn IKeyMintDevice> = self
+ .asp
+ .get_interface()
+ .context("In use_key_in_one_step: Failed to get KeyMint device")?;
+
+ let (key_blob, _blob_metadata) = key_entry
+ .key_blob_info()
+ .as_ref()
+ .ok_or_else(Error::sys)
+ .context("use_key_in_one_step: Keyblob missing")?;
+ let key_blob = KeyBlob::Ref(&key_blob);
+
+ let begin_result: BeginResult = self
+ .upgrade_keyblob_if_required_with(db, &km_dev, key_id_guard, &key_blob, |blob| {
+ map_km_error(km_dev.begin(purpose, blob, operation_parameters, None))
+ })
+ .context("In use_key_in_one_step: Failed to begin operation.")?;
+ let operation: Strong<dyn IKeyMintOperation> = begin_result
+ .operation
+ .ok_or_else(Error::sys)
+ .context("In use_key_in_one_step: Operation missing")?;
+ map_km_error(operation.finish(Some(input), None, None, None, None))
+ .context("In use_key_in_one_step: Failed to finish operation.")
+ }
+}