diff --git a/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnection.swift b/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnection.swift index 100583f3..bf6b8ad7 100644 --- a/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnection.swift +++ b/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnection.swift @@ -227,6 +227,24 @@ public actor UsbReaderConnection: UsbReaderConnectionProtocol, Loggable { } } + public func calculateSignature(for dataToSign: Data, pin2: SecureData) async throws -> Data { + await ensureHandler() + + guard let handler = cardHandler else { + UsbReaderConnection.logger().error("ID-CARD: Unable to calculate signature to sign with ID-card reader") + throw IdCardInternalError.readerProcessFailed + } + + do { + return try await handler.calculateSignature(for: dataToSign, withPin2: pin2) + } catch { + guard let exception = error as? IdCardInternalError else { + throw IdCardError.sessionError + } + throw exception.getIdCardError() + } + } + // MARK: Handler private func ensureHandler() async { diff --git a/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnectionProtocol.swift b/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnectionProtocol.swift index 2e01948e..5c535e07 100644 --- a/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnectionProtocol.swift +++ b/Modules/IdCardLib/Sources/IdCardLib/Operations/UsbReaderConnectionProtocol.swift @@ -36,4 +36,5 @@ public protocol UsbReaderConnectionProtocol: Actor { func isPUKChangeable() async throws -> Bool func changeCode(_ codeType: CodeType, to newCode: Data, verifyCode: Data) async throws func unblockCode(_ codeType: CodeType, puk: Data, newCode: Data) async throws + func calculateSignature(for dataToSign: Data, pin2: SecureData) async throws -> Data } diff --git a/RIADigiDoc/DI/AppContainer.swift b/RIADigiDoc/DI/AppContainer.swift index 9546dfbc..25ca5dfa 100644 --- a/RIADigiDoc/DI/AppContainer.swift +++ b/RIADigiDoc/DI/AppContainer.swift @@ -419,7 +419,9 @@ extension Container { idCardRepository: self.idCardRepository(), sharedMyEidSession: self.sharedMyEidSession(), certificateUtil: self.certificateUtil(), - nameUtil: self.nameUtil() + nameUtil: self.nameUtil(), + dataStore: self.dataStore(), + userAgentUtil: self.userAgentUtil() ) } } diff --git a/RIADigiDoc/Domain/Repository/IdCard/IdCardRepository.swift b/RIADigiDoc/Domain/Repository/IdCard/IdCardRepository.swift index 1703d1b0..116fe7a0 100644 --- a/RIADigiDoc/Domain/Repository/IdCard/IdCardRepository.swift +++ b/RIADigiDoc/Domain/Repository/IdCard/IdCardRepository.swift @@ -72,4 +72,8 @@ actor IdCardRepository: IdCardRepositoryProtocol { func unblockCode(_ codeType: CodeType, puk: Data, newCode: Data) async throws { try await idCardService.unblockCode(codeType, puk: puk, newCode: newCode) } + + func calculateSignature(for dataToSign: Data, pin2: SecureData) async throws -> Data { + try await idCardService.calculateSignature(for: dataToSign, pin2: pin2) + } } diff --git a/RIADigiDoc/Domain/Repository/IdCard/IdCardRepositoryProtocol.swift b/RIADigiDoc/Domain/Repository/IdCard/IdCardRepositoryProtocol.swift index d376244e..910d2457 100644 --- a/RIADigiDoc/Domain/Repository/IdCard/IdCardRepositoryProtocol.swift +++ b/RIADigiDoc/Domain/Repository/IdCard/IdCardRepositoryProtocol.swift @@ -33,4 +33,5 @@ public protocol IdCardRepositoryProtocol: Sendable { func isPUKChangeable() async throws -> Bool func changeCode(_ codeType: CodeType, to newCode: Data, verifyCode: Data) async throws func unblockCode(_ codeType: CodeType, puk: Data, newCode: Data) async throws + func calculateSignature(for dataToSign: Data, pin2: SecureData) async throws -> Data } diff --git a/RIADigiDoc/Domain/Service/IdCard/IdCardService.swift b/RIADigiDoc/Domain/Service/IdCard/IdCardService.swift index f24af8dc..e4576188 100644 --- a/RIADigiDoc/Domain/Service/IdCard/IdCardService.swift +++ b/RIADigiDoc/Domain/Service/IdCard/IdCardService.swift @@ -70,4 +70,8 @@ actor IdCardService: IdCardServiceProtocol { func unblockCode(_ codeType: CodeType, puk: Data, newCode: Data) async throws { try await usbReaderConnection.unblockCode(codeType, puk: puk, newCode: newCode) } + + func calculateSignature(for dataToSign: Data, pin2: SecureData) async throws -> Data { + try await usbReaderConnection.calculateSignature(for: dataToSign, pin2: pin2) + } } diff --git a/RIADigiDoc/Domain/Service/IdCard/IdCardServiceProtocol.swift b/RIADigiDoc/Domain/Service/IdCard/IdCardServiceProtocol.swift index 5836dfe6..a4ebf28f 100644 --- a/RIADigiDoc/Domain/Service/IdCard/IdCardServiceProtocol.swift +++ b/RIADigiDoc/Domain/Service/IdCard/IdCardServiceProtocol.swift @@ -33,4 +33,5 @@ public protocol IdCardServiceProtocol: Sendable { func isPUKChangeable() async throws -> Bool func changeCode(_ codeType: CodeType, to newCode: Data, verifyCode: Data) async throws func unblockCode(_ codeType: CodeType, puk: Data, newCode: Data) async throws + func calculateSignature(for dataToSign: Data, pin2: SecureData) async throws -> Data } diff --git a/RIADigiDoc/RIADigiDocApp.swift b/RIADigiDoc/RIADigiDocApp.swift index ce2cfdc3..dbd78fe0 100644 --- a/RIADigiDoc/RIADigiDocApp.swift +++ b/RIADigiDoc/RIADigiDocApp.swift @@ -66,6 +66,7 @@ struct RIADigiDocApp: App { } await librarySetup.setupLibraries() + await crashReportManager.evaluateCrashReporting() fileUtil.removeSavedFilesDirectory(savedFilesDirectory: nil) isInitialLanguageSelected = await dataStore.getIsInitialLanguageSelected() await MainActor.run { @@ -89,15 +90,14 @@ struct RIADigiDocApp: App { ContentView() .environment(pathManager) .appNavigation(pathManager: pathManager) - .task { - await crashReportManager.evaluateCrashReporting() - } .alert( languageSettings.localized("Crash report title"), isPresented: $crashReportManager.showCrashDialog ) { Button(languageSettings.localized("Crash report send")) { - crashReportManager.sendReport() + Task { + await crashReportManager.sendReport() + } } Button(languageSettings.localized("Crash report always send")) { Task { @@ -105,7 +105,9 @@ struct RIADigiDocApp: App { } } Button(languageSettings.localized("Crash report dont send"), role: .cancel) { - crashReportManager.doNotSendReport() + Task { + await crashReportManager.doNotSendReport() + } } } message: { Text(verbatim: languageSettings.localized("Crash report message")) diff --git a/RIADigiDoc/Supporting files/Localizable.xcstrings b/RIADigiDoc/Supporting files/Localizable.xcstrings index 9ed5bb7e..3caa7393 100644 --- a/RIADigiDoc/Supporting files/Localizable.xcstrings +++ b/RIADigiDoc/Supporting files/Localizable.xcstrings @@ -1500,7 +1500,7 @@ }, "et" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Ümbriku krüpteerimine ebaõnnestus" } } @@ -1991,6 +1991,24 @@ } } }, + "ID card conditional speech" : { + "comment" : "ID-card error messages where it’s used in sentences", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID-card's" + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID-kaardi" + } + } + } + }, "ID card connect card reader" : { "comment" : "ID-card view initial message", "extractionState" : "manual", @@ -8049,4 +8067,4 @@ } }, "version" : "1.1" -} +} \ No newline at end of file diff --git a/RIADigiDoc/UI/Component/Container/Signing/IdCard/IdCardView.swift b/RIADigiDoc/UI/Component/Container/Signing/IdCard/IdCardView.swift index 07cef114..7191cf21 100644 --- a/RIADigiDoc/UI/Component/Container/Signing/IdCard/IdCardView.swift +++ b/RIADigiDoc/UI/Component/Container/Signing/IdCard/IdCardView.swift @@ -22,8 +22,10 @@ import FactoryKit import CryptoSwift import LibdigidocLibSwift import IdCardLib +import CommonsLib struct IdCardView: View { + @Environment(\.openURL) private var openURL @Environment(\.dismiss) private var dismiss @Environment(LanguageSettings.self) private var languageSettings @Environment(NavigationPathManager.self) private var pathManager @@ -34,6 +36,7 @@ struct IdCardView: View { @State private var isInProgress: Bool = false @State private var isShowingPinView: Bool = false @State private var isShowingLoadingView: Bool = false + @State private var showRoleView: Bool = false @State private var pinNumber = "" @State private var idCardActionMessage: String = "ID card connect card reader" @@ -42,12 +45,23 @@ struct IdCardView: View { @State private var usbReaderStatus: UsbReaderStatus = .sInitial @State private var idCardData: IdCardData? + @State private var task: Task? + let signedContainer: SignedContainerProtocol? let cryptoContainer: CryptoContainerProtocol? let onSuccess: (SignedContainerProtocol) -> Void let onSuccessDecrypt: (CryptoContainerProtocol) -> Void + var localizedArguments: [String] { + var localized: [String] = [] + for arg in viewModel.idCardAlertMessageExtraArguments { + localized.append(languageSettings.localized(arg)) + } + + return localized + } + private var errorMessage: String { languageSettings.localized( viewModel.errorMessage ?? "", @@ -131,7 +145,7 @@ struct IdCardView: View { Task { let decryptedContainer = await viewModel.decrypt( pin1: pinNumber, - cryptoContainer: container, + cryptoContainer: container ) pinNumber = "" @@ -140,7 +154,7 @@ struct IdCardView: View { guard let container = decryptedContainer else { if shouldDismiss { - cancelDecrypt() + cancelIdCardAction() await viewModel.stopDiscoveringReaders() } @@ -161,7 +175,7 @@ struct IdCardView: View { } await viewModel.stopDiscoveringReaders() - cancelDecrypt() + cancelIdCardAction() isInProgress = false isShowingPinView = false isShowingLoadingView = false @@ -171,8 +185,20 @@ struct IdCardView: View { } } case .signing: - // TODO: Implement signing action isInProgress = true + isShowingPinView = false + isShowingLoadingView = true + + if usbReaderStatus == .sCardConnected { + Task { + let isRoleDataEnabled = await viewModel.isRoleDataEnabled() + if isRoleDataEnabled { + showRoleView = true + } else { + sign() + } + } + } case .myeid: // Do nothing isInProgress = true @@ -206,6 +232,49 @@ struct IdCardView: View { .task { await viewModel.startDiscoveringReaders() } + .fullScreenCover(isPresented: $showRoleView) { + RoleView( + onComplete: { roles, city, state, country, zipCode in + showRoleView = false + sign( + roleData: RoleData( + roles: roles + .split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespaces) }, + city: city, + state: state, + country: country, + zipCode: zipCode + ) + ) + } + ) + } + .alert( + languageSettings.localized( + viewModel.idCardAlertMessageKey ?? "", + localizedArguments + ), + isPresented: $viewModel.showIdCardAlertMessage + ) { + Button(languageSettings.localized("OK")) { + viewModel.resetErrors() + viewModel.resetAlertErrors() + dismiss() + } + + if let messageUrl = viewModel.idCardAlertMessageUrl, !messageUrl.isEmpty { + Button(languageSettings.localized("Additional information")) { + if let url = URL(string: languageSettings.localized(messageUrl)), + UIApplication.shared.canOpenURL(url) { + openURL(url) + } + viewModel.resetErrors() + viewModel.resetAlertErrors() + dismiss() + } + } + } .onChange(of: viewModel.usbReaderStatus) { _, newValue in usbReaderStatus = newValue idCardActionMessage = getStatusText(newValue) @@ -218,6 +287,8 @@ struct IdCardView: View { isInProgress = !notInProgressStates.contains(newValue) + pinNumber = "" + Task { switch actionType { case .decrypt: @@ -241,8 +312,24 @@ struct IdCardView: View { } case .signing: - // TODO: Implement signing action - isInProgress = true + guard newValue == .sCardConnected else { + await MainActor.run { + isShowingPinView = false + isShowingLoadingView = false + } + return + } + + idCardData = await viewModel.getIdCardData() + guard idCardData != nil else { + await handleCardError() + return + } + + await MainActor.run { + isShowingLoadingView = false + isShowingPinView = true + } case .myeid: guard newValue == .sCardConnected else { return } @@ -267,11 +354,7 @@ struct IdCardView: View { } .onDisappear { pinNumber.removeAll() - Task { - await MainActor.run { - viewModel.resetErrors() - } - } + cancelIdCardAction() } } @@ -299,10 +382,86 @@ struct IdCardView: View { } } - private func cancelDecrypt() { + private func cancelIdCardAction() { pinNumber.isEmpty ? () : (pinNumber.removeAll()) isActionEnabled = viewModel .isActionEnabled(pinNumber: pinNumber, pinType: pinCodeType) + resetIdCardAction() + } + + private func resetIdCardAction() { + task?.cancel() + task = nil + viewModel.resetErrors() + } + + private func sign(roleData: RoleData? = nil) { + resetIdCardAction() + + guard let container = signedContainer else { + Toast.show( + languageSettings.localized("General error") + ) + return + } + + task = Task { + let updatedContainer = await viewModel.sign( + pin2: pinNumber, + signedContainer: container, + roleData: roleData ?? RoleData( + roles: [], + city: "", + state: "", + country: "", + zipCode: "" + ) + ) + + pinNumber = "" + + let shouldDismiss = viewModel.shouldDismissForError + + guard let container = updatedContainer else { + if shouldDismiss { + cancelIdCardAction() + await viewModel.stopDiscoveringReaders() + } + + await MainActor.run { + Toast.show(errorMessage) + viewModel.resetErrors() + if shouldDismiss { + dismiss() + } + + if viewModel.showIdCardAlertMessage { + idCardActionMessage = "ID card connect card reader" + isInProgress = false + isShowingPinView = false + isShowingLoadingView = false + } else { + isInProgress = !viewModel.shouldDismissForError + isShowingPinView = !viewModel.shouldDismissForError + isShowingLoadingView = false + } + return + } + + return + } + + await viewModel.stopDiscoveringReaders() + cancelIdCardAction() + isInProgress = false + isShowingPinView = false + isShowingLoadingView = false + + Toast.show(languageSettings.localized("Signature added")) + + onSuccess(container) + dismiss() + } } } diff --git a/RIADigiDoc/UI/Component/Toast/Toast.swift b/RIADigiDoc/UI/Component/Toast/Toast.swift index 09e0af25..ea6fd2db 100644 --- a/RIADigiDoc/UI/Component/Toast/Toast.swift +++ b/RIADigiDoc/UI/Component/Toast/Toast.swift @@ -21,6 +21,8 @@ import SwiftUI struct Toast { static func show(_ message: String, duration: TimeInterval = 5.0) { + if message.isEmpty { return } + Task { await ToastQueue.shared.enqueue( message: message, diff --git a/RIADigiDoc/Util/Manager/CrashReport/CrashReportManager.swift b/RIADigiDoc/Util/Manager/CrashReport/CrashReportManager.swift index fff7959a..74c88de6 100644 --- a/RIADigiDoc/Util/Manager/CrashReport/CrashReportManager.swift +++ b/RIADigiDoc/Util/Manager/CrashReport/CrashReportManager.swift @@ -36,40 +36,47 @@ final class CrashReportManager: CrashReportManagerProtocol, Loggable { } func evaluateCrashReporting() async { - if await dataStore.getIsCrashlyticsAlwaysEnabled() { - checkForUnsentReports(send: true) + let isAlwaysEnabled = await dataStore.getIsCrashlyticsAlwaysEnabled() + let hasUnsentReports = await Crashlytics.crashlytics().checkForUnsentReports() + + guard hasUnsentReports else { + showCrashDialog = false + return + } + + if isAlwaysEnabled { + await checkForUnsentReports(send: true) } else { showCrashDialog = true } } - func sendReport() { + func sendReport() async { CrashReportManager.logger().info("Sending crash report") - checkForUnsentReports(send: true) + await checkForUnsentReports(send: true) showCrashDialog = false } func alwaysSendReport() async { CrashReportManager.logger().info("(Always) sending crash report") await dataStore.setIsCrashlyticsAlwaysEnabled(true) - checkForUnsentReports(send: true) + await checkForUnsentReports(send: true) showCrashDialog = false } - func doNotSendReport() { + func doNotSendReport() async { CrashReportManager.logger().info("Not sending crash report") - checkForUnsentReports(send: false) + await checkForUnsentReports(send: false) showCrashDialog = false } - private func checkForUnsentReports(send: Bool) { - Crashlytics.crashlytics().checkForUnsentReports { hasUnsentReports in - CrashReportManager.logger().info("Has unsent crash reports: \(hasUnsentReports)") - if send && hasUnsentReports { - Crashlytics.crashlytics().sendUnsentReports() - } else { - Crashlytics.crashlytics().deleteUnsentReports() - } + private func checkForUnsentReports(send: Bool) async { + let hasUnsentReports = await Crashlytics.crashlytics().checkForUnsentReports() + CrashReportManager.logger().info("Has unsent crash reports: \(hasUnsentReports)") + if send && hasUnsentReports { + Crashlytics.crashlytics().sendUnsentReports() + } else { + Crashlytics.crashlytics().deleteUnsentReports() } } } diff --git a/RIADigiDoc/Util/Manager/CrashReport/CrashReportManagerProtocol.swift b/RIADigiDoc/Util/Manager/CrashReport/CrashReportManagerProtocol.swift index 0cc99d61..2ecf3424 100644 --- a/RIADigiDoc/Util/Manager/CrashReport/CrashReportManagerProtocol.swift +++ b/RIADigiDoc/Util/Manager/CrashReport/CrashReportManagerProtocol.swift @@ -23,7 +23,7 @@ import Foundation @MainActor protocol CrashReportManagerProtocol: Sendable { func evaluateCrashReporting() async - func sendReport() + func sendReport() async func alwaysSendReport() async - func doNotSendReport() + func doNotSendReport() async } diff --git a/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModel.swift b/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModel.swift index 2c915b6b..5db92b8e 100644 --- a/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModel.swift +++ b/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModel.swift @@ -23,6 +23,7 @@ import IdCardLib import CommonsLib import X509 import UtilsLib +import LibdigidocLibSwift @Observable @MainActor @@ -31,10 +32,16 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { private let sharedMyEidSession: SharedMyEidSessionProtocol private let certificateUtil: CertificateUtilProtocol private let nameUtil: NameUtilProtocol + private let dataStore: DataStoreProtocol + private let userAgentUtil: UserAgentUtilProtocol var errorMessage: String? var errorExtraArguments: [String] = [] var shouldDismissForError = false + var showIdCardAlertMessage = false + var idCardAlertMessageKey: String? + var idCardAlertMessageUrl: String? + var idCardAlertMessageExtraArguments: [String] = [] var pinNumberErrorKey: String? var pinNumberErrorExtraArguments: [String] = [] @@ -47,12 +54,16 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { idCardRepository: IdCardRepositoryProtocol, sharedMyEidSession: SharedMyEidSessionProtocol, certificateUtil: CertificateUtilProtocol, - nameUtil: NameUtilProtocol + nameUtil: NameUtilProtocol, + dataStore: DataStoreProtocol, + userAgentUtil: UserAgentUtilProtocol ) { self.idCardRepository = idCardRepository self.sharedMyEidSession = sharedMyEidSession self.certificateUtil = certificateUtil self.nameUtil = nameUtil + self.dataStore = dataStore + self.userAgentUtil = userAgentUtil } func startDiscoveringReaders() async { @@ -92,6 +103,7 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { return container } catch { + IdCardViewModel.logger().error("ID-CARD: Unable to decrypt container with ID-card reader. \(error)") guard let exception = error as? IdCardInternalError else { IdCardViewModel.logger().error("ID-CARD: ID Card General error.") errorMessage = "General error" @@ -106,6 +118,58 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { } } + func sign( + pin2: String, + signedContainer: SignedContainerProtocol, + roleData: RoleData + ) async -> SignedContainerProtocol? { + do { + let containerFile = await signedContainer.getRawContainerFile() ?? URL(fileURLWithPath: "") + let pinSecureData = SecureData(Array(pin2.utf8)) + let signatureCertificate = try await idCardRepository.readSignatureCertificate() + + IdCardViewModel.logger().info("ID-CARD: Getting language") + let appLanguage = await dataStore.getSelectedLanguage() + + IdCardViewModel.logger().info("ID-CARD: Getting User-Agent") + let userAgent = userAgentUtil.userAgent(diagnostics: .devices, language: appLanguage) + + let dataToSign: Data + do { + dataToSign = try await prepareSignature( + cert: signatureCertificate, + containerFile: containerFile, + roleData: roleData, + signedContainer: signedContainer, + userAgent: userAgent + ) + } catch { + IdCardViewModel.logger().error( + "ID-CARD: Unable to prepare signature for signing with ID-card reader. \(error)" + ) + handleError(error, codeType: .pin2) + return nil + } + + let signatureData = try await idCardRepository.calculateSignature( + for: dataToSign, + pin2: pinSecureData + ) + + return try await signedContainer.addSignature( + signature: signatureData, + containerFile: containerFile + ) + } catch { + IdCardViewModel.logger().error( + "ID-CARD: Unable to read ID-card data to sign with ID-Card reader. \(error)" + ) + + handleError(error, codeType: .pin2) + return nil + } + } + func getIdCardData() async -> IdCardData? { do { let publicData = try await getPublicData() @@ -138,10 +202,21 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { shouldDismissForError = false } + func resetAlertErrors() { + showIdCardAlertMessage = false + idCardAlertMessageKey = nil + idCardAlertMessageUrl = nil + idCardAlertMessageExtraArguments = [] + } + func formatPersonalIdentifier(givenName: String, surname: String, personalCode: String) -> String { return "\(nameUtil.formatName("\(givenName) \(surname)")), \(personalCode)" } + func isRoleDataEnabled() async -> Bool { + await dataStore.getIsRoleAndAddressEnabled() + } + private func getPublicData() async throws -> CardInfo { IdCardViewModel.logger().info( "ID-CARD: Getting public data from ID-card with reader" @@ -257,4 +332,97 @@ class IdCardViewModel: IdCardViewModelProtocol, Loggable { } pinNumberErrorKey = "" } + + private func prepareSignature( + cert: Data, + containerFile: URL, + roleData: RoleData, + signedContainer: SignedContainerProtocol, + userAgent: String + ) async throws -> Data { + IdCardViewModel.logger().info( + "ID-CARD: Preparing signature. Calculating hash" + ) + + return try await signedContainer.prepareSignature( + cert: cert, + containerPath: containerFile, + roleData: roleData, + userAgent: userAgent + ) + } + + private func handleError(_ error: Error, codeType: CodeType) { + switch error { + case let idCardError as IdCardError: + handleIdCardError(idCardError, pinType: codeType) + + case let digidocError as DigiDocError: + handleSignatureAddingError(digidocError) + + default: + errorMessage = "General error" + errorExtraArguments = [] + } + } + + // swiftlint:disable:next cyclomatic_complexity + private func handleSignatureAddingError(_ error: Error) { + IdCardViewModel.logger().error("Unable to sign container with ID-card: \(error)") + + if let cancellationError = error as? CancellationError { + IdCardViewModel.logger().error("ID-card signing manually cancelled: \(cancellationError)") + return + } + + guard let digidocError = error as? DigiDocError else { + errorMessage = "General error" + return + } + + switch digidocError { + case .signatureAddingFailed(let errorDetail): + let message = errorDetail.message + + let sslError = "Failed to create ssl connection with host" + let tooManyRequestsError = "Too Many Requests" + let ocspError = "OCSP response not in valid time slot" + let revokedCertError = "Certificate status: revoked" + let connectError = "CONNECT: 403" + let failedToConnectError = "Failed to connect" + let proxyError = "Failed to authenticate with proxy" + + switch true { + case message.contains(sslError): + errorMessage = "SSL handshake failed" + + case message.contains(tooManyRequestsError): + showIdCardAlertMessage = true + idCardAlertMessageKey = "Too many requests" + idCardAlertMessageUrl = "Too many requests url" + idCardAlertMessageExtraArguments = ["ID card conditional speech"] + + case message.contains(ocspError): + showIdCardAlertMessage = true + idCardAlertMessageKey = "OCSP response not in valid time slot" + idCardAlertMessageUrl = "OCSP response not in valid time slot url" + + case message.contains(revokedCertError): + errorMessage = "Certificate status revoked" + + case message.contains(connectError), message.contains(failedToConnectError): + errorMessage = "No Internet connection" + + case message.contains(proxyError): + errorMessage = "Invalid proxy settings" + + default: + errorMessage = "Signing technical error" + idCardAlertMessageExtraArguments = ["ID card conditional speech"] + } + + default: + errorMessage = "General error" + } + } } diff --git a/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModelProtocol.swift b/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModelProtocol.swift index 5175af67..027dd742 100644 --- a/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModelProtocol.swift +++ b/RIADigiDoc/ViewModel/Signing/IdCard/IdCardViewModelProtocol.swift @@ -34,4 +34,5 @@ public protocol IdCardViewModelProtocol: Sendable { func getIdCardData() async -> IdCardData? func resetErrors() func formatPersonalIdentifier(givenName: String, surname: String, personalCode: String) -> String + func isRoleDataEnabled() async -> Bool }