Implemented loading of Keepass 1 (kdb) files. First test passed!

This commit is contained in:
Philipp Crocoll 2014-01-26 03:51:55 -08:00
parent 12dbe597ce
commit 8bbd18d3f8
15 changed files with 210 additions and 74 deletions

View File

@ -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; }
} }
} }

View File

@ -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.

View File

@ -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);

View File

@ -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; }
} }
} }

View File

@ -27,5 +27,6 @@ namespace keepass2android
} }
public byte[] HashOfLastStream { get; private set; } public byte[] HashOfLastStream { get; private set; }
public bool CanWrite { get { return true; } }
} }
} }

View File

@ -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;
} }

View File

@ -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;
@ -13,6 +14,14 @@ namespace Kp2aUnitTests
internal partial class TestLoadDb : TestBase internal partial class TestLoadDb : TestBase
{ {
private void RunLoadTest(string filenameWithoutDir, string password, string keyfile) private void RunLoadTest(string filenameWithoutDir, string password, string 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); Android.Util.Log.Debug("KP2ATest", "Starting for " + filenameWithoutDir + " with " + password + "/" + keyfile);
@ -37,25 +46,28 @@ namespace Kp2aUnitTests
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]

View File

@ -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;

View File

@ -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;

View File

@ -1,5 +1,2 @@
package com.keepassdroid.database; package com.keepassdroid.database;
public abstract class PwGroupId {
}

View File

@ -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;

View File

@ -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();

View File

@ -1,12 +0,0 @@
package com.keepassdroid.database;
public abstract class PwIcon {
public boolean isMetaStreamIcon() {
return false;
}
public void writeBytes() {
}
}

View File

@ -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;
} }

View File

@ -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 );