Updated on 2026-08-14

This commit is contained in:
Tangem 2020-05-08 11:19:32 +03:00
commit 3103a1bc0e
40 changed files with 668 additions and 302 deletions

View file

@ -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())
}
}

View file

@ -37,7 +37,7 @@ interface CardSessionRunnable<T : CommandResponse> {
* @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<Boolean>) -> Unit) {
error: TangemSdkError, callback: (result: CompletionResult<Boolean>) -> 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()))
}
}

View file

@ -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
)

View file

@ -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) {

View file

@ -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<Card>) -> 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<ByteArray>, cardId: String, initialMessage: Message? = null,
callback: (result: CompletionResult<SignResponse>) -> 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<ReadIssuerDataResponse>) -> 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<ReadIssuerExtraDataResponse>) -> Unit) {
@ -126,7 +126,7 @@ class TangemSdk(
* @param issuerDataCounter An optional counter that protect issuer data against replay attack.
* @param callback is triggered on the completion of the [WriteIssuerDataCommand] and provides
* card response in the form of [WriteIssuerDataResponse] if the task was performed successfully
* or [SessionError] in case of an error.
* or [TangemSdkError] in case of an error.
*/
fun writeIssuerData(cardId: String,
issuerData: ByteArray,
@ -164,7 +164,7 @@ class TangemSdk(
* @param issuerDataCounter An optional counter that protect issuer data against replay attack.
* @param callback is triggered on the completion of the [WriteIssuerExtraDataCommand] and provides
* card response in the form of [WriteIssuerDataResponse] if the task was performed successfully
* or [SessionError] in case of an error.
* or [TangemSdkError] in case of an error.
*/
fun writeIssuerExtraData(cardId: String,
issuerData: ByteArray,
@ -224,7 +224,7 @@ class TangemSdk(
* @param cardId CID, Unique Tangem card ID number.
* @param callback is triggered on the completion of the [ReadUserDataCommand] and provides
* card response in the form of [ReadUserDataResponse] if the task was performed successfully
* or [SessionError] in case of an error.
* or [TangemSdkError] in case of an error.
*/
fun readUserData(cardId: String, initialMessage: Message? = null,
callback: (result: CompletionResult<ReadUserDataResponse>) -> Unit) {
@ -246,7 +246,7 @@ class TangemSdk(
* @param cardId CID, Unique Tangem card ID number.
* @param callback is triggered on the completion of the [CreateWalletTask] and provides
* card response in the form of [CreateWalletResponse] if the task was performed successfully
* or [SessionError] in case of an error.
* or [TangemSdkError] in case of an error.
*/
fun createWallet(cardId: String, initialMessage: Message? = null,
callback: (result: CompletionResult<CreateWalletResponse>) -> Unit) {
@ -265,7 +265,7 @@ class TangemSdk(
* @param cardId CID, Unique Tangem card ID number.
* @param callback is triggered on the completion of the [PurgeWalletCommand] and provides
* card response in the form of [PurgeWalletResponse] if the task was performed successfully
* or [SessionError] in case of an error.
* or [TangemSdkError] in case of an error.
*/
fun purgeWallet(cardId: String, initialMessage: Message? = null,
callback: (result: CompletionResult<PurgeWalletResponse>) -> Unit) {
@ -283,7 +283,7 @@ class TangemSdk(
* @param cardId CID, Unique Tangem card ID number.
* @param callback is triggered on the completion of the [DepersonalizeCommand] and provides
* card response in the form of [DepersonalizeResponse] if the task was performed successfully
* or [SessionError] in case of an error.
* or [TangemSdkError] in case of an error.
* */
fun depersonalize(cardId: String, initialMessage: Message? = null,
callback: (result: CompletionResult<DepersonalizeResponse>) -> Unit) {
@ -306,7 +306,7 @@ class TangemSdk(
* (non-EMV) POS terminal infrastructure and transaction processing back-end.
* @param callback is triggered on the completion of the [PersonalizeCommand] and provides
* card response in the form of [Card] if the command was performed successfully
* or [SessionError] in case of an error.
* or [TangemSdkError] in case of an error.
*/
fun personalize(config: CardConfig,
issuer: Issuer, manufacturer: Manufacturer, acquirer: Acquirer? = null,
@ -325,7 +325,7 @@ class TangemSdk(
* @runnable: A custom task, adopting [CardSessionRunnable] protocol
* @cardId: CID, Unique Tangem card ID number. If not null, the SDK will check that you the card
* with which you tapped a phone has this [cardId] and SDK will return
* the [SessionError.WrongCard] otherwise.
* the [TangemSdkError.WrongCard] otherwise.
* @initialMessage: A custom description that shows at the beginning of the NFC session.
* If null, default message will be used.
* @callback: Standard [TangemSdk] callback.
@ -343,14 +343,14 @@ class TangemSdk(
* @cardId: CID, Unique Tangem card ID number. If not null, the SDK will check that you the card
* with which you tapped a phone has this [cardId] and SDK will return
* the [SessionError.WrongCard] otherwise.
* the [TangemSdkError.WrongCard] otherwise.
* @initialMessage: A custom description that shows at the beginning of the NFC session.
* If null, default message will be used.
* @callback: At first, you should check that the [SessionError] is not null,
* @callback: At first, you should check that the [TangemSdkError] is not null,
* then you can use the [CardSession] to interact with a card.
*/
fun startSession(cardId: String? = null, initialMessage: Message? = null,
callback: (session: CardSession, error: SessionError?) -> Unit) {
callback: (session: CardSession, error: TangemSdkError?) -> Unit) {
val cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage)
Thread().run { cardSession.start(callback) }
}
@ -367,7 +367,8 @@ class TangemSdk(
val terminalKeys = if (config.linkedTerminal) terminalKeysService?.getKeys() else null
return SessionEnvironment(
terminalKeys = terminalKeys,
cardFilter = config.cardFilter
cardFilter = config.cardFilter,
handleErrors = config.handleErrors
)
}

View file

@ -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)
}

View file

@ -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<CheckWalletResponse>) -> 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<CheckWalletResponse>) -> 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(

View file

@ -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<T : CommandResponse> : CardSessionRunnable<T> {
override fun run(session: CardSession, callback: (result: CompletionResult<T>) -> 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<T>) -> Unit): Boolean {
return false
}
open fun performAfterCheck(session: CardSession,
result: CompletionResult<T>,
callback: (result: CompletionResult<T>) -> Unit): Boolean {
return false
}
fun transceive(session: CardSession, callback: (result: CompletionResult<T>) -> Unit) {
@ -54,7 +73,7 @@ abstract class Command<T : CommandResponse> : CardSessionRunnable<T> {
}
}
}
} catch (error: SessionError) {
} catch (error: TangemSdkError) {
callback(CompletionResult.Failure(error))
}
}
@ -81,17 +100,17 @@ abstract class Command<T : CommandResponse> : CardSessionRunnable<T> {
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<T : CommandResponse> : CardSessionRunnable<T> {
return tlv?.find { it.tag == TlvTag.Pause }?.value?.toInt()
}
private fun tryHandleError(error: SessionError): Boolean {
private fun tryHandleError(error: TangemSdkError): Boolean {
return false
}

View file

@ -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<CreateWalletResponse>() {
override fun performPreCheck(session: CardSession, callback: (result: CompletionResult<CreateWalletResponse>) -> 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<CreateWalletResponse>,
callback: (result: CompletionResult<CreateWalletResponse>) -> 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<CreateWalletResponse>() {
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(

View file

@ -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<OpenSessi
override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): OpenSessionResponse {
val tlvData = apdu.getTlvData()
?: throw SessionError.DeserializeApduFailed()
?: throw TangemSdkError.DeserializeApduFailed()
val decoder = TlvDecoder(tlvData)
return OpenSessionResponse(

View file

@ -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
@ -29,6 +31,37 @@ class PurgeWalletResponse(
*/
class PurgeWalletCommand : Command<PurgeWalletResponse>() {
override fun performPreCheck(session: CardSession, callback: (result: CompletionResult<PurgeWalletResponse>) -> 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<PurgeWalletResponse>,
callback: (result: CompletionResult<PurgeWalletResponse>) -> 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<PurgeWalletResponse>() {
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(

View file

@ -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<Card>() {
override fun performAfterCheck(session: CardSession, result: CompletionResult<Card>, callback: (result: CompletionResult<Card>) -> 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<Card>() {
override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): Card {
val tlvData = apdu.getTlvData(environment.encryptionKey)
?: throw SessionError.DeserializeApduFailed()
?: throw TangemSdkError.DeserializeApduFailed()
val decoder = TlvDecoder(tlvData)

View file

@ -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<ReadIssuerDataResponse>) -> 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<ReadIssuerDataResponse>) -> 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(

View file

@ -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<ReadIssuerExtraDataResponse>) -> 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)

View file

@ -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<ReadUserDataResponse>() {
class ReadUserDataCommand : Command<ReadUserDataResponse>() {
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<ReadUserDataResponse>) -> 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)
)
}
}

View file

@ -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<ByteArray>)
private val hashSizes = if (hashes.isNotEmpty()) hashes.first().size else 0
override fun performPreCheck(session: CardSession, callback: (result: CompletionResult<SignResponse>) -> 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<SignResponse>,
callback: (result: CompletionResult<SignResponse>) -> 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<ByteArray>)
}
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<ByteArray>)
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(

View file

@ -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<WriteIssuerDataResponse>(), IssuerDataVerifier by verifier {
override fun run(session: CardSession, callback: (result: CompletionResult<WriteIssuerDataResponse>) -> Unit) {
override fun performPreCheck(session: CardSession, callback: (result: CompletionResult<WriteIssuerDataResponse>) -> 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<WriteIssuerDataResponse>,
callback: (result: CompletionResult<WriteIssuerDataResponse>) -> 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
}
}

View file

@ -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<WriteIssuerDataResponse>) -> 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<WriteIssuerDataResponse>) -> 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
}
}

View file

@ -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<WriteUserDataResponse>() {
override fun performPreCheck(session: CardSession, callback: (result: CompletionResult<WriteUserDataResponse>) -> 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<WriteUserDataResponse>,
callback: (result: CompletionResult<WriteUserDataResponse>) -> 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
}
}

View file

@ -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<DepersonalizeResponse>() {
override fun performPreCheck(session: CardSession, callback: (result: CompletionResult<DepersonalizeResponse>) -> 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()

View file

@ -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<Card>() {
override fun performPreCheck(session: CardSession, callback: (result: CompletionResult<Card>) -> 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)

View file

@ -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)

View file

@ -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<T> {
class Success<T>(val data: T) : CompletionResult<T>()
class Failure<T>(val error: SessionError) : CompletionResult<T>()
class Failure<T>(val error: TangemSdkError) : CompletionResult<T>()
}

View file

@ -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()
}
}

View file

@ -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()
}

View file

@ -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<Tlv>) {
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<Tlv>) {
inline fun <reified T> decodeOptional(tag: TlvTag): T? =
try {
decode<T>(tag)
} catch (exception: SessionError.DecodingFailedMissingTag) {
} catch (exception: TangemSdkError.DecodingFailedMissingTag) {
null
}
@ -55,7 +55,7 @@ class TlvDecoder(val tlvList: List<Tlv>) {
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<Tlv>) {
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<Tlv>) {
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<Tlv>) {
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<Tlv>) {
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<Tlv>) {
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<Tlv>) {
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<Tlv>) {
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()
}
}

View file

@ -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()
}
}
}

View file

@ -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<CreateWalletResponse> {
override fun run(session: CardSession, callback: (result: CompletionResult<CreateWalletResponse>) -> 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<CreateWalletResponse> {
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

View file

@ -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<Card> {
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<Card> {
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)

View file

@ -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<SessionError.DecodingFailedMissingTag> {
assertThrows<TangemSdkError.DecodingFailedMissingTag> {
tlvMapper.decode<String>(TlvTag.TokenSymbol)
}
}
@Test
fun `map optional to wrong type throws WrongTypeException`() {
assertThrows<SessionError.DecodingFailedTypeMismatch> {
assertThrows<TangemSdkError.DecodingFailedTypeMismatch> {
tlvMapper.decodeOptional<String?>(TlvTag.CardData)
}
}
@Test
fun `map to wrong type throws WrongTypeException`() {
assertThrows<SessionError.DecodingFailedTypeMismatch> {
assertThrows<TangemSdkError.DecodingFailedTypeMismatch> {
tlvMapper.decode<String>(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<SessionError.DecodingFailed> {
assertThrows<TangemSdkError.DecodingFailed> {
localMapper.decode<EllipticCurve>(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<SessionError.DecodingFailed> {
assertThrows<TangemSdkError.DecodingFailed> {
localMapper.decode<Int>(TlvTag.SignedHashes)
}
}

View file

@ -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"
}

View file

@ -6,8 +6,8 @@ import android.widget.CompoundButton
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.tangem.SessionEnvironment
import com.tangem.SessionError
import com.tangem.TangemSdk
import com.tangem.TangemSdkError
import com.tangem.common.CompletionResult
import com.tangem.tangem_sdk_new.extensions.init
import kotlinx.android.synthetic.main.activity_test_user_data.*
@ -92,8 +92,8 @@ class TestUserDataActivity : AppCompatActivity() {
}
}
private fun handleError(tv: TextView, error: SessionError) {
if (error is SessionError.UserCancelled) return
private fun handleError(tv: TextView, error: TangemSdkError) {
if (error is TangemSdkError.UserCancelled) return
runOnUiThread { tv.text = error::class.simpleName }
}

View file

@ -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))
}
}

View file

@ -3,8 +3,9 @@ package com.tangem.devkit.ucase.ui
import android.view.View
import androidx.annotation.UiThread
import androidx.lifecycle.*
import com.tangem.SessionError
import com.google.gson.Gson
import com.tangem.TangemSdk
import com.tangem.TangemSdkError
import com.tangem.commands.Card
import com.tangem.commands.CommandResponse
import com.tangem.common.CompletionResult
@ -131,7 +132,7 @@ class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif
internal class Notifier(private val vm: ActionViewModel) {
private var notShowedError: SessionError? = null
private var notShowedError: TangemSdkError? = null
fun handleActionResult(result: CompletionResult<*>, list: List<Item>) {
if (list.isNotEmpty()) notifyItemsChanged(list)
@ -160,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 {

View file

@ -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<T>` this is the sealed class for the results of `CardSessionRunnable`.
`Success<T>(val data: T)` is triggered after successful operation and contains a `CommandResponse`.
`Failure<T>(val error: SessionError)` is triggered on error.
`Failure<T>(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<ByteArray>) : 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<ByteArray>) : 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)

View file

@ -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)))

View file

@ -34,9 +34,9 @@
android:toDegrees="360" />
<gradient
android:centerColor="@color/colorAccent"
android:endColor="@color/colorAccent"
android:startColor="@color/colorAccent"
android:centerColor="@color/card_sdk_accent"
android:endColor="@color/card_sdk_accent"
android:startColor="@color/card_sdk_accent"
android:type="sweep" />
</shape>

View file

@ -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"

View file

@ -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"/>
<TextView
android:id="@+id/tvCardId"
@ -47,11 +48,12 @@
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textAllCaps="true"
android:textColor="@color/colorAccent"
android:textColor="@color/card_sdk_accent"
android:textSize="15sp"
android:textStyle="normal"
tools:text="cb22000000027374"
android:visibility="gone"/>
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout>
@ -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" />
<include
@ -85,7 +88,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="sans-serif-light"
android:textColor="@color/colorAccent"
android:textColor="@color/card_sdk_accent"
android:textSize="60dp"
android:textStyle="normal" />
@ -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" />
<ProgressBar
@ -166,8 +170,9 @@
android:gravity="center"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:textColor="@color/card_sdk_text_color"
android:text="@string/dialog_scan_text"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
/>
</LinearLayout>

View file

@ -3,6 +3,9 @@
<color name="colorPrimary">#027aff</color>
<color name="colorPrimaryDark">#027AFF</color>
<color name="colorAccent">#027AFF</color>
<color name="fab">#4f98c0</color>
<color name="card_sdk_accent">#266dd3</color>
<color name="card_sdk_text_color">#000000</color>
<color name="card_sdk_ripple">#2c5ac4</color>
</resources>