mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 10:11:52 +03:00
Merge PR #1096.
This commit is contained in:
commit
861eae846c
@ -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"
|
||||
|
31
android/app/src/main/assets/logback.xml
Normal file
31
android/app/src/main/assets/logback.xml
Normal 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>
|
@ -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"
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user