From 383f963155534c8d739f717e39f42c8eca73a906 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Oct 2019 11:51:31 +0300 Subject: [PATCH] Updated on 2026-08-14 --- tangem-card-new/.gitignore | 1 + tangem-card-new/build.gradle | 32 +++++ .../src/main/java/com/tangem/Card.kt | 23 ++++ .../main/java/com/tangem/CardEnvironment.kt | 16 +++ .../com/tangem/CardEnvironmentRepository.kt | 22 ++++ .../src/main/java/com/tangem/CardManager.kt | 53 ++++++++ .../java/com/tangem/CardManagerDelegate.kt | 12 ++ .../src/main/java/com/tangem/CardReader.kt | 7 ++ .../src/main/java/com/tangem/Command.kt | 112 +++++++++++++++++ .../src/main/java/com/tangem/DataStorage.kt | 10 ++ .../src/main/java/com/tangem/Response.kt | 16 +++ .../src/main/java/com/tangem/Task.kt | 114 ++++++++++++++++++ 12 files changed, 418 insertions(+) create mode 100644 tangem-card-new/.gitignore create mode 100644 tangem-card-new/build.gradle create mode 100644 tangem-card-new/src/main/java/com/tangem/Card.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/CardEnvironment.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/CardEnvironmentRepository.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/CardManager.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/CardManagerDelegate.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/CardReader.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/Command.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/DataStorage.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/Response.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/Task.kt diff --git a/tangem-card-new/.gitignore b/tangem-card-new/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/tangem-card-new/.gitignore @@ -0,0 +1 @@ +/build diff --git a/tangem-card-new/build.gradle b/tangem-card-new/build.gradle new file mode 100644 index 0000000000..74697f2588 --- /dev/null +++ b/tangem-card-new/build.gradle @@ -0,0 +1,32 @@ +apply plugin: "kotlin" + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" +} + +sourceCompatibility = "8" +targetCompatibility = "8" + +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} +repositories { + mavenCentral() +} +compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} +compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} diff --git a/tangem-card-new/src/main/java/com/tangem/Card.kt b/tangem-card-new/src/main/java/com/tangem/Card.kt new file mode 100644 index 0000000000..df131c8ad6 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/Card.kt @@ -0,0 +1,23 @@ +package com.tangem + +data class Card( + val cid: String +) { + +// private var CID: ByteArray? = null +// private var status: Status? = null +// private var UID: String? = null +// private var blockchainID: String? = null +// private var manufacturer = Manufacturer.Unknown +// private var manufacturerConfirmed = false +// private var maxSignatures: Int = 0 +// private var remainingSignatures: Int = 0 +// private var PIN: String? = null +// private var pbWalletKey: ByteArray? = null +// private var pbCardKey: ByteArray? = null +// private var pbWalletKeyRar: ByteArray? = null +// private var dtPersonalization: Date? = null +// private var terminalPrivateKey: ByteArray? = null +// private var terminalPublicKey: ByteArray? = null +// private var terminalIsLinked = false +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/CardEnvironment.kt b/tangem-card-new/src/main/java/com/tangem/CardEnvironment.kt new file mode 100644 index 0000000000..7a3194cbcb --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/CardEnvironment.kt @@ -0,0 +1,16 @@ +package com.tangem + + + +data class CardEnvironment( + val pin1: String = DEFAULT_PIN, + val pin2: String = DEFAULT_PIN2, + val terminalPublicKey: ByteArray? = null, + val terminalPrivateKey: ByteArray? = null +) { + + companion object { + const val DEFAULT_PIN = "000000" + const val DEFAULT_PIN2 = "000" + } +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/CardEnvironmentRepository.kt b/tangem-card-new/src/main/java/com/tangem/CardEnvironmentRepository.kt new file mode 100644 index 0000000000..cc3793f8e3 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/CardEnvironmentRepository.kt @@ -0,0 +1,22 @@ +package com.tangem + +class CardEnvironmentRepository(val dataStorage: DataStorage?) { + + var cardEnvironment: CardEnvironment = initCardEnvironment() + set(value) { + if (cardEnvironment != value) { + saveCardEnvirnoment(value) + } + } + + private fun initCardEnvironment(): CardEnvironment { + return CardEnvironment( + dataStorage?.getPin1() ?: CardEnvironment.DEFAULT_PIN, + dataStorage?.getPin2() ?: CardEnvironment.DEFAULT_PIN2 + ) + } + + private fun saveCardEnvirnoment(cardEnvironment: CardEnvironment) { + //TODO: logic for saving fields in persistence storage + } +} diff --git a/tangem-card-new/src/main/java/com/tangem/CardManager.kt b/tangem-card-new/src/main/java/com/tangem/CardManager.kt new file mode 100644 index 0000000000..4abdfe4be8 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/CardManager.kt @@ -0,0 +1,53 @@ +package com.tangem + +open class CardManager( + private val reader: CardReader, + private val cardManagerDelegate: CardManagerDelegate? = null, + dataStorage: DataStorage? = null) { + + private val cardEnvironmentRepository = CardEnvironmentRepository(dataStorage) + + var card: Card? = null + private set + + fun scanCard(callback: (result: TaskResult) -> Unit) { + + val task = ScanTask(cardManagerDelegate, reader) + + runTask(task, cardEnvironmentRepository.cardEnvironment) { + if (it is TaskResult.CommandCompleted && it.resultData is Card) { + this.card = it.resultData + } + callback.invoke(it) + } + } + + fun signTransaction(signCommandData: SignCommandData, + callback: (result: TaskResult) -> Unit) { + val task = SignTask(signCommandData, cardManagerDelegate, reader) + runTask(task, cardEnvironmentRepository.cardEnvironment, callback) + } + + fun runTask(task: Task, environment: CardEnvironment? = null, + callback: (result: TaskResult) -> Unit) { + + task.run(environment ?: cardEnvironmentRepository.cardEnvironment) {result, returnedEnvironment -> + //TODO: environment is kept in Repository + cardEnvironmentRepository.cardEnvironment = returnedEnvironment + callback.invoke(result) + } + } + + fun runCommand(command: Command, + environment: CardEnvironment? = null, + callback: (result: TaskResult) -> Unit) { + val task = BasicTask(command, cardManagerDelegate, reader) + runTask(task, environment, callback) + } +} + +sealed class TaskResult { + data class Success(val resultData: Any) : TaskResult() + data class CommandCompleted(val resultData: Any) : TaskResult() + data class Error(val error: CardError) : TaskResult() +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/CardManagerDelegate.kt b/tangem-card-new/src/main/java/com/tangem/CardManagerDelegate.kt new file mode 100644 index 0000000000..f281c3e857 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/CardManagerDelegate.kt @@ -0,0 +1,12 @@ +package com.tangem + +interface CardManagerDelegate { + + fun showSecurityDelay(seconds: Int) + fun hideSecurityDelay() + fun requestPin(success: () -> String, error: (cardError: CardError) -> CardError) + + fun openNfcPopup() + fun closeNfcPopup() + +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/CardReader.kt b/tangem-card-new/src/main/java/com/tangem/CardReader.kt new file mode 100644 index 0000000000..9557a05e05 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/CardReader.kt @@ -0,0 +1,7 @@ +package com.tangem + +import com.tangem.data.ResponseApdu + +interface CardReader { + fun sendApdu(apduSerialized: ByteArray, callback: (responseApdu: ResponseApdu) -> Unit) +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/Command.kt b/tangem-card-new/src/main/java/com/tangem/Command.kt new file mode 100644 index 0000000000..16a6d73623 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/Command.kt @@ -0,0 +1,112 @@ +package com.tangem + +import com.tangem.data.CommandApdu +import com.tangem.data.ResponseApduParsed +import com.tangem.data.Tlv +import com.tangem.enums.Instruction +import com.tangem.enums.TlvTag + +abstract class Command { + + abstract val instruction: Instruction + abstract val instructionCode: Int + + abstract fun serialize(cardEnvironment: CardEnvironment): CommandApdu + + abstract fun deserialize(responseApduParsed: ResponseApduParsed): TaskResult +} + + +class ReadCardCommand(val pin1: String) : Command() { + + override val instruction = Instruction.Read + override val instructionCode = instruction.code + + override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + + return CommandApdu( + listOf(Tlv(TlvTag.Pin, TlvTag.Pin.code, pin1.toByteArray())), instruction) + } + + override fun deserialize(responseApduParsed: ResponseApduParsed): TaskResult { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + +} + +class CheckWalletCommand( + val pin1: String, val cid: String, + val challenge: ByteArray, val publicKeyChallenge: ByteArray) : Command() { + + override val instruction = Instruction.CheckWallet + override val instructionCode = instruction.code + + override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun deserialize(responseApduParsed: ResponseApduParsed): TaskResult { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + +} + +class SignCommand(val signCommandData: SignCommandData) : Command() { + + override val instruction = Instruction.Sign + override val instructionCode = instruction.code + + override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun deserialize(responseApduParsed: ResponseApduParsed): TaskResult { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + +} + + + +data class SignCommandData( + val cid: String, + val pin1: String, + val pin2: String, + val cvc: String, + val transactionHash: TransactionHash, + val issuerData: IssuerData, + val terminalPublicKey: ByteArray? = null, + val terminalTransactionSignature: ByteArray? = null + +) + +data class TransactionHash( + val transactionHash: ByteArray, + val transactionRaw: ByteArray, + val hashName: String +) + +data class IssuerData( + val issuerData: ByteArray, + val issuerDataCounter: Int, + val issuerTransactionSignature: ByteArray, + val issuerDataSignature: ByteArray +) + +sealed class CommandData { + data class CheckWalletResponse( + val cid: String, + val salt: ByteArray, + val walletSignature: ByteArray + ) +} + +sealed class CommandResult{ + + data class Success(val data: CommandData) : CommandResult() + data class Error(val error: CardError) : CommandResult() + +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/DataStorage.kt b/tangem-card-new/src/main/java/com/tangem/DataStorage.kt new file mode 100644 index 0000000000..b1c531c671 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/DataStorage.kt @@ -0,0 +1,10 @@ +package com.tangem + +interface DataStorage { + + fun getTerminalPublicKey(): ByteArray? + fun getTerminalPrivateKey(): ByteArray? + fun getPin1(): String? + fun getPin2(): String? + +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/Response.kt b/tangem-card-new/src/main/java/com/tangem/Response.kt new file mode 100644 index 0000000000..56923291f3 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/Response.kt @@ -0,0 +1,16 @@ +package com.tangem + +data class SignResponse( + val cid: String, + val signature: ByteArray, + val remainingSignatures: Int, + val signedHashes: Int +) + + +data class CardError( + val code: Int = 0 +) + + + diff --git a/tangem-card-new/src/main/java/com/tangem/Task.kt b/tangem-card-new/src/main/java/com/tangem/Task.kt new file mode 100644 index 0000000000..4067aeb238 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/Task.kt @@ -0,0 +1,114 @@ +package com.tangem + +abstract class Task(protected val delegate: CardManagerDelegate? = null, private val reader: CardReader) { + + abstract fun run(cardEnvironment: CardEnvironment, + callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) + + protected fun executeCommand(command: Command, cardEnvironment: CardEnvironment, + callback: (result: TaskResult) -> Unit) { + + reader.sendApdu(command.serialize(cardEnvironment).serialize()) { + val parsedApdu = it.deserialize() + if (parsedApdu == null) callback.invoke(TaskResult.Error(CardError())) + else if (!parsedApdu.statusCompleted()) { + callback.invoke(TaskResult.Error(CardError(parsedApdu.status.code))) + } else { + val response = command.deserialize(parsedApdu) + callback.invoke(response) + } + } + } +} + + +class BasicTask( + private val command: Command, + delegate: CardManagerDelegate? = null, + reader: CardReader) : Task(delegate, reader) { + + override fun run(cardEnvironment: CardEnvironment, + callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) { + executeCommand(command, cardEnvironment) { + callback.invoke(it, updateEnvironment()) + } + } + + private fun updateEnvironment() : CardEnvironment{ + //TODO: set logic of changing CardEnvironment when needed + return CardEnvironment() + } +} + + +class SignTask(private val signCommandData: SignCommandData, + delegate: CardManagerDelegate? = null, + reader: CardReader) : Task(delegate, reader) { + + private lateinit var callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit + + override fun run(cardEnvironment: CardEnvironment, + callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) { + + this.callback = callback + + delegate?.openNfcPopup() + val signCommand = SignCommand(signCommandData) + + executeCommand(signCommand, cardEnvironment) { + delegate?.closeNfcPopup() + run { callback(it, updateEnvironment()) } + } + } + + private fun updateEnvironment() : CardEnvironment{ + //TODO: set logic of changing CardEnvironment when needed + return CardEnvironment() + } +} + + +class ScanTask(delegate: CardManagerDelegate? = null, reader: CardReader) : Task(delegate, reader) { + + private lateinit var card: Card + + override fun run(cardEnvironment: CardEnvironment, + callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) { + + delegate?.openNfcPopup() + val readCommand = ReadCardCommand(cardEnvironment.pin1) + executeCommand(readCommand, cardEnvironment) { + + if (it is TaskResult.Success) { + card = it.resultData as Card + val checkWalletCommand = prepareCheckWalletCommand(cardEnvironment) + + executeCommand(checkWalletCommand, cardEnvironment) { + delegate?.closeNfcPopup() + //TODO: pass check wallet response data + run { callback(it, updateEnvironment()) } + } + } else { + //TODO: Handle error + } + } + } + + private fun updateEnvironment() : CardEnvironment{ + //TODO: set logic of changing CardEnvironment when needed + return CardEnvironment() + } + + private fun prepareCheckWalletCommand(cardEnvironment: CardEnvironment): CheckWalletCommand { + val challenge = generateChallenge() + return CheckWalletCommand( + cardEnvironment.pin1, + card.cid, + challenge, + byteArrayOf()) + } + + private fun generateChallenge(): ByteArray { + return byteArrayOf() + } +} \ No newline at end of file