diff --git a/tangem-core/src/main/java/com/tangem/CardEnvironment.kt b/tangem-core/src/main/java/com/tangem/CardEnvironment.kt index 8c32ac506f..39e077006e 100644 --- a/tangem-core/src/main/java/com/tangem/CardEnvironment.kt +++ b/tangem-core/src/main/java/com/tangem/CardEnvironment.kt @@ -10,7 +10,8 @@ data class CardEnvironment( val pin2: String = DEFAULT_PIN2, val cardId: String? = null, val terminalKeys: KeyPair? = null, - val encryptionKey: ByteArray? = null + val encryptionKey: ByteArray? = null, + val cvc: ByteArray? = null ) { companion object { diff --git a/tangem-core/src/main/java/com/tangem/CardManager.kt b/tangem-core/src/main/java/com/tangem/CardManager.kt index 197c82891a..e381f57e3d 100644 --- a/tangem-core/src/main/java/com/tangem/CardManager.kt +++ b/tangem-core/src/main/java/com/tangem/CardManager.kt @@ -122,6 +122,37 @@ class CardManager( runTask(task, cardId, callback) } + /** + * This command will create a new wallet on the card having ‘Empty’ state. + * A key pair WalletPublicKey / WalletPrivateKey is generated and securely stored in the card. + * App will need to obtain Wallet_PublicKey from the response of [CreateWalletCommand] or [ReadCommand] + * and then transform it into an address of corresponding blockchain wallet + * according to a specific blockchain algorithm. + * WalletPrivateKey is never revealed by the card and will be used by [SignCommand] and [CheckWalletCommand]. + * RemainingSignature is set to MaxSignatures. + * @param cardId CID, Unique Tangem card ID number. + */ + fun createWallet(cardId: String, + callback: (result: TaskEvent) -> Unit) { + val createWalletCommand = CreateWalletCommand(cardId) + val task = SingleCommandTask(createWalletCommand) + runTask(task, cardId, callback) + } + + /** + * 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. + * @param cardId CID, Unique Tangem card ID number. + */ + fun purgeWallet(cardId: String, + callback: (result: TaskEvent) -> Unit) { + val purgeWalletCommand = PurgeWalletCommand(cardId) + val task = SingleCommandTask(purgeWalletCommand) + runTask(task, cardId, callback) + } + /** */ @@ -157,6 +188,6 @@ class CardManager( } private fun fetchCardEnvironment(cardId: String?): CardEnvironment { - return cardEnvironmentRepository[cardId] ?: CardEnvironment() + return cardEnvironmentRepository[cardId] ?: CardEnvironment(cardId = cardId) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt new file mode 100644 index 0000000000..833795cbbf --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt @@ -0,0 +1,71 @@ +package com.tangem.commands + +import com.tangem.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.TlvMapper +import com.tangem.common.tlv.TlvTag +import com.tangem.tasks.TaskError + +class CreateWalletResponse( + /** + * CID, Unique Tangem card ID number. + */ + val cardId: String, + /** + * Current status of the card [1 - Empty, 2 - Loaded, 3- Purged] + */ + val status: CardStatus, + /** + + */ + val walletPublicKey: ByteArray +) : CommandResponse + +/** + * This command will create a new wallet on the card having ‘Empty’ state. + * A key pair WalletPublicKey / WalletPrivateKey is generated and securely stored in the card. + * App will need to obtain Wallet_PublicKey from the response of [CreateWalletCommand] or [ReadCommand] + * and then transform it into an address of corresponding blockchain wallet + * according to a specific blockchain algorithm. + * WalletPrivateKey is never revealed by the card and will be used by [SignCommand] and [CheckWalletCommand]. + * RemainingSignature is set to MaxSignatures. + * + * @property cardId CID, Unique Tangem card ID number. + */ +class CreateWalletCommand( + private val cardId: String +) : CommandSerializer() { + + 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) + } + + override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): CreateWalletResponse? { + val tlvData = responseApdu.getTlvData() ?: return null + + return try { + val mapper = TlvMapper(tlvData) + CreateWalletResponse( + cardId = mapper.map(TlvTag.CardId), + status = mapper.map(TlvTag.Status), + walletPublicKey = mapper.map(TlvTag.WalletPublicKey) + ) + } catch (exception: Exception) { + throw TaskError.SerializeCommandError() + } + } +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt new file mode 100644 index 0000000000..f8c3feaf7f --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt @@ -0,0 +1,57 @@ +package com.tangem.commands + +import com.tangem.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.TlvMapper +import com.tangem.common.tlv.TlvTag +import com.tangem.tasks.TaskError + +class PurgeWalletResponse( + /** + * CID, Unique Tangem card ID number. + */ + val cardId: String, + /** + * Current status of the card [1 - Empty, 2 - Loaded, 3- Purged] + */ + val status: CardStatus +) : CommandResponse + +/** +* 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. + * @property cardId CID, Unique Tangem card ID number. + */ +class PurgeWalletCommand( + private val cardId: String +) : CommandSerializer() { + + 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) + } + + override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): PurgeWalletResponse? { + val tlvData = responseApdu.getTlvData() ?: return null + + return try { + val mapper = TlvMapper(tlvData) + PurgeWalletResponse( + cardId = mapper.map(TlvTag.CardId), + status = mapper.map(TlvTag.Status)) + } catch (exception: Exception) { + throw TaskError.SerializeCommandError() + } + } +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt index 491509ae08..ca292d37f4 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt @@ -52,7 +52,7 @@ enum class TlvTag(val code: Int) { Pause(0x1C), ManufactureId(0x20), - ManufacturerSignature(0x21), + ManufacturerSignature(0x86), IssuerDataPublicKey(0x30), IssuerTransactionPublicKey(0x31), diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/MainActivity.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/MainActivity.kt index 018ca60ce5..52b2951309 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/MainActivity.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/MainActivity.kt @@ -45,6 +45,8 @@ class MainActivity : AppCompatActivity() { tv_card_cid?.text = cardId btn_sign.isEnabled = true btn_read_issuer_data.isEnabled = true + btn_purge_wallet.isEnabled = true + btn_create_wallet.isEnabled = true } } } @@ -103,6 +105,32 @@ class MainActivity : AppCompatActivity() { } } } + btn_purge_wallet?.setOnClickListener { _ -> + cardManager.purgeWallet( + cardId) { + when (it) { + is TaskEvent.Completion -> { + if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName } + } + is TaskEvent.Event -> runOnUiThread { + tv_card_cid?.text = it.data.status.name + } + } + } + } + btn_create_wallet?.setOnClickListener { _ -> + cardManager.createWallet( + cardId) { + when (it) { + is TaskEvent.Completion -> { + if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName } + } + is TaskEvent.Event -> runOnUiThread { + tv_card_cid?.text = it.data.status.name + } + } + } + } } private fun createSampleHashes(): Array { diff --git a/tangem-demo/src/main/res/layout/activity_main.xml b/tangem-demo/src/main/res/layout/activity_main.xml index a1cecca79b..5759633c21 100644 --- a/tangem-demo/src/main/res/layout/activity_main.xml +++ b/tangem-demo/src/main/res/layout/activity_main.xml @@ -9,12 +9,12 @@ +