Better binary reader

This commit is contained in:
J-Jamet
2019-11-25 21:14:39 +01:00
parent ebf6f6a52a
commit 1e71dd3031
5 changed files with 95 additions and 94 deletions

View File

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

View File

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

View File

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

View File

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

View File

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