mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
fix: remove ChallengeResponseViewModel and add HardwareKeyActivity
This commit is contained in:
@@ -156,6 +156,8 @@
|
||||
android:name="com.kunzisoft.keepass.settings.SettingsAdvancedUnlockActivity" />
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.settings.AutofillSettingsActivity" />
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.hardware.HardwareKeyActivity" />
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.activities.EntrySelectionLauncherActivity"
|
||||
android:theme="@style/Theme.Transparent"
|
||||
|
||||
@@ -36,7 +36,7 @@ import com.kunzisoft.keepass.R
|
||||
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.hardware.HardwareKeyActivity
|
||||
import com.kunzisoft.keepass.database.element.MainCredential
|
||||
import com.kunzisoft.keepass.password.PasswordEntropy
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
@@ -169,7 +169,7 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
|
||||
hardwareKeySelectionView.selectionListener = { hardwareKey ->
|
||||
hardwareKeyCheckBox.isChecked = true
|
||||
hardwareKeySelectionView.error =
|
||||
if (!HardwareKeyResponseHelper.isHardwareKeyAvailable(requireActivity(), hardwareKey)) {
|
||||
if (!HardwareKeyActivity.isHardwareKeyAvailable(requireActivity(), hardwareKey)) {
|
||||
// show hardware driver dialog if required
|
||||
getString(R.string.error_driver_required, hardwareKey.toString())
|
||||
} else {
|
||||
@@ -231,7 +231,7 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
|
||||
showEmptyPasswordConfirmationDialog()
|
||||
} else if (!error
|
||||
&& hardwareKey != null
|
||||
&& !HardwareKeyResponseHelper.isHardwareKeyAvailable(
|
||||
&& !HardwareKeyActivity.isHardwareKeyAvailable(
|
||||
requireActivity(), hardwareKey, false)
|
||||
) {
|
||||
// show hardware driver dialog if required
|
||||
|
||||
@@ -9,7 +9,6 @@ import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.MainCredential
|
||||
import com.kunzisoft.keepass.model.CipherEncryptDatabase
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.viewmodels.ChallengeResponseViewModel
|
||||
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
||||
|
||||
abstract class DatabaseActivity: StylishActivity(), DatabaseRetrieval {
|
||||
@@ -18,12 +17,10 @@ abstract class DatabaseActivity: StylishActivity(), DatabaseRetrieval {
|
||||
protected var mDatabaseTaskProvider: DatabaseTaskProvider? = null
|
||||
protected var mDatabase: Database? = null
|
||||
|
||||
private val mChallengeResponseViewModel: ChallengeResponseViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
mDatabaseTaskProvider = DatabaseTaskProvider(this, mChallengeResponseViewModel)
|
||||
mDatabaseTaskProvider = DatabaseTaskProvider(this)
|
||||
|
||||
mDatabaseTaskProvider?.onDatabaseRetrieved = { database ->
|
||||
val databaseWasReloaded = database?.wasReloaded == true
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.action
|
||||
|
||||
import android.app.Service
|
||||
import android.content.*
|
||||
import android.content.Context.*
|
||||
import android.net.Uri
|
||||
@@ -43,13 +42,13 @@ import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
||||
import com.kunzisoft.keepass.database.element.node.Node
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
import com.kunzisoft.keepass.database.element.node.Type
|
||||
import com.kunzisoft.keepass.database.exception.InvalidCredentialsDatabaseException
|
||||
import com.kunzisoft.keepass.hardware.HardwareKey
|
||||
import com.kunzisoft.keepass.hardware.HardwareKeyResponseHelper
|
||||
import com.kunzisoft.keepass.hardware.HardwareKeyActivity
|
||||
import com.kunzisoft.keepass.model.CipherEncryptDatabase
|
||||
import com.kunzisoft.keepass.model.ProgressMessage
|
||||
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_CHALLENGE_RESPONDED
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_ASSIGN_PASSWORD_TASK
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_COPY_NODES_TASK
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_ENTRY_TASK
|
||||
@@ -86,7 +85,6 @@ import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.PROGRESS_TASK_DIALOG_TAG
|
||||
import com.kunzisoft.keepass.utils.DATABASE_START_TASK_ACTION
|
||||
import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION
|
||||
import com.kunzisoft.keepass.viewmodels.ChallengeResponseViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
|
||||
@@ -94,10 +92,11 @@ import java.util.*
|
||||
* Utility class to connect an activity or a service to the DatabaseTaskNotificationService,
|
||||
* Useful to retrieve a database instance and sending tasks commands
|
||||
*/
|
||||
class DatabaseTaskProvider {
|
||||
class DatabaseTaskProvider(private var context: Context) {
|
||||
|
||||
private var activity: FragmentActivity? = null
|
||||
private var context: Context
|
||||
// To show dialog only if context is an activity
|
||||
private var activity: FragmentActivity? = try { context as? FragmentActivity? }
|
||||
catch (_: Exception) { null }
|
||||
|
||||
var onDatabaseRetrieved: ((database: Database?) -> Unit)? = null
|
||||
|
||||
@@ -105,7 +104,10 @@ class DatabaseTaskProvider {
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result) -> Unit)? = null
|
||||
|
||||
private var intentDatabaseTask: Intent
|
||||
private var intentDatabaseTask: Intent = Intent(
|
||||
context.applicationContext,
|
||||
DatabaseTaskNotificationService::class.java
|
||||
)
|
||||
|
||||
private var databaseTaskBroadcastReceiver: BroadcastReceiver? = null
|
||||
private var mBinder: DatabaseTaskNotificationService.ActionTaskBinder? = null
|
||||
@@ -115,54 +117,6 @@ class DatabaseTaskProvider {
|
||||
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null
|
||||
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null
|
||||
|
||||
private var mChallengeResponseViewModel: ChallengeResponseViewModel? = null
|
||||
|
||||
constructor(activity: FragmentActivity,
|
||||
challengeResponseViewModel: ChallengeResponseViewModel) {
|
||||
this.activity = activity
|
||||
this.context = activity
|
||||
this.intentDatabaseTask = Intent(activity.applicationContext,
|
||||
DatabaseTaskNotificationService::class.java)
|
||||
|
||||
// ViewModel used to keep response if activity recreated
|
||||
this.mChallengeResponseViewModel = challengeResponseViewModel
|
||||
// To manage hardware key challenge response
|
||||
val hardwareKeyResponseHelper = HardwareKeyResponseHelper(activity)
|
||||
hardwareKeyResponseHelper.buildHardwareKeyResponse { responseData, _ ->
|
||||
// TODO Verify database
|
||||
// Send to view model in case activity is restarted and not yet connected to service
|
||||
challengeResponseViewModel.respond(responseData ?: ByteArray(0))
|
||||
}
|
||||
challengeResponseViewModel.dataResponded.observe(activity) { response ->
|
||||
// Consume the response
|
||||
if (response != null) {
|
||||
val binder = mBinder
|
||||
if (binder != null) {
|
||||
binder.getService().respondToChallenge(response)
|
||||
challengeResponseViewModel.consumeResponse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.requestChallengeListener = object: DatabaseTaskNotificationService.RequestChallengeListener {
|
||||
override fun onChallengeResponseRequested(hardwareKey: HardwareKey, seed: ByteArray?) {
|
||||
if (HardwareKeyResponseHelper.isHardwareKeyAvailable(activity, hardwareKey)) {
|
||||
hardwareKeyResponseHelper.launchChallengeForResponse(hardwareKey, seed)
|
||||
} else {
|
||||
throw InvalidCredentialsDatabaseException(
|
||||
context.getString(R.string.error_driver_required, hardwareKey.toString())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(service: Service) {
|
||||
this.context = service
|
||||
this.intentDatabaseTask = Intent(service.applicationContext,
|
||||
DatabaseTaskNotificationService::class.java)
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
this.activity = null
|
||||
this.onDatabaseRetrieved = null
|
||||
@@ -172,7 +126,6 @@ class DatabaseTaskProvider {
|
||||
this.serviceConnection = null
|
||||
this.progressTaskDialogFragment = null
|
||||
this.databaseChangedDialogFragment = null
|
||||
this.mChallengeResponseViewModel = null
|
||||
}
|
||||
|
||||
private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener {
|
||||
@@ -235,7 +188,19 @@ class DatabaseTaskProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private var requestChallengeListener: DatabaseTaskNotificationService.RequestChallengeListener? = null
|
||||
private var requestChallengeListener = object: DatabaseTaskNotificationService.RequestChallengeListener {
|
||||
override fun onChallengeResponseRequested(
|
||||
hardwareKey: HardwareKey,
|
||||
seed: ByteArray?
|
||||
) {
|
||||
HardwareKeyActivity
|
||||
.launchHardwareKeyActivity(
|
||||
context,
|
||||
hardwareKey,
|
||||
seed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startDialog(progressMessage: ProgressMessage) {
|
||||
activity?.let { activity ->
|
||||
@@ -280,7 +245,6 @@ class DatabaseTaskProvider {
|
||||
getService().checkDatabaseInfo()
|
||||
getService().checkAction()
|
||||
}
|
||||
mChallengeResponseViewModel?.resendResponse()
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
@@ -295,9 +259,7 @@ class DatabaseTaskProvider {
|
||||
service?.addDatabaseListener(databaseListener)
|
||||
service?.addDatabaseFileInfoListener(databaseInfoListener)
|
||||
service?.addActionTaskListener(actionTaskListener)
|
||||
requestChallengeListener?.let {
|
||||
service?.addRequestChallengeListener(it)
|
||||
}
|
||||
service?.setRequestChallengeListener(requestChallengeListener)
|
||||
}
|
||||
|
||||
private fun removeServiceListeners(service: DatabaseTaskNotificationService.ActionTaskBinder?) {
|
||||
@@ -762,6 +724,13 @@ class DatabaseTaskProvider {
|
||||
, ACTION_DATABASE_SAVE)
|
||||
}
|
||||
|
||||
fun startChallengeResponded(response: ByteArray?) {
|
||||
start(Bundle().apply {
|
||||
putByteArray(DatabaseTaskNotificationService.DATA_BYTES, response)
|
||||
}
|
||||
, ACTION_CHALLENGE_RESPONDED)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = DatabaseTaskProvider::class.java.name
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.kunzisoft.keepass.hardware
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.ActivityResultCallback
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.legacy.DatabaseActivity
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
|
||||
/**
|
||||
* Special activity to deal with hardware key drivers,
|
||||
* return the response to the database service once finished
|
||||
*/
|
||||
class HardwareKeyActivity: DatabaseActivity(){
|
||||
|
||||
// To manage hardware key challenge response
|
||||
private val resultCallback = ActivityResultCallback<ActivityResult> { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
val challengeResponse: ByteArray? = result.data?.getByteArrayExtra(HARDWARE_KEY_RESPONSE_KEY)
|
||||
Log.d(TAG, "Response form challenge")
|
||||
mDatabaseTaskProvider?.startChallengeResponded(challengeResponse ?: ByteArray(0))
|
||||
} else {
|
||||
Log.e(TAG, "Response from challenge error")
|
||||
mDatabaseTaskProvider?.startChallengeResponded(ByteArray(0))
|
||||
}
|
||||
finish()
|
||||
}
|
||||
|
||||
private var activityResultLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
resultCallback
|
||||
)
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
|
||||
val hardwareKey = HardwareKey.getHardwareKeyFromString(
|
||||
intent.getStringExtra(DATA_HARDWARE_KEY)
|
||||
)
|
||||
if (isHardwareKeyAvailable(this, hardwareKey)) {
|
||||
when (hardwareKey) {
|
||||
/*
|
||||
HardwareKey.FIDO2_SECRET -> {
|
||||
// TODO FIDO2 under development
|
||||
throw Exception("FIDO2 not implemented")
|
||||
}
|
||||
*/
|
||||
HardwareKey.CHALLENGE_RESPONSE_YUBIKEY -> {
|
||||
launchYubikeyChallengeForResponse(intent.getByteArrayExtra(DATA_SEED))
|
||||
}
|
||||
else -> {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchYubikeyChallengeForResponse(seed: ByteArray?) {
|
||||
// Transform the seed before sending
|
||||
var challenge: ByteArray? = null
|
||||
if (seed != null) {
|
||||
challenge = ByteArray(64)
|
||||
seed.copyInto(challenge, 0, 0, 32)
|
||||
challenge.fill(32, 32, 64)
|
||||
}
|
||||
// Send to the driver
|
||||
activityResultLauncher.launch(
|
||||
Intent(YUBIKEY_CHALLENGE_RESPONSE_INTENT).apply {
|
||||
putExtra(HARDWARE_KEY_CHALLENGE_KEY, challenge)
|
||||
}
|
||||
)
|
||||
Log.d(TAG, "Challenge sent")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = HardwareKeyActivity::class.java.simpleName
|
||||
|
||||
private const val DATA_HARDWARE_KEY = "DATA_HARDWARE_KEY"
|
||||
private const val DATA_SEED = "DATA_SEED"
|
||||
private const val YUBIKEY_CHALLENGE_RESPONSE_INTENT = "android.yubikey.intent.action.CHALLENGE_RESPONSE"
|
||||
private const val HARDWARE_KEY_CHALLENGE_KEY = "challenge"
|
||||
private const val HARDWARE_KEY_RESPONSE_KEY = "response"
|
||||
|
||||
fun launchHardwareKeyActivity(
|
||||
context: Context,
|
||||
hardwareKey: HardwareKey,
|
||||
seed: ByteArray?
|
||||
) {
|
||||
context.startActivity(Intent(context, HardwareKeyActivity::class.java).apply {
|
||||
//flags = FLAG_ACTIVITY_NEW_TASK
|
||||
putExtra(DATA_HARDWARE_KEY, hardwareKey.value)
|
||||
putExtra(DATA_SEED, seed)
|
||||
})
|
||||
}
|
||||
|
||||
fun isHardwareKeyAvailable(
|
||||
context: Context,
|
||||
hardwareKey: HardwareKey?,
|
||||
showDialog: Boolean = true
|
||||
): Boolean {
|
||||
if (hardwareKey == null)
|
||||
return false
|
||||
return when (hardwareKey) {
|
||||
/*
|
||||
HardwareKey.FIDO2_SECRET -> {
|
||||
// TODO FIDO2 under development
|
||||
if (showDialog)
|
||||
UnderDevelopmentFeatureDialogFragment()
|
||||
.show(activity.supportFragmentManager, "underDevFeatureDialog")
|
||||
false
|
||||
}
|
||||
*/
|
||||
HardwareKey.CHALLENGE_RESPONSE_YUBIKEY -> {
|
||||
// Check available intent
|
||||
val yubikeyDriverAvailable =
|
||||
Intent(YUBIKEY_CHALLENGE_RESPONSE_INTENT)
|
||||
.resolveActivity(context.packageManager) != null
|
||||
if (showDialog && !yubikeyDriverAvailable)
|
||||
showHardwareKeyDriverNeeded(context, hardwareKey)
|
||||
yubikeyDriverAvailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showHardwareKeyDriverNeeded(
|
||||
context: Context,
|
||||
hardwareKey: HardwareKey
|
||||
) {
|
||||
val builder = AlertDialog.Builder(context)
|
||||
builder
|
||||
.setMessage(
|
||||
context.getString(R.string.error_driver_required, hardwareKey.toString())
|
||||
)
|
||||
.setPositiveButton(R.string.download) { _, _ ->
|
||||
UriUtil.openExternalApp(context, context.getString(R.string.key_driver_app_id))
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
builder.create().show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
package com.kunzisoft.keepass.hardware
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.ActivityResultCallback
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class HardwareKeyResponseHelper {
|
||||
|
||||
private var activity: FragmentActivity? = null
|
||||
private var fragment: Fragment? = null
|
||||
|
||||
private var getChallengeResponseResultLauncher: ActivityResultLauncher<Intent>? = null
|
||||
|
||||
constructor(context: FragmentActivity) {
|
||||
this.activity = context
|
||||
this.fragment = null
|
||||
}
|
||||
|
||||
constructor(context: Fragment) {
|
||||
this.activity = context.activity
|
||||
this.fragment = context
|
||||
}
|
||||
|
||||
fun buildHardwareKeyResponse(onChallengeResponded: (challengeResponse: ByteArray?,
|
||||
extra: Bundle?) -> Unit) {
|
||||
val resultCallback = ActivityResultCallback<ActivityResult> { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
val challengeResponse: ByteArray? = result.data?.getByteArrayExtra(HARDWARE_KEY_RESPONSE_KEY)
|
||||
Log.d(TAG, "Response form challenge")
|
||||
onChallengeResponded.invoke(challengeResponse,
|
||||
result.data?.getBundleExtra(EXTRA_BUNDLE_KEY))
|
||||
} else {
|
||||
Log.e(TAG, "Response from challenge error")
|
||||
onChallengeResponded.invoke(null,
|
||||
result.data?.getBundleExtra(EXTRA_BUNDLE_KEY))
|
||||
}
|
||||
}
|
||||
|
||||
getChallengeResponseResultLauncher = if (fragment != null) {
|
||||
fragment?.registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
resultCallback
|
||||
)
|
||||
} else {
|
||||
activity?.registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
resultCallback
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun launchChallengeForResponse(hardwareKey: HardwareKey, seed: ByteArray?) {
|
||||
when (hardwareKey) {
|
||||
/*
|
||||
HardwareKey.FIDO2_SECRET -> {
|
||||
// TODO FIDO2 under development
|
||||
throw Exception("FIDO2 not implemented")
|
||||
}
|
||||
*/
|
||||
HardwareKey.CHALLENGE_RESPONSE_YUBIKEY -> {
|
||||
// Transform the seed before sending
|
||||
var challenge: ByteArray? = null
|
||||
if (seed != null) {
|
||||
challenge = ByteArray(64)
|
||||
seed.copyInto(challenge, 0, 0, 32)
|
||||
challenge.fill(32, 32, 64)
|
||||
}
|
||||
// Send to the driver
|
||||
getChallengeResponseResultLauncher!!.launch(
|
||||
Intent(YUBIKEY_CHALLENGE_RESPONSE_INTENT).apply {
|
||||
putExtra(HARDWARE_KEY_CHALLENGE_KEY, challenge)
|
||||
}
|
||||
)
|
||||
Log.d(TAG, "Challenge sent")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = HardwareKeyResponseHelper::class.java.simpleName
|
||||
|
||||
private const val YUBIKEY_CHALLENGE_RESPONSE_INTENT = "android.yubikey.intent.action.CHALLENGE_RESPONSE"
|
||||
private const val HARDWARE_KEY_CHALLENGE_KEY = "challenge"
|
||||
private const val HARDWARE_KEY_RESPONSE_KEY = "response"
|
||||
private const val EXTRA_BUNDLE_KEY = "EXTRA_BUNDLE_KEY"
|
||||
|
||||
fun isHardwareKeyAvailable(
|
||||
activity: FragmentActivity,
|
||||
hardwareKey: HardwareKey,
|
||||
showDialog: Boolean = true
|
||||
): Boolean {
|
||||
return when (hardwareKey) {
|
||||
/*
|
||||
HardwareKey.FIDO2_SECRET -> {
|
||||
// TODO FIDO2 under development
|
||||
if (showDialog)
|
||||
UnderDevelopmentFeatureDialogFragment()
|
||||
.show(activity.supportFragmentManager, "underDevFeatureDialog")
|
||||
false
|
||||
}
|
||||
*/
|
||||
HardwareKey.CHALLENGE_RESPONSE_YUBIKEY -> {
|
||||
// Check available intent
|
||||
val yubikeyDriverAvailable =
|
||||
Intent(YUBIKEY_CHALLENGE_RESPONSE_INTENT)
|
||||
.resolveActivity(activity.packageManager) != null
|
||||
if (showDialog && !yubikeyDriverAvailable)
|
||||
showHardwareKeyDriverNeeded(activity, hardwareKey)
|
||||
yubikeyDriverAvailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showHardwareKeyDriverNeeded(
|
||||
activity: FragmentActivity,
|
||||
hardwareKey: HardwareKey
|
||||
) {
|
||||
activity.lifecycleScope.launch {
|
||||
val builder = AlertDialog.Builder(activity)
|
||||
builder
|
||||
.setMessage(
|
||||
activity.getString(R.string.error_driver_required, hardwareKey.toString())
|
||||
)
|
||||
.setPositiveButton(R.string.download) { _, _ ->
|
||||
UriUtil.openExternalApp(activity, activity.getString(R.string.key_driver_app_id))
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
builder.create().show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,8 +124,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
mActionTaskListeners.remove(actionTaskListener)
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
fun addRequestChallengeListener(requestChallengeListener: RequestChallengeListener) {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun setRequestChallengeListener(requestChallengeListener: RequestChallengeListener) {
|
||||
mainScope.launch {
|
||||
val requestChannel = mRequestChallengeListenerChannel
|
||||
if (requestChannel == null || requestChannel.isEmpty) {
|
||||
@@ -169,7 +169,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
}
|
||||
|
||||
interface RequestChallengeListener {
|
||||
fun onChallengeResponseRequested(hardwareKey: HardwareKey, seed: ByteArray?)
|
||||
fun onChallengeResponseRequested(
|
||||
hardwareKey: HardwareKey,
|
||||
seed: ByteArray?
|
||||
)
|
||||
}
|
||||
|
||||
fun checkDatabase() {
|
||||
@@ -270,8 +273,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
mResponseChallengeChannel = null
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
fun respondToChallenge(response: ByteArray) {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun respondToChallenge(response: ByteArray) {
|
||||
mainScope.launch {
|
||||
val responseChannel = mResponseChallengeChannel
|
||||
if (responseChannel == null || responseChannel.isEmpty) {
|
||||
@@ -323,6 +326,12 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
if (intentAction == ACTION_CHALLENGE_RESPONDED) {
|
||||
intent.getByteArrayExtra(DATA_BYTES)?.let {
|
||||
respondToChallenge(it)
|
||||
}
|
||||
}
|
||||
|
||||
val actionRunnable: ActionRunnable? = when (intentAction) {
|
||||
ACTION_DATABASE_CREATE_TASK -> buildDatabaseCreateActionTask(intent, database)
|
||||
ACTION_DATABASE_LOAD_TASK -> buildDatabaseLoadActionTask(intent, database)
|
||||
@@ -756,7 +765,6 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
databaseToMergeUri,
|
||||
databaseToMergeMainCredential,
|
||||
{ hardwareKey, seed ->
|
||||
// TODO fix first challenge response
|
||||
retrieveResponseFromChallenge(hardwareKey, seed)
|
||||
},
|
||||
database,
|
||||
@@ -1157,6 +1165,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
const val ACTION_DATABASE_UPDATE_PARALLELISM_TASK = "ACTION_DATABASE_UPDATE_PARALLELISM_TASK"
|
||||
const val ACTION_DATABASE_UPDATE_ITERATIONS_TASK = "ACTION_DATABASE_UPDATE_ITERATIONS_TASK"
|
||||
const val ACTION_DATABASE_SAVE = "ACTION_DATABASE_SAVE"
|
||||
const val ACTION_CHALLENGE_RESPONDED = "ACTION_CHALLENGE_RESPONDED"
|
||||
|
||||
const val DATABASE_TASK_TITLE_KEY = "DATABASE_TASK_TITLE_KEY"
|
||||
const val DATABASE_TASK_MESSAGE_KEY = "DATABASE_TASK_MESSAGE_KEY"
|
||||
@@ -1180,6 +1189,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
const val NEW_NODES_KEY = "NEW_NODES_KEY"
|
||||
const val OLD_ELEMENT_KEY = "OLD_ELEMENT_KEY" // Warning type of this thing change every time
|
||||
const val NEW_ELEMENT_KEY = "NEW_ELEMENT_KEY" // Warning type of this thing change every time
|
||||
const val DATA_BYTES = "DATA_BYTES"
|
||||
|
||||
fun getListNodesFromBundle(database: Database, bundle: Bundle): List<Node> {
|
||||
val nodesAction = ArrayList<Node>()
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.kunzisoft.keepass.viewmodels
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class ChallengeResponseViewModel: ViewModel() {
|
||||
|
||||
val dataResponded : LiveData<ByteArray?> get() = _dataResponded
|
||||
private val _dataResponded = MutableLiveData<ByteArray?>()
|
||||
|
||||
fun respond(byteArray: ByteArray) {
|
||||
_dataResponded.value = byteArray
|
||||
}
|
||||
|
||||
fun resendResponse() {
|
||||
dataResponded.value?.let {
|
||||
_dataResponded.value = it
|
||||
}
|
||||
}
|
||||
|
||||
fun consumeResponse() {
|
||||
_dataResponded.value = null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user