diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt
index d0da8c171..cb4c3c977 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt
@@ -384,10 +384,6 @@ class Database {
// Check if the file is writable
this.isReadOnly = readOnly
- if (uri.scheme == "file") {
- val file = File(uri.path!!)
- isReadOnly = !file.canWrite()
- }
// Pass KeyFile Uri as InputStreams
var keyFileInputStream: InputStream? = null
@@ -396,31 +392,36 @@ class Database {
keyfile?.let {
keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyfile)
}
- } catch (e: Exception) {
+
+ // Read database stream for the first time
+ readDatabaseStream(contentResolver, uri,
+ { databaseInputStream ->
+ DatabaseInputKDB(cacheDirectory)
+ .openDatabase(databaseInputStream,
+ password,
+ keyFileInputStream,
+ progressTaskUpdater,
+ fixDuplicateUUID)
+ },
+ { databaseInputStream ->
+ DatabaseInputKDBX(cacheDirectory)
+ .openDatabase(databaseInputStream,
+ password,
+ keyFileInputStream,
+ progressTaskUpdater,
+ fixDuplicateUUID)
+ }
+ )
+ } catch (e: FileNotFoundException) {
+ Log.e(TAG, "Unable to load keyfile", e)
throw FileNotFoundDatabaseException()
+ } catch (e: LoadDatabaseException) {
+ throw e
+ } catch (e: Exception) {
+ throw LoadDatabaseException(e)
} finally {
keyFileInputStream?.close()
}
-
- // Read database stream for the first time
- readDatabaseStream(contentResolver, uri,
- { databaseInputStream ->
- DatabaseInputKDB(cacheDirectory)
- .openDatabase(databaseInputStream,
- password,
- keyFileInputStream,
- progressTaskUpdater,
- fixDuplicateUUID)
- },
- { databaseInputStream ->
- DatabaseInputKDBX(cacheDirectory)
- .openDatabase(databaseInputStream,
- password,
- keyFileInputStream,
- progressTaskUpdater,
- fixDuplicateUUID)
- }
- )
}
@Throws(LoadDatabaseException::class)
@@ -444,7 +445,10 @@ class Database {
progressTaskUpdater)
}
)
- } ?: throw IODatabaseException()
+ } ?: run {
+ Log.e(TAG, "Database URI is null, database cannot be reloaded")
+ throw IODatabaseException()
+ }
}
fun isGroupSearchable(group: Group, omitBackup: Boolean): Boolean {
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseException.kt
index 594154606..2b5fd59e4 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseException.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseException.kt
@@ -67,7 +67,6 @@ class FileNotFoundDatabaseException : LoadDatabaseException {
class InvalidAlgorithmDatabaseException : LoadDatabaseException {
@StringRes
override var errorId: Int = R.string.invalid_algorithm
-
constructor() : super()
constructor(exception: Throwable) : super(exception)
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt
index c13ba5884..16069a060 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt
@@ -452,8 +452,6 @@ class DatabaseInputKDBX(cacheDirectory: File)
val strData = readString(xpp)
if (strData.isNotEmpty()) {
customIconData = Base64.decode(strData, BASE_64_FLAG)
- } else {
- assert(false)
}
} else {
readUnknown(xpp)
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutput.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutput.kt
deleted file mode 100644
index 4cb13458d..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutput.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePassDX.
- *
- * KeePassDX 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 3 of the License, or
- * (at your option) any later version.
- *
- * KeePassDX 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 KeePassDX. If not, see .
- *
- */
-package com.kunzisoft.keepass.database.file.output
-
-open class DatabaseHeaderOutput {
- var hashOfHeader: ByteArray? = null
- protected set
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt
index bd18d8207..de76b662d 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt
@@ -40,13 +40,16 @@ import javax.crypto.spec.SecretKeySpec
class DatabaseHeaderOutputKDBX @Throws(DatabaseOutputException::class)
constructor(private val databaseKDBX: DatabaseKDBX,
private val header: DatabaseHeaderKDBX,
- outputStream: OutputStream) : DatabaseHeaderOutput() {
+ outputStream: OutputStream) {
private val los: LittleEndianDataOutputStream
private val mos: MacOutputStream
private val dos: DigestOutputStream
lateinit var headerHmac: ByteArray
+ var hashOfHeader: ByteArray? = null
+ private set
+
init {
val md: MessageDigest
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseInnerHeaderOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseInnerHeaderOutputKDBX.kt
deleted file mode 100644
index fb3666e90..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseInnerHeaderOutputKDBX.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePassDX.
- *
- * 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 3 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.kunzisoft.keepass.database.file.output
-
-import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
-import com.kunzisoft.keepass.database.element.database.DatabaseKDBX.Companion.BUFFER_SIZE_BYTES
-import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
-import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream
-import com.kunzisoft.keepass.stream.readBytes
-import com.kunzisoft.keepass.utils.UnsignedInt
-import java.io.IOException
-import java.io.OutputStream
-import kotlin.experimental.or
-
-class DatabaseInnerHeaderOutputKDBX(private val database: DatabaseKDBX,
- private val header: DatabaseHeaderKDBX,
- outputStream: OutputStream) {
-
- private val dataOutputStream: LittleEndianDataOutputStream = LittleEndianDataOutputStream(outputStream)
-
- @Throws(IOException::class)
- fun output() {
- dataOutputStream.writeByte(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.InnerRandomStreamID)
- dataOutputStream.writeInt(4)
- if (header.innerRandomStream == null)
- throw IOException("Can't write innerRandomStream")
- dataOutputStream.writeUInt(header.innerRandomStream!!.id)
-
- val streamKeySize = header.innerRandomStreamKey.size
- dataOutputStream.writeByte(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.InnerRandomstreamKey)
- dataOutputStream.writeInt(streamKeySize)
- dataOutputStream.write(header.innerRandomStreamKey)
-
- database.binaryPool.doForEachOrderedBinary { _, keyBinary ->
- val protectedBinary = keyBinary.binary
- // Force decompression to add binary in header
- protectedBinary.decompress()
- // Write type binary
- dataOutputStream.writeByte(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.Binary)
- // Write size
- dataOutputStream.writeUInt(UnsignedInt.fromKotlinLong(protectedBinary.length() + 1))
- // Write protected flag
- var flag = DatabaseHeaderKDBX.KdbxBinaryFlags.None
- if (protectedBinary.isProtected) {
- flag = flag or DatabaseHeaderKDBX.KdbxBinaryFlags.Protected
- }
- dataOutputStream.writeByte(flag)
-
- protectedBinary.getInputDataStream().use { inputStream ->
- inputStream.readBytes(BUFFER_SIZE_BYTES) { buffer ->
- dataOutputStream.write(buffer)
- }
- }
- }
-
- dataOutputStream.writeByte(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.EndOfHeader)
- dataOutputStream.writeInt(0)
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutput.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutput.kt
index 4c0951393..4d456e538 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutput.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutput.kt
@@ -26,7 +26,7 @@ import java.io.OutputStream
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
-abstract class DatabaseOutput protected constructor(protected var mOS: OutputStream) {
+abstract class DatabaseOutput protected constructor(protected var mOutputStream: OutputStream) {
@Throws(DatabaseOutputException::class)
protected open fun setIVs(header: Header): SecureRandom {
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt
index f71345bf8..417865c21 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt
@@ -63,7 +63,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
// and remove any orphaned nodes that are no longer part of the tree hierarchy
sortGroupsForOutput()
- val header = outputHeader(mOS)
+ val header = outputHeader(mOutputStream)
val finalKey = getFinalKey(header)
@@ -85,7 +85,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
cipher.init(Cipher.ENCRYPT_MODE,
SecretKeySpec(finalKey, "AES"),
IvParameterSpec(header.encryptionIV))
- val cos = CipherOutputStream(mOS, cipher)
+ val cos = CipherOutputStream(mOutputStream, cipher)
val bos = BufferedOutputStream(cos)
outputPlanGroupAndEntries(bos)
bos.flush()
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt
index 372623f2c..bfd88cd54 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt
@@ -46,6 +46,7 @@ import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
import com.kunzisoft.keepass.database.file.DatabaseKDBXXML
import com.kunzisoft.keepass.database.file.DateKDBXUtil
import com.kunzisoft.keepass.stream.*
+import com.kunzisoft.keepass.utils.UnsignedInt
import org.bouncycastle.crypto.StreamCipher
import org.joda.time.DateTime
import org.xmlpull.v1.XmlSerializer
@@ -57,6 +58,7 @@ import java.util.*
import java.util.zip.GZIPOutputStream
import javax.crypto.Cipher
import javax.crypto.CipherOutputStream
+import kotlin.experimental.or
class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
@@ -80,20 +82,19 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
throw DatabaseOutputException("No such cipher", e)
}
- header = outputHeader(mOS)
+ header = outputHeader(mOutputStream)
val osPlain: OutputStream
osPlain = if (header!!.version.toKotlinLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toKotlinLong()) {
- val cos = attachStreamEncryptor(header!!, mOS)
+ val cos = attachStreamEncryptor(header!!, mOutputStream)
cos.write(header!!.streamStartBytes)
HashedBlockOutputStream(cos)
} else {
- mOS.write(hashOfHeader!!)
- mOS.write(headerHmac!!)
+ mOutputStream.write(hashOfHeader!!)
+ mOutputStream.write(headerHmac!!)
-
- attachStreamEncryptor(header!!, HmacBlockOutputStream(mOS, mDatabaseKDBX.hmacKey!!))
+ attachStreamEncryptor(header!!, HmacBlockOutputStream(mOutputStream, mDatabaseKDBX.hmacKey!!))
}
val osXml: OutputStream
@@ -104,8 +105,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
}
if (header!!.version.toKotlinLong() >= DatabaseHeaderKDBX.FILE_VERSION_32_4.toKotlinLong()) {
- val ihOut = DatabaseInnerHeaderOutputKDBX(mDatabaseKDBX, header!!, osXml)
- ihOut.output()
+ outputInnerHeader(mDatabaseKDBX, header!!, osXml)
}
outputDatabase(osXml)
@@ -121,6 +121,49 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
}
}
+ @Throws(IOException::class)
+ private fun outputInnerHeader(database: DatabaseKDBX,
+ header: DatabaseHeaderKDBX,
+ outputStream: OutputStream) {
+ val dataOutputStream = LittleEndianDataOutputStream(outputStream)
+
+ dataOutputStream.writeByte(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.InnerRandomStreamID)
+ dataOutputStream.writeInt(4)
+ if (header.innerRandomStream == null)
+ throw IOException("Can't write innerRandomStream")
+ dataOutputStream.writeUInt(header.innerRandomStream!!.id)
+
+ val streamKeySize = header.innerRandomStreamKey.size
+ dataOutputStream.writeByte(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.InnerRandomstreamKey)
+ dataOutputStream.writeInt(streamKeySize)
+ dataOutputStream.write(header.innerRandomStreamKey)
+
+ database.binaryPool.doForEachOrderedBinary { _, keyBinary ->
+ val protectedBinary = keyBinary.binary
+ // Force decompression to add binary in header
+ protectedBinary.decompress()
+ // Write type binary
+ dataOutputStream.writeByte(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.Binary)
+ // Write size
+ dataOutputStream.writeUInt(UnsignedInt.fromKotlinLong(protectedBinary.length() + 1))
+ // Write protected flag
+ var flag = DatabaseHeaderKDBX.KdbxBinaryFlags.None
+ if (protectedBinary.isProtected) {
+ flag = flag or DatabaseHeaderKDBX.KdbxBinaryFlags.Protected
+ }
+ dataOutputStream.writeByte(flag)
+
+ protectedBinary.getInputDataStream().use { inputStream ->
+ inputStream.readBytes(BUFFER_SIZE_BYTES) { buffer ->
+ dataOutputStream.write(buffer)
+ }
+ }
+ }
+
+ dataOutputStream.writeByte(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.EndOfHeader)
+ dataOutputStream.writeInt(0)
+ }
+
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
private fun outputDatabase(outputStream: OutputStream) {