diff options
author | Andres Morales <anmorales@google.com> | 2015-05-14 22:42:26 -0700 |
---|---|---|
committer | Andres Morales <anmorales@google.com> | 2015-05-26 19:48:51 -0700 |
commit | 2397427cb1a0bad8a42e6a342dcf29b31e40a234 (patch) | |
tree | 08695c000f77519a3698bb25044023973d0ce6f8 /core/java/com/android | |
parent | aba3ecb976cacd7c92fe8f8afae20d112781d68e (diff) | |
download | frameworks_base-2397427cb1a0bad8a42e6a342dcf29b31e40a234.zip frameworks_base-2397427cb1a0bad8a42e6a342dcf29b31e40a234.tar.gz frameworks_base-2397427cb1a0bad8a42e6a342dcf29b31e40a234.tar.bz2 |
[LockSettings] migrate password attempt throttling to hardware
leverage root protected, cryptographically secured hardware
if available
Bug: 21118563
Change-Id: Ifa804c5a0728bfd14466eb2a84051bace6d33d57
Diffstat (limited to 'core/java/com/android')
5 files changed, 273 insertions, 37 deletions
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index bfafff6..dfb7c50 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -16,6 +16,8 @@ package com.android.internal.widget; +import com.android.internal.widget.VerifyCredentialResponse; + /** {@hide} */ interface ILockSettings { void setBoolean(in String key, in boolean value, in int userId); @@ -25,11 +27,11 @@ interface ILockSettings { long getLong(in String key, in long defaultValue, in int userId); String getString(in String key, in String defaultValue, in int userId); void setLockPattern(in String pattern, in String savedPattern, int userId); - boolean checkPattern(in String pattern, int userId); - byte[] verifyPattern(in String pattern, long challenge, int userId); + VerifyCredentialResponse checkPattern(in String pattern, int userId); + VerifyCredentialResponse verifyPattern(in String pattern, long challenge, int userId); void setLockPassword(in String password, in String savedPassword, int userId); - boolean checkPassword(in String password, int userId); - byte[] verifyPassword(in String password, long challenge, int userId); + VerifyCredentialResponse checkPassword(in String password, int userId); + VerifyCredentialResponse verifyPassword(in String password, long challenge, int userId); boolean checkVoldPassword(int userId); boolean havePattern(int userId); boolean havePassword(int userId); diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java index ac0f5fe..4880664 100644 --- a/core/java/com/android/internal/widget/LockPatternChecker.java +++ b/core/java/com/android/internal/widget/LockPatternChecker.java @@ -2,6 +2,8 @@ package com.android.internal.widget; import android.os.AsyncTask; +import com.android.internal.widget.LockPatternUtils.RequestThrottledException; + import java.util.List; /** @@ -16,8 +18,10 @@ public final class LockPatternChecker { * Invoked when a security check is finished. * * @param matched Whether the PIN/Password/Pattern matches the stored one. + * @param throttleTimeoutMs The amount of time in ms to wait before reattempting + * the call. Only non-0 if matched is false. */ - void onChecked(boolean matched); + void onChecked(boolean matched, int throttleTimeoutMs); } /** @@ -28,8 +32,10 @@ public final class LockPatternChecker { * Invoked when a security verification is finished. * * @param attestation The attestation that the challenge was verified, or null. + * @param throttleTimeoutMs The amount of time in ms to wait before reattempting + * the call. Only non-0 if attestation is null. */ - void onVerified(byte[] attestation); + void onVerified(byte[] attestation, int throttleTimeoutMs); } /** @@ -47,14 +53,21 @@ public final class LockPatternChecker { final int userId, final OnVerifyCallback callback) { AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { + private int mThrottleTimeout; + @Override protected byte[] doInBackground(Void... args) { - return utils.verifyPattern(pattern, challenge, userId); + try { + return utils.verifyPattern(pattern, challenge, userId); + } catch (RequestThrottledException ex) { + mThrottleTimeout = ex.getTimeoutMs(); + return null; + } } @Override protected void onPostExecute(byte[] result) { - callback.onVerified(result); + callback.onVerified(result, mThrottleTimeout); } }; task.execute(); @@ -74,14 +87,21 @@ public final class LockPatternChecker { final int userId, final OnCheckCallback callback) { AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { + private int mThrottleTimeout; + @Override protected Boolean doInBackground(Void... args) { - return utils.checkPattern(pattern, userId); + try { + return utils.checkPattern(pattern, userId); + } catch (RequestThrottledException ex) { + mThrottleTimeout = ex.getTimeoutMs(); + return false; + } } @Override protected void onPostExecute(Boolean result) { - callback.onChecked(result); + callback.onChecked(result, mThrottleTimeout); } }; task.execute(); @@ -103,14 +123,21 @@ public final class LockPatternChecker { final int userId, final OnVerifyCallback callback) { AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { + private int mThrottleTimeout; + @Override protected byte[] doInBackground(Void... args) { - return utils.verifyPassword(password, challenge, userId); + try { + return utils.verifyPassword(password, challenge, userId); + } catch (RequestThrottledException ex) { + mThrottleTimeout = ex.getTimeoutMs(); + return null; + } } @Override protected void onPostExecute(byte[] result) { - callback.onVerified(result); + callback.onVerified(result, mThrottleTimeout); } }; task.execute(); @@ -130,14 +157,21 @@ public final class LockPatternChecker { final int userId, final OnCheckCallback callback) { AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { + private int mThrottleTimeout; + @Override protected Boolean doInBackground(Void... args) { - return utils.checkPassword(password, userId); + try { + return utils.checkPassword(password, userId); + } catch (RequestThrottledException ex) { + mThrottleTimeout = ex.getTimeoutMs(); + return false; + } } @Override protected void onPostExecute(Boolean result) { - callback.onChecked(result); + callback.onChecked(result, mThrottleTimeout); } }; task.execute(); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index e5ef60c..8d85b33 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -60,24 +60,12 @@ public class LockPatternUtils { private static final boolean DEBUG = false; /** - * The maximum number of incorrect attempts before the user is prevented - * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}. - */ - public static final int FAILED_ATTEMPTS_BEFORE_TIMEOUT = 5; - - /** * The number of incorrect attempts before which we fall back on an alternative * method of verifying the user, and resetting their lock pattern. */ public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20; /** - * How long the user is prevented from trying again after entering the - * wrong pattern too many times. - */ - public static final long FAILED_ATTEMPT_TIMEOUT_MS = 30000L; - - /** * The interval of the countdown for showing progress of the lockout. */ public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L; @@ -109,6 +97,7 @@ public class LockPatternUtils { @Deprecated public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; + public final static String LOCKOUT_ATTEMPT_TIMEOUT_MS = "lockscreen.lockoutattempttimeoutmss"; public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; @Deprecated @@ -144,6 +133,23 @@ public class LockPatternUtils { private DevicePolicyManager mDevicePolicyManager; private ILockSettings mLockSettingsService; + + public static final class RequestThrottledException extends Exception { + private int mTimeoutMs; + public RequestThrottledException(int timeoutMs) { + mTimeoutMs = timeoutMs; + } + + /** + * @return The amount of time in ms before another request may + * be executed + */ + public int getTimeoutMs() { + return mTimeoutMs; + } + + } + public DevicePolicyManager getDevicePolicyManager() { if (mDevicePolicyManager == null) { mDevicePolicyManager = @@ -239,9 +245,23 @@ public class LockPatternUtils { * @param challenge The challenge to verify against the pattern * @return the attestation that the challenge was verified, or null. */ - public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId) { + public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId) + throws RequestThrottledException { try { - return getLockSettings().verifyPattern(patternToString(pattern), challenge, userId); + VerifyCredentialResponse response = + getLockSettings().verifyPattern(patternToString(pattern), challenge, userId); + if (response == null) { + // Shouldn't happen + return null; + } + + if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { + return response.getPayload(); + } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { + throw new RequestThrottledException(response.getTimeout()); + } else { + return null; + } } catch (RemoteException re) { return null; } @@ -253,9 +273,19 @@ public class LockPatternUtils { * @param pattern The pattern to check. * @return Whether the pattern matches the stored one. */ - public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId) { + public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId) + throws RequestThrottledException { try { - return getLockSettings().checkPattern(patternToString(pattern), userId); + VerifyCredentialResponse response = + getLockSettings().checkPattern(patternToString(pattern), userId); + + if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { + return true; + } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { + throw new RequestThrottledException(response.getTimeout()); + } else { + return false; + } } catch (RemoteException re) { return true; } @@ -270,9 +300,19 @@ public class LockPatternUtils { * @param challenge The challenge to verify against the password * @return the attestation that the challenge was verified, or null. */ - public byte[] verifyPassword(String password, long challenge, int userId) { + public byte[] verifyPassword(String password, long challenge, int userId) + throws RequestThrottledException { try { - return getLockSettings().verifyPassword(password, challenge, userId); + VerifyCredentialResponse response = + getLockSettings().verifyPassword(password, challenge, userId); + + if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { + return response.getPayload(); + } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { + throw new RequestThrottledException(response.getTimeout()); + } else { + return null; + } } catch (RemoteException re) { return null; } @@ -284,9 +324,17 @@ public class LockPatternUtils { * @param password The password to check. * @return Whether the password matches the stored one. */ - public boolean checkPassword(String password, int userId) { + public boolean checkPassword(String password, int userId) throws RequestThrottledException { try { - return getLockSettings().checkPassword(password, userId); + VerifyCredentialResponse response = + getLockSettings().checkPassword(password, userId); + if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { + return true; + } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { + throw new RequestThrottledException(response.getTimeout()); + } else { + return false; + } } catch (RemoteException re) { return true; } @@ -969,9 +1017,10 @@ public class LockPatternUtils { * pattern until the deadline has passed. * @return the chosen deadline. */ - public long setLockoutAttemptDeadline(int userId) { - final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS; + public long setLockoutAttemptDeadline(int userId, int timeoutMs) { + final long deadline = SystemClock.elapsedRealtime() + timeoutMs; setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId); + setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, timeoutMs, userId); return deadline; } @@ -982,8 +1031,9 @@ public class LockPatternUtils { */ public long getLockoutAttemptDeadline(int userId) { final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId); + final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId); final long now = SystemClock.elapsedRealtime(); - if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) { + if (deadline < now || deadline > (now + timeoutMs)) { return 0L; } return deadline; diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.aidl b/core/java/com/android/internal/widget/VerifyCredentialResponse.aidl new file mode 100644 index 0000000..59a4bba --- /dev/null +++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +/** + * Response object for an ILockSettings verification request. + * @hide + */ +parcelable VerifyCredentialResponse; + diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java new file mode 100644 index 0000000..48109ca --- /dev/null +++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Response object for a ILockSettings credential verification request. + * @hide + */ +public final class VerifyCredentialResponse implements Parcelable { + + public static final int RESPONSE_ERROR = -1; + public static final int RESPONSE_OK = 0; + public static final int RESPONSE_RETRY = 1; + + public static final VerifyCredentialResponse OK = new VerifyCredentialResponse(); + public static final VerifyCredentialResponse ERROR + = new VerifyCredentialResponse(RESPONSE_ERROR, 0, null); + + private int mResponseCode; + private byte[] mPayload; + private int mTimeout; + + public static final Parcelable.Creator<VerifyCredentialResponse> CREATOR + = new Parcelable.Creator<VerifyCredentialResponse>() { + @Override + public VerifyCredentialResponse createFromParcel(Parcel source) { + int responseCode = source.readInt(); + VerifyCredentialResponse response = new VerifyCredentialResponse(responseCode, 0, null); + if (responseCode == RESPONSE_RETRY) { + response.setTimeout(source.readInt()); + } else if (responseCode == RESPONSE_OK) { + int size = source.readInt(); + if (size > 0) { + byte[] payload = new byte[size]; + source.readByteArray(payload); + response.setPayload(payload); + } + } + return response; + } + + @Override + public VerifyCredentialResponse[] newArray(int size) { + return new VerifyCredentialResponse[size]; + } + + }; + + public VerifyCredentialResponse() { + mResponseCode = RESPONSE_OK; + mPayload = null; + } + + + public VerifyCredentialResponse(byte[] payload) { + mPayload = payload; + mResponseCode = RESPONSE_OK; + } + + public VerifyCredentialResponse(int timeout) { + mTimeout = timeout; + mResponseCode = RESPONSE_RETRY; + mPayload = null; + } + + private VerifyCredentialResponse(int responseCode, int timeout, byte[] payload) { + mResponseCode = responseCode; + mTimeout = timeout; + mPayload = payload; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mResponseCode); + if (mResponseCode == RESPONSE_RETRY) { + dest.writeInt(mTimeout); + } else if (mResponseCode == RESPONSE_OK) { + if (mPayload != null) { + dest.writeInt(mPayload.length); + dest.writeByteArray(mPayload); + } + } + } + + @Override + public int describeContents() { + return 0; + } + + public byte[] getPayload() { + return mPayload; + } + + public int getTimeout() { + return mTimeout; + } + + public int getResponseCode() { + return mResponseCode; + } + + private void setTimeout(int timeout) { + mTimeout = timeout; + } + + private void setPayload(byte[] payload) { + mPayload = payload; + } +} |