Refactor bytes utility methods

This commit is contained in:
J-Jamet
2019-12-30 12:59:50 +01:00
parent efb9b50f85
commit af72098d60
26 changed files with 435 additions and 534 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
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)
}
}

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. * This file is part of KeePass DX.
* *
@@ -19,18 +19,15 @@
*/ */
package com.kunzisoft.keepass.tests 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 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 junit.framework.TestCase
import org.junit.Assert.assertArrayEquals
import java.io.ByteArrayOutputStream
import java.util.*
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils class StringDatabaseKDBUtilsTest : TestCase() {
class DatabaseInputOutputUtilsTest : TestCase() {
fun testReadWriteLongZero() { fun testReadWriteLongZero() {
testReadWriteLong(0.toByte()) testReadWriteLong(0.toByte())
@@ -54,15 +51,9 @@ class DatabaseInputOutputUtilsTest : TestCase() {
private fun testReadWriteLong(value: Byte) { private fun testReadWriteLong(value: Byte) {
val orig = ByteArray(8) val orig = ByteArray(8)
val dest = ByteArray(8) setArray(orig, value, 8)
setArray(orig, value, 0, 8)
val one = bytes64ToLong(orig, 0)
writeLong(one, dest, 0)
assertArrayEquals(orig, dest)
assertArrayEquals(orig, longTo8Bytes(bytes64ToLong(orig)))
} }
fun testReadWriteIntZero() { fun testReadWriteIntZero() {
@@ -79,24 +70,22 @@ class DatabaseInputOutputUtilsTest : TestCase() {
private fun testReadWriteInt(value: Byte) { private fun testReadWriteInt(value: Byte) {
val orig = ByteArray(4) val orig = ByteArray(4)
val dest = ByteArray(4)
for (i in 0..3) { for (i in 0..3) {
orig[i] = 0 orig[i] = 0
} }
setArray(orig, value, 0, 4) setArray(orig, value, 4)
val one = bytes4ToInt(orig, 0) val one = bytes4ToInt(orig)
val dest = intTo4Bytes(one)
writeInt(one, dest, 0)
assertArrayEquals(orig, dest) assertArrayEquals(orig, dest)
} }
private fun setArray(buf: ByteArray, value: Byte, offset: Int, size: Int) { private fun setArray(buf: ByteArray, value: Byte, size: Int) {
for (i in offset until offset + size) { for (i in 0 until size) {
buf[i] = value buf[i] = value
} }
} }
@@ -107,11 +96,10 @@ class DatabaseInputOutputUtilsTest : TestCase() {
orig[0] = 0 orig[0] = 0
orig[1] = 1 orig[1] = 1
val one = bytes2ToUShort(orig, 0) val one = bytes2ToUShort(orig)
val dest = writeUShortBuf(one) val dest = uShortTo2Bytes(one)
assertArrayEquals(orig, dest) assertArrayEquals(orig, dest)
} }
fun testReadWriteShortMin() { fun testReadWriteShortMin() {
@@ -124,15 +112,12 @@ class DatabaseInputOutputUtilsTest : TestCase() {
private fun testReadWriteShort(value: Byte) { private fun testReadWriteShort(value: Byte) {
val orig = ByteArray(2) val orig = ByteArray(2)
val dest = ByteArray(2) setArray(orig, value, 2)
setArray(orig, value, 0, 2) val one = bytes2ToUShort(orig)
val dest = uShortTo2Bytes(one)
val one = bytes2ToUShort(orig, 0)
writeUShort(one, dest, 0)
assertArrayEquals(orig, dest) assertArrayEquals(orig, dest)
} }
fun testReadWriteByteZero() { fun testReadWriteByteZero() {
@@ -148,16 +133,8 @@ class DatabaseInputOutputUtilsTest : TestCase() {
} }
private fun testReadWriteByte(value: Byte) { private fun testReadWriteByte(value: Byte) {
val orig = ByteArray(1) val dest: Byte = uIntToByte(byteToUInt(value))
val dest = ByteArray(1) assert(value == dest)
setArray(orig, value, 0, 1)
val one = DatabaseInputOutputUtils.readUByte(orig, 0)
DatabaseInputOutputUtils.writeUByte(one, dest, 0)
assertArrayEquals(orig, dest)
} }
fun testDate() { fun testDate() {
@@ -167,27 +144,33 @@ class DatabaseInputOutputUtilsTest : TestCase() {
expected.set(2008, 1, 2, 3, 4, 5) expected.set(2008, 1, 2, 3, 4, 5)
val actual = Calendar.getInstance() val actual = Calendar.getInstance()
DatabaseInputOutputUtils.dateToBytes(expected.time, cal)?.let { buf -> dateTo5Bytes(expected.time, cal)?.let { buf ->
actual.time = DatabaseInputOutputUtils.bytes5ToDate(buf, 0, cal).date 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("Year mismatch: ", 2008, actual.get(Calendar.YEAR))
assertEquals("Month mismatch: ", 1, actual.get(Calendar.MONTH)) assertEquals("Month mismatch: ", 1, actual.get(Calendar.MONTH))
assertEquals("Day mismatch: ", 2, actual.get(Calendar.DAY_OF_MONTH)) assertEquals("Day mismatch: ", 2, actual.get(Calendar.DAY_OF_MONTH))
assertEquals("Hour mismatch: ", 3, actual.get(Calendar.HOUR_OF_DAY)) assertEquals("Hour mismatch: ", 3, actual.get(Calendar.HOUR_OF_DAY))
assertEquals("Minute mismatch: ", 4, actual.get(Calendar.MINUTE)) assertEquals("Minute mismatch: ", 4, actual.get(Calendar.MINUTE))
assertEquals("Second mismatch: ", 5, actual.get(Calendar.SECOND)) 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() { fun testUUID() {
val bUUID = ByteArray(16) val bUUID = ByteArray(16)
Random().nextBytes(bUUID) Random().nextBytes(bUUID)
val uuid = DatabaseInputOutputUtils.bytesToUuid(bUUID) val uuid = bytes16ToUuid(bUUID)
val eUUID = DatabaseInputOutputUtils.uuidToBytes(uuid) val eUUID = uuidTo16Bytes(uuid)
val lUUID = bytes16ToUuid(bUUID, 0) val lUUID = bytes16ToUuid(bUUID)
val leUUID = DatabaseInputOutputUtils.uuidToBytes(lUUID) val leUUID = uuidTo16Bytes(lUUID)
assertArrayEquals("UUID match failed", bUUID, eUUID) assertArrayEquals("UUID match failed", bUUID, eUUID)
assertArrayEquals("UUID match failed", bUUID, leUUID) assertArrayEquals("UUID match failed", bUUID, leUUID)
@@ -202,7 +185,7 @@ class DatabaseInputOutputUtilsTest : TestCase() {
val bos = ByteArrayOutputStream() val bos = ByteArrayOutputStream()
val leos = LittleEndianDataOutputStream(bos) val leos = LittleEndianDataOutputStream(bos)
leos.writeLong(DatabaseInputOutputUtils.ULONG_MAX_VALUE) leos.writeLong(ULONG_MAX_VALUE)
leos.close() leos.close()
val uLongMax = bos.toByteArray() val uLongMax = bos.toByteArray()

View File

@@ -20,7 +20,7 @@
package com.kunzisoft.keepass.crypto package com.kunzisoft.keepass.crypto
import com.kunzisoft.keepass.stream.NullOutputStream import com.kunzisoft.keepass.stream.NullOutputStream
import com.kunzisoft.keepass.stream.writeLongBuf import com.kunzisoft.keepass.stream.longTo8Bytes
import java.io.IOException import java.io.IOException
import java.security.DigestOutputStream import java.security.DigestOutputStream
import java.security.MessageDigest import java.security.MessageDigest
@@ -58,7 +58,7 @@ object CryptoUtil {
throw RuntimeException(e) throw RuntimeException(e)
} }
val pbR = writeLongBuf(r) val pbR = longTo8Bytes(r)
val part = hmac.doFinal(pbR) val part = hmac.doFinal(pbR)
val copy = min(cbOut - pos, part.size) val copy = min(cbOut - pos, part.size)

View File

@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.crypto.engine
import com.kunzisoft.keepass.crypto.CipherFactory import com.kunzisoft.keepass.crypto.CipherFactory
import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm 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.InvalidAlgorithmParameterException
import java.security.InvalidKeyException import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
@@ -47,7 +47,22 @@ class AesEngine : CipherEngine() {
companion object { companion object {
val CIPHER_UUID: UUID = DatabaseInputOutputUtils.bytesToUuid( 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())) 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()))
} }
} }

View File

@@ -20,7 +20,7 @@
package com.kunzisoft.keepass.crypto.engine package com.kunzisoft.keepass.crypto.engine
import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm 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 org.spongycastle.jce.provider.BouncyCastleProvider
import java.security.InvalidAlgorithmParameterException import java.security.InvalidAlgorithmParameterException
import java.security.InvalidKeyException import java.security.InvalidKeyException
@@ -50,7 +50,22 @@ class ChaCha20Engine : CipherEngine() {
companion object { companion object {
val CIPHER_UUID: UUID = DatabaseInputOutputUtils.bytesToUuid( 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())) 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()))
} }
} }

View File

@@ -21,13 +21,11 @@ package com.kunzisoft.keepass.crypto.engine
import com.kunzisoft.keepass.crypto.CipherFactory import com.kunzisoft.keepass.crypto.CipherFactory
import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm 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.InvalidAlgorithmParameterException
import java.security.InvalidKeyException import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
import java.util.UUID import java.util.*
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.NoSuchPaddingException import javax.crypto.NoSuchPaddingException
import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.IvParameterSpec
@@ -53,7 +51,22 @@ class TwofishEngine : CipherEngine() {
companion object { companion object {
val CIPHER_UUID: UUID = DatabaseInputOutputUtils.bytesToUuid( 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())) 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()))
} }
} }

View File

@@ -23,7 +23,7 @@ import android.content.res.Resources
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.crypto.CryptoUtil import com.kunzisoft.keepass.crypto.CryptoUtil
import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory 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.io.IOException
import java.security.SecureRandom import java.security.SecureRandom
import java.util.* import java.util.*
@@ -88,7 +88,7 @@ class AesKdf internal constructor() : KdfEngine() {
private const val DEFAULT_ROUNDS = 6000 private const val DEFAULT_ROUNDS = 6000
val CIPHER_UUID: UUID = DatabaseInputOutputUtils.bytesToUuid( val CIPHER_UUID: UUID = bytes16ToUuid(
byteArrayOf(0xC9.toByte(), byteArrayOf(0xC9.toByte(),
0xD9.toByte(), 0xD9.toByte(),
0xF3.toByte(), 0xF3.toByte(),

View File

@@ -21,7 +21,7 @@ package com.kunzisoft.keepass.crypto.keyDerivation
import android.content.res.Resources import android.content.res.Resources
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils import com.kunzisoft.keepass.stream.bytes16ToUuid
import java.io.IOException import java.io.IOException
import java.security.SecureRandom import java.security.SecureRandom
import java.util.* import java.util.*
@@ -126,7 +126,7 @@ class Argon2Kdf internal constructor() : KdfEngine() {
companion object { companion object {
val CIPHER_UUID: UUID = DatabaseInputOutputUtils.bytesToUuid( val CIPHER_UUID: UUID = bytes16ToUuid(
byteArrayOf(0xEF.toByte(), byteArrayOf(0xEF.toByte(),
0x63.toByte(), 0x63.toByte(),
0x6D.toByte(), 0x6D.toByte(),

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. * This file is part of KeePass DX.
* *
@@ -19,20 +19,20 @@
*/ */
package com.kunzisoft.keepass.crypto.keyDerivation package com.kunzisoft.keepass.crypto.keyDerivation
import com.kunzisoft.keepass.utils.VariantDictionary
import com.kunzisoft.keepass.stream.LittleEndianDataInputStream import com.kunzisoft.keepass.stream.LittleEndianDataInputStream
import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream 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.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
import java.util.UUID import java.util.*
class KdfParameters internal constructor(val uuid: UUID) : VariantDictionary() { class KdfParameters internal constructor(val uuid: UUID) : VariantDictionary() {
fun setParamUUID() { fun setParamUUID() {
setByteArray(PARAM_UUID, DatabaseInputOutputUtils.uuidToBytes(uuid)) setByteArray(PARAM_UUID, uuidTo16Bytes(uuid))
} }
companion object { companion object {
@@ -46,7 +46,7 @@ class KdfParameters internal constructor(val uuid: UUID) : VariantDictionary() {
val d = deserialize(lis) ?: return null 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) val kdfP = KdfParameters(uuid)
kdfP.copyTo(d) kdfP.copyTo(d)

View File

@@ -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.element.node.NodeKDBXInterface
import com.kunzisoft.keepass.database.exception.VersionDatabaseException import com.kunzisoft.keepass.database.exception.VersionDatabaseException
import com.kunzisoft.keepass.stream.* import com.kunzisoft.keepass.stream.*
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
@@ -241,12 +240,12 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
throw IOException("Invalid cipher ID.") 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() assignAesKdfEngineIfNotExists()
val rounds = bytes64ToLong(roundsByte!!, 0) val rounds = bytes64ToLong(roundsByte)
databaseV4.kdfParameters?.setUInt64(AesKdf.PARAM_ROUNDS, rounds) databaseV4.kdfParameters?.setUInt64(AesKdf.PARAM_ROUNDS, rounds)
databaseV4.numberKeyEncryptionRounds = rounds databaseV4.numberKeyEncryptionRounds = rounds
} }
@@ -257,7 +256,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
throw IOException("Invalid compression flags.") throw IOException("Invalid compression flags.")
} }
val flag = bytes4ToInt(pbFlags, 0) val flag = bytes4ToInt(pbFlags)
if (flag < 0 || flag >= CompressionAlgorithm.values().size) { if (flag < 0 || flag >= CompressionAlgorithm.values().size) {
throw IOException("Unrecognized compression flag.") throw IOException("Unrecognized compression flag.")
} }
@@ -273,7 +272,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
throw IOException("Invalid stream id.") throw IOException("Invalid stream id.")
} }
val id = bytes4ToInt(streamID, 0) val id = bytes4ToInt(streamID)
if (id < 0 || id >= CrsAlgorithm.values().size) { if (id < 0 || id >= CrsAlgorithm.values().size) {
throw IOException("Invalid stream id.") throw IOException("Invalid stream id.")
} }
@@ -293,6 +292,9 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
} }
companion object { companion object {
var ULONG_MAX_VALUE: Long = -1
const val DBSIG_PRE2 = -0x4ab4049a const val DBSIG_PRE2 = -0x4ab4049a
const val DBSIG_2 = -0x4ab40499 const val DBSIG_2 = -0x4ab40499
@@ -321,7 +323,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
@Throws(IOException::class) @Throws(IOException::class)
fun computeHeaderHmac(header: ByteArray, key: ByteArray): ByteArray { 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 val hmac: Mac
try { try {

View File

@@ -43,7 +43,6 @@ import com.kunzisoft.keepass.database.file.DatabaseKDBXXML
import com.kunzisoft.keepass.database.file.DateKDBXUtil import com.kunzisoft.keepass.database.file.DateKDBXUtil
import com.kunzisoft.keepass.stream.* import com.kunzisoft.keepass.stream.*
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils
import org.spongycastle.crypto.StreamCipher import org.spongycastle.crypto.StreamCipher
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException import org.xmlpull.v1.XmlPullParserException
@@ -823,7 +822,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
buf = buf8 buf = buf8
} }
val seconds = bytes64ToLong(buf, 0) val seconds = bytes64ToLong(buf)
utcDate = DateKDBXUtil.convertKDBX4Time(seconds) utcDate = DateKDBXUtil.convertKDBX4Time(seconds)
} else { } else {
@@ -883,7 +882,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
} }
val buf = Base64.decode(encoded, BASE_64_FLAG) val buf = Base64.decode(encoded, BASE_64_FLAG)
return DatabaseInputOutputUtils.bytesToUuid(buf) return bytes16ToUuid(buf)
} }
@Throws(IOException::class, XmlPullParserException::class) @Throws(IOException::class, XmlPullParserException::class)

View File

@@ -20,7 +20,7 @@
package com.kunzisoft.keepass.database.file.output package com.kunzisoft.keepass.database.file.output
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB 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.IOException
import java.io.OutputStream import java.io.OutputStream
@@ -29,14 +29,14 @@ class DatabaseHeaderOutputKDB(private val mHeader: DatabaseHeaderKDB,
@Throws(IOException::class) @Throws(IOException::class)
fun outputStart() { fun outputStart() {
mOutputStream.write(writeIntBuf(mHeader.signature1)) mOutputStream.write(intTo4Bytes(mHeader.signature1))
mOutputStream.write(writeIntBuf(mHeader.signature2)) mOutputStream.write(intTo4Bytes(mHeader.signature2))
mOutputStream.write(writeIntBuf(mHeader.flags)) mOutputStream.write(intTo4Bytes(mHeader.flags))
mOutputStream.write(writeIntBuf(mHeader.version)) mOutputStream.write(intTo4Bytes(mHeader.version))
mOutputStream.write(mHeader.masterSeed) mOutputStream.write(mHeader.masterSeed)
mOutputStream.write(mHeader.encryptionIV) mOutputStream.write(mHeader.encryptionIV)
mOutputStream.write(writeIntBuf(mHeader.numGroups)) mOutputStream.write(intTo4Bytes(mHeader.numGroups))
mOutputStream.write(writeIntBuf(mHeader.numEntries)) mOutputStream.write(intTo4Bytes(mHeader.numEntries))
} }
@Throws(IOException::class) @Throws(IOException::class)
@@ -47,7 +47,7 @@ class DatabaseHeaderOutputKDB(private val mHeader: DatabaseHeaderKDB,
@Throws(IOException::class) @Throws(IOException::class)
fun outputEnd() { fun outputEnd() {
mOutputStream.write(mHeader.transformSeed) mOutputStream.write(mHeader.transformSeed)
mOutputStream.write(writeIntBuf(mHeader.numKeyEncRounds)) mOutputStream.write(intTo4Bytes(mHeader.numKeyEncRounds))
} }
@Throws(IOException::class) @Throws(IOException::class)

View File

@@ -24,8 +24,8 @@ import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
import com.kunzisoft.keepass.database.exception.DatabaseOutputException import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.database.file.DatabaseHeader import com.kunzisoft.keepass.database.file.DatabaseHeader
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX 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.stream.*
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils
import com.kunzisoft.keepass.utils.VariantDictionary import com.kunzisoft.keepass.utils.VariantDictionary
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
@@ -62,7 +62,7 @@ constructor(private val db: DatabaseKDBX, private val header: DatabaseHeaderKDBX
val hmac: Mac val hmac: Mac
try { try {
hmac = Mac.getInstance("HmacSHA256") 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) hmac.init(signingKey)
} catch (e: NoSuchAlgorithmException) { } catch (e: NoSuchAlgorithmException) {
throw DatabaseOutputException(e) throw DatabaseOutputException(e)
@@ -82,13 +82,13 @@ constructor(private val db: DatabaseKDBX, private val header: DatabaseHeaderKDBX
los.writeUInt(DatabaseHeaderKDBX.DBSIG_2.toLong()) los.writeUInt(DatabaseHeaderKDBX.DBSIG_2.toLong())
los.writeUInt(header.version) los.writeUInt(header.version)
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CipherID, DatabaseInputOutputUtils.uuidToBytes(db.dataCipher)) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CipherID, uuidTo16Bytes(db.dataCipher))
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CompressionFlags, writeIntBuf(DatabaseHeaderKDBX.getFlagFromCompression(db.compressionAlgorithm))) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CompressionFlags, intTo4Bytes(DatabaseHeaderKDBX.getFlagFromCompression(db.compressionAlgorithm)))
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.MasterSeed, header.masterSeed) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.MasterSeed, header.masterSeed)
if (header.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (header.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) {
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformSeed, header.transformSeed) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformSeed, header.transformSeed)
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformRounds, writeLongBuf(db.numberKeyEncryptionRounds)) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformRounds, longTo8Bytes(db.numberKeyEncryptionRounds))
} else { } else {
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.KdfParameters, KdfParameters.serialize(db.kdfParameters!!)) 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) { if (header.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) {
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomstreamKey, header.innerRandomStreamKey) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomstreamKey, header.innerRandomStreamKey)
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes) 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()) { if (db.containsPublicCustomData()) {

View File

@@ -46,11 +46,7 @@ import com.kunzisoft.keepass.database.exception.UnknownKDF
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
import com.kunzisoft.keepass.database.file.DatabaseKDBXXML import com.kunzisoft.keepass.database.file.DatabaseKDBXXML
import com.kunzisoft.keepass.database.file.DateKDBXUtil import com.kunzisoft.keepass.database.file.DateKDBXUtil
import com.kunzisoft.keepass.stream.HashedBlockOutputStream import com.kunzisoft.keepass.stream.*
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 org.joda.time.DateTime import org.joda.time.DateTime
import org.spongycastle.crypto.StreamCipher import org.spongycastle.crypto.StreamCipher
import org.xmlpull.v1.XmlSerializer import org.xmlpull.v1.XmlSerializer
@@ -393,7 +389,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
} else { } else {
val dt = DateTime(value) val dt = DateTime(value)
val seconds = DateKDBXUtil.convertDateToKDBX4Time(dt) val seconds = DateKDBXUtil.convertDateToKDBX4Time(dt)
val buf = writeLongBuf(seconds) val buf = longTo8Bytes(seconds)
val b64 = String(Base64.encode(buf, BASE_64_FLAG)) val b64 = String(Base64.encode(buf, BASE_64_FLAG))
writeObject(name, b64) writeObject(name, b64)
} }
@@ -417,7 +413,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
private fun writeUuid(name: String, uuid: UUID) { 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))) writeObject(name, String(Base64.encode(data, BASE_64_FLAG)))
} }

View File

@@ -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.database.DatabaseKDB
import com.kunzisoft.keepass.database.element.entry.EntryKDB import com.kunzisoft.keepass.database.element.entry.EntryKDB
import com.kunzisoft.keepass.stream.readBytes import com.kunzisoft.keepass.stream.*
import com.kunzisoft.keepass.stream.writeIntBuf import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils
import com.kunzisoft.keepass.stream.writeUShortBuf
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
import java.nio.charset.Charset
class EntryOutputKDB class EntryOutputKDB
/** /**
@@ -49,54 +48,54 @@ class EntryOutputKDB
// UUID // UUID
mOutputStream.write(UUID_FIELD_TYPE) mOutputStream.write(UUID_FIELD_TYPE)
mOutputStream.write(UUID_FIELD_SIZE) mOutputStream.write(UUID_FIELD_SIZE)
mOutputStream.write(DatabaseInputOutputUtils.uuidToBytes(mEntry.id)) mOutputStream.write(uuidTo16Bytes(mEntry.id))
// Group ID // Group ID
mOutputStream.write(GROUPID_FIELD_TYPE) mOutputStream.write(GROUPID_FIELD_TYPE)
mOutputStream.write(LONG_FOUR) mOutputStream.write(LONG_FOUR)
mOutputStream.write(writeIntBuf(mEntry.parent!!.id)) mOutputStream.write(intTo4Bytes(mEntry.parent!!.id))
// Image ID // Image ID
mOutputStream.write(IMAGEID_FIELD_TYPE) mOutputStream.write(IMAGEID_FIELD_TYPE)
mOutputStream.write(LONG_FOUR) mOutputStream.write(LONG_FOUR)
mOutputStream.write(writeIntBuf(mEntry.icon.iconId)) mOutputStream.write(intTo4Bytes(mEntry.icon.iconId))
// Title // Title
//byte[] title = mEntry.title.getBytes("UTF-8"); //byte[] title = mEntry.title.getBytes("UTF-8");
mOutputStream.write(TITLE_FIELD_TYPE) mOutputStream.write(TITLE_FIELD_TYPE)
length += DatabaseInputOutputUtils.writeCString(mEntry.title, mOutputStream).toLong() length += StringDatabaseKDBUtils.writeStringToBytes(mEntry.title, mOutputStream).toLong()
// URL // URL
mOutputStream.write(URL_FIELD_TYPE) mOutputStream.write(URL_FIELD_TYPE)
length += DatabaseInputOutputUtils.writeCString(mEntry.url, mOutputStream).toLong() length += StringDatabaseKDBUtils.writeStringToBytes(mEntry.url, mOutputStream).toLong()
// Username // Username
mOutputStream.write(USERNAME_FIELD_TYPE) mOutputStream.write(USERNAME_FIELD_TYPE)
length += DatabaseInputOutputUtils.writeCString(mEntry.username, mOutputStream).toLong() length += StringDatabaseKDBUtils.writeStringToBytes(mEntry.username, mOutputStream).toLong()
// Password // Password
mOutputStream.write(PASSWORD_FIELD_TYPE) mOutputStream.write(PASSWORD_FIELD_TYPE)
length += DatabaseInputOutputUtils.writePassword(mEntry.password, mOutputStream).toLong() length += writePassword(mEntry.password, mOutputStream).toLong()
// Additional // Additional
mOutputStream.write(ADDITIONAL_FIELD_TYPE) mOutputStream.write(ADDITIONAL_FIELD_TYPE)
length += DatabaseInputOutputUtils.writeCString(mEntry.notes, mOutputStream).toLong() length += StringDatabaseKDBUtils.writeStringToBytes(mEntry.notes, mOutputStream).toLong()
// Create date // Create date
writeDate(CREATE_FIELD_TYPE, DatabaseInputOutputUtils.dateToBytes(mEntry.creationTime.date)) writeDate(CREATE_FIELD_TYPE, dateTo5Bytes(mEntry.creationTime.date))
// Modification date // Modification date
writeDate(MOD_FIELD_TYPE, DatabaseInputOutputUtils.dateToBytes(mEntry.lastModificationTime.date)) writeDate(MOD_FIELD_TYPE, dateTo5Bytes(mEntry.lastModificationTime.date))
// Access date // Access date
writeDate(ACCESS_FIELD_TYPE, DatabaseInputOutputUtils.dateToBytes(mEntry.lastAccessTime.date)) writeDate(ACCESS_FIELD_TYPE, dateTo5Bytes(mEntry.lastAccessTime.date))
// Expiration date // Expiration date
writeDate(EXPIRE_FIELD_TYPE, DatabaseInputOutputUtils.dateToBytes(mEntry.expiryTime.date)) writeDate(EXPIRE_FIELD_TYPE, dateTo5Bytes(mEntry.expiryTime.date))
// Binary description // Binary description
mOutputStream.write(BINARY_DESC_FIELD_TYPE) mOutputStream.write(BINARY_DESC_FIELD_TYPE)
length += DatabaseInputOutputUtils.writeCString(mEntry.binaryDescription, mOutputStream).toLong() length += StringDatabaseKDBUtils.writeStringToBytes(mEntry.binaryDescription, mOutputStream).toLong()
// Binary // Binary
mOutputStream.write(BINARY_DATA_FIELD_TYPE) mOutputStream.write(BINARY_DATA_FIELD_TYPE)
@@ -108,7 +107,7 @@ class EntryOutputKDB
0 // TODO if length > UInt.maxvalue show exception 0 // TODO if length > UInt.maxvalue show exception
} }
// Write data length // Write data length
mOutputStream.write(writeIntBuf(binaryDataLengthRightSize)) mOutputStream.write(intTo4Bytes(binaryDataLengthRightSize))
// Write data // Write data
if (binaryDataLength > 0) { if (binaryDataLength > 0) {
binaryData?.getInputDataStream().use { inputStream -> 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 { companion object {
// Constants // Constants
val UUID_FIELD_TYPE:ByteArray = writeUShortBuf(1) val UUID_FIELD_TYPE:ByteArray = uShortTo2Bytes(1)
val GROUPID_FIELD_TYPE:ByteArray = writeUShortBuf(2) val GROUPID_FIELD_TYPE:ByteArray = uShortTo2Bytes(2)
val IMAGEID_FIELD_TYPE:ByteArray = writeUShortBuf(3) val IMAGEID_FIELD_TYPE:ByteArray = uShortTo2Bytes(3)
val TITLE_FIELD_TYPE:ByteArray = writeUShortBuf(4) val TITLE_FIELD_TYPE:ByteArray = uShortTo2Bytes(4)
val URL_FIELD_TYPE:ByteArray = writeUShortBuf(5) val URL_FIELD_TYPE:ByteArray = uShortTo2Bytes(5)
val USERNAME_FIELD_TYPE:ByteArray = writeUShortBuf(6) val USERNAME_FIELD_TYPE:ByteArray = uShortTo2Bytes(6)
val PASSWORD_FIELD_TYPE:ByteArray = writeUShortBuf(7) val PASSWORD_FIELD_TYPE:ByteArray = uShortTo2Bytes(7)
val ADDITIONAL_FIELD_TYPE:ByteArray = writeUShortBuf(8) val ADDITIONAL_FIELD_TYPE:ByteArray = uShortTo2Bytes(8)
val CREATE_FIELD_TYPE:ByteArray = writeUShortBuf(9) val CREATE_FIELD_TYPE:ByteArray = uShortTo2Bytes(9)
val MOD_FIELD_TYPE:ByteArray = writeUShortBuf(10) val MOD_FIELD_TYPE:ByteArray = uShortTo2Bytes(10)
val ACCESS_FIELD_TYPE:ByteArray = writeUShortBuf(11) val ACCESS_FIELD_TYPE:ByteArray = uShortTo2Bytes(11)
val EXPIRE_FIELD_TYPE:ByteArray = writeUShortBuf(12) val EXPIRE_FIELD_TYPE:ByteArray = uShortTo2Bytes(12)
val BINARY_DESC_FIELD_TYPE:ByteArray = writeUShortBuf(13) val BINARY_DESC_FIELD_TYPE:ByteArray = uShortTo2Bytes(13)
val BINARY_DATA_FIELD_TYPE:ByteArray = writeUShortBuf(14) val BINARY_DATA_FIELD_TYPE:ByteArray = uShortTo2Bytes(14)
val END_FIELD_TYPE:ByteArray = writeUShortBuf(0xFFFF) val END_FIELD_TYPE:ByteArray = uShortTo2Bytes(0xFFFF)
val LONG_FOUR:ByteArray = writeIntBuf(4) val LONG_FOUR:ByteArray = intTo4Bytes(4)
val UUID_FIELD_SIZE:ByteArray = writeIntBuf(16) val UUID_FIELD_SIZE:ByteArray = intTo4Bytes(16)
val DATE_FIELD_SIZE:ByteArray = writeIntBuf(5) val DATE_FIELD_SIZE:ByteArray = intTo4Bytes(5)
val IMAGEID_FIELD_SIZE:ByteArray = LONG_FOUR val IMAGEID_FIELD_SIZE:ByteArray = LONG_FOUR
val LEVEL_FIELD_SIZE:ByteArray = LONG_FOUR val LEVEL_FIELD_SIZE:ByteArray = LONG_FOUR
val FLAGS_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) val ZERO_FIVE:ByteArray = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)
} }
} }

View File

@@ -20,9 +20,10 @@
package com.kunzisoft.keepass.database.file.output package com.kunzisoft.keepass.database.file.output
import com.kunzisoft.keepass.database.element.group.GroupKDB import com.kunzisoft.keepass.database.element.group.GroupKDB
import com.kunzisoft.keepass.stream.writeIntBuf import com.kunzisoft.keepass.stream.dateTo5Bytes
import com.kunzisoft.keepass.stream.writeUShortBuf import com.kunzisoft.keepass.stream.intTo4Bytes
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils import com.kunzisoft.keepass.stream.uShortTo2Bytes
import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
@@ -38,46 +39,46 @@ class GroupOutputKDB (private val mGroup: GroupKDB, private val mOutputStream: O
// Group ID // Group ID
mOutputStream.write(GROUPID_FIELD_TYPE) mOutputStream.write(GROUPID_FIELD_TYPE)
mOutputStream.write(GROUPID_FIELD_SIZE) mOutputStream.write(GROUPID_FIELD_SIZE)
mOutputStream.write(writeIntBuf(mGroup.id)) mOutputStream.write(intTo4Bytes(mGroup.id))
// Name // Name
mOutputStream.write(NAME_FIELD_TYPE) mOutputStream.write(NAME_FIELD_TYPE)
DatabaseInputOutputUtils.writeCString(mGroup.title, mOutputStream) StringDatabaseKDBUtils.writeStringToBytes(mGroup.title, mOutputStream)
// Create date // Create date
mOutputStream.write(CREATE_FIELD_TYPE) mOutputStream.write(CREATE_FIELD_TYPE)
mOutputStream.write(DATE_FIELD_SIZE) mOutputStream.write(DATE_FIELD_SIZE)
mOutputStream.write(DatabaseInputOutputUtils.dateToBytes(mGroup.creationTime.date)) mOutputStream.write(dateTo5Bytes(mGroup.creationTime.date))
// Modification date // Modification date
mOutputStream.write(MOD_FIELD_TYPE) mOutputStream.write(MOD_FIELD_TYPE)
mOutputStream.write(DATE_FIELD_SIZE) mOutputStream.write(DATE_FIELD_SIZE)
mOutputStream.write(DatabaseInputOutputUtils.dateToBytes(mGroup.lastModificationTime.date)) mOutputStream.write(dateTo5Bytes(mGroup.lastModificationTime.date))
// Access date // Access date
mOutputStream.write(ACCESS_FIELD_TYPE) mOutputStream.write(ACCESS_FIELD_TYPE)
mOutputStream.write(DATE_FIELD_SIZE) mOutputStream.write(DATE_FIELD_SIZE)
mOutputStream.write(DatabaseInputOutputUtils.dateToBytes(mGroup.lastAccessTime.date)) mOutputStream.write(dateTo5Bytes(mGroup.lastAccessTime.date))
// Expiration date // Expiration date
mOutputStream.write(EXPIRE_FIELD_TYPE) mOutputStream.write(EXPIRE_FIELD_TYPE)
mOutputStream.write(DATE_FIELD_SIZE) mOutputStream.write(DATE_FIELD_SIZE)
mOutputStream.write(DatabaseInputOutputUtils.dateToBytes(mGroup.expiryTime.date)) mOutputStream.write(dateTo5Bytes(mGroup.expiryTime.date))
// Image ID // Image ID
mOutputStream.write(IMAGEID_FIELD_TYPE) mOutputStream.write(IMAGEID_FIELD_TYPE)
mOutputStream.write(IMAGEID_FIELD_SIZE) mOutputStream.write(IMAGEID_FIELD_SIZE)
mOutputStream.write(writeIntBuf(mGroup.icon.iconId)) mOutputStream.write(intTo4Bytes(mGroup.icon.iconId))
// Level // Level
mOutputStream.write(LEVEL_FIELD_TYPE) mOutputStream.write(LEVEL_FIELD_TYPE)
mOutputStream.write(LEVEL_FIELD_SIZE) mOutputStream.write(LEVEL_FIELD_SIZE)
mOutputStream.write(writeUShortBuf(mGroup.level)) mOutputStream.write(uShortTo2Bytes(mGroup.level))
// Flags // Flags
mOutputStream.write(FLAGS_FIELD_TYPE) mOutputStream.write(FLAGS_FIELD_TYPE)
mOutputStream.write(FLAGS_FIELD_SIZE) mOutputStream.write(FLAGS_FIELD_SIZE)
mOutputStream.write(writeIntBuf(mGroup.flags)) mOutputStream.write(intTo4Bytes(mGroup.flags))
// End // End
mOutputStream.write(END_FIELD_TYPE) mOutputStream.write(END_FIELD_TYPE)
@@ -86,23 +87,23 @@ class GroupOutputKDB (private val mGroup: GroupKDB, private val mOutputStream: O
companion object { companion object {
// Constants // Constants
val GROUPID_FIELD_TYPE: ByteArray = writeUShortBuf(1) val GROUPID_FIELD_TYPE: ByteArray = uShortTo2Bytes(1)
val NAME_FIELD_TYPE:ByteArray = writeUShortBuf(2) val NAME_FIELD_TYPE:ByteArray = uShortTo2Bytes(2)
val CREATE_FIELD_TYPE:ByteArray = writeUShortBuf(3) val CREATE_FIELD_TYPE:ByteArray = uShortTo2Bytes(3)
val MOD_FIELD_TYPE:ByteArray = writeUShortBuf(4) val MOD_FIELD_TYPE:ByteArray = uShortTo2Bytes(4)
val ACCESS_FIELD_TYPE:ByteArray = writeUShortBuf(5) val ACCESS_FIELD_TYPE:ByteArray = uShortTo2Bytes(5)
val EXPIRE_FIELD_TYPE:ByteArray = writeUShortBuf(6) val EXPIRE_FIELD_TYPE:ByteArray = uShortTo2Bytes(6)
val IMAGEID_FIELD_TYPE:ByteArray = writeUShortBuf(7) val IMAGEID_FIELD_TYPE:ByteArray = uShortTo2Bytes(7)
val LEVEL_FIELD_TYPE:ByteArray = writeUShortBuf(8) val LEVEL_FIELD_TYPE:ByteArray = uShortTo2Bytes(8)
val FLAGS_FIELD_TYPE:ByteArray = writeUShortBuf(9) val FLAGS_FIELD_TYPE:ByteArray = uShortTo2Bytes(9)
val END_FIELD_TYPE:ByteArray = writeUShortBuf(0xFFFF) val END_FIELD_TYPE:ByteArray = uShortTo2Bytes(0xFFFF)
val LONG_FOUR:ByteArray = writeIntBuf(4) val LONG_FOUR:ByteArray = intTo4Bytes(4)
val GROUPID_FIELD_SIZE:ByteArray = LONG_FOUR 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 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 FLAGS_FIELD_SIZE:ByteArray = LONG_FOUR
val ZERO_FIELD_SIZE:ByteArray = writeIntBuf(0) val ZERO_FIELD_SIZE:ByteArray = intTo4Bytes(0)
} }
} }

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. * This file is part of KeePass DX.
* *
@@ -19,16 +19,16 @@
*/ */
package com.kunzisoft.keepass.database.search; package com.kunzisoft.keepass.database.search;
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils;
import java.util.UUID; import java.util.UUID;
import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.uuidTo16Bytes;
public class UuidUtil { public class UuidUtil {
public static String toHexString(UUID uuid) { public static String toHexString(UUID uuid) {
if (uuid == null) { return null; } if (uuid == null) { return null; }
byte[] buf = DatabaseInputOutputUtils.INSTANCE.uuidToBytes(uuid); byte[] buf = uuidTo16Bytes(uuid);
int len = buf.length; int len = buf.length;
if (len == 0) { return ""; } if (len == 0) { return ""; }

View File

@@ -19,7 +19,6 @@
*/ */
package com.kunzisoft.keepass.stream package com.kunzisoft.keepass.stream
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.security.MessageDigest import java.security.MessageDigest
@@ -145,7 +144,7 @@ class HashedBlockInputStream(inputStream: InputStream) : InputStream() {
if (!readHashedBlock()) return -1 if (!readHashedBlock()) return -1
} }
val output = DatabaseInputOutputUtils.readUByte(buffer, bufferPos) val output = byteToUInt(buffer[bufferPos])
bufferPos++ bufferPos++
return output return output

View File

@@ -19,7 +19,6 @@
*/ */
package com.kunzisoft.keepass.stream package com.kunzisoft.keepass.stream
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.security.InvalidKeyException import java.security.InvalidKeyException
@@ -44,7 +43,7 @@ class HmacBlockInputStream(baseStream: InputStream, private val verify: Boolean,
if (!readSafeBlock()) return -1 if (!readSafeBlock()) return -1
} }
val output = DatabaseInputOutputUtils.readUByte(buffer, bufferPos) val output = byteToUInt(buffer[bufferPos])
bufferPos++ bufferPos++
return output return output
@@ -93,12 +92,12 @@ class HmacBlockInputStream(baseStream: InputStream, private val verify: Boolean,
throw IOException("File corrupted") throw IOException("File corrupted")
} }
val pbBlockIndex = writeLongBuf(blockIndex) val pbBlockIndex = longTo8Bytes(blockIndex)
val pbBlockSize = baseStream.readBytes(4) val pbBlockSize = baseStream.readBytes(4)
if (pbBlockSize.size != 4) { if (pbBlockSize.size != 4) {
throw IOException("File corrupted") throw IOException("File corrupted")
} }
val blockSize = bytes4ToInt(pbBlockSize, 0) val blockSize = bytes4ToInt(pbBlockSize)
bufferPos = 0 bufferPos = 0
buffer = baseStream.readBytes(blockSize) buffer = baseStream.readBytes(blockSize)

View File

@@ -27,8 +27,8 @@ import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac; import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.writeIntBuf; import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.intTo4Bytes;
import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.writeLongBuf; import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.longTo8Bytes;
public class HmacBlockOutputStream extends OutputStream { public class HmacBlockOutputStream extends OutputStream {
private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024; private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024;
@@ -92,8 +92,8 @@ public class HmacBlockOutputStream extends OutputStream {
} }
private void WriteSafeBlock() throws IOException { private void WriteSafeBlock() throws IOException {
byte[] bufBlockIndex = writeLongBuf(blockIndex); byte[] bufBlockIndex = longTo8Bytes(blockIndex);
byte[] blockSizeBuf = writeIntBuf(bufferPos); byte[] blockSizeBuf = intTo4Bytes(bufferPos);
byte[] blockHmac; byte[] blockHmac;
byte[] blockKey = HmacBlockStream.GetHmacKey64(key, blockIndex); byte[] blockKey = HmacBlockStream.GetHmacKey64(key, blockIndex);

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. * This file is part of KeePass DX.
* *
@@ -24,7 +24,6 @@ import java.io.InputStream
/** /**
* Little endian version of the DataInputStream * Little endian version of the DataInputStream
* @author bpellin
*/ */
class LittleEndianDataInputStream(private val baseStream: InputStream) : InputStream() { class LittleEndianDataInputStream(private val baseStream: InputStream) : InputStream() {
@@ -42,18 +41,12 @@ class LittleEndianDataInputStream(private val baseStream: InputStream) : InputSt
return baseStream.readBytes4ToInt() return baseStream.readBytes4ToInt()
} }
@Throws(IOException::class)
fun readLong(): Long {
val buf = readBytes(8)
return bytes64ToLong(buf, 0)
}
@Throws(IOException::class) @Throws(IOException::class)
fun readUShort(): Int { fun readUShort(): Int {
val buf = ByteArray(2) val buf = ByteArray(2)
if (baseStream.read(buf, 0, 2) != 2) if (baseStream.read(buf, 0, 2) != 2)
throw IOException("Unable to read UShort value") throw IOException("Unable to read UShort value")
return bytes2ToUShort(buf, 0) return bytes2ToUShort(buf)
} }
@Throws(IOException::class) @Throws(IOException::class)

View File

@@ -22,8 +22,6 @@ package com.kunzisoft.keepass.stream
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
import com.kunzisoft.keepass.stream.writeIntBuf
/** /**
* Little Endian version of the DataOutputStream * Little Endian version of the DataOutputStream
@@ -32,8 +30,8 @@ import com.kunzisoft.keepass.stream.writeIntBuf
class LittleEndianDataOutputStream(private val baseStream: OutputStream) : OutputStream() { class LittleEndianDataOutputStream(private val baseStream: OutputStream) : OutputStream() {
@Throws(IOException::class) @Throws(IOException::class)
fun writeUInt(uint: Long) { fun writeUInt(uint: Long) { // TODO UInt
baseStream.write(writeIntBuf(uint.toInt())) baseStream.write(intTo4Bytes(uint.toInt()))
} }
@Throws(IOException::class) @Throws(IOException::class)
@@ -63,24 +61,16 @@ class LittleEndianDataOutputStream(private val baseStream: OutputStream) : Outpu
@Throws(IOException::class) @Throws(IOException::class)
fun writeLong(value: Long) { fun writeLong(value: Long) {
val buf = ByteArray(8) baseStream.write(longTo8Bytes(value))
writeLong(value, buf, 0)
baseStream.write(buf)
} }
@Throws(IOException::class) @Throws(IOException::class)
fun writeInt(value: Int) { fun writeInt(value: Int) {
val buf = ByteArray(4) baseStream.write(intTo4Bytes(value))
writeInt(value, buf, 0)
baseStream.write(buf)
} }
@Throws(IOException::class) @Throws(IOException::class)
fun writeUShort(value: Int) { fun writeUShort(value: Int) {
val buf = ByteArray(2) baseStream.write(uShortTo2Bytes(value))
writeUShort(value, buf, 0)
baseStream.write(buf)
} }
} }

View File

@@ -20,8 +20,7 @@
package com.kunzisoft.keepass.stream package com.kunzisoft.keepass.stream
import com.kunzisoft.keepass.database.element.DateInstant import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils.bytes5ToDate import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils.bytesToString
import com.kunzisoft.keepass.utils.DatabaseInputOutputUtils.bytesToString
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.util.* import java.util.*
@@ -87,27 +86,27 @@ fun InputStream.readBytes4ToUInt(): Long {
@Throws(IOException::class) @Throws(IOException::class)
fun InputStream.readBytes4ToInt(): Int { fun InputStream.readBytes4ToInt(): Int {
return bytes4ToInt(readBytesLength(4), 0) return bytes4ToInt(readBytesLength(4))
} }
@Throws(IOException::class) @Throws(IOException::class)
fun InputStream.readBytes2ToUShort(): Int { fun InputStream.readBytes2ToUShort(): Int {
return bytes2ToUShort(readBytesLength(2), 0) return bytes2ToUShort(readBytesLength(2))
} }
@Throws(IOException::class) @Throws(IOException::class)
fun InputStream.readBytes5ToDate(): DateInstant { fun InputStream.readBytes5ToDate(): DateInstant {
return bytes5ToDate(readBytesLength(5), 0) return bytes5ToDate(readBytesLength(5))
} }
@Throws(IOException::class) @Throws(IOException::class)
fun InputStream.readBytes16ToUuid(): UUID { fun InputStream.readBytes16ToUuid(): UUID {
return bytes16ToUuid(readBytesLength(16), 0) return bytes16ToUuid(readBytesLength(16))
} }
@Throws(IOException::class) @Throws(IOException::class)
fun InputStream.readBytesToString(length: Int, replaceCRLF: Boolean = true): String { 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) @Throws(IOException::class)
@@ -123,99 +122,164 @@ fun InputStream.readBytesLength(length: Int): ByteArray {
/** /**
* Read an unsigned 16-bit value. * Read an unsigned 16-bit value.
*/ */
fun bytes2ToUShort(buf: ByteArray, offset: Int): Int { fun bytes2ToUShort(buf: ByteArray): Int {
return ((buf[offset].toInt() and 0xFF) return ((buf[0].toInt() and 0xFF)
+ (buf[offset + 1].toInt() and 0xFF shl 8)) + (buf[1].toInt() and 0xFF shl 8))
} }
/** /**
* Read a 64 bit long * Read a 64 bit long
*/ */
fun bytes64ToLong(buf: ByteArray, offset: Int): Long { fun bytes64ToLong(buf: ByteArray): Long {
return ((buf[offset].toLong() and 0xFF) return ((buf[0].toLong() and 0xFF)
+ (buf[offset + 1].toLong() and 0xFF shl 8) + (buf[1].toLong() and 0xFF shl 8)
+ (buf[offset + 2].toLong() and 0xFF shl 16) + (buf[2].toLong() and 0xFF shl 16)
+ (buf[offset + 3].toLong() and 0xFF shl 24) + (buf[3].toLong() and 0xFF shl 24)
+ (buf[offset + 4].toLong() and 0xFF shl 32) + (buf[4].toLong() and 0xFF shl 32)
+ (buf[offset + 5].toLong() and 0xFF shl 40) + (buf[5].toLong() and 0xFF shl 40)
+ (buf[offset + 6].toLong() and 0xFF shl 48) + (buf[6].toLong() and 0xFF shl 48)
+ (buf[offset + 7].toLong() and 0xFF shl 56)) + (buf[7].toLong() and 0xFF shl 56))
} }
private const val INT_TO_LONG_MASK: Long = 0xffffffffL private const val INT_TO_LONG_MASK: Long = 0xffffffffL
fun bytes4ToUInt(buf: ByteArray, offset: Int): Long { fun bytes4ToUInt(buf: ByteArray): Long {
return bytes4ToInt(buf, offset).toLong() and INT_TO_LONG_MASK return bytes4ToInt(buf).toLong() and INT_TO_LONG_MASK
} }
/** /**
* Read a 32-bit value. * Read a 32-bit value.
*/ */
fun bytes4ToInt(buf: ByteArray, offset: Int): Int { fun bytes4ToInt(buf: ByteArray): Int {
return ((buf[offset].toInt() and 0xFF) return ((buf[0].toInt() and 0xFF)
+ (buf[offset + 1].toInt() and 0xFF shl 8) + (buf[1].toInt() and 0xFF shl 8)
+ (buf[offset + 2].toInt() and 0xFF shl 16) + (buf[2].toInt() and 0xFF shl 16)
+ (buf[offset + 3].toInt() and 0xFF shl 24)) + (buf[3].toInt() and 0xFF shl 24))
} }
fun bytes16ToUuid(buf: ByteArray, offset: Int): UUID { fun bytes16ToUuid(buf: ByteArray): UUID {
var lsb: Long = 0 var lsb: Long = 0
for (i in 15 downTo 8) { 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 var msb: Long = 0
for (i in 7 downTo 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) return UUID(msb, lsb)
} }
fun writeIntBuf(value: Int): ByteArray { /**
val buf = ByteArray(4) * Unpack date from 5 byte format. The five bytes at 'offset' are unpacked
writeInt(value, buf, 0) * to a java.util.Date instance.
return buf */
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) * Convert an unsigned Integer to byte
writeUShort(value, buf, 0) */
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 return buf
} }
/** /**
* Write an unsigned 16-bit value * Write an unsigned 16-bit value
*/ */
fun writeUShort(value: Int, buf: ByteArray, offset: Int) { fun uShortTo2Bytes(value: Int): ByteArray {
buf[offset + 0] = (value and 0x00FF).toByte() val buf = ByteArray(2)
buf[offset + 1] = (value and 0xFF00 shr 8).toByte() buf[0] = (value and 0x00FF).toByte()
} buf[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)
return buf return buf
} }
fun writeLong(value: Long, buf: ByteArray, offset: Int) { fun longTo8Bytes(value: Long): ByteArray {
buf[offset + 0] = (value and 0xFF).toByte() val buf = ByteArray(8)
buf[offset + 1] = (value.ushr(8) and 0xFF).toByte() for (i in 0 until 8) {
buf[offset + 2] = (value.ushr(16) and 0xFF).toByte() buf[i] = (value.ushr(8 * i) and 0xFF).toByte()
buf[offset + 3] = (value.ushr(24) and 0xFF).toByte() }
buf[offset + 4] = (value.ushr(32) and 0xFF).toByte() return buf
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 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
} }

View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*
KeePass for J2ME
Copyright 2007 Naomaru Itoi <nao@phoneid.org>
This file was derived from
Java clone of KeePass - A KeePass file viewer for Java
Copyright 2006 Bill Zwicky <billzwicky@users.sourceforge.net>
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 <wrzwicky></wrzwicky>@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
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
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
}
}

View File

@@ -21,13 +21,14 @@ package com.kunzisoft.keepass.utils;
import com.kunzisoft.keepass.stream.LittleEndianDataInputStream; import com.kunzisoft.keepass.stream.LittleEndianDataInputStream;
import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream; import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream;
import com.kunzisoft.keepass.stream.StreamBytesUtilsKt;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; 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; import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.bytes64ToLong;
public class VariantDictionary { public class VariantDictionary {
@@ -124,12 +125,12 @@ public class VariantDictionary {
switch (bType) { switch (bType) {
case VdType.UInt32: case VdType.UInt32:
if (valueLen == 4) { if (valueLen == 4) {
d.setUInt32(name, StreamBytesUtilsKt.bytes4ToUInt(valueBuf, 0)); d.setUInt32(name, bytes4ToUInt(valueBuf));
} }
break; break;
case VdType.UInt64: case VdType.UInt64:
if (valueLen == 8) { if (valueLen == 8) {
d.setUInt64(name, bytes64ToLong(valueBuf, 0)); d.setUInt64(name, bytes64ToLong(valueBuf));
} }
break; break;
case VdType.Bool: case VdType.Bool:
@@ -139,12 +140,12 @@ public class VariantDictionary {
break; break;
case VdType.Int32: case VdType.Int32:
if (valueLen == 4) { if (valueLen == 4) {
d.setInt32(name, StreamBytesUtilsKt.bytes4ToInt(valueBuf, 0)); d.setInt32(name, bytes4ToInt(valueBuf));
} }
break; break;
case VdType.Int64: case VdType.Int64:
if (valueLen == 8) { if (valueLen == 8) {
d.setInt64(name, bytes64ToLong(valueBuf, 0)); d.setInt64(name, bytes64ToLong(valueBuf));
} }
break; break;
case VdType.String: case VdType.String:
@@ -161,7 +162,8 @@ public class VariantDictionary {
return d; 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) { if (los == null) {
return; return;
} }