diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 52791941..8a0e95b8 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -10,6 +10,7 @@
+
diff --git a/tangem-sdk-android/src/main/java/com/tangem/sdk/extensions/TangemSdkError.kt b/tangem-sdk-android/src/main/java/com/tangem/sdk/extensions/TangemSdkError.kt
index c4a8580a..92e59614 100644
--- a/tangem-sdk-android/src/main/java/com/tangem/sdk/extensions/TangemSdkError.kt
+++ b/tangem-sdk-android/src/main/java/com/tangem/sdk/extensions/TangemSdkError.kt
@@ -99,6 +99,7 @@ fun TangemSdkError.localizedDescriptionRes(): TangemSdkErrorDescription {
is TangemSdkError.NonHardenedDerivationNotSupported,
is TangemSdkError.AuthenticationNotInitialized,
is TangemSdkError.NfcFeatureIsUnavailable,
+ is TangemSdkError.CardOfflineVerificationFailed,
-> TangemSdkErrorDescription()
is TangemSdkError.BackupFailedEmptyWallets,
diff --git a/tangem-sdk-core/src/main/java/com/tangem/common/core/TangemError.kt b/tangem-sdk-core/src/main/java/com/tangem/common/core/TangemError.kt
index 488f202a..53ef0280 100644
--- a/tangem-sdk-core/src/main/java/com/tangem/common/core/TangemError.kt
+++ b/tangem-sdk-core/src/main/java/com/tangem/common/core/TangemError.kt
@@ -324,6 +324,9 @@ sealed class TangemSdkError(code: Int) : TangemError(code) {
* Returns if NFC feature for device is not supported
*/
class NfcFeatureIsUnavailable : TangemSdkError(code = 50027)
+
+ class CardOfflineVerificationFailed : TangemSdkError(code = 50028)
+
/**
* Get error according to the pin type
* @param userCodeType: Specific user code type
diff --git a/tangem-sdk-core/src/main/java/com/tangem/operations/attestation/AttestationTask.kt b/tangem-sdk-core/src/main/java/com/tangem/operations/attestation/AttestationTask.kt
index 64b34594..8b625b43 100644
--- a/tangem-sdk-core/src/main/java/com/tangem/operations/attestation/AttestationTask.kt
+++ b/tangem-sdk-core/src/main/java/com/tangem/operations/attestation/AttestationTask.kt
@@ -74,7 +74,19 @@ class AttestationTask(
currentAttestationStatus = currentAttestationStatus.copy(
cardKeyAttestation = Attestation.Status.Failed,
)
- continueAttestation(session, callback)
+
+ val isDevelopmentCard =
+ session.environment.card!!.firmwareVersion.type == FirmwareVersion.FirmwareType.Sdk
+
+ if (isDevelopmentCard) {
+ session.viewDelegate.attestationDidFail(
+ isDevCard = true,
+ positive = { complete(session, callback) },
+ negative = { callback(CompletionResult.Failure(TangemSdkError.UserCancelled())) },
+ )
+ } else {
+ callback(CompletionResult.Failure(TangemSdkError.CardOfflineVerificationFailed()))
+ }
} else {
callback(CompletionResult.Failure(result.error))
}
@@ -97,78 +109,6 @@ class AttestationTask(
}
}
- private fun runWalletsAttestation(session: CardSession, callback: CompletionCallback) {
- attestWallets(session) { result ->
- when (result) {
- is CompletionResult.Success -> {
- // Wallets attestation completed. Update status and continue attestation
- val hasWarnings = result.data
- val status = if (hasWarnings) Attestation.Status.Warning else Attestation.Status.Verified
- currentAttestationStatus = currentAttestationStatus.copy(walletKeysAttestation = status)
- runExtraAttestation(session, callback)
- }
- is CompletionResult.Failure -> {
- // Wallets attestation failed. Update status and continue attestation
- if (result.error is TangemSdkError.CardVerificationFailed) {
- currentAttestationStatus = currentAttestationStatus.copy(
- walletKeysAttestation = Attestation.Status.Failed,
- )
- runExtraAttestation(session, callback)
- } else {
- callback(CompletionResult.Failure(result.error))
- }
- }
- }
- }
- }
-
- private fun runExtraAttestation(session: CardSession, callback: CompletionCallback) {
- // TODO: ATTEST_CARD_FIRMWARE, ATTEST_CARD_UNIQUENESS
- waitForOnlineAndComplete(session, callback)
- }
-
- private fun attestWallets(session: CardSession, callback: CompletionCallback) {
- session.scope.launch {
- val card = session.environment.card!!
- val walletsKeys = card.wallets.map { it.publicKey }
- val attestationCommands = walletsKeys.map { AttestWalletKeyCommand(it) }
-
- // check for hacking attempts with signs
- var hasWarnings = card.wallets.mapNotNull { it.totalSignedHashes }.any { it > MAX_COUNTER }
- var shouldReturn = false
- var flowIsCompleted = false
-
- if (attestationCommands.isEmpty()) {
- callback(CompletionResult.Success(hasWarnings))
- return@launch
- }
-
- flow {
- attestationCommands.forEach { emit(it) }
- }.onCompletion {
- flowIsCompleted = true
- }.collect {
- if (shouldReturn) return@collect
-
- it.run(session) { result ->
- when (result) {
- is CompletionResult.Success -> {
- // check for hacking attempts with attestWallet
- if (result.data.counter != null && result.data.counter > MAX_COUNTER) {
- hasWarnings = true
- }
- if (flowIsCompleted) callback(CompletionResult.Success(hasWarnings))
- }
- is CompletionResult.Failure -> {
- shouldReturn = true
- callback(CompletionResult.Failure(result.error))
- }
- }
- }
- }
- }
- }
-
/**
* Dev card will not pass online attestation. Or, if the card already failed offline attestation,
* we can skip online part. So, we can send the error to the publisher immediately
@@ -222,43 +162,26 @@ class AttestationTask(
}
}
- private fun retryOnline(session: CardSession, callback: CompletionCallback) {
- onlineAttestationSubscription = null
- onlineAttestationChannel.cancel()
- onlineAttestationChannel = ConflatedBroadcastChannel()
-
- val card = session.environment.card.guard {
- callback(CompletionResult.Failure(TangemSdkError.MissingPreflightRead()))
- return
- }
-
- runOnlineAttestation(session.scope, card)
- waitForOnlineAndComplete(session, callback)
- }
-
private fun processAttestationReport(session: CardSession, callback: CompletionCallback) {
when (currentAttestationStatus.status) {
- Attestation.Status.Failed, Attestation.Status.Skipped -> {
+ Attestation.Status.Failed,
+ Attestation.Status.Skipped,
+ -> {
val isDevelopmentCard = session.environment.card!!.firmwareVersion.type ==
FirmwareVersion.FirmwareType.Sdk
// Possible production sample or development card
if (isDevelopmentCard || session.environment.config.allowUntrustedCards) {
session.viewDelegate.attestationDidFail(
- isDevelopmentCard,
- {
- complete(session, callback)
- },
- ) {
- callback(CompletionResult.Failure(TangemSdkError.UserCancelled()))
- }
+ isDevCard = isDevelopmentCard,
+ positive = { complete(session, callback) },
+ negative = { callback(CompletionResult.Failure(TangemSdkError.UserCancelled())) },
+ )
} else {
callback(CompletionResult.Failure(TangemSdkError.CardVerificationFailed()))
}
}
- Attestation.Status.Verified -> {
- complete(session, callback)
- }
+ Attestation.Status.Verified -> complete(session, callback)
Attestation.Status.VerifiedOffline -> {
if (session.environment.config.attestationMode == Mode.Offline) {
complete(session, callback)
@@ -266,18 +189,12 @@ class AttestationTask(
}
session.viewDelegate.attestationCompletedOffline(
- {
- complete(session, callback)
- },
- {
- callback(CompletionResult.Failure(TangemSdkError.UserCancelled()))
- },
- {
+ positive = { complete(session, callback) },
+ negative = { callback(CompletionResult.Failure(TangemSdkError.UserCancelled())) },
+ retry = {
retryOnline(session) { result ->
when (result) {
- is CompletionResult.Success -> {
- processAttestationReport(session, callback)
- }
+ is CompletionResult.Success -> processAttestationReport(session, callback)
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
}
}
@@ -292,6 +209,92 @@ class AttestationTask(
}
}
+ private fun retryOnline(session: CardSession, callback: CompletionCallback) {
+ onlineAttestationSubscription = null
+ onlineAttestationChannel.cancel()
+ onlineAttestationChannel = ConflatedBroadcastChannel()
+
+ val card = session.environment.card.guard {
+ callback(CompletionResult.Failure(TangemSdkError.MissingPreflightRead()))
+ return
+ }
+
+ runOnlineAttestation(session.scope, card)
+ waitForOnlineAndComplete(session, callback)
+ }
+
+ private fun runWalletsAttestation(session: CardSession, callback: CompletionCallback) {
+ attestWallets(session) { result ->
+ when (result) {
+ is CompletionResult.Success -> {
+ // Wallets attestation completed. Update status and continue attestation
+ val hasWarnings = result.data
+ val status = if (hasWarnings) Attestation.Status.Warning else Attestation.Status.Verified
+ currentAttestationStatus = currentAttestationStatus.copy(walletKeysAttestation = status)
+ runExtraAttestation(session, callback)
+ }
+ is CompletionResult.Failure -> {
+ // Wallets attestation failed. Update status and continue attestation
+ if (result.error is TangemSdkError.CardVerificationFailed) {
+ currentAttestationStatus = currentAttestationStatus.copy(
+ walletKeysAttestation = Attestation.Status.Failed,
+ )
+ runExtraAttestation(session, callback)
+ } else {
+ callback(CompletionResult.Failure(result.error))
+ }
+ }
+ }
+ }
+ }
+
+ private fun attestWallets(session: CardSession, callback: CompletionCallback) {
+ session.scope.launch {
+ val card = session.environment.card!!
+ val walletsKeys = card.wallets.map { it.publicKey }
+ val attestationCommands = walletsKeys.map { AttestWalletKeyCommand(it) }
+
+ // check for hacking attempts with signs
+ var hasWarnings = card.wallets.mapNotNull { it.totalSignedHashes }.any { it > MAX_COUNTER }
+ var shouldReturn = false
+ var flowIsCompleted = false
+
+ if (attestationCommands.isEmpty()) {
+ callback(CompletionResult.Success(hasWarnings))
+ return@launch
+ }
+
+ flow {
+ attestationCommands.forEach { emit(it) }
+ }.onCompletion {
+ flowIsCompleted = true
+ }.collect {
+ if (shouldReturn) return@collect
+
+ it.run(session) { result ->
+ when (result) {
+ is CompletionResult.Success -> {
+ // check for hacking attempts with attestWallet
+ if (result.data.counter != null && result.data.counter > MAX_COUNTER) {
+ hasWarnings = true
+ }
+ if (flowIsCompleted) callback(CompletionResult.Success(hasWarnings))
+ }
+ is CompletionResult.Failure -> {
+ shouldReturn = true
+ callback(CompletionResult.Failure(result.error))
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun runExtraAttestation(session: CardSession, callback: CompletionCallback) {
+ // TODO: ATTEST_CARD_FIRMWARE, ATTEST_CARD_UNIQUENESS
+ waitForOnlineAndComplete(session, callback)
+ }
+
private fun complete(session: CardSession, callback: CompletionCallback) {
session.environment.card = session.environment.card?.copy(attestation = currentAttestationStatus)
callback(CompletionResult.Success(currentAttestationStatus))