diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 9ad584793..e001841ed 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -55,7 +55,7 @@ import com.kunzisoft.keepass.autofill.AutofillComponent import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.model.RegisterInfo import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.services.DatabaseTaskNotificationService diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index ee746d966..e20bcd8fe 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -69,7 +69,7 @@ import com.kunzisoft.keepass.database.search.SearchParameters import com.kunzisoft.keepass.education.GroupActivityEducation import com.kunzisoft.keepass.magikeyboard.MagikeyboardService import com.kunzisoft.keepass.model.GroupInfo -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.model.RegisterInfo import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt index bdf85beb0..956932f11 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt @@ -57,6 +57,7 @@ import com.kunzisoft.keepass.biometric.AdvancedUnlockFragment import com.kunzisoft.keepass.biometric.AdvancedUnlockManager import com.kunzisoft.keepass.database.action.DatabaseTaskProvider import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException import com.kunzisoft.keepass.database.exception.FileNotFoundDatabaseException import com.kunzisoft.keepass.education.PasswordActivityEducation diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/MainCredentialDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/MainCredentialDialogFragment.kt index 2180caa8a..dea4f73c9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/MainCredentialDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/MainCredentialDialogFragment.kt @@ -27,7 +27,7 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.view.MainCredentialView diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/PasswordEncodingDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/PasswordEncodingDialogFragment.kt index df32460cb..745543d3d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/PasswordEncodingDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/PasswordEncodingDialogFragment.kt @@ -26,7 +26,7 @@ import android.os.Bundle import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential class PasswordEncodingDialogFragment : DialogFragment() { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SetMainCredentialDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SetMainCredentialDialogFragment.kt index dd3bd064e..dab99bae6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SetMainCredentialDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SetMainCredentialDialogFragment.kt @@ -37,7 +37,7 @@ import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.hardware.HardwareKeyResponseHelper -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.password.PasswordEntropy import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.view.HardwareKeySelectionView diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt index 802967e3b..58857caf8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt @@ -7,7 +7,7 @@ import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.database.action.DatabaseTaskProvider import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.model.CipherEncryptDatabase -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.viewmodels.DatabaseViewModel diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt index 14e28631e..415f75807 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt @@ -43,7 +43,7 @@ import com.kunzisoft.keepass.database.element.node.Node import com.kunzisoft.keepass.database.element.node.NodeId import com.kunzisoft.keepass.icons.IconDrawableFactory import com.kunzisoft.keepass.model.GroupInfo -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.services.DatabaseTaskNotificationService import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ActionRunnable diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignMainCredentialInDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignMainCredentialInDatabaseRunnable.kt index 06478eef3..d61d83d69 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignMainCredentialInDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignMainCredentialInDatabaseRunnable.kt @@ -25,14 +25,14 @@ import com.kunzisoft.keepass.app.database.CipherDatabaseAction import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.hardware.HardwareKey -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential open class AssignMainCredentialInDatabaseRunnable ( - context: Context, - database: Database, - protected val mDatabaseUri: Uri, - mainCredential: MainCredential, - challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray) + context: Context, + database: Database, + protected val mDatabaseUri: Uri, + mainCredential: MainCredential, + challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray) : SaveDatabaseRunnable(context, database, true, mainCredential, challengeResponseRetriever) { private var mBackupKey: ByteArray? = null diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt index 52a36bd98..bf3a53954 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt @@ -25,7 +25,7 @@ import android.util.Log import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.hardware.HardwareKey -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.settings.PreferencesUtil class CreateDatabaseRunnable(context: Context, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/DatabaseTaskProvider.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/DatabaseTaskProvider.kt index 67a4fdbb2..c52d4d63d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/DatabaseTaskProvider.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/DatabaseTaskProvider.kt @@ -46,7 +46,7 @@ import com.kunzisoft.keepass.database.exception.InvalidCredentialsDatabaseExcept import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.hardware.HardwareKeyResponseHelper import com.kunzisoft.keepass.model.CipherEncryptDatabase -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.model.SnapFileDatabaseInfo import com.kunzisoft.keepass.services.DatabaseTaskNotificationService import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_ASSIGN_PASSWORD_TASK @@ -394,7 +394,8 @@ class DatabaseTaskProvider { */ fun startDatabaseCreate(databaseUri: Uri, - mainCredential: MainCredential) { + mainCredential: MainCredential + ) { start(Bundle().apply { putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, databaseUri) putParcelable(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY, mainCredential) @@ -447,7 +448,8 @@ class DatabaseTaskProvider { } fun startDatabaseAssignPassword(databaseUri: Uri, - mainCredential: MainCredential) { + mainCredential: MainCredential + ) { start(Bundle().apply { putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, databaseUri) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt index 0ce8d3d5b..1051cc541 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt @@ -28,7 +28,7 @@ import com.kunzisoft.keepass.database.element.binary.BinaryData import com.kunzisoft.keepass.database.exception.LoadDatabaseException import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.model.CipherEncryptDatabase -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ProgressTaskUpdater diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/MergeDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/MergeDatabaseRunnable.kt index a3c925d8e..f2b2d3274 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/MergeDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/MergeDatabaseRunnable.kt @@ -25,7 +25,7 @@ import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.binary.BinaryData import com.kunzisoft.keepass.database.exception.LoadDatabaseException import com.kunzisoft.keepass.hardware.HardwareKey -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ProgressTaskUpdater diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt index 027d4c319..5fdc65e82 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt @@ -24,7 +24,7 @@ import android.net.Uri import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.exception.DatabaseException import com.kunzisoft.keepass.hardware.HardwareKey -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.tasks.ActionRunnable open class SaveDatabaseRunnable(protected var context: Context, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/CompositeKey.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/CompositeKey.kt index 3a429c92c..6423068f9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/CompositeKey.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/CompositeKey.kt @@ -1,28 +1,10 @@ package com.kunzisoft.keepass.database.element -import android.content.ContentResolver -import android.net.Uri -import android.util.Base64 -import android.util.Log -import com.kunzisoft.encrypt.HashManager -import com.kunzisoft.keepass.database.element.database.DatabaseKDBX -import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars -import com.kunzisoft.keepass.utils.StringUtil.toHexString -import com.kunzisoft.keepass.utils.UriUtil -import org.apache.commons.codec.binary.Hex -import org.w3c.dom.Node -import java.io.ByteArrayInputStream -import java.io.IOException -import java.io.InputStream -import java.io.UnsupportedEncodingException -import java.nio.charset.Charset -import javax.xml.XMLConstants -import javax.xml.parsers.DocumentBuilderFactory -import javax.xml.parsers.ParserConfigurationException +import com.kunzisoft.keepass.hardware.HardwareKey data class CompositeKey(var passwordData: ByteArray? = null, var keyFileData: ByteArray? = null, - var hardwareKeyData: ByteArray? = null) { + var hardwareKey: HardwareKey? = null) { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -38,10 +20,7 @@ data class CompositeKey(var passwordData: ByteArray? = null, if (other.keyFileData == null) return false if (!keyFileData.contentEquals(other.keyFileData)) return false } else if (other.keyFileData != null) return false - if (hardwareKeyData != null) { - if (other.hardwareKeyData == null) return false - if (!hardwareKeyData.contentEquals(other.hardwareKeyData)) return false - } else if (other.hardwareKeyData != null) return false + if (hardwareKey != other.hardwareKey) return false return true } @@ -49,189 +28,7 @@ data class CompositeKey(var passwordData: ByteArray? = null, override fun hashCode(): Int { var result = passwordData?.contentHashCode() ?: 0 result = 31 * result + (keyFileData?.contentHashCode() ?: 0) - result = 31 * result + (hardwareKeyData?.contentHashCode() ?: 0) + result = 31 * result + (hardwareKey?.hashCode() ?: 0) return result } - - companion object { - - private val TAG = CompositeKey::class.java.simpleName - - @Throws(IOException::class) - fun retrievePasswordKey(key: String, - encoding: Charset): ByteArray { - val bKey: ByteArray = try { - key.toByteArray(encoding) - } catch (e: UnsupportedEncodingException) { - key.toByteArray() - } - return HashManager.hashSha256(bKey) - } - - @Throws(IOException::class) - fun retrieveFileKey(contentResolver: ContentResolver, - keyFileUri: Uri?, - allowXML: Boolean): ByteArray { - if (keyFileUri == null) - throw IOException("Keyfile URI is null") - val keyData = getKeyFileData(contentResolver, keyFileUri) - ?: throw IOException("No data retrieved") - try { - // Check XML key file - val xmlKeyByteArray = if (allowXML) - loadXmlKeyFile(ByteArrayInputStream(keyData)) - else - null - if (xmlKeyByteArray != null) { - return xmlKeyByteArray - } - - // Check 32 bytes key file - when (keyData.size) { - 32 -> return keyData - 64 -> try { - return Hex.decodeHex(String(keyData).toCharArray()) - } catch (ignoredException: Exception) { - // Key is not base 64, treat it as binary data - } - } - // Hash file as binary data - return HashManager.hashSha256(keyData) - } catch (e: Exception) { - throw IOException("Unable to load the keyfile.", e) - } - } - - @Throws(IOException::class) - fun retrieveHardwareKey(keyData: ByteArray): ByteArray { - return HashManager.hashSha256(keyData) - } - - @Throws(Exception::class) - private fun getKeyFileData(contentResolver: ContentResolver, - keyFileUri: Uri): ByteArray? { - UriUtil.getUriInputStream(contentResolver, keyFileUri)?.use { keyFileInputStream -> - return keyFileInputStream.readBytes() - } - return null - } - - private fun loadXmlKeyFile(keyInputStream: InputStream): ByteArray? { - try { - val documentBuilderFactory = DocumentBuilderFactory.newInstance() - - // Disable certain unsecure XML-Parsing DocumentBuilderFactory features - try { - documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true) - } catch (e : ParserConfigurationException) { - Log.w(TAG, "Unable to add FEATURE_SECURE_PROCESSING to prevent XML eXternal Entity injection (XXE)") - } - - val documentBuilder = documentBuilderFactory.newDocumentBuilder() - val doc = documentBuilder.parse(keyInputStream) - - var xmlKeyFileVersion = 1F - - val docElement = doc.documentElement - val keyFileChildNodes = docElement.childNodes - // Root node - if (docElement == null - || !docElement.nodeName.equals(XML_NODE_ROOT_NAME, ignoreCase = true)) { - return null - } - if (keyFileChildNodes.length < 2) - return null - for (keyFileChildPosition in 0 until keyFileChildNodes.length) { - val keyFileChildNode = keyFileChildNodes.item(keyFileChildPosition) - // - if (keyFileChildNode.nodeName.equals(XML_NODE_META_NAME, ignoreCase = true)) { - val metaChildNodes = keyFileChildNode.childNodes - for (metaChildPosition in 0 until metaChildNodes.length) { - val metaChildNode = metaChildNodes.item(metaChildPosition) - // - if (metaChildNode.nodeName.equals(XML_NODE_VERSION_NAME, ignoreCase = true)) { - val versionChildNodes = metaChildNode.childNodes - for (versionChildPosition in 0 until versionChildNodes.length) { - val versionChildNode = versionChildNodes.item(versionChildPosition) - if (versionChildNode.nodeType == Node.TEXT_NODE) { - val versionText = versionChildNode.textContent.removeSpaceChars() - try { - xmlKeyFileVersion = versionText.toFloat() - Log.i(TAG, "Reading XML KeyFile version : $xmlKeyFileVersion") - } catch (e: Exception) { - Log.e(TAG, "XML Keyfile version cannot be read : $versionText") - } - } - } - } - } - } - // - if (keyFileChildNode.nodeName.equals(XML_NODE_KEY_NAME, ignoreCase = true)) { - val keyChildNodes = keyFileChildNode.childNodes - for (keyChildPosition in 0 until keyChildNodes.length) { - val keyChildNode = keyChildNodes.item(keyChildPosition) - // - if (keyChildNode.nodeName.equals(XML_NODE_DATA_NAME, ignoreCase = true)) { - var hashString : String? = null - if (keyChildNode.hasAttributes()) { - val dataNodeAttributes = keyChildNode.attributes - hashString = dataNodeAttributes - .getNamedItem(XML_ATTRIBUTE_DATA_HASH).nodeValue - } - val dataChildNodes = keyChildNode.childNodes - for (dataChildPosition in 0 until dataChildNodes.length) { - val dataChildNode = dataChildNodes.item(dataChildPosition) - if (dataChildNode.nodeType == Node.TEXT_NODE) { - val dataString = dataChildNode.textContent.removeSpaceChars() - when (xmlKeyFileVersion) { - 1F -> { - // No hash in KeyFile XML version 1 - return Base64.decode(dataString, - DatabaseKDBX.BASE_64_FLAG - ) - } - 2F -> { - return if (hashString != null - && checkKeyFileHash(dataString, hashString)) { - Log.i(TAG, "Successful key file hash check.") - Hex.decodeHex(dataString.toCharArray()) - } else { - Log.e(TAG, "Unable to check the hash of the key file.") - null - } - } - } - } - } - } - } - } - } - } catch (e: Exception) { - return null - } - return null - } - - private fun checkKeyFileHash(data: String, hash: String): Boolean { - var success = false - try { - // hexadecimal encoding of the first 4 bytes of the SHA-256 hash of the key. - val dataDigest = HashManager.hashSha256(Hex.decodeHex(data.toCharArray())) - .copyOfRange(0, 4).toHexString() - success = dataDigest == hash - } catch (e: Exception) { - e.printStackTrace() - } - return success - } - - private const val XML_NODE_ROOT_NAME = "KeyFile" - private const val XML_NODE_META_NAME = "Meta" - private const val XML_NODE_VERSION_NAME = "Version" - private const val XML_NODE_KEY_NAME = "Key" - private const val XML_NODE_DATA_NAME = "Data" - private const val XML_ATTRIBUTE_DATA_HASH = "Hash" - } } \ No newline at end of file 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 b5cbd8c0c..62c964d75 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 @@ -59,7 +59,6 @@ import com.kunzisoft.keepass.database.search.SearchHelper import com.kunzisoft.keepass.database.search.SearchParameters import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.icons.IconDrawableFactory -import com.kunzisoft.keepass.model.MainCredential import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.utils.SingletonHolder import com.kunzisoft.keepass.utils.UriUtil diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/MainCredential.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/MainCredential.kt new file mode 100644 index 000000000..172c228ec --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/MainCredential.kt @@ -0,0 +1,276 @@ +/* + * Copyright 2022 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.database.element + +import android.content.ContentResolver +import android.net.Uri +import android.os.Parcel +import android.os.Parcelable +import android.util.Base64 +import android.util.Log +import com.kunzisoft.encrypt.HashManager +import com.kunzisoft.keepass.database.element.database.DatabaseKDBX +import com.kunzisoft.keepass.hardware.HardwareKey +import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars +import com.kunzisoft.keepass.utils.StringUtil.toHexString +import com.kunzisoft.keepass.utils.UriUtil +import com.kunzisoft.keepass.utils.readEnum +import com.kunzisoft.keepass.utils.writeEnum +import org.apache.commons.codec.binary.Hex +import org.w3c.dom.Node +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.io.UnsupportedEncodingException +import java.nio.charset.Charset +import javax.xml.XMLConstants +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.parsers.ParserConfigurationException + +data class MainCredential(var password: String? = null, + var keyFileUri: Uri? = null, + var hardwareKey: HardwareKey? = null): Parcelable { + + constructor(parcel: Parcel) : this() { + password = parcel.readString() + keyFileUri = parcel.readParcelable(Uri::class.java.classLoader) + hardwareKey = parcel.readEnum() + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(password) + parcel.writeParcelable(keyFileUri, flags) + parcel.writeEnum(hardwareKey) + } + + override fun describeContents(): Int { + return 0 + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as MainCredential + + if (password != other.password) return false + if (keyFileUri != other.keyFileUri) return false + if (hardwareKey != other.hardwareKey) return false + + return true + } + + override fun hashCode(): Int { + var result = password?.hashCode() ?: 0 + result = 31 * result + (keyFileUri?.hashCode() ?: 0) + result = 31 * result + (hardwareKey?.hashCode() ?: 0) + return result + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): MainCredential { + return MainCredential(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + + private val TAG = MainCredential::class.java.simpleName + + @Throws(IOException::class) + fun retrievePasswordKey(key: String, + encoding: Charset + ): ByteArray { + val bKey: ByteArray = try { + key.toByteArray(encoding) + } catch (e: UnsupportedEncodingException) { + key.toByteArray() + } + return HashManager.hashSha256(bKey) + } + + @Throws(IOException::class) + fun retrieveFileKey(contentResolver: ContentResolver, + keyFileUri: Uri?, + allowXML: Boolean): ByteArray { + if (keyFileUri == null) + throw IOException("Keyfile URI is null") + val keyData = getKeyFileData(contentResolver, keyFileUri) + ?: throw IOException("No data retrieved") + try { + // Check XML key file + val xmlKeyByteArray = if (allowXML) + loadXmlKeyFile(ByteArrayInputStream(keyData)) + else + null + if (xmlKeyByteArray != null) { + return xmlKeyByteArray + } + + // Check 32 bytes key file + when (keyData.size) { + 32 -> return keyData + 64 -> try { + return Hex.decodeHex(String(keyData).toCharArray()) + } catch (ignoredException: Exception) { + // Key is not base 64, treat it as binary data + } + } + // Hash file as binary data + return HashManager.hashSha256(keyData) + } catch (e: Exception) { + throw IOException("Unable to load the keyfile.", e) + } + } + + @Throws(IOException::class) + fun retrieveHardwareKey(keyData: ByteArray): ByteArray { + return HashManager.hashSha256(keyData) + } + + @Throws(Exception::class) + private fun getKeyFileData(contentResolver: ContentResolver, + keyFileUri: Uri): ByteArray? { + UriUtil.getUriInputStream(contentResolver, keyFileUri)?.use { keyFileInputStream -> + return keyFileInputStream.readBytes() + } + return null + } + + private fun loadXmlKeyFile(keyInputStream: InputStream): ByteArray? { + try { + val documentBuilderFactory = DocumentBuilderFactory.newInstance() + + // Disable certain unsecure XML-Parsing DocumentBuilderFactory features + try { + documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true) + } catch (e : ParserConfigurationException) { + Log.w(TAG, "Unable to add FEATURE_SECURE_PROCESSING to prevent XML eXternal Entity injection (XXE)") + } + + val documentBuilder = documentBuilderFactory.newDocumentBuilder() + val doc = documentBuilder.parse(keyInputStream) + + var xmlKeyFileVersion = 1F + + val docElement = doc.documentElement + val keyFileChildNodes = docElement.childNodes + // Root node + if (docElement == null + || !docElement.nodeName.equals(XML_NODE_ROOT_NAME, ignoreCase = true)) { + return null + } + if (keyFileChildNodes.length < 2) + return null + for (keyFileChildPosition in 0 until keyFileChildNodes.length) { + val keyFileChildNode = keyFileChildNodes.item(keyFileChildPosition) + // + if (keyFileChildNode.nodeName.equals(XML_NODE_META_NAME, ignoreCase = true)) { + val metaChildNodes = keyFileChildNode.childNodes + for (metaChildPosition in 0 until metaChildNodes.length) { + val metaChildNode = metaChildNodes.item(metaChildPosition) + // + if (metaChildNode.nodeName.equals(XML_NODE_VERSION_NAME, ignoreCase = true)) { + val versionChildNodes = metaChildNode.childNodes + for (versionChildPosition in 0 until versionChildNodes.length) { + val versionChildNode = versionChildNodes.item(versionChildPosition) + if (versionChildNode.nodeType == Node.TEXT_NODE) { + val versionText = versionChildNode.textContent.removeSpaceChars() + try { + xmlKeyFileVersion = versionText.toFloat() + Log.i(TAG, "Reading XML KeyFile version : $xmlKeyFileVersion") + } catch (e: Exception) { + Log.e(TAG, "XML Keyfile version cannot be read : $versionText") + } + } + } + } + } + } + // + if (keyFileChildNode.nodeName.equals(XML_NODE_KEY_NAME, ignoreCase = true)) { + val keyChildNodes = keyFileChildNode.childNodes + for (keyChildPosition in 0 until keyChildNodes.length) { + val keyChildNode = keyChildNodes.item(keyChildPosition) + // + if (keyChildNode.nodeName.equals(XML_NODE_DATA_NAME, ignoreCase = true)) { + var hashString : String? = null + if (keyChildNode.hasAttributes()) { + val dataNodeAttributes = keyChildNode.attributes + hashString = dataNodeAttributes + .getNamedItem(XML_ATTRIBUTE_DATA_HASH).nodeValue + } + val dataChildNodes = keyChildNode.childNodes + for (dataChildPosition in 0 until dataChildNodes.length) { + val dataChildNode = dataChildNodes.item(dataChildPosition) + if (dataChildNode.nodeType == Node.TEXT_NODE) { + val dataString = dataChildNode.textContent.removeSpaceChars() + when (xmlKeyFileVersion) { + 1F -> { + // No hash in KeyFile XML version 1 + return Base64.decode(dataString, + DatabaseKDBX.BASE_64_FLAG + ) + } + 2F -> { + return if (hashString != null + && checkKeyFileHash(dataString, hashString) + ) { + Log.i(TAG, "Successful key file hash check.") + Hex.decodeHex(dataString.toCharArray()) + } else { + Log.e(TAG, "Unable to check the hash of the key file.") + null + } + } + } + } + } + } + } + } + } + } catch (e: Exception) { + return null + } + return null + } + + private fun checkKeyFileHash(data: String, hash: String): Boolean { + var success = false + try { + // hexadecimal encoding of the first 4 bytes of the SHA-256 hash of the key. + val dataDigest = HashManager.hashSha256(Hex.decodeHex(data.toCharArray())) + .copyOfRange(0, 4).toHexString() + success = dataDigest == hash + } catch (e: Exception) { + e.printStackTrace() + } + return success + } + + private const val XML_NODE_ROOT_NAME = "KeyFile" + private const val XML_NODE_META_NAME = "Meta" + private const val XML_NODE_VERSION_NAME = "Version" + private const val XML_NODE_KEY_NAME = "Key" + private const val XML_NODE_DATA_NAME = "Data" + private const val XML_ATTRIBUTE_DATA_HASH = "Hash" + } +} 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 aeaf611ee..c72817dcb 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 @@ -25,7 +25,6 @@ import com.kunzisoft.encrypt.aes.AESTransformer import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory -import com.kunzisoft.keepass.database.element.CompositeKey import com.kunzisoft.keepass.database.element.binary.BinaryData import com.kunzisoft.keepass.database.element.entry.EntryKDB import com.kunzisoft.keepass.database.element.group.GroupKDB @@ -33,7 +32,7 @@ import com.kunzisoft.keepass.database.element.icon.IconImageStandard import com.kunzisoft.keepass.database.element.node.NodeIdInt import com.kunzisoft.keepass.database.element.node.NodeIdUUID import com.kunzisoft.keepass.database.element.node.NodeVersioned -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import java.io.IOException import java.nio.charset.Charset import java.util.* @@ -131,37 +130,30 @@ class DatabaseKDB : DatabaseVersioned() { contentResolver: ContentResolver, mainCredential: MainCredential ) { + // Exception when no password if (mainCredential.password == null && mainCredential.keyFileUri == null) throw IllegalArgumentException("Key cannot be empty.") if (mainCredential.hardwareKey != null) throw IllegalArgumentException("Hardware key is not supported.") - this.masterKey = compositeKeyToMasterKey(retrieveCompositeKey( - contentResolver, - mainCredential - )) - } - @Throws(IOException::class) - private fun retrieveCompositeKey(contentResolver: ContentResolver, - mainCredential: MainCredential - ): CompositeKey { - // Save to rebuild master password with new seed later - mMainCredential = mainCredential + // Retrieve plain data val password = mainCredential.password val keyFileUri = mainCredential.keyFileUri - val passwordBytes = if (password != null) CompositeKey.retrievePasswordKey( + val passwordBytes = if (password != null) MainCredential.retrievePasswordKey( password, passwordEncoding ) else null - val keyFileBytes = if (keyFileUri != null) CompositeKey.retrieveFileKey( + val keyFileBytes = if (keyFileUri != null) MainCredential.retrieveFileKey( contentResolver, keyFileUri, false ) else null - val compositeKey = CompositeKey(passwordBytes, keyFileBytes) - // Save to rebuild master password with new seed later - mCompositeKey = compositeKey - return compositeKey + + // Build master key + this.masterKey = HashManager.hashSha256( + passwordBytes, + keyFileBytes + ) } override fun createGroup(): GroupKDB { 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 b568cd76b..1c2e4a181 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 @@ -52,7 +52,7 @@ import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VER import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_40 import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_41 import com.kunzisoft.keepass.hardware.HardwareKey -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.utils.UnsignedInt import com.kunzisoft.keepass.utils.longTo8Bytes import java.io.IOException @@ -66,6 +66,9 @@ import kotlin.math.min class DatabaseKDBX : DatabaseVersioned { + // To resave the database with same credential when already loaded + private var mCompositeKey = CompositeKey() + var hmacKey: ByteArray? = null private set @@ -231,56 +234,72 @@ class DatabaseKDBX : DatabaseVersioned { mainCredential: MainCredential, challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray ) { - this.masterKey = compositeKeyToMasterKey(retrieveCompositeKey( + // Retrieve each plain credential + val password = mainCredential.password + val keyFileUri = mainCredential.keyFileUri + val hardwareKey = mainCredential.hardwareKey + val passwordBytes = if (password != null) MainCredential.retrievePasswordKey( + password, + passwordEncoding + ) else null + val keyFileBytes = if (keyFileUri != null) MainCredential.retrieveFileKey( contentResolver, - mainCredential, - transformSeed, - challengeResponseRetriever - )) + keyFileUri, + true + ) else null + val hardwareKeyBytes = if (hardwareKey != null) MainCredential.retrieveHardwareKey( + challengeResponseRetriever.invoke(hardwareKey, transformSeed) + ) else null + + // Save to rebuild master password with new seed later + mCompositeKey = CompositeKey(passwordBytes, keyFileBytes, hardwareKey) + + // Build the master key + this.masterKey = composedKeyToMasterKey( + passwordBytes, + keyFileBytes, + hardwareKeyBytes + ) } @Throws(DatabaseOutputException::class) fun deriveCompositeKey( challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray ) { - if (mCompositeKey.hardwareKeyData == null) - this.masterKey = compositeKeyToMasterKey(mCompositeKey) - val hardwareKey = mMainCredential.hardwareKey - val hardwareKeyBytes = if (hardwareKey != null) CompositeKey.retrieveHardwareKey( - challengeResponseRetriever.invoke(hardwareKey, transformSeed) - ) else null - this.masterKey = compositeKeyToMasterKey(mCompositeKey.apply { - this.hardwareKeyData = hardwareKeyBytes - }) + val passwordBytes = mCompositeKey.passwordData + val keyFileBytes = mCompositeKey.keyFileData + val hardwareKey = mCompositeKey.hardwareKey + if (hardwareKey == null) { + // If no hardware key, simply rebuild from composed keys + this.masterKey = composedKeyToMasterKey( + passwordBytes, + keyFileBytes + ) + } else { + val hardwareKeyBytes = MainCredential.retrieveHardwareKey( + challengeResponseRetriever.invoke(hardwareKey, transformSeed) + ) + this.masterKey = composedKeyToMasterKey( + passwordBytes, + keyFileBytes, + hardwareKeyBytes + ) + } } - @Throws(IOException::class) - private fun retrieveCompositeKey(contentResolver: ContentResolver, - mainCredential: MainCredential, - transformSeed: ByteArray?, - challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray, - ): CompositeKey { - // Save to rebuild master password with new seed later - mMainCredential = mainCredential - val password = mainCredential.password - val keyFileUri = mainCredential.keyFileUri - val hardwareKey = mainCredential.hardwareKey - val passwordBytes = if (password != null) CompositeKey.retrievePasswordKey( - password, - passwordEncoding - ) else null - val keyFileBytes = if (keyFileUri != null) CompositeKey.retrieveFileKey( - contentResolver, - keyFileUri, - true - ) else null - val hardwareKeyBytes = if (hardwareKey != null) CompositeKey.retrieveHardwareKey( - challengeResponseRetriever.invoke(hardwareKey, transformSeed) - ) else null - val compositeKey = CompositeKey(passwordBytes, keyFileBytes, hardwareKeyBytes) - // Save to rebuild master password with new seed later - mCompositeKey = compositeKey - return compositeKey + private fun composedKeyToMasterKey(passwordData: ByteArray?, + keyFileData: ByteArray?, + hardwareKeyData: ByteArray? = null): ByteArray { + return HashManager.hashSha256( + passwordData, + keyFileData, + hardwareKeyData + ) + } + + fun copyMasterKeyFrom(databaseVersioned: DatabaseKDBX) { + super.copyMasterKeyFrom(databaseVersioned) + this.mCompositeKey = databaseVersioned.mCompositeKey } fun getMinKdbxVersion(): UnsignedInt { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt index 182475e9d..31597608f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt @@ -20,10 +20,8 @@ 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.CompositeKey import com.kunzisoft.keepass.database.element.binary.AttachmentPool import com.kunzisoft.keepass.database.element.binary.BinaryCache import com.kunzisoft.keepass.database.element.entry.EntryVersioned @@ -33,7 +31,6 @@ import com.kunzisoft.keepass.database.element.icon.IconsManager import com.kunzisoft.keepass.database.element.node.NodeId import com.kunzisoft.keepass.database.element.node.Type import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException -import com.kunzisoft.keepass.model.MainCredential import java.io.InputStream import java.io.UnsupportedEncodingException import java.nio.charset.Charset @@ -59,10 +56,6 @@ abstract class DatabaseVersioned< var masterKey = ByteArray(32) var finalKey: ByteArray? = null protected set - - // To resave the database with same credential when already loaded - protected var mMainCredential = MainCredential() - protected var mCompositeKey = CompositeKey() var transformSeed: ByteArray? = null abstract val version: String @@ -126,19 +119,9 @@ abstract class DatabaseVersioned< fun copyMasterKeyFrom(databaseVersioned: DatabaseVersioned) { this.masterKey = databaseVersioned.masterKey - this.mMainCredential = databaseVersioned.mMainCredential - this.mCompositeKey = databaseVersioned.mCompositeKey this.transformSeed = databaseVersioned.transformSeed } - protected fun compositeKeyToMasterKey(compositeKey: CompositeKey): ByteArray { - return HashManager.hashSha256( - compositeKey.passwordData, - compositeKey.keyFileData, - compositeKey.hardwareKeyData - ) - } - /* * ------------------------------------- * Node Creation diff --git a/app/src/main/java/com/kunzisoft/keepass/model/MainCredential.kt b/app/src/main/java/com/kunzisoft/keepass/model/MainCredential.kt deleted file mode 100644 index 8a7a3c479..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/model/MainCredential.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2022 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.model - -import android.net.Uri -import android.os.Parcel -import android.os.Parcelable -import com.kunzisoft.keepass.hardware.HardwareKey -import com.kunzisoft.keepass.utils.readEnum -import com.kunzisoft.keepass.utils.writeEnum - -data class MainCredential(var password: String? = null, - var keyFileUri: Uri? = null, - var hardwareKey: HardwareKey? = null): Parcelable { - - constructor(parcel: Parcel) : this() { - password = parcel.readString() - keyFileUri = parcel.readParcelable(Uri::class.java.classLoader) - hardwareKey = parcel.readEnum() - } - - override fun writeToParcel(parcel: Parcel, flags: Int) { - parcel.writeString(password) - parcel.writeParcelable(keyFileUri, flags) - parcel.writeEnum(hardwareKey) - } - - override fun describeContents(): Int { - return 0 - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MainCredential - - if (password != other.password) return false - if (keyFileUri != other.keyFileUri) return false - if (hardwareKey != other.hardwareKey) return false - - return true - } - - override fun hashCode(): Int { - var result = password?.hashCode() ?: 0 - result = 31 * result + (keyFileUri?.hashCode() ?: 0) - result = 31 * result + (hardwareKey?.hashCode() ?: 0) - return result - } - - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): MainCredential { - return MainCredential(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt index 4c10db941..5f1e37be0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt @@ -43,7 +43,7 @@ import com.kunzisoft.keepass.database.element.node.NodeId import com.kunzisoft.keepass.database.element.node.Type import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.model.CipherEncryptDatabase -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.model.SnapFileDatabaseInfo import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ProgressTaskUpdater diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt index 932dbce0f..e2438d5bf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -38,7 +38,7 @@ import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity import com.kunzisoft.keepass.database.action.DatabaseTaskProvider import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.hardware.HardwareKeyResponseHelper -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.view.showActionErrorIfNeeded diff --git a/app/src/main/java/com/kunzisoft/keepass/view/MainCredentialView.kt b/app/src/main/java/com/kunzisoft/keepass/view/MainCredentialView.kt index dbd40684c..34a7f2786 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/MainCredentialView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/MainCredentialView.kt @@ -39,7 +39,7 @@ import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener import com.kunzisoft.keepass.model.CredentialStorage -import com.kunzisoft.keepass.model.MainCredential +import com.kunzisoft.keepass.database.element.MainCredential class MainCredentialView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,