Refactor binary class

This commit is contained in:
J-Jamet
2019-11-26 11:07:29 +01:00
parent 1e71dd3031
commit 32343dc937
8 changed files with 87 additions and 206 deletions

View File

@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element
import android.os.Parcel
import android.os.Parcelable
import com.kunzisoft.keepass.utils.MemoryUtil
import com.kunzisoft.keepass.utils.ParcelableUtil
import java.util.HashMap
@@ -48,7 +48,7 @@ class AutoType : Parcelable {
this.enabled = parcel.readByte().toInt() != 0
this.obfuscationOptions = parcel.readLong()
this.defaultSequence = parcel.readString() ?: defaultSequence
this.windowSeqPairs = MemoryUtil.readStringParcelableMap(parcel)
this.windowSeqPairs = ParcelableUtil.readStringParcelableMap(parcel)
}
override fun describeContents(): Int {
@@ -59,7 +59,7 @@ class AutoType : Parcelable {
dest.writeByte((if (enabled) 1 else 0).toByte())
dest.writeLong(obfuscationOptions)
dest.writeString(defaultSequence)
MemoryUtil.writeStringParcelableMap(dest, windowSeqPairs)
ParcelableUtil.writeStringParcelableMap(dest, windowSeqPairs)
}
fun put(key: String, value: String) {

View File

@@ -508,5 +508,7 @@ class PwDatabaseV4 : PwDatabase<UUID, UUID, PwGroupV4, PwEntryV4> {
private const val KeyDataElementName = "Data"
const val BASE_64_FLAG = Base64.DEFAULT
const val BUFFER_SIZE_BYTES = 3 * 128
}
}

View File

@@ -23,7 +23,7 @@ import android.os.Parcel
import android.os.Parcelable
import com.kunzisoft.keepass.database.element.security.ProtectedBinary
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.utils.MemoryUtil
import com.kunzisoft.keepass.utils.ParcelableUtil
import java.util.*
class PwEntryV4 : PwEntry<UUID, UUID, PwGroupV4, PwEntryV4>, PwNodeV4Interface {
@@ -96,9 +96,9 @@ class PwEntryV4 : PwEntry<UUID, UUID, PwGroupV4, PwEntryV4>, PwNodeV4Interface {
iconCustom = parcel.readParcelable(PwIconCustom::class.java.classLoader) ?: iconCustom
usageCount = parcel.readLong()
locationChanged = parcel.readParcelable(PwDate::class.java.classLoader) ?: locationChanged
customData = MemoryUtil.readStringParcelableMap(parcel)
fields = MemoryUtil.readStringParcelableMap(parcel, ProtectedString::class.java)
// TODO binaries = MemoryUtil.readStringParcelableMap(parcel, ProtectedBinary.class);
customData = ParcelableUtil.readStringParcelableMap(parcel)
fields = ParcelableUtil.readStringParcelableMap(parcel, ProtectedString::class.java)
// TODO binaries = ParcelableUtil.readStringParcelableMap(parcel, ProtectedBinary.class);
foregroundColor = parcel.readString() ?: foregroundColor
backgroundColor = parcel.readString() ?: backgroundColor
overrideURL = parcel.readString() ?: overrideURL
@@ -114,9 +114,9 @@ class PwEntryV4 : PwEntry<UUID, UUID, PwGroupV4, PwEntryV4>, PwNodeV4Interface {
dest.writeParcelable(iconCustom, flags)
dest.writeLong(usageCount)
dest.writeParcelable(locationChanged, flags)
MemoryUtil.writeStringParcelableMap(dest, customData)
MemoryUtil.writeStringParcelableMap(dest, flags, fields)
// TODO MemoryUtil.writeStringParcelableMap(dest, flags, binaries);
ParcelableUtil.writeStringParcelableMap(dest, customData)
ParcelableUtil.writeStringParcelableMap(dest, flags, fields)
// TODO ParcelableUtil.writeStringParcelableMap(dest, flags, binaries);
dest.writeString(foregroundColor)
dest.writeString(backgroundColor)
dest.writeString(overrideURL)

View File

@@ -69,7 +69,7 @@ class PwGroupV4 : PwGroup<UUID, UUID, PwGroupV4, PwEntryV4>, PwNodeV4Interface {
iconCustom = parcel.readParcelable(PwIconCustom::class.java.classLoader) ?: iconCustom
usageCount = parcel.readLong()
locationChanged = parcel.readParcelable(PwDate::class.java.classLoader) ?: locationChanged
// TODO customData = MemoryUtil.readStringParcelableMap(in);
// TODO customData = ParcelableUtil.readStringParcelableMap(in);
notes = parcel.readString() ?: notes
isExpanded = parcel.readByte().toInt() != 0
defaultAutoTypeSequence = parcel.readString() ?: defaultAutoTypeSequence
@@ -93,7 +93,7 @@ class PwGroupV4 : PwGroup<UUID, UUID, PwGroupV4, PwEntryV4>, PwNodeV4Interface {
dest.writeParcelable(iconCustom, flags)
dest.writeLong(usageCount)
dest.writeParcelable(locationChanged, flags)
// TODO MemoryUtil.writeStringParcelableMap(dest, customData);
// TODO ParcelableUtil.writeStringParcelableMap(dest, customData);
dest.writeString(notes)
dest.writeByte((if (isExpanded) 1 else 0).toByte())
dest.writeString(defaultAutoTypeSequence)

View File

@@ -26,6 +26,7 @@ import com.kunzisoft.keepass.crypto.StreamCipherFactory
import com.kunzisoft.keepass.crypto.engine.CipherEngine
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.PwDatabaseV4.Companion.BASE_64_FLAG
import com.kunzisoft.keepass.database.element.PwDatabaseV4.Companion.BUFFER_SIZE_BYTES
import com.kunzisoft.keepass.database.element.security.ProtectedBinary
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.database.exception.*
@@ -37,7 +38,6 @@ import com.kunzisoft.keepass.stream.HmacBlockInputStream
import com.kunzisoft.keepass.stream.LEDataInputStream
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils
import com.kunzisoft.keepass.utils.MemoryUtil
import org.apache.commons.io.IOUtils
import org.spongycastle.crypto.StreamCipher
import org.xmlpull.v1.XmlPullParser
@@ -968,7 +968,7 @@ class ImporterV4(private val streamDir: File,
return ProtectedBinary()
val data = Base64.decode(base64, BASE_64_FLAG)
return if (!compressed && data.size <= MemoryUtil.BUFFER_SIZE_BYTES) {
return if (!compressed && data.size <= BUFFER_SIZE_BYTES) {
// Small data, don't need a file
ProtectedBinary(protected, data)
} else {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 Brian Pellin.
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
@@ -20,50 +20,70 @@
package com.kunzisoft.keepass.database.file.save
import com.kunzisoft.keepass.database.element.PwDatabaseV4
import com.kunzisoft.keepass.database.element.PwDatabaseV4.Companion.BUFFER_SIZE_BYTES
import com.kunzisoft.keepass.database.file.PwDbHeaderV4
import com.kunzisoft.keepass.stream.ActionReadBytes
import com.kunzisoft.keepass.stream.LEDataOutputStream
import com.kunzisoft.keepass.utils.MemoryUtil
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import kotlin.experimental.or
class PwDbInnerHeaderOutputV4(private val db: PwDatabaseV4, private val header: PwDbHeaderV4, os: OutputStream) {
class PwDbInnerHeaderOutputV4(private val database: PwDatabaseV4,
private val header: PwDbHeaderV4,
outputStream: OutputStream) {
private val los: LEDataOutputStream = LEDataOutputStream(os)
private val dataOutputStream: LEDataOutputStream = LEDataOutputStream(outputStream)
@Throws(IOException::class)
fun output() {
los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomStreamID.toInt())
los.writeInt(4)
dataOutputStream.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomStreamID.toInt())
dataOutputStream.writeInt(4)
if (header.innerRandomStream == null)
throw IOException("Can't write innerRandomStream")
los.writeInt(header.innerRandomStream!!.id)
dataOutputStream.writeInt(header.innerRandomStream!!.id)
val streamKeySize = header.innerRandomStreamKey.size
los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomstreamKey.toInt())
los.writeInt(streamKeySize)
los.write(header.innerRandomStreamKey)
dataOutputStream.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomstreamKey.toInt())
dataOutputStream.writeInt(streamKeySize)
dataOutputStream.write(header.innerRandomStreamKey)
db.binPool.doForEachBinary { _, protectedBinary ->
database.binPool.doForEachBinary { _, protectedBinary ->
var flag = PwDbHeaderV4.KdbxBinaryFlags.None
if (protectedBinary.isProtected) {
flag = flag or PwDbHeaderV4.KdbxBinaryFlags.Protected
}
los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.Binary.toInt())
los.writeInt(protectedBinary.length().toInt() + 1) // TODO verify
los.write(flag.toInt())
dataOutputStream.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.Binary.toInt())
dataOutputStream.writeInt(protectedBinary.length().toInt() + 1) // TODO verify
dataOutputStream.write(flag.toInt())
protectedBinary.getData()?.let {
MemoryUtil.readBytes(it, ActionReadBytes { buffer ->
los.write(buffer)
readBytes(it, ActionReadBytes { buffer ->
dataOutputStream.write(buffer)
})
} ?: throw IOException("Can't write protected binary")
}
los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.EndOfHeader.toInt())
los.writeInt(0)
dataOutputStream.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.EndOfHeader.toInt())
dataOutputStream.writeInt(0)
}
@Throws(IOException::class)
fun readBytes(inputStream: InputStream, actionReadBytes: ActionReadBytes) {
val buffer = ByteArray(BUFFER_SIZE_BYTES)
var read = 0
while (read != -1) {
read = inputStream.read(buffer, 0, buffer.size)
if (read != -1) {
val optimizedBuffer: ByteArray = if (buffer.size == read) {
buffer
} else {
buffer.copyOf(read)
}
actionReadBytes.doAction(optimizedBuffer)
}
}
}
}

View File

@@ -30,6 +30,7 @@ import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
import com.kunzisoft.keepass.database.NodeHandler
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.PwDatabaseV4.Companion.BASE_64_FLAG
import com.kunzisoft.keepass.database.element.PwDatabaseV4.Companion.BUFFER_SIZE_BYTES
import com.kunzisoft.keepass.database.element.security.ProtectedBinary
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
@@ -40,13 +41,10 @@ import com.kunzisoft.keepass.stream.HashedBlockOutputStream
import com.kunzisoft.keepass.stream.HmacBlockOutputStream
import com.kunzisoft.keepass.stream.LEDataOutputStream
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils
import com.kunzisoft.keepass.utils.MemoryUtil
import org.joda.time.DateTime
import org.spongycastle.crypto.StreamCipher
import org.xmlpull.v1.XmlSerializer
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.io.*
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.util.*
@@ -362,7 +360,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
private fun writeObject(key: String, value: ProtectedBinary) {
private fun writeObject(key: String, binary: ProtectedBinary) {
xml.startTag(null, PwDatabaseV4XML.ElemBinary)
xml.startTag(null, PwDatabaseV4XML.ElemKey)
@@ -370,67 +368,19 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
xml.endTag(null, PwDatabaseV4XML.ElemKey)
xml.startTag(null, PwDatabaseV4XML.ElemValue)
val ref = mDatabaseV4.binPool.findKey(value)
val ref = mDatabaseV4.binPool.findKey(binary)
if (ref != null) {
xml.attribute(null, PwDatabaseV4XML.AttrRef, ref.toString())
} else {
subWriteValue(value)
writeBinary(binary)
}
xml.endTag(null, PwDatabaseV4XML.ElemValue)
xml.endTag(null, PwDatabaseV4XML.ElemBinary)
}
/*
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
private fun subWriteValue(value: ProtectedBinary) {
try {
val inputStream = value.getData()
if (inputStream == null) {
Log.e(TAG, "Can't write a null input stream.")
return
}
if (value.isProtected) {
xml.attribute(null, PwDatabaseV4XML.AttrProtected, PwDatabaseV4XML.ValTrue)
try {
val cypherInputStream =
IOUtil.pipe(inputStream,
o -> new org.spongycastle.crypto.io.CipherOutputStream(o, randomStream))
writeInputStreamInBase64(cypherInputStream)
} catch (e: Exception) {}
} else {
if (mDatabaseV4.compressionAlgorithm == PwCompressionAlgorithm.GZip) {
xml.attribute(null, PwDatabaseV4XML.AttrCompressed, PwDatabaseV4XML.ValTrue)
try {
val gZipInputStream =
IOUtil.pipe(inputStream, GZIPOutputStream::new, (int) value.length())
writeInputStreamInBase64(gZipInputStream)
} catch (e: Exception) {}
} else {
writeInputStreamInBase64(inputStream);
}
}
} catch (e: Exception) {}
}
@Throws(IOException::class)
private fun writeInputStreamInBase64(inputStream: InputStream) {
try {
val base64InputStream = pipe(inputStream, Base64OutputStream(o, Base64OutputStream.DEFAULT))
MemoryUtil.readBytes(base64InputStream,
ActionReadBytes { buffer -> xml.text(Arrays.toString(buffer)) })
} catch (e: Exception) {}
}
*/
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
private fun subWriteValue(value: ProtectedBinary) {
private fun writeBinary(value: ProtectedBinary) {
val valLength = value.length().toInt()
if (valLength > 0) {
@@ -448,8 +398,13 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
if (mDatabaseV4.compressionAlgorithm === PwCompressionAlgorithm.GZip) {
xml.attribute(null, PwDatabaseV4XML.AttrCompressed, PwDatabaseV4XML.ValTrue)
val compressData = MemoryUtil.compress(buffer)
xml.text(String(Base64.encode(compressData, BASE_64_FLAG)))
val byteArrayOutputStream = ByteArrayOutputStream()
val gzipOutputStream = GZIPOutputStream(byteArrayOutputStream)
copyStream(ByteArrayInputStream(buffer), gzipOutputStream)
// IOUtils.copy(ByteArrayInputStream(ByteArrayInputStream(buffer)), gzipOutputStream)
gzipOutputStream.close()
xml.text(String(Base64.encode(byteArrayOutputStream.toByteArray(), BASE_64_FLAG)))
} else {
xml.text(String(Base64.encode(buffer, BASE_64_FLAG)))
@@ -461,6 +416,23 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
}
}
@Throws(IOException::class)
fun copyStream(inputStream: InputStream, out: OutputStream) {
val buffer = ByteArray(BUFFER_SIZE_BYTES)
try {
var read = inputStream.read(buffer)
while (read != -1) {
out.write(buffer, 0, read)
read = inputStream.read(buffer)
if (Thread.interrupted()) {
throw InterruptedException()
}
}
} catch (error: OutOfMemoryError) {
throw IOException(error)
}
}
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
private fun writeObject(name: String, value: String, filterXmlChars: Boolean = false) {
var xmlString = value
@@ -703,7 +675,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
xml.startTag(null, PwDatabaseV4XML.ElemBinary)
xml.attribute(null, PwDatabaseV4XML.AttrId, key.toString())
subWriteValue(binary)
writeBinary(binary)
xml.endTag(null, PwDatabaseV4XML.ElemBinary)
}
@@ -733,16 +705,5 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
companion object {
private val TAG = PwDbV4Output::class.java.name
@Throws(IOException::class)
fun pipe(inputStream: InputStream, outputStream: OutputStream, buf: ByteArray) {
while (true) {
val amt = inputStream.read(buf)
if (amt < 0) {
break
}
outputStream.write(buf, 0, amt)
}
}
}
}

View File

@@ -21,111 +21,9 @@ package com.kunzisoft.keepass.utils
import android.os.Parcel
import android.os.Parcelable
import android.util.Log
import java.util.*
import com.kunzisoft.keepass.stream.ActionReadBytes
import org.apache.commons.io.IOUtils
import java.io.*
import java.util.HashMap
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
object MemoryUtil {
private val TAG = MemoryUtil::class.java.name
const val BUFFER_SIZE_BYTES = 3 * 128
@Throws(IOException::class)
fun copyStream(inputStream: InputStream, out: OutputStream) {
val buffer = ByteArray(BUFFER_SIZE_BYTES)
try {
var read = inputStream.read(buffer)
while (read != -1) {
out.write(buffer, 0, read)
read = inputStream.read(buffer)
if (Thread.interrupted()) {
throw InterruptedException()
}
}
} catch (error: OutOfMemoryError) {
throw IOException(error)
}
}
@Throws(IOException::class)
fun readBytes(inputStream: InputStream, actionReadBytes: ActionReadBytes) {
val buffer = ByteArray(BUFFER_SIZE_BYTES)
var read = 0
while (read != -1) {
read = inputStream.read(buffer, 0, buffer.size)
if (read != -1) {
val optimizedBuffer: ByteArray = if (buffer.size == read) {
buffer
} else {
buffer.copyOf(read)
}
actionReadBytes.doAction(optimizedBuffer)
}
}
}
@Throws(IOException::class)
fun decompress(input: ByteArray): ByteArray {
val bais = ByteArrayInputStream(input)
val gzis = GZIPInputStream(bais)
val baos = ByteArrayOutputStream()
copyStream(gzis, baos)
return baos.toByteArray()
}
@Throws(IOException::class)
fun compress(input: ByteArray): ByteArray {
val bais = ByteArrayInputStream(input)
val baos = ByteArrayOutputStream()
val gzos = GZIPOutputStream(baos)
copyStream(bais, gzos)
gzos.close()
return baos.toByteArray()
}
/**
* Compresses the input data using GZip and outputs the compressed data.
*
* @param input
* An [InputStream] containing the input raw data.
*
* @return An [InputStream] to the compressed data.
*/
fun compress(input: InputStream): InputStream {
val compressedDataStream = PipedInputStream(3 * 128)
Log.d(TAG, "About to compress input data using gzip asynchronously...")
val compressionOutput: PipedOutputStream
var gzipCompressedDataStream: GZIPOutputStream? = null
try {
compressionOutput = PipedOutputStream(compressedDataStream)
gzipCompressedDataStream = GZIPOutputStream(compressionOutput)
IOUtils.copy(input, gzipCompressedDataStream)
Log.e(TAG, "Successfully compressed input data using gzip.")
} catch (e: IOException) {
Log.e(TAG, "Failed to compress input data.", e)
} finally {
if (gzipCompressedDataStream != null) {
try {
gzipCompressedDataStream.close()
} catch (e: IOException) {
Log.e(TAG, "Failed to close gzip output stream.", e)
}
}
}
return compressedDataStream
}
object ParcelableUtil {
// For writing to a Parcel
fun <K : Parcelable, V : Parcelable> writeParcelableMap(