From 559e68e0d2b705500fecac54dda0d399ea2edc38 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Wed, 27 Mar 2024 14:43:28 +0100 Subject: [PATCH] Stop analyzing password strength if it becomes longer than 64 chars This should help reduce the chance that zxcvbn4j explodes on a password input. I also took the opportunity to deduplicate related code a bit. --- .../aegis/helpers/PasswordStrengthHelper.java | 52 +++++++++++++++++-- .../aegis/ui/dialogs/Dialogs.java | 15 ++---- .../aegis/ui/slides/SecuritySetupSlide.java | 38 ++++---------- app/src/main/res/values/strings.xml | 1 + 4 files changed, 64 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/PasswordStrengthHelper.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/PasswordStrengthHelper.java index 518ebcc1..373ef114 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/helpers/PasswordStrengthHelper.java +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/PasswordStrengthHelper.java @@ -1,14 +1,60 @@ package com.beemdevelopment.aegis.helpers; import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.TextView; import com.beemdevelopment.aegis.R; +import com.google.android.material.textfield.TextInputLayout; +import com.google.common.base.Strings; +import com.nulabinc.zxcvbn.Strength; +import com.nulabinc.zxcvbn.Zxcvbn; public class PasswordStrengthHelper { + // Limit the password length to prevent zxcvbn4j from exploding + private static final int MAX_PASSWORD_LENGTH = 64; + // Material design color palette - private static String[] COLORS = {"#FF5252", "#FF5252", "#FFC107", "#8BC34A", "#4CAF50"}; + private final static String[] COLORS = {"#FF5252", "#FF5252", "#FFC107", "#8BC34A", "#4CAF50"}; + + private final Zxcvbn _zxcvbn = new Zxcvbn(); + private final EditText _textPassword; + private final ProgressBar _barPasswordStrength; + private final TextView _textPasswordStrength; + private final TextInputLayout _textPasswordWrapper; + + public PasswordStrengthHelper( + EditText textPassword, + ProgressBar barPasswordStrength, + TextView textPasswordStrength, + TextInputLayout textPasswordWrapper + ) { + _textPassword = textPassword; + _barPasswordStrength = barPasswordStrength; + _textPasswordStrength = textPasswordStrength; + _textPasswordWrapper = textPasswordWrapper; + } + + public void measure(Context context) { + if (_textPassword.getText().length() > MAX_PASSWORD_LENGTH) { + _barPasswordStrength.setProgress(0); + _textPasswordStrength.setText(R.string.password_strength_unknown); + } else { + Strength strength = _zxcvbn.measure(_textPassword.getText()); + _barPasswordStrength.setProgress(strength.getScore()); + _barPasswordStrength.setProgressTintList(ColorStateList.valueOf(Color.parseColor(getColor(strength.getScore())))); + _textPasswordStrength.setText((_textPassword.getText().length() != 0) ? getString(strength.getScore(), context) : ""); + String warning = strength.getFeedback().getWarning(); + _textPasswordWrapper.setError(warning); + _textPasswordWrapper.setErrorEnabled(!Strings.isNullOrEmpty(warning)); + strength.wipe(); + } + } - public static String getString(int score, Context context) { + private static String getString(int score, Context context) { if (score < 0 || score > 4) { throw new IllegalArgumentException("Not a valid zxcvbn score"); } @@ -17,7 +63,7 @@ public class PasswordStrengthHelper { return strings[score]; } - public static String getColor(int score) { + private static String getColor(int score) { if (score < 0 || score > 4) { throw new IllegalArgumentException("Not a valid zxcvbn score"); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/dialogs/Dialogs.java b/app/src/main/java/com/beemdevelopment/aegis/ui/dialogs/Dialogs.java index b1156ee8..7bede047 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/dialogs/Dialogs.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/dialogs/Dialogs.java @@ -5,8 +5,6 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; -import android.content.res.ColorStateList; -import android.graphics.Color; import android.text.InputType; import android.text.SpannableStringBuilder; import android.text.TextWatcher; @@ -43,8 +41,6 @@ import com.beemdevelopment.aegis.vault.slots.SlotException; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputLayout; -import com.nulabinc.zxcvbn.Strength; -import com.nulabinc.zxcvbn.Zxcvbn; import java.util.ArrayList; import java.util.List; @@ -120,7 +116,6 @@ public class Dialogs { } public static void showSetPasswordDialog(ComponentActivity activity, PasswordSlotListener listener) { - Zxcvbn zxcvbn = new Zxcvbn(); View view = activity.getLayoutInflater().inflate(R.layout.dialog_password, null); EditText textPassword = view.findViewById(R.id.text_password); EditText textPasswordConfirm = view.findViewById(R.id.text_password_confirm); @@ -128,6 +123,8 @@ public class Dialogs { TextView textPasswordStrength = view.findViewById(R.id.text_password_strength); TextInputLayout textPasswordWrapper = view.findViewById(R.id.text_password_wrapper); CheckBox switchToggleVisibility = view.findViewById(R.id.check_toggle_visibility); + PasswordStrengthHelper passStrength = new PasswordStrengthHelper( + textPassword, barPasswordStrength, textPasswordStrength, textPasswordWrapper); switchToggleVisibility.setOnCheckedChangeListener((buttonView, isChecked) -> { if (isChecked) { @@ -183,13 +180,7 @@ public class Dialogs { TextWatcher watcher = new SimpleTextWatcher(text -> { boolean equal = EditTextHelper.areEditTextsEqual(textPassword, textPasswordConfirm); buttonOK.get().setEnabled(equal); - - Strength strength = zxcvbn.measure(textPassword.getText()); - barPasswordStrength.setProgress(strength.getScore()); - barPasswordStrength.setProgressTintList(ColorStateList.valueOf(Color.parseColor(PasswordStrengthHelper.getColor(strength.getScore())))); - textPasswordStrength.setText((textPassword.getText().length() != 0) ? PasswordStrengthHelper.getString(strength.getScore(), activity) : ""); - textPasswordWrapper.setError(strength.getFeedback().getWarning()); - strength.wipe(); + passStrength.measure(activity); }); textPassword.addTextChangedListener(watcher); textPasswordConfirm.addTextChangedListener(watcher); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java b/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java index 8611299c..23082fa4 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java @@ -1,10 +1,12 @@ package com.beemdevelopment.aegis.ui.slides; -import android.content.res.ColorStateList; -import android.graphics.Color; +import static com.beemdevelopment.aegis.ui.slides.SecurityPickerSlide.CRYPT_TYPE_BIOMETRIC; +import static com.beemdevelopment.aegis.ui.slides.SecurityPickerSlide.CRYPT_TYPE_INVALID; +import static com.beemdevelopment.aegis.ui.slides.SecurityPickerSlide.CRYPT_TYPE_NONE; +import static com.beemdevelopment.aegis.ui.slides.SecurityPickerSlide.CRYPT_TYPE_PASS; + import android.os.Bundle; import android.text.Editable; -import android.text.TextWatcher; import android.text.method.PasswordTransformationMethod; import android.view.LayoutInflater; import android.view.View; @@ -23,6 +25,7 @@ import com.beemdevelopment.aegis.helpers.BiometricSlotInitializer; import com.beemdevelopment.aegis.helpers.BiometricsHelper; import com.beemdevelopment.aegis.helpers.EditTextHelper; import com.beemdevelopment.aegis.helpers.PasswordStrengthHelper; +import com.beemdevelopment.aegis.helpers.SimpleTextWatcher; import com.beemdevelopment.aegis.ui.dialogs.Dialogs; import com.beemdevelopment.aegis.ui.intro.SlideFragment; import com.beemdevelopment.aegis.ui.tasks.KeyDerivationTask; @@ -32,17 +35,10 @@ import com.beemdevelopment.aegis.vault.slots.PasswordSlot; import com.beemdevelopment.aegis.vault.slots.Slot; import com.beemdevelopment.aegis.vault.slots.SlotException; import com.google.android.material.textfield.TextInputLayout; -import com.nulabinc.zxcvbn.Strength; -import com.nulabinc.zxcvbn.Zxcvbn; import javax.crypto.Cipher; import javax.crypto.SecretKey; -import static com.beemdevelopment.aegis.ui.slides.SecurityPickerSlide.CRYPT_TYPE_BIOMETRIC; -import static com.beemdevelopment.aegis.ui.slides.SecurityPickerSlide.CRYPT_TYPE_INVALID; -import static com.beemdevelopment.aegis.ui.slides.SecurityPickerSlide.CRYPT_TYPE_NONE; -import static com.beemdevelopment.aegis.ui.slides.SecurityPickerSlide.CRYPT_TYPE_PASS; - public class SecuritySetupSlide extends SlideFragment { private EditText _textPassword; private EditText _textPasswordConfirm; @@ -76,27 +72,15 @@ public class SecuritySetupSlide extends SlideFragment { } }); - _textPassword.addTextChangedListener(new TextWatcher() { - private Zxcvbn _zxcvbn = new Zxcvbn(); - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - Strength strength = _zxcvbn.measure(_textPassword.getText()); - _barPasswordStrength.setProgress(strength.getScore()); - _barPasswordStrength.setProgressTintList(ColorStateList.valueOf(Color.parseColor(PasswordStrengthHelper.getColor(strength.getScore())))); - _textPasswordStrength.setText((_textPassword.getText().length() != 0) ? PasswordStrengthHelper.getString(strength.getScore(), requireContext()) : ""); - _textPasswordWrapper.setError(strength.getFeedback().getWarning()); - strength.wipe(); - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + _textPassword.addTextChangedListener(new SimpleTextWatcher(new SimpleTextWatcher.Listener() { + private final PasswordStrengthHelper passStrength = new PasswordStrengthHelper( + _textPassword, _barPasswordStrength, _textPasswordStrength, _textPasswordWrapper); @Override public void afterTextChanged(Editable s) { + passStrength.measure(requireContext()); } - }); + })); return view; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 02cfc4bc..846b15b4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -447,6 +447,7 @@ Fair Good Strong + Password too long for strength analysis Use PIN keyboard on lockscreen Enable this if you want to enable the PIN keyboard on the lockscreen. This only works for numeric passwords