Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c22ce02b authored by Eric Biggers's avatar Eric Biggers
Browse files

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 actual change in behavior intended.  This change does
make TrustManagerService no longer call getBiometricSids() for unlocks,
so technically that is a slight difference; however, for UNLOCK events
Keystore ignored the SID list, so this just eliminates unnecessary work.

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: Ibfaa22ba27d13248c9c4c69a4d2efb2231792c31
parent fc31e4ed
Loading
Loading
Loading
Loading
+25 −15
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.StrictMode;
import android.security.authorization.IKeystoreAuthorization;
import android.security.authorization.LockScreenEvent;
import android.system.keystore2.ResponseCode;
import android.util.Log;

@@ -76,26 +75,37 @@ public class Authorization {
    }

    /**
     * Informs keystore2 about lock screen event.
     *
     * @param locked            - whether it is a lock (true) or unlock (false) event
     * @param syntheticPassword - if it is an unlock event with the password, pass the synthetic
     *                            password provided by the LockSettingService
     * @param unlockingSids     - KeyMint secure user IDs that should be permitted to unlock
     *                            UNLOCKED_DEVICE_REQUIRED keys.
     * Tells Keystore that the device is now unlocked for a user.
     *
     * @param userId - the user's Android user ID
     * @param password - a secret derived from the user's synthetic password, if the unlock method
     *                   is LSKF (or equivalent) and thus has made the synthetic password available
     * @return 0 if successful or a {@code ResponseCode}.
     */
    public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
            @Nullable byte[] syntheticPassword, @Nullable long[] unlockingSids) {
    public static int onDeviceUnlocked(int userId, @Nullable byte[] password) {
        StrictMode.noteDiskWrite();
        try {
            if (locked) {
                getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null, unlockingSids);
            } else {
                getService().onLockScreenEvent(
                        LockScreenEvent.UNLOCK, userId, syntheticPassword, unlockingSids);
            getService().onDeviceUnlocked(userId, password);
            return 0;
        } catch (RemoteException | NullPointerException e) {
            Log.w(TAG, "Can not connect to keystore", e);
            return SYSTEM_ERROR;
        } catch (ServiceSpecificException e) {
            return e.errorCode;
        }
    }

    /**
     * Tells Keystore that the device is now locked for a user.
     *
     * @param userId - the user's Android user ID
     * @param unlockingSids - list of biometric SIDs with which the device may be unlocked again
     * @return 0 if successful or a {@code ResponseCode}.
     */
    public static int onDeviceLocked(int userId, @NonNull long[] unlockingSids) {
        StrictMode.noteDiskWrite();
        try {
            getService().onDeviceLocked(userId, unlockingSids);
            return 0;
        } catch (RemoteException | NullPointerException e) {
            Log.w(TAG, "Can not connect to keystore", e);
+1 −1
Original line number Diff line number Diff line
@@ -1429,7 +1429,7 @@ public class LockSettingsService extends ILockSettings.Stub {
    }

    private void unlockKeystore(int userId, SyntheticPassword sp) {
        Authorization.onLockScreenEvent(false, userId, sp.deriveKeyStorePassword(), null);
        Authorization.onDeviceUnlocked(userId, sp.deriveKeyStorePassword());
    }

    @VisibleForTesting /** Note: this method is overridden in unit tests */
+18 −10
Original line number Diff line number Diff line
@@ -867,21 +867,19 @@ public class TrustManagerService extends SystemService {
            mDeviceLockedForUser.put(userId, locked);
        }
        if (changed) {
            dispatchDeviceLocked(userId, locked);
            Authorization.onLockScreenEvent(locked, userId, null,
                    getBiometricSids(userId));
            notifyTrustAgentsOfDeviceLockState(userId, locked);
            notifyKeystoreOfDeviceLockState(userId, locked);
            // Also update the user's profiles who have unified challenge, since they
            // share the same unlocked state (see {@link #isDeviceLocked(int)})
            for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) {
                if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(profileHandle)) {
                    Authorization.onLockScreenEvent(locked, profileHandle, null,
                            getBiometricSids(profileHandle));
                    notifyKeystoreOfDeviceLockState(profileHandle, locked);
                }
            }
        }
    }

    private void dispatchDeviceLocked(int userId, boolean isLocked) {
    private void notifyTrustAgentsOfDeviceLockState(int userId, boolean isLocked) {
        for (int i = 0; i < mActiveAgents.size(); i++) {
            AgentInfo agent = mActiveAgents.valueAt(i);
            if (agent.userId == userId) {
@@ -894,6 +892,17 @@ public class TrustManagerService extends SystemService {
        }
    }

    private void notifyKeystoreOfDeviceLockState(int userId, boolean isLocked) {
        if (isLocked) {
            Authorization.onDeviceLocked(userId, getBiometricSids(userId));
        } else {
            // Notify Keystore that the device is now unlocked for the user.  Note that for unlocks
            // with LSKF, this is redundant with the call from LockSettingsService which provides
            // the password.  However, for unlocks with biometric or trust agent, this is required.
            Authorization.onDeviceUnlocked(userId, /* password= */ null);
        }
    }

    private void dispatchEscrowTokenActivatedLocked(long handle, int userId) {
        for (int i = 0; i < mActiveAgents.size(); i++) {
            AgentInfo agent = mActiveAgents.valueAt(i);
@@ -1427,10 +1436,10 @@ public class TrustManagerService extends SystemService {
        }
    }

    private long[] getBiometricSids(int userId) {
    private @NonNull long[] getBiometricSids(int userId) {
        BiometricManager biometricManager = mContext.getSystemService(BiometricManager.class);
        if (biometricManager == null) {
            return null;
            return new long[0];
        }
        return biometricManager.getAuthenticatorIds(userId);
    }
@@ -1729,8 +1738,7 @@ public class TrustManagerService extends SystemService {
                        mDeviceLockedForUser.put(userId, locked);
                    }

                    Authorization.onLockScreenEvent(locked, userId, null,
                            getBiometricSids(userId));
                    notifyKeystoreOfDeviceLockState(userId, locked);

                    if (locked) {
                        try {