Loading keystore/java/android/security/keystore/KeyGenParameterSpec.java +41 −3 Original line number Diff line number Diff line Loading @@ -320,6 +320,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final boolean mCriticalToDeviceEncryption; private final int mMaxUsageCount; private final String mAttestKeyAlias; private final long mBoundToSecureUserId; /* * ***NOTE***: All new fields MUST also be added to the following: * ParcelableKeyGenParameterSpec class. Loading Loading @@ -362,7 +364,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu boolean unlockedDeviceRequired, boolean criticalToDeviceEncryption, int maxUsageCount, String attestKeyAlias) { String attestKeyAlias, long boundToSecureUserId) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } Loading Loading @@ -422,6 +425,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mCriticalToDeviceEncryption = criticalToDeviceEncryption; mMaxUsageCount = maxUsageCount; mAttestKeyAlias = attestKeyAlias; mBoundToSecureUserId = boundToSecureUserId; } /** Loading Loading @@ -842,10 +846,20 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** * Return the secure user id that this key should be bound to. * * Normally an authentication-bound key is tied to the secure user id of the current user * (either the root SID from GateKeeper for auth-bound keys with a timeout, or the authenticator * id of the current biometric set for keys requiring explicit biometric authorization). * If this parameter is set (this method returning non-zero value), the key should be tied to * the specified secure user id, overriding the logic above. * * This is only applicable when {@link #isUserAuthenticationRequired} is {@code true} * * @hide */ public long getBoundToSpecificSecureUserId() { return GateKeeper.INVALID_SECURE_USER_ID; return mBoundToSecureUserId; } /** Loading Loading @@ -920,6 +934,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private boolean mCriticalToDeviceEncryption = false; private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT; private String mAttestKeyAlias = null; private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; /** * Creates a new instance of the {@code Builder}. Loading Loading @@ -990,6 +1005,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption(); mMaxUsageCount = sourceSpec.getMaxUsageCount(); mAttestKeyAlias = sourceSpec.getAttestKeyAlias(); mBoundToSecureUserId = sourceSpec.getBoundToSpecificSecureUserId(); } /** Loading Loading @@ -1724,6 +1740,27 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu return this; } /** * Set the secure user id that this key should be bound to. * * Normally an authentication-bound key is tied to the secure user id of the current user * (either the root SID from GateKeeper for auth-bound keys with a timeout, or the * authenticator id of the current biometric set for keys requiring explicit biometric * authorization). If this parameter is set (this method returning non-zero value), the key * should be tied to the specified secure user id, overriding the logic above. * * This is only applicable when {@link #setUserAuthenticationRequired} is set to * {@code true} * * @see KeyGenParameterSpec#getBoundToSpecificSecureUserId() * @hide */ @NonNull public Builder setBoundToSpecificSecureUserId(long secureUserId) { mBoundToSecureUserId = secureUserId; return this; } /** * Builds an instance of {@code KeyGenParameterSpec}. */ Loading Loading @@ -1762,7 +1799,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUnlockedDeviceRequired, mCriticalToDeviceEncryption, mMaxUsageCount, mAttestKeyAlias); mAttestKeyAlias, mBoundToSecureUserId); } } } keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +4 −1 Original line number Diff line number Diff line Loading @@ -111,6 +111,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { out.writeBoolean(mSpec.isCriticalToDeviceEncryption()); out.writeInt(mSpec.getMaxUsageCount()); out.writeString(mSpec.getAttestKeyAlias()); out.writeLong(mSpec.getBoundToSpecificSecureUserId()); } private static Date readDateOrNull(Parcel in) { Loading Loading @@ -172,6 +173,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { final boolean criticalToDeviceEncryption = in.readBoolean(); final int maxUsageCount = in.readInt(); final String attestKeyAlias = in.readString(); final long boundToSecureUserId = in.readLong(); // The KeyGenParameterSpec is intentionally not constructed using a Builder here: // The intention is for this class to break if new parameters are added to the // KeyGenParameterSpec constructor (whereas using a builder would silently drop them). Loading Loading @@ -208,7 +210,8 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { unlockedDeviceRequired, criticalToDeviceEncryption, maxUsageCount, attestKeyAlias); attestKeyAlias, boundToSecureUserId); } public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() { Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +15 −4 Original line number Diff line number Diff line Loading @@ -401,11 +401,13 @@ public class LockSettingsService extends ILockSettings.Stub { profileUserId, /* isLockTiedToParent= */ true); return; } final long parentSid; // Do not tie when the parent has no SID (but does have a screen lock). // This can only happen during an upgrade path where SID is yet to be // generated when the user unlocks for the first time. try { if (getGateKeeperService().getSecureUserId(parentId) == 0) { parentSid = getGateKeeperService().getSecureUserId(parentId); if (parentSid == 0) { return; } } catch (RemoteException e) { Loading @@ -416,7 +418,8 @@ public class LockSettingsService extends ILockSettings.Stub { setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId, /* isLockTiedToParent= */ true); tieProfileLockToParent(profileUserId, parentId, unifiedProfilePassword); mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword); mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, parentSid); } } Loading Loading @@ -575,7 +578,7 @@ public class LockSettingsService extends ILockSettings.Stub { public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache( java.security.KeyStore ks) { return new ManagedProfilePasswordCache(ks, getUserManager()); return new ManagedProfilePasswordCache(ks); } public boolean isHeadlessSystemUserMode() { Loading Loading @@ -1346,7 +1349,13 @@ public class LockSettingsService extends ILockSettings.Stub { LockscreenCredential credential = LockscreenCredential.createManagedPassword( decryptionResult); Arrays.fill(decryptionResult, (byte) 0); mManagedProfilePasswordCache.storePassword(userId, credential); try { long parentSid = getGateKeeperService().getSecureUserId( mUserManager.getProfileParent(userId).id); mManagedProfilePasswordCache.storePassword(userId, credential, parentSid); } catch (RemoteException e) { Slogf.w(TAG, "Failed to talk to GateKeeper service", e); } return credential; } Loading Loading @@ -2224,6 +2233,8 @@ public class LockSettingsService extends ILockSettings.Stub { public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential, int userId, @LockPatternUtils.VerifyFlag int flags) { checkPasswordReadPermission(); Slogf.i(TAG, "Verifying tied profile challenge for user %d", userId); if (!isProfileWithUnifiedLock(userId)) { throw new IllegalArgumentException( "User id must be managed/clone profile with unified lock"); Loading services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java +10 −20 Original line number Diff line number Diff line Loading @@ -17,9 +17,7 @@ package com.android.server.locksettings; import android.annotation.Nullable; import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; import android.security.GateKeeper; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.security.keystore.UserNotAuthenticatedException; Loading @@ -45,12 +43,9 @@ import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; /** * Caches *unified* work challenge for user 0's managed profiles. Only user 0's profile is supported * at the moment because the cached credential is encrypted using a keystore key auth-bound to * user 0: this is to match how unified work challenge is similarly auth-bound to its parent user's * lockscreen credential normally. It's possible to extend this class to support managed profiles * for secondary users, that will require generating auth-bound keys to their corresponding parent * user though (which {@link KeyGenParameterSpec} does not support right now). * Caches *unified* work challenge for managed profiles. The cached credential is encrypted using * a keystore key auth-bound to the parent user's lockscreen credential, similar to how unified * work challenge is normally secured. * * <p> The cache is filled whenever the managed profile's unified challenge is created or derived * (as part of the parent user's credential verification flow). It's removed when the profile is Loading @@ -70,28 +65,23 @@ public class ManagedProfilePasswordCache { private final SparseArray<byte[]> mEncryptedPasswords = new SparseArray<>(); private final KeyStore mKeyStore; private final UserManager mUserManager; public ManagedProfilePasswordCache(KeyStore keyStore, UserManager userManager) { public ManagedProfilePasswordCache(KeyStore keyStore) { mKeyStore = keyStore; mUserManager = userManager; } /** * Encrypt and store the password in the cache. Does NOT overwrite existing password cache * if one for the given user already exists. * * Should only be called on a profile userId. */ public void storePassword(int userId, LockscreenCredential password) { public void storePassword(int userId, LockscreenCredential password, long parentSid) { if (parentSid == GateKeeper.INVALID_SECURE_USER_ID) return; synchronized (mEncryptedPasswords) { if (mEncryptedPasswords.contains(userId)) { return; } UserInfo parent = mUserManager.getProfileParent(userId); if (parent == null || parent.id != UserHandle.USER_SYSTEM) { // Since the cached password is encrypted using a keystore key auth-bound to user 0, // only support caching password for user 0's profile. return; } String keyName = getEncryptionKeyName(userId); KeyGenerator generator; SecretKey key; Loading @@ -104,8 +94,8 @@ public class ManagedProfilePasswordCache { .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setNamespace(SyntheticPasswordCrypto.keyNamespace()) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) // Generate auth-bound key to user 0 (since we the caller is user 0) .setUserAuthenticationRequired(true) .setBoundToSpecificSecureUserId(parentSid) .setUserAuthenticationValidityDurationSeconds(CACHE_TIMEOUT_SECONDS) .build()); key = generator.generateKey(); Loading Loading
keystore/java/android/security/keystore/KeyGenParameterSpec.java +41 −3 Original line number Diff line number Diff line Loading @@ -320,6 +320,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final boolean mCriticalToDeviceEncryption; private final int mMaxUsageCount; private final String mAttestKeyAlias; private final long mBoundToSecureUserId; /* * ***NOTE***: All new fields MUST also be added to the following: * ParcelableKeyGenParameterSpec class. Loading Loading @@ -362,7 +364,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu boolean unlockedDeviceRequired, boolean criticalToDeviceEncryption, int maxUsageCount, String attestKeyAlias) { String attestKeyAlias, long boundToSecureUserId) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } Loading Loading @@ -422,6 +425,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mCriticalToDeviceEncryption = criticalToDeviceEncryption; mMaxUsageCount = maxUsageCount; mAttestKeyAlias = attestKeyAlias; mBoundToSecureUserId = boundToSecureUserId; } /** Loading Loading @@ -842,10 +846,20 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** * Return the secure user id that this key should be bound to. * * Normally an authentication-bound key is tied to the secure user id of the current user * (either the root SID from GateKeeper for auth-bound keys with a timeout, or the authenticator * id of the current biometric set for keys requiring explicit biometric authorization). * If this parameter is set (this method returning non-zero value), the key should be tied to * the specified secure user id, overriding the logic above. * * This is only applicable when {@link #isUserAuthenticationRequired} is {@code true} * * @hide */ public long getBoundToSpecificSecureUserId() { return GateKeeper.INVALID_SECURE_USER_ID; return mBoundToSecureUserId; } /** Loading Loading @@ -920,6 +934,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private boolean mCriticalToDeviceEncryption = false; private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT; private String mAttestKeyAlias = null; private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; /** * Creates a new instance of the {@code Builder}. Loading Loading @@ -990,6 +1005,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption(); mMaxUsageCount = sourceSpec.getMaxUsageCount(); mAttestKeyAlias = sourceSpec.getAttestKeyAlias(); mBoundToSecureUserId = sourceSpec.getBoundToSpecificSecureUserId(); } /** Loading Loading @@ -1724,6 +1740,27 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu return this; } /** * Set the secure user id that this key should be bound to. * * Normally an authentication-bound key is tied to the secure user id of the current user * (either the root SID from GateKeeper for auth-bound keys with a timeout, or the * authenticator id of the current biometric set for keys requiring explicit biometric * authorization). If this parameter is set (this method returning non-zero value), the key * should be tied to the specified secure user id, overriding the logic above. * * This is only applicable when {@link #setUserAuthenticationRequired} is set to * {@code true} * * @see KeyGenParameterSpec#getBoundToSpecificSecureUserId() * @hide */ @NonNull public Builder setBoundToSpecificSecureUserId(long secureUserId) { mBoundToSecureUserId = secureUserId; return this; } /** * Builds an instance of {@code KeyGenParameterSpec}. */ Loading Loading @@ -1762,7 +1799,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUnlockedDeviceRequired, mCriticalToDeviceEncryption, mMaxUsageCount, mAttestKeyAlias); mAttestKeyAlias, mBoundToSecureUserId); } } }
keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +4 −1 Original line number Diff line number Diff line Loading @@ -111,6 +111,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { out.writeBoolean(mSpec.isCriticalToDeviceEncryption()); out.writeInt(mSpec.getMaxUsageCount()); out.writeString(mSpec.getAttestKeyAlias()); out.writeLong(mSpec.getBoundToSpecificSecureUserId()); } private static Date readDateOrNull(Parcel in) { Loading Loading @@ -172,6 +173,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { final boolean criticalToDeviceEncryption = in.readBoolean(); final int maxUsageCount = in.readInt(); final String attestKeyAlias = in.readString(); final long boundToSecureUserId = in.readLong(); // The KeyGenParameterSpec is intentionally not constructed using a Builder here: // The intention is for this class to break if new parameters are added to the // KeyGenParameterSpec constructor (whereas using a builder would silently drop them). Loading Loading @@ -208,7 +210,8 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { unlockedDeviceRequired, criticalToDeviceEncryption, maxUsageCount, attestKeyAlias); attestKeyAlias, boundToSecureUserId); } public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() { Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +15 −4 Original line number Diff line number Diff line Loading @@ -401,11 +401,13 @@ public class LockSettingsService extends ILockSettings.Stub { profileUserId, /* isLockTiedToParent= */ true); return; } final long parentSid; // Do not tie when the parent has no SID (but does have a screen lock). // This can only happen during an upgrade path where SID is yet to be // generated when the user unlocks for the first time. try { if (getGateKeeperService().getSecureUserId(parentId) == 0) { parentSid = getGateKeeperService().getSecureUserId(parentId); if (parentSid == 0) { return; } } catch (RemoteException e) { Loading @@ -416,7 +418,8 @@ public class LockSettingsService extends ILockSettings.Stub { setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId, /* isLockTiedToParent= */ true); tieProfileLockToParent(profileUserId, parentId, unifiedProfilePassword); mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword); mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, parentSid); } } Loading Loading @@ -575,7 +578,7 @@ public class LockSettingsService extends ILockSettings.Stub { public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache( java.security.KeyStore ks) { return new ManagedProfilePasswordCache(ks, getUserManager()); return new ManagedProfilePasswordCache(ks); } public boolean isHeadlessSystemUserMode() { Loading Loading @@ -1346,7 +1349,13 @@ public class LockSettingsService extends ILockSettings.Stub { LockscreenCredential credential = LockscreenCredential.createManagedPassword( decryptionResult); Arrays.fill(decryptionResult, (byte) 0); mManagedProfilePasswordCache.storePassword(userId, credential); try { long parentSid = getGateKeeperService().getSecureUserId( mUserManager.getProfileParent(userId).id); mManagedProfilePasswordCache.storePassword(userId, credential, parentSid); } catch (RemoteException e) { Slogf.w(TAG, "Failed to talk to GateKeeper service", e); } return credential; } Loading Loading @@ -2224,6 +2233,8 @@ public class LockSettingsService extends ILockSettings.Stub { public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential, int userId, @LockPatternUtils.VerifyFlag int flags) { checkPasswordReadPermission(); Slogf.i(TAG, "Verifying tied profile challenge for user %d", userId); if (!isProfileWithUnifiedLock(userId)) { throw new IllegalArgumentException( "User id must be managed/clone profile with unified lock"); Loading
services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java +10 −20 Original line number Diff line number Diff line Loading @@ -17,9 +17,7 @@ package com.android.server.locksettings; import android.annotation.Nullable; import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; import android.security.GateKeeper; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.security.keystore.UserNotAuthenticatedException; Loading @@ -45,12 +43,9 @@ import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; /** * Caches *unified* work challenge for user 0's managed profiles. Only user 0's profile is supported * at the moment because the cached credential is encrypted using a keystore key auth-bound to * user 0: this is to match how unified work challenge is similarly auth-bound to its parent user's * lockscreen credential normally. It's possible to extend this class to support managed profiles * for secondary users, that will require generating auth-bound keys to their corresponding parent * user though (which {@link KeyGenParameterSpec} does not support right now). * Caches *unified* work challenge for managed profiles. The cached credential is encrypted using * a keystore key auth-bound to the parent user's lockscreen credential, similar to how unified * work challenge is normally secured. * * <p> The cache is filled whenever the managed profile's unified challenge is created or derived * (as part of the parent user's credential verification flow). It's removed when the profile is Loading @@ -70,28 +65,23 @@ public class ManagedProfilePasswordCache { private final SparseArray<byte[]> mEncryptedPasswords = new SparseArray<>(); private final KeyStore mKeyStore; private final UserManager mUserManager; public ManagedProfilePasswordCache(KeyStore keyStore, UserManager userManager) { public ManagedProfilePasswordCache(KeyStore keyStore) { mKeyStore = keyStore; mUserManager = userManager; } /** * Encrypt and store the password in the cache. Does NOT overwrite existing password cache * if one for the given user already exists. * * Should only be called on a profile userId. */ public void storePassword(int userId, LockscreenCredential password) { public void storePassword(int userId, LockscreenCredential password, long parentSid) { if (parentSid == GateKeeper.INVALID_SECURE_USER_ID) return; synchronized (mEncryptedPasswords) { if (mEncryptedPasswords.contains(userId)) { return; } UserInfo parent = mUserManager.getProfileParent(userId); if (parent == null || parent.id != UserHandle.USER_SYSTEM) { // Since the cached password is encrypted using a keystore key auth-bound to user 0, // only support caching password for user 0's profile. return; } String keyName = getEncryptionKeyName(userId); KeyGenerator generator; SecretKey key; Loading @@ -104,8 +94,8 @@ public class ManagedProfilePasswordCache { .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setNamespace(SyntheticPasswordCrypto.keyNamespace()) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) // Generate auth-bound key to user 0 (since we the caller is user 0) .setUserAuthenticationRequired(true) .setBoundToSpecificSecureUserId(parentSid) .setUserAuthenticationValidityDurationSeconds(CACHE_TIMEOUT_SECONDS) .build()); key = generator.generateKey(); Loading