From c41e19f9d9c52bff8c964456439cba6b85773126 Mon Sep 17 00:00:00 2001 From: Marten Rebane Date: Mon, 22 Jun 2026 16:57:16 +0300 Subject: [PATCH 1/2] Show locked card message --- RIADigiDoc.xcodeproj/project.pbxproj | 28 +++--- .../Domain/Model/IdCard/PinResponse.swift | 6 ++ RIADigiDoc/Domain/NFC/IdCardError.swift | 1 + .../Domain/NFC/IdCardInternalError.swift | 3 + RIADigiDoc/Domain/NFC/OperationDecrypt.swift | 5 +- .../Domain/NFC/OperationReadCertAndSign.swift | 5 ++ .../Supporting files/Localizable.xcstrings | 90 +++++++++++++++++++ .../Recipient/EncryptRecipientView.swift | 4 +- .../UI/Component/CryptoFileOpeningView.swift | 5 +- RIADigiDoc/UI/Component/FileOpeningView.swift | 5 +- .../My eID/MyEidCertificateCardView.swift | 7 +- .../My eID/MyEidPinsAndCertificatesView.swift | 46 +++++++++- .../UI/Component/My eID/MyEidView.swift | 7 +- .../ViewModel/Signing/NFC/NFCViewModel.swift | 6 ++ .../Signing/NFC/NFCViewModelTests.swift | 48 ++++++++++ 15 files changed, 241 insertions(+), 25 deletions(-) diff --git a/RIADigiDoc.xcodeproj/project.pbxproj b/RIADigiDoc.xcodeproj/project.pbxproj index d741f1f0..9a85f071 100644 --- a/RIADigiDoc.xcodeproj/project.pbxproj +++ b/RIADigiDoc.xcodeproj/project.pbxproj @@ -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 */; }; @@ -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 */ @@ -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"; @@ -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 */ @@ -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; @@ -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 */; diff --git a/RIADigiDoc/Domain/Model/IdCard/PinResponse.swift b/RIADigiDoc/Domain/Model/IdCard/PinResponse.swift index f8fbbf0d..6c663b2a 100644 --- a/RIADigiDoc/Domain/Model/IdCard/PinResponse.swift +++ b/RIADigiDoc/Domain/Model/IdCard/PinResponse.swift @@ -27,3 +27,9 @@ public struct PinResponse: Sendable, Hashable { let pukRetryCount: UInt8 let pukActive: Bool } + +extension PinResponse { + var isCourierCard: Bool { + !pin1Active + } +} diff --git a/RIADigiDoc/Domain/NFC/IdCardError.swift b/RIADigiDoc/Domain/NFC/IdCardError.swift index 493d7bcf..6328b113 100644 --- a/RIADigiDoc/Domain/NFC/IdCardError.swift +++ b/RIADigiDoc/Domain/NFC/IdCardError.swift @@ -26,6 +26,7 @@ public enum IdCardError: Error { case invalidNewPIN case sessionError case pinLocked + case notActivated } extension IdCardError { diff --git a/RIADigiDoc/Domain/NFC/IdCardInternalError.swift b/RIADigiDoc/Domain/NFC/IdCardInternalError.swift index 459f2112..9fcd16f4 100644 --- a/RIADigiDoc/Domain/NFC/IdCardInternalError.swift +++ b/RIADigiDoc/Domain/NFC/IdCardInternalError.swift @@ -49,6 +49,7 @@ public enum IdCardInternalError: Error { case failedToRemovePadding case notSupportedAlgorithm case pinLocked + case notActivated public func getIdCardError() -> IdCardError { switch self { @@ -60,6 +61,8 @@ public enum IdCardInternalError: Error { return .wrongPIN(triesLeft: 0) case .pinLocked: return .pinLocked + case .notActivated: + return .notActivated case .cancelledByUser: return .cancelledByUser case .invalidNewPin: diff --git a/RIADigiDoc/Domain/NFC/OperationDecrypt.swift b/RIADigiDoc/Domain/NFC/OperationDecrypt.swift index 418c812c..da1d1851 100644 --- a/RIADigiDoc/Domain/NFC/OperationDecrypt.swift +++ b/RIADigiDoc/Domain/NFC/OperationDecrypt.swift @@ -107,11 +107,14 @@ public class OperationDecrypt: NFCOperationBase, OperationDecryptProtocol { let cardCommands = try await connection.getCardCommands(session, tag: tag, CAN: canNumber) updateAlertMessage(step: 3) - let (retryCount, _) = try await cardCommands.readCodeTryCounterRecord(.pin1) + let (retryCount, pin1Active) = try await cardCommands.readCodeTryCounterRecord(.pin1) if retryCount == 0 { throw IdCardInternalError.remainingPinRetryCount(Int(retryCount)) } + if !pin1Active { + throw IdCardInternalError.notActivated + } let cert = try await cardCommands.readAuthenticationCertificate() updateAlertMessage(step: 4) diff --git a/RIADigiDoc/Domain/NFC/OperationReadCertAndSign.swift b/RIADigiDoc/Domain/NFC/OperationReadCertAndSign.swift index 00a9fe86..07f60a9c 100644 --- a/RIADigiDoc/Domain/NFC/OperationReadCertAndSign.swift +++ b/RIADigiDoc/Domain/NFC/OperationReadCertAndSign.swift @@ -124,6 +124,11 @@ public class OperationReadCertAndSign: NFCOperationBase, OperationReadCertAndSig updateAlertMessage(step: 3) + let (_, pin1Active) = try await cardCommands.readCodeTryCounterRecord(.pin1) + if !pin1Active { + throw IdCardInternalError.notActivated + } + let (retryCount, pinActive) = try await cardCommands.readCodeTryCounterRecord(.pin2) if retryCount == 0 { diff --git a/RIADigiDoc/Supporting files/Localizable.xcstrings b/RIADigiDoc/Supporting files/Localizable.xcstrings index 03b639f8..3d97be1a 100644 --- a/RIADigiDoc/Supporting files/Localizable.xcstrings +++ b/RIADigiDoc/Supporting files/Localizable.xcstrings @@ -8219,6 +8219,96 @@ } } } + }, + "ID card courier activate URL" : { + "comment" : "Courier (unactivated) ID-card activation URL", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://www.politsei.ee/en/self-service-portal/" + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://www.politsei.ee/et/iseteenindus/" + } + } + } + }, + "ID card courier activate button" : { + "comment" : "Courier (unactivated) ID-card activation link text", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activate ID-card" + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiveeri ID-kaart" + } + } + } + }, + "ID card courier must activate to decrypt" : { + "comment" : "Courier (unactivated) ID-card message shown during decryption", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The ID-card must be activated in order to decrypt." + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dekrüpteerimiseks tuleb ID-kaart aktiveerida." + } + } + } + }, + "ID card courier must activate to sign" : { + "comment" : "Courier (unactivated) ID-card message shown during signing", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The ID-card must be activated in order to sign." + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allkirjastamiseks tuleb ID-kaart aktiveerida." + } + } + } + }, + "ID card courier warning message" : { + "comment" : "Courier (unactivated) ID-card warning shown in My eID", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authentication and signing with the ID-card isn't possible yet. ID-card must be activated in the Police and Border Guard Board's self-service portal in order to use it." + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID-kaardiga isikutuvastamine ja allkirjastamine ei ole veel võimalik. ID-kaardi kasutamiseks tuleb see aktiveerida Politsei- ja Piirivalveameti iseteeninduses." + } + } + } } }, "version" : "1.1" diff --git a/RIADigiDoc/UI/Component/Container/Crypto/Recipient/EncryptRecipientView.swift b/RIADigiDoc/UI/Component/Container/Crypto/Recipient/EncryptRecipientView.swift index 564e9df9..33343c99 100644 --- a/RIADigiDoc/UI/Component/Container/Crypto/Recipient/EncryptRecipientView.swift +++ b/RIADigiDoc/UI/Component/Container/Crypto/Recipient/EncryptRecipientView.swift @@ -343,7 +343,9 @@ struct EncryptRecipientView: View { } .onChange(of: viewModel.successMessage) { _, message in guard let message, !message.key.isEmpty else { return } - let localizedMessage = languageSettings.localized(message.key, [message.args.joined(separator: ", ")]) + let localizedMessage = languageSettings.localized( + message.key, [message.args.joined(separator: ", ")] + ) Toast.show(localizedMessage, type: .success) if voiceOverEnabled { diff --git a/RIADigiDoc/UI/Component/CryptoFileOpeningView.swift b/RIADigiDoc/UI/Component/CryptoFileOpeningView.swift index aa456aa4..61703346 100644 --- a/RIADigiDoc/UI/Component/CryptoFileOpeningView.swift +++ b/RIADigiDoc/UI/Component/CryptoFileOpeningView.swift @@ -95,7 +95,10 @@ struct CryptoFileOpeningView: View { } } } else { - let localizedMessage = languageSettings.localized(errorMessage?.key ?? "General error", errorMessage?.args ?? []) + let localizedMessage = languageSettings.localized( + errorMessage?.key ?? "General error", errorMessage?.args ?? [] + ) + Toast.show(localizedMessage) if voiceOverEnabled { diff --git a/RIADigiDoc/UI/Component/FileOpeningView.swift b/RIADigiDoc/UI/Component/FileOpeningView.swift index 115c5db1..7fdc74cd 100644 --- a/RIADigiDoc/UI/Component/FileOpeningView.swift +++ b/RIADigiDoc/UI/Component/FileOpeningView.swift @@ -136,7 +136,10 @@ struct FileOpeningView: View { } } } else { - let localizedMessage = languageSettings.localized(errorMessage?.key ?? "General error", errorMessage?.args ?? []) + let localizedMessage = languageSettings.localized( + errorMessage?.key ?? "General error", errorMessage?.args ?? [] + ) + Toast.show(localizedMessage) if voiceOverEnabled { diff --git a/RIADigiDoc/UI/Component/My eID/MyEidCertificateCardView.swift b/RIADigiDoc/UI/Component/My eID/MyEidCertificateCardView.swift index 17033a3e..6a454f7a 100644 --- a/RIADigiDoc/UI/Component/My eID/MyEidCertificateCardView.swift +++ b/RIADigiDoc/UI/Component/My eID/MyEidCertificateCardView.swift @@ -30,6 +30,7 @@ struct MyEidCertificateCardView: View { let changePinText: String let isPinBlocked: Bool let isPukBlocked: Bool + let isNotActivated: Bool let showForgotPin: Bool let onForgotPinClick: (() -> Void)? let onChangePinClick: (() -> Void)? @@ -45,6 +46,7 @@ struct MyEidCertificateCardView: View { changePinText: String = "", isPinBlocked: Bool = false, isPukBlocked: Bool = false, + isNotActivated: Bool = false, showForgotPin: Bool = true, onForgotPinClick: (() -> Void)? = nil, onChangePinClick: (() -> Void)? = nil, @@ -58,6 +60,7 @@ struct MyEidCertificateCardView: View { self.changePinText = changePinText self.isPinBlocked = isPinBlocked self.isPukBlocked = isPukBlocked + self.isNotActivated = isNotActivated self.showForgotPin = showForgotPin self.onForgotPinClick = onForgotPinClick self.onChangePinClick = onChangePinClick @@ -107,7 +110,7 @@ struct MyEidCertificateCardView: View { PrimaryOutlinedButton( text: forgotPinText, assetImageName: nil, - isButtonEnabled: !isPukBlocked, + isButtonEnabled: !isPukBlocked && !isNotActivated, action: onForgotPinClick ?? {}, focusedField: forgotPinAccessibilityField, currentFocus: $lastFocused @@ -116,7 +119,7 @@ struct MyEidCertificateCardView: View { PrimaryButton( text: changePinText, - isButtonEnabled: !isPinBlocked, + isButtonEnabled: !isPinBlocked && !isNotActivated, action: onChangePinClick ?? {}, focusedField: changePinAccessibilityField, currentFocus: $lastFocused diff --git a/RIADigiDoc/UI/Component/My eID/MyEidPinsAndCertificatesView.swift b/RIADigiDoc/UI/Component/My eID/MyEidPinsAndCertificatesView.swift index 9cb5b1ac..84d974be 100644 --- a/RIADigiDoc/UI/Component/My eID/MyEidPinsAndCertificatesView.swift +++ b/RIADigiDoc/UI/Component/My eID/MyEidPinsAndCertificatesView.swift @@ -34,6 +34,7 @@ struct MyEidPinsAndCertificatesView: View { var authCertValidTo: String var signCertValidTo: String var isPUKChangeable: Bool + var isCourierCard: Bool @AccessibilityFocusState private var accessibilityFocus: AccessibilityField? @State private var lastFocused: AccessibilityField? @@ -97,7 +98,8 @@ struct MyEidPinsAndCertificatesView: View { pinChangeVariant: Binding = .constant(nil), authCertValidTo: String, signCertValidTo: String, - isPUKChangeable: Bool + isPUKChangeable: Bool, + isCourierCard: Bool = false ) { self._isPin1Blocked = isPin1Blocked self._isPin2Blocked = isPin2Blocked @@ -107,6 +109,7 @@ struct MyEidPinsAndCertificatesView: View { self.authCertValidTo = authCertValidTo self.signCertValidTo = signCertValidTo self.isPUKChangeable = isPUKChangeable + self.isCourierCard = isCourierCard } var body: some View { @@ -122,6 +125,7 @@ struct MyEidPinsAndCertificatesView: View { .localized("Change PIN", [CodeType.pin1.name]), isPinBlocked: isPin1Blocked, isPukBlocked: isPukBlocked, + isNotActivated: isCourierCard, onForgotPinClick: { lastFocused = .myEid(.unblockPin1Button) pinChangeVariant = .pin1Unblock @@ -142,6 +146,10 @@ struct MyEidPinsAndCertificatesView: View { .foregroundStyle(theme.error) .padding(.vertical, Dimensions.Padding.XSPadding) } + + if isCourierCard { + courierCardWarning() + } } .padding(.vertical, Dimensions.Padding.SPadding) @@ -161,6 +169,7 @@ struct MyEidPinsAndCertificatesView: View { .localized("Change PIN", [CodeType.pin2.name]), isPinBlocked: isPin2Blocked, isPukBlocked: isPukBlocked, + isNotActivated: isCourierCard, onForgotPinClick: { lastFocused = .myEid(.unblockPin2Button) pinChangeVariant = .pin2Unblock @@ -182,7 +191,9 @@ struct MyEidPinsAndCertificatesView: View { .padding(.vertical, Dimensions.Padding.XSPadding) } - if !isPin2Activated { + if isCourierCard { + courierCardWarning() + } else if !isPin2Activated { Text(verbatim: pin2LockedMessage) .font(typography.bodySmall) .foregroundStyle(theme.error) @@ -238,11 +249,11 @@ struct MyEidPinsAndCertificatesView: View { } private var opacityForPin1BlockedState: Double { - getOpacityForBlockedState(isBlocked: isPin1Blocked && isPukBlocked) + getOpacityForBlockedState(isBlocked: (isPin1Blocked && isPukBlocked) || isCourierCard) } private var opacityForPin2BlockedState: Double { - getOpacityForBlockedState(isBlocked: isPin2Blocked && isPukBlocked) + getOpacityForBlockedState(isBlocked: (isPin2Blocked && isPukBlocked) || isCourierCard) } private var opacityForPukBlockedState: Double { @@ -292,4 +303,31 @@ struct MyEidPinsAndCertificatesView: View { .font(typography.bodySmall) .foregroundStyle(.link) } + + private var courierWarningMessage: String { + languageSettings.localized("ID card courier warning message") + } + + private var courierActivateUrl: String { + languageSettings.localized("ID card courier activate URL") + } + + @ViewBuilder + private func courierCardWarning() -> some View { + Text(verbatim: courierWarningMessage) + .font(typography.bodySmall) + .foregroundStyle(theme.error) + .padding(.vertical, Dimensions.Padding.XSPadding) + + if let url = URL(string: courierActivateUrl) { + Link( + languageSettings.localized("ID card courier activate button"), + destination: url + ) + .underline() + .font(typography.bodySmall) + .foregroundStyle(.link) + .padding(.vertical, Dimensions.Padding.XSPadding) + } + } } diff --git a/RIADigiDoc/UI/Component/My eID/MyEidView.swift b/RIADigiDoc/UI/Component/My eID/MyEidView.swift index 3681a98e..85766153 100644 --- a/RIADigiDoc/UI/Component/My eID/MyEidView.swift +++ b/RIADigiDoc/UI/Component/My eID/MyEidView.swift @@ -48,6 +48,10 @@ struct MyEidView: View { private let idCardData: IdCardData private let actionMethod: ActionMethod + private var isCourierCard: Bool { + idCardData.pinResponse.isCourierCard + } + private var myDataTitle: String { languageSettings.localized("My data") } @@ -135,7 +139,8 @@ struct MyEidView: View { pinChangeVariant: $pinChangeVariant, authCertValidTo: idCardData.authCertNotValidDate ?? "", signCertValidTo: idCardData.signCertNotValidDate ?? "", - isPUKChangeable: idCardData.isPUKChangeable + isPUKChangeable: idCardData.isPUKChangeable, + isCourierCard: isCourierCard ) } } diff --git a/RIADigiDoc/ViewModel/Signing/NFC/NFCViewModel.swift b/RIADigiDoc/ViewModel/Signing/NFC/NFCViewModel.swift index bd537e1c..f19a37de 100644 --- a/RIADigiDoc/ViewModel/Signing/NFC/NFCViewModel.swift +++ b/RIADigiDoc/ViewModel/Signing/NFC/NFCViewModel.swift @@ -273,6 +273,12 @@ class NFCViewModel: NFCViewModelProtocol, Loggable { showNfcAlertMessage = true nfcAlertMessageKey = "PIN2 locked" nfcAlertMessageUrl = "PIN2 locked URL" + case .notActivated: + showNfcAlertMessage = true + nfcAlertMessageKey = pinType == .pin2 + ? "ID card courier must activate to sign" + : "ID card courier must activate to decrypt" + nfcAlertMessageUrl = "ID card courier activate URL" case .wrongCAN: nfcErrorKey = "Wrong CAN" nfcErrorExtraArguments = [] diff --git a/RIADigiDocTests/ViewModel/Signing/NFC/NFCViewModelTests.swift b/RIADigiDocTests/ViewModel/Signing/NFC/NFCViewModelTests.swift index f844d052..63857ed2 100644 --- a/RIADigiDocTests/ViewModel/Signing/NFC/NFCViewModelTests.swift +++ b/RIADigiDocTests/ViewModel/Signing/NFC/NFCViewModelTests.swift @@ -511,6 +511,54 @@ final class NFCViewModelTests { } } + @Test + func sign_showsCourierAlertWhenCardNotActivated() async { + let mockContainer = SignedContainerProtocolMock() + + mockContainer.getRawContainerFileHandler = { + URL(fileURLWithPath: "/test/container.asice") + } + mockDataStore.getSelectedLanguageHandler = { "et" } + mockUserAgentUtil.userAgentHandler = { _, _ in "TestUserAgent" } + + mockOperationReadCertAndSign.startOperationHandler = + { _, _, _, _, _, _, _ in + throw IdCardInternalError.notActivated + } + + let result = await viewModel.sign( + canNumber: "123456", + pin2: "12345", + roleData: RoleData(roles: [], city: "", state: "", country: "", zipCode: ""), + signedContainer: mockContainer, + strings: mockNFCSessionStrings + ) + + #expect(result == nil) + #expect(viewModel.showNfcAlertMessage) + #expect(viewModel.nfcAlertMessageKey == "ID card courier must activate to sign") + #expect(viewModel.nfcAlertMessageUrl == "ID card courier activate URL") + } + + @Test + func decrypt_showsCourierAlertWhenCardNotActivated() async { + mockOperationDecrypt.processDecryptHandler = { _, _, _, _, _ in + throw IdCardInternalError.notActivated + } + + let result = await viewModel.decrypt( + CAN: "123456", + pin1: "1234", + cryptoContainer: nil, + strings: mockNFCSessionStrings + ) + + #expect(result == nil) + #expect(viewModel.showNfcAlertMessage) + #expect(viewModel.nfcAlertMessageKey == "ID card courier must activate to decrypt") + #expect(viewModel.nfcAlertMessageUrl == "ID card courier activate URL") + } + // MARK: - readCardData tests @Test From e2fa04cd7ad649f45e85feea980fd2eb46bddb70 Mon Sep 17 00:00:00 2001 From: Marten Rebane Date: Fri, 3 Jul 2026 16:21:27 +0300 Subject: [PATCH 2/2] Temporarily make cards courier cards for testing --- RIADigiDoc/Domain/NFC/OperationDecrypt.swift | 2 +- RIADigiDoc/Domain/NFC/OperationReadCertAndSign.swift | 2 +- .../UI/Component/My eID/MyEidPinsAndCertificatesView.swift | 6 +++--- RIADigiDoc/UI/Component/My eID/MyEidView.swift | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/RIADigiDoc/Domain/NFC/OperationDecrypt.swift b/RIADigiDoc/Domain/NFC/OperationDecrypt.swift index da1d1851..6750d4e2 100644 --- a/RIADigiDoc/Domain/NFC/OperationDecrypt.swift +++ b/RIADigiDoc/Domain/NFC/OperationDecrypt.swift @@ -112,7 +112,7 @@ public class OperationDecrypt: NFCOperationBase, OperationDecryptProtocol { if retryCount == 0 { throw IdCardInternalError.remainingPinRetryCount(Int(retryCount)) } - if !pin1Active { + if pin1Active { throw IdCardInternalError.notActivated } diff --git a/RIADigiDoc/Domain/NFC/OperationReadCertAndSign.swift b/RIADigiDoc/Domain/NFC/OperationReadCertAndSign.swift index 07f60a9c..f3158b8a 100644 --- a/RIADigiDoc/Domain/NFC/OperationReadCertAndSign.swift +++ b/RIADigiDoc/Domain/NFC/OperationReadCertAndSign.swift @@ -125,7 +125,7 @@ public class OperationReadCertAndSign: NFCOperationBase, OperationReadCertAndSig updateAlertMessage(step: 3) let (_, pin1Active) = try await cardCommands.readCodeTryCounterRecord(.pin1) - if !pin1Active { + if pin1Active { throw IdCardInternalError.notActivated } diff --git a/RIADigiDoc/UI/Component/My eID/MyEidPinsAndCertificatesView.swift b/RIADigiDoc/UI/Component/My eID/MyEidPinsAndCertificatesView.swift index 84d974be..b3341e6d 100644 --- a/RIADigiDoc/UI/Component/My eID/MyEidPinsAndCertificatesView.swift +++ b/RIADigiDoc/UI/Component/My eID/MyEidPinsAndCertificatesView.swift @@ -99,7 +99,7 @@ struct MyEidPinsAndCertificatesView: View { authCertValidTo: String, signCertValidTo: String, isPUKChangeable: Bool, - isCourierCard: Bool = false + isCourierCard: Bool = true ) { self._isPin1Blocked = isPin1Blocked self._isPin2Blocked = isPin2Blocked @@ -147,7 +147,7 @@ struct MyEidPinsAndCertificatesView: View { .padding(.vertical, Dimensions.Padding.XSPadding) } - if isCourierCard { + if !isCourierCard { courierCardWarning() } } @@ -191,7 +191,7 @@ struct MyEidPinsAndCertificatesView: View { .padding(.vertical, Dimensions.Padding.XSPadding) } - if isCourierCard { + if !isCourierCard { courierCardWarning() } else if !isPin2Activated { Text(verbatim: pin2LockedMessage) diff --git a/RIADigiDoc/UI/Component/My eID/MyEidView.swift b/RIADigiDoc/UI/Component/My eID/MyEidView.swift index 85766153..36dfacca 100644 --- a/RIADigiDoc/UI/Component/My eID/MyEidView.swift +++ b/RIADigiDoc/UI/Component/My eID/MyEidView.swift @@ -49,7 +49,7 @@ struct MyEidView: View { private let actionMethod: ActionMethod private var isCourierCard: Bool { - idCardData.pinResponse.isCourierCard + idCardData.pinResponse.isCourierCard == true } private var myDataTitle: String {