2020-11-25 10:24:35 +01:00
|
|
|
import Foundation
|
|
|
|
import Keycard
|
2021-01-13 17:19:11 +03:00
|
|
|
import UIKit
|
2020-11-25 10:24:35 +01:00
|
|
|
|
2020-11-24 12:29:25 +01:00
|
|
|
@objc(StatusKeycard)
|
2020-12-04 09:21:08 +03:00
|
|
|
class StatusKeycard: RCTEventEmitter {
|
2020-12-03 08:32:00 +03:00
|
|
|
let smartCard = SmartCard()
|
2020-12-04 09:21:08 +03:00
|
|
|
var cardChannel: CardChannel? = nil
|
2021-01-13 12:42:24 +03:00
|
|
|
|
2020-12-04 09:21:08 +03:00
|
|
|
@available(iOS 13.0, *)
|
|
|
|
private(set) lazy var keycardController: KeycardController? = nil
|
2020-11-25 10:24:35 +01:00
|
|
|
|
|
|
|
@objc
|
2020-12-03 10:30:11 +03:00
|
|
|
func nfcIsSupported(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
|
2020-11-25 10:24:35 +01:00
|
|
|
if #available(iOS 13.0, *) {
|
2020-12-03 08:32:00 +03:00
|
|
|
resolve(KeycardController.isAvailable)
|
2020-11-25 10:24:35 +01:00
|
|
|
} else {
|
2020-12-02 16:24:27 +03:00
|
|
|
resolve(false)
|
2020-11-25 10:24:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-02 16:24:27 +03:00
|
|
|
@objc
|
2020-12-03 10:30:11 +03:00
|
|
|
func nfcIsEnabled(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
|
2020-12-02 16:24:27 +03:00
|
|
|
// On iOS NFC is always enabled (if available)
|
2020-12-03 10:30:11 +03:00
|
|
|
nfcIsSupported(resolve, reject: reject)
|
2021-01-13 12:42:24 +03:00
|
|
|
}
|
2020-12-02 16:24:27 +03:00
|
|
|
|
2020-11-25 10:24:35 +01:00
|
|
|
@objc
|
2020-12-03 10:30:11 +03:00
|
|
|
func openNfcSettings(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
|
2020-12-02 16:24:27 +03:00
|
|
|
// NFC cannot be enabled/disabled
|
|
|
|
reject("E_KEYCARD", "Unsupported on iOS", nil)
|
2020-11-25 10:24:35 +01:00
|
|
|
}
|
|
|
|
|
2020-12-01 21:51:39 +01:00
|
|
|
@objc
|
2020-12-02 16:24:27 +03:00
|
|
|
func `init`(_ pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2020-12-03 08:32:00 +03:00
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.initialize(channel: channel, pin: pin, resolve: resolve, reject: reject) }
|
2020-12-01 21:51:39 +01:00
|
|
|
}
|
2021-01-13 12:42:24 +03:00
|
|
|
|
2020-12-03 10:30:11 +03:00
|
|
|
@objc
|
|
|
|
func pair(_ pairingPassword: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.pair(channel: channel, pairingPassword: pairingPassword, resolve: resolve, reject: reject) }
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func generateMnemonic(_ pairing: String, words: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.generateMnemonic(channel: channel, pairingBase64: pairing, words: words, resolve: resolve, reject: reject) }
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func generateAndLoadKey(_ mnemonic: String, pairing: String, pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2021-01-13 12:42:24 +03:00
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.generateAndLoadKey(channel: channel, mnemonic: mnemonic, pairingBase64: pairing, pin: pin, resolve: resolve, reject: reject) }
|
2020-12-03 10:30:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func saveMnemonic(_ mnemonic: String, pairing: String, pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2021-01-13 12:42:24 +03:00
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.saveMnemonic(channel: channel, mnemonic: mnemonic, pairingBase64: pairing, pin: pin, resolve: resolve, reject: reject) }
|
2020-12-03 10:30:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func getApplicationInfo(_ pairingBase64: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.getApplicationInfo(channel: channel, pairingBase64: pairingBase64, resolve: resolve, reject: reject) }
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func deriveKey(_ path: String, pairing: String, pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.deriveKey(channel: channel, path: path, pairingBase64: pairing, pin: pin, resolve: resolve, reject: reject) }
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func exportKey(_ pairing: String, pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.exportKey(channel: channel, pairingBase64: pairing, pin: pin, resolve: resolve, reject: reject) }
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func exportKeyWithPath(_ pairing: String, pin: String, path: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.exportKeyWithPath(channel: channel, pairingBase64: pairing, pin: pin, path: path, resolve: resolve, reject: reject) }
|
|
|
|
}
|
2021-01-13 12:42:24 +03:00
|
|
|
|
2020-12-03 10:30:11 +03:00
|
|
|
@objc
|
|
|
|
func getKeys(_ pairing: String, pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.getKeys(channel: channel, pairingBase64: pairing, pin: pin, resolve: resolve, reject: reject) }
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func sign(_ pairing: String, pin: String, hash: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.sign(channel: channel, pairingBase64: pairing, pin: pin, message: hash, resolve: resolve, reject: reject) }
|
|
|
|
}
|
2021-01-13 12:42:24 +03:00
|
|
|
|
2020-12-03 10:30:11 +03:00
|
|
|
@objc
|
|
|
|
func signWithPath(_ pairing: String, pin: String, path: String, hash: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.signWithPath(channel: channel, pairingBase64: pairing, pin: pin, path: path, message: hash, resolve: resolve, reject: reject) }
|
|
|
|
}
|
2020-12-01 21:51:39 +01:00
|
|
|
|
2020-11-25 10:24:35 +01:00
|
|
|
@objc
|
2020-12-02 16:24:27 +03:00
|
|
|
func signPinless(_ hash: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2020-12-03 10:30:11 +03:00
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.signPinless(channel: channel, message: hash, resolve: resolve, reject: reject) }
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func installApplet(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
reject("E_KEYCARD", "Not implemented (unused)", nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func installAppletAndInitCard(_ pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
reject("E_KEYCARD", "Not implemented (unused)", nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func verifyPin(_ pairing: String, pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2021-01-13 12:42:24 +03:00
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.verifyPin(channel: channel, pairingBase64: pairing, pin: pin, resolve: resolve, reject: reject) }
|
2020-12-03 10:30:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func changePin(_ pairing: String, currentPin: String, newPin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2021-01-13 12:42:24 +03:00
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.changePin(channel: channel, pairingBase64: pairing, currentPin: currentPin, newPin: newPin, resolve: resolve, reject: reject) }
|
2020-12-03 10:30:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func unblockPin(_ pairing: String, puk: String, newPin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2021-01-13 12:42:24 +03:00
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.unblockPin(channel: channel, pairingBase64: pairing, puk: puk, newPin: newPin, resolve: resolve, reject: reject) }
|
2020-12-03 10:30:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func unpair(_ pairing: String, pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2021-01-13 12:42:24 +03:00
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.unpair(channel: channel, pairingBase64: pairing, pin: pin, resolve: resolve, reject: reject) }
|
2020-12-03 10:30:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func delete(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
|
|
reject("E_KEYCARD", "Not implemented (unused)", nil)
|
2020-12-02 17:14:12 +03:00
|
|
|
}
|
2021-01-13 12:42:24 +03:00
|
|
|
|
2020-12-02 17:14:12 +03:00
|
|
|
@objc
|
2020-12-03 10:30:11 +03:00
|
|
|
func removeKey(_ pairing: String, pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2021-01-13 12:42:24 +03:00
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.removeKey(channel: channel, pairingBase64: pairing, pin: pin, resolve: resolve, reject: reject) }
|
2020-11-25 10:24:35 +01:00
|
|
|
}
|
2021-01-13 12:42:24 +03:00
|
|
|
|
2020-12-03 10:30:11 +03:00
|
|
|
@objc
|
|
|
|
func removeKeyWithUnpair(_ pairing: String, pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2021-01-13 12:42:24 +03:00
|
|
|
keycardInvokation(reject) { [unowned self] channel in try self.smartCard.removeKeyWithUnpair(channel: channel, pairingBase64: pairing, pin: pin, resolve: resolve, reject: reject) }
|
2020-12-03 10:30:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func unpairAndDelete(_ pairing: String, pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
2021-01-13 12:42:24 +03:00
|
|
|
reject("E_KEYCARD", "Not implemented (unused)", nil)
|
2020-12-03 10:30:11 +03:00
|
|
|
}
|
|
|
|
|
2020-11-25 10:24:35 +01:00
|
|
|
@objc
|
2020-12-15 13:51:07 +03:00
|
|
|
func startNFC(_ prompt: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
|
2020-12-02 16:24:27 +03:00
|
|
|
if #available(iOS 13.0, *) {
|
2020-12-04 09:21:08 +03:00
|
|
|
if (keycardController == nil) {
|
2021-01-13 12:42:24 +03:00
|
|
|
self.keycardController = KeycardController(onConnect: { [unowned self] channel in
|
2020-12-04 09:21:08 +03:00
|
|
|
self.cardChannel = channel
|
2021-01-13 17:19:11 +03:00
|
|
|
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
2020-12-04 09:21:08 +03:00
|
|
|
self.sendEvent(withName: "keyCardOnConnected", body: nil)
|
2020-12-15 13:51:07 +03:00
|
|
|
self.keycardController?.setAlert("Connected. Don't move your card.")
|
2020-12-04 09:21:08 +03:00
|
|
|
}, onFailure: { [unowned self] _ in
|
|
|
|
self.cardChannel = nil
|
|
|
|
self.sendEvent(withName: "keyCardOnDisconnected", body: nil)
|
2020-12-02 16:24:27 +03:00
|
|
|
})
|
2020-12-15 13:51:07 +03:00
|
|
|
keycardController?.start(alertMessage: prompt.isEmpty ? "Hold your iPhone near a Status Keycard." : prompt)
|
2020-12-04 09:21:08 +03:00
|
|
|
resolve(true)
|
|
|
|
} else {
|
|
|
|
reject("E_KEYCARD", "already started", nil)
|
2020-12-01 21:51:39 +01:00
|
|
|
}
|
2020-12-02 16:24:27 +03:00
|
|
|
} else {
|
|
|
|
reject("E_KEYCARD", "unavailable", nil)
|
2020-12-04 09:21:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-13 12:42:24 +03:00
|
|
|
@objc
|
2020-12-10 16:58:10 +03:00
|
|
|
func stopNFC(_ err: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
|
2020-12-04 09:21:08 +03:00
|
|
|
if #available(iOS 13.0, *) {
|
2020-12-10 16:58:10 +03:00
|
|
|
if (err.isEmpty) {
|
|
|
|
self.keycardController?.stop(alertMessage: "Success")
|
|
|
|
} else {
|
|
|
|
self.keycardController?.stop(errorMessage: err)
|
|
|
|
}
|
2020-12-04 09:21:08 +03:00
|
|
|
self.cardChannel = nil
|
|
|
|
self.keycardController = nil
|
|
|
|
resolve(true)
|
|
|
|
} else {
|
|
|
|
reject("E_KEYCARD", "unavailable", nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-13 12:42:24 +03:00
|
|
|
@objc
|
2020-12-15 13:51:07 +03:00
|
|
|
func setNFCMessage(_ message: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
|
|
|
|
if #available(iOS 13.0, *) {
|
|
|
|
self.keycardController?.setAlert(message)
|
|
|
|
resolve(true)
|
|
|
|
} else {
|
|
|
|
reject("E_KEYCARD", "unavailable", nil)
|
|
|
|
}
|
2021-01-13 12:42:24 +03:00
|
|
|
}
|
2020-12-15 13:51:07 +03:00
|
|
|
|
2020-12-04 09:21:08 +03:00
|
|
|
override static func requiresMainQueueSetup() -> Bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
override func supportedEvents() -> [String]! {
|
2020-12-08 13:16:33 +01:00
|
|
|
return ["keyCardOnConnected", "keyCardOnDisconnected", "keyCardOnNFCEnabled", "keyCardOnNFCDisabled"]
|
|
|
|
}
|
2020-12-04 09:21:08 +03:00
|
|
|
|
|
|
|
func keycardInvokation(_ reject: @escaping RCTPromiseRejectBlock, body: @escaping (CardChannel) throws -> Void) {
|
|
|
|
if self.cardChannel != nil {
|
2020-12-08 11:20:31 +01:00
|
|
|
DispatchQueue.global().async { [unowned self] in
|
2020-12-04 09:21:08 +03:00
|
|
|
do {
|
|
|
|
try body(self.cardChannel!)
|
|
|
|
} catch {
|
2021-01-13 12:42:24 +03:00
|
|
|
var errMsg = ""
|
|
|
|
|
|
|
|
if type(of: error) is NSError.Type {
|
|
|
|
let nsError = error as NSError
|
|
|
|
errMsg = "\(nsError.domain):\(nsError.code)"
|
|
|
|
if nsError.code == 100 && nsError.domain == "NFCError" {
|
|
|
|
self.sendEvent(withName: "keyCardOnDisconnected", body: nil)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
errMsg = "\(error)"
|
|
|
|
}
|
|
|
|
reject("E_KEYCARD", errMsg, error)
|
2020-12-04 09:21:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
reject("E_KEYCARD", "not connected", nil)
|
2021-01-13 12:42:24 +03:00
|
|
|
}
|
2020-12-01 21:51:39 +01:00
|
|
|
}
|
2020-11-24 12:29:25 +01:00
|
|
|
}
|