diff --git a/tangem-core/src/main/java/com/tangem/CardEnvironment.kt b/tangem-core/src/main/java/com/tangem/CardEnvironment.kt index e4fdb8ec16..8c32ac506f 100644 --- a/tangem-core/src/main/java/com/tangem/CardEnvironment.kt +++ b/tangem-core/src/main/java/com/tangem/CardEnvironment.kt @@ -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, diff --git a/tangem-core/src/main/java/com/tangem/CardManager.kt b/tangem-core/src/main/java/com/tangem/CardManager.kt index d0efa5a194..91d7d66af3 100644 --- a/tangem-core/src/main/java/com/tangem/CardManager.kt +++ b/tangem-core/src/main/java/com/tangem/CardManager.kt @@ -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) -> 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, cardId: String, callback: (result: TaskEvent) -> Unit) { val signCommand: SignCommand @@ -42,6 +75,9 @@ class CardManager( runTask(task, cardId, callback) } + /** + + */ fun runTask(task: Task, cardId: String? = null, callback: (result: TaskEvent) -> Unit) { if (isBusy) { @@ -63,6 +99,9 @@ class CardManager( } } + /** + + */ fun runCommand(command: CommandSerializer, cardId: String? = null, callback: (result: TaskEvent) -> Unit) { diff --git a/tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt b/tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt index 124d8e9d01..2492913faa 100644 --- a/tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt +++ b/tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt @@ -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() diff --git a/tangem-core/src/main/java/com/tangem/CardReader.kt b/tangem-core/src/main/java/com/tangem/CardReader.kt index 4b4d00769f..6b0d912eca 100644 --- a/tangem-core/src/main/java/com/tangem/CardReader.kt +++ b/tangem-core/src/main/java/com/tangem/CardReader.kt @@ -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) -> Unit) + + /** + * Signals to [CardReader] to become ready to transceive data. + */ fun startNfcSession() + + /** + * Signals to [CardReader] that no further NFC transition is expected. + */ fun closeSession() } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/Log.kt b/tangem-core/src/main/java/com/tangem/Log.kt index 8f77c9a854..b03303b1a5 100644 --- a/tangem-core/src/main/java/com/tangem/Log.kt +++ b/tangem-core/src/main/java/com/tangem/Log.kt @@ -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) diff --git a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt index b581bc36ce..d7a2567195 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt @@ -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, diff --git a/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt b/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt index a5c7db90a4..0a767328c6 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt @@ -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 { - 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() diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt index f4eb833de6..466c8b8f23 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt @@ -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() { diff --git a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt index df7a61c2ff..db9262466b 100644 --- a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt @@ -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, private val cardId: String) : CommandSerializer() { @@ -51,6 +63,12 @@ class SignCommand(private val hashes: Array, 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) { cardEnvironment.terminalKeys?.let { terminalKeyPair -> val signedData = dataToSign.sign(terminalKeyPair.privateKey) diff --git a/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt b/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt index aac64bbc28..4ee035c5ba 100644 --- a/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt +++ b/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt @@ -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 { class Success(val data: T) : CompletionResult() class Failure(val error: TaskError) : CompletionResult() diff --git a/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt b/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt index 86a706bd3e..97fb84652c 100644 --- a/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt +++ b/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt @@ -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 { diff --git a/tangem-core/src/main/java/com/tangem/common/apdu/Instruction.kt b/tangem-core/src/main/java/com/tangem/common/apdu/Instruction.kt index e4669e620b..904c5437bd 100644 --- a/tangem-core/src/main/java/com/tangem/common/apdu/Instruction.kt +++ b/tangem-core/src/main/java/com/tangem/common/apdu/Instruction.kt @@ -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), diff --git a/tangem-core/src/main/java/com/tangem/common/apdu/ResponseApdu.kt b/tangem-core/src/main/java/com/tangem/common/apdu/ResponseApdu.kt index 9eecdc4448..9100322c8a 100644 --- a/tangem-core/src/main/java/com/tangem/common/apdu/ResponseApdu.kt +++ b/tangem-core/src/main/java/com/tangem/common/apdu/ResponseApdu.kt @@ -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? { return when { data.size < 2 -> null diff --git a/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt b/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt index c346fe1e71..a43cfbe969 100644 --- a/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt +++ b/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt @@ -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"), diff --git a/tangem-core/src/main/java/com/tangem/common/extentions/ByteArray.kt b/tangem-core/src/main/java/com/tangem/common/extentions/ByteArray.kt index db25eeaadd..cfc35a118b 100644 --- a/tangem-core/src/main/java/com/tangem/common/extentions/ByteArray.kt +++ b/tangem-core/src/main/java/com/tangem/common/extentions/ByteArray.kt @@ -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) } diff --git a/tangem-core/src/main/java/com/tangem/common/extentions/String.kt b/tangem-core/src/main/java/com/tangem/common/extentions/String.kt index 656d46bd9e..06a4f1a26e 100644 --- a/tangem-core/src/main/java/com/tangem/common/extentions/String.kt +++ b/tangem-core/src/main/java/com/tangem/common/extentions/String.kt @@ -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")) diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt b/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt index b1b2c3b5fa..68063c8d07 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/Tlv.kt @@ -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 diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt index bec99188c8..bd2efe73dd 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt @@ -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) { + /** + * 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 mapOptional(tag: TlvTag): T? = try { map(tag) @@ -23,6 +37,17 @@ class TlvMapper(val tlvList: List) { 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 map(tag: TlvTag): T { val tlvValue: ByteArray = tlvList.find { it.tag == tag }?.value ?: if (tag.valueType() == TlvValueType.BoolValue && T::class == Boolean::class) { diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt index aa0e0036f8..0b6933f175 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvTag.kt @@ -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 diff --git a/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt b/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt index 1dc0d2bc82..e202fb2727 100644 --- a/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt +++ b/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt @@ -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) diff --git a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt index 4bd9194dc9..10625f335d 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt @@ -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() { override fun onRun(cardEnvironment: CardEnvironment, diff --git a/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt b/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt index 96176afdc6..6c83f6a9de 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt @@ -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( private val command: CommandSerializer ) : Task() { diff --git a/tangem-core/src/main/java/com/tangem/tasks/Task.kt b/tangem-core/src/main/java/com/tangem/tasks/Task.kt index b866f6ad47..c00995e180 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/Task.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/Task.kt @@ -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 { + + /** + * A callback that is triggered when a command returns response from a card + * (on a completion of a [CommandSerializer]). + */ class Event(val data: T) : TaskEvent() + + /** + * 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(val error: TaskError? = null) : TaskEvent() } +/** + * 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 { 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) -> Unit) { delegate?.onNfcSessionStarted() @@ -54,6 +83,12 @@ abstract class Task { 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 { } } + /** + * In this method the individual Tasks' logic should be implemented. + */ protected abstract fun onRun(cardEnvironment: CardEnvironment, callback: (result: TaskEvent) -> 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 sendCommand( command: CommandSerializer, cardEnvironment: CardEnvironment, @@ -116,7 +158,7 @@ abstract class Task { } 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 diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt index e2a6e984f6..4985b73cec 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt @@ -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 diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/NfcLifecycleObserver.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/NfcLifecycleObserver.kt index f8caac7feb..2406b5c123 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/NfcLifecycleObserver.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/NfcLifecycleObserver.kt @@ -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) diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcManager.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcManager.kt index b6e29e445a..cdcb94c1c3 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcManager.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcManager.kt @@ -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() diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt index 713e232cdf..d80afe9c5e 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt @@ -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