From 6317358ac7d4b2fab584d824fab0abf09738f261 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Oct 2019 12:37:15 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../main/java/com/tangem/data/CommandApdu.kt | 75 +++++++++++++++++ .../main/java/com/tangem/data/ResponseApdu.kt | 34 ++++++++ .../src/main/java/com/tangem/data/Tlv.kt | 31 +++++++ .../main/java/com/tangem/data/TlvParser.kt | 55 ++++++++++++ .../main/java/com/tangem/enums/Instruction.kt | 23 +++++ .../src/main/java/com/tangem/enums/Status.kt | 21 +++++ .../src/main/java/com/tangem/enums/TlvTag.kt | 84 +++++++++++++++++++ 7 files changed, 323 insertions(+) create mode 100644 tangem-card-new/src/main/java/com/tangem/data/CommandApdu.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/data/ResponseApdu.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/data/Tlv.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/data/TlvParser.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/enums/Instruction.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/enums/Status.kt create mode 100644 tangem-card-new/src/main/java/com/tangem/enums/TlvTag.kt diff --git a/tangem-card-new/src/main/java/com/tangem/data/CommandApdu.kt b/tangem-card-new/src/main/java/com/tangem/data/CommandApdu.kt new file mode 100644 index 0000000000..0ec5f29219 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/data/CommandApdu.kt @@ -0,0 +1,75 @@ +package com.tangem.data + +import com.tangem.enums.Instruction + +class CommandApdu( + val tlvList: List, + val instruction: Instruction, + val p1: Int = 0x00, + val p2: Int = 0x00, + val encryptionKey: ByteArray? = null, + val rawInstruction: ByteArray = byteArrayOf()) { + + val cla = ISO_CLA + + + fun serialize(): ByteArray { + + var length = 4 // CLA, INS, P1, P2 + + val data = if (tlvList.isNotEmpty()) { + tlvList.toBytes() + } else { + byteArrayOf() + } + + val lc = data.size + + if (data.isNotEmpty()) { + length += 1 // LC + if (lc >= 256) + length += 2 + length += data.size // DATA + } + + val apdu = ByteArray(length) + + var index = 0 + apdu[index] = cla + index++ + apdu[index] = instruction.code.toByte() + index++ + apdu[index] = p1.toByte() + index++ + apdu[index] = p2.toByte() + index++ + if (lc != 0) { + if (lc < 256) { + apdu[index] = lc.toByte() + index++ + } else { + apdu[index] = 0 + index++ + apdu[index] = (lc shr 8).toByte() + index++ + apdu[index] = (lc and 0xFF).toByte() + index++ + } + + System.arraycopy(data, 0, apdu, index, data.size) + index += data.size + } + return apdu + + + } + + + private fun encrypt() { + + } + + companion object { + const val ISO_CLA = 0x00.toByte() + } +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/data/ResponseApdu.kt b/tangem-card-new/src/main/java/com/tangem/data/ResponseApdu.kt new file mode 100644 index 0000000000..1d859869e1 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/data/ResponseApdu.kt @@ -0,0 +1,34 @@ +package com.tangem.data + +import com.tangem.enums.Status + +class ResponseApdu(val data: ByteArray) { + + private val tlvParser = TlvParser() + + fun deserialize(encryptionKey: ByteArray? = null): ResponseApduParsed? { + + if (data.size < 2) return null + + if (data.size == 2) return ResponseApduParsed(parseStatus(data[0], data[1]), emptyList()) + + val tlvList = tlvParser.fromBytes(data.copyOf(data.size - 2)) + val sw1 = data[data.size - 2] + val sw2 = data[data.size -1] + return ResponseApduParsed(parseStatus(sw1, sw2), tlvList) + } + + private fun parseStatus(sw1: Byte, sw2: Byte): Status { + val code = (0x00FF and sw1.toInt()) shl 8 or (0x00FF and sw2.toInt()) + return Status.byCode(code) + } + + private fun decrypt(encryptionKey: ByteArray) { + + } + +} + +data class ResponseApduParsed(val status: Status, val tlvList: List? = null, val parsingError: String? = null) { + fun statusCompleted(): Boolean = status == Status.ProcessCompleted +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/data/Tlv.kt b/tangem-card-new/src/main/java/com/tangem/data/Tlv.kt new file mode 100644 index 0000000000..450801ccdf --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/data/Tlv.kt @@ -0,0 +1,31 @@ +package com.tangem.data + +import com.tangem.enums.TlvTag + +class Tlv(val tag: TlvTag, val tagCode: Int, val value: ByteArray = byteArrayOf()) + +fun List.toBytes(): ByteArray = + this.map { it.toBytes() }.reduce { arr1, arr2 -> arr1 + arr2 } + +fun Tlv.toBytes(): ByteArray { + val tag = byteArrayOf(this.tag.code.toByte()) + val length = getLengthInBytes(this.value.size) + val value = if (this.value.isNotEmpty()) this.value else byteArrayOf(0x00) + return tag + length + value +} + +private fun getLengthInBytes(tlvLength: Int): ByteArray { + return if (tlvLength < 0) { + if (tlvLength > 0xFE) { + byteArrayOf( + 0xFF.toByte(), + (tlvLength shr 8 and 0xFF).toByte(), + (tlvLength and 0xFF).toByte() + ) + } else { + byteArrayOf((tlvLength and 0xFF).toByte()) + } + } else { + byteArrayOf() + } +} diff --git a/tangem-card-new/src/main/java/com/tangem/data/TlvParser.kt b/tangem-card-new/src/main/java/com/tangem/data/TlvParser.kt new file mode 100644 index 0000000000..06e0839e5c --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/data/TlvParser.kt @@ -0,0 +1,55 @@ +package com.tangem.data + +import com.tangem.enums.TlvTag +import java.io.ByteArrayInputStream +import java.io.IOException + +class TlvParser { + + fun readFromStream(stream: ByteArrayInputStream): Tlv? { + val code = stream.read() + if (code == -1) return null + var len = stream.read() + if (len == -1) + throw IOException("Can't read TLV") + if (len == 0xFF) { + val lenH = stream.read() + if (lenH == -1) + throw IOException("Can't read TLV") + len = stream.read() + if (len == -1) + throw IOException("Can't read TLV") + len = len or (lenH shl 8) + } + val value = ByteArray(len) + if (len > 0) { + if (len != stream.read(value)) { + throw IOException("Can't read TLV") + } + } + val tag = TlvTag.byCode(code) + return Tlv(tag, code, value) + } + + fun fromBytes(data: ByteArray): List { + val tlvList = mutableListOf() + val stream = ByteArrayInputStream(data) + var tlv: Tlv? = null + do { + try { + tlv = readFromStream(stream) + if (tlv != null) tlvList.add(tlv) + } catch (e: IOException) { + throw IOException("TLVError: " + e.message) + } + + } while (tlv != null) + return tlvList + } + + + fun parseTlv(data: ByteArray) { + + + } +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/enums/Instruction.kt b/tangem-card-new/src/main/java/com/tangem/enums/Instruction.kt new file mode 100644 index 0000000000..a7ea9c4d2f --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/enums/Instruction.kt @@ -0,0 +1,23 @@ +package com.tangem.enums + +enum class Instruction(var code: Int) { + Unknown(0x00), + Read(0xF2), + VerifyCard(0xF3), + ValidateCard(0xF4), + VerifyCode(0xF5), + WriteIssuerData(0xF6), + GetIssuerData(0xF7), + CreateWallet(0xF8), + CheckWallet(0xF9), + SwapPIN(0xFA), + Sign(0xFB), + PurgeWallet(0xFC), + Activate(0xFE), + OpenSession(0xFF); + + + companion object { + fun byCode(code: Int): Instruction = values().find { it.code == code } ?: Unknown + } +} \ No newline at end of file diff --git a/tangem-card-new/src/main/java/com/tangem/enums/Status.kt b/tangem-card-new/src/main/java/com/tangem/enums/Status.kt new file mode 100644 index 0000000000..4a6b8922f2 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/enums/Status.kt @@ -0,0 +1,21 @@ +package com.tangem.enums + +enum class Status (val code: Int, val description: String){ + + ProcessCompleted(0x9000, "SW_PROCESS_COMPLETED"), + InvalidParams(0x6A86, "SW_INVALID_PARAMS"), + ErrorProcessingCommand(0x6286, "SW_ERROR_PROCESSING_COMMAND"), + InvalidState(0x6985, "SW_INVALID_STATE"), +// PinsNotChanged(ProcessCompleted.code, ProcessCompleted.description), + Pin1Changed(ProcessCompleted.code + 0x0001, "SW_PIN1_CHANGED"), + Pin2Changed(ProcessCompleted.code + 0x0002, "SW_PIN2_CHANGED"), + PinsChanged(ProcessCompleted.code + 0x0003, "SW_PINS_CHANGED"), + InsNotSupported(0x6D00, "SW_INS_NOT_SUPPORTED"), + NeedEnctryption(0x6982, "SW_NEED_ENCRYPTION"), + NeedPause(0x9789, "SW_NEED_PAUSE"); + + companion object { + fun byCode(code: Int): Status = values().find { it.code == code } ?: InvalidParams + } + +} diff --git a/tangem-card-new/src/main/java/com/tangem/enums/TlvTag.kt b/tangem-card-new/src/main/java/com/tangem/enums/TlvTag.kt new file mode 100644 index 0000000000..62625b6fd0 --- /dev/null +++ b/tangem-card-new/src/main/java/com/tangem/enums/TlvTag.kt @@ -0,0 +1,84 @@ +package com.tangem.enums + +enum class TlvTag(val code: Int) { + Unknown(0x00), + CardID(0x01), + Status(0x02), + CardPublicKey(0x03), + CardSignature(0x04), + CurveID(0x05), + HashAlgID(0x06), + SigningMethod(0x07), + MaxSignatures(0x08), + PauseBeforePin2(0x09), + SettingsMask(0x0A), + CardData(0x0C), + NdefData(0x0D), + Health(0x0F), + + Pin(0x10), + Pin2(0x11), + NewPin(0x12), + NewPin2(0x13), + NewPinHash(0x14), + NewPin2Hash(0x15), + Challenge(0x16), + Salt(0x17), + ValidationCounter(0x18), + Cvc(0x19), + + SessionKeyA(0x1A), + SessionKeyB(0x1B), + Pause(0x1C), + + ManufactureId(0x20), + ManufacturerSignature(0x21), + + IssuerDataPublicKey(0x30), + IssuerTransactionPublicKey(0x31), + IssuerData(0x32), + IssuerDataSignature(0x33), + IssuerTransactionSignature(0x34), + IssuerDataCounter(0x35), + + IsActivated(0x3A), + ActivationSeed(0x3B), + ResetPin(0x36), + + CodePageAddress(0x40), + CodePageCount(0x41), + CodeHash(0x42), + + TrOutHash(0x50), + TrOutHashSize(0x51), + TrOutRaw(0x52), + + WalletPublicKey(0x60), + Signature(0x61), + RemainingSignatures(0x62), + SignedHashes(0x63), + + Firmware(0x80), + Batch(0x81), + ManufactureDateTime(0x82), + IssuerId(0x83), + BlockchainId(0x84), + ManufacturerPublicKey(0x85), + CardIdManufacturerSignature(0x86), + + TokenSymbol(0xA0), + TokenContractAddress(0xA1), + TokenDecimal(0xA2), + Denomination(0xC0), + ValidatedBalance(0xC1), + LastSignDate(0xC2), + DenominationText(0xC3), + + TerminalIsLinked(0x58), + TerminalPublicKey(0x5C), + TerminalTransactionSignature(0x57); + + companion object { + fun byCode(code: Int): TlvTag = values().find { it.code == code } ?: Unknown + } +} \ No newline at end of file