Implemented loading of Keepass 1 (kdb) files. First test passed!
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
@@ -55,6 +55,9 @@
|
||||
<Compile Include="database\CheckDatabaseForChanges.cs" />
|
||||
<Compile Include="database\edit\EditGroup.cs" />
|
||||
<Compile Include="database\edit\MoveElement.cs" />
|
||||
<Compile Include="database\DatabaseV1.cs" />
|
||||
<Compile Include="database\KdbDatabaseLoader.cs" />
|
||||
<Compile Include="database\KdbxDatabaseLoader.cs" />
|
||||
<Compile Include="database\SynchronizeCachedDatabase.cs" />
|
||||
<Compile Include="Io\BuiltInFileStorage.cs" />
|
||||
<Compile Include="Io\CachingFileStorage.cs" />
|
||||
@@ -112,6 +115,10 @@
|
||||
<Project>{545b4a6b-8bba-4fbe-92fc-4ac060122a54}</Project>
|
||||
<Name>KeePassLib2Android</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
|
||||
<Project>{70d3844a-d9fa-4a64-b205-a84c6a822196}</Project>
|
||||
<Name>KP2AKdbLibraryBinding</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
@@ -112,8 +112,15 @@ namespace keepass2android
|
||||
Loaded = true;
|
||||
KpDatabase = pwDatabase;
|
||||
SearchHelper = new SearchDbHelper(app);
|
||||
|
||||
CanWrite = databaseLoader.CanWrite;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether it is possible to make changes to this database
|
||||
/// </summary>
|
||||
public bool CanWrite { get; set; }
|
||||
|
||||
protected virtual void PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseLoader databaseLoader)
|
||||
{
|
||||
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using Android.Content;
|
||||
using Com.Keepassdroid.Database;
|
||||
using Com.Keepassdroid.Database.Exception;
|
||||
using Java.Lang;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Cryptography;
|
||||
using KeePassLib.Cryptography.Cipher;
|
||||
using KeePassLib.Interfaces;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Security;
|
||||
using Exception = System.Exception;
|
||||
using PwIcon = KeePassLib.PwIcon;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
class KdbDatabaseLoader: IDatabaseLoader
|
||||
{
|
||||
private Context _ctx;
|
||||
|
||||
public KdbDatabaseLoader(Context ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
private Dictionary<PwUuid, AdditionalGroupData> _groupData = new Dictionary<PwUuid, AdditionalGroupData>();
|
||||
|
||||
public void PopulateDatabaseFromStream(PwDatabase db, CompositeKey key, Stream s, IStatusLogger slLogger)
|
||||
{
|
||||
@@ -49,6 +45,8 @@ namespace keepass2android
|
||||
var dbv3 = importer.OpenDatabase(hashingStream, password, keyfile);
|
||||
|
||||
db.Name = dbv3.Name;
|
||||
db.RootGroup = ConvertGroup(dbv3.RootGroup);
|
||||
|
||||
}
|
||||
catch (InvalidPasswordException e) {
|
||||
|
||||
@@ -71,6 +69,98 @@ namespace keepass2android
|
||||
throw new Exception("hashing didn't work"); //todo remove
|
||||
}
|
||||
|
||||
private PwGroup ConvertGroup(PwGroupV3 groupV3)
|
||||
{
|
||||
PwGroup pwGroup = new PwGroup(true, false);
|
||||
pwGroup.Name = groupV3.Name;
|
||||
|
||||
pwGroup.CreationTime = ConvertTime(groupV3.TCreation);
|
||||
pwGroup.LastAccessTime = ConvertTime(groupV3.TLastAccess);
|
||||
pwGroup.LastModificationTime = ConvertTime(groupV3.TLastMod);
|
||||
pwGroup.Expires = !PwGroupV3.NeverExpire.Equals(groupV3.TExpire);
|
||||
if (pwGroup.Expires)
|
||||
pwGroup.ExpiryTime = ConvertTime(groupV3.TExpire);
|
||||
|
||||
if (groupV3.Icon != null)
|
||||
pwGroup.IconId = (PwIcon) groupV3.Icon.IconId;
|
||||
_groupData.Add(pwGroup.Uuid, new AdditionalGroupData
|
||||
{
|
||||
Flags = groupV3.Flags,
|
||||
Id = groupV3.Id.Id
|
||||
});
|
||||
|
||||
|
||||
for (int i = 0; i < groupV3.ChildGroups.Count;i++)
|
||||
{
|
||||
pwGroup.AddGroup(ConvertGroup(groupV3.GetGroupAt(i)), true);
|
||||
}
|
||||
for (int i = 0; i < groupV3.ChildEntries.Count; i++)
|
||||
{
|
||||
var entry = groupV3.GetEntryAt(i);
|
||||
if (entry.IsMetaStream)
|
||||
continue;
|
||||
pwGroup.AddEntry(ConvertEntry(entry), true);
|
||||
}
|
||||
|
||||
return pwGroup;
|
||||
}
|
||||
|
||||
private PwEntry ConvertEntry(PwEntryV3 entryV3)
|
||||
{
|
||||
PwEntry pwEntry = new PwEntry(false, false);
|
||||
pwEntry.Uuid = new PwUuid(entryV3.Uuid.ToArray());
|
||||
pwEntry.CreationTime = ConvertTime(entryV3.TCreation);
|
||||
pwEntry.LastAccessTime = ConvertTime(entryV3.TLastAccess);
|
||||
pwEntry.LastModificationTime = ConvertTime(entryV3.TLastMod);
|
||||
|
||||
pwEntry.Expires = entryV3.Expires();
|
||||
if (pwEntry.Expires)
|
||||
pwEntry.ExpiryTime = ConvertTime(entryV3.TExpire);
|
||||
|
||||
if (entryV3.Icon != null)
|
||||
pwEntry.IconId = (PwIcon) entryV3.Icon.IconId;
|
||||
SetFieldIfAvailable(pwEntry, PwDefs.TitleField, false, entryV3.Title);
|
||||
SetFieldIfAvailable(pwEntry, PwDefs.UserNameField, false, entryV3.Username);
|
||||
SetFieldIfAvailable(pwEntry, PwDefs.UrlField, false, entryV3.Url);
|
||||
SetFieldIfAvailable(pwEntry, PwDefs.PasswordField, true, entryV3.Password);
|
||||
SetFieldIfAvailable(pwEntry, PwDefs.NotesField, true, entryV3.Additional);
|
||||
|
||||
if (entryV3.GetBinaryData() != null)
|
||||
{
|
||||
pwEntry.Binaries.Set(entryV3.BinaryDesc, new ProtectedBinary(true, entryV3.GetBinaryData()));
|
||||
}
|
||||
return pwEntry;
|
||||
}
|
||||
|
||||
private void SetFieldIfAvailable(PwEntry pwEntry, string fieldName, bool makeProtected, string value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
pwEntry.Strings.Set(fieldName, new ProtectedString(makeProtected, value));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private DateTime ConvertTime(PwDate date)
|
||||
{
|
||||
if (date == null)
|
||||
return PwDefs.DtDefaultNow;
|
||||
return JavaTimeToCSharp(date.JDate.Time);
|
||||
}
|
||||
|
||||
private DateTime JavaTimeToCSharp(long javatime)
|
||||
{
|
||||
return new DateTime(1970, 1, 1).AddMilliseconds(javatime);
|
||||
|
||||
}
|
||||
|
||||
public byte[] HashOfLastStream { get; private set; }
|
||||
public bool CanWrite { get { return false; } }
|
||||
}
|
||||
|
||||
internal class AdditionalGroupData
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int Flags { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -27,5 +27,6 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
public byte[] HashOfLastStream { get; private set; }
|
||||
public bool CanWrite { get { return true; } }
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
@@ -33,6 +32,7 @@ namespace keepass2android
|
||||
private readonly string _keyfileOrProvider;
|
||||
private readonly IKp2aApp _app;
|
||||
private readonly bool _rememberKeyfile;
|
||||
IDatabaseLoader _loader;
|
||||
|
||||
public LoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish): base(finish)
|
||||
{
|
||||
@@ -68,8 +68,8 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
//ok, try to load the database. Let's start with Kdbx format and retry later if that is the wrong guess:
|
||||
IDatabaseLoader loader = new KdbxDatabaseLoader(KdbpFile.GetFormatToUse(_ioc));
|
||||
TryLoad(databaseStream, loader);
|
||||
_loader = new KdbxDatabaseLoader(KdbpFile.GetFormatToUse(_ioc));
|
||||
TryLoad(databaseStream);
|
||||
}
|
||||
catch (KeyFileException)
|
||||
{
|
||||
@@ -99,7 +99,7 @@ namespace keepass2android
|
||||
|
||||
}
|
||||
|
||||
private void TryLoad(MemoryStream databaseStream, IDatabaseLoader loader)
|
||||
private void TryLoad(MemoryStream databaseStream)
|
||||
{
|
||||
//create a copy of the stream so we can try again if we get an exception which indicates we should change parameters
|
||||
//This is not optimal in terms of (short-time) memory usage but is hard to avoid because the Keepass library closes streams also in case of errors.
|
||||
@@ -112,14 +112,15 @@ namespace keepass2android
|
||||
//now let's go:
|
||||
try
|
||||
{
|
||||
_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, loader);
|
||||
_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _loader);
|
||||
SaveFileData(_ioc, _keyfileOrProvider);
|
||||
Kp2aLog.Log("LoadDB OK");
|
||||
Finish(true);
|
||||
}
|
||||
catch (OldFormatException)
|
||||
{
|
||||
TryLoad(databaseStream, new KdbDatabaseLoader(Application.Context));
|
||||
_loader = new KdbDatabaseLoader();
|
||||
TryLoad(databaseStream);
|
||||
}
|
||||
catch (InvalidCompositeKeyException)
|
||||
{
|
||||
@@ -131,7 +132,7 @@ namespace keepass2android
|
||||
//retry without password:
|
||||
_compositeKey.RemoveUserKey(passwordKey);
|
||||
//retry:
|
||||
TryLoad(databaseStream, loader);
|
||||
TryLoad(databaseStream);
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user