From da003bf0d3e38da727f9ebbdaca46362473cd957 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 18 Oct 2019 18:30:41 +0300 Subject: [PATCH] Updated on 2026-08-14 --- tangem-card-new/build.gradle | 3 + .../main/java/com/tangem/CardEnvironment.kt | 6 + .../src/main/java/com/tangem/CardManager.kt | 36 ++--- .../java/com/tangem/CardManagerDelegate.kt | 4 +- .../src/main/java/com/tangem/CardReader.kt | 2 +- .../src/main/java/com/tangem/Command.kt | 112 ------------- .../src/main/java/com/tangem/Response.kt | 26 +-- .../src/main/java/com/tangem/Task.kt | 114 ------------- .../com/tangem/commands/CheckWalletCommand.kt | 53 ++++++ .../com/tangem/commands/CommandSerializer.kt | 59 +++++++ .../com/tangem/commands/ReadCardCommand.kt | 152 ++++++++++++++++++ .../java/com/tangem/commands/SignCommand.kt | 51 ++++++ .../main/java/com/tangem/tasks/ScanTask.kt | 90 +++++++++++ .../main/java/com/tangem/tasks/SignTask.kt | 32 ++++ .../src/main/java/com/tangem/tasks/Task.kt | 102 ++++++++++++ 15 files changed, 577 insertions(+), 265 deletions(-) delete mode 100644 tangem-card-new/src/main/java/com/tangem/Command.kt delete mode 100644 tangem-card-new/src/main/java/com/tangem/Task.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/commands/CheckWalletCommand.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/commands/CommandSerializer.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/commands/ReadCardCommand.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/commands/SignCommand.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/tasks/ScanTask.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/tasks/SignTask.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/tasks/Task.kt diff --git a/tangem-card-new/build.gradle b/tangem-card-new/build.gradle index 74697f2588..4ff0558797 100644 --- a/tangem-card-new/build.gradle +++ b/tangem-card-new/build.gradle @@ -3,6 +3,9 @@ apply plugin: "kotlin" dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + implementation "com.madgag.spongycastle:core:1.56.0.0" + implementation "com.madgag.spongycastle:prov:1.56.0.0" + implementation 'net.i2p.crypto:eddsa:0.3.0' } sourceCompatibility = "8" diff --git a/tangem-card-new/src/main/java/com/tangem/CardEnvironment.kt b/tangem-card-new/src/main/java/com/tangem/CardEnvironment.kt index 7a3194cbcb..b0e5191c8d 100644 --- a/tangem-card-new/src/main/java/com/tangem/CardEnvironment.kt +++ b/tangem-card-new/src/main/java/com/tangem/CardEnvironment.kt @@ -13,4 +13,10 @@ data class CardEnvironment( const val DEFAULT_PIN = "000000" const val DEFAULT_PIN2 = "000" } +} + +enum class EncryptionMode(val code: Byte) { + NONE(0x0), + FAST(0x1), + STRONG(0x2) } \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/CardManager.kt b/tangem-card-new/src/main/java/com/tangem/CardManager.kt index 4abdfe4be8..8fc59eb08f 100644 --- a/tangem-card-new/src/main/java/com/tangem/CardManager.kt +++ b/tangem-card-new/src/main/java/com/tangem/CardManager.kt @@ -1,36 +1,30 @@ package com.tangem -open class CardManager( +import com.tangem.commands.CommandResponse +import com.tangem.commands.CommandSerializer +import com.tangem.commands.SignCommandData +import com.tangem.tasks.* + +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) - } + runTask(task, cardEnvironmentRepository.cardEnvironment, callback) } - fun signTransaction(signCommandData: SignCommandData, - callback: (result: TaskResult) -> Unit) { + fun sign(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) { - + callback: (result: TaskResult) -> Unit) { task.run(environment ?: cardEnvironmentRepository.cardEnvironment) {result, returnedEnvironment -> //TODO: environment is kept in Repository cardEnvironmentRepository.cardEnvironment = returnedEnvironment @@ -38,16 +32,10 @@ open class CardManager( } } - fun runCommand(command: Command, + fun runCommand(commandSerializer: CommandSerializer, environment: CardEnvironment? = null, callback: (result: TaskResult) -> Unit) { - val task = BasicTask(command, cardManagerDelegate, reader) + val task = BasicTask(commandSerializer, 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 index f281c3e857..ff835bf94f 100644 --- a/tangem-card-new/src/main/java/com/tangem/CardManagerDelegate.kt +++ b/tangem-card-new/src/main/java/com/tangem/CardManagerDelegate.kt @@ -1,10 +1,12 @@ package com.tangem +import com.tangem.tasks.TaskError + interface CardManagerDelegate { fun showSecurityDelay(seconds: Int) fun hideSecurityDelay() - fun requestPin(success: () -> String, error: (cardError: CardError) -> CardError) + fun requestPin(success: () -> String, error: (cardError: TaskError.CardError) -> TaskError.CardError) fun openNfcPopup() fun closeNfcPopup() diff --git a/tangem-card-new/src/main/java/com/tangem/CardReader.kt b/tangem-card-new/src/main/java/com/tangem/CardReader.kt index 9557a05e05..784e084282 100644 --- a/tangem-card-new/src/main/java/com/tangem/CardReader.kt +++ b/tangem-card-new/src/main/java/com/tangem/CardReader.kt @@ -1,6 +1,6 @@ package com.tangem -import com.tangem.data.ResponseApdu +import com.tangem.common.apdu.ResponseApdu interface CardReader { fun sendApdu(apduSerialized: ByteArray, callback: (responseApdu: ResponseApdu) -> Unit) diff --git a/tangem-card-new/src/main/java/com/tangem/Command.kt b/tangem-card-new/src/main/java/com/tangem/Command.kt deleted file mode 100644 index 16a6d73623..0000000000 --- a/tangem-card-new/src/main/java/com/tangem/Command.kt +++ /dev/null @@ -1,112 +0,0 @@ -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/Response.kt b/tangem-card-new/src/main/java/com/tangem/Response.kt index 56923291f3..33b18cee40 100644 --- a/tangem-card-new/src/main/java/com/tangem/Response.kt +++ b/tangem-card-new/src/main/java/com/tangem/Response.kt @@ -1,16 +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 -) - +// +//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 deleted file mode 100644 index 4067aeb238..0000000000 --- a/tangem-card-new/src/main/java/com/tangem/Task.kt +++ /dev/null @@ -1,114 +0,0 @@ -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 diff --git a/tangem-card-new/src/main/java/com/tangem/commands/CheckWalletCommand.kt b/tangem-card-new/src/main/java/com/tangem/commands/CheckWalletCommand.kt new file mode 100644 index 0000000000..009f6293fe --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/commands/CheckWalletCommand.kt @@ -0,0 +1,53 @@ +package com.tangem.commands + +import com.tangem.CardEnvironment +import com.tangem.common.apdu.CommandApdu +import com.tangem.common.apdu.ResponseApdu +import com.tangem.common.extentions.calculateSha256 +import com.tangem.common.extentions.hexToBytes +import com.tangem.common.tlv.Tlv +import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvTag +import com.tangem.enums.Instruction + +class CheckWalletResponse( + val cardId: String, + val salt: ByteArray, + val walletSignature: ByteArray +) : CommandResponse + + +class CheckWalletCommand( + val pin1: String, val cid: String, + val challenge: ByteArray, val publicKeyChallenge: ByteArray) : CommandSerializer() { + + override val instruction = Instruction.CheckWallet + override val instructionCode = instruction.code + + override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + val tlvData = listOf( + Tlv(TlvTag.Pin, TlvTag.Pin.code, cardEnvironment.pin1.calculateSha256()), + Tlv(TlvTag.CardId, TlvTag.CardId.code, cid.hexToBytes()), + Tlv(TlvTag.Challenge, TlvTag.Challenge.code, challenge) + ) + + return CommandApdu(instructionCode, tlvData) + } + + override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): CheckWalletResponse? { + val tlvData = responseApdu.getTlvData() ?: return null + + return try { + val mapper = TlvMapper(tlvData) + CheckWalletResponse( + mapper.map(TlvTag.CardId), + mapper.map(TlvTag.Salt), + mapper.map(TlvTag.Signature) + ) + } catch (exception: Exception) { + null + } + } + + +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/commands/CommandSerializer.kt b/tangem-card-new/src/main/java/com/tangem/commands/CommandSerializer.kt new file mode 100644 index 0000000000..dec341e879 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/commands/CommandSerializer.kt @@ -0,0 +1,59 @@ +package com.tangem.commands + +import com.tangem.CardEnvironment +import com.tangem.common.apdu.CommandApdu +import com.tangem.common.apdu.ResponseApdu +import com.tangem.enums.Instruction + +//interface TlvMappable { +// fun responseFromTlv(tlvList: List): CommandResponse? +//} + +interface CommandResponse +// : TlvMappable { +// +//} + + +abstract class CommandSerializer() { + + abstract val instruction: Instruction + abstract val instructionCode: Int + + abstract fun serialize(cardEnvironment: CardEnvironment): CommandApdu + abstract fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): T? +} + +sealed class CommandEvent { + class Success(val response: Any) : CommandEvent() + class Failure() : CommandEvent() + class UserCancellation : CommandEvent() +} + +//class CheckWalletResponse( +// val cardId: String, +// val salt: ByteArray, +// val walletSignature: ByteArray +//) : CommandResponse, TlvMappable by CheckWalletResponse { +// +// companion object : TlvMappable { +// override fun responseFromTlv(tlvList: List): CommandResponse? { +// val mapper = TlvMapper(tlvList) +// +// return try { +// CheckWalletResponse( +// mapper.map(TlvTag.CardId), +// mapper.map(TlvTag.Salt), +// mapper.map(TlvTag.Signature) +// ) +// } catch (exception: TlvMapperException) { +// null +// } +// } +// +// } +//} + + + + diff --git a/tangem-card-new/src/main/java/com/tangem/commands/ReadCardCommand.kt b/tangem-card-new/src/main/java/com/tangem/commands/ReadCardCommand.kt new file mode 100644 index 0000000000..4c22b34e62 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/commands/ReadCardCommand.kt @@ -0,0 +1,152 @@ +package com.tangem.commands + +import com.tangem.CardEnvironment +import com.tangem.common.apdu.CommandApdu +import com.tangem.common.apdu.ResponseApdu +import com.tangem.common.extentions.calculateSha256 +import com.tangem.common.tlv.Tlv +import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvTag +import com.tangem.data.SettingsMask +import com.tangem.enums.Instruction + +enum class SigningMethod(val code: Int) { + SignHash(0), + SignRaw(1), + SignHashValidatedByIssuer(2), + SignRawValidatedByIssuer(3), + SignHashValidatedByIssuerAndWriteIssuerData(4), + SignRawValidatedByIssuerAndWriteIssuerData(5), + SignPos(6); + + companion object { + fun byCode(code: Int): SigningMethod? = values().find { it.code == code } + } +} + +enum class EllipticCurve(val curve: String) { + Secp256k1("secp256k1"), + Ed25519("ed25519"); + + companion object { + fun byName(curve: String): EllipticCurve? = values().find { it.curve == curve } + } +} + +enum class CardStatus(val code: Int) { + NotPersonalized(0), + Empty(1), + Loaded(2), + Purged(3); + + companion object { + fun byCode(code: Int): CardStatus? = values().find { it.code == code } + } +} + +enum class ProductMask(val code: Byte) { + Note(0x01), + Tag(0x02), + Card(0x04); + + companion object { + fun byCode(code: Byte): ProductMask? = values().find { it.code == code } + } +} + +class ReadCardResponse( + val cardId: String, + val manufacturerName: String, + val status: CardStatus, + + val firmwareVersion: String?, + val cardPublicKey: String?, + val settingsMask: SettingsMask?, + val issuerPublicKey: String?, + val curve: EllipticCurve?, + val maxSignatures: Int?, + val signingMethpod: SigningMethod?, + val pauseBeforePin2: Int?, + val walletPublicKey: ByteArray?, + val walletRemainingSignatures: Int?, + val walletSignedHashes: Int?, + val health: Int?, + val isActivated: Boolean?, + val activationSeed: ByteArray?, + val paymentFlowVersion: ByteArray?, + val userCounter: Int?, + + //Card Data + val batchId: Int?, + val manufactureDateTime: String?, + val issuerName: String?, + val blockchainName: String?, + val manufacturerSignature: ByteArray?, + val productMask: ProductMask?, + + val tokenSymbol: String?, + val tokenContractAddress: String?, + val tokenDecimal: Int? +) : CommandResponse + + +class ReadCardCommand(private val pin1: String) : CommandSerializer() { + + override val instruction = Instruction.Read + override val instructionCode = instruction.code + + override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + + val tlvData = listOf( + Tlv(TlvTag.Pin, TlvTag.Pin.code, pin1.calculateSha256()) + ) + + return CommandApdu(instructionCode, tlvData) + } + + override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): ReadCardResponse? { + val tlvData = responseApdu.getTlvData() ?: return null + + return try { + val tlvMapper = TlvMapper(tlvData) + + ReadCardResponse( + tlvMapper.map(TlvTag.CardId), + tlvMapper.map(TlvTag.ManufactureId), + tlvMapper.map(TlvTag.Status), + + tlvMapper.mapOptional(TlvTag.Firmware), + tlvMapper.mapOptional(TlvTag.CardPublicKey), + tlvMapper.mapOptional(TlvTag.SettingsMask), + tlvMapper.mapOptional(TlvTag.IssuerDataPublicKey), + tlvMapper.mapOptional(TlvTag.CurveId), + tlvMapper.mapOptional(TlvTag.MaxSignatures), + tlvMapper.mapOptional(TlvTag.SigningMethod), + tlvMapper.mapOptional(TlvTag.PauseBeforePin2), + tlvMapper.mapOptional(TlvTag.WalletPublicKey), + tlvMapper.mapOptional(TlvTag.RemainingSignatures), + tlvMapper.mapOptional(TlvTag.SignedHashes), + tlvMapper.mapOptional(TlvTag.Health), + tlvMapper.mapOptional(TlvTag.IsActivated), + tlvMapper.mapOptional(TlvTag.ActivationSeed), + tlvMapper.mapOptional(TlvTag.PaymentFlowVersion), + tlvMapper.mapOptional(TlvTag.UserCounter), + + tlvMapper.mapOptional(TlvTag.Batch), + tlvMapper.mapOptional(TlvTag.ManufactureDateTime), + tlvMapper.mapOptional(TlvTag.IssuerId), + tlvMapper.mapOptional(TlvTag.BlockchainId), + tlvMapper.mapOptional(TlvTag.ManufacturerSignature), + tlvMapper.mapOptional(TlvTag.ProductMask), + + tlvMapper.mapOptional(TlvTag.TokenSymbol), + tlvMapper.mapOptional(TlvTag.TokenContractAddress), + tlvMapper.mapOptional(TlvTag.TokenDecimal) + ) + } catch (exception: Exception) { + null + } + } + + +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/commands/SignCommand.kt b/tangem-card-new/src/main/java/com/tangem/commands/SignCommand.kt new file mode 100644 index 0000000000..68e2a8284f --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/commands/SignCommand.kt @@ -0,0 +1,51 @@ +package com.tangem.commands + +import com.tangem.CardEnvironment +import com.tangem.common.apdu.CommandApdu +import com.tangem.common.apdu.ResponseApdu +import com.tangem.enums.Instruction + +class SignCommand(val signCommandData: SignCommandData) : CommandSerializer() { + + 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(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): SignCommandResponse? { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} + +class SignCommandResponse( + +) : CommandResponse + + +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 +) \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/tasks/ScanTask.kt b/tangem-card-new/src/main/java/com/tangem/tasks/ScanTask.kt new file mode 100644 index 0000000000..edcba623d8 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/tasks/ScanTask.kt @@ -0,0 +1,90 @@ +package com.tangem.tasks + +import com.tangem.CardEnvironment +import com.tangem.CardManagerDelegate +import com.tangem.CardReader +import com.tangem.commands.* +import com.tangem.crypto.generateRandomBytes +import com.tangem.crypto.verify + +sealed class ScanEvent { + data class OnReadEvent(val result: ReadCardResponse) : ScanEvent() + data class OnVerifyEvent(val verified: Boolean) : ScanEvent() + class OnUserCancelledEvent() : ScanEvent() + data class Failure(val error: TaskError) : ScanEvent() + data class Succcess(val result: Any) : ScanEvent() +} + +// +//sealed class ScanResult { +// data class ReadResult(val card: Card) : ScanResult() +// data class VerifyResult(val verified: Boolean) : ScanResult() +// data class Failure(val error: TaskError) : ScanResult() +//} + + +class ScanTask(delegate: CardManagerDelegate? = null, reader: CardReader) : Task(delegate, reader) { + + private lateinit var readCardData: ReadCardResponse + private lateinit var challenge: ByteArray + private lateinit var curve: EllipticCurve + private lateinit var walletPublickKey: ByteArray + + override fun onRun(cardEnvironment: CardEnvironment, + callback: (result: ScanEvent, cardEnvironment: CardEnvironment) -> Unit) { + + val readCommand = ReadCardCommand(cardEnvironment.pin1) + sendCommand(readCommand, cardEnvironment) { + + if (it is CommandEvent.Success) { + readCardData = it.response as ReadCardResponse + + callback(ScanEvent.OnReadEvent(readCardData), cardEnvironment) + + if (readCardData.curve != null && readCardData.walletPublicKey != null) { + curve = readCardData.curve!! + walletPublickKey = readCardData.walletPublicKey!! + } else { + callback(ScanEvent.Failure(TaskError.CardError()), cardEnvironment) + } + + val checkWalletCommand = prepareCheckWalletCommand(cardEnvironment) + + sendCommand(checkWalletCommand, cardEnvironment) { checkWalletResult -> + delegate?.closeNfcPopup() + + if (checkWalletResult is CommandEvent.Success) { + val checkWalletResponse = checkWalletResult.response as CheckWalletResponse + val verified = verify(walletPublickKey, + challenge + checkWalletResponse.salt, + checkWalletResponse.walletSignature, + curve) + if (verified) { + callback(ScanEvent.OnVerifyEvent(true), cardEnvironment) + } else { + callback(ScanEvent.Failure(TaskError.VefificationFailed()), cardEnvironment) + } + } + + + } + } else { + //TODO: Handle error + } + } + } + + private fun updateEnvironment(): CardEnvironment { + //TODO: set logic of changing CardEnvironment when needed + return CardEnvironment() + } + + private fun prepareCheckWalletCommand(cardEnvironment: CardEnvironment): CheckWalletCommand { + challenge = generateRandomBytes(16) + return CheckWalletCommand( + cardEnvironment.pin1, + readCardData.cardId, + challenge, + byteArrayOf()) + } +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/tasks/SignTask.kt b/tangem-card-new/src/main/java/com/tangem/tasks/SignTask.kt new file mode 100644 index 0000000000..662c814936 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/tasks/SignTask.kt @@ -0,0 +1,32 @@ +package com.tangem.tasks + +import com.tangem.CardEnvironment +import com.tangem.CardManagerDelegate +import com.tangem.CardReader +import com.tangem.commands.SignCommand +import com.tangem.commands.SignCommandData + +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 onRun(cardEnvironment: CardEnvironment, + callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) { + + this.callback = callback + + val signCommand = SignCommand(signCommandData) + + sendCommand(signCommand, cardEnvironment) { + delegate?.closeNfcPopup() + run { callback(it, updateEnvironment()) } + } + } + + private fun updateEnvironment(): CardEnvironment { + //TODO: set logic of changing CardEnvironment when needed + return CardEnvironment() + } +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/tasks/Task.kt b/tangem-card-new/src/main/java/com/tangem/tasks/Task.kt new file mode 100644 index 0000000000..8f98ef6d50 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/tasks/Task.kt @@ -0,0 +1,102 @@ +package com.tangem.tasks + +import com.tangem.CardEnvironment +import com.tangem.CardManagerDelegate +import com.tangem.CardReader +import com.tangem.commands.CommandEvent +import com.tangem.commands.CommandResponse +import com.tangem.commands.CommandSerializer +import com.tangem.enums.Status + +abstract class Task(protected val delegate: CardManagerDelegate? = null, private val reader: CardReader) { + + fun run(cardEnvironment: CardEnvironment, + callback: (result: T, cardEnvironment: CardEnvironment) -> Unit) { + + delegate?.openNfcPopup() + onRun(cardEnvironment, callback) + + } + + abstract fun onRun(cardEnvironment: CardEnvironment, + callback: (result: T, cardEnvironment: CardEnvironment) -> Unit) + + protected fun sendCommand(commandSerializer: CommandSerializer, cardEnvironment: CardEnvironment, + callback: (result: CommandEvent) -> Unit) { + + reader.sendApdu( + commandSerializer.serialize(cardEnvironment).toBytes()) { responseApdu -> + + when (responseApdu.status) { + Status.ProcessCompleted, Status.Pin1Changed, Status.Pin2Changed, Status.PinsChanged + -> { + val responseData = commandSerializer.deserialize(cardEnvironment, responseApdu) + + if (responseData == null) { + + } else { + callback( + CommandEvent.Success(responseData) + ) + } + + } + Status.InvalidParams -> TODO() + Status.ErrorProcessingCommand -> TODO() + Status.InvalidState -> TODO() + + Status.InsNotSupported -> TODO() + Status.NeedEnctryption -> TODO() + Status.NeedPause -> TODO() + } + +// val parsedApdu = responseApdu.getTlvData() +// if (parsedApdu == null) callback.invoke(TaskResult.Error(CardError())) +// else if (!parsedApdu.statusCompleted()) { +// callback.invoke(TaskResult.Error(CardError(parsedApdu.status.code))) +// } else { +// val response = commandSerializer.deserialize(parsedApdu) +// callback.invoke(response) +// } + } + } +} + +sealed class TaskError : Exception() { + class UnknownStatus(sw: Int) : TaskError() + class MappingError : TaskError() + class ErrorProcessingCommand : TaskError() + class InvalidState : TaskError() + class InsNotSupported : TaskError() + class VefificationFailed : TaskError() + class CardError : TaskError() + class ReaderError() : TaskError() +} + +sealed class TaskResult { + data class Success(val resultData: Any) : TaskResult() + data class CommandCompleted(val resultData: Any) : TaskResult() + data class Error(val error: TaskError) : TaskResult() +} + + + +class BasicTask( + private val commandSerializer: CommandSerializer, + delegate: CardManagerDelegate? = null, + reader: CardReader) : Task(delegate, reader) { + + override fun onRun(cardEnvironment: CardEnvironment, + callback: (result: TaskResult, cardEnvironment: CardEnvironment) -> Unit) { + sendCommand(commandSerializer, cardEnvironment) { + callback.invoke(it, updateEnvironment()) + } + } + + private fun updateEnvironment(): CardEnvironment { + //TODO: set logic of changing CardEnvironment when needed + return CardEnvironment() + } +} + +