Updated on 2026-08-14
This commit is contained in:
parent
1ba5e6299e
commit
b7f323fd14
17 changed files with 192 additions and 107 deletions
|
|
@ -1,12 +1,13 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.TlvBuilder
|
||||
import com.tangem.common.tlv.TlvMapper
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.tasks.TaskError
|
||||
|
|
@ -38,12 +39,11 @@ class CheckWalletCommand(
|
|||
) : CommandSerializer<CheckWalletResponse>() {
|
||||
|
||||
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
|
||||
val tlvData = listOf(
|
||||
Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256()),
|
||||
Tlv(TlvTag.CardId, cardId.hexToBytes()),
|
||||
Tlv(TlvTag.Challenge, challenge)
|
||||
)
|
||||
return CommandApdu(Instruction.CheckWallet, tlvData)
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1)
|
||||
tlvBuilder.append(TlvTag.CardId, cardId)
|
||||
tlvBuilder.append(TlvTag.Challenge, challenge)
|
||||
return CommandApdu(Instruction.CheckWallet, tlvBuilder.serialize())
|
||||
}
|
||||
|
||||
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): CheckWalletResponse? {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.extensions.toInt
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.TlvBuilder
|
||||
import com.tangem.common.tlv.TlvMapper
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.tasks.TaskError
|
||||
|
|
@ -42,16 +43,12 @@ class CreateWalletCommand(
|
|||
) : CommandSerializer<CreateWalletResponse>() {
|
||||
|
||||
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
|
||||
val tlvData = mutableListOf(
|
||||
Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256()),
|
||||
Tlv(TlvTag.CardId, cardId.hexToBytes()),
|
||||
Tlv(TlvTag.Pin2, cardEnvironment.pin2.calculateSha256())
|
||||
)
|
||||
if (cardEnvironment.cvc != null) {
|
||||
tlvData.add(Tlv(TlvTag.Cvc, cardEnvironment.cvc))
|
||||
}
|
||||
|
||||
return CommandApdu(Instruction.CreateWallet, tlvData)
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1)
|
||||
tlvBuilder.append(TlvTag.CardId, cardId)
|
||||
tlvBuilder.append(TlvTag.Pin2, cardEnvironment.pin2)
|
||||
tlvBuilder.append(TlvTag.Cvc, cardEnvironment.cvc)
|
||||
return CommandApdu(Instruction.CreateWallet, tlvBuilder.serialize())
|
||||
}
|
||||
|
||||
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): CreateWalletResponse? {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.TlvBuilder
|
||||
import com.tangem.common.tlv.TlvMapper
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.tasks.TaskError
|
||||
|
|
@ -23,7 +24,7 @@ class PurgeWalletResponse(
|
|||
) : CommandResponse
|
||||
|
||||
/**
|
||||
* This command deletes all wallet data. If Is_Reusable flag is enabled during personalization,
|
||||
* This command deletes all wallet data. If Is_Reusable flag is enabled during personalization,
|
||||
|
||||
* If Is_Reusable flag is disabled, the card switches to ‘Purged’ state.
|
||||
* ‘Purged’ state is final, it makes the card useless.
|
||||
|
|
@ -34,12 +35,11 @@ class PurgeWalletCommand(
|
|||
) : CommandSerializer<PurgeWalletResponse>() {
|
||||
|
||||
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
|
||||
val tlvData = mutableListOf(
|
||||
Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256()),
|
||||
Tlv(TlvTag.CardId, cardId.hexToBytes()),
|
||||
Tlv(TlvTag.Pin2, cardEnvironment.pin2.calculateSha256())
|
||||
)
|
||||
return CommandApdu(Instruction.PurgeWallet, tlvData)
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1)
|
||||
tlvBuilder.append(TlvTag.CardId, cardId)
|
||||
tlvBuilder.append(TlvTag.Pin2, cardEnvironment.pin2)
|
||||
return CommandApdu(Instruction.PurgeWallet, tlvBuilder.serialize())
|
||||
}
|
||||
|
||||
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): PurgeWalletResponse? {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.TlvBuilder
|
||||
import com.tangem.common.tlv.TlvMapper
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.tasks.TaskError
|
||||
|
|
@ -296,19 +297,16 @@ class Card(
|
|||
class ReadCommand : CommandSerializer<Card>() {
|
||||
|
||||
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
|
||||
val tlvBuilder = TlvBuilder()
|
||||
/**
|
||||
* [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 { terminalKeys ->
|
||||
Tlv(TlvTag.TerminalPublicKey, terminalKeys.publicKey)
|
||||
}
|
||||
|
||||
return CommandApdu(Instruction.Read, tlvData)
|
||||
tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1)
|
||||
tlvBuilder.append(TlvTag.TerminalPublicKey, cardEnvironment.terminalKeys?.publicKey)
|
||||
return CommandApdu(Instruction.Read, tlvBuilder.serialize())
|
||||
}
|
||||
|
||||
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): Card? {
|
||||
|
|
@ -349,7 +347,7 @@ class ReadCommand : CommandSerializer<Card>() {
|
|||
|
||||
private fun deserializeCardData(tlvData: List<Tlv>): CardData? {
|
||||
val cardDataTlvs = tlvData.find { it.tag == TlvTag.CardData }?.let {
|
||||
Tlv.tlvListFromBytes(it.value)
|
||||
Tlv.deserialize(it.value)
|
||||
}
|
||||
if (cardDataTlvs.isNullOrEmpty()) return null
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.TlvBuilder
|
||||
import com.tangem.common.tlv.TlvMapper
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.tasks.TaskError
|
||||
|
|
@ -54,11 +55,10 @@ class ReadIssuerDataCommand(
|
|||
) : CommandSerializer<ReadIssuerDataResponse>() {
|
||||
|
||||
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
|
||||
val tlvData = listOf(
|
||||
Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256()),
|
||||
Tlv(TlvTag.CardId, cardId.hexToBytes())
|
||||
)
|
||||
return CommandApdu(Instruction.ReadIssuerData, tlvData)
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1)
|
||||
tlvBuilder.append(TlvTag.CardId, cardId)
|
||||
return CommandApdu(Instruction.ReadIssuerData, tlvBuilder.serialize())
|
||||
}
|
||||
|
||||
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): ReadIssuerDataResponse? {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.TlvBuilder
|
||||
import com.tangem.common.tlv.TlvMapper
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.crypto.sign
|
||||
|
|
@ -50,17 +51,16 @@ class SignCommand(private val hashes: Array<ByteArray>, private val cardId: Stri
|
|||
}
|
||||
|
||||
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
|
||||
val tlvData = mutableListOf(
|
||||
Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256()),
|
||||
Tlv(TlvTag.Pin2, cardEnvironment.pin2.calculateSha256()),
|
||||
Tlv(TlvTag.CardId, cardId.hexToBytes()),
|
||||
Tlv(TlvTag.TransactionOutHashSize, byteArrayOf(hashSizes.toByte())),
|
||||
Tlv(TlvTag.TransactionOutHash, dataToSign)
|
||||
)
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1)
|
||||
tlvBuilder.append(TlvTag.Pin2, cardEnvironment.pin2)
|
||||
tlvBuilder.append(TlvTag.CardId, cardId)
|
||||
tlvBuilder.append(TlvTag.TransactionOutHashSize, byteArrayOf(hashSizes.toByte()))
|
||||
tlvBuilder.append(TlvTag.TransactionOutHash, dataToSign)
|
||||
tlvBuilder.append(TlvTag.Cvc, cardEnvironment.cvc)
|
||||
|
||||
addTerminalSignature(cardEnvironment, tlvData)
|
||||
|
||||
return CommandApdu(Instruction.Sign, tlvData)
|
||||
addTerminalSignature(cardEnvironment, tlvBuilder)
|
||||
return CommandApdu(Instruction.Sign, tlvBuilder.serialize())
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -70,11 +70,11 @@ class SignCommand(private val hashes: Array<ByteArray>, private val cardId: Stri
|
|||
* TerminalTransactionSignature parameter containing a correct signature of raw data to be signed made with TerminalPrivateKey
|
||||
* (this key should be generated and securily stored by the application).
|
||||
*/
|
||||
private fun addTerminalSignature(cardEnvironment: CardEnvironment, tlvData: MutableList<Tlv>) {
|
||||
private fun addTerminalSignature(cardEnvironment: CardEnvironment, tlvBuilder: TlvBuilder) {
|
||||
cardEnvironment.terminalKeys?.let { terminalKeyPair ->
|
||||
val signedData = dataToSign.sign(terminalKeyPair.privateKey)
|
||||
tlvData.add(Tlv(TlvTag.TerminalTransactionSignature, signedData))
|
||||
tlvData.add(Tlv(TlvTag.TerminalPublicKey, terminalKeyPair.publicKey))
|
||||
tlvBuilder.append(TlvTag.TerminalTransactionSignature, signedData)
|
||||
tlvBuilder.append(TlvTag.TerminalPublicKey, terminalKeyPair.publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.common.extensions.calculateSha256
|
|||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toByteArray
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.TlvBuilder
|
||||
import com.tangem.common.tlv.TlvMapper
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.tasks.TaskError
|
||||
|
|
@ -37,17 +38,14 @@ class WriteIssuerDataCommand(
|
|||
) : CommandSerializer<WriteIssuerDataResponse>() {
|
||||
|
||||
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
|
||||
val tlvData = mutableListOf(
|
||||
Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256()),
|
||||
Tlv(TlvTag.CardId, cardId.hexToBytes()),
|
||||
Tlv(TlvTag.IssuerData, issuerData),
|
||||
Tlv(TlvTag.IssuerDataSignature, issuerDataSignature)
|
||||
)
|
||||
if (issuerDataCounter != null) {
|
||||
tlvData.add(Tlv(TlvTag.IssuerDataCounter, issuerDataCounter.toByteArray()))
|
||||
}
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1)
|
||||
tlvBuilder.append(TlvTag.CardId, cardId)
|
||||
tlvBuilder.append(TlvTag.IssuerData, issuerData)
|
||||
tlvBuilder.append(TlvTag.IssuerDataSignature, issuerDataSignature)
|
||||
tlvBuilder.append(TlvTag.IssuerDataCounter, issuerDataCounter)
|
||||
|
||||
return CommandApdu(Instruction.WriteIssuerData, tlvData)
|
||||
return CommandApdu(Instruction.WriteIssuerData, tlvBuilder.serialize())
|
||||
}
|
||||
|
||||
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): WriteIssuerDataResponse? {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.common.apdu
|
||||
|
||||
import com.tangem.EncryptionMode
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.toBytes
|
||||
import com.tangem.common.EncryptionMode
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
/**
|
||||
|
|
@ -15,7 +13,7 @@ import java.io.ByteArrayOutputStream
|
|||
class CommandApdu(
|
||||
|
||||
private val ins: Int,
|
||||
private val tlvList: List<Tlv>,
|
||||
private val tlvs: ByteArray,
|
||||
|
||||
private val cla: Byte = ISO_CLA,
|
||||
private val p1: Byte = 0x00,
|
||||
|
|
@ -28,12 +26,12 @@ class CommandApdu(
|
|||
|
||||
constructor(
|
||||
instruction: Instruction,
|
||||
tlvList: List<Tlv>,
|
||||
tlvs: ByteArray,
|
||||
encryptionMode: EncryptionMode = EncryptionMode.NONE,
|
||||
encryptionKey: ByteArray? = null
|
||||
) : this(
|
||||
instruction.code,
|
||||
tlvList,
|
||||
tlvs,
|
||||
encryptionMode = encryptionMode,
|
||||
encryptionKey = encryptionKey
|
||||
)
|
||||
|
|
@ -51,13 +49,7 @@ class CommandApdu(
|
|||
|
||||
private fun toBytes(): ByteArray {
|
||||
|
||||
val data = if (tlvList.isNotEmpty()) {
|
||||
tlvList.toBytes()
|
||||
} else {
|
||||
byteArrayOf()
|
||||
}
|
||||
|
||||
val lc = data.size
|
||||
val lc = tlvs.size
|
||||
|
||||
val byteStream = ByteArrayOutputStream()
|
||||
byteStream.write(cla.toInt())
|
||||
|
|
@ -66,7 +58,7 @@ class CommandApdu(
|
|||
byteStream.write(p2.toInt())
|
||||
if (lc != 0) {
|
||||
writeLength(byteStream, lc)
|
||||
byteStream.write(data)
|
||||
byteStream.write(tlvs)
|
||||
}
|
||||
return byteStream.toByteArray()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class ResponseApdu(private val data: ByteArray) {
|
|||
fun getTlvData(encryptionKey: ByteArray? = null): List<Tlv>? {
|
||||
return when {
|
||||
data.size <= 2 -> null
|
||||
else -> Tlv.tlvListFromBytes(data.copyOf(data.size - 2))
|
||||
else -> Tlv.deserialize(data.copyOf(data.size - 2))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,13 +53,13 @@ class Tlv {
|
|||
}
|
||||
|
||||
|
||||
fun tlvListFromBytes(mData: ByteArray): List<Tlv>? {
|
||||
fun deserialize(mData: ByteArray): List<Tlv>? {
|
||||
val tlvList = mutableListOf<Tlv>()
|
||||
val stream = ByteArrayInputStream(mData)
|
||||
var tlv: Tlv? = null
|
||||
var tlv: Tlv?
|
||||
do {
|
||||
try {
|
||||
tlv = Tlv.tlvFromBytes(stream)
|
||||
tlv = tlvFromBytes(stream)
|
||||
if (tlv != null) tlvList.add(tlv)
|
||||
} catch (e: IOException) {
|
||||
Log.e(this::class.java.simpleName,"TLVError: " + e.message)
|
||||
|
|
@ -73,10 +73,10 @@ class Tlv {
|
|||
|
||||
}
|
||||
|
||||
fun List<Tlv>.toBytes(): ByteArray =
|
||||
this.map { it.toBytes() }.reduce { arr1, arr2 -> arr1 + arr2 }
|
||||
fun List<Tlv>.serialize(): ByteArray =
|
||||
this.map { it.serialize() }.reduce { arr1, arr2 -> arr1 + arr2 }
|
||||
|
||||
fun Tlv.toBytes(): ByteArray {
|
||||
fun Tlv.serialize(): ByteArray {
|
||||
val tag = byteArrayOf(this.tag.code.toByte())
|
||||
val length = getLengthInBytes(this.value.size)
|
||||
val value = if (this.value.isNotEmpty()) this.value else byteArrayOf(0x00)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.common.tlv
|
||||
|
||||
class TlvBuilder {
|
||||
private val tlvs = mutableListOf<Tlv>()
|
||||
private val encoder = TlvEncoder()
|
||||
|
||||
internal inline fun <reified T> append(tag: TlvTag, value: T?) {
|
||||
if (value == null) return
|
||||
|
||||
tlvs.add(encoder.encode(tag, value))
|
||||
}
|
||||
|
||||
fun serialize(): ByteArray = tlvs.serialize()
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package com.tangem.common.tlv
|
||||
|
||||
import com.tangem.commands.*
|
||||
import com.tangem.common.extensions.*
|
||||
import com.tangem.tasks.TaskError
|
||||
import java.time.Year
|
||||
import java.util.*
|
||||
|
||||
class TlvEncoder {
|
||||
internal inline fun <reified T> encode(tag: TlvTag, value: T?): Tlv {
|
||||
if (value != null) {
|
||||
return Tlv(tag, encodeValue(value, tag))
|
||||
} else {
|
||||
throw TaskError.SerializeCommandError("Encoding error. Value for tag $tag is null")
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T> encodeValue(value: T, tag: TlvTag): ByteArray {
|
||||
return when (tag.valueType()) {
|
||||
TlvValueType.HexString -> {
|
||||
typeCheck<T, String>(tag)
|
||||
return if (tag == TlvTag.Pin || tag == TlvTag.Pin2) {
|
||||
(value as String).calculateSha256()
|
||||
} else {
|
||||
(value as String).hexToBytes()
|
||||
}
|
||||
}
|
||||
TlvValueType.Utf8String -> {
|
||||
typeCheck<T, String>(tag)
|
||||
(value as String).toByteArray()
|
||||
}
|
||||
TlvValueType.IntValue -> {
|
||||
typeCheck<T, Int>(tag)
|
||||
(value as Int).toByteArray()
|
||||
}
|
||||
TlvValueType.BoolValue -> {
|
||||
typeCheck<T, Boolean>(tag)
|
||||
throw ConversionException("Usopported operation: Boolean to ByteArray for tag $tag")
|
||||
}
|
||||
TlvValueType.ByteArray -> {
|
||||
typeCheck<T, ByteArray>(tag)
|
||||
value as ByteArray
|
||||
}
|
||||
TlvValueType.EllipticCurve -> {
|
||||
typeCheck<T, EllipticCurve>(tag)
|
||||
(value as EllipticCurve).curve.plus("\\0").toByteArray()
|
||||
}
|
||||
TlvValueType.DateTime -> {
|
||||
typeCheck<T, Date>(tag)
|
||||
val calendar = Calendar.getInstance().apply { time = (value as Date) }
|
||||
val year = calendar.get(Calendar.YEAR)
|
||||
val month = calendar.get(Calendar.MONTH) + 1
|
||||
val day = calendar.get(Calendar.DAY_OF_MONTH)
|
||||
return year.toByteArray() + month.toByteArray() + day.toByteArray()
|
||||
}
|
||||
TlvValueType.ProductMask -> {
|
||||
typeCheck<T, ProductMask>(tag)
|
||||
byteArrayOf(
|
||||
(value as ProductMask).code
|
||||
)
|
||||
}
|
||||
TlvValueType.SettingsMask -> {
|
||||
typeCheck<T, SettingsMask>(tag)
|
||||
(value as SettingsMask).rawValue.toByteArray()
|
||||
}
|
||||
TlvValueType.CardStatus -> {
|
||||
typeCheck<T, CardStatus>(tag)
|
||||
(value as CardStatus).code.toByteArray()
|
||||
}
|
||||
TlvValueType.SigningMethod -> {
|
||||
typeCheck<T, SigningMethod>(tag)
|
||||
(value as SigningMethod).rawValue.toByteArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T, reified ExpectedT> typeCheck(tag: TlvTag) {
|
||||
if (T::class != ExpectedT::class)
|
||||
throw WrongTypeException("Mapping error. Type for tag: $tag must be ${tag.valueType()}. It is ${T::class}")
|
||||
}
|
||||
}
|
||||
|
|
@ -108,7 +108,7 @@ enum class TlvTag(val code: Int) {
|
|||
*/
|
||||
fun valueType(): TlvValueType {
|
||||
return when (this) {
|
||||
CardId, Pin, Batch -> TlvValueType.HexString
|
||||
CardId, Pin, Pin2, Batch -> TlvValueType.HexString
|
||||
ManufactureId, Firmware, IssuerId, BlockchainId, TokenSymbol, TokenContractAddress ->
|
||||
TlvValueType.Utf8String
|
||||
CurveId -> TlvValueType.EllipticCurve
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ package com.tangem.common.apdu
|
|||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.TlvBuilder
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import org.junit.Test
|
||||
|
||||
|
|
@ -11,12 +13,13 @@ class CommandApduTest {
|
|||
|
||||
@Test
|
||||
fun `simple READ command to bytes`() {
|
||||
val pinInBytes = byteArrayOf(-111, -76, -47, 66, -126, 63, 125, 32, -59, -16, -115, -10, -111,
|
||||
34, -34, 67, -13, 95, 5, 122, -104, -115, -106, 25, -10, -45, 19, -124, -123, -55, -94, 3)
|
||||
val pin = "000000"
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.Pin, pin)
|
||||
val commandApdu = CommandApdu(
|
||||
Instruction.Read,
|
||||
mutableListOf(Tlv(TlvTag.Pin, pinInBytes)))
|
||||
|
||||
tlvBuilder.serialize()
|
||||
)
|
||||
val expected = byteArrayOf(0, -14, 0, 0, 0, 0, 34, 16, 32, -111, -76, -47, 66, -126, 63, 125,
|
||||
32, -59, -16, -115, -10, -111, 34, -34, 67, -13, 95, 5, 122, -104, -115, -106, 25, -10,
|
||||
-45, 19, -124, -123, -55, -94, 3)
|
||||
|
|
@ -27,17 +30,18 @@ class CommandApduTest {
|
|||
|
||||
@Test
|
||||
fun `READ with terminal key to bytes`() {
|
||||
val pinInBytes = byteArrayOf(-111, -76, -47, 66, -126, 63, 125, 32, -59, -16, -115, -10, -111,
|
||||
34, -34, 67, -13, 95, 5, 122, -104, -115, -106, 25, -10, -45, 19, -124, -123, -55, -94, 3)
|
||||
val pin = "000000"
|
||||
val terminalPublicKey = byteArrayOf(4, 80, -122, 58, -42, 74, -121, -82, -118, 47, -24, 60,
|
||||
26, -15, -88, 64, 60, -75, 63, 83, -28, -122, -40, 81, 29, -83, -118, 4, -120, 126,
|
||||
91, 35, 82, 44, -44, 112, 36, 52, 83, -94, -103, -6, -98, 119, 35, 119, 22, 16, 58,
|
||||
-68, 17, -95, -33, 56, -123, 94, -42, -14, -18, 24, 126, -100, 88, 43, -90)
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.Pin, pin)
|
||||
tlvBuilder.append(TlvTag.TerminalPublicKey, terminalPublicKey)
|
||||
val commandApdu = CommandApdu(
|
||||
Instruction.Read,
|
||||
mutableListOf(
|
||||
Tlv(TlvTag.Pin, pinInBytes),
|
||||
Tlv(TlvTag.TerminalPublicKey, terminalPublicKey)))
|
||||
tlvBuilder.serialize()
|
||||
)
|
||||
|
||||
val expected = byteArrayOf(0, -14, 0, 0, 0, 0, 101, 16, 32, -111, -76, -47, 66, -126, 63,
|
||||
125, 32, -59, -16, -115, -10, -111, 34, -34, 67, -13, 95, 5, 122, -104, -115, -106, 25,
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ class TlvMapperTest {
|
|||
|
||||
private val rawData = byteArrayOf(1, 8, -53, 34, 0, 0, 0, 2, 115, 116, 32, 11, 83, 77, 65, 82, 84, 32, 67, 65, 83, 72, 0, 2, 1, 2, -128, 6, 50, 46, 49, 49, 114, 0, 3, 65, 4, -49, 11, -50, -66, -121, -25, -2, 65, 65, -13, 14, 49, 27, -82, -33, -85, -113, 65, 20, 8, -39, -75, 57, 45, 65, -31, 35, 44, 38, 40, 63, -44, 113, -45, -75, -95, -118, 118, 29, 65, 117, -24, -53, 82, -72, 91, -20, -96, -77, -103, -14, -63, 52, -127, -123, -27, -16, -128, -67, -3, -104, -26, -22, 65, 10, 4, 0, 0, 126, 33, 12, 90, -127, 2, 0, 41, -126, 4, 7, -29, 5, 2, -125, 7, 84, 65, 78, 71, 69, 77, 0, -124, 3, 69, 84, 72, -122, 64, 111, -103, 48, -114, -40, 18, -103, 26, -102, -12, -38, -78, -90, -9, -98, 88, -47, -100, -24, 24, -105, -70, -72, 6, 94, -96, -77, 11, -123, -28, -118, 37, 63, 107, -55, -11, 23, -12, 13, -23, -121, -63, 36, -59, 70, 116, 91, -125, -34, -69, 23, -112, 6, 17, 4, -49, 68, -56, 29, -45, 81, 10, 97, 83, 48, 65, 4, -127, -106, -86, 75, 65, 10, -60, 74, 59, -100, -50, 24, -25, -66, 34, 106, -22, 7, 10, -52, -125, -87, -49, 103, 84, 15, -84, 73, -81, 37, 18, -97, 106, 83, -118, 40, -83, 99, 65, 53, -114, 60, 79, -103, 99, 6, 79, 126, 54, 83, 114, -90, 81, -45, 116, -27, -62, 60, -35, 55, -3, 9, -101, -14, 5, 10, 115, 101, 99, 112, 50, 53, 54, 107, 49, 0, 8, 4, 0, 15, 66, 64, 7, 1, 0, 9, 2, 11, -72, 96, 65, 4, -42, -5, -41, -84, -23, 88, 2, 86, -63, -118, -123, -10, -66, -82, -107, -68, -93, 111, 47, 93, -20, -86, 74, 28, 21, 81, 93, -21, -124, -57, -102, 55, 17, 84, -66, -68, -22, -128, 126, -99, -65, -54, -42, 59, -25, -21, -124, 5, 59, -16, -72, 73, 48, 16, -27, 103, -112, -73, 2, 96, -51, 41, -42, 116, 98, 4, 0, 15, 66, 52, 99, 4, 0, 0, 0, 13, 15, 1, 0)
|
||||
|
||||
private val tlvData = Tlv.tlvListFromBytes(rawData)
|
||||
private val tlvData = Tlv.deserialize(rawData)
|
||||
|
||||
private val tlvMapper = TlvMapper(tlvData!!)
|
||||
|
||||
private val cardDataRaw: ByteArray = tlvMapper.map(TlvTag.CardData)
|
||||
private val cardDataMapper = TlvMapper(Tlv.tlvListFromBytes(cardDataRaw)!!)
|
||||
private val cardDataMapper = TlvMapper(Tlv.deserialize(cardDataRaw)!!)
|
||||
|
||||
@Test
|
||||
fun `map optional when value is present`() {
|
||||
|
|
@ -88,7 +88,7 @@ class TlvMapperTest {
|
|||
|
||||
@Test
|
||||
fun `map SigningMethods set of methods returns correct value`() {
|
||||
val localMapper = TlvMapper(Tlv.tlvListFromBytes("070195".hexToBytes())!!)
|
||||
val localMapper = TlvMapper(Tlv.deserialize("070195".hexToBytes())!!)
|
||||
|
||||
val signingMethod: SigningMethod = localMapper.map(TlvTag.SigningMethod)
|
||||
assertThat(signingMethod.contains(SigningMethod.signHash))
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class TlvTest {
|
|||
-10, -111, 34, -34, 67, -13, 95, 5, 122, -104, -115, -106, 25, -10, -45, 19, -124,
|
||||
-123, -55, -94, 3)
|
||||
|
||||
assertThat(tlvs.toBytes())
|
||||
assertThat(tlvs.serialize())
|
||||
.isEqualTo(expected)
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ class TlvTest {
|
|||
-55, -94, 3, 1, 8, -53, 34, 0, 0, 0, 2, 115, 116, 22, 16, -82, -78, -31, 34, 66, -19,
|
||||
-86, -1, 26, 8, 100, -126, -74, 20, -28, 83)
|
||||
|
||||
assertThat(tlvs.toBytes())
|
||||
assertThat(tlvs.serialize())
|
||||
.isEqualTo(expected)
|
||||
}
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ class TlvTest {
|
|||
-10, -111, 34, -34, 67, -13, 95, 5, 122, -104, -115, -106, 25, -10, -45, 19, -124,
|
||||
-123, -55, -94, 3)
|
||||
|
||||
val tlvs = Tlv.tlvListFromBytes(bytes)
|
||||
val tlvs = Tlv.deserialize(bytes)
|
||||
|
||||
assertThat(tlvs)
|
||||
.isNotNull()
|
||||
|
|
@ -65,7 +65,7 @@ class TlvTest {
|
|||
-55, -94, 3, 1, 8, -53, 34, 0, 0, 0, 2, 115, 116, 22, 16, -82, -78, -31, 34, 66, -19,
|
||||
-86, -1, 26, 8, 100, -126, -74, 20, -28, 83)
|
||||
|
||||
val tlvs = Tlv.tlvListFromBytes(bytes)
|
||||
val tlvs = Tlv.deserialize(bytes)
|
||||
|
||||
assertThat(tlvs)
|
||||
.isNotNull()
|
||||
|
|
@ -91,12 +91,12 @@ class TlvTest {
|
|||
@Test
|
||||
fun `Bytes to TLVs, wrong values`() {
|
||||
val bytes = byteArrayOf(0)
|
||||
val tlvs = Tlv.tlvListFromBytes(bytes)
|
||||
val tlvs = Tlv.deserialize(bytes)
|
||||
assertThat(tlvs)
|
||||
.isNull()
|
||||
|
||||
val bytes1 = byteArrayOf(0, 0, 0, 0, 0, 0, 0)
|
||||
val tlvs1 = Tlv.tlvListFromBytes(bytes1)
|
||||
val tlvs1 = Tlv.deserialize(bytes1)
|
||||
assertThat(tlvs1)
|
||||
.isNull()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue