diff --git a/app/build.gradle b/app/build.gradle index 07bb7d859f..af2ce223fa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,7 +99,7 @@ dependencies { implementation 'com.google.dagger:dagger:2.24' kapt 'com.google.dagger:dagger-compiler:2.21' annotationProcessor 'com.google.dagger:dagger-compiler:2.21' - implementation 'com.google.zxing:core:3.4.0' + implementation 'com.google.zxing:core:3.3.3' // Do not update to 3.4.0, it requires minSdk 24 implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.madgag.spongycastle:core:1.56.0.0' implementation 'com.madgag.spongycastle:prov:1.56.0.0' 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..880796d9e7 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, @@ -183,29 +183,51 @@ class TangemSdk( } /** - * This method launches a [WriteUserDataCommand] on a new thread. + * This method launches a [WriteUserDataCommand] on a new thread, writing UserData and UserCounter fields. * - * This command writes some of UserData, UserProtectedData, UserCounter and UserProtectedCounter fields. - * User_Data and User_ProtectedData are never changed or parsed by the executable code the Tangem COS. - * The App defines purpose of use, format and it's payload. For example, this field may contain cashed information + * User_Data is never changed or parsed by the executable code the Tangem COS. + * The App defines purpose of use, format and its payload. For example, this field may contain cashed information * from blockchain to accelerate preparing new transaction. - * User_Counter and User_ProtectedCounter are counters, that initial values can be set by App and increased on every signing + * The initial value of User_Counter can be set by an App and increased on every signing * 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. * * Writing of UserCounter and UserData is protected only by PIN1. - * UserProtectedCounter and UserProtectedData need additionally PIN2 to confirmation. */ fun writeUserData( cardId: String, userData: ByteArray? = null, - userProtectedData: ByteArray? = null, userCounter: Int? = null, + initialMessage: Message? = null, + callback: (result: CompletionResult) -> Unit + ) { + val command = WriteUserDataCommand(userData = userData,userCounter = userCounter) + startSessionWithRunnable(command, cardId, initialMessage, callback) + } + + /** + * This method launches a [WriteUserDataCommand] on a new thread, + * writing UserProtectedData and UserProtectedCounter fields. + * + * User_ProtectedData is never changed or parsed by the executable code the Tangem COS. + * The App defines purpose of use, format and its payload. For example, this field may contain cashed information + * from blockchain to accelerate preparing new transaction. + * The initial value of User_ProtectedCounter can be set by an App and increased on every signing + * of a new transaction (on SIGN command that calculate new signatures). The App defines the purpose of use. + * For example, this fields may contain blockchain nonce value. + * + * UserProtectedCounter and UserProtectedData require PIN2 for confirmation. + */ + fun writeProtectedUserData( + cardId: String, + userProtectedData: ByteArray? = null, userProtectedCounter: Int? = null, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit ) { - val command = WriteUserDataCommand(userData, userProtectedData, userCounter, userProtectedCounter) + val command = WriteUserDataCommand( + userProtectedData = userProtectedData, userProtectedCounter = userProtectedCounter + ) startSessionWithRunnable(command, cardId, initialMessage, callback) } @@ -224,7 +246,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 +268,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 +287,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 +305,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 +328,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 +347,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 +365,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 +389,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..5628c01f29 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.* @@ -50,9 +50,7 @@ class TestUserDataActivity : AppCompatActivity() { tangemSdk.writeUserData( writeOptions.cardId!!, writeOptions.userData, - writeOptions.userProtectedData, - writeOptions.userCounter, - writeOptions.userProtectedCounter + writeOptions.userCounter ) { when (it) { is CompletionResult.Failure -> handleError(tv_write_result, it.error) @@ -92,8 +90,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/_main/entryPoint/ActionListFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/_main/entryPoint/ActionListFragment.kt index 5a1008d717..2656204d6b 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/_main/entryPoint/ActionListFragment.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/_main/entryPoint/ActionListFragment.kt @@ -71,7 +71,8 @@ class ActionListFragment : BaseFragment() { ActionType.ReadIssuerExData, ActionType.WriteIssuerExData, ActionType.ReadUserData, - ActionType.WriteUserData + ActionType.WriteUserData, + ActionType.WriteProtectedUserData ) } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteUserDataAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteUserDataAction.kt index d7d6da6107..7727546c05 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteUserDataAction.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteUserDataAction.kt @@ -11,12 +11,10 @@ class WriteUserDataAction : BaseAction() { override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { val userData = (attrs.itemList.findItem(TlvId.UserData)?.getData() as? String)?.toByteArray() ?: return - val protectedUserData = (attrs.itemList.findItem(TlvId.ProtectedUserData)?.getData() as? String)?.toByteArray() - ?: return val cardId = attrs.itemList.findItem(TlvId.CardId)?.viewModel?.data ?: return val counter = (attrs.itemList.findItem(TlvId.Counter)?.viewModel?.data as? Int) ?: 1 - attrs.tangemSdk.writeUserData(stringOf(cardId), userData, protectedUserData, counter, counter) { + attrs.tangemSdk.writeUserData(stringOf(cardId), userData, counter) { handleResult(payload, it, null, attrs, callback) } } @@ -24,7 +22,6 @@ class WriteUserDataAction : BaseAction() { override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { return when (id) { TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } - TlvId.Counter -> { callback -> ReadUserDataAction().executeMainAction(payload, attrs, callback) } else -> null } } diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteUserProtectedDataAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteUserProtectedDataAction.kt new file mode 100644 index 0000000000..e315107b8b --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/WriteUserProtectedDataAction.kt @@ -0,0 +1,29 @@ +package com.tangem.devkit.ucase.domain.actions + +import com.tangem.devkit._arch.structure.Id +import com.tangem.devkit._arch.structure.PayloadHolder +import com.tangem.devkit._arch.structure.abstraction.findItem +import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback +import com.tangem.devkit.ucase.variants.TlvId +import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf + + +class WriteUserProtectedDataAction : BaseAction() { + override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { + val protectedUserData = (attrs.itemList.findItem(TlvId.ProtectedUserData)?.getData() as? String)?.toByteArray() + ?: return + val cardId = attrs.itemList.findItem(TlvId.CardId)?.viewModel?.data ?: return + val counter = (attrs.itemList.findItem(TlvId.Counter)?.viewModel?.data as? Int) ?: 1 + + attrs.tangemSdk.writeProtectedUserData(stringOf(cardId), protectedUserData, counter) { + handleResult(payload, it, null, attrs, callback) + } + } + + override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { + return when (id) { + TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt index a5085dccb2..bb6b16540e 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt @@ -96,9 +96,18 @@ class WriteUserDataItemsManager : BaseItemsManager(WriteUserDataAction()) { setItems(listOf( EditTextItem(TlvId.CardId, null), EditTextItem(TlvId.Counter, "1"), - EditTextItem(TlvId.UserData, "User data to be written on a card"), - EditTextItem(TlvId.ProtectedUserData, "Protected user data to be written on a card") - + EditTextItem(TlvId.UserData, "User data to be written on a card") )) } +} + + class WriteProtectedUserDataItemsManager : BaseItemsManager(WriteUserProtectedDataAction()) { + init { + setItemChangeConsequences(CardIdConsequence()) + setItems(listOf( + EditTextItem(TlvId.CardId, null), + EditTextItem(TlvId.Counter, "1"), + EditTextItem(TlvId.ProtectedUserData, "Protected user data to be written on a card") + )) + } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/Ids.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/Ids.kt index 1daedb6326..90e2d54588 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/Ids.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/Ids.kt @@ -16,6 +16,7 @@ enum class ActionType : Id { WriteIssuerExData, ReadUserData, WriteUserData, + WriteProtectedUserData, Personalize, Depersonalize, Unknown, diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/ActionResources.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/ActionResources.kt index 2d7adcea1c..7ca4301b5f 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/ActionResources.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/resources/initializers/ActionResources.kt @@ -24,6 +24,7 @@ class ActionResources { holder.register(ActionType.WriteIssuerExData, ActionRes(R.string.action_issuer_write_ex_data, R.string.info_action_issuer_write_ex_data, R.id.action_nav_entry_point_to_nav_issuer_write_ex_data)) holder.register(ActionType.ReadUserData, ActionRes(R.string.action_user_read_data, R.string.info_action_user_read_data, R.id.action_nav_entry_point_to_nav_user_read_data)) holder.register(ActionType.WriteUserData, ActionRes(R.string.action_user_write_data, R.string.info_action_user_write_data, R.id.action_nav_entry_point_to_nav_user_write_data)) + holder.register(ActionType.WriteProtectedUserData, ActionRes(R.string.action_user_write_protected_data, R.string.info_action_user_write_protected_data, R.id.action_nav_entry_point_to_nav_user_write_protected_data)) // holder.register(ActionType.Unknown, getIfNotContains()) } } \ No newline at end of file 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-devkit/src/main/java/com/tangem/devkit/ucase/variants/userdata/ui/WriteProtectedUserDataFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/userdata/ui/WriteProtectedUserDataFragment.kt new file mode 100644 index 0000000000..4962b4537a --- /dev/null +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/userdata/ui/WriteProtectedUserDataFragment.kt @@ -0,0 +1,10 @@ +package com.tangem.devkit.ucase.variants.userdata.ui + +import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager +import com.tangem.devkit.ucase.domain.paramsManager.managers.WriteProtectedUserDataItemsManager +import com.tangem.devkit.ucase.ui.BaseCardActionFragment + +class WriteProtectedUserDataFragment : BaseCardActionFragment() { + + override val itemsManager: ItemsManager by lazy { WriteProtectedUserDataItemsManager() } +} \ No newline at end of file diff --git a/tangem-devkit/src/main/res/navigation/navigation.xml b/tangem-devkit/src/main/res/navigation/navigation.xml index a08466aff6..f89849a75c 100644 --- a/tangem-devkit/src/main/res/navigation/navigation.xml +++ b/tangem-devkit/src/main/res/navigation/navigation.xml @@ -44,6 +44,9 @@ + @@ -119,6 +122,12 @@ android:label="@string/action_user_write_data" tools:layout="@layout/fg_base_action_layout" /> + + Write Issuer Extra Data Read User Data Write User Data + Write Protected User DataThis command returns all data about the card and the wallet, including unique card number (CID) that has to be submitted while calling all other commands Depending on Signing_Method parameter defined during personalization, this command signs data using Wallet_PrivateKey @@ -26,7 +27,8 @@ This command retrieves Issuer Extra Data field and its issuer’s signature. This command writes Issuer Extra Data field and its issuer’s signature to the card. This command returns User Data and User Protected Data (up to 512-byte each) and two counters: User Counter and User Protected Counter. - This command writes to the card any of User Data, User Protected Data, User Counter and User Protected Counter fields. - + This command writes to the card User Data and User Counter fields. + This command writes to the card User Protected Data and User Protected Counter fields. + \ No newline at end of file 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