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",
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;
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;
private boolean mFirstCallToVold;
protected IGateKeeperService mGateKeeperService;
+ protected IAuthSecret mAuthSecretService;
/**
* The UIDs that are used for system credential storage in keystore.
} 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);
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)) {
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();
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(
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;
DevicePolicyManagerInternal mDevicePolicyManagerInternal;
KeyStore mKeyStore;
MockSyntheticPasswordManager mSpManager;
+ IAuthSecret mAuthSecretService;
@Override
protected void setUp() throws Exception {
};
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
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;
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.
*
.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";
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;
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;
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
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;
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
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();