Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions RIADigiDoc.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
DF4E43CB2D0BA38600967997 /* FileImportShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = DF4E43C12D0BA38600967997 /* FileImportShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
DF4E6D822D11DE7B00F6111A /* CommonsLib in Frameworks */ = {isa = PBXBuildFile; productRef = DF4E6D812D11DE7B00F6111A /* CommonsLib */; };
DF4E6D842D120CFC00F6111A /* UtilsLib in Frameworks */ = {isa = PBXBuildFile; productRef = DF4E6D832D120CFC00F6111A /* UtilsLib */; };
DF54F82F2D431BD50021D05A /* X509 in Frameworks */ = {isa = PBXBuildFile; productRef = DF54F82E2D431BD50021D05A /* X509 */; };
DF5903762ECCC41F00D1A278 /* MobileIdLibMocks in Frameworks */ = {isa = PBXBuildFile; productRef = DF5903752ECCC41F00D1A278 /* MobileIdLibMocks */; };
DF5903782ECCC42500D1A278 /* SmartIdLibMocks in Frameworks */ = {isa = PBXBuildFile; productRef = DF5903772ECCC42500D1A278 /* SmartIdLibMocks */; };
DF5C54272E82F07A006E2251 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = DFA3EE5C2D14C9B7001D951B /* Alamofire */; };
Expand All @@ -53,7 +54,6 @@
DFB663A22F16917E00804545 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB663A12F16917E00804545 /* SwiftUI.framework */; };
DFB663AF2F16918100804545 /* WidgetExtensionExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = DFB6639E2F16917D00804545 /* WidgetExtensionExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
DFBD09F62CE3FBDC006AF9C2 /* LibdigidocLib in Frameworks */ = {isa = PBXBuildFile; productRef = DFBD09F52CE3FBDC006AF9C2 /* LibdigidocLib */; };
DF54F82F2D431BD50021D05A /* X509 in Frameworks */ = {isa = PBXBuildFile; productRef = DF54F82E2D431BD50021D05A /* X509 */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -1704,6 +1704,14 @@
version = 12.12.1;
};
};
DF54F82D2D431BD50021D05A /* XCRemoteSwiftPackageReference "swift-certificates" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-certificates.git";
requirement = {
kind = exactVersion;
version = 1.19.0;
};
};
DF9AFE902E00D30A0062C64D /* XCRemoteSwiftPackageReference "Factory" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/hmlongco/Factory";
Expand All @@ -1728,14 +1736,6 @@
version = 0.63.2;
};
};
DF54F82D2D431BD50021D05A /* XCRemoteSwiftPackageReference "swift-certificates" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-certificates.git";
requirement = {
kind = exactVersion;
version = 1.19.0;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
Expand Down Expand Up @@ -1818,6 +1818,11 @@
isa = XCSwiftPackageProductDependency;
productName = UtilsLib;
};
DF54F82E2D431BD50021D05A /* X509 */ = {
isa = XCSwiftPackageProductDependency;
package = DF54F82D2D431BD50021D05A /* XCRemoteSwiftPackageReference "swift-certificates" */;
productName = X509;
};
DF5903752ECCC41F00D1A278 /* MobileIdLibMocks */ = {
isa = XCSwiftPackageProductDependency;
productName = MobileIdLibMocks;
Expand Down Expand Up @@ -1917,11 +1922,6 @@
isa = XCSwiftPackageProductDependency;
productName = LibdigidocLib;
};
DF54F82E2D431BD50021D05A /* X509 */ = {
isa = XCSwiftPackageProductDependency;
package = DF54F82D2D431BD50021D05A /* XCRemoteSwiftPackageReference "swift-certificates" */;
productName = X509;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = DFDB14732CC97B0E00153876 /* Project object */;
Expand Down
100 changes: 91 additions & 9 deletions RIADigiDoc/Domain/NFC/NFCOperationBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ import UtilsLib
@MainActor
public class NFCOperationBase: NSObject, Loggable, @MainActor NFCTagReaderSessionDelegate {
var session: NFCTagReaderSession?
var isFinished = false
var canNumber: String = ""
var nfcError: String = ""
var strings: NFCSessionStrings?
var didCompleteSuccessfully = false
var operationError: Error?

let connection = NFCConnection()
Expand All @@ -53,7 +50,6 @@ public class NFCOperationBase: NSObject, Loggable, @MainActor NFCTagReaderSessio
}

func success() {
didCompleteSuccessfully = true
session?.alertMessage = strings?.successMessage ?? ""
session?.invalidate()
}
Expand Down Expand Up @@ -149,20 +145,106 @@ public class NFCOperationBase: NSObject, Loggable, @MainActor NFCTagReaderSessio
session: NFCTagReaderSession
) {
Self.logger().error("NFC: Unknown error type: \(type(of: error))")
Self.logger().error("NFC: Error details: \(error.localizedDescription)")
Self.logger().error("NFC: Error details: \(String(describing: error))")
operationError = error
session.invalidate(errorMessage: strings?.sessionErrorMessage ?? "")
}

func handleNFCError(_ error: Error, session: NFCTagReaderSession) -> Bool {
if (error as NSError).localizedDescription == "Failed to find lock for cert" {
handleNoCertLockError(error: error, session: session)
return true
}
if let idCardInternalError = error as? IdCardInternalError {
handleIdCardInternalError(idCardInternalError, session: session)
return true
}
if let nfcIdCardError = error as? nfclib.IdCardInternalError {
handleIdCardInternalError(nfcIdCardError, session: session)
return true
}
return false
}

func handleSessionError(_ error: Error, session: NFCTagReaderSession) -> Error {
if let nfcError = error as? NFCReaderError,
nfcError.code == .readerSessionInvalidationErrorUserCanceled {
return IdCardInternalError.cancelledByUser
}
if handleNFCError(error, session: session) {
return operationError ?? error
}
if let digiDocError = error as? DigiDocError {
handleDigiDocError(digiDocError, session: session)
return digiDocError
}
handleUnknownError(error, session: session)
return error
}

private var tagContinuation: CheckedContinuation<[NFCTag], Error>?

func waitForTagConnected() async throws -> any NFCISO7816Tag {
let tags = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<[NFCTag], Error>) in
guard NFCTagReaderSession.readingAvailable else {
continuation.resume(throwing: IdCardInternalError.nfcNotSupported)
return
}
tagContinuation = continuation
session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self)
updateAlertMessage(step: 0)
session?.begin()
}

guard let session else {
throw IdCardInternalError.nfcNotSupported
}
updateAlertMessage(step: 1)
return try await connection.setup(session, tags: tags)
}

func withCardCommands<T>(
canNumber: String,
strings: NFCSessionStrings,
_ body: (any CardCommands) async throws -> T
) async throws -> T {
self.strings = strings
let tag = try await waitForTagConnected()
guard let session else {
Self.logger().error("Unable to get session")
throw IdCardInternalError.nfcNotSupported
}
do {
updateAlertMessage(step: 2)
let cardCommands = try await connection.getCardCommands(session, tag: tag, CAN: canNumber)
let result = try await body(cardCommands)
success()
return result
} catch {
Self.logger().error("Unable to get card commands: \(String(describing: error))")
throw handleSessionError(error, session: session)
}
}

// MARK: - NFCTagReaderSessionDelegate

public func tagReaderSessionDidBecomeActive(_: NFCTagReaderSession) { }

public func tagReaderSession(_: NFCTagReaderSession, didDetect _: [NFCTag]) {
// Override in subclasses
public func tagReaderSession(_: NFCTagReaderSession, didDetect tags: [NFCTag]) {
tagContinuation?.resume(returning: tags)
tagContinuation = nil
}

public func tagReaderSession(_: NFCTagReaderSession, didInvalidateWithError _: Error) {
// Override in subclasses
public func tagReaderSession(_: NFCTagReaderSession, didInvalidateWithError error: Error) {
Self.logger().info("NFC: Reader session finished with error: \(String(describing: error))")
session = nil
guard let tagContinuation else { return }
self.tagContinuation = nil
if let nfcError = error as? NFCReaderError,
nfcError.code == .readerSessionInvalidationErrorUserCanceled {
tagContinuation.resume(throwing: IdCardInternalError.cancelledByUser)
} else {
tagContinuation.resume(throwing: error)
}
}
}
119 changes: 6 additions & 113 deletions RIADigiDoc/Domain/NFC/OperationChangePin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,128 +24,21 @@ import nfclib

@MainActor
public class OperationChangePin: NFCOperationBase, OperationChangePinProtocol {
private var codeType: CodeType?
private var currentPin: SecureData?
private var newPin: SecureData?
private var continuation: CheckedContinuation<Void, Error>?

public func startChanging(
canNumber: String,
codeType: CodeType,
currentPin: SecureData,
newPin: SecureData,
strings: NFCSessionStrings,
) async throws {
self.canNumber = canNumber
self.codeType = codeType
self.currentPin = currentPin
self.newPin = newPin
self.strings = strings

return try await withCheckedThrowingContinuation { continuation in
self.continuation = continuation

guard NFCTagReaderSession.readingAvailable else {
continuation.resume(throwing: IdCardInternalError.nfcNotSupported)
return
}

session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self)
updateAlertMessage(step: 0)
session?.begin()
}
}

// MARK: - NFCTagReaderSessionDelegate

public override func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
Task { @MainActor in
defer {
self.session = nil
}

guard let codeType = self.codeType,
let currentPin = self.currentPin,
let newPin = self.newPin else {
let error = ChangePinError.missingRequiredParameter
operationError = error
OperationChangePin.logger().error("NFC: \(error.localizedDescription)")
session.invalidate(errorMessage: strings?.technicalErrorMessage ??
"Missing required parameters")
return
}

do {
updateAlertMessage(step: 1)
OperationChangePin.logger().info("NFC: Setting up NFC connection for PIN change...")
let tag = try await self.connection.setup(session, tags: tags)

updateAlertMessage(step: 2)
let cardCommands = try await self.connection.getCardCommands(session, tag: tag, CAN: self.canNumber)

updateAlertMessage(step: 3)
OperationChangePin.logger().info("NFC: Changing \(codeType.name)...")
try await cardCommands.changeCode(codeType, to: newPin, verifyCode: currentPin)
OperationChangePin.logger().info("NFC: \(codeType.name) changed successfully")

success()
} catch {
if (error as NSError).localizedDescription == "Failed to find lock for cert" {
handleNoCertLockError(error: error, session: session)
return
}

if let idCardInternalError = error as? IdCardInternalError {
handleIdCardInternalError(idCardInternalError, session: session)
return
}

if let nfcIdCardError = error as? nfclib.IdCardInternalError {
handleIdCardInternalError(nfcIdCardError, session: session)
return
}

if let changePinError = error as? ChangePinError {
operationError = changePinError
OperationChangePin.logger()
.error("NFC: changePinError: \(changePinError.localizedDescription)")
session.invalidate(errorMessage: strings?.technicalErrorMessage ?? "")
return
}

handleUnknownError(error, session: session)
}
defer {
currentPin.secureZero()
newPin.secureZero()
}
}

public override func tagReaderSession(_: NFCTagReaderSession, didInvalidateWithError error: Error) {
Self.logger().info("NFC: Reader session finished with error: \(error)")
self.session = nil

guard let continuationToResume = self.continuation else { return }
self.continuation = nil

if didCompleteSuccessfully {
continuationToResume.resume(with: .success(()))
return
try await withCardCommands(canNumber: canNumber, strings: strings) { cardCommands in
updateAlertMessage(step: 3)
try await cardCommands.changeCode(codeType, to: newPin, verifyCode: currentPin)
}

if let storedError = self.operationError {
continuationToResume.resume(throwing: storedError)
return
}

if let nfcError = error as? NFCReaderError {
switch nfcError.code {
case .readerSessionInvalidationErrorUserCanceled:
continuationToResume.resume(throwing: IdCardInternalError.cancelledByUser)
return

default:
break
}
}

continuationToResume.resume(throwing: error)
}
}
Loading
Loading