mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Better binary reader
This commit is contained in:
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseDuplicateUuidException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseKeyFileEmptyException
|
||||
import com.kunzisoft.keepass.utils.MemoryUtil
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.*
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
@@ -124,7 +124,7 @@ abstract class PwDatabase<
|
||||
protected fun getFileKey(keyInputStream: InputStream): ByteArray {
|
||||
|
||||
val keyByteArrayOutputStream = ByteArrayOutputStream()
|
||||
MemoryUtil.copyStream(keyInputStream, keyByteArrayOutputStream)
|
||||
IOUtils.copy(keyInputStream, keyByteArrayOutputStream)
|
||||
val keyData = keyByteArrayOutputStream.toByteArray()
|
||||
|
||||
val keyByteArrayInputStream = ByteArrayInputStream(keyData)
|
||||
|
||||
@@ -36,12 +36,13 @@ class ProtectedBinary : Parcelable {
|
||||
private set
|
||||
private var data: ByteArray? = null
|
||||
private var dataFile: File? = null
|
||||
private var size: Int = 0
|
||||
|
||||
fun length(): Long {
|
||||
if (data != null)
|
||||
return data!!.size.toLong()
|
||||
return if (dataFile != null) size.toLong() else 0
|
||||
if (dataFile != null)
|
||||
return dataFile!!.length()
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,31 +52,24 @@ class ProtectedBinary : Parcelable {
|
||||
this.isProtected = false
|
||||
this.data = null
|
||||
this.dataFile = null
|
||||
this.size = 0
|
||||
}
|
||||
|
||||
constructor(protectedBinary: ProtectedBinary) {
|
||||
this.isProtected = protectedBinary.isProtected
|
||||
this.data = protectedBinary.data
|
||||
this.dataFile = protectedBinary.dataFile
|
||||
this.size = protectedBinary.size
|
||||
}
|
||||
|
||||
constructor(enableProtection: Boolean, data: ByteArray?) {
|
||||
this.isProtected = enableProtection
|
||||
this.data = data
|
||||
this.dataFile = null
|
||||
if (data != null)
|
||||
this.size = data.size
|
||||
else
|
||||
this.size = 0
|
||||
}
|
||||
|
||||
constructor(enableProtection: Boolean, dataFile: File, size: Int) {
|
||||
constructor(enableProtection: Boolean, dataFile: File) {
|
||||
this.isProtected = enableProtection
|
||||
this.data = null
|
||||
this.dataFile = dataFile
|
||||
this.size = size
|
||||
}
|
||||
|
||||
private constructor(parcel: Parcel) {
|
||||
@@ -83,7 +77,6 @@ class ProtectedBinary : Parcelable {
|
||||
data = ByteArray(parcel.readInt())
|
||||
parcel.readByteArray(data)
|
||||
dataFile = File(parcel.readString())
|
||||
size = parcel.readInt()
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
@@ -118,9 +111,7 @@ class ProtectedBinary : Parcelable {
|
||||
&& dataFile == null && other.dataFile == null)
|
||||
sameData = true
|
||||
|
||||
return isProtected == other.isProtected &&
|
||||
size == other.size &&
|
||||
sameData
|
||||
return isProtected == other.isProtected && sameData
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
@@ -128,7 +119,6 @@ class ProtectedBinary : Parcelable {
|
||||
var result = 0
|
||||
result = 31 * result + if (isProtected) 1 else 0
|
||||
result = 31 * result + dataFile!!.hashCode()
|
||||
result = 31 * result + Integer.valueOf(size).hashCode()
|
||||
result = 31 * result + Arrays.hashCode(data)
|
||||
return result
|
||||
}
|
||||
@@ -142,7 +132,6 @@ class ProtectedBinary : Parcelable {
|
||||
dest.writeInt(data?.size ?: 0)
|
||||
dest.writeByteArray(data)
|
||||
dest.writeString(dataFile?.absolutePath)
|
||||
dest.writeInt(size)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -38,6 +38,7 @@ 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
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
@@ -215,10 +216,10 @@ class ImporterV4(private val streamDir: File,
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun readInnerHeader(lis: LEDataInputStream, header: PwDbHeaderV4): Boolean {
|
||||
val fieldId = lis.read().toByte()
|
||||
private fun readInnerHeader(dataInputStream: LEDataInputStream, header: PwDbHeaderV4): Boolean {
|
||||
val fieldId = dataInputStream.read().toByte()
|
||||
|
||||
val size = lis.readInt()
|
||||
val size = dataInputStream.readInt()
|
||||
if (size < 0) throw IOException("Corrupted file")
|
||||
|
||||
when (fieldId) {
|
||||
@@ -226,23 +227,23 @@ class ImporterV4(private val streamDir: File,
|
||||
return false
|
||||
}
|
||||
PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomStreamID -> {
|
||||
val data = if (size > 0) lis.readBytes(size) else ByteArray(0)
|
||||
val data = if (size > 0) dataInputStream.readBytes(size) else ByteArray(0)
|
||||
header.setRandomStreamID(data)
|
||||
}
|
||||
PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomstreamKey -> {
|
||||
val data = if (size > 0) lis.readBytes(size) else ByteArray(0)
|
||||
val data = if (size > 0) dataInputStream.readBytes(size) else ByteArray(0)
|
||||
header.innerRandomStreamKey = data
|
||||
}
|
||||
PwDbHeaderV4.PwDbInnerHeaderV4Fields.Binary -> {
|
||||
val flag = lis.readBytes(1)[0].toInt() != 0
|
||||
val flag = dataInputStream.readBytes(1)[0].toInt() != 0
|
||||
val protectedFlag = flag && PwDbHeaderV4.KdbxBinaryFlags.Protected.toInt() != PwDbHeaderV4.KdbxBinaryFlags.None.toInt()
|
||||
val byteLength = size - 1
|
||||
// Read in a file
|
||||
val file = File(streamDir, unusedCacheFileName)
|
||||
FileOutputStream(file).use { outputStream ->
|
||||
lis.readBytes(byteLength) { outputStream.write(it) }
|
||||
dataInputStream.readBytes(byteLength) { outputStream.write(it) }
|
||||
}
|
||||
val protectedBinary = ProtectedBinary(protectedFlag, file, byteLength)
|
||||
val protectedBinary = ProtectedBinary(protectedFlag, file)
|
||||
mDatabase.binPool.add(protectedBinary)
|
||||
}
|
||||
else -> {
|
||||
@@ -832,7 +833,7 @@ class ImporterV4(private val streamDir: File,
|
||||
private fun readUnknown(xpp: XmlPullParser) {
|
||||
if (xpp.isEmptyElementTag) return
|
||||
|
||||
processNode(xpp)
|
||||
readProtectedBase64String(xpp)
|
||||
while (xpp.next() != XmlPullParser.END_DOCUMENT) {
|
||||
if (xpp.eventType == XmlPullParser.END_TAG) break
|
||||
if (xpp.eventType == XmlPullParser.START_TAG) continue
|
||||
@@ -923,7 +924,7 @@ class ImporterV4(private val streamDir: File,
|
||||
|
||||
@Throws(XmlPullParserException::class, IOException::class)
|
||||
private fun readProtectedString(xpp: XmlPullParser): ProtectedString {
|
||||
val buf = processNode(xpp)
|
||||
val buf = readProtectedBase64String(xpp)
|
||||
|
||||
if (buf != null) {
|
||||
try {
|
||||
@@ -932,23 +933,11 @@ class ImporterV4(private val streamDir: File,
|
||||
e.printStackTrace()
|
||||
throw IOException(e.localizedMessage)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ProtectedString(false, readString(xpp))
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun createProtectedBinaryFromData(protection: Boolean, data: ByteArray): ProtectedBinary {
|
||||
return if (data.size > MemoryUtil.BUFFER_SIZE_BYTES) {
|
||||
val file = File(streamDir, unusedCacheFileName)
|
||||
FileOutputStream(file).use { outputStream -> outputStream.write(data) }
|
||||
ProtectedBinary(protection, file, data.size)
|
||||
} else {
|
||||
ProtectedBinary(protection, data)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(XmlPullParserException::class, IOException::class)
|
||||
private fun readProtectedBinary(xpp: XmlPullParser): ProtectedBinary? {
|
||||
val ref = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrRef)
|
||||
@@ -960,32 +949,46 @@ class ImporterV4(private val streamDir: File,
|
||||
}
|
||||
|
||||
var compressed = false
|
||||
val comp = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrCompressed)
|
||||
if (comp != null) {
|
||||
compressed = comp.equals(PwDatabaseV4XML.ValTrue, ignoreCase = true)
|
||||
}
|
||||
var protected = false
|
||||
|
||||
val buf = processNode(xpp)
|
||||
if (buf != null) {
|
||||
createProtectedBinaryFromData(true, buf)
|
||||
if (xpp.attributeCount > 0) {
|
||||
val compress = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrCompressed)
|
||||
if (compress != null) {
|
||||
compressed = compress.equals(PwDatabaseV4XML.ValTrue, ignoreCase = true)
|
||||
}
|
||||
|
||||
val protect = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrProtected)
|
||||
if (protect != null) {
|
||||
protected = protect.equals(PwDatabaseV4XML.ValTrue, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
val base64 = readString(xpp)
|
||||
if (base64.isEmpty())
|
||||
return ProtectedBinary()
|
||||
val data = Base64.decode(base64, BASE_64_FLAG)
|
||||
|
||||
var data = Base64.decode(base64, BASE_64_FLAG)
|
||||
|
||||
if (compressed) {
|
||||
data = MemoryUtil.decompress(data)
|
||||
return if (!compressed && data.size <= MemoryUtil.BUFFER_SIZE_BYTES) {
|
||||
// Small data, don't need a file
|
||||
ProtectedBinary(protected, data)
|
||||
} else {
|
||||
val file = File(streamDir, unusedCacheFileName)
|
||||
if (compressed) {
|
||||
FileOutputStream(file).use { outputStream ->
|
||||
IOUtils.copy(GZIPInputStream(ByteArrayInputStream(data)), outputStream)
|
||||
}
|
||||
} else {
|
||||
FileOutputStream(file).use { outputStream ->
|
||||
outputStream.write(data)
|
||||
}
|
||||
}
|
||||
ProtectedBinary(protected, file)
|
||||
}
|
||||
|
||||
return createProtectedBinaryFromData(false, data)
|
||||
}
|
||||
|
||||
@Throws(IOException::class, XmlPullParserException::class)
|
||||
private fun readString(xpp: XmlPullParser): String {
|
||||
val buf = processNode(xpp)
|
||||
val buf = readProtectedBase64String(xpp)
|
||||
|
||||
if (buf != null) {
|
||||
try {
|
||||
@@ -993,7 +996,6 @@ class ImporterV4(private val streamDir: File,
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
throw IOException(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return xpp.safeNextText()
|
||||
@@ -1003,16 +1005,16 @@ class ImporterV4(private val streamDir: File,
|
||||
private fun readBase64String(xpp: XmlPullParser): ByteArray {
|
||||
|
||||
//readNextNode = false;
|
||||
Base64.decode(xpp.safeNextText(), BASE_64_FLAG)?.let { buffer ->
|
||||
val plainText = ByteArray(buffer.size)
|
||||
randomStream?.processBytes(buffer, 0, buffer.size, plainText, 0)
|
||||
Base64.decode(xpp.safeNextText(), BASE_64_FLAG)?.let { data ->
|
||||
val plainText = ByteArray(data.size)
|
||||
randomStream?.processBytes(data, 0, data.size, plainText, 0)
|
||||
return plainText
|
||||
}
|
||||
return ByteArray(0)
|
||||
}
|
||||
|
||||
@Throws(XmlPullParserException::class, IOException::class)
|
||||
private fun processNode(xpp: XmlPullParser): ByteArray? {
|
||||
private fun readProtectedBase64String(xpp: XmlPullParser): ByteArray? {
|
||||
//(xpp.getEventType() == XmlPullParser.START_TAG);
|
||||
|
||||
if (xpp.attributeCount > 0) {
|
||||
|
||||
@@ -45,6 +45,7 @@ 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.security.NoSuchAlgorithmException
|
||||
import java.security.SecureRandom
|
||||
@@ -53,6 +54,7 @@ import java.util.zip.GZIPOutputStream
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.CipherOutputStream
|
||||
|
||||
|
||||
class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputStream) : PwDbOutput<PwDbHeaderV4>(outputStream) {
|
||||
|
||||
private var randomStream: StreamCipher? = null
|
||||
@@ -380,49 +382,52 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
|
||||
}
|
||||
|
||||
/*
|
||||
TODO Make with pipe
|
||||
private void subWriteValue(ProtectedBinary value) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
try (InputStream inputStream = value.getData()) {
|
||||
@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;
|
||||
Log.e(TAG, "Can't write a null input stream.")
|
||||
return
|
||||
}
|
||||
|
||||
if (value.isProtected()) {
|
||||
xml.attribute(null, PwDatabaseV4XML.AttrProtected, PwDatabaseV4XML.ValTrue);
|
||||
if (value.isProtected) {
|
||||
xml.attribute(null, PwDatabaseV4XML.AttrProtected, PwDatabaseV4XML.ValTrue)
|
||||
|
||||
try (InputStream cypherInputStream =
|
||||
IOUtil.pipe(inputStream,
|
||||
o -> new org.spongycastle.crypto.io.CipherOutputStream(o, randomStream))) {
|
||||
writeInputStreamInBase64(cypherInputStream);
|
||||
}
|
||||
try {
|
||||
val cypherInputStream =
|
||||
IOUtil.pipe(inputStream,
|
||||
o -> new org.spongycastle.crypto.io.CipherOutputStream(o, randomStream))
|
||||
writeInputStreamInBase64(cypherInputStream)
|
||||
} catch (e: Exception) {}
|
||||
|
||||
} else {
|
||||
if (mDatabaseV4.getCompressionAlgorithm() == PwCompressionAlgorithm.GZip) {
|
||||
if (mDatabaseV4.compressionAlgorithm == PwCompressionAlgorithm.GZip) {
|
||||
xml.attribute(null, PwDatabaseV4XML.AttrCompressed, PwDatabaseV4XML.ValTrue)
|
||||
|
||||
xml.attribute(null, PwDatabaseV4XML.AttrCompressed, PwDatabaseV4XML.ValTrue);
|
||||
|
||||
try (InputStream gZipInputStream =
|
||||
IOUtil.pipe(inputStream, GZIPOutputStream::new, (int) value.length())) {
|
||||
writeInputStreamInBase64(gZipInputStream);
|
||||
}
|
||||
try {
|
||||
val gZipInputStream =
|
||||
IOUtil.pipe(inputStream, GZIPOutputStream::new, (int) value.length())
|
||||
writeInputStreamInBase64(gZipInputStream)
|
||||
} catch (e: Exception) {}
|
||||
|
||||
} else {
|
||||
writeInputStreamInBase64(inputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {}
|
||||
}
|
||||
|
||||
private void writeInputStreamInBase64(InputStream inputStream) throws IOException {
|
||||
try (InputStream base64InputStream =
|
||||
IOUtil.pipe(inputStream,
|
||||
o -> new Base64OutputStream(o, DEFAULT))) {
|
||||
@Throws(IOException::class)
|
||||
private fun writeInputStreamInBase64(inputStream: InputStream) {
|
||||
try {
|
||||
val base64InputStream = pipe(inputStream, Base64OutputStream(o, Base64OutputStream.DEFAULT))
|
||||
MemoryUtil.readBytes(base64InputStream,
|
||||
buffer -> xml.text(Arrays.toString(buffer)));
|
||||
}
|
||||
ActionReadBytes { buffer -> xml.text(Arrays.toString(buffer)) })
|
||||
|
||||
} catch (e: Exception) {}
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||
private fun subWriteValue(value: ProtectedBinary) {
|
||||
@@ -728,5 +733,16 @@ 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
@@ -26,14 +26,8 @@ import android.util.Log
|
||||
import com.kunzisoft.keepass.stream.ActionReadBytes
|
||||
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.*
|
||||
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.io.PipedInputStream
|
||||
import java.io.PipedOutputStream
|
||||
import java.util.HashMap
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
Reference in New Issue
Block a user