Biometric support for UNLOCKED_DEVICE_REQUIRED
When the device is locked, keystore is passed a list of biometric
SIDs which should allow unlock of UNLOCKED_DEVICE_REQUIRED keys.
It creates a KM key protected by these SIDs and uses it to encrypt
the UNLOCKED_DEVICE_REQUIRED secrets, and uses this key to recover
those secrets when the device is unlocked.
Test: aosp/1686345
Bug: 163866361
Change-Id: Ic73ed0089cd9567a83c38aed61e20215862aa0be
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index cdec79b..06432fe 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -25,12 +25,13 @@
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,
+ BeginResult::BeginResult, ErrorCode::ErrorCode, HardwareAuthToken::HardwareAuthToken,
+ IKeyMintDevice::IKeyMintDevice, IKeyMintOperation::IKeyMintOperation,
+ KeyCreationResult::KeyCreationResult, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
SecurityLevel::SecurityLevel,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+ Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
};
use anyhow::{Context, Result};
use binder::Strong;
@@ -57,23 +58,26 @@
Ok(KeyMintDevice { asp, km_uuid })
}
- /// Generate a KM key and store in the database.
- fn generate_and_store_key(
+ /// Create a KM key and store in the database.
+ pub fn create_and_store_key<F>(
&self,
db: &mut KeystoreDB,
key_desc: &KeyDescriptor,
- params: &[KeyParameter],
- ) -> Result<()> {
+ creator: F,
+ ) -> Result<()>
+ where
+ F: FnOnce(Strong<dyn IKeyMintDevice>) -> Result<KeyCreationResult, binder::Status>,
+ {
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")?;
+ .context("In create_and_store_key: Failed to get KeyMint device")?;
+ let creation_result =
+ map_km_error(creator(km_dev)).context("In create_and_store_key: creator 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")?;
+ DateTime::now().context("In create_and_store_key: DateTime::now() failed")?;
let mut key_metadata = KeyMetaData::new();
key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
@@ -88,10 +92,44 @@
&key_metadata,
&self.km_uuid,
)
- .context("In generate_and_store_key: store_new_key failed")?;
+ .context("In create_and_store_key: store_new_key failed")?;
Ok(())
}
+ /// Generate a KeyDescriptor for internal-use keys.
+ pub fn internal_descriptor(alias: String) -> KeyDescriptor {
+ KeyDescriptor {
+ domain: Domain::APP,
+ nspace: AID_KEYSTORE as i64,
+ alias: Some(alias),
+ blob: None,
+ }
+ }
+
+ /// Look up an internal-use key in the database given a key descriptor.
+ pub fn lookup_from_desc(
+ db: &mut KeystoreDB,
+ key_desc: &KeyDescriptor,
+ ) -> Result<(KeyIdGuard, KeyEntry)> {
+ db.load_key_entry(&key_desc, KeyType::Client, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| {
+ Ok(())
+ })
+ .context("In lookup_from_desc: load_key_entry failed")
+ }
+
+ /// Look up the key in the database, and return None if it is absent.
+ pub fn not_found_is_none(
+ lookup: Result<(KeyIdGuard, KeyEntry)>,
+ ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+ match lookup {
+ Ok(result) => Ok(Some(result)),
+ Err(e) => match e.root_cause().downcast_ref::<Error>() {
+ Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(None),
+ _ => Err(e),
+ },
+ }
+ }
+
/// This does the lookup and store in separate transactions; caller must
/// hold a lock before calling.
pub fn lookup_or_generate_key(
@@ -105,26 +143,16 @@
// - 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),
- },
+ let lookup = Self::not_found_is_none(Self::lookup_from_desc(db, key_desc))
+ .context("In lookup_or_generate_key: first lookup failed")?;
+ if let Some(result) = lookup {
+ Ok(result)
+ } else {
+ self.create_and_store_key(db, &key_desc, |km_dev| km_dev.generateKey(¶ms, None))
+ .context("In lookup_or_generate_key: generate_and_store_key failed")?;
+ Self::lookup_from_desc(db, key_desc)
+ .context("In lookup_or_generate_key: secpnd lookup failed")
}
- 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
@@ -133,7 +161,7 @@
&self,
db: &mut KeystoreDB,
km_dev: &Strong<dyn IKeyMintDevice>,
- key_id_guard: KeyIdGuard,
+ key_id_guard: &KeyIdGuard,
key_blob: &KeyBlob,
f: F,
) -> Result<T>
@@ -149,7 +177,7 @@
new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
db.set_blob(
- &key_id_guard,
+ key_id_guard,
SubComponentType::KEY_BLOB,
Some(&upgraded_blob),
Some(&new_blob_metadata),
@@ -170,13 +198,15 @@
/// Use the created key in an operation that can be done with
/// a call to begin followed by a call to finish.
+ #[allow(clippy::too_many_arguments)]
pub fn use_key_in_one_step(
&self,
db: &mut KeystoreDB,
- key_id_guard: KeyIdGuard,
+ key_id_guard: &KeyIdGuard,
key_entry: &KeyEntry,
purpose: KeyPurpose,
operation_parameters: &[KeyParameter],
+ auth_token: Option<&HardwareAuthToken>,
input: &[u8],
) -> Result<Vec<u8>> {
let km_dev: Strong<dyn IKeyMintDevice> = self
@@ -193,7 +223,7 @@
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))
+ map_km_error(km_dev.begin(purpose, blob, operation_parameters, auth_token))
})
.context("In use_key_in_one_step: Failed to begin operation.")?;
let operation: Strong<dyn IKeyMintOperation> = begin_result