mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Refactor bytes utility methods
This commit is contained in:
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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()
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ""; }
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user