diff --git a/app/src/androidTest/java/com/keepassdroid/tests/TestUtil.java b/app/src/androidTest/java/com/keepassdroid/tests/TestUtil.java index e54344059..57febad83 100644 --- a/app/src/androidTest/java/com/keepassdroid/tests/TestUtil.java +++ b/app/src/androidTest/java/com/keepassdroid/tests/TestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2010 Brian Pellin. + * Copyright 2010-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -19,11 +19,16 @@ */ package com.keepassdroid.tests; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import android.content.Context; import android.content.res.AssetManager; +import android.net.Uri; + +import com.keepassdroid.utils.EmptyUtils; +import com.keepassdroid.utils.UriUtil; public class TestUtil { @@ -46,4 +51,13 @@ public class TestUtil { } + public static InputStream getKeyFileInputStream(Context ctx, String keyfile) throws FileNotFoundException { + InputStream keyIs = null; + if (!EmptyUtils.isNullOrEmpty(keyfile)) { + Uri uri = UriUtil.parseDefaultFile(keyfile); + keyIs = UriUtil.getUriInputStream(ctx, uri); + } + + return keyIs; + } } diff --git a/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb3.java b/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb3.java index 0fd2f03e7..10e3c6bb3 100644 --- a/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb3.java +++ b/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb3.java @@ -1,5 +1,5 @@ /* - * Copyright 2010 Brian Pellin. + * Copyright 2010-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -21,11 +21,13 @@ package com.keepassdroid.tests.database; import android.content.Context; import android.content.res.AssetManager; +import android.net.Uri; import android.os.Environment; import android.test.AndroidTestCase; import com.keepassdroid.database.load.ImporterV3; import com.keepassdroid.tests.TestUtil; +import com.keepassdroid.utils.UriUtil; import java.io.InputStream; import java.io.File; @@ -44,7 +46,7 @@ public class Kdb3 extends AndroidTestCase { InputStream is = am.open(dbAsset, AssetManager.ACCESS_STREAMING); ImporterV3 importer = new ImporterV3(); - importer.openDatabase(is, password, keyPath); + importer.openDatabase(is, password, TestUtil.getKeyFileInputStream(ctx, keyPath)); is.close(); } diff --git a/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb3Twofish.java b/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb3Twofish.java index 0a837ccdf..fbce258ce 100644 --- a/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb3Twofish.java +++ b/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb3Twofish.java @@ -1,5 +1,5 @@ /* - * Copyright 2010 Brian Pellin. + * Copyright 2010-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -38,7 +38,7 @@ public class Kdb3Twofish extends AndroidTestCase { ImporterV3 importer = new ImporterV3(); - PwDatabaseV3 db = importer.openDatabase(is, "12345", ""); + PwDatabaseV3 db = importer.openDatabase(is, "12345", null); assertTrue(db.algorithm == PwEncryptionAlgorithm.Twofish); diff --git a/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb4.java b/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb4.java index 926a2329e..259a9a40f 100644 --- a/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb4.java +++ b/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb4.java @@ -62,7 +62,7 @@ public class Kdb4 extends AndroidTestCase { InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); ImporterV4 importer = new ImporterV4(); - importer.openDatabase(is, "12345", ""); + importer.openDatabase(is, "12345", null); is.close(); @@ -76,7 +76,7 @@ public class Kdb4 extends AndroidTestCase { InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); ImporterV4 importer = new ImporterV4(); - PwDatabaseV4 db = importer.openDatabase(is, "12345", ""); + PwDatabaseV4 db = importer.openDatabase(is, "12345", null); is.close(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); @@ -91,7 +91,7 @@ public class Kdb4 extends AndroidTestCase { InputStream bis = new ByteArrayInputStream(data); bis = new CopyInputStream(bis, fos); importer = new ImporterV4(); - db = importer.openDatabase(bis, "12345", ""); + db = importer.openDatabase(bis, "12345", null); bis.close(); fos.close(); @@ -112,7 +112,7 @@ public class Kdb4 extends AndroidTestCase { InputStream is = am.open("keyfile.kdbx", AssetManager.ACCESS_STREAMING); ImporterV4 importer = new ImporterV4(); - importer.openDatabase(is, "12345", "/sdcard/key"); + importer.openDatabase(is, "12345", TestUtil.getKeyFileInputStream(ctx,"/sdcard/key")); is.close(); @@ -125,7 +125,7 @@ public class Kdb4 extends AndroidTestCase { InputStream is = am.open("key-only.kdbx", AssetManager.ACCESS_STREAMING); ImporterV4 importer = new ImporterV4(); - importer.openDatabase(is, "", "/sdcard/key"); + importer.openDatabase(is, "", TestUtil.getKeyFileInputStream(ctx, "/sdcard/key")); is.close(); @@ -139,7 +139,7 @@ public class Kdb4 extends AndroidTestCase { InputStream is = am.open("no-encrypt.kdbx", AssetManager.ACCESS_STREAMING); ImporterV4 importer = new ImporterV4(); - importer.openDatabase(is, "12345", ""); + importer.openDatabase(is, "12345", null); is.close(); diff --git a/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb4Header.java b/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb4Header.java index 6eb5e873c..da50d8b55 100644 --- a/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb4Header.java +++ b/app/src/androidTest/java/com/keepassdroid/tests/database/Kdb4Header.java @@ -1,5 +1,5 @@ /* - * Copyright 2010 Brian Pellin. + * Copyright 2010-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -38,7 +38,7 @@ public class Kdb4Header extends AndroidTestCase { ImporterV4 importer = new ImporterV4(); - PwDatabaseV4 db = importer.openDatabase(is, "12345", ""); + PwDatabaseV4 db = importer.openDatabase(is, "12345", null); assertEquals(6000, db.numKeyEncRounds); diff --git a/app/src/androidTest/java/com/keepassdroid/tests/database/SprEngineTest.java b/app/src/androidTest/java/com/keepassdroid/tests/database/SprEngineTest.java index 2b9baab00..e7696922e 100644 --- a/app/src/androidTest/java/com/keepassdroid/tests/database/SprEngineTest.java +++ b/app/src/androidTest/java/com/keepassdroid/tests/database/SprEngineTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Brian Pellin. + * Copyright 2014-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -47,7 +47,7 @@ public class SprEngineTest extends AndroidTestCase { InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); ImporterV4 importer = new ImporterV4(); - db = importer.openDatabase(is, "12345", ""); + db = importer.openDatabase(is, "12345", null); is.close(); diff --git a/app/src/androidTest/java/com/keepassdroid/tests/database/TestData.java b/app/src/androidTest/java/com/keepassdroid/tests/database/TestData.java index eb8609915..88cf2219f 100644 --- a/app/src/androidTest/java/com/keepassdroid/tests/database/TestData.java +++ b/app/src/androidTest/java/com/keepassdroid/tests/database/TestData.java @@ -28,6 +28,9 @@ import android.net.Uri; import com.keepassdroid.Database; import com.keepassdroid.database.PwDatabaseV3Debug; import com.keepassdroid.database.load.Importer; +import com.keepassdroid.tests.TestUtil; +import com.keepassdroid.utils.EmptyUtils; +import com.keepassdroid.utils.UriUtil; public class TestData { private static final String TEST1_KEYFILE = ""; @@ -54,7 +57,10 @@ public class TestData { InputStream is = am.open(asset, AssetManager.ACCESS_STREAMING); Database Db = new Database(); - Db.LoadData(ctx, is, password, keyfile, Importer.DEBUG); + + InputStream keyIs = TestUtil.getKeyFileInputStream(ctx, keyfile); + + Db.LoadData(ctx, is, password, keyIs, Importer.DEBUG); Uri.Builder b = new Uri.Builder(); Db.mUri = b.scheme("file").path(filename).build(); diff --git a/app/src/main/java/com/keepassdroid/Database.java b/app/src/main/java/com/keepassdroid/Database.java index d53c9c53f..761abf1ba 100644 --- a/app/src/main/java/com/keepassdroid/Database.java +++ b/app/src/main/java/com/keepassdroid/Database.java @@ -31,6 +31,7 @@ import java.io.SyncFailedException; import java.util.HashSet; import java.util.Set; +import android.content.ContentResolver; import android.content.Context; import android.net.Uri; @@ -44,6 +45,7 @@ import com.keepassdroid.database.load.ImporterFactory; import com.keepassdroid.database.save.PwDbOutput; import com.keepassdroid.icons.DrawableFactory; import com.keepassdroid.search.SearchDbHelper; +import com.keepassdroid.utils.UriUtil; /** * @author bpellin @@ -68,19 +70,19 @@ public class Database { loaded = true; } - public void LoadData(Context ctx, InputStream is, String password, String keyfile) throws IOException, InvalidDBException { - LoadData(ctx, is, password, keyfile, new UpdateStatus(), !Importer.DEBUG); + public void LoadData(Context ctx, InputStream is, String password, InputStream keyInputStream) throws IOException, InvalidDBException { + LoadData(ctx, is, password, keyInputStream, new UpdateStatus(), !Importer.DEBUG); } - public void LoadData(Context ctx, Uri uri, String password, String keyfile) throws IOException, FileNotFoundException, InvalidDBException { + public void LoadData(Context ctx, Uri uri, String password, Uri keyfile) throws IOException, FileNotFoundException, InvalidDBException { LoadData(ctx, uri, password, keyfile, new UpdateStatus(), !Importer.DEBUG); } - public void LoadData(Context ctx, Uri uri, String password, String keyfile, UpdateStatus status) throws IOException, FileNotFoundException, InvalidDBException { + public void LoadData(Context ctx, Uri uri, String password, Uri keyfile, UpdateStatus status) throws IOException, FileNotFoundException, InvalidDBException { LoadData(ctx, uri, password, keyfile, status, !Importer.DEBUG); } - public void LoadData(Context ctx, Uri uri, String password, String keyfile, UpdateStatus status, boolean debug) throws IOException, FileNotFoundException, InvalidDBException { + public void LoadData(Context ctx, Uri uri, String password, Uri keyfile, UpdateStatus status, boolean debug) throws IOException, FileNotFoundException, InvalidDBException { mUri = uri; readOnly = false; if (uri.getScheme().equals("file")) { @@ -88,16 +90,17 @@ public class Database { readOnly = !file.canWrite(); } - InputStream is = ctx.getContentResolver().openInputStream(uri); + InputStream is = UriUtil.getUriInputStream(ctx, uri); + InputStream kfIs = UriUtil.getUriInputStream(ctx, keyfile); - LoadData(ctx, is, password, keyfile, status, debug); + LoadData(ctx, is, password, kfIs, status, debug); } - public void LoadData(Context ctx, InputStream is, String password, String keyfile, boolean debug) throws IOException, InvalidDBException { - LoadData(ctx, is, password, keyfile, new UpdateStatus(), debug); + public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, boolean debug) throws IOException, InvalidDBException { + LoadData(ctx, is, password, kfIs, new UpdateStatus(), debug); } - public void LoadData(Context ctx, InputStream is, String password, String keyfile, UpdateStatus status, boolean debug) throws IOException, InvalidDBException { + public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, UpdateStatus status, boolean debug) throws IOException, InvalidDBException { BufferedInputStream bis = new BufferedInputStream(is); @@ -112,19 +115,19 @@ public class Database { bis.reset(); // Return to the start - pm = imp.openDatabase(bis, password, keyfile, status); + pm = imp.openDatabase(bis, password, kfIs, status); if ( pm != null ) { PwGroup root = pm.rootGroup; pm.populateGlobals(root); - LoadData(ctx, pm, password, keyfile, status); + LoadData(ctx, pm, password, kfIs, status); } loaded = true; } - public void LoadData(Context ctx, PwDatabase pm, String password, String keyfile, UpdateStatus status) { + public void LoadData(Context ctx, PwDatabase pm, String password, InputStream keyInputStream, UpdateStatus status) { if ( pm != null ) { passwordEncodingError = !pm.validatePasswordEncoding(password); } @@ -148,7 +151,7 @@ public class Database { } public void SaveData(Context ctx, Uri uri) throws IOException, PwDbOutputException { - if (uri.getScheme().equals("data")) { + if (uri.getScheme().equals("file")) { String filename = uri.getPath(); File tempFile = new File(filename + ".tmp"); FileOutputStream fos = new FileOutputStream(tempFile); diff --git a/app/src/main/java/com/keepassdroid/PasswordActivity.java b/app/src/main/java/com/keepassdroid/PasswordActivity.java index 748d16ccd..5a6173e2e 100644 --- a/app/src/main/java/com/keepassdroid/PasswordActivity.java +++ b/app/src/main/java/com/keepassdroid/PasswordActivity.java @@ -21,6 +21,7 @@ package com.keepassdroid; import java.io.File; import java.io.FileNotFoundException; +import java.io.InputStream; import java.net.URLDecoder; import android.app.Activity; @@ -33,7 +34,6 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; -import android.os.ParcelFileDescriptor; import android.preference.PreferenceManager; import android.text.InputType; import android.view.Menu; @@ -61,7 +61,7 @@ import com.keepassdroid.fileselect.BrowserDialog; import com.keepassdroid.intents.Intents; import com.keepassdroid.settings.AppSettingsActivity; import com.keepassdroid.utils.Interaction; -import com.keepassdroid.utils.StrUtil; +import com.keepassdroid.utils.UriUtil; import com.keepassdroid.utils.Util; public class PasswordActivity extends LockingActivity { @@ -76,9 +76,10 @@ public class PasswordActivity extends LockingActivity { private static final int FILE_BROWSE = 256; public static final int GET_CONTENT = 257; - private String mFileName; - private String mKeyFile; - private Uri mUri = null; + //private String mFileName; + //private String mKeyFile; + private Uri mDbUri = null; + private Uri mKeyUri = null; private boolean mRememberKeyfile; SharedPreferences prefs; @@ -123,11 +124,13 @@ public class PasswordActivity extends LockingActivity { if (resultCode == RESULT_OK) { String filename = data.getDataString(); if (filename != null) { + /* if (filename.startsWith("file://")) { filename = filename.substring(7); } filename = URLDecoder.decode(filename); + */ EditText fn = (EditText) findViewById(R.id.pass_keyfile); fn.setText(filename); @@ -182,27 +185,27 @@ public class PasswordActivity extends LockingActivity { private void retrieveSettings() { String defaultFilename = prefs.getString(KEY_DEFAULT_FILENAME, ""); - if (mFileName.length() > 0 && mFileName.equals(defaultFilename)) { + if (mDbUri.getPath().length() > 0 && UriUtil.equalsDefaultfile(mDbUri, defaultFilename)) { CheckBox checkbox = (CheckBox) findViewById(R.id.default_database); checkbox.setChecked(true); } } - private String getKeyFile(String filename) { + private Uri getKeyFile(Uri dbUri) { if ( mRememberKeyfile ) { - String keyfile = App.getFileHistory().getFileByName(filename); - - return keyfile; + return App.getFileHistory().getFileByName(dbUri); } else { - return ""; + return null; } } private void populateView() { - setEditText(R.id.filename, mFileName); + String db = (mDbUri == null) ? "" : mDbUri.toString(); + setEditText(R.id.filename, db); - setEditText(R.id.pass_keyfile, mKeyFile); + String key = (mKeyUri == null) ? "" : mKeyUri.toString(); + setEditText(R.id.pass_keyfile, key); } /* @@ -226,7 +229,7 @@ public class PasswordActivity extends LockingActivity { String newDefaultFileName; if (isChecked) { - newDefaultFileName = mFileName; + newDefaultFileName = mDbUri.toString(); } else { newDefaultFileName = ""; } @@ -251,9 +254,13 @@ public class PasswordActivity extends LockingActivity { } } - private void loadDatabase(String pass, String keyfile) + private void loadDatabase(String pass, String keyfile) { + loadDatabase(pass, UriUtil.parseDefaultFile(keyfile)); + } + + private void loadDatabase(String pass, Uri keyfile) { - if ( pass.length() == 0 && keyfile.length() == 0 ) { + if ( pass.length() == 0 && (keyfile == null || keyfile.toString().length() == 0)) { errorMessage(R.string.error_nopass); return; } @@ -268,19 +275,8 @@ public class PasswordActivity extends LockingActivity { // Clear the shutdown flag App.clearShutdown(); - Uri uri; - if (mUri != null) { - uri = mUri; - } else { - uri = Uri.parse(fileName); + Uri uri = UriUtil.parseDefaultFile(fileName); - String scheme = uri.getScheme(); - if (scheme == null || scheme.equals("")) { - Uri.Builder builder = new Uri.Builder(); - builder.scheme("file").authority("").path(fileName); - uri = builder.build(); - } - } Handler handler = new Handler(); LoadDB task = new LoadDB(db, PasswordActivity.this, uri, pass, keyfile, new AfterLoad(handler, db)); ProgressTask pt = new ProgressTask(PasswordActivity.this, task, R.string.loading_database); @@ -365,39 +361,38 @@ public class PasswordActivity extends LockingActivity { String action = i.getAction();; if ( action != null && action.equals(VIEW_INTENT) ) { Uri incoming = i.getData(); + mDbUri = incoming; if (incoming.getScheme().equals("file")) { - mFileName = incoming.getPath(); + String fileName = incoming.getPath(); - if (mFileName.length() == 0) { + if (fileName.length() == 0) { // No file name return R.string.FileNotFound; } - File dbFile = new File(mFileName); + File dbFile = new File(fileName); if (!dbFile.exists()) { // File does not exist return R.string.FileNotFound; } - mKeyFile = getKeyFile(mFileName); + mKeyUri = getKeyFile(mDbUri); } else if (incoming.getScheme().equals("content")) { - mUri = incoming; - mFileName = mUri.toString(); - mKeyFile = getKeyFile(mFileName); + mKeyUri = getKeyFile(mDbUri); } else { return R.string.error_can_not_handle_uri; } } else { - mFileName = i.getStringExtra(KEY_FILENAME); - mKeyFile = i.getStringExtra(KEY_KEYFILE); + mDbUri = UriUtil.parseDefaultFile(i.getStringExtra(KEY_FILENAME)); + mKeyUri = UriUtil.parseDefaultFile(i.getStringExtra(KEY_KEYFILE)); password = i.getStringExtra(KEY_PASSWORD); launch_immediately = i.getBooleanExtra(KEY_LAUNCH_IMMEDIATELY, false); - if ( mKeyFile == null || mKeyFile.length() == 0) { - mKeyFile = getKeyFile(mFileName); + if ( mKeyUri == null || mKeyUri.toString().length() == 0) { + mKeyUri = getKeyFile(mDbUri); } } return null; @@ -458,12 +453,19 @@ public class PasswordActivity extends LockingActivity { if (Interaction.isIntentAvailable(PasswordActivity.this, Intents.OPEN_INTENTS_FILE_BROWSE)) { Intent i = new Intent(Intents.OPEN_INTENTS_FILE_BROWSE); - if (mFileName.length() > 0) { - File keyfile = new File(mFileName); - File parent = keyfile.getParentFile(); - if (parent != null) { - i.setData(Uri.parse("file://" + parent.getAbsolutePath())); + // Get file path parent if possible + try { + if (mDbUri != null && mDbUri.toString().length() > 0) { + if (mDbUri.getScheme().equals("file")) { + File keyfile = new File(mDbUri.getPath()); + File parent = keyfile.getParentFile(); + if (parent != null) { + i.setData(Uri.parse("file://" + parent.getAbsolutePath())); + } + } } + } catch (Exception e) { + // Ignore } try { @@ -485,7 +487,7 @@ public class PasswordActivity extends LockingActivity { retrieveSettings(); if (launch_immediately) - loadDatabase(password, mKeyFile); + loadDatabase(password, mDbUri); } } } diff --git a/app/src/main/java/com/keepassdroid/SetPasswordDialog.java b/app/src/main/java/com/keepassdroid/SetPasswordDialog.java index 3799c9b8b..f8cb753c1 100644 --- a/app/src/main/java/com/keepassdroid/SetPasswordDialog.java +++ b/app/src/main/java/com/keepassdroid/SetPasswordDialog.java @@ -21,6 +21,7 @@ package com.keepassdroid; import android.content.Context; import android.content.DialogInterface; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.view.View; @@ -33,11 +34,13 @@ import com.keepassdroid.app.App; import com.keepassdroid.database.edit.FileOnFinish; import com.keepassdroid.database.edit.OnFinish; import com.keepassdroid.database.edit.SetPassword; +import com.keepassdroid.utils.EmptyUtils; +import com.keepassdroid.utils.UriUtil; public class SetPasswordDialog extends CancelDialog { private byte[] masterKey; - private String mKeyfile; + private Uri mKeyfile; private FileOnFinish mFinish; public SetPasswordDialog(Context context) { @@ -54,7 +57,7 @@ public class SetPasswordDialog extends CancelDialog { return masterKey; } - public String keyfile() { + public Uri keyfile() { return mKeyfile; } @@ -83,11 +86,11 @@ public class SetPasswordDialog extends CancelDialog { } TextView keyfileView = (TextView) findViewById(R.id.pass_keyfile); - String keyfile = keyfileView.getText().toString(); + Uri keyfile = UriUtil.parseDefaultFile(keyfileView.getText().toString()); mKeyfile = keyfile; // Verify that a password or keyfile is set - if ( pass.length() == 0 && keyfile.length() == 0 ) { + if ( pass.length() == 0 && EmptyUtils.isNullOrEmpty(keyfile)) { Toast.makeText(getContext(), R.string.error_nopass, Toast.LENGTH_LONG).show(); return; diff --git a/app/src/main/java/com/keepassdroid/database/PwDatabase.java b/app/src/main/java/com/keepassdroid/database/PwDatabase.java index 53c555ba6..3e48db3fa 100644 --- a/app/src/main/java/com/keepassdroid/database/PwDatabase.java +++ b/app/src/main/java/com/keepassdroid/database/PwDatabase.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2015 Brian Pellin. + * Copyright 2009-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -20,10 +20,12 @@ package com.keepassdroid.database; import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.DigestOutputStream; import java.security.MessageDigest; @@ -40,6 +42,7 @@ import com.keepassdroid.crypto.finalkey.FinalKeyFactory; import com.keepassdroid.database.exception.InvalidKeyFileException; import com.keepassdroid.database.exception.KeyFileEmptyException; import com.keepassdroid.stream.NullOutputStream; +import com.keepassdroid.utils.Util; public abstract class PwDatabase { @@ -100,20 +103,20 @@ public abstract class PwDatabase { } - public abstract byte[] getMasterKey(String key, String keyFileName) throws InvalidKeyFileException, IOException; + public abstract byte[] getMasterKey(String key, InputStream keyInputStream) throws InvalidKeyFileException, IOException; - public void setMasterKey(String key, String keyFileName) + public void setMasterKey(String key, InputStream keyInputStream) throws InvalidKeyFileException, IOException { - assert( key != null && keyFileName != null ); + assert(key != null); - masterKey = getMasterKey(key, keyFileName); + masterKey = getMasterKey(key, keyInputStream); } - protected byte[] getCompositeKey(String key, String keyFileName) + protected byte[] getCompositeKey(String key, InputStream keyInputStream) throws InvalidKeyFileException, IOException { - assert(key != null && keyFileName != null); + assert(key != null && keyInputStream != null); - byte[] fileKey = getFileKey(keyFileName); + byte[] fileKey = getFileKey(keyInputStream); byte[] passwordKey = getPasswordKey(key); @@ -129,53 +132,32 @@ public abstract class PwDatabase { return md.digest(fileKey); } - protected byte[] getFileKey(String fileName) + protected byte[] getFileKey(InputStream keyInputStream) throws InvalidKeyFileException, IOException { - assert(fileName != null); - - File keyfile = new File(fileName); - - if ( ! keyfile.exists() ) { - throw new InvalidKeyFileException(); - } - - byte[] key = loadXmlKeyFile(fileName); + assert(keyInputStream != null); + + byte[] key = loadXmlKeyFile(keyInputStream); if ( key != null ) { return key; } - - FileInputStream fis; - try { - fis = new FileInputStream(keyfile); - } catch (FileNotFoundException e) { - throw new InvalidKeyFileException(); - } - - BufferedInputStream bis = new BufferedInputStream(fis, 64); - - long fileSize = keyfile.length(); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + Util.copyStream(keyInputStream, bos); + byte[] keyData = bos.toByteArray(); + + + long fileSize = keyData.length; if ( fileSize == 0 ) { throw new KeyFileEmptyException(); } else if ( fileSize == 32 ) { - byte[] outputKey = new byte[32]; - if ( bis.read(outputKey, 0, 32) != 32 ) { - throw new IOException("Error reading key."); - } - - return outputKey; + return keyData; } else if ( fileSize == 64 ) { byte[] hex = new byte[64]; - bis.mark(64); - if ( bis.read(hex, 0, 64) != 64 ) { - throw new IOException("Error reading key."); - } - try { - return hexStringToByteArray(new String(hex)); + return hexStringToByteArray(new String(keyData)); } catch (IndexOutOfBoundsException e) { // Key is not base 64, treat it as binary data - bis.reset(); } } @@ -190,14 +172,7 @@ public abstract class PwDatabase { int offset = 0; try { - while (true) { - int bytesRead = bis.read(buffer, 0, 2048); - if ( bytesRead == -1 ) break; // End of file - - md.update(buffer, 0, bytesRead); - offset += bytesRead; - - } + md.update(keyData); } catch (Exception e) { System.out.println(e.toString()); } @@ -205,7 +180,7 @@ public abstract class PwDatabase { return md.digest(); } - protected abstract byte[] loadXmlKeyFile(String fileName); + protected abstract byte[] loadXmlKeyFile(InputStream keyInputStream); public static byte[] hexStringToByteArray(String s) { int len = s.length(); diff --git a/app/src/main/java/com/keepassdroid/database/PwDatabaseV3.java b/app/src/main/java/com/keepassdroid/database/PwDatabaseV3.java index 93b110b43..b06c8c5c5 100644 --- a/app/src/main/java/com/keepassdroid/database/PwDatabaseV3.java +++ b/app/src/main/java/com/keepassdroid/database/PwDatabaseV3.java @@ -47,6 +47,7 @@ package com.keepassdroid.database; // Java import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -219,16 +220,16 @@ public class PwDatabaseV3 extends PwDatabase { return newId; } - public byte[] getMasterKey(String key, String keyFileName) + public byte[] getMasterKey(String key, InputStream keyInputStream) throws InvalidKeyFileException, IOException { - assert (key != null && keyFileName != null); + assert (key != null); - if (key.length() > 0 && keyFileName.length() > 0) { - return getCompositeKey(key, keyFileName); + if (key.length() > 0 && keyInputStream != null) { + return getCompositeKey(key, keyInputStream); } else if (key.length() > 0) { return getPasswordKey(key); - } else if (keyFileName.length() > 0) { - return getFileKey(keyFileName); + } else if (keyInputStream != null) { + return getFileKey(keyInputStream); } else { throw new IllegalArgumentException("Key cannot be empty."); } @@ -241,7 +242,7 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - protected byte[] loadXmlKeyFile(String fileName) { + protected byte[] loadXmlKeyFile(InputStream keyInputStream) { return null; } diff --git a/app/src/main/java/com/keepassdroid/database/PwDatabaseV4.java b/app/src/main/java/com/keepassdroid/database/PwDatabaseV4.java index 16856a10b..353eb9954 100644 --- a/app/src/main/java/com/keepassdroid/database/PwDatabaseV4.java +++ b/app/src/main/java/com/keepassdroid/database/PwDatabaseV4.java @@ -21,6 +21,7 @@ package com.keepassdroid.database; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -107,18 +108,18 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public byte[] getMasterKey(String key, String keyFileName) + public byte[] getMasterKey(String key, InputStream keyInputStream) throws InvalidKeyFileException, IOException { - assert( key != null && keyFileName != null ); + assert(key != null); byte[] fKey; - if ( key.length() > 0 && keyFileName.length() > 0 ) { - return getCompositeKey(key, keyFileName); + if ( key.length() > 0 && keyInputStream != null) { + return getCompositeKey(key, keyInputStream); } else if ( key.length() > 0 ) { fKey = getPasswordKey(key); - } else if ( keyFileName.length() > 0 ) { - fKey = getFileKey(keyFileName); + } else if ( keyInputStream != null) { + fKey = getFileKey(keyInputStream); } else { throw new IllegalArgumentException( "Key cannot be empty." ); } @@ -145,12 +146,11 @@ public class PwDatabaseV4 extends PwDatabase { private static final String KeyDataElementName = "Data"; @Override - protected byte[] loadXmlKeyFile(String fileName) { + protected byte[] loadXmlKeyFile(InputStream keyInputStream) { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); - FileInputStream fis = new FileInputStream(fileName); - Document doc = db.parse(fis); + Document doc = db.parse(keyInputStream); Element el = doc.getDocumentElement(); if (el == null || ! el.getNodeName().equalsIgnoreCase(RootElementName)) { diff --git a/app/src/main/java/com/keepassdroid/database/edit/CreateDB.java b/app/src/main/java/com/keepassdroid/database/edit/CreateDB.java index 6888e945a..984f7a914 100644 --- a/app/src/main/java/com/keepassdroid/database/edit/CreateDB.java +++ b/app/src/main/java/com/keepassdroid/database/edit/CreateDB.java @@ -28,6 +28,7 @@ import com.keepassdroid.app.App; import com.keepassdroid.database.PwDatabase; import com.keepassdroid.database.PwDatabaseV3; import com.keepassdroid.database.PwEncryptionAlgorithm; +import com.keepassdroid.utils.UriUtil; public class CreateDB extends RunnableOnFinish { @@ -57,7 +58,7 @@ public class CreateDB extends RunnableOnFinish { // Set Database state db.pm = pm; Uri.Builder b = new Uri.Builder(); - db.mUri = b.scheme("path").path(mFilename).build(); + db.mUri = UriUtil.parseDefaultFile(mFilename); db.setLoaded(); // Commit changes diff --git a/app/src/main/java/com/keepassdroid/database/edit/FileOnFinish.java b/app/src/main/java/com/keepassdroid/database/edit/FileOnFinish.java index 2f00b2fea..c542caff4 100644 --- a/app/src/main/java/com/keepassdroid/database/edit/FileOnFinish.java +++ b/app/src/main/java/com/keepassdroid/database/edit/FileOnFinish.java @@ -1,7 +1,28 @@ +/* + * Copyright 2016 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * KeePassDroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePassDroid. If not, see . + * + */ package com.keepassdroid.database.edit; +import android.net.Uri; + public class FileOnFinish extends OnFinish { - private String mFilename = ""; + private Uri mFilename = null; protected FileOnFinish mOnFinish; public FileOnFinish(FileOnFinish finish) { @@ -10,11 +31,11 @@ public class FileOnFinish extends OnFinish { mOnFinish = finish; } - public void setFilename(String filename) { + public void setFilename(Uri filename) { mFilename = filename; } - public String getFilename() { + public Uri getFilename() { return mFilename; } diff --git a/app/src/main/java/com/keepassdroid/database/edit/LoadDB.java b/app/src/main/java/com/keepassdroid/database/edit/LoadDB.java index eac25e096..7afabc8ef 100644 --- a/app/src/main/java/com/keepassdroid/database/edit/LoadDB.java +++ b/app/src/main/java/com/keepassdroid/database/edit/LoadDB.java @@ -42,12 +42,12 @@ import com.keepassdroid.database.exception.KeyFileEmptyException; public class LoadDB extends RunnableOnFinish { private Uri mUri; private String mPass; - private String mKey; + private Uri mKey; private Database mDb; private Context mCtx; private boolean mRememberKeyfile; - public LoadDB(Database db, Context ctx, Uri uri, String pass, String key, OnFinish finish) { + public LoadDB(Database db, Context ctx, Uri uri, String pass, Uri key, OnFinish finish) { super(finish); mDb = db; @@ -105,9 +105,9 @@ public class LoadDB extends RunnableOnFinish { finish(true); } - private void saveFileData(Uri uri, String key) { + private void saveFileData(Uri uri, Uri key) { if ( ! mRememberKeyfile ) { - key = ""; + key = null; } App.getFileHistory().createFile(uri, key); diff --git a/app/src/main/java/com/keepassdroid/database/edit/SetPassword.java b/app/src/main/java/com/keepassdroid/database/edit/SetPassword.java index a904165ee..09aa46bcf 100644 --- a/app/src/main/java/com/keepassdroid/database/edit/SetPassword.java +++ b/app/src/main/java/com/keepassdroid/database/edit/SetPassword.java @@ -20,29 +20,32 @@ package com.keepassdroid.database.edit; import java.io.IOException; +import java.io.InputStream; import android.content.Context; import android.content.DialogInterface; +import android.net.Uri; import com.keepassdroid.Database; import com.keepassdroid.database.PwDatabase; import com.keepassdroid.database.exception.InvalidKeyFileException; import com.keepassdroid.dialog.PasswordEncodingDialogHelper; +import com.keepassdroid.utils.UriUtil; public class SetPassword extends RunnableOnFinish { private String mPassword; - private String mKeyfile; + private Uri mKeyfile; private Database mDb; private boolean mDontSave; private Context ctx; - public SetPassword(Context ctx, Database db, String password, String keyfile, OnFinish finish) { + public SetPassword(Context ctx, Database db, String password, Uri keyfile, OnFinish finish) { this(ctx, db, password, keyfile, finish, false); } - public SetPassword(Context ctx, Database db, String password, String keyfile, OnFinish finish, boolean dontSave) { + public SetPassword(Context ctx, Database db, String password, Uri keyfile, OnFinish finish, boolean dontSave) { super(finish); mDb = db; @@ -71,7 +74,8 @@ public class SetPassword extends RunnableOnFinish { // Set key try { - pm.setMasterKey(mPassword, mKeyfile); + InputStream is = UriUtil.getUriInputStream(ctx, mKeyfile); + pm.setMasterKey(mPassword, is); } catch (InvalidKeyFileException e) { erase(backupKey); finish(false, e.getMessage()); diff --git a/app/src/main/java/com/keepassdroid/database/load/Importer.java b/app/src/main/java/com/keepassdroid/database/load/Importer.java index fce4a176d..da0797ef1 100644 --- a/app/src/main/java/com/keepassdroid/database/load/Importer.java +++ b/app/src/main/java/com/keepassdroid/database/load/Importer.java @@ -1,5 +1,5 @@ /* - * Copyright 2009 Brian Pellin. + * Copyright 2009-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -30,10 +30,10 @@ public abstract class Importer { public static final boolean DEBUG = true; - public abstract PwDatabase openDatabase( InputStream inStream, String password, String keyfile ) + public abstract PwDatabase openDatabase( InputStream inStream, String password, InputStream keyInputStream) throws IOException, InvalidDBException; - public abstract PwDatabase openDatabase( InputStream inStream, String password, String keyfile, UpdateStatus status ) + public abstract PwDatabase openDatabase( InputStream inStream, String password, InputStream keyInputStream, UpdateStatus status ) throws IOException, InvalidDBException; diff --git a/app/src/main/java/com/keepassdroid/database/load/ImporterV3.java b/app/src/main/java/com/keepassdroid/database/load/ImporterV3.java index 8f1a72db6..e0bc82788 100644 --- a/app/src/main/java/com/keepassdroid/database/load/ImporterV3.java +++ b/app/src/main/java/com/keepassdroid/database/load/ImporterV3.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2012 Brian Pellin. + * Copyright 2009-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -105,9 +105,8 @@ public class ImporterV3 extends Importer { /** * Load a v3 database file, return contents in a new PwDatabaseV3. * - * @param infile Existing file to load. + * @param inStream Existing file to load. * @param password Pass phrase for infile. - * @param pRepair (unused) * @return new PwDatabaseV3 container. * * @throws IOException on any file error. @@ -123,13 +122,13 @@ public class ImporterV3 extends Importer { * @throws InvalidAlgorithmParameterException if error decrypting main file body. * @throws ShortBufferException if error decrypting main file body. */ - public PwDatabaseV3 openDatabase( InputStream inStream, String password, String keyfile ) + public PwDatabaseV3 openDatabase( InputStream inStream, String password, InputStream kfIs) throws IOException, InvalidDBException { - return openDatabase(inStream, password, keyfile, new UpdateStatus()); + return openDatabase(inStream, password, kfIs, new UpdateStatus()); } - public PwDatabaseV3 openDatabase( InputStream inStream, String password, String keyfile, UpdateStatus status ) + public PwDatabaseV3 openDatabase( InputStream inStream, String password, InputStream kfIs, UpdateStatus status ) throws IOException, InvalidDBException { PwDatabaseV3 newManager; @@ -157,7 +156,7 @@ public class ImporterV3 extends Importer { status.updateMessage(R.string.creating_db_key); newManager = createDB(); - newManager.setMasterKey( password, keyfile ); + newManager.setMasterKey(password, kfIs); // Select algorithm if( (hdr.flags & PwDbHeaderV3.FLAG_RIJNDAEL) != 0 ) { diff --git a/app/src/main/java/com/keepassdroid/database/load/ImporterV3Debug.java b/app/src/main/java/com/keepassdroid/database/load/ImporterV3Debug.java index 2e019de72..09f41485d 100644 --- a/app/src/main/java/com/keepassdroid/database/load/ImporterV3Debug.java +++ b/app/src/main/java/com/keepassdroid/database/load/ImporterV3Debug.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 Brian Pellin. + * Copyright 2011-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -35,9 +35,9 @@ public class ImporterV3Debug extends ImporterV3 { @Override public PwDatabaseV3Debug openDatabase(InputStream inStream, String password, - String keyfile, UpdateStatus status) throws IOException, + InputStream keyInputStream, UpdateStatus status) throws IOException, InvalidDBException { - return (PwDatabaseV3Debug) super.openDatabase(inStream, password, keyfile, status); + return (PwDatabaseV3Debug) super.openDatabase(inStream, password, keyInputStream, status); } diff --git a/app/src/main/java/com/keepassdroid/database/load/ImporterV4.java b/app/src/main/java/com/keepassdroid/database/load/ImporterV4.java index 94284b80a..8775cf156 100644 --- a/app/src/main/java/com/keepassdroid/database/load/ImporterV4.java +++ b/app/src/main/java/com/keepassdroid/database/load/ImporterV4.java @@ -84,14 +84,14 @@ public class ImporterV4 extends Importer { @Override public PwDatabaseV4 openDatabase(InputStream inStream, String password, - String keyfile) throws IOException, InvalidDBException { + InputStream keyInputStream) throws IOException, InvalidDBException { - return openDatabase(inStream, password, keyfile, new UpdateStatus()); + return openDatabase(inStream, password, keyInputStream, new UpdateStatus()); } @Override public PwDatabaseV4 openDatabase(InputStream inStream, String password, - String keyfile, UpdateStatus status) throws IOException, + InputStream keyInputStream, UpdateStatus status) throws IOException, InvalidDBException { db = createDB(); @@ -100,7 +100,7 @@ public class ImporterV4 extends Importer { hashOfHeader = header.loadFromFile(inStream); - db.setMasterKey(password, keyfile); + db.setMasterKey(password, keyInputStream); db.makeFinalKey(header.masterSeed, header.transformSeed, (int)db.numKeyEncRounds); // Attach decryptor diff --git a/app/src/main/java/com/keepassdroid/database/load/ImporterV4Debug.java b/app/src/main/java/com/keepassdroid/database/load/ImporterV4Debug.java index 5cc35b14d..9943d60fd 100644 --- a/app/src/main/java/com/keepassdroid/database/load/ImporterV4Debug.java +++ b/app/src/main/java/com/keepassdroid/database/load/ImporterV4Debug.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 Brian Pellin. + * Copyright 2011-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -35,9 +35,9 @@ public class ImporterV4Debug extends ImporterV4 { @Override public PwDatabaseV4Debug openDatabase(InputStream inStream, String password, - String keyfile, UpdateStatus status) throws IOException, + InputStream keyInputFile, UpdateStatus status) throws IOException, InvalidDBException { - return (PwDatabaseV4Debug) super.openDatabase(inStream, password, keyfile, status); + return (PwDatabaseV4Debug) super.openDatabase(inStream, password, keyInputFile, status); } } diff --git a/app/src/main/java/com/keepassdroid/fileselect/RecentFileHistory.java b/app/src/main/java/com/keepassdroid/fileselect/RecentFileHistory.java index 3b20729c3..29267e2f7 100644 --- a/app/src/main/java/com/keepassdroid/fileselect/RecentFileHistory.java +++ b/app/src/main/java/com/keepassdroid/fileselect/RecentFileHistory.java @@ -25,6 +25,7 @@ import java.util.List; import com.android.keepass.R; import com.keepassdroid.compat.EditorCompat; +import com.keepassdroid.utils.UriUtil; import android.content.Context; import android.content.SharedPreferences; @@ -125,8 +126,8 @@ public class RecentFileHistory { return db.exists(); } - public void createFile(Uri uri, String keyFile) { - if (!enabled) return; + public void createFile(Uri uri, Uri keyUri) { + if (!enabled || uri == null || keyUri == null) return; init(); @@ -134,7 +135,7 @@ public class RecentFileHistory { deleteFile(uri, false); databases.add(0, uri.toString()); - keyfiles.add(0, keyFile); + keyfiles.add(0, keyUri.toString()); trimLists(); savePrefs(); @@ -220,19 +221,19 @@ public class RecentFileHistory { return databases; } - public String getFileByName(String database) { - if (!enabled) return ""; + public Uri getFileByName(Uri database) { + if (!enabled) return null; init(); int size = databases.size(); for (int i = 0; i < size; i++) { - if (database.equals(databases.get(i))) { - return keyfiles.get(i); + if (UriUtil.equalsDefaultfile(database,databases.get(i))) { + return UriUtil.parseDefaultFile(keyfiles.get(i)); } } - return ""; + return null; } public void deleteAll() { diff --git a/app/src/main/java/com/keepassdroid/utils/EmptyUtils.java b/app/src/main/java/com/keepassdroid/utils/EmptyUtils.java index 544d2065a..3b3d250dd 100644 --- a/app/src/main/java/com/keepassdroid/utils/EmptyUtils.java +++ b/app/src/main/java/com/keepassdroid/utils/EmptyUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Brian Pellin. + * Copyright 2012-2016 Brian Pellin. * * This file is part of KeePassDroid. * @@ -19,6 +19,8 @@ */ package com.keepassdroid.utils; +import android.net.Uri; + import com.keepassdroid.database.PwDate; import com.keepassdroid.database.PwEntryV3; @@ -34,4 +36,8 @@ public class EmptyUtils { public static boolean isNullOrEmpty(PwDate date) { return (date == null) || date.equals(PwEntryV3.DEFAULT_PWDATE); } + + public static boolean isNullOrEmpty(Uri uri) { + return (uri==null) || (uri.toString().length() == 0); + } } diff --git a/app/src/main/java/com/keepassdroid/utils/UriUtil.java b/app/src/main/java/com/keepassdroid/utils/UriUtil.java new file mode 100644 index 000000000..becbad19c --- /dev/null +++ b/app/src/main/java/com/keepassdroid/utils/UriUtil.java @@ -0,0 +1,74 @@ +/* + * Copyright 2016 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * KeePassDroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePassDroid. If not, see . + * + */ +package com.keepassdroid.utils; + +import android.content.Context; +import android.net.Uri; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +/** + * Created by bpellin on 3/5/16. + */ +public class UriUtil { + public static Uri parseDefaultFile(String text) { + if (EmptyUtils.isNullOrEmpty(text)) { + return null; + } + + Uri uri = Uri.parse(text); + if (EmptyUtils.isNullOrEmpty(uri.getScheme())) { + uri = uri.buildUpon().scheme("file").authority("").build(); + } + + return uri; + } + public static Uri parseDefaultFile(Uri uri) { + if (EmptyUtils.isNullOrEmpty(uri.getScheme())) { + uri = uri.buildUpon().scheme("file").authority("").build(); + } + + return uri; + } + + public static boolean equalsDefaultfile(Uri left, String right) { + left = parseDefaultFile(left); + Uri uriRight = parseDefaultFile(right); + + return left.equals(uriRight); + } + + public static InputStream getUriInputStream(Context ctx, Uri uri) throws FileNotFoundException { + if (uri == null) return null; + + String scheme = uri.getScheme(); + if (scheme.equals("file")) { + return new FileInputStream(uri.getPath()); + } + else if (scheme.equals("content")) { + return ctx.getContentResolver().openInputStream(uri); + } + else { + return null; + } + } +}