Updated on 2026-08-14
This commit is contained in:
commit
8a6020c42b
8 changed files with 127 additions and 4 deletions
|
|
@ -24,12 +24,13 @@ data class SessionEnvironment(
|
|||
val handleErrors: Boolean = true
|
||||
) {
|
||||
|
||||
var isDefaultPin1: Boolean = Companion.pin1.contentEquals(DEFAULT_PIN.calculateSha256())
|
||||
|
||||
var pin1: ByteArray = SessionEnvironment.pin1
|
||||
private set
|
||||
get() = SessionEnvironment.pin1
|
||||
|
||||
fun isCurrentPin1Default(): Boolean = Companion.pin1.contentEquals(DEFAULT_PIN.calculateSha256())
|
||||
fun isCurrentPin2Default(): Boolean = pin2.contentEquals(DEFAULT_PIN2.calculateSha256())
|
||||
|
||||
fun setPin1(pin1: String) {
|
||||
SessionEnvironment.pin1 = pin1.calculateSha256()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ interface CommandResponse
|
|||
abstract class Command<T : CommandResponse> : ApduSerializable<T>, CardSessionRunnable<T> {
|
||||
|
||||
override val performPreflightRead: Boolean = true
|
||||
open val requiresPin2: Boolean = false
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<T>) -> Unit) {
|
||||
Log.i("Command", "Initializing ${this::class.java.simpleName}")
|
||||
|
|
@ -58,6 +59,11 @@ abstract class Command<T : CommandResponse> : ApduSerializable<T>, CardSessionRu
|
|||
}
|
||||
}
|
||||
|
||||
if (requiresPin2 && session.environment.isCurrentPin2Default()) {
|
||||
handlePin2(session, callback)
|
||||
return
|
||||
}
|
||||
|
||||
val apdu = serialize(session.environment)
|
||||
transceiveApdu(apdu, session) { result ->
|
||||
when (result) {
|
||||
|
|
@ -181,7 +187,7 @@ abstract class Command<T : CommandResponse> : ApduSerializable<T>, CardSessionRu
|
|||
session: CardSession,
|
||||
callback: (result: CompletionResult<T>) -> Unit
|
||||
) {
|
||||
if (!session.environment.isDefaultPin1) {
|
||||
if (!session.environment.isCurrentPin1Default()) {
|
||||
session.environment.setPin1(SessionEnvironment.DEFAULT_PIN)
|
||||
transceive(session, callback)
|
||||
return
|
||||
|
|
@ -199,4 +205,29 @@ abstract class Command<T : CommandResponse> : ApduSerializable<T>, CardSessionRu
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePin2(
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<T>) -> Unit
|
||||
) {
|
||||
val checkPinCommand = SetPinCommand(session.environment.pin1, session.environment.pin2)
|
||||
checkPinCommand.run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
session.viewDelegate.onPinRequested { pin2 ->
|
||||
if (!pin2.isNullOrEmpty()) {
|
||||
session.environment.setPin2(pin2)
|
||||
transceive(session, callback)
|
||||
} else {
|
||||
session.environment.setPin2(SessionEnvironment.DEFAULT_PIN2)
|
||||
callback(CompletionResult.Failure(TangemSdkError.Pin2OrCvcRequired()))
|
||||
}
|
||||
}
|
||||
}
|
||||
is CompletionResult.Success -> {
|
||||
transceive(session, callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,8 @@ class CreateWalletResponse(
|
|||
*/
|
||||
class CreateWalletCommand : Command<CreateWalletResponse>() {
|
||||
|
||||
override val requiresPin2 = true
|
||||
|
||||
override fun performPreCheck(card: Card): TangemSdkError? {
|
||||
if (card.isActivated) {
|
||||
return TangemSdkError.NotActivated()
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ class PurgeWalletResponse(
|
|||
*/
|
||||
class PurgeWalletCommand : Command<PurgeWalletResponse>() {
|
||||
|
||||
override val requiresPin2 = true
|
||||
|
||||
override fun performPreCheck(card: Card): TangemSdkError? {
|
||||
if (card.status == CardStatus.NotPersonalized) {
|
||||
return TangemSdkError.NotPersonalized()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.SessionEnvironment
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.apdu.StatusWord
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.tlv.TlvBuilder
|
||||
import com.tangem.common.tlv.TlvDecoder
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
|
||||
enum class SetPinStatus {
|
||||
PinsNotChanged,
|
||||
Pin1Changed,
|
||||
Pin2Changed,
|
||||
Pin3Changed,
|
||||
Pins12Changed,
|
||||
Pins13Changed,
|
||||
Pins23Changed,
|
||||
Pins123Changed,
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun fromStatusWord(statusWord: StatusWord): SetPinStatus? {
|
||||
return when (statusWord) {
|
||||
StatusWord.ProcessCompleted -> PinsNotChanged
|
||||
StatusWord.Pin1Changed -> Pin1Changed
|
||||
StatusWord.Pin2Changed -> Pin2Changed
|
||||
StatusWord.Pins12Changed -> Pins12Changed
|
||||
StatusWord.Pin3Changed -> Pin3Changed
|
||||
StatusWord.Pins13Changed -> Pins13Changed
|
||||
StatusWord.Pins23Changed -> Pins23Changed
|
||||
StatusWord.Pins123Changed -> Pins123Changed
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SetPinResponse(
|
||||
/**
|
||||
* CID, Unique Tangem card ID number.
|
||||
*/
|
||||
val cardId: String,
|
||||
/**
|
||||
*
|
||||
*/
|
||||
val status: SetPinStatus
|
||||
) : CommandResponse
|
||||
|
||||
|
||||
class SetPinCommand(
|
||||
private val newPin1: ByteArray = SessionEnvironment.DEFAULT_PIN.calculateSha256(),
|
||||
private val newPin2: ByteArray = SessionEnvironment.DEFAULT_PIN2.calculateSha256(),
|
||||
private val newPin3: ByteArray? = null
|
||||
) : Command<SetPinResponse>() {
|
||||
|
||||
override fun serialize(environment: SessionEnvironment): CommandApdu {
|
||||
val tlvBuilder = TlvBuilder()
|
||||
tlvBuilder.append(TlvTag.Pin, environment.pin1)
|
||||
tlvBuilder.append(TlvTag.CardId, environment.card?.cardId)
|
||||
tlvBuilder.append(TlvTag.Pin2, environment.pin2)
|
||||
tlvBuilder.append(TlvTag.Cvc, environment.cvc)
|
||||
tlvBuilder.append(TlvTag.NewPin, newPin1)
|
||||
tlvBuilder.append(TlvTag.NewPin2, newPin2)
|
||||
tlvBuilder.append(TlvTag.NewPin3, newPin3)
|
||||
return CommandApdu(Instruction.SetPin, tlvBuilder.serialize())
|
||||
}
|
||||
|
||||
override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): SetPinResponse {
|
||||
val tlvData = apdu.getTlvData() ?: throw TangemSdkError.DeserializeApduFailed()
|
||||
|
||||
val status = SetPinStatus.fromStatusWord(apdu.statusWord) ?: throw TangemSdkError.DecodingFailed()
|
||||
|
||||
val decoder = TlvDecoder(tlvData)
|
||||
return SetPinResponse(
|
||||
cardId = decoder.decode(TlvTag.CardId),
|
||||
status = status
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,8 @@ class SignResponse(
|
|||
*/
|
||||
class SignCommand(private val hashes: Array<ByteArray>) : Command<SignResponse>() {
|
||||
|
||||
override val requiresPin2 = true
|
||||
|
||||
//TODO: Allow signing more than 10 hashes
|
||||
|
||||
private val hashSizes = if (hashes.isNotEmpty()) hashes.first().size else 0
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ class WriteUserDataCommand(private val userData: ByteArray? = null, private val
|
|||
private val userCounter: Int? = null,
|
||||
private val userProtectedCounter: Int? = null) : Command<WriteUserDataResponse>() {
|
||||
|
||||
override val requiresPin2 = true
|
||||
|
||||
override fun performPreCheck(card: Card): TangemSdkError? {
|
||||
if (card.status == CardStatus.NotPersonalized) {
|
||||
return TangemSdkError.NotPersonalized()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ enum class Instruction(var code: Int) {
|
|||
ReadIssuerData(0xF7),
|
||||
CreateWallet(0xF8),
|
||||
CheckWallet(0xF9),
|
||||
SwapPIN(0xFA),
|
||||
SetPin(0xFA),
|
||||
Sign(0xFB),
|
||||
PurgeWallet(0xFC),
|
||||
Activate(0xFE),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue