From ee06721852794015c7054f5e64163c0ee5944fd9 Mon Sep 17 00:00:00 2001 From: Dmitry Dementyev Date: Mon, 6 Feb 2023 20:29:42 -0800 Subject: [PATCH] Add remote lockscreen verification APIs to KeyguardManager. Adds CHECK_REMOTE_LOCKSCREEN system permission. go/setup-lskf-apis Test: manual Bug: 254335492 Change-Id: Iab2f2ff5f710e05dfb2912201408bd12759695e2 --- core/api/system-current.txt | 39 ++++++++++++++++++ core/java/android/app/KeyguardManager.java | 40 +++++++++++++++++++ .../app/RemoteLockscreenValidationResult.java | 2 + .../app/StartLockscreenValidationRequest.java | 2 + .../internal/widget/ILockSettings.aidl | 4 ++ .../internal/widget/LockPatternUtils.java | 27 +++++++++++++ core/res/AndroidManifest.xml | 5 +++ .../locksettings/LockSettingsService.java | 2 +- .../RecoverableKeyStoreManager.java | 5 +-- .../RecoverableKeyStoreManagerTest.java | 4 +- 10 files changed, 124 insertions(+), 6 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index d6aa7fc4bf90c..ce5a5e2efc8ec 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -100,6 +100,7 @@ package android { field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; field public static final String CHANGE_APP_LAUNCH_TIME_ESTIMATE = "android.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE"; field public static final String CHANGE_DEVICE_IDLE_TEMP_WHITELIST = "android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"; + field public static final String CHECK_REMOTE_LOCKSCREEN = "android.permission.CHECK_REMOTE_LOCKSCREEN"; field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; field public static final String COMPANION_APPROVE_WIFI_CONNECTIONS = "android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; @@ -919,7 +920,9 @@ package android.app { method @RequiresPermission(android.Manifest.permission.SHOW_KEYGUARD_MESSAGE) public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable CharSequence, @Nullable android.app.KeyguardManager.KeyguardDismissCallback); method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean setLock(int, @NonNull byte[], int); method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public void setPrivateNotificationsAllowed(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.StartLockscreenValidationRequest startRemoteLockscreenValidation(); method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean unregisterWeakEscrowTokenRemovedListener(@NonNull android.app.KeyguardManager.WeakEscrowTokenRemovedListener); + method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.RemoteLockscreenValidationResult validateRemoteLockscreen(@NonNull byte[]); field public static final int PASSWORD = 0; // 0x0 field public static final int PATTERN = 2; // 0x2 field public static final int PIN = 1; // 0x1 @@ -996,6 +999,25 @@ package android.app { field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; } + public final class RemoteLockscreenValidationResult implements android.os.Parcelable { + method public int describeContents(); + method public int getResultCode(); + method public long getTimeoutMillis(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int RESULT_GUESS_INVALID = 2; // 0x2 + field public static final int RESULT_GUESS_VALID = 1; // 0x1 + field public static final int RESULT_LOCKOUT = 3; // 0x3 + field public static final int RESULT_NO_REMAINING_ATTEMPTS = 4; // 0x4 + } + + public static final class RemoteLockscreenValidationResult.Builder { + ctor public RemoteLockscreenValidationResult.Builder(); + method @NonNull public android.app.RemoteLockscreenValidationResult build(); + method @NonNull public android.app.RemoteLockscreenValidationResult.Builder setResultCode(int); + method @NonNull public android.app.RemoteLockscreenValidationResult.Builder setTimeoutMillis(long); + } + public final class RuntimeAppOpAccessMessage implements android.os.Parcelable { ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int); method public int describeContents(); @@ -1013,6 +1035,23 @@ package android.app { method public void launchAssist(@Nullable android.os.Bundle); } + public final class StartLockscreenValidationRequest implements android.os.Parcelable { + method public int describeContents(); + method public int getLockscreenUiType(); + method public int getRemainingAttempts(); + method @NonNull public byte[] getSourcePublicKey(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class StartLockscreenValidationRequest.Builder { + ctor public StartLockscreenValidationRequest.Builder(); + method @NonNull public android.app.StartLockscreenValidationRequest build(); + method @NonNull public android.app.StartLockscreenValidationRequest.Builder setLockscreenUiType(int); + method @NonNull public android.app.StartLockscreenValidationRequest.Builder setRemainingAttempts(int); + method @NonNull public android.app.StartLockscreenValidationRequest.Builder setSourcePublicKey(@NonNull byte[]); + } + public class StatusBarManager { method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo(); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarMode(); diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 88c506474419b..be3d5a6a236f6 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -1075,6 +1075,46 @@ public class KeyguardManager { return response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK; } + /** Starts a session to verify lockscreen credentials provided by a remote device. + * + * The session and corresponding public key will be removed when + * {@code validateRemoteLockScreen} provides a correct guess or after 10 minutes of inactivity. + * + * @return information necessary to perform remote lock screen credentials check, including + + * short lived public key used to send encrypted guess and lock screen type. + * + * @throws IllegalStateException if lock screen is not set + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN) + @NonNull + public StartLockscreenValidationRequest startRemoteLockscreenValidation() { + return mLockPatternUtils.startRemoteLockscreenValidation(); + } + + /** + * Verifies credentials guess from a remote device. + * + *

Secret must be encrypted using {@code SecureBox} library + * with public key from {@code StartLockscreenValidationRequest} + * and header set to {@code "encrypted_remote_credentials"} in UTF-8 encoding. + * + * @throws IllegalStateException if there is no active lock screen validation session or + * there was a decryption error. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN) + @NonNull + public RemoteLockscreenValidationResult validateRemoteLockscreen( + @NonNull byte[] encryptedCredential) { + return mLockPatternUtils.validateRemoteLockscreen(encryptedCredential); + } + private LockscreenCredential createLockscreenCredential( @LockTypes int lockType, @Nullable byte[] password) { if (password == null) { diff --git a/core/java/android/app/RemoteLockscreenValidationResult.java b/core/java/android/app/RemoteLockscreenValidationResult.java index 4f15be209bce1..0245f8c254ad4 100644 --- a/core/java/android/app/RemoteLockscreenValidationResult.java +++ b/core/java/android/app/RemoteLockscreenValidationResult.java @@ -19,6 +19,7 @@ package android.app; import android.annotation.DurationMillisLong; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -29,6 +30,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@SystemApi public final class RemoteLockscreenValidationResult implements Parcelable { /** diff --git a/core/java/android/app/StartLockscreenValidationRequest.java b/core/java/android/app/StartLockscreenValidationRequest.java index 69c268bcbec63..e81819566bd1e 100644 --- a/core/java/android/app/StartLockscreenValidationRequest.java +++ b/core/java/android/app/StartLockscreenValidationRequest.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.app.KeyguardManager.LockTypes; import android.os.Parcel; import android.os.Parcelable; @@ -28,6 +29,7 @@ import java.util.Objects; * * @hide */ +@SystemApi public final class StartLockscreenValidationRequest implements Parcelable { @LockTypes diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 3494c9e05b4a7..a646df35b714b 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -17,6 +17,8 @@ package com.android.internal.widget; import android.app.PendingIntent; +import android.app.RemoteLockscreenValidationResult; +import android.app.StartLockscreenValidationRequest; import android.app.trust.IStrongAuthTracker; import android.os.Bundle; import android.security.keystore.recovery.WrappedApplicationKey; @@ -93,6 +95,8 @@ interface ILockSettings { in byte[] recoveryKeyBlob, in List applicationKeys); void closeSession(in String sessionId); + StartLockscreenValidationRequest startRemoteLockscreenValidation(); + RemoteLockscreenValidationResult validateRemoteLockscreen(in byte[] encryptedCredential); boolean hasSecureLockScreen(); boolean tryUnlockWithCachedUnifiedChallenge(int userId); void removeCachedUnifiedChallenge(int userId); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 4d820acd77b2b..44c37dcb2383d 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -28,6 +28,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.PropertyInvalidatedCache; +import android.app.RemoteLockscreenValidationResult; +import android.app.StartLockscreenValidationRequest; import android.app.admin.DevicePolicyManager; import android.app.admin.PasswordMetrics; import android.app.trust.IStrongAuthTracker; @@ -1822,4 +1824,29 @@ public class LockPatternUtils { public void removeUser(@UserIdInt int userId) { getLockSettingsInternal().removeUser(userId); } + + /** + * Starts a session to verify lockscreen credentials provided by a remote device. + */ + @NonNull + public StartLockscreenValidationRequest startRemoteLockscreenValidation() { + try { + return getLockSettings().startRemoteLockscreenValidation(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Verifies credentials guess from a remote device. + */ + @NonNull + public RemoteLockscreenValidationResult validateRemoteLockscreen( + @NonNull byte[] encryptedCredential) { + try { + return getLockSettings().validateRemoteLockscreen(encryptedCredential); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0b6b0a1350cb6..5b77e3eaa4fbb 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5975,6 +5975,11 @@ + + + diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 1457bffc13f19..920e38d69569c 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2553,7 +2553,7 @@ public class LockSettingsService extends ILockSettings.Stub { */ @NonNull public RemoteLockscreenValidationResult - validateRemoteLockScreen2(@NonNull byte[] encryptedCredential) { + validateRemoteLockscreen(@NonNull byte[] encryptedCredential) { return mRecoverableKeyStoreManager.validateRemoteLockscreen(encryptedCredential, this); } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index 33dc7efe0a9be..6bf64eb219926 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -1126,11 +1126,10 @@ public class RecoverableKeyStoreManager { } private void checkVerifyRemoteLockscreenPermission() { - // TODO(b/254335492): Check new system permission mContext.enforceCallingOrSelfPermission( - Manifest.permission.RECOVER_KEYSTORE, + Manifest.permission.CHECK_REMOTE_LOCKSCREEN, "Caller " + Binder.getCallingUid() - + " doesn't have verifyRemoteLockscreen permission."); + + " doesn't have CHECK_REMOTE_LOCKSCREEN permission."); int userId = UserHandle.getCallingUserId(); int uid = Binder.getCallingUid(); mCleanupManager.registerRecoveryAgent(userId, uid); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index 32cb8c4e0a91d..ad6d6b930a52f 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -1316,10 +1316,10 @@ public class RecoverableKeyStoreManagerTest { mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); - // TODO(b/254335492): Check new system permission verify(mMockContext, times(1)) .enforceCallingOrSelfPermission( - eq(Manifest.permission.RECOVER_KEYSTORE), any()); + eq(Manifest.permission.CHECK_REMOTE_LOCKSCREEN), any()); + mRemoteLockscreenValidationSessionStorage.finishSession(mUserId); } @Test public void startRemoteLockscreenValidation_returnsCredentailsType() throws Exception {