diff --git a/tangem-core/src/main/java/com/tangem/CardManager.kt b/tangem-core/src/main/java/com/tangem/CardManager.kt index bc3abf024f..c5a1f99e77 100644 --- a/tangem-core/src/main/java/com/tangem/CardManager.kt +++ b/tangem-core/src/main/java/com/tangem/CardManager.kt @@ -1,11 +1,11 @@ package com.tangem import com.tangem.commands.* -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.CardConfig import com.tangem.commands.personalization.entities.Issuer import com.tangem.commands.personalization.entities.Manufacturer import com.tangem.common.CardEnvironment diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/CardConfig.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/CardConfig.kt deleted file mode 100644 index 15548f9871..0000000000 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/CardConfig.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.tangem.commands.personalization - -import com.tangem.commands.* - -data class NdefRecord( - val type: Type, - val value: String -) { - enum class Type { - URI, AAR, TEXT - } - - val valueInBytes: ByteArray by lazy { value.toByteArray() } -} - -/** - * It is a configuration file with all the card settings that are written on the card - * during [PersonalizeCommand]. - */ -data class CardConfig( - val issuerName: String? = null, - val acquirerName: String? = null, - val series: String? = null, - val startNumber: Long = 0, - val count: Int = 0, - val pin: String, - val pin2: String, - val pin3: String, - val hexCrExKey: String?, - val cvc: String, - val pauseBeforePin2: Int, - val smartSecurityDelay: Boolean, - val curveID: EllipticCurve, - val signingMethod: SigningMethod, - val maxSignatures: Int, - val isReusable: Boolean, - val allowSwapPin: Boolean, - val allowSwapPin2: Boolean, - val useActivation: Boolean, - val useCvc: Boolean, - val useNdef: Boolean, - val useDynamicNdef: Boolean, - val useOneCommandAtTime: Boolean, - val useBlock: Boolean, - val allowSelectBlockchain: Boolean, - val forbidPurgeWallet: Boolean, - val protocolAllowUnencrypted: Boolean, - val protocolAllowStaticEncryption: Boolean, - val protectIssuerDataAgainstReplay: Boolean, - val forbidDefaultPin: Boolean, - val disablePrecomputedNdef: Boolean, - val skipSecurityDelayIfValidatedByIssuer: Boolean, - val skipCheckPIN2andCVCIfValidatedByIssuer: Boolean, - val skipSecurityDelayIfValidatedByLinkedTerminal: Boolean, - - val restrictOverwriteIssuerDataEx: Boolean, - - val requireTerminalTxSignature: Boolean, - val requireTerminalCertSignature: Boolean, - val checkPin3onCard: Boolean, - - val createWallet: Boolean, - - val cardData: CardData, - val ndefRecords: List -) { - - fun getSettingsMask(): SettingsMask { - val builder = SettingsMaskBuilder() - - if (allowSwapPin) builder.add(Settings.AllowSwapPIN) - if (allowSwapPin2) builder.add(Settings.AllowSwapPIN2) - if (useCvc) builder.add(Settings.UseCVC) - if (isReusable) builder.add(Settings.IsReusable) - - if (useOneCommandAtTime) builder.add(Settings.UseOneCommandAtTime) - if (useNdef) builder.add(Settings.UseNdef) - if (useDynamicNdef) builder.add(Settings.UseDynamicNdef) - if (disablePrecomputedNdef) builder.add(Settings.DisablePrecomputedNdef) - - if (protocolAllowUnencrypted) builder.add(Settings.ProtocolAllowUnencrypted) - if (protocolAllowStaticEncryption) builder.add(Settings.ProtocolAllowStaticEncryption) - - if (forbidDefaultPin) builder.add(Settings.ForbidDefaultPIN) - - if (useActivation) builder.add(Settings.UseActivation) - - if (useBlock) builder.add(Settings.UseBlock) - if (smartSecurityDelay) builder.add(Settings.SmartSecurityDelay) - - if (protectIssuerDataAgainstReplay) builder.add(Settings.ProtectIssuerDataAgainstReplay) - - if (forbidPurgeWallet) builder.add(Settings.ForbidPurgeWallet) - if (allowSelectBlockchain) builder.add(Settings.AllowSelectBlockchain) - - if (skipCheckPIN2andCVCIfValidatedByIssuer) builder.add(Settings.SkipCheckPin2andCvcIfValidatedByIssuer) - if (skipSecurityDelayIfValidatedByIssuer) builder.add(Settings.SkipSecurityDelayIfValidatedByIssuer) - - if (skipSecurityDelayIfValidatedByLinkedTerminal) builder.add(Settings.SkipSecurityDelayIfValidatedByLinkedTerminal) - if (restrictOverwriteIssuerDataEx) builder.add(Settings.RestrictOverwriteIssuerDataEx) - - if (requireTerminalTxSignature) builder.add(Settings.RequireTermTxSignature) - - if (requireTerminalCertSignature) builder.add(Settings.RequireTermCertSignature) - - if (checkPin3onCard) builder.add(Settings.CheckPIN3onCard) - - return builder.build() - } - - companion object -} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/NdefEncoder.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/NdefEncoder.kt index a034d171cb..6c6103556e 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/NdefEncoder.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/NdefEncoder.kt @@ -1,5 +1,6 @@ package com.tangem.commands.personalization +import com.tangem.commands.personalization.entities.NdefRecord import java.io.ByteArrayOutputStream import java.nio.charset.StandardCharsets diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt index eedcced227..a0ec909e12 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt @@ -3,21 +3,16 @@ 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.util.CardIdCreator +import com.tangem.commands.personalization.entities.* 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 import com.tangem.tasks.TaskError /** @@ -104,18 +99,18 @@ class PersonalizeCommand( } private fun serializePersonalizationData(config: CardConfig): ByteArray { - val cardId = CardIdCreator.create(config.series, config.startNumber) - ?: throw TaskError.SerializeCommandError() + val cardId = config.createCardId() ?: throw TaskError.SerializeCommandError() val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.CardId, cardId) tlvBuilder.append(TlvTag.CurveId, config.curveID) tlvBuilder.append(TlvTag.MaxSignatures, config.maxSignatures) tlvBuilder.append(TlvTag.SigningMethod, config.signingMethod) - tlvBuilder.append(TlvTag.SettingsMask, config.getSettingsMask()) + tlvBuilder.append(TlvTag.SettingsMask, config.createSettingsMask()) tlvBuilder.append(TlvTag.PauseBeforePin2, config.pauseBeforePin2 / 10) tlvBuilder.append(TlvTag.Cvc, config.cvc.toByteArray()) - if (!config.ndefRecords.isNullOrEmpty()) tlvBuilder.append(TlvTag.NdefData, serializeNdef(config.ndefRecords)) + if (!config.ndefRecords.isNullOrEmpty()) + tlvBuilder.append(TlvTag.NdefData, config.serializeNdef(config.ndefRecords)) tlvBuilder.append(TlvTag.CreateWalletAtPersonalize, config.createWallet) @@ -128,37 +123,10 @@ class PersonalizeCommand( tlvBuilder.append(TlvTag.AcquirerPublicKey, acquirer?.keyPair?.publicKey) - tlvBuilder.append(TlvTag.CardData, serializeCardData(cardId, config.cardData)) + tlvBuilder.append(TlvTag.CardData, config.serializeCardData(cardId, issuer, manufacturer)) return tlvBuilder.serialize() } - private fun serializeCardData(cardId: String, cardData: CardData): ByteArray { - val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Batch, cardData.batchId) - tlvBuilder.append(TlvTag.ProductMask, cardData.productMask) - - tlvBuilder.append(TlvTag.ManufactureDateTime, cardData.manufactureDateTime) - - tlvBuilder.append(TlvTag.IssuerId, issuer.id) - - tlvBuilder.append(TlvTag.BlockchainId, cardData.blockchainName) - - if (cardData.tokenSymbol != null) { - tlvBuilder.append(TlvTag.TokenSymbol, cardData.tokenSymbol) - tlvBuilder.append(TlvTag.TokenContractAddress, cardData.tokenContractAddress) - tlvBuilder.append(TlvTag.TokenDecimal, cardData.tokenDecimal) - } - tlvBuilder.append( - TlvTag.CardIdManufacturerSignature, - cardId.hexToBytes().sign(manufacturer.keyPair.privateKey) - ) - return tlvBuilder.serialize() - } - - private fun serializeNdef(ndefRecords: List): ByteArray { - return NdefEncoder(ndefRecords, config.useDynamicNdef).encode() - } - companion object { val devPersonalizationKey = "1234".calculateSha256().copyOf(32) } diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt new file mode 100644 index 0000000000..bc1a082237 --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt @@ -0,0 +1,70 @@ +package com.tangem.commands.personalization.entities + +import com.tangem.commands.CardData +import com.tangem.commands.EllipticCurve +import com.tangem.commands.SigningMethod + +data class NdefRecord( + val type: Type, + val value: String +) { + enum class Type { + URI, AAR, TEXT + } + + val valueInBytes: ByteArray by lazy { value.toByteArray() } +} + +/** + * It is a configuration file with all the card settings that are written on the card + * during [PersonalizeCommand]. + */ +data class CardConfig( + val issuerName: String? = null, + val acquirerName: String? = null, + val series: String? = null, + val startNumber: Long = 0, + val count: Int = 0, + val pin: String, + val pin2: String, + val pin3: String, + val hexCrExKey: String?, + val cvc: String, + val pauseBeforePin2: Int, + val smartSecurityDelay: Boolean, + val curveID: EllipticCurve, + val signingMethod: SigningMethod, + val maxSignatures: Int, + val isReusable: Boolean, + val allowSwapPin: Boolean, + val allowSwapPin2: Boolean, + val useActivation: Boolean, + val useCvc: Boolean, + val useNdef: Boolean, + val useDynamicNdef: Boolean, + val useOneCommandAtTime: Boolean, + val useBlock: Boolean, + val allowSelectBlockchain: Boolean, + val forbidPurgeWallet: Boolean, + val protocolAllowUnencrypted: Boolean, + val protocolAllowStaticEncryption: Boolean, + val protectIssuerDataAgainstReplay: Boolean, + val forbidDefaultPin: Boolean, + val disablePrecomputedNdef: Boolean, + val skipSecurityDelayIfValidatedByIssuer: Boolean, + val skipCheckPIN2andCVCIfValidatedByIssuer: Boolean, + val skipSecurityDelayIfValidatedByLinkedTerminal: Boolean, + + val restrictOverwriteIssuerDataEx: Boolean, + + val requireTerminalTxSignature: Boolean, + val requireTerminalCertSignature: Boolean, + val checkPin3onCard: Boolean, + + val createWallet: Boolean, + + val cardData: CardData, + val ndefRecords: List +) { + companion object +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfigExtensions.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfigExtensions.kt new file mode 100644 index 0000000000..5deb6fa048 --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfigExtensions.kt @@ -0,0 +1,113 @@ +package com.tangem.commands.personalization.entities + +import com.tangem.commands.Settings +import com.tangem.commands.SettingsMask +import com.tangem.commands.SettingsMaskBuilder +import com.tangem.commands.personalization.NdefEncoder +import com.tangem.common.extensions.hexToBytes +import com.tangem.common.tlv.TlvBuilder +import com.tangem.common.tlv.TlvTag +import com.tangem.crypto.sign + +/** +[REDACTED_AUTHOR] + */ +internal fun CardConfig.createSettingsMask(): SettingsMask { + val builder = SettingsMaskBuilder() + + if (allowSwapPin) builder.add(Settings.AllowSwapPIN) + if (allowSwapPin2) builder.add(Settings.AllowSwapPIN2) + if (useCvc) builder.add(Settings.UseCVC) + if (isReusable) builder.add(Settings.IsReusable) + + if (useOneCommandAtTime) builder.add(Settings.UseOneCommandAtTime) + if (useNdef) builder.add(Settings.UseNdef) + if (useDynamicNdef) builder.add(Settings.UseDynamicNdef) + if (disablePrecomputedNdef) builder.add(Settings.DisablePrecomputedNdef) + + if (protocolAllowUnencrypted) builder.add(Settings.ProtocolAllowUnencrypted) + if (protocolAllowStaticEncryption) builder.add(Settings.ProtocolAllowStaticEncryption) + + if (forbidDefaultPin) builder.add(Settings.ForbidDefaultPIN) + + if (useActivation) builder.add(Settings.UseActivation) + + if (useBlock) builder.add(Settings.UseBlock) + if (smartSecurityDelay) builder.add(Settings.SmartSecurityDelay) + + if (protectIssuerDataAgainstReplay) builder.add(Settings.ProtectIssuerDataAgainstReplay) + + if (forbidPurgeWallet) builder.add(Settings.ForbidPurgeWallet) + if (allowSelectBlockchain) builder.add(Settings.AllowSelectBlockchain) + + if (skipCheckPIN2andCVCIfValidatedByIssuer) builder.add(Settings.SkipCheckPin2andCvcIfValidatedByIssuer) + if (skipSecurityDelayIfValidatedByIssuer) builder.add(Settings.SkipSecurityDelayIfValidatedByIssuer) + + if (skipSecurityDelayIfValidatedByLinkedTerminal) builder.add(Settings.SkipSecurityDelayIfValidatedByLinkedTerminal) + if (restrictOverwriteIssuerDataEx) builder.add(Settings.RestrictOverwriteIssuerDataEx) + + if (requireTerminalTxSignature) builder.add(Settings.RequireTermTxSignature) + + if (requireTerminalCertSignature) builder.add(Settings.RequireTermCertSignature) + + if (checkPin3onCard) builder.add(Settings.CheckPIN3onCard) + + return builder.build() +} + +internal fun CardConfig.createCardId(): String? { + if (series == null) return null + if (startNumber <= 0 || (series.length != 2 && series.length != 4)) return null + + val Alf = "ABCDEF0123456789" + fun checkSeries(series: String): Boolean { + val containsList = series.filter { Alf.contains(it) } + return containsList.length == series.length + } + if (!checkSeries(series)) return null + + val tail = if (series.length == 2) String.format("%013d", startNumber) else String.format("%011d", startNumber) + var cardId = (series + tail).replace(" ", "") + if (cardId.length != 15 || Alf.indexOf(cardId[0]) == -1 || Alf.indexOf(cardId[1]) == -1) + return null + + cardId += "0" + val length = cardId.length + var sum = 0 + for (i in 0 until length) { + // get digits in reverse order + var digit: Int + val cDigit = cardId[length - i - 1] + digit = if (cDigit in '0'..'9') cDigit - '0' else cDigit - 'A' + + // every 2nd number multiply with 2 + if (i % 2 == 1) digit *= 2 + sum += if (digit > 9) digit - 9 else digit + } + val lunh = (10 - sum % 10) % 10 + return cardId.substring(0, 15) + String.format("%d", lunh) +} + +internal fun CardConfig.serializeNdef(ndefRecords: List): ByteArray { + return NdefEncoder(ndefRecords, useDynamicNdef).encode() +} + +internal fun CardConfig.serializeCardData(cardId: String, issuer: Issuer, manufacturer: Manufacturer): ByteArray { + val tlvBuilder = TlvBuilder() + tlvBuilder.append(TlvTag.Batch, cardData.batchId) + tlvBuilder.append(TlvTag.ProductMask, cardData.productMask) + tlvBuilder.append(TlvTag.ManufactureDateTime, cardData.manufactureDateTime) + tlvBuilder.append(TlvTag.IssuerId, issuer.id) + tlvBuilder.append(TlvTag.BlockchainId, cardData.blockchainName) + + if (cardData.tokenSymbol != null) { + tlvBuilder.append(TlvTag.TokenSymbol, cardData.tokenSymbol) + tlvBuilder.append(TlvTag.TokenContractAddress, cardData.tokenContractAddress) + tlvBuilder.append(TlvTag.TokenDecimal, cardData.tokenDecimal) + } + tlvBuilder.append( + TlvTag.CardIdManufacturerSignature, + cardId.hexToBytes().sign(manufacturer.keyPair.privateKey) + ) + return tlvBuilder.serialize() +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/util/CardIdCreator.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/util/CardIdCreator.kt deleted file mode 100644 index a95e6f9cfc..0000000000 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/util/CardIdCreator.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.tangem.commands.personalization.util - -/** -[REDACTED_AUTHOR] - */ -internal class CardIdCreator { - - companion object { - private val Alf = "ABCDEF0123456789" - - fun create(nsSeries: String?, number: Long): String? { - val series = nsSeries ?: return null - if (number <= 0 || (series.length != 2 && series.length != 4)) return null - if (!checkSeries(series)) return null - - val tail = if (series.length == 2) String.format("%013d", number) else String.format("%011d", number) - val cardId = series + tail - return completeCardID(cardId) - } - - private fun checkSeries(series: String): Boolean { - val containsList = series.filter { Alf.contains(it) } - return containsList.length == series.length - } - - private fun completeCardID(imCardId: String): String? { - var cardId = imCardId - cardId = cardId.replace(" ", "") - if (cardId.length != 15 || Alf.indexOf(cardId[0]) == -1 || Alf.indexOf(cardId[1]) == -1) - return null - - cardId += "0" - val length = cardId.length - var sum = 0 - for (i in 0 until length) { - // get digits in reverse order - var digit: Int - val cDigit = cardId[length - i - 1] - digit = if (cDigit in '0'..'9') cDigit - '0' else cDigit - 'A' - - // every 2nd number multiply with 2 - if (i % 2 == 1) digit *= 2 - sum += if (digit > 9) digit - 9 else digit - } - val lunh = (10 - sum % 10) % 10 - return cardId.substring(0, 15) + String.format("%d", lunh) - } - } -} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt index 5412ab7c15..0ef2666e1d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt @@ -3,8 +3,8 @@ package com.tangem.tangemtest.extensions import android.app.Application import android.content.Context import com.tangem.commands.* -import com.tangem.commands.personalization.CardConfig -import com.tangem.commands.personalization.NdefRecord +import com.tangem.commands.personalization.entities.CardConfig +import com.tangem.commands.personalization.entities.NdefRecord import java.util.* fun CardConfig.Companion.init(application: Application): CardConfig {