add passphrase, password generator profiles, password strength estimation, closes https://github.com/PhilippC/keepass2android/issues/81
This commit is contained in:
@@ -16,15 +16,22 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
|
using Android.App.Admin;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.Content.PM;
|
using Android.Content.PM;
|
||||||
|
using Android.Graphics;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Preferences;
|
using Android.Preferences;
|
||||||
using Android.Views;
|
using Android.Views;
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
|
using Java.Util;
|
||||||
|
using KeePassLib.Cryptography;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using OtpKeyProv;
|
||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
@@ -84,6 +91,48 @@ namespace keepass2android
|
|||||||
act.StartActivityForResult(i, 0);
|
act.StartActivityForResult(i, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PasswordProfiles _profiles;
|
||||||
|
|
||||||
|
private bool _updateDisabled = false;
|
||||||
|
|
||||||
|
class PasswordProfiles
|
||||||
|
{
|
||||||
|
public List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>> Profiles { get; set; }
|
||||||
|
|
||||||
|
public PasswordGenerator.CombinedKeyOptions LastUsedSettings { get; set; }
|
||||||
|
|
||||||
|
public int? TryFindLastUsedProfileIndex()
|
||||||
|
{
|
||||||
|
for (int i=0;i<Profiles.Count;i++)
|
||||||
|
{
|
||||||
|
var kvp = Profiles[i];
|
||||||
|
if (kvp.Value.Equals(LastUsedSettings))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(string key, PasswordGenerator.CombinedKeyOptions options)
|
||||||
|
{
|
||||||
|
for (var index = 0; index < Profiles.Count; index++)
|
||||||
|
{
|
||||||
|
var kvp = Profiles[index];
|
||||||
|
if (kvp.Key == key)
|
||||||
|
{
|
||||||
|
Profiles[index] = new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(key, options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiles.Add(new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(key, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(in int profileIndex)
|
||||||
|
{
|
||||||
|
Profiles.RemoveAt(profileIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnCreate(Bundle savedInstanceState) {
|
protected override void OnCreate(Bundle savedInstanceState) {
|
||||||
_design.ApplyTheme();
|
_design.ApplyTheme();
|
||||||
@@ -95,13 +144,13 @@ namespace keepass2android
|
|||||||
var prefs = GetPreferences(FileCreationMode.Private);
|
var prefs = GetPreferences(FileCreationMode.Private);
|
||||||
|
|
||||||
|
|
||||||
PasswordGenerator.PasswordGenerationOptions options = null;
|
|
||||||
string jsonOptions = prefs.GetString("password_generator_options", null);
|
string jsonOptions = prefs.GetString("password_generator_profiles", null);
|
||||||
if (jsonOptions != null)
|
if (jsonOptions != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
options = JsonConvert.DeserializeObject<PasswordGenerator.PasswordGenerationOptions>(jsonOptions);
|
_profiles = JsonConvert.DeserializeObject<PasswordProfiles>(jsonOptions);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -110,37 +159,57 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
options = new PasswordGenerator.PasswordGenerationOptions()
|
PasswordGenerator.CombinedKeyOptions options = new PasswordGenerator.CombinedKeyOptions()
|
||||||
{
|
{
|
||||||
Length = prefs.GetInt("length", 12),
|
PasswordGenerationOptions = new PasswordGenerator.PasswordGenerationOptions()
|
||||||
UpperCase = prefs.GetBoolean("cb_uppercase", true),
|
{
|
||||||
LowerCase = prefs.GetBoolean("cb_lowercase", true),
|
Length = prefs.GetInt("length", 12),
|
||||||
Digits = prefs.GetBoolean("cb_digits", true),
|
UpperCase = prefs.GetBoolean("cb_uppercase", true),
|
||||||
Minus = prefs.GetBoolean("cb_minus", false),
|
LowerCase = prefs.GetBoolean("cb_lowercase", true),
|
||||||
Underline = prefs.GetBoolean("cb_underline", false),
|
Digits = prefs.GetBoolean("cb_digits", true),
|
||||||
Space = prefs.GetBoolean("cb_space", false),
|
Minus = prefs.GetBoolean("cb_minus", false),
|
||||||
Specials = prefs.GetBoolean("cb_specials", false),
|
Underline = prefs.GetBoolean("cb_underline", false),
|
||||||
SpecialsExtended = false,
|
Space = prefs.GetBoolean("cb_space", false),
|
||||||
Brackets = prefs.GetBoolean("cb_brackets", false)
|
Specials = prefs.GetBoolean("cb_specials", false),
|
||||||
|
SpecialsExtended = false,
|
||||||
|
Brackets = prefs.GetBoolean("cb_brackets", false)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_profiles = new PasswordProfiles()
|
||||||
|
{
|
||||||
|
LastUsedSettings = options,
|
||||||
|
Profiles = GetDefaultProfiles()
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_uppercase)).Checked = options.UpperCase;
|
_profiles ??= new PasswordProfiles();
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_lowercase)).Checked = options.LowerCase;
|
_profiles.LastUsedSettings ??= new PasswordGenerator.CombinedKeyOptions()
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_digits)).Checked = options.Digits;
|
{
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_minus)).Checked = options.Minus;
|
PasswordGenerationOptions = new PasswordGenerator.PasswordGenerationOptions()
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_underline)).Checked = options.Underline;
|
{Length = 7, UpperCase = true, LowerCase = true, Digits = true}
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_space)).Checked = options.Space;
|
};
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_specials)).Checked = options.Specials;
|
_profiles.Profiles ??= new List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>>();
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_specials_extended)).Checked = options.SpecialsExtended;
|
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_brackets)).Checked = options.Brackets;
|
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_exclude_lookalike)).Checked = options.ExcludeLookAlike;
|
|
||||||
((CheckBox)FindViewById(Resource.Id.cb_at_least_one_from_each_group)).Checked = options.AtLeastOneFromEachGroup;
|
|
||||||
|
|
||||||
((EditText)FindViewById(Resource.Id.length)).Text = options.Length.ToString(CultureInfo.InvariantCulture);
|
_updateDisabled = true;
|
||||||
|
PopulateFieldsFromOptions(_profiles.LastUsedSettings);
|
||||||
foreach (int id in _buttonLengthButtonIds) {
|
_updateDisabled = false;
|
||||||
|
|
||||||
|
var profileSpinner = UpdateProfileSpinner();
|
||||||
|
|
||||||
|
profileSpinner.ItemSelected += (sender, args) =>
|
||||||
|
{
|
||||||
|
if (profileSpinner.SelectedItemPosition > 0)
|
||||||
|
{
|
||||||
|
_profiles.LastUsedSettings = _profiles.Profiles[profileSpinner.SelectedItemPosition - 1].Value;
|
||||||
|
_updateDisabled = true;
|
||||||
|
PopulateFieldsFromOptions(_profiles.LastUsedSettings);
|
||||||
|
_updateDisabled = false;
|
||||||
|
UpdatePassword();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (int id in _buttonLengthButtonIds) {
|
||||||
Button button = (Button) FindViewById(id);
|
Button button = (Button) FindViewById(id);
|
||||||
button.Click += (sender, e) =>
|
button.Click += (sender, e) =>
|
||||||
{
|
{
|
||||||
@@ -148,15 +217,33 @@ namespace keepass2android
|
|||||||
|
|
||||||
EditText editText = (EditText) FindViewById(Resource.Id.length);
|
EditText editText = (EditText) FindViewById(Resource.Id.length);
|
||||||
editText.Text = b.Text;
|
editText.Text = b.Text;
|
||||||
UpdatePassword();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FindViewById<EditText>(Resource.Id.length).TextChanged += (sender, args) => UpdatePassword();
|
||||||
|
FindViewById<EditText>(Resource.Id.wordcount).TextChanged += (sender, args) => UpdatePassword();
|
||||||
|
FindViewById<EditText>(Resource.Id.wordseparator).TextChanged += (sender, args) => UpdatePassword();
|
||||||
|
|
||||||
foreach (int id in _checkboxIds)
|
foreach (int id in _checkboxIds)
|
||||||
{
|
{
|
||||||
FindViewById<CheckBox>(id).CheckedChange += (sender, args) => UpdatePassword();
|
FindViewById<CheckBox>(id).CheckedChange += (sender, args) => UpdatePassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mode_spinner = FindViewById<Spinner>(Resource.Id.spinner_password_generator_mode);
|
||||||
|
mode_spinner.ItemSelected += (sender, args) =>
|
||||||
|
{
|
||||||
|
FindViewById(Resource.Id.passphraseOptions).Visibility =
|
||||||
|
mode_spinner.SelectedItemPosition == 0 ? ViewStates.Gone : ViewStates.Visible;
|
||||||
|
FindViewById(Resource.Id.passwordOptions).Visibility =
|
||||||
|
mode_spinner.SelectedItemPosition == 1 ? ViewStates.Gone : ViewStates.Visible;
|
||||||
|
|
||||||
|
UpdatePassword();
|
||||||
|
};
|
||||||
|
|
||||||
|
FindViewById<Spinner>(Resource.Id.spinner_password_generator_case_mode).ItemSelected += (sender, args) =>
|
||||||
|
{
|
||||||
|
UpdatePassword();
|
||||||
|
};
|
||||||
|
|
||||||
Button genPassButton = (Button) FindViewById(Resource.Id.generate_password_button);
|
Button genPassButton = (Button) FindViewById(Resource.Id.generate_password_button);
|
||||||
genPassButton.Click += (sender, e) => { UpdatePassword(); };
|
genPassButton.Click += (sender, e) => { UpdatePassword(); };
|
||||||
@@ -184,75 +271,320 @@ namespace keepass2android
|
|||||||
Finish();
|
Finish();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FindViewById(Resource.Id.btn_password_generator_profile_save)
|
||||||
EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit);
|
.Click += (sender, args) =>
|
||||||
txtPasswordToSet.Text = GeneratePassword();
|
{
|
||||||
|
var editText = new EditText(this);
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.SetMessage(Resource.String.save_password_generation_profile_text)
|
||||||
|
.SetView(editText)
|
||||||
|
.SetPositiveButton(Android.Resource.String.Ok, (o, eventArgs) =>
|
||||||
|
{
|
||||||
|
_profiles.Add(editText.Text, GetOptions());
|
||||||
|
UpdateProfileSpinner();
|
||||||
|
})
|
||||||
|
.Show();
|
||||||
|
};
|
||||||
|
|
||||||
|
FindViewById(Resource.Id.btn_password_generator_profile_delete)
|
||||||
|
.Click += (sender, args) =>
|
||||||
|
{
|
||||||
|
if (profileSpinner.SelectedItemPosition > 0)
|
||||||
|
{
|
||||||
|
_profiles.Remove(profileSpinner.SelectedItemPosition-1);
|
||||||
|
UpdateProfileSpinner();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit);
|
||||||
|
|
||||||
_passwordFont.ApplyTo(txtPasswordToSet);
|
_passwordFont.ApplyTo(txtPasswordToSet);
|
||||||
|
|
||||||
|
UpdatePassword();
|
||||||
|
|
||||||
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
|
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||||
SupportActionBar.SetHomeButtonEnabled(true);
|
SupportActionBar.SetHomeButtonEnabled(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Spinner UpdateProfileSpinner()
|
||||||
|
{
|
||||||
|
string[] profileNames = new List<string> {GetString(Resource.String.custom_settings)}
|
||||||
|
.Concat(_profiles.Profiles.Select(p => p.Key))
|
||||||
|
.ToArray();
|
||||||
|
ArrayAdapter<String> profileArrayAdapter = new ArrayAdapter<String>(this,
|
||||||
|
Android.Resource.Layout.SimpleSpinnerDropDownItem,
|
||||||
|
profileNames);
|
||||||
|
var profileSpinner = FindViewById<Spinner>(Resource.Id.spinner_password_generator_profile);
|
||||||
|
profileSpinner.Adapter = profileArrayAdapter;
|
||||||
|
|
||||||
|
UpdateProfileSpinnerSelection();
|
||||||
|
return profileSpinner;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>> GetDefaultProfiles()
|
||||||
|
{
|
||||||
|
return new List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>>()
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
|
||||||
|
"Simple12", new PasswordGenerator.CombinedKeyOptions()
|
||||||
|
{
|
||||||
|
PasswordGenerationOptions
|
||||||
|
= new PasswordGenerator.PasswordGenerationOptions()
|
||||||
|
{
|
||||||
|
Length = 12, AtLeastOneFromEachGroup = true, ExcludeLookAlike = true,
|
||||||
|
Digits = true, LowerCase = true, UpperCase = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
|
||||||
|
"Special12", new PasswordGenerator.CombinedKeyOptions()
|
||||||
|
{
|
||||||
|
PasswordGenerationOptions
|
||||||
|
= new PasswordGenerator.PasswordGenerationOptions()
|
||||||
|
{
|
||||||
|
Length = 12, AtLeastOneFromEachGroup = true, ExcludeLookAlike = true,
|
||||||
|
Digits = true, LowerCase = true, UpperCase = true,Specials = true,Brackets = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
|
||||||
|
"Password64", new PasswordGenerator.CombinedKeyOptions()
|
||||||
|
{
|
||||||
|
PasswordGenerationOptions
|
||||||
|
= new PasswordGenerator.PasswordGenerationOptions()
|
||||||
|
{
|
||||||
|
Length = 64, AtLeastOneFromEachGroup = true,
|
||||||
|
Digits = true, LowerCase = true, UpperCase = true, ExcludeLookAlike = false,Specials = true,Brackets = true, Minus = true, Space = true, SpecialsExtended = true,Underline = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
|
||||||
|
|
||||||
|
"Passphrase7", new PasswordGenerator.CombinedKeyOptions()
|
||||||
|
{
|
||||||
|
PassphraseGenerationOptions
|
||||||
|
= new PasswordGenerator.PassphraseGenerationOptions()
|
||||||
|
{
|
||||||
|
WordCount = 7,
|
||||||
|
CaseMode = PasswordGenerator.PassphraseGenerationOptions.PassphraseCaseMode.Lowercase,
|
||||||
|
Separator = " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
|
||||||
|
"Passphrase5Plus", new PasswordGenerator.CombinedKeyOptions()
|
||||||
|
{
|
||||||
|
PassphraseGenerationOptions
|
||||||
|
= new PasswordGenerator.PassphraseGenerationOptions()
|
||||||
|
{
|
||||||
|
WordCount = 5,
|
||||||
|
CaseMode = PasswordGenerator.PassphraseGenerationOptions.PassphraseCaseMode.PascalCase,
|
||||||
|
Separator = " "
|
||||||
|
},
|
||||||
|
PasswordGenerationOptions = new PasswordGenerator.PasswordGenerationOptions()
|
||||||
|
{
|
||||||
|
Length = 2,
|
||||||
|
AtLeastOneFromEachGroup = true,
|
||||||
|
ExcludeLookAlike = true,
|
||||||
|
Digits = true,
|
||||||
|
Specials = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateFieldsFromOptions(PasswordGenerator.CombinedKeyOptions combinedOptions)
|
||||||
|
{
|
||||||
|
PasswordGenerator.PasswordGenerationOptions passwordOptions = combinedOptions.PasswordGenerationOptions;
|
||||||
|
if (passwordOptions != null)
|
||||||
|
{
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_uppercase)).Checked = passwordOptions.UpperCase;
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_lowercase)).Checked = passwordOptions.LowerCase;
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_digits)).Checked = passwordOptions.Digits;
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_minus)).Checked = passwordOptions.Minus;
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_underline)).Checked = passwordOptions.Underline;
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_space)).Checked = passwordOptions.Space;
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_specials)).Checked = passwordOptions.Specials;
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_specials_extended)).Checked = passwordOptions.SpecialsExtended;
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_brackets)).Checked = passwordOptions.Brackets;
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_exclude_lookalike)).Checked = passwordOptions.ExcludeLookAlike;
|
||||||
|
((CheckBox)FindViewById(Resource.Id.cb_at_least_one_from_each_group)).Checked = passwordOptions.AtLeastOneFromEachGroup;
|
||||||
|
|
||||||
|
((EditText)FindViewById(Resource.Id.length)).Text = passwordOptions.Length.ToString(CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
FindViewById(Resource.Id.passwordOptions).Visibility = ViewStates.Visible;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FindViewById(Resource.Id.passwordOptions).Visibility = ViewStates.Gone;
|
||||||
|
}
|
||||||
|
|
||||||
|
var passphraseOptions = combinedOptions.PassphraseGenerationOptions;
|
||||||
|
|
||||||
|
if (passphraseOptions != null)
|
||||||
|
{
|
||||||
|
FindViewById<EditText>(Resource.Id.wordcount).Text = passphraseOptions.WordCount.ToString(CultureInfo.InvariantCulture);
|
||||||
|
FindViewById<EditText>(Resource.Id.wordseparator).Text = passphraseOptions.Separator;
|
||||||
|
FindViewById<Spinner>(Resource.Id.spinner_password_generator_case_mode)
|
||||||
|
.SetSelection((int)passphraseOptions.CaseMode);
|
||||||
|
|
||||||
|
FindViewById(Resource.Id.passphraseOptions).Visibility = ViewStates.Visible;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FindViewById(Resource.Id.passphraseOptions).Visibility = ViewStates.Gone;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int mode;
|
||||||
|
if (combinedOptions.PasswordGenerationOptions != null &&
|
||||||
|
combinedOptions.PassphraseGenerationOptions != null)
|
||||||
|
mode = 2;
|
||||||
|
else if (combinedOptions.PasswordGenerationOptions == null &&
|
||||||
|
combinedOptions.PassphraseGenerationOptions != null)
|
||||||
|
mode = 1;
|
||||||
|
else mode = 0;
|
||||||
|
|
||||||
|
FindViewById<Spinner>(Resource.Id.spinner_password_generator_mode)
|
||||||
|
.SetSelection(mode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdatePassword()
|
private void UpdatePassword()
|
||||||
{
|
{
|
||||||
|
if (_updateDisabled)
|
||||||
|
return;
|
||||||
String password = GeneratePassword();
|
String password = GeneratePassword();
|
||||||
|
|
||||||
EditText txtPassword = (EditText) FindViewById(Resource.Id.password_edit);
|
EditText txtPassword = (EditText) FindViewById(Resource.Id.password_edit);
|
||||||
txtPassword.Text = password;
|
txtPassword.Text = password;
|
||||||
|
|
||||||
|
var progressBar = FindViewById<ProgressBar>(Resource.Id.pb_password_strength);
|
||||||
|
var passwordBits = QualityEstimation.EstimatePasswordBits(password.ToCharArray());
|
||||||
|
progressBar.Progress = (int)passwordBits;
|
||||||
|
progressBar.Max = 128;
|
||||||
|
|
||||||
|
Color color = new Color(196, 63, 49);
|
||||||
|
if (passwordBits > 40)
|
||||||
|
{
|
||||||
|
color = new Color(219, 152, 55);
|
||||||
|
}
|
||||||
|
if (passwordBits > 64)
|
||||||
|
{
|
||||||
|
color = new Color(96, 138, 38);
|
||||||
|
}
|
||||||
|
if (passwordBits > 100)
|
||||||
|
{
|
||||||
|
color = new Color(31, 128, 31);
|
||||||
|
}
|
||||||
|
progressBar.ProgressDrawable.SetColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SrcIn));
|
||||||
|
|
||||||
|
FindViewById<TextView>(Resource.Id.tv_password_strength).Text = " " + passwordBits + " bits";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
UpdateProfileSpinnerSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateProfileSpinnerSelection()
|
||||||
|
{
|
||||||
|
int? lastUsedIndex = _profiles.TryFindLastUsedProfileIndex();
|
||||||
|
FindViewById<Spinner>(Resource.Id.spinner_password_generator_profile)
|
||||||
|
.SetSelection(lastUsedIndex != null ? lastUsedIndex.Value + 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String GeneratePassword() {
|
public String GeneratePassword() {
|
||||||
String password = "";
|
String password = "";
|
||||||
|
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
|
PasswordGenerator generator = new PasswordGenerator(this);
|
||||||
|
|
||||||
int length;
|
var options = GetOptions();
|
||||||
if (!int.TryParse(((EditText) FindViewById(Resource.Id.length)).Text, out length))
|
|
||||||
{
|
|
||||||
Toast.MakeText(this, Resource.String.error_wrong_length, ToastLength.Long).Show();
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
password = generator.GeneratePassword(options);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(GetString(Resource.String.error_pass_gen_type));
|
||||||
|
}
|
||||||
|
|
||||||
PasswordGenerator generator = new PasswordGenerator(this);
|
_profiles.LastUsedSettings = options;
|
||||||
|
|
||||||
var options = GetOptions(length);
|
SaveProfiles();
|
||||||
|
}
|
||||||
password = generator.GeneratePassword(options);
|
catch (Exception e)
|
||||||
|
{
|
||||||
var prefs = GetPreferences(FileCreationMode.Private);
|
|
||||||
prefs.Edit()
|
|
||||||
.PutString("password_generator_options", JsonConvert.SerializeObject(options))
|
|
||||||
.Commit();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} catch (ArgumentException e) {
|
|
||||||
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
|
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PasswordGenerator.PasswordGenerationOptions GetOptions(int length)
|
private void SaveProfiles()
|
||||||
{
|
{
|
||||||
PasswordGenerator.PasswordGenerationOptions options = new PasswordGenerator.PasswordGenerationOptions()
|
var prefs = GetPreferences(FileCreationMode.Private);
|
||||||
|
prefs.Edit()
|
||||||
|
.PutString("password_generator_profiles", JsonConvert.SerializeObject(_profiles))
|
||||||
|
.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PasswordGenerator.CombinedKeyOptions GetOptions()
|
||||||
|
{
|
||||||
|
PasswordGenerator.CombinedKeyOptions options = new PasswordGenerator.CombinedKeyOptions();
|
||||||
|
if (FindViewById(Resource.Id.passphraseOptions).Visibility == ViewStates.Visible)
|
||||||
{
|
{
|
||||||
Length = length,
|
int wordCount;
|
||||||
UpperCase = ((CheckBox) FindViewById(Resource.Id.cb_uppercase)).Checked,
|
if (!int.TryParse(((EditText)FindViewById(Resource.Id.wordcount)).Text, out wordCount))
|
||||||
LowerCase = ((CheckBox) FindViewById(Resource.Id.cb_lowercase)).Checked,
|
{
|
||||||
Digits = ((CheckBox) FindViewById(Resource.Id.cb_digits)).Checked,
|
throw new Exception(GetString(Resource.String.error_wrong_length));
|
||||||
Minus = ((CheckBox) FindViewById(Resource.Id.cb_minus)).Checked,
|
}
|
||||||
Underline = ((CheckBox) FindViewById(Resource.Id.cb_underline)).Checked,
|
|
||||||
Space = ((CheckBox) FindViewById(Resource.Id.cb_space)).Checked,
|
options.PassphraseGenerationOptions =
|
||||||
Specials = ((CheckBox) FindViewById(Resource.Id.cb_specials)).Checked,
|
new PasswordGenerator.PassphraseGenerationOptions()
|
||||||
SpecialsExtended = ((CheckBox) FindViewById(Resource.Id.cb_specials_extended)).Checked,
|
{
|
||||||
Brackets = ((CheckBox) FindViewById(Resource.Id.cb_brackets)).Checked,
|
WordCount = wordCount,
|
||||||
ExcludeLookAlike = ((CheckBox) FindViewById(Resource.Id.cb_exclude_lookalike)).Checked,
|
Separator = FindViewById<EditText>(Resource.Id.wordseparator).Text,
|
||||||
AtLeastOneFromEachGroup = ((CheckBox) FindViewById(Resource.Id.cb_at_least_one_from_each_group)).Checked
|
CaseMode = (PasswordGenerator.PassphraseGenerationOptions.PassphraseCaseMode)FindViewById<Spinner>(Resource.Id.spinner_password_generator_case_mode).SelectedItemPosition
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FindViewById(Resource.Id.passwordOptions).Visibility == ViewStates.Visible)
|
||||||
|
{
|
||||||
|
int length;
|
||||||
|
if (!int.TryParse(((EditText) FindViewById(Resource.Id.length)).Text, out length))
|
||||||
|
{
|
||||||
|
throw new Exception(GetString(Resource.String.error_wrong_length));
|
||||||
|
}
|
||||||
|
|
||||||
|
options.PasswordGenerationOptions =
|
||||||
|
new PasswordGenerator.PasswordGenerationOptions()
|
||||||
|
{
|
||||||
|
Length = length,
|
||||||
|
UpperCase = ((CheckBox) FindViewById(Resource.Id.cb_uppercase)).Checked,
|
||||||
|
LowerCase = ((CheckBox) FindViewById(Resource.Id.cb_lowercase)).Checked,
|
||||||
|
Digits = ((CheckBox) FindViewById(Resource.Id.cb_digits)).Checked,
|
||||||
|
Minus = ((CheckBox) FindViewById(Resource.Id.cb_minus)).Checked,
|
||||||
|
Underline = ((CheckBox) FindViewById(Resource.Id.cb_underline)).Checked,
|
||||||
|
Space = ((CheckBox) FindViewById(Resource.Id.cb_space)).Checked,
|
||||||
|
Specials = ((CheckBox) FindViewById(Resource.Id.cb_specials)).Checked,
|
||||||
|
SpecialsExtended = ((CheckBox) FindViewById(Resource.Id.cb_specials_extended)).Checked,
|
||||||
|
Brackets = ((CheckBox) FindViewById(Resource.Id.cb_brackets)).Checked,
|
||||||
|
ExcludeLookAlike = ((CheckBox) FindViewById(Resource.Id.cb_exclude_lookalike)).Checked,
|
||||||
|
AtLeastOneFromEachGroup = ((CheckBox) FindViewById(Resource.Id.cb_at_least_one_from_each_group))
|
||||||
|
.Checked
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:background="?activityBackgroundColor"
|
android:background="?activityBackgroundColor"
|
||||||
android:layout_height="fill_parent">
|
android:layout_height="fill_parent">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ android:background="?activityBackgroundColor"
|
|||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:id="@+id/ScrollView"
|
android:id="@+id/ScrollView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_above="@id/bottom_bar"
|
android:layout_above="@id/bottom_bar"
|
||||||
android:layout_marginBottom="12dip"
|
android:layout_marginBottom="12dip"
|
||||||
@@ -40,95 +40,220 @@ android:background="?activityBackgroundColor"
|
|||||||
android:layout_marginRight="12dip"
|
android:layout_marginRight="12dip"
|
||||||
android:layout_marginTop="12dip"
|
android:layout_marginTop="12dip"
|
||||||
android:layout_alignParentTop="false">
|
android:layout_alignParentTop="false">
|
||||||
<RelativeLayout
|
<LinearLayout
|
||||||
android:id="@+id/RelativeLayout"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/password_edit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ems="10"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:typeface="monospace"
|
||||||
|
android:hint="@string/hint_generated_password" />
|
||||||
|
<Button
|
||||||
|
android:id="@+id/generate_password_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/generate_password" />
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<ProgressBar
|
||||||
|
|
||||||
|
android:id="@+id/pb_password_strength"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||||
|
android:indeterminate="false"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_password_strength"
|
||||||
|
android:paddingLeft="4dp"
|
||||||
|
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="4dp"
|
||||||
|
android:paddingBottom="1dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:text="@string/password_generation_profile"/>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="wrap_content">
|
android:orientation="horizontal"
|
||||||
<EditText
|
>
|
||||||
android:id="@+id/password_edit"
|
<Spinner
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
android:id="@+id/spinner_password_generator_profile"
|
||||||
android:ems="10"
|
android:layout_width="0dp"
|
||||||
android:singleLine="true"
|
android:layout_weight="1"
|
||||||
android:typeface="monospace"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/hint_generated_password" />
|
/>
|
||||||
<Button
|
<ImageButton
|
||||||
android:id="@+id/generate_password_button"
|
android:id="@+id/btn_password_generator_profile_save"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="50dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="50dp"
|
||||||
android:layout_below="@id/password_edit"
|
android:scaleType="fitCenter"
|
||||||
android:text="@string/generate_password" />
|
android:src="@drawable/ic_menu_save"
|
||||||
<TextView
|
android:layout_alignParentRight="true"
|
||||||
android:id="@+id/length_label"
|
android:layout_alignParentTop="true" />
|
||||||
android:text="@string/length"
|
|
||||||
android:layout_height="fill_parent"
|
<ImageButton
|
||||||
android:layout_width="fill_parent"
|
android:id="@+id/btn_password_generator_profile_delete"
|
||||||
android:layout_below="@id/generate_password_button" />
|
android:layout_width="50dp"
|
||||||
<Button
|
android:layout_height="50dp"
|
||||||
android:id="@+id/btn_length32"
|
android:scaleType="fitCenter"
|
||||||
android:text="32"
|
android:src="@drawable/ic_menu_close"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_width="50sp"
|
android:layout_alignParentTop="true" />
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinner_password_generator_mode"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ems="10"
|
||||||
|
android:layout_marginBottom="64dip"
|
||||||
|
android:entries="@array/PasswordGeneratorModes"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/passphraseOptions"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:id="@+id/wordcountlayout">
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/wordcount"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="7"
|
||||||
|
android:hint="@string/hint_wordcount" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:id="@+id/wordseparatorlayout">
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/wordseparator"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text=" "
|
||||||
|
android:hint="@string/hint_wordseparator" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="4dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:text="@string/passphrase_capitalization"/>
|
||||||
|
<Spinner
|
||||||
|
|
||||||
|
android:id="@+id/spinner_password_generator_case_mode"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:entries="@array/PasswordGeneratorCaseModes"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:id="@+id/passwordOptions"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:id="@+id/pwd_buttons"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/length_label" />
|
android:layout_width="match_parent">
|
||||||
<Button
|
|
||||||
android:id="@+id/btn_length24"
|
|
||||||
android:text="24"
|
<android.support.design.widget.TextInputLayout
|
||||||
android:layout_toLeftOf="@id/btn_length32"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="50sp"
|
android:layout_width="50dp"
|
||||||
android:layout_alignTop="@id/btn_length32" />
|
android:layout_weight="1"
|
||||||
<Button
|
android:id="@+id/lengthlayout">
|
||||||
android:id="@+id/btn_length16"
|
<EditText
|
||||||
android:text="16"
|
android:layout_width="match_parent"
|
||||||
android:layout_toLeftOf="@id/btn_length24"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="50sp"
|
|
||||||
android:layout_alignTop="@id/btn_length32" />
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btn_length12"
|
|
||||||
android:text="12"
|
|
||||||
android:layout_toLeftOf="@id/btn_length16"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="50sp"
|
|
||||||
android:layout_alignTop="@id/btn_length32" />
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btn_length8"
|
|
||||||
android:text="8"
|
|
||||||
android:layout_toLeftOf="@id/btn_length12"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="50sp"
|
|
||||||
android:layout_alignTop="@id/btn_length32" />
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btn_length6"
|
|
||||||
android:text="6"
|
|
||||||
android:layout_toLeftOf="@id/btn_length8"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="50sp"
|
|
||||||
android:layout_alignTop="@id/btn_length32" />
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/length"
|
android:id="@+id/length"
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_toLeftOf="@id/btn_length6"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignTop="@id/btn_length32"
|
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="12"
|
android:text="12"
|
||||||
android:hint="@string/hint_length" />
|
android:hint="@string/hint_length" />
|
||||||
<CheckBox
|
</android.support.design.widget.TextInputLayout>
|
||||||
android:id="@+id/cb_uppercase"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_length6"
|
||||||
|
android:text="6"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/uppercase"
|
android:layout_width="50sp"
|
||||||
android:checked="true"
|
/>
|
||||||
android:layout_below="@id/length" />
|
<Button
|
||||||
|
android:id="@+id/btn_length8"
|
||||||
|
android:text="8"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="50sp"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_length12"
|
||||||
|
android:text="12"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="50sp"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_length16"
|
||||||
|
android:text="16"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="50sp"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_length24"
|
||||||
|
android:text="24"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="50sp"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_length32"
|
||||||
|
android:text="32"
|
||||||
|
android:layout_width="50sp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/cb_uppercase"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/uppercase"
|
||||||
|
android:checked="true" />
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/cb_lowercase"
|
android:id="@+id/cb_lowercase"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/lowercase"
|
android:text="@string/lowercase"
|
||||||
android:checked="true"
|
android:checked="true" />
|
||||||
android:layout_below="@id/cb_uppercase" />
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/cb_digits"
|
android:id="@+id/cb_digits"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -185,6 +310,10 @@ android:background="?activityBackgroundColor"
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/at_least_one_from_each_group"
|
android:text="@string/at_least_one_from_each_group"
|
||||||
android:layout_below="@id/cb_exclude_lookalike" />
|
android:layout_below="@id/cb_exclude_lookalike" />
|
||||||
</RelativeLayout>
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</RelativeLayout>
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -266,7 +266,26 @@
|
|||||||
<string name="special_extended">Extended Special</string>
|
<string name="special_extended">Extended Special</string>
|
||||||
<string name="at_least_one_from_each_group">At least one from each group</string>
|
<string name="at_least_one_from_each_group">At least one from each group</string>
|
||||||
<string name="exclude_lookalike">Exclude look-alike characters</string>
|
<string name="exclude_lookalike">Exclude look-alike characters</string>
|
||||||
<string name="search_hint">Find what</string>
|
<string name="password_generation_profile">Profile</string>
|
||||||
|
<string name="save_password_generation_profile_text">Enter the name of the profile to save. Enter an existing name to overwrite.</string>
|
||||||
|
|
||||||
|
<string name="hint_wordcount">Passphrase word count</string>
|
||||||
|
<string name="hint_wordseparator">Word separator</string>
|
||||||
|
<string-array name="PasswordGeneratorModes">
|
||||||
|
<item>Password</item>
|
||||||
|
<item>Passphrase</item>
|
||||||
|
<item>Passphrase + Password</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="PasswordGeneratorCaseModes">
|
||||||
|
<item>lowercase</item>
|
||||||
|
<item>UPPERCASE</item>
|
||||||
|
<item>First Character Uppercase</item>
|
||||||
|
</string-array>
|
||||||
|
<string name="custom_settings">Custom settings</string>
|
||||||
|
<string name="passphrase_capitalization">Passphrase capitalization</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="search_hint">Find what</string>
|
||||||
<string name="search_results">Search results</string>
|
<string name="search_results">Search results</string>
|
||||||
<string name="search_in">Search in</string>
|
<string name="search_in">Search in</string>
|
||||||
<string name="select_other_entry">Select another entry</string>
|
<string name="select_other_entry">Select another entry</string>
|
||||||
|
|||||||
7793
src/keepass2android/Wordlist.cs
Normal file
7793
src/keepass2android/Wordlist.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -283,6 +283,7 @@
|
|||||||
<Compile Include="AttachmentContentProvider.cs" />
|
<Compile Include="AttachmentContentProvider.cs" />
|
||||||
<Compile Include="app\AppTask.cs" />
|
<Compile Include="app\AppTask.cs" />
|
||||||
<Compile Include="views\TextWithHelp.cs" />
|
<Compile Include="views\TextWithHelp.cs" />
|
||||||
|
<Compile Include="Wordlist.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\xml\searchable.xml">
|
<AndroidResource Include="Resources\xml\searchable.xml">
|
||||||
|
|||||||
@@ -20,10 +20,26 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
|
public static class StringExtension
|
||||||
|
{
|
||||||
|
public static string ToUpperFirstLetter(this string source)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(source))
|
||||||
|
return string.Empty;
|
||||||
|
// convert to char array of the string
|
||||||
|
char[] letters = source.ToCharArray();
|
||||||
|
// upper case the first char
|
||||||
|
letters[0] = char.ToUpper(letters[0]);
|
||||||
|
// return the array made of the new char array
|
||||||
|
return new string(letters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Password generator
|
/// Password generator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -85,9 +101,64 @@ namespace keepass2android
|
|||||||
_cxt = cxt;
|
_cxt = cxt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CombinedKeyOptions
|
||||||
|
{
|
||||||
|
protected bool Equals(CombinedKeyOptions other)
|
||||||
|
{
|
||||||
|
return Equals(PassphraseGenerationOptions, other.PassphraseGenerationOptions) && Equals(PasswordGenerationOptions, other.PasswordGenerationOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, obj)) return false;
|
||||||
|
if (ReferenceEquals(this, obj)) return true;
|
||||||
|
if (obj.GetType() != this.GetType()) return false;
|
||||||
|
return Equals((CombinedKeyOptions) obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(PassphraseGenerationOptions, PasswordGenerationOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PassphraseGenerationOptions PassphraseGenerationOptions { get; set; }
|
||||||
|
public PasswordGenerationOptions PasswordGenerationOptions { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public class PasswordGenerationOptions
|
public class PasswordGenerationOptions
|
||||||
{
|
{
|
||||||
|
protected bool Equals(PasswordGenerationOptions other)
|
||||||
|
{
|
||||||
|
return Length == other.Length && UpperCase == other.UpperCase && LowerCase == other.LowerCase && Digits == other.Digits && Minus == other.Minus && Underline == other.Underline && Space == other.Space && Specials == other.Specials && SpecialsExtended == other.SpecialsExtended && Brackets == other.Brackets && ExcludeLookAlike == other.ExcludeLookAlike && AtLeastOneFromEachGroup == other.AtLeastOneFromEachGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, obj)) return false;
|
||||||
|
if (ReferenceEquals(this, obj)) return true;
|
||||||
|
if (obj.GetType() != this.GetType()) return false;
|
||||||
|
return Equals((PasswordGenerationOptions) obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
var hashCode = new HashCode();
|
||||||
|
hashCode.Add(Length);
|
||||||
|
hashCode.Add(UpperCase);
|
||||||
|
hashCode.Add(LowerCase);
|
||||||
|
hashCode.Add(Digits);
|
||||||
|
hashCode.Add(Minus);
|
||||||
|
hashCode.Add(Underline);
|
||||||
|
hashCode.Add(Space);
|
||||||
|
hashCode.Add(Specials);
|
||||||
|
hashCode.Add(SpecialsExtended);
|
||||||
|
hashCode.Add(Brackets);
|
||||||
|
hashCode.Add(ExcludeLookAlike);
|
||||||
|
hashCode.Add(AtLeastOneFromEachGroup);
|
||||||
|
return hashCode.ToHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
public int Length { get; set; }
|
public int Length { get; set; }
|
||||||
public bool UpperCase { get; set; }
|
public bool UpperCase { get; set; }
|
||||||
public bool LowerCase { get; set; }
|
public bool LowerCase { get; set; }
|
||||||
@@ -104,63 +175,141 @@ namespace keepass2android
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class PassphraseGenerationOptions
|
||||||
public String GeneratePassword(PasswordGenerationOptions options) {
|
{
|
||||||
if (options.Length <= 0)
|
protected bool Equals(PassphraseGenerationOptions other)
|
||||||
throw new ArgumentException(_cxt.GetString(Resource.String.error_wrong_length));
|
|
||||||
|
|
||||||
|
|
||||||
var groups = GetCharacterGroups(options);
|
|
||||||
String characterSet = GetCharacterSet(options, groups);
|
|
||||||
|
|
||||||
if (characterSet.Length == 0)
|
|
||||||
throw new ArgumentException(_cxt.GetString(Resource.String.error_pass_gen_type));
|
|
||||||
|
|
||||||
int size = characterSet.Length;
|
|
||||||
|
|
||||||
StringBuilder buffer = new StringBuilder();
|
|
||||||
|
|
||||||
Random random = new SecureRandom();
|
|
||||||
|
|
||||||
if (options.AtLeastOneFromEachGroup)
|
|
||||||
{
|
{
|
||||||
foreach (var g in groups)
|
return CaseMode == other.CaseMode && Separator == other.Separator && WordCount == other.WordCount;
|
||||||
{
|
|
||||||
if (g.Length > 0)
|
|
||||||
{
|
|
||||||
buffer.Append(g[random.Next(g.Length)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size > 0)
|
public override bool Equals(object obj)
|
||||||
{
|
|
||||||
while (buffer.Length < options.Length)
|
|
||||||
{
|
|
||||||
buffer.Append(characterSet[random.Next(size)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var password = buffer.ToString();
|
|
||||||
|
|
||||||
if (options.AtLeastOneFromEachGroup)
|
|
||||||
{
|
{
|
||||||
//shuffle
|
if (ReferenceEquals(null, obj)) return false;
|
||||||
StringBuilder sb = new StringBuilder(password);
|
if (ReferenceEquals(this, obj)) return true;
|
||||||
for (int i = (password.Length - 1); i >= 1; i--)
|
if (obj.GetType() != this.GetType()) return false;
|
||||||
{
|
return Equals((PassphraseGenerationOptions) obj);
|
||||||
int j = random.Next(i + 1);
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine((int) CaseMode, Separator, WordCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PassphraseCaseMode
|
||||||
|
{
|
||||||
|
Lowercase,
|
||||||
|
Uppercase,
|
||||||
|
PascalCase
|
||||||
|
};
|
||||||
|
public PassphraseCaseMode CaseMode { get; set; }
|
||||||
|
public string Separator { get; set; }
|
||||||
|
public int WordCount { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public String GeneratePassword(CombinedKeyOptions options)
|
||||||
|
{
|
||||||
|
if ((options.PassphraseGenerationOptions== null || options.PassphraseGenerationOptions.WordCount == 0)
|
||||||
|
&& (options.PasswordGenerationOptions == null || options.PasswordGenerationOptions.Length == 0))
|
||||||
|
{
|
||||||
|
throw new Exception("Bad options");
|
||||||
|
}
|
||||||
|
|
||||||
|
string key = "";
|
||||||
|
|
||||||
|
Random random = new SecureRandom();
|
||||||
|
|
||||||
|
var passwordOptions = options.PasswordGenerationOptions;
|
||||||
|
var passphraseOptions = options.PassphraseGenerationOptions;
|
||||||
|
if (passphraseOptions != null && passphraseOptions.WordCount > 0)
|
||||||
|
{
|
||||||
|
var wl = new Wordlist();
|
||||||
|
string passphrase = "";
|
||||||
|
for (int i = 0; i < passphraseOptions.WordCount; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
string word = wl.GetWord(random);
|
||||||
|
|
||||||
|
if (passphraseOptions.CaseMode == PassphraseGenerationOptions.PassphraseCaseMode.Uppercase)
|
||||||
|
{
|
||||||
|
word = word.ToUpper();
|
||||||
|
}
|
||||||
|
else if (passphraseOptions.CaseMode == PassphraseGenerationOptions.PassphraseCaseMode.Lowercase)
|
||||||
|
{
|
||||||
|
word = word.ToLower();
|
||||||
|
}
|
||||||
|
else if (passphraseOptions.CaseMode == PassphraseGenerationOptions.PassphraseCaseMode.PascalCase)
|
||||||
|
{
|
||||||
|
word = word.ToUpperFirstLetter();
|
||||||
|
}
|
||||||
|
|
||||||
|
passphrase += word;
|
||||||
|
|
||||||
|
if (i < passphraseOptions.WordCount - 1 || passwordOptions != null)
|
||||||
|
passphrase += passphraseOptions.Separator;
|
||||||
|
|
||||||
var tmp = sb[i];
|
|
||||||
sb[i] = sb[j];
|
|
||||||
sb[j] = tmp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
password = sb.ToString();
|
key += passphrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return password;
|
if (passwordOptions != null)
|
||||||
|
{
|
||||||
|
var groups = GetCharacterGroups(passwordOptions);
|
||||||
|
String characterSet = GetCharacterSet(passwordOptions, groups);
|
||||||
|
|
||||||
|
if (characterSet.Length == 0)
|
||||||
|
throw new Exception("Bad options");
|
||||||
|
|
||||||
|
int size = characterSet.Length;
|
||||||
|
|
||||||
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
|
||||||
|
if (passwordOptions.AtLeastOneFromEachGroup)
|
||||||
|
{
|
||||||
|
foreach (var g in groups)
|
||||||
|
{
|
||||||
|
if (g.Length > 0)
|
||||||
|
{
|
||||||
|
buffer.Append(g[random.Next(g.Length)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 0)
|
||||||
|
{
|
||||||
|
while (buffer.Length < passwordOptions.Length)
|
||||||
|
{
|
||||||
|
buffer.Append(characterSet[random.Next(size)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var password = buffer.ToString();
|
||||||
|
|
||||||
|
if (passwordOptions.AtLeastOneFromEachGroup)
|
||||||
|
{
|
||||||
|
//shuffle
|
||||||
|
StringBuilder sb = new StringBuilder(password);
|
||||||
|
for (int i = (password.Length - 1); i >= 1; i--)
|
||||||
|
{
|
||||||
|
int j = random.Next(i + 1);
|
||||||
|
|
||||||
|
var tmp = sb[i];
|
||||||
|
sb[i] = sb[j];
|
||||||
|
sb[j] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
password = sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
key += password;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetCharacterSet(PasswordGenerationOptions options, List<string> groups)
|
public string GetCharacterSet(PasswordGenerationOptions options, List<string> groups)
|
||||||
|
|||||||
Reference in New Issue
Block a user