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
This commit is contained in:
Andrew Scull
2018-01-05 18:33:58 +00:00
parent eb3442cf8a
commit e6527c1285
7 changed files with 121 additions and 5 deletions

View File

@@ -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",

View File

@@ -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)) {

View File

@@ -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(

View File

@@ -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

View File

@@ -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";

View File

@@ -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

View File

@@ -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();