diff options
7 files changed, 121 insertions, 5 deletions
diff --git a/services/core/Android.bp b/services/core/Android.bp index 3369458595a8..8da6d1e48ffe 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -32,6 +32,7 @@ java_library_static {      static_libs: [          "time_zone_distro",          "time_zone_distro_installer", +        "android.hardware.authsecret-V1.0-java",          "android.hardware.broadcastradio-V2.0-java",          "android.hardware.health-V1.0-java",          "android.hardware.health-V2.0-java", diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index bd72a009d28d..8f6c2fb1e999 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -53,6 +53,7 @@ import android.content.pm.UserInfo;  import android.content.res.Resources;  import android.database.ContentObserver;  import android.database.sqlite.SQLiteDatabase; +import android.hardware.authsecret.V1_0.IAuthSecret;  import android.net.Uri;  import android.os.Binder;  import android.os.Bundle; @@ -126,8 +127,10 @@ import java.security.SecureRandom;  import java.security.UnrecoverableKeyException;  import java.security.cert.CertificateException;  import java.util.Arrays; +import java.util.ArrayList;  import java.util.List;  import java.util.Map; +import java.util.NoSuchElementException;  import java.util.concurrent.CountDownLatch;  import java.util.concurrent.TimeUnit; @@ -183,6 +186,7 @@ public class LockSettingsService extends ILockSettings.Stub {      private boolean mFirstCallToVold;      protected IGateKeeperService mGateKeeperService; +    protected IAuthSecret mAuthSecretService;      /**       * The UIDs that are used for system credential storage in keystore. @@ -613,6 +617,14 @@ public class LockSettingsService extends ILockSettings.Stub {          } catch (RemoteException e) {              Slog.e(TAG, "Failure retrieving IGateKeeperService", e);          } +        // Find the AuthSecret HAL +        try { +            mAuthSecretService = IAuthSecret.getService(); +        } catch (NoSuchElementException e) { +            Slog.i(TAG, "Device doesn't implement AuthSecret HAL"); +        } catch (RemoteException e) { +            Slog.w(TAG, "Failed to get AuthSecret HAL", e); +        }          mDeviceProvisionedObserver.onSystemReady();          // TODO: maybe skip this for split system user mode.          mStorage.prefetchUser(UserHandle.USER_SYSTEM); @@ -2128,6 +2140,20 @@ public class LockSettingsService extends ILockSettings.Stub {      private SparseArray<AuthenticationToken> mSpCache = new SparseArray();      private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) { +        // Pass the primary user's auth secret to the HAL +        if (mAuthSecretService != null && mUserManager.getUserInfo(userId).isPrimary()) { +            try { +                final byte[] rawSecret = auth.deriveVendorAuthSecret(); +                final ArrayList<Byte> secret = new ArrayList<>(rawSecret.length); +                for (int i = 0; i < rawSecret.length; ++i) { +                    secret.add(rawSecret[i]); +                } +                mAuthSecretService.primaryUserCredential(secret); +            } catch (RemoteException e) { +                Slog.w(TAG, "Failed to pass primary user secret to AuthSecret HAL", e); +            } +        } +          // Update the SP cache, removing the entry when allowed          synchronized (mSpManager) {              if (shouldCacheSpForUser(userId)) { diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index 7a3a746e1868..88b2a3685088 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -121,6 +121,7 @@ public class SyntheticPasswordManager {      private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();      private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();      private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes(); +    private static final byte[] PERSONALIZATION_AUTHSECRET_KEY = "authsecret-hal".getBytes();      private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();      private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();      private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes(); @@ -159,6 +160,11 @@ public class SyntheticPasswordManager {                      syntheticPassword.getBytes());          } +        public byte[] deriveVendorAuthSecret() { +            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY, +                    syntheticPassword.getBytes()); +        } +          private void initialize(byte[] P0, byte[] P1) {              this.P1 = P1;              this.syntheticPassword = String.valueOf(HexEncoding.encode( diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index 272b5d899d4b..e8648701bd0d 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -31,6 +31,7 @@ import android.app.admin.DevicePolicyManagerInternal;  import android.app.trust.TrustManager;  import android.content.ComponentName;  import android.content.pm.UserInfo; +import android.hardware.authsecret.V1_0.IAuthSecret;  import android.os.FileUtils;  import android.os.IProgressListener;  import android.os.RemoteException; @@ -80,6 +81,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {      DevicePolicyManagerInternal mDevicePolicyManagerInternal;      KeyStore mKeyStore;      MockSyntheticPasswordManager mSpManager; +    IAuthSecret mAuthSecretService;      @Override      protected void setUp() throws Exception { @@ -115,17 +117,21 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {          };          mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService,                  mUserManager); +        mAuthSecretService = mock(IAuthSecret.class);          mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,                  mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager, -                mSpManager); +                mSpManager, mAuthSecretService);          when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);          mPrimaryUserProfiles.add(PRIMARY_USER_INFO);          installChildProfile(MANAGED_PROFILE_USER_ID);          installAndTurnOffChildProfile(TURNED_OFF_PROFILE_USER_ID); -        when(mUserManager.getUsers(anyBoolean())).thenReturn(mPrimaryUserProfiles);          when(mUserManager.getProfiles(eq(PRIMARY_USER_ID))).thenReturn(mPrimaryUserProfiles);          when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(SECONDARY_USER_INFO); +        final ArrayList<UserInfo> allUsers = new ArrayList<>(mPrimaryUserProfiles); +        allUsers.add(SECONDARY_USER_INFO); +        when(mUserManager.getUsers(anyBoolean())).thenReturn(allUsers); +          when(mActivityManager.unlockUser(anyInt(), any(), any(), any())).thenAnswer(                  new Answer<Boolean>() {              @Override diff --git a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java index 4ad9f19735c4..d2caa0af0ba2 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java @@ -22,6 +22,7 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSW  import static com.android.server.testutils.TestUtils.assertExpectException;  import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.atLeastOnce;  import static org.mockito.Mockito.when;  import static org.mockito.Mockito.verify; @@ -31,6 +32,10 @@ import com.android.internal.widget.LockPatternUtils;  import com.android.internal.widget.VerifyCredentialResponse;  import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult; +import java.util.ArrayList; + +import org.mockito.ArgumentCaptor; +  /**   * Run the synthetic password tests with caching enabled.   * @@ -88,6 +93,26 @@ public class CachedSyntheticPasswordTests extends SyntheticPasswordTests {                      .getResponseCode());      } +    public void testUntrustedCredentialChangeMaintainsAuthSecret() throws RemoteException { +        final String PASSWORD = "testUntrustedCredentialChangeMaintainsAuthSecret-password"; +        final String NEWPASSWORD = "testUntrustedCredentialChangeMaintainsAuthSecret-newpassword"; + +        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); +        // Untrusted change password +        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, +                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); + +        // Verify the password +        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( +                NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) +                    .getResponseCode()); + +        // Ensure the same secret was passed each time +        ArgumentCaptor<ArrayList<Byte>> secret = ArgumentCaptor.forClass(ArrayList.class); +        verify(mAuthSecretService, atLeastOnce()).primaryUserCredential(secret.capture()); +        assertEquals(1, secret.getAllValues().stream().distinct().count()); +    } +      public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException {          final String PASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-password";          final String NEWPASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword"; diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java index 0916a335b51e..fe683abe7e1b 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -20,6 +20,7 @@ import static org.mockito.Mockito.mock;  import android.app.IActivityManager;  import android.content.Context; +import android.hardware.authsecret.V1_0.IAuthSecret;  import android.os.Handler;  import android.os.Looper;  import android.os.Process; @@ -42,10 +43,12 @@ public class LockSettingsServiceTestable extends LockSettingsService {          private LockPatternUtils mLockPatternUtils;          private IStorageManager mStorageManager;          private SyntheticPasswordManager mSpManager; +        private IAuthSecret mAuthSecretService;          public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,                  IActivityManager activityManager, LockPatternUtils lockPatternUtils, -                IStorageManager storageManager, SyntheticPasswordManager spManager) { +                IStorageManager storageManager, SyntheticPasswordManager spManager, +                IAuthSecret authSecretService) {              super(context);              mLockSettingsStorage = storage;              mKeyStore = keyStore; @@ -109,10 +112,11 @@ public class LockSettingsServiceTestable extends LockSettingsService {      protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,              LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,              IStorageManager storageManager, IActivityManager mActivityManager, -            SyntheticPasswordManager spManager) { +            SyntheticPasswordManager spManager, IAuthSecret authSecretService) {          super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils, -                storageManager, spManager)); +                storageManager, spManager, authSecretService));          mGateKeeperService = gatekeeper; +        mAuthSecretService = authSecretService;      }      @Override diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index b07d6ac5dc04..294c3e99ad7e 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -24,6 +24,10 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSW  import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;  import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset;  import static org.mockito.Mockito.verify;  import android.app.admin.PasswordMetrics; @@ -36,6 +40,10 @@ import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationRe  import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;  import com.android.server.locksettings.SyntheticPasswordManager.PasswordData; +import java.util.ArrayList; + +import org.mockito.ArgumentCaptor; +  /**   * runtest frameworks-services -c com.android.server.locksettings.SyntheticPasswordTests @@ -169,6 +177,46 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {          assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));      } +    public void testSyntheticPasswordChangeCredentialKeepsAuthSecret() throws RemoteException { +        final String PASSWORD = "testSyntheticPasswordChangeCredentialKeepsAuthSecret-password"; +        final String NEWPASSWORD = "testSyntheticPasswordChangeCredentialKeepsAuthSecret-new"; + +        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); +        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD, +                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); +        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( +                NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) +                        .getResponseCode()); + +        // Check the same secret was passed each time +        ArgumentCaptor<ArrayList<Byte>> secret = ArgumentCaptor.forClass(ArrayList.class); +        verify(mAuthSecretService, atLeastOnce()).primaryUserCredential(secret.capture()); +        assertEquals(1, secret.getAllValues().stream().distinct().count()); +    } + +    public void testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret() throws RemoteException { +        final String PASSWORD = "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-password"; +        final String NEWPASSWORD = "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-new"; + +        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); +        reset(mAuthSecretService); +        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( +                PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) +                        .getResponseCode()); +        verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class)); +    } + +    public void testSecondaryUserDoesNotPassAuthSecret() throws RemoteException { +        final String PASSWORD = "testSecondaryUserDoesNotPassAuthSecret-password"; +        final String NEWPASSWORD = "testSecondaryUserDoesNotPassAuthSecret-new"; + +        initializeCredentialUnderSP(PASSWORD, SECONDARY_USER_ID); +        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( +                PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, SECONDARY_USER_ID) +                        .getResponseCode()); +        verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); +    } +      public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {          final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd";          disableSyntheticPassword();  |