Unify NFC and USB transport construction
This commit is contained in:
@@ -421,11 +421,8 @@ class CredentialProviderActivity : AppCompatActivity() {
|
||||
bottomSheet?.getCurrentPinIfValid()?.let { pendingPin = it }
|
||||
bottomSheet?.showPinInput(false)
|
||||
|
||||
val isoDep = IsoDep.get(tag) ?: throw AuthnkeyError.NotIsoDepTag()
|
||||
val transport = NfcTransport(isoDep)
|
||||
|
||||
if (!transport.selectFidoApplet()) {
|
||||
throw AuthnkeyError.FidoAppletNotFound()
|
||||
val transport = withContext(Dispatchers.IO) {
|
||||
NfcTransport.connect(tag)
|
||||
}
|
||||
|
||||
currentTransport = transport
|
||||
@@ -464,8 +461,8 @@ class CredentialProviderActivity : AppCompatActivity() {
|
||||
setState(CredentialBottomSheet.State.PROCESSING)
|
||||
|
||||
val transport = withContext(Dispatchers.IO) {
|
||||
UsbTransport.create(usbManager, device)
|
||||
} ?: throw AuthnkeyError.ConnectionFailed()
|
||||
UsbTransport.connect(usbManager, device)
|
||||
}
|
||||
|
||||
currentTransport = transport
|
||||
pinProtocol = PinProtocol(transport)
|
||||
|
||||
@@ -266,11 +266,8 @@ class MainActivity : AppCompatActivity() {
|
||||
currentTransport?.close()
|
||||
currentTransport = null
|
||||
|
||||
val isoDep = IsoDep.get(tag) ?: throw AuthnkeyError.NotIsoDepTag()
|
||||
val transport = NfcTransport(isoDep)
|
||||
|
||||
if (!transport.selectFidoApplet()) {
|
||||
throw AuthnkeyError.FidoAppletNotFound()
|
||||
val transport = withContext(Dispatchers.IO) {
|
||||
NfcTransport.connect(tag)
|
||||
}
|
||||
|
||||
currentTransport = transport
|
||||
@@ -374,8 +371,8 @@ class MainActivity : AppCompatActivity() {
|
||||
statusText.text = getString(R.string.connecting_usb)
|
||||
|
||||
val transport = withContext(Dispatchers.IO) {
|
||||
UsbTransport.create(usbManager, device)
|
||||
} ?: throw AuthnkeyError.ConnectionFailed()
|
||||
UsbTransport.connect(usbManager, device)
|
||||
}
|
||||
|
||||
currentTransport = transport
|
||||
pinProtocol = PinProtocol(transport)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package pl.lebihan.authnkey
|
||||
|
||||
import android.nfc.Tag
|
||||
import android.nfc.TagLostException
|
||||
import android.nfc.tech.IsoDep
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -8,7 +9,7 @@ import kotlinx.coroutines.withContext
|
||||
/**
|
||||
* FIDO transport over NFC using ISO 7816-4 APDUs
|
||||
*/
|
||||
class NfcTransport(private val isoDep: IsoDep) : FidoTransport {
|
||||
class NfcTransport private constructor(private val isoDep: IsoDep) : FidoTransport {
|
||||
|
||||
override val transportType = TransportType.NFC
|
||||
|
||||
@@ -25,17 +26,10 @@ class NfcTransport(private val isoDep: IsoDep) : FidoTransport {
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
if (!isoDep.isConnected) {
|
||||
isoDep.connect()
|
||||
}
|
||||
isoDep.timeout = 5000
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the FIDO applet on the NFC device
|
||||
*/
|
||||
suspend fun selectFidoApplet(): Boolean = withContext(Dispatchers.IO) {
|
||||
private suspend fun selectFidoApplet(): Boolean = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val response = isoDep.transceive(SELECT_FIDO_APPLET)
|
||||
isSuccess(response)
|
||||
@@ -136,11 +130,31 @@ class NfcTransport(private val isoDep: IsoDep) : FidoTransport {
|
||||
|
||||
companion object {
|
||||
// FIDO Alliance AID
|
||||
val SELECT_FIDO_APPLET = byteArrayOf(
|
||||
private val SELECT_FIDO_APPLET = byteArrayOf(
|
||||
0x00, 0xA4.toByte(), 0x04, 0x00, // SELECT command
|
||||
0x08, // Length of AID
|
||||
0xA0.toByte(), 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01, // FIDO AID
|
||||
0x00 // Le
|
||||
)
|
||||
|
||||
/**
|
||||
* Connect to a FIDO NFC device via the given NFC tag.
|
||||
* Opens the ISO-DEP connection and selects the FIDO applet.
|
||||
*/
|
||||
suspend fun connect(tag: Tag): NfcTransport {
|
||||
val isoDep = IsoDep.get(tag) ?: throw AuthnkeyError.NotIsoDepTag()
|
||||
|
||||
if (!isoDep.isConnected) {
|
||||
isoDep.connect()
|
||||
}
|
||||
isoDep.timeout = 5000
|
||||
|
||||
val transport = NfcTransport(isoDep)
|
||||
if (!transport.selectFidoApplet()) {
|
||||
transport.close()
|
||||
throw AuthnkeyError.FidoAppletNotFound()
|
||||
}
|
||||
return transport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,7 @@ import kotlin.random.Random
|
||||
/**
|
||||
* FIDO transport over USB HID using CTAPHID protocol
|
||||
*/
|
||||
class UsbTransport(
|
||||
private val usbManager: UsbManager,
|
||||
private val device: UsbDevice,
|
||||
class UsbTransport private constructor(
|
||||
private val connection: UsbDeviceConnection,
|
||||
private val hidInterface: UsbInterface,
|
||||
private val inEndpoint: UsbEndpoint,
|
||||
@@ -32,7 +30,7 @@ class UsbTransport(
|
||||
/**
|
||||
* Initialize CTAPHID channel
|
||||
*/
|
||||
suspend fun init(): Boolean = withContext(Dispatchers.IO) {
|
||||
private suspend fun init(): Boolean = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
// Send INIT command to get a channel
|
||||
val nonce = ByteArray(8).also { Random.nextBytes(it) }
|
||||
@@ -308,25 +306,29 @@ class UsbTransport(
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a UsbTransport from a USB device
|
||||
* Connect to a FIDO USB device.
|
||||
* Opens the HID interface and initializes the CTAPHID channel.
|
||||
*/
|
||||
suspend fun create(usbManager: UsbManager, device: UsbDevice): UsbTransport? {
|
||||
val (hidInterface, endpoints) = findFidoInterface(device) ?: return null
|
||||
suspend fun connect(usbManager: UsbManager, device: UsbDevice): UsbTransport {
|
||||
val (hidInterface, endpoints) = findFidoInterface(device)
|
||||
?: throw AuthnkeyError.ConnectionFailed()
|
||||
val (inEp, outEp) = endpoints
|
||||
|
||||
val connection = usbManager.openDevice(device) ?: return null
|
||||
val connection = usbManager.openDevice(device)
|
||||
?: throw AuthnkeyError.ConnectionFailed()
|
||||
|
||||
if (!connection.claimInterface(hidInterface, true)) {
|
||||
connection.close()
|
||||
return null
|
||||
throw AuthnkeyError.ConnectionFailed()
|
||||
}
|
||||
|
||||
val transport = UsbTransport(usbManager, device, connection, hidInterface, inEp, outEp)
|
||||
val transport = UsbTransport(connection, hidInterface, inEp, outEp)
|
||||
|
||||
return if (transport.init()) transport else {
|
||||
if (!transport.init()) {
|
||||
transport.close()
|
||||
null
|
||||
throw AuthnkeyError.ConnectionFailed()
|
||||
}
|
||||
return transport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user