Merge pull request #442 from orangenbaumblatt/feature/password-strength

Add password strength meter
This commit is contained in:
Alexander Bakker 2020-06-22 13:21:44 +02:00 committed by GitHub
commit bf9173301c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 182 additions and 42 deletions

View file

@ -122,6 +122,7 @@ dependencies {
implementation 'com.google.protobuf:protobuf-javalite:3.12.1'
implementation "com.mikepenz:iconics-core:3.2.5"
implementation 'com.mikepenz:material-design-iconic-typeface:2.2.0.5@aar'
implementation 'com.nulab-inc:zxcvbn:1.3.0'
implementation 'de.hdodenhof:circleimageview:3.1.0'
implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0'
implementation 'me.dm7.barcodescanner:zxing:1.9.8'

View file

@ -0,0 +1,27 @@
package com.beemdevelopment.aegis.helpers;
import android.content.Context;
import com.beemdevelopment.aegis.R;
public class PasswordStrengthHelper {
// Material design color palette
private static String[] COLORS = {"#FF5252", "#FF5252", "#FFC107", "#8BC34A", "#4CAF50"};
public static String getString(int score, Context context) {
if (score < 0 || score > 4) {
throw new IllegalArgumentException("Not a valid zxcvbn score");
}
String[] strings = context.getResources().getStringArray(R.array.password_strength);
return strings[score];
}
public static String getColor(int score) {
if (score < 0 || score > 4) {
throw new IllegalArgumentException("Not a valid zxcvbn score");
}
return COLORS[score];
}
}

View file

@ -7,6 +7,8 @@ import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.provider.Settings;
import android.text.Editable;
import android.text.InputType;
@ -20,6 +22,7 @@ import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.NumberPicker;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.StringRes;
@ -28,10 +31,14 @@ import androidx.appcompat.app.AlertDialog;
import com.beemdevelopment.aegis.Preferences;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.helpers.EditTextHelper;
import com.beemdevelopment.aegis.helpers.PasswordStrengthHelper;
import com.beemdevelopment.aegis.ui.tasks.KeyDerivationTask;
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 java.util.concurrent.atomic.AtomicReference;
@ -90,9 +97,13 @@ public class Dialogs {
}
public static void showSetPasswordDialog(Activity activity, Dialogs.SlotListener 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);
ProgressBar barPasswordStrength = view.findViewById(R.id.progressBar);
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);
switchToggleVisibility.setOnCheckedChangeListener((CompoundButton.OnCheckedChangeListener) (buttonView, isChecked) -> {
@ -145,14 +156,24 @@ public class Dialogs {
});
TextWatcher watcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence c, int start, int before, int count) {
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();
}
@Override
public void beforeTextChanged(CharSequence c, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable c) {
}
};

View file

@ -1,13 +1,19 @@
package com.beemdevelopment.aegis.ui.slides;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
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;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.biometric.BiometricPrompt;
@ -17,6 +23,7 @@ import com.beemdevelopment.aegis.R;
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.ui.Dialogs;
import com.beemdevelopment.aegis.ui.IntroActivity;
import com.beemdevelopment.aegis.ui.tasks.KeyDerivationTask;
@ -28,6 +35,9 @@ import com.beemdevelopment.aegis.vault.slots.SlotException;
import com.github.appintro.SlidePolicy;
import com.github.appintro.SlideSelectionListener;
import com.google.android.material.snackbar.Snackbar;
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;
@ -37,16 +47,23 @@ public class SecuritySetupSlide extends Fragment implements SlidePolicy, SlideSe
private EditText _textPassword;
private EditText _textPasswordConfirm;
private CheckBox _checkPasswordVisibility;
private ProgressBar _barPasswordStrength;
private TextView _textPasswordStrength;
private TextInputLayout _textPasswordWrapper;
private int _cryptType;
private VaultFileCredentials _creds;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Zxcvbn zxcvbn = new Zxcvbn();
final View view = inflater.inflate(R.layout.fragment_security_setup_slide, container, false);
_textPassword = view.findViewById(R.id.text_password);
_textPasswordConfirm = view.findViewById(R.id.text_password_confirm);
_checkPasswordVisibility = view.findViewById(R.id.check_toggle_visibility);
_barPasswordStrength = view.findViewById(R.id.progressBar);
_textPasswordStrength = view.findViewById(R.id.text_password_strength);
_textPasswordWrapper = view.findViewById(R.id.text_password_wrapper);
_checkPasswordVisibility.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
@ -60,6 +77,26 @@ public class SecuritySetupSlide extends Fragment implements SlidePolicy, SlideSe
}
});
_textPassword.addTextChangedListener(new TextWatcher() {
@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(), getContext()) : "");
_textPasswordWrapper.setError(strength.getFeedback().getWarning());
strength.wipe();
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
view.findViewById(R.id.main).setBackgroundColor(_bgColor);
return view;
}

View file

@ -14,19 +14,50 @@
android:textColor="#FF0000"
android:textStyle="bold"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"/>
<EditText
android:id="@+id/text_password"
android:hint="@string/password"
android:inputType="textPassword"
android:layout_marginEnd="5dp"
android:layout_marginBottom="10dp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_password_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:hint="@string/confirm_password"
android:id="@+id/text_password_confirm"
android:inputType="textPassword"
android:layout_height="wrap_content">
<EditText
android:id="@+id/text_password"
android:hint="@string/password"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_password_confirm_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<EditText
android:hint="@string/confirm_password"
android:id="@+id/text_password_confirm"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:max="4"
android:paddingStart="4dp"
android:paddingEnd="3.5dp" />
<TextView
android:id="@+id/text_password_strength"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end" />
<CheckBox
android:id="@+id/check_toggle_visibility"
@ -34,4 +65,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="@string/show_password" />
<TextView
android:id="@+id/text_password_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end" />
</LinearLayout>

View file

@ -28,44 +28,47 @@
android:layout_height="wrap_content"
android:layout_marginTop="24dp">
<TextView
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_password_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:id="@+id/textView4"
android:text="@string/set_password"/>
android:layout_height="wrap_content">
<EditText
<EditText
android:id="@+id/text_password"
android:hint="@string/set_password"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_password_confirm_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<EditText
android:hint="@string/set_password_confirm"
android:id="@+id/text_password_confirm"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:ems="10"
android:id="@+id/text_password"
android:layout_below="@+id/textView3"
android:layout_alignParentStart="true"
android:layout_marginTop="10dp"
android:layout_alignParentEnd="true"/>
android:max="4"
android:paddingStart="4dp"
android:paddingEnd="3.5dp" />
<TextView
android:layout_width="match_parent"
android:id="@+id/text_password_strength"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView3"
android:text="@string/set_password_confirm"
android:layout_marginTop="20dp"
android:layout_below="@+id/textView4"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:ems="10"
android:id="@+id/text_password_confirm"
android:layout_marginTop="8dp"
android:layout_below="@+id/textView4"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"/>
android:layout_gravity="end" />
<CheckBox
android:id="@+id/check_toggle_visibility"

View file

@ -64,5 +64,13 @@
<item>tr</item>
</string-array>
<string-array name="password_strength">
<item>@string/password_strength_very_weak</item>
<item>@string/password_strength_weak</item>
<item>@string/password_strength_fair</item>
<item>@string/password_strength_good</item>
<item>@string/password_strength_strong</item>
</string-array>
</resources>

View file

@ -276,4 +276,10 @@
<string name="previous">Previous</string>
<string name="transfer_entry">Transfer entry</string>
<string name="transfer_entry_description">Scan this QR code with the authenticator app you would like to transfer this entry to</string>
<string name="password_strength_very_weak">Very weak</string>
<string name="password_strength_weak">Weak</string>
<string name="password_strength_fair">Fair</string>
<string name="password_strength_good">Good</string>
<string name="password_strength_strong">Strong</string>
</resources>