From bbdcad141066a00ce89a01615ce8d2b2a45f61a4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Nov 2019 14:27:46 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../src/main/java/com/tangem/CardManager.kt | 29 +++---- .../java/com/tangem/CardManagerDelegate.kt | 9 +- .../src/main/java/com/tangem/CardReader.kt | 2 +- .../src/main/java/com/tangem/DataStorage.kt | 10 --- .../src/main/java/com/tangem/Response.kt | 16 ---- .../com/tangem/commands/CheckWalletCommand.kt | 13 ++- .../java/com/tangem/commands/SignCommand.kt | 17 ++-- .../com/tangem/common/apdu/CommandApdu.kt | 18 +++- .../java/com/tangem/common/apdu/StatusWord.kt | 4 +- .../main/java/com/tangem/common/tlv/Tlv.kt | 6 +- .../main/java/com/tangem/common/tlv/TlvTag.kt | 14 +++ .../com/tangem/common/tlv/TlvValueType.kt | 15 ---- .../java/com/tangem/crypto/CryptoUtils.kt | 41 +++++---- .../main/java/com/tangem/data/SettingsMask.kt | 32 ------- .../main/java/com/tangem/tasks/ScanTask.kt | 87 ++++++++----------- .../com/tangem/tasks/SingleCommandTask.kt | 17 ++-- .../src/main/java/com/tangem/tasks/Task.kt | 80 ++++++++++++----- .../main/java/com/tangem/tasks/TaskError.kt | 30 ------- 18 files changed, 188 insertions(+), 252 deletions(-) delete mode 100644 tangem-core/src/main/java/com/tangem/DataStorage.kt delete mode 100644 tangem-core/src/main/java/com/tangem/Response.kt delete mode 100644 tangem-core/src/main/java/com/tangem/common/tlv/TlvValueType.kt delete mode 100644 tangem-core/src/main/java/com/tangem/data/SettingsMask.kt delete mode 100644 tangem-core/src/main/java/com/tangem/tasks/TaskError.kt diff --git a/tangem-core/src/main/java/com/tangem/CardManager.kt b/tangem-core/src/main/java/com/tangem/CardManager.kt index 349289bb83..d0efa5a194 100644 --- a/tangem-core/src/main/java/com/tangem/CardManager.kt +++ b/tangem-core/src/main/java/com/tangem/CardManager.kt @@ -4,7 +4,7 @@ import com.tangem.commands.CommandResponse import com.tangem.commands.CommandSerializer import com.tangem.commands.SignCommand import com.tangem.commands.SignResponse -import com.tangem.crypto.initCrypto +import com.tangem.crypto.CryptoUtils import com.tangem.tasks.* import java.util.concurrent.Executors @@ -17,7 +17,7 @@ class CardManager( private val cardManagerExecutor = Executors.newSingleThreadExecutor() init { - initCrypto() + CryptoUtils.initCrypto() } fun scanCard(callback: (result: TaskEvent) -> Unit) { @@ -56,26 +56,21 @@ class CardManager( task.delegate = cardManagerDelegate cardManagerExecutor.execute { - task.run(environment) { - when (it) { - is TaskEvent.Event -> callback(it) - is TaskEvent.Completion -> { - isBusy = false - callback(it) - } - } + task.run(environment) { taskEvent -> + if (taskEvent is TaskEvent.Completion) isBusy = false + callback(taskEvent) } } } + fun runCommand(command: CommandSerializer, + cardId: String? = null, + callback: (result: TaskEvent) -> Unit) { + val task = SingleCommandTask(command) + runTask(task, cardId, callback) + } + private fun fetchCardEnvironment(cardId: String?): CardEnvironment { return cardEnvironmentRepository[cardId] ?: CardEnvironment() } - - fun runCommand(commandSerializer: CommandSerializer, - cardId: String? = null, - callback: (result: TaskEvent) -> Unit) { - val task = SingleCommandTask(commandSerializer) - runTask(task, cardId, callback) - } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt b/tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt index 82051b143d..94d62f79dc 100644 --- a/tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt +++ b/tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt @@ -5,10 +5,11 @@ import com.tangem.tasks.TaskError interface CardManagerDelegate { - fun onTaskStarted() - fun showSecurityDelay(ms: Int) - fun onTaskCompleted() - fun onTaskError(error: TaskError? = null) + fun onNfcSessionStarted() + fun onSecurityDelay(ms: Int) + fun hideSecurityDelay() + fun onNfcSessionCompleted() + fun onError(error: TaskError? = null) fun requestPin(callback: (result: CompletionResult) -> Unit) diff --git a/tangem-core/src/main/java/com/tangem/CardReader.kt b/tangem-core/src/main/java/com/tangem/CardReader.kt index 15f9fa9eea..4b4d00769f 100644 --- a/tangem-core/src/main/java/com/tangem/CardReader.kt +++ b/tangem-core/src/main/java/com/tangem/CardReader.kt @@ -7,6 +7,6 @@ import com.tangem.common.apdu.ResponseApdu interface CardReader { var readingActive: Boolean fun transceiveApdu(apdu: CommandApdu, callback: (response: CompletionResult) -> Unit) - fun setStartSession() + fun startNfcSession() fun closeSession() } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/DataStorage.kt b/tangem-core/src/main/java/com/tangem/DataStorage.kt deleted file mode 100644 index b1c531c671..0000000000 --- a/tangem-core/src/main/java/com/tangem/DataStorage.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tangem - -interface DataStorage { - - fun getTerminalPublicKey(): ByteArray? - fun getTerminalPrivateKey(): ByteArray? - fun getPin1(): String? - fun getPin2(): String? - -} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/Response.kt b/tangem-core/src/main/java/com/tangem/Response.kt deleted file mode 100644 index 33b18cee40..0000000000 --- a/tangem-core/src/main/java/com/tangem/Response.kt +++ /dev/null @@ -1,16 +0,0 @@ -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 -//) -// - - diff --git a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt index 7a189d8c7a..b581bc36ce 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt @@ -19,20 +19,19 @@ class CheckWalletResponse( class CheckWalletCommand( - val pin1: String, val cid: String, - val challenge: ByteArray, val publicKeyChallenge: ByteArray) : CommandSerializer() { - - override val instruction = Instruction.CheckWallet - override val instructionCode = instruction.code + private val pin1: String, + private val cardId: String, + private val challenge: ByteArray +) : CommandSerializer() { override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { val tlvData = listOf( Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256()), - Tlv(TlvTag.CardId, cid.hexToBytes()), + Tlv(TlvTag.CardId, cardId.hexToBytes()), Tlv(TlvTag.Challenge, challenge) ) - return CommandApdu(instructionCode, tlvData) + return CommandApdu(Instruction.CheckWallet, tlvData) } override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): CheckWalletResponse? { diff --git a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt index 736c68507e..df7a61c2ff 100644 --- a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt @@ -23,9 +23,6 @@ class SignResponse( class SignCommand(private val hashes: Array, private val cardId: String) : CommandSerializer() { - override val instruction = Instruction.Sign - override val instructionCode = instruction.code - private val hashSizes = if (hashes.isNotEmpty()) hashes.first().size else 0 private val dataToSign = flattenHashes() @@ -36,7 +33,7 @@ class SignCommand(private val hashes: Array, private val cardId: Stri private fun checkForErrors() { if (hashes.isEmpty()) throw TaskError.EmptyHashes() - if (hashes.size > 10) throw TaskError.TooMuchHashes() + if (hashes.size > 10) throw TaskError.TooMuchHashes() if (hashes.any { it.size != hashSizes }) throw TaskError.HashSizeMustBeEqual() } @@ -45,20 +42,20 @@ class SignCommand(private val hashes: Array, private val cardId: Stri Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256()), Tlv(TlvTag.Pin2, cardEnvironment.pin2.calculateSha256()), Tlv(TlvTag.CardId, cardId.hexToBytes()), - Tlv(TlvTag.TransactionOutHashSize,byteArrayOf(hashSizes.toByte())), + Tlv(TlvTag.TransactionOutHashSize, byteArrayOf(hashSizes.toByte())), Tlv(TlvTag.TransactionOutHash, dataToSign) ) addTerminalSignature(cardEnvironment, tlvData) - return CommandApdu(instructionCode, tlvData) + return CommandApdu(Instruction.Sign, tlvData) } private fun addTerminalSignature(cardEnvironment: CardEnvironment, tlvData: MutableList) { - cardEnvironment.terminalKeys?.let { - val signedData = dataToSign.sign(it.privateKey) + cardEnvironment.terminalKeys?.let { terminalKeyPair -> + val signedData = dataToSign.sign(terminalKeyPair.privateKey) tlvData.add(Tlv(TlvTag.TerminalTransactionSignature, signedData)) - tlvData.add(Tlv(TlvTag.TerminalPublicKey, it.publicKey)) + tlvData.add(Tlv(TlvTag.TerminalPublicKey, terminalKeyPair.publicKey)) } } @@ -67,7 +64,7 @@ class SignCommand(private val hashes: Array, private val cardId: Stri val tlvMapper = TlvMapper(tlvData) return SignResponse( - cardId= tlvMapper.map(TlvTag.CardId), + cardId = tlvMapper.map(TlvTag.CardId), signature = tlvMapper.map(TlvTag.Signature), remainingSignatures = tlvMapper.map(TlvTag.RemainingSignatures), signedHashes = tlvMapper.map(TlvTag.SignedHashes) diff --git a/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt b/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt index 1fee89160a..86a706bd3e 100644 --- a/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt +++ b/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt @@ -7,7 +7,7 @@ import java.io.ByteArrayOutputStream class CommandApdu( - private val instruction: Int, + private val ins: Int, private val tlvList: List, private val cla: Byte = ISO_CLA, @@ -19,6 +19,18 @@ class CommandApdu( private val encryptionMode: EncryptionMode = EncryptionMode.NONE, private val encryptionKey: ByteArray? = null) { + constructor( + instruction: Instruction, + tlvList: List, + encryptionMode: EncryptionMode = EncryptionMode.NONE, + encryptionKey: ByteArray? = null + ) : this( + instruction.code, + tlvList, + encryptionMode = encryptionMode, + encryptionKey = encryptionKey + ) + val apduData: ByteArray init { @@ -39,7 +51,7 @@ class CommandApdu( val byteStream = ByteArrayOutputStream() byteStream.write(cla.toInt()) - byteStream.write(instruction) + byteStream.write(ins) byteStream.write(p1.toInt()) byteStream.write(p2.toInt()) if (lc != 0) { @@ -58,7 +70,7 @@ class CommandApdu( private fun encrypt() { - + TODO("not implemented") } companion object { diff --git a/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt b/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt index 4cbc0d2a81..c346fe1e71 100644 --- a/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt +++ b/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt @@ -6,13 +6,13 @@ enum class StatusWord (val code: Int, val description: String){ InvalidParams(0x6A86, "SW_INVALID_PARAMS"), ErrorProcessingCommand(0x6286, "SW_ERROR_PROCESSING_COMMAND"), InvalidState(0x6985, "SW_INVALID_STATE"), - // PinsNotChanged(ProcessCompleted.code, ProcessCompleted.description), Pin1Changed(ProcessCompleted.code + 0x0001, "SW_PIN1_CHANGED"), Pin2Changed(ProcessCompleted.code + 0x0002, "SW_PIN2_CHANGED"), PinsChanged(ProcessCompleted.code + 0x0003, "SW_PINS_CHANGED"), InsNotSupported(0x6D00, "SW_INS_NOT_SUPPORTED"), NeedEncryption(0x6982, "SW_NEED_ENCRYPTION"), - NeedPause(0x9789, "SW_NEED_PAUSE"); + NeedPause(0x9789, "SW_NEED_PAUSE"), + Unknown(0x0000, "SW_UNKNOWN"); companion object { private val values = values() diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt b/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt index a22dad60c1..b1b2c3b5fa 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt @@ -7,17 +7,17 @@ class Tlv { val tag: TlvTag val value: ByteArray - val tagCode: Int + val tagRaw: Int constructor(tagCode: Int, value: ByteArray = byteArrayOf()) { this.tag = TlvTag.byCode(tagCode) - this.tagCode = tagCode + this.tagRaw = tagCode this.value = value } constructor(tag: TlvTag, value: ByteArray = byteArrayOf()) { this.tag = tag - this.tagCode = tag.code + this.tagRaw = tag.code this.value = value } diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt index 71eb6d009f..aa0e0036f8 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt @@ -1,5 +1,19 @@ package com.tangem.common.tlv +enum class TlvValueType { + HexString, + Utf8String, + IntValue, + BoolValue, + ByteArray, + EllipticCurve, + DateTime, + ProductMask, + SettingsMask, + CardStatus, + SigningMethod +} + enum class TlvTag(val code: Int) { Unknown(0x00), CardId(0x01), diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvValueType.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvValueType.kt deleted file mode 100644 index c71a19fa4a..0000000000 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvValueType.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tangem.common.tlv - -enum class TlvValueType { - HexString, - Utf8String, - IntValue, - BoolValue, - ByteArray, - EllipticCurve, - DateTime, - ProductMask, - SettingsMask, - CardStatus, - SigningMethod -} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt b/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt index 14ca802dc2..1dc0d2bc82 100644 --- a/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt +++ b/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt @@ -5,35 +5,34 @@ import net.i2p.crypto.eddsa.EdDSASecurityProvider import java.security.SecureRandom import java.security.Security -fun generateRandomBytes(length: Int): ByteArray { - val bytes = ByteArray(length) - SecureRandom().nextBytes(bytes) - return bytes -} -fun verify(publicKey: ByteArray, message: ByteArray, signature: ByteArray, - curve: EllipticCurve = EllipticCurve.Secp256k1): Boolean { - return when (curve) { - EllipticCurve.Secp256k1 -> verifySecp256k1(publicKey, message, signature) - EllipticCurve.Ed25519 -> verifyEd25519(publicKey, message, signature) +object CryptoUtils { + + fun initCrypto() { + Security.insertProviderAt(org.spongycastle.jce.provider.BouncyCastleProvider(), 1) + Security.addProvider(EdDSASecurityProvider()) + } + + fun generateRandomBytes(length: Int): ByteArray { + val bytes = ByteArray(length) + SecureRandom().nextBytes(bytes) + return bytes + } + + fun verify(publicKey: ByteArray, message: ByteArray, signature: ByteArray, + curve: EllipticCurve = EllipticCurve.Secp256k1): Boolean { + return when (curve) { + EllipticCurve.Secp256k1 -> verifySecp256k1(publicKey, message, signature) + EllipticCurve.Ed25519 -> verifyEd25519(publicKey, message, signature) + } } } - -fun initCrypto() { - Security.insertProviderAt(org.spongycastle.jce.provider.BouncyCastleProvider(), 1) - Security.addProvider(EdDSASecurityProvider()) -} - - fun ByteArray.sign(privateKeyArray: ByteArray, curve: EllipticCurve = EllipticCurve.Secp256k1): ByteArray { - return when (curve) { - EllipticCurve.Secp256k1 -> signSecp256k1(this,privateKeyArray) + EllipticCurve.Secp256k1 -> signSecp256k1(this, privateKeyArray) EllipticCurve.Ed25519 -> signEd25519(this, privateKeyArray) } - - } diff --git a/tangem-core/src/main/java/com/tangem/data/SettingsMask.kt b/tangem-core/src/main/java/com/tangem/data/SettingsMask.kt deleted file mode 100644 index 696b947202..0000000000 --- a/tangem-core/src/main/java/com/tangem/data/SettingsMask.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.data - -data class SettingsMask(val rawValue: Int) { - - companion object{ - const val IsReusable = 0x0001 - const val UseActivation = 0x0002 - const val ForbidPurgeWallet = 0x0004 - const val UseBlock = 0x0008 - - const val AllowSwapPIN = 0x0010 - const val AllowSwapPIN2 = 0x0020 - const val UseCVC = 0x0040 - const val ForbidDefaultPIN = 0x0080 - - const val UseOneCommandAtTime = 0x0100 - const val UseNDEF = 0x0200 - const val UseDynamicNDEF = 0x0400 - const val SmartSecurityDelay = 0x0800 - - const val Protocol_AllowUnencrypted = 0x1000 - const val Protocol_AllowStaticEncryption = 0x2000 - - const val ProtectIssuerDataAgainstReplay = 0x4000 - - const val AllowSelectBlockchain = 0x8000 - - const val DisablePrecomputedNDEF = 0x00010000 - - const val SkipSecurityDelayIfValidatedByLinkedTerminal = 0x00080000 - } -} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt index 371e5686e7..4bd9194dc9 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt @@ -3,11 +3,9 @@ package com.tangem.tasks import com.tangem.CardEnvironment import com.tangem.commands.Card import com.tangem.commands.CheckWalletCommand -import com.tangem.commands.EllipticCurve -import com.tangem.commands.ReadCardCommand +import com.tangem.commands.ReadCommand import com.tangem.common.CompletionResult -import com.tangem.crypto.generateRandomBytes -import com.tangem.crypto.verify +import com.tangem.crypto.CryptoUtils sealed class ScanEvent { data class OnReadEvent(val card: Card) : ScanEvent() @@ -17,76 +15,67 @@ sealed class ScanEvent { internal class ScanTask : Task() { - private lateinit var cardData: Card - private lateinit var challenge: ByteArray - private lateinit var curve: EllipticCurve - private lateinit var walletPublickKey: ByteArray - override fun onRun(cardEnvironment: CardEnvironment, callback: (result: TaskEvent) -> Unit) { - val readCommand = ReadCardCommand() - sendCommand(readCommand, cardEnvironment) { readEvent -> + val readCommand = ReadCommand() + sendCommand(readCommand, cardEnvironment) { readResult -> + + when (readResult) { + + is CompletionResult.Failure -> { + if (readResult.error !is TaskError.UserCancelledError) { + completeNfcSession(true, readResult.error) + } + callback(TaskEvent.Completion(readResult.error)) + } - when (readEvent) { is CompletionResult.Success -> { - cardData = readEvent.data + val card = readResult.data - callback(TaskEvent.Event(ScanEvent.OnReadEvent(cardData))) + callback(TaskEvent.Event(ScanEvent.OnReadEvent(card))) - if (cardData.curve != null && cardData.walletPublicKey != null) { - curve = cardData.curve!! - walletPublickKey = cardData.walletPublicKey!! - } else { - onTaskCompleted(true) + if (card.curve == null || card.walletPublicKey == null) { + completeNfcSession(true) callback(TaskEvent.Completion(TaskError.CardError())) + return@sendCommand } - val checkWalletCommand = prepareCheckWalletCommand(cardEnvironment) + val challenge = CryptoUtils.generateRandomBytes(16) + val checkWalletCommand = CheckWalletCommand( + cardEnvironment.pin1, + card.cardId, + challenge) + + sendCommand(checkWalletCommand, cardEnvironment) { result -> + when (result) { + is CompletionResult.Failure -> { + if (result.error !is TaskError.UserCancelledError) { + completeNfcSession(true, result.error) + } + callback(TaskEvent.Completion(result.error)) + } - sendCommand(checkWalletCommand, cardEnvironment) { checkWalletEvent -> - when (checkWalletEvent) { is CompletionResult.Success -> { - val checkWalletResponse = checkWalletEvent.data - val verified = verify(walletPublickKey, + completeNfcSession() + val checkWalletResponse = result.data + val verified = CryptoUtils.verify( + card.walletPublicKey, challenge + checkWalletResponse.salt, checkWalletResponse.walletSignature, - curve) + card.curve) if (verified) { - onTaskCompleted() - callback(TaskEvent.Completion()) callback(TaskEvent.Event(ScanEvent.OnVerifyEvent(true))) + callback(TaskEvent.Completion()) } else { - onTaskCompleted(true) callback(TaskEvent.Completion(TaskError.VefificationFailed())) } } - is CompletionResult.Failure -> { - if (checkWalletEvent.error !is TaskError.UserCancelledError) { - onTaskCompleted(true, checkWalletEvent.error) - } - callback(TaskEvent.Completion(checkWalletEvent.error)) - } } } } - is CompletionResult.Failure -> { - if (readEvent.error !is TaskError.UserCancelledError) { - onTaskCompleted(true, readEvent.error) - } - callback(TaskEvent.Completion(readEvent.error)) - } } } } - - private fun prepareCheckWalletCommand(cardEnvironment: CardEnvironment): CheckWalletCommand { - challenge = generateRandomBytes(16) - return CheckWalletCommand( - cardEnvironment.pin1, - cardData.cardId, - challenge, - byteArrayOf()) - } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt b/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt index 5728ed9305..96176afdc6 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt @@ -6,22 +6,23 @@ import com.tangem.commands.CommandSerializer import com.tangem.common.CompletionResult class SingleCommandTask( - private val commandSerializer: CommandSerializer) : Task() { + private val command: CommandSerializer +) : Task() { override fun onRun(cardEnvironment: CardEnvironment, callback: (result: TaskEvent) -> Unit) { - sendCommand(commandSerializer, cardEnvironment) { completionResult -> - when (completionResult) { + sendCommand(command, cardEnvironment) { result -> + when (result) { is CompletionResult.Success -> { - onTaskCompleted() - callback(TaskEvent.Event(completionResult.data)) + completeNfcSession() + callback(TaskEvent.Event(result.data)) callback(TaskEvent.Completion()) } is CompletionResult.Failure -> { - if (completionResult.error !is TaskError.UserCancelledError) { - onTaskCompleted(true, completionResult.error) + if (result.error !is TaskError.UserCancelledError) { + completeNfcSession(true, result.error) } - callback(TaskEvent.Completion(completionResult.error)) + callback(TaskEvent.Completion(result.error)) } } } diff --git a/tangem-core/src/main/java/com/tangem/tasks/Task.kt b/tangem-core/src/main/java/com/tangem/tasks/Task.kt index cfcfddc25e..b866f6ad47 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/Task.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/Task.kt @@ -7,8 +7,40 @@ import com.tangem.Log import com.tangem.commands.CommandResponse import com.tangem.commands.CommandSerializer import com.tangem.common.CompletionResult +import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.StatusWord +sealed class TaskError(description: String? = null) : Exception(description) { + class UnknownStatus(sw: Int) : TaskError("Unknown StatusWord: $sw") + class MappingError : TaskError() + class GenericError(description: String? = null) : TaskError(description) + class UserCancelledError() : TaskError() + class Busy() : TaskError() + class TagLost() : TaskError() + + class ErrorProcessingCommand : TaskError() + class InvalidState : TaskError() + class InsNotSupported : TaskError() + class InvalidParams : TaskError() + class NeedEncryption : TaskError() + class NeedPause : TaskError() + + class VefificationFailed : TaskError() + class CardError : TaskError() + class ReaderError() : TaskError() + class SerializeCommandError() : TaskError() + + class CardIsMissing() : TaskError() + class EmptyHashes() : TaskError() + class TooMuchHashes() : TaskError() + class HashSizeMustBeEqual() : TaskError() +} + +sealed class TaskEvent { + class Event(val data: T) : TaskEvent() + class Completion(val error: TaskError? = null) : TaskEvent() +} + abstract class Task { var delegate: CardManagerDelegate? = null @@ -16,18 +48,18 @@ abstract class Task { fun run(cardEnvironment: CardEnvironment, callback: (result: TaskEvent) -> Unit) { - delegate?.onTaskStarted() - reader?.setStartSession() + delegate?.onNfcSessionStarted() + reader?.startNfcSession() Log.i(this::class.simpleName!!, "Nfc task is started") onRun(cardEnvironment, callback) } - protected fun onTaskCompleted(withError: Boolean = false, taskError: TaskError? = null) { + protected fun completeNfcSession(withError: Boolean = false, taskError: TaskError? = null) { reader?.closeSession() if (withError) { - delegate?.onTaskError(taskError) + delegate?.onError(taskError) } else { - delegate?.onTaskCompleted() + delegate?.onNfcSessionCompleted() } } @@ -35,15 +67,22 @@ abstract class Task { callback: (result: TaskEvent) -> Unit) protected fun sendCommand( - commandSerializer: CommandSerializer, + command: CommandSerializer, cardEnvironment: CardEnvironment, callback: (result: CompletionResult) -> Unit) { - Log.i(this::class.simpleName!!, "Nfc command ${commandSerializer::class.simpleName!!} is initiated") + Log.i(this::class.simpleName!!, "Nfc command ${command::class.simpleName!!} is initiated") + val commandApdu = command.serialize(cardEnvironment) + sendRequest(command, commandApdu, cardEnvironment, callback) + } - reader?.transceiveApdu( - commandSerializer.serialize(cardEnvironment)) { result -> + private fun sendRequest(command: CommandSerializer, + commandApdu: CommandApdu, + cardEnvironment: CardEnvironment, + callback: (result: CompletionResult) -> Unit) { + + reader?.transceiveApdu(commandApdu) { result -> when (result) { is CompletionResult.Success -> { @@ -52,24 +91,26 @@ abstract class Task { StatusWord.ProcessCompleted, StatusWord.Pin1Changed, StatusWord.Pin2Changed, StatusWord.PinsChanged -> { try { - val responseData = commandSerializer.deserialize(cardEnvironment, responseApdu) - Log.i(this::class.simpleName!!, "Nfc command ${commandSerializer::class.simpleName!!} is completed") + val responseData = command.deserialize(cardEnvironment, responseApdu) + Log.i(this::class.simpleName!!, "Nfc command ${command::class.simpleName!!} is completed") callback(CompletionResult.Success(responseData as T)) } catch (error: TaskError) { callback(CompletionResult.Failure(error)) } } StatusWord.InvalidParams -> callback(CompletionResult.Failure(TaskError.InvalidParams())) + StatusWord.Unknown -> callback(CompletionResult.Failure(TaskError.UnknownStatus(result.data.sw))) StatusWord.ErrorProcessingCommand -> callback(CompletionResult.Failure(TaskError.ErrorProcessingCommand())) StatusWord.InvalidState -> callback(CompletionResult.Failure(TaskError.InvalidState())) StatusWord.InsNotSupported -> callback(CompletionResult.Failure(TaskError.InsNotSupported())) StatusWord.NeedEncryption -> callback(CompletionResult.Failure(TaskError.NeedEncryption())) StatusWord.NeedPause -> { - val remainingTime = commandSerializer.deserializeSecurityDelay(responseApdu, cardEnvironment) - if (remainingTime != null) delegate?.showSecurityDelay(remainingTime) - Log.i(this::class.simpleName!!, "Nfc command ${commandSerializer::class.simpleName!!} triggered security delay of $remainingTime milliseconds") - sendCommand(commandSerializer, cardEnvironment, callback) + // When NeedPause is returned from the card whenever security delay is triggered. + val remainingTime = command.deserializeSecurityDelay(responseApdu, cardEnvironment) + if (remainingTime != null) delegate?.onSecurityDelay(remainingTime) + Log.i(this::class.simpleName!!, "Nfc command ${command::class.simpleName!!} triggered security delay of $remainingTime milliseconds") + sendRequest(command, commandApdu, cardEnvironment, callback) } } } @@ -80,18 +121,9 @@ abstract class Task { callback(CompletionResult.Failure(TaskError.UserCancelledError())) reader?.readingActive = false } - } } } } -/** - * Returns in callback from tasks - */ -sealed class TaskEvent { - class Event(val data: T) : TaskEvent() - class Completion(val error: TaskError? = null) : TaskEvent() -} - diff --git a/tangem-core/src/main/java/com/tangem/tasks/TaskError.kt b/tangem-core/src/main/java/com/tangem/tasks/TaskError.kt deleted file mode 100644 index 7287c281cb..0000000000 --- a/tangem-core/src/main/java/com/tangem/tasks/TaskError.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.tangem.tasks - -/** - * An error class that covers typical errors that may occur when performing Tangem SDK tasks. - * Errors are got propagated back within callbacks. - */ -sealed class TaskError(description: String? = null) : Exception(description) { - class UnknownStatus(sw: Int) : TaskError() - class MappingError : TaskError() - class GenericError(description: String? = null) : TaskError(description) - class UserCancelledError() : TaskError() - class Busy() : TaskError() - - class ErrorProcessingCommand : TaskError() - class InvalidState : TaskError() - class InsNotSupported : TaskError() - class InvalidParams : TaskError() - class NeedEncryption : TaskError() - class NeedPause : TaskError() - - class VefificationFailed : TaskError() - class CardError : TaskError() - class ReaderError() : TaskError() - class SerializeCommandError() : TaskError() - - class CardIsMissing() : TaskError() - class EmptyHashes() : TaskError() - class TooMuchHashes() : TaskError() - class HashSizeMustBeEqual() : TaskError() -} \ No newline at end of file