Updated on 2026-08-14

This commit is contained in:
Tangem 2022-12-14 14:22:37 +03:00
commit 888f62179b
10 changed files with 33 additions and 99 deletions

View file

@ -38,7 +38,6 @@ import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
import com.tangem.tap.domain.tasks.product.ScanProductTask
import com.tangem.tap.domain.tokens.UserTokensRepository
import com.tangem.tap.domain.userWalletList.di.USER_WALLETS_BIOMETRIC_KEY_NAME
import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
@ -119,16 +118,6 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
.map { CardDTO(it) }
}
suspend fun unlockBiometricKeys(): CompletionResult<Unit> {
return biometricManager.authenticate(
mode = BiometricManager.AuthenticationMode.Keys(
USER_WALLETS_BIOMETRIC_KEY_NAME,
tangemSdk.config.userCodesBiometricKeyName,
),
)
.map { /* no-op */ }
}
suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
return createUserCodeRepository().save(
cardIds = cardsIds,
@ -137,20 +126,10 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
stringValue = accessCode,
),
)
.map {
biometricManager.unauthenticate(
keyName = tangemSdk.config.userCodesBiometricKeyName,
)
}
}
suspend fun clearSavedUserCodes(): CompletionResult<Unit> {
return createUserCodeRepository().clear()
.map {
biometricManager.unauthenticate(
keyName = tangemSdk.config.userCodesBiometricKeyName,
)
}
}
suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {

View file

@ -59,7 +59,7 @@ class CreateProductWalletTask(
private val type: ProductType,
) : CardSessionRunnable<CreateProductWalletTaskResponse> {
override val allowsAccessCodeFromRepository: Boolean = false
override val allowsRequestAccessCodeFromRepository: Boolean = false
override fun run(
session: CardSession,

View file

@ -45,7 +45,7 @@ class ScanProductTask(
private val additionalBlockchainsToDerive: Collection<Blockchain>? = null,
) : CardSessionRunnable<ScanResponse> {
override val allowsAccessCodeFromRepository: Boolean
override val allowsRequestAccessCodeFromRepository: Boolean
get() = !additionalBlockchainsToDerive.isNullOrEmpty()
override fun run(

View file

@ -14,10 +14,13 @@ import com.tangem.tap.domain.userWalletList.repository.implementation.BiometricU
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultSelectedUserWalletRepository
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsPublicInformationRepository
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsSensitiveInformationRepository
import com.tangem.tap.domain.userWalletList.utils.json.*
import com.tangem.tap.domain.userWalletList.utils.json.ByteArrayKeyAdapter
import com.tangem.tap.domain.userWalletList.utils.json.CardBackupStatusAdapter
import com.tangem.tap.domain.userWalletList.utils.json.ExtendedPublicKeysMapAdapter
import com.tangem.tap.domain.userWalletList.utils.json.ScanResponseDerivedKeysMapAdapter
import com.tangem.tap.domain.userWalletList.utils.json.WalletDerivedKeysMapAdapter
const val USER_WALLETS_STORAGE_NAME = "user_wallets_storage"
const val USER_WALLETS_BIOMETRIC_KEY_NAME = "user_wallets"
private const val USER_WALLETS_STORAGE_NAME = "user_wallets_storage"
fun UserWalletsListManager.Companion.provideBiometricImplementation(
context: Context,
@ -43,7 +46,6 @@ fun UserWalletsListManager.Companion.provideBiometricImplementation(
)
val keysRepository = BiometricUserWalletsKeysRepository(
biometricKeyName = USER_WALLETS_BIOMETRIC_KEY_NAME,
moshi = moshi,
secureStorage = secureStorage,
biometricManager = tangemSdkManager.biometricManager,
@ -61,7 +63,6 @@ fun UserWalletsListManager.Companion.provideBiometricImplementation(
)
return BiometricUserWalletsListManager(
tangemSdkManager = tangemSdkManager,
keysRepository = keysRepository,
publicInformationRepository = publicInformationRepository,
sensitiveInformationRepository = sensitiveInformationRepository,

View file

@ -3,7 +3,6 @@ package com.tangem.tap.domain.userWalletList.implementation
import com.tangem.common.*
import com.tangem.domain.common.util.UserWalletId
import com.tangem.domain.common.util.encryptionKey
import com.tangem.tap.domain.TangemSdkManager
import com.tangem.tap.domain.model.UserWallet
import com.tangem.tap.domain.userWalletList.UserWalletListError
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
@ -20,7 +19,6 @@ import timber.log.Timber
@OptIn(ExperimentalCoroutinesApi::class)
internal class BiometricUserWalletsListManager(
private val tangemSdkManager: TangemSdkManager,
private val keysRepository: UserWalletsKeysRepository,
private val publicInformationRepository: UserWalletsPublicInformationRepository,
private val sensitiveInformationRepository: UserWalletsSensitiveInformationRepository,
@ -83,7 +81,6 @@ internal class BiometricUserWalletsListManager(
}
override fun lock() {
tangemSdkManager.biometricManager.unauthenticate()
state.update { prevState ->
prevState.copy(
encryptionKeys = emptyList(),
@ -122,14 +119,15 @@ internal class BiometricUserWalletsListManager(
if (isWalletSaved && !canOverride) {
CompletionResult.Failure(UserWalletListError.WalletAlreadySaved)
} else {
keysRepository.save(
walletId = userWallet.walletId,
encryptionKey = userWallet.scanResponse.card.encryptionKey,
)
.doOnSuccess { keys ->
val newEncryptionKeys = state.value.encryptionKeys
.plus(UserWalletEncryptionKey(userWallet.walletId, userWallet.scanResponse.card.encryptionKey))
.distinctBy { it.walletId }
keysRepository.store(newEncryptionKeys)
.doOnSuccess {
state.update { prevState ->
prevState.copy(
encryptionKeys = (keys + prevState.encryptionKeys).distinctBy { it.walletId },
encryptionKeys = newEncryptionKeys,
)
}
}
@ -146,21 +144,22 @@ internal class BiometricUserWalletsListManager(
val walletIdsToRemove = state.value.wallets
.map { it.walletId }
.filter { it in walletIds }
val remainingEncryptionKeys = state.value.encryptionKeys
.filter { it.walletId !in walletIdsToRemove }
changeSelectedWalletIfNeeded(walletIdsToRemove)
return sensitiveInformationRepository.delete(walletIdsToRemove)
.flatMap { publicInformationRepository.delete(walletIdsToRemove) }
.flatMap { keysRepository.delete(walletIdsToRemove) }
.map { keys ->
.flatMap { keysRepository.store(remainingEncryptionKeys) }
.map {
state.update { prevState ->
prevState.copy(
encryptionKeys = keys,
encryptionKeys = remainingEncryptionKeys,
wallets = prevState.wallets.filter { it.walletId !in walletIdsToRemove },
)
}
}
.flatMap { loadModels() }
}
override suspend fun clear(): CompletionResult<Unit> {
@ -171,12 +170,11 @@ internal class BiometricUserWalletsListManager(
.flatMap { keysRepository.clear() }
.map {
selectedUserWalletRepository.set(null)
tangemSdkManager.biometricManager.unauthenticate()
state.update { State() }
}
}
override suspend fun get(walletId: UserWalletId): CompletionResult<UserWallet> = withUnlock {
override suspend fun get(walletId: UserWalletId): CompletionResult<UserWallet> {
return catching {
state.value.wallets.first { it.walletId == walletId }
}

View file

@ -1,12 +1,10 @@
package com.tangem.tap.domain.userWalletList.repository
import com.tangem.common.CompletionResult
import com.tangem.domain.common.util.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
internal interface UserWalletsKeysRepository {
suspend fun getAll(): CompletionResult<List<UserWalletEncryptionKey>>
suspend fun save(walletId: UserWalletId, encryptionKey: ByteArray): CompletionResult<List<UserWalletEncryptionKey>>
suspend fun delete(walletIds: List<UserWalletId>): CompletionResult<List<UserWalletEncryptionKey>>
suspend fun store(encryptionKeys: List<UserWalletEncryptionKey>): CompletionResult<Unit>
suspend fun clear(): CompletionResult<Unit>
}

View file

@ -6,24 +6,19 @@ import com.squareup.moshi.Types
import com.tangem.common.CompletionResult
import com.tangem.common.biometric.BiometricManager
import com.tangem.common.biometric.BiometricStorage
import com.tangem.common.flatMap
import com.tangem.common.map
import com.tangem.common.mapFailure
import com.tangem.common.services.secure.SecureStorage
import com.tangem.domain.common.util.UserWalletId
import com.tangem.tap.common.extensions.replaceByOrAdd
import com.tangem.tap.domain.userWalletList.UserWalletListError
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository
internal class BiometricUserWalletsKeysRepository(
biometricKeyName: String,
moshi: Moshi,
secureStorage: SecureStorage,
biometricManager: BiometricManager,
) : UserWalletsKeysRepository {
private val biometricStorage = BiometricStorage(
biometricKeyName = biometricKeyName,
biometricManager = biometricManager,
secureStorage = secureStorage,
)
@ -41,50 +36,16 @@ internal class BiometricUserWalletsKeysRepository(
}
}
override suspend fun save(
walletId: UserWalletId,
encryptionKey: ByteArray,
): CompletionResult<List<UserWalletEncryptionKey>> {
return getAll()
.flatMap { keys ->
if (keys.any { it.walletId == walletId }) {
return@flatMap CompletionResult.Success(Unit)
}
val encodedKeys = keys.toMutableList()
.apply {
replaceByOrAdd(UserWalletEncryptionKey(walletId, encryptionKey)) {
it.walletId == walletId
}
}
.encode()
biometricStorage.store(
key = StorageKey.WalletEncryptionKeys.name,
data = encodedKeys,
)
}
.flatMap { getAll() }
override suspend fun store(encryptionKeys: List<UserWalletEncryptionKey>): CompletionResult<Unit> {
return biometricStorage.store(
key = StorageKey.WalletEncryptionKeys.name,
data = encryptionKeys.encode(),
)
.mapFailure { error ->
UserWalletListError.SaveEncryptionKeysError(error.cause ?: error)
}
}
override suspend fun delete(walletIds: List<UserWalletId>): CompletionResult<List<UserWalletEncryptionKey>> {
return getAll()
.map { keys ->
val keysToRemove = keys.filter { it.walletId in walletIds }.toSet()
(keys - keysToRemove).encode()
}
.flatMap { encodedKeys ->
biometricStorage.store(
key = StorageKey.WalletEncryptionKeys.name,
data = encodedKeys,
)
}
.flatMap { getAll() }
}
override suspend fun clear(): CompletionResult<Unit> {
return biometricStorage.delete(key = StorageKey.WalletEncryptionKeys.name)
}

View file

@ -234,7 +234,6 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
when (action) {
is BackupAction.StartBackup -> {
tangemSdkManager.setAccessCodeRequestPolicy(useBiometricsForAccessCode = false)
Analytics.send(Onboarding.Backup.Started())
backupService.discardSavedBackup()
val primaryCard = scanResponse?.primaryCard

View file

@ -138,13 +138,10 @@ internal class SaveWalletMiddleware {
): CompletionResult<Unit> {
return when {
accessCode != null -> {
tangemSdkManager.unlockBiometricKeys()
.flatMap {
tangemSdkManager.saveAccessCode(
accessCode = accessCode,
cardsIds = cardsInWallet,
)
}
tangemSdkManager.saveAccessCode(
accessCode = accessCode,
cardsIds = cardsInWallet,
)
}
else -> {
CompletionResult.Success(Unit)

View file

@ -59,7 +59,8 @@ object Versions {
// region Tangem
const val tangemBlockchainSdk = "develop-141"
const val tangemCardSgk = "develop-171"
const val tangemCardSgk = "develop-173"
// endregion Tangem
// region Testing