diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/BinaryDataTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/BinaryDataTest.kt
index d47b16928..e44df8a54 100644
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/BinaryDataTest.kt
+++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/BinaryDataTest.kt
@@ -3,7 +3,7 @@ package com.kunzisoft.keepass.tests.stream
import android.content.Context
import androidx.test.platform.app.InstrumentationRegistry
import com.kunzisoft.keepass.database.element.Database
-import com.kunzisoft.keepass.database.element.database.BinaryData
+import com.kunzisoft.keepass.database.element.database.BinaryFile
import com.kunzisoft.keepass.stream.readAllBytes
import com.kunzisoft.keepass.utils.UriUtil
import junit.framework.TestCase.assertEquals
@@ -25,7 +25,7 @@ class BinaryDataTest {
private val loadedKey = Database.LoadedKey.generateNewCipherKey()
- private fun saveBinary(asset: String, binaryData: BinaryData) {
+ private fun saveBinary(asset: String, binaryData: BinaryFile) {
context.assets.open(asset).use { assetInputStream ->
binaryData.getOutputDataStream(loadedKey).use { binaryOutputStream ->
assetInputStream.readAllBytes(DEFAULT_BUFFER_SIZE) { buffer ->
@@ -37,8 +37,8 @@ class BinaryDataTest {
@Test
fun testSaveTextInCache() {
- val binaryA = BinaryData(fileA)
- val binaryB = BinaryData(fileB)
+ val binaryA = BinaryFile(fileA)
+ val binaryB = BinaryFile(fileB)
saveBinary(TEST_TEXT_ASSET, binaryA)
saveBinary(TEST_TEXT_ASSET, binaryB)
assertEquals("Save text binary length failed.", binaryA.getSize(), binaryB.getSize())
@@ -47,8 +47,8 @@ class BinaryDataTest {
@Test
fun testSaveImageInCache() {
- val binaryA = BinaryData(fileA)
- val binaryB = BinaryData(fileB)
+ val binaryA = BinaryFile(fileA)
+ val binaryB = BinaryFile(fileB)
saveBinary(TEST_IMAGE_ASSET, binaryA)
saveBinary(TEST_IMAGE_ASSET, binaryB)
assertEquals("Save image binary length failed.", binaryA.getSize(), binaryB.getSize())
@@ -57,9 +57,9 @@ class BinaryDataTest {
@Test
fun testCompressText() {
- val binaryA = BinaryData(fileA)
- val binaryB = BinaryData(fileB)
- val binaryC = BinaryData(fileC)
+ val binaryA = BinaryFile(fileA)
+ val binaryB = BinaryFile(fileB)
+ val binaryC = BinaryFile(fileC)
saveBinary(TEST_TEXT_ASSET, binaryA)
saveBinary(TEST_TEXT_ASSET, binaryB)
saveBinary(TEST_TEXT_ASSET, binaryC)
@@ -74,9 +74,9 @@ class BinaryDataTest {
@Test
fun testCompressImage() {
- val binaryA = BinaryData(fileA)
- var binaryB = BinaryData(fileB)
- val binaryC = BinaryData(fileC)
+ val binaryA = BinaryFile(fileA)
+ var binaryB = BinaryFile(fileB)
+ val binaryC = BinaryFile(fileC)
saveBinary(TEST_IMAGE_ASSET, binaryA)
saveBinary(TEST_IMAGE_ASSET, binaryB)
saveBinary(TEST_IMAGE_ASSET, binaryC)
@@ -84,7 +84,7 @@ class BinaryDataTest {
binaryB.compress(loadedKey)
assertEquals("Compress image length failed.", binaryA.getSize(), binaryA.getSize())
assertEquals("Compress image failed.", binaryA.binaryHash(), binaryA.binaryHash())
- binaryB = BinaryData(fileB, true)
+ binaryB = BinaryFile(fileB, true)
binaryB.decompress(loadedKey)
assertEquals("Decompress image length failed.", binaryB.getSize(), binaryC.getSize())
assertEquals("Decompress image failed.", binaryB.binaryHash(), binaryC.binaryHash())
@@ -92,7 +92,7 @@ class BinaryDataTest {
@Test
fun testReadText() {
- val binaryA = BinaryData(fileA)
+ val binaryA = BinaryFile(fileA)
saveBinary(TEST_TEXT_ASSET, binaryA)
assert(streamAreEquals(context.assets.open(TEST_TEXT_ASSET),
binaryA.getInputDataStream(loadedKey)))
@@ -100,7 +100,7 @@ class BinaryDataTest {
@Test
fun testReadImage() {
- val binaryA = BinaryData(fileA)
+ val binaryA = BinaryFile(fileA)
saveBinary(TEST_IMAGE_ASSET, binaryA)
assert(streamAreEquals(context.assets.open(TEST_IMAGE_ASSET),
binaryA.getInputDataStream(loadedKey)))
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt
index bd5c988de..cfc4690b5 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt
@@ -21,6 +21,7 @@ package com.kunzisoft.keepass.database.element
import android.os.Parcel
import android.os.Parcelable
+import com.kunzisoft.keepass.database.element.database.BinaryByte
import com.kunzisoft.keepass.database.element.database.BinaryData
@@ -29,7 +30,7 @@ data class Attachment(var name: String,
constructor(parcel: Parcel) : this(
parcel.readString() ?: "",
- parcel.readParcelable(BinaryData::class.java.classLoader) ?: BinaryData()
+ parcel.readParcelable(BinaryData::class.java.classLoader) ?: BinaryByte()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryByte.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryByte.kt
new file mode 100644
index 000000000..e567f33a7
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryByte.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2018 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.element.database
+
+import android.os.Parcel
+import android.os.Parcelable
+import com.kunzisoft.keepass.database.element.Database
+import java.io.*
+
+class BinaryByte : BinaryData {
+
+ private var mDataByte: ByteArray = ByteArray(0)
+
+ /**
+ * Empty protected binary
+ */
+ constructor() : super()
+
+ constructor(byteArray: ByteArray,
+ compressed: Boolean = false,
+ protected: Boolean = false) : super(compressed, protected) {
+ this.mDataByte = byteArray
+ }
+
+ constructor(parcel: Parcel) : super(parcel) {
+ val byteArray = ByteArray(parcel.readInt())
+ parcel.readByteArray(byteArray)
+ mDataByte = byteArray
+ }
+
+ @Throws(IOException::class)
+ override fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream {
+ return ByteArrayInputStream(mDataByte)
+ }
+
+ @Throws(IOException::class)
+ override fun getOutputDataStream(cipherKey: Database.LoadedKey): OutputStream {
+ return ByteOutputStream()
+ }
+
+ @Throws(IOException::class)
+ override fun getUnGzipInputDataStream(cipherKey: Database.LoadedKey): InputStream {
+ return getInputDataStream(cipherKey)
+ }
+
+ @Throws(IOException::class)
+ override fun getGzipOutputDataStream(cipherKey: Database.LoadedKey): OutputStream {
+ return getOutputDataStream(cipherKey)
+ }
+
+ @Throws(IOException::class)
+ override fun compress(cipherKey: Database.LoadedKey) {
+ // TODO compress
+ }
+
+ @Throws(IOException::class)
+ override fun decompress(cipherKey: Database.LoadedKey) {
+ // TODO decompress
+ }
+
+ @Throws(IOException::class)
+ override fun clear() {
+ mDataByte = ByteArray(0)
+ }
+
+ override fun dataExists(): Boolean {
+ return mDataByte.isNotEmpty()
+ }
+
+ override fun getSize(): Long {
+ return mDataByte.size.toLong()
+ }
+
+ /**
+ * Hash of the raw encrypted file in temp folder, only to compare binary data
+ */
+ override fun binaryHash(): Int {
+ return if (dataExists())
+ mDataByte.contentHashCode()
+ else
+ 0
+ }
+
+ override fun toString(): String {
+ return mDataByte.toString()
+ }
+
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ super.writeToParcel(dest, flags)
+ dest.writeInt(mDataByte.size)
+ dest.writeByteArray(mDataByte)
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is BinaryByte) return false
+ if (!super.equals(other)) return false
+
+ if (!mDataByte.contentEquals(other.mDataByte)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = super.hashCode()
+ result = 31 * result + mDataByte.contentHashCode()
+ return result
+ }
+
+ /**
+ * Custom OutputStream to calculate the size and hash of binary file
+ */
+ private inner class ByteOutputStream : ByteArrayOutputStream() {
+ override fun close() {
+ mDataByte = this.toByteArray()
+ super.close()
+ }
+ }
+
+ companion object {
+
+ private val TAG = BinaryByte::class.java.name
+
+ @JvmField
+ val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): BinaryByte {
+ return BinaryByte(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryData.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryData.kt
index 0f727ef0f..64b440932 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryData.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryData.kt
@@ -21,278 +21,92 @@ package com.kunzisoft.keepass.database.element.database
import android.os.Parcel
import android.os.Parcelable
-import android.util.Base64
-import android.util.Base64InputStream
-import android.util.Base64OutputStream
import com.kunzisoft.keepass.database.element.Database
-import com.kunzisoft.keepass.stream.readAllBytes
-import org.apache.commons.io.output.CountingOutputStream
-import java.io.*
-import java.nio.ByteBuffer
-import java.security.MessageDigest
-import java.util.zip.GZIPInputStream
-import java.util.zip.GZIPOutputStream
-import javax.crypto.Cipher
-import javax.crypto.CipherInputStream
-import javax.crypto.CipherOutputStream
-import javax.crypto.spec.IvParameterSpec
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
-class BinaryData : Parcelable {
+abstract class BinaryData : Parcelable {
- private var mDataFile: File? = null
- private var mLength: Long = 0
- private var mBinaryHash = 0
var isCompressed: Boolean = false
- private set
+ protected set
var isProtected: Boolean = false
- private set
+ protected set
var isCorrupted: Boolean = false
- // Cipher to encrypt temp file
- private var cipherEncryption: Cipher = Cipher.getInstance(Database.LoadedKey.BINARY_CIPHER)
- private var cipherDecryption: Cipher = Cipher.getInstance(Database.LoadedKey.BINARY_CIPHER)
/**
* Empty protected binary
*/
- constructor()
+ protected constructor()
- constructor(dataFile: File, compressed: Boolean = false, protected: Boolean = false) {
- this.mDataFile = dataFile
- this.mLength = 0
+ protected constructor(compressed: Boolean = false, protected: Boolean = false) {
this.isCompressed = compressed
this.isProtected = protected
}
- private constructor(parcel: Parcel) {
- parcel.readString()?.let {
- mDataFile = File(it)
- }
- mLength = parcel.readLong()
+ protected constructor(parcel: Parcel) {
isCompressed = parcel.readByte().toInt() != 0
isProtected = parcel.readByte().toInt() != 0
isCorrupted = parcel.readByte().toInt() != 0
}
@Throws(IOException::class)
- fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream {
- return buildInputStream(mDataFile, cipherKey)
- }
+ abstract fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream
@Throws(IOException::class)
- fun getOutputDataStream(cipherKey: Database.LoadedKey): OutputStream {
- return buildOutputStream(mDataFile, cipherKey)
- }
+ abstract fun getOutputDataStream(cipherKey: Database.LoadedKey): OutputStream
@Throws(IOException::class)
- fun getUnGzipInputDataStream(cipherKey: Database.LoadedKey): InputStream {
- return if (isCompressed) {
- GZIPInputStream(getInputDataStream(cipherKey))
- } else {
- getInputDataStream(cipherKey)
- }
- }
+ abstract fun getUnGzipInputDataStream(cipherKey: Database.LoadedKey): InputStream
@Throws(IOException::class)
- fun getGzipOutputDataStream(cipherKey: Database.LoadedKey): OutputStream {
- return if (isCompressed) {
- GZIPOutputStream(getOutputDataStream(cipherKey))
- } else {
- getOutputDataStream(cipherKey)
- }
- }
+ abstract fun getGzipOutputDataStream(cipherKey: Database.LoadedKey): OutputStream
@Throws(IOException::class)
- private fun buildInputStream(file: File?, cipherKey: Database.LoadedKey): InputStream {
- return when {
- file != null && file.length() > 0 -> {
- cipherDecryption.init(Cipher.DECRYPT_MODE, cipherKey.key, IvParameterSpec(cipherKey.iv))
- Base64InputStream(CipherInputStream(FileInputStream(file), cipherDecryption), Base64.NO_WRAP)
- }
- else -> ByteArrayInputStream(ByteArray(0))
- }
- }
+ abstract fun compress(cipherKey: Database.LoadedKey)
@Throws(IOException::class)
- private fun buildOutputStream(file: File?, cipherKey: Database.LoadedKey): OutputStream {
- return when {
- file != null -> {
- cipherEncryption.init(Cipher.ENCRYPT_MODE, cipherKey.key, IvParameterSpec(cipherKey.iv))
- BinaryCountingOutputStream(Base64OutputStream(CipherOutputStream(FileOutputStream(file), cipherEncryption), Base64.NO_WRAP))
- }
- else -> throw IOException("Unable to write in an unknown file")
- }
- }
+ abstract fun decompress(cipherKey: Database.LoadedKey)
@Throws(IOException::class)
- fun compress(cipherKey: Database.LoadedKey) {
- mDataFile?.let { concreteDataFile ->
- // To compress, create a new binary with file
- if (!isCompressed) {
- // Encrypt the new gzipped temp file
- val fileBinaryCompress = File(concreteDataFile.parent, concreteDataFile.name + "_temp")
- getInputDataStream(cipherKey).use { inputStream ->
- GZIPOutputStream(buildOutputStream(fileBinaryCompress, cipherKey)).use { outputStream ->
- inputStream.readAllBytes { buffer ->
- outputStream.write(buffer)
- }
- }
- }
- // Remove ungzip file
- if (concreteDataFile.delete()) {
- if (fileBinaryCompress.renameTo(concreteDataFile)) {
- // Harmonize with database compression
- isCompressed = true
- }
- }
- }
- }
- }
+ abstract fun clear()
- @Throws(IOException::class)
- fun decompress(cipherKey: Database.LoadedKey) {
- mDataFile?.let { concreteDataFile ->
- if (isCompressed) {
- // Encrypt the new ungzipped temp file
- val fileBinaryDecompress = File(concreteDataFile.parent, concreteDataFile.name + "_temp")
- getUnGzipInputDataStream(cipherKey).use { inputStream ->
- buildOutputStream(fileBinaryDecompress, cipherKey).use { outputStream ->
- inputStream.readAllBytes { buffer ->
- outputStream.write(buffer)
- }
- }
- }
- // Remove gzip file
- if (concreteDataFile.delete()) {
- if (fileBinaryDecompress.renameTo(concreteDataFile)) {
- // Harmonize with database compression
- isCompressed = false
- }
- }
- }
- }
- }
+ abstract fun dataExists(): Boolean
- @Throws(IOException::class)
- fun clear() {
- if (mDataFile != null && !mDataFile!!.delete())
- throw IOException("Unable to delete temp file " + mDataFile!!.absolutePath)
- }
+ abstract fun getSize(): Long
- fun dataExists(): Boolean {
- return mDataFile != null && mLength > 0
- }
-
- fun getSize(): Long {
- return mLength
- }
-
- /**
- * Hash of the raw encrypted file in temp folder, only to compare binary data
- */
- @Throws(FileNotFoundException::class)
- fun binaryHash(): Int {
- return mBinaryHash
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other)
- return true
- if (other == null || javaClass != other.javaClass)
- return false
- if (other !is BinaryData)
- return false
-
- var sameData = false
- if (mDataFile != null && mDataFile == other.mDataFile)
- sameData = true
-
- return isCompressed == other.isCompressed
- && isProtected == other.isProtected
- && isCorrupted == other.isCorrupted
- && sameData
- }
-
- override fun hashCode(): Int {
-
- var result = 0
- result = 31 * result + if (isCompressed) 1 else 0
- result = 31 * result + if (isProtected) 1 else 0
- result = 31 * result + if (isCorrupted) 1 else 0
- result = 31 * result + mDataFile!!.hashCode()
- result = 31 * result + mLength.hashCode()
- return result
- }
-
- override fun toString(): String {
- return mDataFile.toString()
- }
+ abstract fun binaryHash(): Int
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel, flags: Int) {
- dest.writeString(mDataFile?.absolutePath)
- dest.writeLong(mLength)
dest.writeByte((if (isCompressed) 1 else 0).toByte())
dest.writeByte((if (isProtected) 1 else 0).toByte())
dest.writeByte((if (isCorrupted) 1 else 0).toByte())
}
- /**
- * Custom OutputStream to calculate the size and hash of binary file
- */
- private inner class BinaryCountingOutputStream(out: OutputStream): CountingOutputStream(out) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is BinaryData) return false
- private val mMessageDigest: MessageDigest
- init {
- mLength = 0
- mMessageDigest = MessageDigest.getInstance("MD5")
- mBinaryHash = 0
- }
+ if (isCompressed != other.isCompressed) return false
+ if (isProtected != other.isProtected) return false
+ if (isCorrupted != other.isCorrupted) return false
- override fun beforeWrite(n: Int) {
- super.beforeWrite(n)
- mLength = byteCount
- }
+ return true
+ }
- override fun write(idx: Int) {
- super.write(idx)
- mMessageDigest.update(idx.toByte())
- }
-
- override fun write(bts: ByteArray) {
- super.write(bts)
- mMessageDigest.update(bts)
- }
-
- override fun write(bts: ByteArray, st: Int, end: Int) {
- super.write(bts, st, end)
- mMessageDigest.update(bts, st, end)
- }
-
- override fun close() {
- super.close()
- mLength = byteCount
- val bytes = mMessageDigest.digest()
- mBinaryHash = ByteBuffer.wrap(bytes).int
- }
+ override fun hashCode(): Int {
+ var result = isCompressed.hashCode()
+ result = 31 * result + isProtected.hashCode()
+ result = 31 * result + isCorrupted.hashCode()
+ return result
}
companion object {
-
private val TAG = BinaryData::class.java.name
-
- @JvmField
- val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
- override fun createFromParcel(parcel: Parcel): BinaryData {
- return BinaryData(parcel)
- }
-
- override fun newArray(size: Int): Array {
- return arrayOfNulls(size)
- }
- }
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryFile.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryFile.kt
new file mode 100644
index 000000000..07c09a204
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryFile.kt
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2018 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.element.database
+
+import android.os.Parcel
+import android.os.Parcelable
+import android.util.Base64
+import android.util.Base64InputStream
+import android.util.Base64OutputStream
+import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.stream.readAllBytes
+import org.apache.commons.io.output.CountingOutputStream
+import java.io.*
+import java.nio.ByteBuffer
+import java.security.MessageDigest
+import java.util.zip.GZIPInputStream
+import java.util.zip.GZIPOutputStream
+import javax.crypto.Cipher
+import javax.crypto.CipherInputStream
+import javax.crypto.CipherOutputStream
+import javax.crypto.spec.IvParameterSpec
+
+class BinaryFile : BinaryData {
+
+ private var mDataFile: File? = null
+ private var mLength: Long = 0
+ private var mBinaryHash = 0
+ // Cipher to encrypt temp file
+ @Transient
+ private var cipherEncryption: Cipher = Cipher.getInstance(Database.LoadedKey.BINARY_CIPHER)
+ @Transient
+ private var cipherDecryption: Cipher = Cipher.getInstance(Database.LoadedKey.BINARY_CIPHER)
+
+ constructor() : super()
+
+ constructor(dataFile: File,
+ compressed: Boolean = false,
+ protected: Boolean = false) : super(compressed, protected) {
+ this.mDataFile = dataFile
+ this.mLength = 0
+ this.mBinaryHash = 0
+ }
+
+ constructor(parcel: Parcel) : super(parcel) {
+ parcel.readString()?.let {
+ mDataFile = File(it)
+ }
+ mLength = parcel.readLong()
+ mBinaryHash = parcel.readInt()
+ }
+
+ @Throws(IOException::class)
+ override fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream {
+ return buildInputStream(mDataFile, cipherKey)
+ }
+
+ @Throws(IOException::class)
+ override fun getOutputDataStream(cipherKey: Database.LoadedKey): OutputStream {
+ return buildOutputStream(mDataFile, cipherKey)
+ }
+
+ @Throws(IOException::class)
+ override fun getUnGzipInputDataStream(cipherKey: Database.LoadedKey): InputStream {
+ return if (isCompressed) {
+ GZIPInputStream(getInputDataStream(cipherKey))
+ } else {
+ getInputDataStream(cipherKey)
+ }
+ }
+
+ @Throws(IOException::class)
+ override fun getGzipOutputDataStream(cipherKey: Database.LoadedKey): OutputStream {
+ return if (isCompressed) {
+ GZIPOutputStream(getOutputDataStream(cipherKey))
+ } else {
+ getOutputDataStream(cipherKey)
+ }
+ }
+
+ @Throws(IOException::class)
+ private fun buildInputStream(file: File?, cipherKey: Database.LoadedKey): InputStream {
+ return when {
+ file != null && file.length() > 0 -> {
+ cipherDecryption.init(Cipher.DECRYPT_MODE, cipherKey.key, IvParameterSpec(cipherKey.iv))
+ Base64InputStream(CipherInputStream(FileInputStream(file), cipherDecryption), Base64.NO_WRAP)
+ }
+ else -> ByteArrayInputStream(ByteArray(0))
+ }
+ }
+
+ @Throws(IOException::class)
+ private fun buildOutputStream(file: File?, cipherKey: Database.LoadedKey): OutputStream {
+ return when {
+ file != null -> {
+ cipherEncryption.init(Cipher.ENCRYPT_MODE, cipherKey.key, IvParameterSpec(cipherKey.iv))
+ BinaryCountingOutputStream(Base64OutputStream(CipherOutputStream(FileOutputStream(file), cipherEncryption), Base64.NO_WRAP))
+ }
+ else -> throw IOException("Unable to write in an unknown file")
+ }
+ }
+
+ @Throws(IOException::class)
+ override fun compress(cipherKey: Database.LoadedKey) {
+ mDataFile?.let { concreteDataFile ->
+ // To compress, create a new binary with file
+ if (!isCompressed) {
+ // Encrypt the new gzipped temp file
+ val fileBinaryCompress = File(concreteDataFile.parent, concreteDataFile.name + "_temp")
+ getInputDataStream(cipherKey).use { inputStream ->
+ GZIPOutputStream(buildOutputStream(fileBinaryCompress, cipherKey)).use { outputStream ->
+ inputStream.readAllBytes { buffer ->
+ outputStream.write(buffer)
+ }
+ }
+ }
+ // Remove ungzip file
+ if (concreteDataFile.delete()) {
+ if (fileBinaryCompress.renameTo(concreteDataFile)) {
+ // Harmonize with database compression
+ isCompressed = true
+ }
+ }
+ }
+ }
+ }
+
+ @Throws(IOException::class)
+ override fun decompress(cipherKey: Database.LoadedKey) {
+ mDataFile?.let { concreteDataFile ->
+ if (isCompressed) {
+ // Encrypt the new ungzipped temp file
+ val fileBinaryDecompress = File(concreteDataFile.parent, concreteDataFile.name + "_temp")
+ getUnGzipInputDataStream(cipherKey).use { inputStream ->
+ buildOutputStream(fileBinaryDecompress, cipherKey).use { outputStream ->
+ inputStream.readAllBytes { buffer ->
+ outputStream.write(buffer)
+ }
+ }
+ }
+ // Remove gzip file
+ if (concreteDataFile.delete()) {
+ if (fileBinaryDecompress.renameTo(concreteDataFile)) {
+ // Harmonize with database compression
+ isCompressed = false
+ }
+ }
+ }
+ }
+ }
+
+ @Throws(IOException::class)
+ override fun clear() {
+ if (mDataFile != null && !mDataFile!!.delete())
+ throw IOException("Unable to delete temp file " + mDataFile!!.absolutePath)
+ }
+
+ override fun dataExists(): Boolean {
+ return mDataFile != null && mLength > 0
+ }
+
+ override fun getSize(): Long {
+ return mLength
+ }
+
+ /**
+ * Hash of the raw encrypted file in temp folder, only to compare binary data
+ */
+ @Throws(FileNotFoundException::class)
+ override fun binaryHash(): Int {
+ return mBinaryHash
+ }
+
+ override fun toString(): String {
+ return mDataFile.toString()
+ }
+
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ super.writeToParcel(dest, flags)
+ dest.writeString(mDataFile?.absolutePath)
+ dest.writeLong(mLength)
+ dest.writeInt(mBinaryHash)
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is BinaryFile) return false
+ if (!super.equals(other)) return false
+
+ return mDataFile != null && mDataFile == other.mDataFile
+ }
+
+ override fun hashCode(): Int {
+ var result = super.hashCode()
+ result = 31 * result + (mDataFile?.hashCode() ?: 0)
+ result = 31 * result + mLength.hashCode()
+ result = 31 * result + mBinaryHash
+ return result
+ }
+
+ /**
+ * Custom OutputStream to calculate the size and hash of binary file
+ */
+ private inner class BinaryCountingOutputStream(out: OutputStream): CountingOutputStream(out) {
+
+ private val mMessageDigest: MessageDigest
+ init {
+ mLength = 0
+ mMessageDigest = MessageDigest.getInstance("MD5")
+ mBinaryHash = 0
+ }
+
+ override fun beforeWrite(n: Int) {
+ super.beforeWrite(n)
+ mLength = byteCount
+ }
+
+ override fun write(idx: Int) {
+ super.write(idx)
+ mMessageDigest.update(idx.toByte())
+ }
+
+ override fun write(bts: ByteArray) {
+ super.write(bts)
+ mMessageDigest.update(bts)
+ }
+
+ override fun write(bts: ByteArray, st: Int, end: Int) {
+ super.write(bts, st, end)
+ mMessageDigest.update(bts, st, end)
+ }
+
+ override fun close() {
+ super.close()
+ mLength = byteCount
+ val bytes = mMessageDigest.digest()
+ mBinaryHash = ByteBuffer.wrap(bytes).int
+ }
+ }
+
+ companion object {
+
+ private val TAG = BinaryFile::class.java.name
+
+ @JvmField
+ val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): BinaryFile {
+ return BinaryFile(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryPool.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryPool.kt
index 827bd260e..5210fe778 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryPool.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryPool.kt
@@ -49,7 +49,7 @@ abstract class BinaryPool {
protection: Boolean = false): KeyBinary {
val fileInCache = File(cacheDirectory, "$poolId$creationId$binaryFileIncrement")
binaryFileIncrement++
- val newBinaryFile = BinaryData(fileInCache, compression, protection)
+ val newBinaryFile = BinaryFile(fileInCache, compression, protection)
val newKey = put(key, newBinaryFile)
return KeyBinary(newBinaryFile, newKey)
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt
index c804904fc..312b99b5f 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt
@@ -278,7 +278,7 @@ class DatabaseKDB : DatabaseVersioned() {
// Generate an unique new file
val fileInCache = File(cacheDirectory, binaryIncrement.toString())
binaryIncrement++
- return BinaryData(fileInCache)
+ return BinaryFile(fileInCache)
}
companion object {
diff --git a/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt b/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt
index 2f0863fae..5fcb9fb67 100644
--- a/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.model
import android.os.Parcel
import android.os.Parcelable
import com.kunzisoft.keepass.database.element.Attachment
-import com.kunzisoft.keepass.database.element.database.BinaryData
+import com.kunzisoft.keepass.database.element.database.BinaryByte
import com.kunzisoft.keepass.utils.readEnum
import com.kunzisoft.keepass.utils.writeEnum
@@ -33,7 +33,7 @@ data class EntryAttachmentState(var attachment: Attachment,
var previewState: AttachmentState = AttachmentState.NULL) : Parcelable {
constructor(parcel: Parcel) : this(
- parcel.readParcelable(Attachment::class.java.classLoader) ?: Attachment("", BinaryData()),
+ parcel.readParcelable(Attachment::class.java.classLoader) ?: Attachment("", BinaryByte()),
parcel.readEnum() ?: StreamDirection.DOWNLOAD,
parcel.readEnum() ?: AttachmentState.NULL,
parcel.readInt(),