Updated on 2026-08-14
This commit is contained in:
parent
3d3d561519
commit
383f963155
12 changed files with 418 additions and 0 deletions
1
tangem-card-new/.gitignore
vendored
Normal file
1
tangem-card-new/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
32
tangem-card-new/build.gradle
Normal file
32
tangem-card-new/build.gradle
Normal file
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
23
tangem-card-new/src/main/java/com/tangem/Card.kt
Normal file
23
tangem-card-new/src/main/java/com/tangem/Card.kt
Normal file
|
|
@ -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
|
||||
}
|
||||
16
tangem-card-new/src/main/java/com/tangem/CardEnvironment.kt
Normal file
16
tangem-card-new/src/main/java/com/tangem/CardEnvironment.kt
Normal file
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
53
tangem-card-new/src/main/java/com/tangem/CardManager.kt
Normal file
53
tangem-card-new/src/main/java/com/tangem/CardManager.kt
Normal file
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
||||
}
|
||||
7
tangem-card-new/src/main/java/com/tangem/CardReader.kt
Normal file
7
tangem-card-new/src/main/java/com/tangem/CardReader.kt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem
|
||||
|
||||
import com.tangem.data.ResponseApdu
|
||||
|
||||
interface CardReader {
|
||||
fun sendApdu(apduSerialized: ByteArray, callback: (responseApdu: ResponseApdu) -> Unit)
|
||||
}
|
||||
112
tangem-card-new/src/main/java/com/tangem/Command.kt
Normal file
112
tangem-card-new/src/main/java/com/tangem/Command.kt
Normal file
|
|
@ -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()
|
||||
|
||||
}
|
||||
10
tangem-card-new/src/main/java/com/tangem/DataStorage.kt
Normal file
10
tangem-card-new/src/main/java/com/tangem/DataStorage.kt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem
|
||||
|
||||
interface DataStorage {
|
||||
|
||||
fun getTerminalPublicKey(): ByteArray?
|
||||
fun getTerminalPrivateKey(): ByteArray?
|
||||
fun getPin1(): String?
|
||||
fun getPin2(): String?
|
||||
|
||||
}
|
||||
16
tangem-card-new/src/main/java/com/tangem/Response.kt
Normal file
16
tangem-card-new/src/main/java/com/tangem/Response.kt
Normal file
|
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
114
tangem-card-new/src/main/java/com/tangem/Task.kt
Normal file
114
tangem-card-new/src/main/java/com/tangem/Task.kt
Normal file
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue