Updated on 2026-08-14
This commit is contained in:
commit
9fcf613a4b
21 changed files with 111 additions and 96 deletions
|
|
@ -5,6 +5,9 @@ import com.tangem.commands.personalization.CardConfig
|
|||
import com.tangem.commands.personalization.DepersonalizeCommand
|
||||
import com.tangem.commands.personalization.DepersonalizeResponse
|
||||
import com.tangem.commands.personalization.PersonalizeCommand
|
||||
import com.tangem.commands.personalization.entities.Acquirer
|
||||
import com.tangem.commands.personalization.entities.Issuer
|
||||
import com.tangem.commands.personalization.entities.Manufacturer
|
||||
import com.tangem.common.CardEnvironment
|
||||
import com.tangem.common.TerminalKeysService
|
||||
import com.tangem.crypto.CryptoUtils
|
||||
|
|
@ -261,6 +264,10 @@ class CardManager(
|
|||
* @param config is a configuration file with all the card settings that are written on the card
|
||||
* during personalization.
|
||||
* @param cardId this parameter will set up CID, Unique Tangem card ID.
|
||||
* @param issuer Issuer is a third-party team or company wishing to use Tangem cards.
|
||||
* @param manufacturer Tangem Card Manufacturer.
|
||||
* @param acquirer Acquirer is a trusted third-party company that operates proprietary
|
||||
* (non-EMV) POS terminal infrastructure and transaction processing back-end.
|
||||
*/
|
||||
fun personalize(config: CardConfig, callback: (result: TaskEvent<Card>) -> Unit) {
|
||||
if (this.config.issuer == null) {
|
||||
|
|
@ -268,6 +275,10 @@ class CardManager(
|
|||
return
|
||||
}
|
||||
val personalizationCommand = PersonalizeCommand(config)
|
||||
fun personalize(config: CardConfig,
|
||||
issuer: Issuer, manufacturer: Manufacturer, acquirer: Acquirer? = null,
|
||||
callback: (result: TaskEvent<Card>) -> Unit) {
|
||||
val personalizationCommand = PersonalizeCommand(config, issuer, manufacturer, acquirer)
|
||||
val task = SingleCommandTask(personalizationCommand)
|
||||
task.performPreflightRead = false
|
||||
runTask(task, callback = callback)
|
||||
|
|
@ -318,10 +329,7 @@ class CardManager(
|
|||
val terminalKeys = if (config.linkedTerminal) terminalKeysService?.getKeys() else null
|
||||
return CardEnvironment(
|
||||
cardId = cardId,
|
||||
terminalKeys = terminalKeys,
|
||||
manufacturerKeyPair = config.manufacturerKeyPair,
|
||||
acquirerKeyPair = config.acquirerKeyPair,
|
||||
issuer = config.issuer
|
||||
terminalKeys = terminalKeys
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
package com.tangem
|
||||
|
||||
import com.tangem.commands.personalization.entities.Issuer
|
||||
import com.tangem.common.KeyPair
|
||||
|
||||
class Config(
|
||||
val linkedTerminal: Boolean = true,
|
||||
val issuerPublicKey: ByteArray? = null,
|
||||
val manufacturerKeyPair: KeyPair? = null,
|
||||
val acquirerKeyPair: KeyPair? = null,
|
||||
val issuer: Issuer? = null
|
||||
val issuerPublicKey: ByteArray? = null
|
||||
)
|
||||
|
|
@ -3,7 +3,10 @@ package com.tangem.commands.personalization
|
|||
import com.tangem.commands.Card
|
||||
import com.tangem.commands.CardData
|
||||
import com.tangem.commands.CommandSerializer
|
||||
import com.tangem.commands.personalization.entities.Acquirer
|
||||
import com.tangem.commands.personalization.entities.Issuer
|
||||
import com.tangem.commands.personalization.entities.Manufacturer
|
||||
import com.tangem.commands.personalization.entities.Manufacturer
|
||||
import com.tangem.commands.personalization.util.CardIdCreator
|
||||
import com.tangem.common.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
|
|
@ -24,21 +27,23 @@ import com.tangem.tasks.TaskError
|
|||
* Personalization is an initialization procedure, required before starting using a card.
|
||||
* During this procedure a card setting is set up.
|
||||
* During this procedure all data exchange is encrypted.
|
||||
* @param config is a configuration file with all the card settings that are written on the card
|
||||
* @property config is a configuration file with all the card settings that are written on the card
|
||||
* during personalization.
|
||||
* @param cardId this parameter will set up CID, Unique Tangem card ID.
|
||||
* @property issuer Issuer is a third-party team or company wishing to use Tangem cards.
|
||||
* @property manufacturer Tangem Card Manufacturer.
|
||||
* @property acquirer Acquirer is a trusted third-party company that operates proprietary
|
||||
* (non-EMV) POS terminal infrastructure and transaction processing back-end.
|
||||
*/
|
||||
class PersonalizeCommand(private val config: CardConfig) : CommandSerializer<Card>() {
|
||||
class PersonalizeCommand(
|
||||
private val config: CardConfig,
|
||||
private val issuer: Issuer, private val manufacturer: Manufacturer,
|
||||
private val acquirer: Acquirer? = null
|
||||
) : CommandSerializer<Card>() {
|
||||
|
||||
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
|
||||
if (cardEnvironment.issuer == null || cardEnvironment.manufacturerKeyPair == null) {
|
||||
throw TaskError.SerializeCommandError()
|
||||
}
|
||||
return CommandApdu(
|
||||
Instruction.Personalize,
|
||||
serializePersonalizationData(
|
||||
config, cardEnvironment.issuer,
|
||||
cardEnvironment.manufacturerKeyPair.privateKey, cardEnvironment.acquirerKeyPair?.publicKey),
|
||||
serializePersonalizationData(config),
|
||||
encryptionKey = devPersonalizationKey
|
||||
)
|
||||
}
|
||||
|
|
@ -99,12 +104,10 @@ class PersonalizeCommand(private val config: CardConfig) : CommandSerializer<Car
|
|||
)
|
||||
}
|
||||
|
||||
private fun serializePersonalizationData(config: CardConfig, issuer: Issuer,
|
||||
manufacturerPrivateKey: ByteArray,
|
||||
acquirePublicKey: ByteArray?
|
||||
): ByteArray {
|
||||
private fun serializePersonalizationData(config: CardConfig): ByteArray {
|
||||
val cardId = CardIdCreator.create(config.series, config.startNumber)
|
||||
?: throw TaskError.SerializeCommandError()
|
||||
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.CardId, cardId)
|
||||
tlvBuilder.append(TlvTag.CurveId, config.curveID)
|
||||
|
|
@ -124,17 +127,13 @@ class PersonalizeCommand(private val config: CardConfig) : CommandSerializer<Car
|
|||
tlvBuilder.append(TlvTag.IssuerDataPublicKey, issuer.dataKeyPair.publicKey)
|
||||
tlvBuilder.append(TlvTag.IssuerTransactionPublicKey, issuer.transactionKeyPair.publicKey)
|
||||
|
||||
tlvBuilder.append(TlvTag.AcquirerPublicKey, acquirePublicKey)
|
||||
tlvBuilder.append(TlvTag.AcquirerPublicKey, acquirer?.keyPair?.publicKey)
|
||||
|
||||
tlvBuilder.append(
|
||||
TlvTag.CardData, serializeCardData(cardId, config.cardData, issuer, manufacturerPrivateKey)
|
||||
)
|
||||
tlvBuilder.append(TlvTag.CardData, serializeCardData(cardId, config.cardData))
|
||||
return tlvBuilder.serialize()
|
||||
}
|
||||
|
||||
private fun serializeCardData(
|
||||
cardId: String, cardData: CardData,
|
||||
issuer: Issuer, manufacturerPrivateKey: ByteArray): ByteArray {
|
||||
private fun serializeCardData(cardId: String, cardData: CardData): ByteArray {
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.Batch, cardData.batchId)
|
||||
tlvBuilder.append(TlvTag.ProductMask, cardData.productMask)
|
||||
|
|
@ -151,7 +150,8 @@ class PersonalizeCommand(private val config: CardConfig) : CommandSerializer<Car
|
|||
tlvBuilder.append(TlvTag.TokenDecimal, cardData.tokenDecimal)
|
||||
}
|
||||
tlvBuilder.append(
|
||||
TlvTag.CardIdManufacturerSignature, cardId.hexToBytes().sign(manufacturerPrivateKey)
|
||||
TlvTag.CardIdManufacturerSignature,
|
||||
cardId.hexToBytes().sign(manufacturer.keyPair.privateKey)
|
||||
)
|
||||
return tlvBuilder.serialize()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.commands.personalization.entities
|
||||
|
||||
import com.tangem.common.KeyPair
|
||||
|
||||
data class Acquirer(
|
||||
val keyPair: KeyPair,
|
||||
val name: String? = null,
|
||||
val id: String? = null
|
||||
)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.commands.personalization.entities
|
||||
|
||||
import com.tangem.common.KeyPair
|
||||
|
||||
data class Manufacturer(
|
||||
val keyPair: KeyPair,
|
||||
val name: String? = null
|
||||
)
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
package com.tangem.common
|
||||
|
||||
import com.tangem.commands.personalization.entities.Issuer
|
||||
|
||||
|
||||
/**
|
||||
* Contains data relating to a Tangem card. It is used in constructing all the commands,
|
||||
|
|
@ -14,10 +12,7 @@ data class CardEnvironment(
|
|||
val terminalKeys: KeyPair? = null,
|
||||
var encryptionMode: EncryptionMode = EncryptionMode.NONE,
|
||||
var encryptionKey: ByteArray? = null,
|
||||
val cvc: ByteArray? = null,
|
||||
val manufacturerKeyPair: KeyPair? = null,
|
||||
val acquirerKeyPair: KeyPair? = null,
|
||||
val issuer: Issuer? = null
|
||||
val cvc: ByteArray? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -141,12 +141,6 @@ sealed class TaskError(val code: Int) : Exception() {
|
|||
* (when the card's requires it), but the counter is missing.
|
||||
*/
|
||||
class MissingCounter : TaskError(7001)
|
||||
|
||||
/**
|
||||
* This error is returned when [com.tangem.commands.personalization.PersonalizeCommand] is attempted
|
||||
* without [com.tangem.commands.personalization.entities.Issuer] being set in the [com.tangem.Config].
|
||||
*/
|
||||
class IssuerIsRequired : TaskError(7002)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue