Implemented loading of Keepass 1 (kdb) files. First test passed!
This commit is contained in:
		@@ -9,5 +9,7 @@ namespace KeePassLib
 | 
				
			|||||||
		void PopulateDatabaseFromStream(PwDatabase db, CompositeKey key, Stream s, IStatusLogger slLogger);
 | 
							void PopulateDatabaseFromStream(PwDatabase db, CompositeKey key, Stream s, IStatusLogger slLogger);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		byte[] HashOfLastStream { get; }
 | 
							byte[] HashOfLastStream { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bool CanWrite { get;  }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -20,7 +20,7 @@
 | 
				
			|||||||
    <DebugType>full</DebugType>
 | 
					    <DebugType>full</DebugType>
 | 
				
			||||||
    <Optimize>false</Optimize>
 | 
					    <Optimize>false</Optimize>
 | 
				
			||||||
    <OutputPath>bin\Debug\</OutputPath>
 | 
					    <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>
 | 
					    <ErrorReport>prompt</ErrorReport>
 | 
				
			||||||
    <WarningLevel>4</WarningLevel>
 | 
					    <WarningLevel>4</WarningLevel>
 | 
				
			||||||
  </PropertyGroup>
 | 
					  </PropertyGroup>
 | 
				
			||||||
@@ -55,6 +55,9 @@
 | 
				
			|||||||
    <Compile Include="database\CheckDatabaseForChanges.cs" />
 | 
					    <Compile Include="database\CheckDatabaseForChanges.cs" />
 | 
				
			||||||
    <Compile Include="database\edit\EditGroup.cs" />
 | 
					    <Compile Include="database\edit\EditGroup.cs" />
 | 
				
			||||||
    <Compile Include="database\edit\MoveElement.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="database\SynchronizeCachedDatabase.cs" />
 | 
				
			||||||
    <Compile Include="Io\BuiltInFileStorage.cs" />
 | 
					    <Compile Include="Io\BuiltInFileStorage.cs" />
 | 
				
			||||||
    <Compile Include="Io\CachingFileStorage.cs" />
 | 
					    <Compile Include="Io\CachingFileStorage.cs" />
 | 
				
			||||||
@@ -112,6 +115,10 @@
 | 
				
			|||||||
      <Project>{545b4a6b-8bba-4fbe-92fc-4ac060122a54}</Project>
 | 
					      <Project>{545b4a6b-8bba-4fbe-92fc-4ac060122a54}</Project>
 | 
				
			||||||
      <Name>KeePassLib2Android</Name>
 | 
					      <Name>KeePassLib2Android</Name>
 | 
				
			||||||
    </ProjectReference>
 | 
					    </ProjectReference>
 | 
				
			||||||
 | 
					    <ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
 | 
				
			||||||
 | 
					      <Project>{70d3844a-d9fa-4a64-b205-a84c6a822196}</Project>
 | 
				
			||||||
 | 
					      <Name>KP2AKdbLibraryBinding</Name>
 | 
				
			||||||
 | 
					    </ProjectReference>
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
  <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
 | 
					  <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. 
 | 
					  <!-- 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;
 | 
								Loaded = true;
 | 
				
			||||||
			KpDatabase = pwDatabase;
 | 
								KpDatabase = pwDatabase;
 | 
				
			||||||
			SearchHelper = new SearchDbHelper(app);
 | 
								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)
 | 
							protected  virtual void PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseLoader databaseLoader)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
 | 
								IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,23 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
using System.Security.Cryptography;
 | 
					using System.Security.Cryptography;
 | 
				
			||||||
using Android.Content;
 | 
					 | 
				
			||||||
using Com.Keepassdroid.Database;
 | 
					using Com.Keepassdroid.Database;
 | 
				
			||||||
using Com.Keepassdroid.Database.Exception;
 | 
					using Com.Keepassdroid.Database.Exception;
 | 
				
			||||||
using Java.Lang;
 | 
					 | 
				
			||||||
using KeePassLib;
 | 
					using KeePassLib;
 | 
				
			||||||
using KeePassLib.Cryptography;
 | 
					using KeePassLib.Cryptography;
 | 
				
			||||||
using KeePassLib.Cryptography.Cipher;
 | 
					 | 
				
			||||||
using KeePassLib.Interfaces;
 | 
					using KeePassLib.Interfaces;
 | 
				
			||||||
using KeePassLib.Keys;
 | 
					using KeePassLib.Keys;
 | 
				
			||||||
 | 
					using KeePassLib.Security;
 | 
				
			||||||
using Exception = System.Exception;
 | 
					using Exception = System.Exception;
 | 
				
			||||||
 | 
					using PwIcon = KeePassLib.PwIcon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace keepass2android
 | 
					namespace keepass2android
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	class KdbDatabaseLoader: IDatabaseLoader
 | 
						class KdbDatabaseLoader: IDatabaseLoader
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private Context _ctx;
 | 
							private Dictionary<PwUuid, AdditionalGroupData> _groupData = new Dictionary<PwUuid, AdditionalGroupData>();
 | 
				
			||||||
 | 
					 | 
				
			||||||
		public KdbDatabaseLoader(Context ctx)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			_ctx = ctx;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public void PopulateDatabaseFromStream(PwDatabase db, CompositeKey key, Stream s, IStatusLogger slLogger)
 | 
							public void PopulateDatabaseFromStream(PwDatabase db, CompositeKey key, Stream s, IStatusLogger slLogger)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -49,6 +45,8 @@ namespace keepass2android
 | 
				
			|||||||
				var dbv3 = importer.OpenDatabase(hashingStream, password, keyfile);
 | 
									var dbv3 = importer.OpenDatabase(hashingStream, password, keyfile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				db.Name = dbv3.Name;
 | 
									db.Name = dbv3.Name;
 | 
				
			||||||
 | 
									db.RootGroup = ConvertGroup(dbv3.RootGroup);
 | 
				
			||||||
 | 
									
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			catch (InvalidPasswordException e) {
 | 
								catch (InvalidPasswordException e) {
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
@@ -71,6 +69,98 @@ namespace keepass2android
 | 
				
			|||||||
				throw new Exception("hashing didn't work"); //todo remove
 | 
									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 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 byte[] HashOfLastStream { get; private set; }
 | 
				
			||||||
 | 
							public bool CanWrite { get { return true; } }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -19,7 +19,6 @@ using System;
 | 
				
			|||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Android.App;
 | 
					 | 
				
			||||||
using KeePassLib;
 | 
					using KeePassLib;
 | 
				
			||||||
using KeePassLib.Keys;
 | 
					using KeePassLib.Keys;
 | 
				
			||||||
using KeePassLib.Serialization;
 | 
					using KeePassLib.Serialization;
 | 
				
			||||||
@@ -33,6 +32,7 @@ namespace keepass2android
 | 
				
			|||||||
		private readonly string _keyfileOrProvider;
 | 
							private readonly string _keyfileOrProvider;
 | 
				
			||||||
		private readonly IKp2aApp _app;
 | 
							private readonly IKp2aApp _app;
 | 
				
			||||||
		private readonly bool _rememberKeyfile;
 | 
							private readonly bool _rememberKeyfile;
 | 
				
			||||||
 | 
							IDatabaseLoader _loader;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		public LoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish): base(finish)
 | 
							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:
 | 
									//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)); 
 | 
									_loader = new KdbxDatabaseLoader(KdbpFile.GetFormatToUse(_ioc));
 | 
				
			||||||
				TryLoad(databaseStream, loader);
 | 
									TryLoad(databaseStream);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			catch (KeyFileException)
 | 
								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
 | 
								//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.
 | 
								//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:
 | 
								//now let's go:
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, loader);
 | 
									_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _loader);
 | 
				
			||||||
				SaveFileData(_ioc, _keyfileOrProvider);
 | 
									SaveFileData(_ioc, _keyfileOrProvider);
 | 
				
			||||||
				Kp2aLog.Log("LoadDB OK");
 | 
									Kp2aLog.Log("LoadDB OK");
 | 
				
			||||||
				Finish(true);
 | 
									Finish(true);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			catch (OldFormatException)
 | 
								catch (OldFormatException)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				TryLoad(databaseStream, new KdbDatabaseLoader(Application.Context));
 | 
									_loader = new KdbDatabaseLoader();
 | 
				
			||||||
 | 
									TryLoad(databaseStream);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			catch (InvalidCompositeKeyException)
 | 
								catch (InvalidCompositeKeyException)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
@@ -131,7 +132,7 @@ namespace keepass2android
 | 
				
			|||||||
					//retry without password:
 | 
										//retry without password:
 | 
				
			||||||
					_compositeKey.RemoveUserKey(passwordKey);
 | 
										_compositeKey.RemoveUserKey(passwordKey);
 | 
				
			||||||
					//retry:
 | 
										//retry:
 | 
				
			||||||
					TryLoad(databaseStream, loader);
 | 
										TryLoad(databaseStream);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				else throw;
 | 
									else throw;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using Android.App;
 | 
					using Android.App;
 | 
				
			||||||
 | 
					using KeePassLib;
 | 
				
			||||||
using KeePassLib.Serialization;
 | 
					using KeePassLib.Serialization;
 | 
				
			||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
 | 
					using Microsoft.VisualStudio.TestTools.UnitTesting;
 | 
				
			||||||
using keepass2android;
 | 
					using keepass2android;
 | 
				
			||||||
@@ -14,7 +15,15 @@ namespace Kp2aUnitTests
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		private void RunLoadTest(string filenameWithoutDir, string password, string keyfile)
 | 
							private void RunLoadTest(string filenameWithoutDir, string password, string keyfile)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Android.Util.Log.Debug("KP2ATest", "Starting for " + filenameWithoutDir+" with " + password+"/"+keyfile);
 | 
								var app = PerformLoad(filenameWithoutDir, password, keyfile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Assert.AreEqual(6,app.GetDb().KpDatabase.RootGroup.Groups.Count());
 | 
				
			||||||
 | 
								Assert.AreEqual(2,app.GetDb().KpDatabase.RootGroup.Entries.Count());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private IKp2aApp PerformLoad(string filenameWithoutDir, string password, string keyfile)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Android.Util.Log.Debug("KP2ATest", "Starting for " + filenameWithoutDir + " with " + password + "/" + keyfile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			IKp2aApp app = new TestKp2aApp();
 | 
								IKp2aApp app = new TestKp2aApp();
 | 
				
			||||||
			app.CreateNewDatabase();
 | 
								app.CreateNewDatabase();
 | 
				
			||||||
@@ -22,40 +31,43 @@ namespace Kp2aUnitTests
 | 
				
			|||||||
			var key = CreateKey(password, keyfile);
 | 
								var key = CreateKey(password, keyfile);
 | 
				
			||||||
			string loadErrorMessage = "";
 | 
								string loadErrorMessage = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			LoadDb task = new LoadDb(app, new IOConnectionInfo { Path = TestDbDirectory+filenameWithoutDir }, null,
 | 
								LoadDb task = new LoadDb(app, new IOConnectionInfo {Path = TestDbDirectory + filenameWithoutDir}, null,
 | 
				
			||||||
				key, keyfile, new ActionOnFinish((success, message) =>
 | 
								                         key, keyfile, new ActionOnFinish((success, message) =>
 | 
				
			||||||
					{
 | 
									                         {
 | 
				
			||||||
						loadErrorMessage = message;
 | 
										                         loadErrorMessage = message;
 | 
				
			||||||
						if (!success)
 | 
										                         if (!success)
 | 
				
			||||||
							Android.Util.Log.Debug("KP2ATest", "error loading db: " + message);
 | 
											                         Android.Util.Log.Debug("KP2ATest", "error loading db: " + message);
 | 
				
			||||||
						loadSuccesful = success; 		
 | 
										                         loadSuccesful = success;
 | 
				
			||||||
					})
 | 
									                         })
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
			ProgressTask pt = new ProgressTask(app, Application.Context, task);
 | 
								ProgressTask pt = new ProgressTask(app, Application.Context, task);
 | 
				
			||||||
			Android.Util.Log.Debug("KP2ATest", "Running ProgressTask");
 | 
								Android.Util.Log.Debug("KP2ATest", "Running ProgressTask");
 | 
				
			||||||
			pt.Run();
 | 
								pt.Run();
 | 
				
			||||||
			pt.JoinWorkerThread();
 | 
								pt.JoinWorkerThread();
 | 
				
			||||||
			Android.Util.Log.Debug("KP2ATest", "PT.run finished");
 | 
								Android.Util.Log.Debug("KP2ATest", "PT.run finished");
 | 
				
			||||||
			Assert.IsTrue(loadSuccesful, "didn't succesfully load database :-( "+loadErrorMessage);
 | 
								Assert.IsTrue(loadSuccesful, "didn't succesfully load database :-( " + loadErrorMessage);
 | 
				
			||||||
			
 | 
								return app;
 | 
				
			||||||
			Assert.AreEqual(6,app.GetDb().KpDatabase.RootGroup.Groups.Count());
 | 
					 | 
				
			||||||
			Assert.AreEqual(2,app.GetDb().KpDatabase.RootGroup.Entries.Count());
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		[TestMethod]
 | 
							[TestMethod]
 | 
				
			||||||
		public void TestLoadWithPasswordOnly()
 | 
							public void TestLoadWithPasswordOnly()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			RunLoadTest("passwordonly.kdbx", DefaultPassword, "");
 | 
								RunLoadTest("passwordonly.kdbx", DefaultPassword, "");
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[TestMethod]
 | 
							[TestMethod]
 | 
				
			||||||
		public void TestLoadKdb1()
 | 
							public void TestLoadKdb1()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			RunLoadTest("test1.kdb", "12345", "");
 | 
								var app = PerformLoad("keepass.kdb", "test", "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								//contents of the kdb file are a little different because the root group cannot have entries
 | 
				
			||||||
 | 
								Assert.AreEqual(6, app.GetDb().KpDatabase.RootGroup.Groups.Count());
 | 
				
			||||||
 | 
								PwGroup generalGroup = app.GetDb().KpDatabase.RootGroup.Groups.Single(g => g.Name == "General");
 | 
				
			||||||
 | 
								Assert.AreEqual(2, generalGroup.Entries.Count());
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[TestMethod]
 | 
							[TestMethod]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -310,7 +310,7 @@ public class PwDatabaseV3 {
 | 
				
			|||||||
	 *            ID number to check for
 | 
						 *            ID number to check for
 | 
				
			||||||
	 * @return True if the ID is used, false otherwise
 | 
						 * @return True if the ID is used, false otherwise
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected boolean isGroupIdUsed(PwGroupId id) {
 | 
						protected boolean isGroupIdUsed(PwGroupIdV3 id) {
 | 
				
			||||||
		List<PwGroupV3> groups = getGroups();
 | 
							List<PwGroupV3> groups = getGroups();
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		for (int i = 0; i < groups.size(); i++) {
 | 
							for (int i = 0; i < groups.size(); i++) {
 | 
				
			||||||
@@ -382,9 +382,9 @@ public class PwDatabaseV3 {
 | 
				
			|||||||
		groups = grp;
 | 
							groups = grp;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public List<PwGroupV3> getGrpRoots() {
 | 
						public ArrayList<PwGroupV3> getGrpRoots() {
 | 
				
			||||||
		int target = 0;
 | 
							int target = 0;
 | 
				
			||||||
		List<PwGroupV3> kids = new ArrayList<PwGroupV3>();
 | 
							ArrayList<PwGroupV3> kids = new ArrayList<PwGroupV3>();
 | 
				
			||||||
		for (int i = 0; i < groups.size(); i++) {
 | 
							for (int i = 0; i < groups.size(); i++) {
 | 
				
			||||||
			PwGroupV3 grp = (PwGroupV3) groups.get(i);
 | 
								PwGroupV3 grp = (PwGroupV3) groups.get(i);
 | 
				
			||||||
			if (grp.level == target)
 | 
								if (grp.level == target)
 | 
				
			||||||
@@ -404,10 +404,10 @@ public class PwDatabaseV3 {
 | 
				
			|||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public List<PwGroupV3> getGrpChildren(PwGroupV3 parent) {
 | 
						public ArrayList<PwGroupV3> getGrpChildren(PwGroupV3 parent) {
 | 
				
			||||||
		int idx = groups.indexOf(parent);
 | 
							int idx = groups.indexOf(parent);
 | 
				
			||||||
		int target = parent.level + 1;
 | 
							int target = parent.level + 1;
 | 
				
			||||||
		List<PwGroupV3> kids = new ArrayList<PwGroupV3>();
 | 
							ArrayList<PwGroupV3> kids = new ArrayList<PwGroupV3>();
 | 
				
			||||||
		while (++idx < groups.size()) {
 | 
							while (++idx < groups.size()) {
 | 
				
			||||||
			PwGroupV3 grp = (PwGroupV3) groups.get(idx);
 | 
								PwGroupV3 grp = (PwGroupV3) groups.get(idx);
 | 
				
			||||||
			if (grp.level < target)
 | 
								if (grp.level < target)
 | 
				
			||||||
@@ -418,8 +418,8 @@ public class PwDatabaseV3 {
 | 
				
			|||||||
		return kids;
 | 
							return kids;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public List<PwEntryV3> getEntries(PwGroupV3 parent) {
 | 
						public ArrayList<PwEntryV3> getEntries(PwGroupV3 parent) {
 | 
				
			||||||
		List<PwEntryV3> kids = new ArrayList<PwEntryV3>();
 | 
							ArrayList<PwEntryV3> kids = new ArrayList<PwEntryV3>();
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * for( Iterator i = entries.iterator(); i.hasNext(); ) { PwEntryV3 ent
 | 
							 * for( Iterator i = entries.iterator(); i.hasNext(); ) { PwEntryV3 ent
 | 
				
			||||||
		 * = (PwEntryV3)i.next(); if( ent.groupId == parent.groupId ) kids.add(
 | 
							 * = (PwEntryV3)i.next(); if( ent.groupId == parent.groupId ) kids.add(
 | 
				
			||||||
@@ -443,7 +443,7 @@ public class PwDatabaseV3 {
 | 
				
			|||||||
			PwGroupV3 root = new PwGroupV3();
 | 
								PwGroupV3 root = new PwGroupV3();
 | 
				
			||||||
			rootGroup = root;
 | 
								rootGroup = root;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			List<PwGroupV3> rootChildGroups = getGrpRoots();
 | 
								ArrayList<PwGroupV3> rootChildGroups = getGrpRoots();
 | 
				
			||||||
			root.setGroups(rootChildGroups);
 | 
								root.setGroups(rootChildGroups);
 | 
				
			||||||
			root.childEntries = new ArrayList<PwEntryV3>();
 | 
								root.childEntries = new ArrayList<PwEntryV3>();
 | 
				
			||||||
			root.level = -1;
 | 
								root.level = -1;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,7 +90,7 @@ protected static final String PMS_TAN_ENTRY = "<TAN>";
 | 
				
			|||||||
	
 | 
						
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public PwIcon getIcon() {
 | 
						public PwIconStandard getIcon() {
 | 
				
			||||||
		return icon;
 | 
							return icon;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -148,7 +148,7 @@ protected static final String PMS_TAN_ENTRY = "<TAN>";
 | 
				
			|||||||
	public int              groupId;
 | 
						public int              groupId;
 | 
				
			||||||
	public 	String 			username;
 | 
						public 	String 			username;
 | 
				
			||||||
	private byte[]          password;
 | 
						private byte[]          password;
 | 
				
			||||||
	private byte[]          uuid;
 | 
						public byte[]          uuid;
 | 
				
			||||||
	public String title;
 | 
						public String title;
 | 
				
			||||||
	public String url;
 | 
						public String url;
 | 
				
			||||||
	public String additional;
 | 
						public String additional;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,2 @@
 | 
				
			|||||||
package com.keepassdroid.database;
 | 
					package com.keepassdroid.database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public abstract class PwGroupId {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
package com.keepassdroid.database;
 | 
					package com.keepassdroid.database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class PwGroupIdV3 extends PwGroupId {
 | 
					public class PwGroupIdV3 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private int id;
 | 
						private int id;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,17 +46,29 @@ public class PwGroupV3 {
 | 
				
			|||||||
	public PwGroupV3() {
 | 
						public PwGroupV3() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public List<PwGroupV3> childGroups = new ArrayList<PwGroupV3>();
 | 
						//Mono for Android binding somehow doesn't return List<PwGroupV3> but only IList and casting of the contents doesn't work.
 | 
				
			||||||
 | 
						//therefore, getGroupAt() must be used in C#, see below
 | 
				
			||||||
 | 
						public ArrayList<PwGroupV3> childGroups = new ArrayList<PwGroupV3>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public List<PwEntryV3> childEntries = new ArrayList<PwEntryV3>();
 | 
						public ArrayList<PwEntryV3> childEntries = new ArrayList<PwEntryV3>();
 | 
				
			||||||
	public String name = "";
 | 
						public String name = "";
 | 
				
			||||||
	public PwIconStandard icon;
 | 
						public PwIconStandard icon;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public PwIcon getIcon() {
 | 
						 
 | 
				
			||||||
 | 
						public PwGroupV3 getGroupAt(int i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return childGroups.get(i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						public PwEntryV3 getEntryAt(int i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return childEntries.get(i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public PwIconStandard getIcon() {
 | 
				
			||||||
		return icon;
 | 
							return icon;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void super_initNewGroup(String nm, PwGroupId newId) {
 | 
						public void super_initNewGroup(String nm, PwGroupIdV3 newId) {
 | 
				
			||||||
		setId(newId);
 | 
							setId(newId);
 | 
				
			||||||
		name = nm;
 | 
							name = nm;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -113,7 +125,7 @@ public class PwGroupV3 {
 | 
				
			|||||||
	/** Used by KeePass internally, don't use */
 | 
						/** Used by KeePass internally, don't use */
 | 
				
			||||||
	public int flags;
 | 
						public int flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void setGroups(List<PwGroupV3> groups) {
 | 
						public void setGroups(ArrayList<PwGroupV3> groups) {
 | 
				
			||||||
		childGroups = groups;
 | 
							childGroups = groups;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -121,11 +133,11 @@ public class PwGroupV3 {
 | 
				
			|||||||
		return parent;
 | 
							return parent;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public PwGroupId getId() {
 | 
						public PwGroupIdV3 getId() {
 | 
				
			||||||
		return new PwGroupIdV3(groupId);
 | 
							return new PwGroupIdV3(groupId);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void setId(PwGroupId id) {
 | 
						public void setId(PwGroupIdV3 id) {
 | 
				
			||||||
		PwGroupIdV3 id3 = (PwGroupIdV3) id;
 | 
							PwGroupIdV3 id3 = (PwGroupIdV3) id;
 | 
				
			||||||
		groupId = id3.getId();
 | 
							groupId = id3.getId();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -144,7 +156,7 @@ public class PwGroupV3 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void initNewGroup(String nm, PwGroupId newId) {
 | 
						public void initNewGroup(String nm, PwGroupIdV3 newId) {
 | 
				
			||||||
		super_initNewGroup(nm, newId);
 | 
							super_initNewGroup(nm, newId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Date now = Calendar.getInstance().getTime();
 | 
							Date now = Calendar.getInstance().getTime();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +0,0 @@
 | 
				
			|||||||
package com.keepassdroid.database;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public abstract class PwIcon {
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public boolean isMetaStreamIcon() {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void writeBytes() {
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -19,8 +19,8 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
package com.keepassdroid.database;
 | 
					package com.keepassdroid.database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class PwIconStandard extends PwIcon {
 | 
					public class PwIconStandard  {
 | 
				
			||||||
	public final int iconId;
 | 
						public /*final*/ int iconId;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public static PwIconStandard FIRST = new PwIconStandard(1);
 | 
						public static PwIconStandard FIRST = new PwIconStandard(1);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
@@ -30,7 +30,6 @@ public class PwIconStandard extends PwIcon {
 | 
				
			|||||||
		this.iconId = iconId;
 | 
							this.iconId = iconId;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public boolean isMetaStreamIcon() {
 | 
						public boolean isMetaStreamIcon() {
 | 
				
			||||||
		return iconId == 0;
 | 
							return iconId == 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,6 +45,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.keepassdroid.database.load;
 | 
					package com.keepassdroid.database.load;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayOutputStream;
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
import java.io.InputStream;
 | 
					import java.io.InputStream;
 | 
				
			||||||
import java.io.UnsupportedEncodingException;
 | 
					import java.io.UnsupportedEncodingException;
 | 
				
			||||||
@@ -135,11 +136,30 @@ public class ImporterV3  {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Load entire file, most of it's encrypted.
 | 
							// Load entire file, most of it's encrypted.
 | 
				
			||||||
		int fileSize = inStream.available();
 | 
							
 | 
				
			||||||
		byte[] filebuf = new byte[fileSize + 16]; // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer
 | 
							ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 | 
				
			||||||
		inStream.read(filebuf, 0, fileSize);
 | 
					
 | 
				
			||||||
 | 
							int nRead;
 | 
				
			||||||
 | 
							byte[] data = new byte[16384];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while ((nRead = inStream.read(data, 0, data.length)) != -1) {
 | 
				
			||||||
 | 
							  buffer.write(data, 0, nRead);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							buffer.flush();
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							int fileSize = buffer.size();
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
						 // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer
 | 
				
			||||||
 | 
							for (int i=0;i<16;i++)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								buffer.write(0);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		inStream.close();
 | 
							inStream.close();
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
 | 
							byte[] filebuf = buffer.toByteArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Parse header (unencrypted)
 | 
							// Parse header (unencrypted)
 | 
				
			||||||
		if( fileSize < PwDbHeaderV3.BUF_SIZE )
 | 
							if( fileSize < PwDbHeaderV3.BUF_SIZE )
 | 
				
			||||||
			throw new IOException( "File too short for header: "+fileSize+"<"+PwDbHeaderV3.BUF_SIZE  );
 | 
								throw new IOException( "File too short for header: "+fileSize+"<"+PwDbHeaderV3.BUF_SIZE  );
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user