Updated on 2026-08-14
This commit is contained in:
parent
7c70545ae7
commit
eb468efb99
9 changed files with 116 additions and 23 deletions
|
|
@ -7,8 +7,8 @@ version '0.1.0'
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "com.madgag.spongycastle:core:1.56.0.0"
|
implementation "com.madgag.spongycastle:core:1.58.0.0"
|
||||||
implementation "com.madgag.spongycastle:prov:1.56.0.0"
|
implementation "com.madgag.spongycastle:prov:1.58.0.0"
|
||||||
implementation 'net.i2p.crypto:eddsa:0.3.0'
|
implementation 'net.i2p.crypto:eddsa:0.3.0'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
|
||||||
testImplementation "com.google.truth:truth:1.0"
|
testImplementation "com.google.truth:truth:1.0"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package com.tangem
|
package com.tangem
|
||||||
|
|
||||||
import com.tangem.commands.*
|
import com.tangem.commands.*
|
||||||
|
import com.tangem.common.CardEnvironment
|
||||||
|
import com.tangem.common.TerminalKeysService
|
||||||
import com.tangem.crypto.CryptoUtils
|
import com.tangem.crypto.CryptoUtils
|
||||||
import com.tangem.tasks.*
|
import com.tangem.tasks.*
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
@ -16,10 +18,12 @@ import java.util.concurrent.Executors
|
||||||
*/
|
*/
|
||||||
class CardManager(
|
class CardManager(
|
||||||
private val reader: CardReader,
|
private val reader: CardReader,
|
||||||
private val cardManagerDelegate: CardManagerDelegate? = null) {
|
private val cardManagerDelegate: CardManagerDelegate? = null,
|
||||||
|
private val config: Config = Config()
|
||||||
|
) {
|
||||||
|
|
||||||
|
private var terminalKeysService: TerminalKeysService? = null
|
||||||
private var isBusy = false
|
private var isBusy = false
|
||||||
private val cardEnvironmentRepository = mutableMapOf<String, CardEnvironment>()
|
|
||||||
private val cardManagerExecutor = Executors.newSingleThreadExecutor()
|
private val cardManagerExecutor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
@ -67,7 +71,7 @@ class CardManager(
|
||||||
callback: (result: TaskEvent<SignResponse>) -> Unit) {
|
callback: (result: TaskEvent<SignResponse>) -> Unit) {
|
||||||
val signCommand: SignCommand
|
val signCommand: SignCommand
|
||||||
try {
|
try {
|
||||||
signCommand = SignCommand(hashes, cardId)
|
signCommand = SignCommand(hashes)
|
||||||
} catch (error: Exception) {
|
} catch (error: Exception) {
|
||||||
if (error is TaskError) {
|
if (error is TaskError) {
|
||||||
callback(TaskEvent.Completion(error))
|
callback(TaskEvent.Completion(error))
|
||||||
|
|
@ -91,7 +95,7 @@ class CardManager(
|
||||||
*/
|
*/
|
||||||
fun readIssuerData(cardId: String,
|
fun readIssuerData(cardId: String,
|
||||||
callback: (result: TaskEvent<ReadIssuerDataResponse>) -> Unit) {
|
callback: (result: TaskEvent<ReadIssuerDataResponse>) -> Unit) {
|
||||||
val getIssuerDataCommand = ReadIssuerDataCommand(cardId)
|
val getIssuerDataCommand = ReadIssuerDataCommand()
|
||||||
val task = SingleCommandTask(getIssuerDataCommand)
|
val task = SingleCommandTask(getIssuerDataCommand)
|
||||||
runTask(task, cardId, callback)
|
runTask(task, cardId, callback)
|
||||||
}
|
}
|
||||||
|
|
@ -114,10 +118,10 @@ class CardManager(
|
||||||
issuerDataCounter: Int? = null,
|
issuerDataCounter: Int? = null,
|
||||||
callback: (result: TaskEvent<WriteIssuerDataResponse>) -> Unit) {
|
callback: (result: TaskEvent<WriteIssuerDataResponse>) -> Unit) {
|
||||||
val writeIssuerDataCommand = WriteIssuerDataCommand(
|
val writeIssuerDataCommand = WriteIssuerDataCommand(
|
||||||
cardId,
|
issuerData,
|
||||||
issuerData,
|
issuerDataSignature,
|
||||||
issuerDataSignature,
|
issuerDataCounter
|
||||||
issuerDataCounter)
|
)
|
||||||
val task = SingleCommandTask(writeIssuerDataCommand)
|
val task = SingleCommandTask(writeIssuerDataCommand)
|
||||||
runTask(task, cardId, callback)
|
runTask(task, cardId, callback)
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +138,7 @@ class CardManager(
|
||||||
*/
|
*/
|
||||||
fun createWallet(cardId: String,
|
fun createWallet(cardId: String,
|
||||||
callback: (result: TaskEvent<CreateWalletResponse>) -> Unit) {
|
callback: (result: TaskEvent<CreateWalletResponse>) -> Unit) {
|
||||||
val createWalletCommand = CreateWalletCommand(cardId)
|
val createWalletCommand = CreateWalletCommand()
|
||||||
val task = SingleCommandTask(createWalletCommand)
|
val task = SingleCommandTask(createWalletCommand)
|
||||||
runTask(task, cardId, callback)
|
runTask(task, cardId, callback)
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +152,7 @@ class CardManager(
|
||||||
*/
|
*/
|
||||||
fun purgeWallet(cardId: String,
|
fun purgeWallet(cardId: String,
|
||||||
callback: (result: TaskEvent<PurgeWalletResponse>) -> Unit) {
|
callback: (result: TaskEvent<PurgeWalletResponse>) -> Unit) {
|
||||||
val purgeWalletCommand = PurgeWalletCommand(cardId)
|
val purgeWalletCommand = PurgeWalletCommand()
|
||||||
val task = SingleCommandTask(purgeWalletCommand)
|
val task = SingleCommandTask(purgeWalletCommand)
|
||||||
runTask(task, cardId, callback)
|
runTask(task, cardId, callback)
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +167,7 @@ class CardManager(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val environment = fetchCardEnvironment(cardId)
|
val environment = prepareCardEnvironment(cardId)
|
||||||
isBusy = true
|
isBusy = true
|
||||||
|
|
||||||
task.reader = reader
|
task.reader = reader
|
||||||
|
|
@ -187,7 +191,19 @@ class CardManager(
|
||||||
runTask(task, cardId, callback)
|
runTask(task, cardId, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchCardEnvironment(cardId: String?): CardEnvironment {
|
/**
|
||||||
return cardEnvironmentRepository[cardId] ?: CardEnvironment(cardId = cardId)
|
* Allows to set a particular [TerminalKeysService] to retrieve terminal keys.
|
||||||
|
* Default implementation is provided in tangem-sdk module: [TerminalKeysStorage].
|
||||||
|
*/
|
||||||
|
fun setTerminalKeysService(terminalKeysService: TerminalKeysService) {
|
||||||
|
this.terminalKeysService = terminalKeysService
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prepareCardEnvironment(cardId: String?): CardEnvironment {
|
||||||
|
val terminalKeys = if (config.linkedTerminal) terminalKeysService?.getKeys() else null
|
||||||
|
return CardEnvironment(
|
||||||
|
cardId = cardId,
|
||||||
|
terminalKeys = terminalKeys
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
5
tangem-core/src/main/java/com/tangem/Config.kt
Normal file
5
tangem-core/src/main/java/com/tangem/Config.kt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.tangem
|
||||||
|
|
||||||
|
data class Config(
|
||||||
|
val linkedTerminal: Boolean = true
|
||||||
|
)
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.tangem
|
package com.tangem.common
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.tangem.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for a service for managing Terminal keypair, used for Linked Terminal feature.
|
||||||
|
* Its implementation Needs to be provided to [com.tangem.CardManager]
|
||||||
|
* by calling [com.tangem.CardManager.setTerminalKeysService].
|
||||||
|
* Default implementation is provided in tangem-sdk module: [TerminalKeysStorage].
|
||||||
|
* Linked Terminal feature can be disabled manually by editing [com.tangem.Config].
|
||||||
|
*/
|
||||||
|
interface TerminalKeysService {
|
||||||
|
fun getKeys(): KeyPair
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private val nfcManager = NfcManager()
|
private val nfcManager = NfcManager()
|
||||||
private val cardManagerDelegate: DefaultCardManagerDelegate = DefaultCardManagerDelegate(nfcManager.reader)
|
private val cardManagerDelegate: DefaultCardManagerDelegate = DefaultCardManagerDelegate(nfcManager.reader)
|
||||||
private val cardManager = CardManager(nfcManager.reader, cardManagerDelegate, TerminalKeysStorage())
|
private val cardManager = CardManager(nfcManager.reader, cardManagerDelegate)
|
||||||
|
|
||||||
private lateinit var cardId: String
|
private lateinit var cardId: String
|
||||||
private lateinit var issuerData: ByteArray
|
private lateinit var issuerData: ByteArray
|
||||||
|
|
@ -26,6 +26,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
|
cardManager.setTerminalKeysService(TerminalKeysStorage(application))
|
||||||
nfcManager.setCurrentActivity(this)
|
nfcManager.setCurrentActivity(this)
|
||||||
cardManagerDelegate.activity = this
|
cardManagerDelegate.activity = this
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,16 +40,19 @@ dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'com.google.android.material:material:1.1.0-beta01'
|
implementation 'com.google.android.material:material:1.1.0'
|
||||||
implementation 'com.skyfishjy.ripplebackground:library:1.0.1'
|
implementation 'com.skyfishjy.ripplebackground:library:1.0.1'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.1.0'
|
implementation 'androidx.core:core-ktx:1.1.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
implementation "androidx.lifecycle:lifecycle-runtime:2.1.0"
|
implementation "androidx.lifecycle:lifecycle-runtime:2.2.0"
|
||||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.1.0"
|
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.61"
|
||||||
|
|
||||||
|
implementation 'at.favre.lib:armadillo:0.9.0'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.50"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.tangem.tangem_sdk_new
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import at.favre.lib.armadillo.Armadillo
|
||||||
|
import com.tangem.common.KeyPair
|
||||||
|
import com.tangem.common.TerminalKeysService
|
||||||
|
import com.tangem.common.extensions.hexToBytes
|
||||||
|
import com.tangem.common.extensions.toHexString
|
||||||
|
import com.tangem.crypto.CryptoUtils
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for managing Terminal keypair, used for Linked Terminal feature.
|
||||||
|
* Needs to be provided to [com.tangem.CardManager] by calling [com.tangem.CardManager.setTerminalKeysService]
|
||||||
|
* Linked Terminal feature can be disabled manually by editing [com.tangem.Config].
|
||||||
|
* @param applicationContext is required to retrieve an instance of [SharedPreferences]
|
||||||
|
*/
|
||||||
|
class TerminalKeysStorage(applicationContext: Application): TerminalKeysService {
|
||||||
|
|
||||||
|
private val preferences: SharedPreferences by lazy {
|
||||||
|
Armadillo.create(applicationContext, PREFERENCES_KEY)
|
||||||
|
.encryptionFingerprint(applicationContext)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves generated keys from encrypted shared preferences if the keys exist.
|
||||||
|
* Generates new and stores them in encrypted shared preferences otherwise
|
||||||
|
*/
|
||||||
|
override fun getKeys(): KeyPair {
|
||||||
|
val privateKey = preferences.getString(TERMINAL_PRIVATE_KEY, null)?.hexToBytes()
|
||||||
|
val publicKey = preferences.getString(TERMINAL_PUBLIC_KEY, null)?.hexToBytes()
|
||||||
|
return if (privateKey == null || publicKey == null) {
|
||||||
|
generateAndSaveKeys()
|
||||||
|
} else {
|
||||||
|
KeyPair(publicKey, privateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateAndSaveKeys(): KeyPair {
|
||||||
|
val privateKey = CryptoUtils.generateRandomBytes(32)
|
||||||
|
val publicKey = CryptoUtils.generatePublicKey(privateKey)
|
||||||
|
preferences.edit().putString(TERMINAL_PRIVATE_KEY, privateKey.toHexString()).apply()
|
||||||
|
preferences.edit().putString(TERMINAL_PUBLIC_KEY, publicKey.toHexString()).apply()
|
||||||
|
return KeyPair(publicKey, privateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PREFERENCES_KEY = "myPrefs"
|
||||||
|
const val TERMINAL_PRIVATE_KEY = "terminalPrivateKey"
|
||||||
|
const val TERMINAL_PUBLIC_KEY = "terminalPublicKey"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ import android.os.Bundle
|
||||||
import com.tangem.Log
|
import com.tangem.Log
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helps use of NFC, leveraging Android NFC functionality.
|
* Helps use NFC, leveraging Android NFC functionality.
|
||||||
* Launches [NfcAdapter], manages it with [Activity] lifecycle,
|
* Launches [NfcAdapter], manages it with [Activity] lifecycle,
|
||||||
* enables and disables Nfc Reading Mode, receives NFC [Tag].
|
* enables and disables Nfc Reading Mode, receives NFC [Tag].
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue