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.
*
@@ -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()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

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.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()) {

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

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

View File

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

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.
*
@@ -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 ""; }

View File

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

View File

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

View File

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

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

View File

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

View File

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

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.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;
}