Updated on 2026-08-14

This commit is contained in:
Tangem 2020-02-25 08:52:21 +00:00
commit f77158c082
13 changed files with 208 additions and 47 deletions

View file

@ -96,8 +96,7 @@ class CardManager(
*/
fun readIssuerData(cardId: String,
callback: (result: TaskEvent<ReadIssuerDataResponse>) -> Unit) {
val getIssuerDataCommand = ReadIssuerDataCommand()
val task = SingleCommandTask(getIssuerDataCommand)
val task = ReadIssuerDataTask(config.issuerPublicKey)
runTask(task, cardId, callback)
}
@ -134,12 +133,12 @@ class CardManager(
issuerDataSignature: ByteArray,
issuerDataCounter: Int? = null,
callback: (result: TaskEvent<WriteIssuerDataResponse>) -> Unit) {
val writeIssuerDataCommand = WriteIssuerDataCommand(
val task = WriteIssuerDataTask(
issuerData,
issuerDataSignature,
issuerDataCounter
issuerDataCounter,
config.issuerPublicKey
)
val task = SingleCommandTask(writeIssuerDataCommand)
runTask(task, cardId, callback)
}

View file

@ -1,12 +1,12 @@
package com.tangem.commands
import com.tangem.commands.common.DefaultIssuerDataVerifier
import com.tangem.commands.common.IssuerDataMode
import com.tangem.commands.common.IssuerDataVerifier
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
@ -50,12 +50,16 @@ class ReadIssuerDataResponse(
* wallet balance signed by the issuer or additional issuers attestation data.
* @property cardId CID, Unique Tangem card ID number.
*/
class ReadIssuerDataCommand : CommandSerializer<ReadIssuerDataResponse>() {
class ReadIssuerDataCommand(
verifier: IssuerDataVerifier = DefaultIssuerDataVerifier()
) : CommandSerializer<ReadIssuerDataResponse>(),
IssuerDataVerifier by verifier {
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
val tlvBuilder = TlvBuilder()
tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1)
tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId)
tlvBuilder.append(TlvTag.Mode, IssuerDataMode.ReadData)
return CommandApdu(Instruction.ReadIssuerData, tlvBuilder.serialize())
}

View file

@ -1,5 +1,8 @@
package com.tangem.commands
import com.tangem.commands.common.DefaultIssuerDataVerifier
import com.tangem.commands.common.IssuerDataMode
import com.tangem.commands.common.IssuerDataVerifier
import com.tangem.common.CardEnvironment
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.Instruction
@ -62,7 +65,7 @@ class ReadIssuerExtraDataCommand(
val tlvBuilder = TlvBuilder()
tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1)
tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId)
tlvBuilder.append(TlvTag.Mode, EXTRA_DATA_MODE)
tlvBuilder.append(TlvTag.Mode, IssuerDataMode.ReadExtraData)
tlvBuilder.append(TlvTag.Offset, offset)
return CommandApdu(Instruction.ReadIssuerData, tlvBuilder.serialize())
}

View file

@ -1,13 +1,12 @@
package com.tangem.commands
import com.tangem.commands.common.DefaultIssuerDataVerifier
import com.tangem.commands.common.IssuerDataMode
import com.tangem.commands.common.IssuerDataVerifier
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.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
@ -33,13 +32,15 @@ class WriteIssuerDataResponse(
class WriteIssuerDataCommand(
private val issuerData: ByteArray,
private val issuerDataSignature: ByteArray,
private val issuerDataCounter: Int? = null
) : CommandSerializer<WriteIssuerDataResponse>() {
private val issuerDataCounter: Int? = null,
verifier: IssuerDataVerifier = DefaultIssuerDataVerifier()
) : CommandSerializer<WriteIssuerDataResponse>(), IssuerDataVerifier by verifier {
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
val tlvBuilder = TlvBuilder()
tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1)
tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId)
tlvBuilder.append(TlvTag.Mode, IssuerDataMode.WriteData)
tlvBuilder.append(TlvTag.IssuerData, issuerData)
tlvBuilder.append(TlvTag.IssuerDataSignature, issuerDataSignature)
tlvBuilder.append(TlvTag.IssuerDataCounter, issuerDataCounter)

View file

@ -1,5 +1,8 @@
package com.tangem.commands
import com.tangem.commands.common.DefaultIssuerDataVerifier
import com.tangem.commands.common.IssuerDataMode
import com.tangem.commands.common.IssuerDataVerifier
import com.tangem.common.CardEnvironment
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.Instruction
@ -9,32 +12,6 @@ import com.tangem.common.tlv.TlvMapper
import com.tangem.common.tlv.TlvTag
import com.tangem.tasks.TaskError
/**
* This enum specifies modes for [WriteIssuerExtraDataCommand].
*/
enum class IssuerDataMode(val code: Byte) {
/**
* This mode is required to initiate writing issuer extra data to the card.
*/
InitializeWritingExtraData(1),
/**
* With this mode, the command writes part of issuer extra data
* (block of a size [WriteIssuerExtraDataCommand.SINGLE_WRITE_SIZE]) to the card.
*/
WriteExtraData(2),
/**
* This mode is used after the issuer extra data was fully written to the card.
* Under this mode the command provides the issuer signature
* to confirm the validity of data that was written to card.
*/
FinalizeExtraData(3);
companion object {
private val values = CardStatus.values()
fun byCode(code: Int): CardStatus? = values.find { it.code == code }
}
}
/**
* This command writes Issuer Extra Data field and its issuers signature.
* Issuer Extra Data is never changed or parsed from within the Tangem COS.

View file

@ -0,0 +1,44 @@
package com.tangem.commands.common
import com.tangem.commands.WriteIssuerExtraDataCommand
/**
* This enum specifies modes for [WriteIssuerExtraDataCommand].
*/
enum class IssuerDataMode(val code: Byte) {
/**
* This mode is required to read issuer data from the card.
*/
ReadData(0),
/**
* This mode is required to write issuer data to the card.
*/
WriteData(0),
/**
* This mode is required to read issuer extra data from the card.
*/
ReadExtraData(1),
/**
* This mode is required to initiate writing issuer extra data to the card.
*/
InitializeWritingExtraData(1),
/**
* With this mode, the command writes part of issuer extra data
* (block of a size [WriteIssuerExtraDataCommand.SINGLE_WRITE_SIZE]) to the card.
*/
WriteExtraData(2),
/**
* This mode is used after the issuer extra data was fully written to the card.
* Under this mode the command provides the issuer signature
* to confirm the validity of data that was written to card.
*/
FinalizeExtraData(3);
companion object {
private val values = values()
fun byCode(code: Byte): IssuerDataMode? = values.find { it.code == code }
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.commands
package com.tangem.commands.common
import com.tangem.common.tlv.TlvEncoder
import com.tangem.common.tlv.TlvTag

View file

@ -2,7 +2,10 @@ package com.tangem.common.tlv
import com.tangem.Log
import com.tangem.commands.*
import com.tangem.common.extensions.*
import com.tangem.commands.common.IssuerDataMode
import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toByteArray
import com.tangem.tasks.TaskError
import java.util.*

View file

@ -2,6 +2,7 @@ package com.tangem.common.tlv
import com.tangem.Log
import com.tangem.commands.*
import com.tangem.commands.common.IssuerDataMode
import com.tangem.common.extensions.toDate
import com.tangem.common.extensions.toHexString
import com.tangem.common.extensions.toInt
@ -132,7 +133,7 @@ class TlvMapper(val tlvList: List<Tlv>) {
TlvValueType.IssuerDataMode -> {
typeCheck<T, IssuerDataMode>(tag)
try {
IssuerDataMode.byCode(tlvValue.toInt()) as T
IssuerDataMode.byCode(tlvValue.toInt().toByte()) as T
} catch (exception: Exception) {
logException(tag, tlvValue.toInt().toString(), exception)
throw TaskError.ConvertError()

View file

@ -0,0 +1,53 @@
package com.tangem.tasks
import com.tangem.commands.Card
import com.tangem.commands.ReadIssuerDataCommand
import com.tangem.commands.ReadIssuerDataResponse
import com.tangem.commands.common.IssuerDataToVerify
import com.tangem.common.CardEnvironment
import com.tangem.common.CompletionResult
class ReadIssuerDataTask(private val issuerPublicKey: ByteArray? = null) : Task<ReadIssuerDataResponse>() {
override fun onRun(
cardEnvironment: CardEnvironment,
currentCard: Card?,
callback: (result: TaskEvent<ReadIssuerDataResponse>) -> Unit) {
val command = ReadIssuerDataCommand()
sendCommand(command, cardEnvironment) { result ->
when (result) {
is CompletionResult.Success -> {
val data = result.data
if (data.issuerData.isEmpty()) {
completeNfcSession()
callback(TaskEvent.Event(result.data))
callback(TaskEvent.Completion())
return@sendCommand
}
val publicKey = issuerPublicKey ?: currentCard!!.issuerPublicKey!!
val issuerDataToVerify = IssuerDataToVerify(
cardEnvironment.cardId!!,
data.issuerData,
data.issuerDataCounter
)
if (command.verify(publicKey, data.issuerDataSignature, issuerDataToVerify)) {
completeNfcSession()
callback(TaskEvent.Event(result.data))
callback(TaskEvent.Completion())
} else {
completeNfcSession(TaskError.VerificationFailed())
callback(TaskEvent.Completion(TaskError.VerificationFailed()))
}
}
is CompletionResult.Failure -> {
if (result.error !is TaskError.UserCancelled) {
completeNfcSession(result.error)
}
callback(TaskEvent.Completion(result.error))
}
}
}
}
}

View file

@ -1,6 +1,10 @@
package com.tangem.tasks
import com.tangem.commands.*
import com.tangem.commands.Card
import com.tangem.commands.ReadIssuerExtraDataCommand
import com.tangem.commands.ReadIssuerExtraDataResponse
import com.tangem.commands.WriteIssuerExtraDataCommand
import com.tangem.commands.common.IssuerDataToVerify
import com.tangem.common.CardEnvironment
import com.tangem.common.CompletionResult
import java.io.ByteArrayOutputStream

View file

@ -0,0 +1,67 @@
package com.tangem.tasks
import com.tangem.commands.Card
import com.tangem.commands.SettingsMask
import com.tangem.commands.WriteIssuerDataCommand
import com.tangem.commands.WriteIssuerDataResponse
import com.tangem.commands.common.IssuerDataToVerify
import com.tangem.common.CardEnvironment
import com.tangem.common.CompletionResult
class WriteIssuerDataTask(
private val issuerData: ByteArray,
private val issuerDataSignature: ByteArray,
private val issuerDataCounter: Int? = null,
private val issuerPublicKey: ByteArray? = null
) : Task<WriteIssuerDataResponse>() {
private lateinit var card: Card
override fun onRun(
cardEnvironment: CardEnvironment,
currentCard: Card?,
callback: (result: TaskEvent<WriteIssuerDataResponse>) -> Unit) {
card = currentCard!!
val command = WriteIssuerDataCommand(
issuerData, issuerDataSignature, issuerDataCounter
)
if (!isCounterValid(issuerDataCounter)) {
completeNfcSession(TaskError.MissingCounter())
callback(TaskEvent.Completion(TaskError.MissingCounter()))
} else if (!verifySignature(command, cardEnvironment.cardId!!)) {
completeNfcSession(TaskError.VerificationFailed())
callback(TaskEvent.Completion(TaskError.VerificationFailed()))
}
sendCommand(command, cardEnvironment) { result ->
when (result) {
is CompletionResult.Success -> {
completeNfcSession()
callback(TaskEvent.Event(result.data))
callback(TaskEvent.Completion())
}
is CompletionResult.Failure -> {
if (result.error !is TaskError.UserCancelled) {
completeNfcSession(result.error)
}
callback(TaskEvent.Completion(result.error))
}
}
}
}
private fun isCounterValid(issuerDataCounter: Int?): Boolean =
if (isCounterRequired()) issuerDataCounter != null else true
private fun isCounterRequired(): Boolean =
card.settingsMask?.contains(SettingsMask.protectIssuerDataAgainstReplay) != false
private fun verifySignature(command: WriteIssuerDataCommand, cardId: String): Boolean {
return command.verify(
issuerPublicKey ?: card.issuerPublicKey!!,
issuerDataSignature,
IssuerDataToVerify(cardId, issuerData, issuerDataCounter)
)
}
}

View file

@ -1,6 +1,11 @@
package com.tangem.tasks
import com.tangem.commands.*
import com.tangem.commands.Card
import com.tangem.commands.SettingsMask
import com.tangem.commands.WriteIssuerDataResponse
import com.tangem.commands.WriteIssuerExtraDataCommand
import com.tangem.commands.common.IssuerDataMode
import com.tangem.commands.common.IssuerDataToVerify
import com.tangem.common.CardEnvironment
import com.tangem.common.CompletionResult