diff --git a/app/build.gradle b/app/build.gradle index 94d37ff858..e6cb6cd396 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,8 +76,8 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation 'com.tangem:blockchain:develop-38' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-47' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-47' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-70' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-70' // WebView implementation "androidx.browser:browser:1.3.0" diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 415eb6e9cc..0cf3af21e6 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 415eb6e9ccfcdc3573391b869adeba433f790087 +Subproject commit 0cf3af21e6f441e6323a1ba127ee7518168797df diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Context.kt b/app/src/main/java/com/tangem/tap/common/extensions/Context.kt new file mode 100644 index 0000000000..60345e1ab7 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Context.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.common.extensions + +import android.content.Context + +fun Context.readFile(fileName: String): String = + this.openFileInput(fileName).bufferedReader().readText() + +fun Context.rewriteFile(content: String, fileName: String) { + this.openFileOutput(fileName, Context.MODE_PRIVATE).use { + it.write(content.toByteArray(), 0, content.length) + } +} + +fun Context.readAssetAsString(fileName: String): String { + return this.assets.open("$fileName.json").bufferedReader().readText() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigLoaders.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigLoaders.kt index a47e8fec1c..4c19bab889 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigLoaders.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigLoaders.kt @@ -6,6 +6,7 @@ import com.google.firebase.remoteconfig.ktx.remoteConfig import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.readAssetAsString import com.tangem.tap.domain.configurable.Loader import timber.log.Timber @@ -22,8 +23,8 @@ class FeaturesLocalLoader( val featureAdapter: JsonAdapter = moshi.adapter(FeatureModel::class.java) val valuesAdapter: JsonAdapter = moshi.adapter(ConfigValueModel::class.java) - val jsonFeatures = readAssetAsString(Loader.featuresName) - val jsonConfigValues = readAssetAsString(Loader.configValuesName) + val jsonFeatures = context.readAssetAsString(Loader.featuresName) + val jsonConfigValues = context.readAssetAsString(Loader.configValuesName) ConfigModel(featureAdapter.fromJson(jsonFeatures), valuesAdapter.fromJson(jsonConfigValues)) } catch (ex: Exception) { @@ -32,10 +33,6 @@ class FeaturesLocalLoader( } onComplete(config) } - - private fun readAssetAsString(fileName: String): String { - return context.assets.open("$fileName.json").bufferedReader().readText() - } } class FeaturesRemoteLoader( diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt index ee2cda0d50..c75a37a709 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt @@ -21,7 +21,7 @@ class CreateWalletAndRescanTask : CardSessionRunnable { val firmwareVersion = card.firmwareVersion val task = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) { - CreateWalletCommand(card.supportedCurves.first(), false) + CreateWalletCommand(card.supportedCurves.first()) } else { CreateWalletsTask() } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt index 1443371141..f1afec8e00 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt @@ -33,7 +33,7 @@ class CreateWalletsTask(curves: List = emptyList()) : CardSession callback: (result: CompletionResult) -> Unit ) { - CreateWalletCommand(curve, false).run(session) { result -> + CreateWalletCommand(curve).run(session) { result -> when (result) { is CompletionResult.Success -> { if (index == curves.lastIndex) { diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt index 0ca3f113c3..400ee19ae3 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt @@ -20,14 +20,14 @@ class CreateFirstTwinWalletTask : CardSessionRunnable { when (response) { is CompletionResult.Success -> { session.environment.card = session.environment.card?.setWallets(emptyList()) - CreateWalletCommand(EllipticCurve.Secp256k1, false) + CreateWalletCommand(EllipticCurve.Secp256k1) .run(session) { callback(it) } } is CompletionResult.Failure -> callback(CompletionResult.Failure(response.error)) } } } else { - CreateWalletCommand(EllipticCurve.Secp256k1, false) + CreateWalletCommand(EllipticCurve.Secp256k1) .run(session) { callback(it) } } } diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt index 1c0af37da4..826e57090b 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt @@ -2,6 +2,7 @@ package com.tangem.tap.domain.twins import com.tangem.Message import com.tangem.common.CompletionResult +import com.tangem.common.KeyPair import com.tangem.common.card.EllipticCurve import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable @@ -13,6 +14,7 @@ import com.tangem.tap.domain.extensions.getSingleWallet class CreateSecondTwinWalletTask( private val firstPublicKey: String, + private val issuerKeys: KeyPair, private val preparingMessage: Message, private val creatingWalletMessage: Message ) : CardSessionRunnable { @@ -36,13 +38,13 @@ class CreateSecondTwinWalletTask( private fun finishTask(session: CardSession, callback: (result: CompletionResult) -> Unit) { session.setInitialMessage(creatingWalletMessage) - CreateWalletCommand(EllipticCurve.Secp256k1, false).run(session) { result -> + CreateWalletCommand(EllipticCurve.Secp256k1).run(session) { result -> when (result) { is CompletionResult.Success -> { session.environment.card = session.environment.card?.updateWallet(result.data.wallet) WriteProtectedIssuerDataTask( - firstPublicKey.hexToBytes(), TwinCardsManager.issuerKeys + firstPublicKey.hexToBytes(), issuerKeys ).run(session) { writeResult -> when (writeResult) { is CompletionResult.Success -> callback(result) diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt index 0282a18d65..8f742b5852 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt @@ -1,5 +1,8 @@ package com.tangem.tap.domain.twins +import android.content.Context +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Types import com.tangem.Message import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult @@ -10,10 +13,12 @@ import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString import com.tangem.crypto.CryptoUtils import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.readAssetAsString import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.network.createMoshi import com.tangem.tap.tangemSdkManager -class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { +class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: Context) { private val currentCardId: String = scanNoteResponse.card.cardId private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId) @@ -21,9 +26,13 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { private var currentCardPublicKey: String? = null private var secondCardPublicKey: String? = null + private val issuerKeyPair: KeyPair = getIssuerKeys( + context, scanNoteResponse.card.issuer.publicKey.toHexString() + ) + suspend fun createFirstWallet(message: Message): SimpleResult { val response = tangemSdkManager.runTaskAsync( - CreateFirstTwinWalletTask(), currentCardId, message + CreateFirstTwinWalletTask(), currentCardId, message ) when (response) { is CompletionResult.Success -> { @@ -46,13 +55,17 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { suspend fun createSecondWallet( - initialMessage: Message, - preparingMessage: Message, - creatingWalletMessage: Message + initialMessage: Message, + preparingMessage: Message, + creatingWalletMessage: Message, ): SimpleResult { val response = tangemSdkManager.runTaskAsync( - CreateSecondTwinWalletTask(currentCardPublicKey!!, preparingMessage, creatingWalletMessage), - secondCardId, initialMessage + CreateSecondTwinWalletTask( + firstPublicKey = currentCardPublicKey!!, + issuerKeys = issuerKeyPair, + preparingMessage = preparingMessage, + creatingWalletMessage = creatingWalletMessage), + secondCardId, initialMessage ) when (response) { is CompletionResult.Success -> { @@ -75,8 +88,8 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { suspend fun complete(message: Message): Result { val response = tangemSdkManager.runTaskAsync( - FinalizeTwinTask(secondCardPublicKey!!.hexToBytes(), issuerKeys), - currentCardId, message + FinalizeTwinTask(secondCardPublicKey!!.hexToBytes(), issuerKeyPair), + currentCardId, message ) return when (response) { is CompletionResult.Success -> Result.Success(response.data) @@ -94,13 +107,6 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { } companion object { - val issuerKeys = KeyPair( - privateKey = "F9F4C50636C9E6FC65F92655BD5C21C85A5F6A34DCD0F1E75FCEA1980FE242F5".hexToBytes(), - publicKey = ("048196AA4B410AC44A3B9CCE18E7BE226AEA070ACC83A9CF67540F" + - "AC49AF25129F6A538A28AD6341358E3C4F9963064F" + - "7E365372A651D374E5C23CDD37FD099BF2").hexToBytes() - ) - fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean { if (issuerData.size < 65) return false val publicKey = issuerData.sliceArray(0 until 65) @@ -108,5 +114,30 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { return (cardWalletPublicKey != null && CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey)) } + + private fun getIssuerKeys(context: Context, publicKey: String): KeyPair { + val issuer = getIssuers(context).first { it.publicKey == publicKey } + return KeyPair( + publicKey = issuer.publicKey.hexToBytes(), + privateKey = issuer.privateKey.hexToBytes() + ) + } + + private fun getAdapter(): JsonAdapter> { + return createMoshi().adapter( + Types.newParameterizedType(List::class.java, Issuer::class.java) + ) + } + + private fun getIssuers(context: Context): List { + val file = context.readAssetAsString("tangem-app-config/issuers") + return getAdapter().fromJson(file)!! + } } -} \ No newline at end of file +} + +private class Issuer( + val id: String, + val privateKey: String, + val publicKey: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 79067f026e..4d3882a2a7 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.details.redux +import android.content.Context import com.tangem.Message import com.tangem.blockchain.common.Wallet import com.tangem.common.card.Card @@ -63,7 +64,9 @@ sealed class DetailsAction : Action { object Confirm : CreateTwinWalletAction() } - data class LaunchFirstStep(val message: Message) : CreateTwinWalletAction() { + data class LaunchFirstStep( + val message: Message, val context: Context + ) : CreateTwinWalletAction() { object Success : CreateTwinWalletAction() object Failure : CreateTwinWalletAction() } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt index e99f7c7ad6..fa81ea9a18 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt @@ -49,7 +49,7 @@ class CreateTwinWalletMiddleware { } is DetailsAction.CreateTwinWalletAction.LaunchFirstStep -> { store.state.globalState.scanNoteResponse?.let { - twinsManager = TwinCardsManager(it) + twinsManager = TwinCardsManager(it, action.context) } scope.launch { val result = twinsManager?.createFirstWallet(action.message) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt index 50d87de052..f8a09c7131 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt @@ -97,11 +97,12 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards), v_step_3.setBackgroundColor(defaultColor) btn_tap.setOnClickListener { store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchFirstStep( - Message(getString( - R.string.details_twins_recreate_title_format, - twinCardNumberString - ) - ))) + Message(getString( + R.string.details_twins_recreate_title_format, + twinCardNumberString + )), + requireContext() + )) } btn_tap.text = getString(R.string.details_twins_recreate_button_format, twinCardNumber.number.toString())