From b3f232c840516e7277f29e8a9a4dfd59b2612d1c Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 13 Nov 2019 15:57:52 +0100 Subject: [PATCH] Upgrade Importer V3 --- .../keepass/database/element/PwEntryV3.kt | 64 ++--------- .../element/security/ProtectedBinary.kt | 4 +- .../keepass/database/file/load/ImporterV3.kt | 4 +- .../database/file/save/PwEntryOutputV3.kt | 102 ++++++++---------- .../com/kunzisoft/keepass/utils/Types.java | 80 +++++++------- 5 files changed, 99 insertions(+), 155 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.kt index 9c69dfd6c..f379c07ad 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.kt @@ -21,11 +21,7 @@ package com.kunzisoft.keepass.database.element import android.os.Parcel import android.os.Parcelable - -import java.io.UnsupportedEncodingException -import java.util.Arrays -import java.util.UUID - +import java.util.* /** * Structure containing information about one entry. @@ -56,12 +52,11 @@ class PwEntryV3 : PwEntry, PwNodeV3Interface { * @return the actual binaryData byte array. */ var binaryData: ByteArray = ByteArray(0) - private set // Determine if this is a MetaStream entry val isMetaStream: Boolean get() { - if (Arrays.equals(binaryData, ByteArray(0))) return false + if (binaryData.contentEquals(ByteArray(0))) return false if (notes.isEmpty()) return false if (binaryDesc != PMS_ID_BINDESC) return false if (title.isEmpty()) return false @@ -85,10 +80,11 @@ class PwEntryV3 : PwEntry, PwNodeV3Interface { constructor(parcel: Parcel) : super(parcel) { title = parcel.readString() ?: title username = parcel.readString() ?: username - parcel.readByteArray(passwordBytes) + password = parcel.readString() ?: password url = parcel.readString() ?: url notes = parcel.readString() ?: notes binaryDesc = parcel.readString() ?: binaryDesc + binaryData = ByteArray(parcel.readInt()) parcel.readByteArray(binaryData) } @@ -104,10 +100,11 @@ class PwEntryV3 : PwEntry, PwNodeV3Interface { super.writeToParcel(dest, flags) dest.writeString(title) dest.writeString(username) - dest.writeByteArray(passwordBytes) + dest.writeString(password) dest.writeString(url) dest.writeString(notes) dest.writeString(binaryDesc) + dest.writeInt(binaryData.size) dest.writeByteArray(binaryData) } @@ -115,11 +112,7 @@ class PwEntryV3 : PwEntry, PwNodeV3Interface { super.updateWith(source) title = source.title username = source.username - - val passLen = source.passwordBytes.size - passwordBytes = ByteArray(passLen) - System.arraycopy(source.passwordBytes, 0, passwordBytes, 0, passLen) - + password = source.password url = source.url notes = source.notes binaryDesc = source.binaryDesc @@ -131,32 +124,10 @@ class PwEntryV3 : PwEntry, PwNodeV3Interface { override var username = "" - var passwordBytes: ByteArray = ByteArray(0) - private set - - /** Securely erase old password before copying new. */ - fun setPassword(buf: ByteArray, offset: Int, len: Int) { - fill(passwordBytes, 0.toByte()) - passwordBytes = ByteArray(len) - System.arraycopy(buf, offset, passwordBytes, 0, len) - } - /** * @return the actual password byte array. */ - override var password: String - get() = String(passwordBytes) - set(pass) { - var password: ByteArray - try { - password = pass.toByteArray(charset("UTF-8")) - setPassword(password, 0, password.size) - } catch (e: UnsupportedEncodingException) { - password = pass.toByteArray() - setPassword(password, 0, password.size) - } - - } + override var password = "" override var url = "" @@ -167,13 +138,6 @@ class PwEntryV3 : PwEntry, PwNodeV3Interface { override val type: Type get() = Type.ENTRY - fun setBinaryData(buf: ByteArray, offset: Int, len: Int) { - /** Securely erase old data before copying new. */ - fill(binaryData, 0.toByte()) - binaryData = ByteArray(len) - System.arraycopy(buf, offset, binaryData, 0, len) - } - companion object { /** Size of byte buffer needed to hold this struct. */ @@ -184,21 +148,13 @@ class PwEntryV3 : PwEntry, PwNodeV3Interface { @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { - override fun createFromParcel(`in`: Parcel): PwEntryV3 { - return PwEntryV3(`in`) + override fun createFromParcel(parcel: Parcel): PwEntryV3 { + return PwEntryV3(parcel) } override fun newArray(size: Int): Array { return arrayOfNulls(size) } } - - /** - * fill byte array - */ - private fun fill(array: ByteArray, value: Byte) { - for (i in array.indices) - array[i] = value - } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt index ff413ea05..2df9a1004 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt @@ -80,6 +80,7 @@ class ProtectedBinary : Parcelable { private constructor(parcel: Parcel) { isProtected = parcel.readByte().toInt() != 0 + data = ByteArray(parcel.readInt()) parcel.readByteArray(data) dataFile = File(parcel.readString()) size = parcel.readInt() @@ -130,8 +131,9 @@ class ProtectedBinary : Parcelable { override fun writeToParcel(dest: Parcel, flags: Int) { dest.writeByte((if (isProtected) 1 else 0).toByte()) + dest.writeInt(data?.size ?: 0) dest.writeByteArray(data) - dest.writeString(dataFile!!.absolutePath) + dest.writeString(dataFile?.absolutePath) dest.writeInt(size) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt index be42a451a..9fde49309 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt @@ -328,14 +328,14 @@ class ImporterV3 : Importer() { 0x0004 -> ent.title = Types.readCString(buf, offsetMutable) 0x0005 -> ent.url = Types.readCString(buf, offsetMutable) 0x0006 -> ent.username = Types.readCString(buf, offsetMutable) - 0x0007 -> ent.setPassword(buf, offsetMutable, Types.strlen(buf, offsetMutable)) + 0x0007 -> ent.password = Types.readPassword(buf, offsetMutable) 0x0008 -> ent.notes = Types.readCString(buf, offsetMutable) 0x0009 -> ent.creationTime = PwDate(buf, offsetMutable) 0x000A -> ent.lastModificationTime = PwDate(buf, offsetMutable) 0x000B -> ent.lastAccessTime = PwDate(buf, offsetMutable) 0x000C -> ent.expiryTime = PwDate(buf, offsetMutable) 0x000D -> ent.binaryDesc = Types.readCString(buf, offsetMutable) - 0x000E -> ent.setBinaryData(buf, offsetMutable, fieldSize) + 0x000E -> ent.binaryData = Types.readBytes(buf, offsetMutable, fieldSize) }// Ignore field } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt index 91d78a338..e1db46981 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt @@ -30,7 +30,7 @@ class PwEntryOutputV3 /** * Output the PwGroupV3 to the stream */ -(private val mPE: PwEntryV3, private val mOS: OutputStream) { +(private val mEntry: PwEntryV3, private val mOutputStream: OutputStream) { /** * Returns the number of bytes written by the stream * @return Number of bytes written @@ -45,98 +45,82 @@ class PwEntryOutputV3 length += 134 // Length of fixed size fields // UUID - mOS.write(UUID_FIELD_TYPE) - mOS.write(UUID_FIELD_SIZE) - mOS.write(Types.UUIDtoBytes(mPE.id)) + mOutputStream.write(UUID_FIELD_TYPE) + mOutputStream.write(UUID_FIELD_SIZE) + mOutputStream.write(Types.UUIDtoBytes(mEntry.id)) // Group ID - mOS.write(GROUPID_FIELD_TYPE) - mOS.write(LONG_FOUR) - mOS.write(LEDataOutputStream.writeIntBuf(mPE.parent!!.id)) + mOutputStream.write(GROUPID_FIELD_TYPE) + mOutputStream.write(LONG_FOUR) + mOutputStream.write(LEDataOutputStream.writeIntBuf(mEntry.parent!!.id)) // Image ID - mOS.write(IMAGEID_FIELD_TYPE) - mOS.write(LONG_FOUR) - mOS.write(LEDataOutputStream.writeIntBuf(mPE.icon.iconId)) + mOutputStream.write(IMAGEID_FIELD_TYPE) + mOutputStream.write(LONG_FOUR) + mOutputStream.write(LEDataOutputStream.writeIntBuf(mEntry.icon.iconId)) // Title - //byte[] title = mPE.title.getBytes("UTF-8"); - mOS.write(TITLE_FIELD_TYPE) - val titleLen = Types.writeCString(mPE.title, mOS) - length += titleLen.toLong() + //byte[] title = mEntry.title.getBytes("UTF-8"); + mOutputStream.write(TITLE_FIELD_TYPE) + length += Types.writeCString(mEntry.title, mOutputStream).toLong() // URL - mOS.write(URL_FIELD_TYPE) - val urlLen = Types.writeCString(mPE.url, mOS) - length += urlLen.toLong() + mOutputStream.write(URL_FIELD_TYPE) + length += Types.writeCString(mEntry.url, mOutputStream).toLong() // Username - mOS.write(USERNAME_FIELD_TYPE) - val userLen = Types.writeCString(mPE.username, mOS) - length += userLen.toLong() + mOutputStream.write(USERNAME_FIELD_TYPE) + length += Types.writeCString(mEntry.username, mOutputStream).toLong() // Password - val password = mPE.passwordBytes - mOS.write(PASSWORD_FIELD_TYPE) - mOS.write(LEDataOutputStream.writeIntBuf(password.size + 1)) - mOS.write(password) - mOS.write(0) - length += (password.size + 1).toLong() + mOutputStream.write(PASSWORD_FIELD_TYPE) + length += Types.writePassword(mEntry.password, mOutputStream).toLong() // Additional - mOS.write(ADDITIONAL_FIELD_TYPE) - val addlLen = Types.writeCString(mPE.notes, mOS) - length += addlLen.toLong() + mOutputStream.write(ADDITIONAL_FIELD_TYPE) + length += Types.writeCString(mEntry.notes, mOutputStream).toLong() // Create date - writeDate(CREATE_FIELD_TYPE, mPE.creationTime.byteArrayDate) + writeDate(CREATE_FIELD_TYPE, mEntry.creationTime.byteArrayDate) // Modification date - writeDate(MOD_FIELD_TYPE, mPE.lastModificationTime.byteArrayDate) + writeDate(MOD_FIELD_TYPE, mEntry.lastModificationTime.byteArrayDate) // Access date - writeDate(ACCESS_FIELD_TYPE, mPE.lastAccessTime.byteArrayDate) + writeDate(ACCESS_FIELD_TYPE, mEntry.lastAccessTime.byteArrayDate) // Expiration date - writeDate(EXPIRE_FIELD_TYPE, mPE.expiryTime.byteArrayDate) + writeDate(EXPIRE_FIELD_TYPE, mEntry.expiryTime.byteArrayDate) - // Binary desc - mOS.write(BINARY_DESC_FIELD_TYPE) - val descLen = Types.writeCString(mPE.binaryDesc, mOS) - length += descLen.toLong() - - // Binary data - val dataLen = writeByteArray(mPE.binaryData) - length += dataLen.toLong() + // Binary + writeBinary(mEntry.binaryData) // End - mOS.write(END_FIELD_TYPE) - mOS.write(ZERO_FIELD_SIZE) - } - - @Throws(IOException::class) - private fun writeByteArray(data: ByteArray?): Int { - val dataLen: Int = data?.size ?: 0 - mOS.write(BINARY_DATA_FIELD_TYPE) - mOS.write(LEDataOutputStream.writeIntBuf(dataLen)) - if (data != null) { - mOS.write(data) - } - - return dataLen + mOutputStream.write(END_FIELD_TYPE) + mOutputStream.write(ZERO_FIELD_SIZE) } @Throws(IOException::class) private fun writeDate(type: ByteArray, date: ByteArray?) { - mOS.write(type) - mOS.write(DATE_FIELD_SIZE) + mOutputStream.write(type) + mOutputStream.write(DATE_FIELD_SIZE) if (date != null) { - mOS.write(date) + mOutputStream.write(date) } else { - mOS.write(ZERO_FIVE) + mOutputStream.write(ZERO_FIVE) } } + @Throws(IOException::class) + private fun writeBinary(data: ByteArray?) { + mOutputStream.write(BINARY_DESC_FIELD_TYPE) + length += Types.writeCString(mEntry.binaryDesc, mOutputStream).toLong() + + val dataLen: Int = data?.size ?: 0 + mOutputStream.write(BINARY_DATA_FIELD_TYPE) + length += Types.writeBytes(data, dataLen, mOutputStream) + } + companion object { // Constants val UUID_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(1) diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/Types.java b/app/src/main/java/com/kunzisoft/keepass/utils/Types.java index 71eaacf12..25d431dee 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/Types.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/Types.java @@ -46,7 +46,7 @@ import com.kunzisoft.keepass.stream.LEDataOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.util.UUID; @@ -59,16 +59,20 @@ public class Types { public static long ULONG_MAX_VALUE = -1; + private static Charset defaultCharset = Charset.forName("UTF-8"); + + private static final byte[] CRLFbuf = { 0x0D, 0x0A }; + private static final String CRLF = new String(CRLFbuf); + private static final String SEP = System.getProperty("line.separator"); + private static final boolean REPLACE = !SEP.equals(CRLF); + /** Read an unsigned byte */ public static int readUByte( byte[] buf, int offset ) { return ((int)buf[offset] & 0xFF); } - /** Write an unsigned byte - * - * @param val - * @param buf - * @param offset + /** + * Write an unsigned byte */ public static void writeUByte(int val, byte[] buf, int offset) { buf[offset] = (byte)(val & 0xFF); @@ -85,42 +89,16 @@ public class Types { /** * Return len of null-terminated string (i.e. distance to null) * within a byte buffer. - * - * @param buf - * @param offset - * @return */ - public static int strlen( byte[] buf, int offset ) { + private static int strlen( byte[] buf, int offset ) { int len = 0; while( buf[offset + len] != 0 ) len++; return len; } - - - /** - * Copy a sequence of bytes into a new array. - * - * @param b - source array - * @param offset - first byte - * @param len - number of bytes - * @return new byte[len] - */ - public static byte[] extract( byte[] b, int offset, int len ) { - byte[] b2 = new byte[len]; - System.arraycopy( b, offset, b2, 0, len ); - return b2; - } - - - private static final byte[] CRLFbuf = { 0x0D, 0x0A }; - private static final String CRLF = new String(CRLFbuf); - private static final String SEP = System.getProperty("line.separator"); - private static final boolean REPLACE = ! SEP.equals(CRLF); - - public static String readCString(byte[] buf, int offset) throws UnsupportedEncodingException { - String jstring = new String(buf, offset, strlen(buf, offset), "UTF-8"); + public static String readCString(byte[] buf, int offset) { + String jstring = new String(buf, offset, strlen(buf, offset), defaultCharset); if ( REPLACE ) { jstring = jstring.replace(CRLF, SEP); @@ -141,7 +119,7 @@ public class Types { str = str.replace(SEP, CRLF); } - byte[] initial = str.getBytes("UTF-8"); + byte[] initial = str.getBytes(defaultCharset); int length = initial.length+1; os.write(LEDataOutputStream.writeIntBuf(length)); @@ -151,6 +129,33 @@ public class Types { return length; } + public static String readPassword(byte[] buf, int offset) { + return new String(buf, offset, strlen(buf, offset), defaultCharset); + } + + public static int writePassword(String str, OutputStream os) throws IOException { + byte[] initial = str.getBytes(defaultCharset); + int length = initial.length+1; + os.write(LEDataOutputStream.writeIntBuf(length)); + os.write(initial); + os.write(0x00); + return length; + } + + public static byte[] readBytes(byte[] buf, int offset, int len) { + byte[] binaryData = new byte[len]; + System.arraycopy(buf, offset, binaryData, 0, len); + return binaryData; + } + + public static int writeBytes(byte[] data, int dataLen, OutputStream os ) throws IOException { + os.write(LEDataOutputStream.writeIntBuf(dataLen)); + if (data != null) { + os.write(data); + } + return dataLen; + } + public static UUID bytestoUUID(byte[] buf) { return bytestoUUID(buf, 0); } @@ -167,15 +172,12 @@ public class Types { } return new UUID(msb, lsb); - } public static byte[] UUIDtoBytes(UUID uuid) { byte[] buf = new byte[16]; - LEDataOutputStream.writeLong(uuid.getMostSignificantBits(), buf, 0); LEDataOutputStream.writeLong(uuid.getLeastSignificantBits(), buf, 8); - return buf; }