Updated on 2026-08-14

This commit is contained in:
Tangem 2025-01-27 12:55:34 +03:00
parent 6b7a309a37
commit e00f5390d2
8 changed files with 92 additions and 62 deletions

View file

@ -8,6 +8,7 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.common.map
import com.tangem.common.timemeasure.RealtimeMonotonicTimeSource
import com.tangem.crypto.CryptoUtils
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.datasource.local.visa.VisaOTPStorage
@ -29,6 +30,7 @@ import kotlinx.coroutines.*
import timber.log.Timber
import kotlin.coroutines.resume
import kotlin.jvm.Throws
import kotlin.time.measureTimedValue
@Suppress("LongParameterList")
class VisaCardActivationTask @AssistedInject constructor(
@ -69,31 +71,38 @@ class VisaCardActivationTask @AssistedInject constructor(
session = session,
)
return if (challengeToSign != null) {
context.signAuthorizationChallenge(challengeToSign)
} else {
val activationOrder = runCatching { visaActivationRepository.getActivationOrderToSign() }
.getOrElse {
return CompletionResult.Failure(TangemSdkError.Underlying(it.message ?: ""))
}
val timedResult = RealtimeMonotonicTimeSource.measureTimedValue {
if (challengeToSign != null) {
context.signAuthorizationChallenge(challengeToSign)
} else {
val activationOrder = runCatching { visaActivationRepository.getActivationOrderToSign() }
.getOrElse {
return CompletionResult.Failure(TangemSdkError.Underlying(it.message ?: ""))
}
context.signOrder(activationOrder)
context.signOrder(activationOrder)
}
}
Timber.i("VisaCardActivationTask all time: ${timedResult.duration}")
return timedResult.value
}
private suspend fun SessionContext.signAuthorizationChallenge(
challengeToSign: VisaAuthChallenge.Card,
): CompletionResult<VisaCardActivationResponse> {
val attestationCommand = AttestCardKeyCommand(challenge = CryptoUtils.generateRandomBytes(length = 16))
val result = suspendCancellableCoroutine { continuation ->
attestationCommand.run(session = session) { attestationResponse ->
continuation.resume(attestationResponse)
val timedResult = RealtimeMonotonicTimeSource.measureTimedValue {
suspendCancellableCoroutine { continuation ->
attestationCommand.run(session = session) { attestationResponse ->
continuation.resume(attestationResponse)
}
}
}
Timber.i("AttestCardKeyCommand time: ${timedResult.duration}")
return when (result) {
return when (val result = timedResult.value) {
is CompletionResult.Success -> {
Timber.tag("ASDASD").e("AttestCardKeyCommand success")
Timber.i("AttestCardKeyCommand success")
processSignedAuthorizationChallenge(
signedChallenge = challengeToSign.toSignedChallenge(
signedChallenge = result.data.cardSignature.toHexString(),
@ -102,7 +111,7 @@ class VisaCardActivationTask @AssistedInject constructor(
)
}
is CompletionResult.Failure -> {
Timber.tag("ASDASD").e("AttestCardKeyCommand failure ${result.error}")
Timber.e("AttestCardKeyCommand failure ${result.error}")
CompletionResult.Failure(result.error)
}
}
@ -153,19 +162,24 @@ class VisaCardActivationTask @AssistedInject constructor(
createOTP(session)
} else {
val createWalletTask = CreateWalletTask(VisaUtilities.mandatoryCurve)
val result = suspendCancellableCoroutine { continuation ->
createWalletTask.run(session) { createWalletResult ->
continuation.resume(createWalletResult)
val timedResult = RealtimeMonotonicTimeSource.measureTimedValue {
suspendCancellableCoroutine { continuation ->
createWalletTask.run(session) { createWalletResult ->
continuation.resume(createWalletResult)
}
}
}
when (result) {
Timber.i("CreateWalletTask time: ${timedResult.duration}")
when (val result = timedResult.value) {
is CompletionResult.Success -> {
Timber.tag("ASDASD").e("CreateWalletTask success")
Timber.i("CreateWalletTask success")
createOTP(session)
}
is CompletionResult.Failure -> {
Timber.tag("ASDASD").e("CreateWalletTask failure ${result.error}")
Timber.e("CreateWalletTask failure ${result.error}")
CompletionResult.Failure(result.error)
}
}
@ -179,20 +193,24 @@ class VisaCardActivationTask @AssistedInject constructor(
CompletionResult.Success(Unit)
} else {
val otpCommand = GenerateOTPCommand()
val result = suspendCancellableCoroutine { continuation ->
otpCommand.run(session) { otpResult ->
continuation.resume(otpResult)
val timedResult = RealtimeMonotonicTimeSource.measureTimedValue {
suspendCancellableCoroutine { continuation ->
otpCommand.run(session) { otpResult ->
continuation.resume(otpResult)
}
}
}
when (result) {
Timber.i("GenerateOTPCommand time: ${timedResult.duration}")
when (val result = timedResult.value) {
is CompletionResult.Success -> {
Timber.tag("ASDASD").e("GenerateOTPCommand success")
Timber.i("GenerateOTPCommand success")
otpStorage.saveOTP(cardId, result.data.rootOTP)
CompletionResult.Success(Unit)
}
is CompletionResult.Failure -> {
Timber.tag("ASDASD").e("GenerateOTPCommand failure ${result.error}")
Timber.e("GenerateOTPCommand failure ${result.error}")
CompletionResult.Failure(result.error)
}
}
@ -212,22 +230,26 @@ class VisaCardActivationTask @AssistedInject constructor(
derivationPath = VisaUtilities.visaDefaultDerivationPath,
)
val result = suspendCancellableCoroutine { continuation ->
task.run(session) { signResult ->
continuation.resume(signResult)
val timedResult = RealtimeMonotonicTimeSource.measureTimedValue {
suspendCancellableCoroutine { continuation ->
task.run(session) { signResult ->
continuation.resume(signResult)
}
}
}
return when (result) {
Timber.i("SignHashCommand time: ${timedResult.duration}")
return when (val result = timedResult.value) {
is CompletionResult.Success -> {
Timber.tag("ASDASD").e("SignHashCommand success")
Timber.i("SignHashCommand success")
handleSignedOrder(
activationOrder = order,
response = result.data,
)
}
is CompletionResult.Failure -> {
Timber.tag("ASDASD").e("SignHashCommand failure ${result.error}")
Timber.e("SignHashCommand failure ${result.error}")
CompletionResult.Failure(result.error)
}
}
@ -261,22 +283,27 @@ class VisaCardActivationTask @AssistedInject constructor(
return CompletionResult.Success(Unit)
}
Timber.tag("ASDASD").e("Setting access code: $accessCode")
Timber.i("Setting access code")
val task = SetUserCodeCommand.changeAccessCode(accessCode)
val result = suspendCancellableCoroutine { continuation ->
task.run(session) { setAccessCodeResult ->
continuation.resume(setAccessCodeResult)
val timedResult = RealtimeMonotonicTimeSource.measureTimedValue {
suspendCancellableCoroutine { continuation ->
task.run(session) { setAccessCodeResult ->
continuation.resume(setAccessCodeResult)
}
}
}
return when (result) {
Timber.i("SetUserCodeCommand time: ${timedResult.duration}")
return when (val result = timedResult.value) {
is CompletionResult.Success -> {
Timber.tag("ASDASD").e("SetUserCodeCommand success")
Timber.i("SetUserCodeCommand success")
CompletionResult.Success(Unit)
}
is CompletionResult.Failure -> {
Timber.tag("ASDASD").e("SetUserCodeCommand failure ${result.error}")
Timber.i("SetUserCodeCommand failure ${result.error}")
CompletionResult.Failure(result.error)
}
}

View file

@ -1,10 +1,10 @@
package com.tangem.tap.domain.visa
import com.tangem.common.CompletionResult
import com.tangem.common.card.Card
import com.tangem.common.card.CardWallet
import com.tangem.common.core.CardSession
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
@ -258,33 +258,26 @@ internal class VisaCardScanHandler @Inject constructor(
derivationPath: DerivationPath,
nonce: String,
): CompletionResult<SignHashResponse> {
val signHashCommand = SignHashCommand(publicKey, nonce.toByteArray(), derivationPath)
val result = suspendCancellableCoroutine {
val signHashCommand = SignHashCommand(
hash = nonce.hexToBytes(),
walletPublicKey = publicKey,
derivationPath = derivationPath,
)
return suspendCancellableCoroutine {
signHashCommand.run(session) { result ->
it.resume(result)
}
}
return result
}
private suspend fun SessionContext.signChallengeWithCard(
challenge: String,
): CompletionResult<AttestCardKeyResponse> {
val signHashCommand = AttestCardKeyCommand(challenge = challenge.toByteArray())
val result = suspendCancellableCoroutine { continuation ->
val signHashCommand = AttestCardKeyCommand(challenge = challenge.hexToBytes())
return suspendCancellableCoroutine { continuation ->
signHashCommand.run(session) { result ->
continuation.resume(result)
}
}
return when (result) {
is CompletionResult.Success -> {
CompletionResult.Success(result.data)
}
is CompletionResult.Failure -> {
CompletionResult.Failure(result.error)
}
}
}
}

View file

@ -0,0 +1,9 @@
package com.tangem.common.timemeasure
import android.os.SystemClock
import kotlin.time.AbstractLongTimeSource
import kotlin.time.DurationUnit
object RealtimeMonotonicTimeSource : AbstractLongTimeSource(DurationUnit.NANOSECONDS) {
override fun read(): Long = SystemClock.elapsedRealtimeNanos()
}

View file

@ -14,6 +14,7 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.withContext
@Suppress("UnusedPrivateMember")
internal class DefaultVisaActivationRepository @AssistedInject constructor(
@Assisted private val cardId: String,
private val visaApi: TangemVisaApi,
@ -38,7 +39,7 @@ internal class DefaultVisaActivationRepository @AssistedInject constructor(
}
override suspend fun getActivationOrderToSign(): ActivationOrder = withContext(dispatcherProvider.io) {
ActivationOrder(CryptoUtils.generateRandomBytes(32).toHexString())
ActivationOrder(CryptoUtils.generateRandomBytes(length = 32).toHexString())
}
@AssistedFactory

View file

@ -12,6 +12,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import javax.inject.Inject
@Suppress("UnusedPrivateMember")
internal class DefaultVisaAuthRepository @Inject constructor(
private val visaAuthApi: TangemVisaAuthApi,
private val dispatchers: CoroutineDispatcherProvider,
@ -30,7 +31,7 @@ internal class DefaultVisaAuthRepository @Inject constructor(
// )
VisaAuthChallenge.Card(
challenge = CryptoUtils.generateRandomBytes(16).toHexString(),
challenge = CryptoUtils.generateRandomBytes(length = 16).toHexString(),
session = VisaAuthSession("session"),
)
}
@ -49,7 +50,7 @@ internal class DefaultVisaAuthRepository @Inject constructor(
// session = VisaAuthSession(response.sessionId),
// )
VisaAuthChallenge.Wallet(
challenge = CryptoUtils.generateRandomBytes(32).toHexString(),
challenge = CryptoUtils.generateRandomBytes(length = 32).toHexString(),
session = VisaAuthSession("session"),
)
}

View file

@ -16,7 +16,8 @@ object VisaUtilities {
val visaBlockchain = Blockchain.Polygon
val visaDefaultDerivationPath = visaBlockchain.derivationPath(DerivationStyle.V3)
val visaDefaultDerivationPath
get() = visaBlockchain.derivationPath(DerivationStyle.V3)
fun visaDefaultDerivationPath(style: DerivationStyle) = visaBlockchain.derivationPath(style)

View file

@ -8,7 +8,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.visa.model.VisaDataForApprove
import com.tangem.features.onboarding.v2.visa.impl.DefaultOnboardingVisaComponent
@ -44,7 +43,7 @@ internal class OnboardingVisaAccessCodeComponent(
BackHandler(onBack = model::onBack)
DisableScreenshotsDisposableEffect()
// DisableScreenshotsDisposableEffect()
OnboardingVisaAccessCode(state, modifier)
}

View file

@ -17,7 +17,6 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.math.tan
@Stable
@ComponentScoped