@doko/react-native-yubikit 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +126 -0
- package/RNYubikit.podspec +22 -0
- package/android/build.gradle +63 -0
- package/android/src/main/AndroidManifest.xml +9 -0
- package/android/src/main/java/com/yubikit/YubiKitManagerHolder.kt +330 -0
- package/android/src/main/java/com/yubikit/YubikitCoreModule.kt +142 -0
- package/android/src/main/java/com/yubikit/YubikitFidoModule.kt +418 -0
- package/android/src/main/java/com/yubikit/YubikitManagementModule.kt +132 -0
- package/android/src/main/java/com/yubikit/YubikitOathModule.kt +224 -0
- package/android/src/main/java/com/yubikit/YubikitOpenPgpModule.kt +317 -0
- package/android/src/main/java/com/yubikit/YubikitPackage.kt +44 -0
- package/android/src/main/java/com/yubikit/YubikitPivModule.kt +310 -0
- package/android/src/main/java/com/yubikit/YubikitSupportModule.kt +108 -0
- package/android/src/main/java/com/yubikit/YubikitYubiOtpModule.kt +248 -0
- package/android/src/main/java/com/yubikit/utils/YubikitUtils.kt +321 -0
- package/ios/YubikitCore.h +5 -0
- package/ios/YubikitCore.mm +101 -0
- package/ios/YubikitDeviceInfoHelper.h +13 -0
- package/ios/YubikitDeviceInfoHelper.mm +110 -0
- package/ios/YubikitFido.h +5 -0
- package/ios/YubikitFido.mm +373 -0
- package/ios/YubikitManagement.h +5 -0
- package/ios/YubikitManagement.mm +153 -0
- package/ios/YubikitManager.h +27 -0
- package/ios/YubikitManager.m +169 -0
- package/ios/YubikitOath.h +5 -0
- package/ios/YubikitOath.mm +583 -0
- package/ios/YubikitOpenPgp.h +5 -0
- package/ios/YubikitOpenPgp.mm +210 -0
- package/ios/YubikitPiv.h +5 -0
- package/ios/YubikitPiv.mm +813 -0
- package/ios/YubikitSupport.h +5 -0
- package/ios/YubikitSupport.mm +58 -0
- package/ios/YubikitYubiOtp.h +5 -0
- package/ios/YubikitYubiOtp.mm +120 -0
- package/lib/module/core.js +34 -0
- package/lib/module/core.js.map +1 -0
- package/lib/module/fido.js +40 -0
- package/lib/module/fido.js.map +1 -0
- package/lib/module/hooks/index.js +5 -0
- package/lib/module/hooks/index.js.map +1 -0
- package/lib/module/hooks/use-yubikey.js +12 -0
- package/lib/module/hooks/use-yubikey.js.map +1 -0
- package/lib/module/hooks/yubikey-provider.js +85 -0
- package/lib/module/hooks/yubikey-provider.js.map +1 -0
- package/lib/module/index.js +12 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/management.js +17 -0
- package/lib/module/management.js.map +1 -0
- package/lib/module/oath.js +53 -0
- package/lib/module/oath.js.map +1 -0
- package/lib/module/openpgp.js +89 -0
- package/lib/module/openpgp.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/piv.js +71 -0
- package/lib/module/piv.js.map +1 -0
- package/lib/module/specs/NativeYubikitCore.js +10 -0
- package/lib/module/specs/NativeYubikitCore.js.map +1 -0
- package/lib/module/specs/NativeYubikitFido.js +5 -0
- package/lib/module/specs/NativeYubikitFido.js.map +1 -0
- package/lib/module/specs/NativeYubikitManagement.js +5 -0
- package/lib/module/specs/NativeYubikitManagement.js.map +1 -0
- package/lib/module/specs/NativeYubikitOath.js +5 -0
- package/lib/module/specs/NativeYubikitOath.js.map +1 -0
- package/lib/module/specs/NativeYubikitOpenPgp.js +5 -0
- package/lib/module/specs/NativeYubikitOpenPgp.js.map +1 -0
- package/lib/module/specs/NativeYubikitPiv.js +5 -0
- package/lib/module/specs/NativeYubikitPiv.js.map +1 -0
- package/lib/module/specs/NativeYubikitSupport.js +5 -0
- package/lib/module/specs/NativeYubikitSupport.js.map +1 -0
- package/lib/module/specs/NativeYubikitYubiOtp.js +5 -0
- package/lib/module/specs/NativeYubikitYubiOtp.js.map +1 -0
- package/lib/module/support.js +11 -0
- package/lib/module/support.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/yubiotp.js +32 -0
- package/lib/module/yubiotp.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/core.d.ts +14 -0
- package/lib/typescript/src/core.d.ts.map +1 -0
- package/lib/typescript/src/fido.d.ts +24 -0
- package/lib/typescript/src/fido.d.ts.map +1 -0
- package/lib/typescript/src/hooks/index.d.ts +3 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/use-yubikey.d.ts +3 -0
- package/lib/typescript/src/hooks/use-yubikey.d.ts.map +1 -0
- package/lib/typescript/src/hooks/yubikey-provider.d.ts +23 -0
- package/lib/typescript/src/hooks/yubikey-provider.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +10 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/management.d.ts +8 -0
- package/lib/typescript/src/management.d.ts.map +1 -0
- package/lib/typescript/src/oath.d.ts +20 -0
- package/lib/typescript/src/oath.d.ts.map +1 -0
- package/lib/typescript/src/openpgp.d.ts +35 -0
- package/lib/typescript/src/openpgp.d.ts.map +1 -0
- package/lib/typescript/src/piv.d.ts +26 -0
- package/lib/typescript/src/piv.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitCore.d.ts +49 -0
- package/lib/typescript/src/specs/NativeYubikitCore.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitFido.d.ts +121 -0
- package/lib/typescript/src/specs/NativeYubikitFido.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitManagement.d.ts +53 -0
- package/lib/typescript/src/specs/NativeYubikitManagement.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitOath.d.ts +70 -0
- package/lib/typescript/src/specs/NativeYubikitOath.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitOpenPgp.d.ts +53 -0
- package/lib/typescript/src/specs/NativeYubikitOpenPgp.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitPiv.d.ts +47 -0
- package/lib/typescript/src/specs/NativeYubikitPiv.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitSupport.d.ts +49 -0
- package/lib/typescript/src/specs/NativeYubikitSupport.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitYubiOtp.d.ts +45 -0
- package/lib/typescript/src/specs/NativeYubikitYubiOtp.d.ts.map +1 -0
- package/lib/typescript/src/support.d.ts +6 -0
- package/lib/typescript/src/support.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +293 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/yubiotp.d.ts +13 -0
- package/lib/typescript/src/yubiotp.d.ts.map +1 -0
- package/package.json +181 -0
- package/src/core.ts +58 -0
- package/src/fido.ts +112 -0
- package/src/hooks/index.tsx +2 -0
- package/src/hooks/use-yubikey.tsx +10 -0
- package/src/hooks/yubikey-provider.tsx +146 -0
- package/src/index.tsx +10 -0
- package/src/management.ts +44 -0
- package/src/oath.ts +134 -0
- package/src/openpgp.ts +220 -0
- package/src/piv.ts +193 -0
- package/src/specs/NativeYubikitCore.ts +68 -0
- package/src/specs/NativeYubikitFido.ts +149 -0
- package/src/specs/NativeYubikitManagement.ts +78 -0
- package/src/specs/NativeYubikitOath.ts +110 -0
- package/src/specs/NativeYubikitOpenPgp.ts +148 -0
- package/src/specs/NativeYubikitPiv.ts +108 -0
- package/src/specs/NativeYubikitSupport.ts +61 -0
- package/src/specs/NativeYubikitYubiOtp.ts +82 -0
- package/src/support.ts +18 -0
- package/src/types.ts +404 -0
- package/src/yubiotp.ts +96 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
package com.yubikit
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.Promise
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.bridge.ReadableMap
|
|
7
|
+
import com.facebook.react.bridge.WritableArray
|
|
8
|
+
import com.yubico.yubikit.android.transport.nfc.NfcConfiguration
|
|
9
|
+
import com.yubico.yubikit.android.transport.nfc.NfcNotAvailable
|
|
10
|
+
import com.yubico.yubikit.android.transport.usb.UsbConfiguration
|
|
11
|
+
import com.yubico.yubikit.core.fido.FidoConnection
|
|
12
|
+
import com.yubico.yubikit.core.otp.OtpConnection
|
|
13
|
+
import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
|
14
|
+
import com.yubikit.YubiKitManagerHolder.deviceToBundle
|
|
15
|
+
import com.yubikit.utils.YubikitUtils.base64Decode
|
|
16
|
+
import com.yubikit.utils.YubikitUtils.base64Encode
|
|
17
|
+
import com.yubikit.utils.YubikitUtils.toWritableMap
|
|
18
|
+
import kotlinx.coroutines.CoroutineScope
|
|
19
|
+
import kotlinx.coroutines.Dispatchers
|
|
20
|
+
import kotlinx.coroutines.launch
|
|
21
|
+
import kotlinx.coroutines.withContext
|
|
22
|
+
import java.io.IOException
|
|
23
|
+
|
|
24
|
+
@Suppress("unused")
|
|
25
|
+
class YubikitCoreModule(reactContext: ReactApplicationContext) :
|
|
26
|
+
NativeYubikitCoreSpec(reactContext) {
|
|
27
|
+
|
|
28
|
+
private val moduleScope = CoroutineScope(Dispatchers.IO)
|
|
29
|
+
|
|
30
|
+
init {
|
|
31
|
+
YubiKitManagerHolder.setEventHandler { params -> emitOnYubiKeyEvent(params) }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override fun getName(): String = NAME
|
|
35
|
+
|
|
36
|
+
override fun startUsbDiscovery(config: ReadableMap?) {
|
|
37
|
+
YubiKitManagerHolder.initialize(reactApplicationContext)
|
|
38
|
+
val usbConfig = UsbConfiguration()
|
|
39
|
+
config?.let {
|
|
40
|
+
if (it.hasKey("handlePermissions") && !it.isNull("handlePermissions")) {
|
|
41
|
+
usbConfig.handlePermissions(it.getBoolean("handlePermissions"))
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
YubiKitManagerHolder.setUsbConfiguration(usbConfig)
|
|
45
|
+
YubiKitManagerHolder.startUsbDiscovery()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override fun stopUsbDiscovery() {
|
|
49
|
+
YubiKitManagerHolder.stopUsbDiscovery()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override fun startNfcDiscovery(config: ReadableMap?) {
|
|
53
|
+
YubiKitManagerHolder.initialize(reactApplicationContext)
|
|
54
|
+
val nfcConfig = NfcConfiguration()
|
|
55
|
+
config?.let {
|
|
56
|
+
if (it.hasKey("timeout") && !it.isNull("timeout")) {
|
|
57
|
+
nfcConfig.timeout(it.getInt("timeout"))
|
|
58
|
+
}
|
|
59
|
+
if (it.hasKey("disableNfcDiscoverySound") && !it.isNull("disableNfcDiscoverySound")) {
|
|
60
|
+
nfcConfig.disableNfcDiscoverySound(it.getBoolean("disableNfcDiscoverySound"))
|
|
61
|
+
}
|
|
62
|
+
if (it.hasKey("skipNdefCheck") && !it.isNull("skipNdefCheck")) {
|
|
63
|
+
nfcConfig.skipNdefCheck(it.getBoolean("skipNdefCheck"))
|
|
64
|
+
}
|
|
65
|
+
if (it.hasKey("handleUnavailableNfc") && !it.isNull("handleUnavailableNfc")) {
|
|
66
|
+
nfcConfig.handleUnavailableNfc(it.getBoolean("handleUnavailableNfc"))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
YubiKitManagerHolder.setNfcConfiguration(nfcConfig)
|
|
70
|
+
val activity = reactApplicationContext.currentActivity
|
|
71
|
+
?: throw IllegalStateException("No current Activity for NFC discovery")
|
|
72
|
+
YubiKitManagerHolder.startNfcDiscovery(activity)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
override fun stopNfcDiscovery() {
|
|
76
|
+
val activity = reactApplicationContext.currentActivity
|
|
77
|
+
?: throw IllegalStateException("No current Activity for NFC discovery")
|
|
78
|
+
YubiKitManagerHolder.stopNfcDiscovery(activity)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
override fun requestConnection(deviceHandle: String, connectionType: String, promise: Promise) {
|
|
82
|
+
moduleScope.launch {
|
|
83
|
+
try {
|
|
84
|
+
val clazz = when (connectionType) {
|
|
85
|
+
"SmartCardConnection" -> SmartCardConnection::class.java
|
|
86
|
+
"OtpConnection" -> OtpConnection::class.java
|
|
87
|
+
"FidoConnection" -> FidoConnection::class.java
|
|
88
|
+
else -> throw IllegalArgumentException("Unknown connection type: $connectionType")
|
|
89
|
+
}
|
|
90
|
+
var result: Result<Pair<String, com.yubico.yubikit.core.YubiKeyConnection>>? = null
|
|
91
|
+
YubiKitManagerHolder.requestConnection(deviceHandle, clazz) { res ->
|
|
92
|
+
result = res
|
|
93
|
+
}
|
|
94
|
+
// Wait briefly for callback (requestConnection callback is typically synchronous-ish)
|
|
95
|
+
withContext(Dispatchers.IO) {
|
|
96
|
+
var waited = 0
|
|
97
|
+
while (result == null && waited < 5000) {
|
|
98
|
+
Thread.sleep(50)
|
|
99
|
+
waited += 50
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
val pair = result?.getOrThrow()
|
|
103
|
+
?: throw IOException("Timeout waiting for connection")
|
|
104
|
+
promise.resolve(pair.first)
|
|
105
|
+
} catch (e: Exception) {
|
|
106
|
+
promise.reject("CONNECTION_ERROR", e.message, e)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
override fun sendApdu(connectionHandle: String, apdu: String, promise: Promise) {
|
|
112
|
+
moduleScope.launch {
|
|
113
|
+
try {
|
|
114
|
+
val connection = YubiKitManagerHolder.getConnection(connectionHandle)
|
|
115
|
+
if (connection !is SmartCardConnection) {
|
|
116
|
+
throw IllegalArgumentException("sendApdu requires a SmartCardConnection")
|
|
117
|
+
}
|
|
118
|
+
val data = base64Decode(apdu) ?: byteArrayOf()
|
|
119
|
+
val response = connection.sendAndReceive(data)
|
|
120
|
+
promise.resolve(base64Encode(response))
|
|
121
|
+
} catch (e: Exception) {
|
|
122
|
+
promise.reject("APDU_ERROR", e.message, e)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
override fun closeConnection(connectionHandle: String) {
|
|
128
|
+
YubiKitManagerHolder.removeConnection(connectionHandle)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
override fun getDiscoveredDevices(): WritableArray {
|
|
132
|
+
val array = Arguments.createArray()
|
|
133
|
+
YubiKitManagerHolder.listDevices().forEach { (handle, device) ->
|
|
134
|
+
array.pushMap(deviceToBundle(handle, device).toWritableMap())
|
|
135
|
+
}
|
|
136
|
+
return array
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
companion object {
|
|
140
|
+
const val NAME = "YubikitCore"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
package com.yubikit
|
|
2
|
+
|
|
3
|
+
import android.util.Base64
|
|
4
|
+
import com.facebook.react.bridge.Arguments
|
|
5
|
+
import com.facebook.react.bridge.Promise
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.bridge.ReadableMap
|
|
8
|
+
import com.facebook.react.bridge.WritableMap
|
|
9
|
+
import com.yubico.yubikit.core.YubiKeyConnection
|
|
10
|
+
import com.yubico.yubikit.core.fido.FidoConnection
|
|
11
|
+
import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
|
12
|
+
import com.yubico.yubikit.fido.client.Ctap2Client
|
|
13
|
+
import com.yubico.yubikit.fido.client.CredentialManager
|
|
14
|
+
import com.yubico.yubikit.fido.client.WebAuthnClient
|
|
15
|
+
import com.yubico.yubikit.fido.client.clientdata.ClientDataProvider
|
|
16
|
+
import com.yubico.yubikit.fido.ctap.Ctap2Session
|
|
17
|
+
import com.yubico.yubikit.fido.webauthn.AuthenticatorAssertionResponse
|
|
18
|
+
import com.yubico.yubikit.fido.webauthn.AuthenticatorAttestationResponse
|
|
19
|
+
import com.yubico.yubikit.fido.webauthn.AuthenticatorSelectionCriteria
|
|
20
|
+
import com.yubico.yubikit.fido.webauthn.Extensions
|
|
21
|
+
import com.yubico.yubikit.fido.webauthn.PublicKeyCredential
|
|
22
|
+
import com.yubico.yubikit.fido.webauthn.PublicKeyCredentialCreationOptions
|
|
23
|
+
import com.yubico.yubikit.fido.webauthn.PublicKeyCredentialDescriptor
|
|
24
|
+
import com.yubico.yubikit.fido.webauthn.PublicKeyCredentialParameters
|
|
25
|
+
import com.yubico.yubikit.fido.webauthn.PublicKeyCredentialRequestOptions
|
|
26
|
+
import com.yubico.yubikit.fido.webauthn.PublicKeyCredentialRpEntity
|
|
27
|
+
import com.yubico.yubikit.fido.webauthn.PublicKeyCredentialUserEntity
|
|
28
|
+
import com.yubikit.utils.YubikitUtils.base64Decode
|
|
29
|
+
import com.yubikit.utils.YubikitUtils.base64Encode
|
|
30
|
+
import kotlinx.coroutines.CoroutineScope
|
|
31
|
+
import kotlinx.coroutines.Dispatchers
|
|
32
|
+
import kotlinx.coroutines.launch
|
|
33
|
+
import org.json.JSONObject
|
|
34
|
+
|
|
35
|
+
@Suppress("unused")
|
|
36
|
+
class YubikitFidoModule(reactContext: ReactApplicationContext) :
|
|
37
|
+
NativeYubikitFidoSpec(reactContext) {
|
|
38
|
+
|
|
39
|
+
private val moduleScope = CoroutineScope(Dispatchers.IO)
|
|
40
|
+
|
|
41
|
+
override fun getName(): String = NAME
|
|
42
|
+
|
|
43
|
+
// NfcYubiKeyDevice only ever supports SmartCardConnection, so hardcoding
|
|
44
|
+
// FidoConnection here would break every CTAP2/FIDO2 operation over NFC.
|
|
45
|
+
// Ctap2Session accepts either connection type, so prefer FidoConnection (USB)
|
|
46
|
+
// and fall back to SmartCardConnection (NFC and USB devices without CCID).
|
|
47
|
+
private val ctap2ConnectionOrder = listOf(
|
|
48
|
+
FidoConnection::class.java as Class<out YubiKeyConnection>,
|
|
49
|
+
SmartCardConnection::class.java
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
private fun openCtap2Session(connection: YubiKeyConnection): Ctap2Session {
|
|
53
|
+
return when (connection) {
|
|
54
|
+
is FidoConnection -> Ctap2Session(connection)
|
|
55
|
+
is SmartCardConnection -> Ctap2Session(connection)
|
|
56
|
+
else -> throw IllegalArgumentException("Unsupported connection type for CTAP2: $connection")
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private inline fun <R> withFidoConnection(
|
|
61
|
+
deviceHandle: String,
|
|
62
|
+
promise: Promise,
|
|
63
|
+
crossinline block: (YubiKeyConnection) -> R
|
|
64
|
+
) {
|
|
65
|
+
moduleScope.launch {
|
|
66
|
+
try {
|
|
67
|
+
val result = YubiKitManagerHolder.withAnyConnection(deviceHandle, ctap2ConnectionOrder) { connection -> block(connection) }
|
|
68
|
+
when (result) {
|
|
69
|
+
is Unit -> promise.resolve(null)
|
|
70
|
+
else -> promise.resolve(result)
|
|
71
|
+
}
|
|
72
|
+
} catch (e: Exception) {
|
|
73
|
+
promise.reject("FIDO_ERROR", e.message, e)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The SDK only accepts a pre-built clientDataJSON hash, so the standard WebAuthn
|
|
80
|
+
* clientDataJSON must be constructed here from the JS-supplied challenge.
|
|
81
|
+
*/
|
|
82
|
+
private fun base64UrlEncode(bytes: ByteArray): String {
|
|
83
|
+
return Base64.encodeToString(bytes, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private fun buildClientData(type: String, challenge: ByteArray, effectiveDomain: String): ClientDataProvider {
|
|
87
|
+
val json = JSONObject().apply {
|
|
88
|
+
put("type", type)
|
|
89
|
+
put("challenge", base64UrlEncode(challenge))
|
|
90
|
+
put("origin", "https://$effectiveDomain")
|
|
91
|
+
put("crossOrigin", false)
|
|
92
|
+
}
|
|
93
|
+
return ClientDataProvider.fromClientDataJson(json.toString().toByteArray(Charsets.UTF_8))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private fun mapToPublicKeyCredential(cred: PublicKeyCredential): WritableMap {
|
|
97
|
+
return Arguments.createMap().apply {
|
|
98
|
+
putString("id", base64Encode(cred.rawId))
|
|
99
|
+
putString("rawId", base64Encode(cred.rawId))
|
|
100
|
+
putString("type", "public-key")
|
|
101
|
+
putMap("response", Arguments.createMap().apply {
|
|
102
|
+
val response = cred.response
|
|
103
|
+
putString("clientDataJSON", base64Encode(response.clientDataJson))
|
|
104
|
+
when (response) {
|
|
105
|
+
is AuthenticatorAttestationResponse -> {
|
|
106
|
+
putString("authenticatorData", base64Encode(response.authenticatorData.bytes))
|
|
107
|
+
putString("attestationObject", base64Encode(response.attestationObject))
|
|
108
|
+
}
|
|
109
|
+
is AuthenticatorAssertionResponse -> {
|
|
110
|
+
putString("authenticatorData", base64Encode(response.authenticatorData))
|
|
111
|
+
putString("signature", base64Encode(response.signature))
|
|
112
|
+
response.userHandle?.let { putString("userHandle", base64Encode(it)) }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private fun mapCtap2Info(info: Ctap2Session.InfoData): WritableMap {
|
|
120
|
+
return Arguments.createMap().apply {
|
|
121
|
+
val versions = Arguments.createArray()
|
|
122
|
+
info.versions.forEach { versions.pushString(it) }
|
|
123
|
+
putArray("versions", versions)
|
|
124
|
+
putString("aaguid", base64Encode(info.aaguid))
|
|
125
|
+
val extensions = Arguments.createArray()
|
|
126
|
+
info.extensions.forEach { extensions.pushString(it) }
|
|
127
|
+
putArray("extensions", extensions)
|
|
128
|
+
val optsMap = Arguments.createMap()
|
|
129
|
+
info.options.forEach { (k, v) -> if (v is Boolean) optsMap.putBoolean(k, v) }
|
|
130
|
+
putMap("options", optsMap)
|
|
131
|
+
putInt("maxMsgSize", info.maxMsgSize)
|
|
132
|
+
val protos = Arguments.createArray()
|
|
133
|
+
info.pinUvAuthProtocols.forEach { protos.pushInt(it) }
|
|
134
|
+
putArray("pinUvAuthProtocols", protos)
|
|
135
|
+
info.maxCredentialCountInList?.let { putInt("maxCredentialCountInList", it) }
|
|
136
|
+
info.maxCredentialIdLength?.let { putInt("maxCredentialIdLength", it) }
|
|
137
|
+
val transports = Arguments.createArray()
|
|
138
|
+
info.transports.forEach { transports.pushString(it) }
|
|
139
|
+
putArray("transports", transports)
|
|
140
|
+
val algorithms = Arguments.createArray()
|
|
141
|
+
info.algorithms.forEach { alg ->
|
|
142
|
+
algorithms.pushMap(Arguments.createMap().apply {
|
|
143
|
+
putString("type", alg.type)
|
|
144
|
+
putInt("alg", alg.alg)
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
putArray("algorithms", algorithms)
|
|
148
|
+
putInt("maxLargeBlob", info.maxSerializedLargeBlobArray)
|
|
149
|
+
putInt("minPinLength", info.minPinLength)
|
|
150
|
+
putInt("maxCredBlobLength", info.maxCredBlobLength)
|
|
151
|
+
putInt("maxRpidsForMinPinLength", info.maxRpidsForSetMinPinLength)
|
|
152
|
+
info.preferredPlatformUvAttempts?.let { putInt("preferredPlatformUvAttempts", it) }
|
|
153
|
+
putInt("uvModality", info.uvModality)
|
|
154
|
+
val certsMap = Arguments.createMap()
|
|
155
|
+
info.certifications.forEach { (k, v) -> if (v is Int) certsMap.putInt(k, v) }
|
|
156
|
+
putMap("certifications", certsMap)
|
|
157
|
+
info.remainingDiscoverableCredentials?.let { putInt("remainingDiscoverableCredentials", it) }
|
|
158
|
+
info.vendorPrototypeConfigCommands?.let { cmds ->
|
|
159
|
+
val arr = Arguments.createArray()
|
|
160
|
+
cmds.forEach { arr.pushInt(it) }
|
|
161
|
+
putArray("vendorPrototypeConfigCommands", arr)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
override fun getInfo(deviceHandle: String, promise: Promise) {
|
|
167
|
+
withFidoConnection(deviceHandle, promise) { connection ->
|
|
168
|
+
openCtap2Session(connection).use { session ->
|
|
169
|
+
mapCtap2Info(session.info)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
override fun makeCredential(
|
|
175
|
+
deviceHandle: String,
|
|
176
|
+
options: ReadableMap,
|
|
177
|
+
effectiveDomain: String,
|
|
178
|
+
pin: String?,
|
|
179
|
+
enterpriseAttestation: Double?,
|
|
180
|
+
promise: Promise
|
|
181
|
+
) {
|
|
182
|
+
moduleScope.launch {
|
|
183
|
+
try {
|
|
184
|
+
val credential = YubiKitManagerHolder.withAnyConnection(deviceHandle, ctap2ConnectionOrder) { connection ->
|
|
185
|
+
WebAuthnClient.create(connection, null, null).use { client ->
|
|
186
|
+
val rpMap = options.getMap("rp")!!
|
|
187
|
+
val userMap = options.getMap("user")!!
|
|
188
|
+
val challenge = base64Decode(options.getString("challenge")) ?: byteArrayOf()
|
|
189
|
+
val clientData = buildClientData("webauthn.create", challenge, effectiveDomain)
|
|
190
|
+
|
|
191
|
+
val pubKeyCredParams = options.getArray("pubKeyCredParams")!!.let { arr ->
|
|
192
|
+
(0 until arr.size()).map { i ->
|
|
193
|
+
val m = arr.getMap(i)!!
|
|
194
|
+
PublicKeyCredentialParameters(
|
|
195
|
+
m.getString("type") ?: "public-key",
|
|
196
|
+
m.getInt("alg")
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
val rp = PublicKeyCredentialRpEntity(
|
|
202
|
+
rpMap.getString("name") ?: rpMap.getString("id") ?: effectiveDomain,
|
|
203
|
+
rpMap.getString("id") ?: effectiveDomain
|
|
204
|
+
)
|
|
205
|
+
val user = PublicKeyCredentialUserEntity(
|
|
206
|
+
userMap.getString("name") ?: "",
|
|
207
|
+
base64Decode(userMap.getString("id")) ?: byteArrayOf(),
|
|
208
|
+
userMap.getString("displayName") ?: ""
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
val excludeCredentials = options.getArray("excludeCredentials")?.let { arr ->
|
|
212
|
+
(0 until arr.size()).map { i ->
|
|
213
|
+
val m = arr.getMap(i)!!
|
|
214
|
+
PublicKeyCredentialDescriptor(
|
|
215
|
+
m.getString("type") ?: "public-key",
|
|
216
|
+
base64Decode(m.getString("id")) ?: byteArrayOf(),
|
|
217
|
+
m.getArray("transports")?.toArrayList()?.map { it.toString() }
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
val authenticatorSelection = options.getMap("authenticatorSelection")?.let { sel ->
|
|
223
|
+
AuthenticatorSelectionCriteria(
|
|
224
|
+
sel.getString("authenticatorAttachment"),
|
|
225
|
+
sel.getString("residentKey"),
|
|
226
|
+
sel.getString("userVerification")
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
val extensions = options.getMap("extensions")?.toHashMap()?.let { Extensions.fromMap(it) }
|
|
231
|
+
|
|
232
|
+
val creationOptions = PublicKeyCredentialCreationOptions(
|
|
233
|
+
rp,
|
|
234
|
+
user,
|
|
235
|
+
challenge,
|
|
236
|
+
pubKeyCredParams,
|
|
237
|
+
if (options.hasKey("timeout") && !options.isNull("timeout")) options.getDouble("timeout").toLong() else null,
|
|
238
|
+
excludeCredentials,
|
|
239
|
+
authenticatorSelection,
|
|
240
|
+
options.getString("attestation"),
|
|
241
|
+
extensions
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
client.makeCredential(
|
|
245
|
+
clientData,
|
|
246
|
+
creationOptions,
|
|
247
|
+
effectiveDomain,
|
|
248
|
+
pin?.toCharArray(),
|
|
249
|
+
enterpriseAttestation?.toInt(),
|
|
250
|
+
null
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
promise.resolve(mapToPublicKeyCredential(credential))
|
|
255
|
+
} catch (e: Exception) {
|
|
256
|
+
promise.reject("FIDO_ERROR", e.message, e)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
override fun getAssertion(
|
|
262
|
+
deviceHandle: String,
|
|
263
|
+
options: ReadableMap,
|
|
264
|
+
effectiveDomain: String,
|
|
265
|
+
pin: String?,
|
|
266
|
+
promise: Promise
|
|
267
|
+
) {
|
|
268
|
+
moduleScope.launch {
|
|
269
|
+
try {
|
|
270
|
+
val credential = YubiKitManagerHolder.withAnyConnection(deviceHandle, ctap2ConnectionOrder) { connection ->
|
|
271
|
+
WebAuthnClient.create(connection, null, null).use { client ->
|
|
272
|
+
val challenge = base64Decode(options.getString("challenge")) ?: byteArrayOf()
|
|
273
|
+
val clientData = buildClientData("webauthn.get", challenge, effectiveDomain)
|
|
274
|
+
val rpId = options.getString("rpId") ?: effectiveDomain
|
|
275
|
+
|
|
276
|
+
val allowCredentials = options.getArray("allowCredentials")?.let { arr ->
|
|
277
|
+
(0 until arr.size()).map { i ->
|
|
278
|
+
val m = arr.getMap(i)!!
|
|
279
|
+
PublicKeyCredentialDescriptor(
|
|
280
|
+
m.getString("type") ?: "public-key",
|
|
281
|
+
base64Decode(m.getString("id")) ?: byteArrayOf(),
|
|
282
|
+
m.getArray("transports")?.toArrayList()?.map { it.toString() }
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
val extensions = options.getMap("extensions")?.toHashMap()?.let { Extensions.fromMap(it) }
|
|
288
|
+
|
|
289
|
+
val requestOptions = PublicKeyCredentialRequestOptions(
|
|
290
|
+
challenge,
|
|
291
|
+
if (options.hasKey("timeout") && !options.isNull("timeout")) options.getDouble("timeout").toLong() else null,
|
|
292
|
+
rpId,
|
|
293
|
+
allowCredentials,
|
|
294
|
+
options.getString("userVerification"),
|
|
295
|
+
extensions
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
client.getAssertion(clientData, requestOptions, effectiveDomain, pin?.toCharArray(), null)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
promise.resolve(mapToPublicKeyCredential(credential))
|
|
302
|
+
} catch (e: Exception) {
|
|
303
|
+
promise.reject("FIDO_ERROR", e.message, e)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
override fun reset(deviceHandle: String, promise: Promise) {
|
|
309
|
+
withFidoConnection(deviceHandle, promise) { connection ->
|
|
310
|
+
openCtap2Session(connection).use { it.reset(null) }
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private fun withCredentialManager(
|
|
315
|
+
deviceHandle: String,
|
|
316
|
+
pin: String,
|
|
317
|
+
promise: Promise,
|
|
318
|
+
block: (CredentialManager) -> Any?
|
|
319
|
+
) {
|
|
320
|
+
moduleScope.launch {
|
|
321
|
+
try {
|
|
322
|
+
val result = YubiKitManagerHolder.withAnyConnection(deviceHandle, ctap2ConnectionOrder) { connection ->
|
|
323
|
+
Ctap2Client(openCtap2Session(connection)).use { client ->
|
|
324
|
+
val manager = client.getCredentialManager(pin.toCharArray())
|
|
325
|
+
block(manager)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
when (result) {
|
|
329
|
+
is Unit -> promise.resolve(null)
|
|
330
|
+
else -> promise.resolve(result)
|
|
331
|
+
}
|
|
332
|
+
} catch (e: Exception) {
|
|
333
|
+
promise.reject("FIDO_ERROR", e.message, e)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
override fun getCredentialCount(deviceHandle: String, pin: String, promise: Promise) {
|
|
339
|
+
withCredentialManager(deviceHandle, pin, promise) { it.credentialCount }
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
override fun getRpIdList(deviceHandle: String, pin: String, promise: Promise) {
|
|
343
|
+
withCredentialManager(deviceHandle, pin, promise) {
|
|
344
|
+
val list = it.rpIdList
|
|
345
|
+
val arr = Arguments.createArray()
|
|
346
|
+
list.forEach { rpId -> arr.pushString(rpId) }
|
|
347
|
+
arr
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
override fun getCredentials(
|
|
352
|
+
deviceHandle: String,
|
|
353
|
+
rpId: String,
|
|
354
|
+
pin: String,
|
|
355
|
+
promise: Promise
|
|
356
|
+
) {
|
|
357
|
+
withCredentialManager(deviceHandle, pin, promise) { manager ->
|
|
358
|
+
val creds = manager.getCredentials(rpId)
|
|
359
|
+
val arr = Arguments.createArray()
|
|
360
|
+
creds.forEach { (descriptor, user) ->
|
|
361
|
+
arr.pushMap(Arguments.createMap().apply {
|
|
362
|
+
putMap("credential", Arguments.createMap().apply {
|
|
363
|
+
putString("id", base64Encode(descriptor.id))
|
|
364
|
+
putString("type", descriptor.type)
|
|
365
|
+
})
|
|
366
|
+
putMap("user", Arguments.createMap().apply {
|
|
367
|
+
putString("id", base64Encode(user.id))
|
|
368
|
+
putString("name", user.name)
|
|
369
|
+
putString("displayName", user.displayName)
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
arr
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
override fun deleteCredential(
|
|
378
|
+
deviceHandle: String,
|
|
379
|
+
credential: ReadableMap,
|
|
380
|
+
pin: String,
|
|
381
|
+
promise: Promise
|
|
382
|
+
) {
|
|
383
|
+
withCredentialManager(deviceHandle, pin, promise) { manager ->
|
|
384
|
+
manager.deleteCredential(
|
|
385
|
+
PublicKeyCredentialDescriptor(
|
|
386
|
+
credential.getString("type") ?: "public-key",
|
|
387
|
+
base64Decode(credential.getString("id")) ?: byteArrayOf()
|
|
388
|
+
)
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
override fun updateUserInformation(
|
|
394
|
+
deviceHandle: String,
|
|
395
|
+
credential: ReadableMap,
|
|
396
|
+
user: ReadableMap,
|
|
397
|
+
pin: String,
|
|
398
|
+
promise: Promise
|
|
399
|
+
) {
|
|
400
|
+
withCredentialManager(deviceHandle, pin, promise) { manager ->
|
|
401
|
+
manager.updateUserInformation(
|
|
402
|
+
PublicKeyCredentialDescriptor(
|
|
403
|
+
credential.getString("type") ?: "public-key",
|
|
404
|
+
base64Decode(credential.getString("id")) ?: byteArrayOf()
|
|
405
|
+
),
|
|
406
|
+
PublicKeyCredentialUserEntity(
|
|
407
|
+
user.getString("name") ?: "",
|
|
408
|
+
base64Decode(user.getString("id")) ?: byteArrayOf(),
|
|
409
|
+
user.getString("displayName") ?: ""
|
|
410
|
+
)
|
|
411
|
+
)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
companion object {
|
|
416
|
+
const val NAME = "YubikitFido"
|
|
417
|
+
}
|
|
418
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
package com.yubikit
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.facebook.react.bridge.ReadableMap
|
|
6
|
+
import com.yubico.yubikit.core.YubiKeyConnection
|
|
7
|
+
import com.yubico.yubikit.core.fido.FidoConnection
|
|
8
|
+
import com.yubico.yubikit.core.otp.OtpConnection
|
|
9
|
+
import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
|
10
|
+
import com.yubico.yubikit.management.ManagementSession
|
|
11
|
+
import com.yubico.yubikit.core.UsbInterface
|
|
12
|
+
import com.yubikit.utils.YubikitUtils.base64Decode
|
|
13
|
+
import com.yubikit.utils.YubikitUtils.deviceInfoToMap
|
|
14
|
+
import com.yubikit.utils.YubikitUtils.parseDeviceConfig
|
|
15
|
+
import com.yubikit.utils.YubikitUtils.parseUsbMode
|
|
16
|
+
import kotlinx.coroutines.CoroutineScope
|
|
17
|
+
import kotlinx.coroutines.Dispatchers
|
|
18
|
+
import kotlinx.coroutines.launch
|
|
19
|
+
|
|
20
|
+
@Suppress("unused")
|
|
21
|
+
class YubikitManagementModule(reactContext: ReactApplicationContext) :
|
|
22
|
+
NativeYubikitManagementSpec(reactContext) {
|
|
23
|
+
|
|
24
|
+
private val moduleScope = CoroutineScope(Dispatchers.IO)
|
|
25
|
+
|
|
26
|
+
// ManagementSession supports SmartCard, Otp and Fido connections; hardcoding
|
|
27
|
+
// SmartCardConnection breaks OTP-only and FIDO-only-mode devices (e.g. a YubiKey
|
|
28
|
+
// with CCID disabled via USB mode config).
|
|
29
|
+
private val managementConnectionOrder = listOf(
|
|
30
|
+
SmartCardConnection::class.java as Class<out YubiKeyConnection>,
|
|
31
|
+
OtpConnection::class.java,
|
|
32
|
+
FidoConnection::class.java
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
private fun openManagementSession(connection: YubiKeyConnection): ManagementSession {
|
|
36
|
+
return when (connection) {
|
|
37
|
+
is SmartCardConnection -> ManagementSession(connection)
|
|
38
|
+
is OtpConnection -> ManagementSession(connection)
|
|
39
|
+
is FidoConnection -> ManagementSession(connection)
|
|
40
|
+
else -> throw IllegalArgumentException("Unsupported connection type for Management: $connection")
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override fun getName(): String = NAME
|
|
45
|
+
|
|
46
|
+
override fun getDeviceInfo(deviceHandle: String, promise: Promise) {
|
|
47
|
+
moduleScope.launch {
|
|
48
|
+
try {
|
|
49
|
+
val info = YubiKitManagerHolder.withAnyConnection(deviceHandle, managementConnectionOrder) { connection ->
|
|
50
|
+
openManagementSession(connection).use { session ->
|
|
51
|
+
session.deviceInfo
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
promise.resolve(deviceInfoToMap(info))
|
|
55
|
+
} catch (e: Exception) {
|
|
56
|
+
promise.reject("MANAGEMENT_ERROR", e.message, e)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override fun updateDeviceConfig(
|
|
62
|
+
deviceHandle: String,
|
|
63
|
+
config: ReadableMap,
|
|
64
|
+
reboot: Boolean,
|
|
65
|
+
currentLockCode: String?,
|
|
66
|
+
newLockCode: String?,
|
|
67
|
+
promise: Promise
|
|
68
|
+
) {
|
|
69
|
+
moduleScope.launch {
|
|
70
|
+
try {
|
|
71
|
+
val deviceConfig = parseDeviceConfig(config)
|
|
72
|
+
YubiKitManagerHolder.withAnyConnection(deviceHandle, managementConnectionOrder) { connection ->
|
|
73
|
+
openManagementSession(connection).use { session ->
|
|
74
|
+
session.updateDeviceConfig(
|
|
75
|
+
deviceConfig,
|
|
76
|
+
reboot,
|
|
77
|
+
base64Decode(currentLockCode),
|
|
78
|
+
base64Decode(newLockCode)
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
promise.resolve(null)
|
|
83
|
+
} catch (e: Exception) {
|
|
84
|
+
promise.reject("MANAGEMENT_ERROR", e.message, e)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
override fun setMode(
|
|
90
|
+
deviceHandle: String,
|
|
91
|
+
mode: String,
|
|
92
|
+
chalrespTimeout: Double,
|
|
93
|
+
autoejectTimeout: Double,
|
|
94
|
+
promise: Promise
|
|
95
|
+
) {
|
|
96
|
+
moduleScope.launch {
|
|
97
|
+
try {
|
|
98
|
+
YubiKitManagerHolder.withAnyConnection(deviceHandle, managementConnectionOrder) { connection ->
|
|
99
|
+
openManagementSession(connection).use { session ->
|
|
100
|
+
session.setMode(
|
|
101
|
+
parseUsbMode(mode),
|
|
102
|
+
chalrespTimeout.toInt().toByte(),
|
|
103
|
+
autoejectTimeout.toInt().toShort()
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
promise.resolve(null)
|
|
108
|
+
} catch (e: Exception) {
|
|
109
|
+
promise.reject("MANAGEMENT_ERROR", e.message, e)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
override fun deviceReset(deviceHandle: String, promise: Promise) {
|
|
115
|
+
moduleScope.launch {
|
|
116
|
+
try {
|
|
117
|
+
YubiKitManagerHolder.withAnyConnection(deviceHandle, managementConnectionOrder) { connection ->
|
|
118
|
+
openManagementSession(connection).use { session ->
|
|
119
|
+
session.deviceReset()
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
promise.resolve(null)
|
|
123
|
+
} catch (e: Exception) {
|
|
124
|
+
promise.reject("MANAGEMENT_ERROR", e.message, e)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
companion object {
|
|
130
|
+
const val NAME = "YubikitManagement"
|
|
131
|
+
}
|
|
132
|
+
}
|