Updated on 2026-08-14

This commit is contained in:
Tangem 2019-11-14 17:19:15 +03:00
parent d6d7c292e7
commit 7db8aa448c
15 changed files with 77 additions and 90 deletions

View file

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":tangem-card-old" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":tangem-card-old" />
<option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" />
<option name="LAST_KNOWN_AGP_VERSION" />
</configuration>
</facet>
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="true" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
<output url="file://$MODULE_DIR$/build/classes/java/main" />
<output-test url="file://$MODULE_DIR$/build/classes/java/test" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="Gradle: prov-1.56.0.0" level="project" />
<orderEntry type="library" exported="" name="Gradle: core-1.56.0.0" level="project" />
<orderEntry type="library" exported="" name="Gradle: eddsa-0.3.0" level="project" />
</component>
</module>

View file

@ -14,7 +14,7 @@ import java.util.concurrent.Executors
* @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.
* @property cardManagerDelegate An interface that allows interaction with users and shows relevant UI.
* Its default implementation, DefaultCardManagerDelegate, is in our tangem-sdk module.
*/
class CardManager(
@ -30,7 +30,11 @@ class CardManager(
}
/**
* A method that allows to read a card and verify that its private key.
* To start using any card, you first need to read it using the scanCard() method.
* This method launches an NFC session, and once its connected with the card,
* it obtains the card data. Optionally, if the card contains a wallet (private and public key pair),
* it proves that the wallet owns a private key that corresponds to a public one.
*
* 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]
@ -43,8 +47,10 @@ class CardManager(
}
/**
* 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 allows you to sign one or multiple hashes.
* Simultaneous signing of array of hashes in a single [SignCommand] is required to support
* Bitcoin-type multi-input blockchains (UTXO).
* The [SignCommand] will return a corresponding array of signatures.
*
* This method launches on the new thread [SignCommand] that will send the following events in a callback:
* [SignResponse] after completing [SignCommand]
@ -53,7 +59,7 @@ class CardManager(
* 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 hashes Array of transaction hashes. It can be from one or up to ten hashes of the same length.
* @param cardId CID, Unique Tangem card ID number
* @param callback
*

View file

@ -10,12 +10,36 @@ import com.tangem.tasks.TaskError
*/
interface CardManagerDelegate {
/**
* It is called when user is expected to scan a Tangem Card with an Android device.
*/
fun onNfcSessionStarted()
/**
* It is called when security delay is triggered by the card.
* A user is expected to hold the card until the security delay is over.
*/
fun onSecurityDelay(ms: Int)
/**
* It is called when user takes the card away from the Android device during the scanning
* (for example when security delay is in progress) and the TagLostException is received.
*/
fun onTagLost()
/**
* It is called when NFC session was completed and a user can take the card away from the Android device.
*/
fun onNfcSessionCompleted()
/**
* It is called when some error occur during NFC session.
*/
fun onError(error: TaskError? = null)
fun requestPin(callback: (result: CompletionResult<String>) -> Unit)
/**
* It is called when a user is expected to enter pin code.
*/
fun onPinRequested(callback: (result: CompletionResult<String>) -> Unit)
}

View file

@ -11,25 +11,19 @@ import com.tangem.common.apdu.ResponseApdu
*/
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]
* @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()
fun openSession()
/**
* Signals to [CardReader] that no further NFC transition is expected.

View file

@ -16,8 +16,7 @@ import com.tangem.tasks.TaskError
*
* @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.
* @property walletSignature Challenge and salt signed with the wallet private key.
*/
class CheckWalletResponse(
val cardId: String,
@ -29,8 +28,7 @@ class CheckWalletResponse(
* 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 users pin 1 code to access the card. Default unhashed value: 000000.
* Pin code should be taken from card environment.
* @property pin1 Hashed users pin 1 code to access the card. Default unhashed value: 000000.
* @property cardId Unique Tangem card ID number
* @property challenge Random challenge generated by application
*/

View file

@ -20,7 +20,7 @@ abstract class CommandSerializer<T : CommandResponse> {
* 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].
* @return Command data that can be converted to raw bytes with a method [CommandApdu.toBytes].
*/
abstract fun serialize(cardEnvironment: CardEnvironment): CommandApdu
@ -28,7 +28,7 @@ abstract class CommandSerializer<T : CommandResponse> {
* 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].
* @return Card response, converted to a [CommandResponse] of a type [T].
*/
abstract fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): T?

View file

@ -71,7 +71,7 @@ enum class ProductMask(val code: Byte) {
/**
* Stores and maps Tangem card settings.
*
* @property rawValue are card settings in a form of flags,
* @property rawValue Card settings in a form of flags,
* while flags definitions and values are in [SettingsMask.Companion] as constants.
*/
data class SettingsMask(val rawValue: Int) {

View file

@ -14,7 +14,7 @@ import com.tangem.tasks.TaskError
/**
* @param cardId CID, Unique Tangem card ID number
* @param signature signed hashes (array of resulting signatures)
* @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
@ -64,10 +64,12 @@ class SignCommand(private val hashes: Array<ByteArray>, private val cardId: Stri
}
/**
* 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).
d
* Application can optionally submit a public key Terminal_PublicKey in [SignCommand].
* Submitted key is stored by the Tangem card if it differs from a previous submitted Terminal_PublicKey.
* The Tangem card will not enforce security delay if [SignCommand] will be called with
* TerminalTransactionSignature parameter containing a correct signature of raw data to be signed made with TerminalPrivateKey
* (this key should be generated and securily stored by the application).
*/
private fun addTerminalSignature(cardEnvironment: CardEnvironment, tlvData: MutableList<Tlv>) {
cardEnvironment.terminalKeys?.let { terminalKeyPair ->

View file

@ -5,7 +5,7 @@ import com.tangem.tasks.TaskError
/**
* Response class encapsulating successful and failed results.
* [T] is a type of data that is returned in [Success].
* @param T Type of data that is returned in [Success].
*/
sealed class CompletionResult<T> {
class Success<T>(val data: T) : CompletionResult<T>()

View file

@ -10,7 +10,7 @@ import java.io.ByteArrayOutputStream
* 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
* @property tlvList A list of TLVs that are to be sent to the card
*/
class CommandApdu(

View file

@ -5,9 +5,9 @@ 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 data Raw response from the card.
* @property sw Status word code, reflecting the status of the response.
* @property statusWord parsed status word.
* @property statusWord Parsed status word.
*/
class ResponseApdu(val data: ByteArray) {

View file

@ -28,7 +28,7 @@ class TlvMapper(val tlvList: List<Tlv>) {
*
* @param tag [TlvTag] of a [Tlv] which value is to be returned.
*
* @return value converted to a nullable type [T].
* @return Value converted to a nullable type [T].
*/
inline fun <reified T> mapOptional(tag: TlvTag): T? =
try {

View file

@ -30,12 +30,12 @@ object CryptoUtils {
* 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
* @param publicKey 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
* @return Result of a verification
*/
fun verify(publicKey: ByteArray, message: ByteArray, signature: ByteArray,
curve: EllipticCurve = EllipticCurve.Secp256k1): Boolean {
@ -48,10 +48,10 @@ object CryptoUtils {
/**
* 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
* @param privateKeyArray A private key from which a public key is generated
* @param curve Elliptic curve used
*
* @return public key [ByteArray]
* @return Public key [ByteArray]
*/
fun generatePublicKey(
privateKeyArray: ByteArray,
@ -67,10 +67,10 @@ object CryptoUtils {
/**
* 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
* @param privateKeyArray Key to sign data
* @param curve Elliptic curve that is used to sign data
*
* @return signed data
* @return Signed data
*/
fun ByteArray.sign(privateKeyArray: ByteArray, curve: EllipticCurve = EllipticCurve.Secp256k1): ByteArray {
return when (curve) {

View file

@ -8,7 +8,7 @@ import com.tangem.common.CompletionResult
/**
* Allows to perform a single command.
*
* @property command is a command that will be performed.
* @property command A command that will be performed.
*/
class SingleCommandTask<Event : CommandResponse>(
private val command: CommandSerializer<Event>

View file

@ -41,13 +41,12 @@ sealed class TaskError(description: String? = null) : Exception(description) {
}
/**
* Events that are are sent in callbacks from [Task] during [Task] and Commands completions.
* Events that are are sent in callbacks from [Task].
*/
sealed class TaskEvent<T> {
/**
* A callback that is triggered when a command returns response from a card
* (on a completion of a [CommandSerializer]).
* A callback that is triggered by a Task.
*/
class Event<T>(val data: T) : TaskEvent<T>()
@ -72,13 +71,13 @@ abstract class Task<T> {
/**
* 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]
* @param cardEnvironment Relevant current version of a card environment
* @param callback It will be triggered during the performance of the [Task]
*/
fun run(cardEnvironment: CardEnvironment,
callback: (result: TaskEvent<T>) -> Unit) {
delegate?.onNfcSessionStarted()
reader?.startNfcSession()
reader?.openSession()
Log.i(this::class.simpleName!!, "Nfc task is started")
onRun(cardEnvironment, callback)
}
@ -86,8 +85,8 @@ abstract class Task<T> {
/**
* 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]
* @param withError 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()
@ -161,7 +160,7 @@ abstract class Task<T> {
delegate?.onTagLost()
} else if (result.error is TaskError.UserCancelledError) {
callback(CompletionResult.Failure(TaskError.UserCancelledError()))
reader?.readingActive = false
reader?.closeSession()
}
}
}