diff --git a/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt b/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt index 809eb2e976..038c0ea66c 100644 --- a/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt +++ b/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt @@ -4,8 +4,8 @@ import android.os.Bundle import android.text.Editable import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import com.tangem.SessionError import com.tangem.TangemSdk +import com.tangem.TangemSdkError import com.tangem.blockchain.common.* import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.Signer @@ -65,7 +65,7 @@ class BlockchainDemoActivity : AppCompatActivity() { getInfo() } is CompletionResult.Failure -> { - if (result.error !is SessionError.UserCancelled) { + if (result.error !is TangemSdkError.UserCancelled) { handleError(result.error.toString()) } } diff --git a/tangem-core/src/main/java/com/tangem/CardSession.kt b/tangem-core/src/main/java/com/tangem/CardSession.kt index e2cbafb179..34f243fc4f 100644 --- a/tangem-core/src/main/java/com/tangem/CardSession.kt +++ b/tangem-core/src/main/java/com/tangem/CardSession.kt @@ -37,7 +37,7 @@ interface CardSessionRunnable { * @property viewDelegate is an interface that allows interaction with users and shows relevant UI. * @property cardId ID, Unique Tangem card ID number. If not null, the SDK will check that you the card * with which you tapped a phone has this [cardId] and SDK will return - * the [SessionError.WrongCard] otherwise. + * the [TangemSdkError.WrongCard] otherwise. * @property initialMessage A custom description that will be shown at the beginning of the NFC session. * If null, a default header and text body will be used. */ @@ -86,12 +86,12 @@ class CardSession( /** * Starts a card session and performs preflight [ReadCommand]. - * @param callback: callback with the card session. Can contain [SessionError] if something goes wrong. + * @param callback: callback with the card session. Can contain [TangemSdkError] if something goes wrong. */ - fun start(callback: (session: CardSession, error: SessionError?) -> Unit) { + fun start(callback: (session: CardSession, error: TangemSdkError?) -> Unit) { try { startSession() - } catch (error: SessionError) { + } catch (error: TangemSdkError) { callback(this, error) } @@ -109,7 +109,7 @@ class CardSession( } private fun startSession() { - if (isBusy) throw SessionError.Busy() + if (isBusy) throw TangemSdkError.Busy() isBusy = true viewDelegate.onNfcSessionStarted(cardId, initialMessage) reader.openSession() @@ -133,14 +133,14 @@ class CardSession( is CompletionResult.Success -> { val receivedCardId = result.data.cardId if (cardId != null && receivedCardId != cardId) { - stopWithError(SessionError.WrongCard()) - callback(CompletionResult.Failure(SessionError.WrongCard())) + stopWithError(TangemSdkError.WrongCard()) + callback(CompletionResult.Failure(TangemSdkError.WrongCard())) return@run } val allowedCardTypes = environment.cardFilter.allowedCardTypes if (!allowedCardTypes.contains(result.data.getType())) { - stopWithError(SessionError.WrongCardType()) - callback(CompletionResult.Failure(SessionError.WrongCardType())) + stopWithError(TangemSdkError.WrongCardType()) + callback(CompletionResult.Failure(TangemSdkError.WrongCardType())) return@run } environment.card = result.data @@ -169,12 +169,12 @@ class CardSession( reader.closeSession() isBusy = false - val errorMessage = if (error is SessionError) { + val errorMessage = if (error is TangemSdkError) { "${error::class.simpleName}: ${error.code}" } else { error.localizedMessage } - if (error !is SessionError.UserCancelled) { + if (error !is TangemSdkError.UserCancelled) { Log.e("tag", "Finishing with error: $errorMessage") viewDelegate.onError(errorMessage) } @@ -185,10 +185,10 @@ class CardSession( } private fun tryHandleError( - error: SessionError, callback: (result: CompletionResult) -> Unit) { + error: TangemSdkError, callback: (result: CompletionResult) -> Unit) { when (error) { - is SessionError.NeedEncryption -> { + is TangemSdkError.NeedEncryption -> { Log.i(tag, "Establishing encryption") when (environment.encryptionMode) { EncryptionMode.NONE -> { @@ -201,12 +201,12 @@ class CardSession( } EncryptionMode.STRONG -> { Log.e(tag, "Encryption doesn't work") - callback(CompletionResult.Failure(SessionError.NeedEncryption())) + callback(CompletionResult.Failure(TangemSdkError.NeedEncryption())) } } return establishEncryption(callback) } - else -> callback(CompletionResult.Failure(SessionError.UnknownError())) + else -> callback(CompletionResult.Failure(TangemSdkError.UnknownError())) } } diff --git a/tangem-core/src/main/java/com/tangem/Config.kt b/tangem-core/src/main/java/com/tangem/Config.kt index 20c5c849bd..45da4b6912 100644 --- a/tangem-core/src/main/java/com/tangem/Config.kt +++ b/tangem-core/src/main/java/com/tangem/Config.kt @@ -34,5 +34,8 @@ class Config( /** * Filter that can be used to limit cards that can be interacted with in TangemSdk. */ - val cardFilter: CardFilter = CardFilter() + val cardFilter: CardFilter = CardFilter(), + + var handleErrors: Boolean = true + ) \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt b/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt index c1c9a296b2..e51d5e5d97 100644 --- a/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt +++ b/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt @@ -21,7 +21,8 @@ data class SessionEnvironment( var encryptionMode: EncryptionMode = EncryptionMode.NONE, var encryptionKey: ByteArray? = null, val cvc: ByteArray? = null, - var cardFilter: CardFilter = CardFilter() + var cardFilter: CardFilter = CardFilter(), + val handleErrors: Boolean = true ) { fun setPin1(pin1: String) { diff --git a/tangem-core/src/main/java/com/tangem/TangemSdk.kt b/tangem-core/src/main/java/com/tangem/TangemSdk.kt index a5f90dc0ca..8fc1fb4e2e 100644 --- a/tangem-core/src/main/java/com/tangem/TangemSdk.kt +++ b/tangem-core/src/main/java/com/tangem/TangemSdk.kt @@ -46,7 +46,7 @@ class TangemSdk( * it proves that the wallet owns a private key that corresponds to a public one. * * @param callback is triggered on the completion of the [ScanTask] and provides card response - * in the form of [Card] if the task was performed successfully or [SessionError] in case of an error. + * in the form of [Card] if the task was performed successfully or [TangemSdkError] in case of an error. */ fun scanCard(initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { startSessionWithRunnable(ScanTask(), null, initialMessage, callback) @@ -68,7 +68,7 @@ class TangemSdk( * @param cardId CID, Unique Tangem card ID number * @param callback is triggered on the completion of the [SignCommand] and provides card response * in the form of [SignResponse] if the task was performed successfully - * or [SessionError] in case of an error. + * or [TangemSdkError] in case of an error. */ fun sign(hashes: Array, cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { @@ -86,7 +86,7 @@ class TangemSdk( * @param cardId CID, Unique Tangem card ID number. * @param callback is triggered on the completion of the [ReadIssuerDataCommand] and provides * card response in the form of [ReadIssuerDataResponse] if the task was performed successfully - * or [SessionError] in case of an error. + * or [TangemSdkError] in case of an error. */ fun readIssuerData(cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { @@ -105,7 +105,7 @@ class TangemSdk( * @param cardId CID, Unique Tangem card ID number. * @param callback is triggered on the completion of the [ReadIssuerExtraDataCommand] and provides * card response in the form of [ReadIssuerExtraDataResponse] if the task was performed successfully - * or [SessionError] in case of an error. + * or [TangemSdkError] in case of an error. */ fun readIssuerExtraData(cardId: String, callback: (result: CompletionResult) -> Unit) { @@ -126,7 +126,7 @@ class TangemSdk( * @param issuerDataCounter An optional counter that protect issuer data against replay attack. * @param callback is triggered on the completion of the [WriteIssuerDataCommand] and provides * card response in the form of [WriteIssuerDataResponse] if the task was performed successfully - * or [SessionError] in case of an error. + * or [TangemSdkError] in case of an error. */ fun writeIssuerData(cardId: String, issuerData: ByteArray, @@ -164,7 +164,7 @@ class TangemSdk( * @param issuerDataCounter An optional counter that protect issuer data against replay attack. * @param callback is triggered on the completion of the [WriteIssuerExtraDataCommand] and provides * card response in the form of [WriteIssuerDataResponse] if the task was performed successfully - * or [SessionError] in case of an error. + * or [TangemSdkError] in case of an error. */ fun writeIssuerExtraData(cardId: String, issuerData: ByteArray, @@ -224,7 +224,7 @@ class TangemSdk( * @param cardId CID, Unique Tangem card ID number. * @param callback is triggered on the completion of the [ReadUserDataCommand] and provides * card response in the form of [ReadUserDataResponse] if the task was performed successfully - * or [SessionError] in case of an error. + * or [TangemSdkError] in case of an error. */ fun readUserData(cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { @@ -246,7 +246,7 @@ class TangemSdk( * @param cardId CID, Unique Tangem card ID number. * @param callback is triggered on the completion of the [CreateWalletTask] and provides * card response in the form of [CreateWalletResponse] if the task was performed successfully - * or [SessionError] in case of an error. + * or [TangemSdkError] in case of an error. */ fun createWallet(cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { @@ -265,7 +265,7 @@ class TangemSdk( * @param cardId CID, Unique Tangem card ID number. * @param callback is triggered on the completion of the [PurgeWalletCommand] and provides * card response in the form of [PurgeWalletResponse] if the task was performed successfully - * or [SessionError] in case of an error. + * or [TangemSdkError] in case of an error. */ fun purgeWallet(cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { @@ -283,7 +283,7 @@ class TangemSdk( * @param cardId CID, Unique Tangem card ID number. * @param callback is triggered on the completion of the [DepersonalizeCommand] and provides * card response in the form of [DepersonalizeResponse] if the task was performed successfully - * or [SessionError] in case of an error. + * or [TangemSdkError] in case of an error. * */ fun depersonalize(cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { @@ -306,7 +306,7 @@ class TangemSdk( * (non-EMV) POS terminal infrastructure and transaction processing back-end. * @param callback is triggered on the completion of the [PersonalizeCommand] and provides * card response in the form of [Card] if the command was performed successfully - * or [SessionError] in case of an error. + * or [TangemSdkError] in case of an error. */ fun personalize(config: CardConfig, issuer: Issuer, manufacturer: Manufacturer, acquirer: Acquirer? = null, @@ -325,7 +325,7 @@ class TangemSdk( * @runnable: A custom task, adopting [CardSessionRunnable] protocol * @cardId: CID, Unique Tangem card ID number. If not null, the SDK will check that you the card * with which you tapped a phone has this [cardId] and SDK will return - * the [SessionError.WrongCard] otherwise. + * the [TangemSdkError.WrongCard] otherwise. * @initialMessage: A custom description that shows at the beginning of the NFC session. * If null, default message will be used. * @callback: Standard [TangemSdk] callback. @@ -343,14 +343,14 @@ class TangemSdk( * @cardId: CID, Unique Tangem card ID number. If not null, the SDK will check that you the card * with which you tapped a phone has this [cardId] and SDK will return - * the [SessionError.WrongCard] otherwise. + * the [TangemSdkError.WrongCard] otherwise. * @initialMessage: A custom description that shows at the beginning of the NFC session. * If null, default message will be used. - * @callback: At first, you should check that the [SessionError] is not null, + * @callback: At first, you should check that the [TangemSdkError] is not null, * then you can use the [CardSession] to interact with a card. */ fun startSession(cardId: String? = null, initialMessage: Message? = null, - callback: (session: CardSession, error: SessionError?) -> Unit) { + callback: (session: CardSession, error: TangemSdkError?) -> Unit) { val cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage) Thread().run { cardSession.start(callback) } } @@ -367,7 +367,8 @@ class TangemSdk( val terminalKeys = if (config.linkedTerminal) terminalKeysService?.getKeys() else null return SessionEnvironment( terminalKeys = terminalKeys, - cardFilter = config.cardFilter + cardFilter = config.cardFilter, + handleErrors = config.handleErrors ) } diff --git a/tangem-core/src/main/java/com/tangem/SessionError.kt b/tangem-core/src/main/java/com/tangem/TangemSdkError.kt similarity index 55% rename from tangem-core/src/main/java/com/tangem/SessionError.kt rename to tangem-core/src/main/java/com/tangem/TangemSdkError.kt index b5f702ceb2..4ea2612c51 100644 --- a/tangem-core/src/main/java/com/tangem/SessionError.kt +++ b/tangem-core/src/main/java/com/tangem/TangemSdkError.kt @@ -9,52 +9,42 @@ import com.tangem.tasks.ScanTask * An error class that represent typical errors that may occur when performing Tangem SDK tasks. * Errors are propagated back to the caller in callbacks. */ -sealed class SessionError(val code: Int) : Exception() { +sealed class TangemSdkError(val code: Int) : Exception(code.toString()) { - //Errors in serializing APDU /** - * This error is returned when there [CommandSerializer] cannot deserialize [com.tangem.common.tlv.Tlv] - * (this error is a wrapper around internal [com.tangem.common.tlv.TlvDecoder] errors). + * This error is returned when Android NFC reader loses a tag + * (e.g. a user detaches card from the phone's NFC module) while the NFC session is in progress. */ - class SerializeCommandError : SessionError(1000) + class TagLost : TangemSdkError(10001) - class DeserializeApduFailed : SessionError(1001) - class EncodingFailedTypeMismatch : SessionError(1002) - class EncodingFailed : SessionError(1003) - class DecodingFailedMissingTag : SessionError(1004) - class DecodingFailedTypeMismatch : SessionError(1005) - class DecodingFailed : SessionError(1005) + class SerializeCommandError : TangemSdkError(20001) + class DeserializeApduFailed : TangemSdkError(20002) + class EncodingFailedTypeMismatch : TangemSdkError(20003) + class EncodingFailed : TangemSdkError(20004) + class DecodingFailedMissingTag : TangemSdkError(20005) + class DecodingFailedTypeMismatch : TangemSdkError(20006) + class DecodingFailed : TangemSdkError(20007) /** * This error is returned when unknown [StatusWord] is received from a card. */ - class UnknownStatus : SessionError(2001) - + class UnknownStatus : TangemSdkError(30001) /** * This error is returned when a card's reply is [StatusWord.ErrorProcessingCommand]. * The card sends this status in case of internal card error. */ - class ErrorProcessingCommand : SessionError(2002) - - /** - * This error is returned when a task (such as [ScanTask]) requires that [ReadCommand] - * is executed before performing other commands. - */ - class MissingPreflightRead : SessionError(2003) - + class ErrorProcessingCommand : TangemSdkError(30002) /** * This error is returned when a card's reply is [StatusWord.InvalidState]. * The card sends this status when command can not be executed in the current state of a card. */ - class InvalidState : SessionError(2004) - + class InvalidState : TangemSdkError(30003) /** * This error is returned when a card's reply is [StatusWord.InsNotSupported]. * The card sends this status when the card cannot process the [com.tangem.common.apdu.Instruction]. */ - class InsNotSupported : SessionError(2005) - + class InsNotSupported : TangemSdkError(30004) /** * This error is returned when a card's reply is [StatusWord.InvalidParams]. * The card sends this status when there are wrong or not sufficient parameters in TLV request, @@ -62,85 +52,105 @@ sealed class SessionError(val code: Int) : Exception() { * The error may be caused, for example, by wrong parameters of the [Task], [CommandSerializer], * mapping or serialization errors. */ - class InvalidParams : SessionError(2006) - + class InvalidParams : TangemSdkError(30005) /** * This error is returned when a card's reply is [StatusWord.NeedEncryption] * and the encryption was not established by TangemSdk. */ - class NeedEncryption : SessionError(2007) + class NeedEncryption : TangemSdkError(30006) - //Scan errors - /** - * This error is returned when a [Task] checks unsuccessfully either - * a card's ability to sign with its private key, or the validity of issuer data. - */ - class VerificationFailed : SessionError(3000) + //Personalization Errors + class AlreadyPersonalized : TangemSdkError(40101) - /** - * This error is returned when a [ScanTask] returns a [Card] without some of the essential fields. - */ - class CardError : SessionError(3001) + //Depersonalization Errors + class CannotBeDepersonalized : TangemSdkError(40201) + //Read Errors + class Pin1Required : TangemSdkError(40401) /** * This error is returned when a [Task] expects a user to use a particular card, - * and a user tries to use a different card. + * but the user tries to use a different card. */ - class WrongCard : SessionError(3002) - + class WrongCard : TangemSdkError(40403) /** - * Tangem cards can sign currently up to 10 hashes during one [com.tangem.commands.SignCommand]. - * This error is returned when a [com.tangem.commands.SignCommand] receives more than 10 hashes to sign. + * This error is returned when a user scans a card of a [com.tangem.common.extensions.CardType] + * that is not specified in [Config.cardFilter]. */ - class TooMuchHashesInOneTransaction : SessionError(3003) + class WrongCardType : TangemSdkError(40404) + //CreateWallet Errors + class AlreadyCreated : TangemSdkError(40501) + + //PurgeWallet Errors + class PurgeWalletProhibited : TangemSdkError(40601) + + //SetPin Errors + class Pin1CannotBeChanged : TangemSdkError(40801) + class Pin2CannotBeChanged : TangemSdkError(40802) + class Pin1CannotBeDefault : TangemSdkError(40803) + + //Sign Errors + class NoRemainingSignatures : TangemSdkError(40901) /** * This error is returned when a [com.tangem.commands.SignCommand] * receives only empty hashes for signature. */ - class EmptyHashes : SessionError(3004) - + class EmptyHashes : TangemSdkError(40902) /** * This error is returned when a [com.tangem.commands.SignCommand] * receives hashes of different lengths for signature. */ - class HashSizeMustBeEqual : SessionError(3005) - - + class HashSizeMustBeEqual : TangemSdkError(40903) + class CardIsEmpty : TangemSdkError(40904) + class SignHashesNotAvailable : TangemSdkError(40905) /** - * This error is returned when a user scans a card of a [com.tangem.common.extensions.CardType] - * that is not specified in [Config.allowedCards]. + * Tangem cards can sign currently up to 10 hashes during one [com.tangem.commands.SignCommand]. + * This error is returned when a [com.tangem.commands.SignCommand] receives more than 10 hashes to sign. */ - class WrongCardType : SessionError(3006) + class TooManyhHashesInOneTransaction : TangemSdkError(40906) + //General Errors + class NotPersonalized() : TangemSdkError(40001) + class NotActivated : TangemSdkError(40002) + class CardIsPurged : TangemSdkError(40003) + class Pin2OrCvcRequired : TangemSdkError(40004) /** - * This error is returned when [com.tangem.TangemSdk] was called with a new [Task], - * while a previous [Task] is still in progress. + * This error is returned when a [Task] checks unsuccessfully either + * a card's ability to sign with its private key, or the validity of issuer data. */ - class Busy : SessionError(4000) - - /** - * This error is returned when a user manually closes NFC Reading Bottom Sheet Dialog. - */ - class UserCancelled : SessionError(4001) - - //NFC errors - class NfcReaderError : SessionError(5002) - - /** - * This error is returned when Android NFC reader loses a tag - * (e.g. a user detaches card from the phone's NFC module) while the NFC session is in progress. - */ - class TagLost : SessionError(5003) - - class UnknownError : SessionError(6000) - - //Specific Command Errors + class VerificationFailed : TangemSdkError(40005) + class DataSizeTooLarge : TangemSdkError(40006) /** * This error is returned when [ReadIssuerDataTask] or [ReadIssuerExtraDataTask] expects a counter * (when the card's requires it), but the counter is missing. */ - class MissingCounter : SessionError(7001) + class MissingCounter : TangemSdkError(40007) + class OverwritingDataIsProhibited : TangemSdkError(40008) + class DataCannotBeWritten : TangemSdkError(40009) + class MissingIssuerPubicKey : TangemSdkError(40010) + /** + * This error is returned when a [ScanTask] returns a [Card] without some of the essential fields. + */ + class CardError : TangemSdkError(40011) + + + //SDK Errors + class UnknownError: TangemSdkError(50001) + /** + * This error is returned when a user manually closes NFC Reading Bottom Sheet Dialog. + */ + class UserCancelled: TangemSdkError(50002) + /** + * This error is returned when [com.tangem.TangemSdk] was called with a new [Task], + * while a previous [Task] is still in progress. + */ + class Busy : TangemSdkError(50003) + /** + * This error is returned when a task (such as [ScanTask]) requires that [ReadCommand] + * is executed before performing other commands. + */ + class MissingPreflightRead : TangemSdkError(50004) + + +} - class MissingIssuerPubicKey : SessionError(7002) -} \ No newline at end of file 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 4d9c3a53ff..e3cbdf970a 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt @@ -2,7 +2,7 @@ package com.tangem.commands import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction @@ -49,7 +49,7 @@ class CheckWalletCommand( private val challenge = CryptoUtils.generateRandomBytes(16) override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - transceive(session) { result -> + super.run(session) { result -> when (result) { is CompletionResult.Failure -> { callback(CompletionResult.Failure(result.error)) @@ -63,13 +63,28 @@ class CheckWalletCommand( if (verified) { callback(CompletionResult.Success(result.data)) } else { - callback(CompletionResult.Failure(SessionError.VerificationFailed())) + callback(CompletionResult.Failure(TangemSdkError.VerificationFailed())) } } } } } + override fun performPreCheck( + session: CardSession, + callback: (result: CompletionResult) -> Unit + ): Boolean { + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) + return true + } + if (session.environment.card?.isActivated == true) { + callback(CompletionResult.Failure(TangemSdkError.NotActivated())) + return true + } + return false + } + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) @@ -83,7 +98,7 @@ class CheckWalletCommand( override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): CheckWalletResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() val decoder = TlvDecoder(tlvData) return CheckWalletResponse( diff --git a/tangem-core/src/main/java/com/tangem/commands/Command.kt b/tangem-core/src/main/java/com/tangem/commands/Command.kt index 225fa97430..fad1b0d025 100644 --- a/tangem-core/src/main/java/com/tangem/commands/Command.kt +++ b/tangem-core/src/main/java/com/tangem/commands/Command.kt @@ -5,7 +5,7 @@ import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.ResponseApdu import com.tangem.common.apdu.StatusWord -import com.tangem.common.apdu.toSessionError +import com.tangem.common.apdu.toTangemSdkError import com.tangem.common.extensions.toInt import com.tangem.common.tlv.TlvTag @@ -39,7 +39,26 @@ abstract class Command : CardSessionRunnable { override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { Log.i("Command", "Sending ${this::class.java.simpleName}") - transceive(session, callback) + if (session.environment.handleErrors) { + if (performPreCheck(session, callback)) return + } + transceive(session) { result -> + if (session.environment.handleErrors) { + if (performAfterCheck(session, result, callback)) return@transceive + } + callback(result) + } + } + + open fun performPreCheck(session: CardSession, + callback: (result: CompletionResult) -> Unit): Boolean { + return false + } + + open fun performAfterCheck(session: CardSession, + result: CompletionResult, + callback: (result: CompletionResult) -> Unit): Boolean { + return false } fun transceive(session: CardSession, callback: (result: CompletionResult) -> Unit) { @@ -54,7 +73,7 @@ abstract class Command : CardSessionRunnable { } } } - } catch (error: SessionError) { + } catch (error: TangemSdkError) { callback(CompletionResult.Failure(error)) } } @@ -81,17 +100,17 @@ abstract class Command : CardSessionRunnable { transceiveApdu(apdu, session, callback) } else -> { - val error = responseApdu.statusWord.toSessionError() + val error = responseApdu.statusWord.toTangemSdkError() if (error != null && !tryHandleError(error)) { callback(CompletionResult.Failure(error)) } else { - callback(CompletionResult.Failure(SessionError.UnknownError())) + callback(CompletionResult.Failure(TangemSdkError.UnknownError())) } } } } is CompletionResult.Failure -> - if (result.error is SessionError.TagLost) { + if (result.error is TangemSdkError.TagLost) { session.viewDelegate.onTagLost() } else { callback(CompletionResult.Failure(result.error)) @@ -110,7 +129,7 @@ abstract class Command : CardSessionRunnable { return tlv?.find { it.tag == TlvTag.Pause }?.value?.toInt() } - private fun tryHandleError(error: SessionError): Boolean { + private fun tryHandleError(error: TangemSdkError): Boolean { return false } diff --git a/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt index 089a134702..e0bf433c99 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt @@ -1,7 +1,9 @@ package com.tangem.commands +import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -37,6 +39,41 @@ class CreateWalletResponse( */ class CreateWalletCommand : Command() { + override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) + return true + } + if (session.environment.card?.isActivated == true) { + callback(CompletionResult.Failure(TangemSdkError.NotActivated())) + return true + } + if (session.environment.card?.status == CardStatus.Purged) { + callback(CompletionResult.Failure(TangemSdkError.CardIsPurged())) + return true + } + if (session.environment.card?.status == CardStatus.Loaded) { + callback(CompletionResult.Failure(TangemSdkError.AlreadyCreated())) + return true + } + return false + } + + override fun performAfterCheck(session: CardSession, + result: CompletionResult, + callback: (result: CompletionResult) -> Unit): Boolean { + when (result) { + is CompletionResult.Failure -> { + if (result.error is TangemSdkError.InvalidParams) { + callback(CompletionResult.Failure(TangemSdkError.Pin2OrCvcRequired())) + return true + } + return false + } + else -> return false + } + } + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) @@ -51,7 +88,7 @@ class CreateWalletCommand : Command() { override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): CreateWalletResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() val decoder = TlvDecoder(tlvData) return CreateWalletResponse( diff --git a/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt b/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt index 7484f9d48b..891e28f3c1 100644 --- a/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt @@ -1,7 +1,7 @@ package com.tangem.commands import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -32,7 +32,7 @@ class OpenSessionCommand(private val sessionKeyA: ByteArray) : Command() { + override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) + return true + } + if (session.environment.card?.isActivated == true) { + callback(CompletionResult.Failure(TangemSdkError.NotActivated())) + return true + } + if (session.environment.card?.settingsMask?.contains(Settings.ProhibitPurgeWallet) == true) { + callback(CompletionResult.Failure(TangemSdkError.PurgeWalletProhibited())) + return true + } + return false + } + + override fun performAfterCheck(session: CardSession, + result: CompletionResult, + callback: (result: CompletionResult) -> Unit): Boolean { + when (result) { + is CompletionResult.Failure -> { + if (result.error is TangemSdkError.InvalidParams) { + callback(CompletionResult.Failure(TangemSdkError.Pin2OrCvcRequired())) + return true + } + return false + } + else -> return false + } + } + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) @@ -42,7 +75,7 @@ class PurgeWalletCommand : Command() { override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): PurgeWalletResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() val decoder = TlvDecoder(tlvData) return PurgeWalletResponse( diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt index e3b5d4ccee..c8bbda44ff 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt @@ -1,7 +1,9 @@ package com.tangem.commands +import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -132,7 +134,7 @@ data class SettingsMask(val rawValue: Int) { enum class Settings(val code: Int) { IsReusable(0x0001), UseActivation(0x0002), - ForbidPurgeWallet(0x0004), + ProhibitPurgeWallet(0x0004), UseBlock(0x0008), AllowSwapPIN(0x0010), @@ -366,6 +368,19 @@ class Card( */ class ReadCommand : Command() { + override fun performAfterCheck(session: CardSession, result: CompletionResult, callback: (result: CompletionResult) -> Unit): Boolean { + when (result) { + is CompletionResult.Failure -> { + if (result.error is TangemSdkError.InvalidParams) { + callback(CompletionResult.Failure(TangemSdkError.Pin1Required())) + return true + } + return false + } + else -> return false + } + } + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() /** @@ -384,7 +399,7 @@ class ReadCommand : Command() { override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): Card { val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() val decoder = TlvDecoder(tlvData) diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt index 7055bc403f..d01a3adbe8 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt @@ -2,7 +2,7 @@ package com.tangem.commands import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode import com.tangem.commands.common.IssuerDataToVerify @@ -61,12 +61,12 @@ class ReadIssuerDataCommand( override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val card = session.environment.card if (card == null) { - callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) + callback(CompletionResult.Failure(TangemSdkError.MissingPreflightRead())) return } val publicKey = issuerPublicKey ?: card.issuerPublicKey if (publicKey == null) { - callback(CompletionResult.Failure(SessionError.MissingIssuerPubicKey())) + callback(CompletionResult.Failure(TangemSdkError.MissingIssuerPubicKey())) return } super.run(session) { result -> @@ -83,13 +83,21 @@ class ReadIssuerDataCommand( if (verify(publicKey, result.data.issuerDataSignature, issuerDataToVerify)) { callback(result) } else { - callback(CompletionResult.Failure(SessionError.VerificationFailed())) + callback(CompletionResult.Failure(TangemSdkError.VerificationFailed())) } } } } } + override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) + return true + } + return false + } + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) @@ -103,7 +111,7 @@ class ReadIssuerDataCommand( override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): ReadIssuerDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() val decoder = TlvDecoder(tlvData) return ReadIssuerDataResponse( diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt index db748d8ec5..82e148a05d 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt @@ -2,7 +2,7 @@ package com.tangem.commands import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode import com.tangem.commands.common.IssuerDataToVerify @@ -71,12 +71,16 @@ class ReadIssuerExtraDataCommand( override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val card = session.environment.card if (card == null) { - callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) + callback(CompletionResult.Failure(TangemSdkError.MissingPreflightRead())) return } val publicKey = issuerPublicKey ?: card.issuerPublicKey if (publicKey == null) { - callback(CompletionResult.Failure(SessionError.MissingIssuerPubicKey())) + callback(CompletionResult.Failure(TangemSdkError.MissingIssuerPubicKey())) + return + } + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) return } @@ -138,7 +142,7 @@ class ReadIssuerExtraDataCommand( ) callback(CompletionResult.Success(finalResult)) } else { - callback(CompletionResult.Failure(SessionError.VerificationFailed())) + callback(CompletionResult.Failure(TangemSdkError.VerificationFailed())) } } @@ -156,7 +160,7 @@ class ReadIssuerExtraDataCommand( override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): ReadIssuerExtraDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() val decoder = TlvDecoder(tlvData) diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt index 528ba98fb0..b8ee2022d6 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt @@ -1,7 +1,9 @@ package com.tangem.commands +import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -10,32 +12,32 @@ import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag class ReadUserDataResponse( - /** - * CID, Unique Tangem card ID number. - */ - val cardId: String, + /** + * CID, Unique Tangem card ID number. + */ + val cardId: String, - /** - * Data defined by user's App. - */ - val userData: ByteArray, + /** + * Data defined by user's App. + */ + val userData: ByteArray, - /** - * Data defined by user's App (confirmed by PIN2). - */ - val userProtectedData: ByteArray, + /** + * Data defined by user's App (confirmed by PIN2). + */ + val userProtectedData: ByteArray, - /** - * Counter initialized by user's App and increased on every signing of new transaction - */ - val userCounter: Int, + /** + * Counter initialized by user's App and increased on every signing of new transaction + */ + val userCounter: Int, - /** - * Counter initialized by user's App (confirmed by PIN2) and increased on every signing of new transaction - */ - val userProtectedCounter: Int + /** + * Counter initialized by user's App (confirmed by PIN2) and increased on every signing of new transaction + */ + val userProtectedCounter: Int -): CommandResponse +) : CommandResponse /** * This command returns two up to 512-byte User_Data, User_Protected_Data and two counters User_Counter and @@ -47,30 +49,42 @@ class ReadUserDataResponse( * of new transaction (on SIGN command that calculate new signatures). The App defines purpose of use. * For example, this fields may contain blockchain nonce value. */ -class ReadUserDataCommand: Command() { +class ReadUserDataCommand : Command() { - override fun serialize(environment: SessionEnvironment): CommandApdu { - val builder = TlvBuilder() - builder.append(TlvTag.CardId, environment.card?.cardId) - builder.append(TlvTag.Pin, environment.pin1) + override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) + return true + } + if (session.environment.card?.isActivated == true) { + callback(CompletionResult.Failure(TangemSdkError.NotActivated())) + return true + } + return false + } - return CommandApdu( - Instruction.ReadUserData, builder.serialize(), - environment.encryptionMode, environment.encryptionKey - ) - } + override fun serialize(environment: SessionEnvironment): CommandApdu { + val builder = TlvBuilder() + builder.append(TlvTag.CardId, environment.card?.cardId) + builder.append(TlvTag.Pin, environment.pin1) - override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): ReadUserDataResponse { - val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + return CommandApdu( + Instruction.ReadUserData, builder.serialize(), + environment.encryptionMode, environment.encryptionKey + ) + } - val decoder = TlvDecoder(tlvData) - return ReadUserDataResponse( - cardId = decoder.decode(TlvTag.CardId), - userData = decoder.decode(TlvTag.UserData), - userProtectedData = decoder.decode(TlvTag.UserProtectedData), - userCounter = decoder.decode(TlvTag.UserCounter), - userProtectedCounter = decoder.decode(TlvTag.UserProtectedCounter) - ) - } + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): ReadUserDataResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw TangemSdkError.DeserializeApduFailed() + + val decoder = TlvDecoder(tlvData) + return ReadUserDataResponse( + cardId = decoder.decode(TlvTag.CardId), + userData = decoder.decode(TlvTag.UserData), + userProtectedData = decoder.decode(TlvTag.UserProtectedData), + userCounter = decoder.decode(TlvTag.UserCounter), + userProtectedCounter = decoder.decode(TlvTag.UserProtectedCounter) + ) + } } \ No newline at end of file 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 7707f82197..607ad41cd7 100644 --- a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt @@ -1,7 +1,9 @@ package com.tangem.commands +import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -35,6 +37,57 @@ class SignCommand(private val hashes: Array) private val hashSizes = if (hashes.isNotEmpty()) hashes.first().size else 0 + override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) + return true + } + if (session.environment.card?.isActivated == true) { + callback(CompletionResult.Failure(TangemSdkError.NotActivated())) + return true + } + if (session.environment.card?.status == CardStatus.Purged) { + callback(CompletionResult.Failure(TangemSdkError.CardIsPurged())) + return true + } + if (session.environment.card?.status == CardStatus.Empty) { + callback(CompletionResult.Failure(TangemSdkError.CardIsEmpty())) + return true + } + if (session.environment.card?.walletRemainingSignatures == 0) { + callback(CompletionResult.Failure(TangemSdkError.NoRemainingSignatures())) + return true + } + if (session.environment.card?.signingMethods?.contains(SigningMethod.SignHash) != true) { + callback(CompletionResult.Failure(TangemSdkError.SignHashesNotAvailable())) + return true + } + if (hashSizes == 0) { + callback(CompletionResult.Failure(TangemSdkError.EmptyHashes())) + return true + } + if (hashes.any { it.size != hashSizes }) { + callback(CompletionResult.Failure(TangemSdkError.HashSizeMustBeEqual())) + return true + } + return false + } + + override fun performAfterCheck(session: CardSession, + result: CompletionResult, + callback: (result: CompletionResult) -> Unit): Boolean { + when (result) { + is CompletionResult.Failure -> { + if (result.error is TangemSdkError.InvalidParams) { + callback(CompletionResult.Failure(TangemSdkError.Pin2OrCvcRequired())) + return true + } + return false + } + else -> return false + } + } + override fun serialize(environment: SessionEnvironment): CommandApdu { val dataToSign = flattenHashes() val tlvBuilder = TlvBuilder() @@ -58,9 +111,9 @@ class SignCommand(private val hashes: Array) } private fun checkForErrors() { - if (hashes.isEmpty()) throw SessionError.EmptyHashes() - if (hashes.size > 10) throw SessionError.TooMuchHashesInOneTransaction() - if (hashes.any { it.size != hashSizes }) throw SessionError.HashSizeMustBeEqual() + if (hashes.isEmpty()) throw TangemSdkError.EmptyHashes() + if (hashes.size > 10) throw TangemSdkError.TooManyhHashesInOneTransaction() + if (hashes.any { it.size != hashSizes }) throw TangemSdkError.HashSizeMustBeEqual() } /** @@ -81,7 +134,7 @@ class SignCommand(private val hashes: Array) override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): SignResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() val decoder = TlvDecoder(tlvData) return SignResponse( diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt index a425f5a740..2574a2f4a6 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt @@ -2,7 +2,7 @@ package com.tangem.commands import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode import com.tangem.commands.common.IssuerDataToVerify @@ -40,33 +40,62 @@ class WriteIssuerDataCommand( verifier: IssuerDataVerifier = DefaultIssuerDataVerifier() ) : Command(), IssuerDataVerifier by verifier { - override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - + override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { val card = session.environment.card if (card == null) { - callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) - return + callback(CompletionResult.Failure(TangemSdkError.MissingPreflightRead())) + return true } val publicKey = issuerPublicKey ?: card.issuerPublicKey if (publicKey == null) { - callback(CompletionResult.Failure(SessionError.MissingIssuerPubicKey())) - return + callback(CompletionResult.Failure(TangemSdkError.MissingIssuerPubicKey())) + return true + } + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) + return true + } + if (session.environment.card?.isActivated == true) { + callback(CompletionResult.Failure(TangemSdkError.NotActivated())) + return true + } + if (issuerData.size > MAX_SIZE) { + callback(CompletionResult.Failure(TangemSdkError.DataSizeTooLarge())) + return true } - if (!isCounterValid(issuerDataCounter, card)) { - callback(CompletionResult.Failure(SessionError.MissingCounter())) - } else if (!verifySignature(publicKey, card.cardId)) { - callback(CompletionResult.Failure(SessionError.VerificationFailed())) - } else { - super.run(session, callback) + callback(CompletionResult.Failure(TangemSdkError.MissingCounter())) + return true + } + if (!verifySignature(publicKey, card.cardId)) { + callback(CompletionResult.Failure(TangemSdkError.VerificationFailed())) + return true + } + return false + } + + override fun performAfterCheck(session: CardSession, + result: CompletionResult, + callback: (result: CompletionResult) -> Unit + ): Boolean { + when (result) { + is CompletionResult.Failure -> { + if (result.error is TangemSdkError.InvalidParams && + isCounterRequired(session.environment.card)) { + callback(CompletionResult.Failure(TangemSdkError.DataCannotBeWritten())) + return true + } + return false + } + else -> return false } } private fun isCounterValid(issuerDataCounter: Int?, card: Card): Boolean = if (isCounterRequired(card)) issuerDataCounter != null else true - private fun isCounterRequired(card: Card): Boolean = - card.settingsMask?.contains(Settings.ProtectIssuerDataAgainstReplay) != false + private fun isCounterRequired(card: Card?): Boolean = + card?.settingsMask?.contains(Settings.ProtectIssuerDataAgainstReplay) != false private fun verifySignature(publicKey: ByteArray, cardId: String): Boolean { return verify( @@ -93,11 +122,15 @@ class WriteIssuerDataCommand( override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): WriteIssuerDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() val decoder = TlvDecoder(tlvData) return WriteIssuerDataResponse( cardId = decoder.decode(TlvTag.CardId) ) } + + companion object { + const val MAX_SIZE = 512 + } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt index 4a93ebd734..8cb0dfc949 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt @@ -2,7 +2,7 @@ package com.tangem.commands import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode import com.tangem.commands.common.IssuerDataToVerify @@ -46,24 +46,68 @@ class WriteIssuerExtraDataCommand( override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val card = session.environment.card if (card == null) { - callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) + callback(CompletionResult.Failure(TangemSdkError.MissingPreflightRead())) return } val publicKey = issuerPublicKey ?: card.issuerPublicKey if (publicKey == null) { - callback(CompletionResult.Failure(SessionError.MissingIssuerPubicKey())) + callback(CompletionResult.Failure(TangemSdkError.MissingIssuerPubicKey())) return } - if (!isCounterValid(issuerDataCounter, card)) { - callback(CompletionResult.Failure(SessionError.MissingCounter())) - } else if (!verifySignatures(card.cardId, publicKey)) { - callback(CompletionResult.Failure(SessionError.VerificationFailed())) - } else { - writeIssuerData(session, card.cardId, publicKey, callback) + writeIssuerData(session, card.cardId, publicKey) { response -> + when (response) { + is CompletionResult.Success -> callback(response) + is CompletionResult.Failure -> { + if (response.error is TangemSdkError.InvalidParams && isCounterRequired(card)) { + callback(CompletionResult.Failure(TangemSdkError.DataCannotBeWritten())) + return@writeIssuerData + } + if (response.error is TangemSdkError.InvalidState && + card.settingsMask?.contains(Settings.ProtectIssuerDataAgainstReplay) != false) { + callback(CompletionResult.Failure(TangemSdkError.OverwritingDataIsProhibited())) + return@writeIssuerData + } + } + } } } + override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { + val card = session.environment.card + if (card == null) { + callback(CompletionResult.Failure(TangemSdkError.MissingPreflightRead())) + return true + } + val publicKey = issuerPublicKey ?: card.issuerPublicKey + if (publicKey == null) { + callback(CompletionResult.Failure(TangemSdkError.MissingIssuerPubicKey())) + return true + } + + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) + return true + } + if (session.environment.card?.isActivated == true) { + callback(CompletionResult.Failure(TangemSdkError.NotActivated())) + return true + } + if (issuerData.size > MAX_SIZE) { + callback(CompletionResult.Failure(TangemSdkError.DataSizeTooLarge())) + return true + } + if (!isCounterValid(issuerDataCounter, card)) { + callback(CompletionResult.Failure(TangemSdkError.MissingCounter())) + return true + } + if (!verifySignatures(card.cardId, publicKey)) { + callback(CompletionResult.Failure(TangemSdkError.VerificationFailed())) + return true + } + return false + } + private fun isCounterValid(issuerDataCounter: Int?, card: Card): Boolean = if (isCounterRequired(card)) issuerDataCounter != null else true @@ -156,7 +200,7 @@ class WriteIssuerExtraDataCommand( override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): WriteIssuerDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() return WriteIssuerDataResponse(cardId = TlvDecoder(tlvData).decode(TlvTag.CardId) ) @@ -164,5 +208,6 @@ class WriteIssuerExtraDataCommand( companion object { const val SINGLE_WRITE_SIZE = 1524 + const val MAX_SIZE = 32 * 1024 } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt index 7b43e879e7..fc0d0467ae 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt @@ -1,7 +1,9 @@ package com.tangem.commands +import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -32,6 +34,38 @@ class WriteUserDataCommand(private val userData: ByteArray? = null, private val private val userCounter: Int? = null, private val userProtectedCounter: Int? = null) : Command() { + override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) + return true + } + if (session.environment.card?.isActivated == true) { + callback(CompletionResult.Failure(TangemSdkError.NotActivated())) + return true + } + if (userData?.size ?: 0 > MAX_SIZE || userProtectedData?.size ?: 0 > MAX_SIZE) { + callback(CompletionResult.Failure(TangemSdkError.DataSizeTooLarge())) + return true + } + return false + } + + override fun performAfterCheck(session: CardSession, + result: CompletionResult, + callback: (result: CompletionResult) -> Unit + ): Boolean { + when (result) { + is CompletionResult.Failure -> { + if (result.error is TangemSdkError.InvalidParams) { + callback(CompletionResult.Failure(TangemSdkError.Pin2OrCvcRequired())) + return true + } + return false + } + else -> return false + } + } + override fun serialize(environment: SessionEnvironment): CommandApdu { val builder = TlvBuilder() builder.append(TlvTag.CardId, environment.card?.cardId) @@ -51,7 +85,11 @@ class WriteUserDataCommand(private val userData: ByteArray? = null, private val override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): WriteUserDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() return WriteUserDataResponse(TlvDecoder(tlvData).decode(TlvTag.CardId)) } + + companion object{ + const val MAX_SIZE = 512 + } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt index 2ed9366331..c3bb2d4de8 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt @@ -1,8 +1,12 @@ package com.tangem.commands.personalization +import com.tangem.CardSession import com.tangem.SessionEnvironment +import com.tangem.TangemSdkError +import com.tangem.commands.CardStatus import com.tangem.commands.Command import com.tangem.commands.CommandResponse +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -17,6 +21,18 @@ data class DepersonalizeResponse(val success: Boolean) : CommandResponse */ class DepersonalizeCommand : Command() { + override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { + if (session.environment.card?.status == CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) + return true + } + if (session.environment.card?.firmwareVersion?.contains("SDK") == false) { + callback(CompletionResult.Failure(TangemSdkError.CannotBeDepersonalized())) + return true + } + return false + } + override fun serialize(environment: SessionEnvironment): CommandApdu { return CommandApdu( Instruction.Depersonalize, byteArrayOf() diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt index e4fe1a8b15..52d85278fa 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt @@ -1,11 +1,14 @@ package com.tangem.commands.personalization +import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.commands.Card import com.tangem.commands.CardData +import com.tangem.commands.CardStatus import com.tangem.commands.Command import com.tangem.commands.personalization.entities.* +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -36,6 +39,14 @@ class PersonalizeCommand( private val acquirer: Acquirer? = null ) : Command() { + override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { + if (session.environment.card?.status != CardStatus.NotPersonalized) { + callback(CompletionResult.Failure(TangemSdkError.AlreadyPersonalized())) + return true + } + return false + } + override fun serialize(environment: SessionEnvironment): CommandApdu { return CommandApdu( Instruction.Personalize, @@ -46,7 +57,7 @@ class PersonalizeCommand( override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): Card { val tlvData = apdu.getTlvData(devPersonalizationKey) - ?: throw SessionError.DeserializeApduFailed() + ?: throw TangemSdkError.DeserializeApduFailed() val decoder = TlvDecoder(tlvData) return Card( @@ -99,7 +110,7 @@ class PersonalizeCommand( } private fun serializePersonalizationData(config: CardConfig): ByteArray { - val cardId = config.createCardId() ?: throw SessionError.SerializeCommandError() + val cardId = config.createCardId() ?: throw TangemSdkError.SerializeCommandError() val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.CardId, cardId) diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfigExtensions.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfigExtensions.kt index 4d7895f3c4..78e5709596 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfigExtensions.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfigExtensions.kt @@ -29,7 +29,7 @@ internal fun CardConfig.createSettingsMask(): SettingsMask { if (protectIssuerDataAgainstReplay) builder.add(Settings.ProtectIssuerDataAgainstReplay) - if (forbidPurgeWallet) builder.add(Settings.ForbidPurgeWallet) + if (forbidPurgeWallet) builder.add(Settings.ProhibitPurgeWallet) if (allowSelectBlockchain) builder.add(Settings.AllowSelectBlockchain) if (skipCheckPIN2andCVCIfValidatedByIssuer) builder.add(Settings.SkipCheckPin2andCvcIfValidatedByIssuer) diff --git a/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt b/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt index 71f460c7d7..52bf7a9351 100644 --- a/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt +++ b/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt @@ -1,6 +1,6 @@ package com.tangem.common -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.common.CompletionResult.Success /** @@ -9,5 +9,5 @@ import com.tangem.common.CompletionResult.Success */ sealed class CompletionResult { class Success(val data: T) : CompletionResult() - class Failure(val error: SessionError) : CompletionResult() + class Failure(val error: TangemSdkError) : CompletionResult() } \ No newline at end of file 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 3938cee65f..96daaa2826 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 @@ -1,6 +1,6 @@ package com.tangem.common.apdu -import com.tangem.SessionError +import com.tangem.TangemSdkError /** * Part of a response from the card, shows the status of the operation @@ -25,16 +25,16 @@ enum class StatusWord(val code: Int, val description: String) { } } -fun StatusWord.toSessionError(): SessionError? { +fun StatusWord.toTangemSdkError(): TangemSdkError? { return when (this) { StatusWord.ProcessCompleted, StatusWord.Pin1Changed, StatusWord.Pin2Changed, StatusWord.PinsChanged -> null StatusWord.NeedPause -> null - StatusWord.InvalidParams -> SessionError.InvalidParams() - StatusWord.ErrorProcessingCommand -> SessionError.ErrorProcessingCommand() - StatusWord.InvalidState -> SessionError.InvalidState() - StatusWord.InsNotSupported -> SessionError.InsNotSupported() - StatusWord.NeedEncryption -> SessionError.NeedEncryption() - StatusWord.Unknown -> SessionError.UnknownStatus() + StatusWord.InvalidParams -> TangemSdkError.InvalidParams() + StatusWord.ErrorProcessingCommand -> TangemSdkError.ErrorProcessingCommand() + StatusWord.InvalidState -> TangemSdkError.InvalidState() + StatusWord.InsNotSupported -> TangemSdkError.InsNotSupported() + StatusWord.NeedEncryption -> TangemSdkError.NeedEncryption() + StatusWord.Unknown -> TangemSdkError.UnknownStatus() } } diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvBuilder.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvBuilder.kt index 3e3c8557f2..3cade3e92a 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvBuilder.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvBuilder.kt @@ -14,7 +14,7 @@ class TlvBuilder { fun serialize(): ByteArray { Log.v("TLV", - "List of TLV that are being sent to a card:\n${tlvs.joinToString("\n")}") + "List of encoded TLVs:\n${tlvs.joinToString("\n")}") return tlvs.serialize() } diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt index 4954c38c46..62c0899f2d 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt @@ -1,7 +1,7 @@ package com.tangem.common.tlv import com.tangem.Log -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.commands.* import com.tangem.commands.common.IssuerDataMode import com.tangem.common.extensions.toDate @@ -20,7 +20,7 @@ class TlvDecoder(val tlvList: List) { init { Log.v("TLV", - "List of TLV received from card:\n${tlvList.joinToString("\n")}") + "List of decoded TLVs:\n${tlvList.joinToString("\n")}") } /** @@ -34,7 +34,7 @@ class TlvDecoder(val tlvList: List) { inline fun decodeOptional(tag: TlvTag): T? = try { decode(tag) - } catch (exception: SessionError.DecodingFailedMissingTag) { + } catch (exception: TangemSdkError.DecodingFailedMissingTag) { null } @@ -55,7 +55,7 @@ class TlvDecoder(val tlvList: List) { return false as T } else { Log.e(this::class.simpleName!!, "Tag $tag not found") - throw SessionError.DecodingFailedMissingTag() + throw TangemSdkError.DecodingFailedMissingTag() } return when (tag.valueType()) { @@ -73,7 +73,7 @@ class TlvDecoder(val tlvList: List) { tlvValue.toInt() as T } catch (exception: IllegalArgumentException) { Log.e(this::class.simpleName!!, exception.message ?: "") - throw SessionError.DecodingFailed() + throw TangemSdkError.DecodingFailed() } } TlvValueType.BoolValue -> { @@ -90,7 +90,7 @@ class TlvDecoder(val tlvList: List) { EllipticCurve.byName(tlvValue.toUtf8()) as T } catch (exception: Exception) { logException(tag, tlvValue.toUtf8(), exception) - throw SessionError.DecodingFailed() + throw TangemSdkError.DecodingFailed() } @@ -101,7 +101,7 @@ class TlvDecoder(val tlvList: List) { tlvValue.toDate() as T } catch (exception: Exception) { logException(tag, tlvValue.toHexString(), exception) - throw SessionError.DecodingFailed() + throw TangemSdkError.DecodingFailed() } } TlvValueType.ProductMask -> { @@ -118,7 +118,7 @@ class TlvDecoder(val tlvList: List) { CardStatus.byCode(tlvValue.toInt()) as T } catch (exception: Exception) { logException(tag, tlvValue.toInt().toString(), exception) - throw SessionError.DecodingFailed() + throw TangemSdkError.DecodingFailed() } } TlvValueType.SigningMethod -> { @@ -127,7 +127,7 @@ class TlvDecoder(val tlvList: List) { SigningMethodMask(tlvValue.toInt()) as T } catch (exception: Exception) { logException(tag, tlvValue.toInt().toString(), exception) - throw SessionError.DecodingFailed() + throw TangemSdkError.DecodingFailed() } } TlvValueType.IssuerDataMode -> { @@ -136,7 +136,7 @@ class TlvDecoder(val tlvList: List) { IssuerDataMode.byCode(tlvValue.toInt().toByte()) as T } catch (exception: Exception) { logException(tag, tlvValue.toInt().toString(), exception) - throw SessionError.DecodingFailed() + throw TangemSdkError.DecodingFailed() } } } @@ -151,7 +151,7 @@ class TlvDecoder(val tlvList: List) { if (T::class != ExpectedT::class) { Log.e(this::class.simpleName!!, "Mapping error. Type for tag: $tag must be ${tag.valueType()}. It is ${T::class}") - throw SessionError.DecodingFailedTypeMismatch() + throw TangemSdkError.DecodingFailedTypeMismatch() } } diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt index 057b0807e8..99589b7d40 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt @@ -1,7 +1,7 @@ package com.tangem.common.tlv import com.tangem.Log -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.commands.* import com.tangem.commands.common.IssuerDataMode import com.tangem.common.extensions.calculateSha256 @@ -24,7 +24,7 @@ class TlvEncoder { return Tlv(tag, encodeValue(tag, value)) } else { Log.e(this::class.simpleName!!, "Encoding error. Value for tag $tag is null") - throw SessionError.EncodingFailed() + throw TangemSdkError.EncodingFailed() } } @@ -106,7 +106,7 @@ class TlvEncoder { if (T::class != ExpectedT::class) { Log.e(this::class.simpleName!!, "Mapping error. Type for tag: $tag must be ${tag.valueType()}. It is ${T::class}") - throw SessionError.EncodingFailedTypeMismatch() + throw TangemSdkError.EncodingFailedTypeMismatch() } } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt b/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt index 6b21be102c..c9b87b4da2 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt @@ -2,7 +2,7 @@ package com.tangem.tasks import com.tangem.CardSession import com.tangem.CardSessionRunnable -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.commands.CardStatus import com.tangem.commands.CheckWalletCommand import com.tangem.commands.CreateWalletCommand @@ -14,7 +14,7 @@ class CreateWalletTask : CardSessionRunnable { override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val curve = session.environment.card?.curve if (curve == null) { - callback(CompletionResult.Failure(SessionError.CardError())) + callback(CompletionResult.Failure(TangemSdkError.CardError())) return } @@ -24,7 +24,7 @@ class CreateWalletTask : CardSessionRunnable { is CompletionResult.Failure -> callback(createWalletResult) is CompletionResult.Success -> { if (createWalletResult.data.status != CardStatus.Loaded) { - callback(CompletionResult.Failure(SessionError.UnknownError())) + callback(CompletionResult.Failure(TangemSdkError.UnknownError())) } else { val checkWalletCommand = CheckWalletCommand( curve, createWalletResult.data.walletPublicKey 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 39dea59f0b..db78568806 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt @@ -2,7 +2,7 @@ package com.tangem.tasks import com.tangem.CardSession import com.tangem.CardSessionRunnable -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.commands.* import com.tangem.common.CompletionResult @@ -18,7 +18,7 @@ internal class ScanTask : CardSessionRunnable { val card = session.environment.card if (card == null) { - callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) + callback(CompletionResult.Failure(TangemSdkError.MissingPreflightRead())) } else if (card.cardData?.productMask?.contains(Product.Tag) != false) { callback(CompletionResult.Success(card)) @@ -27,7 +27,7 @@ internal class ScanTask : CardSessionRunnable { callback(CompletionResult.Success(card)) } else if (card.curve == null || card.walletPublicKey == null) { - callback(CompletionResult.Failure(SessionError.CardError())) + callback(CompletionResult.Failure(TangemSdkError.CardError())) } else { val checkWalletCommand = CheckWalletCommand(card.curve, card.walletPublicKey) diff --git a/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt b/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt index fbd4514c11..8f7bd0b91e 100644 --- a/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt +++ b/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt @@ -1,7 +1,7 @@ package com.tangem.common.tlv import com.google.common.truth.Truth.assertThat -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.commands.* import com.tangem.common.extensions.hexToBytes import org.junit.Test @@ -35,21 +35,21 @@ class TlvDecoderTest { @Test fun `map when value is null throws MissingTagException`() { - assertThrows { + assertThrows { tlvMapper.decode(TlvTag.TokenSymbol) } } @Test fun `map optional to wrong type throws WrongTypeException`() { - assertThrows { + assertThrows { tlvMapper.decodeOptional(TlvTag.CardData) } } @Test fun `map to wrong type throws WrongTypeException`() { - assertThrows { + assertThrows { tlvMapper.decode(TlvTag.CardData) } } @@ -76,7 +76,7 @@ class TlvDecoderTest { .isTrue() assertThat(settingsMask.contains(Settings.UseDynamicNdef)) .isTrue() - assertThat(settingsMask.contains(Settings.ForbidPurgeWallet)) + assertThat(settingsMask.contains(Settings.ProhibitPurgeWallet)) .isFalse() } @@ -134,7 +134,7 @@ class TlvDecoderTest { @Test fun `map Enum with unknown code throws ConversionException error`() { val localMapper = TlvDecoder(listOf(Tlv(TlvTag.CurveId, "test".toByteArray()))) - assertThrows { + assertThrows { localMapper.decode(TlvTag.CurveId) } } @@ -171,7 +171,7 @@ class TlvDecoderTest { @Test fun `map Int with wrong value throws ConversionException`() { val localMapper = TlvDecoder(listOf(Tlv(TlvTag.SignedHashes, byteArrayOf(1, 2, 3, 4, 5)))) - assertThrows { + assertThrows { localMapper.decode(TlvTag.SignedHashes) } } diff --git a/tangem-devkit/build.gradle b/tangem-devkit/build.gradle index e692140432..266e0bf816 100644 --- a/tangem-devkit/build.gradle +++ b/tangem-devkit/build.gradle @@ -9,8 +9,8 @@ android { applicationId "com.tangem.devkit" minSdkVersion 21 targetSdkVersion 29 - versionCode 1 - versionName "1.0" + versionCode 3 + versionName "1.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/TestUserDataActivity.kt b/tangem-devkit/src/main/java/com/tangem/devkit/TestUserDataActivity.kt index c6124d542f..1fd9eeedb6 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/TestUserDataActivity.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/TestUserDataActivity.kt @@ -6,8 +6,8 @@ import android.widget.CompoundButton import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import com.tangem.SessionEnvironment -import com.tangem.SessionError import com.tangem.TangemSdk +import com.tangem.TangemSdkError import com.tangem.common.CompletionResult import com.tangem.tangem_sdk_new.extensions.init import kotlinx.android.synthetic.main.activity_test_user_data.* @@ -92,8 +92,8 @@ class TestUserDataActivity : AppCompatActivity() { } } - private fun handleError(tv: TextView, error: SessionError) { - if (error is SessionError.UserCancelled) return + private fun handleError(tv: TextView, error: TangemSdkError) { + if (error is TangemSdkError.UserCancelled) return runOnUiThread { tv.text = error::class.simpleName } } diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/TlvResources.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/TlvResources.kt index 32bb68d840..c7527b36ed 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/TlvResources.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/TlvResources.kt @@ -18,7 +18,7 @@ class TlvResources { holder.register(TlvId.TransactionOutHash, Resources(R.string.tlv_transaction_out_hash, R.string.info_tlv_transaction_out_hash)) holder.register(TlvId.Counter, Resources(R.string.tlv_counter, R.string.info_tlv_counter)) holder.register(TlvId.IssuerData, Resources(R.string.tlv_issuer_data, R.string.info_tlv_issuer_data)) - holder.register(TlvId.UserData, Resources(R.string.tlv_issuer_data, R.string.info_tlv_user_data)) + holder.register(TlvId.UserData, Resources(R.string.tlv_user_data, R.string.info_tlv_user_data)) holder.register(TlvId.ProtectedUserData, Resources(R.string.tlv_user_protected_data, R.string.info_tlv_protected_user_data)) } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/ActionViewModel.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/ActionViewModel.kt index 3c04d86d59..272bb11671 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/ActionViewModel.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/ui/ActionViewModel.kt @@ -4,8 +4,8 @@ import android.view.View import androidx.annotation.UiThread import androidx.lifecycle.* import com.google.gson.Gson -import com.tangem.SessionError import com.tangem.TangemSdk +import com.tangem.TangemSdkError import com.tangem.commands.Card import com.tangem.commands.CommandResponse import com.tangem.common.CompletionResult @@ -16,7 +16,6 @@ import com.tangem.devkit._arch.structure.Payload import com.tangem.devkit._arch.structure.abstraction.Item import com.tangem.devkit._arch.structure.abstraction.iterate import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager -import com.tangem.devkit.ucase.domain.responses.ResponseJsonConverter import com.tangem.devkit.ucase.resources.ActionType import com.tangem.devkit.ucase.tunnel.ViewScreen import com.tangem.devkit.ucase.variants.personalize.converter.ItemTypes @@ -133,8 +132,7 @@ class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif internal class Notifier(private val vm: ActionViewModel) { - private var notShowedError: SessionError? = null - private val gson: Gson = ResponseJsonConverter().gson + private var notShowedError: TangemSdkError? = null fun handleActionResult(result: CompletionResult<*>, list: List) { if (list.isNotEmpty()) notifyItemsChanged(list) @@ -163,10 +161,10 @@ internal class Notifier(private val vm: ActionViewModel) { } } - private fun handleError(error: SessionError) { + private fun handleError(error: TangemSdkError) { Log.d(this, "error = $error") when (error) { - is SessionError.UserCancelled -> { + is TangemSdkError.UserCancelled -> { if (notShowedError == null) { vm.seError.postValue("User canceled the action") } else { diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/BaseResponseConverter.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/BaseResponseConverter.kt index c1eded3858..34ead3b362 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/BaseResponseConverter.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/converter/BaseResponseConverter.kt @@ -2,8 +2,8 @@ package com.tangem.devkit.ucase.variants.responses.converter import com.tangem.devkit._arch.structure.Id import com.tangem.devkit._arch.structure.abstraction.* -import com.tangem.devkit.ucase.domain.responses.ResponseFieldConverter import com.tangem.devkit.ucase.variants.responses.item.TextHeaderItem +import com.tangem.tangem_sdk_new.converter.ResponseFieldConverter import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf /** diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/ui/ResponseFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/ui/ResponseFragment.kt index 0dcb15e807..ea236daef2 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/ui/ResponseFragment.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/responses/ui/ResponseFragment.kt @@ -12,10 +12,10 @@ import com.tangem.devkit.R import com.tangem.devkit._arch.widget.WidgetBuilder import com.tangem.devkit._main.MainViewModel import com.tangem.devkit.extensions.shareText -import com.tangem.devkit.ucase.domain.responses.ResponseJsonConverter import com.tangem.devkit.ucase.ui.BaseFragment import com.tangem.devkit.ucase.variants.responses.ResponseViewModel import com.tangem.devkit.ucase.variants.responses.ui.widget.ResponseItemBuilder +import com.tangem.tangem_sdk_new.converter.ResponseConverter /** [REDACTED_AUTHOR] @@ -68,7 +68,7 @@ open class ResponseFragment : BaseFragment() { override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.action_share -> { - shareText(ResponseJsonConverter().convertResponse(mainActivityVM.commandResponse)) + shareText(ResponseConverter().convertResponse(mainActivityVM.commandResponse)) } } return super.onOptionsItemSelected(item) diff --git a/tangem-sdk-android-config/README.md b/tangem-sdk-android-config/README.md index 5acf8ff48f..9a9fc5efc4 100644 --- a/tangem-sdk-android-config/README.md +++ b/tangem-sdk-android-config/README.md @@ -117,7 +117,7 @@ tangemSdk.scanCard { result -> } } is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -132,7 +132,7 @@ Communication with the card is an asynchronous operation. In order to get a resu `CompletionResult` – this is the sealed class for the results of `CardSessionRunnable`. `Success(val data: T)` is triggered after successful operation and contains a `CommandResponse`. -`Failure(val error: SessionError)` is triggered on error. +`Failure(val error: TangemSdkError)` is triggered on error. #### Sign This method allows you to sign one or multiple hashes. Simultaneous signing of array of hashes in a single SIGN command is required to support Bitcoin-type multi-input blockchains (UTXO). The SIGN command will return a corresponding array of signatures. @@ -143,7 +143,7 @@ tangemSdk.sign( cardId) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -164,7 +164,7 @@ An example of usage (description is available at documentation for `TangemSdk.re tangemSdk.readIssuerData(cardId) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -184,7 +184,7 @@ An example of usage (description is available at documentation for `TangemSdk.re tangemSdk.readIssuerExtraData(cardId) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -204,7 +204,7 @@ An example of usage (description is available at documentation for `TangemSdk.wr tangemSdk.writeIssuerData(cardId, issuerData, issuerDataSignature, issuerDataCounter) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -225,7 +225,7 @@ An example of usage (description is available at documentation for `TangemSdk.wr ) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -246,7 +246,7 @@ An example of usage (description is available at documentation for `TangemSdk.wr ) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -265,7 +265,7 @@ An example of usage (description is available at documentation for `TangemSdk.re tangemSdk.readUserData(cardId) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -284,7 +284,7 @@ An example of usage (description is available at documentation for `TangemSdk.cr tangemSdk.createWallet(cardId) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -303,7 +303,7 @@ An example of usage (description is available at documentation for `TangemSdk.pu tangemSdk.purgeWallet(cardId) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -323,7 +323,7 @@ An example of usage (description is available at documentation for `TangemSdk.de tangemSdk.depersonalize(cardId) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -342,7 +342,7 @@ An example of usage (description is available at documentation for `TangemSdk.pe tangemSdk.personalize(config, issuer, manufacturer, acquirer) { result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -383,7 +383,7 @@ To do this, you need to call `tangemSdk.startSession()` method and get a `CardSe SignCommand(createSampleHashes())) {result -> when (result) { is CompletionResult.Failure -> { - if (result.error is SessionError.UserCancelledError) { + if (result.error is TangemSdkError.UserCancelledError) { // Handle case when user cancelled manually } // Handle other errors @@ -410,7 +410,7 @@ class OneTapSignTask(private val hashesToSign: Array) : CardSessionRu val card = session.environment.card if (card == null) { - callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) + callback(CompletionResult.Failure(TangemSdkError.MissingPreflightRead())) } else if (card.cardData?.productMask?.contains(Product.Tag) != false) { callback(CompletionResult.Success(card)) @@ -419,7 +419,7 @@ class OneTapSignTask(private val hashesToSign: Array) : CardSessionRu callback(CompletionResult.Success(card)) } else if (card.curve == null || card.walletPublicKey == null) { - callback(CompletionResult.Failure(SessionError.CardError())) + callback(CompletionResult.Failure(TangemSdkError.CardError())) } else { val signCommand = SignCommand(hashesToSign) diff --git a/tangem-sdk/build.gradle b/tangem-sdk/build.gradle index 14d86acb0d..9c2742da98 100644 --- a/tangem-sdk/build.gradle +++ b/tangem-sdk/build.gradle @@ -58,4 +58,6 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' -} + + implementation 'com.google.code.gson:gson:2.8.6' +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/responses/ResponseJsonConverter.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/converter/ResponseConverter.kt similarity index 97% rename from tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/responses/ResponseJsonConverter.kt rename to tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/converter/ResponseConverter.kt index 4c6148b6f9..392bdc9f96 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/responses/ResponseJsonConverter.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/converter/ResponseConverter.kt @@ -1,9 +1,9 @@ -package com.tangem.devkit.ucase.domain.responses +package com.tangem.tangem_sdk_new.converter import com.google.gson.* import com.tangem.commands.* import com.tangem.common.extensions.toHexString -import com.tangem.devkit.extensions.print +import com.tangem.tangem_sdk_new.extensions.print import java.lang.reflect.Type import java.text.DateFormat import java.util.* @@ -11,7 +11,7 @@ import java.util.* /** [REDACTED_AUTHOR] */ -class ResponseJsonConverter { +class ResponseConverter { val gson: Gson by lazy { init() } diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/List.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/List.kt new file mode 100644 index 0000000000..a86c326453 --- /dev/null +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/List.kt @@ -0,0 +1,13 @@ +package com.tangem.tangem_sdk_new.extensions + +fun List.print(delimiter: String = ", ", wrap: Boolean = true): String { + val builder = StringBuilder() + forEach { builder.append(it).append(delimiter) } + val length = builder.length + if (length > delimiter.length) { + builder.delete(length - delimiter.length, length) + } + val result = builder.toString() + + return if (wrap) "[$result]" else result +} \ No newline at end of file diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt index 845dbb2abd..225edfb595 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt @@ -6,7 +6,7 @@ import android.nfc.tech.IsoDep import android.nfc.tech.NfcV import com.tangem.CardReader import com.tangem.Log -import com.tangem.SessionError +import com.tangem.TangemSdkError import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.ResponseApdu @@ -38,7 +38,7 @@ class NfcReader : CardReader { // Stops reading and sends failure callback to a task // if reading is cancelled (when user closes nfc bottom sheet dialog). closeSession() - callback?.invoke(CompletionResult.Failure(SessionError.UserCancelled())) + callback?.invoke(CompletionResult.Failure(TangemSdkError.UserCancelled())) } } @@ -67,7 +67,7 @@ class NfcReader : CardReader { private fun transceiveData() { if (readingCancelled) { - callback?.invoke(CompletionResult.Failure(SessionError.UserCancelled())) + callback?.invoke(CompletionResult.Failure(TangemSdkError.UserCancelled())) return } if (data == null) return @@ -76,10 +76,11 @@ class NfcReader : CardReader { try { rawResponse = isoDep?.transceive(data) } catch (exception: TagLostException) { - callback?.invoke(CompletionResult.Failure(SessionError.TagLost())) + callback?.invoke(CompletionResult.Failure(TangemSdkError.TagLost())) isoDep = null return } catch (exception: Exception) { + Log.i(this::class.simpleName!!, exception.localizedMessage ?: "Error tranceiving data") isoDep = null return } @@ -110,7 +111,7 @@ class NfcReader : CardReader { when (response) { is SlixReadResult.Failure -> { Log.e(this::class.simpleName!!, "${response.exception.message}") - callback?.invoke(CompletionResult.Failure(SessionError.ErrorProcessingCommand())) + callback?.invoke(CompletionResult.Failure(TangemSdkError.ErrorProcessingCommand())) } is SlixReadResult.Success -> { callback?.invoke(CompletionResult.Success(ResponseApdu(response.data))) diff --git a/tangem-sdk/src/main/res/drawable/pb_circle.xml b/tangem-sdk/src/main/res/drawable/pb_circle.xml index a3e74eb416..cf65fbe9d4 100644 --- a/tangem-sdk/src/main/res/drawable/pb_circle.xml +++ b/tangem-sdk/src/main/res/drawable/pb_circle.xml @@ -34,9 +34,9 @@ android:toDegrees="360" /> diff --git a/tangem-sdk/src/main/res/layout/layout_touch_card.xml b/tangem-sdk/src/main/res/layout/layout_touch_card.xml index c8f66c0bc4..ba8e553180 100644 --- a/tangem-sdk/src/main/res/layout/layout_touch_card.xml +++ b/tangem-sdk/src/main/res/layout/layout_touch_card.xml @@ -34,7 +34,7 @@ android:id="@+id/rippleBackgroundNfc" android:layout_width="150dp" android:layout_height="150dp" - app:rb_color="@color/fab" + app:rb_color="@color/card_sdk_ripple" app:rb_duration="3000" app:rb_radius="16dp" app:rb_rippleAmount="4" diff --git a/tangem-sdk/src/main/res/layout/nfc_bottom_sheet.xml b/tangem-sdk/src/main/res/layout/nfc_bottom_sheet.xml index b077eb38ce..33bf9ad0a3 100644 --- a/tangem-sdk/src/main/res/layout/nfc_bottom_sheet.xml +++ b/tangem-sdk/src/main/res/layout/nfc_bottom_sheet.xml @@ -32,10 +32,11 @@ android:gravity="center" android:letterSpacing="0.02" android:text="@string/header_card" - android:textColor="#000000" + android:textColor="@color/card_sdk_text_color" android:textSize="15sp" android:textStyle="normal" - android:visibility="gone"/> + android:visibility="gone" + tools:visibility="visible"/> + android:visibility="gone" + tools:visibility="visible"/> @@ -63,6 +65,7 @@ android:layout_gravity="center" android:layout_marginTop="24dp" android:text="@string/dialog_ready_to_scan" + android:textColor="@color/card_sdk_text_color" android:textAppearance="@style/TextAppearance.AppCompat.Large" /> @@ -113,6 +116,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + android:tint="@color/card_sdk_accent" android:src="@drawable/ic_done_135dp" /> + /> \ No newline at end of file diff --git a/tangem-sdk/src/main/res/values/colors.xml b/tangem-sdk/src/main/res/values/colors.xml index b154cb7818..ea2433c6ad 100644 --- a/tangem-sdk/src/main/res/values/colors.xml +++ b/tangem-sdk/src/main/res/values/colors.xml @@ -3,6 +3,9 @@ #027aff #027AFF #027AFF - #4f98c0 + + #266dd3 + #000000 + #2c5ac4 \ No newline at end of file