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:
@@ -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",
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user