From ca5f82f665e90c5f0158a7d212c40f886d025a1c Mon Sep 17 00:00:00 2001 From: Adam Velebil Date: Fri, 19 Apr 2024 16:01:35 +0200 Subject: [PATCH] probe hw key applications if getDeviceInfo fails --- .../com/yubico/authenticator/MainActivity.kt | 14 +------ .../authenticator/device/UnknownDevice.kt | 29 ++++++++++++++- .../authenticator/yubikit/DeviceInfoHelper.kt | 37 +++++++++++++++++-- lib/home/views/home_screen.dart | 4 +- 4 files changed, 65 insertions(+), 19 deletions(-) diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/MainActivity.kt b/android/app/src/main/kotlin/com/yubico/authenticator/MainActivity.kt index 5c18841f..36a7eed1 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/MainActivity.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/MainActivity.kt @@ -40,7 +40,6 @@ import androidx.core.view.WindowCompat import androidx.lifecycle.lifecycleScope import com.google.android.material.color.DynamicColors import com.yubico.authenticator.device.DeviceManager -import com.yubico.authenticator.device.UnknownDevice import com.yubico.authenticator.fido.FidoManager import com.yubico.authenticator.fido.FidoViewModel import com.yubico.authenticator.logging.FlutterLog @@ -54,7 +53,6 @@ import com.yubico.yubikit.android.transport.nfc.NfcConfiguration import com.yubico.yubikit.android.transport.nfc.NfcNotAvailable import com.yubico.yubikit.android.transport.nfc.NfcYubiKeyDevice import com.yubico.yubikit.android.transport.usb.UsbConfiguration -import com.yubico.yubikit.core.Transport import com.yubico.yubikit.core.YubiKeyDevice import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.FlutterEngine @@ -273,17 +271,7 @@ class MainActivity : FlutterFragmentActivity() { private fun processYubiKey(device: YubiKeyDevice) { lifecycleScope.launch { - - val deviceInfo = try { - getDeviceInfo(device) - } catch (e: IllegalArgumentException) { - logger.debug("Device was not recognized") - UnknownDevice.copy(isNfc = device.transport == Transport.NFC) - } catch (e: Exception) { - logger.error("Failure getting device info", e) - null - } - + val deviceInfo = getDeviceInfo(device) deviceManager.setDeviceInfo(deviceInfo) if (deviceInfo == null) { diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/device/UnknownDevice.kt b/android/app/src/main/kotlin/com/yubico/authenticator/device/UnknownDevice.kt index 6fc29f9c..52b61e2b 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/device/UnknownDevice.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/device/UnknownDevice.kt @@ -1,5 +1,7 @@ package com.yubico.authenticator.device +import com.yubico.yubikit.core.Transport +import com.yubico.yubikit.management.Capability import com.yubico.yubikit.management.FormFactor val UnknownDevice = Info( @@ -20,4 +22,29 @@ val UnknownDevice = Info( usbPid = null, pinComplexity = false, supportedCapabilities = Capabilities() -) \ No newline at end of file +) + +fun unknownDeviceWithCapability(transport: Transport, bit: Int = 0) : Info { + val isNfc = transport == Transport.NFC + val capabilities = Capabilities( + nfc = if (isNfc) bit else null, + usb = if (!isNfc) bit else null + ) + return UnknownDevice.copy( + isNfc = isNfc, + config = UnknownDevice.config.copy(enabledCapabilities = capabilities), + supportedCapabilities = capabilities + ) +} + +fun unknownOathDeviceInfo(transport: Transport) : Info { + return unknownDeviceWithCapability(transport, Capability.OATH.bit).copy( + name = "OATH device" + ) +} + +fun unknownFido2DeviceInfo(transport: Transport) : Info { + return unknownDeviceWithCapability(transport, Capability.FIDO2.bit).copy( + name = "FIDO2 device" + ) +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/yubikit/DeviceInfoHelper.kt b/android/app/src/main/kotlin/com/yubico/authenticator/yubikit/DeviceInfoHelper.kt index 9a985c92..c4a9904c 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/yubikit/DeviceInfoHelper.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/yubikit/DeviceInfoHelper.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023 Yubico. + * Copyright (C) 2022-2024 Yubico. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,18 +18,24 @@ package com.yubico.authenticator.yubikit import com.yubico.authenticator.device.Info import com.yubico.authenticator.compatUtil +import com.yubico.authenticator.device.unknownDeviceWithCapability +import com.yubico.authenticator.device.unknownFido2DeviceInfo +import com.yubico.authenticator.device.unknownOathDeviceInfo import com.yubico.yubikit.android.transport.nfc.NfcYubiKeyDevice import com.yubico.yubikit.android.transport.usb.UsbYubiKeyDevice import com.yubico.yubikit.core.YubiKeyDevice +import com.yubico.yubikit.core.application.ApplicationNotAvailableException import com.yubico.yubikit.core.fido.FidoConnection import com.yubico.yubikit.core.otp.OtpConnection import com.yubico.yubikit.core.smartcard.SmartCardConnection +import com.yubico.yubikit.fido.ctap.Ctap2Session import com.yubico.yubikit.management.DeviceInfo +import com.yubico.yubikit.oath.OathSession import com.yubico.yubikit.support.DeviceUtil import org.slf4j.LoggerFactory -suspend fun getDeviceInfo(device: YubiKeyDevice): Info { +suspend fun getDeviceInfo(device: YubiKeyDevice): Info? { val pid = (device as? UsbYubiKeyDevice)?.pid val logger = LoggerFactory.getLogger("getDeviceInfo") @@ -45,8 +51,31 @@ suspend fun getDeviceInfo(device: YubiKeyDevice): Info { logger.debug("FIDO connection not available: {}", t.message) return SkyHelper(compatUtil).getDeviceInfo(device) }.getOrElse { - logger.debug("Failed to recognize device: {}", it.message) - throw it + // this is not a YubiKey + logger.debug("Probing unknown device") + try { + device.openConnection(SmartCardConnection::class.java).use { smartCardConnection -> + try { + // if OATH session is available use it + OathSession(smartCardConnection) + logger.debug("Device supports OATH") + return unknownOathDeviceInfo(device.transport) + } catch (applicationNotAvailable: ApplicationNotAvailableException) { + try { + // probe for CTAP2 availability + Ctap2Session(smartCardConnection) + logger.debug("Device supports FIDO2") + return unknownFido2DeviceInfo(device.transport) + } catch (applicationNotAvailable: ApplicationNotAvailableException) { + logger.debug("Device not recognized") + return unknownDeviceWithCapability(device.transport) + } + } + } + } catch (e: Exception) { + // no smart card connectivity + return null + } } val name = DeviceUtil.getName(deviceInfo, pid?.type) diff --git a/lib/home/views/home_screen.dart b/lib/home/views/home_screen.dart index 3f6afde1..a0cb019c 100644 --- a/lib/home/views/home_screen.dart +++ b/lib/home/views/home_screen.dart @@ -141,7 +141,9 @@ class _DeviceContent extends ConsumerWidget { final name = deviceData.name; final serial = deviceData.info.serial; - final version = deviceData.info.version; + final version = deviceData.info.version == const Version(0, 0, 0) + ? 'unknown' + : deviceData.info.version; final label = initialCustomization?.name; String displayName = label != null ? '$label ($name)' : name;