Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +114 −25 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRY import static android.content.Context.KEYGUARD_SERVICE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_SYSTEM; import static android.provider.DeviceConfig.NAMESPACE_AUTO_PIN_CONFIRMATION; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; Loading Loading @@ -70,6 +71,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.ContentObserver; import android.database.sqlite.SQLiteDatabase; import android.hardware.authsecret.IAuthSecret; Loading Loading @@ -219,6 +221,8 @@ public class LockSettingsService extends ILockSettings.Stub { private static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_"; private static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_"; private static final int HEADLESS_VENDOR_AUTH_SECRET_LENGTH = 32; // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this // Do not call into ActivityManager while holding mSpManager lock. private final Object mSeparateChallengeLock = new Object(); Loading Loading @@ -267,6 +271,13 @@ public class LockSettingsService extends ILockSettings.Stub { @VisibleForTesting protected boolean mHasSecureLockScreen; @VisibleForTesting protected final Object mHeadlessAuthSecretLock = new Object(); @VisibleForTesting @GuardedBy("mHeadlessAuthSecretLock") protected byte[] mAuthSecret; protected IGateKeeperService mGateKeeperService; protected IAuthSecret mAuthSecretService; Loading Loading @@ -563,6 +574,15 @@ public class LockSettingsService extends ILockSettings.Stub { java.security.KeyStore ks) { return new ManagedProfilePasswordCache(ks, getUserManager()); } public boolean isHeadlessSystemUserMode() { return UserManager.isHeadlessSystemUserMode(); } public boolean isMainUserPermanentAdmin() { return Resources.getSystem() .getBoolean(com.android.internal.R.bool.config_isMainUserPermanentAdmin); } } public LockSettingsService(Context context) { Loading Loading @@ -1697,7 +1717,7 @@ public class LockSettingsService extends ILockSettings.Stub { throw new IllegalStateException("password change failed"); } onSyntheticPasswordKnown(userId, sp); onSyntheticPasswordUnlocked(userId, sp); setLockCredentialWithSpLocked(credential, sp, userId); sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent); return true; Loading Loading @@ -2009,7 +2029,7 @@ public class LockSettingsService extends ILockSettings.Stub { Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId); return; } onSyntheticPasswordKnown(userId, result.syntheticPassword); onSyntheticPasswordUnlocked(userId, result.syntheticPassword); unlockUserKey(userId, result.syntheticPassword); } } Loading Loading @@ -2602,43 +2622,112 @@ public class LockSettingsService extends ILockSettings.Stub { } } private void onSyntheticPasswordKnown(@UserIdInt int userId, SyntheticPassword sp) { private void onSyntheticPasswordCreated(@UserIdInt int userId, SyntheticPassword sp) { onSyntheticPasswordKnown(userId, sp, true); } private void onSyntheticPasswordUnlocked(@UserIdInt int userId, SyntheticPassword sp) { onSyntheticPasswordKnown(userId, sp, false); } private void onSyntheticPasswordKnown( @UserIdInt int userId, SyntheticPassword sp, boolean justCreated) { if (mInjector.isGsiRunning()) { Slog.w(TAG, "Running in GSI; skipping calls to AuthSecret and RebootEscrow"); return; } mRebootEscrowManager.callToRebootEscrowIfNeeded(userId, sp.getVersion(), sp.getSyntheticPassword()); callToAuthSecretIfNeeded(userId, sp); mRebootEscrowManager.callToRebootEscrowIfNeeded( userId, sp.getVersion(), sp.getSyntheticPassword()); callToAuthSecretIfNeeded(userId, sp, justCreated); } private void callToAuthSecretIfNeeded(@UserIdInt int userId, SyntheticPassword sp) { // If the given user is the primary user, pass the auth secret to the HAL. Only the system // user can be primary. Check for the system user ID before calling getUserInfo(), as other // users may still be under construction. /** * Handles generation, storage, and sending of the vendor auth secret. Here we try to retrieve * the auth secret to send it to the auth secret HAL, generate a fresh secret if need be, store * it encrypted on disk so that the given user can unlock it in future, and stash it in memory * so that when future users are created they can also unlock it. * * <p>Called whenever the SP of a user is available, except in GSI. */ private void callToAuthSecretIfNeeded( @UserIdInt int userId, SyntheticPassword sp, boolean justCreated) { if (mAuthSecretService == null) { // If there's no IAuthSecret service, we don't need to maintain a auth secret return; } // User may be partially created, so use the internal user manager interface final UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal(); final UserInfo userInfo = userManagerInternal.getUserInfo(userId); if (userInfo == null) { // User may be partially deleted, skip this. return; } final byte[] authSecret; if (!mInjector.isHeadlessSystemUserMode()) { // On non-headless systems, the auth secret is derived from user 0's // SP, and only user 0 passes it to the HAL. if (userId != USER_SYSTEM) { return; } authSecret = sp.deriveVendorAuthSecret(); } else if (!mInjector.isMainUserPermanentAdmin() || !userInfo.isFull()) { // Only full users can receive or pass on the auth secret. // If there is no main permanent admin user, we don't try to create or send // an auth secret, since there may sometimes be no full users. return; } else if (justCreated) { if (userInfo.isMain()) { // The first user is just being created, so we create a new auth secret // at the same time. Slog.i(TAG, "Generating new vendor auth secret and storing for user: " + userId); authSecret = SecureRandomUtils.randomBytes(HEADLESS_VENDOR_AUTH_SECRET_LENGTH); // Store it in memory, for when new users are created. synchronized (mHeadlessAuthSecretLock) { mAuthSecret = authSecret; } } else { // A new user is being created. Another user should already have logged in at // this point, and therefore the auth secret should be stored in memory. synchronized (mHeadlessAuthSecretLock) { authSecret = mAuthSecret; } if (authSecret == null) { Slog.e(TAG, "Creating non-main user " + userId + " but vendor auth secret is not in memory"); return; } if (userId == UserHandle.USER_SYSTEM && mUserManager.getUserInfo(userId).isPrimary()) { final byte[] secret = sp.deriveVendorAuthSecret(); } // Store the auth secret encrypted using the user's SP (which was just created). mSpManager.writeVendorAuthSecret(authSecret, sp, userId); } else { // The user already exists, so the auth secret should be stored encrypted // with that user's SP. authSecret = mSpManager.readVendorAuthSecret(sp, userId); if (authSecret == null) { Slog.e(TAG, "Unable to read vendor auth secret for user: " + userId); return; } // Store it in memory, for when new users are created. synchronized (mHeadlessAuthSecretLock) { mAuthSecret = authSecret; } } Slog.i(TAG, "Sending vendor auth secret to IAuthSecret HAL as user: " + userId); try { mAuthSecretService.setPrimaryUserCredential(secret); mAuthSecretService.setPrimaryUserCredential(authSecret); } catch (RemoteException e) { Slog.w(TAG, "Failed to pass primary user secret to AuthSecret HAL", e); } Slog.w(TAG, "Failed to send vendor auth secret to IAuthSecret HAL", e); } } /** * Creates the synthetic password (SP) for the given user, protects it with an empty LSKF, and * protects the user's CE key with a key derived from the SP. * <p> * This is called just once in the lifetime of the user: at user creation time (possibly delayed * until the time when Weaver is guaranteed to be available), or when upgrading from Android 13 * or earlier where users with no LSKF didn't necessarily have an SP. * * <p>This is called just once in the lifetime of the user: at user creation time (possibly * delayed until the time when Weaver is guaranteed to be available), or when upgrading from * Android 13 or earlier where users with no LSKF didn't necessarily have an SP. */ @VisibleForTesting SyntheticPassword initializeSyntheticPassword(int userId) { Loading @@ -2653,7 +2742,7 @@ public class LockSettingsService extends ILockSettings.Stub { LockscreenCredential.createNone(), sp, userId); setCurrentLskfBasedProtectorId(protectorId, userId); setUserKeyProtection(userId, sp.deriveFileBasedEncryptionKey()); onSyntheticPasswordKnown(userId, sp); onSyntheticPasswordCreated(userId, sp); return sp; } } Loading Loading @@ -2720,7 +2809,7 @@ public class LockSettingsService extends ILockSettings.Stub { } mStrongAuth.reportSuccessfulStrongAuthUnlock(userId); onSyntheticPasswordKnown(userId, sp); onSyntheticPasswordUnlocked(userId, sp); } private void setDeviceUnlockedForUser(int userId) { Loading Loading @@ -3008,7 +3097,7 @@ public class LockSettingsService extends ILockSettings.Stub { + "verification."); return false; } onSyntheticPasswordKnown(userId, result.syntheticPassword); onSyntheticPasswordUnlocked(userId, result.syntheticPassword); setLockCredentialWithSpLocked(credential, result.syntheticPassword, userId); return true; } Loading services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +32 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChang import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.admin.PasswordMetrics; import android.content.Context; import android.content.pm.UserInfo; Loading Loading @@ -93,6 +94,9 @@ import java.util.Set; * while the LSKF is nonempty. * SP_E0_NAME, SP_P1_NAME: Information needed to create and use escrow token-based protectors. * Deleted when escrow token support is disabled for the user. * VENDOR_AUTH_SECRET_NAME: A copy of the secret passed using the IAuthSecret interface, * encrypted using a secret derived from the SP using * PERSONALIZATION_AUTHSECRET_ENCRYPTION_KEY. * * For each protector, stored under the corresponding protector ID: * SP_BLOB_NAME: The encrypted SP secret (the SP itself or the P0 value). Always exists. Loading Loading @@ -120,6 +124,7 @@ class SyntheticPasswordManager { private static final String PASSWORD_DATA_NAME = "pwd"; private static final String WEAVER_SLOT_NAME = "weaver"; private static final String PASSWORD_METRICS_NAME = "metrics"; private static final String VENDOR_AUTH_SECRET_NAME = "vendor_auth_secret"; // used for files associated with the SP itself, not with a particular protector public static final long NULL_PROTECTOR_ID = 0L; Loading Loading @@ -158,6 +163,8 @@ class SyntheticPasswordManager { 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_AUTHSECRET_ENCRYPTION_KEY = "vendor-authsecret-encryption-key".getBytes(); private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes(); private static final byte[] PERSONALIZATION_PASSWORD_HASH = "pw-hash".getBytes(); private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes(); Loading Loading @@ -249,6 +256,10 @@ class SyntheticPasswordManager { return deriveSubkey(PERSONALIZATION_PASSWORD_METRICS); } public byte[] deriveVendorAuthSecretEncryptionKey() { return deriveSubkey(PERSONALIZATION_AUTHSECRET_ENCRYPTION_KEY); } /** * Assigns escrow data to this synthetic password. This is a prerequisite to call * {@link SyntheticPassword#recreateFromEscrow}. Loading Loading @@ -1737,4 +1748,25 @@ class SyntheticPasswordManager { mListeners.finishBroadcast(); } } public void writeVendorAuthSecret( @NonNull final byte[] vendorAuthSecret, @NonNull final SyntheticPassword sp, @UserIdInt final int userId) { final byte[] encrypted = SyntheticPasswordCrypto.encrypt( sp.deriveVendorAuthSecretEncryptionKey(), new byte[0], vendorAuthSecret); saveState(VENDOR_AUTH_SECRET_NAME, encrypted, NULL_PROTECTOR_ID, userId); syncState(userId); } public @Nullable byte[] readVendorAuthSecret( @NonNull final SyntheticPassword sp, @UserIdInt final int userId) { final byte[] encrypted = loadState(VENDOR_AUTH_SECRET_NAME, NULL_PROTECTOR_ID, userId); if (encrypted == null) { return null; } return SyntheticPasswordCrypto.decrypt( sp.deriveVendorAuthSecretEncryptionKey(), new byte[0], encrypted); } } services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +56 −16 Original line number Diff line number Diff line Loading @@ -49,8 +49,8 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.provider.Settings; import android.provider.DeviceConfig; import android.provider.Settings; import android.security.KeyStore; import androidx.test.InstrumentationRegistry; Loading Loading @@ -83,16 +83,15 @@ public abstract class BaseLockSettingsServiceTests { protected static final int MANAGED_PROFILE_USER_ID = 12; protected static final int TURNED_OFF_PROFILE_USER_ID = 17; protected static final int SECONDARY_USER_ID = 20; protected static final int TERTIARY_USER_ID = 21; private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER_ID, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_MAIN); private static final UserInfo SECONDARY_USER_INFO = new UserInfo(SECONDARY_USER_ID, null, null, UserInfo.FLAG_INITIALIZED); protected UserInfo mPrimaryUserInfo; protected UserInfo mSecondaryUserInfo; protected UserInfo mTertiaryUserInfo; private ArrayList<UserInfo> mPrimaryUserProfiles = new ArrayList<>(); LockSettingsService mService; LockSettingsServiceTestable mService; LockSettingsInternal mLocalService; MockLockSettingsContext mContext; Loading @@ -117,6 +116,7 @@ public abstract class BaseLockSettingsServiceTests { FingerprintManager mFingerprintManager; FaceManager mFaceManager; PackageManager mPackageManager; LockSettingsServiceTestable.MockInjector mInjector; @Rule public FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule(); Loading Loading @@ -162,22 +162,61 @@ public abstract class BaseLockSettingsServiceTests { mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService, mUserManager, mPasswordSlotManager); mAuthSecretService = mock(IAuthSecret.class); mService = new LockSettingsServiceTestable(mContext, mStorage, mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager, mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager, mUserManagerInternal, mDeviceStateCache); mInjector = new LockSettingsServiceTestable.MockInjector( mContext, mStorage, mKeyStore, mActivityManager, setUpStorageManagerMock(), mSpManager, mGsiService, mRecoverableKeyStoreManager, mUserManagerInternal, mDeviceStateCache); mService = new LockSettingsServiceTestable(mInjector, mGateKeeperService, mAuthSecretService); mService.mHasSecureLockScreen = true; when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO); mPrimaryUserProfiles.add(PRIMARY_USER_INFO); mPrimaryUserInfo = new UserInfo( PRIMARY_USER_ID, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_MAIN | UserInfo.FLAG_FULL); mSecondaryUserInfo = new UserInfo( SECONDARY_USER_ID, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_FULL); mTertiaryUserInfo = new UserInfo( TERTIARY_USER_ID, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_FULL); when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(mPrimaryUserInfo); when(mUserManagerInternal.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(mPrimaryUserInfo); mPrimaryUserProfiles.add(mPrimaryUserInfo); installChildProfile(MANAGED_PROFILE_USER_ID); installAndTurnOffChildProfile(TURNED_OFF_PROFILE_USER_ID); for (UserInfo profile : mPrimaryUserProfiles) { when(mUserManager.getProfiles(eq(profile.id))).thenReturn(mPrimaryUserProfiles); } when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(SECONDARY_USER_INFO); when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(mSecondaryUserInfo); when(mUserManagerInternal.getUserInfo(eq(SECONDARY_USER_ID))) .thenReturn(mSecondaryUserInfo); when(mUserManager.getUserInfo(eq(TERTIARY_USER_ID))).thenReturn(mTertiaryUserInfo); when(mUserManagerInternal.getUserInfo(eq(TERTIARY_USER_ID))).thenReturn(mTertiaryUserInfo); final ArrayList<UserInfo> allUsers = new ArrayList<>(mPrimaryUserProfiles); allUsers.add(SECONDARY_USER_INFO); allUsers.add(mSecondaryUserInfo); allUsers.add(mTertiaryUserInfo); when(mUserManager.getUsers()).thenReturn(allUsers); when(mActivityManager.unlockUser2(anyInt(), any())).thenAnswer( Loading Loading @@ -227,9 +266,10 @@ public abstract class BaseLockSettingsServiceTests { userInfo.profileGroupId = PRIMARY_USER_ID; mPrimaryUserProfiles.add(userInfo); when(mUserManager.getUserInfo(eq(profileId))).thenReturn(userInfo); when(mUserManager.getProfileParent(eq(profileId))).thenReturn(PRIMARY_USER_INFO); when(mUserManager.getProfileParent(eq(profileId))).thenReturn(mPrimaryUserInfo); when(mUserManager.isUserRunning(eq(profileId))).thenReturn(true); when(mUserManager.isUserUnlocked(eq(profileId))).thenReturn(true); when(mUserManagerInternal.getUserInfo(eq(profileId))).thenReturn(userInfo); // TODO(b/258213147): Remove when(mUserManagerInternal.isUserManaged(eq(profileId))).thenReturn(true); when(mDeviceStateCache.isUserOrganizationManaged(eq(profileId))) Loading services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +25 −12 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.os.RemoteException; import android.os.storage.IStorageManager; import android.security.KeyStore; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.service.gatekeeper.IGateKeeperService; import com.android.internal.widget.LockscreenCredential; import com.android.server.ServiceThread; Loading @@ -40,7 +41,7 @@ import java.io.FileNotFoundException; public class LockSettingsServiceTestable extends LockSettingsService { private static class MockInjector extends LockSettingsService.Injector { public static class MockInjector extends LockSettingsService.Injector { private LockSettingsStorage mLockSettingsStorage; private KeyStore mKeyStore; Loading @@ -52,6 +53,9 @@ public class LockSettingsServiceTestable extends LockSettingsService { private UserManagerInternal mUserManagerInternal; private DeviceStateCache mDeviceStateCache; public boolean mIsHeadlessSystemUserMode = false; public boolean mIsMainUserPermanentAdmin = false; public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore, IActivityManager activityManager, IStorageManager storageManager, SyntheticPasswordManager spManager, Loading Loading @@ -140,19 +144,22 @@ public class LockSettingsServiceTestable extends LockSettingsService { return mock(ManagedProfilePasswordCache.class); } @Override public boolean isHeadlessSystemUserMode() { return mIsHeadlessSystemUserMode; } public MockInjector mInjector; @Override public boolean isMainUserPermanentAdmin() { return mIsMainUserPermanentAdmin; } } protected LockSettingsServiceTestable(Context context, LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore, IStorageManager storageManager, IActivityManager mActivityManager, SyntheticPasswordManager spManager, IAuthSecret authSecretService, FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager, UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) { super(new MockInjector(context, storage, keystore, mActivityManager, storageManager, spManager, gsiService, recoverableKeyStoreManager, userManagerInternal, deviceStateCache)); protected LockSettingsServiceTestable( LockSettingsService.Injector injector, IGateKeeperService gatekeeper, IAuthSecret authSecretService) { super(injector); mGateKeeperService = gatekeeper; mAuthSecretService = authSecretService; } Loading Loading @@ -199,4 +206,10 @@ public class LockSettingsServiceTestable extends LockSettingsService { UserInfo userInfo = mUserManager.getUserInfo(userId); return userInfo.isCloneProfile() || userInfo.isManagedProfile(); } void clearAuthSecret() { synchronized (mHeadlessAuthSecretLock) { mAuthSecret = null; } } } services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +64 −1 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +114 −25 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRY import static android.content.Context.KEYGUARD_SERVICE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_SYSTEM; import static android.provider.DeviceConfig.NAMESPACE_AUTO_PIN_CONFIRMATION; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; Loading Loading @@ -70,6 +71,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.ContentObserver; import android.database.sqlite.SQLiteDatabase; import android.hardware.authsecret.IAuthSecret; Loading Loading @@ -219,6 +221,8 @@ public class LockSettingsService extends ILockSettings.Stub { private static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_"; private static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_"; private static final int HEADLESS_VENDOR_AUTH_SECRET_LENGTH = 32; // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this // Do not call into ActivityManager while holding mSpManager lock. private final Object mSeparateChallengeLock = new Object(); Loading Loading @@ -267,6 +271,13 @@ public class LockSettingsService extends ILockSettings.Stub { @VisibleForTesting protected boolean mHasSecureLockScreen; @VisibleForTesting protected final Object mHeadlessAuthSecretLock = new Object(); @VisibleForTesting @GuardedBy("mHeadlessAuthSecretLock") protected byte[] mAuthSecret; protected IGateKeeperService mGateKeeperService; protected IAuthSecret mAuthSecretService; Loading Loading @@ -563,6 +574,15 @@ public class LockSettingsService extends ILockSettings.Stub { java.security.KeyStore ks) { return new ManagedProfilePasswordCache(ks, getUserManager()); } public boolean isHeadlessSystemUserMode() { return UserManager.isHeadlessSystemUserMode(); } public boolean isMainUserPermanentAdmin() { return Resources.getSystem() .getBoolean(com.android.internal.R.bool.config_isMainUserPermanentAdmin); } } public LockSettingsService(Context context) { Loading Loading @@ -1697,7 +1717,7 @@ public class LockSettingsService extends ILockSettings.Stub { throw new IllegalStateException("password change failed"); } onSyntheticPasswordKnown(userId, sp); onSyntheticPasswordUnlocked(userId, sp); setLockCredentialWithSpLocked(credential, sp, userId); sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent); return true; Loading Loading @@ -2009,7 +2029,7 @@ public class LockSettingsService extends ILockSettings.Stub { Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId); return; } onSyntheticPasswordKnown(userId, result.syntheticPassword); onSyntheticPasswordUnlocked(userId, result.syntheticPassword); unlockUserKey(userId, result.syntheticPassword); } } Loading Loading @@ -2602,43 +2622,112 @@ public class LockSettingsService extends ILockSettings.Stub { } } private void onSyntheticPasswordKnown(@UserIdInt int userId, SyntheticPassword sp) { private void onSyntheticPasswordCreated(@UserIdInt int userId, SyntheticPassword sp) { onSyntheticPasswordKnown(userId, sp, true); } private void onSyntheticPasswordUnlocked(@UserIdInt int userId, SyntheticPassword sp) { onSyntheticPasswordKnown(userId, sp, false); } private void onSyntheticPasswordKnown( @UserIdInt int userId, SyntheticPassword sp, boolean justCreated) { if (mInjector.isGsiRunning()) { Slog.w(TAG, "Running in GSI; skipping calls to AuthSecret and RebootEscrow"); return; } mRebootEscrowManager.callToRebootEscrowIfNeeded(userId, sp.getVersion(), sp.getSyntheticPassword()); callToAuthSecretIfNeeded(userId, sp); mRebootEscrowManager.callToRebootEscrowIfNeeded( userId, sp.getVersion(), sp.getSyntheticPassword()); callToAuthSecretIfNeeded(userId, sp, justCreated); } private void callToAuthSecretIfNeeded(@UserIdInt int userId, SyntheticPassword sp) { // If the given user is the primary user, pass the auth secret to the HAL. Only the system // user can be primary. Check for the system user ID before calling getUserInfo(), as other // users may still be under construction. /** * Handles generation, storage, and sending of the vendor auth secret. Here we try to retrieve * the auth secret to send it to the auth secret HAL, generate a fresh secret if need be, store * it encrypted on disk so that the given user can unlock it in future, and stash it in memory * so that when future users are created they can also unlock it. * * <p>Called whenever the SP of a user is available, except in GSI. */ private void callToAuthSecretIfNeeded( @UserIdInt int userId, SyntheticPassword sp, boolean justCreated) { if (mAuthSecretService == null) { // If there's no IAuthSecret service, we don't need to maintain a auth secret return; } // User may be partially created, so use the internal user manager interface final UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal(); final UserInfo userInfo = userManagerInternal.getUserInfo(userId); if (userInfo == null) { // User may be partially deleted, skip this. return; } final byte[] authSecret; if (!mInjector.isHeadlessSystemUserMode()) { // On non-headless systems, the auth secret is derived from user 0's // SP, and only user 0 passes it to the HAL. if (userId != USER_SYSTEM) { return; } authSecret = sp.deriveVendorAuthSecret(); } else if (!mInjector.isMainUserPermanentAdmin() || !userInfo.isFull()) { // Only full users can receive or pass on the auth secret. // If there is no main permanent admin user, we don't try to create or send // an auth secret, since there may sometimes be no full users. return; } else if (justCreated) { if (userInfo.isMain()) { // The first user is just being created, so we create a new auth secret // at the same time. Slog.i(TAG, "Generating new vendor auth secret and storing for user: " + userId); authSecret = SecureRandomUtils.randomBytes(HEADLESS_VENDOR_AUTH_SECRET_LENGTH); // Store it in memory, for when new users are created. synchronized (mHeadlessAuthSecretLock) { mAuthSecret = authSecret; } } else { // A new user is being created. Another user should already have logged in at // this point, and therefore the auth secret should be stored in memory. synchronized (mHeadlessAuthSecretLock) { authSecret = mAuthSecret; } if (authSecret == null) { Slog.e(TAG, "Creating non-main user " + userId + " but vendor auth secret is not in memory"); return; } if (userId == UserHandle.USER_SYSTEM && mUserManager.getUserInfo(userId).isPrimary()) { final byte[] secret = sp.deriveVendorAuthSecret(); } // Store the auth secret encrypted using the user's SP (which was just created). mSpManager.writeVendorAuthSecret(authSecret, sp, userId); } else { // The user already exists, so the auth secret should be stored encrypted // with that user's SP. authSecret = mSpManager.readVendorAuthSecret(sp, userId); if (authSecret == null) { Slog.e(TAG, "Unable to read vendor auth secret for user: " + userId); return; } // Store it in memory, for when new users are created. synchronized (mHeadlessAuthSecretLock) { mAuthSecret = authSecret; } } Slog.i(TAG, "Sending vendor auth secret to IAuthSecret HAL as user: " + userId); try { mAuthSecretService.setPrimaryUserCredential(secret); mAuthSecretService.setPrimaryUserCredential(authSecret); } catch (RemoteException e) { Slog.w(TAG, "Failed to pass primary user secret to AuthSecret HAL", e); } Slog.w(TAG, "Failed to send vendor auth secret to IAuthSecret HAL", e); } } /** * Creates the synthetic password (SP) for the given user, protects it with an empty LSKF, and * protects the user's CE key with a key derived from the SP. * <p> * This is called just once in the lifetime of the user: at user creation time (possibly delayed * until the time when Weaver is guaranteed to be available), or when upgrading from Android 13 * or earlier where users with no LSKF didn't necessarily have an SP. * * <p>This is called just once in the lifetime of the user: at user creation time (possibly * delayed until the time when Weaver is guaranteed to be available), or when upgrading from * Android 13 or earlier where users with no LSKF didn't necessarily have an SP. */ @VisibleForTesting SyntheticPassword initializeSyntheticPassword(int userId) { Loading @@ -2653,7 +2742,7 @@ public class LockSettingsService extends ILockSettings.Stub { LockscreenCredential.createNone(), sp, userId); setCurrentLskfBasedProtectorId(protectorId, userId); setUserKeyProtection(userId, sp.deriveFileBasedEncryptionKey()); onSyntheticPasswordKnown(userId, sp); onSyntheticPasswordCreated(userId, sp); return sp; } } Loading Loading @@ -2720,7 +2809,7 @@ public class LockSettingsService extends ILockSettings.Stub { } mStrongAuth.reportSuccessfulStrongAuthUnlock(userId); onSyntheticPasswordKnown(userId, sp); onSyntheticPasswordUnlocked(userId, sp); } private void setDeviceUnlockedForUser(int userId) { Loading Loading @@ -3008,7 +3097,7 @@ public class LockSettingsService extends ILockSettings.Stub { + "verification."); return false; } onSyntheticPasswordKnown(userId, result.syntheticPassword); onSyntheticPasswordUnlocked(userId, result.syntheticPassword); setLockCredentialWithSpLocked(credential, result.syntheticPassword, userId); return true; } Loading
services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +32 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChang import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.admin.PasswordMetrics; import android.content.Context; import android.content.pm.UserInfo; Loading Loading @@ -93,6 +94,9 @@ import java.util.Set; * while the LSKF is nonempty. * SP_E0_NAME, SP_P1_NAME: Information needed to create and use escrow token-based protectors. * Deleted when escrow token support is disabled for the user. * VENDOR_AUTH_SECRET_NAME: A copy of the secret passed using the IAuthSecret interface, * encrypted using a secret derived from the SP using * PERSONALIZATION_AUTHSECRET_ENCRYPTION_KEY. * * For each protector, stored under the corresponding protector ID: * SP_BLOB_NAME: The encrypted SP secret (the SP itself or the P0 value). Always exists. Loading Loading @@ -120,6 +124,7 @@ class SyntheticPasswordManager { private static final String PASSWORD_DATA_NAME = "pwd"; private static final String WEAVER_SLOT_NAME = "weaver"; private static final String PASSWORD_METRICS_NAME = "metrics"; private static final String VENDOR_AUTH_SECRET_NAME = "vendor_auth_secret"; // used for files associated with the SP itself, not with a particular protector public static final long NULL_PROTECTOR_ID = 0L; Loading Loading @@ -158,6 +163,8 @@ class SyntheticPasswordManager { 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_AUTHSECRET_ENCRYPTION_KEY = "vendor-authsecret-encryption-key".getBytes(); private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes(); private static final byte[] PERSONALIZATION_PASSWORD_HASH = "pw-hash".getBytes(); private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes(); Loading Loading @@ -249,6 +256,10 @@ class SyntheticPasswordManager { return deriveSubkey(PERSONALIZATION_PASSWORD_METRICS); } public byte[] deriveVendorAuthSecretEncryptionKey() { return deriveSubkey(PERSONALIZATION_AUTHSECRET_ENCRYPTION_KEY); } /** * Assigns escrow data to this synthetic password. This is a prerequisite to call * {@link SyntheticPassword#recreateFromEscrow}. Loading Loading @@ -1737,4 +1748,25 @@ class SyntheticPasswordManager { mListeners.finishBroadcast(); } } public void writeVendorAuthSecret( @NonNull final byte[] vendorAuthSecret, @NonNull final SyntheticPassword sp, @UserIdInt final int userId) { final byte[] encrypted = SyntheticPasswordCrypto.encrypt( sp.deriveVendorAuthSecretEncryptionKey(), new byte[0], vendorAuthSecret); saveState(VENDOR_AUTH_SECRET_NAME, encrypted, NULL_PROTECTOR_ID, userId); syncState(userId); } public @Nullable byte[] readVendorAuthSecret( @NonNull final SyntheticPassword sp, @UserIdInt final int userId) { final byte[] encrypted = loadState(VENDOR_AUTH_SECRET_NAME, NULL_PROTECTOR_ID, userId); if (encrypted == null) { return null; } return SyntheticPasswordCrypto.decrypt( sp.deriveVendorAuthSecretEncryptionKey(), new byte[0], encrypted); } }
services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +56 −16 Original line number Diff line number Diff line Loading @@ -49,8 +49,8 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.provider.Settings; import android.provider.DeviceConfig; import android.provider.Settings; import android.security.KeyStore; import androidx.test.InstrumentationRegistry; Loading Loading @@ -83,16 +83,15 @@ public abstract class BaseLockSettingsServiceTests { protected static final int MANAGED_PROFILE_USER_ID = 12; protected static final int TURNED_OFF_PROFILE_USER_ID = 17; protected static final int SECONDARY_USER_ID = 20; protected static final int TERTIARY_USER_ID = 21; private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER_ID, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_MAIN); private static final UserInfo SECONDARY_USER_INFO = new UserInfo(SECONDARY_USER_ID, null, null, UserInfo.FLAG_INITIALIZED); protected UserInfo mPrimaryUserInfo; protected UserInfo mSecondaryUserInfo; protected UserInfo mTertiaryUserInfo; private ArrayList<UserInfo> mPrimaryUserProfiles = new ArrayList<>(); LockSettingsService mService; LockSettingsServiceTestable mService; LockSettingsInternal mLocalService; MockLockSettingsContext mContext; Loading @@ -117,6 +116,7 @@ public abstract class BaseLockSettingsServiceTests { FingerprintManager mFingerprintManager; FaceManager mFaceManager; PackageManager mPackageManager; LockSettingsServiceTestable.MockInjector mInjector; @Rule public FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule(); Loading Loading @@ -162,22 +162,61 @@ public abstract class BaseLockSettingsServiceTests { mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService, mUserManager, mPasswordSlotManager); mAuthSecretService = mock(IAuthSecret.class); mService = new LockSettingsServiceTestable(mContext, mStorage, mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager, mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager, mUserManagerInternal, mDeviceStateCache); mInjector = new LockSettingsServiceTestable.MockInjector( mContext, mStorage, mKeyStore, mActivityManager, setUpStorageManagerMock(), mSpManager, mGsiService, mRecoverableKeyStoreManager, mUserManagerInternal, mDeviceStateCache); mService = new LockSettingsServiceTestable(mInjector, mGateKeeperService, mAuthSecretService); mService.mHasSecureLockScreen = true; when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO); mPrimaryUserProfiles.add(PRIMARY_USER_INFO); mPrimaryUserInfo = new UserInfo( PRIMARY_USER_ID, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_MAIN | UserInfo.FLAG_FULL); mSecondaryUserInfo = new UserInfo( SECONDARY_USER_ID, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_FULL); mTertiaryUserInfo = new UserInfo( TERTIARY_USER_ID, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_FULL); when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(mPrimaryUserInfo); when(mUserManagerInternal.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(mPrimaryUserInfo); mPrimaryUserProfiles.add(mPrimaryUserInfo); installChildProfile(MANAGED_PROFILE_USER_ID); installAndTurnOffChildProfile(TURNED_OFF_PROFILE_USER_ID); for (UserInfo profile : mPrimaryUserProfiles) { when(mUserManager.getProfiles(eq(profile.id))).thenReturn(mPrimaryUserProfiles); } when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(SECONDARY_USER_INFO); when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(mSecondaryUserInfo); when(mUserManagerInternal.getUserInfo(eq(SECONDARY_USER_ID))) .thenReturn(mSecondaryUserInfo); when(mUserManager.getUserInfo(eq(TERTIARY_USER_ID))).thenReturn(mTertiaryUserInfo); when(mUserManagerInternal.getUserInfo(eq(TERTIARY_USER_ID))).thenReturn(mTertiaryUserInfo); final ArrayList<UserInfo> allUsers = new ArrayList<>(mPrimaryUserProfiles); allUsers.add(SECONDARY_USER_INFO); allUsers.add(mSecondaryUserInfo); allUsers.add(mTertiaryUserInfo); when(mUserManager.getUsers()).thenReturn(allUsers); when(mActivityManager.unlockUser2(anyInt(), any())).thenAnswer( Loading Loading @@ -227,9 +266,10 @@ public abstract class BaseLockSettingsServiceTests { userInfo.profileGroupId = PRIMARY_USER_ID; mPrimaryUserProfiles.add(userInfo); when(mUserManager.getUserInfo(eq(profileId))).thenReturn(userInfo); when(mUserManager.getProfileParent(eq(profileId))).thenReturn(PRIMARY_USER_INFO); when(mUserManager.getProfileParent(eq(profileId))).thenReturn(mPrimaryUserInfo); when(mUserManager.isUserRunning(eq(profileId))).thenReturn(true); when(mUserManager.isUserUnlocked(eq(profileId))).thenReturn(true); when(mUserManagerInternal.getUserInfo(eq(profileId))).thenReturn(userInfo); // TODO(b/258213147): Remove when(mUserManagerInternal.isUserManaged(eq(profileId))).thenReturn(true); when(mDeviceStateCache.isUserOrganizationManaged(eq(profileId))) Loading
services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +25 −12 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.os.RemoteException; import android.os.storage.IStorageManager; import android.security.KeyStore; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.service.gatekeeper.IGateKeeperService; import com.android.internal.widget.LockscreenCredential; import com.android.server.ServiceThread; Loading @@ -40,7 +41,7 @@ import java.io.FileNotFoundException; public class LockSettingsServiceTestable extends LockSettingsService { private static class MockInjector extends LockSettingsService.Injector { public static class MockInjector extends LockSettingsService.Injector { private LockSettingsStorage mLockSettingsStorage; private KeyStore mKeyStore; Loading @@ -52,6 +53,9 @@ public class LockSettingsServiceTestable extends LockSettingsService { private UserManagerInternal mUserManagerInternal; private DeviceStateCache mDeviceStateCache; public boolean mIsHeadlessSystemUserMode = false; public boolean mIsMainUserPermanentAdmin = false; public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore, IActivityManager activityManager, IStorageManager storageManager, SyntheticPasswordManager spManager, Loading Loading @@ -140,19 +144,22 @@ public class LockSettingsServiceTestable extends LockSettingsService { return mock(ManagedProfilePasswordCache.class); } @Override public boolean isHeadlessSystemUserMode() { return mIsHeadlessSystemUserMode; } public MockInjector mInjector; @Override public boolean isMainUserPermanentAdmin() { return mIsMainUserPermanentAdmin; } } protected LockSettingsServiceTestable(Context context, LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore, IStorageManager storageManager, IActivityManager mActivityManager, SyntheticPasswordManager spManager, IAuthSecret authSecretService, FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager, UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) { super(new MockInjector(context, storage, keystore, mActivityManager, storageManager, spManager, gsiService, recoverableKeyStoreManager, userManagerInternal, deviceStateCache)); protected LockSettingsServiceTestable( LockSettingsService.Injector injector, IGateKeeperService gatekeeper, IAuthSecret authSecretService) { super(injector); mGateKeeperService = gatekeeper; mAuthSecretService = authSecretService; } Loading Loading @@ -199,4 +206,10 @@ public class LockSettingsServiceTestable extends LockSettingsService { UserInfo userInfo = mUserManager.getUserInfo(userId); return userInfo.isCloneProfile() || userInfo.isManagedProfile(); } void clearAuthSecret() { synchronized (mHeadlessAuthSecretLock) { mAuthSecret = null; } } }
services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +64 −1 File changed.Preview size limit exceeded, changes collapsed. Show changes