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

Commit 16c823eb authored by Rubin Xu's avatar Rubin Xu Committed by Andrew Scull
Browse files

Fix LSS unit tests and make behaviour consistent under synthetic password

1. Fix LSS unit tests: new credential initialization steps when synthetic
   password is used.
2. Fix LSS behaviour under SP: If credential matches but type doesn't, treat
   this as failure.
3. Fix LSS behaviour under SP: when changing credential, if old credential is
   provided but is incorrect, fail instead of performing an untrusted enroll.

Bug: 63064202
Test: runtest frameworks-services -p com.android.server.locksettings
Change-Id: I762d3f4cc8fa5e4270b851721e0208c7a0f0152a
parent d8aad5ac
Loading
Loading
Loading
Loading
+16 −2
Original line number Original line Diff line number Diff line
@@ -1931,7 +1931,8 @@ public class LockSettingsService extends ILockSettings.Stub {
     *     This is the untrusted credential reset, OR the user sets a new lockscreen password
     *     This is the untrusted credential reset, OR the user sets a new lockscreen password
     *     FOR THE FIRST TIME on a SP-enabled device. New credential and new SID will be created
     *     FOR THE FIRST TIME on a SP-enabled device. New credential and new SID will be created
     */
     */
    private AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
    @VisibleForTesting
    protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
            String credential, int credentialType, int requestedQuality,
            String credential, int credentialType, int requestedQuality,
            int userId) throws RemoteException {
            int userId) throws RemoteException {
        Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
        Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
@@ -1982,7 +1983,8 @@ public class LockSettingsService extends ILockSettings.Stub {
      return enabled != 0 && handle != SyntheticPasswordManager.DEFAULT_HANDLE;
      return enabled != 0 && handle != SyntheticPasswordManager.DEFAULT_HANDLE;
    }
    }


    private boolean shouldMigrateToSyntheticPasswordLocked(int userId) throws RemoteException {
    @VisibleForTesting
    protected boolean shouldMigrateToSyntheticPasswordLocked(int userId) throws RemoteException {
        long handle = getSyntheticPasswordHandleLocked(userId);
        long handle = getSyntheticPasswordHandleLocked(userId);
        // This is a global setting
        // This is a global setting
        long enabled = getLong(SYNTHETIC_PASSWORD_ENABLED_KEY,
        long enabled = getLong(SYNTHETIC_PASSWORD_ENABLED_KEY,
@@ -2017,6 +2019,10 @@ public class LockSettingsService extends ILockSettings.Stub {
            authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
            authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
                    getGateKeeperService(), handle, userCredential, userId);
                    getGateKeeperService(), handle, userCredential, userId);


            if (authResult.credentialType != credentialType) {
                Slog.e(TAG, "Credential type mismatch.");
                return VerifyCredentialResponse.ERROR;
            }
            response = authResult.gkResponse;
            response = authResult.gkResponse;
            // credential has matched
            // credential has matched
            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
@@ -2136,6 +2142,14 @@ public class LockSettingsService extends ILockSettings.Stub {
                getGateKeeperService(), handle, savedCredential, userId);
                getGateKeeperService(), handle, savedCredential, userId);
        VerifyCredentialResponse response = authResult.gkResponse;
        VerifyCredentialResponse response = authResult.gkResponse;
        AuthenticationToken auth = authResult.authToken;
        AuthenticationToken auth = authResult.authToken;

        // If existing credential is provided, then it must match.
        if (savedCredential != null && auth == null) {
            throw new RemoteException("Failed to enroll " +
                    (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password"
                            : "pattern"));
        }

        if (auth != null) {
        if (auth != null) {
            // We are performing a trusted credential change i.e. a correct existing credential
            // We are performing a trusted credential change i.e. a correct existing credential
            // is provided
            // is provided
+4 −0
Original line number Original line Diff line number Diff line
@@ -127,6 +127,7 @@ public class SyntheticPasswordManager {
    static class AuthenticationResult {
    static class AuthenticationResult {
        public AuthenticationToken authToken;
        public AuthenticationToken authToken;
        public VerifyCredentialResponse gkResponse;
        public VerifyCredentialResponse gkResponse;
        public int credentialType;
    }
    }


    static class AuthenticationToken {
    static class AuthenticationToken {
@@ -754,6 +755,8 @@ public class SyntheticPasswordManager {
     * Decrypt a synthetic password by supplying the user credential and corresponding password
     * Decrypt a synthetic password by supplying the user credential and corresponding password
     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
     * verification to referesh the SID & Auth token maintained by the system.
     * verification to referesh the SID & Auth token maintained by the system.
     * Note: the credential type is not validated here since there are call sites where the type is
     * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
     */
     */
    public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
    public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
            long handle, String credential, int userId) throws RemoteException {
            long handle, String credential, int userId) throws RemoteException {
@@ -762,6 +765,7 @@ public class SyntheticPasswordManager {
        }
        }
        AuthenticationResult result = new AuthenticationResult();
        AuthenticationResult result = new AuthenticationResult();
        PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
        PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
        result.credentialType = pwd.passwordType;
        byte[] pwdToken = computePasswordToken(credential, pwd);
        byte[] pwdToken = computePasswordToken(credential, pwd);


        final byte[] applicationId;
        final byte[] applicationId;
+11 −6
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.when;
import android.app.IActivityManager;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo;
import android.os.FileUtils;
import android.os.FileUtils;
@@ -38,6 +39,7 @@ import android.os.storage.IStorageManager;
import android.security.KeyStore;
import android.security.KeyStore;
import android.test.AndroidTestCase;
import android.test.AndroidTestCase;


import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils;


import org.mockito.invocation.InvocationOnMock;
import org.mockito.invocation.InvocationOnMock;
@@ -67,7 +69,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
    LockSettingsStorageTestable mStorage;
    LockSettingsStorageTestable mStorage;


    LockPatternUtils mLockPatternUtils;
    LockPatternUtils mLockPatternUtils;
    MockGateKeeperService mGateKeeperService;
    FakeGateKeeperService mGateKeeperService;
    NotificationManager mNotificationManager;
    NotificationManager mNotificationManager;
    UserManager mUserManager;
    UserManager mUserManager;
    FakeStorageManager mStorageManager;
    FakeStorageManager mStorageManager;
@@ -80,8 +82,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
    protected void setUp() throws Exception {
    protected void setUp() throws Exception {
        super.setUp();
        super.setUp();


        mLockPatternUtils = mock(LockPatternUtils.class);
        mGateKeeperService = new FakeGateKeeperService();
        mGateKeeperService = new MockGateKeeperService();
        mNotificationManager = mock(NotificationManager.class);
        mNotificationManager = mock(NotificationManager.class);
        mUserManager = mock(UserManager.class);
        mUserManager = mock(UserManager.class);
        mStorageManager = new FakeStorageManager();
        mStorageManager = new FakeStorageManager();
@@ -89,7 +90,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
        mDevicePolicyManager = mock(DevicePolicyManager.class);
        mDevicePolicyManager = mock(DevicePolicyManager.class);


        mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
        mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
                mDevicePolicyManager, mock(StorageManager.class));
                mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class));
        mStorage = new LockSettingsStorageTestable(mContext,
        mStorage = new LockSettingsStorageTestable(mContext,
                new File(getContext().getFilesDir(), "locksettings"));
                new File(getContext().getFilesDir(), "locksettings"));
        File storageDir = mStorage.mStorageDir;
        File storageDir = mStorage.mStorageDir;
@@ -99,6 +100,12 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
            storageDir.mkdirs();
            storageDir.mkdirs();
        }
        }


        mLockPatternUtils = new LockPatternUtils(mContext) {
            @Override
            public ILockSettings getLockSettings() {
                return mService;
            }
        };
        mSpManager = new MockSyntheticPasswordManager(mStorage, mGateKeeperService, mUserManager);
        mSpManager = new MockSyntheticPasswordManager(mStorage, mGateKeeperService, mUserManager);
        mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
        mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
                mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
                mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
@@ -122,8 +129,6 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
            }
            }
        });
        });


        when(mLockPatternUtils.getLockSettings()).thenReturn(mService);

        // Adding a fake Device Owner app which will enable escrow token support in LSS.
        // Adding a fake Device Owner app which will enable escrow token support in LSS.
        when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(
        when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(
                new ComponentName("com.dummy.package", ".FakeDeviceOwner"));
                new ComponentName("com.dummy.package", ".FakeDeviceOwner"));
+10 −10
Original line number Original line Diff line number Diff line
@@ -28,7 +28,7 @@ import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Random;
import java.util.Random;


public class MockGateKeeperService implements IGateKeeperService {
public class FakeGateKeeperService implements IGateKeeperService {
    static class VerifyHandle {
    static class VerifyHandle {
        public byte[] password;
        public byte[] password;
        public long sid;
        public long sid;
@@ -92,7 +92,6 @@ public class MockGateKeeperService implements IGateKeeperService {
    @Override
    @Override
    public GateKeeperResponse enroll(int uid, byte[] currentPasswordHandle, byte[] currentPassword,
    public GateKeeperResponse enroll(int uid, byte[] currentPasswordHandle, byte[] currentPassword,
            byte[] desiredPassword) throws android.os.RemoteException {
            byte[] desiredPassword) throws android.os.RemoteException {

        if (currentPasswordHandle != null) {
        if (currentPasswordHandle != null) {
            VerifyHandle handle = new VerifyHandle(currentPasswordHandle);
            VerifyHandle handle = new VerifyHandle(currentPasswordHandle);
            if (Arrays.equals(currentPassword, handle.password)) {
            if (Arrays.equals(currentPassword, handle.password)) {
@@ -101,18 +100,19 @@ public class MockGateKeeperService implements IGateKeeperService {
                refreshSid(uid, handle.sid, false);
                refreshSid(uid, handle.sid, false);
                handleMap.put(uid, newHandle.toBytes());
                handleMap.put(uid, newHandle.toBytes());
                return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
                return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
            } else {
            } else if (currentPassword != null) {
                // current password is provided but does not match handle, this is an error case.
                return null;
                return null;
            }
            }
        } else {
            // Fall through: password handle is provided, but no password
            // Untrusted enroll
        }
        // Untrusted/new enrollment: generate a new SID
        long newSid = new Random().nextLong();
        long newSid = new Random().nextLong();
        VerifyHandle newHandle = new VerifyHandle(desiredPassword, newSid);
        VerifyHandle newHandle = new VerifyHandle(desiredPassword, newSid);
        refreshSid(uid, newSid, true);
        refreshSid(uid, newSid, true);
        handleMap.put(uid, newHandle.toBytes());
        handleMap.put(uid, newHandle.toBytes());
        return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
        return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
    }
    }
    }


    @Override
    @Override
    public GateKeeperResponse verify(int uid, byte[] enrolledPasswordHandle,
    public GateKeeperResponse verify(int uid, byte[] enrolledPasswordHandle,
+2 −3
Original line number Original line Diff line number Diff line
@@ -103,12 +103,10 @@ public class LockSettingsServiceTestable extends LockSettingsService {
        public int binderGetCallingUid() {
        public int binderGetCallingUid() {
            return Process.SYSTEM_UID;
            return Process.SYSTEM_UID;
        }
        }


    }
    }


    protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
    protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
            LockSettingsStorage storage, MockGateKeeperService gatekeeper, KeyStore keystore,
            LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
            IStorageManager storageManager, IActivityManager mActivityManager,
            IStorageManager storageManager, IActivityManager mActivityManager,
            SyntheticPasswordManager spManager) {
            SyntheticPasswordManager spManager) {
        super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
        super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
@@ -137,4 +135,5 @@ public class LockSettingsServiceTestable extends LockSettingsService {
        }
        }
        return new String(storedData);
        return new String(storedData);
    }
    }

}
}
Loading