mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
feat: Add KPEX_PASSKEY_FLAG_BE and KPEX_PASSKEY_FLAG_BS flags #2212
This commit is contained in:
@@ -3,7 +3,7 @@ KeePassDX(4.2.0)
|
||||
* Confirm usage of passkey #2165 #2124
|
||||
* Dialog to manage missing signature #2152 #2155 #2161 #2160
|
||||
* Capture error #2159
|
||||
* Change Passkey Backup Eligibility & Backup State #2135 #2150
|
||||
* Change Passkey Backup Eligibility & Backup State #2135 #2150 #2212
|
||||
* Search settings #2112 #2181 #2187
|
||||
* Autofill refactoring #765 #2196
|
||||
* Small fixes #2157 #2164 #2171 #2122 #2180 #2209
|
||||
|
||||
@@ -388,11 +388,15 @@ object PasskeyHelper {
|
||||
* Utility method to create a passkey and the associated creation request parameters
|
||||
* [intent] allows to retrieve the request
|
||||
* [context] context to manage package verification files
|
||||
* [defaultBackupEligibility] the default backup eligibility to add the the passkey entry
|
||||
* [defaultBackupState] the default backup state to add the the passkey entry
|
||||
* [passkeyCreated] is called asynchronously when the passkey has been created
|
||||
*/
|
||||
suspend fun retrievePasskeyCreationRequestParameters(
|
||||
intent: Intent,
|
||||
context: Context,
|
||||
defaultBackupEligibility: Boolean?,
|
||||
defaultBackupState: Boolean?,
|
||||
passkeyCreated: suspend (Passkey, AppOrigin?, PublicKeyCredentialCreationParameters) -> Unit
|
||||
) {
|
||||
val createCredentialRequest = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
|
||||
@@ -420,7 +424,9 @@ object PasskeyHelper {
|
||||
privateKeyPem = privateKeyPem,
|
||||
credentialId = b64Encode(credentialId),
|
||||
userHandle = b64Encode(userHandle),
|
||||
relyingParty = relyingParty
|
||||
relyingParty = relyingParty,
|
||||
backupEligibility = defaultBackupEligibility,
|
||||
backupState = defaultBackupState
|
||||
)
|
||||
|
||||
// create new entry in database
|
||||
@@ -554,8 +560,8 @@ object PasskeyHelper {
|
||||
requestOptions: PublicKeyCredentialRequestOptions,
|
||||
clientDataResponse: ClientDataResponse,
|
||||
passkey: Passkey,
|
||||
backupEligibility: Boolean,
|
||||
backupState: Boolean
|
||||
defaultBackupEligibility: Boolean,
|
||||
defaultBackupState: Boolean
|
||||
): PublicKeyCredential {
|
||||
val getCredentialResponse = FidoPublicKeyCredential(
|
||||
id = passkey.credentialId,
|
||||
@@ -563,8 +569,8 @@ object PasskeyHelper {
|
||||
requestOptions = requestOptions,
|
||||
userPresent = true,
|
||||
userVerified = true,
|
||||
backupEligibility = backupEligibility,
|
||||
backupState = backupState,
|
||||
backupEligibility = passkey.backupEligibility ?: defaultBackupEligibility,
|
||||
backupState = passkey.backupState ?: defaultBackupState,
|
||||
userHandle = passkey.userHandle,
|
||||
privateKey = passkey.privateKeyPem,
|
||||
clientDataResponse = clientDataResponse
|
||||
|
||||
@@ -307,8 +307,8 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
||||
appOrigin = appOrigin
|
||||
),
|
||||
passkey = passkey,
|
||||
backupEligibility = mBackupEligibility,
|
||||
backupState = mBackupState
|
||||
defaultBackupEligibility = mBackupEligibility,
|
||||
defaultBackupState = mBackupState
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -363,8 +363,8 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
||||
appOrigin = appOrigin
|
||||
),
|
||||
passkey = passkey,
|
||||
backupEligibility = mBackupEligibility,
|
||||
backupState = mBackupState
|
||||
defaultBackupEligibility = mBackupEligibility,
|
||||
defaultBackupState = mBackupState
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -400,6 +400,8 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
||||
retrievePasskeyCreationRequestParameters(
|
||||
intent = intent,
|
||||
context = getApplication(),
|
||||
defaultBackupEligibility = mBackupEligibility,
|
||||
defaultBackupState = mBackupState,
|
||||
passkeyCreated = { passkey, appInfoToStore, publicKeyCredentialParameters ->
|
||||
// Save the requested parameters
|
||||
mPasskey = passkey
|
||||
@@ -503,8 +505,10 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
||||
intent = responseIntent,
|
||||
response = buildCreatePublicKeyCredentialResponse(
|
||||
publicKeyCredentialCreationParameters = it,
|
||||
backupEligibility = mBackupEligibility,
|
||||
backupState = mBackupState
|
||||
backupEligibility = passkey?.backupEligibility
|
||||
?: mBackupEligibility,
|
||||
backupState = passkey?.backupState
|
||||
?: mBackupState
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ import com.kunzisoft.keepass.database.exception.UnknownDatabaseLocationException
|
||||
import com.kunzisoft.keepass.database.exception.VersionDatabaseException
|
||||
import com.kunzisoft.keepass.database.exception.XMLMalformedDatabaseException
|
||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.FIELD_CREDENTIAL_ID
|
||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.FIELD_FLAG_BE
|
||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.FIELD_FLAG_BS
|
||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.FIELD_PRIVATE_KEY
|
||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.FIELD_RELYING_PARTY
|
||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.FIELD_USERNAME
|
||||
@@ -146,6 +148,8 @@ fun TemplateField.getLocalizedName(context: Context?, name: String): String {
|
||||
FIELD_CREDENTIAL_ID.equals(name, true) -> context.getString(R.string.passkey_credential_id)
|
||||
FIELD_USER_HANDLE.equals(name, true) -> context.getString(R.string.passkey_user_handle)
|
||||
FIELD_RELYING_PARTY.equals(name, true) -> context.getString(R.string.passkey_relying_party)
|
||||
FIELD_FLAG_BE.equals(name, true) -> context.getString(R.string.passkey_backup_eligibility)
|
||||
FIELD_FLAG_BS.equals(name, true) -> context.getString(R.string.passkey_backup_state)
|
||||
|
||||
else -> name
|
||||
}
|
||||
|
||||
@@ -771,5 +771,7 @@
|
||||
<string name="passkey_credential_id">Passkey Credential Id</string>
|
||||
<string name="passkey_user_handle">Passkey User Handle</string>
|
||||
<string name="passkey_relying_party">Passkey Relying Party</string>
|
||||
<string name="passkey_backup_eligibility">Passkey Backup Eligibility</string>
|
||||
<string name="passkey_backup_state">Passkey Backup State</string>
|
||||
<string name="error_passkey_result">Unable to return the passkey</string>
|
||||
</resources>
|
||||
@@ -74,6 +74,17 @@ class ProtectedString : Parcelable {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
fun String.toBooleanCompat(): Boolean {
|
||||
return if (this.equals("1", ignoreCase = true))
|
||||
true
|
||||
else
|
||||
this.toBoolean()
|
||||
}
|
||||
|
||||
fun Boolean.toFieldValue(): String {
|
||||
return if (this) "1" else "0"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,5 +28,32 @@ data class Passkey(
|
||||
val privateKeyPem: String,
|
||||
val credentialId: String,
|
||||
val userHandle: String,
|
||||
val relyingParty: String
|
||||
): Parcelable
|
||||
val relyingParty: String,
|
||||
val backupEligibility: Boolean?,
|
||||
val backupState: Boolean?
|
||||
): Parcelable {
|
||||
// Do not compare BE and BS because are modifiable by the user
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Passkey
|
||||
|
||||
if (username != other.username) return false
|
||||
if (privateKeyPem != other.privateKeyPem) return false
|
||||
if (credentialId != other.credentialId) return false
|
||||
if (userHandle != other.userHandle) return false
|
||||
if (relyingParty != other.relyingParty) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = username.hashCode()
|
||||
result = 31 * result + privateKeyPem.hashCode()
|
||||
result = 31 * result + credentialId.hashCode()
|
||||
result = 31 * result + userHandle.hashCode()
|
||||
result = 31 * result + relyingParty.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.kunzisoft.keepass.model
|
||||
|
||||
import com.kunzisoft.keepass.database.element.Field
|
||||
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||
import com.kunzisoft.keepass.database.element.security.ProtectedString.Companion.toBooleanCompat
|
||||
import com.kunzisoft.keepass.database.element.security.ProtectedString.Companion.toFieldValue
|
||||
|
||||
object PasskeyEntryFields {
|
||||
|
||||
@@ -12,6 +14,8 @@ object PasskeyEntryFields {
|
||||
const val FIELD_CREDENTIAL_ID = "KPEX_PASSKEY_CREDENTIAL_ID"
|
||||
const val FIELD_USER_HANDLE = "KPEX_PASSKEY_USER_HANDLE"
|
||||
const val FIELD_RELYING_PARTY = "KPEX_PASSKEY_RELYING_PARTY"
|
||||
const val FIELD_FLAG_BE = "KPEX_PASSKEY_FLAG_BE"
|
||||
const val FIELD_FLAG_BS = "KPEX_PASSKEY_FLAG_BS"
|
||||
|
||||
const val PASSKEY_FIELD = "Passkey"
|
||||
const val PASSKEY_TAG = "Passkey"
|
||||
@@ -20,11 +24,14 @@ object PasskeyEntryFields {
|
||||
* Parse fields of an entry to retrieve a Passkey
|
||||
*/
|
||||
fun parseFields(getField: (id: String) -> String?): Passkey? {
|
||||
val usernameField = getField(FIELD_USERNAME)
|
||||
val privateKeyField = getField(FIELD_PRIVATE_KEY)
|
||||
val credentialIdField = getField(FIELD_CREDENTIAL_ID)
|
||||
val userHandleField = getField(FIELD_USER_HANDLE)
|
||||
val relyingPartyField = getField(FIELD_RELYING_PARTY)
|
||||
val usernameField: String? = getField(FIELD_USERNAME)
|
||||
val privateKeyField: String? = getField(FIELD_PRIVATE_KEY)
|
||||
val credentialIdField: String? = getField(FIELD_CREDENTIAL_ID)
|
||||
val userHandleField: String? = getField(FIELD_USER_HANDLE)
|
||||
val relyingPartyField: String? = getField(FIELD_RELYING_PARTY)
|
||||
// Optional fields
|
||||
val backupEligibilityField: Boolean? = getField(FIELD_FLAG_BE)?.toBooleanCompat()
|
||||
val backupStateField: Boolean? = getField(FIELD_FLAG_BS)?.toBooleanCompat()
|
||||
if (usernameField == null
|
||||
|| privateKeyField == null
|
||||
|| credentialIdField == null
|
||||
@@ -36,7 +43,9 @@ object PasskeyEntryFields {
|
||||
privateKeyPem = privateKeyField,
|
||||
credentialId = credentialIdField,
|
||||
userHandle = userHandleField,
|
||||
relyingParty = relyingPartyField
|
||||
relyingParty = relyingPartyField,
|
||||
backupEligibility = backupEligibilityField,
|
||||
backupState = backupStateField
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,6 +100,24 @@ object PasskeyEntryFields {
|
||||
ProtectedString(enableProtection = false, passkey.relyingParty)
|
||||
)
|
||||
)
|
||||
passkey.backupEligibility?.let { backupEligibility ->
|
||||
addOrReplaceField(
|
||||
Field(
|
||||
FIELD_FLAG_BE,
|
||||
ProtectedString(enableProtection = false,
|
||||
backupEligibility.toFieldValue())
|
||||
)
|
||||
)
|
||||
}
|
||||
passkey.backupState?.let { backupState ->
|
||||
addOrReplaceField(
|
||||
Field(
|
||||
FIELD_FLAG_BS,
|
||||
ProtectedString(enableProtection = false,
|
||||
backupState.toFieldValue())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
return overwrite
|
||||
}
|
||||
@@ -107,17 +134,23 @@ object PasskeyEntryFields {
|
||||
val credentialIdField = Field(FIELD_CREDENTIAL_ID)
|
||||
val userHandleField = Field(FIELD_USER_HANDLE)
|
||||
val relyingPartyField = Field(FIELD_RELYING_PARTY)
|
||||
val backupEligibilityField = Field(FIELD_FLAG_BE)
|
||||
val backupStateField = Field(FIELD_FLAG_BS)
|
||||
newCustomFields.remove(usernameField)
|
||||
newCustomFields.remove(privateKeyField)
|
||||
newCustomFields.remove(credentialIdField)
|
||||
newCustomFields.remove(userHandleField)
|
||||
newCustomFields.remove(relyingPartyField)
|
||||
// Empty auto generated OTP Token field
|
||||
newCustomFields.remove(backupEligibilityField)
|
||||
newCustomFields.remove(backupStateField)
|
||||
// Empty auto generated Passkey field
|
||||
if (fieldsToParse.contains(usernameField)
|
||||
|| fieldsToParse.contains(privateKeyField)
|
||||
|| fieldsToParse.contains(credentialIdField)
|
||||
|| fieldsToParse.contains(userHandleField)
|
||||
|| fieldsToParse.contains(relyingPartyField)
|
||||
|| fieldsToParse.contains(backupEligibilityField)
|
||||
|| fieldsToParse.contains(backupStateField)
|
||||
)
|
||||
newCustomFields.add(
|
||||
Field(
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
* Passkeys management #1421 (Thx @cali-95)
|
||||
* Passkeys management #1421
|
||||
* Add KPEX_PASSKEY_FLAG_BE and KPEX_PASSKEY_FLAG_BS flags #2212
|
||||
@@ -1 +1,2 @@
|
||||
* Gestion de Passkeys #1421 (Thx @cali-95)
|
||||
* Gestion de Passkeys #1421
|
||||
* Ajout des flags KPEX_PASSKEY_FLAG_BE et KPEX_PASSKEY_FLAG_BS #2212
|
||||
Reference in New Issue
Block a user