Updated on 2026-08-14
This commit is contained in:
parent
ac8ca2e51c
commit
da003bf0d3
15 changed files with 577 additions and 265 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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<CommandResponse>,
|
||||
environment: CardEnvironment? = null,
|
||||
callback: (result: TaskResult) -> Unit) {
|
||||
val task = BasicTask(command, cardManagerDelegate, reader)
|
||||
val task = BasicTask<CommandResponse>(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()
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
//)
|
||||
//
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CheckWalletResponse>() {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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<Tlv>): CommandResponse?
|
||||
//}
|
||||
|
||||
interface CommandResponse
|
||||
// : TlvMappable {
|
||||
//
|
||||
//}
|
||||
|
||||
|
||||
abstract class CommandSerializer<T : CommandResponse>() {
|
||||
|
||||
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<Tlv>): CommandResponse? {
|
||||
// val mapper = TlvMapper(tlvList)
|
||||
//
|
||||
// return try {
|
||||
// CheckWalletResponse(
|
||||
// mapper.map(TlvTag.CardId),
|
||||
// mapper.map(TlvTag.Salt),
|
||||
// mapper.map(TlvTag.Signature)
|
||||
// )
|
||||
// } catch (exception: TlvMapperException) {
|
||||
// null
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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<ReadCardResponse>() {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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<SignCommandResponse>() {
|
||||
|
||||
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
|
||||
)
|
||||
90
tangem-card-new/src/main/java/com/tangem/tasks/ScanTask.kt
Normal file
90
tangem-card-new/src/main/java/com/tangem/tasks/ScanTask.kt
Normal file
|
|
@ -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<out: ScanEvent>(delegate: CardManagerDelegate? = null, reader: CardReader) : Task<ScanEvent>(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())
|
||||
}
|
||||
}
|
||||
32
tangem-card-new/src/main/java/com/tangem/tasks/SignTask.kt
Normal file
32
tangem-card-new/src/main/java/com/tangem/tasks/SignTask.kt
Normal file
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
102
tangem-card-new/src/main/java/com/tangem/tasks/Task.kt
Normal file
102
tangem-card-new/src/main/java/com/tangem/tasks/Task.kt
Normal file
|
|
@ -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<T>(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 <T: CommandResponse>sendCommand(commandSerializer: CommandSerializer<T>, 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<T: CommandResponse>(
|
||||
private val commandSerializer: CommandSerializer<CommandResponse>,
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue