mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 10:11:52 +03:00
prevent crashes by catching exceptions
This commit is contained in:
parent
e86c50fb64
commit
8b8cdf40bb
@ -1,51 +0,0 @@
|
||||
package com.yubico.authenticator.data.device
|
||||
|
||||
import com.yubico.yubikit.core.Transport
|
||||
import com.yubico.yubikit.core.Version
|
||||
import com.yubico.yubikit.management.DeviceConfig
|
||||
import com.yubico.yubikit.management.DeviceInfo
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
|
||||
fun DeviceConfig.toJson() = JsonObject(
|
||||
mapOf(
|
||||
"device_flags" to JsonPrimitive(deviceFlags),
|
||||
"challenge_response_timeout" to JsonPrimitive(challengeResponseTimeout),
|
||||
"auto_eject_timeout" to JsonPrimitive(autoEjectTimeout),
|
||||
"enabled_capabilities" to JsonObject(
|
||||
mapOf(
|
||||
"usb" to JsonPrimitive(getEnabledCapabilities(Transport.USB) ?: 0),
|
||||
"nfc" to JsonPrimitive(getEnabledCapabilities(Transport.NFC) ?: 0),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun Version.toJson() = JsonArray(
|
||||
listOf(
|
||||
JsonPrimitive(major),
|
||||
JsonPrimitive(minor),
|
||||
JsonPrimitive(micro)
|
||||
)
|
||||
)
|
||||
|
||||
fun DeviceInfo.toJson(name: String, isNfcDevice: Boolean) = JsonObject(
|
||||
mapOf(
|
||||
"config" to config.toJson(),
|
||||
"serial" to JsonPrimitive(serialNumber),
|
||||
"version" to version.toJson(),
|
||||
"form_factor" to JsonPrimitive(formFactor.value),
|
||||
"is_locked" to JsonPrimitive(isLocked),
|
||||
"is_sky" to JsonPrimitive(isSky),
|
||||
"is_fips" to JsonPrimitive(isFips),
|
||||
"name" to JsonPrimitive(name),
|
||||
"is_nfc" to JsonPrimitive(isNfcDevice),
|
||||
"supported_capabilities" to JsonObject(
|
||||
mapOf(
|
||||
"usb" to JsonPrimitive(getSupportedCapabilities(Transport.USB)),
|
||||
"nfc" to JsonPrimitive(getSupportedCapabilities(Transport.NFC)),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -0,0 +1,34 @@
|
||||
package com.yubico.authenticator.management
|
||||
|
||||
import com.yubico.yubikit.core.Transport
|
||||
import com.yubico.yubikit.core.Version
|
||||
import com.yubico.yubikit.management.DeviceConfig
|
||||
import com.yubico.yubikit.management.DeviceInfo
|
||||
|
||||
|
||||
fun DeviceConfig.model() = Model.DeviceConfig(
|
||||
deviceFlags = deviceFlags,
|
||||
challengeResponseTimeout = challengeResponseTimeout,
|
||||
autoEjectTimeout = autoEjectTimeout,
|
||||
enabledCapabilities = mapOf(
|
||||
"usb" to (getEnabledCapabilities(Transport.USB) ?: 0),
|
||||
"nfc" to (getEnabledCapabilities(Transport.NFC) ?: 0)
|
||||
)
|
||||
)
|
||||
|
||||
fun DeviceInfo.model(name: String, isNfc: Boolean, usbPid: Int?) = Model.AppDeviceInfo(
|
||||
config = config.model(),
|
||||
serialNumber = serialNumber,
|
||||
version = listOf(version.major, version.minor, version.micro),
|
||||
formFactor = formFactor.value,
|
||||
isLocked = isLocked,
|
||||
isSky = isSky,
|
||||
isFips = isFips,
|
||||
name = name,
|
||||
isNfc = isNfc,
|
||||
usbPid = usbPid,
|
||||
supportedCapabilities = mapOf(
|
||||
"usb" to getSupportedCapabilities(Transport.USB),
|
||||
"nfc" to getSupportedCapabilities(Transport.NFC),
|
||||
)
|
||||
)
|
@ -0,0 +1,46 @@
|
||||
package com.yubico.authenticator.management
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
class Model {
|
||||
|
||||
@Serializable
|
||||
data class DeviceConfig(
|
||||
@SerialName("device_flags")
|
||||
val deviceFlags: Int?,
|
||||
@SerialName("challenge_response_timeout")
|
||||
val challengeResponseTimeout: Byte?,
|
||||
@SerialName("auto_eject_timeout")
|
||||
val autoEjectTimeout: Short?,
|
||||
@SerialName("enabled_capabilities")
|
||||
val enabledCapabilities: Map<String, Int>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class AppDeviceInfo(
|
||||
@SerialName("config")
|
||||
val config: DeviceConfig,
|
||||
@SerialName("serial")
|
||||
val serialNumber: Int?,
|
||||
@SerialName("version")
|
||||
val version: List<Byte>,
|
||||
@SerialName("form_factor")
|
||||
val formFactor: Int,
|
||||
@SerialName("is_locked")
|
||||
val isLocked: Boolean,
|
||||
@SerialName("is_sky")
|
||||
val isSky: Boolean,
|
||||
@SerialName("is_fips")
|
||||
val isFips: Boolean,
|
||||
@SerialName("name")
|
||||
val name: String,
|
||||
@SerialName("is_nfc")
|
||||
val isNfc: Boolean,
|
||||
@SerialName("usb_pid")
|
||||
val usbPid: Int?,
|
||||
@SerialName("supported_capabilities")
|
||||
val supportedCapabilities: Map<String, Int>
|
||||
)
|
||||
|
||||
}
|
@ -6,20 +6,25 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.yubico.authenticator.*
|
||||
import com.yubico.authenticator.api.Pigeon.*
|
||||
import com.yubico.authenticator.data.device.toJson
|
||||
import com.yubico.authenticator.logging.Log
|
||||
import com.yubico.authenticator.management.model
|
||||
import com.yubico.authenticator.management.Model.AppDeviceInfo
|
||||
import com.yubico.authenticator.oath.keystore.ClearingMemProvider
|
||||
import com.yubico.authenticator.oath.keystore.KeyStoreProvider
|
||||
import com.yubico.yubikit.android.transport.nfc.NfcYubiKeyDevice
|
||||
import com.yubico.yubikit.android.transport.usb.UsbYubiKeyDevice
|
||||
import com.yubico.yubikit.core.Logger
|
||||
import com.yubico.yubikit.core.YubiKeyDevice
|
||||
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.oath.*
|
||||
import com.yubico.yubikit.support.DeviceUtil
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.coroutines.resume
|
||||
@ -99,20 +104,18 @@ class OathManager(
|
||||
|
||||
_isUsbKey = device is UsbYubiKeyDevice
|
||||
|
||||
try {
|
||||
coroutineScope.launch {
|
||||
if (pendingYubiKeyAction.value != null) {
|
||||
provideYubiKey(com.yubico.yubikit.core.util.Result.success(device))
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
sendDeviceInfo(device)
|
||||
sendOathInfo(device)
|
||||
sendOathCodes(device)
|
||||
}
|
||||
}
|
||||
val handler = CoroutineExceptionHandler { _, throwable ->
|
||||
Log.e(TAG, "Exception caught: ${throwable.message}")
|
||||
}
|
||||
|
||||
coroutineScope.launch(handler) {
|
||||
if (pendingYubiKeyAction.value != null) {
|
||||
provideYubiKey(com.yubico.yubikit.core.util.Result.success(device))
|
||||
} else {
|
||||
sendDeviceInfo(device)
|
||||
sendOathInfo(device)
|
||||
sendOathCodes(device)
|
||||
}
|
||||
} catch (illegalStateException: IllegalStateException) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,27 +394,84 @@ class OathManager(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun sendDeviceInfo(device: YubiKeyDevice) {
|
||||
|
||||
val deviceInfoData = suspendCoroutine<String> {
|
||||
device.requestConnection(SmartCardConnection::class.java) { result ->
|
||||
try {
|
||||
val pid = (device as? UsbYubiKeyDevice)?.pid
|
||||
val deviceInfo = DeviceUtil.readInfo(result.value, pid)
|
||||
val name = DeviceUtil.getName(deviceInfo, pid?.type)
|
||||
|
||||
val deviceInfoData = deviceInfo
|
||||
.toJson(name, device is NfcYubiKeyDevice)
|
||||
.toString()
|
||||
it.resume(deviceInfoData)
|
||||
} catch (cause: Throwable) {
|
||||
Logger.e("Failed to get device info", cause)
|
||||
it.resumeWithException(cause)
|
||||
private suspend fun <T> withSmartCardConnection(
|
||||
device: YubiKeyDevice,
|
||||
block: (SmartCardConnection) -> T
|
||||
) =
|
||||
suspendCoroutine<T> { continuation ->
|
||||
device.requestConnection(SmartCardConnection::class.java) {
|
||||
if (it.isError) {
|
||||
continuation.resumeWithException(IllegalStateException("Failed to get SmartCardConnection"))
|
||||
} else {
|
||||
continuation.resume(block(it.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_fManagementApi.updateDeviceInfo(deviceInfoData) {}
|
||||
private suspend fun <T> withOTPConnection(device: YubiKeyDevice, block: (OtpConnection) -> T) =
|
||||
suspendCoroutine<T> { continuation ->
|
||||
device.requestConnection(OtpConnection::class.java) {
|
||||
if (it.isError) {
|
||||
continuation.resumeWithException(IllegalStateException("Failed to get OtpConnection"))
|
||||
} else {
|
||||
continuation.resume(block(it.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> withFidoConnection(
|
||||
device: YubiKeyDevice,
|
||||
block: (FidoConnection) -> T
|
||||
) =
|
||||
suspendCoroutine<T> { continuation ->
|
||||
device.requestConnection(FidoConnection::class.java) {
|
||||
if (it.isError) {
|
||||
continuation.resumeWithException(IllegalStateException("Failed to get FidoConnection"))
|
||||
} else {
|
||||
continuation.resume(block(it.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getDeviceInfo(device: YubiKeyDevice): AppDeviceInfo =
|
||||
try {
|
||||
withSmartCardConnection(device) {
|
||||
val pid = (device as? UsbYubiKeyDevice)?.pid
|
||||
val deviceInfo = DeviceUtil.readInfo(it, pid)
|
||||
val name = DeviceUtil.getName(deviceInfo, pid?.type)
|
||||
deviceInfo.model(name, device is NfcYubiKeyDevice, pid?.value)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.d(TAG, "Smart card connection not available")
|
||||
try {
|
||||
withOTPConnection(device) {
|
||||
val pid = (device as? UsbYubiKeyDevice)?.pid
|
||||
val deviceInfo = DeviceUtil.readInfo(it, pid)
|
||||
val name = DeviceUtil.getName(deviceInfo, pid?.type)
|
||||
deviceInfo.model(name, device is NfcYubiKeyDevice, pid?.value)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.d(TAG, "OTP connection not available")
|
||||
try {
|
||||
withFidoConnection(device) {
|
||||
val pid = (device as? UsbYubiKeyDevice)?.pid
|
||||
val deviceInfo = DeviceUtil.readInfo(it, pid)
|
||||
val name = DeviceUtil.getName(deviceInfo, pid?.type)
|
||||
deviceInfo.model(name, device is NfcYubiKeyDevice, pid?.value)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "No connection available for getting device info")
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun sendDeviceInfo(device: YubiKeyDevice) {
|
||||
val deviceInfoData = getDeviceInfo(device)
|
||||
withContext(Dispatchers.Main) {
|
||||
Log.d(TAG, "Sending device info: $deviceInfoData")
|
||||
_fManagementApi.updateDeviceInfo(Json.encodeToString(deviceInfoData)) {}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun sendOathInfo(device: YubiKeyDevice) {
|
||||
@ -448,7 +508,9 @@ class OathManager(
|
||||
}
|
||||
}
|
||||
|
||||
_fOathApi.updateSession(oathSessionData) {}
|
||||
withContext(Dispatchers.Main) {
|
||||
_fOathApi.updateSession(oathSessionData) {}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun sendOathCodes(device: YubiKeyDevice) {
|
||||
@ -466,7 +528,9 @@ class OathManager(
|
||||
}
|
||||
}
|
||||
|
||||
_fOathApi.updateOathCredentials(sendOathCodes) {}
|
||||
withContext(Dispatchers.Main) {
|
||||
_fOathApi.updateOathCredentials(sendOathCodes) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,13 +37,14 @@ class _YubikeyProvider extends StateNotifier<YubiKeyData?> {
|
||||
DeviceInfo deviceInfo = DeviceInfo.fromJson(args);
|
||||
String name = args['name'];
|
||||
bool isNfc = args['is_nfc'];
|
||||
int? usbPid = args['usb_pid'];
|
||||
|
||||
DeviceNode deviceNode = isNfc
|
||||
? DeviceNode.nfcReader(DevicePath([]), name)
|
||||
: DeviceNode.usbYubiKey(
|
||||
DevicePath([]),
|
||||
name,
|
||||
/*TODO: replace with correct PID*/ UsbPid.yk4OtpFidoCcid,
|
||||
usbPid != null ? UsbPid.fromValue(usbPid) : UsbPid.yk4OtpFidoCcid,
|
||||
deviceInfo);
|
||||
|
||||
// reset oath providers on key change
|
||||
|
Loading…
Reference in New Issue
Block a user