From af72098d60bcd40029003de2659b56ee78fe5bfc Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 30 Dec 2019 12:59:50 +0100 Subject: [PATCH] Refactor bytes utility methods --- .../com/kunzisoft/keepass/tests/PwDateTest.kt | 38 --- ...sTest.kt => StringDatabaseKDBUtilsTest.kt} | 85 +++---- .../kunzisoft/keepass/crypto/CryptoUtil.kt | 4 +- .../keepass/crypto/engine/AesEngine.kt | 21 +- .../keepass/crypto/engine/ChaCha20Engine.kt | 21 +- .../keepass/crypto/engine/TwofishEngine.kt | 25 +- .../keepass/crypto/keyDerivation/AesKdf.kt | 4 +- .../keepass/crypto/keyDerivation/Argon2Kdf.kt | 4 +- .../crypto/keyDerivation/KdfParameters.kt | 14 +- .../database/file/DatabaseHeaderKDBX.kt | 16 +- .../database/file/input/DatabaseInputKDBX.kt | 5 +- .../file/output/DatabaseHeaderOutputKDB.kt | 16 +- .../file/output/DatabaseHeaderOutputKDBX.kt | 12 +- .../file/output/DatabaseOutputKDBX.kt | 10 +- .../database/file/output/EntryOutputKDB.kt | 83 ++++--- .../database/file/output/GroupOutputKDB.kt | 53 ++--- .../keepass/database/search/UuidUtil.java | 8 +- .../keepass/stream/HashedBlockInputStream.kt | 3 +- .../keepass/stream/HmacBlockInputStream.kt | 7 +- .../keepass/stream/HmacBlockOutputStream.java | 8 +- .../stream/LittleEndianDataInputStream.kt | 11 +- .../stream/LittleEndianDataOutputStream.kt | 20 +- .../keepass/stream/StreamBytesUtils.kt | 190 ++++++++++----- .../keepass/utils/DatabaseInputOutputUtils.kt | 219 ------------------ .../keepass/utils/StringDatabaseKDBUtils.kt | 78 +++++++ .../keepass/utils/VariantDictionary.java | 14 +- 26 files changed, 435 insertions(+), 534 deletions(-) delete mode 100644 app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.kt rename app/src/androidTest/java/com/kunzisoft/keepass/tests/{DatabaseInputOutputUtilsTest.kt => StringDatabaseKDBUtilsTest.kt} (70%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/utils/DatabaseInputOutputUtils.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/utils/StringDatabaseKDBUtils.kt diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.kt deleted file mode 100644 index 4216c1bb3..000000000 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2019 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX 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. - * - * KeePass DX 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 KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.tests - -import junit.framework.TestCase - -import com.kunzisoft.keepass.database.element.DateInstant -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils -import org.junit.Assert - -class PwDateTest : TestCase() { - - fun testDate() { - val jDate = DateInstant(System.currentTimeMillis()) - val intermediate = DateInstant(jDate) - val cDate = DatabaseInputOutputUtils.bytes5ToDate(DatabaseInputOutputUtils.dateToBytes(intermediate.date)!!, 0) - - Assert.assertTrue("jDate and intermediate not equal", jDate == intermediate) - Assert.assertTrue("jDate $jDate and cDate $cDate not equal", cDate == jDate) - } -} diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/DatabaseInputOutputUtilsTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/StringDatabaseKDBUtilsTest.kt similarity index 70% rename from app/src/androidTest/java/com/kunzisoft/keepass/tests/DatabaseInputOutputUtilsTest.kt rename to app/src/androidTest/java/com/kunzisoft/keepass/tests/StringDatabaseKDBUtilsTest.kt index c3f223b9a..ffb0bee4e 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/DatabaseInputOutputUtilsTest.kt +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/StringDatabaseKDBUtilsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -19,18 +19,15 @@ */ package com.kunzisoft.keepass.tests +import com.kunzisoft.keepass.database.element.DateInstant +import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.ULONG_MAX_VALUE import com.kunzisoft.keepass.stream.* -import org.junit.Assert.assertArrayEquals - -import java.io.ByteArrayOutputStream -import java.util.Calendar -import java.util.Random - import junit.framework.TestCase +import org.junit.Assert.assertArrayEquals +import java.io.ByteArrayOutputStream +import java.util.* -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils - -class DatabaseInputOutputUtilsTest : TestCase() { +class StringDatabaseKDBUtilsTest : TestCase() { fun testReadWriteLongZero() { testReadWriteLong(0.toByte()) @@ -54,15 +51,9 @@ class DatabaseInputOutputUtilsTest : TestCase() { private fun testReadWriteLong(value: Byte) { val orig = ByteArray(8) - val dest = ByteArray(8) - - setArray(orig, value, 0, 8) - - val one = bytes64ToLong(orig, 0) - writeLong(one, dest, 0) - - assertArrayEquals(orig, dest) + setArray(orig, value, 8) + assertArrayEquals(orig, longTo8Bytes(bytes64ToLong(orig))) } fun testReadWriteIntZero() { @@ -79,24 +70,22 @@ class DatabaseInputOutputUtilsTest : TestCase() { private fun testReadWriteInt(value: Byte) { val orig = ByteArray(4) - val dest = ByteArray(4) for (i in 0..3) { orig[i] = 0 } - setArray(orig, value, 0, 4) + setArray(orig, value, 4) - val one = bytes4ToInt(orig, 0) - - writeInt(one, dest, 0) + val one = bytes4ToInt(orig) + val dest = intTo4Bytes(one) assertArrayEquals(orig, dest) } - private fun setArray(buf: ByteArray, value: Byte, offset: Int, size: Int) { - for (i in offset until offset + size) { + private fun setArray(buf: ByteArray, value: Byte, size: Int) { + for (i in 0 until size) { buf[i] = value } } @@ -107,11 +96,10 @@ class DatabaseInputOutputUtilsTest : TestCase() { orig[0] = 0 orig[1] = 1 - val one = bytes2ToUShort(orig, 0) - val dest = writeUShortBuf(one) + val one = bytes2ToUShort(orig) + val dest = uShortTo2Bytes(one) assertArrayEquals(orig, dest) - } fun testReadWriteShortMin() { @@ -124,15 +112,12 @@ class DatabaseInputOutputUtilsTest : TestCase() { private fun testReadWriteShort(value: Byte) { val orig = ByteArray(2) - val dest = ByteArray(2) + setArray(orig, value, 2) - setArray(orig, value, 0, 2) - - val one = bytes2ToUShort(orig, 0) - writeUShort(one, dest, 0) + val one = bytes2ToUShort(orig) + val dest = uShortTo2Bytes(one) assertArrayEquals(orig, dest) - } fun testReadWriteByteZero() { @@ -148,16 +133,8 @@ class DatabaseInputOutputUtilsTest : TestCase() { } private fun testReadWriteByte(value: Byte) { - val orig = ByteArray(1) - val dest = ByteArray(1) - - setArray(orig, value, 0, 1) - - val one = DatabaseInputOutputUtils.readUByte(orig, 0) - DatabaseInputOutputUtils.writeUByte(one, dest, 0) - - assertArrayEquals(orig, dest) - + val dest: Byte = uIntToByte(byteToUInt(value)) + assert(value == dest) } fun testDate() { @@ -167,27 +144,33 @@ class DatabaseInputOutputUtilsTest : TestCase() { expected.set(2008, 1, 2, 3, 4, 5) val actual = Calendar.getInstance() - DatabaseInputOutputUtils.dateToBytes(expected.time, cal)?.let { buf -> - actual.time = DatabaseInputOutputUtils.bytes5ToDate(buf, 0, cal).date + dateTo5Bytes(expected.time, cal)?.let { buf -> + actual.time = bytes5ToDate(buf, cal).date } + val jDate = DateInstant(System.currentTimeMillis()) + val intermediate = DateInstant(jDate) + val cDate = bytes5ToDate(dateTo5Bytes(intermediate.date)!!) + assertEquals("Year mismatch: ", 2008, actual.get(Calendar.YEAR)) assertEquals("Month mismatch: ", 1, actual.get(Calendar.MONTH)) assertEquals("Day mismatch: ", 2, actual.get(Calendar.DAY_OF_MONTH)) assertEquals("Hour mismatch: ", 3, actual.get(Calendar.HOUR_OF_DAY)) assertEquals("Minute mismatch: ", 4, actual.get(Calendar.MINUTE)) assertEquals("Second mismatch: ", 5, actual.get(Calendar.SECOND)) + assertTrue("jDate and intermediate not equal", jDate == intermediate) + assertTrue("jDate $jDate and cDate $cDate not equal", cDate == jDate) } fun testUUID() { val bUUID = ByteArray(16) Random().nextBytes(bUUID) - val uuid = DatabaseInputOutputUtils.bytesToUuid(bUUID) - val eUUID = DatabaseInputOutputUtils.uuidToBytes(uuid) + val uuid = bytes16ToUuid(bUUID) + val eUUID = uuidTo16Bytes(uuid) - val lUUID = bytes16ToUuid(bUUID, 0) - val leUUID = DatabaseInputOutputUtils.uuidToBytes(lUUID) + val lUUID = bytes16ToUuid(bUUID) + val leUUID = uuidTo16Bytes(lUUID) assertArrayEquals("UUID match failed", bUUID, eUUID) assertArrayEquals("UUID match failed", bUUID, leUUID) @@ -202,7 +185,7 @@ class DatabaseInputOutputUtilsTest : TestCase() { val bos = ByteArrayOutputStream() val leos = LittleEndianDataOutputStream(bos) - leos.writeLong(DatabaseInputOutputUtils.ULONG_MAX_VALUE) + leos.writeLong(ULONG_MAX_VALUE) leos.close() val uLongMax = bos.toByteArray() diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/CryptoUtil.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/CryptoUtil.kt index 5b6c7f0c2..468784dcf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/CryptoUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/CryptoUtil.kt @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.crypto import com.kunzisoft.keepass.stream.NullOutputStream -import com.kunzisoft.keepass.stream.writeLongBuf +import com.kunzisoft.keepass.stream.longTo8Bytes import java.io.IOException import java.security.DigestOutputStream import java.security.MessageDigest @@ -58,7 +58,7 @@ object CryptoUtil { throw RuntimeException(e) } - val pbR = writeLongBuf(r) + val pbR = longTo8Bytes(r) val part = hmac.doFinal(pbR) val copy = min(cbOut - pos, part.size) diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.kt index 01aabbc1f..284031a38 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.kt @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.crypto.engine import com.kunzisoft.keepass.crypto.CipherFactory import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils +import com.kunzisoft.keepass.stream.bytes16ToUuid import java.security.InvalidAlgorithmParameterException import java.security.InvalidKeyException import java.security.NoSuchAlgorithmException @@ -47,7 +47,22 @@ class AesEngine : CipherEngine() { companion object { - val CIPHER_UUID: UUID = DatabaseInputOutputUtils.bytesToUuid( - byteArrayOf(0x31.toByte(), 0xC1.toByte(), 0xF2.toByte(), 0xE6.toByte(), 0xBF.toByte(), 0x71.toByte(), 0x43.toByte(), 0x50.toByte(), 0xBE.toByte(), 0x58.toByte(), 0x05.toByte(), 0x21.toByte(), 0x6A.toByte(), 0xFC.toByte(), 0x5A.toByte(), 0xFF.toByte())) + val CIPHER_UUID: UUID = bytes16ToUuid( + byteArrayOf(0x31.toByte(), + 0xC1.toByte(), + 0xF2.toByte(), + 0xE6.toByte(), + 0xBF.toByte(), + 0x71.toByte(), + 0x43.toByte(), + 0x50.toByte(), + 0xBE.toByte(), + 0x58.toByte(), + 0x05.toByte(), + 0x21.toByte(), + 0x6A.toByte(), + 0xFC.toByte(), + 0x5A.toByte(), + 0xFF.toByte())) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/ChaCha20Engine.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/ChaCha20Engine.kt index 2828c5e22..73f523ca0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/ChaCha20Engine.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/ChaCha20Engine.kt @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.crypto.engine import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils +import com.kunzisoft.keepass.stream.bytes16ToUuid import org.spongycastle.jce.provider.BouncyCastleProvider import java.security.InvalidAlgorithmParameterException import java.security.InvalidKeyException @@ -50,7 +50,22 @@ class ChaCha20Engine : CipherEngine() { companion object { - val CIPHER_UUID: UUID = DatabaseInputOutputUtils.bytesToUuid( - byteArrayOf(0xD6.toByte(), 0x03.toByte(), 0x8A.toByte(), 0x2B.toByte(), 0x8B.toByte(), 0x6F.toByte(), 0x4C.toByte(), 0xB5.toByte(), 0xA5.toByte(), 0x24.toByte(), 0x33.toByte(), 0x9A.toByte(), 0x31.toByte(), 0xDB.toByte(), 0xB5.toByte(), 0x9A.toByte())) + val CIPHER_UUID: UUID = bytes16ToUuid( + byteArrayOf(0xD6.toByte(), + 0x03.toByte(), + 0x8A.toByte(), + 0x2B.toByte(), + 0x8B.toByte(), + 0x6F.toByte(), + 0x4C.toByte(), + 0xB5.toByte(), + 0xA5.toByte(), + 0x24.toByte(), + 0x33.toByte(), + 0x9A.toByte(), + 0x31.toByte(), + 0xDB.toByte(), + 0xB5.toByte(), + 0x9A.toByte())) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/TwofishEngine.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/TwofishEngine.kt index 5718ac33c..eb106eaf1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/TwofishEngine.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/TwofishEngine.kt @@ -21,13 +21,11 @@ package com.kunzisoft.keepass.crypto.engine import com.kunzisoft.keepass.crypto.CipherFactory import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils - +import com.kunzisoft.keepass.stream.bytes16ToUuid import java.security.InvalidAlgorithmParameterException import java.security.InvalidKeyException import java.security.NoSuchAlgorithmException -import java.util.UUID - +import java.util.* import javax.crypto.Cipher import javax.crypto.NoSuchPaddingException import javax.crypto.spec.IvParameterSpec @@ -53,7 +51,22 @@ class TwofishEngine : CipherEngine() { companion object { - val CIPHER_UUID: UUID = DatabaseInputOutputUtils.bytesToUuid( - byteArrayOf(0xAD.toByte(), 0x68.toByte(), 0xF2.toByte(), 0x9F.toByte(), 0x57.toByte(), 0x6F.toByte(), 0x4B.toByte(), 0xB9.toByte(), 0xA3.toByte(), 0x6A.toByte(), 0xD4.toByte(), 0x7A.toByte(), 0xF9.toByte(), 0x65.toByte(), 0x34.toByte(), 0x6C.toByte())) + val CIPHER_UUID: UUID = bytes16ToUuid( + byteArrayOf(0xAD.toByte(), + 0x68.toByte(), + 0xF2.toByte(), + 0x9F.toByte(), + 0x57.toByte(), + 0x6F.toByte(), + 0x4B.toByte(), + 0xB9.toByte(), + 0xA3.toByte(), + 0x6A.toByte(), + 0xD4.toByte(), + 0x7A.toByte(), + 0xF9.toByte(), + 0x65.toByte(), + 0x34.toByte(), + 0x6C.toByte())) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/AesKdf.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/AesKdf.kt index 4612ee9dd..705979fb8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/AesKdf.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/AesKdf.kt @@ -23,7 +23,7 @@ import android.content.res.Resources import com.kunzisoft.keepass.R import com.kunzisoft.keepass.crypto.CryptoUtil import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils +import com.kunzisoft.keepass.stream.bytes16ToUuid import java.io.IOException import java.security.SecureRandom import java.util.* @@ -88,7 +88,7 @@ class AesKdf internal constructor() : KdfEngine() { private const val DEFAULT_ROUNDS = 6000 - val CIPHER_UUID: UUID = DatabaseInputOutputUtils.bytesToUuid( + val CIPHER_UUID: UUID = bytes16ToUuid( byteArrayOf(0xC9.toByte(), 0xD9.toByte(), 0xF3.toByte(), diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Kdf.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Kdf.kt index 8e60b330d..cb5267096 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Kdf.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Kdf.kt @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.crypto.keyDerivation import android.content.res.Resources import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils +import com.kunzisoft.keepass.stream.bytes16ToUuid import java.io.IOException import java.security.SecureRandom import java.util.* @@ -126,7 +126,7 @@ class Argon2Kdf internal constructor() : KdfEngine() { companion object { - val CIPHER_UUID: UUID = DatabaseInputOutputUtils.bytesToUuid( + val CIPHER_UUID: UUID = bytes16ToUuid( byteArrayOf(0xEF.toByte(), 0x63.toByte(), 0x6D.toByte(), diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/KdfParameters.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/KdfParameters.kt index 70fd4f4ab..02b42f7b1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/KdfParameters.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/KdfParameters.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -19,20 +19,20 @@ */ package com.kunzisoft.keepass.crypto.keyDerivation -import com.kunzisoft.keepass.utils.VariantDictionary import com.kunzisoft.keepass.stream.LittleEndianDataInputStream import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils - +import com.kunzisoft.keepass.stream.bytes16ToUuid +import com.kunzisoft.keepass.stream.uuidTo16Bytes +import com.kunzisoft.keepass.utils.VariantDictionary import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.IOException -import java.util.UUID +import java.util.* class KdfParameters internal constructor(val uuid: UUID) : VariantDictionary() { fun setParamUUID() { - setByteArray(PARAM_UUID, DatabaseInputOutputUtils.uuidToBytes(uuid)) + setByteArray(PARAM_UUID, uuidTo16Bytes(uuid)) } companion object { @@ -46,7 +46,7 @@ class KdfParameters internal constructor(val uuid: UUID) : VariantDictionary() { val d = deserialize(lis) ?: return null - val uuid = DatabaseInputOutputUtils.bytesToUuid(d.getByteArray(PARAM_UUID)) + val uuid = bytes16ToUuid(d.getByteArray(PARAM_UUID)) val kdfP = KdfParameters(uuid) kdfP.copyTo(d) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt index 1880d604b..d5361438c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt @@ -31,7 +31,6 @@ import com.kunzisoft.keepass.database.element.group.GroupKDBX import com.kunzisoft.keepass.database.element.node.NodeKDBXInterface import com.kunzisoft.keepass.database.exception.VersionDatabaseException import com.kunzisoft.keepass.stream.* -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils import java.io.ByteArrayOutputStream import java.io.IOException import java.io.InputStream @@ -241,12 +240,12 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( throw IOException("Invalid cipher ID.") } - databaseV4.dataCipher = DatabaseInputOutputUtils.bytesToUuid(pbId) + databaseV4.dataCipher = bytes16ToUuid(pbId) } - private fun setTransformRound(roundsByte: ByteArray?) { + private fun setTransformRound(roundsByte: ByteArray) { assignAesKdfEngineIfNotExists() - val rounds = bytes64ToLong(roundsByte!!, 0) + val rounds = bytes64ToLong(roundsByte) databaseV4.kdfParameters?.setUInt64(AesKdf.PARAM_ROUNDS, rounds) databaseV4.numberKeyEncryptionRounds = rounds } @@ -257,7 +256,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( throw IOException("Invalid compression flags.") } - val flag = bytes4ToInt(pbFlags, 0) + val flag = bytes4ToInt(pbFlags) if (flag < 0 || flag >= CompressionAlgorithm.values().size) { throw IOException("Unrecognized compression flag.") } @@ -273,7 +272,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( throw IOException("Invalid stream id.") } - val id = bytes4ToInt(streamID, 0) + val id = bytes4ToInt(streamID) if (id < 0 || id >= CrsAlgorithm.values().size) { throw IOException("Invalid stream id.") } @@ -293,6 +292,9 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( } companion object { + + var ULONG_MAX_VALUE: Long = -1 + const val DBSIG_PRE2 = -0x4ab4049a const val DBSIG_2 = -0x4ab40499 @@ -321,7 +323,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( @Throws(IOException::class) fun computeHeaderHmac(header: ByteArray, key: ByteArray): ByteArray { - val blockKey = HmacBlockStream.GetHmacKey64(key, DatabaseInputOutputUtils.ULONG_MAX_VALUE) + val blockKey = HmacBlockStream.GetHmacKey64(key, ULONG_MAX_VALUE) val hmac: Mac try { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt index 1aa19adca..3b4ec6f44 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt @@ -43,7 +43,6 @@ import com.kunzisoft.keepass.database.file.DatabaseKDBXXML import com.kunzisoft.keepass.database.file.DateKDBXUtil import com.kunzisoft.keepass.stream.* import com.kunzisoft.keepass.tasks.ProgressTaskUpdater -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils import org.spongycastle.crypto.StreamCipher import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException @@ -823,7 +822,7 @@ class DatabaseInputKDBX(cacheDirectory: File, buf = buf8 } - val seconds = bytes64ToLong(buf, 0) + val seconds = bytes64ToLong(buf) utcDate = DateKDBXUtil.convertKDBX4Time(seconds) } else { @@ -883,7 +882,7 @@ class DatabaseInputKDBX(cacheDirectory: File, } val buf = Base64.decode(encoded, BASE_64_FLAG) - return DatabaseInputOutputUtils.bytesToUuid(buf) + return bytes16ToUuid(buf) } @Throws(IOException::class, XmlPullParserException::class) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDB.kt index 924da7352..13f2c1398 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDB.kt @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.database.file.output import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB -import com.kunzisoft.keepass.stream.writeIntBuf +import com.kunzisoft.keepass.stream.intTo4Bytes import java.io.IOException import java.io.OutputStream @@ -29,14 +29,14 @@ class DatabaseHeaderOutputKDB(private val mHeader: DatabaseHeaderKDB, @Throws(IOException::class) fun outputStart() { - mOutputStream.write(writeIntBuf(mHeader.signature1)) - mOutputStream.write(writeIntBuf(mHeader.signature2)) - mOutputStream.write(writeIntBuf(mHeader.flags)) - mOutputStream.write(writeIntBuf(mHeader.version)) + mOutputStream.write(intTo4Bytes(mHeader.signature1)) + mOutputStream.write(intTo4Bytes(mHeader.signature2)) + mOutputStream.write(intTo4Bytes(mHeader.flags)) + mOutputStream.write(intTo4Bytes(mHeader.version)) mOutputStream.write(mHeader.masterSeed) mOutputStream.write(mHeader.encryptionIV) - mOutputStream.write(writeIntBuf(mHeader.numGroups)) - mOutputStream.write(writeIntBuf(mHeader.numEntries)) + mOutputStream.write(intTo4Bytes(mHeader.numGroups)) + mOutputStream.write(intTo4Bytes(mHeader.numEntries)) } @Throws(IOException::class) @@ -47,7 +47,7 @@ class DatabaseHeaderOutputKDB(private val mHeader: DatabaseHeaderKDB, @Throws(IOException::class) fun outputEnd() { mOutputStream.write(mHeader.transformSeed) - mOutputStream.write(writeIntBuf(mHeader.numKeyEncRounds)) + mOutputStream.write(intTo4Bytes(mHeader.numKeyEncRounds)) } @Throws(IOException::class) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt index 57fa39b56..68f3d8b31 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt @@ -24,8 +24,8 @@ import com.kunzisoft.keepass.database.element.database.DatabaseKDBX import com.kunzisoft.keepass.database.exception.DatabaseOutputException import com.kunzisoft.keepass.database.file.DatabaseHeader import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX +import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.ULONG_MAX_VALUE import com.kunzisoft.keepass.stream.* -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils import com.kunzisoft.keepass.utils.VariantDictionary import java.io.ByteArrayOutputStream import java.io.IOException @@ -62,7 +62,7 @@ constructor(private val db: DatabaseKDBX, private val header: DatabaseHeaderKDBX val hmac: Mac try { hmac = Mac.getInstance("HmacSHA256") - val signingKey = SecretKeySpec(HmacBlockStream.GetHmacKey64(db.hmacKey, DatabaseInputOutputUtils.ULONG_MAX_VALUE), "HmacSHA256") + val signingKey = SecretKeySpec(HmacBlockStream.GetHmacKey64(db.hmacKey, ULONG_MAX_VALUE), "HmacSHA256") hmac.init(signingKey) } catch (e: NoSuchAlgorithmException) { throw DatabaseOutputException(e) @@ -82,13 +82,13 @@ constructor(private val db: DatabaseKDBX, private val header: DatabaseHeaderKDBX los.writeUInt(DatabaseHeaderKDBX.DBSIG_2.toLong()) los.writeUInt(header.version) - writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CipherID, DatabaseInputOutputUtils.uuidToBytes(db.dataCipher)) - writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CompressionFlags, writeIntBuf(DatabaseHeaderKDBX.getFlagFromCompression(db.compressionAlgorithm))) + writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CipherID, uuidTo16Bytes(db.dataCipher)) + writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CompressionFlags, intTo4Bytes(DatabaseHeaderKDBX.getFlagFromCompression(db.compressionAlgorithm))) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.MasterSeed, header.masterSeed) if (header.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) { writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformSeed, header.transformSeed) - writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformRounds, writeLongBuf(db.numberKeyEncryptionRounds)) + writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformRounds, longTo8Bytes(db.numberKeyEncryptionRounds)) } else { writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.KdfParameters, KdfParameters.serialize(db.kdfParameters!!)) } @@ -100,7 +100,7 @@ constructor(private val db: DatabaseKDBX, private val header: DatabaseHeaderKDBX if (header.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) { writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomstreamKey, header.innerRandomStreamKey) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes) - writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomStreamID, writeIntBuf(header.innerRandomStream!!.id)) + writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomStreamID, intTo4Bytes(header.innerRandomStream!!.id)) } if (db.containsPublicCustomData()) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt index 2b7d8235a..a10420fdd 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt @@ -46,11 +46,7 @@ import com.kunzisoft.keepass.database.exception.UnknownKDF import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX import com.kunzisoft.keepass.database.file.DatabaseKDBXXML import com.kunzisoft.keepass.database.file.DateKDBXUtil -import com.kunzisoft.keepass.stream.HashedBlockOutputStream -import com.kunzisoft.keepass.stream.HmacBlockOutputStream -import com.kunzisoft.keepass.stream.readBytes -import com.kunzisoft.keepass.stream.writeLongBuf -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils +import com.kunzisoft.keepass.stream.* import org.joda.time.DateTime import org.spongycastle.crypto.StreamCipher import org.xmlpull.v1.XmlSerializer @@ -393,7 +389,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, } else { val dt = DateTime(value) val seconds = DateKDBXUtil.convertDateToKDBX4Time(dt) - val buf = writeLongBuf(seconds) + val buf = longTo8Bytes(seconds) val b64 = String(Base64.encode(buf, BASE_64_FLAG)) writeObject(name, b64) } @@ -417,7 +413,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) private fun writeUuid(name: String, uuid: UUID) { - val data = DatabaseInputOutputUtils.uuidToBytes(uuid) + val data = uuidTo16Bytes(uuid) writeObject(name, String(Base64.encode(data, BASE_64_FLAG))) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt index 4d03a3138..e14e5ae0c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt @@ -21,12 +21,11 @@ package com.kunzisoft.keepass.database.file.output import com.kunzisoft.keepass.database.element.database.DatabaseKDB import com.kunzisoft.keepass.database.element.entry.EntryKDB -import com.kunzisoft.keepass.stream.readBytes -import com.kunzisoft.keepass.stream.writeIntBuf -import com.kunzisoft.keepass.stream.writeUShortBuf -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils +import com.kunzisoft.keepass.stream.* +import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils import java.io.IOException import java.io.OutputStream +import java.nio.charset.Charset class EntryOutputKDB /** @@ -49,54 +48,54 @@ class EntryOutputKDB // UUID mOutputStream.write(UUID_FIELD_TYPE) mOutputStream.write(UUID_FIELD_SIZE) - mOutputStream.write(DatabaseInputOutputUtils.uuidToBytes(mEntry.id)) + mOutputStream.write(uuidTo16Bytes(mEntry.id)) // Group ID mOutputStream.write(GROUPID_FIELD_TYPE) mOutputStream.write(LONG_FOUR) - mOutputStream.write(writeIntBuf(mEntry.parent!!.id)) + mOutputStream.write(intTo4Bytes(mEntry.parent!!.id)) // Image ID mOutputStream.write(IMAGEID_FIELD_TYPE) mOutputStream.write(LONG_FOUR) - mOutputStream.write(writeIntBuf(mEntry.icon.iconId)) + mOutputStream.write(intTo4Bytes(mEntry.icon.iconId)) // Title //byte[] title = mEntry.title.getBytes("UTF-8"); mOutputStream.write(TITLE_FIELD_TYPE) - length += DatabaseInputOutputUtils.writeCString(mEntry.title, mOutputStream).toLong() + length += StringDatabaseKDBUtils.writeStringToBytes(mEntry.title, mOutputStream).toLong() // URL mOutputStream.write(URL_FIELD_TYPE) - length += DatabaseInputOutputUtils.writeCString(mEntry.url, mOutputStream).toLong() + length += StringDatabaseKDBUtils.writeStringToBytes(mEntry.url, mOutputStream).toLong() // Username mOutputStream.write(USERNAME_FIELD_TYPE) - length += DatabaseInputOutputUtils.writeCString(mEntry.username, mOutputStream).toLong() + length += StringDatabaseKDBUtils.writeStringToBytes(mEntry.username, mOutputStream).toLong() // Password mOutputStream.write(PASSWORD_FIELD_TYPE) - length += DatabaseInputOutputUtils.writePassword(mEntry.password, mOutputStream).toLong() + length += writePassword(mEntry.password, mOutputStream).toLong() // Additional mOutputStream.write(ADDITIONAL_FIELD_TYPE) - length += DatabaseInputOutputUtils.writeCString(mEntry.notes, mOutputStream).toLong() + length += StringDatabaseKDBUtils.writeStringToBytes(mEntry.notes, mOutputStream).toLong() // Create date - writeDate(CREATE_FIELD_TYPE, DatabaseInputOutputUtils.dateToBytes(mEntry.creationTime.date)) + writeDate(CREATE_FIELD_TYPE, dateTo5Bytes(mEntry.creationTime.date)) // Modification date - writeDate(MOD_FIELD_TYPE, DatabaseInputOutputUtils.dateToBytes(mEntry.lastModificationTime.date)) + writeDate(MOD_FIELD_TYPE, dateTo5Bytes(mEntry.lastModificationTime.date)) // Access date - writeDate(ACCESS_FIELD_TYPE, DatabaseInputOutputUtils.dateToBytes(mEntry.lastAccessTime.date)) + writeDate(ACCESS_FIELD_TYPE, dateTo5Bytes(mEntry.lastAccessTime.date)) // Expiration date - writeDate(EXPIRE_FIELD_TYPE, DatabaseInputOutputUtils.dateToBytes(mEntry.expiryTime.date)) + writeDate(EXPIRE_FIELD_TYPE, dateTo5Bytes(mEntry.expiryTime.date)) // Binary description mOutputStream.write(BINARY_DESC_FIELD_TYPE) - length += DatabaseInputOutputUtils.writeCString(mEntry.binaryDescription, mOutputStream).toLong() + length += StringDatabaseKDBUtils.writeStringToBytes(mEntry.binaryDescription, mOutputStream).toLong() // Binary mOutputStream.write(BINARY_DATA_FIELD_TYPE) @@ -108,7 +107,7 @@ class EntryOutputKDB 0 // TODO if length > UInt.maxvalue show exception } // Write data length - mOutputStream.write(writeIntBuf(binaryDataLengthRightSize)) + mOutputStream.write(intTo4Bytes(binaryDataLengthRightSize)) // Write data if (binaryDataLength > 0) { binaryData?.getInputDataStream().use { inputStream -> @@ -136,30 +135,40 @@ class EntryOutputKDB } } + @Throws(IOException::class) + private fun writePassword(str: String, os: OutputStream): Int { + val initial = str.toByteArray(Charset.forName("UTF-8")) + val length = initial.size + 1 + os.write(intTo4Bytes(length)) + os.write(initial) + os.write(0x00) + return length + } + companion object { // Constants - val UUID_FIELD_TYPE:ByteArray = writeUShortBuf(1) - val GROUPID_FIELD_TYPE:ByteArray = writeUShortBuf(2) - val IMAGEID_FIELD_TYPE:ByteArray = writeUShortBuf(3) - val TITLE_FIELD_TYPE:ByteArray = writeUShortBuf(4) - val URL_FIELD_TYPE:ByteArray = writeUShortBuf(5) - val USERNAME_FIELD_TYPE:ByteArray = writeUShortBuf(6) - val PASSWORD_FIELD_TYPE:ByteArray = writeUShortBuf(7) - val ADDITIONAL_FIELD_TYPE:ByteArray = writeUShortBuf(8) - val CREATE_FIELD_TYPE:ByteArray = writeUShortBuf(9) - val MOD_FIELD_TYPE:ByteArray = writeUShortBuf(10) - val ACCESS_FIELD_TYPE:ByteArray = writeUShortBuf(11) - val EXPIRE_FIELD_TYPE:ByteArray = writeUShortBuf(12) - val BINARY_DESC_FIELD_TYPE:ByteArray = writeUShortBuf(13) - val BINARY_DATA_FIELD_TYPE:ByteArray = writeUShortBuf(14) - val END_FIELD_TYPE:ByteArray = writeUShortBuf(0xFFFF) - val LONG_FOUR:ByteArray = writeIntBuf(4) - val UUID_FIELD_SIZE:ByteArray = writeIntBuf(16) - val DATE_FIELD_SIZE:ByteArray = writeIntBuf(5) + val UUID_FIELD_TYPE:ByteArray = uShortTo2Bytes(1) + val GROUPID_FIELD_TYPE:ByteArray = uShortTo2Bytes(2) + val IMAGEID_FIELD_TYPE:ByteArray = uShortTo2Bytes(3) + val TITLE_FIELD_TYPE:ByteArray = uShortTo2Bytes(4) + val URL_FIELD_TYPE:ByteArray = uShortTo2Bytes(5) + val USERNAME_FIELD_TYPE:ByteArray = uShortTo2Bytes(6) + val PASSWORD_FIELD_TYPE:ByteArray = uShortTo2Bytes(7) + val ADDITIONAL_FIELD_TYPE:ByteArray = uShortTo2Bytes(8) + val CREATE_FIELD_TYPE:ByteArray = uShortTo2Bytes(9) + val MOD_FIELD_TYPE:ByteArray = uShortTo2Bytes(10) + val ACCESS_FIELD_TYPE:ByteArray = uShortTo2Bytes(11) + val EXPIRE_FIELD_TYPE:ByteArray = uShortTo2Bytes(12) + val BINARY_DESC_FIELD_TYPE:ByteArray = uShortTo2Bytes(13) + val BINARY_DATA_FIELD_TYPE:ByteArray = uShortTo2Bytes(14) + val END_FIELD_TYPE:ByteArray = uShortTo2Bytes(0xFFFF) + val LONG_FOUR:ByteArray = intTo4Bytes(4) + val UUID_FIELD_SIZE:ByteArray = intTo4Bytes(16) + val DATE_FIELD_SIZE:ByteArray = intTo4Bytes(5) val IMAGEID_FIELD_SIZE:ByteArray = LONG_FOUR val LEVEL_FIELD_SIZE:ByteArray = LONG_FOUR val FLAGS_FIELD_SIZE:ByteArray = LONG_FOUR - val ZERO_FIELD_SIZE:ByteArray = writeIntBuf(0) + val ZERO_FIELD_SIZE:ByteArray = intTo4Bytes(0) val ZERO_FIVE:ByteArray = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/GroupOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/GroupOutputKDB.kt index 2cae753c9..559a8f0b8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/GroupOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/GroupOutputKDB.kt @@ -20,9 +20,10 @@ package com.kunzisoft.keepass.database.file.output import com.kunzisoft.keepass.database.element.group.GroupKDB -import com.kunzisoft.keepass.stream.writeIntBuf -import com.kunzisoft.keepass.stream.writeUShortBuf -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils +import com.kunzisoft.keepass.stream.dateTo5Bytes +import com.kunzisoft.keepass.stream.intTo4Bytes +import com.kunzisoft.keepass.stream.uShortTo2Bytes +import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils import java.io.IOException import java.io.OutputStream @@ -38,46 +39,46 @@ class GroupOutputKDB (private val mGroup: GroupKDB, private val mOutputStream: O // Group ID mOutputStream.write(GROUPID_FIELD_TYPE) mOutputStream.write(GROUPID_FIELD_SIZE) - mOutputStream.write(writeIntBuf(mGroup.id)) + mOutputStream.write(intTo4Bytes(mGroup.id)) // Name mOutputStream.write(NAME_FIELD_TYPE) - DatabaseInputOutputUtils.writeCString(mGroup.title, mOutputStream) + StringDatabaseKDBUtils.writeStringToBytes(mGroup.title, mOutputStream) // Create date mOutputStream.write(CREATE_FIELD_TYPE) mOutputStream.write(DATE_FIELD_SIZE) - mOutputStream.write(DatabaseInputOutputUtils.dateToBytes(mGroup.creationTime.date)) + mOutputStream.write(dateTo5Bytes(mGroup.creationTime.date)) // Modification date mOutputStream.write(MOD_FIELD_TYPE) mOutputStream.write(DATE_FIELD_SIZE) - mOutputStream.write(DatabaseInputOutputUtils.dateToBytes(mGroup.lastModificationTime.date)) + mOutputStream.write(dateTo5Bytes(mGroup.lastModificationTime.date)) // Access date mOutputStream.write(ACCESS_FIELD_TYPE) mOutputStream.write(DATE_FIELD_SIZE) - mOutputStream.write(DatabaseInputOutputUtils.dateToBytes(mGroup.lastAccessTime.date)) + mOutputStream.write(dateTo5Bytes(mGroup.lastAccessTime.date)) // Expiration date mOutputStream.write(EXPIRE_FIELD_TYPE) mOutputStream.write(DATE_FIELD_SIZE) - mOutputStream.write(DatabaseInputOutputUtils.dateToBytes(mGroup.expiryTime.date)) + mOutputStream.write(dateTo5Bytes(mGroup.expiryTime.date)) // Image ID mOutputStream.write(IMAGEID_FIELD_TYPE) mOutputStream.write(IMAGEID_FIELD_SIZE) - mOutputStream.write(writeIntBuf(mGroup.icon.iconId)) + mOutputStream.write(intTo4Bytes(mGroup.icon.iconId)) // Level mOutputStream.write(LEVEL_FIELD_TYPE) mOutputStream.write(LEVEL_FIELD_SIZE) - mOutputStream.write(writeUShortBuf(mGroup.level)) + mOutputStream.write(uShortTo2Bytes(mGroup.level)) // Flags mOutputStream.write(FLAGS_FIELD_TYPE) mOutputStream.write(FLAGS_FIELD_SIZE) - mOutputStream.write(writeIntBuf(mGroup.flags)) + mOutputStream.write(intTo4Bytes(mGroup.flags)) // End mOutputStream.write(END_FIELD_TYPE) @@ -86,23 +87,23 @@ class GroupOutputKDB (private val mGroup: GroupKDB, private val mOutputStream: O companion object { // Constants - val GROUPID_FIELD_TYPE: ByteArray = writeUShortBuf(1) - val NAME_FIELD_TYPE:ByteArray = writeUShortBuf(2) - val CREATE_FIELD_TYPE:ByteArray = writeUShortBuf(3) - val MOD_FIELD_TYPE:ByteArray = writeUShortBuf(4) - val ACCESS_FIELD_TYPE:ByteArray = writeUShortBuf(5) - val EXPIRE_FIELD_TYPE:ByteArray = writeUShortBuf(6) - val IMAGEID_FIELD_TYPE:ByteArray = writeUShortBuf(7) - val LEVEL_FIELD_TYPE:ByteArray = writeUShortBuf(8) - val FLAGS_FIELD_TYPE:ByteArray = writeUShortBuf(9) - val END_FIELD_TYPE:ByteArray = writeUShortBuf(0xFFFF) - val LONG_FOUR:ByteArray = writeIntBuf(4) + val GROUPID_FIELD_TYPE: ByteArray = uShortTo2Bytes(1) + val NAME_FIELD_TYPE:ByteArray = uShortTo2Bytes(2) + val CREATE_FIELD_TYPE:ByteArray = uShortTo2Bytes(3) + val MOD_FIELD_TYPE:ByteArray = uShortTo2Bytes(4) + val ACCESS_FIELD_TYPE:ByteArray = uShortTo2Bytes(5) + val EXPIRE_FIELD_TYPE:ByteArray = uShortTo2Bytes(6) + val IMAGEID_FIELD_TYPE:ByteArray = uShortTo2Bytes(7) + val LEVEL_FIELD_TYPE:ByteArray = uShortTo2Bytes(8) + val FLAGS_FIELD_TYPE:ByteArray = uShortTo2Bytes(9) + val END_FIELD_TYPE:ByteArray = uShortTo2Bytes(0xFFFF) + val LONG_FOUR:ByteArray = intTo4Bytes(4) val GROUPID_FIELD_SIZE:ByteArray = LONG_FOUR - val DATE_FIELD_SIZE:ByteArray = writeIntBuf(5) + val DATE_FIELD_SIZE:ByteArray = intTo4Bytes(5) val IMAGEID_FIELD_SIZE:ByteArray = LONG_FOUR - val LEVEL_FIELD_SIZE:ByteArray = writeIntBuf(2) + val LEVEL_FIELD_SIZE:ByteArray = intTo4Bytes(2) val FLAGS_FIELD_SIZE:ByteArray = LONG_FOUR - val ZERO_FIELD_SIZE:ByteArray = writeIntBuf(0) + val ZERO_FIELD_SIZE:ByteArray = intTo4Bytes(0) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/UuidUtil.java b/app/src/main/java/com/kunzisoft/keepass/database/search/UuidUtil.java index b3119ca01..f81124a57 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/UuidUtil.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/UuidUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -19,16 +19,16 @@ */ package com.kunzisoft.keepass.database.search; -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils; - import java.util.UUID; +import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.uuidTo16Bytes; + public class UuidUtil { public static String toHexString(UUID uuid) { if (uuid == null) { return null; } - byte[] buf = DatabaseInputOutputUtils.INSTANCE.uuidToBytes(uuid); + byte[] buf = uuidTo16Bytes(uuid); int len = buf.length; if (len == 0) { return ""; } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.kt b/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.kt index 2a7f930f3..de7fca7e9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.kt @@ -19,7 +19,6 @@ */ package com.kunzisoft.keepass.stream -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils import java.io.IOException import java.io.InputStream import java.security.MessageDigest @@ -145,7 +144,7 @@ class HashedBlockInputStream(inputStream: InputStream) : InputStream() { if (!readHashedBlock()) return -1 } - val output = DatabaseInputOutputUtils.readUByte(buffer, bufferPos) + val output = byteToUInt(buffer[bufferPos]) bufferPos++ return output diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockInputStream.kt b/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockInputStream.kt index d9eb3ae8c..d75b701c6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockInputStream.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockInputStream.kt @@ -19,7 +19,6 @@ */ package com.kunzisoft.keepass.stream -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils import java.io.IOException import java.io.InputStream import java.security.InvalidKeyException @@ -44,7 +43,7 @@ class HmacBlockInputStream(baseStream: InputStream, private val verify: Boolean, if (!readSafeBlock()) return -1 } - val output = DatabaseInputOutputUtils.readUByte(buffer, bufferPos) + val output = byteToUInt(buffer[bufferPos]) bufferPos++ return output @@ -93,12 +92,12 @@ class HmacBlockInputStream(baseStream: InputStream, private val verify: Boolean, throw IOException("File corrupted") } - val pbBlockIndex = writeLongBuf(blockIndex) + val pbBlockIndex = longTo8Bytes(blockIndex) val pbBlockSize = baseStream.readBytes(4) if (pbBlockSize.size != 4) { throw IOException("File corrupted") } - val blockSize = bytes4ToInt(pbBlockSize, 0) + val blockSize = bytes4ToInt(pbBlockSize) bufferPos = 0 buffer = baseStream.readBytes(blockSize) diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockOutputStream.java b/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockOutputStream.java index 14ac24c2e..32d96cc28 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockOutputStream.java +++ b/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockOutputStream.java @@ -27,8 +27,8 @@ import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.writeIntBuf; -import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.writeLongBuf; +import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.intTo4Bytes; +import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.longTo8Bytes; public class HmacBlockOutputStream extends OutputStream { private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024; @@ -92,8 +92,8 @@ public class HmacBlockOutputStream extends OutputStream { } private void WriteSafeBlock() throws IOException { - byte[] bufBlockIndex = writeLongBuf(blockIndex); - byte[] blockSizeBuf = writeIntBuf(bufferPos); + byte[] bufBlockIndex = longTo8Bytes(blockIndex); + byte[] blockSizeBuf = intTo4Bytes(bufferPos); byte[] blockHmac; byte[] blockKey = HmacBlockStream.GetHmacKey64(key, blockIndex); diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataInputStream.kt b/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataInputStream.kt index c864790a9..834f33ff6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataInputStream.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataInputStream.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -24,7 +24,6 @@ import java.io.InputStream /** * Little endian version of the DataInputStream - * @author bpellin */ class LittleEndianDataInputStream(private val baseStream: InputStream) : InputStream() { @@ -42,18 +41,12 @@ class LittleEndianDataInputStream(private val baseStream: InputStream) : InputSt return baseStream.readBytes4ToInt() } - @Throws(IOException::class) - fun readLong(): Long { - val buf = readBytes(8) - return bytes64ToLong(buf, 0) - } - @Throws(IOException::class) fun readUShort(): Int { val buf = ByteArray(2) if (baseStream.read(buf, 0, 2) != 2) throw IOException("Unable to read UShort value") - return bytes2ToUShort(buf, 0) + return bytes2ToUShort(buf) } @Throws(IOException::class) diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataOutputStream.kt b/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataOutputStream.kt index 9c64c61df..c5dd2cd43 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataOutputStream.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataOutputStream.kt @@ -22,8 +22,6 @@ package com.kunzisoft.keepass.stream import java.io.IOException import java.io.OutputStream -import com.kunzisoft.keepass.stream.writeIntBuf - /** * Little Endian version of the DataOutputStream @@ -32,8 +30,8 @@ import com.kunzisoft.keepass.stream.writeIntBuf class LittleEndianDataOutputStream(private val baseStream: OutputStream) : OutputStream() { @Throws(IOException::class) - fun writeUInt(uint: Long) { - baseStream.write(writeIntBuf(uint.toInt())) + fun writeUInt(uint: Long) { // TODO UInt + baseStream.write(intTo4Bytes(uint.toInt())) } @Throws(IOException::class) @@ -63,24 +61,16 @@ class LittleEndianDataOutputStream(private val baseStream: OutputStream) : Outpu @Throws(IOException::class) fun writeLong(value: Long) { - val buf = ByteArray(8) - - writeLong(value, buf, 0) - baseStream.write(buf) + baseStream.write(longTo8Bytes(value)) } @Throws(IOException::class) fun writeInt(value: Int) { - val buf = ByteArray(4) - writeInt(value, buf, 0) - - baseStream.write(buf) + baseStream.write(intTo4Bytes(value)) } @Throws(IOException::class) fun writeUShort(value: Int) { - val buf = ByteArray(2) - writeUShort(value, buf, 0) - baseStream.write(buf) + baseStream.write(uShortTo2Bytes(value)) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/StreamBytesUtils.kt b/app/src/main/java/com/kunzisoft/keepass/stream/StreamBytesUtils.kt index 312526637..4b6442efe 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/StreamBytesUtils.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/StreamBytesUtils.kt @@ -20,8 +20,7 @@ package com.kunzisoft.keepass.stream import com.kunzisoft.keepass.database.element.DateInstant -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils.bytes5ToDate -import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils.bytesToString +import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils.bytesToString import java.io.IOException import java.io.InputStream import java.util.* @@ -87,27 +86,27 @@ fun InputStream.readBytes4ToUInt(): Long { @Throws(IOException::class) fun InputStream.readBytes4ToInt(): Int { - return bytes4ToInt(readBytesLength(4), 0) + return bytes4ToInt(readBytesLength(4)) } @Throws(IOException::class) fun InputStream.readBytes2ToUShort(): Int { - return bytes2ToUShort(readBytesLength(2), 0) + return bytes2ToUShort(readBytesLength(2)) } @Throws(IOException::class) fun InputStream.readBytes5ToDate(): DateInstant { - return bytes5ToDate(readBytesLength(5), 0) + return bytes5ToDate(readBytesLength(5)) } @Throws(IOException::class) fun InputStream.readBytes16ToUuid(): UUID { - return bytes16ToUuid(readBytesLength(16), 0) + return bytes16ToUuid(readBytesLength(16)) } @Throws(IOException::class) fun InputStream.readBytesToString(length: Int, replaceCRLF: Boolean = true): String { - return bytesToString(this.readBytesLength(length), 0, replaceCRLF) + return bytesToString(this.readBytesLength(length), replaceCRLF) } @Throws(IOException::class) @@ -123,99 +122,164 @@ fun InputStream.readBytesLength(length: Int): ByteArray { /** * Read an unsigned 16-bit value. */ -fun bytes2ToUShort(buf: ByteArray, offset: Int): Int { - return ((buf[offset].toInt() and 0xFF) - + (buf[offset + 1].toInt() and 0xFF shl 8)) +fun bytes2ToUShort(buf: ByteArray): Int { + return ((buf[0].toInt() and 0xFF) + + (buf[1].toInt() and 0xFF shl 8)) } /** * Read a 64 bit long */ -fun bytes64ToLong(buf: ByteArray, offset: Int): Long { - return ((buf[offset].toLong() and 0xFF) - + (buf[offset + 1].toLong() and 0xFF shl 8) - + (buf[offset + 2].toLong() and 0xFF shl 16) - + (buf[offset + 3].toLong() and 0xFF shl 24) - + (buf[offset + 4].toLong() and 0xFF shl 32) - + (buf[offset + 5].toLong() and 0xFF shl 40) - + (buf[offset + 6].toLong() and 0xFF shl 48) - + (buf[offset + 7].toLong() and 0xFF shl 56)) +fun bytes64ToLong(buf: ByteArray): Long { + return ((buf[0].toLong() and 0xFF) + + (buf[1].toLong() and 0xFF shl 8) + + (buf[2].toLong() and 0xFF shl 16) + + (buf[3].toLong() and 0xFF shl 24) + + (buf[4].toLong() and 0xFF shl 32) + + (buf[5].toLong() and 0xFF shl 40) + + (buf[6].toLong() and 0xFF shl 48) + + (buf[7].toLong() and 0xFF shl 56)) } private const val INT_TO_LONG_MASK: Long = 0xffffffffL -fun bytes4ToUInt(buf: ByteArray, offset: Int): Long { - return bytes4ToInt(buf, offset).toLong() and INT_TO_LONG_MASK +fun bytes4ToUInt(buf: ByteArray): Long { + return bytes4ToInt(buf).toLong() and INT_TO_LONG_MASK } /** * Read a 32-bit value. */ -fun bytes4ToInt(buf: ByteArray, offset: Int): Int { - return ((buf[offset].toInt() and 0xFF) - + (buf[offset + 1].toInt() and 0xFF shl 8) - + (buf[offset + 2].toInt() and 0xFF shl 16) - + (buf[offset + 3].toInt() and 0xFF shl 24)) +fun bytes4ToInt(buf: ByteArray): Int { + return ((buf[0].toInt() and 0xFF) + + (buf[1].toInt() and 0xFF shl 8) + + (buf[2].toInt() and 0xFF shl 16) + + (buf[3].toInt() and 0xFF shl 24)) } -fun bytes16ToUuid(buf: ByteArray, offset: Int): UUID { +fun bytes16ToUuid(buf: ByteArray): UUID { var lsb: Long = 0 for (i in 15 downTo 8) { - lsb = lsb shl 8 or (buf[i + offset].toLong() and 0xff) + lsb = lsb shl 8 or (buf[i].toLong() and 0xff) } var msb: Long = 0 for (i in 7 downTo 0) { - msb = msb shl 8 or (buf[i + offset].toLong() and 0xff) + msb = msb shl 8 or (buf[i].toLong() and 0xff) } return UUID(msb, lsb) } -fun writeIntBuf(value: Int): ByteArray { - val buf = ByteArray(4) - writeInt(value, buf, 0) - return buf +/** + * Unpack date from 5 byte format. The five bytes at 'offset' are unpacked + * to a java.util.Date instance. + */ +fun bytes5ToDate(buf: ByteArray, calendar: Calendar = Calendar.getInstance()): DateInstant { + val dateSize = 5 + val cDate = ByteArray(dateSize) + System.arraycopy(buf, 0, cDate, 0, dateSize) + + val readOffset = 0 + val dw1 = byteToUInt(cDate[readOffset]) + val dw2 = byteToUInt(cDate[readOffset + 1]) + val dw3 = byteToUInt(cDate[readOffset + 2]) + val dw4 = byteToUInt(cDate[readOffset + 3]) + val dw5 = byteToUInt(cDate[readOffset + 4]) + + // Unpack 5 byte structure to date and time + val year = dw1 shl 6 or (dw2 shr 2) + val month = dw2 and 0x00000003 shl 2 or (dw3 shr 6) + + val day = dw3 shr 1 and 0x0000001F + val hour = dw3 and 0x00000001 shl 4 or (dw4 shr 4) + val minute = dw4 and 0x0000000F shl 2 or (dw5 shr 6) + val second = dw5 and 0x0000003F + + // File format is a 1 based month, java Calendar uses a zero based month + // File format is a 1 based day, java Calendar uses a 1 based day + calendar.set(year, month - 1, day, hour, minute, second) + + return DateInstant(calendar.time) } -fun writeUShortBuf(value: Int): ByteArray { - val buf = ByteArray(2) - writeUShort(value, buf, 0) +/** + * Convert an unsigned Integer to byte + */ +fun uIntToByte(value: Int): Byte { + return (value and 0xFF).toByte() +} + +/** + * Write a 32-bit value. + */ +fun intTo4Bytes(value: Int): ByteArray { + val buf = ByteArray(4) + for (i in 0 until 4) { + buf[i] = (value.ushr(8 * i) and 0xFF).toByte() + } return buf } /** * Write an unsigned 16-bit value */ -fun writeUShort(value: Int, buf: ByteArray, offset: Int) { - buf[offset + 0] = (value and 0x00FF).toByte() - buf[offset + 1] = (value and 0xFF00 shr 8).toByte() -} - -/** - * Write a 32-bit value. - */ -fun writeInt(value: Int, buf: ByteArray, offset: Int) { - buf[offset + 0] = (value and 0xFF).toByte() - buf[offset + 1] = (value.ushr(8) and 0xFF).toByte() - buf[offset + 2] = (value.ushr(16) and 0xFF).toByte() - buf[offset + 3] = (value.ushr(24) and 0xFF).toByte() -} - -fun writeLongBuf(value: Long): ByteArray { - val buf = ByteArray(8) - writeLong(value, buf, 0) +fun uShortTo2Bytes(value: Int): ByteArray { + val buf = ByteArray(2) + buf[0] = (value and 0x00FF).toByte() + buf[1] = (value and 0xFF00 shr 8).toByte() return buf } -fun writeLong(value: Long, buf: ByteArray, offset: Int) { - buf[offset + 0] = (value and 0xFF).toByte() - buf[offset + 1] = (value.ushr(8) and 0xFF).toByte() - buf[offset + 2] = (value.ushr(16) and 0xFF).toByte() - buf[offset + 3] = (value.ushr(24) and 0xFF).toByte() - buf[offset + 4] = (value.ushr(32) and 0xFF).toByte() - buf[offset + 5] = (value.ushr(40) and 0xFF).toByte() - buf[offset + 6] = (value.ushr(48) and 0xFF).toByte() - buf[offset + 7] = (value.ushr(56) and 0xFF).toByte() +fun longTo8Bytes(value: Long): ByteArray { + val buf = ByteArray(8) + for (i in 0 until 8) { + buf[i] = (value.ushr(8 * i) and 0xFF).toByte() + } + return buf +} + +fun uuidTo16Bytes(uuid: UUID): ByteArray { + val buf = ByteArray(16) + for (i in 0 until 8) { + buf[i] = (uuid.mostSignificantBits.ushr(8 * i) and 0xFF).toByte() + } + for (i in 8 until 16) { + buf[i] = (uuid.leastSignificantBits.ushr(8 * i) and 0xFF).toByte() + } + return buf +} + +fun dateTo5Bytes(date: Date?, calendar: Calendar = Calendar.getInstance()): ByteArray? { + if (date == null) { + return null + } + + val buf = ByteArray(5) + calendar.time = date + + val year = calendar.get(Calendar.YEAR) + // File format is a 1 based month, java Calendar uses a zero based month + val month = calendar.get(Calendar.MONTH) + 1 + // File format is a 1 based day, java Calendar uses a 1 based day + val day = calendar.get(Calendar.DAY_OF_MONTH) + val hour = calendar.get(Calendar.HOUR_OF_DAY) + val minute = calendar.get(Calendar.MINUTE) + val second = calendar.get(Calendar.SECOND) + + buf[0] = uIntToByte(year shr 6 and 0x0000003F) + buf[1] = uIntToByte(year and 0x0000003F shl 2 or (month shr 2 and 0x00000003)) + buf[2] = (month and 0x00000003 shl 6 + or (day and 0x0000001F shl 1) or (hour shr 4 and 0x00000001)).toByte() + buf[3] = (hour and 0x0000000F shl 4 or (minute shr 2 and 0x0000000F)).toByte() + buf[4] = (minute and 0x00000003 shl 6 or (second and 0x0000003F)).toByte() + + return buf +} + + +/** Convert a byte to an unsigned byte */ +fun byteToUInt(byte: Byte): Int { + return byte.toInt() and 0xFF } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/DatabaseInputOutputUtils.kt b/app/src/main/java/com/kunzisoft/keepass/utils/DatabaseInputOutputUtils.kt deleted file mode 100644 index f3355966c..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/utils/DatabaseInputOutputUtils.kt +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX 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. - * - * KeePass DX 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 KeePass DX. If not, see . - * - * -KeePass for J2ME - -Copyright 2007 Naomaru Itoi - -This file was derived from - -Java clone of KeePass - A KeePass file viewer for Java -Copyright 2006 Bill Zwicky - -This program 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; version 2 - -This program 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 this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -package com.kunzisoft.keepass.utils - -import com.kunzisoft.keepass.database.element.DateInstant -import com.kunzisoft.keepass.stream.bytes16ToUuid -import com.kunzisoft.keepass.stream.writeIntBuf -import com.kunzisoft.keepass.stream.writeLong -import java.io.IOException -import java.io.OutputStream -import java.nio.charset.Charset -import java.util.* - - -/** - * Tools for slicing and dicing Java and KeePass data types. - * - * @author Bill Zwicky @pobox.com> - */ -object DatabaseInputOutputUtils { - - var ULONG_MAX_VALUE: Long = -1 - - private val defaultCharset = Charset.forName("UTF-8") - - private val CRLFbuf = byteArrayOf(0x0D, 0x0A) - private val CRLF = String(CRLFbuf) - private val SEP = System.getProperty("line.separator") - private val REPLACE = SEP != CRLF - - /** Read an unsigned byte */ - fun readUByte(buf: ByteArray, offset: Int): Int { - return buf[offset].toInt() and 0xFF - } - - /** - * Write an unsigned byte - */ - fun writeUByte(value: Int, buf: ByteArray, offset: Int) { - buf[offset] = (value and 0xFF).toByte() - } - - fun writeUByte(value: Int): Byte { - val buf = ByteArray(1) - - writeUByte(value, buf, 0) - - return buf[0] - } - - /** - * Return len of null-terminated string (i.e. distance to null) - * within a byte buffer. - */ - fun bytesStringLength(buf: ByteArray, offset: Int): Int { - var len = 0 - while (buf[offset + len].toInt() != 0) - len++ - return len - } - - fun bytesToString(buf: ByteArray, offset: Int, replaceCRLF: Boolean = true): String { - var jstring = String(buf, offset, bytesStringLength(buf, offset), defaultCharset) - if (replaceCRLF && REPLACE) { - jstring = jstring.replace(CRLF, SEP!!) - } - return jstring - } - - @Throws(IOException::class) - fun writeCString(string: String?, os: OutputStream): Int { - var str = string - if (str == null) { - // Write out a null character - os.write(writeIntBuf(1)) - os.write(0x00) - return 0 - } - - if (REPLACE) { - str = str.replace(SEP!!, CRLF) - } - - val initial = str.toByteArray(defaultCharset) - - val length = initial.size + 1 - os.write(writeIntBuf(length)) - os.write(initial) - os.write(0x00) - - return length - } - - /** - * Unpack date from 5 byte format. The five bytes at 'offset' are unpacked - * to a java.util.Date instance. - */ - fun bytes5ToDate(buf: ByteArray, offset: Int, calendar: Calendar = Calendar.getInstance()): DateInstant { - val dateSize = 5 - val cDate = ByteArray(dateSize) - System.arraycopy(buf, offset, cDate, 0, dateSize) - - val readOffset = 0 - val dw1 = readUByte(cDate, readOffset) - val dw2 = readUByte(cDate, readOffset + 1) - val dw3 = readUByte(cDate, readOffset + 2) - val dw4 = readUByte(cDate, readOffset + 3) - val dw5 = readUByte(cDate, readOffset + 4) - - // Unpack 5 byte structure to date and time - val year = dw1 shl 6 or (dw2 shr 2) - val month = dw2 and 0x00000003 shl 2 or (dw3 shr 6) - - val day = dw3 shr 1 and 0x0000001F - val hour = dw3 and 0x00000001 shl 4 or (dw4 shr 4) - val minute = dw4 and 0x0000000F shl 2 or (dw5 shr 6) - val second = dw5 and 0x0000003F - - // File format is a 1 based month, java Calendar uses a zero based month - // File format is a 1 based day, java Calendar uses a 1 based day - calendar.set(year, month - 1, day, hour, minute, second) - - return DateInstant(calendar.time) - } - - fun dateToBytes(date: Date?, calendar: Calendar = Calendar.getInstance()): ByteArray? { - if (date == null) { - return null - } - - val buf = ByteArray(5) - calendar.time = date - - val year = calendar.get(Calendar.YEAR) - // File format is a 1 based month, java Calendar uses a zero based month - val month = calendar.get(Calendar.MONTH) + 1 - // File format is a 1 based day, java Calendar uses a 1 based day - val day = calendar.get(Calendar.DAY_OF_MONTH) - val hour = calendar.get(Calendar.HOUR_OF_DAY) - val minute = calendar.get(Calendar.MINUTE) - val second = calendar.get(Calendar.SECOND) - - buf[0] = writeUByte(year shr 6 and 0x0000003F) - buf[1] = writeUByte(year and 0x0000003F shl 2 or (month shr 2 and 0x00000003)) - buf[2] = (month and 0x00000003 shl 6 - or (day and 0x0000001F shl 1) or (hour shr 4 and 0x00000001)).toByte() - buf[3] = (hour and 0x0000000F shl 4 or (minute shr 2 and 0x0000000F)).toByte() - buf[4] = (minute and 0x00000003 shl 6 or (second and 0x0000003F)).toByte() - - return buf - } - - @Throws(IOException::class) - fun writePassword(str: String, os: OutputStream): Int { - val initial = str.toByteArray(defaultCharset) - val length = initial.size + 1 - os.write(writeIntBuf(length)) - os.write(initial) - os.write(0x00) - return length - } - - fun readBytes(buf: ByteArray, offset: Int, len: Int): ByteArray { - val binaryData = ByteArray(len) - System.arraycopy(buf, offset, binaryData, 0, len) - return binaryData - } - - fun bytesToUuid(buf: ByteArray): UUID { - return bytes16ToUuid(buf, 0) - } - - fun uuidToBytes(uuid: UUID): ByteArray { - val buf = ByteArray(16) - writeLong(uuid.mostSignificantBits, buf, 0) - writeLong(uuid.leastSignificantBits, buf, 8) - return buf - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/StringDatabaseKDBUtils.kt b/app/src/main/java/com/kunzisoft/keepass/utils/StringDatabaseKDBUtils.kt new file mode 100644 index 000000000..4f2ebffd0 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/utils/StringDatabaseKDBUtils.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX 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. + * + * KeePass DX 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 KeePass DX. If not, see . + * + */ + +package com.kunzisoft.keepass.utils + +import com.kunzisoft.keepass.stream.intTo4Bytes +import java.io.IOException +import java.io.OutputStream +import java.nio.charset.Charset + + +/** + * Tools for slicing and dicing Java and KeePass data types. + */ +object StringDatabaseKDBUtils { + + private val defaultCharset = Charset.forName("UTF-8") + + private val CRLFbuf = byteArrayOf(0x0D, 0x0A) + private val CRLF = String(CRLFbuf) + private val SEP = System.getProperty("line.separator") + private val REPLACE = SEP != CRLF + + fun bytesToString(buf: ByteArray, replaceCRLF: Boolean = true): String { + // length of null-terminated string (i.e. distance to null) within a byte buffer. + var len = 0 + while (buf[len].toInt() != 0) { + len++ + } + // Get string + var jstring = String(buf, 0, len, defaultCharset) + if (replaceCRLF && REPLACE) { + jstring = jstring.replace(CRLF, SEP!!) + } + return jstring + } + + @Throws(IOException::class) + fun writeStringToBytes(string: String?, os: OutputStream): Int { + var str = string + if (str == null) { + // Write out a null character + os.write(intTo4Bytes(1)) + os.write(0x00) + return 0 + } + + if (REPLACE) { + str = str.replace(SEP!!, CRLF) + } + + val initial = str.toByteArray(defaultCharset) + + val length = initial.size + 1 + os.write(intTo4Bytes(length)) + os.write(initial) + os.write(0x00) + + return length + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/VariantDictionary.java b/app/src/main/java/com/kunzisoft/keepass/utils/VariantDictionary.java index 9a23a1597..e2b9c8550 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/VariantDictionary.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/VariantDictionary.java @@ -21,13 +21,14 @@ package com.kunzisoft.keepass.utils; import com.kunzisoft.keepass.stream.LittleEndianDataInputStream; import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream; -import com.kunzisoft.keepass.stream.StreamBytesUtilsKt; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; +import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.bytes4ToInt; +import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.bytes4ToUInt; import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.bytes64ToLong; public class VariantDictionary { @@ -124,12 +125,12 @@ public class VariantDictionary { switch (bType) { case VdType.UInt32: if (valueLen == 4) { - d.setUInt32(name, StreamBytesUtilsKt.bytes4ToUInt(valueBuf, 0)); + d.setUInt32(name, bytes4ToUInt(valueBuf)); } break; case VdType.UInt64: if (valueLen == 8) { - d.setUInt64(name, bytes64ToLong(valueBuf, 0)); + d.setUInt64(name, bytes64ToLong(valueBuf)); } break; case VdType.Bool: @@ -139,12 +140,12 @@ public class VariantDictionary { break; case VdType.Int32: if (valueLen == 4) { - d.setInt32(name, StreamBytesUtilsKt.bytes4ToInt(valueBuf, 0)); + d.setInt32(name, bytes4ToInt(valueBuf)); } break; case VdType.Int64: if (valueLen == 8) { - d.setInt64(name, bytes64ToLong(valueBuf, 0)); + d.setInt64(name, bytes64ToLong(valueBuf)); } break; case VdType.String: @@ -161,7 +162,8 @@ public class VariantDictionary { return d; } - public static void serialize(VariantDictionary d, LittleEndianDataOutputStream los) throws IOException{ + public static void serialize(VariantDictionary d, + LittleEndianDataOutputStream los) throws IOException{ if (los == null) { return; }