mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 02:01:36 +03:00
read device info on connect
This commit is contained in:
parent
11cb26ac5c
commit
2038ec0012
@ -40,6 +40,7 @@ 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
|
||||
@ -47,11 +48,13 @@ import com.yubico.authenticator.management.ManagementHandler
|
||||
import com.yubico.authenticator.oath.AppLinkMethodChannel
|
||||
import com.yubico.authenticator.oath.OathManager
|
||||
import com.yubico.authenticator.oath.OathViewModel
|
||||
import com.yubico.authenticator.yubikit.getDeviceInfo
|
||||
import com.yubico.yubikit.android.YubiKitManager
|
||||
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,24 +276,36 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
private fun processYubiKey(device: YubiKeyDevice) {
|
||||
lifecycleScope.launch {
|
||||
|
||||
if (device is NfcYubiKeyDevice) {
|
||||
// verify that current context supports connection provided by the YubiKey
|
||||
// if not, switch to a context which supports the connection
|
||||
val supportedApps = DeviceManager.getSupportedContexts(device)
|
||||
logger.debug("Connected key supports: {}", supportedApps)
|
||||
if (!supportedApps.contains(viewModel.appContext.value)) {
|
||||
val preferredContext = DeviceManager.getPreferredContext(supportedApps)
|
||||
logger.debug(
|
||||
"Current context ({}) is not supported by the key. Using preferred context {}",
|
||||
viewModel.appContext.value,
|
||||
preferredContext
|
||||
)
|
||||
switchContext(preferredContext)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if (contextManager == null) {
|
||||
switchContext(DeviceManager.getPreferredContext(supportedApps))
|
||||
}
|
||||
deviceManager.setDeviceInfo(deviceInfo)
|
||||
|
||||
if (deviceInfo == null) {
|
||||
return@launch
|
||||
}
|
||||
|
||||
val supportedContexts = DeviceManager.getSupportedContexts(deviceInfo)
|
||||
logger.debug("Connected key supports: {}", supportedContexts)
|
||||
if (!supportedContexts.contains(viewModel.appContext.value)) {
|
||||
val preferredContext = DeviceManager.getPreferredContext(supportedContexts)
|
||||
logger.debug(
|
||||
"Current context ({}) is not supported by the key. Using preferred context {}",
|
||||
viewModel.appContext.value,
|
||||
preferredContext
|
||||
)
|
||||
switchContext(preferredContext)
|
||||
}
|
||||
|
||||
if (contextManager == null) {
|
||||
switchContext(DeviceManager.getPreferredContext(supportedContexts))
|
||||
}
|
||||
|
||||
contextManager?.let {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Yubico.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.yubico.authenticator.device
|
||||
|
||||
import androidx.collection.ArraySet
|
||||
@ -8,10 +24,7 @@ import com.yubico.authenticator.MainViewModel
|
||||
import com.yubico.authenticator.OperationContext
|
||||
import com.yubico.yubikit.android.transport.usb.UsbYubiKeyDevice
|
||||
import com.yubico.yubikit.core.YubiKeyDevice
|
||||
import com.yubico.yubikit.core.fido.FidoConnection
|
||||
import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
||||
import com.yubico.yubikit.fido.ctap.Ctap2Session
|
||||
import com.yubico.yubikit.oath.OathSession
|
||||
import com.yubico.yubikit.management.Capability
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
interface DeviceListener {
|
||||
@ -45,49 +58,36 @@ class DeviceManager(
|
||||
const val NFC_DATA_CLEANUP_DELAY = 30L * 1000 // 30s
|
||||
private val logger = LoggerFactory.getLogger(DeviceManager::class.java)
|
||||
|
||||
fun getSupportedContexts(device: YubiKeyDevice) : ArraySet<OperationContext> = try {
|
||||
private val capabilityContextMap = mapOf(
|
||||
Capability.OATH to listOf(OperationContext.Oath),
|
||||
Capability.FIDO2 to listOf(
|
||||
OperationContext.FidoFingerprints,
|
||||
OperationContext.FidoPasskeys
|
||||
)
|
||||
)
|
||||
|
||||
fun getSupportedContexts(deviceInfo: Info): ArraySet<OperationContext> {
|
||||
val operationContexts = ArraySet<OperationContext>()
|
||||
|
||||
if (device.supportsConnection(SmartCardConnection::class.java)) {
|
||||
// try which apps are available
|
||||
device.openConnection(SmartCardConnection::class.java).use {
|
||||
try {
|
||||
OathSession(it)
|
||||
operationContexts.add(OperationContext.Oath)
|
||||
} catch (e: Throwable) { // ignored
|
||||
}
|
||||
val capabilities = (
|
||||
if (deviceInfo.isNfc)
|
||||
deviceInfo.config.enabledCapabilities.nfc else
|
||||
deviceInfo.config.enabledCapabilities.usb
|
||||
) ?: 0
|
||||
|
||||
try {
|
||||
Ctap2Session(it)
|
||||
operationContexts.add(OperationContext.FidoPasskeys)
|
||||
} catch (e: Throwable) { // ignored
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (device.supportsConnection(FidoConnection::class.java)) {
|
||||
device.openConnection(FidoConnection::class.java).use {
|
||||
try {
|
||||
Ctap2Session(it)
|
||||
operationContexts.add(OperationContext.FidoPasskeys)
|
||||
operationContexts.add(OperationContext.FidoFingerprints)
|
||||
} catch (e: Throwable) { // ignored
|
||||
}
|
||||
capabilityContextMap.forEach { entry ->
|
||||
if (capabilities and entry.key.bit == entry.key.bit) {
|
||||
operationContexts.addAll(entry.value)
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Device supports following contexts: {}", operationContexts)
|
||||
operationContexts
|
||||
} catch(e: Exception) {
|
||||
logger.debug("The device does not support any context. The following exception was caught: ", e)
|
||||
ArraySet<OperationContext>()
|
||||
return operationContexts
|
||||
}
|
||||
|
||||
fun getPreferredContext(contexts: ArraySet<OperationContext>) : OperationContext {
|
||||
fun getPreferredContext(contexts: ArraySet<OperationContext>): OperationContext {
|
||||
// custom sort
|
||||
for(context in contexts) {
|
||||
for (context in contexts) {
|
||||
if (context == OperationContext.Oath) {
|
||||
return context
|
||||
} else if (context == OperationContext.FidoPasskeys) {
|
||||
|
@ -188,21 +188,7 @@ class FidoManager(
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// something went wrong, try to get DeviceInfo from any available connection type
|
||||
logger.error("Failure when processing YubiKey", e)
|
||||
if (device.transport == Transport.USB || e is ApplicationNotAvailableException) {
|
||||
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
|
||||
}
|
||||
|
||||
logger.debug("Setting device info: {}", deviceInfo)
|
||||
deviceManager.setDeviceInfo(deviceInfo)
|
||||
}
|
||||
logger.error("Failure when processing YubiKey: ", e)
|
||||
|
||||
// Clear any cached FIDO state
|
||||
fidoViewModel.clearSessionState()
|
||||
@ -244,18 +230,6 @@ class FidoManager(
|
||||
fidoSession.cachedInfo, pinStore.hasPin()
|
||||
)
|
||||
)
|
||||
|
||||
// Update deviceInfo since the deviceId has changed
|
||||
val pid = (device as? UsbYubiKeyDevice)?.pid
|
||||
val deviceInfo = DeviceUtil.readInfo(connection, pid)
|
||||
deviceManager.setDeviceInfo(
|
||||
Info(
|
||||
name = DeviceUtil.getName(deviceInfo, pid?.type),
|
||||
isNfc = device.transport == Transport.NFC,
|
||||
usbPid = pid?.value,
|
||||
deviceInfo = deviceInfo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,18 +282,6 @@ class OathManager(
|
||||
return@withConnection
|
||||
}
|
||||
}
|
||||
|
||||
// Update deviceInfo since the deviceId has changed
|
||||
val pid = (device as? UsbYubiKeyDevice)?.pid
|
||||
val deviceInfo = DeviceUtil.readInfo(connection, pid)
|
||||
deviceManager.setDeviceInfo(
|
||||
Info(
|
||||
name = DeviceUtil.getName(deviceInfo, pid?.type),
|
||||
isNfc = device.transport == Transport.NFC,
|
||||
usbPid = pid?.value,
|
||||
deviceInfo = deviceInfo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
logger.debug(
|
||||
@ -301,21 +289,7 @@ class OathManager(
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
// OATH not enabled/supported, try to get DeviceInfo over other USB interfaces
|
||||
logger.error("Failed to connect to CCID", e)
|
||||
if (device.transport == Transport.USB || e is ApplicationNotAvailableException) {
|
||||
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
|
||||
}
|
||||
|
||||
logger.debug("Setting device info: {}", deviceInfo)
|
||||
deviceManager.setDeviceInfo(deviceInfo)
|
||||
}
|
||||
logger.error("Failed to connect to CCID: ", e)
|
||||
|
||||
// Clear any cached OATH state
|
||||
oathViewModel.clearSession()
|
||||
|
Loading…
Reference in New Issue
Block a user