From 2555e5f1040984a3422c5a4db43421e87d4ca0ce Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Fri, 7 Nov 2014 00:52:57 -0800 Subject: [PATCH] Prevent factory reset for short passwords in CryptKeeper Since patterns and passwords can't be shorter than 4 elements, we shouldn't count them against attempts to unlock the device. If we do, we risk resetting the device due to stray input. Bug: 18211475 Bug: 18273380 Change-Id: Ib329cd3383471483cda68bfac4278b17d0c09c57 Conflicts: src/com/android/settings/CryptKeeper.java --- src/com/android/settings/CryptKeeper.java | 142 ++++++++++++++-------- 1 file changed, 88 insertions(+), 54 deletions(-) diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java index eded55bc73d..33fa9fd199a 100644 --- a/src/com/android/settings/CryptKeeper.java +++ b/src/com/android/settings/CryptKeeper.java @@ -135,7 +135,18 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList // how long we wait to clear a right pattern private static final int RIGHT_PATTERN_CLEAR_TIMEOUT_MS = 500; - private Runnable mClearPatternRunnable = new Runnable() { + // When the user enters a short pin/password, run this to show an error, + // but don't count it against attempts. + private final Runnable mFakeUnlockAttemptRunnable = new Runnable() { + public void run() { + handleBadAttempt(1 /* failedAttempt */); + } + }; + + // TODO: this should be tuned to match minimum decryption timeout + private static final int FAKE_ATTEMPT_DELAY = 1000; + + private final Runnable mClearPatternRunnable = new Runnable() { @Override public void run() { mLockPatternView.clearPattern(); @@ -164,8 +175,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList @Override protected void onPreExecute() { super.onPreExecute(); - final TextView status = (TextView) findViewById(R.id.status); - status.setText(R.string.checking_decryption); + beginAttempt(); } @Override @@ -207,59 +217,66 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList showFactoryReset(true); return; } else { - // Wrong entry. Handle pattern case. - if (mLockPatternView != null) { - mLockPatternView.setDisplayMode(DisplayMode.Wrong); - mLockPatternView.removeCallbacks(mClearPatternRunnable); - mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS); + handleBadAttempt(failedAttempts); + } + } + } + + private void beginAttempt() { + final TextView status = (TextView) findViewById(R.id.status); + status.setText(R.string.checking_decryption); + } + + private void handleBadAttempt(Integer failedAttempts) { + // Wrong entry. Handle pattern case. + if (mLockPatternView != null) { + mLockPatternView.setDisplayMode(DisplayMode.Wrong); + mLockPatternView.removeCallbacks(mClearPatternRunnable); + mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS); + } + if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) { + mCooldown = COOL_DOWN_INTERVAL; + cooldown(); + } else { + final TextView status = (TextView) findViewById(R.id.status); + + int remainingAttempts = MAX_FAILED_ATTEMPTS - failedAttempts; + if (remainingAttempts < COOL_DOWN_ATTEMPTS) { + CharSequence warningTemplate = getText(R.string.crypt_keeper_warn_wipe); + CharSequence warning = TextUtils.expandTemplate(warningTemplate, + Integer.toString(remainingAttempts)); + status.setText(warning); + } else { + int passwordType = StorageManager.CRYPT_TYPE_PASSWORD; + try { + final IMountService service = getMountService(); + passwordType = service.getPasswordType(); + } catch (Exception e) { + Log.e(TAG, "Error calling mount service " + e); } - if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) { - mCooldown = COOL_DOWN_INTERVAL; - cooldown(); + + if (passwordType == StorageManager.CRYPT_TYPE_PIN) { + status.setText(R.string.cryptkeeper_wrong_pin); + } else if (passwordType == StorageManager.CRYPT_TYPE_PATTERN) { + status.setText(R.string.cryptkeeper_wrong_pattern); } else { - final TextView status = (TextView) findViewById(R.id.status); - - int remainingAttempts = MAX_FAILED_ATTEMPTS - failedAttempts; - if (remainingAttempts < COOL_DOWN_ATTEMPTS) { - CharSequence warningTemplate = getText(R.string.crypt_keeper_warn_wipe); - CharSequence warning = TextUtils.expandTemplate(warningTemplate, - Integer.toString(remainingAttempts)); - status.setText(warning); - } else { - int passwordType = StorageManager.CRYPT_TYPE_PASSWORD; - try { - final IMountService service = getMountService(); - passwordType = service.getPasswordType(); - } catch (Exception e) { - Log.e(TAG, "Error calling mount service " + e); - } - - if (passwordType == StorageManager.CRYPT_TYPE_PIN) { - status.setText(R.string.cryptkeeper_wrong_pin); - } else if (passwordType == StorageManager.CRYPT_TYPE_PATTERN) { - status.setText(R.string.cryptkeeper_wrong_pattern); - } else { - status.setText(R.string.cryptkeeper_wrong_password); - } - } - - - if (mLockPatternView != null) { - mLockPatternView.setDisplayMode(DisplayMode.Wrong); - } - // Reenable the password entry - if (mPasswordEntry != null) { - mPasswordEntry.setEnabled(true); - final InputMethodManager imm = (InputMethodManager) getSystemService( - Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(mPasswordEntry, 0); - setBackFunctionality(true); - } - if (mLockPatternView != null) { - mLockPatternView.setEnabled(true); - } + status.setText(R.string.cryptkeeper_wrong_password); } } + + if (mLockPatternView != null) { + mLockPatternView.setDisplayMode(DisplayMode.Wrong); + mLockPatternView.setEnabled(true); + } + + // Reenable the password entry + if (mPasswordEntry != null) { + mPasswordEntry.setEnabled(true); + final InputMethodManager imm = (InputMethodManager) getSystemService( + Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mPasswordEntry, 0); + setBackFunctionality(true); + } } } @@ -329,6 +346,8 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList | StatusBarManager.DISABLE_SEARCH | StatusBarManager.DISABLE_RECENT; + protected static final int MIN_LENGTH_BEFORE_REPORT = LockPatternUtils.MIN_LOCK_PATTERN_SIZE; + /** @return whether or not this Activity was started for debugging the UI only. */ private boolean isDebugView() { return getIntent().hasExtra(EXTRA_FORCE_VIEW); @@ -701,6 +720,11 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList } } + private void fakeUnlockAttempt(View postingView) { + beginAttempt(); + postingView.postDelayed(mFakeUnlockAttemptRunnable, FAKE_ATTEMPT_DELAY); + } + protected LockPatternView.OnPatternListener mChooseNewLockPatternListener = new LockPatternView.OnPatternListener() { @@ -716,7 +740,12 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList @Override public void onPatternDetected(List pattern) { mLockPatternView.setEnabled(false); - new DecryptTask().execute(LockPatternUtils.patternToString(pattern)); + if (pattern.size() >= MIN_LENGTH_BEFORE_REPORT) { + new DecryptTask().execute(LockPatternUtils.patternToString(pattern)); + } else { + // Allow user to make as many of these as they want. + fakeUnlockAttempt(mLockPatternView); + } } @Override @@ -873,7 +902,12 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList mPasswordEntry.setEnabled(false); setBackFunctionality(false); - new DecryptTask().execute(password); + if (password.length() >= LockPatternUtils.MIN_LOCK_PATTERN_SIZE) { + new DecryptTask().execute(password); + } else { + // Allow user to make as many of these as they want. + fakeUnlockAttempt(mPasswordEntry); + } return true; }