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 59ad16364..62913ec9f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -54,7 +54,7 @@ import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.autofill.AutofillComponent import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.database.ContextualDatabase -import com.kunzisoft.keepass.database.element.MainCredential +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.model.RegisterInfo 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 3fb5859de..9acdb01ca 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -75,10 +75,10 @@ import com.kunzisoft.keepass.adapters.BreadcrumbAdapter import com.kunzisoft.keepass.autofill.AutofillComponent import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.database.ContextualDatabase +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.database.element.DateInstant import com.kunzisoft.keepass.database.element.Entry import com.kunzisoft.keepass.database.element.Group -import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.database.element.SortNodeEnum import com.kunzisoft.keepass.database.element.node.Node import com.kunzisoft.keepass.database.element.node.NodeId 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 9159d571d..382ce1ce8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt @@ -56,7 +56,7 @@ import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.biometric.AdvancedUnlockFragment import com.kunzisoft.keepass.biometric.AdvancedUnlockManager import com.kunzisoft.keepass.database.ContextualDatabase -import com.kunzisoft.keepass.database.element.MainCredential +import com.kunzisoft.keepass.database.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 020fd1c0a..38bc3c26a 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.database.element.MainCredential +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.utils.UriUtil.getDocumentFile 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 745543d3d..a389a0a75 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.database.element.MainCredential +import com.kunzisoft.keepass.database.MainCredential class PasswordEncodingDialogFragment : DialogFragment() { @@ -78,8 +78,10 @@ class PasswordEncodingDialogFragment : DialogFragment() { private const val DATABASE_URI_KEY = "DATABASE_URI_KEY" private const val MAIN_CREDENTIAL = "MAIN_CREDENTIAL" - fun getInstance(databaseUri: Uri, - mainCredential: MainCredential): SortDialogFragment { + fun getInstance( + databaseUri: Uri, + mainCredential: MainCredential + ): SortDialogFragment { val fragment = SortDialogFragment() fragment.arguments = Bundle().apply { putParcelable(DATABASE_URI_KEY, databaseUri) 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 700b7eb13..9a28fd940 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 @@ -35,7 +35,7 @@ import com.google.android.material.textfield.TextInputLayout import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener -import com.kunzisoft.keepass.database.element.MainCredential +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.hardware.HardwareKeyActivity import com.kunzisoft.keepass.password.PasswordEntropy 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 a81546259..8dbc74c7a 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 @@ -5,8 +5,8 @@ import android.os.Bundle import androidx.activity.viewModels import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.database.ContextualDatabase +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.database.action.DatabaseTaskProvider -import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.model.CipherEncryptDatabase import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.utils.UriUtil.getBinaryDir 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 07f7e5c1b..154ac7c54 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 @@ -37,10 +37,10 @@ import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.SpecialMode import com.kunzisoft.keepass.database.ContextualDatabase +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Entry import com.kunzisoft.keepass.database.element.Group -import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.database.element.node.Node import com.kunzisoft.keepass.database.element.node.NodeId import com.kunzisoft.keepass.model.GroupInfo @@ -251,7 +251,7 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), mDatabase?.let { database -> database.fileUri?.let { databaseUri -> // Show the progress dialog now or after dialog confirmation - if (database.validatePasswordEncoding(mainCredential)) { + if (database.isValidCredential(mainCredential.toMasterCredential(contentResolver))) { assignDatabasePassword(databaseUri, mainCredential) } else { PasswordEncodingDialogFragment.getInstance(databaseUri, mainCredential) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ContextualDatabase.kt b/app/src/main/java/com/kunzisoft/keepass/database/ContextualDatabase.kt index 1212b62e0..4d28a5077 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ContextualDatabase.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/ContextualDatabase.kt @@ -1,5 +1,6 @@ package com.kunzisoft.keepass.database +import android.net.Uri import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.icon.IconImageCustom import com.kunzisoft.keepass.icons.IconDrawableFactory @@ -8,6 +9,8 @@ import java.io.File class ContextualDatabase: Database() { + var fileUri: Uri? = null + val iconDrawableFactory = IconDrawableFactory( retrieveBinaryCache = { binaryCache }, retrieveCustomIconBinary = { iconId -> getBinaryForCustomIcon(iconId) } @@ -23,6 +26,11 @@ class ContextualDatabase: Database() { super.clearIndexesAndBinaries(filesDirectory) } + override fun clearAndClose(filesDirectory: File?) { + super.clearAndClose(filesDirectory) + this.fileUri = null + } + companion object : SingletonHolder(::ContextualDatabase) { private val TAG = ContextualDatabase::class.java.name } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/MainCredential.kt b/app/src/main/java/com/kunzisoft/keepass/database/MainCredential.kt new file mode 100644 index 000000000..cfac31011 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/MainCredential.kt @@ -0,0 +1,100 @@ +/* + * 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 + +import android.content.ContentResolver +import android.net.Uri +import android.os.Parcel +import android.os.Parcelable +import com.kunzisoft.keepass.database.element.MasterCredential +import com.kunzisoft.keepass.hardware.HardwareKey +import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream +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 + } + + fun toMasterCredential(contentResolver: ContentResolver): MasterCredential { + return MasterCredential( + this.password, + this.keyFileUri?.let { + getKeyFileData(contentResolver, it) + }, + this.hardwareKey + ) + } + + private fun getKeyFileData(contentResolver: ContentResolver, + keyFileUri: Uri): ByteArray? { + contentResolver.getUriInputStream(keyFileUri)?.use { keyFileInputStream -> + return keyFileInputStream.readBytes() + } + return null + } + + 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 + } +} 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 3ac2af47c..ee1a4d751 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 @@ -24,7 +24,7 @@ import android.net.Uri import com.kunzisoft.keepass.app.database.CipherDatabaseAction import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.database.ContextualDatabase -import com.kunzisoft.keepass.database.element.MainCredential +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.hardware.HardwareKey open class AssignMainCredentialInDatabaseRunnable ( 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 c6bd0e2d9..229ae1412 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 @@ -24,7 +24,7 @@ import android.net.Uri import android.util.Log import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.database.ContextualDatabase -import com.kunzisoft.keepass.database.element.MainCredential +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.utils.UriUtil.getBinaryDir @@ -45,7 +45,8 @@ class CreateDatabaseRunnable( try { // Create new database record mDatabase.apply { - createData(mDatabaseUri, databaseName, rootName, templateGroupName) + this.fileUri = mDatabaseUri + createData(databaseName, rootName, templateGroupName) } } catch (e: Exception) { mDatabase.clearAndClose(context.getBinaryDir()) 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 9bc3dabf8..69eeec2c3 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 @@ -40,11 +40,11 @@ import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Companion.DATABASE_CHANGED_DIALOG_TAG import com.kunzisoft.keepass.database.ContextualDatabase +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine import com.kunzisoft.keepass.database.element.Entry import com.kunzisoft.keepass.database.element.Group -import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm import com.kunzisoft.keepass.database.element.node.Node import com.kunzisoft.keepass.database.element.node.NodeId 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 b5d691643..7d2278c99 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 @@ -24,14 +24,16 @@ import android.net.Uri import com.kunzisoft.keepass.app.database.CipherDatabaseAction import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.database.ContextualDatabase -import com.kunzisoft.keepass.database.element.MainCredential +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.database.element.binary.BinaryData import com.kunzisoft.keepass.database.exception.DatabaseInputException +import com.kunzisoft.keepass.database.exception.UnknownDatabaseLocationException import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.model.CipherEncryptDatabase import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ProgressTaskUpdater +import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream import com.kunzisoft.keepass.utils.UriUtil.getBinaryDir class LoadDatabaseRunnable( @@ -56,10 +58,13 @@ class LoadDatabaseRunnable( override fun onActionRun() { try { + val contentResolver = context.contentResolver + // Save database URI + mDatabase.fileUri = mDatabaseUri mDatabase.loadData( - context.contentResolver, - mDatabaseUri, - mMainCredential, + contentResolver.getUriInputStream(mDatabaseUri) + ?: throw UnknownDatabaseLocationException(), + mMainCredential.toMasterCredential(contentResolver), mChallengeResponseRetriever, mReadonly, binaryDir, 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 116eb2f81..723d313d1 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 @@ -22,12 +22,14 @@ package com.kunzisoft.keepass.database.action import android.content.Context import android.net.Uri import com.kunzisoft.keepass.database.ContextualDatabase -import com.kunzisoft.keepass.database.element.MainCredential +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.database.element.binary.BinaryData import com.kunzisoft.keepass.database.exception.DatabaseException +import com.kunzisoft.keepass.database.exception.UnknownDatabaseLocationException import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ProgressTaskUpdater +import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream class MergeDatabaseRunnable( context: Context, @@ -48,10 +50,12 @@ class MergeDatabaseRunnable( override fun onActionRun() { try { + val contentResolver = context.contentResolver database.mergeData( - context.contentResolver, - mDatabaseToMergeUri, - mDatabaseToMergeMainCredential, + context.contentResolver.getUriInputStream( + mDatabaseToMergeUri ?: database.fileUri + ) ?: throw UnknownDatabaseLocationException(), + mDatabaseToMergeMainCredential?.toMasterCredential(contentResolver), mDatabaseToMergeChallengeResponseRetriever, { memoryWanted -> BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt index 275a88914..6dc5b902f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt @@ -23,9 +23,11 @@ import android.content.Context import com.kunzisoft.keepass.database.ContextualDatabase import com.kunzisoft.keepass.database.element.binary.BinaryData import com.kunzisoft.keepass.database.exception.DatabaseException +import com.kunzisoft.keepass.database.exception.UnknownDatabaseLocationException import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ProgressTaskUpdater +import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream import com.kunzisoft.keepass.utils.UriUtil.getBinaryDir class ReloadDatabaseRunnable( @@ -45,7 +47,9 @@ class ReloadDatabaseRunnable( override fun onActionRun() { try { - mDatabase.reloadData(context.contentResolver, + mDatabase.reloadData( + context.contentResolver.getUriInputStream(mDatabase.fileUri) + ?: throw UnknownDatabaseLocationException(), { memoryWanted -> BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted) }, 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 0f98912a9..9341a9b33 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 @@ -22,10 +22,12 @@ package com.kunzisoft.keepass.database.action import android.content.Context import android.net.Uri import com.kunzisoft.keepass.database.ContextualDatabase -import com.kunzisoft.keepass.database.element.MainCredential +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.database.exception.DatabaseException import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.tasks.ActionRunnable +import com.kunzisoft.keepass.utils.UriHelper.getUriOutputStream +import java.io.File open class SaveDatabaseRunnable( protected var context: Context, @@ -44,11 +46,14 @@ open class SaveDatabaseRunnable( database.checkVersion() if (saveDatabase && result.isSuccess) { try { + val contentResolver = context.contentResolver + // Build temp database file to avoid file corruption if error database.saveData( - context.contentResolver, - context.cacheDir, - databaseCopyUri, - mainCredential, + cacheFile = File(context.cacheDir, databaseCopyUri.hashCode().toString()), + databaseOutputStream = context.contentResolver + .getUriOutputStream(databaseCopyUri ?: database.fileUri), + isNewLocation = databaseCopyUri == null, + mainCredential?.toMasterCredential(contentResolver), challengeResponseRetriever) } catch (e: DatabaseException) { setError(e) 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 9dda602cd..e530aad40 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt @@ -32,6 +32,7 @@ import androidx.media.app.NotificationCompat import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.GroupActivity import com.kunzisoft.keepass.database.ContextualDatabase +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.database.action.* import com.kunzisoft.keepass.database.action.history.DeleteEntryHistoryDatabaseRunnable import com.kunzisoft.keepass.database.action.history.RestoreEntryHistoryDatabaseRunnable @@ -39,7 +40,6 @@ import com.kunzisoft.keepass.database.action.node.* import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Entry import com.kunzisoft.keepass.database.element.Group -import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm import com.kunzisoft.keepass.database.element.node.Node import com.kunzisoft.keepass.database.element.node.NodeId 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 3d3810c52..c64caf7e5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -36,7 +36,7 @@ import com.kunzisoft.keepass.activities.dialogs.SetMainCredentialDialogFragment import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity import com.kunzisoft.keepass.database.ContextualDatabase -import com.kunzisoft.keepass.database.element.MainCredential +import com.kunzisoft.keepass.database.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 325e18ba1..70e65f078 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/MainCredentialView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/MainCredentialView.kt @@ -38,9 +38,9 @@ import androidx.appcompat.app.AppCompatActivity 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.database.element.MainCredential +import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.hardware.HardwareKey +import com.kunzisoft.keepass.model.CredentialStorage class MainCredentialView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, diff --git a/database/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/database/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 9622ec4aa..86b8674d3 100644 --- a/database/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/database/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -19,9 +19,7 @@ */ package com.kunzisoft.keepass.database.element -import android.content.ContentResolver import android.graphics.Color -import android.net.Uri import android.util.Log import com.kunzisoft.androidclearchroma.ChromaUtil import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm @@ -56,8 +54,6 @@ import com.kunzisoft.keepass.database.search.SearchParameters import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.utils.* -import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream -import com.kunzisoft.keepass.utils.UriHelper.getUriOutputStream import java.io.* import java.util.* @@ -68,9 +64,6 @@ open class Database { private var mDatabaseKDB: DatabaseKDB? = null private var mDatabaseKDBX: DatabaseKDBX? = null - var fileUri: Uri? = null - private set - private var mSearchHelper: SearchHelper = SearchHelper() var isReadOnly = false @@ -548,22 +541,19 @@ open class Database { } fun createData( - databaseUri: Uri, databaseName: String, rootName: String, templateGroupName: String? ) { setDatabaseKDBX(DatabaseKDBX(databaseName, rootName, templateGroupName)) - this.fileUri = databaseUri // Set Database state this.dataModifiedSinceLastLoading = false } @Throws(DatabaseInputException::class) fun loadData( - contentResolver: ContentResolver, - databaseUri: Uri, - mainCredential: MainCredential, + databaseStream: InputStream, + masterCredential: MasterCredential, challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray, readOnly: Boolean, cacheDirectory: File, @@ -571,16 +561,12 @@ open class Database { fixDuplicateUUID: Boolean, progressTaskUpdater: ProgressTaskUpdater? ) { - - // Save database URI - this.fileUri = databaseUri - // Check if the file is writable this.isReadOnly = readOnly try { // Read database stream for the first time - readDatabaseStream(contentResolver, databaseUri, + readDatabaseStream(databaseStream, { databaseInputStream -> val databaseKDB = DatabaseKDB().apply { binaryCache.cacheDirectory = cacheDirectory @@ -591,9 +577,8 @@ open class Database { progressTaskUpdater ) { databaseKDB.deriveMasterKey( - contentResolver, - mainCredential - ) + masterCredential + ) } setDatabaseKDB(databaseKDB) }, @@ -607,8 +592,7 @@ open class Database { openDatabase(databaseInputStream, progressTaskUpdater) { databaseKDBX.deriveMasterKey( - contentResolver, - mainCredential, + masterCredential, challengeResponseRetriever ) } @@ -633,9 +617,8 @@ open class Database { @Throws(DatabaseInputException::class) fun mergeData( - contentResolver: ContentResolver, - databaseToMergeUri: Uri?, - databaseToMergeMainCredential: MainCredential?, + databaseToMergeStream: InputStream, + databaseToMergeMasterCredential: MasterCredential?, databaseToMergeChallengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray, isRAMSufficient: (memoryWanted: Long) -> Boolean, progressTaskUpdater: ProgressTaskUpdater? @@ -647,71 +630,58 @@ open class Database { // New database instance to get new changes val databaseToMerge = Database() - databaseToMerge.fileUri = databaseToMergeUri ?: this.fileUri - try { - val databaseUri = databaseToMerge.fileUri - if (databaseUri != null) { - readDatabaseStream(contentResolver, databaseUri, - { databaseInputStream -> - val databaseToMergeKDB = DatabaseKDB() - DatabaseInputKDB(databaseToMergeKDB) - .openDatabase(databaseInputStream, progressTaskUpdater) { - if (databaseToMergeMainCredential != null) { - databaseToMergeKDB.deriveMasterKey( - contentResolver, - databaseToMergeMainCredential - ) - } else { - this@Database.mDatabaseKDB?.let { thisDatabaseKDB -> - databaseToMergeKDB.copyMasterKeyFrom(thisDatabaseKDB) - } - } - } - databaseToMerge.setDatabaseKDB(databaseToMergeKDB) - }, - { databaseInputStream -> - val databaseToMergeKDBX = DatabaseKDBX() - DatabaseInputKDBX(databaseToMergeKDBX).apply { - setMethodToCheckIfRAMIsSufficient(isRAMSufficient) - openDatabase(databaseInputStream, progressTaskUpdater) { - if (databaseToMergeMainCredential != null) { - databaseToMergeKDBX.deriveMasterKey( - contentResolver, - databaseToMergeMainCredential, - databaseToMergeChallengeResponseRetriever - ) - } else { - this@Database.mDatabaseKDBX?.let { thisDatabaseKDBX -> - databaseToMergeKDBX.copyMasterKeyFrom(thisDatabaseKDBX) - } + readDatabaseStream(databaseToMergeStream, + { databaseInputStream -> + val databaseToMergeKDB = DatabaseKDB() + DatabaseInputKDB(databaseToMergeKDB) + .openDatabase(databaseInputStream, progressTaskUpdater) { + if (databaseToMergeMasterCredential != null) { + databaseToMergeKDB.deriveMasterKey( + databaseToMergeMasterCredential + ) + } else { + this@Database.mDatabaseKDB?.let { thisDatabaseKDB -> + databaseToMergeKDB.copyMasterKeyFrom(thisDatabaseKDB) } } } - databaseToMerge.setDatabaseKDBX(databaseToMergeKDBX) - } - ) - loaded = true - - mDatabaseKDBX?.let { currentDatabaseKDBX -> - val databaseMerger = DatabaseKDBXMerger(currentDatabaseKDBX).apply { - this.isRAMSufficient = isRAMSufficient - } - databaseToMerge.mDatabaseKDB?.let { databaseKDBToMerge -> - databaseMerger.merge(databaseKDBToMerge) - if (databaseToMergeUri != null) { - this.dataModifiedSinceLastLoading = true - } - } - databaseToMerge.mDatabaseKDBX?.let { databaseKDBXToMerge -> - databaseMerger.merge(databaseKDBXToMerge) - if (databaseToMergeUri != null) { - this.dataModifiedSinceLastLoading = true + databaseToMerge.setDatabaseKDB(databaseToMergeKDB) + }, + { databaseInputStream -> + val databaseToMergeKDBX = DatabaseKDBX() + DatabaseInputKDBX(databaseToMergeKDBX).apply { + setMethodToCheckIfRAMIsSufficient(isRAMSufficient) + openDatabase(databaseInputStream, progressTaskUpdater) { + if (databaseToMergeMasterCredential != null) { + databaseToMergeKDBX.deriveMasterKey( + databaseToMergeMasterCredential, + databaseToMergeChallengeResponseRetriever + ) + } else { + this@Database.mDatabaseKDBX?.let { thisDatabaseKDBX -> + databaseToMergeKDBX.copyMasterKeyFrom(thisDatabaseKDBX) + } + } } } + databaseToMerge.setDatabaseKDBX(databaseToMergeKDBX) + } + ) + loaded = true + + mDatabaseKDBX?.let { currentDatabaseKDBX -> + val databaseMerger = DatabaseKDBXMerger(currentDatabaseKDBX).apply { + this.isRAMSufficient = isRAMSufficient + } + databaseToMerge.mDatabaseKDB?.let { databaseKDBToMerge -> + databaseMerger.merge(databaseKDBToMerge) + this.dataModifiedSinceLastLoading = true + } + databaseToMerge.mDatabaseKDBX?.let { databaseKDBXToMerge -> + databaseMerger.merge(databaseKDBXToMerge) + this.dataModifiedSinceLastLoading = true } - } else { - throw UnknownDatabaseLocationException() } } catch (e: Exception) { Log.e(TAG, "Unable to merge the database") @@ -725,49 +695,43 @@ open class Database { @Throws(DatabaseInputException::class) fun reloadData( - contentResolver: ContentResolver, + databaseStream: InputStream, isRAMSufficient: (memoryWanted: Long) -> Boolean, progressTaskUpdater: ProgressTaskUpdater? ) { - - // Retrieve the stream from the old database URI try { - val oldDatabaseUri = fileUri - if (oldDatabaseUri != null) { - readDatabaseStream(contentResolver, oldDatabaseUri, - { databaseInputStream -> - val databaseKDB = DatabaseKDB() - mDatabaseKDB?.let { - databaseKDB.binaryCache = it.binaryCache - } - DatabaseInputKDB(databaseKDB) - .openDatabase(databaseInputStream, progressTaskUpdater) { - this@Database.mDatabaseKDB?.let { thisDatabaseKDB -> - databaseKDB.copyMasterKeyFrom(thisDatabaseKDB) - } - } - setDatabaseKDB(databaseKDB) - }, - { databaseInputStream -> - val databaseKDBX = DatabaseKDBX() - mDatabaseKDBX?.let { - databaseKDBX.binaryCache = it.binaryCache - } - DatabaseInputKDBX(databaseKDBX).apply { - setMethodToCheckIfRAMIsSufficient(isRAMSufficient) - openDatabase(databaseInputStream, progressTaskUpdater) { - this@Database.mDatabaseKDBX?.let { thisDatabaseKDBX -> - databaseKDBX.copyMasterKeyFrom(thisDatabaseKDBX) - } - } - } - setDatabaseKDBX(databaseKDBX) + // Retrieve the stream from the old database + readDatabaseStream(databaseStream, + { databaseInputStream -> + val databaseKDB = DatabaseKDB() + mDatabaseKDB?.let { + databaseKDB.binaryCache = it.binaryCache } - ) - loaded = true - } else { - throw UnknownDatabaseLocationException() - } + DatabaseInputKDB(databaseKDB) + .openDatabase(databaseInputStream, progressTaskUpdater) { + this@Database.mDatabaseKDB?.let { thisDatabaseKDB -> + databaseKDB.copyMasterKeyFrom(thisDatabaseKDB) + } + } + setDatabaseKDB(databaseKDB) + }, + { databaseInputStream -> + val databaseKDBX = DatabaseKDBX() + mDatabaseKDBX?.let { + databaseKDBX.binaryCache = it.binaryCache + } + DatabaseInputKDBX(databaseKDBX).apply { + setMethodToCheckIfRAMIsSufficient(isRAMSufficient) + openDatabase(databaseInputStream, progressTaskUpdater) { + this@Database.mDatabaseKDBX?.let { thisDatabaseKDBX -> + databaseKDBX.copyMasterKeyFrom(thisDatabaseKDBX) + } + } + } + setDatabaseKDBX(databaseKDBX) + } + ) + loaded = true } catch (e: Exception) { Log.e(TAG, "Unable to reload the database") if (e is DatabaseException) @@ -780,16 +744,12 @@ open class Database { @Throws(Exception::class) private fun readDatabaseStream( - contentResolver: ContentResolver, - databaseUri: Uri, + databaseStream: InputStream, openDatabaseKDB: (InputStream) -> Unit, openDatabaseKDBX: (InputStream) -> Unit ) { try { - // Load Data, pass Uris as InputStreams - val databaseStream = contentResolver.getUriInputStream(databaseUri) - ?: throw UnknownDatabaseLocationException() - + // Load Data by InputStream BufferedInputStream(databaseStream).use { databaseInputStream -> // We'll end up reading 8 bytes to identify the header. Might as well use two extra. @@ -822,63 +782,54 @@ open class Database { @Throws(DatabaseOutputException::class) fun saveData( - contentResolver: ContentResolver, - cacheDir: File, - databaseCopyUri: Uri?, - mainCredential: MainCredential?, + cacheFile: File, + databaseOutputStream: OutputStream?, + isNewLocation: Boolean, + masterCredential: MasterCredential?, challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray ) { - val saveUri = databaseCopyUri ?: this.fileUri - // Build temp database file to avoid file corruption if error - val cacheFile = File(cacheDir, saveUri.hashCode().toString()) try { - if (saveUri != null) { - // Save in a temp memory to avoid exception - cacheFile.outputStream().use { outputStream -> - mDatabaseKDB?.let { databaseKDB -> - DatabaseOutputKDB(databaseKDB).apply { - writeDatabase(outputStream) { - if (mainCredential != null) { - databaseKDB.deriveMasterKey( - contentResolver, - mainCredential - ) - } else { - // No master key change - } - } - } - } - ?: mDatabaseKDBX?.let { databaseKDBX -> - DatabaseOutputKDBX(databaseKDBX).apply { - writeDatabase(outputStream) { - if (mainCredential != null) { - // Build new master key from MainCredential - databaseKDBX.deriveMasterKey( - contentResolver, - mainCredential, - challengeResponseRetriever - ) - } else { - // Reuse composite key parts - databaseKDBX.deriveCompositeKey( - challengeResponseRetriever - ) - } + // Save in a temp memory to avoid exception + cacheFile.outputStream().use { outputStream -> + mDatabaseKDB?.let { databaseKDB -> + DatabaseOutputKDB(databaseKDB).apply { + writeDatabase(outputStream) { + if (masterCredential != null) { + databaseKDB.deriveMasterKey( + masterCredential + ) + } else { + // No master key change } } } } - // Copy from the cache to the final stream - contentResolver.getUriOutputStream(saveUri)?.use { outputStream -> - cacheFile.inputStream().use { inputStream -> - inputStream.readAllBytes { buffer -> - outputStream.write(buffer) + ?: mDatabaseKDBX?.let { databaseKDBX -> + DatabaseOutputKDBX(databaseKDBX).apply { + writeDatabase(outputStream) { + if (masterCredential != null) { + // Build new master key from MainCredential + databaseKDBX.deriveMasterKey( + masterCredential, + challengeResponseRetriever + ) + } else { + // Reuse composite key parts + databaseKDBX.deriveCompositeKey( + challengeResponseRetriever + ) + } } } } - } else { - throw UnknownDatabaseLocationException() + } + // Copy from the cache to the final stream + databaseOutputStream?.use { outputStream -> + cacheFile.inputStream().use { inputStream -> + inputStream.readAllBytes { buffer -> + outputStream.write(buffer) + } + } } } catch (e: Exception) { Log.e(TAG, "Unable to save database", e) @@ -892,7 +843,7 @@ open class Database { } catch (e: Exception) { Log.e(TAG, "Cache file $cacheFile cannot be deleted", e) } - if (databaseCopyUri == null) { + if (isNewLocation) { this.dataModifiedSinceLastLoading = false } } @@ -1010,11 +961,10 @@ open class Database { } } - fun clearAndClose(filesDirectory: File? = null) { + open fun clearAndClose(filesDirectory: File? = null) { clearIndexesAndBinaries(filesDirectory) this.mDatabaseKDB = null this.mDatabaseKDBX = null - this.fileUri = null this.loaded = false } @@ -1029,11 +979,11 @@ open class Database { } } - fun validatePasswordEncoding(mainCredential: MainCredential): Boolean { - val password = mainCredential.password - val containsKeyFile = mainCredential.keyFileUri != null - return mDatabaseKDB?.validatePasswordEncoding(password, containsKeyFile) - ?: mDatabaseKDBX?.validatePasswordEncoding(password, containsKeyFile) + fun isValidCredential(masterCredential: MasterCredential): Boolean { + val password = masterCredential.password + val containsKeyFile = masterCredential.keyFileData != null + return mDatabaseKDB?.isValidCredential(password, containsKeyFile) + ?: mDatabaseKDBX?.isValidCredential(password, containsKeyFile) ?: false } diff --git a/database/src/main/java/com/kunzisoft/keepass/database/element/MainCredential.kt b/database/src/main/java/com/kunzisoft/keepass/database/element/MasterCredential.kt similarity index 84% rename from database/src/main/java/com/kunzisoft/keepass/database/element/MainCredential.kt rename to database/src/main/java/com/kunzisoft/keepass/database/element/MasterCredential.kt index 296c3cc70..2d0eb960f 100644 --- a/database/src/main/java/com/kunzisoft/keepass/database/element/MainCredential.kt +++ b/database/src/main/java/com/kunzisoft/keepass/database/element/MasterCredential.kt @@ -18,8 +18,6 @@ */ 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 @@ -29,8 +27,9 @@ 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.UriHelper.getUriInputStream +import com.kunzisoft.keepass.utils.readByteArrayCompat import com.kunzisoft.keepass.utils.readEnum +import com.kunzisoft.keepass.utils.writeByteArrayCompat import com.kunzisoft.keepass.utils.writeEnum import org.apache.commons.codec.binary.Hex import org.w3c.dom.Node @@ -43,19 +42,19 @@ 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 { +data class MasterCredential(var password: String? = null, + var keyFileData: ByteArray? = null, + var hardwareKey: HardwareKey? = null): Parcelable { constructor(parcel: Parcel) : this() { password = parcel.readString() - keyFileUri = parcel.readParcelable(Uri::class.java.classLoader) + keyFileData = parcel.readByteArrayCompat() hardwareKey = parcel.readEnum() } override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(password) - parcel.writeParcelable(keyFileUri, flags) + parcel.writeByteArrayCompat(keyFileData) parcel.writeEnum(hardwareKey) } @@ -67,10 +66,10 @@ data class MainCredential(var password: String? = null, if (this === other) return true if (javaClass != other?.javaClass) return false - other as MainCredential + other as MasterCredential if (password != other.password) return false - if (keyFileUri != other.keyFileUri) return false + if (!keyFileData.contentEquals(other.keyFileData)) return false if (hardwareKey != other.hardwareKey) return false return true @@ -78,21 +77,21 @@ data class MainCredential(var password: String? = null, override fun hashCode(): Int { var result = password?.hashCode() ?: 0 - result = 31 * result + (keyFileUri?.hashCode() ?: 0) + result = 31 * result + (keyFileData?.hashCode() ?: 0) result = 31 * result + (hardwareKey?.hashCode() ?: 0) return result } - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): MainCredential { - return MainCredential(parcel) + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): MasterCredential { + return MasterCredential(parcel) } - override fun newArray(size: Int): Array { + override fun newArray(size: Int): Array { return arrayOfNulls(size) } - private val TAG = MainCredential::class.java.simpleName + private val TAG = MasterCredential::class.java.simpleName @Throws(IOException::class) fun retrievePasswordKey(key: String, @@ -107,17 +106,14 @@ data class MainCredential(var password: String? = null, } @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") + fun retrieveKeyFileDecodedKey( + keyFileData: ByteArray, + allowXML: Boolean + ): ByteArray { try { // Check XML key file val xmlKeyByteArray = if (allowXML) - loadXmlKeyFile(ByteArrayInputStream(keyData)) + loadXmlKeyFile(ByteArrayInputStream(keyFileData)) else null if (xmlKeyByteArray != null) { @@ -125,16 +121,16 @@ data class MainCredential(var password: String? = null, } // Check 32 bytes key file - when (keyData.size) { - 32 -> return keyData + when (keyFileData.size) { + 32 -> return keyFileData 64 -> try { - return Hex.decodeHex(String(keyData).toCharArray()) + return Hex.decodeHex(String(keyFileData).toCharArray()) } catch (ignoredException: Exception) { // Key is not base 64, treat it as binary data } } // Hash file as binary data - return HashManager.hashSha256(keyData) + return HashManager.hashSha256(keyFileData) } catch (e: Exception) { throw IOException("Unable to load the keyfile.", e) } @@ -145,15 +141,6 @@ data class MainCredential(var password: String? = null, return HashManager.hashSha256(keyData) } - @Throws(Exception::class) - private fun getKeyFileData(contentResolver: ContentResolver, - keyFileUri: Uri): ByteArray? { - contentResolver.getUriInputStream(keyFileUri)?.use { keyFileInputStream -> - return keyFileInputStream.readBytes() - } - return null - } - private fun loadXmlKeyFile(keyInputStream: InputStream): ByteArray? { try { val documentBuilderFactory = DocumentBuilderFactory.newInstance() diff --git a/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt b/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt index b98826cb3..2ff7508a3 100644 --- a/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt +++ b/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt @@ -19,13 +19,12 @@ package com.kunzisoft.keepass.database.element.database -import android.content.ContentResolver import com.kunzisoft.encrypt.HashManager 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.MainCredential +import com.kunzisoft.keepass.database.element.MasterCredential import com.kunzisoft.keepass.database.element.binary.BinaryData import com.kunzisoft.keepass.database.element.entry.EntryKDB import com.kunzisoft.keepass.database.element.group.GroupKDB @@ -129,25 +128,23 @@ class DatabaseKDB : DatabaseVersioned() { } fun deriveMasterKey( - contentResolver: ContentResolver, - mainCredential: MainCredential + masterCredential: MasterCredential ) { // Exception when no password - if (mainCredential.hardwareKey != null) + if (masterCredential.hardwareKey != null) throw HardwareKeyDatabaseException() - if (mainCredential.password == null && mainCredential.keyFileUri == null) + if (masterCredential.password == null && masterCredential.keyFileData == null) throw EmptyKeyDatabaseException() // Retrieve plain data - val password = mainCredential.password - val keyFileUri = mainCredential.keyFileUri - val passwordBytes = if (password != null) MainCredential.retrievePasswordKey( + val password = masterCredential.password + val keyFileData = masterCredential.keyFileData + val passwordBytes = if (password != null) MasterCredential.retrievePasswordKey( password, passwordEncoding ) else null - val keyFileBytes = if (keyFileUri != null) MainCredential.retrieveFileKey( - contentResolver, - keyFileUri, + val keyFileBytes = if (keyFileData != null) MasterCredential.retrieveKeyFileDecodedKey( + keyFileData, false ) else null diff --git a/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt b/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt index dba4b93f2..6a372b661 100644 --- a/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt +++ b/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt @@ -19,7 +19,6 @@ */ package com.kunzisoft.keepass.database.element.database -import android.content.ContentResolver import android.util.Base64 import android.util.Log import com.kunzisoft.encrypt.HashManager @@ -226,24 +225,22 @@ class DatabaseKDBX : DatabaseVersioned { } fun deriveMasterKey( - contentResolver: ContentResolver, - mainCredential: MainCredential, + masterCredential: MasterCredential, challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray, ) { // Retrieve each plain credential - val password = mainCredential.password - val keyFileUri = mainCredential.keyFileUri - val hardwareKey = mainCredential.hardwareKey - val passwordBytes = if (password != null) MainCredential.retrievePasswordKey( + val password = masterCredential.password + val keyFileData = masterCredential.keyFileData + val hardwareKey = masterCredential.hardwareKey + val passwordBytes = if (password != null) MasterCredential.retrievePasswordKey( password, passwordEncoding ) else null - val keyFileBytes = if (keyFileUri != null) MainCredential.retrieveFileKey( - contentResolver, - keyFileUri, + val keyFileBytes = if (keyFileData != null) MasterCredential.retrieveKeyFileDecodedKey( + keyFileData, true ) else null - val hardwareKeyBytes = if (hardwareKey != null) MainCredential.retrieveHardwareKey( + val hardwareKeyBytes = if (hardwareKey != null) MasterCredential.retrieveHardwareKey( challengeResponseRetriever.invoke(hardwareKey, transformSeed) ) else null @@ -272,7 +269,7 @@ class DatabaseKDBX : DatabaseVersioned { keyFileBytes ) } else { - val hardwareKeyBytes = MainCredential.retrieveHardwareKey( + val hardwareKeyBytes = MasterCredential.retrieveHardwareKey( challengeResponseRetriever.invoke(hardwareKey, transformSeed) ) this.masterKey = composedKeyToMasterKey( @@ -873,10 +870,10 @@ class DatabaseKDBX : DatabaseVersioned { } } - override fun validatePasswordEncoding(password: String?, containsKeyFile: Boolean): Boolean { + override fun isValidCredential(password: String?, containsKeyFile: Boolean): Boolean { if (password == null) return true - return super.validatePasswordEncoding(password, containsKeyFile) + return super.isValidCredential(password, containsKeyFile) } override fun clearIndexes() { diff --git a/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt b/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt index a17b436b8..a1b2dc4f7 100644 --- a/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt +++ b/database/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt @@ -93,7 +93,7 @@ abstract class DatabaseVersioned< return null } - open fun validatePasswordEncoding(password: String?, containsKeyFile: Boolean): Boolean { + open fun isValidCredential(password: String?, containsKeyFile: Boolean): Boolean { if (password == null && !containsKeyFile) return false