Updated on 2026-08-14
This commit is contained in:
parent
587f7d2913
commit
d6d7c292e7
27 changed files with 344 additions and 23 deletions
|
|
@ -1,6 +1,10 @@
|
|||
package com.tangem
|
||||
|
||||
|
||||
/**
|
||||
* Contains data relating to a Tangem card. It is used in constructing all the commands,
|
||||
* and commands can return modified [CardEnvironment].
|
||||
*/
|
||||
data class CardEnvironment(
|
||||
val pin1: String = DEFAULT_PIN,
|
||||
val pin2: String = DEFAULT_PIN2,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,15 @@ import com.tangem.crypto.CryptoUtils
|
|||
import com.tangem.tasks.*
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/**
|
||||
* The main interface of Tangem SDK that allows your app to communicate with Tangem cards.
|
||||
*
|
||||
* @property reader is an interface that is responsible for NFC connection and
|
||||
* transfer of data to and from the Tangem Card.
|
||||
* Its default implementation, NfcCardReader, is in our tangem-sdk module.
|
||||
* @property cardManagerDelegate is an interface that allows interaction with users and shows relevant UI.
|
||||
* Its default implementation, DefaultCardManagerDelegate, is in our tangem-sdk module.
|
||||
*/
|
||||
class CardManager(
|
||||
private val reader: CardReader,
|
||||
private val cardManagerDelegate: CardManagerDelegate? = null) {
|
||||
|
|
@ -20,11 +29,35 @@ class CardManager(
|
|||
CryptoUtils.initCrypto()
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that allows to read a card and verify that its private key.
|
||||
* It launches on the new thread a [ScanTask] that will send the following events in a callback:
|
||||
* [ScanEvent.OnReadEvent] after completing [com.tangem.commands.ReadCommand]
|
||||
* [ScanEvent.OnVerifyEvent] after completing [com.tangem.commands.CheckWalletCommand]
|
||||
* [TaskEvent.Completion] with an error field null after successful completion of a task or
|
||||
* [TaskEvent.Completion] with a [TaskError] if some error occurs.
|
||||
*/
|
||||
fun scanCard(callback: (result: TaskEvent<ScanEvent>) -> Unit) {
|
||||
val task = ScanTask()
|
||||
runTask(task, callback = callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that allows to sign hashes (usually a blockchain transaction) with a private key
|
||||
* from a Tangem card. (Please note that the private key itself never leaves the Tangem card).
|
||||
*
|
||||
* This method launches on the new thread [SignCommand] that will send the following events in a callback:
|
||||
* [SignResponse] after completing [SignCommand]
|
||||
* [TaskEvent.Completion] with an error field null after successful completion of a task or
|
||||
* [TaskEvent.Completion] with a [TaskError] if some error occurs.
|
||||
* Please note that Tangem cards usually protect the signing with a security delay
|
||||
* that may last up to 90 seconds, depending on a card.
|
||||
* It is for [CardManagerDelegate] to notify users of security delay.
|
||||
* @param hashes Array of transaction hashes. It can be a single hash or several hashes of the same length.
|
||||
* @param cardId CID, Unique Tangem card ID number
|
||||
* @param callback
|
||||
*
|
||||
*/
|
||||
fun sign(hashes: Array<ByteArray>, cardId: String,
|
||||
callback: (result: TaskEvent<SignResponse>) -> Unit) {
|
||||
val signCommand: SignCommand
|
||||
|
|
@ -42,6 +75,9 @@ class CardManager(
|
|||
runTask(task, cardId, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
fun <T> runTask(task: Task<T>, cardId: String? = null,
|
||||
callback: (result: TaskEvent<T>) -> Unit) {
|
||||
if (isBusy) {
|
||||
|
|
@ -63,6 +99,9 @@ class CardManager(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
fun <T : CommandResponse> runCommand(command: CommandSerializer<T>,
|
||||
cardId: String? = null,
|
||||
callback: (result: TaskEvent<T>) -> Unit) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ package com.tangem
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.tasks.TaskError
|
||||
|
||||
/**
|
||||
* Allows interaction with users and shows visual elements.
|
||||
*
|
||||
* Its default implementation, DefaultCardManagerDelegate, is in our tangem-sdk module.
|
||||
*/
|
||||
interface CardManagerDelegate {
|
||||
|
||||
fun onNfcSessionStarted()
|
||||
|
|
|
|||
|
|
@ -4,9 +4,35 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
|
||||
/**
|
||||
* Allows interaction between the phone or any other terminal and Tangem card.
|
||||
*
|
||||
* Its default implementation, NfcCardReader, is in our tangem-sdk module.
|
||||
*/
|
||||
interface CardReader {
|
||||
|
||||
/**
|
||||
* [com.tangem.tasks.Task] sets it to true before the first command,
|
||||
* and it should be set to false on completion of the task.
|
||||
*/
|
||||
var readingActive: Boolean
|
||||
|
||||
/**
|
||||
* Sends data to the card and receives the reply.
|
||||
*
|
||||
* @param apdu data to be sent. [CommandApdu] serializes it to a [ByteArray]
|
||||
* @param callback returns response from the card,
|
||||
* [ResponseApdu] allows to convert raw data to [Tlv]
|
||||
*/
|
||||
fun transceiveApdu(apdu: CommandApdu, callback: (response: CompletionResult<ResponseApdu>) -> Unit)
|
||||
|
||||
/**
|
||||
* Signals to [CardReader] to become ready to transceive data.
|
||||
*/
|
||||
fun startNfcSession()
|
||||
|
||||
/**
|
||||
* Signals to [CardReader] that no further NFC transition is expected.
|
||||
*/
|
||||
fun closeSession()
|
||||
}
|
||||
|
|
@ -22,6 +22,11 @@ object Log {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for logging events within the SDK.
|
||||
*
|
||||
* It allows to use Android logger or to choose another.
|
||||
*/
|
||||
interface LoggerInterface {
|
||||
fun i(logTag: String, message: String)
|
||||
fun e(logTag: String, message: String)
|
||||
|
|
|
|||
|
|
@ -11,13 +11,29 @@ import com.tangem.common.tlv.TlvMapper
|
|||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.tasks.TaskError
|
||||
|
||||
/**
|
||||
* Deserialized response from the Tangem card after [CheckWalletCommand].
|
||||
*
|
||||
* @property cardId Unique Tangem card ID number
|
||||
* @property salt Random salt generated by the card
|
||||
* @property walletSignature Signature with wallet private key of challenge and salt.
|
||||
* It uses SHA256 for ‘secp256k1’ curve and SHA512 for ‘ed25519’ curve.
|
||||
*/
|
||||
class CheckWalletResponse(
|
||||
val cardId: String,
|
||||
val salt: ByteArray,
|
||||
val walletSignature: ByteArray
|
||||
) : CommandResponse
|
||||
|
||||
|
||||
/**
|
||||
* This command proves that the wallet private key from the card corresponds to the wallet public key.
|
||||
* Standard challenge/response scheme is used.
|
||||
*
|
||||
* @property pin1 hashed user’s pin 1 code to access the card. Default unhashed value: ‘000000’.
|
||||
* Pin code should be taken from card environment.
|
||||
* @property cardId Unique Tangem card ID number
|
||||
* @property challenge Random challenge generated by application
|
||||
*/
|
||||
class CheckWalletCommand(
|
||||
private val pin1: String,
|
||||
private val cardId: String,
|
||||
|
|
|
|||
|
|
@ -2,22 +2,41 @@ package com.tangem.commands
|
|||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.extentions.toInt
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
|
||||
/**
|
||||
* Simple interface for responses received after sending commands to Tangem cards.
|
||||
*/
|
||||
interface CommandResponse
|
||||
|
||||
|
||||
/**
|
||||
* Abstract class for all Tangem card commands.
|
||||
*/
|
||||
abstract class CommandSerializer<T : CommandResponse> {
|
||||
|
||||
abstract val instruction: Instruction
|
||||
abstract val instructionCode: Int
|
||||
|
||||
/**
|
||||
* Serializes data into a [List] of [com.tangem.common.tlv.Tlv],
|
||||
* then creates [CommandApdu] with this data.
|
||||
*
|
||||
* @return command data that can be converted to raw bytes with a method [CommandApdu.toBytes].
|
||||
*/
|
||||
abstract fun serialize(cardEnvironment: CardEnvironment): CommandApdu
|
||||
|
||||
/**
|
||||
* Deserializes data, received from a card and stored in [ResponseApdu],
|
||||
* into a [List] of [com.tangem.common.tlv.Tlv]. Then this method maps it into a [CommandResponse].
|
||||
*
|
||||
* @return card response, converted to a [CommandResponse] of a type [T].
|
||||
*/
|
||||
abstract fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): T?
|
||||
|
||||
/**
|
||||
* Helper method to parse security delay information received from a card.
|
||||
*
|
||||
* @return Remaining security delay in milliseconds.
|
||||
*/
|
||||
fun deserializeSecurityDelay(responseApdu: ResponseApdu, cardEnvironment: CardEnvironment): Int? {
|
||||
val tlv = responseApdu.getTlvData(cardEnvironment.encryptionKey)
|
||||
return tlv?.find { it.tag == TlvTag.Pause }?.value?.toInt()
|
||||
|
|
|
|||
|
|
@ -152,7 +152,6 @@ class Card(
|
|||
/**
|
||||
* This command receives from the Tangem Card all the data about the card and the wallet,
|
||||
* including unique card number (CID or cardId) that has to be submitted while calling all other commands.
|
||||
*
|
||||
*/
|
||||
class ReadCommand : CommandSerializer<Card>() {
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,13 @@ import com.tangem.common.tlv.TlvTag
|
|||
import com.tangem.crypto.sign
|
||||
import com.tangem.tasks.TaskError
|
||||
|
||||
/**
|
||||
* @param cardId CID, Unique Tangem card ID number
|
||||
* @param signature signed hashes (array of resulting signatures)
|
||||
* @param remainingSignatures Remaining number of sign operations before the wallet will stop signing transactions.
|
||||
* @param signedHashes Total number of signed single hashes returned by the card in sign command responses.
|
||||
* Sums up array elements within all SIGN commands
|
||||
*/
|
||||
class SignResponse(
|
||||
val cardId: String,
|
||||
val signature: ByteArray,
|
||||
|
|
@ -19,7 +26,12 @@ class SignResponse(
|
|||
val signedHashes: Int
|
||||
) : CommandResponse
|
||||
|
||||
|
||||
/**
|
||||
* Signs transaction hashes using a wallet private key, stored on the card.
|
||||
*
|
||||
* @property hashes Array of transaction hashes.
|
||||
* @property cardId CID, Unique Tangem card ID number
|
||||
*/
|
||||
class SignCommand(private val hashes: Array<ByteArray>, private val cardId: String)
|
||||
: CommandSerializer<SignResponse>() {
|
||||
|
||||
|
|
@ -51,6 +63,12 @@ class SignCommand(private val hashes: Array<ByteArray>, private val cardId: Stri
|
|||
return CommandApdu(Instruction.Sign, tlvData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds to the command data the terminal public key (generated by the application) and
|
||||
* transaction hashes signed by the terminal private key.
|
||||
* This allows to link the card to the Android device and skip security delay.
|
||||
* (as described in Linked Terminal section of the Tangem Card Manual).
|
||||
*/
|
||||
private fun addTerminalSignature(cardEnvironment: CardEnvironment, tlvData: MutableList<Tlv>) {
|
||||
cardEnvironment.terminalKeys?.let { terminalKeyPair ->
|
||||
val signedData = dataToSign.sign(terminalKeyPair.privateKey)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
package com.tangem.common
|
||||
|
||||
import com.tangem.common.CompletionResult.Success
|
||||
import com.tangem.tasks.TaskError
|
||||
|
||||
/**
|
||||
* Response class encapsulating successful and failed results.
|
||||
* [T] is a type of data that is returned in [Success].
|
||||
*/
|
||||
sealed class CompletionResult<T> {
|
||||
class Success<T>(val data: T) : CompletionResult<T>()
|
||||
class Failure<T>(val error: TaskError) : CompletionResult<T>()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,13 @@ import com.tangem.common.tlv.Tlv
|
|||
import com.tangem.common.tlv.toBytes
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
/**
|
||||
* Class that provides conversion of serialized request and Instruction code
|
||||
* to a raw data that can be sent to the card.
|
||||
*
|
||||
* @property ins Instruction code that determines the type of request for the card.
|
||||
* @property tlvList list of TLVs that are to be sent to the card
|
||||
*/
|
||||
class CommandApdu(
|
||||
|
||||
private val ins: Int,
|
||||
|
|
@ -31,6 +38,10 @@ class CommandApdu(
|
|||
encryptionKey = encryptionKey
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* Request converted to a raw data
|
||||
*/
|
||||
val apduData: ByteArray
|
||||
|
||||
init {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
package com.tangem.common.apdu
|
||||
|
||||
/**
|
||||
* Instruction code that determines the type of the command that is sent to the Tangem card.
|
||||
* It is used in the construction of [com.tangem.common.apdu.CommandApdu].
|
||||
*/
|
||||
enum class Instruction(var code: Int) {
|
||||
Unknown(0x00),
|
||||
Read(0xF2),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,13 @@ package com.tangem.common.apdu
|
|||
|
||||
import com.tangem.common.tlv.Tlv
|
||||
|
||||
/**
|
||||
* Stores response data from the card and parses it to [Tlv] and [StatusWord].
|
||||
*
|
||||
* @property data raw response from the card.
|
||||
* @property sw Status word code, reflecting the status of the response.
|
||||
* @property statusWord parsed status word.
|
||||
*/
|
||||
class ResponseApdu(val data: ByteArray) {
|
||||
|
||||
private val sw1: Int = 0x00FF and data[data.size - 2].toInt()
|
||||
|
|
@ -11,6 +18,12 @@ class ResponseApdu(val data: ByteArray) {
|
|||
|
||||
val statusWord: StatusWord = StatusWord.byCode(sw)
|
||||
|
||||
/**
|
||||
* Converts raw response data to the list of TLVs.
|
||||
*
|
||||
* @param encryptionKey key to decrypt response.
|
||||
* (Encryption / decryption functionality is not implemented yet.)
|
||||
*/
|
||||
fun getTlvData(encryptionKey: ByteArray? = null): List<Tlv>? {
|
||||
return when {
|
||||
data.size < 2 -> null
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.common.apdu
|
||||
|
||||
enum class StatusWord (val code: Int, val description: String){
|
||||
/**
|
||||
* Part of a response from the card, shows the status of the operation
|
||||
*/
|
||||
enum class StatusWord(val code: Int, val description: String) {
|
||||
|
||||
ProcessCompleted(0x9000, "SW_PROCESS_COMPLETED"),
|
||||
InvalidParams(0x6A86, "SW_INVALID_PARAMS"),
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import java.security.MessageDigest
|
|||
import java.util.*
|
||||
import kotlin.experimental.and
|
||||
|
||||
/**
|
||||
* Extension functions for [ByteArray].
|
||||
*/
|
||||
|
||||
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package com.tangem.common.extentions
|
|||
import java.nio.charset.Charset
|
||||
import java.security.MessageDigest
|
||||
|
||||
|
||||
/**
|
||||
* Extension functions for [String].
|
||||
*/
|
||||
fun String.calculateSha256(): ByteArray {
|
||||
val sha256 = MessageDigest.getInstance("SHA-256")
|
||||
val data = this.toByteArray(Charset.forName("UTF-8"))
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ package com.tangem.common.tlv
|
|||
import java.io.ByteArrayInputStream
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* The data converted to the Tag Length Value protocol.
|
||||
*/
|
||||
class Tlv {
|
||||
|
||||
val tag: TlvTag
|
||||
|
|
|
|||
|
|
@ -14,8 +14,22 @@ class MissingTagException(message: String? = null) : TlvMapperException(message)
|
|||
class WrongTypeException(message: String? = null) : TlvMapperException(message)
|
||||
class ConversionException(message: String? = null) : TlvMapperException(message)
|
||||
|
||||
/**
|
||||
* Maps value fields in [Tlv] from raw [ByteArray] to concrete classes
|
||||
* according to their [TlvTag] and corresponding [TlvValueType].
|
||||
*
|
||||
* @property tlvList List of TLVs, which values are to be converted to particular classes.
|
||||
*/
|
||||
class TlvMapper(val tlvList: List<Tlv>) {
|
||||
|
||||
/**
|
||||
* Finds [Tlv] by its [TlvTag].
|
||||
* Returns null if [Tlv] is not found, otherwise converts its value to [T].
|
||||
*
|
||||
* @param tag [TlvTag] of a [Tlv] which value is to be returned.
|
||||
*
|
||||
* @return value converted to a nullable type [T].
|
||||
*/
|
||||
inline fun <reified T> mapOptional(tag: TlvTag): T? =
|
||||
try {
|
||||
map<T>(tag)
|
||||
|
|
@ -23,6 +37,17 @@ class TlvMapper(val tlvList: List<Tlv>) {
|
|||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds [Tlv] by its [TlvTag].
|
||||
* Throws [MissingTagException] if [Tlv] is not found,
|
||||
* otherwise converts [Tlv] value to [T].
|
||||
*
|
||||
* @param tag [TlvTag] of a [Tlv] which value is to be returned.
|
||||
*
|
||||
* @return [Tlv] value converted to a nullable type [T].
|
||||
*
|
||||
* @throws [MissingTagException] if no [Tlv] is found by the Tag.
|
||||
*/
|
||||
inline fun <reified T> map(tag: TlvTag): T {
|
||||
val tlvValue: ByteArray = tlvList.find { it.tag == tag }?.value
|
||||
?: if (tag.valueType() == TlvValueType.BoolValue && T::class == Boolean::class) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package com.tangem.common.tlv
|
||||
|
||||
/**
|
||||
* Contains all possible value types that value for [TlvTag] can contain.
|
||||
*/
|
||||
enum class TlvValueType {
|
||||
HexString,
|
||||
Utf8String,
|
||||
|
|
@ -14,6 +17,9 @@ enum class TlvValueType {
|
|||
SigningMethod
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains all TLV tags, with their code and descriptive name.
|
||||
*/
|
||||
enum class TlvTag(val code: Int) {
|
||||
Unknown(0x00),
|
||||
CardId(0x01),
|
||||
|
|
@ -97,13 +103,9 @@ enum class TlvTag(val code: Int) {
|
|||
TerminalPublicKey(0x5C),
|
||||
TerminalTransactionSignature(0x57);
|
||||
|
||||
fun hasNestedTlv(): Boolean {
|
||||
return when (this) {
|
||||
TlvTag.CardData -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [TlvValueType] associated with a [TlvTag]
|
||||
*/
|
||||
fun valueType(): TlvValueType {
|
||||
return when (this) {
|
||||
CardId, Pin, Batch -> TlvValueType.HexString
|
||||
|
|
|
|||
|
|
@ -13,21 +13,65 @@ object CryptoUtils {
|
|||
Security.addProvider(EdDSASecurityProvider())
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates ByteArray of random bytes.
|
||||
* It is used, among other things, to generate helper private keys
|
||||
* (not the one for the blockchains, that one is generated on the card and does not leave the card).
|
||||
*
|
||||
* @param length length of the ByteArray that is to be generated.
|
||||
*/
|
||||
fun generateRandomBytes(length: Int): ByteArray {
|
||||
val bytes = ByteArray(length)
|
||||
SecureRandom().nextBytes(bytes)
|
||||
return bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to verify that the data was signed with a private key that corresponds
|
||||
* to the provided public key.
|
||||
*
|
||||
* @param publicKey public key corresponding to the private key that was used to sing a message
|
||||
* @param message the data that was signed
|
||||
* @param signature signed data
|
||||
* @param curve elliptic curve used
|
||||
*
|
||||
* @return result of a verification
|
||||
*/
|
||||
fun verify(publicKey: ByteArray, message: ByteArray, signature: ByteArray,
|
||||
curve: EllipticCurve = EllipticCurve.Secp256k1): Boolean {
|
||||
return when (curve) {
|
||||
EllipticCurve.Secp256k1 -> verifySecp256k1(publicKey, message, signature)
|
||||
EllipticCurve.Ed25519 -> verifyEd25519(publicKey, message, signature)
|
||||
EllipticCurve.Secp256k1 -> Sepc256k1.verify(publicKey, message, signature)
|
||||
EllipticCurve.Ed25519 -> Ed25519.verify(publicKey, message, signature)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that generates public key from a private key.
|
||||
*
|
||||
* @param privateKeyArray a private key from which a public key is generated
|
||||
* @param curve elliptic curve used
|
||||
*
|
||||
* @return public key [ByteArray]
|
||||
*/
|
||||
fun generatePublicKey(
|
||||
privateKeyArray: ByteArray,
|
||||
curve: EllipticCurve = EllipticCurve.Secp256k1
|
||||
): ByteArray {
|
||||
return when (curve) {
|
||||
EllipticCurve.Secp256k1 -> Sepc256k1.generatePublicKey(privateKeyArray)
|
||||
EllipticCurve.Ed25519 -> Ed25519.generatePublicKey(privateKeyArray)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension function to sign a ByteArray with an elliptic curve cryptography.
|
||||
*
|
||||
* @param privateKeyArray key to sign data
|
||||
* @param curve curve that is used to sign data
|
||||
*
|
||||
* @return signed data
|
||||
*/
|
||||
fun ByteArray.sign(privateKeyArray: ByteArray, curve: EllipticCurve = EllipticCurve.Secp256k1): ByteArray {
|
||||
return when (curve) {
|
||||
EllipticCurve.Secp256k1 -> signSecp256k1(this, privateKeyArray)
|
||||
|
|
|
|||
|
|
@ -7,12 +7,27 @@ import com.tangem.commands.ReadCommand
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.crypto.CryptoUtils
|
||||
|
||||
/**
|
||||
* Events that [ScanTask] returns on completion of its commands.
|
||||
*/
|
||||
sealed class ScanEvent {
|
||||
|
||||
/**
|
||||
* Contains data from a Tangem card after successful completion of [ReadCommand].
|
||||
*/
|
||||
data class OnReadEvent(val card: Card) : ScanEvent()
|
||||
|
||||
/**
|
||||
* Shows whether the Tangem card was verified on completion of [CheckWalletCommand].
|
||||
*/
|
||||
data class OnVerifyEvent(val isGenuine: Boolean) : ScanEvent()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Task that allows to read Tangem card and verify its private key.
|
||||
*
|
||||
* It performs two commands, [ReadCommand] and [CheckWalletCommand], subsequently.
|
||||
*/
|
||||
internal class ScanTask : Task<ScanEvent>() {
|
||||
|
||||
override fun onRun(cardEnvironment: CardEnvironment,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ import com.tangem.commands.CommandResponse
|
|||
import com.tangem.commands.CommandSerializer
|
||||
import com.tangem.common.CompletionResult
|
||||
|
||||
/**
|
||||
* Allows to perform a single command.
|
||||
*
|
||||
* @property command is a command that will be performed.
|
||||
*/
|
||||
class SingleCommandTask<Event : CommandResponse>(
|
||||
private val command: CommandSerializer<Event>
|
||||
) : Task<Event>() {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.StatusWord
|
||||
|
||||
/**
|
||||
* An error class that represent typical errors that may occur when performing Tangem SDK tasks.
|
||||
* Errors are propagated back to the caller in callbacks.
|
||||
*/
|
||||
sealed class TaskError(description: String? = null) : Exception(description) {
|
||||
class UnknownStatus(sw: Int) : TaskError("Unknown StatusWord: $sw")
|
||||
class MappingError : TaskError()
|
||||
|
|
@ -36,16 +40,41 @@ sealed class TaskError(description: String? = null) : Exception(description) {
|
|||
class HashSizeMustBeEqual() : TaskError()
|
||||
}
|
||||
|
||||
/**
|
||||
* Events that are are sent in callbacks from [Task] during [Task] and Commands completions.
|
||||
*/
|
||||
sealed class TaskEvent<T> {
|
||||
|
||||
/**
|
||||
* A callback that is triggered when a command returns response from a card
|
||||
* (on a completion of a [CommandSerializer]).
|
||||
*/
|
||||
class Event<T>(val data: T) : TaskEvent<T>()
|
||||
|
||||
/**
|
||||
* A callback that is triggered when a [Task] is completed.
|
||||
*
|
||||
* @param error is null if it's a successful completion of a [Task]
|
||||
*/
|
||||
class Completion<T>(val error: TaskError? = null) : TaskEvent<T>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to perform a group of commands interacting between the card and the application.
|
||||
* A task opens an NFC session, sends commands to the card and receives its responses,
|
||||
* repeats the commands if needed, and closes session after receiving the last answer.
|
||||
*/
|
||||
abstract class Task<T> {
|
||||
|
||||
var delegate: CardManagerDelegate? = null
|
||||
var reader: CardReader? = null
|
||||
|
||||
/**
|
||||
* This method should be called to run the [Task] and perform all its operations.
|
||||
*
|
||||
* @param cardEnvironment is a relevant current version of a card environment
|
||||
* @param callback is a callback that will be triggered during the performance of the [Task]
|
||||
*/
|
||||
fun run(cardEnvironment: CardEnvironment,
|
||||
callback: (result: TaskEvent<T>) -> Unit) {
|
||||
delegate?.onNfcSessionStarted()
|
||||
|
|
@ -54,6 +83,12 @@ abstract class Task<T> {
|
|||
onRun(cardEnvironment, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called on [Task] completion, whether it was successful or with failure.
|
||||
*
|
||||
* @param withError is true when there is an error
|
||||
* @param taskError the error to be shown by [CardManagerDelegate]
|
||||
*/
|
||||
protected fun completeNfcSession(withError: Boolean = false, taskError: TaskError? = null) {
|
||||
reader?.closeSession()
|
||||
if (withError) {
|
||||
|
|
@ -63,9 +98,16 @@ abstract class Task<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In this method the individual Tasks' logic should be implemented.
|
||||
*/
|
||||
protected abstract fun onRun(cardEnvironment: CardEnvironment,
|
||||
callback: (result: TaskEvent<T>) -> Unit)
|
||||
|
||||
/**
|
||||
* This method should be called by Tasks in their [onRun] method wherever
|
||||
* they need to communicate with the Tangem Card by launching commands.
|
||||
*/
|
||||
protected fun <T : CommandResponse> sendCommand(
|
||||
command: CommandSerializer<T>,
|
||||
cardEnvironment: CardEnvironment,
|
||||
|
|
@ -116,7 +158,7 @@ abstract class Task<T> {
|
|||
}
|
||||
is CompletionResult.Failure ->
|
||||
if (result.error is TaskError.TagLost) {
|
||||
delegate?.hideSecurityDelay()
|
||||
delegate?.onTagLost()
|
||||
} else if (result.error is TaskError.UserCancelledError) {
|
||||
callback(CompletionResult.Failure(TaskError.UserCancelledError()))
|
||||
reader?.readingActive = false
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ import com.tangem.tasks.TaskError
|
|||
import kotlinx.android.synthetic.main.layout_touch_card.*
|
||||
import kotlinx.android.synthetic.main.nfc_bottom_sheet.*
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of [CardManagerDelegate].
|
||||
* If no customisation is required, this is the preferred way to use Tangem SDK.
|
||||
*/
|
||||
class DefaultCardManagerDelegate(private val reader: NfcReader) : CardManagerDelegate {
|
||||
|
||||
lateinit var activity: FragmentActivity
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import androidx.lifecycle.LifecycleOwner
|
|||
import androidx.lifecycle.OnLifecycleEvent
|
||||
import com.tangem.tangem_sdk_new.nfc.NfcManager
|
||||
|
||||
|
||||
/**
|
||||
* [LifecycleObserver] for [NfcManager], helps to coordinate NFC modes with Activity lifecycle.
|
||||
*/
|
||||
class NfcLifecycleObserver(private var nfcManager: NfcManager) : LifecycleObserver {
|
||||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ import android.os.Build
|
|||
import android.os.Bundle
|
||||
import com.tangem.Log
|
||||
|
||||
/**
|
||||
* Helps use of NFC, leveraging Android NFC functionality.
|
||||
* Launches [NfcAdapter], manages it with [Activity] lifecycle,
|
||||
* enables and disables Nfc Reading Mode, receives NFC [Tag].
|
||||
*/
|
||||
class NfcManager : NfcAdapter.ReaderCallback {
|
||||
|
||||
val reader = NfcReader()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ import com.tangem.common.apdu.CommandApdu
|
|||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.tasks.TaskError
|
||||
|
||||
/**
|
||||
* Provides NFC communication between an Android application and Tangem card.
|
||||
*/
|
||||
class NfcReader : CardReader {
|
||||
|
||||
override var readingActive = false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue