From 9b9f0699490e508c46bc985c01c333abfb5ba05f Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 8 Sep 2016 04:41:41 +0200 Subject: [PATCH] 1.01 pre1: added preferences to switch encryption+kdf and to edit kdf parameters (for AES and Argon2) --- .../Cryptography/Cipher/CipherPool.cs | 8 +- .../Cryptography/KeyDerivation/AesKdf.cs | 1 + src/keepass2android/ChangeLog.cs | 1 + .../Properties/AndroidManifest_net.xml | 4 +- .../Resources/layout/database_settings.xml | 1 - .../Resources/values/config.xml | 6 +- .../Resources/values/strings.xml | 19 +++ .../Resources/xml/preferences.xml | 54 ++++++- src/keepass2android/keepass2android.csproj | 7 +- .../settings/Argon2Preference.cs | 101 +++++++++++++ .../settings/DatabaseSettingsActivity.cs | 142 ++++++++++++++++-- .../settings/RoundsPreference.cs | 115 +++++++++----- 12 files changed, 390 insertions(+), 69 deletions(-) create mode 100644 src/keepass2android/settings/Argon2Preference.cs diff --git a/src/KeePassLib2Android/Cryptography/Cipher/CipherPool.cs b/src/KeePassLib2Android/Cryptography/Cipher/CipherPool.cs index 836a157c..4799abd1 100644 --- a/src/KeePassLib2Android/Cryptography/Cipher/CipherPool.cs +++ b/src/KeePassLib2Android/Cryptography/Cipher/CipherPool.cs @@ -46,7 +46,6 @@ namespace KeePassLib.Cryptography.Cipher cp = new CipherPool(); cp.AddCipher(new StandardAesEngine()); cp.AddCipher(new ChaCha20Engine()); - m_poolGlobal = cp; } @@ -161,5 +160,12 @@ namespace KeePassLib.Cryptography.Cipher return m_vCiphers[nIndex]; } } + + public IEnumerable Engines + { + get { + return m_vCiphers; + } + } } } diff --git a/src/KeePassLib2Android/Cryptography/KeyDerivation/AesKdf.cs b/src/KeePassLib2Android/Cryptography/KeyDerivation/AesKdf.cs index 194b43d5..ee0595b2 100644 --- a/src/KeePassLib2Android/Cryptography/KeyDerivation/AesKdf.cs +++ b/src/KeePassLib2Android/Cryptography/KeyDerivation/AesKdf.cs @@ -122,6 +122,7 @@ namespace KeePassLib.Cryptography.KeyDerivation // Try to use the native library first if (NativeLib.TransformKey256(pbNewKey, pbKeySeed32, uNumRounds)) { + //no need to hash, this is already done in the native library. byte[] pbKey = new byte[32]; Array.Copy(pbNewKey, pbKey, pbNewKey.Length); diff --git a/src/keepass2android/ChangeLog.cs b/src/keepass2android/ChangeLog.cs index 3187ad58..5303c80a 100644 --- a/src/keepass2android/ChangeLog.cs +++ b/src/keepass2android/ChangeLog.cs @@ -26,6 +26,7 @@ namespace keepass2android AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(ctx, Android.Resource.Style.ThemeHoloLightDialog)); builder.SetTitle(ctx.GetString(Resource.String.ChangeLog_title)); List changeLog = new List{ + ctx.GetString(Resource.String.ChangeLog_1_01_preview), ctx.GetString(Resource.String.ChangeLog_1_0_0e), ctx.GetString(Resource.String.ChangeLog_1_0_0), ctx.GetString(Resource.String.ChangeLog_0_9_9c), diff --git a/src/keepass2android/Properties/AndroidManifest_net.xml b/src/keepass2android/Properties/AndroidManifest_net.xml index c319e383..d00b6d31 100644 --- a/src/keepass2android/Properties/AndroidManifest_net.xml +++ b/src/keepass2android/Properties/AndroidManifest_net.xml @@ -1,7 +1,7 @@  diff --git a/src/keepass2android/Resources/layout/database_settings.xml b/src/keepass2android/Resources/layout/database_settings.xml index ef23e0af..fe262d2d 100644 --- a/src/keepass2android/Resources/layout/database_settings.xml +++ b/src/keepass2android/Resources/layout/database_settings.xml @@ -26,7 +26,6 @@ android:layout_width="fill_parent" android:inputType="number"/> diff --git a/src/keepass2android/Resources/values/config.xml b/src/keepass2android/Resources/values/config.xml index b18ccf23..f7d61ab0 100644 --- a/src/keepass2android/Resources/values/config.xml +++ b/src/keepass2android/Resources/values/config.xml @@ -52,8 +52,10 @@ app_timeout_key show_kill_app_key clip_timeout_key - db - rounds + db + kdf_screen + kdf_key + rounds change_master_pwd keyfile maskpass diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml index 12a38a7b..65c6c161 100644 --- a/src/keepass2android/Resources/values/strings.xml +++ b/src/keepass2android/Resources/values/strings.xml @@ -64,6 +64,9 @@ Brackets Cancel Clipboard cleared. + + + Clipboard timeout Time before clearing clipboard after copying username or password Select to copy username to clipboard @@ -138,6 +141,8 @@ At least one password generation type must be selected Passwords do not match. Rounds must be a number. + Parameter must be a number. + A title is required. Enter a positive integer on length field File not found. @@ -216,9 +221,17 @@ Remove Rijndael (AES) Root + + Key derivation function + Encryption Rounds Higher encryption rounds provide additional protection against brute force attacks, but can really slow down loading and saving. rounds + + + Memory for Argon 2 (bytes) + Parallelism for Argon 2 + Database name Default user name for new entries Saving database… @@ -662,6 +675,12 @@ * Bug fix: Previous release contained two input methods (one crashing)\n + + Version 1.01-pre1\n + * NOTE: this is a preview, ONLY use for testing!\n + * added preview-support for the new KDBX-4-format (see http://keepass.info/help/kb/kdbx_4.html) including Argon2 key derivation and ChaCha20 encryption. Argon2 is currently only implemented in managed code. Expect a slow transformation process.\n + + Version 1.0.0e\n * fix for Fingerprint Unlock on older Samsung devices with Android 6\n diff --git a/src/keepass2android/Resources/xml/preferences.xml b/src/keepass2android/Resources/xml/preferences.xml index fded5265..0db91090 100644 --- a/src/keepass2android/Resources/xml/preferences.xml +++ b/src/keepass2android/Resources/xml/preferences.xml @@ -31,18 +31,59 @@ android:title="@string/database_name" android:persistent="false" android:key="@string/database_name_key"/> - - + + + + + + + + + - + + + + + + + + + full false bin\Debug - DEBUG;EXCLUDE_TWOFISH;_EXCLUDE_KEYBOARD;_EXCLUDE_FILECHOOSER;_EXCLUDE_JAVAFILESTORAGE;INCLUDE_KEYTRANSFORM + DEBUG;_EXCLUDE_TWOFISH;_EXCLUDE_KEYBOARD;_EXCLUDE_FILECHOOSER;_EXCLUDE_JAVAFILESTORAGE;INCLUDE_KEYTRANSFORM prompt 4 False @@ -182,6 +182,7 @@ + @@ -590,7 +591,9 @@ Designer - + + Designer + Designer diff --git a/src/keepass2android/settings/Argon2Preference.cs b/src/keepass2android/settings/Argon2Preference.cs new file mode 100644 index 00000000..b8396f23 --- /dev/null +++ b/src/keepass2android/settings/Argon2Preference.cs @@ -0,0 +1,101 @@ +using Android.Content; +using Android.Util; +using KeePassLib.Cryptography.KeyDerivation; + +namespace keepass2android.settings +{ + public class Argon2RoundsPreference: KdfNumberParamPreference + { + public Argon2RoundsPreference(Context context, IAttributeSet attrs) : base(context, attrs) + { + } + + public Argon2RoundsPreference(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle) + { + } + + public override ulong ParamValue + { + get + { + var kdfparams = App.Kp2a.GetDb().KpDatabase.KdfParameters; + var kdf = KdfPool.Get(App.Kp2a.GetDb().KpDatabase.KdfParameters.KdfUuid); + if (!(kdf is Argon2Kdf)) + { + new Argon2Kdf().GetDefaultParameters(); + } + + return kdfparams.GetUInt64(Argon2Kdf.ParamIterations, 0); + } + set + { + App.Kp2a.GetDb().KpDatabase.KdfParameters.SetUInt64(Argon2Kdf.ParamIterations, value); + } + } + } + + public class Argon2ParallelismPreference : KdfNumberParamPreference + { + public Argon2ParallelismPreference(Context context, IAttributeSet attrs) + : base(context, attrs) + { + } + + public Argon2ParallelismPreference(Context context, IAttributeSet attrs, int defStyle) + : base(context, attrs, defStyle) + { + } + + public override ulong ParamValue + { + get + { + var kdfparams = App.Kp2a.GetDb().KpDatabase.KdfParameters; + var kdf = KdfPool.Get(App.Kp2a.GetDb().KpDatabase.KdfParameters.KdfUuid); + if (!(kdf is Argon2Kdf)) + { + new Argon2Kdf().GetDefaultParameters(); + } + + return kdfparams.GetUInt32(Argon2Kdf.ParamParallelism, 0); + } + set + { + App.Kp2a.GetDb().KpDatabase.KdfParameters.SetUInt32(Argon2Kdf.ParamParallelism, (uint) value); + } + } + } + + public class Argon2MemoryPreference : KdfNumberParamPreference + { + public Argon2MemoryPreference(Context context, IAttributeSet attrs) + : base(context, attrs) + { + } + + public Argon2MemoryPreference(Context context, IAttributeSet attrs, int defStyle) + : base(context, attrs, defStyle) + { + } + + public override ulong ParamValue + { + get + { + var kdfparams = App.Kp2a.GetDb().KpDatabase.KdfParameters; + var kdf = KdfPool.Get(App.Kp2a.GetDb().KpDatabase.KdfParameters.KdfUuid); + if (!(kdf is Argon2Kdf)) + { + new Argon2Kdf().GetDefaultParameters(); + } + + return kdfparams.GetUInt64(Argon2Kdf.ParamMemory, 0); + } + set + { + App.Kp2a.GetDb().KpDatabase.KdfParameters.SetUInt64(Argon2Kdf.ParamMemory, value); + } + } + } + +} \ No newline at end of file diff --git a/src/keepass2android/settings/DatabaseSettingsActivity.cs b/src/keepass2android/settings/DatabaseSettingsActivity.cs index 52e6ec4a..95fe6101 100644 --- a/src/keepass2android/settings/DatabaseSettingsActivity.cs +++ b/src/keepass2android/settings/DatabaseSettingsActivity.cs @@ -39,6 +39,8 @@ using KeePassLib.Serialization; using KeePassLib.Utility; using keepass2android.Io; using keepass2android.Utils; +using KeePassLib; +using KeePassLib.Cryptography.KeyDerivation; namespace keepass2android { @@ -313,8 +315,9 @@ namespace keepass2android } private KeyboardSwitchPrefManager _switchPrefManager; + private Preference aesRounds, argon2parallelism, argon2rounds, argon2memory; - void OnRememberKeyFileHistoryChanged(object sender, Preference.PreferenceChangeEventArgs eventArgs) + void OnRememberKeyFileHistoryChanged(object sender, Preference.PreferenceChangeEventArgs eventArgs) { if (!(bool)eventArgs.NewValue) { @@ -344,19 +347,38 @@ namespace keepass2android Database db = App.Kp2a.GetDb(); if (db.Loaded) { - /*Preference rounds = FindPreference(GetString(Resource.String.rounds_key)); - rounds.PreferenceChange += (sender, e) => SetRounds(db, e.Preference); - rounds.Enabled = db.CanWrite; - SetRounds(db, rounds); - */ + ListPreference kdfPref = (ListPreference) FindPreference(GetString(Resource.String.kdf_key)); + kdfPref.SetEntries(KdfPool.Engines.Select(eng => eng.Name).ToArray()); + string[] kdfValues = KdfPool.Engines.Select(eng => eng.Uuid.ToHexString()).ToArray(); + kdfPref.SetEntryValues(kdfValues); + kdfPref.SetValueIndex(kdfValues.Select((v, i) => new {kdf = v, index = i}).First(el => el.kdf == db.KpDatabase.KdfParameters.KdfUuid.ToHexString()).index); + kdfPref.PreferenceChange += OnKdfChange; + + aesRounds = FindPreference(GetString(Resource.String.rounds_key)); + argon2rounds = FindPreference("argon2rounds"); + argon2memory = FindPreference("argon2memory"); + argon2parallelism = FindPreference("argon2parallelism"); + + aesRounds.PreferenceChange += (sender, e) => UpdateKdfSummary(e.Preference); + argon2rounds.PreferenceChange += (sender, e) => UpdateKdfSummary(e.Preference); + argon2memory.PreferenceChange += (sender, e) => UpdateKdfSummary(e.Preference); + argon2parallelism.PreferenceChange += (sender, e) => UpdateKdfSummary(e.Preference); + + UpdateKdfScreen(); + PrepareDefaultUsername(db); PrepareDatabaseName(db); PrepareMasterPassword(); PrepareTemplates(db); - Preference algorithm = FindPreference(GetString(Resource.String.algorithm_key)); - SetAlgorithm(db, algorithm); - + ListPreference algorithmPref = (ListPreference)FindPreference(GetString(Resource.String.algorithm_key)); + algorithmPref.SetEntries(CipherPool.GlobalPool.Engines.Select(eng => eng.DisplayName).ToArray()); + string[] algoValues = CipherPool.GlobalPool.Engines.Select(eng => eng.CipherUuid.ToHexString()).ToArray(); + algorithmPref.SetEntryValues(algoValues); + algorithmPref.SetValueIndex(algoValues.Select((v, i) => new { kdf = v, index = i }).First(el => el.kdf == db.KpDatabase.DataCipherUuid.ToHexString()).index); + algorithmPref.PreferenceChange += AlgorithmPrefChange; + algorithmPref.Summary = + CipherPool.GlobalPool.GetCipher(App.Kp2a.GetDb().KpDatabase.DataCipherUuid).DisplayName; UpdateImportDbPref(); UpdateImportKeyfilePref(); } @@ -425,7 +447,99 @@ namespace keepass2android } - private void PrepareNoDonationReminderPreference(Activity ctx, PreferenceScreen screen, Preference preference) + private void AlgorithmPrefChange(object sender, Preference.PreferenceChangeEventArgs preferenceChangeEventArgs) + { + var db = App.Kp2a.GetDb(); + var previousCipher = db.KpDatabase.DataCipherUuid; + db.KpDatabase.DataCipherUuid = new PwUuid(MemUtil.HexStringToByteArray((string)preferenceChangeEventArgs.NewValue)); + + SaveDb save = new SaveDb(Activity, App.Kp2a, new ActionOnFinish((success, message) => + { + if (!success) + { + db.KpDatabase.DataCipherUuid = previousCipher; + Toast.MakeText(Activity, message, ToastLength.Long).Show(); + return; + } + preferenceChangeEventArgs.Preference.Summary = + CipherPool.GlobalPool.GetCipher(db.KpDatabase.DataCipherUuid).DisplayName; + })); + ProgressTask pt = new ProgressTask(App.Kp2a, Activity, save); + pt.Run(); + } + + private void UpdateKdfScreen() + { + var db = App.Kp2a.GetDb(); + var kdf = KdfPool.Get(db.KpDatabase.KdfParameters.KdfUuid); + + var kdfpref = FindPreference(GetString(Resource.String.kdf_key)); + + + kdfpref.Summary = kdf.Name; + + var kdfscreen = ((PreferenceScreen)FindPreference(GetString(Resource.String.kdf_screen_key))); + if (kdf is AesKdf) + { + if (kdfscreen.FindPreference(GetString(Resource.String.rounds_key)) == null) + kdfscreen.AddPreference(aesRounds); + kdfscreen.RemovePreference(argon2rounds); + kdfscreen.RemovePreference(argon2memory); + kdfscreen.RemovePreference(argon2parallelism); + + aesRounds.Enabled = db.CanWrite; + UpdateKdfSummary(aesRounds); + } + else + { + kdfscreen.RemovePreference(aesRounds); + if (kdfscreen.FindPreference("argon2rounds") == null) + { + kdfscreen.AddPreference(argon2rounds); + kdfscreen.AddPreference(argon2memory); + kdfscreen.AddPreference(argon2parallelism); + } + UpdateKdfSummary(argon2rounds); + UpdateKdfSummary(argon2memory); + UpdateKdfSummary(argon2parallelism); + } + + } + + private void OnKdfChange(object sender, Preference.PreferenceChangeEventArgs preferenceChangeEventArgs) + { + var db = App.Kp2a.GetDb(); + var previousKdfParams = db.KpDatabase.KdfParameters; + Kp2aLog.Log("previous kdf: " + KdfPool.Get(db.KpDatabase.KdfParameters.KdfUuid) + " " + db.KpDatabase.KdfParameters.KdfUuid.ToHexString() ); + db.KpDatabase.KdfParameters = + KdfPool.Get( + new PwUuid(MemUtil.HexStringToByteArray((string)preferenceChangeEventArgs.NewValue))) + .GetDefaultParameters(); + + Kp2aLog.Log("--new kdf: " + KdfPool.Get(db.KpDatabase.KdfParameters.KdfUuid) + " " + db.KpDatabase.KdfParameters.KdfUuid.ToHexString()); + + SaveDb save = new SaveDb(Activity, App.Kp2a, new ActionOnFinish((success, message) => + { + if (!success) + { + db.KpDatabase.KdfParameters = previousKdfParams; + Toast.MakeText(Activity, message, ToastLength.Long).Show(); + return; + } + UpdateKdfScreen(); + + })); + ProgressTask pt = new ProgressTask(App.Kp2a, Activity, save); + pt.Run(); + + } + + private void UpdateKdfSummary(Preference preference) + { + preference.Summary = ((keepass2android.settings.KdfNumberParamPreference)preference).ParamValue.ToString(); + } + + private void PrepareNoDonationReminderPreference(Activity ctx, PreferenceScreen screen, Preference preference) { ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(ctx); @@ -797,12 +911,8 @@ namespace keepass2android return targetIoc; } - /* - private void SetRounds(Database db, Preference rounds) - { - rounds.Summary = db.KpDatabase.KeyEncryptionRounds.ToString(CultureInfo.InvariantCulture); - }*/ - + + private void SetAlgorithm(Database db, Preference algorithm) { algorithm.Summary = CipherPool.GlobalPool.GetCipher(db.KpDatabase.DataCipherUuid).DisplayName; diff --git a/src/keepass2android/settings/RoundsPreference.cs b/src/keepass2android/settings/RoundsPreference.cs index 234de33b..2536c30a 100644 --- a/src/keepass2android/settings/RoundsPreference.cs +++ b/src/keepass2android/settings/RoundsPreference.cs @@ -27,80 +27,68 @@ using KeePassLib.Cryptography.KeyDerivation; namespace keepass2android.settings { - /// - /// Represents the setting for the number of key transformation rounds. Changing this requires to save the database. - /// - public class RoundsPreference : DialogPreference { + public abstract class KdfNumberParamPreference: DialogPreference { - internal TextView RoundsView; + internal TextView edittext; protected override View OnCreateDialogView() { View view = base.OnCreateDialogView(); - RoundsView = (TextView) view.FindViewById(Resource.Id.rounds); + edittext = (TextView) view.FindViewById(Resource.Id.rounds); - ulong numRounds = KeyEncryptionRounds; - RoundsView.Text = numRounds.ToString(CultureInfo.InvariantCulture); + ulong numRounds = ParamValue; + edittext.Text = numRounds.ToString(CultureInfo.InvariantCulture); + + view.FindViewById(Resource.Id.rounds_explaination).Text = ExplanationString; return view; } - public ulong KeyEncryptionRounds + public virtual string ExplanationString { - get - { - AesKdf kdf = new AesKdf(); - if (!kdf.Uuid.Equals(App.Kp2a.GetDb().KpDatabase.KdfParameters.KdfUuid)) - return (uint) PwDefs.DefaultKeyEncryptionRounds; - else - { - ulong uRounds = App.Kp2a.GetDb().KpDatabase.KdfParameters.GetUInt64( - AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds); - uRounds = Math.Min(uRounds, 0xFFFFFFFEUL); - - return (uint) uRounds; - } - } - set { App.Kp2a.GetDb().KpDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds, value); } + get { return ""; } } - public RoundsPreference(Context context, IAttributeSet attrs):base(context, attrs) { + public abstract ulong ParamValue { get; set; } + public KdfNumberParamPreference(Context context, IAttributeSet attrs):base(context, attrs) { } - - public RoundsPreference(Context context, IAttributeSet attrs, int defStyle): base(context, attrs, defStyle) { + + public KdfNumberParamPreference(Context context, IAttributeSet attrs, int defStyle) + : base(context, attrs, defStyle) + { } protected override void OnDialogClosed(bool positiveResult) { base.OnDialogClosed(positiveResult); if ( positiveResult ) { - ulong rounds; + ulong paramValue; - String strRounds = RoundsView.Text; - if (!(ulong.TryParse(strRounds,out rounds))) + String strRounds = edittext.Text; + if (!(ulong.TryParse(strRounds,out paramValue))) { - Toast.MakeText(Context, Resource.String.error_rounds_not_number, ToastLength.Long).Show(); + Toast.MakeText(Context, Resource.String.error_param_not_number, ToastLength.Long).Show(); return; } - if ( rounds < 1 ) { - rounds = 1; + if ( paramValue < 1 ) { + paramValue = 1; } Database db = App.Kp2a.GetDb(); - ulong oldRounds = KeyEncryptionRounds; + ulong oldValue = ParamValue; - if (oldRounds == rounds) + if (oldValue == paramValue) { return; } - KeyEncryptionRounds = rounds; + ParamValue = paramValue; Handler handler = new Handler(); - SaveDb save = new SaveDb(Context, App.Kp2a, new AfterSave(Context, handler, oldRounds, this)); + SaveDb save = new SaveDb(Context, App.Kp2a, new KdfNumberParamPreference.AfterSave(Context, handler, oldValue, this)); ProgressTask pt = new ProgressTask(App.Kp2a, Context, save); pt.Run(); @@ -111,9 +99,9 @@ namespace keepass2android.settings private class AfterSave : OnFinish { private readonly ulong _oldRounds; private readonly Context _ctx; - private readonly RoundsPreference _pref; + private readonly KdfNumberParamPreference _pref; - public AfterSave(Context ctx, Handler handler, ulong oldRounds, RoundsPreference pref):base(handler) { + public AfterSave(Context ctx, Handler handler, ulong oldRounds, KdfNumberParamPreference pref):base(handler) { _pref = pref; _ctx = ctx; @@ -139,5 +127,54 @@ namespace keepass2android.settings } + /// + /// Represents the setting for the number of key transformation rounds. Changing this requires to save the database. + /// + public class RoundsPreference : KdfNumberParamPreference { + private readonly Context _context; + + + public ulong KeyEncryptionRounds + { + get + { + AesKdf kdf = new AesKdf(); + if (!kdf.Uuid.Equals(App.Kp2a.GetDb().KpDatabase.KdfParameters.KdfUuid)) + return (uint) PwDefs.DefaultKeyEncryptionRounds; + else + { + ulong uRounds = App.Kp2a.GetDb().KpDatabase.KdfParameters.GetUInt64( + AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds); + uRounds = Math.Min(uRounds, 0xFFFFFFFEUL); + + return (uint) uRounds; + } + } + set { App.Kp2a.GetDb().KpDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds, value); } + } + + public RoundsPreference(Context context, IAttributeSet attrs):base(context, attrs) + { + _context = context; + } + + public RoundsPreference(Context context, IAttributeSet attrs, int defStyle): base(context, attrs, defStyle) + { + _context = context; + } + + public override string ExplanationString + { + get { return _context.GetString(Resource.String.rounds_explaination); } + } + + public override ulong ParamValue + { + get { return KeyEncryptionRounds; } + set { KeyEncryptionRounds = value; } + } + + } + }