Updated on 2026-08-14

This commit is contained in:
Tangem 2020-03-29 17:20:18 +03:00
parent b55b705ee1
commit bfbf4fff95
7 changed files with 195 additions and 127 deletions

View file

@ -1,52 +0,0 @@
{
"issuerData": null,
"issuerName": "TANGEM SDK",
"series": "DB",
"numberFormat": "",
"PIN": "000000",
"PIN2": "000",
"PIN3": "",
"hexCrExKey": "",
"CVC": "000",
"curveID": "ed25519",
"ndef": "com.tangem.wallet",
"startNumber": 300000000,
"count": 20,
"SigningMethod": 0,
"MaxSignatures": 1000000,
"pauseBeforePIN2": 15000,
"smartSecurityDelay": true,
"releaseVersion": false,
"isReusable": true,
"allowSwapPIN": false,
"allowSwapPIN2": true,
"useActivation": false,
"useCVC": false,
"useNDEF": true,
"useDynamicNDEF": true,
"useOneCommandAtTime": false,
"useBlock": false,
"allowSelectBlockchain": false,
"forbidPurgeWallet": false,
"protocolAllowUnencrypted": true,
"protocolAllowStaticEncryption": true,
"protectIssuerDataAgainstReplay": true,
"forbidDefaultPIN": false,
"disablePrecomputedNDEF": false,
"skipSecurityDelayIfValidatedByIssuer": false,
"skipCheckPIN2andCVCIfValidatedByIssuer": false,
"skipSecurityDelayIfValidatedByLinkedTerminal": true,
"restrictOverwriteIssuerDataEx": false,
"requireTerminalTxSignature": false,
"requireTerminalCertSignature": false,
"checkPIN3onCard": false,
"cardData": {
"date": "2020-02-17",
"batch": "FF87",
"blockchain": "XLM",
"product_note": true,
"product_tag": false,
"product_id_card": false
},
"createWallet": true
}

View file

@ -10,25 +10,25 @@ import com.tangem.tasks.TaskEvent
*/
class PersonalizeAction : BaseCardAction() {
override fun executeMainAction(attrs: AttrForAction, callback: ActionCallback) {
var cardId: String? = null
ScanAction().executeMainAction(attrs) { response, b ->
when (response) {
is TaskEvent.Event -> {
val taskEvent = response as TaskEvent.Event
val cardId = handleDataEvent(taskEvent.data)
cardId = handleDataEvent(taskEvent.data)
if (cardId == null) {
handleResponse(response, null, attrs, callback)
return@executeMainAction
}
}
is TaskEvent.Completion -> {
val cardConfig = attrs.payload.remove(cardConfig) as? CardConfig
?: throw IllegalArgumentException("CardConfig must be in the payloads of the ParamsManager")
attrs.cardManager.personalize(cardConfig, cardId) {
attrs.cardManager.personalize(cardConfig, cardId!!) {
handleResponse(it, null, attrs, callback)
}
}
is TaskEvent.Completion -> {
handleResponse(response, null, attrs, callback)
}
}

View file

@ -244,46 +244,47 @@ class PersonalizeConfigConverter {
}?.let { ndefs.add(it) }
return CardConfig(
cardData = cardData,
curveID = EllipticCurve.byName(applyConfig.curveID) ?: EllipticCurve.Secp256k1,
signingMethod = signingMethod,
createWallet = applyConfig.createWallet,
maxSignatures = applyConfig.MaxSignatures.toInt(),
isReusable = applyConfig.isReusable,
protocolAllowUnencrypted = applyConfig.protocolAllowUnencrypted,
protocolAllowStaticEncryption = applyConfig.protocolAllowStaticEncryption,
useActivation = applyConfig.useActivation,
useOneCommandAtTime = applyConfig.oneApdu,
useCvc = applyConfig.useCVC,
useBlock = applyConfig.useBlock,
allowSwapPin = applyConfig.allowSwapPIN,
allowSwapPin2 = applyConfig.allowSwapPIN2,
useNdef = applyConfig.useNDEF,
useDynamicNdef = applyConfig.useDynamicNDEF,
protectIssuerDataAgainstReplay = applyConfig.protectIssuerDataAgainstReplay,
forbidDefaultPin = applyConfig.forbidDefaultPIN,
smartSecurityDelay = applyConfig.smartSecurityDelay,
pauseBeforePin2 = applyConfig.pauseBeforePIN2.toInt(),
allowSelectBlockchain = applyConfig.allowSelectBlockchain,
forbidPurgeWallet = applyConfig.forbidPurgeWallet,
disablePrecomputedNdef = applyConfig.disablePrecomputedNDEF,
skipSecurityDelayIfValidatedByIssuer = applyConfig.skipSecurityDelayIfValidatedByIssuer,
skipCheckPIN2andCVCIfValidatedByIssuer = applyConfig.skipCheckPIN2andCVCIfValidatedByIssuer,
skipSecurityDelayIfValidatedByLinkedTerminal = applyConfig.skipSecurityDelayIfValidatedByLinkedTerminal,
restrictOverwriteIssuerDataEx = applyConfig.restrictOverwriteIssuerDataEx,
requireTerminalTxSignature = applyConfig.requireTerminalTxSignature,
requireTerminalCertSignature = applyConfig.requireTerminalCertSignature,
checkPin3onCard = applyConfig.checkPIN3onCard,
cvc = applyConfig.CVC,
pin = applyConfig.PIN,
pin2 = applyConfig.PIN2,
pin3 = applyConfig.PIN3,
hexCrExKey = applyConfig.hexCrExKey,
ndefRecords = ndefs
"Tangem",
"MyHomeLts",
"HS",
0,
1000,
applyConfig.PIN,
applyConfig.PIN2,
applyConfig.PIN3,
applyConfig.hexCrExKey,
applyConfig.CVC,
applyConfig.pauseBeforePIN2.toInt(),
applyConfig.smartSecurityDelay,
EllipticCurve.byName(applyConfig.curveID) ?: EllipticCurve.Secp256k1,
signingMethod,
applyConfig.MaxSignatures.toInt(),
applyConfig.isReusable,
applyConfig.allowSwapPIN,
applyConfig.allowSwapPIN2,
applyConfig.useActivation,
applyConfig.useCVC,
applyConfig.useNDEF,
applyConfig.useDynamicNDEF,
applyConfig.oneApdu,
applyConfig.useBlock,
applyConfig.allowSelectBlockchain,
applyConfig.forbidPurgeWallet,
applyConfig.protocolAllowUnencrypted,
applyConfig.protocolAllowStaticEncryption,
applyConfig.protectIssuerDataAgainstReplay,
applyConfig.forbidDefaultPIN,
applyConfig.disablePrecomputedNDEF,
applyConfig.skipSecurityDelayIfValidatedByIssuer,
applyConfig.skipCheckPIN2andCVCIfValidatedByIssuer,
applyConfig.skipSecurityDelayIfValidatedByLinkedTerminal,
applyConfig.restrictOverwriteIssuerDataEx,
applyConfig.requireTerminalTxSignature,
applyConfig.requireTerminalCertSignature,
applyConfig.checkPIN3onCard,
applyConfig.createWallet,
cardData,
ndefs
)
}
}

View file

@ -0,0 +1,54 @@
package com.tangem.tangemtest.ucase.variants.personalize.dto
import com.tangem.Config
import com.tangem.commands.personalization.entities.Issuer
import com.tangem.common.KeyPair
/**
[REDACTED_AUTHOR]
*/
interface CardManagerConfig {
companion object {
fun default(): Config {
val issuer = issuer()
return Config(
true,
issuer = issuer(),
issuerPublicKey = null,
acquirerKeyPair = acquirer(),
manufacturerKeyPair = manufacturer()
)
}
private fun issuer(): Issuer {
return Issuer(
name = "TANGEM SDK",
id = "TANGEM SDK" + "\u0000",
dataKeyPair = KeyPair(
"".toByteArray(),
"11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92".toByteArray()
),
transactionKeyPair = KeyPair(
"".toByteArray(),
"11121314151617184771ED81F2BACF57479E4735EB1405081918171615141312".toByteArray()
)
)
}
private fun acquirer(): KeyPair {
return KeyPair(
"".toByteArray(),
"21222324252627284771ED81F2BACF57479E4735EB1405083927372D40DA9E92".toByteArray()
)
}
private fun manufacturer(): KeyPair {
val manufacturer = Manufacturer(Manufacturer.Mode.Developer)
return KeyPair(
manufacturer.publicKey,
manufacturer.privateKey
)
}
}
}

View file

@ -0,0 +1,53 @@
package com.tangem.tangemtest.ucase.variants.personalize.dto
class Manufacturer(private val mode: Mode) {
private val devPrivateKeyArray = byteArrayOf(
0x1B.toByte(), 0x48.toByte(), 0xCF.toByte(), 0xD2.toByte(), 0x4B.toByte(), 0xBB.toByte(), 0x5B.toByte(), 0x39.toByte(),
0x47.toByte(), 0x71.toByte(), 0xED.toByte(), 0x81.toByte(), 0xF2.toByte(), 0xBA.toByte(), 0xCF.toByte(), 0x57.toByte(),
0x47.toByte(), 0x9E.toByte(), 0x47.toByte(), 0x35.toByte(), 0xEB.toByte(), 0x14.toByte(), 0x05.toByte(), 0x08.toByte(),
0x39.toByte(), 0x27.toByte(), 0x37.toByte(), 0x2D.toByte(), 0x40.toByte(), 0xDA.toByte(), 0x9E.toByte(), 0x92.toByte()
)
private val devPublicKeyArray = byteArrayOf(0x04,
0xBA.toByte(), 0xB8.toByte(), 0x6D.toByte(), 0x56.toByte(), 0x29.toByte(), 0x8C.toByte(), 0x99.toByte(), 0x6F.toByte(),
0x56.toByte(), 0x4A.toByte(), 0x84.toByte(), 0xFC.toByte(), 0x88.toByte(), 0xE2.toByte(), 0x8A.toByte(), 0xED.toByte(),
0x38.toByte(), 0x18.toByte(), 0x4B.toByte(), 0x12.toByte(), 0xF0.toByte(), 0x7E.toByte(), 0x51.toByte(), 0x91.toByte(),
0x13.toByte(), 0xBE.toByte(), 0xF4.toByte(), 0x8C.toByte(), 0x76.toByte(), 0xF3.toByte(), 0xDF.toByte(), 0x3A.toByte(),
0xDC.toByte(), 0x30.toByte(), 0x35.toByte(), 0x99.toByte(), 0xB0.toByte(), 0x8A.toByte(), 0xC0.toByte(), 0x5B.toByte(),
0x55.toByte(), 0xEC.toByte(), 0x3D.toByte(), 0xF9.toByte(), 0x8D.toByte(), 0x93.toByte(), 0x38.toByte(), 0x57.toByte(),
0x3A.toByte(), 0x62.toByte(), 0x42.toByte(), 0xF7.toByte(), 0x6F.toByte(), 0x5D.toByte(), 0x28.toByte(), 0xF4.toByte(),
0xF0.toByte(), 0xF3.toByte(), 0x64.toByte(), 0xE8.toByte(), 0x7E.toByte(), 0x8F.toByte(), 0xCA.toByte(), 0x2F.toByte()
)
private val releasePublicKeyArray = byteArrayOf(0x04.toByte(),
0x2E.toByte(), 0xDE.toByte(), 0x11.toByte(), 0x9B.toByte(), 0xF3.toByte(), 0x37.toByte(), 0xB2.toByte(), 0x64.toByte(),
0xFD.toByte(), 0xA1.toByte(), 0x32.toByte(), 0xCF.toByte(), 0xC7.toByte(), 0xC1.toByte(), 0x77.toByte(), 0x82.toByte(),
0x4D.toByte(), 0x36.toByte(), 0x17.toByte(), 0xDA.toByte(), 0xC8.toByte(), 0x0F.toByte(), 0x25.toByte(), 0xDB.toByte(),
0xB2.toByte(), 0xA4.toByte(), 0xA8.toByte(), 0xA1.toByte(), 0x18.toByte(), 0x3C.toByte(), 0x03.toByte(), 0xB9.toByte(),
0x15.toByte(), 0x23.toByte(), 0x05.toByte(), 0xF8.toByte(), 0xF1.toByte(), 0xDB.toByte(), 0x97.toByte(), 0x00.toByte(),
0x45.toByte(), 0x18.toByte(), 0x48.toByte(), 0x0D.toByte(), 0x50.toByte(), 0x91.toByte(), 0xAD.toByte(), 0xC1.toByte(),
0xCA.toByte(), 0xB9.toByte(), 0xEA.toByte(), 0xCC.toByte(), 0xC1.toByte(), 0x8E.toByte(), 0x1B.toByte(), 0x9E.toByte(),
0x9C.toByte(), 0x3B.toByte(), 0xEF.toByte(), 0xB2.toByte(), 0x93.toByte(), 0xDD.toByte(), 0x37.toByte(), 0xB2.toByte()
)
val publicKey: ByteArray
get() = if (mode == Mode.Developer) {
devPublicKeyArray
} else {
releasePublicKeyArray
}
@get:Throws(Exception::class)
val privateKey: ByteArray
get() = if (mode == Mode.Developer) {
devPrivateKeyArray
} else {
throw Exception("Real manufacturer key is very secret!")
}
val name: String
get() = if (mode == Mode.Release) "SMART CASH" else "SMART CASH SDK"
enum class Mode {
Developer, Release
}
}

View file

@ -1,6 +1,5 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -13,7 +12,10 @@ import androidx.lifecycle.Observer
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.tangem.CardManager
import com.tangem.tangem_sdk_new.extensions.init
import com.tangem.tangem_sdk_new.DefaultCardManagerDelegate
import com.tangem.tangem_sdk_new.NfcLifecycleObserver
import com.tangem.tangem_sdk_new.TerminalKeysStorage
import com.tangem.tangem_sdk_new.nfc.NfcManager
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.abstraction.Block
import com.tangem.tangemtest._main.MainViewModel
@ -23,12 +25,11 @@ import com.tangem.tangemtest.ucase.domain.paramsManager.ParamsManagerFactory
import com.tangem.tangemtest.ucase.resources.ActionType
import com.tangem.tangemtest.ucase.ui.ActionViewModelFactory
import com.tangem.tangemtest.ucase.ui.ParamsViewModel
import com.tangem.tangemtest.ucase.variants.personalize.dto.CardManagerConfig
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.WidgetBuilder
import kotlinx.android.synthetic.main.fg_personalize.*
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
import java.io.IOException
import java.io.InputStream
import java.nio.charset.Charset
/**
[REDACTED_AUTHOR]
@ -36,7 +37,7 @@ import java.nio.charset.Charset
class PersonalizeFragment : Fragment() {
private val mainActivityVM: MainViewModel by activityViewModels()
private val personalizeVM: PersonalizeViewModel by viewModels { PersonalizeViewModelFactory(getPersonalizeJson()) }
private val personalizeVM: PersonalizeViewModel by viewModels { PersonalizeViewModelFactory(PersonalizeConfig()) }
private val paramsManager: ParamsManager by lazy { ParamsManagerFactory.createFactory().get(ActionType.Personalize)!! }
private val paramsVM: ParamsViewModel by viewModels() { ActionViewModelFactory(paramsManager) }
@ -55,6 +56,8 @@ class PersonalizeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val cardManager = initCardManager() ?: return
val blockList = mutableListOf<Block>()
personalizeVM.ldBlockList.observe(viewLifecycleOwner, Observer { list ->
blockList.clear()
@ -65,7 +68,7 @@ class PersonalizeFragment : Fragment() {
personalizeVM.toggleDescriptionVisibility(it)
})
paramsVM.setCardManager(CardManager.init(requireActivity()))
paramsVM.setCardManager(cardManager)
paramsVM.ldResponse.observe(viewLifecycleOwner, Observer {
Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}")
showSnackbarMessage(it)
@ -78,27 +81,37 @@ class PersonalizeFragment : Fragment() {
}
}
private fun initCardManager(): CardManager? {
val activity = activity ?: return null
val nfcManager = NfcManager().apply {
this.setCurrentActivity(activity)
activity.lifecycle.addObserver(NfcLifecycleObserver(this))
}
val cardManagerDelegate = DefaultCardManagerDelegate(nfcManager.reader).apply {
this.activity = activity
}
return CardManager(nfcManager.reader, cardManagerDelegate, CardManagerConfig.default()).apply {
this.setTerminalKeysService(TerminalKeysStorage(activity.application))
}
}
protected fun showSnackbarMessage(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}
private fun getPersonalizeJson(): String {
return getJsonFromAssets(requireContext(), "personalize_default.json") ?: "[]"
}
private fun getJsonFromAssets(context: Context, fileName: String): String? {
return try {
val stream: InputStream = context.assets.open(fileName)
val size: Int = stream.available()
val buffer = ByteArray(size)
stream.read(buffer)
stream.close()
String(buffer, Charset.forName("UTF-8"))
} catch (e: IOException) {
e.printStackTrace()
null
}
}
// private fun getJsonFromAssets(context: Context, fileName: String): String? {
// return try {
// val stream: InputStream = context.assets.open(fileName)
// val size: Int = stream.available()
// val buffer = ByteArray(size)
// stream.read(buffer)
// stream.close()
// String(buffer, Charset.forName("UTF-8"))
// } catch (e: IOException) {
// e.printStackTrace()
// null
// }
// }
}

View file

@ -14,18 +14,17 @@ import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
/**
[REDACTED_AUTHOR]
*/
class PersonalizeViewModelFactory(private val jsonPersonalizeString: String) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = PersonalizeViewModel(jsonPersonalizeString) as T
class PersonalizeViewModelFactory(private val config: PersonalizeConfig) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = PersonalizeViewModel(config) as T
}
class PersonalizeViewModel(private val jsonPersonalizeString: String) : ViewModel() {
class PersonalizeViewModel(private val config: PersonalizeConfig) : ViewModel() {
val ldBlockList: MutableLiveData<List<Block>> by lazy { MutableLiveData(initBlockList()) }
private fun initBlockList(): List<Block> = parseJsonToBlockList(jsonPersonalizeString)
private fun initBlockList(): List<Block> = createBlocksFromConfig(config)
fun parseJsonToBlockList(jsonString: String): List<Block> {
val config = Gson().fromJson(jsonString, PersonalizeConfig::class.java)
fun createBlocksFromConfig(config: PersonalizeConfig): List<Block> {
return PersonalizeConfigConverter().toBlock(config)
}