implement delete FIDO credential

This commit is contained in:
Adam Velebil 2024-01-05 10:31:14 +01:00
parent 5f4b136718
commit 8292d557a1
No known key found for this signature in database
GPG Key ID: C9B1E4A3CBBD2E10
11 changed files with 78 additions and 24 deletions

View File

@ -22,7 +22,8 @@ enum class FidoActionDescription(private val value: Int) {
Reset(0),
Unlock(1),
SetPin(2),
ActionFailure(3);
DeleteCredential(3),
ActionFailure(4);
val id: Int
get() = value + dialogDescriptionOathIndex

View File

@ -20,7 +20,6 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import com.yubico.authenticator.AppContextManager
import com.yubico.authenticator.AppPreferences
import com.yubico.authenticator.DialogIcon
import com.yubico.authenticator.DialogManager
import com.yubico.authenticator.DialogTitle
@ -159,8 +158,8 @@ class FidoManager(
)
"delete_credential" -> deleteCredential(
args["rpId"] as String,
args["credentialId"] as String
args["rp_id"] as String,
args["credential_id"] as String
)
else -> throw NotImplementedError()
@ -390,12 +389,13 @@ class FidoManager(
val rpIds = credMan.enumerateRps()
val credentials = rpIds.map { rpData ->
credMan.enumerateCredentials(rpData.rpIdHash).map { credential ->
credMan.enumerateCredentials(rpData.rpIdHash).map { credentialData ->
FidoCredential(
rpData.rp["id"] as String,
(credential.credentialId["id"] as ByteArray).asString(),
(credential.user["id"] as ByteArray).asString(),
credential.user["name"] as String
(credentialData.credentialId["id"] as ByteArray).asString(),
(credentialData.user["id"] as ByteArray).asString(),
credentialData.user["name"] as String,
publicKeyCredentialDescriptor = credentialData.credentialId
)
}
}.reduceOrNull { credentials, credentialList ->
@ -408,8 +408,30 @@ class FidoManager(
}
private suspend fun deleteCredential(rpId: String, credentialId: String): String =
useSession(FidoActionDescription.SetPin) { _ ->
""
useSession(FidoActionDescription.DeleteCredential) { fidoSession ->
val credMan = CredentialManagement(fidoSession, clientPin!!.pinUvAuth, token!!)
val credentialDescriptor =
fidoViewModel.credentials.value?.firstOrNull {
it.credentialId == credentialId && it.rpId == rpId
}?.publicKeyCredentialDescriptor
credentialDescriptor?.let {
credMan.deleteCredential(credentialDescriptor)
fidoViewModel.removeCredential(rpId, credentialId)
return@useSession JSONObject(
mapOf(
"success" to true,
)
).toString()
}
// could not find the credential to delete
JSONObject(
mapOf(
"success" to false,
)
).toString()
}
private suspend fun <T> useSession(

View File

@ -37,4 +37,10 @@ class FidoViewModel : ViewModel() {
fun updateCredentials(credentials: List<FidoCredential>) {
_credentials.postValue(credentials)
}
fun removeCredential(rpId: String, credentialId: String) {
_credentials.postValue(_credentials.value?.filter {
it.credentialId != credentialId || it.rpId != rpId
})
}
}

View File

@ -18,6 +18,7 @@ package com.yubico.authenticator.fido.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
@Serializable
data class FidoCredential(
@ -28,5 +29,7 @@ data class FidoCredential(
@SerialName("user_id")
val userId: String,
@SerialName("user_name")
val userName: String
val userName: String,
@Transient
val publicKeyCredentialDescriptor: Map<String, Any?> = emptyMap()
)

View File

@ -161,8 +161,6 @@ class _FidoCredentialsNotifier extends FidoCredentialsNotifier {
if (json == null) {
state = const AsyncValue.loading();
} else {
// final fidoState = FidoState.fromJson(json);
_log.debug('Received event: $event');
List<FidoCredential> newState = List.from(
(json as List).map((e) => FidoCredential.fromJson(e)).toList());
state = AsyncValue.data(newState);
@ -172,18 +170,34 @@ class _FidoCredentialsNotifier extends FidoCredentialsNotifier {
});
ref.onDispose(_sub.cancel);
// final fidoState = ref.watch(fidoStateProvider(devicePath));
// // fidoState.whenData((value) async {
// // if (value.unlocked) {
// // final unlockResponse =
// // jsonDecode(await _methods.invokeMethod('credentials'));
// // }
// // });
return Completer<List<FidoCredential>>().future;
}
@override
Future<void> deleteCredential(FidoCredential credential) async {}
Future<void> deleteCredential(FidoCredential credential) async {
try {
final deleteCredentialResponse = jsonDecode(await _methods.invokeMethod(
'delete_credential',
{
'rp_id': credential.rpId,
'credential_id': credential.credentialId,
},
));
if (deleteCredentialResponse['success'] == true) {
_log.debug('FIDO delete credential succeeded');
} else {
_log.debug('FIDO delete credential failed');
}
} on PlatformException catch (pe) {
var decodedException = pe.decode();
if (decodedException is CancellationException) {
_log.debug('User cancelled delete credential FIDO operation');
} else {
_log.error('Delete credential FIDO operation failed.', pe);
}
throw decodedException;
}
}
}

View File

@ -77,6 +77,7 @@ enum _DDesc {
fidoResetApplet,
fidoUnlockSession,
fidoSetPin,
fidoDeleteCredential,
fidoActionFailure,
// Others
invalid;
@ -99,7 +100,8 @@ enum _DDesc {
dialogDescriptionFidoIndex + 0: fidoResetApplet,
dialogDescriptionFidoIndex + 1: fidoUnlockSession,
dialogDescriptionFidoIndex + 2: fidoSetPin,
dialogDescriptionFidoIndex + 3: fidoActionFailure,
dialogDescriptionFidoIndex + 3: fidoDeleteCredential,
dialogDescriptionFidoIndex + 4: fidoActionFailure,
}[id] ??
_DDesc.invalid;
}
@ -176,6 +178,7 @@ class _DialogProvider {
_DDesc.fidoResetApplet => l10n.s_nfc_dialog_fido_reset,
_DDesc.fidoUnlockSession => l10n.s_nfc_dialog_fido_unlock,
_DDesc.fidoSetPin => l10n.l_nfc_dialog_fido_set_pin,
_DDesc.fidoDeleteCredential => l10n.s_nfc_dialog_fido_delete_credential,
_DDesc.fidoActionFailure => l10n.s_nfc_dialog_fido_failure,
_ => ''
};

View File

@ -725,6 +725,7 @@
"s_nfc_dialog_fido_reset": null,
"s_nfc_dialog_fido_unlock": null,
"l_nfc_dialog_fido_set_pin": null,
"s_nfc_dialog_fido_delete_credential": null,
"s_nfc_dialog_fido_failure": null,
"@_ndef": {},

View File

@ -725,6 +725,7 @@
"s_nfc_dialog_fido_reset": "Action: reset FIDO applet",
"s_nfc_dialog_fido_unlock": "Action: unlock FIDO applet",
"l_nfc_dialog_fido_set_pin": "Action: set or change FIDO applet PIN",
"s_nfc_dialog_fido_delete_credential": "Action: delete Passkey",
"s_nfc_dialog_fido_failure": "FIDO operation failed",
"@_ndef": {},

View File

@ -725,6 +725,7 @@
"s_nfc_dialog_fido_reset": null,
"s_nfc_dialog_fido_unlock": null,
"l_nfc_dialog_fido_set_pin": null,
"s_nfc_dialog_fido_delete_credential": null,
"s_nfc_dialog_fido_failure": null,
"@_ndef": {},

View File

@ -725,6 +725,7 @@
"s_nfc_dialog_fido_reset": null,
"s_nfc_dialog_fido_unlock": null,
"l_nfc_dialog_fido_set_pin": null,
"s_nfc_dialog_fido_delete_credential": null,
"s_nfc_dialog_fido_failure": null,
"@_ndef": {},

View File

@ -725,6 +725,7 @@
"s_nfc_dialog_fido_reset": null,
"s_nfc_dialog_fido_unlock": null,
"l_nfc_dialog_fido_set_pin": null,
"s_nfc_dialog_fido_delete_credential": null,
"s_nfc_dialog_fido_failure": null,
"@_ndef": {},