diff --git a/app/build.gradle b/app/build.gradle index dacaa978b..79c49d7c8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -118,4 +118,7 @@ dependencies { // Icon pack implementation project(path: ':icon-pack-classic') implementation project(path: ':icon-pack-material') + + // Tests + androidTestImplementation 'junit:junit:4.12' } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/StringDatabaseKDBUtilsTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/StringDatabaseKDBUtilsTest.kt index 94dfa6715..dab86f23c 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/StringDatabaseKDBUtilsTest.kt +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/StringDatabaseKDBUtilsTest.kt @@ -20,8 +20,9 @@ package com.kunzisoft.keepass.tests import com.kunzisoft.keepass.database.element.DateInstant -import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.ULONG_MAX_VALUE import com.kunzisoft.keepass.stream.* +import com.kunzisoft.keepass.utils.UnsignedInt +import com.kunzisoft.keepass.utils.UnsignedLong import junit.framework.TestCase import org.junit.Assert.assertArrayEquals import java.io.ByteArrayOutputStream @@ -77,8 +78,8 @@ class StringDatabaseKDBUtilsTest : TestCase() { setArray(orig, value, 4) - val one = bytes4ToInt(orig) - val dest = intTo4Bytes(one) + val one = bytes4ToUInt(orig) + val dest = uIntTo4Bytes(one) assertArrayEquals(orig, dest) @@ -133,7 +134,7 @@ class StringDatabaseKDBUtilsTest : TestCase() { } private fun testReadWriteByte(value: Byte) { - val dest: Byte = uIntToByte(byteToUInt(value)) + val dest: Byte = UnsignedInt(UnsignedInt.fromByte(value)).toByte() assert(value == dest) } @@ -185,7 +186,7 @@ class StringDatabaseKDBUtilsTest : TestCase() { val bos = ByteArrayOutputStream() val leos = LittleEndianDataOutputStream(bos) - leos.writeLong(ULONG_MAX_VALUE) + leos.writeLong(UnsignedLong.ULONG_MAX_VALUE) leos.close() val uLongMax = bos.toByteArray() diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.kt index 1424eceed..038790b54 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.kt +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.kt @@ -26,8 +26,8 @@ import java.util.Random import junit.framework.TestCase -import com.kunzisoft.keepass.crypto.finalkey.AndroidFinalKey -import com.kunzisoft.keepass.crypto.finalkey.NativeFinalKey +import com.kunzisoft.keepass.crypto.finalkey.AndroidAESKeyTransformer +import com.kunzisoft.keepass.crypto.finalkey.NativeAESKeyTransformer class FinalKeyTest : TestCase() { private var mRand: Random? = null @@ -56,10 +56,10 @@ class FinalKeyTest : TestCase() { mRand!!.nextBytes(seed) mRand!!.nextBytes(key) - val aKey = AndroidFinalKey() + val aKey = AndroidAESKeyTransformer() androidKey = aKey.transformMasterKey(seed, key, rounds.toLong()) - val nKey = NativeFinalKey() + val nKey = NativeAESKeyTransformer() nativeKey = nKey.transformMasterKey(seed, key, rounds.toLong()) assertArrayEquals("Does not match", androidKey, nativeKey) diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/CrsAlgorithm.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/CrsAlgorithm.kt index b6c0085e6..d72020bed 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/CrsAlgorithm.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/CrsAlgorithm.kt @@ -19,16 +19,18 @@ */ package com.kunzisoft.keepass.crypto -enum class CrsAlgorithm constructor(val id: Int) { +import com.kunzisoft.keepass.utils.UnsignedInt - Null(0), - ArcFourVariant(1), - Salsa20(2), - ChaCha20(3); +enum class CrsAlgorithm constructor(val id: UnsignedInt) { + + Null(UnsignedInt(0)), + ArcFourVariant(UnsignedInt(1)), + Salsa20(UnsignedInt(2)), + ChaCha20(UnsignedInt(3)); companion object { - fun fromId(num: Int): CrsAlgorithm? { + fun fromId(num: UnsignedInt): CrsAlgorithm? { for (e in values()) { if (e.id == num) { return e diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.kt index d43cf9152..4682692d6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.kt @@ -30,7 +30,7 @@ object NativeLib { fun init(): Boolean { if (!isLoaded) { try { - System.loadLibrary("final-key") + System.loadLibrary("final-key") // TODO Rename System.loadLibrary("argon2") } catch (e: UnsatisfiedLinkError) { return false diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKeyFactory.java b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AESFactory.java similarity index 75% rename from app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKeyFactory.java rename to app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AESFactory.java index dd2b6a973..8809a606c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKeyFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AESFactory.java @@ -21,14 +21,17 @@ package com.kunzisoft.keepass.crypto.finalkey; import com.kunzisoft.keepass.crypto.CipherFactory; -public class FinalKeyFactory { - public static FinalKey createFinalKey() { +public class AESFactory { + + // TODO Encaspulate + public static KeyTransformer createFinalKey() { // Prefer the native final key implementation - if ( !CipherFactory.INSTANCE.deviceBlacklisted() && NativeFinalKey.available() ) { - return new NativeFinalKey(); + if ( !CipherFactory.INSTANCE.deviceBlacklisted() + && NativeAESKeyTransformer.available() ) { + return new NativeAESKeyTransformer(); } else { // Fall back on the android crypto implementation - return new AndroidFinalKey(); + return new AndroidAESKeyTransformer(); } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AndroidFinalKey.java b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AndroidAESKeyTransformer.java similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AndroidFinalKey.java rename to app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AndroidAESKeyTransformer.java index c9b9992ed..80477227c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AndroidFinalKey.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AndroidAESKeyTransformer.java @@ -29,7 +29,7 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; import javax.crypto.spec.SecretKeySpec; -public class AndroidFinalKey extends FinalKey { +public class AndroidAESKeyTransformer extends KeyTransformer { @Override public byte[] transformMasterKey(byte[] pKeySeed, byte[] pKey, long rounds) throws IOException { diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKey.java b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/KeyTransformer.java similarity index 96% rename from app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKey.java rename to app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/KeyTransformer.java index 09120d123..f3234d44a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKey.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/KeyTransformer.java @@ -21,6 +21,6 @@ package com.kunzisoft.keepass.crypto.finalkey; import java.io.IOException; -public abstract class FinalKey { +public abstract class KeyTransformer { public abstract byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException; } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeAESKeyTransformer.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java rename to app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeAESKeyTransformer.java index 055a7f306..1bc804cf7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeAESKeyTransformer.java @@ -24,7 +24,7 @@ import com.kunzisoft.keepass.crypto.NativeLib; import java.io.IOException; -public class NativeFinalKey extends FinalKey { +public class NativeAESKeyTransformer extends KeyTransformer { public static boolean available() { return NativeLib.INSTANCE.init(); diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/AesKdf.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/AesKdf.kt index e117db533..cbf052437 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/AesKdf.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/AesKdf.kt @@ -22,8 +22,9 @@ package com.kunzisoft.keepass.crypto.keyDerivation import android.content.res.Resources import com.kunzisoft.keepass.R import com.kunzisoft.keepass.crypto.CryptoUtil -import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory +import com.kunzisoft.keepass.crypto.finalkey.AESFactory import com.kunzisoft.keepass.stream.bytes16ToUuid +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.security.SecureRandom import java.util.* @@ -34,12 +35,11 @@ class AesKdf internal constructor() : KdfEngine() { get() { return KdfParameters(uuid!!).apply { setParamUUID() - setUInt32(PARAM_ROUNDS, DEFAULT_ROUNDS.toLong()) + setUInt32(PARAM_ROUNDS, UnsignedInt.fromLong(defaultKeyRounds)) } } - override val defaultKeyRounds: Long - get() = DEFAULT_ROUNDS.toLong() + override val defaultKeyRounds: Long = 6000L init { uuid = CIPHER_UUID @@ -63,8 +63,7 @@ class AesKdf internal constructor() : KdfEngine() { seed = CryptoUtil.hashSha256(seed) } - val key = FinalKeyFactory.createFinalKey() - return key.transformMasterKey(seed, currentMasterKey, rounds) + return AESFactory.createFinalKey().transformMasterKey(seed, currentMasterKey, rounds) } override fun randomize(p: KdfParameters) { @@ -86,8 +85,6 @@ class AesKdf internal constructor() : KdfEngine() { companion object { - private const val DEFAULT_ROUNDS = 6000 - val CIPHER_UUID: UUID = bytes16ToUuid( byteArrayOf(0xC9.toByte(), 0xD9.toByte(), diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Kdf.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Kdf.kt index a2433b7f4..fa33c5a3d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Kdf.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Kdf.kt @@ -22,6 +22,7 @@ package com.kunzisoft.keepass.crypto.keyDerivation import android.content.res.Resources import com.kunzisoft.keepass.R import com.kunzisoft.keepass.stream.bytes16ToUuid +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.security.SecureRandom import java.util.* @@ -56,15 +57,21 @@ class Argon2Kdf internal constructor() : KdfEngine() { override fun transform(masterKey: ByteArray, p: KdfParameters): ByteArray { val salt = p.getByteArray(PARAM_SALT) - val parallelism = p.getUInt32(PARAM_PARALLELISM).toInt() - val memory = p.getUInt64(PARAM_MEMORY) - val iterations = p.getUInt64(PARAM_ITERATIONS) - val version = p.getUInt32(PARAM_VERSION) + val parallelism = UnsignedInt(p.getUInt32(PARAM_PARALLELISM)) + val memory = UnsignedInt.fromLong(p.getUInt64(PARAM_MEMORY) / MEMORY_BLOCK_SIZE) + val iterations = UnsignedInt.fromLong(p.getUInt64(PARAM_ITERATIONS)) + val version = UnsignedInt(p.getUInt32(PARAM_VERSION)) val secretKey = p.getByteArray(PARAM_SECRET_KEY) val assocData = p.getByteArray(PARAM_ASSOC_DATA) - return Argon2Native.transformKey(masterKey, salt, parallelism, memory, iterations, - secretKey, assocData, version) + return Argon2Native.transformKey(masterKey, + salt, + parallelism, + memory, + iterations, + secretKey, + assocData, + version) } override fun randomize(p: KdfParameters) { @@ -107,21 +114,21 @@ class Argon2Kdf internal constructor() : KdfEngine() { override val maxMemoryUsage: Long get() = MAX_MEMORY - override fun getParallelism(p: KdfParameters): Int { - return p.getUInt32(PARAM_PARALLELISM).toInt() // TODO Verify #443 + override fun getParallelism(p: KdfParameters): Long { + return UnsignedInt(p.getUInt32(PARAM_PARALLELISM)).toLong() } - override fun setParallelism(p: KdfParameters, parallelism: Int) { - p.setUInt32(PARAM_PARALLELISM, parallelism.toLong()) + override fun setParallelism(p: KdfParameters, parallelism: Long) { + p.setUInt32(PARAM_PARALLELISM, UnsignedInt.fromLong(parallelism)) } - override val defaultParallelism: Int - get() = DEFAULT_PARALLELISM.toInt() + override val defaultParallelism: Long + get() = DEFAULT_PARALLELISM.toLong() - override val minParallelism: Int + override val minParallelism: Long get() = MIN_PARALLELISM - override val maxParallelism: Int + override val maxParallelism: Long get() = MAX_PARALLELISM companion object { @@ -152,23 +159,24 @@ class Argon2Kdf internal constructor() : KdfEngine() { private const val PARAM_SECRET_KEY = "K" // byte[] private const val PARAM_ASSOC_DATA = "A" // byte[] - private const val MIN_VERSION: Long = 0x10 - private const val MAX_VERSION: Long = 0x13 + private val MIN_VERSION = UnsignedInt(0x10) + private val MAX_VERSION = UnsignedInt(0x13) 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 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 MAX_PARALLELISM = (1 shl 24) - 1 + private const val MIN_PARALLELISM: Long = 1L + 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_PARALLELISM: Long = 2 + private val DEFAULT_PARALLELISM = UnsignedInt(2) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Native.java b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Native.java index 5ef3a0b6c..9a2b51195 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Native.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Native.java @@ -20,20 +20,29 @@ package com.kunzisoft.keepass.crypto.keyDerivation; import com.kunzisoft.keepass.crypto.NativeLib; +import com.kunzisoft.keepass.utils.UnsignedInt; import java.io.IOException; public class Argon2Native { - public static byte[] transformKey(byte[] password, byte[] salt, int parallelism, - long memory, long iterations, byte[] secretKey, - byte[] associatedData, long version) throws IOException { + public static byte[] transformKey(byte[] password, byte[] salt, UnsignedInt parallelism, + UnsignedInt memory, UnsignedInt iterations, byte[] secretKey, + byte[] associatedData, UnsignedInt version) throws IOException { 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, - long memory, long iterations, byte[] secretKey, - byte[] associatedData, long version) throws IOException; + int memory, int iterations, byte[] secretKey, + byte[] associatedData, int version) throws IOException; } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/KdfEngine.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/KdfEngine.kt index 499ef643c..43d9ffff1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/KdfEngine.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/KdfEngine.kt @@ -20,6 +20,7 @@ package com.kunzisoft.keepass.crypto.keyDerivation import com.kunzisoft.keepass.utils.ObjectNameResource +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.io.Serializable @@ -51,14 +52,14 @@ abstract class KdfEngine : ObjectNameResource, Serializable { get() = 1 open val maxKeyRounds: Long - get() = Int.MAX_VALUE.toLong() + get() = UnsignedInt.MAX_VALUE.toLong() /* * MEMORY */ open fun getMemoryUsage(p: KdfParameters): Long { - return UNKNOWN_VALUE.toLong() + return UNKNOWN_VALUE } open fun setMemoryUsage(p: KdfParameters, memory: Long) { @@ -66,36 +67,36 @@ abstract class KdfEngine : ObjectNameResource, Serializable { } open val defaultMemoryUsage: Long - get() = UNKNOWN_VALUE.toLong() + get() = UNKNOWN_VALUE open val minMemoryUsage: Long get() = 1 open val maxMemoryUsage: Long - get() = Int.MAX_VALUE.toLong() + get() = UnsignedInt.MAX_VALUE.toLong() /* * PARALLELISM */ - open fun getParallelism(p: KdfParameters): Int { + open fun getParallelism(p: KdfParameters): Long { return UNKNOWN_VALUE } - open fun setParallelism(p: KdfParameters, parallelism: Int) { + open fun setParallelism(p: KdfParameters, parallelism: Long) { // Do nothing by default } - open val defaultParallelism: Int + open val defaultParallelism: Long get() = UNKNOWN_VALUE - open val minParallelism: Int - get() = 1 + open val minParallelism: Long + get() = 1L - open val maxParallelism: Int - get() = Int.MAX_VALUE + open val maxParallelism: Long + get() = UnsignedInt.MAX_VALUE.toLong() companion object { - const val UNKNOWN_VALUE = -1 + const val UNKNOWN_VALUE: Long = -1L } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogThread.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogThread.kt index 0a1f9f571..e84365960 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogThread.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogThread.kt @@ -552,12 +552,12 @@ class ProgressDialogThread(private val activity: FragmentActivity) { , ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK) } - fun startDatabaseSaveParallelism(oldParallelism: Int, - newParallelism: Int, + fun startDatabaseSaveParallelism(oldParallelism: Long, + newParallelism: Long, save: Boolean) { start(Bundle().apply { - putInt(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldParallelism) - putInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newParallelism) + putLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldParallelism) + putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newParallelism) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) } , ACTION_DATABASE_UPDATE_PARALLELISM_TASK) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index a51333703..bdde5e416 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -47,7 +47,7 @@ import com.kunzisoft.keepass.database.search.SearchHelper import com.kunzisoft.keepass.database.search.SearchParameters import com.kunzisoft.keepass.icons.IconDrawableFactory 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.utils.SingletonHolder import com.kunzisoft.keepass.utils.UriUtil @@ -206,7 +206,6 @@ class Database { var numberKeyEncryptionRounds: Long get() = mDatabaseKDB?.numberKeyEncryptionRounds ?: mDatabaseKDBX?.numberKeyEncryptionRounds ?: 0 - @Throws(NumberFormatException::class) set(numberRounds) { mDatabaseKDB?.numberKeyEncryptionRounds = numberRounds mDatabaseKDBX?.numberKeyEncryptionRounds = numberRounds @@ -214,13 +213,13 @@ class Database { var memoryUsage: Long get() { - return mDatabaseKDBX?.memoryUsage ?: return KdfEngine.UNKNOWN_VALUE.toLong() + return mDatabaseKDBX?.memoryUsage ?: return KdfEngine.UNKNOWN_VALUE } set(memory) { mDatabaseKDBX?.memoryUsage = memory } - var parallelism: Int + var parallelism: Long get() = mDatabaseKDBX?.parallelism ?: KdfEngine.UNKNOWN_VALUE set(parallelism) { mDatabaseKDBX?.parallelism = parallelism @@ -348,8 +347,8 @@ class Database { databaseInputStream.mark(10) // Get the file directory to save the attachments - val sig1 = databaseInputStream.readBytes4ToInt() - val sig2 = databaseInputStream.readBytes4ToInt() + val sig1 = databaseInputStream.readBytes4ToUInt() + val sig2 = databaseInputStream.readBytes4ToUInt() // Return to the start databaseInputStream.reset() diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt index 21f0c66c2..6f44336a6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt @@ -19,7 +19,7 @@ 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.KdfFactory import com.kunzisoft.keepass.database.element.entry.EntryKDB @@ -38,8 +38,6 @@ import kotlin.collections.ArrayList class DatabaseKDB : DatabaseVersioned() { - private var numKeyEncRounds: Int = 0 - var backupGroupId: Int = BACKUP_FOLDER_UNDEFINED_ID private var kdfListV3: MutableList = ArrayList() @@ -87,19 +85,10 @@ class DatabaseKDB : DatabaseVersioned() { override val passwordEncoding: String get() = "ISO-8859-1" - override var numberKeyEncryptionRounds: Long - get() = numKeyEncRounds.toLong() - @Throws(NumberFormatException::class) - set(rounds) { - if (rounds > Integer.MAX_VALUE || rounds < Integer.MIN_VALUE) { - throw NumberFormatException() - } - numKeyEncRounds = rounds.toInt() - } + override var numberKeyEncryptionRounds = 300L init { algorithm = EncryptionAlgorithm.AESRijndael - numKeyEncRounds = DEFAULT_ENCRYPTION_ROUNDS } /** @@ -158,9 +147,9 @@ class DatabaseKDB : DatabaseVersioned() { val nos = NullOutputStream() 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(transformedMasterKey) + dos.write(AESFactory.createFinalKey().transformMasterKey(masterSeed2, masterKey, numRounds)) finalKey = messageDigest.digest() } @@ -266,19 +255,6 @@ class DatabaseKDB : DatabaseVersioned() { const val BACKUP_FOLDER_TITLE = "Backup" private const val BACKUP_FOLDER_UNDEFINED_ID = -1 - private const val DEFAULT_ENCRYPTION_ROUNDS = 300 - 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) - } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt index 8c242efad..5e38ad747 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt @@ -42,6 +42,7 @@ import com.kunzisoft.keepass.database.element.security.MemoryProtectionConfig 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_4 +import com.kunzisoft.keepass.utils.UnsignedInt import com.kunzisoft.keepass.utils.VariantDictionary import org.w3c.dom.Node import org.w3c.dom.Text @@ -67,7 +68,7 @@ class DatabaseKDBX : DatabaseVersioned { private var numKeyEncRounds: Long = 0 var publicCustomData = VariantDictionary() - var kdbxVersion: Long = 0 + var kdbxVersion = UnsignedInt(0) var name = "" var nameChanged = DateInstant() // TODO change setting date @@ -83,7 +84,7 @@ class DatabaseKDBX : DatabaseVersioned { var keyChangeForceDays: Long = 1 var isKeyChangeForceOnce = false - var maintenanceHistoryDays: Long = 365 + var maintenanceHistoryDays = UnsignedInt(365) var color = "" /** * Determine if RecycleBin is enable or not @@ -219,7 +220,6 @@ class DatabaseKDBX : DatabaseVersioned { numKeyEncRounds = kdfEngine.getKeyRounds(kdfParameters!!) return numKeyEncRounds } - @Throws(NumberFormatException::class) set(rounds) { val kdfEngine = kdfEngine if (kdfEngine != null && kdfParameters != null) @@ -232,7 +232,7 @@ class DatabaseKDBX : DatabaseVersioned { val kdfEngine = kdfEngine return if (kdfEngine != null && kdfParameters != null) { kdfEngine.getMemoryUsage(kdfParameters!!) - } else KdfEngine.UNKNOWN_VALUE.toLong() + } else KdfEngine.UNKNOWN_VALUE } set(memory) { val kdfEngine = kdfEngine @@ -240,7 +240,7 @@ class DatabaseKDBX : DatabaseVersioned { kdfEngine.setMemoryUsage(kdfParameters!!, memory) } - var parallelism: Int + var parallelism: Long get() { val kdfEngine = kdfEngine return if (kdfEngine != null && kdfParameters != null) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/AutoType.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/AutoType.kt index 85bccf259..0d7496901 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/AutoType.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/AutoType.kt @@ -23,6 +23,7 @@ import android.os.Parcel import android.os.Parcelable import com.kunzisoft.keepass.utils.ParcelableUtil +import com.kunzisoft.keepass.utils.UnsignedInt import java.util.HashMap @@ -46,7 +47,7 @@ class AutoType : Parcelable { constructor(parcel: Parcel) { this.enabled = parcel.readByte().toInt() != 0 - this.obfuscationOptions = parcel.readLong() + this.obfuscationOptions = UnsignedInt(parcel.readInt()) this.defaultSequence = parcel.readString() ?: defaultSequence this.windowSeqPairs = ParcelableUtil.readStringParcelableMap(parcel) } @@ -57,7 +58,7 @@ class AutoType : Parcelable { override fun writeToParcel(dest: Parcel, flags: Int) { dest.writeByte((if (enabled) 1 else 0).toByte()) - dest.writeLong(obfuscationOptions) + dest.writeInt(obfuscationOptions.toInt()) dest.writeString(defaultSequence) ParcelableUtil.writeStringParcelableMap(dest, windowSeqPairs) } @@ -71,7 +72,7 @@ class AutoType : Parcelable { } companion object { - private const val OBF_OPT_NONE: Long = 0 + private val OBF_OPT_NONE = UnsignedInt(0) @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt index 2037ede0e..b00522331 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt @@ -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.ProtectedString import com.kunzisoft.keepass.utils.ParcelableUtil +import com.kunzisoft.keepass.utils.UnsignedLong import java.util.* class EntryKDBX : EntryVersioned, NodeKDBXInterface { @@ -104,7 +105,7 @@ class EntryKDBX : EntryVersioned, NodeKDBXInte constructor(parcel: Parcel) : super(parcel) { iconCustom = parcel.readParcelable(IconImageCustom::class.java.classLoader) ?: iconCustom - usageCount = parcel.readLong() + usageCount = UnsignedLong(parcel.readLong()) locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged customData = ParcelableUtil.readStringParcelableMap(parcel) fields = ParcelableUtil.readStringParcelableMap(parcel, ProtectedString::class.java) @@ -122,7 +123,7 @@ class EntryKDBX : EntryVersioned, NodeKDBXInte override fun writeToParcel(dest: Parcel, flags: Int) { super.writeToParcel(dest, flags) dest.writeParcelable(iconCustom, flags) - dest.writeLong(usageCount) + dest.writeLong(usageCount.toLong()) dest.writeParcelable(locationChanged, flags) ParcelableUtil.writeStringParcelableMap(dest, customData) ParcelableUtil.writeStringParcelableMap(dest, flags, fields) @@ -243,7 +244,7 @@ class EntryKDBX : EntryVersioned, NodeKDBXInte fields[STR_NOTES] = ProtectedString(protect, value) } - override var usageCount: Long = 0 + override var usageCount = UnsignedLong(0) override var locationChanged = DateInstant() @@ -332,7 +333,8 @@ class EntryKDBX : EntryVersioned, NodeKDBXInte override fun touch(modified: Boolean, touchParents: Boolean) { super.touch(modified, touchParents) - ++usageCount + // TODO unsigned long + usageCount = UnsignedLong(usageCount.toLong() + 1) } companion object { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/group/GroupKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/group/GroupKDB.kt index 2c7e186e3..2a44ab229 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/group/GroupKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/group/GroupKDB.kt @@ -32,14 +32,14 @@ import java.util.* class GroupKDB : GroupVersioned, NodeKDBInterface { var level = 0 // short - /** Used by KeePass internally, don't use */ - var flags: Int = 0 + // Used by KeePass internally, don't use + var groupFlags = 0 constructor() : super() constructor(parcel: Parcel) : super(parcel) { level = parcel.readInt() - flags = parcel.readInt() + groupFlags = parcel.readInt() } override fun readParentParcelable(parcel: Parcel): GroupKDB? { @@ -53,13 +53,13 @@ class GroupKDB : GroupVersioned, NodeKDBInterface override fun writeToParcel(dest: Parcel, flags: Int) { super.writeToParcel(dest, flags) dest.writeInt(level) - dest.writeInt(flags) + dest.writeInt(groupFlags) } fun updateWith(source: GroupKDB) { super.updateWith(source) level = source.level - flags = source.flags + groupFlags = source.groupFlags } override val type: Type diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/group/GroupKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/group/GroupKDBX.kt index 1f06a9ff7..da0eaecc0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/group/GroupKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/group/GroupKDBX.kt @@ -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.NodeKDBXInterface import com.kunzisoft.keepass.database.element.node.Type +import com.kunzisoft.keepass.utils.UnsignedLong import java.util.HashMap import java.util.UUID @@ -77,7 +78,7 @@ class GroupKDBX : GroupVersioned, NodeKDBXInte constructor(parcel: Parcel) : super(parcel) { iconCustom = parcel.readParcelable(IconImageCustom::class.java.classLoader) ?: iconCustom - usageCount = parcel.readLong() + usageCount = UnsignedLong(parcel.readLong()) locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged // TODO customData = ParcelableUtil.readStringParcelableMap(parcel); notes = parcel.readString() ?: notes @@ -101,7 +102,7 @@ class GroupKDBX : GroupVersioned, NodeKDBXInte override fun writeToParcel(dest: Parcel, flags: Int) { super.writeToParcel(dest, flags) dest.writeParcelable(iconCustom, flags) - dest.writeLong(usageCount) + dest.writeLong(usageCount.toLong()) dest.writeParcelable(locationChanged, flags) // TODO ParcelableUtil.writeStringParcelableMap(dest, customData); dest.writeString(notes) @@ -130,7 +131,7 @@ class GroupKDBX : GroupVersioned, NodeKDBXInte lastTopVisibleEntry = source.lastTopVisibleEntry } - override var usageCount: Long = 0 + override var usageCount = UnsignedLong(0) override var locationChanged = DateInstant() diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/node/NodeKDBXInterface.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/node/NodeKDBXInterface.kt index c8690b916..222f25a53 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/node/NodeKDBXInterface.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/node/NodeKDBXInterface.kt @@ -20,10 +20,11 @@ package com.kunzisoft.keepass.database.element.node import com.kunzisoft.keepass.database.element.DateInstant +import com.kunzisoft.keepass.utils.UnsignedLong interface NodeKDBXInterface : NodeTimeInterface { - var usageCount: Long + var usageCount: UnsignedLong var locationChanged: DateInstant diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeader.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeader.kt index edc660ca3..7f1f30838 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeader.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeader.kt @@ -19,6 +19,8 @@ */ package com.kunzisoft.keepass.database.file +import com.kunzisoft.keepass.utils.UnsignedInt + abstract class DatabaseHeader { /** @@ -32,7 +34,7 @@ abstract class DatabaseHeader { var encryptionIV = ByteArray(16) companion object { - const val PWM_DBSIG_1 = -0x655d26fd + val PWM_DBSIG_1 = UnsignedInt(-0x655d26fd) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDB.kt index fb481c824..7dc835f1f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDB.kt @@ -21,9 +21,9 @@ package com.kunzisoft.keepass.database.file -import com.kunzisoft.keepass.stream.bytes4ToInt 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.InputStream @@ -34,15 +34,15 @@ class DatabaseHeaderKDB : DatabaseHeader() { */ var transformSeed = ByteArray(32) - var signature1: Int = 0 // = PWM_DBSIG_1 - var signature2: Int = 0 // = DBSIG_2 - var flags: Int = 0 - var version: Int = 0 + var signature1 = UnsignedInt(0) // = PWM_DBSIG_1 + var signature2 = UnsignedInt(0) // = DBSIG_2 + var flags= UnsignedInt(0) + var version= UnsignedInt(0) /** Number of groups in the database */ - var numGroups: Int = 0 + var numGroups = UnsignedInt(0) /** Number of entries in the database */ - var numEntries: Int = 0 + var numEntries = UnsignedInt(0) /** * SHA-256 hash of the database, used for integrity check @@ -50,28 +50,24 @@ class DatabaseHeaderKDB : DatabaseHeader() { var contentsHash = ByteArray(32) // As UInt - var numKeyEncRounds: Int = 0 + var numKeyEncRounds = UnsignedInt(0) /** * Parse given buf, as read from file. */ @Throws(IOException::class) fun loadFromFile(inputStream: InputStream) { - signature1 = inputStream.readBytes4ToInt() // 4 bytes - signature2 = inputStream.readBytes4ToInt() // 4 bytes - flags = inputStream.readBytes4ToInt() // 4 bytes - version = inputStream.readBytes4ToInt() // 4 bytes + signature1 = inputStream.readBytes4ToUInt() // 4 bytes + signature2 = inputStream.readBytes4ToUInt() // 4 bytes + flags = inputStream.readBytes4ToUInt() // 4 bytes + version = inputStream.readBytes4ToUInt() // 4 bytes masterSeed = inputStream.readBytesLength(16) // 16 bytes encryptionIV = inputStream.readBytesLength(16) // 16 bytes - numGroups = inputStream.readBytes4ToInt() // 4 bytes - numEntries = inputStream.readBytes4ToInt() // 4 bytes + numGroups = inputStream.readBytes4ToUInt() // 4 bytes + numEntries = inputStream.readBytes4ToUInt() // 4 bytes contentsHash = inputStream.readBytesLength(32) // 32 bytes transformSeed = inputStream.readBytesLength(32) // 32 bytes - numKeyEncRounds = inputStream.readBytes4ToInt() - if (numKeyEncRounds < 0) { - // TODO: Really treat this like an unsigned integer #443 - throw IOException("Does not support more than " + Integer.MAX_VALUE + " rounds.") - } + numKeyEncRounds = inputStream.readBytes4ToUInt() } init { @@ -88,24 +84,24 @@ class DatabaseHeaderKDB : DatabaseHeader() { companion object { // DB sig from KeePass 1.03 - const val DBSIG_2 = -0x4ab4049b + val DBSIG_2 = UnsignedInt(-0x4ab4049b) // DB sig from KeePass 1.03 - const val DBVER_DW = 0x00030003 + val DBVER_DW = UnsignedInt(0x00030003) - const val FLAG_SHA2 = 1 - const val FLAG_RIJNDAEL = 2 - const val FLAG_ARCFOUR = 4 - const val FLAG_TWOFISH = 8 + val FLAG_SHA2 = UnsignedInt(1) + val FLAG_RIJNDAEL = UnsignedInt(2) + val FLAG_ARCFOUR = UnsignedInt(4) + val FLAG_TWOFISH = UnsignedInt(8) /** Size of byte buffer needed to hold this struct. */ const val BUF_SIZE = 124 - fun matchesHeader(sig1: Int, sig2: Int): Boolean { - return sig1 == PWM_DBSIG_1 && sig2 == DBSIG_2 + fun matchesHeader(sig1: UnsignedInt, sig2: UnsignedInt): Boolean { + return sig1.toInt() == PWM_DBSIG_1.toInt() && sig2.toInt() == DBSIG_2.toInt() } - fun compatibleHeaders(one: Int, two: Int): Boolean { - return one and -0x100 == two and -0x100 + fun compatibleHeaders(one: UnsignedInt, two: UnsignedInt): Boolean { + return one.toInt() and -0x100 == two.toInt() and -0x100 } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt index 1c7be7b01..bdbacb964 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 Jeremy Jamet / Kunzisoft. + * Copyright 2020 Jeremy Jamet / Kunzisoft. * * 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.exception.VersionDatabaseException import com.kunzisoft.keepass.stream.* +import com.kunzisoft.keepass.utils.UnsignedInt +import com.kunzisoft.keepass.utils.UnsignedLong import java.io.ByteArrayOutputStream import java.io.IOException import java.io.InputStream @@ -45,7 +47,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( var innerRandomStreamKey: ByteArray = ByteArray(32) var streamStartBytes: ByteArray = ByteArray(32) var innerRandomStream: CrsAlgorithm? = null - var version: Long = 0 + var version: UnsignedInt = UnsignedInt(0) // version < FILE_VERSION_32_4) 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 // Return v4 if AES is not use @@ -147,8 +149,8 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( val digestInputStream = DigestInputStream(copyInputStream, messageDigest) val littleEndianDataInputStream = LittleEndianDataInputStream(digestInputStream) - val sig1 = littleEndianDataInputStream.readInt() - val sig2 = littleEndianDataInputStream.readInt() + val sig1 = littleEndianDataInputStream.readUInt() + val sig2 = littleEndianDataInputStream.readUInt() if (!matchesHeader(sig1, sig2)) { throw VersionDatabaseException() @@ -172,10 +174,10 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( private fun readHeaderField(dis: LittleEndianDataInputStream): Boolean { 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() } else { - dis.readInt() + dis.readUInt().toInt() } var fieldData: ByteArray? = null @@ -198,20 +200,20 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( PwDbHeaderV4Fields.MasterSeed -> masterSeed = fieldData - PwDbHeaderV4Fields.TransformSeed -> if (version < FILE_VERSION_32_4) + PwDbHeaderV4Fields.TransformSeed -> if (version.toLong() < FILE_VERSION_32_4.toLong()) transformSeed = fieldData - PwDbHeaderV4Fields.TransformRounds -> if (version < FILE_VERSION_32_4) + PwDbHeaderV4Fields.TransformRounds -> if (version.toLong() < FILE_VERSION_32_4.toLong()) setTransformRound(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 PwDbHeaderV4Fields.StreamStartBytes -> streamStartBytes = fieldData - PwDbHeaderV4Fields.InnerRandomStreamID -> if (version < FILE_VERSION_32_4) + PwDbHeaderV4Fields.InnerRandomStreamID -> if (version.toLong() < FILE_VERSION_32_4.toLong()) setRandomStreamID(fieldData) PwDbHeaderV4Fields.KdfParameters -> databaseV4.kdfParameters = KdfParameters.deserialize(fieldData) @@ -256,8 +258,8 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( throw IOException("Invalid compression flags.") } - val flag = bytes4ToInt(pbFlags) - if (flag < 0 || flag >= CompressionAlgorithm.values().size) { + val flag = bytes4ToUInt(pbFlags) + if (flag.toLong() < 0 || flag.toLong() >= CompressionAlgorithm.values().size) { throw IOException("Unrecognized compression flag.") } @@ -272,8 +274,8 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( throw IOException("Invalid stream id.") } - val id = bytes4ToInt(streamID) - if (id < 0 || id >= CrsAlgorithm.values().size) { + val id = bytes4ToUInt(streamID) + if (id.toInt() < 0 || id.toInt() >= CrsAlgorithm.values().size) { throw IOException("Invalid stream id.") } @@ -287,43 +289,42 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( * @param version Database version * @return true if it's a supported version */ - private fun validVersion(version: Long): Boolean { - return version and FILE_VERSION_CRITICAL_MASK <= FILE_VERSION_32_4 and FILE_VERSION_CRITICAL_MASK + private fun validVersion(version: UnsignedInt): Boolean { + return version.toInt() and FILE_VERSION_CRITICAL_MASK.toInt() <= + FILE_VERSION_32_4.toInt() and FILE_VERSION_CRITICAL_MASK.toInt() } companion object { - var ULONG_MAX_VALUE: Long = -1 + val DBSIG_PRE2 = UnsignedInt(-0x4ab4049a) + val DBSIG_2 = UnsignedInt(-0x4ab40499) - const val DBSIG_PRE2 = -0x4ab4049a - const val DBSIG_2 = -0x4ab40499 + private val FILE_VERSION_CRITICAL_MASK = UnsignedInt(-0x10000) + val FILE_VERSION_32_3 = UnsignedInt(0x00030001) + val FILE_VERSION_32_4 = UnsignedInt(0x00040000) - private const val FILE_VERSION_CRITICAL_MASK: Long = -0x10000 - const val FILE_VERSION_32_3: Long = 0x00030001 - const val FILE_VERSION_32_4: Long = 0x00040000 - - fun getCompressionFromFlag(flag: Int): CompressionAlgorithm? { - return when (flag) { + fun getCompressionFromFlag(flag: UnsignedInt): CompressionAlgorithm? { + return when (flag.toInt()) { 0 -> CompressionAlgorithm.None 1 -> CompressionAlgorithm.GZip else -> null } } - fun getFlagFromCompression(compression: CompressionAlgorithm): Int { + fun getFlagFromCompression(compression: CompressionAlgorithm): UnsignedInt { return when (compression) { - CompressionAlgorithm.GZip -> 1 - else -> 0 + CompressionAlgorithm.GZip -> UnsignedInt(1) + 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) } @Throws(IOException::class) 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 try { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt index e2cd3c80f..ce57e6db5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt @@ -90,10 +90,10 @@ class DatabaseInputKDB(cacheDirectory: File, // Select algorithm when { - header.flags and DatabaseHeaderKDB.FLAG_RIJNDAEL != 0 -> { + header.flags.toInt() and DatabaseHeaderKDB.FLAG_RIJNDAEL.toInt() != 0 -> { mDatabaseToOpen.encryptionAlgorithm = EncryptionAlgorithm.AESRijndael } - header.flags and DatabaseHeaderKDB.FLAG_TWOFISH != 0 -> { + header.flags.toInt() and DatabaseHeaderKDB.FLAG_TWOFISH.toInt() != 0 -> { mDatabaseToOpen.encryptionAlgorithm = EncryptionAlgorithm.Twofish } else -> throw InvalidAlgorithmDatabaseException() @@ -160,8 +160,8 @@ class DatabaseInputKDB(cacheDirectory: File, var newEntry: EntryKDB? = null var currentGroupNumber = 0 var currentEntryNumber = 0 - while (currentGroupNumber < header.numGroups - || currentEntryNumber < header.numEntries) { + while (currentGroupNumber < header.numGroups.toLong() + || currentEntryNumber < header.numEntries.toLong()) { val fieldType = cipherInputStream.readBytes2ToUShort() val fieldSize = cipherInputStream.readBytes4ToUInt().toInt() @@ -175,7 +175,7 @@ class DatabaseInputKDB(cacheDirectory: File, when (fieldSize) { 4 -> { newGroup = mDatabaseToOpen.createGroup().apply { - setGroupId(cipherInputStream.readBytes4ToInt()) + setGroupId(cipherInputStream.readBytes4ToUInt().toInt()) } } 16 -> { @@ -194,7 +194,7 @@ class DatabaseInputKDB(cacheDirectory: File, } ?: newEntry?.let { entry -> val groupKDB = mDatabaseToOpen.createGroup() - groupKDB.nodeId = NodeIdInt(cipherInputStream.readBytes4ToInt()) + groupKDB.nodeId = NodeIdInt(cipherInputStream.readBytes4ToUInt().toInt()) entry.parent = groupKDB } } @@ -203,7 +203,7 @@ class DatabaseInputKDB(cacheDirectory: File, group.creationTime = cipherInputStream.readBytes5ToDate() } ?: newEntry?.let { entry -> - var iconId = cipherInputStream.readBytes4ToInt() + var iconId = cipherInputStream.readBytes4ToUInt().toInt() // Clean up after bug that set icon ids to -1 if (iconId == -1) { iconId = 0 @@ -237,7 +237,7 @@ class DatabaseInputKDB(cacheDirectory: File, } 0x0007 -> { newGroup?.let { group -> - group.icon = mDatabaseToOpen.iconFactory.getIcon(cipherInputStream.readBytes4ToInt()) + group.icon = mDatabaseToOpen.iconFactory.getIcon(cipherInputStream.readBytes4ToUInt().toInt()) } ?: newEntry?.let { entry -> entry.password = cipherInputStream.readBytesToString(fieldSize,false) @@ -253,7 +253,7 @@ class DatabaseInputKDB(cacheDirectory: File, } 0x0009 -> { newGroup?.let { group -> - group.flags = cipherInputStream.readBytes4ToInt() + group.groupFlags = cipherInputStream.readBytes4ToUInt().toInt() } ?: newEntry?.let { entry -> entry.creationTime = cipherInputStream.readBytes5ToDate() diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt index a7887d290..ab1d498d5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt @@ -43,6 +43,8 @@ import com.kunzisoft.keepass.database.file.DatabaseKDBXXML import com.kunzisoft.keepass.database.file.DateKDBXUtil import com.kunzisoft.keepass.stream.* import com.kunzisoft.keepass.tasks.ProgressTaskUpdater +import com.kunzisoft.keepass.utils.UnsignedInt +import com.kunzisoft.keepass.utils.UnsignedLong import org.bouncycastle.crypto.StreamCipher import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException @@ -130,7 +132,7 @@ class DatabaseInputKDBX(cacheDirectory: File, } 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 dataDecrypted = LittleEndianDataInputStream(decrypted) @@ -178,7 +180,7 @@ class DatabaseInputKDBX(cacheDirectory: File, else -> isPlain } - if (mDatabase.kdbxVersion >= DatabaseHeaderKDBX.FILE_VERSION_32_4) { + if (mDatabase.kdbxVersion.toLong() >= DatabaseHeaderKDBX.FILE_VERSION_32_4.toLong()) { loadInnerHeader(inputStreamXml, header) } @@ -226,7 +228,7 @@ class DatabaseInputKDBX(cacheDirectory: File, header: DatabaseHeaderKDBX): Boolean { val fieldId = dataInputStream.read().toByte() - val size = dataInputStream.readInt() + val size = dataInputStream.readUInt().toInt() if (size < 0) throw IOException("Corrupted file") when (fieldId) { @@ -488,7 +490,7 @@ class DatabaseInputKDBX(cacheDirectory: File, } else if (name.equals(DatabaseKDBXXML.ElemNotes, ignoreCase = true)) { ctxGroup?.notes = readString(xpp) } 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)) { ctxGroup?.iconCustom = mDatabase.iconFactory.getIcon(readUuid(xpp)) } 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)) { ctxEntry?.nodeId = NodeIdUUID(readUuid(xpp)) } 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)) { ctxEntry?.iconCustom = mDatabase.iconFactory.getIcon(readUuid(xpp)) } 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.ElemExpiryTime, ignoreCase = true) -> tl?.expiryTime = readPwTime(xpp) 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) else -> readUnknown(xpp) } @@ -621,7 +623,7 @@ class DatabaseInputKDBX(cacheDirectory: File, KdbContext.EntryAutoType -> if (name.equals(DatabaseKDBXXML.ElemAutoTypeEnabled, ignoreCase = true)) { ctxEntry?.autoType?.enabled = readBool(xpp, 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)) { ctxEntry?.autoType?.defaultSequence = readString(xpp) } else if (name.equals(DatabaseKDBXXML.ElemAutoTypeItem, ignoreCase = true)) { @@ -815,7 +817,7 @@ class DatabaseInputKDBX(cacheDirectory: File, val sDate = readString(xpp) 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) if (buf.size != 8) { val buf8 = ByteArray(8) @@ -887,48 +889,39 @@ class DatabaseInputKDBX(cacheDirectory: File, } @Throws(IOException::class, XmlPullParserException::class) - private fun readInt(xpp: XmlPullParser, def: Int): Int { - val str = readString(xpp) - + private fun readInt(xpp: XmlPullParser, default: Int): Int { return try { - Integer.parseInt(str) - } catch (e: NumberFormatException) { - def + readString(xpp).toInt() + } catch (e: Exception) { + default } } @Throws(IOException::class, XmlPullParserException::class) - private fun readUInt(xpp: XmlPullParser, uDefault: Long): Long { - 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) - + private fun readUInt(xpp: XmlPullParser, default: UnsignedInt): UnsignedInt { return try { - java.lang.Long.parseLong(str) - } catch (e: NumberFormatException) { - def + UnsignedInt(readString(xpp).toInt()) + } catch (e: Exception) { + default } } @Throws(IOException::class, XmlPullParserException::class) - private fun readULong(xpp: XmlPullParser, uDefault: Long): Long { - var u = readLong(xpp, uDefault) - - if (u < 0) { - u = uDefault + private fun readLong(xpp: XmlPullParser, default: Long): Long { + return try { + readString(xpp).toLong() + } catch (e: Exception) { + 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) @@ -1050,7 +1043,7 @@ class DatabaseInputKDBX(cacheDirectory: File, companion object { - private const val DEFAULT_HISTORY_DAYS: Long = 365 + private val DEFAULT_HISTORY_DAYS = UnsignedInt(365) @Throws(XmlPullParserException::class) private fun createPullParser(readerStream: InputStream): XmlPullParser { @@ -1062,8 +1055,6 @@ class DatabaseInputKDBX(cacheDirectory: File, return xpp } - - private const val MAX_UINT = 4294967296L // 2^32 } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDB.kt index 71a38715c..6cf726239 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDB.kt @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.database.file.output import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB -import com.kunzisoft.keepass.stream.intTo4Bytes +import com.kunzisoft.keepass.stream.uIntTo4Bytes import java.io.IOException import java.io.OutputStream @@ -29,14 +29,14 @@ class DatabaseHeaderOutputKDB(private val mHeader: DatabaseHeaderKDB, @Throws(IOException::class) fun outputStart() { - mOutputStream.write(intTo4Bytes(mHeader.signature1)) - mOutputStream.write(intTo4Bytes(mHeader.signature2)) - mOutputStream.write(intTo4Bytes(mHeader.flags)) - mOutputStream.write(intTo4Bytes(mHeader.version)) + mOutputStream.write(uIntTo4Bytes(mHeader.signature1)) + mOutputStream.write(uIntTo4Bytes(mHeader.signature2)) + mOutputStream.write(uIntTo4Bytes(mHeader.flags)) + mOutputStream.write(uIntTo4Bytes(mHeader.version)) mOutputStream.write(mHeader.masterSeed) mOutputStream.write(mHeader.encryptionIV) - mOutputStream.write(intTo4Bytes(mHeader.numGroups)) - mOutputStream.write(intTo4Bytes(mHeader.numEntries)) + mOutputStream.write(uIntTo4Bytes(mHeader.numGroups)) + mOutputStream.write(uIntTo4Bytes(mHeader.numEntries)) } @Throws(IOException::class) @@ -47,7 +47,7 @@ class DatabaseHeaderOutputKDB(private val mHeader: DatabaseHeaderKDB, @Throws(IOException::class) fun outputEnd() { mOutputStream.write(mHeader.transformSeed) - mOutputStream.write(intTo4Bytes(mHeader.numKeyEncRounds)) + mOutputStream.write(uIntTo4Bytes(mHeader.numKeyEncRounds)) } @Throws(IOException::class) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt index f5a987f6f..571e7c136 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt @@ -24,8 +24,8 @@ import com.kunzisoft.keepass.database.element.database.DatabaseKDBX import com.kunzisoft.keepass.database.exception.DatabaseOutputException import com.kunzisoft.keepass.database.file.DatabaseHeader import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX -import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.ULONG_MAX_VALUE import com.kunzisoft.keepass.stream.* +import com.kunzisoft.keepass.utils.UnsignedLong import com.kunzisoft.keepass.utils.VariantDictionary import java.io.ByteArrayOutputStream import java.io.IOException @@ -66,7 +66,7 @@ constructor(private val databaseKDBX: DatabaseKDBX, val hmac: Mac try { 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) } catch (e: NoSuchAlgorithmException) { throw DatabaseOutputException(e) @@ -82,15 +82,15 @@ constructor(private val databaseKDBX: DatabaseKDBX, @Throws(IOException::class) fun output() { - los.writeUInt(DatabaseHeader.PWM_DBSIG_1.toLong()) - los.writeUInt(DatabaseHeaderKDBX.DBSIG_2.toLong()) + los.writeUInt(DatabaseHeader.PWM_DBSIG_1) + los.writeUInt(DatabaseHeaderKDBX.DBSIG_2) los.writeUInt(header.version) 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) - 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.TransformRounds, longTo8Bytes(databaseKDBX.numberKeyEncryptionRounds)) } else { @@ -101,10 +101,10 @@ constructor(private val databaseKDBX: DatabaseKDBX, 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.StreamStartBytes, header.streamStartBytes) - writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomStreamID, intTo4Bytes(header.innerRandomStream!!.id)) + writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomStreamID, uIntTo4Bytes(header.innerRandomStream!!.id)) } if (databaseKDBX.containsPublicCustomData()) { @@ -136,7 +136,7 @@ constructor(private val databaseKDBX: DatabaseKDBX, @Throws(IOException::class) 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) } else { los.writeInt(size) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseInnerHeaderOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseInnerHeaderOutputKDBX.kt index c87289252..ea16d04a8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseInnerHeaderOutputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseInnerHeaderOutputKDBX.kt @@ -40,7 +40,7 @@ class DatabaseInnerHeaderOutputKDBX(private val database: DatabaseKDBX, dataOutputStream.writeInt(4) if (header.innerRandomStream == null) throw IOException("Can't write innerRandomStream") - dataOutputStream.writeInt(header.innerRandomStream!!.id) + dataOutputStream.writeInt(header.innerRandomStream!!.id.toInt()) val streamKeySize = header.innerRandomStreamKey.size dataOutputStream.write(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.InnerRandomstreamKey.toInt()) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt index 056b0129d..1c429ccaf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt @@ -28,6 +28,7 @@ import com.kunzisoft.keepass.database.file.DatabaseHeader import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream import com.kunzisoft.keepass.stream.NullOutputStream +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.BufferedOutputStream import java.io.ByteArrayOutputStream import java.io.IOException @@ -117,18 +118,18 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, when { 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 -> { - header.flags = header.flags or DatabaseHeaderKDB.FLAG_TWOFISH + header.flags = UnsignedInt(header.flags.toInt() or DatabaseHeaderKDB.FLAG_TWOFISH.toInt()) } else -> throw DatabaseOutputException("Unsupported algorithm.") } header.version = DatabaseHeaderKDB.DBVER_DW - header.numGroups = mDatabaseKDB.numberOfGroups() - header.numEntries = mDatabaseKDB.numberOfEntries() - header.numKeyEncRounds = mDatabaseKDB.numberKeyEncryptionRounds.toInt() // TODO Signed Long - Unsigned Int #443 + header.numGroups = UnsignedInt(mDatabaseKDB.numberOfGroups()) + header.numEntries = UnsignedInt(mDatabaseKDB.numberOfEntries()) + header.numKeyEncRounds = UnsignedInt.fromLong(mDatabaseKDB.numberKeyEncryptionRounds) setIVs(header) @@ -279,6 +280,5 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, if (data != null) { los.write(data) } - } } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt index e4248946b..cdc2015a5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt @@ -85,7 +85,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, header = outputHeader(mOS) 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) cos.write(header!!.streamStartBytes) @@ -105,7 +105,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, 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) ihOut.output() } @@ -209,7 +209,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, writeObject(DatabaseKDBXXML.ElemDbDescChanged, mDatabaseKDBX.descriptionChanged.date) writeObject(DatabaseKDBXXML.ElemDbDefaultUser, mDatabaseKDBX.defaultUserName, true) writeObject(DatabaseKDBXXML.ElemDbDefaultUserChanged, mDatabaseKDBX.defaultUserNameChanged.date) - writeObject(DatabaseKDBXXML.ElemDbMntncHistoryDays, mDatabaseKDBX.maintenanceHistoryDays) + writeObject(DatabaseKDBXXML.ElemDbMntncHistoryDays, mDatabaseKDBX.maintenanceHistoryDays.toLong()) writeObject(DatabaseKDBXXML.ElemDbColor, mDatabaseKDBX.color) writeObject(DatabaseKDBXXML.ElemDbKeyChanged, mDatabaseKDBX.keyLastChanged.date) writeObject(DatabaseKDBXXML.ElemDbKeyChangeRec, mDatabaseKDBX.keyChangeRecDays) @@ -230,7 +230,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, writeUuid(DatabaseKDBXXML.ElemLastTopVisibleGroup, mDatabaseKDBX.lastTopVisibleGroupUUID) // 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() writeCustomData(mDatabaseKDBX.customData) @@ -274,7 +274,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, 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.innerRandomStreamKey = ByteArray(32) } else { @@ -288,7 +288,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, 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) } @@ -385,7 +385,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) 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)) } else { val dt = DateTime(value) @@ -489,7 +489,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, xml.startTag(null, DatabaseKDBXXML.ElemAutoType) writeObject(DatabaseKDBXXML.ElemAutoTypeEnabled, autoType.enabled) - writeObject(DatabaseKDBXXML.ElemAutoTypeObfuscation, autoType.obfuscationOptions) + writeObject(DatabaseKDBXXML.ElemAutoTypeObfuscation, autoType.obfuscationOptions.toLong()) if (autoType.defaultSequence.isNotEmpty()) { writeObject(DatabaseKDBXXML.ElemAutoTypeDefaultSeq, autoType.defaultSequence, true) @@ -629,7 +629,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, writeObject(DatabaseKDBXXML.ElemLastAccessTime, node.lastAccessTime.date) writeObject(DatabaseKDBXXML.ElemExpiryTime, node.expiryTime.date) writeObject(DatabaseKDBXXML.ElemExpires, node.expires) - writeObject(DatabaseKDBXXML.ElemUsageCount, node.usageCount) + writeObject(DatabaseKDBXXML.ElemUsageCount, node.usageCount.toLong()) writeObject(DatabaseKDBXXML.ElemLocationChanged, node.locationChanged.date) xml.endTag(null, DatabaseKDBXXML.ElemTimes) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt index f870a1e29..511e3612b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt @@ -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.stream.* import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.io.OutputStream import java.nio.charset.Charset @@ -54,12 +55,12 @@ class EntryOutputKDB // Group ID mOutputStream.write(GROUPID_FIELD_TYPE) mOutputStream.write(GROUPID_FIELD_SIZE) - mOutputStream.write(intTo4Bytes(mEntry.parent!!.id)) + mOutputStream.write(uIntTo4Bytes(UnsignedInt(mEntry.parent!!.id))) // Image ID mOutputStream.write(IMAGEID_FIELD_TYPE) mOutputStream.write(IMAGEID_FIELD_SIZE) - mOutputStream.write(intTo4Bytes(mEntry.icon.iconId)) + mOutputStream.write(uIntTo4Bytes(UnsignedInt(mEntry.icon.iconId))) // Title //byte[] title = mEntry.title.getBytes("UTF-8"); @@ -101,14 +102,9 @@ class EntryOutputKDB // Binary mOutputStream.write(BINARY_DATA_FIELD_TYPE) val binaryData = mEntry.binaryData - val binaryDataLength = binaryData?.length() ?: 0 - val binaryDataLengthRightSize = if (binaryDataLength <= Int.MAX_VALUE) { - binaryDataLength.toInt() - } else { - 0 // TODO if length > UInt.maxvalue show exception #443 - } + val binaryDataLength = binaryData?.length() ?: 0L // Write data length - mOutputStream.write(intTo4Bytes(binaryDataLengthRightSize)) + mOutputStream.write(uIntTo4Bytes(UnsignedInt.fromLong(binaryDataLength))) // Write data if (binaryDataLength > 0) { binaryData?.getInputDataStream().use { inputStream -> @@ -140,7 +136,7 @@ class EntryOutputKDB private fun writePassword(str: String, os: OutputStream): Int { val initial = str.toByteArray(Charset.forName("UTF-8")) val length = initial.size + 1 - os.write(intTo4Bytes(length)) + os.write(uIntTo4Bytes(UnsignedInt(length))) os.write(initial) os.write(0x00) return length @@ -164,13 +160,13 @@ class EntryOutputKDB val BINARY_DATA_FIELD_TYPE:ByteArray = uShortTo2Bytes(14) val END_FIELD_TYPE:ByteArray = uShortTo2Bytes(0xFFFF) - val LONG_FOUR:ByteArray = intTo4Bytes(4) - val UUID_FIELD_SIZE:ByteArray = intTo4Bytes(16) - val DATE_FIELD_SIZE:ByteArray = intTo4Bytes(5) - val IMAGEID_FIELD_SIZE:ByteArray = intTo4Bytes(4) - val LEVEL_FIELD_SIZE:ByteArray = intTo4Bytes(4) - val FLAGS_FIELD_SIZE:ByteArray = intTo4Bytes(4) - val ZERO_FIELD_SIZE:ByteArray = intTo4Bytes(0) + val LONG_FOUR:ByteArray = uIntTo4Bytes(UnsignedInt(4)) + val UUID_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(16)) + val DATE_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(5)) + val IMAGEID_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4)) + val LEVEL_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4)) + val FLAGS_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4)) + val ZERO_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(0)) val ZERO_FIVE:ByteArray = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/GroupOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/GroupOutputKDB.kt index b0b054e47..d1e157fa3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/GroupOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/GroupOutputKDB.kt @@ -21,9 +21,10 @@ package com.kunzisoft.keepass.database.file.output import com.kunzisoft.keepass.database.element.group.GroupKDB 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.utils.StringDatabaseKDBUtils +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.io.OutputStream @@ -39,7 +40,7 @@ class GroupOutputKDB (private val mGroup: GroupKDB, private val mOutputStream: O // Group ID mOutputStream.write(GROUPID_FIELD_TYPE) mOutputStream.write(GROUPID_FIELD_SIZE) - mOutputStream.write(intTo4Bytes(mGroup.id)) + mOutputStream.write(uIntTo4Bytes(UnsignedInt(mGroup.id))) // Name mOutputStream.write(NAME_FIELD_TYPE) @@ -68,7 +69,7 @@ class GroupOutputKDB (private val mGroup: GroupKDB, private val mOutputStream: O // Image ID mOutputStream.write(IMAGEID_FIELD_TYPE) mOutputStream.write(IMAGEID_FIELD_SIZE) - mOutputStream.write(intTo4Bytes(mGroup.icon.iconId)) + mOutputStream.write(uIntTo4Bytes(UnsignedInt(mGroup.icon.iconId))) // Level mOutputStream.write(LEVEL_FIELD_TYPE) @@ -78,7 +79,7 @@ class GroupOutputKDB (private val mGroup: GroupKDB, private val mOutputStream: O // Flags mOutputStream.write(FLAGS_FIELD_TYPE) mOutputStream.write(FLAGS_FIELD_SIZE) - mOutputStream.write(intTo4Bytes(mGroup.flags)) + mOutputStream.write(uIntTo4Bytes(UnsignedInt(mGroup.groupFlags))) // End 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 END_FIELD_TYPE:ByteArray = uShortTo2Bytes(0xFFFF) - val GROUPID_FIELD_SIZE:ByteArray = intTo4Bytes(4) - val DATE_FIELD_SIZE:ByteArray = intTo4Bytes(5) - val IMAGEID_FIELD_SIZE:ByteArray = intTo4Bytes(4) - val LEVEL_FIELD_SIZE:ByteArray = intTo4Bytes(2) - val FLAGS_FIELD_SIZE:ByteArray = intTo4Bytes(4) - val ZERO_FIELD_SIZE:ByteArray = intTo4Bytes(0) + val GROUPID_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4)) + val DATE_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(5)) + val IMAGEID_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4)) + val LEVEL_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(2)) + val FLAGS_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(4)) + val ZERO_FIELD_SIZE:ByteArray = uIntTo4Bytes(UnsignedInt(0)) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt index cdd94e5ea..425e5a0eb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt @@ -444,8 +444,8 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() { mMemoryPref?.summary = memoryToShow.toString() } DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_PARALLELISM_TASK -> { - val oldParallelism = data.getInt(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) - val newParallelism = data.getInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) + val oldParallelism = data.getLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) + val newParallelism = data.getLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) val parallelismToShow = if (result.isSuccess) { newParallelism diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.kt index 33fbf1cff..f5645c89a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.kt @@ -35,27 +35,26 @@ class ParallelismPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFr override fun onDialogClosed(positiveResult: Boolean) { if (positiveResult) { database?.let { database -> - var parallelism: Int = try { - inputText.toInt() + val parallelism: Long = try { + inputText.toLong() } catch (e: NumberFormatException) { MIN_PARALLELISM } - if (parallelism < MIN_PARALLELISM) { - parallelism = MIN_PARALLELISM - } - // TODO Max Parallelism val oldParallelism = database.parallelism database.parallelism = parallelism - mProgressDialogThread?.startDatabaseSaveParallelism(oldParallelism, parallelism, mDatabaseAutoSaveEnable) + mProgressDialogThread?.startDatabaseSaveParallelism( + oldParallelism, + parallelism, + mDatabaseAutoSaveEnable) } } } companion object { - const val MIN_PARALLELISM = 1 + const val MIN_PARALLELISM = 1L fun newInstance(key: String): ParallelismPreferenceDialogFragmentCompat { val fragment = ParallelismPreferenceDialogFragmentCompat() diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.kt b/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.kt index 8e79ac783..ead4231c9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.kt @@ -19,6 +19,7 @@ */ package com.kunzisoft.keepass.stream +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.io.InputStream import java.security.MessageDigest @@ -80,7 +81,7 @@ class HashedBlockInputStream(inputStream: InputStream) : InputStream() { bufferPos = 0 val index = baseStream.readUInt() - if (index != bufferIndex) { + if (index.toLong() != bufferIndex) { throw IOException("Invalid data format") } bufferIndex++ @@ -90,11 +91,7 @@ class HashedBlockInputStream(inputStream: InputStream) : InputStream() { throw IOException("Invalid data format") } - val bufferSize = baseStream.readBytes4ToInt() - if (bufferSize < 0) { - throw IOException("Invalid data format") - } - + val bufferSize = baseStream.readBytes4ToUInt().toInt() if (bufferSize == 0) { for (hash in 0 until HASH_SIZE) { if (storedHash[hash].toInt() != 0) { @@ -144,7 +141,7 @@ class HashedBlockInputStream(inputStream: InputStream) : InputStream() { if (!readHashedBlock()) return -1 } - val output = byteToUInt(buffer[bufferPos]) + val output = UnsignedInt.fromByte(buffer[bufferPos]).toInt() bufferPos++ return output diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockOutputStream.kt b/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockOutputStream.kt index 3d788ffb0..5ebc3337a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockOutputStream.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockOutputStream.kt @@ -19,6 +19,7 @@ */ package com.kunzisoft.keepass.stream +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.io.OutputStream import java.security.MessageDigest @@ -98,7 +99,7 @@ class HashedBlockOutputStream : OutputStream { @Throws(IOException::class) private fun writeHashedBlock() { - baseStream.writeUInt(bufferIndex) + baseStream.writeUInt(UnsignedInt.fromLong(bufferIndex)) bufferIndex++ if (bufferPos > 0) { diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockInputStream.kt b/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockInputStream.kt index 3365fb402..f7649348f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockInputStream.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockInputStream.kt @@ -19,6 +19,7 @@ */ package com.kunzisoft.keepass.stream +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.io.InputStream import java.security.InvalidKeyException @@ -43,7 +44,7 @@ class HmacBlockInputStream(baseStream: InputStream, private val verify: Boolean, if (!readSafeBlock()) return -1 } - val output = byteToUInt(buffer[bufferPos]) + val output = UnsignedInt.fromByte(buffer[bufferPos]).toInt() bufferPos++ return output @@ -97,10 +98,10 @@ class HmacBlockInputStream(baseStream: InputStream, private val verify: Boolean, if (pbBlockSize.size != 4) { throw IOException("File corrupted") } - val blockSize = bytes4ToInt(pbBlockSize) + val blockSize = bytes4ToUInt(pbBlockSize) bufferPos = 0 - buffer = baseStream.readBytes(blockSize) + buffer = baseStream.readBytes(blockSize.toInt()) if (verify) { val cmpHmac: ByteArray @@ -134,7 +135,7 @@ class HmacBlockInputStream(baseStream: InputStream, private val verify: Boolean, blockIndex++ - if (blockSize == 0) { + if (blockSize.toLong() == 0L) { endOfStream = true return false } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockOutputStream.kt b/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockOutputStream.kt index a1dd11740..76c476586 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockOutputStream.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/HmacBlockOutputStream.kt @@ -19,6 +19,7 @@ */ package com.kunzisoft.keepass.stream +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.io.OutputStream import java.security.InvalidKeyException @@ -87,7 +88,7 @@ class HmacBlockOutputStream(outputStream: OutputStream, @Throws(IOException::class) private fun writeSafeBlock() { val bufBlockIndex = longTo8Bytes(blockIndex) - val blockSizeBuf = intTo4Bytes(bufferPos) + val blockSizeBuf = uIntTo4Bytes(UnsignedInt(bufferPos)) val blockHmac: ByteArray val blockKey = HmacBlockStream.getHmacKey64(key, blockIndex) diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataInputStream.kt b/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataInputStream.kt index c880109f3..46e030941 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataInputStream.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataInputStream.kt @@ -19,6 +19,7 @@ */ package com.kunzisoft.keepass.stream +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.io.InputStream @@ -32,15 +33,10 @@ class LittleEndianDataInputStream(private val baseStream: InputStream) : InputSt * be interpreted as an unsigned integer. */ @Throws(IOException::class) - fun readUInt(): Long { + fun readUInt(): UnsignedInt { return baseStream.readBytes4ToUInt() } - @Throws(IOException::class) - fun readInt(): Int { - return baseStream.readBytes4ToInt() - } - @Throws(IOException::class) fun readUShort(): Int { val buf = ByteArray(2) diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataOutputStream.kt b/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataOutputStream.kt index e759472f4..f445afced 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataOutputStream.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/LittleEndianDataOutputStream.kt @@ -19,6 +19,7 @@ */ package com.kunzisoft.keepass.stream +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.io.OutputStream @@ -30,8 +31,8 @@ import java.io.OutputStream class LittleEndianDataOutputStream(private val baseStream: OutputStream) : OutputStream() { @Throws(IOException::class) - fun writeUInt(uint: Long) { // TODO UInt #443 - baseStream.write(intTo4Bytes(uint.toInt())) + fun writeUInt(uInt: UnsignedInt) { + baseStream.write(uIntTo4Bytes(uInt)) } @Throws(IOException::class) @@ -66,7 +67,7 @@ class LittleEndianDataOutputStream(private val baseStream: OutputStream) : Outpu @Throws(IOException::class) fun writeInt(value: Int) { - baseStream.write(intTo4Bytes(value)) + baseStream.write(uIntTo4Bytes(UnsignedInt(value))) } @Throws(IOException::class) diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/StreamBytesUtils.kt b/app/src/main/java/com/kunzisoft/keepass/stream/StreamBytesUtils.kt index 348aa2d91..c08c0d5a3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/StreamBytesUtils.kt +++ b/app/src/main/java/com/kunzisoft/keepass/stream/StreamBytesUtils.kt @@ -21,6 +21,7 @@ package com.kunzisoft.keepass.stream import com.kunzisoft.keepass.database.element.DateInstant import com.kunzisoft.keepass.utils.StringDatabaseKDBUtils.bytesToString +import com.kunzisoft.keepass.utils.UnsignedInt import java.io.IOException import java.io.InputStream import java.util.* @@ -80,13 +81,8 @@ fun InputStream.readBytes(length: Int, bufferSize: Int, readBytes: (bytesRead: B * be interpreted as an unsigned integer. */ @Throws(IOException::class) -fun InputStream.readBytes4ToUInt(): Long { - return readBytes4ToInt().toLong() and INT_TO_LONG_MASK -} - -@Throws(IOException::class) -fun InputStream.readBytes4ToInt(): Int { - return bytes4ToInt(readBytesLength(4)) +fun InputStream.readBytes4ToUInt(): UnsignedInt { + return bytes4ToUInt(readBytesLength(4)) } @Throws(IOException::class) @@ -141,18 +137,11 @@ fun bytes64ToLong(buf: ByteArray): Long { + (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. */ -fun bytes4ToInt(buf: ByteArray): Int { - return ((buf[0].toInt() and 0xFF) +fun bytes4ToUInt(buf: ByteArray): UnsignedInt { + return UnsignedInt((buf[0].toInt() and 0xFF) + (buf[1].toInt() and 0xFF shl 8) + (buf[2].toInt() and 0xFF shl 16) + (buf[3].toInt() and 0xFF shl 24)) @@ -182,11 +171,11 @@ fun bytes5ToDate(buf: ByteArray, calendar: Calendar = Calendar.getInstance()): D System.arraycopy(buf, 0, cDate, 0, dateSize) val readOffset = 0 - val dw1 = byteToUInt(cDate[readOffset]) - val dw2 = byteToUInt(cDate[readOffset + 1]) - val dw3 = byteToUInt(cDate[readOffset + 2]) - val dw4 = byteToUInt(cDate[readOffset + 3]) - val dw5 = byteToUInt(cDate[readOffset + 4]) + val dw1 = UnsignedInt.fromByte(cDate[readOffset]).toInt() + val dw2 = UnsignedInt.fromByte(cDate[readOffset + 1]).toInt() + val dw3 = UnsignedInt.fromByte(cDate[readOffset + 2]).toInt() + val dw4 = UnsignedInt.fromByte(cDate[readOffset + 3]).toInt() + val dw5 = UnsignedInt.fromByte(cDate[readOffset + 4]).toInt() // Unpack 5 byte structure to date and time 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 { - return (value and 0xFF).toByte() -} - -/** - * Write a 32-bit value. - */ -fun intTo4Bytes(value: Int): ByteArray { +fun uIntTo4Bytes(value: UnsignedInt): ByteArray { val buf = ByteArray(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 } @@ -264,8 +246,8 @@ fun dateTo5Bytes(date: Date, calendar: Calendar = Calendar.getInstance()): ByteA val minute = calendar.get(Calendar.MINUTE) val second = calendar.get(Calendar.SECOND) - buf[0] = uIntToByte(year shr 6 and 0x0000003F) - buf[1] = uIntToByte(year and 0x0000003F shl 2 or (month shr 2 and 0x00000003)) + buf[0] = UnsignedInt(year shr 6 and 0x0000003F).toByte() + buf[1] = UnsignedInt(year and 0x0000003F shl 2 or (month shr 2 and 0x00000003)).toByte() buf[2] = (month and 0x00000003 shl 6 or (day and 0x0000001F shl 1) or (hour shr 4 and 0x00000001)).toByte() buf[3] = (hour and 0x0000000F shl 4 or (minute shr 2 and 0x0000000F)).toByte() @@ -273,9 +255,3 @@ fun dateTo5Bytes(date: Date, calendar: Calendar = Calendar.getInstance()): ByteA return buf } - - -/** Convert a byte to an unsigned byte */ -fun byteToUInt(byte: Byte): Int { - return byte.toInt() and 0xFF -} diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/StringDatabaseKDBUtils.kt b/app/src/main/java/com/kunzisoft/keepass/utils/StringDatabaseKDBUtils.kt index ca9141fdb..611e97b34 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/StringDatabaseKDBUtils.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/StringDatabaseKDBUtils.kt @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.utils -import com.kunzisoft.keepass.stream.intTo4Bytes +import com.kunzisoft.keepass.stream.uIntTo4Bytes import java.io.IOException import java.io.OutputStream import java.nio.charset.Charset @@ -57,7 +57,7 @@ object StringDatabaseKDBUtils { var str = string if (str == null) { // Write out a null character - os.write(intTo4Bytes(1)) + os.write(uIntTo4Bytes(UnsignedInt(1))) os.write(0x00) return 0 } @@ -69,7 +69,7 @@ object StringDatabaseKDBUtils { val initial = str.toByteArray(defaultCharset) val length = initial.size + 1 - os.write(intTo4Bytes(length)) + os.write(uIntTo4Bytes(UnsignedInt(length))) os.write(initial) os.write(0x00) diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UnsignedInt.kt b/app/src/main/java/com/kunzisoft/keepass/utils/UnsignedInt.kt new file mode 100644 index 000000000..2ee23cab1 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/utils/UnsignedInt.kt @@ -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 . + * + */ +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()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UnsignedLong.kt b/app/src/main/java/com/kunzisoft/keepass/utils/UnsignedLong.kt new file mode 100644 index 000000000..804bab699 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/utils/UnsignedLong.kt @@ -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 . + * + */ +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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/VariantDictionary.java b/app/src/main/java/com/kunzisoft/keepass/utils/VariantDictionary.java index 8b10f4e0d..d7de12d6f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/VariantDictionary.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/VariantDictionary.java @@ -24,10 +24,10 @@ import com.kunzisoft.keepass.stream.LittleEndianDataOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; -import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.bytes4ToInt; import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.bytes4ToUInt; import static com.kunzisoft.keepass.stream.StreamBytesUtilsKt.bytes64ToLong; @@ -35,6 +35,7 @@ public class VariantDictionary { private static final int VdVersion = 0x0100; private static final int VdmCritical = 0xFF00; private static final int VdmInfo = 0x00FF; + private static Charset UTF8Charset = Charset.forName("UTF-8"); private Map dict = new HashMap<>(); @@ -69,8 +70,8 @@ public class VariantDictionary { dict.put(name, new VdType(type, value)); } - public void setUInt32(String name, long value) { putType(VdType.UInt32, name, value); } - public long getUInt32(String name) { return (long)dict.get(name).value; } + public void setUInt32(String name, UnsignedInt value) { putType(VdType.UInt32, 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 long getUInt64(String name) { return (long)dict.get(name).value; } @@ -109,14 +110,14 @@ public class VariantDictionary { break; } - int nameLen = lis.readInt(); + int nameLen = lis.readUInt().toInt(); byte[] nameBuf = lis.readBytes(nameLen); if (nameLen != nameBuf.length) { 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); if (valueLen != valueBuf.length) { throw new IOException("Invalid format"); @@ -140,7 +141,7 @@ public class VariantDictionary { break; case VdType.Int32: if (valueLen == 4) { - d.setInt32(name, bytes4ToInt(valueBuf)); + d.setInt32(name, bytes4ToUInt(valueBuf).toInt()); } break; case VdType.Int64: @@ -149,7 +150,7 @@ public class VariantDictionary { } break; case VdType.String: - d.setString(name, new String(valueBuf, "UTF-8")); + d.setString(name, new String(valueBuf, UTF8Charset)); break; case VdType.ByteArray: d.setByteArray(name, valueBuf); @@ -172,12 +173,7 @@ public class VariantDictionary { for (Map.Entry entry: d.dict.entrySet()) { String name = entry.getKey(); - byte[] nameBuf = null; - try { - nameBuf = name.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IOException("Couldn't encode parameter name."); - } + byte[] nameBuf = nameBuf = name.getBytes(UTF8Charset); VdType vd = entry.getValue(); @@ -189,7 +185,7 @@ public class VariantDictionary { switch (vd.type) { case VdType.UInt32: los.writeInt(4); - los.writeUInt((long)vd.value); + los.writeUInt((UnsignedInt) vd.value); break; case VdType.UInt64: los.writeInt(8); @@ -210,7 +206,7 @@ public class VariantDictionary { break; case VdType.String: String value = (String)vd.value; - buf = value.getBytes("UTF-8"); + buf = value.getBytes(UTF8Charset); los.writeInt(buf.length); los.write(buf); break; @@ -231,7 +227,6 @@ public class VariantDictionary { for (Map.Entry entry : d.dict.entrySet()) { String key = entry.getKey(); VdType value = entry.getValue(); - dict.put(key, value); } } diff --git a/app/src/main/jni/argon2/argon2_jni.c b/app/src/main/jni/argon2/argon2_jni.c index bf4d8c9c1..64203f9e4 100644 --- a/app/src/main/jni/argon2/argon2_jni.c +++ b/app/src/main/jni/argon2/argon2_jni.c @@ -126,12 +126,11 @@ void throwExceptionF(JNIEnv *env, jclass exception, const char *format, ...) { } #define ARGON2_HASHLEN 32 -#define NB_BLOCKSIZE 1024 JNIEXPORT jbyteArray JNICALL Java_com_kunzisoft_keepass_crypto_keyDerivation_Argon2Native_nTransformMasterKey(JNIEnv *env, - jobject this, jbyteArray password, jbyteArray salt, jint parallelism, jlong memory, - jlong iterations, jbyteArray secretKey, jbyteArray associatedData, jlong version) { + jobject this, jbyteArray password, jbyteArray salt, jint parallelism, jint memory, + jint iterations, jbyteArray secretKey, jbyteArray associatedData, jint version) { argon2_context context; uint8_t *out; @@ -151,7 +150,7 @@ JNICALL Java_com_kunzisoft_keepass_crypto_keyDerivation_Argon2Native_nTransformM uint8_t *adBuf; uint32_t adLen = getJNIArray(env, associatedData, &adBuf); - context.out = (uint8_t *) out; + context.out = out; context.outlen = ARGON2_HASHLEN; context.pwd = passwordBuf; context.pwdlen = passwordLen; @@ -162,7 +161,7 @@ JNICALL Java_com_kunzisoft_keepass_crypto_keyDerivation_Argon2Native_nTransformM context.ad = adBuf; context.adlen = adLen; 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.threads = (uint32_t) parallelism; context.allocate_cbk = NULL; diff --git a/app/src/main/jni/final_key/kpd_jni.c b/app/src/main/jni/final_key/kpd_jni.c index 0fe5c1b02..670e97009 100644 --- a/app/src/main/jni/final_key/kpd_jni.c +++ b/app/src/main/jni/final_key/kpd_jni.c @@ -438,7 +438,7 @@ uint32_t generate_key_material(void *arg) { 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; uint32_t flip; pthread_t t1, t2;