Skip to content

Commit

Permalink
Merge pull request #329 from tangem/IOS-4025_refactor_prefligh_filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
tureck1y authored Nov 28, 2023
2 parents dddb7ef + f25eaeb commit 4399166
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 46 deletions.
20 changes: 20 additions & 0 deletions TangemSdk/TangemSdk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@
DC1244E829BB9E0C0037BC05 /* ExtendedKeySerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC1244E729BB9E0C0037BC05 /* ExtendedKeySerializer.swift */; };
DC22228729D431AB001129F8 /* SetUserCodeRecoveryAllowed.json in Resources */ = {isa = PBXBuildFile; fileRef = DC22228629D431AB001129F8 /* SetUserCodeRecoveryAllowed.json */; };
DC234CC629F1A3F100082063 /* ImportWalletMnemonic.json in Resources */ = {isa = PBXBuildFile; fileRef = DC234CC529F1A3F100082063 /* ImportWalletMnemonic.json */; };
DC30D70A2AF024D000744FA4 /* PreflightReadFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC30D7092AF024D000744FA4 /* PreflightReadFilter.swift */; };
DC3D97F92A77D3C6001EEE7A /* SLIP23.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3D97F82A77D3C6001EEE7A /* SLIP23.swift */; };
DC3D97FB2A77E079001EEE7A /* SLIP23Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3D97FA2A77E079001EEE7A /* SLIP23Tests.swift */; };
DC3D97FE2A77F5C6001EEE7A /* BIP32MasterKeyFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3D97FD2A77F5C6001EEE7A /* BIP32MasterKeyFactory.swift */; };
Expand All @@ -281,6 +282,8 @@
DC59CB0A29AF6F9C00EC14E1 /* EntropyLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC59CB0929AF6F9C00EC14E1 /* EntropyLength.swift */; };
DC59CB0C29AF706100EC14E1 /* MnemonicError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC59CB0B29AF706100EC14E1 /* MnemonicError.swift */; };
DC59CB0E29AF70C700EC14E1 /* Mnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC59CB0D29AF70C700EC14E1 /* Mnemonic.swift */; };
DC612D6C2AFD58A3005A547F /* CardIdPreflightReadFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC612D6B2AFD58A3005A547F /* CardIdPreflightReadFilter.swift */; };
DC612D722AFD60C2005A547F /* SessionFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC612D712AFD60C2005A547F /* SessionFilter.swift */; };
DC70AD652A80FC9F00928836 /* CommonFirmwareTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC70AD642A80FC9F00928836 /* CommonFirmwareTests.swift */; };
DC70AD672A8115BB00928836 /* FWTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC70AD662A8115BB00928836 /* FWTestCase.swift */; };
DC7254902A03E20A0003FE1B /* DerivedKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC72548F2A03E20A0003FE1B /* DerivedKeys.swift */; };
Expand Down Expand Up @@ -652,6 +655,7 @@
DC1244E729BB9E0C0037BC05 /* ExtendedKeySerializer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedKeySerializer.swift; sourceTree = "<group>"; };
DC22228629D431AB001129F8 /* SetUserCodeRecoveryAllowed.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = SetUserCodeRecoveryAllowed.json; sourceTree = "<group>"; };
DC234CC529F1A3F100082063 /* ImportWalletMnemonic.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ImportWalletMnemonic.json; sourceTree = "<group>"; };
DC30D7092AF024D000744FA4 /* PreflightReadFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreflightReadFilter.swift; sourceTree = "<group>"; };
DC3D97F82A77D3C6001EEE7A /* SLIP23.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SLIP23.swift; sourceTree = "<group>"; };
DC3D97FA2A77E079001EEE7A /* SLIP23Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SLIP23Tests.swift; sourceTree = "<group>"; };
DC3D97FD2A77F5C6001EEE7A /* BIP32MasterKeyFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BIP32MasterKeyFactory.swift; sourceTree = "<group>"; };
Expand All @@ -665,6 +669,8 @@
DC59CB0929AF6F9C00EC14E1 /* EntropyLength.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntropyLength.swift; sourceTree = "<group>"; };
DC59CB0B29AF706100EC14E1 /* MnemonicError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicError.swift; sourceTree = "<group>"; };
DC59CB0D29AF70C700EC14E1 /* Mnemonic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mnemonic.swift; sourceTree = "<group>"; };
DC612D6B2AFD58A3005A547F /* CardIdPreflightReadFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardIdPreflightReadFilter.swift; sourceTree = "<group>"; };
DC612D712AFD60C2005A547F /* SessionFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionFilter.swift; sourceTree = "<group>"; };
DC70AD642A80FC9F00928836 /* CommonFirmwareTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonFirmwareTests.swift; sourceTree = "<group>"; };
DC70AD662A8115BB00928836 /* FWTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FWTestCase.swift; sourceTree = "<group>"; };
DC72548F2A03E20A0003FE1B /* DerivedKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DerivedKeys.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1067,6 +1073,7 @@
children = (
5D58202124221E2B0057EF40 /* TangemSdkError.swift */,
5D58201F24221E060057EF40 /* CardSession.swift */,
DC612D712AFD60C2005A547F /* SessionFilter.swift */,
5D7D5FB123449D4000058D69 /* SessionEnvironment.swift */,
5D705B5A23DAF2BB002CCD7A /* Config.swift */,
5D379C28268FA4AC00C7F473 /* CardSessionRunnable.swift */,
Expand Down Expand Up @@ -1107,6 +1114,7 @@
5D3F77B424B9B8E700E8695B /* Attestation */,
5D86CBD624A10F3D00FB5BA7 /* Personalization */,
B0BD1C8E255E58BE00119D82 /* Wallet */,
DC612D6A2AFD585B005A547F /* PreflightReadFilter */,
5D7D5FB323449F2300058D69 /* Command.swift */,
5D6A92ED23463D1200158457 /* ScanTask.swift */,
B006971325FFA4420040D203 /* PreflightReadTask.swift */,
Expand Down Expand Up @@ -1536,6 +1544,15 @@
path = Wordlists;
sourceTree = "<group>";
};
DC612D6A2AFD585B005A547F /* PreflightReadFilter */ = {
isa = PBXGroup;
children = (
DC30D7092AF024D000744FA4 /* PreflightReadFilter.swift */,
DC612D6B2AFD58A3005A547F /* CardIdPreflightReadFilter.swift */,
);
path = PreflightReadFilter;
sourceTree = "<group>";
};
DCB5A4E12A1FAB190021E12D /* BLS */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1936,6 +1953,7 @@
5DFCE40524B30589007B95AF /* Storage.swift in Sources */,
5DDD6C7325D30C1400E48D7B /* TangemSdkLogger.swift in Sources */,
DCD2D9162A1CF64A00AB00B6 /* precomputed_ecmult.c in Sources */,
DC612D6C2AFD58A3005A547F /* CardIdPreflightReadFilter.swift in Sources */,
5D81348426D62F8900494A71 /* StringCodable.swift in Sources */,
B091C0F8253EF3EE003D57E7 /* FileHashData.swift in Sources */,
5DF5FB1C244F2C15002DB244 /* IssuerDataVerifier.swift in Sources */,
Expand Down Expand Up @@ -2130,6 +2148,7 @@
B006971425FFA4420040D203 /* PreflightReadTask.swift in Sources */,
5DA3A2EF251CA507009A8E08 /* CheckUserCodesCommand.swift in Sources */,
5D705B5B23DAF2BB002CCD7A /* Config.swift in Sources */,
DC612D722AFD60C2005A547F /* SessionFilter.swift in Sources */,
5D6A92EC2346069700158457 /* TangemSdk.swift in Sources */,
DC1244B329B60B6F0037BC05 /* BIP39.swift in Sources */,
5DFFC49F233B9D69004964E8 /* NFCReader.swift in Sources */,
Expand All @@ -2142,6 +2161,7 @@
5DB4406F234B750200AC39F1 /* String+.swift in Sources */,
5D2FE06524DD82BA0086B5E8 /* VerifyCardRequest.swift in Sources */,
5DFFC4A2233BA5E0004964E8 /* Byte+.swift in Sources */,
DC30D70A2AF024D000744FA4 /* PreflightReadFilter.swift in Sources */,
B06947BF2534570B0056A887 /* DeleteFilesTask.swift in Sources */,
DC1244DA29BB65970037BC05 /* ExtendedKeySerializable.swift in Sources */,
5DD6762C26CD621500D8C909 /* UIScreen+.swift in Sources */,
Expand Down
33 changes: 23 additions & 10 deletions TangemSdk/TangemSdk/Common/Core/CardSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,22 @@ public class CardSession {
public let viewDelegate: SessionViewDelegate

var state: CardSessionState = .inactive

/// Contains data relating to the current Tangem card. It is used in constructing all the commands,
/// and commands can modify `SessionEnvironment`.

private(set) var cardId: String?

var cardId: String? {
switch filter {
case .cardId(let cardId):
return cardId
default:
return nil
}
}

// initial environment to be able to reset a current one
private let _environment: SessionEnvironment
public internal(set) var environment: SessionEnvironment

private let reader: CardReader
private let jsonConverter: JSONRPCConverter
private let initialMessage: Message?
Expand All @@ -39,7 +48,8 @@ public class CardSession {
private var resetCodesController: ResetCodesController? = nil
/// Allows access codes to be stored in a secure location
private var accessCodeRepository: AccessCodeRepository? = nil

private let filter: SessionFilter?

private var shouldRequestBiometrics: Bool {
guard let accessCodeRepository = self.accessCodeRepository else {
return false
Expand All @@ -55,24 +65,25 @@ public class CardSession {
/// Main initializer
/// - Parameters:
/// - environment: Contains data relating to a Tangem card
/// - cardId: CID, Unique Tangem card ID number. If not nil, the SDK will check that you tapped the card with this cardID and will return the `wrongCard` error' otherwise
/// - filter: Filters card to be read. Optional.
/// - initialMessage: A custom description that shows at the beginning of the NFC session. If nil, default message will be used
/// - cardReader: NFC-reader implementation
/// - viewDelegate: viewDelegate implementation
/// - jsonConverter: JSONRPCConverter
/// - accessCodeRepository: Optional AccessCodeRepository that saves access codes to Apple Keychain
init(environment: SessionEnvironment,
cardId: String? = nil,
filter: SessionFilter? = nil,
initialMessage: Message? = nil,
cardReader: CardReader,
viewDelegate: SessionViewDelegate,
jsonConverter: JSONRPCConverter,
accessCodeRepository: AccessCodeRepository?) {
self.reader = cardReader
self.viewDelegate = viewDelegate
self._environment = environment
self.environment = environment
self.initialMessage = initialMessage
self.cardId = cardId
self.filter = filter
self.jsonConverter = jsonConverter
self.accessCodeRepository = accessCodeRepository
}
Expand Down Expand Up @@ -399,7 +410,7 @@ public class CardSession {
// MARK: - Preflight check
private func preflightCheck(_ onSessionStarted: @escaping (CardSession, TangemSdkError?) -> Void) {
Log.session("Start preflight check")
let preflightTask = PreflightReadTask(readMode: preflightReadMode, cardId: cardId)
let preflightTask = PreflightReadTask(readMode: preflightReadMode, filter: filter?.preflightReadFilter)
preflightTask.run(in: self) { [weak self] readResult in
guard let self = self else { return }

Expand All @@ -408,8 +419,10 @@ public class CardSession {
onSessionStarted(self, nil)
case .failure(let error):
switch error {
case .wrongCardType, .wrongCardNumber:
case .preflightFiltered:
self.viewDelegate.wrongCard(message: error.localizedDescription)
// We have to return environment to initial state to reset all the changes
self.environment = self._environment
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
guard self.reader.isReady else {
onSessionStarted(self, .userCancelled)
Expand Down
35 changes: 35 additions & 0 deletions TangemSdk/TangemSdk/Common/Core/SessionFilter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// SessionFilter.swift
// TangemSdk
//
// Created by Alexander Osokin on 09.11.2023.
// Copyright © 2023 Tangem AG. All rights reserved.
//

import Foundation

@available(iOS 13.0, *)
public enum SessionFilter {
case cardId(String)
case custom(PreflightReadFilter)
}

@available(iOS 13.0, *)
extension SessionFilter {
var preflightReadFilter: PreflightReadFilter {
switch self {
case .cardId(let cardId):
return CardIdPreflightReadFilter(cardId: cardId)
case .custom(let filter):
return filter
}
}

init?(from cardId: String?) {
guard let cardId else {
return nil
}

self = .cardId(cardId)
}
}
9 changes: 6 additions & 3 deletions TangemSdk/TangemSdk/Common/Core/TangemSdkError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ public enum TangemSdkError: Error, LocalizedError, Encodable {
/// This error is returned when a user scans a card of a [com.tangem.common.extensions.CardType]
/// that is not specified in [Config.cardFilter].
case wrongCardType(_ localizedDescription: String?)


case preflightFiltered(_ error: Error)

/// This error is returned when the scanned card doesn't have some essential fields.
case cardError

Expand Down Expand Up @@ -429,7 +431,8 @@ public enum TangemSdkError: Error, LocalizedError, Encodable {
case .underlying: return 50012
case .userForgotTheCode: return 50013
case .biometricsUnavailable: return 50014

case .preflightFiltered: return 50015

case .wrongInteractionMode: return 50027

// MARK: 9xxxx Errors
Expand Down Expand Up @@ -498,7 +501,7 @@ public enum TangemSdkError: Error, LocalizedError, Encodable {
case .wrongCardType(let localizedDescription): return localizedDescription ?? "error_wrong_card_type".localized
case .accessCodeRequired: return "error_pin_required_format".localized(UserCodeType.accessCode.name)
case .passcodeRequired: return "error_pin_required_format".localized(UserCodeType.passcode.name)
case .underlying(let error): return error.localizedDescription
case .underlying(let error), .preflightFiltered(let error): return error.localizedDescription
case .fileNotFound: return "error_file_not_found".localized
case .walletNotFound: return "wallet_not_found".localized
case .wrongAccessCode: return "error_wrong_pin_format".localized(UserCodeType.accessCode.name)
Expand Down
1 change: 1 addition & 0 deletions TangemSdk/TangemSdk/Operations/Backup/BackupService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ public class BackupService: ObservableObject {
currentCommand = command

sdk.startSession(with: command,
filter: nil,
initialMessage: Message(header: nil,
body: "backup_add_backup_card_message".localized)) {[weak self] result in
guard let self = self else { return }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class PersonalizeCommand: Command {
}

public func run(in session: CardSession, completion: @escaping CompletionResult<Card>) {
let read = PreflightReadTask(readMode: .readCardOnly, cardId: nil) //We have to run preflight read ourseleves to catch the notPersonalized error
let read = PreflightReadTask(readMode: .readCardOnly, filter: nil) //We have to run preflight read ourseleves to catch the notPersonalized error
read.run(in: session) { readResult in
switch readResult {
case .success:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// CardIdPreflightReadFilter.swift
// TangemSdk
//
// Created by Alexander Osokin on 09.11.2023.
// Copyright © 2023 Tangem AG. All rights reserved.
//

import Foundation

@available(iOS 13.0, *)
struct CardIdPreflightReadFilter: PreflightReadFilter {
private let expectedCardId: String

init(cardId: String) {
expectedCardId = cardId
}

func onCardRead(_ card: Card, environment: SessionEnvironment) throws {
if expectedCardId.caseInsensitiveCompare(card.cardId) == .orderedSame {
return
}

let formatter = CardIdFormatter(style: environment.config.cardIdDisplayFormat)
let expectedCardIdFormatted = formatter.string(from: expectedCardId)
throw TangemSdkError.wrongCardNumber(expectedCardId: expectedCardIdFormatted)
}

func onFullCardRead(_ card: Card, environment: SessionEnvironment) throws {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// PreflightReadFilter.swift
// TangemSdk
//
// Created by Alexander Osokin on 30.10.2023.
// Copyright © 2023 Tangem AG. All rights reserved.
//

import Foundation

@available(iOS 13.0, *)
/// Use this filter to filter out cards on preflight read stage. If preflight mode is set to `readCardOnly` or `fullCardRead`. `HandleErrors` flag must be switched on.
public protocol PreflightReadFilter {
/// This method calls right after public information is read. User code is not required. If preflight mode is set to `readCardOnly` or `fullCardRead`
/// - Parameter card: The card that was read
/// - Parameter environment: Current environment
/// - Throws: Throw an error with a localized message to the user, if the card should not be worked with.
func onCardRead(_ card: Card, environment: SessionEnvironment) throws

/// This method calls right after full card information is read. User code is required. If preflight mode is set to `fullCardRead`
/// - Parameter card: The card that was read
/// - Parameter environment: Current environment
/// - Throws: Throw an error with a localized message to the user, if the card should not be worked with.
func onFullCardRead(_ card: Card, environment: SessionEnvironment) throws
}
Loading

0 comments on commit 4399166

Please sign in to comment.