Better algorithm implementation

This commit is contained in:
J-Jamet
2022-01-17 21:31:05 +01:00
parent e1d4c172f4
commit 3426b3cdeb
10 changed files with 106 additions and 161 deletions

View File

@@ -309,14 +309,12 @@ class Database {
val availableEncryptionAlgorithms: List<EncryptionAlgorithm>
get() = mDatabaseKDB?.availableEncryptionAlgorithms ?: mDatabaseKDBX?.availableEncryptionAlgorithms ?: ArrayList()
var encryptionAlgorithm: EncryptionAlgorithm?
get() = mDatabaseKDB?.encryptionAlgorithm ?: mDatabaseKDBX?.encryptionAlgorithm
set(algorithm) {
algorithm?.let {
mDatabaseKDBX?.encryptionAlgorithm = algorithm
mDatabaseKDBX?.setDataEngine(algorithm.cipherEngine)
mDatabaseKDBX?.cipherUuid = algorithm.uuid
}
var encryptionAlgorithm: EncryptionAlgorithm
get() = mDatabaseKDB?.encryptionAlgorithm
?: mDatabaseKDBX?.encryptionAlgorithm
?: EncryptionAlgorithm.AESRijndael
set(value) {
mDatabaseKDBX?.encryptionAlgorithm = value
}
val availableKdfEngines: List<KdfEngine>

View File

@@ -34,11 +34,27 @@ import com.kunzisoft.keepass.database.element.node.NodeVersioned
import java.io.IOException
import java.io.InputStream
import java.util.*
import kotlin.collections.ArrayList
class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
private var kdfListV3: MutableList<KdfEngine> = ArrayList()
override var encryptionAlgorithm = EncryptionAlgorithm.AESRijndael
override val availableEncryptionAlgorithms: List<EncryptionAlgorithm> = listOf(
EncryptionAlgorithm.AESRijndael,
EncryptionAlgorithm.Twofish
)
override val kdfEngine: KdfEngine
get() = kdfAvailableList[0]
override val kdfAvailableList: List<KdfEngine> = listOf(
KdfFactory.aesKdf
)
override val passwordEncoding: String
get() = "ISO-8859-1"
override var numberKeyEncryptionRounds = 300L
override val version: String
get() = "V1"
@@ -48,7 +64,6 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
rootGroup = createGroup().apply {
icon.standard = getStandardIcon(IconImageStandard.DATABASE_ID)
}
kdfListV3.add(KdfFactory.aesKdf)
}
val backupGroup: GroupKDB?
@@ -65,24 +80,6 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
var color: Int? = null
override val kdfEngine: KdfEngine
get() = kdfListV3[0]
override val kdfAvailableList: List<KdfEngine>
get() = kdfListV3
override val availableEncryptionAlgorithms: List<EncryptionAlgorithm>
get() {
val list = ArrayList<EncryptionAlgorithm>()
list.add(EncryptionAlgorithm.AESRijndael)
list.add(EncryptionAlgorithm.Twofish)
return list
}
override val passwordEncoding: String
get() = "ISO-8859-1"
override var numberKeyEncryptionRounds = 300L
/**
* Generates an unused random tree id

View File

@@ -25,8 +25,6 @@ import android.util.Log
import com.kunzisoft.encrypt.HashManager
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.action.node.NodeHandler
import com.kunzisoft.keepass.database.crypto.AesEngine
import com.kunzisoft.keepass.database.crypto.CipherEngine
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.VariantDictionary
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
@@ -48,7 +46,6 @@ import com.kunzisoft.keepass.database.element.node.NodeVersioned
import com.kunzisoft.keepass.database.element.security.MemoryProtectionConfig
import com.kunzisoft.keepass.database.element.template.Template
import com.kunzisoft.keepass.database.element.template.TemplateEngineCompatible
import com.kunzisoft.keepass.database.exception.UnknownKDF
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_31
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_40
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_41
@@ -75,17 +72,51 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
var hmacKey: ByteArray? = null
private set
var cipherUuid = EncryptionAlgorithm.AESRijndael.uuid
private var dataEngine: CipherEngine = AesEngine()
var compressionAlgorithm = CompressionAlgorithm.GZip
override var encryptionAlgorithm = EncryptionAlgorithm.AESRijndael
fun setEncryptionAlgorithmFromUUID(uuid: UUID) {
encryptionAlgorithm = EncryptionAlgorithm.getFrom(uuid)
}
override val availableEncryptionAlgorithms: List<EncryptionAlgorithm> = listOf(
EncryptionAlgorithm.AESRijndael,
EncryptionAlgorithm.Twofish,
EncryptionAlgorithm.ChaCha20
)
override val kdfEngine: KdfEngine?
get() {
val keyDerivationFunctionParameters = kdfParameters ?: return null
for (engine in kdfAvailableList) {
if (engine.uuid == keyDerivationFunctionParameters.uuid) {
return engine
}
}
Log.i(TAG, "Unable to retrieve KDF engine")
return null
}
override val kdfAvailableList: List<KdfEngine> = listOf(
KdfFactory.aesKdf,
KdfFactory.argon2dKdf,
KdfFactory.argon2idKdf
)
var kdfParameters: KdfParameters? = null
private var kdfList: MutableList<KdfEngine> = ArrayList()
private var numKeyEncRounds: Long = 0
var publicCustomData = VariantDictionary()
fun randomize() {
kdfParameters?.let {
kdfEngine?.randomize(it)
}
}
var compressionAlgorithm = CompressionAlgorithm.GZip
private val mFieldReferenceEngine = FieldReferencesEngine(this)
private val mTemplateEngine = TemplateEngineCompatible(this)
var kdbxVersion = UnsignedInt(0)
var name = ""
var nameChanged = DateInstant()
var description = ""
@@ -115,16 +146,12 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
var lastTopVisibleGroupUUID = UUID_ZERO
var memoryProtection = MemoryProtectionConfig()
val deletedObjects = HashSet<DeletedObject>()
var publicCustomData = VariantDictionary()
val customData = CustomData()
var localizedAppName = "KeePassDX"
init {
kdfList.add(KdfFactory.aesKdf)
kdfList.add(KdfFactory.argon2dKdf)
kdfList.add(KdfFactory.argon2idKdf)
}
constructor()
/**
@@ -147,6 +174,8 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
}
}
var kdbxVersion = UnsignedInt(0)
override val version: String
get() {
val kdbxStringVersion = when(kdbxVersion) {
@@ -158,38 +187,10 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
return "V2 - KDBX$kdbxStringVersion"
}
override val kdfEngine: KdfEngine?
get() = try {
getEngineKDBX4(kdfParameters)
} catch (unknownKDF: UnknownKDF) {
Log.i(TAG, "Unable to retrieve KDF engine", unknownKDF)
null
}
override val kdfAvailableList: List<KdfEngine>
get() = kdfList
@Throws(UnknownKDF::class)
fun getEngineKDBX4(kdfParameters: KdfParameters?): KdfEngine {
val unknownKDFException = UnknownKDF()
if (kdfParameters == null) {
throw unknownKDFException
}
for (engine in kdfList) {
if (engine.uuid == kdfParameters.uuid) {
return engine
}
}
throw unknownKDFException
}
val availableCompressionAlgorithms: List<CompressionAlgorithm>
get() {
val list = ArrayList<CompressionAlgorithm>()
list.add(CompressionAlgorithm.None)
list.add(CompressionAlgorithm.GZip)
return list
}
val availableCompressionAlgorithms: List<CompressionAlgorithm> = listOf(
CompressionAlgorithm.None,
CompressionAlgorithm.GZip
)
fun changeBinaryCompression(oldCompression: CompressionAlgorithm,
newCompression: CompressionAlgorithm) {
@@ -244,15 +245,6 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
}
}
override val availableEncryptionAlgorithms: List<EncryptionAlgorithm>
get() {
val list = ArrayList<EncryptionAlgorithm>()
list.add(EncryptionAlgorithm.AESRijndael)
list.add(EncryptionAlgorithm.Twofish)
list.add(EncryptionAlgorithm.ChaCha20)
return list
}
override var numberKeyEncryptionRounds: Long
get() {
val kdfEngine = kdfEngine
@@ -312,10 +304,6 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
val lastTopVisibleGroup: GroupKDBX?
get() = getGroupByUUID(lastTopVisibleGroupUUID)
fun setDataEngine(dataEngine: CipherEngine) {
this.dataEngine = dataEngine
}
override fun getStandardIcon(iconId: Int): IconImageStandard {
return this.iconsManager.getIcon(iconId)
}
@@ -480,28 +468,30 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
@Throws(IOException::class)
fun makeFinalKey(masterSeed: ByteArray) {
kdfParameters?.let { keyDerivationFunctionParameters ->
val kdfEngine = getEngineKDBX4(keyDerivationFunctionParameters)
kdfEngine?.let { keyDerivationFunctionEngine ->
kdfParameters?.let { keyDerivationFunctionParameters ->
var transformedMasterKey = kdfEngine.transform(masterKey, keyDerivationFunctionParameters)
if (transformedMasterKey.size != 32) {
transformedMasterKey = HashManager.hashSha256(transformedMasterKey)
}
var transformedMasterKey =
keyDerivationFunctionEngine.transform(masterKey, keyDerivationFunctionParameters)
if (transformedMasterKey.size != 32) {
transformedMasterKey = HashManager.hashSha256(transformedMasterKey)
}
val cmpKey = ByteArray(65)
System.arraycopy(masterSeed, 0, cmpKey, 0, 32)
System.arraycopy(transformedMasterKey, 0, cmpKey, 32, 32)
finalKey = resizeKey(cmpKey, dataEngine.keyLength())
val cmpKey = ByteArray(65)
System.arraycopy(masterSeed, 0, cmpKey, 0, 32)
System.arraycopy(transformedMasterKey, 0, cmpKey, 32, 32)
finalKey = resizeKey(cmpKey, encryptionAlgorithm.cipherEngine.keyLength())
val messageDigest: MessageDigest
try {
messageDigest = MessageDigest.getInstance("SHA-512")
cmpKey[64] = 1
hmacKey = messageDigest.digest(cmpKey)
} catch (e: NoSuchAlgorithmException) {
throw IOException("No SHA-512 implementation")
} finally {
Arrays.fill(cmpKey, 0.toByte())
val messageDigest: MessageDigest
try {
messageDigest = MessageDigest.getInstance("SHA-512")
cmpKey[64] = 1
hmacKey = messageDigest.digest(cmpKey)
} catch (e: NoSuchAlgorithmException) {
throw IOException("No SHA-512 implementation")
} finally {
Arrays.fill(cmpKey, 0.toByte())
}
}
}
}

View File

@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.database.element.database
import android.util.Log
import com.kunzisoft.encrypt.HashManager
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
import com.kunzisoft.keepass.database.element.binary.BinaryCache
import com.kunzisoft.keepass.database.element.entry.EntryVersioned
@@ -47,11 +48,11 @@ abstract class DatabaseVersioned<
// Algorithm used to encrypt the database
var encryptionAlgorithm: EncryptionAlgorithm = EncryptionAlgorithm.AESRijndael
abstract var encryptionAlgorithm: EncryptionAlgorithm
abstract val availableEncryptionAlgorithms: List<EncryptionAlgorithm>
abstract val kdfEngine: com.kunzisoft.keepass.database.crypto.kdf.KdfEngine?
abstract val kdfAvailableList: List<com.kunzisoft.keepass.database.crypto.kdf.KdfEngine>
abstract val kdfEngine: KdfEngine?
abstract val kdfAvailableList: List<KdfEngine>
abstract var numberKeyEncryptionRounds: Long
protected abstract val passwordEncoding: String

View File

@@ -1,24 +0,0 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.database.exception
import java.io.IOException
class UnknownKDF : IOException("Unknown key derivation function")

View File

@@ -256,8 +256,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
if (pbId == null || pbId.size != 16) {
throw IOException("Invalid cipher ID.")
}
databaseV4.cipherUuid = bytes16ToUuid(pbId)
databaseV4.setEncryptionAlgorithmFromUUID(bytes16ToUuid(pbId))
}
private fun setTransformRound(roundsByte: ByteArray) {

View File

@@ -22,9 +22,7 @@ package com.kunzisoft.keepass.database.file.input
import android.util.Base64
import android.util.Log
import com.kunzisoft.encrypt.StreamCipher
import com.kunzisoft.keepass.database.crypto.CipherEngine
import com.kunzisoft.keepass.database.crypto.CrsAlgorithm
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.HmacBlock
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.binary.BinaryData
@@ -140,13 +138,10 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
stopKeyTimer()
startContentTimer(progressTaskUpdater)
val engine: CipherEngine
val cipher: Cipher
try {
engine = EncryptionAlgorithm.getFrom(mDatabase.cipherUuid).cipherEngine
val engine = mDatabase.encryptionAlgorithm.cipherEngine
engine.forcePaddingCompatibility = true
mDatabase.setDataEngine(engine)
mDatabase.encryptionAlgorithm = engine.getEncryptionAlgorithm()
cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.finalKey!!, header.encryptionIV)
engine.forcePaddingCompatibility = false
} catch (e: Exception) {

View File

@@ -25,7 +25,6 @@ import com.kunzisoft.keepass.database.crypto.VariantDictionary
import com.kunzisoft.keepass.database.crypto.kdf.KdfParameters
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.FILE_VERSION_40
import com.kunzisoft.keepass.stream.MacOutputStream
@@ -72,7 +71,7 @@ constructor(private val databaseKDBX: DatabaseKDBX,
mos.write4BytesUInt(DatabaseHeaderKDBX.DBSIG_2)
mos.write4BytesUInt(header.version)
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CipherID, uuidTo16Bytes(databaseKDBX.cipherUuid))
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CipherID, uuidTo16Bytes(databaseKDBX.encryptionAlgorithm.uuid))
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CompressionFlags, uIntTo4Bytes(DatabaseHeaderKDBX.getFlagFromCompression(databaseKDBX.compressionAlgorithm)))
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.MasterSeed, header.masterSeed)

View File

@@ -26,7 +26,6 @@ import com.kunzisoft.encrypt.StreamCipher
import com.kunzisoft.keepass.database.action.node.NodeHandler
import com.kunzisoft.keepass.database.crypto.CipherEngine
import com.kunzisoft.keepass.database.crypto.CrsAlgorithm
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
@@ -39,7 +38,6 @@ import com.kunzisoft.keepass.database.element.group.GroupKDBX
import com.kunzisoft.keepass.database.element.node.NodeKDBXInterface
import com.kunzisoft.keepass.database.element.security.MemoryProtectionConfig
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.database.exception.UnknownKDF
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_40
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_41
@@ -76,7 +74,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
try {
try {
engine = EncryptionAlgorithm.getFrom(mDatabaseKDBX.cipherUuid).cipherEngine
engine = mDatabaseKDBX.encryptionAlgorithm.cipherEngine
} catch (e: NoSuchAlgorithmException) {
throw DatabaseOutputException("No such cipher", e)
}
@@ -301,13 +299,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
if (mDatabaseKDBX.kdfParameters == null) {
mDatabaseKDBX.kdfParameters = KdfFactory.aesKdf.defaultParameters
}
try {
val kdf = mDatabaseKDBX.getEngineKDBX4(mDatabaseKDBX.kdfParameters)
kdf.randomize(mDatabaseKDBX.kdfParameters!!)
} catch (unknownKDF: UnknownKDF) {
Log.e(TAG, "Unable to retrieve header", unknownKDF)
mDatabaseKDBX.randomize()
}
if (header.version.isBefore(FILE_VERSION_40)) {

View File

@@ -63,13 +63,11 @@ class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
super.onDialogClosed(database, positiveResult)
if (positiveResult) {
database?.let {
if (algorithmSelected != null) {
val newAlgorithm = algorithmSelected
val oldAlgorithm = database.encryptionAlgorithm
val newAlgorithm = algorithmSelected
val oldAlgorithm = database.encryptionAlgorithm
if (newAlgorithm != null) {
database.encryptionAlgorithm = newAlgorithm
if (oldAlgorithm != null && newAlgorithm != null)
saveEncryption(oldAlgorithm, newAlgorithm)
saveEncryption(oldAlgorithm, newAlgorithm)
}
}
}