diff --git a/.gitignore b/.gitignore index e4c2d26..89e0687 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ timeline.xctimeline playground.xcworkspace .DS_Store + +## Swift Package Manager +.swiftpm/ diff --git a/Package.swift b/Package.swift index 69432d4..9dee966 100644 --- a/Package.swift +++ b/Package.swift @@ -22,6 +22,5 @@ let package = Package( .product(name: "SwiftECC", package: "SwiftECC") ] ) - ], - swiftLanguageModes: [.v5] + ] ) diff --git a/Sources/nfclib/CardActions/CardCommands.swift b/Sources/nfclib/CardActions/CardCommands.swift index 7308f03..ee7baff 100644 --- a/Sources/nfclib/CardActions/CardCommands.swift +++ b/Sources/nfclib/CardActions/CardCommands.swift @@ -125,7 +125,7 @@ public protocol CardCommands: Sendable { * - Throws: An error if the operation fails. * - Returns: The authentication response as `Data`. */ - func authenticate(for hash: Data, withPin1 pin1: SecureData) async throws -> Data + func authenticate(for hash: any DataType, withPin1 pin1: SecureData) async throws -> Data /** * Calculates a digital signature for the given hash. @@ -136,7 +136,7 @@ public protocol CardCommands: Sendable { * - Throws: An error if the operation fails. * - Returns: The signature as `Data`. */ - func calculateSignature(for hash: Data, withPin2 pin2: SecureData) async throws -> Data + func calculateSignature(for hash: any DataType, withPin2 pin2: SecureData) async throws -> Data /** * Decrypts data using PIN 1. @@ -147,5 +147,5 @@ public protocol CardCommands: Sendable { * - Throws: An error if the operation fails. * - Returns: The decrypted data. */ - func decryptData(_ hash: Data, withPin1 pin1: SecureData) async throws -> Data + func decryptData(_ hash: any DataType, withPin1 pin1: SecureData) async throws -> Data } diff --git a/Sources/nfclib/CardActions/CardReader.swift b/Sources/nfclib/CardActions/CardReader.swift index f7ca6ff..7a660d4 100644 --- a/Sources/nfclib/CardActions/CardReader.swift +++ b/Sources/nfclib/CardActions/CardReader.swift @@ -3,8 +3,6 @@ import Foundation -typealias Bytes = [UInt8] - protocol CardReader: Sendable { /** * Sends an APDU (Application Protocol Data Unit) command to the smart card and retrieves the response. diff --git a/Sources/nfclib/CardActions/CardReaderNFC.swift b/Sources/nfclib/CardActions/CardReaderNFC.swift index 50a81f6..7ff2649 100644 --- a/Sources/nfclib/CardActions/CardReaderNFC.swift +++ b/Sources/nfclib/CardActions/CardReaderNFC.swift @@ -3,7 +3,6 @@ import OSLog import CoreNFC -import CommonCrypto import CryptoTokenKit internal import SwiftECC internal import BigInt @@ -56,8 +55,12 @@ class CardReaderNFC: CardReader { var ksMac: Bytes = AES.Zero var SSC: Bytes = AES.Zero - init(_ tag: NFCISO7816Tag, CAN: String) async throws { - self.tag = SendableISO7816Tag(tag: tag) + init( + _ tag: NFCISO7816Tag, + CAN: String, + invalidationError: @escaping @Sendable @MainActor () -> Error? = { nil } + ) async throws { + self.tag = SendableISO7816Tag(tag: tag, invalidationError: invalidationError) CardReaderNFC.logger.debug("Select CardAccess") _ = try await self.tag.sendCommand(cls: 0x00, ins: 0xA4, p1Byte: 0x02, p2Byte: 0x0C, data: Data([0x01, 0x1C])) @@ -308,8 +311,8 @@ class CardReaderNFC: CardReader { // MARK: - Utils // swiftlint:disable force_unwrapping static private func pseudoRandomNumberMappingAES( - sVal: any AES.DataType, - tVal: any AES.DataType, + sVal: any DataType, + tVal: any DataType, domain: Domain ) throws -> BInt { let lVal = sVal.count * 8 @@ -383,7 +386,7 @@ class CardReaderNFC: CardReader { Point(x3Val, (AVal * UVal).mod(pVal)) } - static private func decryptNonce(CAN: String, encryptedNonce: T) throws -> Bytes { + static private func decryptNonce(CAN: String, encryptedNonce: T) throws -> Bytes { let decryptionKey = KDF(key: Bytes(CAN.utf8), counter: 3) let cipher = AES.CBC(key: decryptionKey) return try cipher.decrypt(encryptedNonce) @@ -392,14 +395,7 @@ class CardReaderNFC: CardReader { static private func KDF(key: Bytes, counter: UInt8) -> Bytes { var keydata = key + Bytes(repeating: 0x00, count: 4) keydata[keydata.count - 1] = counter - return SHA256(data: keydata) - } - - static private func SHA256(data: Bytes) -> Bytes { - Bytes(unsafeUninitializedCapacity: Int(CC_SHA256_DIGEST_LENGTH)) { buffer, initializedCount in - CC_SHA256(data, CC_LONG(data.count), buffer.baseAddress) - initializedCount = Int(CC_SHA256_DIGEST_LENGTH) - } + return sha(hashLength: .bits256, data: keydata) } static private func random(count: Int) throws -> Data { diff --git a/Sources/nfclib/CardActions/Idemia.swift b/Sources/nfclib/CardActions/Idemia.swift index 89c1c06..6613459 100644 --- a/Sources/nfclib/CardActions/Idemia.swift +++ b/Sources/nfclib/CardActions/Idemia.swift @@ -132,25 +132,25 @@ final class Idemia: CardCommandsInternal { // MARK: - Authentication & Signing - func authenticate(for hash: Data, withPin1 pin1: SecureData) async throws -> Data { + func authenticate(for hash: any DataType, withPin1 pin1: SecureData) async throws -> Data { _ = try await select(file: kAIDOberthur) try await verifyCode(.pin1, code: pin1) try await setSecEnv(mode: 0xA4, algo: [0xFF, 0x20, 0x08, 0x00], keyRef: AUTHKEY) var paddedHash = Data(repeating: 0x00, count: max(48, hash.count) - hash.count) - paddedHash.append(hash) + paddedHash.append(contentsOf: hash) return try await reader.sendAPDU(ins: 0x88, data: paddedHash, leByte: 0x00) } - func calculateSignature(for hash: Data, withPin2 pin2: SecureData) async throws -> Data { + func calculateSignature(for hash: any DataType, withPin2 pin2: SecureData) async throws -> Data { _ = try await select(file: kAIDQSCD) try await verifyCode(.pin2, code: pin2) try await setSecEnv(mode: 0xB6, algo: [0xFF, 0x15, 0x08, 0x00], keyRef: SIGNKEY) var paddedHash = Data(repeating: 0x00, count: max(48, hash.count) - hash.count) - paddedHash.append(hash) + paddedHash.append(contentsOf: hash) return try await reader.sendAPDU(ins: 0x2A, p1Byte: 0x9E, p2Byte: 0x9A, data: paddedHash, leByte: 0x00) } - func decryptData(_ hash: Data, withPin1 pin1: SecureData) async throws -> Data { + func decryptData(_ hash: any DataType, withPin1 pin1: SecureData) async throws -> Data { _ = try await select(file: kAIDOberthur) try await verifyCode(.pin1, code: pin1) try await setSecEnv(mode: 0xB8, algo: [0xFF, 0x30, 0x04, 0x00], keyRef: AUTHKEY) diff --git a/Sources/nfclib/CardActions/Thales.swift b/Sources/nfclib/CardActions/Thales.swift index 08675a1..0e01e39 100644 --- a/Sources/nfclib/CardActions/Thales.swift +++ b/Sources/nfclib/CardActions/Thales.swift @@ -129,7 +129,7 @@ final class Thales: CardCommandsInternal { // MARK: - Authentication & Signing - private func sign(type: CodeType, pin: SecureData, keyRef: UInt8, hash: Data) async throws -> Data { + private func sign(type: CodeType, pin: SecureData, keyRef: UInt8, hash: any DataType) async throws -> Data { try await verifyCode(type, code: pin) try await setSecEnv(mode: 0xB6, algo: [0x24 + UInt8(hash.count)], keyRef: keyRef) _ = try await reader @@ -147,12 +147,12 @@ final class Thales: CardCommandsInternal { return try await reader.sendAPDU(ins: 0x2A, p1Byte: 0x9E, p2Byte: 0x9A, leByte: 0x00) } - func authenticate(for hash: Data, withPin1 pin1: SecureData) async throws -> Data { + func authenticate(for hash: any DataType, withPin1 pin1: SecureData) async throws -> Data { _ = try await select(file: Thales.kAID) return try await sign(type: .pin1, pin: pin1, keyRef: Thales.AUTHKEY, hash: hash) } - func calculateSignature(for hash: Data, withPin2 pin2: SecureData) async throws -> Data { + func calculateSignature(for hash: any DataType, withPin2 pin2: SecureData) async throws -> Data { let (_, pin2Changed) = try await readCodeTryCounterRecord(.pin2) guard pin2Changed else { throw IdCardInternalError.notActivated } @@ -160,7 +160,7 @@ final class Thales: CardCommandsInternal { return try await sign(type: .pin2, pin: pin2, keyRef: Thales.SIGNKEY, hash: hash) } - func decryptData(_ hash: Data, withPin1 pin1: SecureData) async throws -> Data { + func decryptData(_ hash: any DataType, withPin1 pin1: SecureData) async throws -> Data { _ = try await select(file: Thales.kAID) try await verifyCode(.pin1, code: pin1) try await setSecEnv(mode: 0xB8, keyRef: Thales.AUTHKEY) diff --git a/Sources/nfclib/Extensions/NFCISO7816Tag+Extensions.swift b/Sources/nfclib/Extensions/NFCISO7816Tag+Extensions.swift index fb0fd69..3a15bcc 100644 --- a/Sources/nfclib/Extensions/NFCISO7816Tag+Extensions.swift +++ b/Sources/nfclib/Extensions/NFCISO7816Tag+Extensions.swift @@ -8,6 +8,8 @@ import CryptoTokenKit @MainActor struct SendableISO7816Tag { var tag: NFCISO7816Tag + /// Why the reader session ended, if it has. Reported instead of the transceive error it causes. + var invalidationError: @Sendable @MainActor () -> Error? = { nil } } extension SendableISO7816Tag { @@ -28,14 +30,19 @@ extension SendableISO7816Tag { data: data, expectedResponseLength: leByte ) - let result: (Data, UInt8, UInt8) = try await withCheckedThrowingContinuation { continuation in - tag.sendCommand(apdu: apdu) { responseData, sw1, sw2, error in - if let error { - continuation.resume(throwing: error) - } else { - continuation.resume(returning: (responseData, sw1, sw2)) + let result: (Data, UInt8, UInt8) + do { + result = try await withCheckedThrowingContinuation { continuation in + tag.sendCommand(apdu: apdu) { responseData, sw1, sw2, error in + if let error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: (responseData, sw1, sw2)) + } } } + } catch { + throw invalidationError() ?? error } switch result { case (_, 0x63, 0x00): diff --git a/Sources/nfclib/Operations/NFCConnection.swift b/Sources/nfclib/Operations/NFCConnection.swift index 203aa4e..f77ac49 100644 --- a/Sources/nfclib/Operations/NFCConnection.swift +++ b/Sources/nfclib/Operations/NFCConnection.swift @@ -4,51 +4,89 @@ import Foundation @preconcurrency import CoreNFC +// Allow CoreNFC types to cross async boundaries in this controlled context +extension NFCTagReaderSession: @unchecked @retroactive Sendable {} +extension NFCTag: @unchecked @retroactive Sendable {} + @MainActor -public class NFCConnection { - public init() {} - public func setup(_ session: NFCTagReaderSession, tags: [NFCTag]) async throws -> NFCISO7816Tag { - if tags.count > 1 { - session.invalidate(errorMessage: "Failed to read data") - throw IdCardInternalError.multipleTagsDetected - } +public class NFCConnection: NSObject, @MainActor NFCTagReaderSessionDelegate { + private var session: NFCTagReaderSession? + private var continuation: CheckedContinuation? + private var invalidationError: Error? - guard let firstTag = tags.first else { - session.invalidate(errorMessage: "Failed to read data") - throw IdCardInternalError.invalidTag - } + override private init() {} + /// Runs an operation on a new connection. On success the session is closed with `successMessage`, + /// on failure `onError` maps the error to the error that is thrown and the message the session is + /// invalidated with. Cancelling the calling task invalidates the session, and any failure after + /// cancellation is passed to `onError` as `cancelledByUser`. A failure after the session was + /// invalidated (e.g. the user cancelled the sheet or it timed out) is passed as that reason. + public static func run( + successMessage: String = "Data read", + onError: (Error) -> (error: Error, message: String) = { (error: $0, message: "Failed to read data") }, + _ operation: (NFCConnection) async throws -> T + ) async throws -> T { + let connection = NFCConnection() + defer { connection.session = nil } do { - try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - session.connect(to: firstTag) { error in - if let error { - continuation.resume(throwing: error) - } else { - continuation.resume() - } - } + let result = try await withTaskCancellationHandler { + try await operation(connection) + } onCancel: { + Task { @MainActor [weak connection] in connection?.session?.invalidate() } } + connection.session?.alertMessage = successMessage + connection.session?.invalidate() + return result } catch { - session.invalidate(errorMessage: "Failed to read data") - throw IdCardInternalError.connectionFailed + let error: Error = if Task.isCancelled || error is CancellationError { + IdCardInternalError.cancelledByUser + } else { + connection.invalidationError ?? error + } + let failure = onError(error) + connection.session?.invalidate(errorMessage: failure.message) + throw failure.error } + } - guard case let .iso7816(tag) = firstTag else { - session.invalidate(errorMessage: "Failed to read data") - throw IdCardInternalError.invalidTag + /// Starts the session showing `message`, and once a card is detected shows `detectedMessage` + /// while it is connected to and set up with `CAN`. + public func connect(CAN: String, message: String, detectedMessage: String) async throws -> CardCommands { + try Task.checkCancellation() + guard NFCTagReaderSession.readingAvailable else { + throw IdCardInternalError.nfcNotSupported + } + guard self.session == nil else { throw IdCardInternalError.operationInProgress } + guard let session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self, queue: DispatchQueue.main) else { + throw IdCardInternalError.nfcNotSupported + } + session.alertMessage = message + self.session = session + invalidationError = nil + return try await withTaskCancellationHandler { + let tag = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + self.continuation = continuation + session.begin() + } + session.alertMessage = detectedMessage + do { + try await session.connect(to: .iso7816(tag)) + } catch { + throw invalidationError ?? IdCardInternalError.connectionFailed + } + return try await cardCommands(tag: tag, CAN: CAN) + } onCancel: { + Task { @MainActor [weak self] in self?.session?.invalidate() } } - - return tag } - @MainActor - public func getCardCommands(_: NFCTagReaderSession, tag: NFCISO7816Tag, CAN: String) async throws -> CardCommands { - let initialSelectedAID = tag.initialSelectedAID - let reader = try await CardReaderNFC(tag, CAN: CAN) - guard let aid = Bytes(hex: initialSelectedAID) else { + private func cardCommands(tag: NFCISO7816Tag, CAN: String) async throws -> CardCommands { + guard let aid = Bytes(hex: tag.initialSelectedAID), !aid.isEmpty else { throw IdCardInternalError.connectionFailed } + let reader = try await CardReaderNFC(tag, CAN: CAN) { [weak self] in self?.invalidationError } + // Try Idemia with explicit AID if let cmd = Idemia(reader: reader, aid: aid) { return cmd @@ -68,5 +106,39 @@ public class NFCConnection { throw IdCardInternalError.cardNotSupported } -} + public func message(_ message: String) { + session?.alertMessage = message + } + + public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { + guard tags.count == 1, let firstTag = tags.first else { + session.alertMessage = "More than one card detected, please present only your ID card" + Task { @MainActor [weak self] in + // Give the user time to remove the extra card before polling again + try? await Task.sleep(for: .milliseconds(500)) + self?.session?.restartPolling() + } + return + } + guard case let .iso7816(tag) = firstTag else { + session.alertMessage = "Card not supported, please present your ID card" + session.restartPolling() + return + } + continuation?.resume(returning: tag) + continuation = nil + } + + public func tagReaderSessionDidBecomeActive(_: NFCTagReaderSession) { } + + public func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) { + guard session === self.session else { return } + self.session = nil + let error = IdCardInternalError.mapSessionInvalidation(error) + invalidationError = error + guard let continuation else { return } + self.continuation = nil + continuation.resume(throwing: error) + } +} diff --git a/Sources/nfclib/Operations/OperationReadCertificate.swift b/Sources/nfclib/Operations/OperationReadCertificate.swift index 0408579..07a151e 100644 --- a/Sources/nfclib/Operations/OperationReadCertificate.swift +++ b/Sources/nfclib/Operations/OperationReadCertificate.swift @@ -2,10 +2,8 @@ // SPDX-License-Identifier: LGPL-2.1-or-later import Foundation -import CoreNFC import Security - // MARK: - Local Types public enum ReadCertificateError: Error { @@ -19,89 +17,26 @@ public enum CertificateUsage { case sign } -// Allow CoreNFC types to cross async boundaries in this controlled context -extension NFCTagReaderSession: @unchecked @retroactive Sendable {} -extension NFCTag: @unchecked @retroactive Sendable {} - @MainActor -public class OperationReadCertificate: NSObject { - private var session: NFCTagReaderSession? - private var CAN: String = "" - private var certUsage: CertificateUsage! - private let nfcMessage: String = "Please place your ID card against the smart device" - private let connection = NFCConnection() - private var continuation: CheckedContinuation? +public class OperationReadCertificate { + public init() {} public func startReading(CAN: String, certUsage: CertificateUsage) async throws -> SecCertificate { - - return try await withCheckedThrowingContinuation { continuation in - guard self.continuation == nil else { - continuation.resume(throwing: IdCardInternalError.operationInProgress) - return - } - self.continuation = continuation - - guard NFCTagReaderSession.readingAvailable else { - self.finish(.failure(IdCardInternalError.nfcNotSupported)) - return - } - - self.CAN = CAN - self.certUsage = certUsage - session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self, queue: DispatchQueue.main) - session?.alertMessage = nfcMessage - session?.begin() - } - } - - private func finish(_ result: Result) { - guard let continuation else { return } - self.continuation = nil - continuation.resume(with: result) - } -} - -extension OperationReadCertificate: @MainActor NFCTagReaderSessionDelegate { - public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { - Task { @MainActor in - guard let certUsage else { - finish(.failure(ReadCertificateError.certificateUsageNotSpecified)) - session.invalidate(errorMessage: "Failed to read data") - return - } + try await NFCConnection.run { connection in + let cardCommands = try await connection.connect( + CAN: CAN, + message: "Please place your ID card against the smart device", + detectedMessage: "Hold your ID card against your smart device until the data is read" + ) do { - session.alertMessage = "Hold your ID card against your smart device until the data is read" - let tag = try await connection.setup(session, tags: tags) - let cardCommands = try await connection.getCardCommands(session, tag: tag, CAN: CAN) - do { - switch certUsage { - case .auth: - let cert = try await cardCommands.readAuthenticationCertificate() - let x509Certificate = try convertBytesToX509Certificate(cert) - finish(.success(x509Certificate)) - case .sign: - let cert = try await cardCommands.readSignatureCertificate() - let x509Certificate = try convertBytesToX509Certificate(cert) - finish(.success(x509Certificate)) - } - session.alertMessage = "Data read" - session.invalidate() - } catch { - finish(.failure(ReadCertificateError.failedToReadCertificate)) - session.invalidate(errorMessage: "Failed to read data") + let cert = switch certUsage { + case .auth: try await cardCommands.readAuthenticationCertificate() + case .sign: try await cardCommands.readSignatureCertificate() } + return try convertBytesToX509Certificate(cert) } catch { - finish(.failure(error)) - session.invalidate(errorMessage: "Failed to read data") + throw ReadCertificateError.failedToReadCertificate } } } - - public func tagReaderSessionDidBecomeActive(_: NFCTagReaderSession) { } - - public func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) { - guard session === self.session else { return } - self.session = nil - finish(.failure(IdCardInternalError.mapSessionInvalidation(error))) - } } diff --git a/Sources/nfclib/Operations/OperationReadPublicData.swift b/Sources/nfclib/Operations/OperationReadPublicData.swift index b721737..5986039 100644 --- a/Sources/nfclib/Operations/OperationReadPublicData.swift +++ b/Sources/nfclib/Operations/OperationReadPublicData.swift @@ -2,66 +2,19 @@ // SPDX-License-Identifier: LGPL-2.1-or-later import Foundation -import CoreNFC -@MainActor final public class OperationReadPublicData: NSObject { - private var session: NFCTagReaderSession? - private var CAN: String = "" - private let nfcMessage: String = "Please place your ID card against the smart device" - private let connection = NFCConnection() - private var continuation: CheckedContinuation? +@MainActor +final public class OperationReadPublicData { + public init() {} public func startReading(CAN: String) async throws -> CardInfo { - return try await withCheckedThrowingContinuation { continuation in - guard self.continuation == nil else { - continuation.resume(throwing: IdCardInternalError.operationInProgress) - return - } - self.continuation = continuation - - guard NFCTagReaderSession.readingAvailable else { - self.finish(.failure(IdCardInternalError.nfcNotSupported)) - return - } - - self.CAN = CAN - session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self, queue: DispatchQueue.main) - session?.alertMessage = nfcMessage - session?.begin() - } - } - - private func finish(_ result: Result) { - guard let continuation else { return } - self.continuation = nil - continuation.resume(with: result) - } -} - -extension OperationReadPublicData: @MainActor NFCTagReaderSessionDelegate { - public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { - Task { - do { - session.alertMessage = "Hold your ID card against your smart device until the data is read" - let tag = try await connection.setup(session, tags: tags) - let cardCommands = try await connection.getCardCommands(session, tag: tag, CAN: CAN) - let cardInfo = try await cardCommands.readPublicData() - - finish(.success(cardInfo)) - session.alertMessage = "Data read" - session.invalidate() - } catch { - finish(.failure(error)) - session.invalidate(errorMessage: "Failed to read data") - } + try await NFCConnection.run { connection in + let cardCommands = try await connection.connect( + CAN: CAN, + message: "Please place your ID card against the smart device", + detectedMessage: "Hold your ID card against your smart device until the data is read" + ) + return try await cardCommands.readPublicData() } } - - public func tagReaderSessionDidBecomeActive(_: NFCTagReaderSession) { } - - public func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) { - guard session === self.session else { return } - self.session = nil - finish(.failure(IdCardInternalError.mapSessionInvalidation(error))) - } } diff --git a/Sources/nfclib/Operations/OperationSignHash.swift b/Sources/nfclib/Operations/OperationSignHash.swift index 4931458..99f451a 100644 --- a/Sources/nfclib/Operations/OperationSignHash.swift +++ b/Sources/nfclib/Operations/OperationSignHash.swift @@ -2,42 +2,25 @@ // SPDX-License-Identifier: LGPL-2.1-or-later import Foundation -import CoreNFC @MainActor -public class OperationSignHash: NSObject { - private var session: NFCTagReaderSession? - private var CAN: String = "" - private var PIN: SecureData = SecureData([0x00]) - private var hashToSign: Data? - private let nfcMessage: String = "Please place your ID card against the smart device" - private var continuation: CheckedContinuation? - private var connection = NFCConnection() +public class OperationSignHash { + public init() {} public func startSigning(CAN: String, PIN2: SecureData, hash: Data) async throws -> Data { - - return try await withCheckedThrowingContinuation { continuation in - guard self.continuation == nil else { - continuation.resume(throwing: IdCardInternalError.operationInProgress) - return - } - self.continuation = continuation - - guard NFCTagReaderSession.readingAvailable else { - self.finish(.failure(IdCardInternalError.nfcNotSupported)) - return - } - self.CAN = CAN - self.PIN = PIN2 - self.hashToSign = hash - - session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self, queue: DispatchQueue.main) - updateAlertMessage(step: 0) - session?.begin() + try await NFCConnection.run { connection in + let cardCommands = try await connection.connect( + CAN: CAN, + message: alertMessage(step: 0), + detectedMessage: alertMessage(step: 2) + ) + connection.message(alertMessage(step: 3)) + try Task.checkCancellation() + return try await cardCommands.calculateSignature(for: hash, withPin2: PIN2) } } - private func updateAlertMessage(step: Int) { + private func alertMessage(step: Int) -> String { let stepMessages = [ "Please place your ID card against the smart device", "Hold your ID card against your smart device until the data is read", @@ -47,49 +30,6 @@ public class OperationSignHash: NSObject { let stepMessage = stepMessages[min(step, stepMessages.count - 1)] let progressBar = ProgressBar(currentStep: step) - var message = stepMessage - message += "\n\n\(progressBar.generate())" - session?.alertMessage = message - } - - private func finish(_ result: Result) { - guard let continuation else { return } - self.continuation = nil - PIN = SecureData([0x00]) - continuation.resume(with: result) - } -} - -extension OperationSignHash: @MainActor NFCTagReaderSessionDelegate { - public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { - - Task { @MainActor in - do { - updateAlertMessage(step: 1) - guard let hashToSign else { - throw IdCardInternalError.invalidAPDU - } - let pin2 = PIN - let tag = try await connection.setup(session, tags: tags) - updateAlertMessage(step: 2) - let cardCommands = try await connection.getCardCommands(session, tag: tag, CAN: CAN) - updateAlertMessage(step: 3) - let signatureValue = try await cardCommands.calculateSignature(for: hashToSign, withPin2: pin2) - finish(.success(signatureValue)) - session.alertMessage = "Data read" - session.invalidate() - } catch { - finish(.failure(error)) - session.invalidate(errorMessage: "Failed to read data") - } - } - } - - public func tagReaderSessionDidBecomeActive(_: NFCTagReaderSession) { } - - public func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) { - guard session === self.session else { return } - self.session = nil - finish(.failure(IdCardInternalError.mapSessionInvalidation(error))) + return "\(stepMessage)\n\n\(progressBar.generate())" } } diff --git a/Sources/nfclib/Operations/OperationUnblockPin.swift b/Sources/nfclib/Operations/OperationUnblockPin.swift index 1caf169..4669125 100644 --- a/Sources/nfclib/Operations/OperationUnblockPin.swift +++ b/Sources/nfclib/Operations/OperationUnblockPin.swift @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-2.1-or-later import Foundation -import CoreNFC public enum UnblockPINError: Error { case missingRequiredParameter @@ -10,79 +9,18 @@ public enum UnblockPINError: Error { } @MainActor -public class OperationUnblockPin: NSObject { - private var session: NFCTagReaderSession? - private var CAN: String = "" - private var codeType: CodeType? - private var puk: SecureData? - private var newPin: SecureData? - private let nfcMessage: String = "Please place your ID card against the smart device" - private let connection = NFCConnection() - private var continuation: CheckedContinuation? +public class OperationUnblockPin { + public init() {} public func startReading(CAN: String, codeType: CodeType, puk: SecureData, newPin: SecureData) async throws { - - return try await withCheckedThrowingContinuation { continuation in - guard self.continuation == nil else { - continuation.resume(throwing: IdCardInternalError.operationInProgress) - return - } - self.continuation = continuation - - guard NFCTagReaderSession.readingAvailable else { - self.finish(.failure(IdCardInternalError.nfcNotSupported)) - return - } - - self.CAN = CAN - self.codeType = codeType - self.puk = puk - self.newPin = newPin - session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self, queue: DispatchQueue.main) - session?.alertMessage = nfcMessage - session?.begin() + try await NFCConnection.run(successMessage: "PIN changed", onError: { (error: $0, message: "PIN change failed") }) { connection in + let cardCommands = try await connection.connect( + CAN: CAN, + message: "Please place your ID card against the smart device", + detectedMessage: "Hold your ID card against your smart device until the data is read" + ) + try Task.checkCancellation() + try await cardCommands.unblockCode(codeType, puk: puk, newCode: newPin) } } - - private func finish(_ result: Result) { - guard let continuation else { return } - self.continuation = nil - puk = nil - newPin = nil - continuation.resume(with: result) - } -} - -extension OperationUnblockPin: @MainActor NFCTagReaderSessionDelegate { - public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { - Task { @MainActor [weak self] in - guard let self else { return } - guard let codeType = self.codeType, let puk = self.puk, let newPin = self.newPin else { - finish(.failure(UnblockPINError.missingRequiredParameter)) - session.invalidate(errorMessage: "PIN change failed") - return - } - do { - session.alertMessage = "Hold your ID card against your smart device until the data is read" - let tag = try await self.connection.setup(session, tags: tags) - let cardCommands = try await self.connection.getCardCommands(session, tag: tag, CAN: self.CAN) - try await cardCommands.unblockCode(codeType, puk: puk, newCode: newPin) - - finish(.success(())) - session.alertMessage = "PIN changed" - session.invalidate() - } catch { - finish(.failure(error)) - session.invalidate(errorMessage: "PIN change failed") - } - } - } - - public func tagReaderSessionDidBecomeActive(_: NFCTagReaderSession) { } - - public func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) { - guard session === self.session else { return } - self.session = nil - finish(.failure(IdCardInternalError.mapSessionInvalidation(error))) - } } diff --git a/Sources/nfclib/Utilities/AES.swift b/Sources/nfclib/Utilities/AES.swift index f7bd417..c10bd1b 100644 --- a/Sources/nfclib/Utilities/AES.swift +++ b/Sources/nfclib/Utilities/AES.swift @@ -5,7 +5,6 @@ import CommonCrypto import Foundation class AES { - typealias DataType = DataProtocol & ContiguousBytes static let BlockSize: Int = kCCBlockSizeAES128 static let Zero = Bytes(repeating: 0x00, count: BlockSize) diff --git a/Sources/nfclib/Utilities/SHA.swift b/Sources/nfclib/Utilities/SHA.swift new file mode 100644 index 0000000..ca382c2 --- /dev/null +++ b/Sources/nfclib/Utilities/SHA.swift @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Estonian Information System Authority +// SPDX-License-Identifier: LGPL-2.1-or-later + +import Foundation +import CommonCrypto + +enum HashLength { + case bits256 + case bits384 + case bits512 +} + +func sha(hashLength: HashLength, data: some DataType) -> Bytes { + let (digestLength, hashFunction) = switch hashLength { + case .bits256: + (Int(CC_SHA256_DIGEST_LENGTH), CC_SHA256) + case .bits384: + (Int(CC_SHA384_DIGEST_LENGTH), CC_SHA384) + case .bits512: + (Int(CC_SHA512_DIGEST_LENGTH), CC_SHA512) + } + + return Bytes(unsafeUninitializedCapacity: digestLength) { buffer, initializedCount in + data.withUnsafeBytes { dataBytes in + _ = hashFunction(dataBytes.baseAddress, CC_LONG(dataBytes.count), buffer.baseAddress) + } + initializedCount = digestLength + } +} diff --git a/Sources/nfclib/Utilities/Types.swift b/Sources/nfclib/Utilities/Types.swift new file mode 100644 index 0000000..6100c18 --- /dev/null +++ b/Sources/nfclib/Utilities/Types.swift @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Estonian Information System Authority +// SPDX-License-Identifier: LGPL-2.1-or-later + +import Foundation + +typealias Bytes = [UInt8] + +public typealias DataType = DataProtocol & ContiguousBytes diff --git a/Sources/nfclib/Utilities/Utilities.swift b/Sources/nfclib/Utilities/Utilities.swift index 76f518d..3470e8f 100644 --- a/Sources/nfclib/Utilities/Utilities.swift +++ b/Sources/nfclib/Utilities/Utilities.swift @@ -4,10 +4,6 @@ import Foundation import Security -public let rsaAlgorithmName = "RSA" -public let ecAlgorithmName = "EC" -public let unknownAlgorithmName = "Unknown" - func convertBytesToX509Certificate(_ data: Data) throws -> SecCertificate { guard let certificate = SecCertificateCreateWithData(nil, data as CFData) else { throw CertificateConversionError.creationFailed diff --git a/Sources/nfclib/WebEID/OperationAuthenticateWithWebEID.swift b/Sources/nfclib/WebEID/OperationAuthenticateWithWebEID.swift index 1b63344..c93f0ba 100644 --- a/Sources/nfclib/WebEID/OperationAuthenticateWithWebEID.swift +++ b/Sources/nfclib/WebEID/OperationAuthenticateWithWebEID.swift @@ -2,54 +2,76 @@ // SPDX-License-Identifier: LGPL-2.1-or-later import Foundation -import CoreNFC import Security public enum AuthenticateWithWebEidError: Error { case failedToReadPublicKey case failedToDetermineAlgorithm - case failedToHashData case failedToMapAlgorithm case failedCertificateExpired case failedCertificateNotYetValid } @MainActor -public class OperationAuthenticateWithWebEID: NSObject { - private let CAN: String - private var pin1: SecureData? - private let challenge: String - private let origin: String - private let connection = NFCConnection() +public class OperationAuthenticateWithWebEID { + public init() {} + + public func startReading(CAN: String, pin1: SecureData, challenge: String, origin: String) async throws -> WebEidData { + try await NFCConnection.run(onError: Self.failure) { connection in + let cardCommands = try await connection.connect( + CAN: CAN, + message: alertMessage(step: 0), + detectedMessage: alertMessage(step: 2) + ) + connection.message(alertMessage(step: 3)) + let certBytes = try await cardCommands.readAuthenticationCertificate() + let authCertificate = try convertBytesToX509Certificate(certBytes) + + let (notBefore, notAfter) = try validateCertificateStructure(authCertificate) + guard Date() >= notBefore else { + throw AuthenticateWithWebEidError.failedCertificateNotYetValid + } + guard Date() <= notAfter else { + throw AuthenticateWithWebEidError.failedCertificateExpired + } + guard let publicKey = SecCertificateCopyKey(authCertificate) else { + throw AuthenticateWithWebEidError.failedToReadPublicKey + } + + let algorithm = try WebEidSignatureAlgorithm.forAuthenticationKey(publicKey) + let hashLength = algorithm.hashLength + + let originHash = sha(hashLength: hashLength, data: Bytes(origin.utf8)) + let challengeHash = sha(hashLength: hashLength, data: Bytes(challenge.utf8)) + let webEidHash = sha(hashLength: hashLength, data: originHash + challengeHash) - private var session: NFCTagReaderSession? - private var continuation: CheckedContinuation? + connection.message(alertMessage(step: 4)) + try Task.checkCancellation() + let authResult = try await cardCommands.authenticate(for: webEidHash, withPin1: pin1) + let signingCertificateBytes = try await cardCommands.readSignatureCertificate() - public init(CAN: String, pin1: SecureData, challenge: String, origin: String) { - self.CAN = CAN - self.pin1 = pin1 - self.challenge = challenge - self.origin = origin + return WebEidData( + unverifiedCertificate: certBytes.base64EncodedString(), + algorithm: algorithm, + signature: authResult.base64EncodedString(), + signingCertificate: signingCertificateBytes.base64EncodedString() + ) + } } - public func startReading() async throws -> WebEidData { - return try await withCheckedThrowingContinuation { continuation in - guard self.continuation == nil else { - continuation.resume(throwing: IdCardInternalError.operationInProgress) - return - } - self.continuation = continuation - guard NFCTagReaderSession.readingAvailable else { - self.finish(.failure(IdCardInternalError.nfcNotSupported)) - return - } - session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self, queue: DispatchQueue.main) - updateAlertMessage(step: 0) - session?.begin() + private static func failure(_ error: Error) -> (error: Error, message: String) { + let message = switch error { + case AuthenticateWithWebEidError.failedCertificateNotYetValid: + "Certificate not yet valid" + case AuthenticateWithWebEidError.failedCertificateExpired: + "Certificate has expired" + default: + "Failed to read data" } + return (error, message) } - private func updateAlertMessage(step: Int) { + private func alertMessage(step: Int) -> String { let stepMessages = [ "Please place your ID card against the smart device", "Hold your ID card against your smart device until the data is read", @@ -58,134 +80,7 @@ public class OperationAuthenticateWithWebEID: NSObject { ] let stepMessage = stepMessages[min(step, stepMessages.count - 1)] - let progressBar = ProgressBar(currentStep: step) - - var message = stepMessage - - message += "\n\n\(progressBar.generate())" - - session?.alertMessage = message + return "\(stepMessage)\n\n\(progressBar.generate())" } - - private func finish(_ result: Result) { - guard let continuation else { return } - self.continuation = nil - pin1 = nil - continuation.resume(with: result) - } -} - -extension OperationAuthenticateWithWebEID: @MainActor NFCTagReaderSessionDelegate { - public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { - Task { @MainActor [weak self] in - guard let self else { return } - do { - updateAlertMessage(step: 1) - let tag = try await connection.setup(session, tags: tags) - updateAlertMessage(step: 2) - let cardCommands = try await connection.getCardCommands(session, tag: tag, CAN: CAN) - - updateAlertMessage(step: 3) - let certBytes = try await cardCommands.readAuthenticationCertificate() - let authCertificate = try convertBytesToX509Certificate(certBytes) - - let (notBefore, notAfter) = try validateCertificateStructure(authCertificate) - - guard Date() >= notBefore else { - let errorMessage = "Certificate not yet valid" - finish(.failure(AuthenticateWithWebEidError.failedCertificateNotYetValid)) - session.invalidate(errorMessage: errorMessage) - return - } - - guard Date() <= notAfter else { - let errorMessage = "Certificate has expired" - finish(.failure(AuthenticateWithWebEidError.failedCertificateExpired)) - session.invalidate(errorMessage: errorMessage) - return - } - - guard let publicKey = SecCertificateCopyKey(authCertificate) else { - let errorMessage = "Failed to read data" - finish(.failure(AuthenticateWithWebEidError.failedToReadPublicKey)) - session.invalidate(errorMessage: errorMessage) - return - } - - guard let keyAlgorithmData = getAlgorithmNameTypeAndLength(from: publicKey) else { - let errorMessage = "Failed to read data" - finish(.failure(AuthenticateWithWebEidError.failedToDetermineAlgorithm)) - session.invalidate(errorMessage: errorMessage) - return - } - - guard let hashLength = hashLengthFromInt(keyAlgorithmData.keyLength), - let originData = origin.data(using: .utf8), - let challengeData = challenge.data(using: .utf8), - let originHash = sha(hashLength: hashLength, data: originData), - let challengeHash = sha(hashLength: hashLength, data: challengeData), - let webEidHash = sha(hashLength: hashLength, data: originHash + challengeHash) - else { - let errorMessage = "Failed to read data" - finish(.failure(AuthenticateWithWebEidError.failedToHashData)) - session.invalidate(errorMessage: errorMessage) - return - } - - updateAlertMessage(step: 4) - guard let pin1 else { throw IdCardInternalError.pinVerificationFailed(codeType: .pin1) } - let authResult = try await cardCommands.authenticate(for: webEidHash, withPin1: pin1) - let signingCertificateBytes = try await cardCommands.readSignatureCertificate() - - let webEidData = WebEidData( - unverifiedCertificate: certBytes.base64EncodedString(), - algorithm: keyAlgorithmData.algorithm, - signature: authResult.base64EncodedString(), - signingCertificate: signingCertificateBytes.base64EncodedString() - ) - finish(.success(webEidData)) - session.alertMessage = "Data read" - session.invalidate() - } catch { - finish(.failure(error)) - session.invalidate(errorMessage: "Failed to read data") - } - } - } - - public func getAlgorithmNameTypeAndLength(from key: SecKey) -> (algorithm: String, keyLength: Int)? { - // Get the algorithm type from the key - if let algorithmAttributes = SecKeyCopyAttributes(key) as? [String: Any], let algorithmType = - algorithmAttributes[kSecAttrKeyType as String] as? String { - - // Get the algorithm name based on the type - var algorithmName = "" - var keyLength = 0 - - switch algorithmType { - case String(kSecAttrKeyTypeRSA): - algorithmName = rsaAlgorithmName - keyLength = (algorithmAttributes[kSecAttrKeySizeInBits as String] as? Int) ?? 0 - case String(kSecAttrKeyTypeEC): - algorithmName = ecAlgorithmName - keyLength = (algorithmAttributes[kSecAttrKeySizeInBits as String] as? Int) ?? 0 - default: - algorithmName = unknownAlgorithmName - } - - return (algorithm: algorithmName, keyLength: keyLength) - } else { - return nil - } - } - - public func tagReaderSessionDidBecomeActive(_: NFCTagReaderSession) { } - - public func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) { - guard session === self.session else { return } - self.session = nil - finish(.failure(IdCardInternalError.mapSessionInvalidation(error))) - } - } diff --git a/Sources/nfclib/WebEID/WebEIDHash.swift b/Sources/nfclib/WebEID/WebEIDHash.swift deleted file mode 100644 index 0265387..0000000 --- a/Sources/nfclib/WebEID/WebEIDHash.swift +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: Estonian Information System Authority -// SPDX-License-Identifier: LGPL-2.1-or-later - -import Foundation -import CommonCrypto - -enum HashLength: Int { - case bits256 = 256 - case bits384 = 384 - case bits512 = 512 -} - -func hashLengthFromInt(_ intValue: Int) -> HashLength? { - return HashLength(rawValue: intValue) -} - -func sha(hashLength: HashLength, data: Data) -> Data? { - let digestLength: Int - let hashFunction: (UnsafeRawPointer?, CC_LONG, UnsafeMutablePointer?) -> UnsafeMutablePointer? - - switch hashLength { - case .bits256: - digestLength = Int(CC_SHA256_DIGEST_LENGTH) - hashFunction = CC_SHA256 - case .bits384: - digestLength = Int(CC_SHA384_DIGEST_LENGTH) - hashFunction = CC_SHA384 - case .bits512: - digestLength = Int(CC_SHA512_DIGEST_LENGTH) - hashFunction = CC_SHA512 - } - - let hashBytes = Bytes(unsafeUninitializedCapacity: digestLength) { buffer, initializedCount in - data.withUnsafeBytes { dataBytes in - _ = hashFunction(dataBytes.baseAddress, CC_LONG(data.count), buffer.baseAddress) - } - initializedCount = digestLength - } - - return Data(hashBytes) -} diff --git a/Sources/nfclib/WebEID/WebEidData.swift b/Sources/nfclib/WebEID/WebEidData.swift index c992cc9..221f4bc 100644 --- a/Sources/nfclib/WebEID/WebEidData.swift +++ b/Sources/nfclib/WebEID/WebEidData.swift @@ -6,11 +6,11 @@ import Foundation public final class WebEidData: Sendable { public let unverifiedCertificate: String public let signingCertificate: String - public let algorithm: String + public let algorithm: WebEidSignatureAlgorithm public let signature: String init(unverifiedCertificate: String, - algorithm: String, + algorithm: WebEidSignatureAlgorithm, signature: String, signingCertificate: String) { self.unverifiedCertificate = unverifiedCertificate @@ -24,7 +24,7 @@ public final class WebEidData: Sendable { ==== unverifiedCertificate: \(unverifiedCertificate) ==== - algorithm: \(algorithm) + algorithm: \(algorithm.rawValue) ==== signature: \(signature) ==== diff --git a/Sources/nfclib/WebEID/WebEidSignatureAlgorithm.swift b/Sources/nfclib/WebEID/WebEidSignatureAlgorithm.swift new file mode 100644 index 0000000..a71590f --- /dev/null +++ b/Sources/nfclib/WebEID/WebEidSignatureAlgorithm.swift @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Estonian Information System Authority +// SPDX-License-Identifier: LGPL-2.1-or-later + +import Foundation +import Security + +public enum WebEidSignatureAlgorithm: String, Sendable { + case es256 = "ES256" + case es384 = "ES384" + + var hashLength: HashLength { + switch self { + case .es256: .bits256 + case .es384: .bits384 + } + } + + static func forAuthenticationKey( + _ key: SecKey + ) throws(AuthenticateWithWebEidError) -> WebEidSignatureAlgorithm { + guard let attributes = SecKeyCopyAttributes(key) as? [String: Any], + let keyType = attributes[kSecAttrKeyType as String] as? String, + let keySizeInBits = attributes[kSecAttrKeySizeInBits as String] as? Int else { + throw .failedToDetermineAlgorithm + } + + guard keyType == String(kSecAttrKeyTypeECSECPrimeRandom) else { + throw .failedToMapAlgorithm + } + + switch keySizeInBits { + case 256: return .es256 + case 384: return .es384 + default: throw .failedToMapAlgorithm + } + } +} diff --git a/mvoting-nfc/nfc-demo.xcodeproj/project.pbxproj b/mvoting-nfc/nfc-demo.xcodeproj/project.pbxproj index 711b41a..db12753 100644 --- a/mvoting-nfc/nfc-demo.xcodeproj/project.pbxproj +++ b/mvoting-nfc/nfc-demo.xcodeproj/project.pbxproj @@ -90,7 +90,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1500; - LastUpgradeCheck = 1500; + LastUpgradeCheck = 2700; TargetAttributes = { 139195A72AFA29CC00EDA022 = { CreatedOnToolsVersion = 15.0.1; @@ -146,7 +146,6 @@ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; @@ -174,10 +173,9 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 5YYXE2JG54; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -197,6 +195,7 @@ MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -209,7 +208,6 @@ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; @@ -236,11 +234,9 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 5YYXE2JG54; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -253,6 +249,7 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_COMPILATION_MODE = wholemodule; VALIDATE_PRODUCT = YES; }; @@ -266,8 +263,6 @@ CODE_SIGN_ENTITLEMENTS = "nfc-demo/nfc-demo.entitlements"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"nfc-demo/Preview Content\""; - DEVELOPMENT_TEAM = 5YYXE2JG54; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "nfc-demo/Info.plist"; @@ -298,8 +293,6 @@ CODE_SIGN_ENTITLEMENTS = "nfc-demo/nfc-demo.entitlements"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"nfc-demo/Preview Content\""; - DEVELOPMENT_TEAM = 5YYXE2JG54; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "nfc-demo/Info.plist"; diff --git a/mvoting-nfc/nfc-demo/Operator.swift b/mvoting-nfc/nfc-demo/Operator.swift index 826e0c6..a9ad109 100644 --- a/mvoting-nfc/nfc-demo/Operator.swift +++ b/mvoting-nfc/nfc-demo/Operator.swift @@ -24,11 +24,10 @@ extension Operator: CardOperations { do { let result = try await OperationReadPublicData().startReading(CAN: CAN) return result + } catch let error as IdCardInternalError { + throw error.getIdCardError() } catch { - guard let e = error as? IdCardInternalError else { - throw IdCardError.sessionError - } - throw e.getIdCardError() + throw IdCardError.sessionError } } @@ -36,11 +35,10 @@ extension Operator: CardOperations { do { let cert = try await OperationReadCertificate().startReading(CAN: CAN, certUsage: .auth) return cert + } catch let error as IdCardInternalError { + throw error.getIdCardError() } catch { - guard let e = error as? IdCardInternalError else { - throw IdCardError.sessionError - } - throw e.getIdCardError() + throw IdCardError.sessionError } } @@ -48,23 +46,21 @@ extension Operator: CardOperations { do { let cert = try await OperationReadCertificate().startReading(CAN: CAN, certUsage: .sign) return cert + } catch let error as IdCardInternalError { + throw error.getIdCardError() } catch { - guard let e = error as? IdCardInternalError else { - throw IdCardError.sessionError - } - throw e.getIdCardError() + throw IdCardError.sessionError } } public func loadWebEIDAuthenticationData(CAN: String, pin1: SecureData, challenge: String, origin: String) async throws -> WebEidData { do { - let webEidData = try await OperationAuthenticateWithWebEID(CAN: CAN, pin1: pin1, challenge: challenge, origin: origin).startReading() + let webEidData = try await OperationAuthenticateWithWebEID().startReading(CAN: CAN, pin1: pin1, challenge: challenge, origin: origin) return webEidData + } catch let error as IdCardInternalError { + throw error.getIdCardError() } catch { - guard let e = error as? IdCardInternalError else { - throw IdCardError.sessionError - } - throw e.getIdCardError() + throw IdCardError.sessionError } } @@ -72,33 +68,30 @@ extension Operator: CardOperations { do { let signature = try await OperationSignHash().startSigning(CAN: CAN, PIN2: pin2, hash: hash) return signature + } catch let error as IdCardInternalError { + throw error.getIdCardError() } catch { - guard let e = error as? IdCardInternalError else { - throw IdCardError.sessionError - } - throw e.getIdCardError() + throw IdCardError.sessionError } } public func unblockPin1(CAN: String, puk: SecureData, newCode: SecureData) async throws { do { try await OperationUnblockPin().startReading(CAN: CAN, codeType: .pin1, puk: puk, newPin: newCode) + } catch let error as IdCardInternalError { + throw error.getIdCardError() } catch { - guard let e = error as? IdCardInternalError else { - throw IdCardError.sessionError - } - throw e.getIdCardError() + throw IdCardError.sessionError } } public func unblockPin2(CAN: String, puk: SecureData, newCode: SecureData) async throws { do { try await OperationUnblockPin().startReading(CAN: CAN, codeType: .pin2, puk: puk, newPin: newCode) + } catch let error as IdCardInternalError { + throw error.getIdCardError() } catch { - guard let e = error as? IdCardInternalError else { - throw IdCardError.sessionError - } - throw e.getIdCardError() + throw IdCardError.sessionError } } } diff --git a/mvoting-nfc/nfc-demo/Preview Content/Preview Assets.xcassets/Contents.json b/mvoting-nfc/nfc-demo/Preview Content/Preview Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/mvoting-nfc/nfc-demo/Preview Content/Preview Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -}