diff options
| author | 2018-01-05 18:33:58 +0000 | |
|---|---|---|
| committer | 2018-01-19 15:01:44 +0000 | |
| commit | e6527c1285cf38057d95c33f5fac4f4ea124e003 (patch) | |
| tree | 215582aa35aeed7a3673b5fc4cf965c5fd4b7aa9 | |
| parent | eb3442cf8ab6b2548e9503f29de34c441eb1b0b2 (diff) | |
Make us of the authsecret HAL.
Derive a secret from the primary user's synthetic password and pass it
to the HAL.
Bug: 71527305
Test: runtest frameworks-services -p com.android.server.locksettings
Test: cts-tradefed run cts-dev -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy
Change-Id: If3ed5d56375e9fd81fcbb16b172e908804fd568a
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(); |