This commit is contained in:
Adam Velebil 2023-06-01 09:58:00 +02:00
commit 861eae846c
No known key found for this signature in database
GPG Key ID: C9B1E4A3CBBD2E10
13 changed files with 150 additions and 144 deletions

View File

@ -105,6 +105,8 @@ dependencies {
implementation 'com.google.android.material:material:1.9.0'
implementation 'com.github.tony19:logback-android:3.0.0'
// testing dependencies
testImplementation "junit:junit:$project.junitVersion"
testImplementation "org.mockito:mockito-core:$project.mockitoVersion"

View File

@ -0,0 +1,31 @@
<!--
~ Copyright (C) 2023 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.
-->
<configuration xmlns="https://tony19.github.io/logback-android/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://tony19.github.io/logback-android/xml https://cdn.jsdelivr.net/gh/tony19/logback-android/logback.xsd" >
<appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
<encoder>
<pattern>%msg</pattern>
</encoder>
</appender>
<!-- Write TRACE (and higher-level) messages to logcat -->
<root level="TRACE">
<appender-ref ref="logcat" />
</root>
</configuration>

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +16,18 @@
package com.yubico.authenticator
import com.yubico.authenticator.logging.Log
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.CoroutineScope
import org.slf4j.LoggerFactory
class AppContext(messenger: BinaryMessenger, coroutineScope: CoroutineScope, private val appViewModel: MainViewModel) {
private val channel = MethodChannel(messenger, "android.state.appContext")
private val logger = LoggerFactory.getLogger(AppContext::class.java)
init {
channel.setHandler(coroutineScope) { method, args ->
when (method) {
@ -36,11 +40,7 @@ class AppContext(messenger: BinaryMessenger, coroutineScope: CoroutineScope, pri
private suspend fun setContext(subPageIndex: Int): String {
val appContext = OperationContext.getByValue(subPageIndex)
appViewModel.setAppContext(appContext)
Log.d(TAG, "App context is now $appContext")
logger.debug("App context is now {}", appContext)
return NULL
}
companion object {
const val TAG = "appContext"
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +19,8 @@ package com.yubico.authenticator
import android.content.Context
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import com.yubico.authenticator.logging.Log
import org.slf4j.LoggerFactory
class AppPreferences(context: Context) {
companion object {
@ -32,15 +33,15 @@ class AppPreferences(context: Context) {
const val PREF_CLIP_KBD_LAYOUT = "flutter.prefClipKbdLayout"
const val DEFAULT_CLIP_KBD_LAYOUT = "US"
const val TAG = "AppPreferences"
}
private val logger = LoggerFactory.getLogger(AppPreferences::class.java)
private val prefs: SharedPreferences =
context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE).also {
Log.d(TAG, "Current app preferences:")
logger.debug("Current app preferences:")
it.all.map { preference ->
Log.d(TAG, "${preference.key}: ${preference.value}")
logger.debug("{}: {}", preference.key, preference.value)
}
}
@ -66,12 +67,12 @@ class AppPreferences(context: Context) {
get() = prefs.getBoolean(PREF_USB_OPEN_APP, false)
fun registerListener(listener: OnSharedPreferenceChangeListener) {
Log.d(TAG, "registering change listener")
logger.debug("registering change listener")
prefs.registerOnSharedPreferenceChangeListener(listener)
}
fun unregisterListener(listener: OnSharedPreferenceChangeListener) {
prefs.unregisterOnSharedPreferenceChangeListener(listener)
Log.d(TAG, "unregistered change listener")
logger.debug("unregistered change listener")
}
}

View File

@ -23,11 +23,12 @@ import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.os.PersistableBundle
import com.yubico.authenticator.logging.Log
import org.slf4j.LoggerFactory
object ClipboardUtil {
private const val TAG = "ClipboardUtil"
private val logger = LoggerFactory.getLogger(ClipboardUtil::class.java)
fun setPrimaryClip(context: Context, toClipboard: String, isSensitive: Boolean) {
try {
@ -41,7 +42,7 @@ object ClipboardUtil {
clipboardManager.setPrimaryClip(clipData)
} catch (e: Exception) {
Log.e(TAG, "Failed to set string to clipboard", e.stackTraceToString())
logger.error( "Failed to set string to clipboard", e)
throw UnsupportedOperationException()
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -91,8 +91,4 @@ class DialogManager(messenger: BinaryMessenger, private val coroutineScope: Coro
}
return NULL
}
companion object {
const val TAG = "dialogManager"
}
}

View File

@ -39,7 +39,6 @@ import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import com.yubico.authenticator.logging.FlutterLog
import com.yubico.authenticator.logging.Log
import com.yubico.authenticator.oath.AppLinkMethodChannel
import com.yubico.authenticator.oath.OathManager
import com.yubico.authenticator.oath.OathViewModel
@ -48,7 +47,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.Logger
import com.yubico.yubikit.core.YubiKeyDevice
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
@ -56,6 +54,7 @@ import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.launch
import org.json.JSONObject
import org.slf4j.LoggerFactory
import java.io.Closeable
import java.util.concurrent.Executors
@ -73,6 +72,8 @@ class MainActivity : FlutterFragmentActivity() {
private val qrScannerCameraClosedBR = QRScannerCameraClosedBR()
private val nfcAdapterStateChangeBR = NfcAdapterStateChangedBR()
private val logger = LoggerFactory.getLogger(MainActivity::class.java)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -85,8 +86,6 @@ class MainActivity : FlutterFragmentActivity() {
allowScreenshots(false)
yubikit = YubiKitManager(this)
setupYubiKitLogger()
}
/**
@ -117,7 +116,7 @@ class MainActivity : FlutterFragmentActivity() {
private fun startNfcDiscovery() =
try {
Log.d(TAG, "Starting nfc discovery")
logger.debug("Starting nfc discovery")
yubikit.startNfcDiscovery(
nfcConfiguration.disableNfcDiscoverySound(appPreferences.silenceNfcSounds),
this,
@ -131,16 +130,16 @@ class MainActivity : FlutterFragmentActivity() {
private fun stopNfcDiscovery() {
if (hasNfc) {
yubikit.stopNfcDiscovery(this)
Log.d(TAG, "Stopped nfc discovery")
logger.debug("Stopped nfc discovery")
}
}
private fun startUsbDiscovery() {
Log.d(TAG, "Starting usb discovery")
logger.debug("Starting usb discovery")
val usbConfiguration = UsbConfiguration().handlePermissions(true)
yubikit.startUsbDiscovery(usbConfiguration) { device ->
viewModel.setConnectedYubiKey(device) {
Log.d(TAG, "YubiKey was disconnected, stopping usb discovery")
logger.debug("YubiKey was disconnected, stopping usb discovery")
stopUsbDiscovery()
}
processYubiKey(device)
@ -149,22 +148,7 @@ class MainActivity : FlutterFragmentActivity() {
private fun stopUsbDiscovery() {
yubikit.stopUsbDiscovery()
Log.d(TAG, "Stopped usb discovery")
}
private fun setupYubiKitLogger() {
Logger.setLogger(object : Logger() {
private val TAG = "yubikit"
override fun logDebug(message: String) {
// redirect yubikit debug logs to traffic
Log.t(TAG, message)
}
override fun logError(message: String, throwable: Throwable) {
Log.e(TAG, message, throwable.message ?: throwable.toString())
}
})
logger.debug("Stopped usb discovery")
}
@SuppressLint("WrongConstant")
@ -230,7 +214,7 @@ class MainActivity : FlutterFragmentActivity() {
startNfcDiscovery()
}
} catch (e: Throwable) {
Log.e(TAG, "Error processing YubiKey in AppContextManager", e.toString())
logger.error("Error processing YubiKey in AppContextManager", e)
}
}
} else {
@ -280,7 +264,7 @@ class MainActivity : FlutterFragmentActivity() {
try {
it.processYubiKey(device)
} catch (e: Throwable) {
Log.e(TAG, "Error processing YubiKey in AppContextManager", e.toString())
logger.error("Error processing YubiKey in AppContextManager", e)
}
}
}
@ -336,7 +320,6 @@ class MainActivity : FlutterFragmentActivity() {
}
companion object {
const val TAG = "MainActivity"
const val YUBICO_VENDOR_ID = 4176
const val FLAG_SECURE = WindowManager.LayoutParams.FLAG_SECURE
}
@ -363,6 +346,9 @@ class MainActivity : FlutterFragmentActivity() {
}
class NfcAdapterStateChangedBR : BroadcastReceiver() {
private val logger = LoggerFactory.getLogger(NfcAdapterStateChangedBR::class.java)
companion object {
val intentFilter = IntentFilter("android.nfc.action.ADAPTER_STATE_CHANGED")
}
@ -370,7 +356,7 @@ class MainActivity : FlutterFragmentActivity() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.let {
val state = it.getIntExtra("android.nfc.extra.ADAPTER_STATE", 0)
Log.d(TAG, "NfcAdapter state changed to $state")
logger.debug("NfcAdapter state changed to {}", state)
if (state == STATE_ON || state == STATE_TURNING_OFF) {
(context as? MainActivity)?.appMethodChannel?.nfcAdapterStateChanged(state == STATE_ON)
}
@ -430,7 +416,7 @@ class MainActivity : FlutterFragmentActivity() {
startActivity(Intent(ACTION_NFC_SETTINGS))
result.success(true)
}
else -> Log.w(TAG, "Unknown app method: ${methodCall.method}")
else -> logger.warn("Unknown app method: {}", methodCall.method)
}
}
}
@ -446,10 +432,10 @@ class MainActivity : FlutterFragmentActivity() {
private fun allowScreenshots(value: Boolean): Boolean {
// Note that FLAG_SECURE is the inverse of allowScreenshots
if (value) {
Log.d(TAG, "Clearing FLAG_SECURE (allow screenshots)")
logger.debug("Clearing FLAG_SECURE (allow screenshots)")
window.clearFlags(FLAG_SECURE)
} else {
Log.d(TAG, "Setting FLAG_SECURE (disallow screenshots)")
logger.debug("Setting FLAG_SECURE (disallow screenshots)")
window.setFlags(FLAG_SECURE, FLAG_SECURE)
}

View File

@ -24,9 +24,12 @@ import android.nfc.Tag
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import com.yubico.authenticator.logging.Log
import com.yubico.authenticator.ndef.KeyboardLayout
import com.yubico.yubikit.core.util.NdefUtils
import org.slf4j.LoggerFactory
import java.nio.charset.StandardCharsets
typealias ResourceId = Int
@ -34,6 +37,8 @@ typealias ResourceId = Int
class NdefActivity : Activity() {
private lateinit var appPreferences: AppPreferences
private val logger = LoggerFactory.getLogger(NdefActivity::class.java)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
appPreferences = AppPreferences(this)
@ -68,10 +73,9 @@ class NdefActivity : Activity() {
}
} catch (illegalArgumentException: IllegalArgumentException) {
Log.e(
TAG,
logger.error(
illegalArgumentException.message ?: "Failure when handling YubiKey OTP",
illegalArgumentException.stackTraceToString()
illegalArgumentException
)
showToast(R.string.otp_parse_failure, Toast.LENGTH_LONG)
} catch (_: UnsupportedOperationException) {
@ -111,10 +115,6 @@ class NdefActivity : Activity() {
}
}
companion object {
const val TAG = "YubicoAuthenticatorOTPActivity"
}
enum class OtpType {
Otp, Password
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +16,16 @@
package com.yubico.authenticator.logging
import android.util.Log
import ch.qos.logback.classic.Level
import com.yubico.authenticator.BuildConfig
import org.slf4j.Logger
import org.slf4j.LoggerFactory
object Log {
private val logger = LoggerFactory.getLogger("com.yubico.authenticator")
enum class LogLevel {
TRAFFIC,
DEBUG,
@ -42,34 +47,10 @@ object Log {
LogLevel.INFO
}
private const val TAG = "yubico-authenticator"
@Suppress("unused")
fun t(tag: String, message: String, error: String? = null) {
log(LogLevel.TRAFFIC, tag, message, error)
init {
setLevel(level)
}
@Suppress("unused")
fun d(tag: String, message: String, error: String? = null) {
log(LogLevel.DEBUG, tag, message, error)
}
@Suppress("unused")
fun i(tag: String, message: String, error: String? = null) {
log(LogLevel.INFO, tag, message, error)
}
@Suppress("unused")
fun w(tag: String, message: String, error: String? = null) {
log(LogLevel.WARNING, tag, message, error)
}
@Suppress("unused")
fun e(tag: String, message: String, error: String? = null) {
log(LogLevel.ERROR, tag, message, error)
}
@Suppress("unused")
fun log(level: LogLevel, loggerName: String, message: String, error: String?) {
if (level < this.level) {
return
@ -79,27 +60,33 @@ object Log {
buffer.removeAt(0)
}
val logMessage = "[$loggerName] ${level.name}: $message".also {
buffer.add(it)
}
val logMessage = (if (error == null)
"[$loggerName] ${level.name}: $message"
else
"[$loggerName] ${level.name}: $message (err: $error)"
).also {
buffer.add(it)
}
when (level) {
LogLevel.TRAFFIC -> Log.v(TAG, logMessage)
LogLevel.DEBUG -> Log.d(TAG, logMessage)
LogLevel.INFO -> Log.i(TAG, logMessage)
LogLevel.WARNING -> Log.w(TAG, logMessage)
LogLevel.ERROR -> Log.e(TAG, logMessage)
}
error?.let {
Log.e(TAG, "[$loggerName] ${level.name}(details): $error".also {
buffer.add(it)
})
LogLevel.TRAFFIC -> logger.trace(logMessage)
LogLevel.DEBUG -> logger.debug(logMessage)
LogLevel.INFO -> logger.info(logMessage)
LogLevel.WARNING -> logger.warn(logMessage)
LogLevel.ERROR -> logger.error(logMessage)
}
}
@Suppress("unused")
fun setLevel(newLevel: LogLevel) {
level = newLevel
val root = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger
root.level = when (newLevel) {
LogLevel.TRAFFIC -> Level.TRACE
LogLevel.DEBUG -> Level.DEBUG
LogLevel.INFO -> Level.INFO
LogLevel.WARNING -> Level.WARN
LogLevel.ERROR -> Level.ERROR
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,25 +17,26 @@
package com.yubico.authenticator.oath
import android.net.Uri
import androidx.annotation.UiThread
import com.yubico.authenticator.logging.Log
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodChannel
import org.json.JSONObject
import org.slf4j.LoggerFactory
class AppLinkMethodChannel(messenger: BinaryMessenger) {
private val methodChannel = MethodChannel(messenger, "app.link.methods")
private val logger = LoggerFactory.getLogger(AppLinkMethodChannel::class.java)
@UiThread
fun handleUri(uri: Uri) {
Log.t(TAG, "Handling URI: $uri")
logger.trace("Handling URI: {}", uri)
methodChannel.invokeMethod(
"handleOtpAuthLink",
JSONObject(mapOf("link" to uri.toString())).toString()
)
}
companion object {
const val TAG = "AppLinkMethodChannel"
}
}

View File

@ -61,6 +61,7 @@ import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.*
import kotlinx.serialization.encodeToString
import org.slf4j.LoggerFactory
import java.net.URI
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean
@ -77,7 +78,6 @@ class OathManager(
private val appPreferences: AppPreferences,
) : AppContextManager {
companion object {
const val TAG = "OathManager"
const val NFC_DATA_CLEANUP_DELAY = 30L * 1000 // 30s
val OTP_AID = byteArrayOf(0xa0.toByte(), 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01)
}
@ -98,6 +98,8 @@ class OathManager(
)
}
private val logger = LoggerFactory.getLogger(OathManager::class.java)
@TargetApi(Build.VERSION_CODES.M)
private fun createKeyStoreProviderM(): KeyProvider = KeyStoreProvider()
@ -117,7 +119,7 @@ class OathManager(
// cancel any pending actions, except for addToAny
if (!addToAny) {
pendingAction?.let {
Log.d(TAG, "Cancelling pending action/closing nfc dialog.")
logger.debug("Cancelling pending action/closing nfc dialog.")
it.invoke(Result.failure(CancellationException()))
coroutineScope.launch {
dialogManager.closeDialog()
@ -134,7 +136,7 @@ class OathManager(
if (canInvoke) {
if (appViewModel.connectedYubiKey.value == null) {
// no USB YubiKey is connected, reset known data on resume
Log.d(TAG, "Removing NFC data after resume.")
logger.debug("Removing NFC data after resume.")
appViewModel.setDeviceInfo(null)
oathViewModel.setSessionState(null)
}
@ -169,7 +171,7 @@ class OathManager(
refreshJob = coroutineScope.launch {
val delayMs = earliest - now
Log.d(TAG, "Will execute refresh in ${delayMs}ms")
logger.debug("Will execute refresh in {}ms", delayMs)
if (delayMs > 0) {
delay(delayMs)
}
@ -177,9 +179,9 @@ class OathManager(
if (currentState.isAtLeast(Lifecycle.State.RESUMED)) {
requestRefresh()
} else {
Log.d(
TAG,
"Cannot run credential refresh in current lifecycle state: $currentState"
logger.debug(
"Cannot run credential refresh in current lifecycle state: {}",
currentState
)
}
}
@ -257,7 +259,7 @@ class OathManager(
try {
oathViewModel.updateCredentials(calculateOathCodes(session))
} catch (error: Exception) {
Log.e(TAG, "Failed to refresh codes", error.toString())
logger.error("Failed to refresh codes", error)
}
}
} else {
@ -296,7 +298,7 @@ class OathManager(
try {
SmartCardProtocol(connection).select(OTP_AID)
} catch (e: Exception) {
Log.e(TAG, "Failed to recognize this OATH device.")
logger.error("Failed to recognize this OATH device.")
// we know this is NFC device and it supports OATH
val oathCapabilities = Capabilities(nfc = 0x20)
appViewModel.setDeviceInfo(
@ -325,25 +327,24 @@ class OathManager(
}
}
Log.d(
TAG,
logger.debug(
"Successfully read Oath session info (and credentials if unlocked) from connected key"
)
} catch (e: Exception) {
// OATH not enabled/supported, try to get DeviceInfo over other USB interfaces
Log.e(TAG, "Failed to connect to CCID", e.toString())
logger.error("Failed to connect to CCID", e)
if (device.transport == Transport.USB || e is ApplicationNotAvailableException) {
val deviceInfo = try {
getDeviceInfo(device)
} catch (e: IllegalArgumentException) {
Log.d(TAG, "Device was not recognized")
logger.debug("Device was not recognized")
UnknownDevice.copy(isNfc = device.transport == Transport.NFC)
} catch (e: Exception) {
Log.d(TAG, "Failure getting device info: ${e.message}")
logger.error("Failure getting device info", e)
null
}
Log.d(TAG, "Setting device info: $deviceInfo")
logger.debug("Setting device info: {}", deviceInfo)
appViewModel.setDeviceInfo(deviceInfo)
}
@ -377,7 +378,7 @@ class OathManager(
Code.from(code)
)
Log.d(TAG, "Added cred $credential")
logger.debug("Added cred {}", credential)
jsonSerializer.encodeToString(addedCred)
}
}
@ -431,7 +432,7 @@ class OathManager(
session.setAccessKey(accessKey)
keyManager.addKey(session.deviceId, accessKey, false)
oathViewModel.setSessionState(Session(session, false))
Log.d(TAG, "Successfully set password")
logger.debug("Successfully set password")
NULL
}
@ -443,7 +444,7 @@ class OathManager(
session.deleteAccessKey()
keyManager.removeKey(session.deviceId)
oathViewModel.setSessionState(Session(session, false))
Log.d(TAG, "Successfully unset password")
logger.debug("Successfully unset password")
return@useOathSession NULL
}
}
@ -452,7 +453,7 @@ class OathManager(
private suspend fun forgetPassword(): String {
keyManager.clearAll()
Log.d(TAG, "Cleared all keys.")
logger.debug("Cleared all keys.")
oathViewModel.sessionState.value?.let {
oathViewModel.setSessionState(
it.copy(
@ -516,7 +517,7 @@ class OathManager(
oathViewModel.updateCredentials(calculateOathCodes(session))
} catch (apduException: ApduException) {
if (apduException.sw == SW.SECURITY_CONDITION_NOT_SATISFIED) {
Log.d(TAG, "Handled oath credential refresh on locked session.")
logger.debug("Handled oath credential refresh on locked session.")
oathViewModel.setSessionState(
Session(
session,
@ -524,10 +525,9 @@ class OathManager(
)
)
} else {
Log.e(
TAG,
logger.error(
"Unexpected sw when refreshing oath credentials",
apduException.message
apduException
)
}
}
@ -543,7 +543,7 @@ class OathManager(
Credential(credential, session.deviceId),
code
)
Log.d(TAG, "Code calculated $code")
logger.debug("Code calculated {}", code)
jsonSerializer.encodeToString(code)
}
@ -679,7 +679,7 @@ class OathManager(
})
}
dialogManager.showDialog(Icon.NFC, "Tap your key", title) {
Log.d(TAG, "Cancelled Dialog $title")
logger.debug("Cancelled Dialog {}", title)
pendingAction?.invoke(Result.failure(CancellationException()))
pendingAction = null
}

View File

@ -17,8 +17,6 @@
package com.yubico.authenticator.yubikit
import com.yubico.authenticator.device.Info
import com.yubico.authenticator.logging.Log
import com.yubico.authenticator.oath.OathManager
import com.yubico.authenticator.compatUtil
import com.yubico.yubikit.android.transport.nfc.NfcYubiKeyDevice
import com.yubico.yubikit.android.transport.usb.UsbYubiKeyDevice
@ -29,22 +27,25 @@ import com.yubico.yubikit.core.smartcard.SmartCardConnection
import com.yubico.yubikit.management.DeviceInfo
import com.yubico.yubikit.support.DeviceUtil
import org.slf4j.LoggerFactory
suspend fun getDeviceInfo(device: YubiKeyDevice): Info {
val pid = (device as? UsbYubiKeyDevice)?.pid
val logger = LoggerFactory.getLogger("getDeviceInfo")
val deviceInfo = runCatching {
device.withConnection<SmartCardConnection, DeviceInfo> { DeviceUtil.readInfo(it, pid) }
}.recoverCatching { t ->
Log.d(OathManager.TAG, "Smart card connection not available: ${t.message}")
logger.debug("Smart card connection not available: {}", t.message)
device.withConnection<OtpConnection, DeviceInfo> { DeviceUtil.readInfo(it, pid) }
}.recoverCatching { t ->
Log.d(OathManager.TAG, "OTP connection not available: ${t.message}")
logger.debug("OTP connection not available: {}", t.message)
device.withConnection<FidoConnection, DeviceInfo> { DeviceUtil.readInfo(it, pid) }
}.recoverCatching { t ->
Log.d(OathManager.TAG, "FIDO connection not available: ${t.message}")
logger.debug("FIDO connection not available: {}", t.message)
return SkyHelper(compatUtil).getDeviceInfo(device)
}.getOrElse {
Log.e(OathManager.TAG, "Failed to recognize device: ${it.message}")
logger.debug("Failed to recognize device: {}", it.message)
throw it
}

View File

@ -24,7 +24,7 @@ allprojects {
targetSdkVersion = 33
compileSdkVersion = 33
yubiKitVersion = "2.2.0"
yubiKitVersion = "2.3.0"
junitVersion = "4.13.2"
mockitoVersion = "5.3.1"
}