248 lines
10 KiB
Swift
Executable File
248 lines
10 KiB
Swift
Executable File
//
|
|
// BrizziApi.swift
|
|
// Emoney Info
|
|
//
|
|
// Created by Wira Irawan on 27/07/24.
|
|
//
|
|
|
|
import Foundation
|
|
import CoreNFC
|
|
|
|
public class BrizziApi : UnifiedNfcApi {
|
|
var emoney : Emoney = Emoney()
|
|
var riwayatList: [RiwayatCard] = []
|
|
var uid : String?
|
|
var rawLog : String = ""
|
|
|
|
public override init() {}
|
|
|
|
public func getUid(){
|
|
apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BRI_UID01, completionHandler: {response in
|
|
if (response.sw1 == 0x91 && response.sw2 == 0xAF){
|
|
self.getUid02()
|
|
} else {
|
|
self.apduRunner.invalidateSession(msg: "readFailed".localizeString(string: self.langCode!))
|
|
}
|
|
})
|
|
}
|
|
|
|
private func getUid02(){
|
|
apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BRI_UID02, completionHandler: {response in
|
|
if (response.sw1 == 0x91 && response.sw2 == 0xAF){
|
|
self.getUid02()
|
|
} else {
|
|
self.uid = response.getData().hexEncodedString().subString(from: 0, to: 14)
|
|
self.getCardNumber()
|
|
}
|
|
})
|
|
}
|
|
|
|
private func getCardNumber(){
|
|
apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BRI_APDU01, completionHandler: {response in
|
|
if (response.sw1 == 0x91 && response.sw2 == 0x00){
|
|
self.emoney.setCardLabel("Brizzi")
|
|
self.emoney.setCardNumber(response.getData().hexEncodedString().subString(from: 6, to: 22))
|
|
self.process01()
|
|
} else {
|
|
self.apduRunner.invalidateSession(msg: "readFailed".localizeString(string: self.langCode!))
|
|
}
|
|
})
|
|
}
|
|
|
|
private func process01(){
|
|
apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BRI_APDU02, completionHandler: {response in
|
|
if (response.sw1 == 0x91 && response.sw2 == 0x00){
|
|
self.process02()
|
|
} else {
|
|
self.apduRunner.invalidateSession(msg: "readFailed".localizeString(string: self.langCode!))
|
|
}
|
|
})
|
|
}
|
|
|
|
private func process02(){
|
|
apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BRI_APDU03, completionHandler: {response in
|
|
if (response.sw1 == 0x91 && response.sw2 == 0x00) || (response.sw1 == 0x91 && response.sw2 == 0xAF){
|
|
self.process03(data: response.getData())
|
|
} else {
|
|
self.apduRunner.invalidateSession(msg: "readFailed".localizeString(string: self.langCode!))
|
|
}
|
|
})
|
|
}
|
|
|
|
private func process03(data : Data){
|
|
let brizziSamHelper = BrizziSamHelper()
|
|
brizziSamHelper.keyCard = data.hexEncodedString()
|
|
let random = "8DC0DC40FE1DC582CF7099E2AACFBC10".hex2byte()
|
|
let command = self.emoney.getCardNumber() + self.uid! + "FF"
|
|
let decrypted = BrizziSamHelper.decryptDeSeDe(random)?.hexEncodedString()
|
|
|
|
let decryptedFinal = decrypted?.subString(from: 0, to: 32)
|
|
let encrypted = BrizziSamHelper.encryptDeSeDe(command, decryptedFinal!, "0000000000000000")?.hexEncodedString()
|
|
|
|
brizziSamHelper.encryptedKey = encrypted?.subString(from: 0, to: 32)
|
|
|
|
|
|
let randomHex = "3C37029CA595FE4E7E62FCB2F7909B2C".hex2byte()
|
|
let randomHexDecrypted = BrizziSamHelper.decryptDeSeDe(randomHex)
|
|
|
|
let randomHexFinal = randomHexDecrypted?.hexEncodedString().subString(from: 0, to: 32)
|
|
let randomHexEncrypted = BrizziSamHelper.encryptDeSeDe(brizziSamHelper.encryptedKey!, randomHexFinal!, brizziSamHelper.authKey)
|
|
brizziSamHelper.random = (randomHexEncrypted?.hexEncodedString())!.subString(from: 0, to: 32)
|
|
|
|
|
|
let samChallenge = brizziSamHelper.generateSamRandom().subString(from: 0, to: 32)
|
|
let BRI_APDU04 = NFCISO7816APDU(instructionClass : 0x90, instructionCode : 0xAF, p1Parameter : 0x00, p2Parameter : 0x00, data : Data(_: samChallenge.hex2byte()), expectedResponseLength : CommonConstants.LE_GET_ALL_RESPONSE_DATA)
|
|
apduRunner.exchangeApdu(apduCommand: BRI_APDU04, completionHandler: {response in
|
|
if (response.sw1 == 0x91 && response.sw2 == 0x00) || (response.sw1 == 0x91 && response.sw2 == 0xAF){
|
|
self.process04()
|
|
} else {
|
|
self.apduRunner.invalidateSession(msg: "readFailed".localizeString(string: self.langCode!))
|
|
}
|
|
})
|
|
}
|
|
|
|
private func process04(){
|
|
apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BRI_APDU05, completionHandler: {response in
|
|
if (response.sw1 == 0x91 && response.sw2 == 0x00) || (response.sw1 == 0x91 && response.sw2 == 0xAF){
|
|
self.emoney.setBalance(self.getRealBalance(reverseHexa: response.getData().hexEncodedString().subString(from: 0, to: 8)))
|
|
self.getLog()
|
|
} else {
|
|
self.apduRunner.invalidateSession(msg: "readFailed".localizeString(string: self.langCode!))
|
|
}
|
|
})
|
|
}
|
|
|
|
private func getLog(){
|
|
apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BRI_LOG01, completionHandler: {response in
|
|
if (response.sw1 == 0x91 && response.sw2 == 0x00) || (response.sw1 == 0x91 && response.sw2 == 0xAF){
|
|
self.rawLog.append(response.getData().hexEncodedString())
|
|
self.getMoreLog()
|
|
} else {
|
|
self.updateScreen()
|
|
self.apduRunner.sessionEx?.alertMessage = "readFinish".localizeString(string: self.langCode!)
|
|
self.apduRunner.invalidateSession()
|
|
}
|
|
})
|
|
}
|
|
|
|
private func getMoreLog(){
|
|
apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BRI_LOG02, completionHandler: {response in
|
|
if (response.sw1 == 0x91 && response.sw2 == 0xAF){
|
|
self.rawLog.append(response.getData().hexEncodedString())
|
|
self.getMoreLog()
|
|
} else {
|
|
self.rawLog.append(response.getData().hexEncodedString())
|
|
|
|
if (self.parseLog()){
|
|
self.riwayatList = self.riwayatList.sorted(by: { $0.getTransationTime()?.compare($1.getTransationTime()!) == .orderedDescending })
|
|
self.emoney.setRiwayatList(self.riwayatList)
|
|
self.emoney.setTampilRiwayat(true)
|
|
}
|
|
self.updateScreen()
|
|
self.apduRunner.sessionEx?.alertMessage = "readFinish".localizeString(string: self.langCode!)
|
|
self.apduRunner.invalidateSession()
|
|
}
|
|
})
|
|
}
|
|
|
|
private func parseLog() -> Bool {
|
|
let logs = self.rawLog.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
if (logs.count % 64 != 0){
|
|
return false
|
|
}
|
|
let total = logs.count/64
|
|
for i in 0..<total {
|
|
let start = i*64
|
|
let end = start + 64
|
|
let data = logs.subString(from: start, to: end)
|
|
let riwayat = RiwayatCard()
|
|
// riwayat.setLocationId(data.subString(from: 16, to: 32))
|
|
// riwayat.setLocationName(data.subString(from: 0, to: 16))
|
|
riwayat.setTitle(self.getCode(trxCode: data.subString(from: 44, to: 46)))
|
|
riwayat.setProsesTipe(self.getTipe(trxCode: data.subString(from: 44, to: 46)))
|
|
riwayat.setAmount(self.getRealBalance(reverseHexa: data.subString(from: 46, to: 52)))
|
|
|
|
let time = self.getTransactionTime(formatDate: data.subString(from: 32, to: 38), formatTime: data.subString(from: 38, to: 44))
|
|
riwayat.setTransactionTime(time!)
|
|
|
|
riwayatList.append(riwayat)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func getTransactionTime(formatDate : String, formatTime : String) -> Date?{
|
|
let dateFormatter2 = DateFormatter()
|
|
dateFormatter2.dateFormat = "HHmmss"
|
|
let date12 = dateFormatter2.date(from: formatTime)!
|
|
|
|
dateFormatter2.dateFormat = "hh:mm a"
|
|
let date22 = dateFormatter2.string(from: date12)
|
|
|
|
let dateFormatter = DateFormatter()
|
|
dateFormatter.locale = Locale(identifier: "en_US_POSIX") // set locale to reliable US_POSIX
|
|
dateFormatter.dateFormat = "ddMMyy hh:mm a"
|
|
return dateFormatter.date(from:(formatDate + " " + date22))
|
|
}
|
|
|
|
private func getCode(trxCode: String) -> String {
|
|
let trxUp = trxCode.uppercased()
|
|
if trxUp.contains("5F") {
|
|
return "reactivation".localizeString(string: self.langCode!)
|
|
} else if trxUp.contains("EB") {
|
|
return "payment".localizeString(string: self.langCode!)
|
|
} else if trxUp.contains("EC") {
|
|
return "topup".localizeString(string: self.langCode!)
|
|
} else if trxUp.contains("ED") {
|
|
return "void".localizeString(string: self.langCode!)
|
|
} else if trxUp.contains("EF") {
|
|
return "updateBalance".localizeString(string: self.langCode!)
|
|
} else {
|
|
return "-"
|
|
}
|
|
}
|
|
|
|
private func getTipe(trxCode: String) -> Int {
|
|
let trxUp = trxCode.uppercased()
|
|
if trxUp.contains("5F") {
|
|
return 2
|
|
} else if trxUp.contains("EB") {
|
|
return 1
|
|
} else if trxUp.contains("EC") {
|
|
return 0
|
|
} else if trxUp.contains("ED") {
|
|
return 0
|
|
} else if trxUp.contains("EF") {
|
|
return 1
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
|
|
func getRealBalance(reverseHexa: String?) -> Int {
|
|
guard let reverseHexa = reverseHexa?.trimmingCharacters(in: .whitespacesAndNewlines), !reverseHexa.isEmpty else {
|
|
return 0
|
|
}
|
|
|
|
if reverseHexa.count % 2 != 0 {
|
|
return 0
|
|
}
|
|
|
|
var sb = ""
|
|
for l in stride(from: reverseHexa.count / 2, through: 1, by: -1) {
|
|
let index1 = reverseHexa.index(reverseHexa.startIndex, offsetBy: l * 2 - 2)
|
|
let index2 = reverseHexa.index(reverseHexa.startIndex, offsetBy: l * 2 - 1)
|
|
sb.append(reverseHexa[index1])
|
|
sb.append(reverseHexa[index2])
|
|
}
|
|
return sb.hex2decimal()
|
|
}
|
|
|
|
private func updateScreen(){
|
|
if (self.apduRunner.callback != nil){
|
|
self.apduRunner.callback?.complete(emoney: self.emoney)
|
|
}
|
|
}
|
|
}
|