mirror of
https://github.com/moparisthebest/keepass2android
synced 2025-02-07 02:10:10 -05:00
renamed DatabaseLoader to DatabaseFormat
allow write access for kdb (UI not yet completed, but saving done)
This commit is contained in:
parent
fc2e0fa15d
commit
3506e253db
@ -4,7 +4,7 @@ using KeePassLib.Keys;
|
||||
|
||||
namespace KeePassLib
|
||||
{
|
||||
public interface IDatabaseLoader
|
||||
public interface IDatabaseFormat
|
||||
{
|
||||
void PopulateDatabaseFromStream(PwDatabase db, CompositeKey key, Stream s, IStatusLogger slLogger);
|
||||
|
||||
@ -12,5 +12,6 @@ namespace KeePassLib
|
||||
|
||||
bool CanWrite { get; }
|
||||
string SuccessMessage { get; }
|
||||
void Save(PwDatabase kpDatabase, Stream stream);
|
||||
}
|
||||
}
|
@ -12,6 +12,8 @@
|
||||
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
|
||||
<AndroidResgenClass>Resource</AndroidResgenClass>
|
||||
<AssemblyName>KeePassLib2Android</AssemblyName>
|
||||
<AndroidUseLatestPlatformSdk />
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
@ -54,7 +56,7 @@
|
||||
<Reference Include="Mono.Security" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="IDatabaseLoader.cs" />
|
||||
<Compile Include="IDatabaseFormat.cs" />
|
||||
<Compile Include="Kp2aLog.cs" />
|
||||
<Compile Include="Resources\Resource.designer.cs" />
|
||||
<Compile Include="Resources\KLRes.Generated.cs" />
|
||||
|
@ -151,6 +151,11 @@ namespace KeePassLib.Keys
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public T GetUserKey<T>() where T : IUserKey
|
||||
{
|
||||
return (T) GetUserKey(typeof (T));
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
|
@ -564,12 +564,12 @@ namespace KeePassLib
|
||||
/// <param name="ioSource">IO connection to load the database from.</param>
|
||||
/// s<param name="pwKey">Key used to open the specified database.</param>
|
||||
/// <param name="slLogger">Logger, which gets all status messages.</param>
|
||||
/// <param name="loader"></param>
|
||||
/// <param name="format"></param>
|
||||
public void Open(IOConnectionInfo ioSource, CompositeKey pwKey,
|
||||
IStatusLogger slLogger, IDatabaseLoader loader)
|
||||
IStatusLogger slLogger, IDatabaseFormat format)
|
||||
{
|
||||
Open(IOConnection.OpenRead(ioSource), UrlUtil.StripExtension(
|
||||
UrlUtil.GetFileName(ioSource.Path)), ioSource, pwKey, slLogger , loader);
|
||||
UrlUtil.GetFileName(ioSource.Path)), ioSource, pwKey, slLogger , format);
|
||||
|
||||
}
|
||||
|
||||
@ -577,7 +577,7 @@ namespace KeePassLib
|
||||
/// Open a database provided as Stream.
|
||||
/// </summary>
|
||||
public void Open(Stream s, string fileNameWithoutPathAndExt, IOConnectionInfo ioSource, CompositeKey pwKey,
|
||||
IStatusLogger slLogger, IDatabaseLoader loader)
|
||||
IStatusLogger slLogger, IDatabaseFormat format)
|
||||
{
|
||||
Debug.Assert(s != null);
|
||||
if (s == null) throw new ArgumentNullException("s");
|
||||
@ -600,10 +600,10 @@ namespace KeePassLib
|
||||
|
||||
m_bModified = false;
|
||||
|
||||
loader.PopulateDatabaseFromStream(this, pwKey, s, slLogger);
|
||||
format.PopulateDatabaseFromStream(this, pwKey, s, slLogger);
|
||||
|
||||
m_pbHashOfLastIO = loader.HashOfLastStream;
|
||||
m_pbHashOfFileOnDisk = loader.HashOfLastStream;
|
||||
m_pbHashOfLastIO = format.HashOfLastStream;
|
||||
m_pbHashOfFileOnDisk = format.HashOfLastStream;
|
||||
Debug.Assert(m_pbHashOfFileOnDisk != null);
|
||||
|
||||
m_bDatabaseOpened = true;
|
||||
|
@ -27,7 +27,7 @@ namespace keepass2android
|
||||
/// Loads the specified data as the currently open database, as unlocked.
|
||||
/// </summary>
|
||||
void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey,
|
||||
ProgressDialogStatusLogger statusLogger, IDatabaseLoader databaseLoader);
|
||||
ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current database
|
||||
|
@ -58,8 +58,8 @@
|
||||
<Compile Include="database\CheckDatabaseForChanges.cs" />
|
||||
<Compile Include="database\edit\EditGroup.cs" />
|
||||
<Compile Include="database\edit\MoveElement.cs" />
|
||||
<Compile Include="database\KdbDatabaseLoader.cs" />
|
||||
<Compile Include="database\KdbxDatabaseLoader.cs" />
|
||||
<Compile Include="database\KdbDatabaseFormat.cs" />
|
||||
<Compile Include="database\KdbxDatabaseFormat.cs" />
|
||||
<Compile Include="database\PwEntryOutput.cs" />
|
||||
<Compile Include="database\SynchronizeCachedDatabase.cs" />
|
||||
<Compile Include="DataExchange\FileFormatProvider.cs" />
|
||||
|
@ -15,8 +15,10 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using Android.Content;
|
||||
using Java.Lang;
|
||||
using KeePassLib;
|
||||
@ -73,8 +75,9 @@ namespace keepass2android
|
||||
private bool _loaded;
|
||||
|
||||
private bool _reloadRequested;
|
||||
private IDatabaseFormat _databaseFormat;
|
||||
|
||||
public bool ReloadRequested
|
||||
public bool ReloadRequested
|
||||
{
|
||||
get { return _reloadRequested; }
|
||||
set { _reloadRequested = value; }
|
||||
@ -99,14 +102,14 @@ namespace keepass2android
|
||||
/// <summary>
|
||||
/// Do not call this method directly. Call App.Kp2a.LoadDatabase instead.
|
||||
/// </summary>
|
||||
public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseLoader databaseLoader)
|
||||
public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseFormat databaseFormat)
|
||||
{
|
||||
PwDatabase pwDatabase = new PwDatabase();
|
||||
|
||||
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
|
||||
Stream s = databaseData ?? fileStorage.OpenFileForRead(iocInfo);
|
||||
var fileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(iocInfo);
|
||||
PopulateDatabaseFromStream(pwDatabase, s, iocInfo, compositeKey, status, databaseLoader);
|
||||
PopulateDatabaseFromStream(pwDatabase, s, iocInfo, compositeKey, status, databaseFormat);
|
||||
LastFileVersion = fileVersion;
|
||||
|
||||
status.UpdateSubMessage("");
|
||||
@ -119,7 +122,9 @@ namespace keepass2android
|
||||
KpDatabase = pwDatabase;
|
||||
SearchHelper = new SearchDbHelper(app);
|
||||
|
||||
CanWrite = databaseLoader.CanWrite && !fileStorage.IsReadOnly(iocInfo);
|
||||
_databaseFormat = databaseFormat;
|
||||
|
||||
CanWrite = databaseFormat.CanWrite && !fileStorage.IsReadOnly(iocInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -127,11 +132,11 @@ namespace keepass2android
|
||||
/// </summary>
|
||||
public bool CanWrite { get; set; }
|
||||
|
||||
protected virtual void PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseLoader databaseLoader)
|
||||
protected virtual void PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseFormat databaseFormat)
|
||||
{
|
||||
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
|
||||
var filename = fileStorage.GetFilenameWithoutPathAndExt(iocInfo);
|
||||
pwDatabase.Open(s, filename, iocInfo, compositeKey, status, databaseLoader);
|
||||
pwDatabase.Open(s, filename, iocInfo, compositeKey, status, databaseFormat);
|
||||
}
|
||||
|
||||
|
||||
@ -163,12 +168,13 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
|
||||
public virtual void SaveData(Context ctx) {
|
||||
public void SaveData(Context ctx) {
|
||||
|
||||
KpDatabase.UseFileTransactions = _app.GetBooleanPreference(PreferenceKey.UseFileTransactions);
|
||||
using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, KpDatabase.UseFileTransactions))
|
||||
{
|
||||
KpDatabase.Save(trans.OpenFile(), null);
|
||||
_databaseFormat.Save(KpDatabase, trans.OpenFile());
|
||||
|
||||
trans.CommitWrite();
|
||||
}
|
||||
|
||||
@ -186,7 +192,7 @@ namespace keepass2android
|
||||
{
|
||||
if (Entries.ContainsKey(e.Uuid))
|
||||
{
|
||||
throw new DuplicateUuidsException();
|
||||
throw new DuplicateUuidsException("Same UUID for entries '"+Entries[e.Uuid].Strings.ReadSafe(PwDefs.TitleField)+"' and '"+e.Strings.ReadSafe(PwDefs.TitleField)+"'.");
|
||||
}
|
||||
|
||||
}
|
||||
@ -196,11 +202,12 @@ namespace keepass2android
|
||||
{
|
||||
if (checkForDuplicateUuids)
|
||||
{
|
||||
|
||||
/*Disable check. Annoying for users, no problem for KP2A.
|
||||
if (Groups.ContainsKey(g.Uuid))
|
||||
{
|
||||
throw new DuplicateUuidsException();
|
||||
}
|
||||
* */
|
||||
}
|
||||
Groups[g.Uuid] = g;
|
||||
PopulateGlobals(g);
|
||||
@ -236,8 +243,22 @@ namespace keepass2android
|
||||
|
||||
}
|
||||
|
||||
internal class DuplicateUuidsException : Exception
|
||||
[Serializable]
|
||||
public class DuplicateUuidsException : Exception
|
||||
{
|
||||
public DuplicateUuidsException()
|
||||
{
|
||||
}
|
||||
|
||||
public DuplicateUuidsException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
protected DuplicateUuidsException(
|
||||
SerializationInfo info,
|
||||
StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
364
src/Kp2aBusinessLogic/database/KdbDatabaseFormat.cs
Normal file
364
src/Kp2aBusinessLogic/database/KdbDatabaseFormat.cs
Normal file
@ -0,0 +1,364 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
#if !EXCLUDE_KEYTRANSFORM
|
||||
using Android.App;
|
||||
using Com.Keepassdroid.Database;
|
||||
using Com.Keepassdroid.Database.Exception;
|
||||
#endif
|
||||
using Com.Keepassdroid.Database.Save;
|
||||
using Java.Util;
|
||||
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;
|
||||
using Random = System.Random;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
class KdbDatabaseFormat: IDatabaseFormat
|
||||
{
|
||||
private Dictionary<PwUuid, AdditionalGroupData> _groupData = new Dictionary<PwUuid, AdditionalGroupData>();
|
||||
private static readonly DateTime _expireNever = new DateTime(2999,12,28,23,59,59);
|
||||
|
||||
public void PopulateDatabaseFromStream(PwDatabase db, CompositeKey key, Stream s, IStatusLogger slLogger)
|
||||
{
|
||||
#if !EXCLUDE_KEYTRANSFORM
|
||||
var importer = new Com.Keepassdroid.Database.Load.ImporterV3();
|
||||
|
||||
var hashingStream = new HashingStreamEx(s, false, new SHA256Managed());
|
||||
|
||||
string password = "";//no need to distinguish between null and "" because empty passwords are invalid (and null is not allowed)
|
||||
KcpPassword passwordKey = (KcpPassword)key.GetUserKey(typeof(KcpPassword));
|
||||
if (passwordKey != null)
|
||||
{
|
||||
password = passwordKey.Password.ReadString();
|
||||
}
|
||||
|
||||
KcpKeyFile passwordKeyfile = (KcpKeyFile)key.GetUserKey(typeof(KcpKeyFile));
|
||||
MemoryStream keyfileStream = null;
|
||||
if (passwordKeyfile != null)
|
||||
{
|
||||
keyfileStream = new MemoryStream(passwordKeyfile.RawFileData.ReadData());
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var dbv3 = importer.OpenDatabase(hashingStream, password, keyfileStream);
|
||||
|
||||
db.Name = dbv3.Name;
|
||||
db.RootGroup = ConvertGroup(dbv3.RootGroup);
|
||||
if (dbv3.Algorithm == PwEncryptionAlgorithm.Rjindal)
|
||||
{
|
||||
db.DataCipherUuid = StandardAesEngine.AesUuid;
|
||||
}
|
||||
else
|
||||
{
|
||||
db.DataCipherUuid = new PwUuid(TwofishCipher.TwofishCipherEngine.TwofishCipherUuidBytes);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Java.IO.FileNotFoundException e)
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
e.Message, e);
|
||||
}
|
||||
catch (Java.Lang.Exception e)
|
||||
{
|
||||
throw new Exception(e.LocalizedMessage ??
|
||||
e.Message ??
|
||||
e.GetType().Name, e);
|
||||
}
|
||||
|
||||
HashOfLastStream = hashingStream.Hash;
|
||||
if (HashOfLastStream == null)
|
||||
throw new Exception("hashing didn't work"); //todo remove
|
||||
#else
|
||||
throw new Exception("Kdb is excluded with Key transform!");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !EXCLUDE_KEYTRANSFORM
|
||||
|
||||
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 fromEntry)
|
||||
{
|
||||
PwEntry toEntry = new PwEntry(false, false);
|
||||
toEntry.Uuid = new PwUuid(fromEntry.Uuid.ToArray());
|
||||
String modTime = Android.Text.Format.DateFormat.GetTimeFormat(Application.Context).Format(fromEntry.TCreation.JDate);
|
||||
Android.Util.Log.Debug("KP2A", modTime);
|
||||
toEntry.CreationTime = ConvertTime(fromEntry.TCreation);
|
||||
toEntry.LastAccessTime = ConvertTime(fromEntry.TLastAccess);
|
||||
toEntry.LastModificationTime = ConvertTime(fromEntry.TLastMod);
|
||||
|
||||
toEntry.ExpiryTime = ConvertTime(fromEntry.TExpire);
|
||||
|
||||
toEntry.ExpiryTime = new DateTime(toEntry.ExpiryTime.Year, toEntry.ExpiryTime.Month, toEntry.ExpiryTime.Day, toEntry.ExpiryTime.Hour, toEntry.ExpiryTime.Minute, toEntry.ExpiryTime.Second);
|
||||
toEntry.Expires = !(Math.Abs((toEntry.ExpiryTime - _expireNever).TotalMilliseconds) < 500);
|
||||
|
||||
if (fromEntry.Icon != null)
|
||||
toEntry.IconId = (PwIcon) fromEntry.Icon.IconId;
|
||||
SetFieldIfAvailable(toEntry, PwDefs.TitleField, false, fromEntry.Title);
|
||||
SetFieldIfAvailable(toEntry, PwDefs.UserNameField, false, fromEntry.Username);
|
||||
SetFieldIfAvailable(toEntry, PwDefs.UrlField, false, fromEntry.Url);
|
||||
SetFieldIfAvailable(toEntry, PwDefs.PasswordField, true, fromEntry.Password);
|
||||
SetFieldIfAvailable(toEntry, PwDefs.NotesField, true, fromEntry.Additional);
|
||||
|
||||
if ((fromEntry.GetBinaryData() != null) && (fromEntry.GetBinaryData().Length > 0))
|
||||
{
|
||||
toEntry.Binaries.Set(fromEntry.BinaryDesc, new ProtectedBinary(true, fromEntry.GetBinaryData()));
|
||||
}
|
||||
return toEntry;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
var utcTime = new DateTime(1970, 1, 1).AddMilliseconds(javatime);
|
||||
return TimeZoneInfo.ConvertTimeFromUtc(utcTime, TimeZoneInfo.Local);
|
||||
|
||||
}
|
||||
#endif
|
||||
public byte[] HashOfLastStream { get; private set; }
|
||||
public bool CanWrite { get { return true; } }
|
||||
public string SuccessMessage { get
|
||||
{
|
||||
return "";
|
||||
} }
|
||||
|
||||
public void Save(PwDatabase kpDatabase, Stream stream)
|
||||
{
|
||||
PwDatabaseV3 db =new PwDatabaseV3();
|
||||
KcpPassword pwd = kpDatabase.MasterKey.GetUserKey<KcpPassword>();
|
||||
string password = pwd != null ? pwd.Password.ReadString() : "";
|
||||
KcpKeyFile keyfile = kpDatabase.MasterKey.GetUserKey<KcpKeyFile>();
|
||||
Stream keyfileContents = null;
|
||||
if (keyfile != null)
|
||||
{
|
||||
keyfileContents = new MemoryStream(keyfile.RawFileData.ReadData());
|
||||
}
|
||||
db.SetMasterKey(password, keyfileContents);
|
||||
db.NumRounds = (long) kpDatabase.KeyEncryptionRounds;
|
||||
db.Name = kpDatabase.Name;
|
||||
if (kpDatabase.DataCipherUuid.Equals(StandardAesEngine.AesUuid))
|
||||
{
|
||||
db.Algorithm = PwEncryptionAlgorithm.Rjindal;
|
||||
}
|
||||
else
|
||||
{
|
||||
db.Algorithm = PwEncryptionAlgorithm.Twofish;
|
||||
}
|
||||
|
||||
//create groups
|
||||
db.Groups.Clear();
|
||||
var fromGroups = kpDatabase.RootGroup.GetGroups(true);
|
||||
Dictionary<int, PwGroupV3> groupV3s = new Dictionary<int, PwGroupV3>(fromGroups.Count());
|
||||
foreach (PwGroup g in fromGroups)
|
||||
{
|
||||
if (g == kpDatabase.RootGroup)
|
||||
continue;
|
||||
PwGroupV3 groupV3 = ConvertGroup(g, db);
|
||||
db.Groups.Add(groupV3);
|
||||
groupV3s[groupV3.Id.Id] = groupV3;
|
||||
}
|
||||
|
||||
//traverse again and assign parents
|
||||
db.RootGroup = new PwGroupV3() { Level = -1};
|
||||
AssignParent(kpDatabase.RootGroup, db, groupV3s);
|
||||
|
||||
|
||||
|
||||
foreach (PwEntry e in kpDatabase.RootGroup.GetEntries(true))
|
||||
{
|
||||
PwEntryV3 entryV3 = ConvertEntry(e, db);
|
||||
entryV3.Parent = groupV3s[_groupData[e.ParentGroup.Uuid].Id];
|
||||
entryV3.Parent.ChildEntries.Add(entryV3);
|
||||
entryV3.GroupId = entryV3.Parent.Id.Id;
|
||||
db.Entries.Add(entryV3);
|
||||
}
|
||||
|
||||
|
||||
PwDbV3Output output = new PwDbV3Output(db, stream);
|
||||
output.Output();
|
||||
}
|
||||
|
||||
private void AssignParent(PwGroup kpParent, PwDatabaseV3 dbV3, Dictionary<int, PwGroupV3> groupV3s)
|
||||
{
|
||||
PwGroupV3 parentV3;
|
||||
if (kpParent.ParentGroup == null)
|
||||
{
|
||||
parentV3 = dbV3.RootGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentV3 = groupV3s[_groupData[kpParent.Uuid].Id];
|
||||
}
|
||||
|
||||
foreach (PwGroup g in kpParent.Groups)
|
||||
{
|
||||
PwGroupV3 groupV3 = groupV3s[_groupData[g.Uuid].Id];
|
||||
|
||||
parentV3.ChildGroups.Add(groupV3);
|
||||
groupV3.Parent = parentV3;
|
||||
|
||||
AssignParent(g, dbV3, groupV3s);
|
||||
}
|
||||
}
|
||||
|
||||
private PwGroupV3 ConvertGroup(PwGroup fromGroup, PwDatabaseV3 dbTo)
|
||||
{
|
||||
PwGroupV3 toGroup = new PwGroupV3();
|
||||
toGroup.Name = fromGroup.Name;
|
||||
|
||||
toGroup.TCreation = new PwDate(ConvertTime(fromGroup.CreationTime));
|
||||
toGroup.TLastAccess= new PwDate(ConvertTime(fromGroup.LastAccessTime));
|
||||
toGroup.TLastMod = new PwDate(ConvertTime(fromGroup.LastModificationTime));
|
||||
if (fromGroup.Expires)
|
||||
{
|
||||
toGroup.TExpire = new PwDate(ConvertTime(fromGroup.ExpiryTime));
|
||||
}
|
||||
else
|
||||
{
|
||||
toGroup.TExpire = new PwDate(PwGroupV3.NeverExpire);
|
||||
}
|
||||
|
||||
toGroup.Icon = dbTo.IconFactory.GetIcon((int) fromGroup.IconId);
|
||||
AdditionalGroupData groupData;
|
||||
if (_groupData.TryGetValue(fromGroup.Uuid, out groupData))
|
||||
{
|
||||
toGroup.Id = new PwGroupIdV3(groupData.Id);
|
||||
toGroup.Flags = groupData.Flags;
|
||||
}
|
||||
else
|
||||
{
|
||||
//group was added
|
||||
do
|
||||
{
|
||||
toGroup.Id = new PwGroupIdV3(new Random().Next());
|
||||
} while (_groupData.Values.Any(gd => gd.Id == toGroup.Id.Id)); //retry if id already exists
|
||||
//store to block new id and reuse when saving again (without loading in between)
|
||||
_groupData[fromGroup.Uuid] = new AdditionalGroupData
|
||||
{
|
||||
Id = toGroup.Id.Id
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return toGroup;
|
||||
}
|
||||
|
||||
private PwEntryV3 ConvertEntry(PwEntry fromEntry, PwDatabaseV3 dbTo)
|
||||
{
|
||||
PwEntryV3 toEntry = new PwEntryV3();
|
||||
toEntry.Uuid = fromEntry.Uuid.UuidBytes;
|
||||
toEntry.CreationTime = ConvertTime(fromEntry.CreationTime);
|
||||
toEntry.LastAccessTime = ConvertTime(fromEntry.LastAccessTime);
|
||||
toEntry.LastModificationTime = ConvertTime(fromEntry.LastModificationTime);
|
||||
|
||||
if (fromEntry.Expires)
|
||||
{
|
||||
toEntry.ExpiryTime = ConvertTime(fromEntry.ExpiryTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
toEntry.ExpiryTime = ConvertTime(_expireNever);
|
||||
}
|
||||
|
||||
|
||||
toEntry.Icon = dbTo.IconFactory.GetIcon((int) fromEntry.IconId);
|
||||
toEntry.SetTitle(GetString(fromEntry, PwDefs.TitleField), dbTo);
|
||||
toEntry.SetUsername(GetString(fromEntry, PwDefs.UserNameField), dbTo);
|
||||
toEntry.SetUrl(GetString(fromEntry, PwDefs.UrlField), dbTo);
|
||||
toEntry.SetPassword(GetString(fromEntry, PwDefs.PasswordField), dbTo);
|
||||
toEntry.SetNotes(GetString(fromEntry, PwDefs.NotesField), dbTo);
|
||||
if (fromEntry.Binaries.Any())
|
||||
{
|
||||
var binaryData = fromEntry.Binaries.First().Value.ReadData();
|
||||
toEntry.SetBinaryData(binaryData, 0, binaryData.Length);
|
||||
}
|
||||
|
||||
return toEntry;
|
||||
}
|
||||
|
||||
private string GetString(PwEntry fromEntry, string id)
|
||||
{
|
||||
ProtectedString protectedString = fromEntry.Strings.Get(id);
|
||||
if (protectedString == null)
|
||||
return null;
|
||||
return protectedString.ReadString();
|
||||
}
|
||||
|
||||
private Date ConvertTime(DateTime dateTime)
|
||||
{
|
||||
long timestamp = (long)(dateTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1))).TotalMilliseconds;
|
||||
return new Date(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class AdditionalGroupData
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int Flags { get; set; }
|
||||
}
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
#if !EXCLUDE_KEYTRANSFORM
|
||||
using Com.Keepassdroid.Database;
|
||||
using Com.Keepassdroid.Database.Exception;
|
||||
#endif
|
||||
using KeePassLib;
|
||||
using KeePassLib.Cryptography;
|
||||
using KeePassLib.Interfaces;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Security;
|
||||
using Exception = System.Exception;
|
||||
using PwIcon = KeePassLib.PwIcon;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
class KdbDatabaseLoader: IDatabaseLoader
|
||||
{
|
||||
private Dictionary<PwUuid, AdditionalGroupData> _groupData = new Dictionary<PwUuid, AdditionalGroupData>();
|
||||
|
||||
public void PopulateDatabaseFromStream(PwDatabase db, CompositeKey key, Stream s, IStatusLogger slLogger)
|
||||
{
|
||||
#if !EXCLUDE_KEYTRANSFORM
|
||||
var importer = new Com.Keepassdroid.Database.Load.ImporterV3();
|
||||
|
||||
var hashingStream = new HashingStreamEx(s, false, new SHA256Managed());
|
||||
|
||||
string password = "";//no need to distinguish between null and "" because empty passwords are invalid (and null is not allowed)
|
||||
KcpPassword passwordKey = (KcpPassword)key.GetUserKey(typeof(KcpPassword));
|
||||
if (passwordKey != null)
|
||||
{
|
||||
password = passwordKey.Password.ReadString();
|
||||
}
|
||||
|
||||
KcpKeyFile passwordKeyfile = (KcpKeyFile)key.GetUserKey(typeof(KcpKeyFile));
|
||||
MemoryStream keyfileStream = null;
|
||||
if (passwordKeyfile != null)
|
||||
{
|
||||
keyfileStream = new MemoryStream(passwordKeyfile.RawFileData.ReadData());
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var dbv3 = importer.OpenDatabase(hashingStream, password, keyfileStream);
|
||||
|
||||
db.Name = dbv3.Name;
|
||||
db.RootGroup = ConvertGroup(dbv3.RootGroup);
|
||||
|
||||
}
|
||||
catch (Java.IO.FileNotFoundException e)
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
e.Message, e);
|
||||
}
|
||||
catch (Java.Lang.Exception e)
|
||||
{
|
||||
throw new Exception(e.LocalizedMessage ??
|
||||
e.Message ??
|
||||
e.GetType().Name, e);
|
||||
}
|
||||
|
||||
HashOfLastStream = hashingStream.Hash;
|
||||
if (HashOfLastStream == null)
|
||||
throw new Exception("hashing didn't work"); //todo remove
|
||||
#else
|
||||
throw new Exception("Kdb is excluded with Key transform!");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !EXCLUDE_KEYTRANSFORM
|
||||
|
||||
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) && (entryV3.GetBinaryData().Length > 0))
|
||||
{
|
||||
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);
|
||||
|
||||
}
|
||||
#endif
|
||||
public byte[] HashOfLastStream { get; private set; }
|
||||
public bool CanWrite { get { return false; } }
|
||||
public string SuccessMessage { get
|
||||
{
|
||||
return
|
||||
".kdb-support is read-only. Export as .kdbx if you want to modify the database. This app is for use with Keepass 2.x!";
|
||||
} }
|
||||
}
|
||||
|
||||
|
||||
internal class AdditionalGroupData
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int Flags { get; set; }
|
||||
}
|
||||
}
|
@ -6,11 +6,11 @@ using KeePassLib.Serialization;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
public class KdbxDatabaseLoader : IDatabaseLoader
|
||||
public class KdbxDatabaseFormat : IDatabaseFormat
|
||||
{
|
||||
private readonly KdbxFormat _format;
|
||||
|
||||
public KdbxDatabaseLoader(KdbxFormat format)
|
||||
public KdbxDatabaseFormat(KdbxFormat format)
|
||||
{
|
||||
_format = format;
|
||||
}
|
||||
@ -29,5 +29,9 @@ namespace keepass2android
|
||||
public byte[] HashOfLastStream { get; private set; }
|
||||
public bool CanWrite { get { return true; } }
|
||||
public string SuccessMessage { get { return null; } }
|
||||
public void Save(PwDatabase kpDatabase, Stream stream)
|
||||
{
|
||||
kpDatabase.Save(stream, null);
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ namespace keepass2android
|
||||
private readonly string _keyfileOrProvider;
|
||||
private readonly IKp2aApp _app;
|
||||
private readonly bool _rememberKeyfile;
|
||||
IDatabaseLoader _loader;
|
||||
IDatabaseFormat _format;
|
||||
|
||||
public LoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish): base(finish)
|
||||
{
|
||||
@ -68,7 +68,7 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
//ok, try to load the database. Let's start with Kdbx format and retry later if that is the wrong guess:
|
||||
_loader = new KdbxDatabaseLoader(KdbpFile.GetFormatToUse(_ioc));
|
||||
_format = new KdbxDatabaseFormat(KdbpFile.GetFormatToUse(_ioc));
|
||||
TryLoad(databaseStream);
|
||||
}
|
||||
catch (KeyFileException)
|
||||
@ -92,7 +92,7 @@ namespace keepass2android
|
||||
catch (DuplicateUuidsException e)
|
||||
{
|
||||
Kp2aLog.Log("Exception: " + e);
|
||||
Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError)+" " + _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional));
|
||||
Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError)+" " +e.Message+ _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional));
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -118,14 +118,14 @@ namespace keepass2android
|
||||
//now let's go:
|
||||
try
|
||||
{
|
||||
_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _loader);
|
||||
_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format);
|
||||
SaveFileData(_ioc, _keyfileOrProvider);
|
||||
Kp2aLog.Log("LoadDB OK");
|
||||
Finish(true, _loader.SuccessMessage);
|
||||
Finish(true, _format.SuccessMessage);
|
||||
}
|
||||
catch (OldFormatException)
|
||||
{
|
||||
_loader = new KdbDatabaseLoader();
|
||||
_format = new KdbDatabaseFormat();
|
||||
TryLoad(databaseStream);
|
||||
}
|
||||
catch (InvalidCompositeKeyException)
|
||||
|
@ -51,9 +51,9 @@ namespace Kp2aUnitTests
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey, ProgressDialogStatusLogger statusLogger, IDatabaseLoader databaseLoader)
|
||||
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey, ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat)
|
||||
{
|
||||
_db.LoadData(this, ioConnectionInfo, memoryStream, compKey, statusLogger, databaseLoader);
|
||||
_db.LoadData(this, ioConnectionInfo, memoryStream, compKey, statusLogger, databaseFormat);
|
||||
}
|
||||
public Database GetDb()
|
||||
{
|
||||
|
@ -39,7 +39,7 @@ namespace TwofishCipher
|
||||
|
||||
private PwUuid m_uuidCipher;
|
||||
|
||||
private static readonly byte[] TwofishCipherUuidBytes = new byte[]{
|
||||
public static readonly byte[] TwofishCipherUuidBytes = new byte[]{
|
||||
0xAD, 0x68, 0xF2, 0x9F, 0x57, 0x6F, 0x4B, 0xB9,
|
||||
0xA3, 0x6A, 0xD4, 0x7A, 0xF9, 0x65, 0x34, 0x6C
|
||||
};
|
||||
|
Binary file not shown.
Binary file not shown.
@ -146,9 +146,9 @@ namespace keepass2android
|
||||
|
||||
|
||||
|
||||
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compositeKey, ProgressDialogStatusLogger statusLogger, IDatabaseLoader databaseLoader)
|
||||
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compositeKey, ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat)
|
||||
{
|
||||
_db.LoadData(this, ioConnectionInfo, memoryStream, compositeKey, statusLogger, databaseLoader);
|
||||
_db.LoadData(this, ioConnectionInfo, memoryStream, compositeKey, statusLogger, databaseFormat);
|
||||
|
||||
UpdateOngoingNotification();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user