diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadCardCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt similarity index 53% rename from tangem-core/src/main/java/com/tangem/commands/ReadCardCommand.kt rename to tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt index e7309a33f1..f4eb833de6 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadCardCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt @@ -8,10 +8,12 @@ import com.tangem.common.extentions.calculateSha256 import com.tangem.common.tlv.Tlv import com.tangem.common.tlv.TlvMapper import com.tangem.common.tlv.TlvTag -import com.tangem.data.SettingsMask import com.tangem.tasks.TaskError import java.util.* +/** + * Determines which type of data is required for signing. + */ enum class SigningMethod(val code: Int) { SignHash(0), SignRaw(1), @@ -22,19 +24,27 @@ enum class SigningMethod(val code: Int) { SignPos(6); companion object { - fun byCode(code: Int): SigningMethod? = values().find { it.code == code } + private val values = values() + fun byCode(code: Int): SigningMethod? = values.find { it.code == code } } } +/** + * Elliptic curve used for wallet key operations. + */ enum class EllipticCurve(val curve: String) { Secp256k1("secp256k1"), Ed25519("ed25519"); companion object { - fun byName(curve: String): EllipticCurve? = values().find { it.curve == curve } + private val values = values() + fun byName(curve: String): EllipticCurve? = values.find { it.curve == curve } } } +/** + * Status of the card and its wallet. + */ enum class CardStatus(val code: Int) { NotPersonalized(0), Empty(1), @@ -42,7 +52,8 @@ enum class CardStatus(val code: Int) { Purged(3); companion object { - fun byCode(code: Int): CardStatus? = values().find { it.code == code } + private val values = values() + fun byCode(code: Int): CardStatus? = values.find { it.code == code } } } @@ -52,10 +63,67 @@ enum class ProductMask(val code: Byte) { Card(0x04); companion object { - fun byCode(code: Byte): ProductMask? = values().find { it.code == code } + private val values = values() + fun byCode(code: Byte): ProductMask? = values.find { it.code == code } } } +/** + * Stores and maps Tangem card settings. + * + * @property rawValue are card settings in a form of flags, + * while flags definitions and values are in [SettingsMask.Companion] as constants. + */ +data class SettingsMask(val rawValue: Int) { + + companion object { + const val isReusable = 0x0001 + const val useActivation = 0x0002 + const val forbidPurgeWallet = 0x0004 + const val useBlock = 0x0008 + + const val allowSwapPIN = 0x0010 + const val allowSwapPIN2 = 0x0020 + const val useCVC = 0x0040 + const val forbidDefaultPIN = 0x0080 + + const val useOneCommandAtTime = 0x0100 + const val useNdef = 0x0200 + const val useDynamicNdef = 0x0400 + const val smartSecurityDelay = 0x0800 + + const val protocolAllowUnencrypted = 0x1000 + const val protocolAllowStaticEncryption = 0x2000 + + const val protectIssuerDataAgainstReplay = 0x4000 + + const val allowSelectBlockchain = 0x8000 + + const val disablePrecomputedNdef = 0x00010000 + + const val skipSecurityDelayIfValidatedByLinkedTerminal = 0x00080000 + } +} + +/** + * Detailed information about card contents. + */ +class CardData( + val batchId: String?, + val manufactureDateTime: Date?, + val issuerName: String?, + val blockchainName: String?, + val manufacturerSignature: ByteArray?, + val productMask: ProductMask?, + + val tokenSymbol: String?, + val tokenContractAddress: String?, + val tokenDecimal: Int? +) + +/** + * Response for [ReadCommand]. Contains detailed card information. + */ class Card( val cardId: String, val manufacturerName: String, @@ -78,35 +146,30 @@ class Card( val paymentFlowVersion: ByteArray?, val userCounter: Int?, val terminalIsLinked: Boolean, - - //Card Data - val batchId: String?, - val manufactureDateTime: Date?, - val issuerName: String?, - val blockchainName: String?, - val manufacturerSignature: ByteArray?, - val productMask: ProductMask?, - - val tokenSymbol: String?, - val tokenContractAddress: String?, - val tokenDecimal: Int? + val cardData: CardData? ) : CommandResponse - -class ReadCardCommand : CommandSerializer() { - - override val instruction = Instruction.Read - override val instructionCode = instruction.code +/** + * This command receives from the Tangem Card all the data about the card and the wallet, + * including unique card number (CID or cardId) that has to be submitted while calling all other commands. + * + */ +class ReadCommand : CommandSerializer() { override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { - + /** + * [CardEnvironment] stores the pin1 value. If no pin1 value was set, it will contain + * default value of ‘000000’. + * In order to obtain card’s data, [ReadCommand] should use the correct pin 1 value. + * The card will not respond if wrong pin 1 has been submitted. + */ val tlvData = mutableListOf(Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256())) - cardEnvironment.terminalKeys?.let { - Tlv(TlvTag.TerminalPublicKey, it.publicKey) + cardEnvironment.terminalKeys?.let { terminalKeys -> + Tlv(TlvTag.TerminalPublicKey, terminalKeys.publicKey) } - return CommandApdu(instructionCode, tlvData) + return CommandApdu(Instruction.Read, tlvData) } override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): Card? { @@ -138,21 +201,31 @@ class ReadCardCommand : CommandSerializer() { userCounter = tlvMapper.mapOptional(TlvTag.UserCounter), terminalIsLinked = tlvMapper.map(TlvTag.TerminalIsLinked), - batchId = tlvMapper.mapOptional(TlvTag.Batch), - manufactureDateTime = tlvMapper.mapOptional(TlvTag.ManufactureDateTime), - issuerName = tlvMapper.mapOptional(TlvTag.IssuerId), - blockchainName = tlvMapper.mapOptional(TlvTag.BlockchainId), - manufacturerSignature = tlvMapper.mapOptional(TlvTag.ManufacturerSignature), - productMask = tlvMapper.mapOptional(TlvTag.ProductMask), - - tokenSymbol = tlvMapper.mapOptional(TlvTag.TokenSymbol), - tokenContractAddress = tlvMapper.mapOptional(TlvTag.TokenContractAddress), - tokenDecimal = tlvMapper.mapOptional(TlvTag.TokenDecimal) + cardData = deserializeCardData(tlvData) ) } catch (exception: Exception) { throw TaskError.SerializeCommandError() } } + private fun deserializeCardData(tlvData: List): CardData? { + val cardDataTlvs = tlvData.find { it.tag == TlvTag.CardData }?.let { + Tlv.tlvListFromBytes(it.value) + } + if (cardDataTlvs.isNullOrEmpty()) return null + val tlvMapper = TlvMapper(cardDataTlvs) + return CardData( + batchId = tlvMapper.mapOptional(TlvTag.Batch), + manufactureDateTime = tlvMapper.mapOptional(TlvTag.ManufactureDateTime), + issuerName = tlvMapper.mapOptional(TlvTag.IssuerId), + blockchainName = tlvMapper.mapOptional(TlvTag.BlockchainId), + manufacturerSignature = tlvMapper.mapOptional(TlvTag.ManufacturerSignature), + productMask = tlvMapper.mapOptional(TlvTag.ProductMask), + + tokenSymbol = tlvMapper.mapOptional(TlvTag.TokenSymbol), + tokenContractAddress = tlvMapper.mapOptional(TlvTag.TokenContractAddress), + tokenDecimal = tlvMapper.mapOptional(TlvTag.TokenDecimal) + ) + } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/common/apdu/ResponseApdu.kt b/tangem-core/src/main/java/com/tangem/common/apdu/ResponseApdu.kt index 8d65b0422f..9eecdc4448 100644 --- a/tangem-core/src/main/java/com/tangem/common/apdu/ResponseApdu.kt +++ b/tangem-core/src/main/java/com/tangem/common/apdu/ResponseApdu.kt @@ -12,22 +12,13 @@ class ResponseApdu(val data: ByteArray) { val statusWord: StatusWord = StatusWord.byCode(sw) fun getTlvData(encryptionKey: ByteArray? = null): List? { - val tlvs = when { + return when { data.size < 2 -> null data.size == 2 -> emptyList() - else -> Tlv.fromBytes(data.copyOf(data.size - 2)) + else -> Tlv.tlvListFromBytes(data.copyOf(data.size - 2)) } - return flattenNestedTlvs(tlvs) } - private fun flattenNestedTlvs(tlvs: List?): List? = - tlvs?.flatMap { - if (it.tag.hasNestedTlv()) { - Tlv.fromBytes(it.value) - } else { - listOf(it) - } - } private fun decrypt(encryptionKey: ByteArray) { TODO("not implemented") diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt b/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt index 2f58f5d3e8..a22dad60c1 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt @@ -49,7 +49,7 @@ class Tlv { } - fun fromBytes(mData: ByteArray): List { + fun tlvListFromBytes(mData: ByteArray): List { val tlvList = mutableListOf() val stream = ByteArrayInputStream(mData) var tlv: Tlv? = null