diff --git a/ModernKeePass/Common/DatabaseHelper.cs b/ModernKeePass/Common/DatabaseHelper.cs
index afa5d44..c4b8c19 100644
--- a/ModernKeePass/Common/DatabaseHelper.cs
+++ b/ModernKeePass/Common/DatabaseHelper.cs
@@ -54,10 +54,12 @@ namespace ModernKeePass.Common
}
}
+ public StorageFile KeyFile { get; set; }
+
public PwUuid DataCipher
{
get { return _pwDatabase.DataCipherUuid; }
- internal set { _pwDatabase.DataCipherUuid = value; }
+ set { _pwDatabase.DataCipherUuid = value; }
}
public PwCompressionAlgorithm CompressionAlgorithm
@@ -66,6 +68,12 @@ namespace ModernKeePass.Common
set { _pwDatabase.Compression = value; }
}
+ public KdfParameters KeyDerivation
+ {
+ get { return _pwDatabase.KdfParameters; }
+ set { _pwDatabase.KdfParameters = value; }
+ }
+
///
/// Open a KeePass database
///
@@ -77,7 +85,8 @@ namespace ModernKeePass.Common
var key = new CompositeKey();
try
{
- key.AddUserKey(new KcpPassword(password));
+ if (password != null) key.AddUserKey(new KcpPassword(password));
+ if (KeyFile != null) key.AddUserKey(new KcpKeyFile(IOConnectionInfo.FromFile(KeyFile)));
var ioConnection = IOConnectionInfo.FromFile(DatabaseFile);
if (createNew) _pwDatabase.New(ioConnection, key);
else _pwDatabase.Open(ioConnection, key, new NullStatusLogger());
@@ -120,7 +129,7 @@ namespace ModernKeePass.Common
public void Save()
{
// TODO: Save is disabled for now for Argon2Kdf because it corrupts DB (read works)
- if (_pwDatabase == null || !_pwDatabase.IsOpen /*|| KdfPool.Get(_pwDatabase.KdfParameters.KdfUuid) is Argon2Kdf*/) return;
+ if (_pwDatabase == null || !_pwDatabase.IsOpen || KdfPool.Get(_pwDatabase.KdfParameters.KdfUuid) is Argon2Kdf) return;
_pwDatabase.Save(new NullStatusLogger());
}
diff --git a/ModernKeePass/Controls/OpenDatabaseUserControl.xaml b/ModernKeePass/Controls/OpenDatabaseUserControl.xaml
index 3967b25..c6d2ee5 100644
--- a/ModernKeePass/Controls/OpenDatabaseUserControl.xaml
+++ b/ModernKeePass/Controls/OpenDatabaseUserControl.xaml
@@ -8,342 +8,32 @@
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:ModernKeePass.Actions"
mc:Ignorable="d"
- d:DesignHeight="60"
+ d:DesignHeight="120"
d:DesignWidth="550" >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Visible
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ModernKeePass/Controls/OpenDatabaseUserControl.xaml.cs b/ModernKeePass/Controls/OpenDatabaseUserControl.xaml.cs
index c14b808..f4f6ed0 100644
--- a/ModernKeePass/Controls/OpenDatabaseUserControl.xaml.cs
+++ b/ModernKeePass/Controls/OpenDatabaseUserControl.xaml.cs
@@ -1,4 +1,5 @@
using System;
+using Windows.Storage.Pickers;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
@@ -49,7 +50,7 @@ namespace ModernKeePass.Controls
{
ValidationChecking?.Invoke(this, new EventArgs());
var app = (App)Application.Current;
- StatusTextBlock.Text = app.Database.Open(PasswordBox.Password, CreateNew);
+ StatusTextBlock.Text = app.Database.Open(PasswordCheckBox.IsChecked.HasValue && PasswordCheckBox.IsChecked.Value ? PasswordBox.Password : null, CreateNew);
if (app.Database.Status == DatabaseHelper.DatabaseStatus.Opened)
{
ValidationChecked?.Invoke(this, new PasswordEventArgs(app.Database.RootGroup));
@@ -69,5 +70,23 @@ namespace ModernKeePass.Controls
StatusTextBlock.Text = string.Empty;
}
}
+
+ private async void KeyFileButton_Click(object sender, RoutedEventArgs e)
+ {
+ var picker =
+ new FileOpenPicker
+ {
+ ViewMode = PickerViewMode.List,
+ SuggestedStartLocation = PickerLocationId.DocumentsLibrary
+ };
+ picker.FileTypeFilter.Add(".key");
+
+ // Application now has read/write access to the picked file
+ var file = await picker.PickSingleFileAsync();
+ if (file == null) return;
+ var app = (App)Application.Current;
+ app.Database.KeyFile = file;
+ StatusTextBlock.Text = $"Key file: {file.Name}";
+ }
}
}
diff --git a/ModernKeePass/Pages/SettingsPageFrames/SettingsDatabasePage.xaml b/ModernKeePass/Pages/SettingsPageFrames/SettingsDatabasePage.xaml
index 7087abc..27797de 100644
--- a/ModernKeePass/Pages/SettingsPageFrames/SettingsDatabasePage.xaml
+++ b/ModernKeePass/Pages/SettingsPageFrames/SettingsDatabasePage.xaml
@@ -10,6 +10,7 @@
+
@@ -22,5 +23,7 @@
+
+
diff --git a/ModernKeePass/ViewModels/SettingsDatabaseVm.cs b/ModernKeePass/ViewModels/SettingsDatabaseVm.cs
index ff67b3b..3eabd6e 100644
--- a/ModernKeePass/ViewModels/SettingsDatabaseVm.cs
+++ b/ModernKeePass/ViewModels/SettingsDatabaseVm.cs
@@ -8,6 +8,7 @@ using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePassLib;
using ModernKeePassLib.Cryptography.Cipher;
+using ModernKeePassLib.Cryptography.KeyDerivation;
namespace ModernKeePass.ViewModels
{
@@ -60,6 +61,13 @@ namespace ModernKeePass.ViewModels
get { return Enum.GetName(typeof(PwCompressionAlgorithm), _app.Database.CompressionAlgorithm); }
set { _app.Database.CompressionAlgorithm = (PwCompressionAlgorithm)Enum.Parse(typeof(PwCompressionAlgorithm), value); }
}
+ public IEnumerable KeyDerivations => KdfPool.Engines.Select(e => e.Name);
+
+ public string KeyDerivationName
+ {
+ get { return KdfPool.Get(_app.Database.KeyDerivation.KdfUuid).Name; }
+ set { _app.Database.KeyDerivation = KdfPool.Engines.FirstOrDefault(e => e.Name == value)?.GetDefaultParameters(); }
+ }
public ISelectableModel SelectedItem
{
diff --git a/ModernKeePassLib.Test/Cryptography/Hash/HmacTests.cs b/ModernKeePassLib.Test/Cryptography/Hash/HmacTests.cs
index ebcd198..52648c6 100644
--- a/ModernKeePassLib.Test/Cryptography/Hash/HmacTests.cs
+++ b/ModernKeePassLib.Test/Cryptography/Hash/HmacTests.cs
@@ -44,6 +44,41 @@ namespace ModernKeePassLib.Test.Cryptography.Hash
HmacEval(pbKey, pbMsg, pbExpc);
}
+ [Test]
+ public void TestHmacSha1ComputeHash()
+ {
+ var expectedHash = "AC2C2E614882CE7158F69B7E3B12114465945D01";
+ var message = StrUtil.Utf8.GetBytes("testing123");
+ var key = StrUtil.Utf8.GetBytes("hello");
+ using (var result = new HMACSHA1(key))
+ {
+ Assert.That(ByteToString(result.ComputeHash(message)), Is.EqualTo(expectedHash));
+ }
+ }
+
+ [Test]
+ public void TestHmacSha256ComputeHash()
+ {
+ var expectedHash = "09C1BD2DE4E5659C0EFAF9E6AE4723E9CF96B69609B4E562F6AFF1745D7BF4E0";
+ var message = StrUtil.Utf8.GetBytes("testing123");
+ var key = StrUtil.Utf8.GetBytes("hello");
+ using (var result = new HMACSHA256(key))
+ {
+ Assert.That(ByteToString(result.ComputeHash(message)), Is.EqualTo(expectedHash));
+ }
+ }
+
+ public static string ByteToString(byte[] buff)
+ {
+ string sbinary = "";
+
+ for (int i = 0; i < buff.Length; i++)
+ {
+ sbinary += buff[i].ToString("X2"); // hex format
+ }
+ return (sbinary);
+ }
+
[Test]
public void TestHmacOtp()
{
diff --git a/ModernKeePassLib.Test/ModernKeePassLib.Test.csproj b/ModernKeePassLib.Test/ModernKeePassLib.Test.csproj
index 30b8dfe..ce8e718 100644
--- a/ModernKeePassLib.Test/ModernKeePassLib.Test.csproj
+++ b/ModernKeePassLib.Test/ModernKeePassLib.Test.csproj
@@ -72,6 +72,7 @@
+
diff --git a/ModernKeePassLib.Test/Utility/MemUtilTests.cs b/ModernKeePassLib.Test/Utility/MemUtilTests.cs
new file mode 100644
index 0000000..f57ee2c
--- /dev/null
+++ b/ModernKeePassLib.Test/Utility/MemUtilTests.cs
@@ -0,0 +1,89 @@
+using System.Text;
+using ModernKeePassLib.Cryptography;
+using ModernKeePassLib.Utility;
+using NUnit.Framework;
+
+namespace ModernKeePassLib.Test.Utility
+{
+ [TestFixture]
+ public class MemUtilTests
+ {
+ private byte[] _pb = CryptoRandom.Instance.GetRandomBytes((uint)CryptoRandom.NewWeakRandom().Next(0, 0x2FFFF));
+
+ [Test]
+ public void TestGzip()
+ {
+ var pbCompressed = MemUtil.Compress(_pb);
+ Assert.That(MemUtil.ArraysEqual(MemUtil.Decompress(pbCompressed), _pb), Is.True);
+ }
+
+ [Test]
+ public void TestMemUtil()
+ {
+ Encoding enc = StrUtil.Utf8;
+ _pb = enc.GetBytes("012345678901234567890a");
+ byte[] pbN = enc.GetBytes("9012");
+ Assert.That(MemUtil.IndexOf(_pb, pbN), Is.EqualTo(9));
+
+ pbN = enc.GetBytes("01234567890123");
+ Assert.That(MemUtil.IndexOf(_pb, pbN), Is.EqualTo(0));
+
+ pbN = enc.GetBytes("a");
+ Assert.That(MemUtil.IndexOf(_pb, pbN), Is.EqualTo(21));
+
+ pbN = enc.GetBytes("0a");
+ Assert.That(MemUtil.IndexOf(_pb, pbN), Is.EqualTo(20));
+
+ pbN = enc.GetBytes("1");
+ Assert.That(MemUtil.IndexOf(_pb, pbN), Is.EqualTo(1));
+
+ pbN = enc.GetBytes("b");
+ Assert.That(MemUtil.IndexOf(_pb, pbN), Is.LessThan(0));
+
+ pbN = enc.GetBytes("012b");
+ Assert.That(MemUtil.IndexOf(_pb, pbN), Is.LessThan(0));
+ }
+
+ [Test]
+ public void TestBase32()
+ {
+ byte[] pbRes = MemUtil.ParseBase32("MY======");
+ byte[] pbExp = Encoding.UTF8.GetBytes("f");
+ Assert.That(MemUtil.ArraysEqual(pbRes, pbExp), Is.True);
+
+ pbRes = MemUtil.ParseBase32("MZXQ====");
+ pbExp = Encoding.UTF8.GetBytes("fo");
+ Assert.That(MemUtil.ArraysEqual(pbRes, pbExp), Is.True);
+
+ pbRes = MemUtil.ParseBase32("MZXW6===");
+ pbExp = Encoding.UTF8.GetBytes("foo");
+ Assert.That(MemUtil.ArraysEqual(pbRes, pbExp), Is.True);
+
+ pbRes = MemUtil.ParseBase32("MZXW6YQ=");
+ pbExp = Encoding.UTF8.GetBytes("foob");
+ Assert.That(MemUtil.ArraysEqual(pbRes, pbExp), Is.True);
+
+ pbRes = MemUtil.ParseBase32("MZXW6YTB");
+ pbExp = Encoding.UTF8.GetBytes("fooba");
+ Assert.That(MemUtil.ArraysEqual(pbRes, pbExp), Is.True);
+
+ pbRes = MemUtil.ParseBase32("MZXW6YTBOI======");
+ pbExp = Encoding.UTF8.GetBytes("foobar");
+ Assert.That(MemUtil.ArraysEqual(pbRes, pbExp), Is.True);
+
+ pbRes = MemUtil.ParseBase32("JNSXSIDQOJXXM2LEMVZCAYTBONSWIIDPNYQG63TFFV2GS3LFEBYGC43TO5XXEZDTFY======");
+ pbExp = Encoding.UTF8.GetBytes("Key provider based on one-time passwords.");
+ Assert.That(MemUtil.ArraysEqual(pbRes, pbExp), Is.True);
+ }
+
+ [Test]
+ public void TestMemUtil2()
+ {
+ var i = 0 - 0x10203040;
+ var pbRes = MemUtil.Int32ToBytes(i);
+ Assert.That(MemUtil.ByteArrayToHexString(pbRes), Is.EqualTo("C0CFDFEF"));
+ Assert.That(MemUtil.BytesToUInt32(pbRes), Is.EqualTo((uint)i));
+ Assert.That(MemUtil.BytesToInt32(pbRes), Is.EqualTo(i));
+ }
+ }
+}
\ No newline at end of file
diff --git a/ModernKeePassLib.Test/app.config b/ModernKeePassLib.Test/app.config
index 8f23061..9d20672 100644
--- a/ModernKeePassLib.Test/app.config
+++ b/ModernKeePassLib.Test/app.config
@@ -16,7 +16,7 @@
-
+
diff --git a/ModernKeePassLib/Serialization/BinaryReaderEx.cs b/ModernKeePassLib/Serialization/BinaryReaderEx.cs
index deef447..757515a 100644
--- a/ModernKeePassLib/Serialization/BinaryReaderEx.cs
+++ b/ModernKeePassLib/Serialization/BinaryReaderEx.cs
@@ -75,7 +75,7 @@ namespace ModernKeePassLib.Serialization
if(m_sCopyTo != null) m_sCopyTo.Write(pb, 0, pb.Length);
return pb;
}
- catch(Exception)
+ catch(Exception ex)
{
if(!string.IsNullOrEmpty(m_strReadExcp))
throw new IOException(m_strReadExcp);
diff --git a/ModernKeePassLib/Utility/MemUtil.cs b/ModernKeePassLib/Utility/MemUtil.cs
index 18ea610..9883115 100644
--- a/ModernKeePassLib/Utility/MemUtil.cs
+++ b/ModernKeePassLib/Utility/MemUtil.cs
@@ -249,8 +249,8 @@ namespace ModernKeePassLib.Utility
///
/// Set all bytes in a byte array to zero.
///
- /// Input array. All bytes of this array will be set
- /// to zero.
+ /// Input array. All bytes of this array
+ /// will be set to zero.
public static void ZeroByteArray(byte[] pbArray)
{
Debug.Assert(pbArray != null);
diff --git a/ModernKeePassLib/Utility/StrUtil.cs b/ModernKeePassLib/Utility/StrUtil.cs
index a29cf4a..3d1216b 100644
--- a/ModernKeePassLib/Utility/StrUtil.cs
+++ b/ModernKeePassLib/Utility/StrUtil.cs
@@ -20,17 +20,18 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.Text;
+using System.Diagnostics;
using System.Drawing;
+using System.Globalization;
using System.IO;
+using System.Text;
using System.Text.RegularExpressions;
+
#if ModernKeePassLib
using Windows.Security.Cryptography;
#else
using System.Security.Cryptography;
#endif
-using System.Globalization;
-using System.Diagnostics;
using ModernKeePassLib.Collections;
using ModernKeePassLib.Cryptography.PasswordGenerator;