Generalize file/URI handling

This commit is contained in:
Brian Pellin
2016-03-06 22:00:03 -06:00
parent e80a050350
commit b0b5ca305d
25 changed files with 299 additions and 187 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
assert(keyInputStream != null);
File keyfile = new File(fileName);
if ( ! keyfile.exists() ) {
throw new InvalidKeyFileException();
}
byte[] key = loadXmlKeyFile(fileName);
byte[] key = loadXmlKeyFile(keyInputStream);
if ( key != null ) {
return key;
}
FileInputStream fis;
try {
fis = new FileInputStream(keyfile);
} catch (FileNotFoundException e) {
throw new InvalidKeyFileException();
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Util.copyStream(keyInputStream, bos);
byte[] keyData = bos.toByteArray();
BufferedInputStream bis = new BufferedInputStream(fis, 64);
long fileSize = keyfile.length();
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();

View File

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

View File

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

View File

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

View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
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;
}
}
}