Updated on 2026-08-14

This commit is contained in:
Tangem 2019-10-18 18:30:41 +03:00
parent ac8ca2e51c
commit da003bf0d3
15 changed files with 577 additions and 265 deletions

View file

@ -3,6 +3,9 @@ apply plugin: "kotlin"
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "com.madgag.spongycastle:core:1.56.0.0"
implementation "com.madgag.spongycastle:prov:1.56.0.0"
implementation 'net.i2p.crypto:eddsa:0.3.0'
}
sourceCompatibility = "8"

View file

@ -13,4 +13,10 @@ data class CardEnvironment(
const val DEFAULT_PIN = "000000"
const val DEFAULT_PIN2 = "000"
}
}
enum class EncryptionMode(val code: Byte) {
NONE(0x0),
FAST(0x1),
STRONG(0x2)
}

View file

@ -1,36 +1,30 @@
package com.tangem
open class CardManager(
import com.tangem.commands.CommandResponse
import com.tangem.commands.CommandSerializer
import com.tangem.commands.SignCommandData
import com.tangem.tasks.*
class CardManager(
private val reader: CardReader,
private val cardManagerDelegate: CardManagerDelegate? = null,
dataStorage: DataStorage? = null) {
private val cardEnvironmentRepository = CardEnvironmentRepository(dataStorage)
var card: Card? = null
private set
fun scanCard(callback: (result: TaskResult) -> Unit) {
val task = ScanTask(cardManagerDelegate, reader)
runTask(task, cardEnvironmentRepository.cardEnvironment) {
if (it is TaskResult.CommandCompleted && it.resultData is Card) {
this.card = it.resultData
}
callback.invoke(it)
}
runTask(task, cardEnvironmentRepository.cardEnvironment, callback)
}
fun signTransaction(signCommandData: SignCommandData,
callback: (result: TaskResult) -> Unit) {
fun sign(signCommandData: SignCommandData,
callback: (result: TaskResult) -> Unit) {
val task = SignTask(signCommandData, cardManagerDelegate, reader)
runTask(task, cardEnvironmentRepository.cardEnvironment, callback)
}
fun runTask(task: Task, environment: CardEnvironment? = null,
callback: (result: TaskResult) -> Unit) {
callback: (result: TaskResult) -> Unit) {
task.run(environment ?: cardEnvironmentRepository.cardEnvironment) {result, returnedEnvironment ->
//TODO: environment is kept in Repository
cardEnvironmentRepository.cardEnvironment = returnedEnvironment
@ -38,16 +32,10 @@ open class CardManager(
}
}
fun runCommand(command: Command,
fun runCommand(commandSerializer: CommandSerializer<CommandResponse>,
environment: CardEnvironment? = null,
callback: (result: TaskResult) -> Unit) {
val task = BasicTask(command, cardManagerDelegate, reader)
val task = BasicTask<CommandResponse>(commandSerializer, cardManagerDelegate, reader)
runTask(task, environment, callback)
}
}
sealed class TaskResult {
data class Success(val resultData: Any) : TaskResult()
data class CommandCompleted(val resultData: Any) : TaskResult()
data class Error(val error: CardError) : TaskResult()
}

View file

@ -1,10 +1,12 @@
package com.tangem
import com.tangem.tasks.TaskError
interface CardManagerDelegate {
fun showSecurityDelay(seconds: Int)
fun hideSecurityDelay()
fun requestPin(success: () -> String, error: (cardError: CardError) -> CardError)
fun requestPin(success: () -> String, error: (cardError: TaskError.CardError) -> TaskError.CardError)
fun openNfcPopup()
fun closeNfcPopup()

View file

@ -1,6 +1,6 @@
package com.tangem
import com.tangem.data.ResponseApdu
import com.tangem.common.apdu.ResponseApdu
interface CardReader {
fun sendApdu(apduSerialized: ByteArray, callback: (responseApdu: ResponseApdu) -> Unit)

View file

@ -1,112 +0,0 @@
package com.tangem
import com.tangem.data.CommandApdu
import com.tangem.data.ResponseApduParsed
import com.tangem.data.Tlv
import com.tangem.enums.Instruction
import com.tangem.enums.TlvTag
abstract class Command {
abstract val instruction: Instruction
abstract val instructionCode: Int
abstract fun serialize(cardEnvironment: CardEnvironment): CommandApdu
abstract fun deserialize(responseApduParsed: ResponseApduParsed): TaskResult
}
class ReadCardCommand(val pin1: String) : Command() {
override val instruction = Instruction.Read
override val instructionCode = instruction.code
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
return CommandApdu(
listOf(Tlv(TlvTag.Pin, TlvTag.Pin.code, pin1.toByteArray())), instruction)
}
override fun deserialize(responseApduParsed: ResponseApduParsed): TaskResult {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class CheckWalletCommand(
val pin1: String, val cid: String,
val challenge: ByteArray, val publicKeyChallenge: ByteArray) : Command() {
override val instruction = Instruction.CheckWallet
override val instructionCode = instruction.code
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun deserialize(responseApduParsed: ResponseApduParsed): TaskResult {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class SignCommand(val signCommandData: SignCommandData) : Command() {
override val instruction = Instruction.Sign
override val instructionCode = instruction.code
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun deserialize(responseApduParsed: ResponseApduParsed): TaskResult {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
data class SignCommandData(
val cid: String,
val pin1: String,
val pin2: String,
val cvc: String,
val transactionHash: TransactionHash,
val issuerData: IssuerData,
val terminalPublicKey: ByteArray? = null,
val terminalTransactionSignature: ByteArray? = null
)
data class TransactionHash(
val transactionHash: ByteArray,
val transactionRaw: ByteArray,
val hashName: String
)
data class IssuerData(
val issuerData: ByteArray,
val issuerDataCounter: Int,
val issuerTransactionSignature: ByteArray,
val issuerDataSignature: ByteArray
)
sealed class CommandData {
data class CheckWalletResponse(
val cid: String,
val salt: ByteArray,
val walletSignature: ByteArray
)
}
sealed class CommandResult{
data class Success(val data: CommandData) : CommandResult()
data class Error(val error: CardError) : CommandResult()
}

View file

@ -1,16 +1,16 @@
package com.tangem
data class SignResponse(
val cid: String,
val signature: ByteArray,
val remainingSignatures: Int,
val signedHashes: Int
)
data class CardError(
val code: Int = 0
)
//
//data class SignResponse(
// val cid: String,
// val signature: ByteArray,
// val remainingSignatures: Int,
// val signedHashes: Int
//)
//
//
//data class CardError(
// val code: Int = 0
//)
//

View file

@ -1,114 +0,0 @@
package com.tangem
abstract class Task(protected val delegate: CardManagerDelegate? = null, private val reader: CardReader) {
abstract fun run(cardEnvironment: CardEnvironment,
callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit)
protected fun executeCommand(command: Command, cardEnvironment: CardEnvironment,
callback: (result: TaskResult) -> Unit) {
reader.sendApdu(command.serialize(cardEnvironment).serialize()) {
val parsedApdu = it.deserialize()
if (parsedApdu == null) callback.invoke(TaskResult.Error(CardError()))
else if (!parsedApdu.statusCompleted()) {
callback.invoke(TaskResult.Error(CardError(parsedApdu.status.code)))
} else {
val response = command.deserialize(parsedApdu)
callback.invoke(response)
}
}
}
}
class BasicTask(
private val command: Command,
delegate: CardManagerDelegate? = null,
reader: CardReader) : Task(delegate, reader) {
override fun run(cardEnvironment: CardEnvironment,
callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) {
executeCommand(command, cardEnvironment) {
callback.invoke(it, updateEnvironment())
}
}
private fun updateEnvironment() : CardEnvironment{
//TODO: set logic of changing CardEnvironment when needed
return CardEnvironment()
}
}
class SignTask(private val signCommandData: SignCommandData,
delegate: CardManagerDelegate? = null,
reader: CardReader) : Task(delegate, reader) {
private lateinit var callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit
override fun run(cardEnvironment: CardEnvironment,
callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) {
this.callback = callback
delegate?.openNfcPopup()
val signCommand = SignCommand(signCommandData)
executeCommand(signCommand, cardEnvironment) {
delegate?.closeNfcPopup()
run { callback(it, updateEnvironment()) }
}
}
private fun updateEnvironment() : CardEnvironment{
//TODO: set logic of changing CardEnvironment when needed
return CardEnvironment()
}
}
class ScanTask(delegate: CardManagerDelegate? = null, reader: CardReader) : Task(delegate, reader) {
private lateinit var card: Card
override fun run(cardEnvironment: CardEnvironment,
callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) {
delegate?.openNfcPopup()
val readCommand = ReadCardCommand(cardEnvironment.pin1)
executeCommand(readCommand, cardEnvironment) {
if (it is TaskResult.Success) {
card = it.resultData as Card
val checkWalletCommand = prepareCheckWalletCommand(cardEnvironment)
executeCommand(checkWalletCommand, cardEnvironment) {
delegate?.closeNfcPopup()
//TODO: pass check wallet response data
run { callback(it, updateEnvironment()) }
}
} else {
//TODO: Handle error
}
}
}
private fun updateEnvironment() : CardEnvironment{
//TODO: set logic of changing CardEnvironment when needed
return CardEnvironment()
}
private fun prepareCheckWalletCommand(cardEnvironment: CardEnvironment): CheckWalletCommand {
val challenge = generateChallenge()
return CheckWalletCommand(
cardEnvironment.pin1,
card.cid,
challenge,
byteArrayOf())
}
private fun generateChallenge(): ByteArray {
return byteArrayOf()
}
}

View file

@ -0,0 +1,53 @@
package com.tangem.commands
import com.tangem.CardEnvironment
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.ResponseApdu
import com.tangem.common.extentions.calculateSha256
import com.tangem.common.extentions.hexToBytes
import com.tangem.common.tlv.Tlv
import com.tangem.common.tlv.TlvMapper
import com.tangem.common.tlv.TlvTag
import com.tangem.enums.Instruction
class CheckWalletResponse(
val cardId: String,
val salt: ByteArray,
val walletSignature: ByteArray
) : CommandResponse
class CheckWalletCommand(
val pin1: String, val cid: String,
val challenge: ByteArray, val publicKeyChallenge: ByteArray) : CommandSerializer<CheckWalletResponse>() {
override val instruction = Instruction.CheckWallet
override val instructionCode = instruction.code
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
val tlvData = listOf(
Tlv(TlvTag.Pin, TlvTag.Pin.code, cardEnvironment.pin1.calculateSha256()),
Tlv(TlvTag.CardId, TlvTag.CardId.code, cid.hexToBytes()),
Tlv(TlvTag.Challenge, TlvTag.Challenge.code, challenge)
)
return CommandApdu(instructionCode, tlvData)
}
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): CheckWalletResponse? {
val tlvData = responseApdu.getTlvData() ?: return null
return try {
val mapper = TlvMapper(tlvData)
CheckWalletResponse(
mapper.map(TlvTag.CardId),
mapper.map(TlvTag.Salt),
mapper.map(TlvTag.Signature)
)
} catch (exception: Exception) {
null
}
}
}

View file

@ -0,0 +1,59 @@
package com.tangem.commands
import com.tangem.CardEnvironment
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.ResponseApdu
import com.tangem.enums.Instruction
//interface TlvMappable {
// fun responseFromTlv(tlvList: List<Tlv>): CommandResponse?
//}
interface CommandResponse
// : TlvMappable {
//
//}
abstract class CommandSerializer<T : CommandResponse>() {
abstract val instruction: Instruction
abstract val instructionCode: Int
abstract fun serialize(cardEnvironment: CardEnvironment): CommandApdu
abstract fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): T?
}
sealed class CommandEvent {
class Success(val response: Any) : CommandEvent()
class Failure() : CommandEvent()
class UserCancellation : CommandEvent()
}
//class CheckWalletResponse(
// val cardId: String,
// val salt: ByteArray,
// val walletSignature: ByteArray
//) : CommandResponse, TlvMappable by CheckWalletResponse {
//
// companion object : TlvMappable {
// override fun responseFromTlv(tlvList: List<Tlv>): CommandResponse? {
// val mapper = TlvMapper(tlvList)
//
// return try {
// CheckWalletResponse(
// mapper.map(TlvTag.CardId),
// mapper.map(TlvTag.Salt),
// mapper.map(TlvTag.Signature)
// )
// } catch (exception: TlvMapperException) {
// null
// }
// }
//
// }
//}

View file

@ -0,0 +1,152 @@
package com.tangem.commands
import com.tangem.CardEnvironment
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.ResponseApdu
import com.tangem.common.extentions.calculateSha256
import com.tangem.common.tlv.Tlv
import com.tangem.common.tlv.TlvMapper
import com.tangem.common.tlv.TlvTag
import com.tangem.data.SettingsMask
import com.tangem.enums.Instruction
enum class SigningMethod(val code: Int) {
SignHash(0),
SignRaw(1),
SignHashValidatedByIssuer(2),
SignRawValidatedByIssuer(3),
SignHashValidatedByIssuerAndWriteIssuerData(4),
SignRawValidatedByIssuerAndWriteIssuerData(5),
SignPos(6);
companion object {
fun byCode(code: Int): SigningMethod? = values().find { it.code == code }
}
}
enum class EllipticCurve(val curve: String) {
Secp256k1("secp256k1"),
Ed25519("ed25519");
companion object {
fun byName(curve: String): EllipticCurve? = values().find { it.curve == curve }
}
}
enum class CardStatus(val code: Int) {
NotPersonalized(0),
Empty(1),
Loaded(2),
Purged(3);
companion object {
fun byCode(code: Int): CardStatus? = values().find { it.code == code }
}
}
enum class ProductMask(val code: Byte) {
Note(0x01),
Tag(0x02),
Card(0x04);
companion object {
fun byCode(code: Byte): ProductMask? = values().find { it.code == code }
}
}
class ReadCardResponse(
val cardId: String,
val manufacturerName: String,
val status: CardStatus,
val firmwareVersion: String?,
val cardPublicKey: String?,
val settingsMask: SettingsMask?,
val issuerPublicKey: String?,
val curve: EllipticCurve?,
val maxSignatures: Int?,
val signingMethpod: SigningMethod?,
val pauseBeforePin2: Int?,
val walletPublicKey: ByteArray?,
val walletRemainingSignatures: Int?,
val walletSignedHashes: Int?,
val health: Int?,
val isActivated: Boolean?,
val activationSeed: ByteArray?,
val paymentFlowVersion: ByteArray?,
val userCounter: Int?,
//Card Data
val batchId: Int?,
val manufactureDateTime: String?,
val issuerName: String?,
val blockchainName: String?,
val manufacturerSignature: ByteArray?,
val productMask: ProductMask?,
val tokenSymbol: String?,
val tokenContractAddress: String?,
val tokenDecimal: Int?
) : CommandResponse
class ReadCardCommand(private val pin1: String) : CommandSerializer<ReadCardResponse>() {
override val instruction = Instruction.Read
override val instructionCode = instruction.code
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
val tlvData = listOf(
Tlv(TlvTag.Pin, TlvTag.Pin.code, pin1.calculateSha256())
)
return CommandApdu(instructionCode, tlvData)
}
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): ReadCardResponse? {
val tlvData = responseApdu.getTlvData() ?: return null
return try {
val tlvMapper = TlvMapper(tlvData)
ReadCardResponse(
tlvMapper.map(TlvTag.CardId),
tlvMapper.map(TlvTag.ManufactureId),
tlvMapper.map(TlvTag.Status),
tlvMapper.mapOptional(TlvTag.Firmware),
tlvMapper.mapOptional(TlvTag.CardPublicKey),
tlvMapper.mapOptional(TlvTag.SettingsMask),
tlvMapper.mapOptional(TlvTag.IssuerDataPublicKey),
tlvMapper.mapOptional(TlvTag.CurveId),
tlvMapper.mapOptional(TlvTag.MaxSignatures),
tlvMapper.mapOptional(TlvTag.SigningMethod),
tlvMapper.mapOptional(TlvTag.PauseBeforePin2),
tlvMapper.mapOptional(TlvTag.WalletPublicKey),
tlvMapper.mapOptional(TlvTag.RemainingSignatures),
tlvMapper.mapOptional(TlvTag.SignedHashes),
tlvMapper.mapOptional(TlvTag.Health),
tlvMapper.mapOptional(TlvTag.IsActivated),
tlvMapper.mapOptional(TlvTag.ActivationSeed),
tlvMapper.mapOptional(TlvTag.PaymentFlowVersion),
tlvMapper.mapOptional(TlvTag.UserCounter),
tlvMapper.mapOptional(TlvTag.Batch),
tlvMapper.mapOptional(TlvTag.ManufactureDateTime),
tlvMapper.mapOptional(TlvTag.IssuerId),
tlvMapper.mapOptional(TlvTag.BlockchainId),
tlvMapper.mapOptional(TlvTag.ManufacturerSignature),
tlvMapper.mapOptional(TlvTag.ProductMask),
tlvMapper.mapOptional(TlvTag.TokenSymbol),
tlvMapper.mapOptional(TlvTag.TokenContractAddress),
tlvMapper.mapOptional(TlvTag.TokenDecimal)
)
} catch (exception: Exception) {
null
}
}
}

View file

@ -0,0 +1,51 @@
package com.tangem.commands
import com.tangem.CardEnvironment
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.ResponseApdu
import com.tangem.enums.Instruction
class SignCommand(val signCommandData: SignCommandData) : CommandSerializer<SignCommandResponse>() {
override val instruction = Instruction.Sign
override val instructionCode = instruction.code
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): SignCommandResponse? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class SignCommandResponse(
) : CommandResponse
data class SignCommandData(
val cid: String,
val pin1: String,
val pin2: String,
val cvc: String,
val transactionHash: TransactionHash,
val issuerData: IssuerData,
val terminalPublicKey: ByteArray? = null,
val terminalTransactionSignature: ByteArray? = null
)
data class TransactionHash(
val transactionHash: ByteArray,
val transactionRaw: ByteArray,
val hashName: String
)
data class IssuerData(
val issuerData: ByteArray,
val issuerDataCounter: Int,
val issuerTransactionSignature: ByteArray,
val issuerDataSignature: ByteArray
)

View file

@ -0,0 +1,90 @@
package com.tangem.tasks
import com.tangem.CardEnvironment
import com.tangem.CardManagerDelegate
import com.tangem.CardReader
import com.tangem.commands.*
import com.tangem.crypto.generateRandomBytes
import com.tangem.crypto.verify
sealed class ScanEvent {
data class OnReadEvent(val result: ReadCardResponse) : ScanEvent()
data class OnVerifyEvent(val verified: Boolean) : ScanEvent()
class OnUserCancelledEvent() : ScanEvent()
data class Failure(val error: TaskError) : ScanEvent()
data class Succcess(val result: Any) : ScanEvent()
}
//
//sealed class ScanResult {
// data class ReadResult(val card: Card) : ScanResult()
// data class VerifyResult(val verified: Boolean) : ScanResult()
// data class Failure(val error: TaskError) : ScanResult()
//}
class ScanTask<out: ScanEvent>(delegate: CardManagerDelegate? = null, reader: CardReader) : Task<ScanEvent>(delegate, reader) {
private lateinit var readCardData: ReadCardResponse
private lateinit var challenge: ByteArray
private lateinit var curve: EllipticCurve
private lateinit var walletPublickKey: ByteArray
override fun onRun(cardEnvironment: CardEnvironment,
callback: (result: ScanEvent, cardEnvironment: CardEnvironment) -> Unit) {
val readCommand = ReadCardCommand(cardEnvironment.pin1)
sendCommand(readCommand, cardEnvironment) {
if (it is CommandEvent.Success) {
readCardData = it.response as ReadCardResponse
callback(ScanEvent.OnReadEvent(readCardData), cardEnvironment)
if (readCardData.curve != null && readCardData.walletPublicKey != null) {
curve = readCardData.curve!!
walletPublickKey = readCardData.walletPublicKey!!
} else {
callback(ScanEvent.Failure(TaskError.CardError()), cardEnvironment)
}
val checkWalletCommand = prepareCheckWalletCommand(cardEnvironment)
sendCommand(checkWalletCommand, cardEnvironment) { checkWalletResult ->
delegate?.closeNfcPopup()
if (checkWalletResult is CommandEvent.Success) {
val checkWalletResponse = checkWalletResult.response as CheckWalletResponse
val verified = verify(walletPublickKey,
challenge + checkWalletResponse.salt,
checkWalletResponse.walletSignature,
curve)
if (verified) {
callback(ScanEvent.OnVerifyEvent(true), cardEnvironment)
} else {
callback(ScanEvent.Failure(TaskError.VefificationFailed()), cardEnvironment)
}
}
}
} else {
//TODO: Handle error
}
}
}
private fun updateEnvironment(): CardEnvironment {
//TODO: set logic of changing CardEnvironment when needed
return CardEnvironment()
}
private fun prepareCheckWalletCommand(cardEnvironment: CardEnvironment): CheckWalletCommand {
challenge = generateRandomBytes(16)
return CheckWalletCommand(
cardEnvironment.pin1,
readCardData.cardId,
challenge,
byteArrayOf())
}
}

View file

@ -0,0 +1,32 @@
package com.tangem.tasks
import com.tangem.CardEnvironment
import com.tangem.CardManagerDelegate
import com.tangem.CardReader
import com.tangem.commands.SignCommand
import com.tangem.commands.SignCommandData
class SignTask(private val signCommandData: SignCommandData,
delegate: CardManagerDelegate? = null,
reader: CardReader) : Task(delegate, reader) {
private lateinit var callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit
override fun onRun(cardEnvironment: CardEnvironment,
callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) {
this.callback = callback
val signCommand = SignCommand(signCommandData)
sendCommand(signCommand, cardEnvironment) {
delegate?.closeNfcPopup()
run { callback(it, updateEnvironment()) }
}
}
private fun updateEnvironment(): CardEnvironment {
//TODO: set logic of changing CardEnvironment when needed
return CardEnvironment()
}
}

View file

@ -0,0 +1,102 @@
package com.tangem.tasks
import com.tangem.CardEnvironment
import com.tangem.CardManagerDelegate
import com.tangem.CardReader
import com.tangem.commands.CommandEvent
import com.tangem.commands.CommandResponse
import com.tangem.commands.CommandSerializer
import com.tangem.enums.Status
abstract class Task<T>(protected val delegate: CardManagerDelegate? = null, private val reader: CardReader) {
fun run(cardEnvironment: CardEnvironment,
callback: (result: T, cardEnvironment: CardEnvironment) -> Unit) {
delegate?.openNfcPopup()
onRun(cardEnvironment, callback)
}
abstract fun onRun(cardEnvironment: CardEnvironment,
callback: (result: T, cardEnvironment: CardEnvironment) -> Unit)
protected fun <T: CommandResponse>sendCommand(commandSerializer: CommandSerializer<T>, cardEnvironment: CardEnvironment,
callback: (result: CommandEvent) -> Unit) {
reader.sendApdu(
commandSerializer.serialize(cardEnvironment).toBytes()) { responseApdu ->
when (responseApdu.status) {
Status.ProcessCompleted, Status.Pin1Changed, Status.Pin2Changed, Status.PinsChanged
-> {
val responseData = commandSerializer.deserialize(cardEnvironment, responseApdu)
if (responseData == null) {
} else {
callback(
CommandEvent.Success(responseData)
)
}
}
Status.InvalidParams -> TODO()
Status.ErrorProcessingCommand -> TODO()
Status.InvalidState -> TODO()
Status.InsNotSupported -> TODO()
Status.NeedEnctryption -> TODO()
Status.NeedPause -> TODO()
}
// val parsedApdu = responseApdu.getTlvData()
// if (parsedApdu == null) callback.invoke(TaskResult.Error(CardError()))
// else if (!parsedApdu.statusCompleted()) {
// callback.invoke(TaskResult.Error(CardError(parsedApdu.status.code)))
// } else {
// val response = commandSerializer.deserialize(parsedApdu)
// callback.invoke(response)
// }
}
}
}
sealed class TaskError : Exception() {
class UnknownStatus(sw: Int) : TaskError()
class MappingError : TaskError()
class ErrorProcessingCommand : TaskError()
class InvalidState : TaskError()
class InsNotSupported : TaskError()
class VefificationFailed : TaskError()
class CardError : TaskError()
class ReaderError() : TaskError()
}
sealed class TaskResult {
data class Success(val resultData: Any) : TaskResult()
data class CommandCompleted(val resultData: Any) : TaskResult()
data class Error(val error: TaskError) : TaskResult()
}
class BasicTask<T: CommandResponse>(
private val commandSerializer: CommandSerializer<CommandResponse>,
delegate: CardManagerDelegate? = null,
reader: CardReader) : Task(delegate, reader) {
override fun onRun(cardEnvironment: CardEnvironment,
callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) {
sendCommand(commandSerializer, cardEnvironment) {
callback.invoke(it, updateEnvironment())
}
}
private fun updateEnvironment(): CardEnvironment {
//TODO: set logic of changing CardEnvironment when needed
return CardEnvironment()
}
}