Merge "Split Keystore's onLockScreenEvent into onDevice{Unlocked,Locked}" into main

This commit is contained in:
Eric Biggers 2023-12-07 17:57:55 +00:00 committed by Gerrit Code Review
commit a2609f539d
4 changed files with 80 additions and 136 deletions

View file

@ -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.
*
* 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.
*
* 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.
* 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.
*
* ## 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.
* `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.
*
* @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
* @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 onLockScreenEvent(in LockScreenEvent lockScreenEvent, in int userId,
in @nullable byte[] password, in @nullable long[] unlockingSids);
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 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 onDeviceLocked(in int userId, in long[] unlockingSids);
/**
* Allows Credstore to retrieve a HardwareAuthToken and a TimestampToken.

View file

@ -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,
}

View file

@ -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);
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.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(

View file

@ -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);
}