separate implementation from boilerplate/bridging code

This commit is contained in:
Michele Balistreri 2020-12-02 16:24:27 +03:00
parent f322b67096
commit 89bb22046c
4 changed files with 98 additions and 84 deletions

42
ios/SmartCard.swift Normal file
View File

@ -0,0 +1,42 @@
import Foundation
import Keycard
@available(iOS 13.0, *)
class SmartCard {
func nfcIsSupported() -> Bool {
return KeycardController.isAvailable
}
func initCard(channel: CardChannel, pin: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) throws -> Void {
//let cmdSet = KeycardCommandSet(cardChannel: channel)
//cmdSet.select().checkOK()
//SmartCardSecrets s = SmartCardSecrets.generate(userPin);
//cmdSet.init(s.getPin(), s.getPuk(), s.getPairingPassword()).checkOK();
}
func signPinless(channel: CardChannel, hash: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) throws -> Void {
let cmdSet = CashCommandSet(cardChannel: channel)
let info = try CashApplicationInfo(cmdSet.select().checkOK().data)
let message = self.hexToBytes(hash)
let res = try cmdSet.sign(data: message).checkOK()
resolve(res.data.toHexString())
}
func hexToBytes(_ hex: String) -> [UInt8] {
var last = hex.first
return hex.dropFirst().compactMap {
guard
let lastHexDigitValue = last?.hexDigitValue,
let hexDigitValue = $0.hexDigitValue
else {
last = $0
return nil
}
defer {
last = nil
}
return UInt8(lastHexDigitValue * 16 + hexDigitValue)
}
}
}

View File

@ -4,6 +4,7 @@
RCT_EXTERN_METHOD(nfcIsSupported:(RCTPromiseResolveBlock)resolve rejecter: (RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(openNfcSettings:(RCTPromiseResolveBlock)resolve rejecter: (RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(init:(NSString *)pin resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(signPinless:(NSString *)hash resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)

View File

@ -3,106 +3,73 @@ import Keycard
@objc(StatusKeycard)
class StatusKeycard: NSObject {
@available(iOS 13.0, *)
private(set) lazy var keycardController: KeycardController? = nil
private(set) lazy var smartCard: SmartCard? = nil
override init() {
super.init()
@objc
func select() {
if #available(iOS 13.0, *) {
keycardController = KeycardController(onConnect: { [unowned self] channel in
do {
let cmdSet = KeycardCommandSet(cardChannel: channel)
let info = try ApplicationInfo(cmdSet.select().checkOK().data)
print(info)
self.keycardController?.stop(alertMessage: "Success")
} catch {
print("Error: \(error)")
self.keycardController?.stop(errorMessage: "Read error. Please try again.")
}
self.keycardController = nil
}, onFailure: { [unowned self] error in
print("Disconnected: \(error)")
self.keycardController = nil
})
keycardController?.start(alertMessage: "Hold your iPhone near a Status Keycard.")
} else {
print("Unavailable")
}
self.smartCard = SmartCard()
}
}
@objc
func openNfcSettings(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
reject("E_KEYCARD", "Unsupported on iOS", nil)
}
@objc
func signPinless(_ hash: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
if #available(iOS 13.0, *) {
DispatchQueue.main.async {
self.keycardController = KeycardController(onConnect: { [unowned self] channel in
do {
let cmdSet = CashCommandSet(cardChannel: channel)
let info = try CashApplicationInfo(cmdSet.select().checkOK().data)
print("SELECT")
print(info)
let message = self.hexToBytes(hash)
let res = try cmdSet.sign(data: message).checkOK()
print("SELECT")
print(res)
self.keycardController?.stop(alertMessage: "SELECT: Success")
print("DONE!")
resolve(res.data.toHexString())
} catch {
reject("E_KEYCARD", "error", nil)
print("Error: \(error)")
self.keycardController?.stop(errorMessage: "Read error. Please try again.")
}
self.keycardController = nil
}, onFailure: { [unowned self] error in
reject("E_KEYCARD", "disconnected", nil)
print("Disconnected: \(error)")
self.keycardController = nil
})
self.keycardController?.start(alertMessage: "Hold your iPhone near a Status Keycard.")
}
} else {
print("Unavailable")
reject("E_KEYCARD", "unavailable", nil)
}
}
@objc
func nfcIsSupported(_ resolve: RCTPromiseResolveBlock, rejecter _: RCTPromiseRejectBlock) -> Void {
func nfcIsSupported(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
if #available(iOS 13.0, *) {
resolve(KeycardController.isAvailable)
resolve(smartCard?.nfcIsSupported())
} else {
resolve(false)
}
}
@objc
func nfcIsEnabled(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
// On iOS NFC is always enabled (if available)
nfcIsSupported(resolve, rejecter: reject)
}
@objc
func openNfcSettings(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
// NFC cannot be enabled/disabled
reject("E_KEYCARD", "Unsupported on iOS", nil)
}
@objc
func `init`(_ pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
keycardInvokation(reject) { [unowned self] channel in if #available(iOS 13.0, *) { try self.smartCard?.initCard(channel: channel, pin: pin, resolve: resolve, reject: reject) } }
}
@objc
func signPinless(_ hash: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
keycardInvokation(reject) { [unowned self] channel in if #available(iOS 13.0, *) { try self.smartCard?.signPinless(channel: channel, hash: hash, resolve: resolve, reject: reject)} }
}
@objc
static func requiresMainQueueSetup() -> Bool {
return true
}
func hexToBytes(_ hex: String) -> [UInt8] {
var last = hex.first
return hex.dropFirst().compactMap {
guard
let lastHexDigitValue = last?.hexDigitValue,
let hexDigitValue = $0.hexDigitValue else {
last = $0
return nil
}
defer {
last = nil
}
return UInt8(lastHexDigitValue * 16 + hexDigitValue)
func keycardInvokation(_ reject: @escaping RCTPromiseRejectBlock, body: @escaping (CardChannel) throws -> Void) {
if #available(iOS 13.0, *) {
DispatchQueue.main.async {
var keycardController: KeycardController? = nil;
keycardController = KeycardController(onConnect: { channel in
do {
try body(channel)
keycardController?.stop(alertMessage: "Success")
} catch {
reject("E_KEYCARD", "error", error)
keycardController?.stop(errorMessage: "Read error. Please try again.")
}
}, onFailure: { error in
reject("E_KEYCARD", "disconnected", error)
})
keycardController?.start(alertMessage: "Hold your iPhone near a Status Keycard.")
}
} else {
reject("E_KEYCARD", "unavailable", nil)
}
}
}

View File

@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
65B8C633256D352A00C6A3EE /* Keycard in Frameworks */ = {isa = PBXBuildFile; productRef = 65B8C632256D352A00C6A3EE /* Keycard */; };
F4FF95D7245B92E800C19C63 /* StatusKeycard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* StatusKeycard.swift */; };
F74884362577A87A00D908D2 /* SmartCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74884352577A87A00D908D2 /* SmartCard.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -28,6 +29,7 @@
B3E7B5891CC2AC0600A0062D /* StatusKeycard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StatusKeycard.m; sourceTree = "<group>"; };
F4FF95D5245B92E700C19C63 /* StatusKeycard-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StatusKeycard-Bridging-Header.h"; sourceTree = "<group>"; };
F4FF95D6245B92E800C19C63 /* StatusKeycard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusKeycard.swift; sourceTree = "<group>"; };
F74884352577A87A00D908D2 /* SmartCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartCard.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -53,6 +55,7 @@
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
F74884352577A87A00D908D2 /* SmartCard.swift */,
F4FF95D6245B92E800C19C63 /* StatusKeycard.swift */,
B3E7B5891CC2AC0600A0062D /* StatusKeycard.m */,
F4FF95D5245B92E700C19C63 /* StatusKeycard-Bridging-Header.h */,
@ -124,6 +127,7 @@
buildActionMask = 2147483647;
files = (
F4FF95D7245B92E800C19C63 /* StatusKeycard.swift in Sources */,
F74884362577A87A00D908D2 /* SmartCard.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};