Add UnsignedInt and UnsignedLong

This commit is contained in:
J-Jamet
2020-04-22 15:55:21 +02:00
parent da5490bc0a
commit 9663c3cadd
49 changed files with 493 additions and 401 deletions

View File

@@ -118,4 +118,7 @@ dependencies {
// Icon pack // Icon pack
implementation project(path: ':icon-pack-classic') implementation project(path: ':icon-pack-classic')
implementation project(path: ':icon-pack-material') implementation project(path: ':icon-pack-material')
// Tests
androidTestImplementation 'junit:junit:4.12'
} }

View File

@@ -20,8 +20,9 @@
package com.kunzisoft.keepass.tests package com.kunzisoft.keepass.tests
import com.kunzisoft.keepass.database.element.DateInstant 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 com.kunzisoft.keepass.utils.UnsignedInt
import com.kunzisoft.keepass.utils.UnsignedLong
import junit.framework.TestCase import junit.framework.TestCase
import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertArrayEquals
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
@@ -77,8 +78,8 @@ class StringDatabaseKDBUtilsTest : TestCase() {
setArray(orig, value, 4) setArray(orig, value, 4)
val one = bytes4ToInt(orig) val one = bytes4ToUInt(orig)
val dest = intTo4Bytes(one) val dest = uIntTo4Bytes(one)
assertArrayEquals(orig, dest) assertArrayEquals(orig, dest)
@@ -133,7 +134,7 @@ class StringDatabaseKDBUtilsTest : TestCase() {
} }
private fun testReadWriteByte(value: Byte) { private fun testReadWriteByte(value: Byte) {
val dest: Byte = uIntToByte(byteToUInt(value)) val dest: Byte = UnsignedInt(UnsignedInt.fromByte(value)).toByte()
assert(value == dest) assert(value == dest)
} }
@@ -185,7 +186,7 @@ class StringDatabaseKDBUtilsTest : TestCase() {
val bos = ByteArrayOutputStream() val bos = ByteArrayOutputStream()
val leos = LittleEndianDataOutputStream(bos) val leos = LittleEndianDataOutputStream(bos)
leos.writeLong(ULONG_MAX_VALUE) leos.writeLong(UnsignedLong.ULONG_MAX_VALUE)
leos.close() leos.close()
val uLongMax = bos.toByteArray() val uLongMax = bos.toByteArray()

View File

@@ -26,8 +26,8 @@ import java.util.Random
import junit.framework.TestCase import junit.framework.TestCase
import com.kunzisoft.keepass.crypto.finalkey.AndroidFinalKey import com.kunzisoft.keepass.crypto.finalkey.AndroidAESKeyTransformer
import com.kunzisoft.keepass.crypto.finalkey.NativeFinalKey import com.kunzisoft.keepass.crypto.finalkey.NativeAESKeyTransformer
class FinalKeyTest : TestCase() { class FinalKeyTest : TestCase() {
private var mRand: Random? = null private var mRand: Random? = null
@@ -56,10 +56,10 @@ class FinalKeyTest : TestCase() {
mRand!!.nextBytes(seed) mRand!!.nextBytes(seed)
mRand!!.nextBytes(key) mRand!!.nextBytes(key)
val aKey = AndroidFinalKey() val aKey = AndroidAESKeyTransformer()
androidKey = aKey.transformMasterKey(seed, key, rounds.toLong()) androidKey = aKey.transformMasterKey(seed, key, rounds.toLong())
val nKey = NativeFinalKey() val nKey = NativeAESKeyTransformer()
nativeKey = nKey.transformMasterKey(seed, key, rounds.toLong()) nativeKey = nKey.transformMasterKey(seed, key, rounds.toLong())
assertArrayEquals("Does not match", androidKey, nativeKey) assertArrayEquals("Does not match", androidKey, nativeKey)

View File

@@ -19,16 +19,18 @@
*/ */
package com.kunzisoft.keepass.crypto package com.kunzisoft.keepass.crypto
enum class CrsAlgorithm constructor(val id: Int) { import com.kunzisoft.keepass.utils.UnsignedInt
Null(0), enum class CrsAlgorithm constructor(val id: UnsignedInt) {
ArcFourVariant(1),
Salsa20(2), Null(UnsignedInt(0)),
ChaCha20(3); ArcFourVariant(UnsignedInt(1)),
Salsa20(UnsignedInt(2)),
ChaCha20(UnsignedInt(3));
companion object { companion object {
fun fromId(num: Int): CrsAlgorithm? { fun fromId(num: UnsignedInt): CrsAlgorithm? {
for (e in values()) { for (e in values()) {
if (e.id == num) { if (e.id == num) {
return e return e

View File

@@ -30,7 +30,7 @@ object NativeLib {
fun init(): Boolean { fun init(): Boolean {
if (!isLoaded) { if (!isLoaded) {
try { try {
System.loadLibrary("final-key") System.loadLibrary("final-key") // TODO Rename
System.loadLibrary("argon2") System.loadLibrary("argon2")
} catch (e: UnsatisfiedLinkError) { } catch (e: UnsatisfiedLinkError) {
return false return false

View File

@@ -21,14 +21,17 @@ package com.kunzisoft.keepass.crypto.finalkey;
import com.kunzisoft.keepass.crypto.CipherFactory; import com.kunzisoft.keepass.crypto.CipherFactory;
public class FinalKeyFactory { public class AESFactory {
public static FinalKey createFinalKey() {
// TODO Encaspulate
public static KeyTransformer createFinalKey() {
// Prefer the native final key implementation // Prefer the native final key implementation
if ( !CipherFactory.INSTANCE.deviceBlacklisted() && NativeFinalKey.available() ) { if ( !CipherFactory.INSTANCE.deviceBlacklisted()
return new NativeFinalKey(); && NativeAESKeyTransformer.available() ) {
return new NativeAESKeyTransformer();
} else { } else {
// Fall back on the android crypto implementation // Fall back on the android crypto implementation
return new AndroidFinalKey(); return new AndroidAESKeyTransformer();
} }
} }
} }

View File

@@ -29,7 +29,7 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException; import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
public class AndroidFinalKey extends FinalKey { public class AndroidAESKeyTransformer extends KeyTransformer {
@Override @Override
public byte[] transformMasterKey(byte[] pKeySeed, byte[] pKey, long rounds) throws IOException { public byte[] transformMasterKey(byte[] pKeySeed, byte[] pKey, long rounds) throws IOException {

View File

@@ -21,6 +21,6 @@ package com.kunzisoft.keepass.crypto.finalkey;
import java.io.IOException; import java.io.IOException;
public abstract class FinalKey { public abstract class KeyTransformer {
public abstract byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException; public abstract byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException;
} }

View File

@@ -24,7 +24,7 @@ import com.kunzisoft.keepass.crypto.NativeLib;
import java.io.IOException; import java.io.IOException;
public class NativeFinalKey extends FinalKey { public class NativeAESKeyTransformer extends KeyTransformer {
public static boolean available() { public static boolean available() {
return NativeLib.INSTANCE.init(); return NativeLib.INSTANCE.init();

View File

@@ -22,8 +22,9 @@ 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.crypto.CryptoUtil import com.kunzisoft.keepass.crypto.CryptoUtil
import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory import com.kunzisoft.keepass.crypto.finalkey.AESFactory
import com.kunzisoft.keepass.stream.bytes16ToUuid import com.kunzisoft.keepass.stream.bytes16ToUuid
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.security.SecureRandom import java.security.SecureRandom
import java.util.* import java.util.*
@@ -34,12 +35,11 @@ class AesKdf internal constructor() : KdfEngine() {
get() { get() {
return KdfParameters(uuid!!).apply { return KdfParameters(uuid!!).apply {
setParamUUID() setParamUUID()
setUInt32(PARAM_ROUNDS, DEFAULT_ROUNDS.toLong()) setUInt32(PARAM_ROUNDS, UnsignedInt.fromLong(defaultKeyRounds))
} }
} }
override val defaultKeyRounds: Long override val defaultKeyRounds: Long = 6000L
get() = DEFAULT_ROUNDS.toLong()
init { init {
uuid = CIPHER_UUID uuid = CIPHER_UUID
@@ -63,8 +63,7 @@ class AesKdf internal constructor() : KdfEngine() {
seed = CryptoUtil.hashSha256(seed) seed = CryptoUtil.hashSha256(seed)
} }
val key = FinalKeyFactory.createFinalKey() return AESFactory.createFinalKey().transformMasterKey(seed, currentMasterKey, rounds)
return key.transformMasterKey(seed, currentMasterKey, rounds)
} }
override fun randomize(p: KdfParameters) { override fun randomize(p: KdfParameters) {
@@ -86,8 +85,6 @@ class AesKdf internal constructor() : KdfEngine() {
companion object { companion object {
private const val DEFAULT_ROUNDS = 6000
val CIPHER_UUID: UUID = bytes16ToUuid( val CIPHER_UUID: UUID = bytes16ToUuid(
byteArrayOf(0xC9.toByte(), byteArrayOf(0xC9.toByte(),
0xD9.toByte(), 0xD9.toByte(),

View File

@@ -22,6 +22,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.stream.bytes16ToUuid import com.kunzisoft.keepass.stream.bytes16ToUuid
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.security.SecureRandom import java.security.SecureRandom
import java.util.* import java.util.*
@@ -56,15 +57,21 @@ class Argon2Kdf internal constructor() : KdfEngine() {
override fun transform(masterKey: ByteArray, p: KdfParameters): ByteArray { override fun transform(masterKey: ByteArray, p: KdfParameters): ByteArray {
val salt = p.getByteArray(PARAM_SALT) val salt = p.getByteArray(PARAM_SALT)
val parallelism = p.getUInt32(PARAM_PARALLELISM).toInt() val parallelism = UnsignedInt(p.getUInt32(PARAM_PARALLELISM))
val memory = p.getUInt64(PARAM_MEMORY) val memory = UnsignedInt.fromLong(p.getUInt64(PARAM_MEMORY) / MEMORY_BLOCK_SIZE)
val iterations = p.getUInt64(PARAM_ITERATIONS) val iterations = UnsignedInt.fromLong(p.getUInt64(PARAM_ITERATIONS))
val version = p.getUInt32(PARAM_VERSION) val version = UnsignedInt(p.getUInt32(PARAM_VERSION))
val secretKey = p.getByteArray(PARAM_SECRET_KEY) val secretKey = p.getByteArray(PARAM_SECRET_KEY)
val assocData = p.getByteArray(PARAM_ASSOC_DATA) val assocData = p.getByteArray(PARAM_ASSOC_DATA)
return Argon2Native.transformKey(masterKey, salt, parallelism, memory, iterations, return Argon2Native.transformKey(masterKey,
secretKey, assocData, version) salt,
parallelism,
memory,
iterations,
secretKey,
assocData,
version)
} }
override fun randomize(p: KdfParameters) { override fun randomize(p: KdfParameters) {
@@ -107,21 +114,21 @@ class Argon2Kdf internal constructor() : KdfEngine() {
override val maxMemoryUsage: Long override val maxMemoryUsage: Long
get() = MAX_MEMORY get() = MAX_MEMORY
override fun getParallelism(p: KdfParameters): Int { override fun getParallelism(p: KdfParameters): Long {
return p.getUInt32(PARAM_PARALLELISM).toInt() // TODO Verify #443 return UnsignedInt(p.getUInt32(PARAM_PARALLELISM)).toLong()
} }
override fun setParallelism(p: KdfParameters, parallelism: Int) { override fun setParallelism(p: KdfParameters, parallelism: Long) {
p.setUInt32(PARAM_PARALLELISM, parallelism.toLong()) p.setUInt32(PARAM_PARALLELISM, UnsignedInt.fromLong(parallelism))
} }
override val defaultParallelism: Int override val defaultParallelism: Long
get() = DEFAULT_PARALLELISM.toInt() get() = DEFAULT_PARALLELISM.toLong()
override val minParallelism: Int override val minParallelism: Long
get() = MIN_PARALLELISM get() = MIN_PARALLELISM
override val maxParallelism: Int override val maxParallelism: Long
get() = MAX_PARALLELISM get() = MAX_PARALLELISM
companion object { companion object {
@@ -152,23 +159,24 @@ class Argon2Kdf internal constructor() : KdfEngine() {
private const val PARAM_SECRET_KEY = "K" // byte[] private const val PARAM_SECRET_KEY = "K" // byte[]
private const val PARAM_ASSOC_DATA = "A" // byte[] private const val PARAM_ASSOC_DATA = "A" // byte[]
private const val MIN_VERSION: Long = 0x10 private val MIN_VERSION = UnsignedInt(0x10)
private const val MAX_VERSION: Long = 0x13 private val MAX_VERSION = UnsignedInt(0x13)
private const val MIN_SALT = 8 private const val MIN_SALT = 8
private const val MAX_SALT = Integer.MAX_VALUE private val MAX_SALT = UnsignedInt.MAX_VALUE.toLong()
private const val MIN_ITERATIONS: Long = 1 private const val MIN_ITERATIONS: Long = 1L
private const val MAX_ITERATIONS = 4294967295L private const val MAX_ITERATIONS = 4294967295L
private const val MIN_MEMORY = (1024 * 8).toLong() private const val MIN_MEMORY = (1024 * 8).toLong()
private const val MAX_MEMORY = Integer.MAX_VALUE.toLong() private val MAX_MEMORY = UnsignedInt.MAX_VALUE.toLong()
private const val MEMORY_BLOCK_SIZE: Long = 1024L
private const val MIN_PARALLELISM = 1 private const val MIN_PARALLELISM: Long = 1L
private const val MAX_PARALLELISM = (1 shl 24) - 1 private const val MAX_PARALLELISM: Long = ((1 shl 24) - 1).toLong()
private const val DEFAULT_ITERATIONS: Long = 2 private const val DEFAULT_ITERATIONS: Long = 2L
private const val DEFAULT_MEMORY = (1024 * 1024).toLong() private const val DEFAULT_MEMORY = (1024 * 1024).toLong()
private const val DEFAULT_PARALLELISM: Long = 2 private val DEFAULT_PARALLELISM = UnsignedInt(2)
} }
} }

View File

@@ -20,20 +20,29 @@
package com.kunzisoft.keepass.crypto.keyDerivation; package com.kunzisoft.keepass.crypto.keyDerivation;
import com.kunzisoft.keepass.crypto.NativeLib; import com.kunzisoft.keepass.crypto.NativeLib;
import com.kunzisoft.keepass.utils.UnsignedInt;
import java.io.IOException; import java.io.IOException;
public class Argon2Native { public class Argon2Native {
public static byte[] transformKey(byte[] password, byte[] salt, int parallelism, public static byte[] transformKey(byte[] password, byte[] salt, UnsignedInt parallelism,
long memory, long iterations, byte[] secretKey, UnsignedInt memory, UnsignedInt iterations, byte[] secretKey,
byte[] associatedData, long version) throws IOException { byte[] associatedData, UnsignedInt version) throws IOException {
NativeLib.INSTANCE.init(); NativeLib.INSTANCE.init();
return nTransformMasterKey(password, salt, parallelism, memory, iterations, secretKey, associatedData, version); return nTransformMasterKey(
password,
salt,
parallelism.toInt(),
memory.toInt(),
iterations.toInt(),
secretKey,
associatedData,
version.toInt());
} }
private static native byte[] nTransformMasterKey(byte[] password, byte[] salt, int parallelism, private static native byte[] nTransformMasterKey(byte[] password, byte[] salt, int parallelism,
long memory, long iterations, byte[] secretKey, int memory, int iterations, byte[] secretKey,
byte[] associatedData, long version) throws IOException; byte[] associatedData, int version) throws IOException;
} }

View File

@@ -20,6 +20,7 @@
package com.kunzisoft.keepass.crypto.keyDerivation package com.kunzisoft.keepass.crypto.keyDerivation
import com.kunzisoft.keepass.utils.ObjectNameResource import com.kunzisoft.keepass.utils.ObjectNameResource
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.Serializable import java.io.Serializable
@@ -51,14 +52,14 @@ abstract class KdfEngine : ObjectNameResource, Serializable {
get() = 1 get() = 1
open val maxKeyRounds: Long open val maxKeyRounds: Long
get() = Int.MAX_VALUE.toLong() get() = UnsignedInt.MAX_VALUE.toLong()
/* /*
* MEMORY * MEMORY
*/ */
open fun getMemoryUsage(p: KdfParameters): Long { open fun getMemoryUsage(p: KdfParameters): Long {
return UNKNOWN_VALUE.toLong() return UNKNOWN_VALUE
} }
open fun setMemoryUsage(p: KdfParameters, memory: Long) { open fun setMemoryUsage(p: KdfParameters, memory: Long) {
@@ -66,36 +67,36 @@ abstract class KdfEngine : ObjectNameResource, Serializable {
} }
open val defaultMemoryUsage: Long open val defaultMemoryUsage: Long
get() = UNKNOWN_VALUE.toLong() get() = UNKNOWN_VALUE
open val minMemoryUsage: Long open val minMemoryUsage: Long
get() = 1 get() = 1
open val maxMemoryUsage: Long open val maxMemoryUsage: Long
get() = Int.MAX_VALUE.toLong() get() = UnsignedInt.MAX_VALUE.toLong()
/* /*
* PARALLELISM * PARALLELISM
*/ */
open fun getParallelism(p: KdfParameters): Int { open fun getParallelism(p: KdfParameters): Long {
return UNKNOWN_VALUE return UNKNOWN_VALUE
} }
open fun setParallelism(p: KdfParameters, parallelism: Int) { open fun setParallelism(p: KdfParameters, parallelism: Long) {
// Do nothing by default // Do nothing by default
} }
open val defaultParallelism: Int open val defaultParallelism: Long
get() = UNKNOWN_VALUE get() = UNKNOWN_VALUE
open val minParallelism: Int open val minParallelism: Long
get() = 1 get() = 1L
open val maxParallelism: Int open val maxParallelism: Long
get() = Int.MAX_VALUE get() = UnsignedInt.MAX_VALUE.toLong()
companion object { companion object {
const val UNKNOWN_VALUE = -1 const val UNKNOWN_VALUE: Long = -1L
} }
} }

View File

@@ -552,12 +552,12 @@ class ProgressDialogThread(private val activity: FragmentActivity) {
, ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK) , ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK)
} }
fun startDatabaseSaveParallelism(oldParallelism: Int, fun startDatabaseSaveParallelism(oldParallelism: Long,
newParallelism: Int, newParallelism: Long,
save: Boolean) { save: Boolean) {
start(Bundle().apply { start(Bundle().apply {
putInt(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldParallelism) putLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldParallelism)
putInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newParallelism) putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newParallelism)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_UPDATE_PARALLELISM_TASK) , ACTION_DATABASE_UPDATE_PARALLELISM_TASK)

View File

@@ -47,7 +47,7 @@ import com.kunzisoft.keepass.database.search.SearchHelper
import com.kunzisoft.keepass.database.search.SearchParameters import com.kunzisoft.keepass.database.search.SearchParameters
import com.kunzisoft.keepass.icons.IconDrawableFactory import com.kunzisoft.keepass.icons.IconDrawableFactory
import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.stream.readBytes4ToInt import com.kunzisoft.keepass.stream.readBytes4ToUInt
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.SingletonHolder import com.kunzisoft.keepass.utils.SingletonHolder
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.UriUtil
@@ -206,7 +206,6 @@ class Database {
var numberKeyEncryptionRounds: Long var numberKeyEncryptionRounds: Long
get() = mDatabaseKDB?.numberKeyEncryptionRounds ?: mDatabaseKDBX?.numberKeyEncryptionRounds ?: 0 get() = mDatabaseKDB?.numberKeyEncryptionRounds ?: mDatabaseKDBX?.numberKeyEncryptionRounds ?: 0
@Throws(NumberFormatException::class)
set(numberRounds) { set(numberRounds) {
mDatabaseKDB?.numberKeyEncryptionRounds = numberRounds mDatabaseKDB?.numberKeyEncryptionRounds = numberRounds
mDatabaseKDBX?.numberKeyEncryptionRounds = numberRounds mDatabaseKDBX?.numberKeyEncryptionRounds = numberRounds
@@ -214,13 +213,13 @@ class Database {
var memoryUsage: Long var memoryUsage: Long
get() { get() {
return mDatabaseKDBX?.memoryUsage ?: return KdfEngine.UNKNOWN_VALUE.toLong() return mDatabaseKDBX?.memoryUsage ?: return KdfEngine.UNKNOWN_VALUE
} }
set(memory) { set(memory) {
mDatabaseKDBX?.memoryUsage = memory mDatabaseKDBX?.memoryUsage = memory
} }
var parallelism: Int var parallelism: Long
get() = mDatabaseKDBX?.parallelism ?: KdfEngine.UNKNOWN_VALUE get() = mDatabaseKDBX?.parallelism ?: KdfEngine.UNKNOWN_VALUE
set(parallelism) { set(parallelism) {
mDatabaseKDBX?.parallelism = parallelism mDatabaseKDBX?.parallelism = parallelism
@@ -348,8 +347,8 @@ class Database {
databaseInputStream.mark(10) databaseInputStream.mark(10)
// Get the file directory to save the attachments // Get the file directory to save the attachments
val sig1 = databaseInputStream.readBytes4ToInt() val sig1 = databaseInputStream.readBytes4ToUInt()
val sig2 = databaseInputStream.readBytes4ToInt() val sig2 = databaseInputStream.readBytes4ToUInt()
// Return to the start // Return to the start
databaseInputStream.reset() databaseInputStream.reset()

View File

@@ -19,7 +19,7 @@
package com.kunzisoft.keepass.database.element.database package com.kunzisoft.keepass.database.element.database
import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory import com.kunzisoft.keepass.crypto.finalkey.AESFactory
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
import com.kunzisoft.keepass.database.element.entry.EntryKDB import com.kunzisoft.keepass.database.element.entry.EntryKDB
@@ -38,8 +38,6 @@ import kotlin.collections.ArrayList
class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() { class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
private var numKeyEncRounds: Int = 0
var backupGroupId: Int = BACKUP_FOLDER_UNDEFINED_ID var backupGroupId: Int = BACKUP_FOLDER_UNDEFINED_ID
private var kdfListV3: MutableList<KdfEngine> = ArrayList() private var kdfListV3: MutableList<KdfEngine> = ArrayList()
@@ -87,19 +85,10 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
override val passwordEncoding: String override val passwordEncoding: String
get() = "ISO-8859-1" get() = "ISO-8859-1"
override var numberKeyEncryptionRounds: Long override var numberKeyEncryptionRounds = 300L
get() = numKeyEncRounds.toLong()
@Throws(NumberFormatException::class)
set(rounds) {
if (rounds > Integer.MAX_VALUE || rounds < Integer.MIN_VALUE) {
throw NumberFormatException()
}
numKeyEncRounds = rounds.toInt()
}
init { init {
algorithm = EncryptionAlgorithm.AESRijndael algorithm = EncryptionAlgorithm.AESRijndael
numKeyEncRounds = DEFAULT_ENCRYPTION_ROUNDS
} }
/** /**
@@ -158,9 +147,9 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
val nos = NullOutputStream() val nos = NullOutputStream()
val dos = DigestOutputStream(nos, messageDigest) val dos = DigestOutputStream(nos, messageDigest)
val transformedMasterKey = transformMasterKey(masterSeed2, masterKey, numRounds) // Encrypt the master key a few times to make brute-force key-search harder
dos.write(masterSeed) dos.write(masterSeed)
dos.write(transformedMasterKey) dos.write(AESFactory.createFinalKey().transformMasterKey(masterSeed2, masterKey, numRounds))
finalKey = messageDigest.digest() finalKey = messageDigest.digest()
} }
@@ -266,19 +255,6 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
const val BACKUP_FOLDER_TITLE = "Backup" const val BACKUP_FOLDER_TITLE = "Backup"
private const val BACKUP_FOLDER_UNDEFINED_ID = -1 private const val BACKUP_FOLDER_UNDEFINED_ID = -1
private const val DEFAULT_ENCRYPTION_ROUNDS = 300
const val BUFFER_SIZE_BYTES = 3 * 128 const val BUFFER_SIZE_BYTES = 3 * 128
/**
* Encrypt the master key a few times to make brute-force key-search harder
* @throws IOException
*/
@Throws(IOException::class)
private fun transformMasterKey(pKeySeed: ByteArray, pKey: ByteArray, rounds: Long): ByteArray {
val key = FinalKeyFactory.createFinalKey()
return key.transformMasterKey(pKeySeed, pKey, rounds)
}
} }
} }

View File

@@ -42,6 +42,7 @@ import com.kunzisoft.keepass.database.element.security.MemoryProtectionConfig
import com.kunzisoft.keepass.database.exception.UnknownKDF import com.kunzisoft.keepass.database.exception.UnknownKDF
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_3 import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_3
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_4 import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_4
import com.kunzisoft.keepass.utils.UnsignedInt
import com.kunzisoft.keepass.utils.VariantDictionary import com.kunzisoft.keepass.utils.VariantDictionary
import org.w3c.dom.Node import org.w3c.dom.Node
import org.w3c.dom.Text import org.w3c.dom.Text
@@ -67,7 +68,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
private var numKeyEncRounds: Long = 0 private var numKeyEncRounds: Long = 0
var publicCustomData = VariantDictionary() var publicCustomData = VariantDictionary()
var kdbxVersion: Long = 0 var kdbxVersion = UnsignedInt(0)
var name = "" var name = ""
var nameChanged = DateInstant() var nameChanged = DateInstant()
// TODO change setting date // TODO change setting date
@@ -83,7 +84,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
var keyChangeForceDays: Long = 1 var keyChangeForceDays: Long = 1
var isKeyChangeForceOnce = false var isKeyChangeForceOnce = false
var maintenanceHistoryDays: Long = 365 var maintenanceHistoryDays = UnsignedInt(365)
var color = "" var color = ""
/** /**
* Determine if RecycleBin is enable or not * Determine if RecycleBin is enable or not
@@ -219,7 +220,6 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
numKeyEncRounds = kdfEngine.getKeyRounds(kdfParameters!!) numKeyEncRounds = kdfEngine.getKeyRounds(kdfParameters!!)
return numKeyEncRounds return numKeyEncRounds
} }
@Throws(NumberFormatException::class)
set(rounds) { set(rounds) {
val kdfEngine = kdfEngine val kdfEngine = kdfEngine
if (kdfEngine != null && kdfParameters != null) if (kdfEngine != null && kdfParameters != null)
@@ -232,7 +232,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
val kdfEngine = kdfEngine val kdfEngine = kdfEngine
return if (kdfEngine != null && kdfParameters != null) { return if (kdfEngine != null && kdfParameters != null) {
kdfEngine.getMemoryUsage(kdfParameters!!) kdfEngine.getMemoryUsage(kdfParameters!!)
} else KdfEngine.UNKNOWN_VALUE.toLong() } else KdfEngine.UNKNOWN_VALUE
} }
set(memory) { set(memory) {
val kdfEngine = kdfEngine val kdfEngine = kdfEngine
@@ -240,7 +240,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
kdfEngine.setMemoryUsage(kdfParameters!!, memory) kdfEngine.setMemoryUsage(kdfParameters!!, memory)
} }
var parallelism: Int var parallelism: Long
get() { get() {
val kdfEngine = kdfEngine val kdfEngine = kdfEngine
return if (kdfEngine != null && kdfParameters != null) { return if (kdfEngine != null && kdfParameters != null) {

View File

@@ -23,6 +23,7 @@ import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import com.kunzisoft.keepass.utils.ParcelableUtil import com.kunzisoft.keepass.utils.ParcelableUtil
import com.kunzisoft.keepass.utils.UnsignedInt
import java.util.HashMap import java.util.HashMap
@@ -46,7 +47,7 @@ class AutoType : Parcelable {
constructor(parcel: Parcel) { constructor(parcel: Parcel) {
this.enabled = parcel.readByte().toInt() != 0 this.enabled = parcel.readByte().toInt() != 0
this.obfuscationOptions = parcel.readLong() this.obfuscationOptions = UnsignedInt(parcel.readInt())
this.defaultSequence = parcel.readString() ?: defaultSequence this.defaultSequence = parcel.readString() ?: defaultSequence
this.windowSeqPairs = ParcelableUtil.readStringParcelableMap(parcel) this.windowSeqPairs = ParcelableUtil.readStringParcelableMap(parcel)
} }
@@ -57,7 +58,7 @@ class AutoType : Parcelable {
override fun writeToParcel(dest: Parcel, flags: Int) { override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeByte((if (enabled) 1 else 0).toByte()) dest.writeByte((if (enabled) 1 else 0).toByte())
dest.writeLong(obfuscationOptions) dest.writeInt(obfuscationOptions.toInt())
dest.writeString(defaultSequence) dest.writeString(defaultSequence)
ParcelableUtil.writeStringParcelableMap(dest, windowSeqPairs) ParcelableUtil.writeStringParcelableMap(dest, windowSeqPairs)
} }
@@ -71,7 +72,7 @@ class AutoType : Parcelable {
} }
companion object { companion object {
private const val OBF_OPT_NONE: Long = 0 private val OBF_OPT_NONE = UnsignedInt(0)
@JvmField @JvmField
val CREATOR: Parcelable.Creator<AutoType> = object : Parcelable.Creator<AutoType> { val CREATOR: Parcelable.Creator<AutoType> = object : Parcelable.Creator<AutoType> {

View File

@@ -34,6 +34,7 @@ import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.database.element.security.BinaryAttachment import com.kunzisoft.keepass.database.element.security.BinaryAttachment
import com.kunzisoft.keepass.database.element.security.ProtectedString import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.utils.ParcelableUtil import com.kunzisoft.keepass.utils.ParcelableUtil
import com.kunzisoft.keepass.utils.UnsignedLong
import java.util.* import java.util.*
class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInterface { class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInterface {
@@ -104,7 +105,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
constructor(parcel: Parcel) : super(parcel) { constructor(parcel: Parcel) : super(parcel) {
iconCustom = parcel.readParcelable(IconImageCustom::class.java.classLoader) ?: iconCustom iconCustom = parcel.readParcelable(IconImageCustom::class.java.classLoader) ?: iconCustom
usageCount = parcel.readLong() usageCount = UnsignedLong(parcel.readLong())
locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged
customData = ParcelableUtil.readStringParcelableMap(parcel) customData = ParcelableUtil.readStringParcelableMap(parcel)
fields = ParcelableUtil.readStringParcelableMap(parcel, ProtectedString::class.java) fields = ParcelableUtil.readStringParcelableMap(parcel, ProtectedString::class.java)
@@ -122,7 +123,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
override fun writeToParcel(dest: Parcel, flags: Int) { override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags) super.writeToParcel(dest, flags)
dest.writeParcelable(iconCustom, flags) dest.writeParcelable(iconCustom, flags)
dest.writeLong(usageCount) dest.writeLong(usageCount.toLong())
dest.writeParcelable(locationChanged, flags) dest.writeParcelable(locationChanged, flags)
ParcelableUtil.writeStringParcelableMap(dest, customData) ParcelableUtil.writeStringParcelableMap(dest, customData)
ParcelableUtil.writeStringParcelableMap(dest, flags, fields) ParcelableUtil.writeStringParcelableMap(dest, flags, fields)
@@ -243,7 +244,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
fields[STR_NOTES] = ProtectedString(protect, value) fields[STR_NOTES] = ProtectedString(protect, value)
} }
override var usageCount: Long = 0 override var usageCount = UnsignedLong(0)
override var locationChanged = DateInstant() override var locationChanged = DateInstant()
@@ -332,7 +333,8 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
override fun touch(modified: Boolean, touchParents: Boolean) { override fun touch(modified: Boolean, touchParents: Boolean) {
super.touch(modified, touchParents) super.touch(modified, touchParents)
++usageCount // TODO unsigned long
usageCount = UnsignedLong(usageCount.toLong() + 1)
} }
companion object { companion object {

View File

@@ -32,14 +32,14 @@ import java.util.*
class GroupKDB : GroupVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface { class GroupKDB : GroupVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface {
var level = 0 // short var level = 0 // short
/** Used by KeePass internally, don't use */ // Used by KeePass internally, don't use
var flags: Int = 0 var groupFlags = 0
constructor() : super() constructor() : super()
constructor(parcel: Parcel) : super(parcel) { constructor(parcel: Parcel) : super(parcel) {
level = parcel.readInt() level = parcel.readInt()
flags = parcel.readInt() groupFlags = parcel.readInt()
} }
override fun readParentParcelable(parcel: Parcel): GroupKDB? { override fun readParentParcelable(parcel: Parcel): GroupKDB? {
@@ -53,13 +53,13 @@ class GroupKDB : GroupVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
override fun writeToParcel(dest: Parcel, flags: Int) { override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags) super.writeToParcel(dest, flags)
dest.writeInt(level) dest.writeInt(level)
dest.writeInt(flags) dest.writeInt(groupFlags)
} }
fun updateWith(source: GroupKDB) { fun updateWith(source: GroupKDB) {
super.updateWith(source) super.updateWith(source)
level = source.level level = source.level
flags = source.flags groupFlags = source.groupFlags
} }
override val type: Type override val type: Type

View File

@@ -31,6 +31,7 @@ import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.NodeIdUUID import com.kunzisoft.keepass.database.element.node.NodeIdUUID
import com.kunzisoft.keepass.database.element.node.NodeKDBXInterface import com.kunzisoft.keepass.database.element.node.NodeKDBXInterface
import com.kunzisoft.keepass.database.element.node.Type import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.utils.UnsignedLong
import java.util.HashMap import java.util.HashMap
import java.util.UUID import java.util.UUID
@@ -77,7 +78,7 @@ class GroupKDBX : GroupVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
constructor(parcel: Parcel) : super(parcel) { constructor(parcel: Parcel) : super(parcel) {
iconCustom = parcel.readParcelable(IconImageCustom::class.java.classLoader) ?: iconCustom iconCustom = parcel.readParcelable(IconImageCustom::class.java.classLoader) ?: iconCustom
usageCount = parcel.readLong() usageCount = UnsignedLong(parcel.readLong())
locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged
// TODO customData = ParcelableUtil.readStringParcelableMap(parcel); // TODO customData = ParcelableUtil.readStringParcelableMap(parcel);
notes = parcel.readString() ?: notes notes = parcel.readString() ?: notes
@@ -101,7 +102,7 @@ class GroupKDBX : GroupVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
override fun writeToParcel(dest: Parcel, flags: Int) { override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags) super.writeToParcel(dest, flags)
dest.writeParcelable(iconCustom, flags) dest.writeParcelable(iconCustom, flags)
dest.writeLong(usageCount) dest.writeLong(usageCount.toLong())
dest.writeParcelable(locationChanged, flags) dest.writeParcelable(locationChanged, flags)
// TODO ParcelableUtil.writeStringParcelableMap(dest, customData); // TODO ParcelableUtil.writeStringParcelableMap(dest, customData);
dest.writeString(notes) dest.writeString(notes)
@@ -130,7 +131,7 @@ class GroupKDBX : GroupVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
lastTopVisibleEntry = source.lastTopVisibleEntry lastTopVisibleEntry = source.lastTopVisibleEntry
} }
override var usageCount: Long = 0 override var usageCount = UnsignedLong(0)
override var locationChanged = DateInstant() override var locationChanged = DateInstant()

View File

@@ -20,10 +20,11 @@
package com.kunzisoft.keepass.database.element.node package com.kunzisoft.keepass.database.element.node
import com.kunzisoft.keepass.database.element.DateInstant import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.utils.UnsignedLong
interface NodeKDBXInterface : NodeTimeInterface { interface NodeKDBXInterface : NodeTimeInterface {
var usageCount: Long var usageCount: UnsignedLong
var locationChanged: DateInstant var locationChanged: DateInstant

View File

@@ -19,6 +19,8 @@
*/ */
package com.kunzisoft.keepass.database.file package com.kunzisoft.keepass.database.file
import com.kunzisoft.keepass.utils.UnsignedInt
abstract class DatabaseHeader { abstract class DatabaseHeader {
/** /**
@@ -32,7 +34,7 @@ abstract class DatabaseHeader {
var encryptionIV = ByteArray(16) var encryptionIV = ByteArray(16)
companion object { companion object {
const val PWM_DBSIG_1 = -0x655d26fd val PWM_DBSIG_1 = UnsignedInt(-0x655d26fd)
} }
} }

View File

@@ -21,9 +21,9 @@
package com.kunzisoft.keepass.database.file package com.kunzisoft.keepass.database.file
import com.kunzisoft.keepass.stream.bytes4ToInt
import com.kunzisoft.keepass.stream.readBytesLength import com.kunzisoft.keepass.stream.readBytesLength
import com.kunzisoft.keepass.stream.readBytes4ToInt import com.kunzisoft.keepass.stream.readBytes4ToUInt
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
@@ -34,15 +34,15 @@ class DatabaseHeaderKDB : DatabaseHeader() {
*/ */
var transformSeed = ByteArray(32) var transformSeed = ByteArray(32)
var signature1: Int = 0 // = PWM_DBSIG_1 var signature1 = UnsignedInt(0) // = PWM_DBSIG_1
var signature2: Int = 0 // = DBSIG_2 var signature2 = UnsignedInt(0) // = DBSIG_2
var flags: Int = 0 var flags= UnsignedInt(0)
var version: Int = 0 var version= UnsignedInt(0)
/** Number of groups in the database */ /** Number of groups in the database */
var numGroups: Int = 0 var numGroups = UnsignedInt(0)
/** Number of entries in the database */ /** Number of entries in the database */
var numEntries: Int = 0 var numEntries = UnsignedInt(0)
/** /**
* SHA-256 hash of the database, used for integrity check * SHA-256 hash of the database, used for integrity check
@@ -50,28 +50,24 @@ class DatabaseHeaderKDB : DatabaseHeader() {
var contentsHash = ByteArray(32) var contentsHash = ByteArray(32)
// As UInt // As UInt
var numKeyEncRounds: Int = 0 var numKeyEncRounds = UnsignedInt(0)
/** /**
* Parse given buf, as read from file. * Parse given buf, as read from file.
*/ */
@Throws(IOException::class) @Throws(IOException::class)
fun loadFromFile(inputStream: InputStream) { fun loadFromFile(inputStream: InputStream) {
signature1 = inputStream.readBytes4ToInt() // 4 bytes signature1 = inputStream.readBytes4ToUInt() // 4 bytes
signature2 = inputStream.readBytes4ToInt() // 4 bytes signature2 = inputStream.readBytes4ToUInt() // 4 bytes
flags = inputStream.readBytes4ToInt() // 4 bytes flags = inputStream.readBytes4ToUInt() // 4 bytes
version = inputStream.readBytes4ToInt() // 4 bytes version = inputStream.readBytes4ToUInt() // 4 bytes
masterSeed = inputStream.readBytesLength(16) // 16 bytes masterSeed = inputStream.readBytesLength(16) // 16 bytes
encryptionIV = inputStream.readBytesLength(16) // 16 bytes encryptionIV = inputStream.readBytesLength(16) // 16 bytes
numGroups = inputStream.readBytes4ToInt() // 4 bytes numGroups = inputStream.readBytes4ToUInt() // 4 bytes
numEntries = inputStream.readBytes4ToInt() // 4 bytes numEntries = inputStream.readBytes4ToUInt() // 4 bytes
contentsHash = inputStream.readBytesLength(32) // 32 bytes contentsHash = inputStream.readBytesLength(32) // 32 bytes
transformSeed = inputStream.readBytesLength(32) // 32 bytes transformSeed = inputStream.readBytesLength(32) // 32 bytes
numKeyEncRounds = inputStream.readBytes4ToInt() numKeyEncRounds = inputStream.readBytes4ToUInt()
if (numKeyEncRounds < 0) {
// TODO: Really treat this like an unsigned integer #443
throw IOException("Does not support more than " + Integer.MAX_VALUE + " rounds.")
}
} }
init { init {
@@ -88,24 +84,24 @@ class DatabaseHeaderKDB : DatabaseHeader() {
companion object { companion object {
// DB sig from KeePass 1.03 // DB sig from KeePass 1.03
const val DBSIG_2 = -0x4ab4049b val DBSIG_2 = UnsignedInt(-0x4ab4049b)
// DB sig from KeePass 1.03 // DB sig from KeePass 1.03
const val DBVER_DW = 0x00030003 val DBVER_DW = UnsignedInt(0x00030003)
const val FLAG_SHA2 = 1 val FLAG_SHA2 = UnsignedInt(1)
const val FLAG_RIJNDAEL = 2 val FLAG_RIJNDAEL = UnsignedInt(2)
const val FLAG_ARCFOUR = 4 val FLAG_ARCFOUR = UnsignedInt(4)
const val FLAG_TWOFISH = 8 val FLAG_TWOFISH = UnsignedInt(8)
/** Size of byte buffer needed to hold this struct. */ /** Size of byte buffer needed to hold this struct. */
const val BUF_SIZE = 124 const val BUF_SIZE = 124
fun matchesHeader(sig1: Int, sig2: Int): Boolean { fun matchesHeader(sig1: UnsignedInt, sig2: UnsignedInt): Boolean {
return sig1 == PWM_DBSIG_1 && sig2 == DBSIG_2 return sig1.toInt() == PWM_DBSIG_1.toInt() && sig2.toInt() == DBSIG_2.toInt()
} }
fun compatibleHeaders(one: Int, two: Int): Boolean { fun compatibleHeaders(one: UnsignedInt, two: UnsignedInt): Boolean {
return one and -0x100 == two and -0x100 return one.toInt() and -0x100 == two.toInt() and -0x100
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2020 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePassDX. * This file is part of KeePassDX.
* *
@@ -31,6 +31,8 @@ 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.UnsignedInt
import com.kunzisoft.keepass.utils.UnsignedLong
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
@@ -45,7 +47,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
var innerRandomStreamKey: ByteArray = ByteArray(32) var innerRandomStreamKey: ByteArray = ByteArray(32)
var streamStartBytes: ByteArray = ByteArray(32) var streamStartBytes: ByteArray = ByteArray(32)
var innerRandomStream: CrsAlgorithm? = null var innerRandomStream: CrsAlgorithm? = null
var version: Long = 0 var version: UnsignedInt = UnsignedInt(0)
// version < FILE_VERSION_32_4) // version < FILE_VERSION_32_4)
var transformSeed: ByteArray? var transformSeed: ByteArray?
@@ -103,7 +105,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
} }
} }
private fun getMinKdbxVersion(databaseV4: DatabaseKDBX): Long { private fun getMinKdbxVersion(databaseV4: DatabaseKDBX): UnsignedInt {
// https://keepass.info/help/kb/kdbx_4.html // https://keepass.info/help/kb/kdbx_4.html
// Return v4 if AES is not use // Return v4 if AES is not use
@@ -147,8 +149,8 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
val digestInputStream = DigestInputStream(copyInputStream, messageDigest) val digestInputStream = DigestInputStream(copyInputStream, messageDigest)
val littleEndianDataInputStream = LittleEndianDataInputStream(digestInputStream) val littleEndianDataInputStream = LittleEndianDataInputStream(digestInputStream)
val sig1 = littleEndianDataInputStream.readInt() val sig1 = littleEndianDataInputStream.readUInt()
val sig2 = littleEndianDataInputStream.readInt() val sig2 = littleEndianDataInputStream.readUInt()
if (!matchesHeader(sig1, sig2)) { if (!matchesHeader(sig1, sig2)) {
throw VersionDatabaseException() throw VersionDatabaseException()
@@ -172,10 +174,10 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
private fun readHeaderField(dis: LittleEndianDataInputStream): Boolean { private fun readHeaderField(dis: LittleEndianDataInputStream): Boolean {
val fieldID = dis.read().toByte() val fieldID = dis.read().toByte()
val fieldSize: Int = if (version < FILE_VERSION_32_4) { val fieldSize: Int = if (version.toLong() < FILE_VERSION_32_4.toLong()) {
dis.readUShort() dis.readUShort()
} else { } else {
dis.readInt() dis.readUInt().toInt()
} }
var fieldData: ByteArray? = null var fieldData: ByteArray? = null
@@ -198,20 +200,20 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
PwDbHeaderV4Fields.MasterSeed -> masterSeed = fieldData PwDbHeaderV4Fields.MasterSeed -> masterSeed = fieldData
PwDbHeaderV4Fields.TransformSeed -> if (version < FILE_VERSION_32_4) PwDbHeaderV4Fields.TransformSeed -> if (version.toLong() < FILE_VERSION_32_4.toLong())
transformSeed = fieldData transformSeed = fieldData
PwDbHeaderV4Fields.TransformRounds -> if (version < FILE_VERSION_32_4) PwDbHeaderV4Fields.TransformRounds -> if (version.toLong() < FILE_VERSION_32_4.toLong())
setTransformRound(fieldData) setTransformRound(fieldData)
PwDbHeaderV4Fields.EncryptionIV -> encryptionIV = fieldData PwDbHeaderV4Fields.EncryptionIV -> encryptionIV = fieldData
PwDbHeaderV4Fields.InnerRandomstreamKey -> if (version < FILE_VERSION_32_4) PwDbHeaderV4Fields.InnerRandomstreamKey -> if (version.toLong() < FILE_VERSION_32_4.toLong())
innerRandomStreamKey = fieldData innerRandomStreamKey = fieldData
PwDbHeaderV4Fields.StreamStartBytes -> streamStartBytes = fieldData PwDbHeaderV4Fields.StreamStartBytes -> streamStartBytes = fieldData
PwDbHeaderV4Fields.InnerRandomStreamID -> if (version < FILE_VERSION_32_4) PwDbHeaderV4Fields.InnerRandomStreamID -> if (version.toLong() < FILE_VERSION_32_4.toLong())
setRandomStreamID(fieldData) setRandomStreamID(fieldData)
PwDbHeaderV4Fields.KdfParameters -> databaseV4.kdfParameters = KdfParameters.deserialize(fieldData) PwDbHeaderV4Fields.KdfParameters -> databaseV4.kdfParameters = KdfParameters.deserialize(fieldData)
@@ -256,8 +258,8 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
throw IOException("Invalid compression flags.") throw IOException("Invalid compression flags.")
} }
val flag = bytes4ToInt(pbFlags) val flag = bytes4ToUInt(pbFlags)
if (flag < 0 || flag >= CompressionAlgorithm.values().size) { if (flag.toLong() < 0 || flag.toLong() >= CompressionAlgorithm.values().size) {
throw IOException("Unrecognized compression flag.") throw IOException("Unrecognized compression flag.")
} }
@@ -272,8 +274,8 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
throw IOException("Invalid stream id.") throw IOException("Invalid stream id.")
} }
val id = bytes4ToInt(streamID) val id = bytes4ToUInt(streamID)
if (id < 0 || id >= CrsAlgorithm.values().size) { if (id.toInt() < 0 || id.toInt() >= CrsAlgorithm.values().size) {
throw IOException("Invalid stream id.") throw IOException("Invalid stream id.")
} }
@@ -287,43 +289,42 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
* @param version Database version * @param version Database version
* @return true if it's a supported version * @return true if it's a supported version
*/ */
private fun validVersion(version: Long): Boolean { private fun validVersion(version: UnsignedInt): Boolean {
return version and FILE_VERSION_CRITICAL_MASK <= FILE_VERSION_32_4 and FILE_VERSION_CRITICAL_MASK return version.toInt() and FILE_VERSION_CRITICAL_MASK.toInt() <=
FILE_VERSION_32_4.toInt() and FILE_VERSION_CRITICAL_MASK.toInt()
} }
companion object { companion object {
var ULONG_MAX_VALUE: Long = -1 val DBSIG_PRE2 = UnsignedInt(-0x4ab4049a)
val DBSIG_2 = UnsignedInt(-0x4ab40499)
const val DBSIG_PRE2 = -0x4ab4049a private val FILE_VERSION_CRITICAL_MASK = UnsignedInt(-0x10000)
const val DBSIG_2 = -0x4ab40499 val FILE_VERSION_32_3 = UnsignedInt(0x00030001)
val FILE_VERSION_32_4 = UnsignedInt(0x00040000)
private const val FILE_VERSION_CRITICAL_MASK: Long = -0x10000 fun getCompressionFromFlag(flag: UnsignedInt): CompressionAlgorithm? {
const val FILE_VERSION_32_3: Long = 0x00030001 return when (flag.toInt()) {
const val FILE_VERSION_32_4: Long = 0x00040000
fun getCompressionFromFlag(flag: Int): CompressionAlgorithm? {
return when (flag) {
0 -> CompressionAlgorithm.None 0 -> CompressionAlgorithm.None
1 -> CompressionAlgorithm.GZip 1 -> CompressionAlgorithm.GZip
else -> null else -> null
} }
} }
fun getFlagFromCompression(compression: CompressionAlgorithm): Int { fun getFlagFromCompression(compression: CompressionAlgorithm): UnsignedInt {
return when (compression) { return when (compression) {
CompressionAlgorithm.GZip -> 1 CompressionAlgorithm.GZip -> UnsignedInt(1)
else -> 0 else -> UnsignedInt(0)
} }
} }
fun matchesHeader(sig1: Int, sig2: Int): Boolean { fun matchesHeader(sig1: UnsignedInt, sig2: UnsignedInt): Boolean {
return sig1 == PWM_DBSIG_1 && (sig2 == DBSIG_PRE2 || sig2 == DBSIG_2) return sig1 == PWM_DBSIG_1 && (sig2 == DBSIG_PRE2 || sig2 == DBSIG_2)
} }
@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, ULONG_MAX_VALUE) val blockKey = HmacBlockStream.getHmacKey64(key, UnsignedLong.ULONG_MAX_VALUE)
val hmac: Mac val hmac: Mac
try { try {

View File

@@ -90,10 +90,10 @@ class DatabaseInputKDB(cacheDirectory: File,
// Select algorithm // Select algorithm
when { when {
header.flags and DatabaseHeaderKDB.FLAG_RIJNDAEL != 0 -> { header.flags.toInt() and DatabaseHeaderKDB.FLAG_RIJNDAEL.toInt() != 0 -> {
mDatabaseToOpen.encryptionAlgorithm = EncryptionAlgorithm.AESRijndael mDatabaseToOpen.encryptionAlgorithm = EncryptionAlgorithm.AESRijndael
} }
header.flags and DatabaseHeaderKDB.FLAG_TWOFISH != 0 -> { header.flags.toInt() and DatabaseHeaderKDB.FLAG_TWOFISH.toInt() != 0 -> {
mDatabaseToOpen.encryptionAlgorithm = EncryptionAlgorithm.Twofish mDatabaseToOpen.encryptionAlgorithm = EncryptionAlgorithm.Twofish
} }
else -> throw InvalidAlgorithmDatabaseException() else -> throw InvalidAlgorithmDatabaseException()
@@ -160,8 +160,8 @@ class DatabaseInputKDB(cacheDirectory: File,
var newEntry: EntryKDB? = null var newEntry: EntryKDB? = null
var currentGroupNumber = 0 var currentGroupNumber = 0
var currentEntryNumber = 0 var currentEntryNumber = 0
while (currentGroupNumber < header.numGroups while (currentGroupNumber < header.numGroups.toLong()
|| currentEntryNumber < header.numEntries) { || currentEntryNumber < header.numEntries.toLong()) {
val fieldType = cipherInputStream.readBytes2ToUShort() val fieldType = cipherInputStream.readBytes2ToUShort()
val fieldSize = cipherInputStream.readBytes4ToUInt().toInt() val fieldSize = cipherInputStream.readBytes4ToUInt().toInt()
@@ -175,7 +175,7 @@ class DatabaseInputKDB(cacheDirectory: File,
when (fieldSize) { when (fieldSize) {
4 -> { 4 -> {
newGroup = mDatabaseToOpen.createGroup().apply { newGroup = mDatabaseToOpen.createGroup().apply {
setGroupId(cipherInputStream.readBytes4ToInt()) setGroupId(cipherInputStream.readBytes4ToUInt().toInt())
} }
} }
16 -> { 16 -> {
@@ -194,7 +194,7 @@ class DatabaseInputKDB(cacheDirectory: File,
} ?: } ?:
newEntry?.let { entry -> newEntry?.let { entry ->
val groupKDB = mDatabaseToOpen.createGroup() val groupKDB = mDatabaseToOpen.createGroup()
groupKDB.nodeId = NodeIdInt(cipherInputStream.readBytes4ToInt()) groupKDB.nodeId = NodeIdInt(cipherInputStream.readBytes4ToUInt().toInt())
entry.parent = groupKDB entry.parent = groupKDB
} }
} }
@@ -203,7 +203,7 @@ class DatabaseInputKDB(cacheDirectory: File,
group.creationTime = cipherInputStream.readBytes5ToDate() group.creationTime = cipherInputStream.readBytes5ToDate()
} ?: } ?:
newEntry?.let { entry -> newEntry?.let { entry ->
var iconId = cipherInputStream.readBytes4ToInt() var iconId = cipherInputStream.readBytes4ToUInt().toInt()
// Clean up after bug that set icon ids to -1 // Clean up after bug that set icon ids to -1
if (iconId == -1) { if (iconId == -1) {
iconId = 0 iconId = 0
@@ -237,7 +237,7 @@ class DatabaseInputKDB(cacheDirectory: File,
} }
0x0007 -> { 0x0007 -> {
newGroup?.let { group -> newGroup?.let { group ->
group.icon = mDatabaseToOpen.iconFactory.getIcon(cipherInputStream.readBytes4ToInt()) group.icon = mDatabaseToOpen.iconFactory.getIcon(cipherInputStream.readBytes4ToUInt().toInt())
} ?: } ?:
newEntry?.let { entry -> newEntry?.let { entry ->
entry.password = cipherInputStream.readBytesToString(fieldSize,false) entry.password = cipherInputStream.readBytesToString(fieldSize,false)
@@ -253,7 +253,7 @@ class DatabaseInputKDB(cacheDirectory: File,
} }
0x0009 -> { 0x0009 -> {
newGroup?.let { group -> newGroup?.let { group ->
group.flags = cipherInputStream.readBytes4ToInt() group.groupFlags = cipherInputStream.readBytes4ToUInt().toInt()
} ?: } ?:
newEntry?.let { entry -> newEntry?.let { entry ->
entry.creationTime = cipherInputStream.readBytes5ToDate() entry.creationTime = cipherInputStream.readBytes5ToDate()

View File

@@ -43,6 +43,8 @@ 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.UnsignedInt
import com.kunzisoft.keepass.utils.UnsignedLong
import org.bouncycastle.crypto.StreamCipher import org.bouncycastle.crypto.StreamCipher
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException import org.xmlpull.v1.XmlPullParserException
@@ -130,7 +132,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
} }
val isPlain: InputStream val isPlain: InputStream
if (mDatabase.kdbxVersion < DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (mDatabase.kdbxVersion.toLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
val decrypted = attachCipherStream(databaseInputStream, cipher) val decrypted = attachCipherStream(databaseInputStream, cipher)
val dataDecrypted = LittleEndianDataInputStream(decrypted) val dataDecrypted = LittleEndianDataInputStream(decrypted)
@@ -178,7 +180,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
else -> isPlain else -> isPlain
} }
if (mDatabase.kdbxVersion >= DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (mDatabase.kdbxVersion.toLong() >= DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
loadInnerHeader(inputStreamXml, header) loadInnerHeader(inputStreamXml, header)
} }
@@ -226,7 +228,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
header: DatabaseHeaderKDBX): Boolean { header: DatabaseHeaderKDBX): Boolean {
val fieldId = dataInputStream.read().toByte() val fieldId = dataInputStream.read().toByte()
val size = dataInputStream.readInt() val size = dataInputStream.readUInt().toInt()
if (size < 0) throw IOException("Corrupted file") if (size < 0) throw IOException("Corrupted file")
when (fieldId) { when (fieldId) {
@@ -488,7 +490,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
} else if (name.equals(DatabaseKDBXXML.ElemNotes, ignoreCase = true)) { } else if (name.equals(DatabaseKDBXXML.ElemNotes, ignoreCase = true)) {
ctxGroup?.notes = readString(xpp) ctxGroup?.notes = readString(xpp)
} else if (name.equals(DatabaseKDBXXML.ElemIcon, ignoreCase = true)) { } else if (name.equals(DatabaseKDBXXML.ElemIcon, ignoreCase = true)) {
ctxGroup?.icon = mDatabase.iconFactory.getIcon(readUInt(xpp, 0).toInt()) ctxGroup?.icon = mDatabase.iconFactory.getIcon(readUInt(xpp, UnsignedInt(0)).toInt())
} else if (name.equals(DatabaseKDBXXML.ElemCustomIconID, ignoreCase = true)) { } else if (name.equals(DatabaseKDBXXML.ElemCustomIconID, ignoreCase = true)) {
ctxGroup?.iconCustom = mDatabase.iconFactory.getIcon(readUuid(xpp)) ctxGroup?.iconCustom = mDatabase.iconFactory.getIcon(readUuid(xpp))
} else if (name.equals(DatabaseKDBXXML.ElemTimes, ignoreCase = true)) { } else if (name.equals(DatabaseKDBXXML.ElemTimes, ignoreCase = true)) {
@@ -542,7 +544,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
KdbContext.Entry -> if (name.equals(DatabaseKDBXXML.ElemUuid, ignoreCase = true)) { KdbContext.Entry -> if (name.equals(DatabaseKDBXXML.ElemUuid, ignoreCase = true)) {
ctxEntry?.nodeId = NodeIdUUID(readUuid(xpp)) ctxEntry?.nodeId = NodeIdUUID(readUuid(xpp))
} else if (name.equals(DatabaseKDBXXML.ElemIcon, ignoreCase = true)) { } else if (name.equals(DatabaseKDBXXML.ElemIcon, ignoreCase = true)) {
ctxEntry?.icon = mDatabase.iconFactory.getIcon(readUInt(xpp, 0).toInt()) ctxEntry?.icon = mDatabase.iconFactory.getIcon(readUInt(xpp, UnsignedInt(0)).toInt())
} else if (name.equals(DatabaseKDBXXML.ElemCustomIconID, ignoreCase = true)) { } else if (name.equals(DatabaseKDBXXML.ElemCustomIconID, ignoreCase = true)) {
ctxEntry?.iconCustom = mDatabase.iconFactory.getIcon(readUuid(xpp)) ctxEntry?.iconCustom = mDatabase.iconFactory.getIcon(readUuid(xpp))
} else if (name.equals(DatabaseKDBXXML.ElemFgColor, ignoreCase = true)) { } else if (name.equals(DatabaseKDBXXML.ElemFgColor, ignoreCase = true)) {
@@ -598,7 +600,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
name.equals(DatabaseKDBXXML.ElemLastAccessTime, ignoreCase = true) -> tl?.lastAccessTime = readPwTime(xpp) name.equals(DatabaseKDBXXML.ElemLastAccessTime, ignoreCase = true) -> tl?.lastAccessTime = readPwTime(xpp)
name.equals(DatabaseKDBXXML.ElemExpiryTime, ignoreCase = true) -> tl?.expiryTime = readPwTime(xpp) name.equals(DatabaseKDBXXML.ElemExpiryTime, ignoreCase = true) -> tl?.expiryTime = readPwTime(xpp)
name.equals(DatabaseKDBXXML.ElemExpires, ignoreCase = true) -> tl?.expires = readBool(xpp, false) name.equals(DatabaseKDBXXML.ElemExpires, ignoreCase = true) -> tl?.expires = readBool(xpp, false)
name.equals(DatabaseKDBXXML.ElemUsageCount, ignoreCase = true) -> tl?.usageCount = readULong(xpp, 0) name.equals(DatabaseKDBXXML.ElemUsageCount, ignoreCase = true) -> tl?.usageCount = readULong(xpp, UnsignedLong(0))
name.equals(DatabaseKDBXXML.ElemLocationChanged, ignoreCase = true) -> tl?.locationChanged = readPwTime(xpp) name.equals(DatabaseKDBXXML.ElemLocationChanged, ignoreCase = true) -> tl?.locationChanged = readPwTime(xpp)
else -> readUnknown(xpp) else -> readUnknown(xpp)
} }
@@ -621,7 +623,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
KdbContext.EntryAutoType -> if (name.equals(DatabaseKDBXXML.ElemAutoTypeEnabled, ignoreCase = true)) { KdbContext.EntryAutoType -> if (name.equals(DatabaseKDBXXML.ElemAutoTypeEnabled, ignoreCase = true)) {
ctxEntry?.autoType?.enabled = readBool(xpp, true) ctxEntry?.autoType?.enabled = readBool(xpp, true)
} else if (name.equals(DatabaseKDBXXML.ElemAutoTypeObfuscation, ignoreCase = true)) { } else if (name.equals(DatabaseKDBXXML.ElemAutoTypeObfuscation, ignoreCase = true)) {
ctxEntry?.autoType?.obfuscationOptions = readUInt(xpp, 0) ctxEntry?.autoType?.obfuscationOptions = readUInt(xpp, UnsignedInt(0))
} else if (name.equals(DatabaseKDBXXML.ElemAutoTypeDefaultSeq, ignoreCase = true)) { } else if (name.equals(DatabaseKDBXXML.ElemAutoTypeDefaultSeq, ignoreCase = true)) {
ctxEntry?.autoType?.defaultSequence = readString(xpp) ctxEntry?.autoType?.defaultSequence = readString(xpp)
} else if (name.equals(DatabaseKDBXXML.ElemAutoTypeItem, ignoreCase = true)) { } else if (name.equals(DatabaseKDBXXML.ElemAutoTypeItem, ignoreCase = true)) {
@@ -815,7 +817,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
val sDate = readString(xpp) val sDate = readString(xpp)
var utcDate: Date? = null var utcDate: Date? = null
if (mDatabase.kdbxVersion >= DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (mDatabase.kdbxVersion.toLong() >= DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
var buf = Base64.decode(sDate, BASE_64_FLAG) var buf = Base64.decode(sDate, BASE_64_FLAG)
if (buf.size != 8) { if (buf.size != 8) {
val buf8 = ByteArray(8) val buf8 = ByteArray(8)
@@ -887,48 +889,39 @@ class DatabaseInputKDBX(cacheDirectory: File,
} }
@Throws(IOException::class, XmlPullParserException::class) @Throws(IOException::class, XmlPullParserException::class)
private fun readInt(xpp: XmlPullParser, def: Int): Int { private fun readInt(xpp: XmlPullParser, default: Int): Int {
val str = readString(xpp)
return try { return try {
Integer.parseInt(str) readString(xpp).toInt()
} catch (e: NumberFormatException) { } catch (e: Exception) {
def default
} }
} }
@Throws(IOException::class, XmlPullParserException::class) @Throws(IOException::class, XmlPullParserException::class)
private fun readUInt(xpp: XmlPullParser, uDefault: Long): Long { private fun readUInt(xpp: XmlPullParser, default: UnsignedInt): UnsignedInt {
val u: Long = readULong(xpp, uDefault)
if (u < 0 || u > MAX_UINT) {
throw NumberFormatException("Outside of the uint size")
}
return u
}
@Throws(IOException::class, XmlPullParserException::class)
private fun readLong(xpp: XmlPullParser, def: Long): Long {
val str = readString(xpp)
return try { return try {
java.lang.Long.parseLong(str) UnsignedInt(readString(xpp).toInt())
} catch (e: NumberFormatException) { } catch (e: Exception) {
def default
} }
} }
@Throws(IOException::class, XmlPullParserException::class) @Throws(IOException::class, XmlPullParserException::class)
private fun readULong(xpp: XmlPullParser, uDefault: Long): Long { private fun readLong(xpp: XmlPullParser, default: Long): Long {
var u = readLong(xpp, uDefault) return try {
readString(xpp).toLong()
if (u < 0) { } catch (e: Exception) {
u = uDefault default
} }
}
return u @Throws(IOException::class, XmlPullParserException::class)
private fun readULong(xpp: XmlPullParser, default: UnsignedLong): UnsignedLong {
return try {
UnsignedLong(readString(xpp).toLong())
} catch (e: Exception) {
default
}
} }
@Throws(XmlPullParserException::class, IOException::class) @Throws(XmlPullParserException::class, IOException::class)
@@ -1050,7 +1043,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
companion object { companion object {
private const val DEFAULT_HISTORY_DAYS: Long = 365 private val DEFAULT_HISTORY_DAYS = UnsignedInt(365)
@Throws(XmlPullParserException::class) @Throws(XmlPullParserException::class)
private fun createPullParser(readerStream: InputStream): XmlPullParser { private fun createPullParser(readerStream: InputStream): XmlPullParser {
@@ -1062,8 +1055,6 @@ class DatabaseInputKDBX(cacheDirectory: File,
return xpp return xpp
} }
private const val MAX_UINT = 4294967296L // 2^32
} }
} }

View File

@@ -20,7 +20,7 @@
package com.kunzisoft.keepass.database.file.output package com.kunzisoft.keepass.database.file.output
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB
import com.kunzisoft.keepass.stream.intTo4Bytes import com.kunzisoft.keepass.stream.uIntTo4Bytes
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(intTo4Bytes(mHeader.signature1)) mOutputStream.write(uIntTo4Bytes(mHeader.signature1))
mOutputStream.write(intTo4Bytes(mHeader.signature2)) mOutputStream.write(uIntTo4Bytes(mHeader.signature2))
mOutputStream.write(intTo4Bytes(mHeader.flags)) mOutputStream.write(uIntTo4Bytes(mHeader.flags))
mOutputStream.write(intTo4Bytes(mHeader.version)) mOutputStream.write(uIntTo4Bytes(mHeader.version))
mOutputStream.write(mHeader.masterSeed) mOutputStream.write(mHeader.masterSeed)
mOutputStream.write(mHeader.encryptionIV) mOutputStream.write(mHeader.encryptionIV)
mOutputStream.write(intTo4Bytes(mHeader.numGroups)) mOutputStream.write(uIntTo4Bytes(mHeader.numGroups))
mOutputStream.write(intTo4Bytes(mHeader.numEntries)) mOutputStream.write(uIntTo4Bytes(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(intTo4Bytes(mHeader.numKeyEncRounds)) mOutputStream.write(uIntTo4Bytes(mHeader.numKeyEncRounds))
} }
@Throws(IOException::class) @Throws(IOException::class)

View File

@@ -24,8 +24,8 @@ import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
import com.kunzisoft.keepass.database.exception.DatabaseOutputException import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.database.file.DatabaseHeader import com.kunzisoft.keepass.database.file.DatabaseHeader
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.ULONG_MAX_VALUE
import com.kunzisoft.keepass.stream.* import com.kunzisoft.keepass.stream.*
import com.kunzisoft.keepass.utils.UnsignedLong
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
@@ -66,7 +66,7 @@ constructor(private val databaseKDBX: DatabaseKDBX,
val hmac: Mac val hmac: Mac
try { try {
hmac = Mac.getInstance("HmacSHA256") hmac = Mac.getInstance("HmacSHA256")
val signingKey = SecretKeySpec(HmacBlockStream.getHmacKey64(hmacKey, ULONG_MAX_VALUE), "HmacSHA256") val signingKey = SecretKeySpec(HmacBlockStream.getHmacKey64(hmacKey, UnsignedLong.ULONG_MAX_VALUE), "HmacSHA256")
hmac.init(signingKey) hmac.init(signingKey)
} catch (e: NoSuchAlgorithmException) { } catch (e: NoSuchAlgorithmException) {
throw DatabaseOutputException(e) throw DatabaseOutputException(e)
@@ -82,15 +82,15 @@ constructor(private val databaseKDBX: DatabaseKDBX,
@Throws(IOException::class) @Throws(IOException::class)
fun output() { fun output() {
los.writeUInt(DatabaseHeader.PWM_DBSIG_1.toLong()) los.writeUInt(DatabaseHeader.PWM_DBSIG_1)
los.writeUInt(DatabaseHeaderKDBX.DBSIG_2.toLong()) los.writeUInt(DatabaseHeaderKDBX.DBSIG_2)
los.writeUInt(header.version) los.writeUInt(header.version)
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CipherID, uuidTo16Bytes(databaseKDBX.dataCipher)) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CipherID, uuidTo16Bytes(databaseKDBX.dataCipher))
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CompressionFlags, intTo4Bytes(DatabaseHeaderKDBX.getFlagFromCompression(databaseKDBX.compressionAlgorithm))) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CompressionFlags, uIntTo4Bytes(DatabaseHeaderKDBX.getFlagFromCompression(databaseKDBX.compressionAlgorithm)))
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.MasterSeed, header.masterSeed) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.MasterSeed, header.masterSeed)
if (header.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (header.version.toLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformSeed, header.transformSeed) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformSeed, header.transformSeed)
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformRounds, longTo8Bytes(databaseKDBX.numberKeyEncryptionRounds)) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformRounds, longTo8Bytes(databaseKDBX.numberKeyEncryptionRounds))
} else { } else {
@@ -101,10 +101,10 @@ constructor(private val databaseKDBX: DatabaseKDBX,
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.EncryptionIV, header.encryptionIV) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.EncryptionIV, header.encryptionIV)
} }
if (header.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (header.version.toLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
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, intTo4Bytes(header.innerRandomStream!!.id)) writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomStreamID, uIntTo4Bytes(header.innerRandomStream!!.id))
} }
if (databaseKDBX.containsPublicCustomData()) { if (databaseKDBX.containsPublicCustomData()) {
@@ -136,7 +136,7 @@ constructor(private val databaseKDBX: DatabaseKDBX,
@Throws(IOException::class) @Throws(IOException::class)
private fun writeHeaderFieldSize(size: Int) { private fun writeHeaderFieldSize(size: Int) {
if (header.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (header.version.toLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
los.writeUShort(size) los.writeUShort(size)
} else { } else {
los.writeInt(size) los.writeInt(size)

View File

@@ -40,7 +40,7 @@ class DatabaseInnerHeaderOutputKDBX(private val database: DatabaseKDBX,
dataOutputStream.writeInt(4) dataOutputStream.writeInt(4)
if (header.innerRandomStream == null) if (header.innerRandomStream == null)
throw IOException("Can't write innerRandomStream") throw IOException("Can't write innerRandomStream")
dataOutputStream.writeInt(header.innerRandomStream!!.id) dataOutputStream.writeInt(header.innerRandomStream!!.id.toInt())
val streamKeySize = header.innerRandomStreamKey.size val streamKeySize = header.innerRandomStreamKey.size
dataOutputStream.write(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.InnerRandomstreamKey.toInt()) dataOutputStream.write(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.InnerRandomstreamKey.toInt())

View File

@@ -28,6 +28,7 @@ import com.kunzisoft.keepass.database.file.DatabaseHeader
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB
import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream
import com.kunzisoft.keepass.stream.NullOutputStream import com.kunzisoft.keepass.stream.NullOutputStream
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
@@ -117,18 +118,18 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
when { when {
mDatabaseKDB.encryptionAlgorithm === EncryptionAlgorithm.AESRijndael -> { mDatabaseKDB.encryptionAlgorithm === EncryptionAlgorithm.AESRijndael -> {
header.flags = header.flags or DatabaseHeaderKDB.FLAG_RIJNDAEL header.flags = UnsignedInt(header.flags.toInt() or DatabaseHeaderKDB.FLAG_RIJNDAEL.toInt())
} }
mDatabaseKDB.encryptionAlgorithm === EncryptionAlgorithm.Twofish -> { mDatabaseKDB.encryptionAlgorithm === EncryptionAlgorithm.Twofish -> {
header.flags = header.flags or DatabaseHeaderKDB.FLAG_TWOFISH header.flags = UnsignedInt(header.flags.toInt() or DatabaseHeaderKDB.FLAG_TWOFISH.toInt())
} }
else -> throw DatabaseOutputException("Unsupported algorithm.") else -> throw DatabaseOutputException("Unsupported algorithm.")
} }
header.version = DatabaseHeaderKDB.DBVER_DW header.version = DatabaseHeaderKDB.DBVER_DW
header.numGroups = mDatabaseKDB.numberOfGroups() header.numGroups = UnsignedInt(mDatabaseKDB.numberOfGroups())
header.numEntries = mDatabaseKDB.numberOfEntries() header.numEntries = UnsignedInt(mDatabaseKDB.numberOfEntries())
header.numKeyEncRounds = mDatabaseKDB.numberKeyEncryptionRounds.toInt() // TODO Signed Long - Unsigned Int #443 header.numKeyEncRounds = UnsignedInt.fromLong(mDatabaseKDB.numberKeyEncryptionRounds)
setIVs(header) setIVs(header)
@@ -279,6 +280,5 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
if (data != null) { if (data != null) {
los.write(data) los.write(data)
} }
} }
} }

View File

@@ -85,7 +85,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
header = outputHeader(mOS) header = outputHeader(mOS)
val osPlain: OutputStream val osPlain: OutputStream
osPlain = if (header!!.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) { osPlain = if (header!!.version.toLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
val cos = attachStreamEncryptor(header!!, mOS) val cos = attachStreamEncryptor(header!!, mOS)
cos.write(header!!.streamStartBytes) cos.write(header!!.streamStartBytes)
@@ -105,7 +105,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
else -> osPlain else -> osPlain
} }
if (header!!.version >= DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (header!!.version.toLong() >= DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
val ihOut = DatabaseInnerHeaderOutputKDBX(mDatabaseKDBX, header!!, osXml) val ihOut = DatabaseInnerHeaderOutputKDBX(mDatabaseKDBX, header!!, osXml)
ihOut.output() ihOut.output()
} }
@@ -209,7 +209,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
writeObject(DatabaseKDBXXML.ElemDbDescChanged, mDatabaseKDBX.descriptionChanged.date) writeObject(DatabaseKDBXXML.ElemDbDescChanged, mDatabaseKDBX.descriptionChanged.date)
writeObject(DatabaseKDBXXML.ElemDbDefaultUser, mDatabaseKDBX.defaultUserName, true) writeObject(DatabaseKDBXXML.ElemDbDefaultUser, mDatabaseKDBX.defaultUserName, true)
writeObject(DatabaseKDBXXML.ElemDbDefaultUserChanged, mDatabaseKDBX.defaultUserNameChanged.date) writeObject(DatabaseKDBXXML.ElemDbDefaultUserChanged, mDatabaseKDBX.defaultUserNameChanged.date)
writeObject(DatabaseKDBXXML.ElemDbMntncHistoryDays, mDatabaseKDBX.maintenanceHistoryDays) writeObject(DatabaseKDBXXML.ElemDbMntncHistoryDays, mDatabaseKDBX.maintenanceHistoryDays.toLong())
writeObject(DatabaseKDBXXML.ElemDbColor, mDatabaseKDBX.color) writeObject(DatabaseKDBXXML.ElemDbColor, mDatabaseKDBX.color)
writeObject(DatabaseKDBXXML.ElemDbKeyChanged, mDatabaseKDBX.keyLastChanged.date) writeObject(DatabaseKDBXXML.ElemDbKeyChanged, mDatabaseKDBX.keyLastChanged.date)
writeObject(DatabaseKDBXXML.ElemDbKeyChangeRec, mDatabaseKDBX.keyChangeRecDays) writeObject(DatabaseKDBXXML.ElemDbKeyChangeRec, mDatabaseKDBX.keyChangeRecDays)
@@ -230,7 +230,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
writeUuid(DatabaseKDBXXML.ElemLastTopVisibleGroup, mDatabaseKDBX.lastTopVisibleGroupUUID) writeUuid(DatabaseKDBXXML.ElemLastTopVisibleGroup, mDatabaseKDBX.lastTopVisibleGroupUUID)
// Seem to work properly if always in meta // Seem to work properly if always in meta
if (header!!.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) if (header!!.version.toLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong())
writeMetaBinaries() writeMetaBinaries()
writeCustomData(mDatabaseKDBX.customData) writeCustomData(mDatabaseKDBX.customData)
@@ -274,7 +274,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
Log.e(TAG, "Unable to retrieve header", unknownKDF) Log.e(TAG, "Unable to retrieve header", unknownKDF)
} }
if (header.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (header.version.toLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
header.innerRandomStream = CrsAlgorithm.Salsa20 header.innerRandomStream = CrsAlgorithm.Salsa20
header.innerRandomStreamKey = ByteArray(32) header.innerRandomStreamKey = ByteArray(32)
} else { } else {
@@ -288,7 +288,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
throw DatabaseOutputException("Invalid random cipher") throw DatabaseOutputException("Invalid random cipher")
} }
if (header.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (header.version.toLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
random.nextBytes(header.streamStartBytes) random.nextBytes(header.streamStartBytes)
} }
@@ -385,7 +385,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
private fun writeObject(name: String, value: Date) { private fun writeObject(name: String, value: Date) {
if (header!!.version < DatabaseHeaderKDBX.FILE_VERSION_32_4) { if (header!!.version.toLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) {
writeObject(name, DatabaseKDBXXML.DateFormatter.format(value)) writeObject(name, DatabaseKDBXXML.DateFormatter.format(value))
} else { } else {
val dt = DateTime(value) val dt = DateTime(value)
@@ -489,7 +489,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
xml.startTag(null, DatabaseKDBXXML.ElemAutoType) xml.startTag(null, DatabaseKDBXXML.ElemAutoType)
writeObject(DatabaseKDBXXML.ElemAutoTypeEnabled, autoType.enabled) writeObject(DatabaseKDBXXML.ElemAutoTypeEnabled, autoType.enabled)
writeObject(DatabaseKDBXXML.ElemAutoTypeObfuscation, autoType.obfuscationOptions) writeObject(DatabaseKDBXXML.ElemAutoTypeObfuscation, autoType.obfuscationOptions.toLong())
if (autoType.defaultSequence.isNotEmpty()) { if (autoType.defaultSequence.isNotEmpty()) {
writeObject(DatabaseKDBXXML.ElemAutoTypeDefaultSeq, autoType.defaultSequence, true) writeObject(DatabaseKDBXXML.ElemAutoTypeDefaultSeq, autoType.defaultSequence, true)
@@ -629,7 +629,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
writeObject(DatabaseKDBXXML.ElemLastAccessTime, node.lastAccessTime.date) writeObject(DatabaseKDBXXML.ElemLastAccessTime, node.lastAccessTime.date)
writeObject(DatabaseKDBXXML.ElemExpiryTime, node.expiryTime.date) writeObject(DatabaseKDBXXML.ElemExpiryTime, node.expiryTime.date)
writeObject(DatabaseKDBXXML.ElemExpires, node.expires) writeObject(DatabaseKDBXXML.ElemExpires, node.expires)
writeObject(DatabaseKDBXXML.ElemUsageCount, node.usageCount) writeObject(DatabaseKDBXXML.ElemUsageCount, node.usageCount.toLong())
writeObject(DatabaseKDBXXML.ElemLocationChanged, node.locationChanged.date) writeObject(DatabaseKDBXXML.ElemLocationChanged, node.locationChanged.date)
xml.endTag(null, DatabaseKDBXXML.ElemTimes) xml.endTag(null, DatabaseKDBXXML.ElemTimes)

View File

@@ -24,6 +24,7 @@ import com.kunzisoft.keepass.database.element.entry.EntryKDB
import com.kunzisoft.keepass.database.file.output.GroupOutputKDB.Companion.GROUPID_FIELD_SIZE import com.kunzisoft.keepass.database.file.output.GroupOutputKDB.Companion.GROUPID_FIELD_SIZE
import com.kunzisoft.keepass.stream.* import com.kunzisoft.keepass.stream.*
import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
import java.nio.charset.Charset import java.nio.charset.Charset
@@ -54,12 +55,12 @@ class EntryOutputKDB
// 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(intTo4Bytes(mEntry.parent!!.id)) mOutputStream.write(uIntTo4Bytes(UnsignedInt(mEntry.parent!!.id)))
// 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(intTo4Bytes(mEntry.icon.iconId)) mOutputStream.write(uIntTo4Bytes(UnsignedInt(mEntry.icon.iconId)))
// Title // Title
//byte[] title = mEntry.title.getBytes("UTF-8"); //byte[] title = mEntry.title.getBytes("UTF-8");
@@ -101,14 +102,9 @@ class EntryOutputKDB
// Binary // Binary
mOutputStream.write(BINARY_DATA_FIELD_TYPE) mOutputStream.write(BINARY_DATA_FIELD_TYPE)
val binaryData = mEntry.binaryData val binaryData = mEntry.binaryData
val binaryDataLength = binaryData?.length() ?: 0 val binaryDataLength = binaryData?.length() ?: 0L
val binaryDataLengthRightSize = if (binaryDataLength <= Int.MAX_VALUE) {
binaryDataLength.toInt()
} else {
0 // TODO if length > UInt.maxvalue show exception #443
}
// Write data length // Write data length
mOutputStream.write(intTo4Bytes(binaryDataLengthRightSize)) mOutputStream.write(uIntTo4Bytes(UnsignedInt.fromLong(binaryDataLength)))
// Write data // Write data
if (binaryDataLength > 0) { if (binaryDataLength > 0) {
binaryData?.getInputDataStream().use { inputStream -> binaryData?.getInputDataStream().use { inputStream ->
@@ -140,7 +136,7 @@ class EntryOutputKDB
private fun writePassword(str: String, os: OutputStream): Int { private fun writePassword(str: String, os: OutputStream): Int {
val initial = str.toByteArray(Charset.forName("UTF-8")) val initial = str.toByteArray(Charset.forName("UTF-8"))
val length = initial.size + 1 val length = initial.size + 1
os.write(intTo4Bytes(length)) os.write(uIntTo4Bytes(UnsignedInt(length)))
os.write(initial) os.write(initial)
os.write(0x00) os.write(0x00)
return length return length
@@ -164,13 +160,13 @@ class EntryOutputKDB
val BINARY_DATA_FIELD_TYPE:ByteArray = uShortTo2Bytes(14) val BINARY_DATA_FIELD_TYPE:ByteArray = uShortTo2Bytes(14)
val END_FIELD_TYPE:ByteArray = uShortTo2Bytes(0xFFFF) val END_FIELD_TYPE:ByteArray = uShortTo2Bytes(0xFFFF)
val LONG_FOUR:ByteArray = intTo4Bytes(4) val LONG_FOUR:ByteArray = uIntTo4Bytes(UnsignedInt(4))
val UUID_FIELD_SIZE:ByteArray = intTo4Bytes(16) val UUID_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(16))
val DATE_FIELD_SIZE:ByteArray = intTo4Bytes(5) val DATE_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(5))
val IMAGEID_FIELD_SIZE:ByteArray = intTo4Bytes(4) val IMAGEID_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4))
val LEVEL_FIELD_SIZE:ByteArray = intTo4Bytes(4) val LEVEL_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4))
val FLAGS_FIELD_SIZE:ByteArray = intTo4Bytes(4) val FLAGS_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4))
val ZERO_FIELD_SIZE:ByteArray = intTo4Bytes(0) val ZERO_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(0))
val ZERO_FIVE:ByteArray = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00) val ZERO_FIVE:ByteArray = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)
} }
} }

View File

@@ -21,9 +21,10 @@ 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.dateTo5Bytes import com.kunzisoft.keepass.stream.dateTo5Bytes
import com.kunzisoft.keepass.stream.intTo4Bytes import com.kunzisoft.keepass.stream.uIntTo4Bytes
import com.kunzisoft.keepass.stream.uShortTo2Bytes import com.kunzisoft.keepass.stream.uShortTo2Bytes
import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
@@ -39,7 +40,7 @@ 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(intTo4Bytes(mGroup.id)) mOutputStream.write(uIntTo4Bytes(UnsignedInt(mGroup.id)))
// Name // Name
mOutputStream.write(NAME_FIELD_TYPE) mOutputStream.write(NAME_FIELD_TYPE)
@@ -68,7 +69,7 @@ class GroupOutputKDB (private val mGroup: GroupKDB, private val mOutputStream: O
// 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(intTo4Bytes(mGroup.icon.iconId)) mOutputStream.write(uIntTo4Bytes(UnsignedInt(mGroup.icon.iconId)))
// Level // Level
mOutputStream.write(LEVEL_FIELD_TYPE) mOutputStream.write(LEVEL_FIELD_TYPE)
@@ -78,7 +79,7 @@ class GroupOutputKDB (private val mGroup: GroupKDB, private val mOutputStream: O
// Flags // Flags
mOutputStream.write(FLAGS_FIELD_TYPE) mOutputStream.write(FLAGS_FIELD_TYPE)
mOutputStream.write(FLAGS_FIELD_SIZE) mOutputStream.write(FLAGS_FIELD_SIZE)
mOutputStream.write(intTo4Bytes(mGroup.flags)) mOutputStream.write(uIntTo4Bytes(UnsignedInt(mGroup.groupFlags)))
// End // End
mOutputStream.write(END_FIELD_TYPE) mOutputStream.write(END_FIELD_TYPE)
@@ -98,11 +99,11 @@ class GroupOutputKDB (private val mGroup: GroupKDB, private val mOutputStream: O
val FLAGS_FIELD_TYPE:ByteArray = uShortTo2Bytes(9) val FLAGS_FIELD_TYPE:ByteArray = uShortTo2Bytes(9)
val END_FIELD_TYPE:ByteArray = uShortTo2Bytes(0xFFFF) val END_FIELD_TYPE:ByteArray = uShortTo2Bytes(0xFFFF)
val GROUPID_FIELD_SIZE:ByteArray = intTo4Bytes(4) val GROUPID_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4))
val DATE_FIELD_SIZE:ByteArray = intTo4Bytes(5) val DATE_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(5))
val IMAGEID_FIELD_SIZE:ByteArray = intTo4Bytes(4) val IMAGEID_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4))
val LEVEL_FIELD_SIZE:ByteArray = intTo4Bytes(2) val LEVEL_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(2))
val FLAGS_FIELD_SIZE:ByteArray = intTo4Bytes(4) val FLAGS_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4))
val ZERO_FIELD_SIZE:ByteArray = intTo4Bytes(0) val ZERO_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(0))
} }
} }

View File

@@ -444,8 +444,8 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() {
mMemoryPref?.summary = memoryToShow.toString() mMemoryPref?.summary = memoryToShow.toString()
} }
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_PARALLELISM_TASK -> { DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_PARALLELISM_TASK -> {
val oldParallelism = data.getInt(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) val oldParallelism = data.getLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
val newParallelism = data.getInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) val newParallelism = data.getLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
val parallelismToShow = val parallelismToShow =
if (result.isSuccess) { if (result.isSuccess) {
newParallelism newParallelism

View File

@@ -35,27 +35,26 @@ class ParallelismPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFr
override fun onDialogClosed(positiveResult: Boolean) { override fun onDialogClosed(positiveResult: Boolean) {
if (positiveResult) { if (positiveResult) {
database?.let { database -> database?.let { database ->
var parallelism: Int = try { val parallelism: Long = try {
inputText.toInt() inputText.toLong()
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {
MIN_PARALLELISM MIN_PARALLELISM
} }
if (parallelism < MIN_PARALLELISM) {
parallelism = MIN_PARALLELISM
}
// TODO Max Parallelism
val oldParallelism = database.parallelism val oldParallelism = database.parallelism
database.parallelism = parallelism database.parallelism = parallelism
mProgressDialogThread?.startDatabaseSaveParallelism(oldParallelism, parallelism, mDatabaseAutoSaveEnable) mProgressDialogThread?.startDatabaseSaveParallelism(
oldParallelism,
parallelism,
mDatabaseAutoSaveEnable)
} }
} }
} }
companion object { companion object {
const val MIN_PARALLELISM = 1 const val MIN_PARALLELISM = 1L
fun newInstance(key: String): ParallelismPreferenceDialogFragmentCompat { fun newInstance(key: String): ParallelismPreferenceDialogFragmentCompat {
val fragment = ParallelismPreferenceDialogFragmentCompat() val fragment = ParallelismPreferenceDialogFragmentCompat()

View File

@@ -19,6 +19,7 @@
*/ */
package com.kunzisoft.keepass.stream package com.kunzisoft.keepass.stream
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.security.MessageDigest import java.security.MessageDigest
@@ -80,7 +81,7 @@ class HashedBlockInputStream(inputStream: InputStream) : InputStream() {
bufferPos = 0 bufferPos = 0
val index = baseStream.readUInt() val index = baseStream.readUInt()
if (index != bufferIndex) { if (index.toLong() != bufferIndex) {
throw IOException("Invalid data format") throw IOException("Invalid data format")
} }
bufferIndex++ bufferIndex++
@@ -90,11 +91,7 @@ class HashedBlockInputStream(inputStream: InputStream) : InputStream() {
throw IOException("Invalid data format") throw IOException("Invalid data format")
} }
val bufferSize = baseStream.readBytes4ToInt() val bufferSize = baseStream.readBytes4ToUInt().toInt()
if (bufferSize < 0) {
throw IOException("Invalid data format")
}
if (bufferSize == 0) { if (bufferSize == 0) {
for (hash in 0 until HASH_SIZE) { for (hash in 0 until HASH_SIZE) {
if (storedHash[hash].toInt() != 0) { if (storedHash[hash].toInt() != 0) {
@@ -144,7 +141,7 @@ class HashedBlockInputStream(inputStream: InputStream) : InputStream() {
if (!readHashedBlock()) return -1 if (!readHashedBlock()) return -1
} }
val output = byteToUInt(buffer[bufferPos]) val output = UnsignedInt.fromByte(buffer[bufferPos]).toInt()
bufferPos++ bufferPos++
return output return output

View File

@@ -19,6 +19,7 @@
*/ */
package com.kunzisoft.keepass.stream package com.kunzisoft.keepass.stream
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
import java.security.MessageDigest import java.security.MessageDigest
@@ -98,7 +99,7 @@ class HashedBlockOutputStream : OutputStream {
@Throws(IOException::class) @Throws(IOException::class)
private fun writeHashedBlock() { private fun writeHashedBlock() {
baseStream.writeUInt(bufferIndex) baseStream.writeUInt(UnsignedInt.fromLong(bufferIndex))
bufferIndex++ bufferIndex++
if (bufferPos > 0) { if (bufferPos > 0) {

View File

@@ -19,6 +19,7 @@
*/ */
package com.kunzisoft.keepass.stream package com.kunzisoft.keepass.stream
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.security.InvalidKeyException import java.security.InvalidKeyException
@@ -43,7 +44,7 @@ class HmacBlockInputStream(baseStream: InputStream, private val verify: Boolean,
if (!readSafeBlock()) return -1 if (!readSafeBlock()) return -1
} }
val output = byteToUInt(buffer[bufferPos]) val output = UnsignedInt.fromByte(buffer[bufferPos]).toInt()
bufferPos++ bufferPos++
return output return output
@@ -97,10 +98,10 @@ class HmacBlockInputStream(baseStream: InputStream, private val verify: Boolean,
if (pbBlockSize.size != 4) { if (pbBlockSize.size != 4) {
throw IOException("File corrupted") throw IOException("File corrupted")
} }
val blockSize = bytes4ToInt(pbBlockSize) val blockSize = bytes4ToUInt(pbBlockSize)
bufferPos = 0 bufferPos = 0
buffer = baseStream.readBytes(blockSize) buffer = baseStream.readBytes(blockSize.toInt())
if (verify) { if (verify) {
val cmpHmac: ByteArray val cmpHmac: ByteArray
@@ -134,7 +135,7 @@ class HmacBlockInputStream(baseStream: InputStream, private val verify: Boolean,
blockIndex++ blockIndex++
if (blockSize == 0) { if (blockSize.toLong() == 0L) {
endOfStream = true endOfStream = true
return false return false
} }

View File

@@ -19,6 +19,7 @@
*/ */
package com.kunzisoft.keepass.stream package com.kunzisoft.keepass.stream
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
import java.security.InvalidKeyException import java.security.InvalidKeyException
@@ -87,7 +88,7 @@ class HmacBlockOutputStream(outputStream: OutputStream,
@Throws(IOException::class) @Throws(IOException::class)
private fun writeSafeBlock() { private fun writeSafeBlock() {
val bufBlockIndex = longTo8Bytes(blockIndex) val bufBlockIndex = longTo8Bytes(blockIndex)
val blockSizeBuf = intTo4Bytes(bufferPos) val blockSizeBuf = uIntTo4Bytes(UnsignedInt(bufferPos))
val blockHmac: ByteArray val blockHmac: ByteArray
val blockKey = HmacBlockStream.getHmacKey64(key, blockIndex) val blockKey = HmacBlockStream.getHmacKey64(key, blockIndex)

View File

@@ -19,6 +19,7 @@
*/ */
package com.kunzisoft.keepass.stream package com.kunzisoft.keepass.stream
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
@@ -32,15 +33,10 @@ class LittleEndianDataInputStream(private val baseStream: InputStream) : InputSt
* be interpreted as an unsigned integer. * be interpreted as an unsigned integer.
*/ */
@Throws(IOException::class) @Throws(IOException::class)
fun readUInt(): Long { fun readUInt(): UnsignedInt {
return baseStream.readBytes4ToUInt() return baseStream.readBytes4ToUInt()
} }
@Throws(IOException::class)
fun readInt(): Int {
return baseStream.readBytes4ToInt()
}
@Throws(IOException::class) @Throws(IOException::class)
fun readUShort(): Int { fun readUShort(): Int {
val buf = ByteArray(2) val buf = ByteArray(2)

View File

@@ -19,6 +19,7 @@
*/ */
package com.kunzisoft.keepass.stream package com.kunzisoft.keepass.stream
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
@@ -30,8 +31,8 @@ import java.io.OutputStream
class LittleEndianDataOutputStream(private val baseStream: OutputStream) : OutputStream() { class LittleEndianDataOutputStream(private val baseStream: OutputStream) : OutputStream() {
@Throws(IOException::class) @Throws(IOException::class)
fun writeUInt(uint: Long) { // TODO UInt #443 fun writeUInt(uInt: UnsignedInt) {
baseStream.write(intTo4Bytes(uint.toInt())) baseStream.write(uIntTo4Bytes(uInt))
} }
@Throws(IOException::class) @Throws(IOException::class)
@@ -66,7 +67,7 @@ class LittleEndianDataOutputStream(private val baseStream: OutputStream) : Outpu
@Throws(IOException::class) @Throws(IOException::class)
fun writeInt(value: Int) { fun writeInt(value: Int) {
baseStream.write(intTo4Bytes(value)) baseStream.write(uIntTo4Bytes(UnsignedInt(value)))
} }
@Throws(IOException::class) @Throws(IOException::class)

View File

@@ -21,6 +21,7 @@ package com.kunzisoft.keepass.stream
import com.kunzisoft.keepass.database.element.DateInstant import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils.bytesToString import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils.bytesToString
import com.kunzisoft.keepass.utils.UnsignedInt
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.util.* import java.util.*
@@ -80,13 +81,8 @@ fun InputStream.readBytes(length: Int, bufferSize: Int, readBytes: (bytesRead: B
* be interpreted as an unsigned integer. * be interpreted as an unsigned integer.
*/ */
@Throws(IOException::class) @Throws(IOException::class)
fun InputStream.readBytes4ToUInt(): Long { fun InputStream.readBytes4ToUInt(): UnsignedInt {
return readBytes4ToInt().toLong() and INT_TO_LONG_MASK return bytes4ToUInt(readBytesLength(4))
}
@Throws(IOException::class)
fun InputStream.readBytes4ToInt(): Int {
return bytes4ToInt(readBytesLength(4))
} }
@Throws(IOException::class) @Throws(IOException::class)
@@ -141,18 +137,11 @@ fun bytes64ToLong(buf: ByteArray): Long {
+ (buf[7].toLong() and 0xFF shl 56)) + (buf[7].toLong() and 0xFF shl 56))
} }
private const val INT_TO_LONG_MASK: Long = 0xffffffffL
fun bytes4ToUInt(buf: ByteArray): Long {
return bytes4ToInt(buf).toLong() and INT_TO_LONG_MASK
}
/** /**
* Read a 32-bit value. * Read a 32-bit value.
*/ */
fun bytes4ToInt(buf: ByteArray): Int { fun bytes4ToUInt(buf: ByteArray): UnsignedInt {
return ((buf[0].toInt() and 0xFF) return UnsignedInt((buf[0].toInt() and 0xFF)
+ (buf[1].toInt() and 0xFF shl 8) + (buf[1].toInt() and 0xFF shl 8)
+ (buf[2].toInt() and 0xFF shl 16) + (buf[2].toInt() and 0xFF shl 16)
+ (buf[3].toInt() and 0xFF shl 24)) + (buf[3].toInt() and 0xFF shl 24))
@@ -182,11 +171,11 @@ fun bytes5ToDate(buf: ByteArray, calendar: Calendar = Calendar.getInstance()): D
System.arraycopy(buf, 0, cDate, 0, dateSize) System.arraycopy(buf, 0, cDate, 0, dateSize)
val readOffset = 0 val readOffset = 0
val dw1 = byteToUInt(cDate[readOffset]) val dw1 = UnsignedInt.fromByte(cDate[readOffset]).toInt()
val dw2 = byteToUInt(cDate[readOffset + 1]) val dw2 = UnsignedInt.fromByte(cDate[readOffset + 1]).toInt()
val dw3 = byteToUInt(cDate[readOffset + 2]) val dw3 = UnsignedInt.fromByte(cDate[readOffset + 2]).toInt()
val dw4 = byteToUInt(cDate[readOffset + 3]) val dw4 = UnsignedInt.fromByte(cDate[readOffset + 3]).toInt()
val dw5 = byteToUInt(cDate[readOffset + 4]) val dw5 = UnsignedInt.fromByte(cDate[readOffset + 4]).toInt()
// Unpack 5 byte structure to date and time // Unpack 5 byte structure to date and time
val year = dw1 shl 6 or (dw2 shr 2) val year = dw1 shl 6 or (dw2 shr 2)
@@ -205,19 +194,12 @@ fun bytes5ToDate(buf: ByteArray, calendar: Calendar = Calendar.getInstance()): D
} }
/** /**
* Convert an unsigned Integer to byte * Write a 32-bit Int value.
*/ */
fun uIntToByte(value: Int): Byte { fun uIntTo4Bytes(value: UnsignedInt): ByteArray {
return (value and 0xFF).toByte()
}
/**
* Write a 32-bit value.
*/
fun intTo4Bytes(value: Int): ByteArray {
val buf = ByteArray(4) val buf = ByteArray(4)
for (i in 0 until 4) { for (i in 0 until 4) {
buf[i] = (value.ushr(8 * i) and 0xFF).toByte() buf[i] = (value.toInt().ushr(8 * i) and 0xFF).toByte()
} }
return buf return buf
} }
@@ -264,8 +246,8 @@ fun dateTo5Bytes(date: Date, calendar: Calendar = Calendar.getInstance()): ByteA
val minute = calendar.get(Calendar.MINUTE) val minute = calendar.get(Calendar.MINUTE)
val second = calendar.get(Calendar.SECOND) val second = calendar.get(Calendar.SECOND)
buf[0] = uIntToByte(year shr 6 and 0x0000003F) buf[0] = UnsignedInt(year shr 6 and 0x0000003F).toByte()
buf[1] = uIntToByte(year and 0x0000003F shl 2 or (month shr 2 and 0x00000003)) buf[1] = UnsignedInt(year and 0x0000003F shl 2 or (month shr 2 and 0x00000003)).toByte()
buf[2] = (month and 0x00000003 shl 6 buf[2] = (month and 0x00000003 shl 6
or (day and 0x0000001F shl 1) or (hour shr 4 and 0x00000001)).toByte() 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[3] = (hour and 0x0000000F shl 4 or (minute shr 2 and 0x0000000F)).toByte()
@@ -273,9 +255,3 @@ fun dateTo5Bytes(date: Date, calendar: Calendar = Calendar.getInstance()): ByteA
return buf return buf
} }
/** Convert a byte to an unsigned byte */
fun byteToUInt(byte: Byte): Int {
return byte.toInt() and 0xFF
}

View File

@@ -20,7 +20,7 @@
package com.kunzisoft.keepass.utils package com.kunzisoft.keepass.utils
import com.kunzisoft.keepass.stream.intTo4Bytes import com.kunzisoft.keepass.stream.uIntTo4Bytes
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
import java.nio.charset.Charset import java.nio.charset.Charset
@@ -57,7 +57,7 @@ object StringDatabaseKDBUtils {
var str = string var str = string
if (str == null) { if (str == null) {
// Write out a null character // Write out a null character
os.write(intTo4Bytes(1)) os.write(uIntTo4Bytes(UnsignedInt(1)))
os.write(0x00) os.write(0x00)
return 0 return 0
} }
@@ -69,7 +69,7 @@ object StringDatabaseKDBUtils {
val initial = str.toByteArray(defaultCharset) val initial = str.toByteArray(defaultCharset)
val length = initial.size + 1 val length = initial.size + 1
os.write(intTo4Bytes(length)) os.write(uIntTo4Bytes(UnsignedInt(length)))
os.write(initial) os.write(initial)
os.write(0x00) os.write(0x00)

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2020 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX 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.
*
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.utils
class UnsignedInt(private var unsignedValue: Int) {
constructor(unsignedValue: UnsignedInt) : this(unsignedValue.toInt())
/**
* Get the int value
*/
fun toInt(): Int {
return unsignedValue
}
/**
* Convert an unsigned Integer to Long
*/
fun toLong(): Long {
return unsignedValue.toLong() and INT_TO_LONG_MASK
}
/**
* Convert an unsigned Integer to Byte
*/
fun toByte(): Byte {
return (unsignedValue and 0xFF).toByte()
}
override fun toString():String {
return toLong().toString()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as UnsignedInt
if (unsignedValue != other.unsignedValue) return false
return true
}
override fun hashCode(): Int {
return unsignedValue
}
companion object {
private const val INT_TO_LONG_MASK: Long = 0xffffffffL
private const val UINT_MAX_VALUE: Long = 4294967296L // 2^32
val MAX_VALUE = UnsignedInt(UINT_MAX_VALUE.toInt())
/**
* Convert a byte to an unsigned byte
*/
fun fromByte(value: Byte): UnsignedInt {
return UnsignedInt(value.toInt() and 0xFF)
}
@Throws(NumberFormatException::class)
fun fromLong(value: Long): UnsignedInt {
if (value > UINT_MAX_VALUE)
throw NumberFormatException("UInt value > $UINT_MAX_VALUE")
return UnsignedInt(value.toInt())
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2020 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX 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.
*
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.utils
class UnsignedLong(private var unsignedValue: Long) {
/**
* Convert an unsigned Integer to Long
*/
fun toLong(): Long {
return unsignedValue
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as UnsignedLong
if (unsignedValue != other.unsignedValue) return false
return true
}
override fun hashCode(): Int {
return unsignedValue.hashCode()
}
companion object {
const val ULONG_MAX_VALUE: Long = -1
}
}

View File

@@ -24,10 +24,10 @@ import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
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.bytes4ToUInt;
import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.bytes64ToLong; import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.bytes64ToLong;
@@ -35,6 +35,7 @@ public class VariantDictionary {
private static final int VdVersion = 0x0100; private static final int VdVersion = 0x0100;
private static final int VdmCritical = 0xFF00; private static final int VdmCritical = 0xFF00;
private static final int VdmInfo = 0x00FF; private static final int VdmInfo = 0x00FF;
private static Charset UTF8Charset = Charset.forName("UTF-8");
private Map<String, VdType> dict = new HashMap<>(); private Map<String, VdType> dict = new HashMap<>();
@@ -69,8 +70,8 @@ public class VariantDictionary {
dict.put(name, new VdType(type, value)); dict.put(name, new VdType(type, value));
} }
public void setUInt32(String name, long value) { putType(VdType.UInt32, name, value); } public void setUInt32(String name, UnsignedInt value) { putType(VdType.UInt32, name, value); }
public long getUInt32(String name) { return (long)dict.get(name).value; } public UnsignedInt getUInt32(String name) { return (UnsignedInt)dict.get(name).value; }
public void setUInt64(String name, long value) { putType(VdType.UInt64, name, value); } public void setUInt64(String name, long value) { putType(VdType.UInt64, name, value); }
public long getUInt64(String name) { return (long)dict.get(name).value; } public long getUInt64(String name) { return (long)dict.get(name).value; }
@@ -109,14 +110,14 @@ public class VariantDictionary {
break; break;
} }
int nameLen = lis.readInt(); int nameLen = lis.readUInt().toInt();
byte[] nameBuf = lis.readBytes(nameLen); byte[] nameBuf = lis.readBytes(nameLen);
if (nameLen != nameBuf.length) { if (nameLen != nameBuf.length) {
throw new IOException("Invalid format"); throw new IOException("Invalid format");
} }
String name = new String(nameBuf, "UTF-8"); String name = new String(nameBuf, UTF8Charset);
int valueLen = lis.readInt(); int valueLen = lis.readUInt().toInt();
byte[] valueBuf = lis.readBytes(valueLen); byte[] valueBuf = lis.readBytes(valueLen);
if (valueLen != valueBuf.length) { if (valueLen != valueBuf.length) {
throw new IOException("Invalid format"); throw new IOException("Invalid format");
@@ -140,7 +141,7 @@ public class VariantDictionary {
break; break;
case VdType.Int32: case VdType.Int32:
if (valueLen == 4) { if (valueLen == 4) {
d.setInt32(name, bytes4ToInt(valueBuf)); d.setInt32(name, bytes4ToUInt(valueBuf).toInt());
} }
break; break;
case VdType.Int64: case VdType.Int64:
@@ -149,7 +150,7 @@ public class VariantDictionary {
} }
break; break;
case VdType.String: case VdType.String:
d.setString(name, new String(valueBuf, "UTF-8")); d.setString(name, new String(valueBuf, UTF8Charset));
break; break;
case VdType.ByteArray: case VdType.ByteArray:
d.setByteArray(name, valueBuf); d.setByteArray(name, valueBuf);
@@ -172,12 +173,7 @@ public class VariantDictionary {
for (Map.Entry<String, VdType> entry: d.dict.entrySet()) { for (Map.Entry<String, VdType> entry: d.dict.entrySet()) {
String name = entry.getKey(); String name = entry.getKey();
byte[] nameBuf = null; byte[] nameBuf = nameBuf = name.getBytes(UTF8Charset);
try {
nameBuf = name.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IOException("Couldn't encode parameter name.");
}
VdType vd = entry.getValue(); VdType vd = entry.getValue();
@@ -189,7 +185,7 @@ public class VariantDictionary {
switch (vd.type) { switch (vd.type) {
case VdType.UInt32: case VdType.UInt32:
los.writeInt(4); los.writeInt(4);
los.writeUInt((long)vd.value); los.writeUInt((UnsignedInt) vd.value);
break; break;
case VdType.UInt64: case VdType.UInt64:
los.writeInt(8); los.writeInt(8);
@@ -210,7 +206,7 @@ public class VariantDictionary {
break; break;
case VdType.String: case VdType.String:
String value = (String)vd.value; String value = (String)vd.value;
buf = value.getBytes("UTF-8"); buf = value.getBytes(UTF8Charset);
los.writeInt(buf.length); los.writeInt(buf.length);
los.write(buf); los.write(buf);
break; break;
@@ -231,7 +227,6 @@ public class VariantDictionary {
for (Map.Entry<String, VdType> entry : d.dict.entrySet()) { for (Map.Entry<String, VdType> entry : d.dict.entrySet()) {
String key = entry.getKey(); String key = entry.getKey();
VdType value = entry.getValue(); VdType value = entry.getValue();
dict.put(key, value); dict.put(key, value);
} }
} }

View File

@@ -126,12 +126,11 @@ void throwExceptionF(JNIEnv *env, jclass exception, const char *format, ...) {
} }
#define ARGON2_HASHLEN 32 #define ARGON2_HASHLEN 32
#define NB_BLOCKSIZE 1024
JNIEXPORT jbyteArray JNIEXPORT jbyteArray
JNICALL Java_com_kunzisoft_keepass_crypto_keyDerivation_Argon2Native_nTransformMasterKey(JNIEnv *env, JNICALL Java_com_kunzisoft_keepass_crypto_keyDerivation_Argon2Native_nTransformMasterKey(JNIEnv *env,
jobject this, jbyteArray password, jbyteArray salt, jint parallelism, jlong memory, jobject this, jbyteArray password, jbyteArray salt, jint parallelism, jint memory,
jlong iterations, jbyteArray secretKey, jbyteArray associatedData, jlong version) { jint iterations, jbyteArray secretKey, jbyteArray associatedData, jint version) {
argon2_context context; argon2_context context;
uint8_t *out; uint8_t *out;
@@ -151,7 +150,7 @@ JNICALL Java_com_kunzisoft_keepass_crypto_keyDerivation_Argon2Native_nTransformM
uint8_t *adBuf; uint8_t *adBuf;
uint32_t adLen = getJNIArray(env, associatedData, &adBuf); uint32_t adLen = getJNIArray(env, associatedData, &adBuf);
context.out = (uint8_t *) out; context.out = out;
context.outlen = ARGON2_HASHLEN; context.outlen = ARGON2_HASHLEN;
context.pwd = passwordBuf; context.pwd = passwordBuf;
context.pwdlen = passwordLen; context.pwdlen = passwordLen;
@@ -162,7 +161,7 @@ JNICALL Java_com_kunzisoft_keepass_crypto_keyDerivation_Argon2Native_nTransformM
context.ad = adBuf; context.ad = adBuf;
context.adlen = adLen; context.adlen = adLen;
context.t_cost = (uint32_t) iterations; context.t_cost = (uint32_t) iterations;
context.m_cost = ((uint32_t) memory) / NB_BLOCKSIZE; context.m_cost = (uint32_t) memory;
context.lanes = (uint32_t) parallelism; context.lanes = (uint32_t) parallelism;
context.threads = (uint32_t) parallelism; context.threads = (uint32_t) parallelism;
context.allocate_cbk = NULL; context.allocate_cbk = NULL;

View File

@@ -438,7 +438,7 @@ uint32_t generate_key_material(void *arg) {
return flip; return flip;
} }
JNIEXPORT jbyteArray JNICALL Java_com_kunzisoft_keepass_crypto_finalkey_NativeFinalKey_nTransformMasterKey(JNIEnv *env, jobject this, jbyteArray seed, jbyteArray key, jlong rounds) { JNIEXPORT jbyteArray JNICALL Java_com_kunzisoft_keepass_crypto_finalkey_NativeAESKeyTransformer_nTransformMasterKey(JNIEnv *env, jobject this, jbyteArray seed, jbyteArray key, jlong rounds) {
master_key mk; master_key mk;
uint32_t flip; uint32_t flip;
pthread_t t1, t2; pthread_t t1, t2;