Merge "Return the auth token to begin call when timestamp token is required."
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index dfc1db0..8d0f4e7 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -50,6 +50,7 @@
"liblazy_static",
"liblibc",
"liblibsqlite3_sys",
+ "liblog_event_list",
"liblog_rust",
"librand",
"librusqlite",
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
index 01616b1..3f33431 100644
--- a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -27,7 +27,6 @@
*/
@SensitiveData
interface IKeystoreAuthorization {
-
/**
* Allows the Android authenticators to hand over an auth token to Keystore.
* Callers require 'AddAuth' permission.
@@ -58,9 +57,29 @@
* @param userId - Android user id
*
* @param password - synthetic password derived by the user denoted by the user id
+ *
+ * @param unlockingSids - list of biometric SIDs for this user. This will be null when
+ * lockScreenEvent is UNLOCK, but may be non-null when
+ * lockScreenEvent is LOCK.
+ *
+ * When the device is unlocked, Keystore stores in memory
+ * a super-encryption key that protects UNLOCKED_DEVICE_REQUIRED
+ * keys; this key is wiped from memory when the device is locked.
+ *
+ * If unlockingSids is non-empty on lock, then before the
+ * super-encryption key is wiped from memory, a copy of it
+ * is stored in memory encrypted with a fresh AES key.
+ * This key is then imported into KM, tagged such that it can be
+ * used given a valid, recent auth token for any of the
+ * unlockingSids.
+ *
+ * Then, when the device is unlocked again, if a suitable auth token
+ * has been sent to keystore, it is used to recover the
+ * super-encryption key, so that UNLOCKED_DEVICE_REQUIRED keys can
+ * be used once again.
*/
void onLockScreenEvent(in LockScreenEvent lockScreenEvent, in int userId,
- in @nullable byte[] password);
+ in @nullable byte[] password, in @nullable long[] unlockingSids);
/**
* Allows Credstore to retrieve a HardwareAuthToken and a TimestampToken.
diff --git a/keystore2/src/audit_log.rs b/keystore2/src/audit_log.rs
new file mode 100644
index 0000000..30fc155
--- /dev/null
+++ b/keystore2/src/audit_log.rs
@@ -0,0 +1,68 @@
+// 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 functions to log audit events to binary security log buffer for NIAP
+//! compliance.
+
+use crate::globals::LOGS_HANDLER;
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+use libc::uid_t;
+use log_event_list::{LogContext, LogIdSecurity};
+
+const TAG_KEY_GENERATED: u32 = 210024;
+const TAG_KEY_IMPORTED: u32 = 210025;
+const TAG_KEY_DESTROYED: u32 = 210026;
+
+const NAMESPACE_MASK: i64 = 0x80000000;
+
+/// For app domain returns calling app uid, for SELinux domain returns masked namespace.
+fn key_owner(key: &KeyDescriptor, calling_app: uid_t) -> i32 {
+ match key.domain {
+ Domain::APP => calling_app as i32,
+ Domain::SELINUX => (key.nspace | NAMESPACE_MASK) as i32,
+ _ => {
+ log::info!("Not logging audit event for key with unexpected domain");
+ 0
+ }
+ }
+}
+
+/// Logs key generation event to NIAP audit log.
+pub fn log_key_generated(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+ log_key_event(TAG_KEY_GENERATED, key, calling_app, success);
+}
+
+/// Logs key import event to NIAP audit log.
+pub fn log_key_imported(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+ log_key_event(TAG_KEY_IMPORTED, key, calling_app, success);
+}
+
+/// Logs key deletion event to NIAP audit log.
+pub fn log_key_deleted(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+ log_key_event(TAG_KEY_DESTROYED, key, calling_app, success);
+}
+
+fn log_key_event(tag: u32, key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+ if let Some(ctx) = LogContext::new(LogIdSecurity, tag) {
+ let event = ctx
+ .append_i32(if success { 1 } else { 0 })
+ .append_str(key.alias.as_ref().map_or("none", String::as_str))
+ .append_i32(key_owner(key, calling_app));
+ LOGS_HANDLER.queue_lo(move |_| {
+ event.write();
+ });
+ }
+}
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index ec1edff..cac75c0 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -130,7 +130,15 @@
lock_screen_event: LockScreenEvent,
user_id: i32,
password: Option<Password>,
+ unlocking_sids: Option<&[i64]>,
) -> Result<()> {
+ log::info!(
+ "on_lock_screen_event({:?}, user_id={:?}, password.is_some()={}, unlocking_sids={:?})",
+ lock_screen_event,
+ user_id,
+ password.is_some(),
+ unlocking_sids
+ );
match (lock_screen_event, password) {
(LockScreenEvent::UNLOCK, Some(password)) => {
// This corresponds to the unlock() method in legacy keystore API.
@@ -172,14 +180,23 @@
check_keystore_permission(KeystorePerm::unlock())
.context("In on_lock_screen_event: Unlock.")?;
ENFORCEMENTS.set_device_locked(user_id, false);
+ DB.with(|db| {
+ SUPER_KEY.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32)
+ })
+ .context("In on_lock_screen_event: try_unlock_user_with_biometric failed")?;
Ok(())
}
(LockScreenEvent::LOCK, None) => {
check_keystore_permission(KeystorePerm::lock())
.context("In on_lock_screen_event: Lock")?;
ENFORCEMENTS.set_device_locked(user_id, true);
- SUPER_KEY.lock_screen_lock_bound_key(user_id as u32);
-
+ DB.with(|db| {
+ SUPER_KEY.lock_screen_lock_bound_key(
+ &mut db.borrow_mut(),
+ user_id as u32,
+ unlocking_sids.unwrap_or(&[]),
+ );
+ });
Ok(())
}
_ => {
@@ -225,9 +242,15 @@
lock_screen_event: LockScreenEvent,
user_id: i32,
password: Option<&[u8]>,
+ unlocking_sids: Option<&[i64]>,
) -> BinderResult<()> {
map_or_log_err(
- self.on_lock_screen_event(lock_screen_event, user_id, password.map(|pw| pw.into())),
+ self.on_lock_screen_event(
+ lock_screen_event,
+ user_id,
+ password.map(|pw| pw.into()),
+ unlocking_sids,
+ ),
Ok,
)
}
diff --git a/keystore2/src/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
index dd69ed7..3084195 100644
--- a/keystore2/src/boot_level_keys.rs
+++ b/keystore2/src/boot_level_keys.rs
@@ -15,209 +15,19 @@
//! 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,
-};
-use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+ Algorithm::Algorithm, Digest::Digest, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
};
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},
-};
-
-/// 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, &Default::default()))
- })
- .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.")
- }
-}
+use crate::{database::KeystoreDB, key_parameter::KeyParameterValue, raw_device::KeyMintDevice};
/// 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.
pub fn get_level_zero_key(db: &mut KeystoreDB) -> Result<ZVec> {
- let key_desc = KeyDescriptor {
- domain: Domain::APP,
- nspace: AID_KEYSTORE as i64,
- alias: Some("boot_level_key".to_string()),
- blob: None,
- };
+ let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string());
let params = [
KeyParameterValue::Algorithm(Algorithm::HMAC).into(),
KeyParameterValue::Digest(Digest::SHA_2_256).into(),
@@ -239,10 +49,11 @@
let level_zero_key = km_dev
.use_key_in_one_step(
db,
- key_id_guard,
+ &key_id_guard,
&key_entry,
KeyPurpose::SIGN,
¶ms,
+ None,
b"Create boot level key",
)
.context("In get_level_zero_key: use_key_in_one_step failed")?;
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 3523a9d..db23d1f 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -30,7 +30,7 @@
pub use zvec::ZVec;
/// Length of the expected initialization vector.
-pub const IV_LENGTH: usize = 16;
+pub const GCM_IV_LENGTH: usize = 12;
/// Length of the expected AEAD TAG.
pub const TAG_LENGTH: usize = 16;
/// Length of an AES 256 key in bytes.
@@ -40,9 +40,9 @@
/// Length of the expected salt for key from password generation.
pub const SALT_LENGTH: usize = 16;
-// This is the number of bytes of the GCM IV that is expected to be initialized
-// with random bytes.
-const GCM_IV_LENGTH: usize = 12;
+/// Older versions of keystore produced IVs with four extra
+/// ignored zero bytes at the end; recognise and trim those.
+pub const LEGACY_IV_LENGTH: usize = 16;
/// Generate an AES256 key, essentially 32 random bytes from the underlying
/// boringssl library discretely stuffed into a ZVec.
@@ -80,10 +80,13 @@
/// freed. Input key is taken as a slice for flexibility, but it is recommended that it is held
/// in a ZVec as well.
pub fn aes_gcm_decrypt(data: &[u8], iv: &[u8], tag: &[u8], key: &[u8]) -> Result<ZVec, Error> {
- if iv.len() != IV_LENGTH {
- return Err(Error::InvalidIvLength);
- }
-
+ // Old versions of aes_gcm_encrypt produced 16 byte IVs, but the last four bytes were ignored
+ // so trim these to the correct size.
+ let iv = match iv.len() {
+ GCM_IV_LENGTH => iv,
+ LEGACY_IV_LENGTH => &iv[..GCM_IV_LENGTH],
+ _ => return Err(Error::InvalidIvLength),
+ };
if tag.len() != TAG_LENGTH {
return Err(Error::InvalidAeadTagLength);
}
@@ -96,8 +99,8 @@
let mut result = ZVec::new(data.len())?;
// Safety: The first two arguments must point to buffers with a size given by the third
- // argument. The key must have a size of 16 or 32 bytes which we check above.
- // The iv and tag arguments must be 16 bytes, which we also check above.
+ // argument. We pass the length of the key buffer along with the key.
+ // The `iv` buffer must be 12 bytes and the `tag` buffer 16, which we check above.
match unsafe {
AES_gcm_decrypt(
data.as_ptr(),
@@ -118,10 +121,9 @@
/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based on
/// the key length. The function generates an initialization vector. The return value is a tuple
/// of `(ciphertext, iv, tag)`.
-pub fn aes_gcm_encrypt(data: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), Error> {
- let mut iv = vec![0; IV_LENGTH];
- // Safety: iv is longer than GCM_IV_LENGTH, which is 12 while IV_LENGTH is 16.
- // The iv needs to be 16 bytes long, but the last 4 bytes remain zeroed.
+pub fn aes_gcm_encrypt(plaintext: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), Error> {
+ let mut iv = vec![0; GCM_IV_LENGTH];
+ // Safety: iv is GCM_IV_LENGTH bytes long.
if !unsafe { randomBytes(iv.as_mut_ptr(), GCM_IV_LENGTH) } {
return Err(Error::RandomNumberGenerationFailed);
}
@@ -131,21 +133,25 @@
_ => return Err(Error::InvalidKeyLength),
}
- let mut result: Vec<u8> = vec![0; data.len()];
+ let mut ciphertext: Vec<u8> = vec![0; plaintext.len()];
let mut tag: Vec<u8> = vec![0; TAG_LENGTH];
- match unsafe {
+ // Safety: The first two arguments must point to buffers with a size given by the third
+ // argument. We pass the length of the key buffer along with the key.
+ // The `iv` buffer must be 12 bytes and the `tag` buffer 16, which we check above.
+ if unsafe {
AES_gcm_encrypt(
- data.as_ptr(),
- result.as_mut_ptr(),
- data.len(),
+ plaintext.as_ptr(),
+ ciphertext.as_mut_ptr(),
+ plaintext.len(),
key.as_ptr(),
key.len(),
iv.as_ptr(),
tag.as_mut_ptr(),
)
} {
- true => Ok((result, iv, tag)),
- false => Err(Error::EncryptionFailed),
+ Ok((ciphertext, iv, tag))
+ } else {
+ Err(Error::EncryptionFailed)
}
}
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 58142a4..bd28ca6 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -172,7 +172,9 @@
&DB_PATH.lock().expect("Could not get the database path for legacy blob loader.")));
/// Legacy migrator. Atomically migrates legacy blobs to the database.
pub static ref LEGACY_MIGRATOR: Arc<LegacyMigrator> =
- Arc::new(LegacyMigrator::new(ASYNC_TASK.clone()));
+ Arc::new(LegacyMigrator::new(Arc::new(Default::default())));
+ /// Background thread which handles logging via statsd and logd
+ pub static ref LOGS_HANDLER: Arc<AsyncTask> = Default::default();
}
static KEYMINT_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index b824aa8..bdc3f2a 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -108,7 +108,6 @@
case Tag::EC_CURVE:
case Tag::RSA_PUBLIC_EXPONENT:
case Tag::RSA_OAEP_MGF_DIGEST:
- case Tag::BLOB_USAGE_REQUIREMENTS:
case Tag::BOOTLOADER_ONLY:
case Tag::ROLLBACK_RESISTANCE:
case Tag::EARLY_BOOT_ONLY:
@@ -589,7 +588,7 @@
ScopedAStatus KeyMintDevice::begin(KeyPurpose in_inPurpose,
const std::vector<uint8_t>& prefixedKeyBlob,
const std::vector<KeyParameter>& in_inParams,
- const HardwareAuthToken& in_inAuthToken,
+ const std::optional<HardwareAuthToken>& in_inAuthToken,
BeginResult* _aidl_return) {
if (!mOperationSlots.claimSlot()) {
return convertErrorCode(V4_0_ErrorCode::TOO_MANY_OPERATIONS);
@@ -688,8 +687,11 @@
return convertErrorCode(km_error);
}
-ScopedAStatus KeyMintDevice::performOperation(const std::vector<uint8_t>& /* request */,
- std::vector<uint8_t>* /* response */) {
+ScopedAStatus
+KeyMintDevice::getKeyCharacteristics(const std::vector<uint8_t>& /* storageKeyBlob */,
+ const std::vector<uint8_t>& /* appId */,
+ const std::vector<uint8_t>& /* appData */,
+ std::vector<KeyCharacteristics>* /* keyCharacteristics */) {
return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
}
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 69c24b4..09c9157 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -109,7 +109,7 @@
ScopedAStatus destroyAttestationIds() override;
ScopedAStatus begin(KeyPurpose in_inPurpose, const std::vector<uint8_t>& in_inKeyBlob,
const std::vector<KeyParameter>& in_inParams,
- const HardwareAuthToken& in_inAuthToken,
+ const std::optional<HardwareAuthToken>& in_inAuthToken,
BeginResult* _aidl_return) override;
ScopedAStatus deviceLocked(bool passwordOnly,
const std::optional<TimeStampToken>& timestampToken) override;
@@ -118,8 +118,10 @@
ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
std::vector<uint8_t>* ephemeralKeyBlob) override;
- ScopedAStatus performOperation(const std::vector<uint8_t>& request,
- std::vector<uint8_t>* response) override;
+ ScopedAStatus
+ getKeyCharacteristics(const std::vector<uint8_t>& storageKeyBlob,
+ const std::vector<uint8_t>& appId, const std::vector<uint8_t>& appData,
+ std::vector<KeyCharacteristics>* keyCharacteristics) override;
// These are public to allow testing code to use them directly.
// This class should not be used publicly anyway.
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index e3240e9..de09477 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -503,9 +503,6 @@
return V4_0::makeKeyParameter(V4_0::TAG_INCLUDE_UNIQUE_ID, v->get());
}
break;
- case KMV1::Tag::BLOB_USAGE_REQUIREMENTS:
- // This tag has been removed. Mapped on invalid.
- break;
case KMV1::Tag::BOOTLOADER_ONLY:
if (auto v = KMV1::authorizationValue(KMV1::TAG_BOOTLOADER_ONLY, kp)) {
return V4_0::makeKeyParameter(V4_0::TAG_BOOTLOADER_ONLY, v->get());
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 5ece8a7..eddd684 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -30,10 +30,9 @@
use super::*;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BeginResult::BeginResult, BlockMode::BlockMode, Digest::Digest,
- ErrorCode::ErrorCode, HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
- KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
- KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
- SecurityLevel::SecurityLevel, Tag::Tag,
+ ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice, KeyCreationResult::KeyCreationResult,
+ KeyFormat::KeyFormat, KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
+ KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
};
use android_hardware_security_keymint::binder::{self, Strong};
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
@@ -260,7 +259,7 @@
if let Some(mut extras) = extra_params {
kps.append(&mut extras);
}
- let result = legacy.begin(purpose, &blob, &kps, &HardwareAuthToken::default());
+ let result = legacy.begin(purpose, &blob, &kps, None);
assert!(result.is_ok(), "{:?}", result);
result.unwrap()
}
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index e631356..29d46ad 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -206,7 +206,7 @@
}
impl LegacyBlobLoader {
- const IV_SIZE: usize = keystore2_crypto::IV_LENGTH;
+ const IV_SIZE: usize = keystore2_crypto::LEGACY_IV_LENGTH;
const GCM_TAG_LENGTH: usize = keystore2_crypto::TAG_LENGTH;
const SALT_SIZE: usize = keystore2_crypto::SALT_LENGTH;
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index f851d3a..3332b83 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;
@@ -42,6 +43,7 @@
pub mod utils;
mod attestation_key_utils;
+mod audit_log;
mod db_utils;
mod gc;
mod super_key;
diff --git a/keystore2/src/metrics.rs b/keystore2/src/metrics.rs
index 5b66307..07c3d64 100644
--- a/keystore2/src/metrics.rs
+++ b/keystore2/src/metrics.rs
@@ -13,9 +13,8 @@
// limitations under the License.
//! This module provides convenience functions for keystore2 logging.
-use crate::async_task::AsyncTask;
use crate::error::get_error_code;
-use crate::globals::DB;
+use crate::globals::{DB, LOGS_HANDLER};
use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
use crate::operation::Outcome;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
@@ -26,7 +25,6 @@
};
use anyhow::Result;
use keystore2_system_property::PropertyWatcher;
-use lazy_static::lazy_static;
use statslog_rust::{
keystore2_key_creation_event_reported::{
Algorithm as StatsdAlgorithm, EcCurve as StatsdEcCurve, KeyOrigin as StatsdKeyOrigin,
@@ -42,11 +40,6 @@
use statslog_rust_header::Atoms;
use statspull_rust::{set_pull_atom_callback, StatsPullResult};
-lazy_static! {
- /// Background thread which handles logging via statsd
- static ref STATSD_LOGS_HANDLER: AsyncTask = Default::default();
-}
-
fn create_default_key_creation_atom() -> Keystore2KeyCreationEventReported {
// If a value is not present, fields represented by bitmaps and i32 fields
// will take 0, except error_code which defaults to 1 indicating NO_ERROR and key_size,
@@ -95,7 +88,7 @@
let key_creation_event_stats =
construct_key_creation_event_stats(sec_level, key_params, result);
- STATSD_LOGS_HANDLER.queue_lo(move |_| {
+ LOGS_HANDLER.queue_lo(move |_| {
let logging_result = key_creation_event_stats.stats_write();
if let Err(e) = logging_result {
@@ -120,7 +113,7 @@
key_upgraded,
);
- STATSD_LOGS_HANDLER.queue_lo(move |_| {
+ LOGS_HANDLER.queue_lo(move |_| {
let logging_result = key_operation_event_stats.stats_write();
if let Err(e) = logging_result {
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
new file mode 100644
index 0000000..06432fe
--- /dev/null
+++ b/keystore2/src/raw_device.rs
@@ -0,0 +1,236 @@
+// 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, HardwareAuthToken::HardwareAuthToken,
+ IKeyMintDevice::IKeyMintDevice, IKeyMintOperation::IKeyMintOperation,
+ KeyCreationResult::KeyCreationResult, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
+ SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, 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 })
+ }
+
+ /// Create a KM key and store in the database.
+ pub fn create_and_store_key<F>(
+ &self,
+ db: &mut KeystoreDB,
+ key_desc: &KeyDescriptor,
+ 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 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 create_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 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(
+ &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 = 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")
+ }
+ }
+
+ /// 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.
+ #[allow(clippy::too_many_arguments)]
+ pub fn use_key_in_one_step(
+ &self,
+ db: &mut KeystoreDB,
+ 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
+ .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, auth_token))
+ })
+ .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.")
+ }
+}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 8530a2e..53880a1 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -25,13 +25,14 @@
use android_hardware_security_keymint::binder::{BinderFeatures, Strong, ThreadState};
use android_system_keystore2::aidl::android::system::keystore2::{
AuthenticatorSpec::AuthenticatorSpec, CreateOperationResponse::CreateOperationResponse,
- Domain::Domain, IKeystoreOperation::IKeystoreOperation,
- IKeystoreSecurityLevel::BnKeystoreSecurityLevel,
+ Domain::Domain, EphemeralStorageKeyResponse::EphemeralStorageKeyResponse,
+ IKeystoreOperation::IKeystoreOperation, IKeystoreSecurityLevel::BnKeystoreSecurityLevel,
IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
KeyMetadata::KeyMetadata, KeyParameters::KeyParameters,
};
use crate::attestation_key_utils::{get_attest_key_info, AttestationKeyInfo};
+use crate::audit_log::{log_key_deleted, log_key_generated, log_key_imported};
use crate::database::{CertificateInfo, KeyIdGuard};
use crate::globals::{DB, ENFORCEMENTS, LEGACY_MIGRATOR, SUPER_KEY};
use crate::key_parameter::KeyParameter as KsKeyParam;
@@ -295,8 +296,6 @@
)
.context("In create_operation.")?;
- let immediate_hat = immediate_hat.unwrap_or_default();
-
let km_blob = SUPER_KEY
.unwrap_key_if_required(&blob_metadata, km_blob)
.context("In create_operation. Failed to handle super encryption.")?;
@@ -318,7 +317,7 @@
purpose,
blob,
&operation_parameters,
- &immediate_hat,
+ immediate_hat.as_ref(),
)) {
Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
self.operation_db.prune(caller_uid, forced)?;
@@ -785,7 +784,10 @@
}
}
- fn convert_storage_key_to_ephemeral(&self, storage_key: &KeyDescriptor) -> Result<Vec<u8>> {
+ fn convert_storage_key_to_ephemeral(
+ &self,
+ storage_key: &KeyDescriptor,
+ ) -> Result<EphemeralStorageKeyResponse> {
if storage_key.domain != Domain::BLOB {
return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(concat!(
"In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: ",
@@ -808,8 +810,26 @@
"In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: ",
"Getting keymint device interface"
))?;
- map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
- .context("In keymint device convertStorageKeyToEphemeral")
+ match map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob)) {
+ Ok(result) => {
+ Ok(EphemeralStorageKeyResponse { ephemeralKey: result, upgradedBlob: None })
+ }
+ Err(error::Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+ let upgraded_blob = map_km_error(km_dev.upgradeKey(key_blob, &[]))
+ .context("In convert_storage_key_to_ephemeral: Failed to upgrade key blob.")?;
+ let ephemeral_key = map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
+ .context(concat!(
+ "In convert_storage_key_to_ephemeral: ",
+ "Failed to retrieve ephemeral key (after upgrade)."
+ ))?;
+ Ok(EphemeralStorageKeyResponse {
+ ephemeralKey: ephemeral_key,
+ upgradedBlob: Some(upgraded_blob),
+ })
+ }
+ Err(e) => Err(e)
+ .context("In convert_storage_key_to_ephemeral: Failed to retrieve ephemeral key."),
+ }
}
fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
@@ -856,6 +876,7 @@
) -> binder::public_api::Result<KeyMetadata> {
let result = self.generate_key(key, attestation_key, params, flags, entropy);
log_key_creation_event_stats(self.security_level, params, &result);
+ log_key_generated(key, ThreadState::get_calling_uid(), result.is_ok());
map_or_log_err(result, Ok)
}
fn importKey(
@@ -868,6 +889,7 @@
) -> binder::public_api::Result<KeyMetadata> {
let result = self.import_key(key, attestation_key, params, flags, key_data);
log_key_creation_event_stats(self.security_level, params, &result);
+ log_key_imported(key, ThreadState::get_calling_uid(), result.is_ok());
map_or_log_err(result, Ok)
}
fn importWrappedKey(
@@ -881,15 +903,18 @@
let result =
self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators);
log_key_creation_event_stats(self.security_level, params, &result);
+ log_key_imported(key, ThreadState::get_calling_uid(), result.is_ok());
map_or_log_err(result, Ok)
}
fn convertStorageKeyToEphemeral(
&self,
storage_key: &KeyDescriptor,
- ) -> binder::public_api::Result<Vec<u8>> {
+ ) -> binder::public_api::Result<EphemeralStorageKeyResponse> {
map_or_log_err(self.convert_storage_key_to_ephemeral(storage_key), Ok)
}
fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
- map_or_log_err(self.delete_key(key), Ok)
+ let result = self.delete_key(key);
+ log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
+ map_or_log_err(result, Ok)
}
}
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index 8d3b66e..b8ea244 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -17,6 +17,7 @@
use std::collections::HashMap;
+use crate::audit_log::log_key_deleted;
use crate::permission::{KeyPerm, KeystorePerm};
use crate::security_level::KeystoreSecurityLevel;
use crate::utils::{
@@ -374,7 +375,9 @@
map_or_log_err(self.list_entries(domain, namespace), Ok)
}
fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
- map_or_log_err(self.delete_key(key), Ok)
+ let result = self.delete_key(key);
+ log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
+ map_or_log_err(result, Ok)
}
fn grant(
&self,
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index b78560f..50a5f31 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -19,31 +19,45 @@
database::EncryptedBy,
database::KeyEntry,
database::KeyType,
- database::{KeyMetaData, KeyMetaEntry, KeystoreDB},
+ database::{KeyIdGuard, KeyMetaData, KeyMetaEntry, KeystoreDB},
ec_crypto::ECDHPrivateKey,
enforcements::Enforcements,
error::Error,
error::ResponseCode,
- key_parameter::KeyParameter,
+ key_parameter::{KeyParameter, KeyParameterValue},
legacy_blob::LegacyBlobLoader,
legacy_migrator::LegacyMigrator,
+ raw_device::KeyMintDevice,
try_insert::TryInsert,
};
-use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthenticatorType::HardwareAuthenticatorType, KeyFormat::KeyFormat,
+ KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
use anyhow::{Context, Result};
use keystore2_crypto::{
aes_gcm_decrypt, aes_gcm_encrypt, generate_aes256_key, generate_salt, Password, ZVec,
AES_256_KEY_LENGTH,
};
use keystore2_system_property::PropertyWatcher;
-use std::ops::Deref;
use std::{
collections::HashMap,
sync::Arc,
sync::{Mutex, Weak},
};
+use std::{convert::TryFrom, ops::Deref};
const MAX_MAX_BOOT_LEVEL: usize = 1_000_000_000;
+/// Allow up to 15 seconds between the user unlocking using a biometric, and the auth
+/// token being used to unlock in [`SuperKeyManager::try_unlock_user_with_biometric`].
+/// This seems short enough for security purposes, while long enough that even the
+/// very slowest device will present the auth token in time.
+const BIOMETRIC_AUTH_TIMEOUT_S: i32 = 15; // seconds
type UserId = u32;
@@ -154,6 +168,66 @@
}
}
+/// A SuperKey that has been encrypted with an AES-GCM key. For
+/// encryption the key is in memory, and for decryption it is in KM.
+struct LockedKey {
+ algorithm: SuperEncryptionAlgorithm,
+ id: SuperKeyIdentifier,
+ nonce: Vec<u8>,
+ ciphertext: Vec<u8>, // with tag appended
+}
+
+impl LockedKey {
+ fn new(key: &[u8], to_encrypt: &Arc<SuperKey>) -> Result<Self> {
+ let (mut ciphertext, nonce, mut tag) = aes_gcm_encrypt(&to_encrypt.key, key)?;
+ ciphertext.append(&mut tag);
+ Ok(LockedKey { algorithm: to_encrypt.algorithm, id: to_encrypt.id, nonce, ciphertext })
+ }
+
+ fn decrypt(
+ &self,
+ db: &mut KeystoreDB,
+ km_dev: &KeyMintDevice,
+ key_id_guard: &KeyIdGuard,
+ key_entry: &KeyEntry,
+ auth_token: &HardwareAuthToken,
+ reencrypt_with: Option<Arc<SuperKey>>,
+ ) -> Result<Arc<SuperKey>> {
+ let key_params = vec![
+ KeyParameterValue::Algorithm(Algorithm::AES),
+ KeyParameterValue::KeySize(256),
+ KeyParameterValue::BlockMode(BlockMode::GCM),
+ KeyParameterValue::PaddingMode(PaddingMode::NONE),
+ KeyParameterValue::Nonce(self.nonce.clone()),
+ KeyParameterValue::MacLength(128),
+ ];
+ let key_params: Vec<KmKeyParameter> = key_params.into_iter().map(|x| x.into()).collect();
+ let key = ZVec::try_from(km_dev.use_key_in_one_step(
+ db,
+ key_id_guard,
+ key_entry,
+ KeyPurpose::DECRYPT,
+ &key_params,
+ Some(auth_token),
+ &self.ciphertext,
+ )?)?;
+ Ok(Arc::new(SuperKey { algorithm: self.algorithm, key, id: self.id, reencrypt_with }))
+ }
+}
+
+/// Keys for unlocking UNLOCKED_DEVICE_REQUIRED keys, as LockedKeys, complete with
+/// a database descriptor for the encrypting key and the sids for the auth tokens
+/// that can be used to decrypt it.
+struct BiometricUnlock {
+ /// List of auth token SIDs that can be used to unlock these keys.
+ sids: Vec<i64>,
+ /// Database descriptor of key to use to unlock.
+ key_desc: KeyDescriptor,
+ /// Locked versions of the matching UserSuperKeys fields
+ screen_lock_bound: LockedKey,
+ screen_lock_bound_private: LockedKey,
+}
+
#[derive(Default)]
struct UserSuperKeys {
/// The per boot key is used for LSKF binding of authentication bound keys. There is one
@@ -168,6 +242,8 @@
/// When the device is locked, screen-lock-bound keys can still be encrypted, using
/// ECDH public-key encryption. This field holds the decryption private key.
screen_lock_bound_private: Option<Arc<SuperKey>>,
+ /// Versions of the above two keys, locked behind a biometric.
+ biometric_unlock: Option<BiometricUnlock>,
}
#[derive(Default)]
@@ -639,7 +715,7 @@
/// Check if super encryption is required and if so, super-encrypt the key to be stored in
/// the database.
- #[allow(clippy::clippy::too_many_arguments)]
+ #[allow(clippy::too_many_arguments)]
pub fn handle_super_encryption_on_key_init(
&self,
db: &mut KeystoreDB,
@@ -831,12 +907,127 @@
}
/// Wipe the screen-lock bound keys for this user from memory.
- pub fn lock_screen_lock_bound_key(&self, user_id: UserId) {
+ pub fn lock_screen_lock_bound_key(
+ &self,
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ unlocking_sids: &[i64],
+ ) {
+ log::info!("Locking screen bound for user {} sids {:?}", user_id, unlocking_sids);
let mut data = self.data.lock().unwrap();
let mut entry = data.user_keys.entry(user_id).or_default();
+ if !unlocking_sids.is_empty() {
+ if let (Some(aes), Some(ecdh)) = (
+ entry.screen_lock_bound.as_ref().cloned(),
+ entry.screen_lock_bound_private.as_ref().cloned(),
+ ) {
+ let res = (|| -> Result<()> {
+ let key_desc = KeyMintDevice::internal_descriptor(format!(
+ "biometric_unlock_key_{}",
+ user_id
+ ));
+ let encrypting_key = generate_aes256_key()?;
+ let km_dev: KeyMintDevice =
+ KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+ .context("In lock_screen_lock_bound_key: KeyMintDevice::get failed")?;
+ let mut key_params = vec![
+ KeyParameterValue::Algorithm(Algorithm::AES),
+ KeyParameterValue::KeySize(256),
+ KeyParameterValue::BlockMode(BlockMode::GCM),
+ KeyParameterValue::PaddingMode(PaddingMode::NONE),
+ KeyParameterValue::CallerNonce,
+ KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+ KeyParameterValue::MinMacLength(128),
+ KeyParameterValue::AuthTimeout(BIOMETRIC_AUTH_TIMEOUT_S),
+ KeyParameterValue::HardwareAuthenticatorType(
+ HardwareAuthenticatorType::FINGERPRINT,
+ ),
+ ];
+ for sid in unlocking_sids {
+ key_params.push(KeyParameterValue::UserSecureID(*sid));
+ }
+ let key_params: Vec<KmKeyParameter> =
+ key_params.into_iter().map(|x| x.into()).collect();
+ km_dev.create_and_store_key(db, &key_desc, |dev| {
+ dev.importKey(key_params.as_slice(), KeyFormat::RAW, &encrypting_key, None)
+ })?;
+ entry.biometric_unlock = Some(BiometricUnlock {
+ sids: unlocking_sids.into(),
+ key_desc,
+ screen_lock_bound: LockedKey::new(&encrypting_key, &aes)?,
+ screen_lock_bound_private: LockedKey::new(&encrypting_key, &ecdh)?,
+ });
+ Ok(())
+ })();
+ // There is no reason to propagate an error here upwards. We must discard
+ // entry.screen_lock_bound* in any case.
+ if let Err(e) = res {
+ log::error!("Error setting up biometric unlock: {:#?}", e);
+ }
+ }
+ }
entry.screen_lock_bound = None;
entry.screen_lock_bound_private = None;
}
+
+ /// User has unlocked, not using a password. See if any of our stored auth tokens can be used
+ /// to unlock the keys protecting UNLOCKED_DEVICE_REQUIRED keys.
+ pub fn try_unlock_user_with_biometric(
+ &self,
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ ) -> Result<()> {
+ let mut data = self.data.lock().unwrap();
+ let mut entry = data.user_keys.entry(user_id).or_default();
+ if let Some(biometric) = entry.biometric_unlock.as_ref() {
+ let (key_id_guard, key_entry) =
+ KeyMintDevice::lookup_from_desc(db, &biometric.key_desc)?;
+ let km_dev: KeyMintDevice = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+ .context("In try_unlock_user_with_biometric: KeyMintDevice::get failed")?;
+ for sid in &biometric.sids {
+ if let Some((auth_token_entry, _)) = db.find_auth_token_entry(|entry| {
+ entry.auth_token().userId == *sid || entry.auth_token().authenticatorId == *sid
+ })? {
+ let res: Result<(Arc<SuperKey>, Arc<SuperKey>)> = (|| {
+ let slb = biometric.screen_lock_bound.decrypt(
+ db,
+ &km_dev,
+ &key_id_guard,
+ &key_entry,
+ auth_token_entry.auth_token(),
+ None,
+ )?;
+ let slbp = biometric.screen_lock_bound_private.decrypt(
+ db,
+ &km_dev,
+ &key_id_guard,
+ &key_entry,
+ auth_token_entry.auth_token(),
+ Some(slb.clone()),
+ )?;
+ Ok((slb, slbp))
+ })();
+ match res {
+ Ok((slb, slbp)) => {
+ entry.screen_lock_bound = Some(slb.clone());
+ entry.screen_lock_bound_private = Some(slbp.clone());
+ data.add_key_to_key_index(&slb)?;
+ data.add_key_to_key_index(&slbp)?;
+ log::info!(concat!(
+ "In try_unlock_user_with_biometric: ",
+ "Successfully unlocked with biometric"
+ ));
+ return Ok(());
+ }
+ Err(e) => {
+ log::warn!("In try_unlock_user_with_biometric: attempt failed: {:?}", e)
+ }
+ }
+ }
+ }
+ }
+ Ok(())
+ }
}
/// This enum represents different states of the user's life cycle in the device.
diff --git a/ondevice-signing/Keymaster.cpp b/ondevice-signing/Keymaster.cpp
index 6cfb565..f9bf9b2 100644
--- a/ondevice-signing/Keymaster.cpp
+++ b/ondevice-signing/Keymaster.cpp
@@ -45,7 +45,6 @@
using android::sp;
using android::base::Error;
using android::base::Result;
-using android::base::unique_fd;
using android::hardware::hidl_vec;
Keymaster::Keymaster() {}
diff --git a/ondevice-signing/KeystoreKey.cpp b/ondevice-signing/KeystoreKey.cpp
index 96e369a..4e59c58 100644
--- a/ondevice-signing/KeystoreKey.cpp
+++ b/ondevice-signing/KeystoreKey.cpp
@@ -51,8 +51,6 @@
using android::base::Error;
using android::base::Result;
-using android::base::unique_fd;
-
// Keystore boot level that the odsign key uses
static const int kOdsignBootLevel = 30;