Loading core/java/android/app/admin/PasswordMetrics.java +9 −13 Original line number Diff line number Diff line Loading @@ -27,9 +27,6 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.admin.DevicePolicyManager.PasswordComplexity; Loading @@ -37,8 +34,7 @@ import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils.CredentialType; import com.android.internal.widget.LockscreenCredential; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -232,15 +228,15 @@ public class PasswordMetrics implements Parcelable { * {@code credential} cannot be null when {@code type} is * {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}. */ public static PasswordMetrics computeForCredential( @CredentialType int type, byte[] credential) { if (type == CREDENTIAL_TYPE_PASSWORD) { Preconditions.checkNotNull(credential, "credential cannot be null"); return PasswordMetrics.computeForPassword(credential); } else if (type == CREDENTIAL_TYPE_PATTERN) { public static PasswordMetrics computeForCredential(LockscreenCredential credential) { if (credential.isPassword()) { return PasswordMetrics.computeForPassword(credential.getCredential()); } else if (credential.isPattern()) { return new PasswordMetrics(PASSWORD_QUALITY_SOMETHING); } else /* if (type == CREDENTIAL_TYPE_NONE) */ { } else if (credential.isNone()) { return new PasswordMetrics(PASSWORD_QUALITY_UNSPECIFIED); } else { throw new IllegalArgumentException("Unknown credential type " + credential.getType()); } } Loading core/java/com/android/internal/widget/LockPatternChecker.java +36 −151 Original line number Diff line number Diff line package com.android.internal.widget; import android.annotation.UnsupportedAppUsage; import android.os.AsyncTask; import com.android.internal.widget.LockPatternUtils.RequestThrottledException; import java.util.ArrayList; import java.util.List; /** * Helper class to check/verify PIN/Password/Pattern asynchronously. */ Loading Loading @@ -53,34 +49,28 @@ public final class LockPatternChecker { } /** * Verify a pattern asynchronously. * Verify a lockscreen credential asynchronously. * * @param utils The LockPatternUtils instance to use. * @param pattern The pattern to check. * @param challenge The challenge to verify against the pattern. * @param userId The user to check against the pattern. * @param credential The credential to check. * @param challenge The challenge to verify against the credential. * @param userId The user to check against the credential. * @param callback The callback to be invoked with the verification result. */ public static AsyncTask<?, ?, ?> verifyPattern(final LockPatternUtils utils, final List<LockPatternView.Cell> pattern, public static AsyncTask<?, ?, ?> verifyCredential(final LockPatternUtils utils, final LockscreenCredential credential, final long challenge, final int userId, final OnVerifyCallback callback) { // Create a copy of the credential since checking credential is asynchrounous. final LockscreenCredential credentialCopy = credential.duplicate(); AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { private int mThrottleTimeout; private List<LockPatternView.Cell> patternCopy; @Override protected void onPreExecute() { // Make a copy of the pattern to prevent race conditions. // No need to clone the individual cells because they are immutable. patternCopy = new ArrayList(pattern); } @Override protected byte[] doInBackground(Void... args) { try { return utils.verifyPattern(patternCopy, challenge, userId); return utils.verifyCredential(credentialCopy, challenge, userId); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return null; Loading @@ -90,6 +80,12 @@ public final class LockPatternChecker { @Override protected void onPostExecute(byte[] result) { callback.onVerified(result, mThrottleTimeout); credentialCopy.zeroize(); } @Override protected void onCancelled() { credentialCopy.zeroize(); } }; task.execute(); Loading @@ -97,32 +93,26 @@ public final class LockPatternChecker { } /** * Checks a pattern asynchronously. * Checks a lockscreen credential asynchronously. * * @param utils The LockPatternUtils instance to use. * @param pattern The pattern to check. * @param userId The user to check against the pattern. * @param credential The credential to check. * @param userId The user to check against the credential. * @param callback The callback to be invoked with the check result. */ public static AsyncTask<?, ?, ?> checkPattern(final LockPatternUtils utils, final List<LockPatternView.Cell> pattern, public static AsyncTask<?, ?, ?> checkCredential(final LockPatternUtils utils, final LockscreenCredential credential, final int userId, final OnCheckCallback callback) { // Create a copy of the credential since checking credential is asynchrounous. final LockscreenCredential credentialCopy = credential.duplicate(); AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { private int mThrottleTimeout; private List<LockPatternView.Cell> patternCopy; @Override protected void onPreExecute() { // Make a copy of the pattern to prevent race conditions. // No need to clone the individual cells because they are immutable. patternCopy = new ArrayList(pattern); } @Override protected Boolean doInBackground(Void... args) { try { return utils.checkPattern(patternCopy, userId, callback::onEarlyMatched); return utils.checkCredential(credentialCopy, userId, callback::onEarlyMatched); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return false; Loading @@ -132,11 +122,13 @@ public final class LockPatternChecker { @Override protected void onPostExecute(Boolean result) { callback.onChecked(result, mThrottleTimeout); credentialCopy.zeroize(); } @Override protected void onCancelled() { callback.onCancelled(); credentialCopy.zeroize(); } }; task.execute(); Loading @@ -144,84 +136,29 @@ public final class LockPatternChecker { } /** * Verify a password asynchronously. * Perform a lockscreen credential verification explicitly on a managed profile with unified * challenge, using the parent user's credential. * * @param utils The LockPatternUtils instance to use. * @param password The password to check. * @param challenge The challenge to verify against the pattern. * @param userId The user to check against the pattern. * @param callback The callback to be invoked with the verification result. * * @deprecated Pass the password as a byte array. */ @Deprecated public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils, final String password, final long challenge, final int userId, final OnVerifyCallback callback) { byte[] passwordBytes = password != null ? password.getBytes() : null; return verifyPassword(utils, passwordBytes, challenge, userId, callback); } /** * Verify a password asynchronously. * * @param utils The LockPatternUtils instance to use. * @param password The password to check. * @param challenge The challenge to verify against the pattern. * @param userId The user to check against the pattern. * @param callback The callback to be invoked with the verification result. */ public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils, final byte[] password, final long challenge, 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) { try { return utils.verifyPassword(password, challenge, userId); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return null; } } @Override protected void onPostExecute(byte[] result) { callback.onVerified(result, mThrottleTimeout); } }; task.execute(); return task; } /** * Verify a password asynchronously. * * @param utils The LockPatternUtils instance to use. * @param password The password to check. * @param challenge The challenge to verify against the pattern. * @param userId The user to check against the pattern. * @param credential The credential to check. * @param challenge The challenge to verify against the credential. * @param userId The user to check against the credential. * @param callback The callback to be invoked with the verification result. */ public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils, final byte[] password, final boolean isPattern, final LockscreenCredential credential, final long challenge, final int userId, final OnVerifyCallback callback) { // Create a copy of the credential since checking credential is asynchrounous. final LockscreenCredential credentialCopy = credential.duplicate(); AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { private int mThrottleTimeout; @Override protected byte[] doInBackground(Void... args) { try { return utils.verifyTiedProfileChallenge(password, isPattern, challenge, userId); return utils.verifyTiedProfileChallenge(credentialCopy, challenge, userId); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return null; Loading @@ -231,64 +168,12 @@ public final class LockPatternChecker { @Override protected void onPostExecute(byte[] result) { callback.onVerified(result, mThrottleTimeout); } }; task.execute(); return task; } /** * Checks a password asynchronously. * * @param utils The LockPatternUtils instance to use. * @param password The password to check. * @param userId The user to check against the pattern. * @param callback The callback to be invoked with the check result. * @deprecated Pass passwords as byte[] */ @UnsupportedAppUsage @Deprecated public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils, final String password, final int userId, final OnCheckCallback callback) { byte[] passwordBytes = password != null ? password.getBytes() : null; return checkPassword(utils, passwordBytes, userId, callback); } /** * Checks a password asynchronously. * * @param utils The LockPatternUtils instance to use. * @param passwordBytes The password to check. * @param userId The user to check against the pattern. * @param callback The callback to be invoked with the check result. */ public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils, final byte[] passwordBytes, 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) { try { return utils.checkPassword(passwordBytes, userId, callback::onEarlyMatched); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return false; } } @Override protected void onPostExecute(Boolean result) { callback.onChecked(result, mThrottleTimeout); credentialCopy.zeroize(); } @Override protected void onCancelled() { callback.onCancelled(); credentialCopy.zeroize(); } }; task.execute(); Loading core/java/com/android/internal/widget/LockPatternUtils.java +163 −336 File changed.Preview size limit exceeded, changes collapsed. Show changes core/java/com/android/internal/widget/LockPatternView.java +1 −1 Original line number Diff line number Diff line Loading @@ -1318,7 +1318,7 @@ public class LockPatternView extends View { super.onRestoreInstanceState(ss.getSuperState()); setPattern( DisplayMode.Correct, LockPatternUtils.stringToPattern(ss.getSerializedPattern())); LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes())); mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()]; mInputEnabled = ss.isInputEnabled(); mInStealthMode = ss.isInStealthMode(); Loading core/java/com/android/internal/widget/LockscreenCredential.java 0 → 100644 +294 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.List; /** * A class representing a lockscreen credential. It can be either an empty password, a pattern * or a password (or PIN). * * <p> As required by some security certification, the framework tries its best to * remove copies of the lockscreen credential bytes from memory. In this regard, this class * abuses the {@link AutoCloseable} interface for sanitizing memory. This * presents a nice syntax to auto-zeroize memory with the try-with-resource statement: * <pre> * try {LockscreenCredential credential = LockscreenCredential.createPassword(...) { * // Process the credential in some way * } * </pre> * With this construct, we can garantee that there will be no copies of the password left in * memory when the credential goes out of scope. This should help mitigate certain class of * attacks where the attcker gains read-only access to full device memory (cold boot attack, * unsecured software/hardware memory dumping interfaces such as JTAG). */ public class LockscreenCredential implements Parcelable, AutoCloseable { private final int mType; // Stores raw credential bytes, or null if credential has been zeroized. An empty password // is represented as a byte array of length 0. private byte[] mCredential; // Store the quality of the password, this is used to distinguish between pin // (PASSWORD_QUALITY_NUMERIC) and password (PASSWORD_QUALITY_ALPHABETIC). private final int mQuality; /** * Private constructor, use static builder methods instead. * * <p> Builder methods should create a private copy of the credential bytes and pass in here. * LockscreenCredential will only store the reference internally without copying. This is to * minimize the number of extra copies introduced. */ private LockscreenCredential(int type, int quality, byte[] credential) { Preconditions.checkNotNull(credential); if (type == CREDENTIAL_TYPE_NONE) { Preconditions.checkArgument(credential.length == 0); } else { Preconditions.checkArgument(credential.length > 0); } mType = type; mQuality = quality; mCredential = credential; } /** * Creates a LockscreenCredential object representing empty password. */ public static LockscreenCredential createNone() { return new LockscreenCredential(CREDENTIAL_TYPE_NONE, PASSWORD_QUALITY_UNSPECIFIED, new byte[0]); } /** * Creates a LockscreenCredential object representing the given pattern. */ public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) { return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING, LockPatternUtils.patternToByteArray(pattern)); } /** * Creates a LockscreenCredential object representing the given alphabetic password. */ public static LockscreenCredential createPassword(@NonNull CharSequence password) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC, charSequenceToByteArray(password)); } /** * Creates a LockscreenCredential object representing the given numeric PIN. */ public static LockscreenCredential createPin(@NonNull CharSequence pin) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_NUMERIC, charSequenceToByteArray(pin)); } /** * Creates a LockscreenCredential object representing the given alphabetic password. * If the supplied password is empty, create an empty credential object. */ public static LockscreenCredential createPasswordOrNone(@Nullable CharSequence password) { if (TextUtils.isEmpty(password)) { return createNone(); } else { return createPassword(password); } } /** * Creates a LockscreenCredential object representing the given numeric PIN. * If the supplied password is empty, create an empty credential object. */ public static LockscreenCredential createPinOrNone(@Nullable CharSequence pin) { if (TextUtils.isEmpty(pin)) { return createNone(); } else { return createPin(pin); } } /** * Create a LockscreenCredential object based on raw credential and type * TODO: Remove once LSS.setUserPasswordMetrics accepts a LockscreenCredential */ public static LockscreenCredential createRaw(int type, byte[] credential) { if (type == CREDENTIAL_TYPE_NONE) { return createNone(); } else { return new LockscreenCredential(type, PASSWORD_QUALITY_UNSPECIFIED, credential); } } private void ensureNotZeroized() { Preconditions.checkState(mCredential != null, "Credential is already zeroized"); } /** * Returns the type of this credential. Can be one of {@link #CREDENTIAL_TYPE_NONE}, * {@link #CREDENTIAL_TYPE_PATTERN} or {@link #CREDENTIAL_TYPE_PASSWORD}. * * TODO: Remove once credential type is internal. Callers should use {@link #isNone}, * {@link #isPattern} and {@link #isPassword} instead. */ public int getType() { ensureNotZeroized(); return mType; } /** * Returns the quality type of the credential */ public int getQuality() { ensureNotZeroized(); return mQuality; } /** * Returns the credential bytes. This is a direct reference of the internal field so * callers should not modify it. * */ public byte[] getCredential() { ensureNotZeroized(); return mCredential; } /** Returns whether this is an empty credential */ public boolean isNone() { ensureNotZeroized(); return mType == CREDENTIAL_TYPE_NONE; } /** Returns whether this is a pattern credential */ public boolean isPattern() { ensureNotZeroized(); return mType == CREDENTIAL_TYPE_PATTERN; } /** Returns whether this is a password credential */ public boolean isPassword() { ensureNotZeroized(); return mType == CREDENTIAL_TYPE_PASSWORD; } /** Returns the length of the credential */ public int size() { ensureNotZeroized(); return mCredential.length; } /** Create a copy of the credential */ public LockscreenCredential duplicate() { return new LockscreenCredential(mType, mQuality, mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null); } /** * Zeroize the credential bytes. */ public void zeroize() { if (mCredential != null) { Arrays.fill(mCredential, (byte) 0); mCredential = null; } } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); dest.writeInt(mQuality); dest.writeByteArray(mCredential); } public static final Parcelable.Creator<LockscreenCredential> CREATOR = new Parcelable.Creator<LockscreenCredential>() { @Override public LockscreenCredential createFromParcel(Parcel source) { return new LockscreenCredential(source.readInt(), source.readInt(), source.createByteArray()); } @Override public LockscreenCredential[] newArray(int size) { return new LockscreenCredential[size]; } }; @Override public int describeContents() { return 0; } @Override public void close() { zeroize(); } @Override public int hashCode() { // Effective Java — Item 9 return ((17 + mType) * 31 + mQuality) * 31 + mCredential.hashCode(); } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof LockscreenCredential)) return false; final LockscreenCredential other = (LockscreenCredential) o; return mType == other.mType && mQuality == other.mQuality && Arrays.equals(mCredential, other.mCredential); } /** * Converts a CharSequence to a byte array without requiring a toString(), which creates an * additional copy. * * @param chars The CharSequence to convert * @return A byte array representing the input */ private static byte[] charSequenceToByteArray(CharSequence chars) { if (chars == null) { return new byte[0]; } byte[] bytes = new byte[chars.length()]; for (int i = 0; i < chars.length(); i++) { bytes[i] = (byte) chars.charAt(i); } return bytes; } } Loading
core/java/android/app/admin/PasswordMetrics.java +9 −13 Original line number Diff line number Diff line Loading @@ -27,9 +27,6 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.admin.DevicePolicyManager.PasswordComplexity; Loading @@ -37,8 +34,7 @@ import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils.CredentialType; import com.android.internal.widget.LockscreenCredential; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -232,15 +228,15 @@ public class PasswordMetrics implements Parcelable { * {@code credential} cannot be null when {@code type} is * {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}. */ public static PasswordMetrics computeForCredential( @CredentialType int type, byte[] credential) { if (type == CREDENTIAL_TYPE_PASSWORD) { Preconditions.checkNotNull(credential, "credential cannot be null"); return PasswordMetrics.computeForPassword(credential); } else if (type == CREDENTIAL_TYPE_PATTERN) { public static PasswordMetrics computeForCredential(LockscreenCredential credential) { if (credential.isPassword()) { return PasswordMetrics.computeForPassword(credential.getCredential()); } else if (credential.isPattern()) { return new PasswordMetrics(PASSWORD_QUALITY_SOMETHING); } else /* if (type == CREDENTIAL_TYPE_NONE) */ { } else if (credential.isNone()) { return new PasswordMetrics(PASSWORD_QUALITY_UNSPECIFIED); } else { throw new IllegalArgumentException("Unknown credential type " + credential.getType()); } } Loading
core/java/com/android/internal/widget/LockPatternChecker.java +36 −151 Original line number Diff line number Diff line package com.android.internal.widget; import android.annotation.UnsupportedAppUsage; import android.os.AsyncTask; import com.android.internal.widget.LockPatternUtils.RequestThrottledException; import java.util.ArrayList; import java.util.List; /** * Helper class to check/verify PIN/Password/Pattern asynchronously. */ Loading Loading @@ -53,34 +49,28 @@ public final class LockPatternChecker { } /** * Verify a pattern asynchronously. * Verify a lockscreen credential asynchronously. * * @param utils The LockPatternUtils instance to use. * @param pattern The pattern to check. * @param challenge The challenge to verify against the pattern. * @param userId The user to check against the pattern. * @param credential The credential to check. * @param challenge The challenge to verify against the credential. * @param userId The user to check against the credential. * @param callback The callback to be invoked with the verification result. */ public static AsyncTask<?, ?, ?> verifyPattern(final LockPatternUtils utils, final List<LockPatternView.Cell> pattern, public static AsyncTask<?, ?, ?> verifyCredential(final LockPatternUtils utils, final LockscreenCredential credential, final long challenge, final int userId, final OnVerifyCallback callback) { // Create a copy of the credential since checking credential is asynchrounous. final LockscreenCredential credentialCopy = credential.duplicate(); AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { private int mThrottleTimeout; private List<LockPatternView.Cell> patternCopy; @Override protected void onPreExecute() { // Make a copy of the pattern to prevent race conditions. // No need to clone the individual cells because they are immutable. patternCopy = new ArrayList(pattern); } @Override protected byte[] doInBackground(Void... args) { try { return utils.verifyPattern(patternCopy, challenge, userId); return utils.verifyCredential(credentialCopy, challenge, userId); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return null; Loading @@ -90,6 +80,12 @@ public final class LockPatternChecker { @Override protected void onPostExecute(byte[] result) { callback.onVerified(result, mThrottleTimeout); credentialCopy.zeroize(); } @Override protected void onCancelled() { credentialCopy.zeroize(); } }; task.execute(); Loading @@ -97,32 +93,26 @@ public final class LockPatternChecker { } /** * Checks a pattern asynchronously. * Checks a lockscreen credential asynchronously. * * @param utils The LockPatternUtils instance to use. * @param pattern The pattern to check. * @param userId The user to check against the pattern. * @param credential The credential to check. * @param userId The user to check against the credential. * @param callback The callback to be invoked with the check result. */ public static AsyncTask<?, ?, ?> checkPattern(final LockPatternUtils utils, final List<LockPatternView.Cell> pattern, public static AsyncTask<?, ?, ?> checkCredential(final LockPatternUtils utils, final LockscreenCredential credential, final int userId, final OnCheckCallback callback) { // Create a copy of the credential since checking credential is asynchrounous. final LockscreenCredential credentialCopy = credential.duplicate(); AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { private int mThrottleTimeout; private List<LockPatternView.Cell> patternCopy; @Override protected void onPreExecute() { // Make a copy of the pattern to prevent race conditions. // No need to clone the individual cells because they are immutable. patternCopy = new ArrayList(pattern); } @Override protected Boolean doInBackground(Void... args) { try { return utils.checkPattern(patternCopy, userId, callback::onEarlyMatched); return utils.checkCredential(credentialCopy, userId, callback::onEarlyMatched); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return false; Loading @@ -132,11 +122,13 @@ public final class LockPatternChecker { @Override protected void onPostExecute(Boolean result) { callback.onChecked(result, mThrottleTimeout); credentialCopy.zeroize(); } @Override protected void onCancelled() { callback.onCancelled(); credentialCopy.zeroize(); } }; task.execute(); Loading @@ -144,84 +136,29 @@ public final class LockPatternChecker { } /** * Verify a password asynchronously. * Perform a lockscreen credential verification explicitly on a managed profile with unified * challenge, using the parent user's credential. * * @param utils The LockPatternUtils instance to use. * @param password The password to check. * @param challenge The challenge to verify against the pattern. * @param userId The user to check against the pattern. * @param callback The callback to be invoked with the verification result. * * @deprecated Pass the password as a byte array. */ @Deprecated public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils, final String password, final long challenge, final int userId, final OnVerifyCallback callback) { byte[] passwordBytes = password != null ? password.getBytes() : null; return verifyPassword(utils, passwordBytes, challenge, userId, callback); } /** * Verify a password asynchronously. * * @param utils The LockPatternUtils instance to use. * @param password The password to check. * @param challenge The challenge to verify against the pattern. * @param userId The user to check against the pattern. * @param callback The callback to be invoked with the verification result. */ public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils, final byte[] password, final long challenge, 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) { try { return utils.verifyPassword(password, challenge, userId); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return null; } } @Override protected void onPostExecute(byte[] result) { callback.onVerified(result, mThrottleTimeout); } }; task.execute(); return task; } /** * Verify a password asynchronously. * * @param utils The LockPatternUtils instance to use. * @param password The password to check. * @param challenge The challenge to verify against the pattern. * @param userId The user to check against the pattern. * @param credential The credential to check. * @param challenge The challenge to verify against the credential. * @param userId The user to check against the credential. * @param callback The callback to be invoked with the verification result. */ public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils, final byte[] password, final boolean isPattern, final LockscreenCredential credential, final long challenge, final int userId, final OnVerifyCallback callback) { // Create a copy of the credential since checking credential is asynchrounous. final LockscreenCredential credentialCopy = credential.duplicate(); AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { private int mThrottleTimeout; @Override protected byte[] doInBackground(Void... args) { try { return utils.verifyTiedProfileChallenge(password, isPattern, challenge, userId); return utils.verifyTiedProfileChallenge(credentialCopy, challenge, userId); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return null; Loading @@ -231,64 +168,12 @@ public final class LockPatternChecker { @Override protected void onPostExecute(byte[] result) { callback.onVerified(result, mThrottleTimeout); } }; task.execute(); return task; } /** * Checks a password asynchronously. * * @param utils The LockPatternUtils instance to use. * @param password The password to check. * @param userId The user to check against the pattern. * @param callback The callback to be invoked with the check result. * @deprecated Pass passwords as byte[] */ @UnsupportedAppUsage @Deprecated public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils, final String password, final int userId, final OnCheckCallback callback) { byte[] passwordBytes = password != null ? password.getBytes() : null; return checkPassword(utils, passwordBytes, userId, callback); } /** * Checks a password asynchronously. * * @param utils The LockPatternUtils instance to use. * @param passwordBytes The password to check. * @param userId The user to check against the pattern. * @param callback The callback to be invoked with the check result. */ public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils, final byte[] passwordBytes, 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) { try { return utils.checkPassword(passwordBytes, userId, callback::onEarlyMatched); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return false; } } @Override protected void onPostExecute(Boolean result) { callback.onChecked(result, mThrottleTimeout); credentialCopy.zeroize(); } @Override protected void onCancelled() { callback.onCancelled(); credentialCopy.zeroize(); } }; task.execute(); Loading
core/java/com/android/internal/widget/LockPatternUtils.java +163 −336 File changed.Preview size limit exceeded, changes collapsed. Show changes
core/java/com/android/internal/widget/LockPatternView.java +1 −1 Original line number Diff line number Diff line Loading @@ -1318,7 +1318,7 @@ public class LockPatternView extends View { super.onRestoreInstanceState(ss.getSuperState()); setPattern( DisplayMode.Correct, LockPatternUtils.stringToPattern(ss.getSerializedPattern())); LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes())); mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()]; mInputEnabled = ss.isInputEnabled(); mInStealthMode = ss.isInStealthMode(); Loading
core/java/com/android/internal/widget/LockscreenCredential.java 0 → 100644 +294 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.List; /** * A class representing a lockscreen credential. It can be either an empty password, a pattern * or a password (or PIN). * * <p> As required by some security certification, the framework tries its best to * remove copies of the lockscreen credential bytes from memory. In this regard, this class * abuses the {@link AutoCloseable} interface for sanitizing memory. This * presents a nice syntax to auto-zeroize memory with the try-with-resource statement: * <pre> * try {LockscreenCredential credential = LockscreenCredential.createPassword(...) { * // Process the credential in some way * } * </pre> * With this construct, we can garantee that there will be no copies of the password left in * memory when the credential goes out of scope. This should help mitigate certain class of * attacks where the attcker gains read-only access to full device memory (cold boot attack, * unsecured software/hardware memory dumping interfaces such as JTAG). */ public class LockscreenCredential implements Parcelable, AutoCloseable { private final int mType; // Stores raw credential bytes, or null if credential has been zeroized. An empty password // is represented as a byte array of length 0. private byte[] mCredential; // Store the quality of the password, this is used to distinguish between pin // (PASSWORD_QUALITY_NUMERIC) and password (PASSWORD_QUALITY_ALPHABETIC). private final int mQuality; /** * Private constructor, use static builder methods instead. * * <p> Builder methods should create a private copy of the credential bytes and pass in here. * LockscreenCredential will only store the reference internally without copying. This is to * minimize the number of extra copies introduced. */ private LockscreenCredential(int type, int quality, byte[] credential) { Preconditions.checkNotNull(credential); if (type == CREDENTIAL_TYPE_NONE) { Preconditions.checkArgument(credential.length == 0); } else { Preconditions.checkArgument(credential.length > 0); } mType = type; mQuality = quality; mCredential = credential; } /** * Creates a LockscreenCredential object representing empty password. */ public static LockscreenCredential createNone() { return new LockscreenCredential(CREDENTIAL_TYPE_NONE, PASSWORD_QUALITY_UNSPECIFIED, new byte[0]); } /** * Creates a LockscreenCredential object representing the given pattern. */ public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) { return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING, LockPatternUtils.patternToByteArray(pattern)); } /** * Creates a LockscreenCredential object representing the given alphabetic password. */ public static LockscreenCredential createPassword(@NonNull CharSequence password) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC, charSequenceToByteArray(password)); } /** * Creates a LockscreenCredential object representing the given numeric PIN. */ public static LockscreenCredential createPin(@NonNull CharSequence pin) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_NUMERIC, charSequenceToByteArray(pin)); } /** * Creates a LockscreenCredential object representing the given alphabetic password. * If the supplied password is empty, create an empty credential object. */ public static LockscreenCredential createPasswordOrNone(@Nullable CharSequence password) { if (TextUtils.isEmpty(password)) { return createNone(); } else { return createPassword(password); } } /** * Creates a LockscreenCredential object representing the given numeric PIN. * If the supplied password is empty, create an empty credential object. */ public static LockscreenCredential createPinOrNone(@Nullable CharSequence pin) { if (TextUtils.isEmpty(pin)) { return createNone(); } else { return createPin(pin); } } /** * Create a LockscreenCredential object based on raw credential and type * TODO: Remove once LSS.setUserPasswordMetrics accepts a LockscreenCredential */ public static LockscreenCredential createRaw(int type, byte[] credential) { if (type == CREDENTIAL_TYPE_NONE) { return createNone(); } else { return new LockscreenCredential(type, PASSWORD_QUALITY_UNSPECIFIED, credential); } } private void ensureNotZeroized() { Preconditions.checkState(mCredential != null, "Credential is already zeroized"); } /** * Returns the type of this credential. Can be one of {@link #CREDENTIAL_TYPE_NONE}, * {@link #CREDENTIAL_TYPE_PATTERN} or {@link #CREDENTIAL_TYPE_PASSWORD}. * * TODO: Remove once credential type is internal. Callers should use {@link #isNone}, * {@link #isPattern} and {@link #isPassword} instead. */ public int getType() { ensureNotZeroized(); return mType; } /** * Returns the quality type of the credential */ public int getQuality() { ensureNotZeroized(); return mQuality; } /** * Returns the credential bytes. This is a direct reference of the internal field so * callers should not modify it. * */ public byte[] getCredential() { ensureNotZeroized(); return mCredential; } /** Returns whether this is an empty credential */ public boolean isNone() { ensureNotZeroized(); return mType == CREDENTIAL_TYPE_NONE; } /** Returns whether this is a pattern credential */ public boolean isPattern() { ensureNotZeroized(); return mType == CREDENTIAL_TYPE_PATTERN; } /** Returns whether this is a password credential */ public boolean isPassword() { ensureNotZeroized(); return mType == CREDENTIAL_TYPE_PASSWORD; } /** Returns the length of the credential */ public int size() { ensureNotZeroized(); return mCredential.length; } /** Create a copy of the credential */ public LockscreenCredential duplicate() { return new LockscreenCredential(mType, mQuality, mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null); } /** * Zeroize the credential bytes. */ public void zeroize() { if (mCredential != null) { Arrays.fill(mCredential, (byte) 0); mCredential = null; } } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); dest.writeInt(mQuality); dest.writeByteArray(mCredential); } public static final Parcelable.Creator<LockscreenCredential> CREATOR = new Parcelable.Creator<LockscreenCredential>() { @Override public LockscreenCredential createFromParcel(Parcel source) { return new LockscreenCredential(source.readInt(), source.readInt(), source.createByteArray()); } @Override public LockscreenCredential[] newArray(int size) { return new LockscreenCredential[size]; } }; @Override public int describeContents() { return 0; } @Override public void close() { zeroize(); } @Override public int hashCode() { // Effective Java — Item 9 return ((17 + mType) * 31 + mQuality) * 31 + mCredential.hashCode(); } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof LockscreenCredential)) return false; final LockscreenCredential other = (LockscreenCredential) o; return mType == other.mType && mQuality == other.mQuality && Arrays.equals(mCredential, other.mCredential); } /** * Converts a CharSequence to a byte array without requiring a toString(), which creates an * additional copy. * * @param chars The CharSequence to convert * @return A byte array representing the input */ private static byte[] charSequenceToByteArray(CharSequence chars) { if (chars == null) { return new byte[0]; } byte[] bytes = new byte[chars.length()]; for (int i = 0; i < chars.length(); i++) { bytes[i] = (byte) chars.charAt(i); } return bytes; } }