Skip to content
Merged
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
1 change: 1 addition & 0 deletions Modules/CommonsLib/Sources/CommonsLib/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public struct Constants {
public struct File {
public static let LibDigidocLog = "libdigidocpp.log"
public static let LDAPCertsPem = "ldapCerts.pem"
public static let nfcCANKey = "canKey.txt"
}

public struct FileBaseName {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ extension CardCommandsInternal {
return
case 0x6A80: // New pin is invalid
throw IdCardInternalError.invalidNewPin
case 0x63C0, 0x6983: // Authentication method blocked
case 0x63C0, 0x6983, 0x6984: // Authentication method blocked
throw IdCardInternalError.pinVerificationFailed
// For pin codes this means verification failed due to wrong pin
case let uInt16 where (uInt16 & 0xFFF0) == 0x63C0:
Expand Down
54 changes: 43 additions & 11 deletions Modules/IdCardLib/Sources/IdCardLib/CardActions/CardReaderNFC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,19 +234,38 @@ class CardReaderNFC: @unchecked CardReader, Loggable {
}
return (tlvEnc, tlvRes, tlvMac)
}

// swiftlint:disable cyclomatic_complexity
func transmit(_ apduData: Bytes) async throws -> (responseData: Bytes, sw: UInt16) {
CardReaderNFC.logger().info("Plain >: \(apduData.toHex)")
guard let apdu = NFCISO7816APDU(data: Data(apduData)) else {
throw IdCardInternalError.invalidAPDU
}
_ = SSC.increment()
let DO87 = try getDO87(apdu)
let DO97 = try getDO97(apdu)
let DO87: Data
if let data = apdu.data, !data.isEmpty {
let ivValue = try AES.CBC(key: ksEnc).encrypt(SSC)
let encData = try AES.CBC(key: ksEnc, ivVal: ivValue).encrypt(data.addPadding())
if apdu.instructionCode & 0x01 == 0 {
DO87 = TLV(tag: 0x87, bytes: [0x01] + encData).data
} else {
DO87 = TLV(tag: 0x85, bytes: encData).data
}
} else {
DO87 = Data()
}
let DO97: Data
if apdu.expectedResponseLength > 0 {
DO97 = TLV(
tag: 0x97,
bytes: [UInt8(apdu.expectedResponseLength == 256 ? 0 : apdu.expectedResponseLength)]
).data
} else {
DO97 = Data()
}
let cmdHeader: Bytes = [apdu.instructionClass | 0x0C, apdu.instructionCode, apdu.p1Parameter, apdu.p2Parameter]
let MValue = cmdHeader.addPadding() + DO87 + DO97
let NValue = SSC + MValue
let mac = try AES.CMAC(key: ksMac).authenticate(bytes: NValue.addPadding())
let mVal = cmdHeader.addPadding() + DO87 + DO97
let nVal = SSC + mVal
let mac = try AES.CMAC(key: ksMac).authenticate(bytes: nVal.addPadding())
let DO8E = TLV(tag: 0x8E, bytes: mac).data
let send = DO87 + DO97 + DO8E
let response = try await tag.sendCommand(
Expand All @@ -257,26 +276,39 @@ class CardReaderNFC: @unchecked CardReader, Loggable {
data: send,
leByte: 256
)
let (tlvEnc, tlvRes, tlvMac) = try getTLVs(response)
var tlvEnc: TKTLVRecord?
var tlvRes: TKTLVRecord?
var tlvMac: TKTLVRecord?
for tlv in TLV.sequenceOfRecords(from: response) ?? [] {
switch tlv.tag {
case 0x85, 0x87: tlvEnc = tlv
case 0x99: tlvRes = tlv
case 0x8E: tlvMac = tlv
default: print("Unknown tag")
}
}
guard let tlvRes else {
throw IdCardInternalError.missingRESTag
}
guard let tlvMac else {
throw IdCardInternalError.missingMACTag
}
let KValue = SSC.increment() + (tlvEnc?.data ?? Data()) + tlvRes.data
if try Data(AES.CMAC(key: ksMac).authenticate(bytes: KValue.addPadding())) != tlvMac.value {
let kVal = SSC.increment() + (tlvEnc?.data ?? Data()) + tlvRes.data
if try Data(AES.CMAC(key: ksMac).authenticate(bytes: kVal.addPadding())) != tlvMac.value {
throw IdCardInternalError.invalidMACValue
}
guard let tlvEnc else {
CardReaderNFC.logger().info("Plain <: \(tlvRes.value.toHex)")
return (.init(), UInt16(tlvRes.value[0], tlvRes.value[1]))
}
let ivValue = try AES.CBC(key: ksEnc).encrypt(SSC)
let responseData = try (try AES.CBC(key: ksEnc, ivVal: ivValue).decrypt(tlvEnc.value[1...])).removePadding()
CardReaderNFC.logger().info("Plain <: \(responseData.toHex) \(tlvRes.value.toHex)")
let responseData = try (try AES.CBC(key: ksEnc, ivVal: ivValue)
.decrypt(tlvEnc.tag == 0x85 ? tlvEnc.value : tlvEnc.value[1...]))
.removePadding()
CardReaderNFC.logger().debug("Plain <: \(responseData.toHex) \(tlvRes.value.toHex)")
return (Bytes(responseData), UInt16(tlvRes.value[0], tlvRes.value[1]))
}
// swiftlint:enable cyclomatic_complexity

// MARK: - Utils

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ final class Thales: CardCommandsInternal {
guard type != .puk else {
throw IdCardInternalError.notSupportedCodeType
}
_ = try await select(file: Thales.kAID)
try await unblockCode(type.pinRef, puk: puk, newCode: newCode)
}

Expand Down

This file was deleted.

15 changes: 13 additions & 2 deletions RIADigiDoc.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -160,19 +160,24 @@
Domain/Model/EncryptionServerInfo.swift,
Domain/Model/EncryptionServerOption.swift,
Domain/Model/EncryptionServerOptionId.swift,
Domain/Model/Error/DecryptError.swift,
Domain/Model/Error/Encryption/EncryptedDataError.swift,
Domain/Model/Error/FileOpeningErrors.swift,
"Domain/Model/Error/My eID/MyEidCodeChangeError.swift",
Domain/Model/Error/ReadCertAndSignError.swift,
Domain/Model/Error/NFC/ChangePinError.swift,
Domain/Model/Error/NFC/DecryptError.swift,
Domain/Model/Error/NFC/ReadCertAndSignError.swift,
Domain/Model/Error/NFC/UnblockPINError.swift,
Domain/Model/FileItem.swift,
Domain/Model/IdCard/IdCardData.swift,
Domain/Model/IdCard/RetryCount.swift,
Domain/Model/KeychainKey.swift,
"Domain/Model/My eID/MyEidDocumentStatus.swift",
"Domain/Model/My eID/MyEidPinCodeAction.swift",
"Domain/Model/My eID/MyEidPinCodeStep.swift",
Domain/Model/NFC/NFCCardData.swift,
Domain/Model/Notification/ContainerNotificationType.swift,
Domain/Model/SettingsMenuBottomSheetPages.swift,
Domain/Model/Signing/ActionType.swift,
Domain/Model/Signing/MobileId/MobileIdInputData.swift,
Domain/Model/Signing/NFC/NFCInputData.swift,
Domain/Model/Signing/SigningMethod.swift,
Expand All @@ -182,9 +187,14 @@
Domain/Model/SupportedLanguage.swift,
Domain/Model/SupportedTheme.swift,
Domain/Model/ToastMessage.swift,
Domain/NFC/NFCOperationBase.swift,
Domain/NFC/NFCSessionStrings.swift,
Domain/NFC/NFCSessionStringsUtil.swift,
Domain/NFC/OperationChangePin.swift,
Domain/NFC/OperationDecrypt.swift,
Domain/NFC/OperationReadCardData.swift,
Domain/NFC/OperationReadCertAndSign.swift,
Domain/NFC/OperationUnblockPin.swift,
Domain/Preferences/DataStore.swift,
Domain/Preferences/DataStoreProtocol.swift,
Domain/Preferences/KeychainStore.swift,
Expand All @@ -211,6 +221,7 @@
UI/Theme/Theme.swift,
Util/Certificate/CertificateUtil.swift,
Util/Certificate/CertificateUtilProtocol.swift,
Util/EncryptedData/EncryptedDataUtil.swift,
Util/File/FileInspector.swift,
Util/File/FileInspectorProtocol.swift,
Util/Language/LanguageSettings.swift,
Expand Down
23 changes: 20 additions & 3 deletions RIADigiDoc/DI/AppContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,10 @@ extension Container {
self { @MainActor in
NFCViewModel(
dataStore: self.dataStore(),
userAgentUtil: self.userAgentUtil()
userAgentUtil: self.userAgentUtil(),
certificateUtil: self.certificateUtil(),
sharedMyEidSession: self.sharedMyEidSession(),
keychainStore: self.keychainStore()
)
}
}
Expand All @@ -523,19 +526,33 @@ extension Container {
}
}

// swiftlint:disable closure_parameter_position
// swiftlint:disable large_tuple
@MainActor
var myEidPinChangeViewModel: ParameterFactory<(MyEidPinCodeAction, CodeType, String), MyEidPinChangeViewModel> {
self { @MainActor (pinAction: MyEidPinCodeAction, codeType: CodeType, personalCode: String
var myEidPinChangeViewModel: ParameterFactory<(
MyEidPinCodeAction,
CodeType,
String,
ActionMethod
), MyEidPinChangeViewModel> {
self { @MainActor (
pinAction: MyEidPinCodeAction,
codeType: CodeType,
personalCode: String,
actionMethod: ActionMethod
) -> MyEidPinChangeViewModel in
MyEidPinChangeViewModel(
pinAction: pinAction,
codeType: codeType,
personalCode: personalCode,
actionMethod: actionMethod,
idCardRepository: self.idCardRepository(),
sharedMyEidSession: self.sharedMyEidSession()
)
}
}
// swiftlint:enable closure_parameter_position
// swiftlint:enable large_tuple

@MainActor
var crashReportManager: Factory<CrashReportManager> {
Expand Down
40 changes: 40 additions & 0 deletions RIADigiDoc/Domain/Model/Error/Encryption/EncryptedDataError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2017 - 2025 Riigi Infosüsteemi Amet
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/

import Foundation

public enum EncryptedDataError: Error, LocalizedError {
case unableToLocateAppSupportDirectory
case keyFileDoesNotExist
case encryptionFailed
case decryptionFailed

public var errorDescription: String? {
switch self {
case .unableToLocateAppSupportDirectory:
return "Unable to locate Application Support directory"
case .keyFileDoesNotExist:
return "Encryption key file does not exist"
case .encryptionFailed:
return "Failed to encrypt data"
case .decryptionFailed:
return "Failed to decrypt data"
}
}
}
39 changes: 39 additions & 0 deletions RIADigiDoc/Domain/Model/Error/NFC/ChangePinError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2017 - 2025 Riigi Infosüsteemi Amet
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/

import Foundation

public enum ChangePinError: Error {
case missingRequiredParameter
case cancelled
case unknown(Error)
}

extension ChangePinError: LocalizedError {
public var errorDescription: String? {
switch self {
case .missingRequiredParameter:
return "Missing required parameter"
case .cancelled:
return "Operation cancelled by user"
case .unknown(let error):
return "Unknown error: \(error.localizedDescription)"
}
}
}
Loading