summaryrefslogtreecommitdiff
path: root/keystore2
diff options
context:
space:
mode:
authorEric Biggers <ebiggers@google.com>2023-12-01 23:05:24 +0000
committerEric Biggers <ebiggers@google.com>2023-12-02 03:05:43 +0000
commit10afa966facb9ddcc6f0db9d2820a28f25250256 (patch)
tree7e0d0de00fe825fc63378298a21a1e6ad1163c18 /keystore2
parent6f80e953124ae8cfe311b8e10b69496d29a4011c (diff)
downloadsecurity-10afa966facb9ddcc6f0db9d2820a28f25250256.tar.gz
Split Keystore's onLockScreenEvent into onDevice{Unlocked,Locked}
Currently Keystore is notified of the device being unlocked and locked for each user via onLockScreenEvent(lockScreenEvent, userId, password, unlockingSids), where lockScreenEvent is UNLOCK or LOCK. This is a bit confusing because the password parameter is only meaningful for UNLOCK, and the unlockingSids parameter is only meaningful for LOCK. This problem will get worse when we add a parameter that tells Keystore whether unlocking via a weak biometric or trust agent is possible, as that will be another parameter that is only meaningful for LOCK. Therefore, this CL splits onLockScreenEvent into two methods onDeviceUnlocked and onDeviceLocked, each with the appropriate parameters. No change in behavior intended. Bug: 296464083 Test: atest -p --include-subdirs system/security/keystore2 \ && atest CtsKeystoreTestCases \ && atest TrustTests \ && atest com.android.server.locksettings Flag: N/A, straightforward refactoring Change-Id: Ie2afd118bddca6112a5469558569c63b68ee10fb
Diffstat (limited to 'keystore2')
-rw-r--r--keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl67
-rw-r--r--keystore2/aidl/android/security/authorization/LockScreenEvent.aidl22
-rw-r--r--keystore2/src/authorization.rs117
-rw-r--r--keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs8
4 files changed, 79 insertions, 135 deletions
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
index b31a51e2..54894ff5 100644
--- a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -16,7 +16,6 @@ package android.security.authorization;
import android.hardware.security.keymint.HardwareAuthToken;
import android.hardware.security.keymint.HardwareAuthenticatorType;
-import android.security.authorization.LockScreenEvent;
import android.security.authorization.AuthorizationTokens;
// TODO: mark the interface with @SensitiveData when the annotation is ready (b/176110256).
@@ -41,41 +40,51 @@ interface IKeystoreAuthorization {
void addAuthToken(in HardwareAuthToken authToken);
/**
- * Unlocks the keystore for the given user id.
+ * Tells Keystore that the device is now unlocked for a user. Requires the 'Unlock' permission.
*
- * Callers require 'Unlock' permission.
+ * This method makes Keystore start allowing the use of the given user's keys that require an
+ * unlocked device, following the device boot or an earlier call to onDeviceLocked() which
+ * disabled the use of such keys. In addition, once per boot, this method must be called with a
+ * password before keys that require user authentication can be used.
*
- * Super-Encryption Key:
- * When the device is unlocked (and password is non-null), Keystore stores in memory
- * a super-encryption key derived from the password that protects UNLOCKED_DEVICE_REQUIRED
- * keys; this key is wiped from memory when the device is locked.
+ * To enable access to these keys, this method attempts to decrypt and cache the user's super
+ * keys. If the password is given, i.e. if the unlock occurred using an LSKF-equivalent
+ * mechanism, then both the AfterFirstUnlock and UnlockedDeviceRequired super keys are decrypted
+ * (if not already done). Otherwise, only the UnlockedDeviceRequired super keys are decrypted,
+ * and this only works if a valid HardwareAuthToken has been added to Keystore for one of the
+ * 'unlockingSids' that was passed to the last call to onDeviceLocked() for the user.
*
- * 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.
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'Unlock' permission.
+ * `ResponseCode::VALUE_CORRUPTED` - if a super key can not be decrypted.
+ * `ResponseCode::KEY_NOT_FOUND` - if a super key is not found.
+ * `ResponseCode::SYSTEM_ERROR` - if another error occurred.
*
- * Options for unlock:
- * - If the password is non-null, the super-encryption key is re-derived as above.
- * - If the password is null, then if a suitable auth token to access the encrypted
- * Super-encryption key stored in KM has been sent to keystore (via addAuthToken), the
- * encrypted super-encryption key is recovered so that UNLOCKED_DEVICE_REQUIRED keys can
- * be used once again.
- * - If neither of these are met, then the operation fails.
+ * @param userId The Android user ID of the user for which the device is now unlocked
+ * @param password If available, a secret derived from the user's synthetic password
+ */
+ void onDeviceUnlocked(in int userId, in @nullable byte[] password);
+
+ /**
+ * Tells Keystore that the device is now locked for a user. Requires the 'Lock' permission.
+ *
+ * This method makes Keystore stop allowing the use of the given user's keys that require an
+ * unlocked device. This is done through logical enforcement, and also through cryptographic
+ * enforcement by wiping the UnlockedDeviceRequired super keys from memory.
+ *
+ * unlockingSids is the list of SIDs of the user's biometrics with which the device may be
+ * unlocked later. If this list is non-empty, then instead of completely wiping the
+ * UnlockedDeviceRequired super keys from memory, this method re-encrypts these super keys with
+ * a new AES key that is imported into KeyMint and bound to the given SIDs. This allows the
+ * UnlockedDeviceRequired super keys to be recovered if the device is unlocked with a biometric.
*
* ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'Unlock' permission.
- * `ResponseCode::SYSTEM_ERROR` - if failed to perform lock/unlock operations due to various
- * `ResponseCode::VALUE_CORRUPTED` - if the super key can not be decrypted.
- * `ResponseCode::KEY_NOT_FOUND` - if the super key is not found.
- *
- * @param lockScreenEvent whether the lock screen locked or unlocked
- * @param userId android user id
- * @param password synthetic password derived from the user's LSKF, must be null on lock
- * @param unlockingSids list of biometric SIDs for this user, ignored on unlock
+ * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'Lock' permission.
+ *
+ * @param userId The Android user ID of the user for which the device is now locked
+ * @param unlockingSids The user's list of biometric SIDs
*/
- void onLockScreenEvent(in LockScreenEvent lockScreenEvent, in int userId,
- in @nullable byte[] password, in @nullable long[] unlockingSids);
+ void onDeviceLocked(in int userId, in long[] unlockingSids);
/**
* Allows Credstore to retrieve a HardwareAuthToken and a TimestampToken.
diff --git a/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl b/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
deleted file mode 100644
index c7553a27..00000000
--- a/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2020, 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.
-
-package android.security.authorization;
-
-/** @hide */
-@Backing(type="int")
-enum LockScreenEvent {
- UNLOCK = 0,
- LOCK = 1,
-}
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 7416b7f2..36f94e99 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -26,8 +26,7 @@ use android_hardware_security_keymint::aidl::android::hardware::security::keymin
};
use android_security_authorization::aidl::android::security::authorization::{
AuthorizationTokens::AuthorizationTokens, IKeystoreAuthorization::BnKeystoreAuthorization,
- IKeystoreAuthorization::IKeystoreAuthorization, LockScreenEvent::LockScreenEvent,
- ResponseCode::ResponseCode,
+ IKeystoreAuthorization::IKeystoreAuthorization, ResponseCode::ResponseCode,
};
use android_security_authorization::binder::{
BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status as BinderStatus,
@@ -144,71 +143,43 @@ impl AuthorizationManager {
Ok(())
}
- fn on_lock_screen_event(
- &self,
- lock_screen_event: LockScreenEvent,
- user_id: i32,
- password: Option<Password>,
- unlocking_sids: Option<&[i64]>,
- ) -> Result<()> {
+ fn on_device_unlocked(&self, user_id: i32, password: Option<Password>) -> Result<()> {
log::info!(
- "on_lock_screen_event({:?}, user_id={:?}, password.is_some()={}, unlocking_sids={:?})",
- lock_screen_event,
+ "on_device_unlocked(user_id={}, password.is_some()={})",
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.
- // check permission
- check_keystore_permission(KeystorePerm::Unlock)
- .context(ks_err!("Unlock with password."))?;
- ENFORCEMENTS.set_device_locked(user_id, false);
-
- let mut skm = SUPER_KEY.write().unwrap();
+ check_keystore_permission(KeystorePerm::Unlock).context(ks_err!("Unlock."))?;
+ ENFORCEMENTS.set_device_locked(user_id, false);
- DB.with(|db| {
- skm.unlock_user(
- &mut db.borrow_mut(),
- &LEGACY_IMPORTER,
- user_id as u32,
- &password,
- )
- })
- .context(ks_err!("Unlock with password."))?;
- Ok(())
- }
- (LockScreenEvent::UNLOCK, None) => {
- check_keystore_permission(KeystorePerm::Unlock).context(ks_err!("Unlock."))?;
- ENFORCEMENTS.set_device_locked(user_id, false);
- let mut skm = SUPER_KEY.write().unwrap();
- DB.with(|db| {
- skm.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32)
- })
- .context(ks_err!("try_unlock_user_with_biometric failed"))?;
- Ok(())
- }
- (LockScreenEvent::LOCK, None) => {
- check_keystore_permission(KeystorePerm::Lock).context(ks_err!("Lock"))?;
- ENFORCEMENTS.set_device_locked(user_id, true);
- let mut skm = SUPER_KEY.write().unwrap();
- DB.with(|db| {
- skm.lock_unlocked_device_required_keys(
- &mut db.borrow_mut(),
- user_id as u32,
- unlocking_sids.unwrap_or(&[]),
- );
- });
- Ok(())
- }
- _ => {
- // Any other combination is not supported.
- Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(ks_err!("Unknown event."))
- }
+ let mut skm = SUPER_KEY.write().unwrap();
+ if let Some(password) = password {
+ DB.with(|db| {
+ skm.unlock_user(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32, &password)
+ })
+ .context(ks_err!("Unlock with password."))
+ } else {
+ DB.with(|db| skm.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32))
+ .context(ks_err!("try_unlock_user_with_biometric failed"))
}
}
+ fn on_device_locked(&self, user_id: i32, unlocking_sids: &[i64]) -> Result<()> {
+ log::info!("on_device_locked(user_id={}, unlocking_sids={:?})", user_id, unlocking_sids);
+
+ check_keystore_permission(KeystorePerm::Lock).context(ks_err!("Lock"))?;
+ ENFORCEMENTS.set_device_locked(user_id, true);
+ let mut skm = SUPER_KEY.write().unwrap();
+ DB.with(|db| {
+ skm.lock_unlocked_device_required_keys(
+ &mut db.borrow_mut(),
+ user_id as u32,
+ unlocking_sids,
+ );
+ });
+ Ok(())
+ }
+
fn get_auth_tokens_for_credstore(
&self,
challenge: i64,
@@ -264,26 +235,14 @@ impl IKeystoreAuthorization for AuthorizationManager {
map_or_log_err(self.add_auth_token(auth_token), Ok)
}
- fn onLockScreenEvent(
- &self,
- lock_screen_event: LockScreenEvent,
- user_id: i32,
- password: Option<&[u8]>,
- unlocking_sids: Option<&[i64]>,
- ) -> BinderResult<()> {
- let _wp =
- wd::watch_millis_with("IKeystoreAuthorization::onLockScreenEvent", 500, move || {
- format!("lock event: {}", lock_screen_event.0)
- });
- map_or_log_err(
- self.on_lock_screen_event(
- lock_screen_event,
- user_id,
- password.map(|pw| pw.into()),
- unlocking_sids,
- ),
- Ok,
- )
+ fn onDeviceUnlocked(&self, user_id: i32, password: Option<&[u8]>) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreAuthorization::onDeviceUnlocked", 500);
+ map_or_log_err(self.on_device_unlocked(user_id, password.map(|pw| pw.into())), Ok)
+ }
+
+ fn onDeviceLocked(&self, user_id: i32, unlocking_sids: &[i64]) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreAuthorization::onDeviceLocked", 500);
+ map_or_log_err(self.on_device_locked(user_id, unlocking_sids), Ok)
}
fn getAuthTokensForCredStore(
diff --git a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
index faf954a2..c2cd3adf 100644
--- a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
+++ b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
@@ -28,7 +28,7 @@ use android_system_keystore2::aidl::android::system::keystore2::{
use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::IKeystoreMaintenance;
use android_security_authorization::aidl::android::security::authorization::{
- IKeystoreAuthorization::IKeystoreAuthorization, LockScreenEvent::LockScreenEvent,
+ IKeystoreAuthorization::IKeystoreAuthorization,
};
use keystore2::key_parameter::KeyParameter as KsKeyparameter;
@@ -229,8 +229,7 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
keystore2_restart_service();
let auth_service = get_authorization();
- match auth_service.onLockScreenEvent(LockScreenEvent::UNLOCK, 99, Some(PASSWORD), None)
- {
+ match auth_service.onDeviceUnlocked(99, Some(PASSWORD)) {
Ok(result) => {
println!("Unlock Result: {:?}", result);
}
@@ -487,8 +486,7 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
keystore2_restart_service();
let auth_service = get_authorization();
- match auth_service.onLockScreenEvent(LockScreenEvent::UNLOCK, 98, Some(PASSWORD), None)
- {
+ match auth_service.onDeviceUnlocked(98, Some(PASSWORD)) {
Ok(result) => {
println!("Unlock Result: {:?}", result);
}