diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index bbf617b817..54b6313c9c 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -5,10 +5,11 @@ import androidx.annotation.StringRes import com.tangem.Message import com.tangem.TangemSdk import com.tangem.common.* -import com.tangem.common.biometric.BiometricManager +import com.tangem.common.authentication.KeystoreManager import com.tangem.common.card.FirmwareVersion import com.tangem.common.core.* import com.tangem.common.extensions.ByteArrayKey +import com.tangem.common.services.secure.SecureStorage import com.tangem.common.usersCode.UserCodeRepository import com.tangem.core.analytics.Analytics import com.tangem.crypto.bip39.DefaultMnemonic @@ -47,19 +48,22 @@ class TangemSdkManager( private val userCodeRepository by lazy { UserCodeRepository( - biometricManager = tangemSdk.biometricManager, + keystoreManager = tangemSdk.keystoreManager, secureStorage = tangemSdk.secureStorage, ) } val canUseBiometry: Boolean - get() = tangemSdk.biometricManager.canAuthenticate || needEnrollBiometrics + get() = tangemSdk.authenticationManager.canAuthenticate || needEnrollBiometrics val needEnrollBiometrics: Boolean - get() = tangemSdk.biometricManager.canEnrollBiometrics + get() = tangemSdk.authenticationManager.canEnrollBiometrics - val biometricManager: BiometricManager - get() = tangemSdk.biometricManager + val keystoreManager: KeystoreManager + get() = tangemSdk.keystoreManager + + val secureStorage: SecureStorage + get() = tangemSdk.secureStorage val userCodeRequestPolicy: UserCodeRequestPolicy get() = tangemSdk.config.userCodeRequestPolicy diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt index 5f02cd1258..2657240db8 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt @@ -3,6 +3,7 @@ package com.tangem.tap.domain.userWalletList.di import android.content.Context import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import com.tangem.common.authentication.AuthenticatedStorage import com.tangem.common.json.TangemSdkAdapter import com.tangem.common.services.secure.SecureStorage import com.tangem.domain.wallets.legacy.UserWalletsListManager @@ -11,6 +12,7 @@ import com.tangem.sdk.storage.createEncryptedSharedPreferences import com.tangem.tap.domain.TangemSdkManager import com.tangem.tap.domain.userWalletList.implementation.BiometricUserWalletsListManager import com.tangem.tap.domain.userWalletList.implementation.RuntimeUserWalletsListManager +import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysStoreDecorator import com.tangem.tap.domain.userWalletList.repository.implementation.BiometricUserWalletsKeysRepository import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultSelectedUserWalletRepository import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsPublicInformationRepository @@ -43,10 +45,19 @@ fun UserWalletsListManager.Companion.provideBiometricImplementation( ), ) + val authenticatedStorage = AuthenticatedStorage( + secureStorage = UserWalletsKeysStoreDecorator( + featureStorage = secureStorage, + cardSdkStorage = tangemSdkManager.secureStorage, + ), + keystoreManager = tangemSdkManager.keystoreManager, + ) + val keysRepository = BiometricUserWalletsKeysRepository( moshi = moshi, secureStorage = secureStorage, - biometricManager = tangemSdkManager.biometricManager, + authenticatedStorage = authenticatedStorage, + ) val publicInformationRepository = DefaultUserWalletsPublicInformationRepository( moshi = moshi, diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt index 76a94f34bd..59819e3d7d 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt @@ -141,7 +141,7 @@ internal class BiometricUserWalletsListManager( return sensitiveInformationRepository.delete(idsToRemove) .flatMap { publicInformationRepository.delete(idsToRemove) } - .flatMap { keysRepository.delete(idsToRemove) } + .map { keysRepository.delete(idsToRemove) } .map { state.update { prevState -> val newUserWallets = prevState.userWallets.filter { it.walletId !in idsToRemove } @@ -158,7 +158,7 @@ internal class BiometricUserWalletsListManager( override suspend fun clear(): CompletionResult { return sensitiveInformationRepository.clear() .flatMap { publicInformationRepository.clear() } - .flatMap { keysRepository.clear() } + .map { keysRepository.clear() } .map { selectedUserWalletRepository.set(null) lock() diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysRepository.kt index 08c509a16c..170296fbe1 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysRepository.kt @@ -23,15 +23,13 @@ internal interface UserWalletsKeysRepository { /** * Delete encryption keys for user wallets. Biometric authentication not required * @param userWalletsIds List of [UserWalletId] whose encryption keys will be deleted - * @return [CompletionResult] of operation * */ - suspend fun delete(userWalletsIds: List): CompletionResult + suspend fun delete(userWalletsIds: List) /** * Clear all encryption keys for user wallets. Biometric authentication not required - * @return [CompletionResult] of operation * */ - suspend fun clear(): CompletionResult + suspend fun clear() /** * Determine if the user has saved user wallets diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt new file mode 100644 index 0000000000..662da27e35 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt @@ -0,0 +1,31 @@ +package com.tangem.tap.domain.userWalletList.repository + +import com.tangem.common.services.secure.SecureStorage + +/** + * A decorator for [SecureStorage] that facilitates data migration between two storages. + * + * @property featureStorage The primary storage, which will eventually contain all user data. + * @property cardSdkStorage The SDK's storage where user data might have been previously stored. + */ +internal class UserWalletsKeysStoreDecorator( + private val featureStorage: SecureStorage, + private val cardSdkStorage: SecureStorage, +) : SecureStorage by featureStorage { + + override fun delete(account: String) { + featureStorage.delete(account) + cardSdkStorage.delete(account) + } + + override fun get(account: String): ByteArray? { + var data = featureStorage.get(account) + + if (data == null) { + data = cardSdkStorage.get(account) ?: return null + featureStorage.store(data, account) + } + + return data + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt index 0d4dd5d1c5..bb40308916 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt @@ -4,8 +4,7 @@ import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.Types import com.tangem.common.* -import com.tangem.common.biometric.BiometricManager -import com.tangem.common.biometric.BiometricStorage +import com.tangem.common.authentication.AuthenticatedStorage import com.tangem.common.core.TangemSdkError import com.tangem.common.services.secure.SecureStorage import com.tangem.domain.wallets.legacy.UserWalletsListError @@ -18,13 +17,10 @@ import kotlinx.coroutines.withContext internal class BiometricUserWalletsKeysRepository( moshi: Moshi, - biometricManager: BiometricManager, + private val authenticatedStorage: AuthenticatedStorage, private val secureStorage: SecureStorage, ) : UserWalletsKeysRepository { - private val biometricStorage = BiometricStorage( - biometricManager = biometricManager, - secureStorage = secureStorage, - ) + private val encryptionKeyAdapter: JsonAdapter = moshi.adapter( UserWalletEncryptionKey::class.java, ) @@ -37,13 +33,13 @@ internal class BiometricUserWalletsKeysRepository( getAllInternal() .mapFailure { error -> when (error) { - is TangemSdkError.BiometricsAuthenticationLockout -> + is TangemSdkError.AuthenticationLockout -> UserWalletsListError.BiometricsAuthenticationLockout(isPermanent = false) - is TangemSdkError.BiometricsAuthenticationPermanentLockout -> + is TangemSdkError.AuthenticationPermanentLockout -> UserWalletsListError.BiometricsAuthenticationLockout(isPermanent = true) - is TangemSdkError.BiometricCryptographyKeyInvalidated -> + is TangemSdkError.KeystoreInvalidated -> UserWalletsListError.EncryptionKeyInvalidated - is TangemSdkError.BiometricsUnavailable -> + is TangemSdkError.AuthenticationUnavailable -> UserWalletsListError.BiometricsAuthenticationDisabled else -> error } @@ -57,26 +53,24 @@ internal class BiometricUserWalletsKeysRepository( } } - override suspend fun delete(userWalletsIds: List): CompletionResult { + override suspend fun delete(userWalletsIds: List) { return withContext(Dispatchers.IO) { - userWalletsIds.map { userWalletId -> + userWalletsIds.forEach { userWalletId -> deleteEncryptionKey(userWalletId) } - .fold() - .map { deleteUserWalletsIds(userWalletsIds) } + + deleteUserWalletsIds(userWalletsIds) } } - override suspend fun clear(): CompletionResult { + override suspend fun clear() { return withContext(Dispatchers.IO) { getUserWalletsIds() - .map { userWalletId -> + .forEach { userWalletId -> deleteEncryptionKey(userWalletId) } - .fold() - .map { - clearUserWalletsIds() - } + + clearUserWalletsIds() } } @@ -89,35 +83,20 @@ internal class BiometricUserWalletsKeysRepository( private suspend fun getAllInternal(): CompletionResult> { return getUserWalletsIds() .map { userWalletId -> - // It is possible to request multiple user wallet keys from biometric storage because - // the biometric cryptography key has an expiration time. - // If this operation runs more than that expiration time, then the user will have to re-authorize - // to receive all user wallets encryption keys getEncryptionKey(userWalletId) - .flatMapOnFailure { error -> - when (error) { - is TangemSdkError.InvalidBiometricCryptographyKey, - is TangemSdkError.BiometricCryptographyOperationFailed, - -> { - // These errors can be skipped as the user has the option to re-save their wallets - // in case they occur - CompletionResult.Success(data = null) - } - else -> CompletionResult.Failure(error) - } - } .doOnFailure { error -> when (error) { - is TangemSdkError.UserCanceledBiometricsAuthentication -> { + is TangemSdkError.UserCanceledAuthentication -> { // If the user cancels biometric authentication, then cancel operation with error return CompletionResult.Failure(error) } - is TangemSdkError.BiometricCryptographyKeyInvalidated -> { + is TangemSdkError.KeystoreInvalidated -> { // If the biometric cryptography key was invalidated, // then delete all user wallets encryption keys and cancel operation with error getUserWalletsIds().forEach { userWalletId -> deleteEncryptionKey(userWalletId) } + return CompletionResult.Failure(error) } } @@ -129,20 +108,22 @@ internal class BiometricUserWalletsKeysRepository( } private suspend fun getEncryptionKey(userWalletId: UserWalletId): CompletionResult { - return biometricStorage.get(StorageKey.UserWalletEncryptionKey(userWalletId).name) + return catching { authenticatedStorage.get(StorageKey.UserWalletEncryptionKey(userWalletId).name) } .map { it.decodeToKey() } } private suspend fun storeEncryptionKey(encryptionKey: UserWalletEncryptionKey): CompletionResult { - return biometricStorage.store( - key = StorageKey.UserWalletEncryptionKey(encryptionKey.walletId).name, - data = encryptionKey.encode(), - ) + return catching { + authenticatedStorage.store( + key = StorageKey.UserWalletEncryptionKey(encryptionKey.walletId).name, + data = encryptionKey.encode(), + ) + } .map { storeUserWalletId(encryptionKey.walletId) } } - private suspend fun deleteEncryptionKey(userWalletId: UserWalletId): CompletionResult { - return biometricStorage.delete(StorageKey.UserWalletEncryptionKey(userWalletId).name) + private fun deleteEncryptionKey(userWalletId: UserWalletId) { + return authenticatedStorage.delete(StorageKey.UserWalletEncryptionKey(userWalletId).name) } private suspend fun getUserWalletsIds(): List { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index e9679b30b3..89c974eda0 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -84,7 +84,7 @@ okHttp-prettyLogging = "3.1.0" # region Tangem tangemBlockchainSdk = "develop-351" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-297" +tangemCardSdk = "develop-300" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds # endregion Tangem