Setting to remember hardware key

This commit is contained in:
J-Jamet
2022-05-18 16:39:35 +02:00
parent f4d5bd1bea
commit 259c8a4bd9
18 changed files with 319 additions and 80 deletions

View File

@@ -0,0 +1,90 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "f8fb4aed546de19ae7ca0797f49b26a4",
"entities": [
{
"tableName": "file_database_history",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`database_uri` TEXT NOT NULL, `database_alias` TEXT NOT NULL, `keyfile_uri` TEXT, `hardware_key` TEXT, `updated` INTEGER NOT NULL, PRIMARY KEY(`database_uri`))",
"fields": [
{
"fieldPath": "databaseUri",
"columnName": "database_uri",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "databaseAlias",
"columnName": "database_alias",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "keyFileUri",
"columnName": "keyfile_uri",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "hardwareKey",
"columnName": "hardware_key",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "updated",
"columnName": "updated",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"database_uri"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "cipher_database",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`database_uri` TEXT NOT NULL, `encrypted_value` TEXT NOT NULL, `specs_parameters` TEXT NOT NULL, PRIMARY KEY(`database_uri`))",
"fields": [
{
"fieldPath": "databaseUri",
"columnName": "database_uri",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "encryptedValue",
"columnName": "encrypted_value",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "specParameters",
"columnName": "specs_parameters",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"database_uri"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f8fb4aed546de19ae7ca0797f49b26a4')"
]
}
}

View File

@@ -56,6 +56,7 @@ import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.RegisterInfo import com.kunzisoft.keepass.model.RegisterInfo
import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
@@ -155,8 +156,9 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
mAdapterDatabaseHistory?.setOnFileDatabaseHistoryOpenListener { fileDatabaseHistoryEntityToOpen -> mAdapterDatabaseHistory?.setOnFileDatabaseHistoryOpenListener { fileDatabaseHistoryEntityToOpen ->
fileDatabaseHistoryEntityToOpen.databaseUri?.let { databaseFileUri -> fileDatabaseHistoryEntityToOpen.databaseUri?.let { databaseFileUri ->
launchPasswordActivity( launchPasswordActivity(
databaseFileUri, databaseFileUri,
fileDatabaseHistoryEntityToOpen.keyFileUri fileDatabaseHistoryEntityToOpen.keyFileUri,
fileDatabaseHistoryEntityToOpen.hardwareKey
) )
} }
} }
@@ -250,7 +252,8 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
?: MainCredential() ?: MainCredential()
databaseFilesViewModel.addDatabaseFile( databaseFilesViewModel.addDatabaseFile(
databaseUri, databaseUri,
mainCredential.keyFileUri mainCredential.keyFileUri,
mainCredential.hardwareKey
) )
} }
} }
@@ -297,10 +300,11 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
Snackbar.make(coordinatorLayout, error, Snackbar.LENGTH_LONG).asError().show() Snackbar.make(coordinatorLayout, error, Snackbar.LENGTH_LONG).asError().show()
} }
private fun launchPasswordActivity(databaseUri: Uri, keyFile: Uri?) { private fun launchPasswordActivity(databaseUri: Uri, keyFile: Uri?, hardwareKey: HardwareKey?) {
MainCredentialActivity.launch(this, MainCredentialActivity.launch(this,
databaseUri, databaseUri,
keyFile, keyFile,
hardwareKey,
{ exception -> { exception ->
fileNoFoundAction(exception) fileNoFoundAction(exception)
}, },
@@ -321,7 +325,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
} }
private fun launchPasswordActivityWithPath(databaseUri: Uri) { private fun launchPasswordActivityWithPath(databaseUri: Uri) {
launchPasswordActivity(databaseUri, null) launchPasswordActivity(databaseUri, null, null)
// Delete flickering for kitkat <= // Delete flickering for kitkat <=
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
overridePendingTransition(0, 0) overridePendingTransition(0, 0)

View File

@@ -60,6 +60,7 @@ import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
import com.kunzisoft.keepass.database.exception.FileNotFoundDatabaseException import com.kunzisoft.keepass.database.exception.FileNotFoundDatabaseException
import com.kunzisoft.keepass.education.PasswordActivityEducation import com.kunzisoft.keepass.education.PasswordActivityEducation
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.* import com.kunzisoft.keepass.model.*
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.CIPHER_DATABASE_KEY import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.CIPHER_DATABASE_KEY
@@ -102,6 +103,8 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
private var mRememberKeyFile: Boolean = false private var mRememberKeyFile: Boolean = false
private var mExternalFileHelper: ExternalFileHelper? = null private var mExternalFileHelper: ExternalFileHelper? = null
private var mRememberHardwareKey: Boolean = false
private var mReadOnly: Boolean = false private var mReadOnly: Boolean = false
private var mForceReadOnly: Boolean = false private var mForceReadOnly: Boolean = false
@@ -134,6 +137,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
PreferencesUtil.enableReadOnlyDatabase(this) PreferencesUtil.enableReadOnlyDatabase(this)
} }
mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this) mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this)
mRememberHardwareKey = PreferencesUtil.rememberHardwareKey(this)
// Build elements to manage keyfile selection // Build elements to manage keyfile selection
mExternalFileHelper = ExternalFileHelper(this) mExternalFileHelper = ExternalFileHelper(this)
@@ -216,10 +220,19 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
databaseKeyFileUri databaseKeyFileUri
} }
val databaseHardwareKey = mainCredentialView?.getMainCredential()?.hardwareKey
val hardwareKey =
if (mRememberHardwareKey
&& databaseHardwareKey == null) {
databaseFile?.hardwareKey
} else {
databaseHardwareKey
}
// Define title // Define title
filenameView?.text = databaseFile?.databaseAlias ?: "" filenameView?.text = databaseFile?.databaseAlias ?: ""
onDatabaseFileLoaded(databaseFile?.databaseUri, keyFileUri) onDatabaseFileLoaded(databaseFile?.databaseUri, keyFileUri, hardwareKey)
} }
} }
@@ -227,6 +240,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
super.onResume() super.onResume()
mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this@MainCredentialActivity) mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this@MainCredentialActivity)
mRememberHardwareKey = PreferencesUtil.rememberHardwareKey(this@MainCredentialActivity)
// Back to previous keyboard is setting activated // Back to previous keyboard is setting activated
if (PreferencesUtil.isKeyboardPreviousDatabaseCredentialsEnable(this@MainCredentialActivity)) { if (PreferencesUtil.isKeyboardPreviousDatabaseCredentialsEnable(this@MainCredentialActivity)) {
@@ -344,24 +358,36 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
private fun getUriFromIntent(intent: Intent?) { private fun getUriFromIntent(intent: Intent?) {
// If is a view intent // If is a view intent
val action = intent?.action val action = intent?.action
if (action != null if (action == VIEW_INTENT) {
&& action == VIEW_INTENT) { fillCredentials(
mDatabaseFileUri = intent.data intent.data,
mainCredentialView?.populateKeyFileView(UriUtil.getUriFromIntent(intent, KEY_KEYFILE)) UriUtil.getUriFromIntent(intent, KEY_KEYFILE),
HardwareKey.getHardwareKeyFromString(intent.getStringExtra(KEY_HARDWARE_KEY))
)
} else { } else {
mDatabaseFileUri = intent?.getParcelableExtra(KEY_FILENAME) fillCredentials(
intent?.getParcelableExtra<Uri?>(KEY_KEYFILE)?.let { intent?.getParcelableExtra(KEY_FILENAME),
mainCredentialView?.populateKeyFileView(it) intent?.getParcelableExtra(KEY_KEYFILE),
} HardwareKey.getHardwareKeyFromString(intent?.getStringExtra(KEY_HARDWARE_KEY))
)
} }
try { try {
intent?.removeExtra(KEY_KEYFILE) intent?.removeExtra(KEY_KEYFILE)
intent?.removeExtra(KEY_HARDWARE_KEY)
} catch (e: Exception) {} } catch (e: Exception) {}
mDatabaseFileUri?.let { mDatabaseFileUri?.let {
mDatabaseFileViewModel.checkIfIsDefaultDatabase(it) mDatabaseFileViewModel.checkIfIsDefaultDatabase(it)
} }
} }
private fun fillCredentials(databaseUri: Uri?,
keyFileUri: Uri?,
hardwareKey: HardwareKey?) {
mDatabaseFileUri = databaseUri
mainCredentialView?.populateKeyFileView(keyFileUri)
mainCredentialView?.populateHardwareKeyView(hardwareKey)
}
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
getUriFromIntent(intent) getUriFromIntent(intent)
@@ -370,7 +396,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
private fun launchGroupActivityIfLoaded(database: Database) { private fun launchGroupActivityIfLoaded(database: Database) {
// Check if database really loaded // Check if database really loaded
if (database.loaded) { if (database.loaded) {
clearCredentialsViews(true) clearCredentialsViews(clearKeyFile = true, clearHardwareKey = true)
GroupActivity.launch(this, GroupActivity.launch(this,
database, database,
{ onValidateSpecialMode() }, { onValidateSpecialMode() },
@@ -435,12 +461,19 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
) )
} }
private fun onDatabaseFileLoaded(databaseFileUri: Uri?, keyFileUri: Uri?) { private fun onDatabaseFileLoaded(databaseFileUri: Uri?,
keyFileUri: Uri?,
hardwareKey: HardwareKey?) {
// Define Key File text // Define Key File text
if (mRememberKeyFile) { if (mRememberKeyFile) {
mainCredentialView?.populateKeyFileView(keyFileUri) mainCredentialView?.populateKeyFileView(keyFileUri)
} }
// Define hardware key
if (mRememberHardwareKey) {
mainCredentialView?.populateHardwareKeyView(hardwareKey)
}
// Define listener for validate button // Define listener for validate button
confirmButtonView?.setOnClickListener { confirmButtonView?.setOnClickListener {
mainCredentialView?.validateCredential() mainCredentialView?.validateCredential()
@@ -476,11 +509,15 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
} }
} }
private fun clearCredentialsViews(clearKeyFile: Boolean = !mRememberKeyFile) { private fun clearCredentialsViews(clearKeyFile: Boolean = !mRememberKeyFile,
clearHardwareKey: Boolean = !mRememberHardwareKey) {
mainCredentialView?.populatePasswordTextView(null) mainCredentialView?.populatePasswordTextView(null)
if (clearKeyFile) { if (clearKeyFile) {
mainCredentialView?.populateKeyFileView(null) mainCredentialView?.populateKeyFileView(null)
} }
if (clearHardwareKey) {
mainCredentialView?.populateHardwareKeyView(null)
}
} }
override fun onPause() { override fun onPause() {
@@ -670,18 +707,24 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
private const val KEY_FILENAME = "fileName" private const val KEY_FILENAME = "fileName"
private const val KEY_KEYFILE = "keyFile" private const val KEY_KEYFILE = "keyFile"
private const val KEY_HARDWARE_KEY = "hardwareKey"
private const val VIEW_INTENT = "android.intent.action.VIEW" private const val VIEW_INTENT = "android.intent.action.VIEW"
private const val KEY_READ_ONLY = "KEY_READ_ONLY" private const val KEY_READ_ONLY = "KEY_READ_ONLY"
private const val KEY_PASSWORD = "password" private const val KEY_PASSWORD = "password"
private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately" private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately"
private fun buildAndLaunchIntent(activity: Activity, databaseFile: Uri, keyFile: Uri?, private fun buildAndLaunchIntent(activity: Activity,
databaseFile: Uri,
keyFile: Uri?,
hardwareKey: HardwareKey?,
intentBuildLauncher: (Intent) -> Unit) { intentBuildLauncher: (Intent) -> Unit) {
val intent = Intent(activity, MainCredentialActivity::class.java) val intent = Intent(activity, MainCredentialActivity::class.java)
intent.putExtra(KEY_FILENAME, databaseFile) intent.putExtra(KEY_FILENAME, databaseFile)
if (keyFile != null) if (keyFile != null)
intent.putExtra(KEY_KEYFILE, keyFile) intent.putExtra(KEY_KEYFILE, keyFile)
if (hardwareKey != null)
intent.putExtra(KEY_HARDWARE_KEY, hardwareKey.toString())
intentBuildLauncher.invoke(intent) intentBuildLauncher.invoke(intent)
} }
@@ -694,8 +737,9 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
@Throws(FileNotFoundException::class) @Throws(FileNotFoundException::class)
fun launch(activity: Activity, fun launch(activity: Activity,
databaseFile: Uri, databaseFile: Uri,
keyFile: Uri?) { keyFile: Uri?,
buildAndLaunchIntent(activity, databaseFile, keyFile) { intent -> hardwareKey: HardwareKey?) {
buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
activity.startActivity(intent) activity.startActivity(intent)
} }
} }
@@ -710,8 +754,9 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launchForSearchResult(activity: Activity, fun launchForSearchResult(activity: Activity,
databaseFile: Uri, databaseFile: Uri,
keyFile: Uri?, keyFile: Uri?,
hardwareKey: HardwareKey?,
searchInfo: SearchInfo) { searchInfo: SearchInfo) {
buildAndLaunchIntent(activity, databaseFile, keyFile) { intent -> buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
EntrySelectionHelper.startActivityForSearchModeResult( EntrySelectionHelper.startActivityForSearchModeResult(
activity, activity,
intent, intent,
@@ -729,8 +774,9 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launchForSaveResult(activity: Activity, fun launchForSaveResult(activity: Activity,
databaseFile: Uri, databaseFile: Uri,
keyFile: Uri?, keyFile: Uri?,
hardwareKey: HardwareKey?,
searchInfo: SearchInfo) { searchInfo: SearchInfo) {
buildAndLaunchIntent(activity, databaseFile, keyFile) { intent -> buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
EntrySelectionHelper.startActivityForSaveModeResult( EntrySelectionHelper.startActivityForSaveModeResult(
activity, activity,
intent, intent,
@@ -748,8 +794,9 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launchForKeyboardResult(activity: Activity, fun launchForKeyboardResult(activity: Activity,
databaseFile: Uri, databaseFile: Uri,
keyFile: Uri?, keyFile: Uri?,
hardwareKey: HardwareKey?,
searchInfo: SearchInfo?) { searchInfo: SearchInfo?) {
buildAndLaunchIntent(activity, databaseFile, keyFile) { intent -> buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
EntrySelectionHelper.startActivityForKeyboardSelectionModeResult( EntrySelectionHelper.startActivityForKeyboardSelectionModeResult(
activity, activity,
intent, intent,
@@ -768,10 +815,11 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launchForAutofillResult(activity: AppCompatActivity, fun launchForAutofillResult(activity: AppCompatActivity,
databaseFile: Uri, databaseFile: Uri,
keyFile: Uri?, keyFile: Uri?,
hardwareKey: HardwareKey?,
activityResultLauncher: ActivityResultLauncher<Intent>?, activityResultLauncher: ActivityResultLauncher<Intent>?,
autofillComponent: AutofillComponent, autofillComponent: AutofillComponent,
searchInfo: SearchInfo?) { searchInfo: SearchInfo?) {
buildAndLaunchIntent(activity, databaseFile, keyFile) { intent -> buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
AutofillHelper.startActivityForAutofillResult( AutofillHelper.startActivityForAutofillResult(
activity, activity,
intent, intent,
@@ -789,8 +837,9 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launchForRegistration(activity: Activity, fun launchForRegistration(activity: Activity,
databaseFile: Uri, databaseFile: Uri,
keyFile: Uri?, keyFile: Uri?,
hardwareKey: HardwareKey?,
registerInfo: RegisterInfo?) { registerInfo: RegisterInfo?) {
buildAndLaunchIntent(activity, databaseFile, keyFile) { intent -> buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
EntrySelectionHelper.startActivityForRegistrationModeResult( EntrySelectionHelper.startActivityForRegistrationModeResult(
activity, activity,
intent, intent,
@@ -806,6 +855,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launch(activity: AppCompatActivity, fun launch(activity: AppCompatActivity,
databaseUri: Uri, databaseUri: Uri,
keyFile: Uri?, keyFile: Uri?,
hardwareKey: HardwareKey?,
fileNoFoundAction: (exception: FileNotFoundException) -> Unit, fileNoFoundAction: (exception: FileNotFoundException) -> Unit,
onCancelSpecialMode: () -> Unit, onCancelSpecialMode: () -> Unit,
onLaunchActivitySpecialMode: () -> Unit, onLaunchActivitySpecialMode: () -> Unit,
@@ -814,43 +864,67 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
try { try {
EntrySelectionHelper.doSpecialAction(activity.intent, EntrySelectionHelper.doSpecialAction(activity.intent,
{ {
MainCredentialActivity.launch(activity, launch(
databaseUri, keyFile) activity,
databaseUri,
keyFile,
hardwareKey
)
}, },
{ searchInfo -> // Search Action { searchInfo -> // Search Action
MainCredentialActivity.launchForSearchResult(activity, launchForSearchResult(
databaseUri, keyFile, activity,
searchInfo) databaseUri,
keyFile,
hardwareKey,
searchInfo
)
onLaunchActivitySpecialMode() onLaunchActivitySpecialMode()
}, },
{ searchInfo -> // Save Action { searchInfo -> // Save Action
MainCredentialActivity.launchForSaveResult(activity, launchForSaveResult(
databaseUri, keyFile, activity,
searchInfo) databaseUri,
keyFile,
hardwareKey,
searchInfo
)
onLaunchActivitySpecialMode() onLaunchActivitySpecialMode()
}, },
{ searchInfo -> // Keyboard Selection Action { searchInfo -> // Keyboard Selection Action
MainCredentialActivity.launchForKeyboardResult(activity, launchForKeyboardResult(
databaseUri, keyFile, activity,
searchInfo) databaseUri,
keyFile,
hardwareKey,
searchInfo
)
onLaunchActivitySpecialMode() onLaunchActivitySpecialMode()
}, },
{ searchInfo, autofillComponent -> // Autofill Selection Action { searchInfo, autofillComponent -> // Autofill Selection Action
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
MainCredentialActivity.launchForAutofillResult(activity, launchForAutofillResult(
databaseUri, keyFile, activity,
autofillActivityResultLauncher, databaseUri,
autofillComponent, keyFile,
searchInfo) hardwareKey,
autofillActivityResultLauncher,
autofillComponent,
searchInfo
)
onLaunchActivitySpecialMode() onLaunchActivitySpecialMode()
} else { } else {
onCancelSpecialMode() onCancelSpecialMode()
} }
}, },
{ registerInfo -> // Registration Action { registerInfo -> // Registration Action
MainCredentialActivity.launchForRegistration(activity, launchForRegistration(
databaseUri, keyFile, activity,
registerInfo) databaseUri,
keyFile,
hardwareKey,
registerInfo
)
onLaunchActivitySpecialMode() onLaunchActivitySpecialMode()
} }
) )

View File

@@ -23,8 +23,15 @@ import androidx.room.Database
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import android.content.Context import android.content.Context
import androidx.room.AutoMigration
@Database(version = 1, entities = [FileDatabaseHistoryEntity::class, CipherDatabaseEntity::class]) @Database(
version = 2,
entities = [FileDatabaseHistoryEntity::class, CipherDatabaseEntity::class],
autoMigrations = [
AutoMigration (from = 1, to = 2)
]
)
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract fun fileDatabaseHistoryDao(): FileDatabaseHistoryDao abstract fun fileDatabaseHistoryDao(): FileDatabaseHistoryDao

View File

@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.app.database
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.DatabaseFile import com.kunzisoft.keepass.model.DatabaseFile
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.SingletonHolderParameter import com.kunzisoft.keepass.utils.SingletonHolderParameter
@@ -44,6 +45,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
DatabaseFile( DatabaseFile(
databaseUri, databaseUri,
UriUtil.parse(fileDatabaseHistoryEntity?.keyFileUri), UriUtil.parse(fileDatabaseHistoryEntity?.keyFileUri),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity?.hardwareKey),
UriUtil.decode(fileDatabaseHistoryEntity?.databaseUri), UriUtil.decode(fileDatabaseHistoryEntity?.databaseUri),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias ?: ""), fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias ?: ""),
fileDatabaseInfo.exists, fileDatabaseInfo.exists,
@@ -85,13 +87,14 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
|| !hideBrokenLocations) { || !hideBrokenLocations) {
databaseFileListLoaded.add( databaseFileListLoaded.add(
DatabaseFile( DatabaseFile(
UriUtil.parse(fileDatabaseHistoryEntity.databaseUri), UriUtil.parse(fileDatabaseHistoryEntity.databaseUri),
UriUtil.parse(fileDatabaseHistoryEntity.keyFileUri), UriUtil.parse(fileDatabaseHistoryEntity.keyFileUri),
UriUtil.decode(fileDatabaseHistoryEntity.databaseUri), HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity.hardwareKey),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias), UriUtil.decode(fileDatabaseHistoryEntity.databaseUri),
fileDatabaseInfo.exists, fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias),
fileDatabaseInfo.getLastModificationString(), fileDatabaseInfo.exists,
fileDatabaseInfo.getSizeString() fileDatabaseInfo.getLastModificationString(),
fileDatabaseInfo.getSizeString()
) )
) )
} }
@@ -107,11 +110,14 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
).execute() ).execute()
} }
fun addOrUpdateDatabaseUri(databaseUri: Uri, keyFileUri: Uri? = null, fun addOrUpdateDatabaseUri(databaseUri: Uri,
keyFileUri: Uri? = null,
hardwareKey: HardwareKey? = null,
databaseFileAddedOrUpdatedResult: ((DatabaseFile?) -> Unit)? = null) { databaseFileAddedOrUpdatedResult: ((DatabaseFile?) -> Unit)? = null) {
addOrUpdateDatabaseFile(DatabaseFile( addOrUpdateDatabaseFile(DatabaseFile(
databaseUri, databaseUri,
keyFileUri keyFileUri,
hardwareKey
), databaseFileAddedOrUpdatedResult) ), databaseFileAddedOrUpdatedResult)
} }
@@ -130,6 +136,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
?: fileDatabaseHistoryRetrieve?.databaseAlias ?: fileDatabaseHistoryRetrieve?.databaseAlias
?: "", ?: "",
databaseFileToAddOrUpdate.keyFileUri?.toString(), databaseFileToAddOrUpdate.keyFileUri?.toString(),
databaseFileToAddOrUpdate.hardwareKey?.value,
System.currentTimeMillis() System.currentTimeMillis()
) )
@@ -147,13 +154,14 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
val fileDatabaseInfo = FileDatabaseInfo(applicationContext, val fileDatabaseInfo = FileDatabaseInfo(applicationContext,
fileDatabaseHistory.databaseUri) fileDatabaseHistory.databaseUri)
DatabaseFile( DatabaseFile(
UriUtil.parse(fileDatabaseHistory.databaseUri), UriUtil.parse(fileDatabaseHistory.databaseUri),
UriUtil.parse(fileDatabaseHistory.keyFileUri), UriUtil.parse(fileDatabaseHistory.keyFileUri),
UriUtil.decode(fileDatabaseHistory.databaseUri), HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias), UriUtil.decode(fileDatabaseHistory.databaseUri),
fileDatabaseInfo.exists, fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias),
fileDatabaseInfo.getLastModificationString(), fileDatabaseInfo.exists,
fileDatabaseInfo.getSizeString() fileDatabaseInfo.getLastModificationString(),
fileDatabaseInfo.getSizeString()
) )
} }
}, },
@@ -172,10 +180,11 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
val returnValue = databaseFileHistoryDao.delete(fileDatabaseHistory) val returnValue = databaseFileHistoryDao.delete(fileDatabaseHistory)
if (returnValue > 0) { if (returnValue > 0) {
DatabaseFile( DatabaseFile(
UriUtil.parse(fileDatabaseHistory.databaseUri), UriUtil.parse(fileDatabaseHistory.databaseUri),
UriUtil.parse(fileDatabaseHistory.keyFileUri), UriUtil.parse(fileDatabaseHistory.keyFileUri),
UriUtil.decode(fileDatabaseHistory.databaseUri), HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
databaseFileToDelete.databaseAlias UriUtil.decode(fileDatabaseHistory.databaseUri),
databaseFileToDelete.databaseAlias
) )
} else { } else {
null null

View File

@@ -35,6 +35,9 @@ data class FileDatabaseHistoryEntity(
@ColumnInfo(name = "keyfile_uri") @ColumnInfo(name = "keyfile_uri")
var keyFileUri: String?, var keyFileUri: String?,
@ColumnInfo(name = "hardware_key")
var hardwareKey: String?,
@ColumnInfo(name = "updated") @ColumnInfo(name = "updated")
val updated: Long val updated: Long
) { ) {

View File

@@ -60,8 +60,11 @@ class CreateDatabaseRunnable(context: Context,
// Add database to recent files // Add database to recent files
if (PreferencesUtil.rememberDatabaseLocations(context)) { if (PreferencesUtil.rememberDatabaseLocations(context)) {
FileDatabaseHistoryAction.getInstance(context.applicationContext) FileDatabaseHistoryAction.getInstance(context.applicationContext)
.addOrUpdateDatabaseUri(mDatabaseUri, .addOrUpdateDatabaseUri(
if (PreferencesUtil.rememberKeyFileLocations(context)) mainCredential.keyFileUri else null) mDatabaseUri,
if (PreferencesUtil.rememberKeyFileLocations(context)) mainCredential.keyFileUri else null,
if (PreferencesUtil.rememberHardwareKey(context)) mainCredential.hardwareKey else null,
)
} }
// Register the current time to init the lock timer // Register the current time to init the lock timer

View File

@@ -75,8 +75,11 @@ class LoadDatabaseRunnable(private val context: Context,
// Save keyFile in app database // Save keyFile in app database
if (PreferencesUtil.rememberDatabaseLocations(context)) { if (PreferencesUtil.rememberDatabaseLocations(context)) {
FileDatabaseHistoryAction.getInstance(context) FileDatabaseHistoryAction.getInstance(context)
.addOrUpdateDatabaseUri(mDatabaseUri, .addOrUpdateDatabaseUri(
if (PreferencesUtil.rememberKeyFileLocations(context)) mMainCredential.keyFileUri else null) mDatabaseUri,
if (PreferencesUtil.rememberKeyFileLocations(context)) mMainCredential.keyFileUri else null,
if (PreferencesUtil.rememberHardwareKey(context)) mMainCredential.hardwareKey else null,
)
} }
// Register the biometric // Register the biometric

View File

@@ -4,6 +4,10 @@ enum class HardwareKey(val value: String) {
FIDO2_SECRET("FIDO2 secret"), FIDO2_SECRET("FIDO2 secret"),
CHALLENGE_RESPONSE_YUBIKEY("Yubikey challenge-response"); CHALLENGE_RESPONSE_YUBIKEY("Yubikey challenge-response");
override fun toString(): String {
return value
}
companion object { companion object {
val DEFAULT = FIDO2_SECRET val DEFAULT = FIDO2_SECRET
@@ -19,7 +23,9 @@ enum class HardwareKey(val value: String) {
} }
} }
fun getHardwareKeyFromString(text: String): HardwareKey { fun getHardwareKeyFromString(text: String?): HardwareKey? {
if (text == null)
return null
values().find { it.value == text }?.let { values().find { it.value == text }?.let {
return it return it
} }

View File

@@ -1,9 +1,11 @@
package com.kunzisoft.keepass.model package com.kunzisoft.keepass.model
import android.net.Uri import android.net.Uri
import com.kunzisoft.keepass.hardware.HardwareKey
data class DatabaseFile(var databaseUri: Uri? = null, data class DatabaseFile(var databaseUri: Uri? = null,
var keyFileUri: Uri? = null, var keyFileUri: Uri? = null,
var hardwareKey: HardwareKey? = null,
var databaseDecodedPath: String? = null, var databaseDecodedPath: String? = null,
var databaseAlias: String? = null, var databaseAlias: String? = null,
var databaseFileExists: Boolean = false, var databaseFileExists: Boolean = false,

View File

@@ -96,6 +96,12 @@ object PreferencesUtil {
context.resources.getBoolean(R.bool.remember_keyfile_locations_default)) context.resources.getBoolean(R.bool.remember_keyfile_locations_default))
} }
fun rememberHardwareKey(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.remember_hardware_key_key),
context.resources.getBoolean(R.bool.remember_hardware_key_default))
}
fun automaticallyFocusSearch(context: Context): Boolean { fun automaticallyFocusSearch(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.auto_focus_search_key), return prefs.getBoolean(context.getString(R.string.auto_focus_search_key),

View File

@@ -226,10 +226,10 @@ object UriUtil {
} }
} }
fun getUriFromIntent(intent: Intent, key: String): Uri? { fun getUriFromIntent(intent: Intent?, key: String): Uri? {
try { try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
val clipData = intent.clipData val clipData = intent?.clipData
if (clipData != null) { if (clipData != null) {
if (clipData.description.label == key) { if (clipData.description.label == key) {
if (clipData.itemCount == 1) { if (clipData.itemCount == 1) {
@@ -242,7 +242,7 @@ object UriUtil {
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
return intent.getParcelableExtra(key) return intent?.getParcelableExtra(key)
} }
return null return null
} }

View File

@@ -71,6 +71,11 @@ class HardwareKeySelectionView @JvmOverloads constructor(context: Context,
hardwareKeyLayout = findViewById(R.id.input_entry_hardware_key_layout) hardwareKeyLayout = findViewById(R.id.input_entry_hardware_key_layout)
hardwareKeyCompletion = findViewById(R.id.input_entry_hardware_key_completion) hardwareKeyCompletion = findViewById(R.id.input_entry_hardware_key_completion)
hardwareKeyCompletion.isFocusable = false
hardwareKeyCompletion.isFocusableInTouchMode = false
//hardwareKeyCompletion.isEnabled = false
hardwareKeyCompletion.isCursorVisible = false
hardwareKeyCompletion.setTextIsSelectable(false)
hardwareKeyCompletion.inputType = InputType.TYPE_NULL hardwareKeyCompletion.inputType = InputType.TYPE_NULL
hardwareKeyCompletion.setAdapter(mHardwareKeyAdapter) hardwareKeyCompletion.setAdapter(mHardwareKeyAdapter)
@@ -89,8 +94,7 @@ class HardwareKeySelectionView @JvmOverloads constructor(context: Context,
} }
set(value) { set(value) {
mHardwareKey = value mHardwareKey = value
if (value != null) hardwareKeyCompletion.setText(value?.toString() ?: "")
hardwareKeyCompletion.setSelection(value.ordinal)
} }
var error: CharSequence? var error: CharSequence?

View File

@@ -40,6 +40,7 @@ import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
import com.kunzisoft.keepass.model.CredentialStorage import com.kunzisoft.keepass.model.CredentialStorage
import com.kunzisoft.keepass.database.element.MainCredential import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.hardware.HardwareKey
class MainCredentialView @JvmOverloads constructor(context: Context, class MainCredentialView @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
@@ -156,6 +157,18 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
} }
} }
fun populateHardwareKeyView(hardwareKey: HardwareKey?) {
if (hardwareKey == null) {
hardwareKeySelectionView.hardwareKey = null
if (checkboxHardwareView.isChecked)
checkboxHardwareView.isChecked = false
} else {
hardwareKeySelectionView.hardwareKey = hardwareKey
if (!checkboxHardwareView.isChecked)
checkboxHardwareView.isChecked = true
}
}
fun setOpenKeyfileClickListener(externalFileHelper: ExternalFileHelper?) { fun setOpenKeyfileClickListener(externalFileHelper: ExternalFileHelper?) {
keyFileSelectionView.setOpenDocumentClickListener(externalFileHelper) keyFileSelectionView.setOpenDocumentClickListener(externalFileHelper)
} }

View File

@@ -7,6 +7,7 @@ import androidx.lifecycle.MutableLiveData
import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.app.App
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.app.database.IOActionTask import com.kunzisoft.keepass.app.database.IOActionTask
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.DatabaseFile import com.kunzisoft.keepass.model.DatabaseFile
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.UriUtil
@@ -72,8 +73,12 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
} }
} }
fun addDatabaseFile(databaseUri: Uri, keyFileUri: Uri?) { fun addDatabaseFile(databaseUri: Uri, keyFileUri: Uri?, hardwareKey: HardwareKey?) {
mFileDatabaseHistoryAction?.addOrUpdateDatabaseUri(databaseUri, keyFileUri) { databaseFileAdded -> mFileDatabaseHistoryAction?.addOrUpdateDatabaseUri(
databaseUri,
keyFileUri,
hardwareKey
) { databaseFileAdded ->
databaseFileAdded?.let { _ -> databaseFileAdded?.let { _ ->
databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply { databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
this.databaseFileAction = DatabaseFileAction.ADD this.databaseFileAction = DatabaseFileAction.ADD
@@ -96,6 +101,7 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
.find { it.databaseUri == databaseFileUpdated.databaseUri } .find { it.databaseUri == databaseFileUpdated.databaseUri }
?.apply { ?.apply {
keyFileUri = databaseFileUpdated.keyFileUri keyFileUri = databaseFileUpdated.keyFileUri
hardwareKey = databaseFileUpdated.hardwareKey
databaseAlias = databaseFileUpdated.databaseAlias databaseAlias = databaseFileUpdated.databaseAlias
databaseFileExists = databaseFileUpdated.databaseFileExists databaseFileExists = databaseFileUpdated.databaseFileExists
databaseLastModified = databaseFileUpdated.databaseLastModified databaseLastModified = databaseFileUpdated.databaseLastModified

View File

@@ -91,6 +91,8 @@
<bool name="hide_broken_locations_default" translatable="false">true</bool> <bool name="hide_broken_locations_default" translatable="false">true</bool>
<string name="remember_keyfile_locations_key" translatable="false">remember_keyfile_locations_key</string> <string name="remember_keyfile_locations_key" translatable="false">remember_keyfile_locations_key</string>
<bool name="remember_keyfile_locations_default" translatable="false">true</bool> <bool name="remember_keyfile_locations_default" translatable="false">true</bool>
<string name="remember_hardware_key_key" translatable="false">remember_hardware_key_key</string>
<bool name="remember_hardware_key_default" translatable="false">true</bool>
<string name="advanced_unlock_explanation_key" translatable="false">advanced_unlock_explanation_key</string> <string name="advanced_unlock_explanation_key" translatable="false">advanced_unlock_explanation_key</string>
<string name="biometric_unlock_enable_key" translatable="false">biometric_unlock_enable_key</string> <string name="biometric_unlock_enable_key" translatable="false">biometric_unlock_enable_key</string>
<bool name="biometric_unlock_enable_default" translatable="false">false</bool> <bool name="biometric_unlock_enable_default" translatable="false">false</bool>

View File

@@ -292,6 +292,8 @@
<string name="remember_database_locations_summary">Keeps track of where databases are stored</string> <string name="remember_database_locations_summary">Keeps track of where databases are stored</string>
<string name="remember_keyfile_locations_title">Remember keyfile locations</string> <string name="remember_keyfile_locations_title">Remember keyfile locations</string>
<string name="remember_keyfile_locations_summary">Keeps track of where keyfiles are stored</string> <string name="remember_keyfile_locations_summary">Keeps track of where keyfiles are stored</string>
<string name="remember_hardware_key_title">Remember hardware keys</string>
<string name="remember_hardware_key_summary">Keeps track of the hardware keys used</string>
<string name="show_recent_files_title">Show recent files</string> <string name="show_recent_files_title">Show recent files</string>
<string name="show_recent_files_summary">Show locations of recent databases</string> <string name="show_recent_files_summary">Show locations of recent databases</string>
<string name="hide_broken_locations_title">Hide broken database links</string> <string name="hide_broken_locations_title">Hide broken database links</string>

View File

@@ -17,8 +17,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>. along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
--> -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory <PreferenceCategory
android:title="@string/general"> android:title="@string/general">
@@ -107,6 +106,12 @@
android:summary="@string/remember_keyfile_locations_summary" android:summary="@string/remember_keyfile_locations_summary"
android:dependency="@string/remember_database_locations_key" android:dependency="@string/remember_database_locations_key"
android:defaultValue="@bool/remember_keyfile_locations_default"/> android:defaultValue="@bool/remember_keyfile_locations_default"/>
<SwitchPreference
android:key="@string/remember_hardware_key_key"
android:title="@string/remember_hardware_key_title"
android:summary="@string/remember_hardware_key_summary"
android:dependency="@string/remember_database_locations_key"
android:defaultValue="@bool/remember_hardware_key_default"/>
<SwitchPreference <SwitchPreference
android:key="@string/show_recent_files_key" android:key="@string/show_recent_files_key"
android:title="@string/show_recent_files_title" android:title="@string/show_recent_files_title"