From 1f403b896cefefc5da6373a7e989f9d8894eb0f8 Mon Sep 17 00:00:00 2001 From: Filippos Sakellaropoulos Date: Sat, 25 Nov 2023 00:29:42 +0200 Subject: [PATCH 1/2] accept rfu strings in device engagement Also accept new initialization options for ble server --- .../MdocDataTransfer18013/BLETransfer/MdocGATTServer.swift | 4 ++-- Sources/MdocDataTransfer18013/Enumerations.swift | 4 +++- Sources/MdocDataTransfer18013/MdocHelpers.swift | 5 ++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Sources/MdocDataTransfer18013/BLETransfer/MdocGATTServer.swift b/Sources/MdocDataTransfer18013/BLETransfer/MdocGATTServer.swift index ad806d3..a05799c 100644 --- a/Sources/MdocDataTransfer18013/BLETransfer/MdocGATTServer.swift +++ b/Sources/MdocDataTransfer18013/BLETransfer/MdocGATTServer.swift @@ -129,11 +129,11 @@ public class MdocGattServer: ObservableObject { // Create a new device engagement object and start the device engagement process. /// /// ``qrCodeImageData`` is set to QR code image data corresponding to the device engagement. - public func performDeviceEngagement() { + public func performDeviceEngagement(rfus: [String]? = nil) { guard !isPreview && !isInErrorState else { logger.info("Current status is \(status)"); return } // Check that the class is in the right state to start the device engagement process. It will fail if the class is in any other state. guard status == .initialized || status == .disconnected || status == .responseSent else { error = Self.makeError(code: .unexpected_error, str: error?.localizedDescription ?? "Not initialized!"); return } - deviceEngagement = DeviceEngagement(isBleServer: true, crv: .p256) + deviceEngagement = DeviceEngagement(isBleServer: true, crv: .p256, rfus: rfus) sessionEncryption = nil #if os(iOS) /// get qrCode image data corresponding to the device engagement diff --git a/Sources/MdocDataTransfer18013/Enumerations.swift b/Sources/MdocDataTransfer18013/Enumerations.swift index 7085ab6..c8e3dcb 100644 --- a/Sources/MdocDataTransfer18013/Enumerations.swift +++ b/Sources/MdocDataTransfer18013/Enumerations.swift @@ -65,7 +65,9 @@ public enum ErrorCode: Int, CustomStringConvertible { public enum InitializeKeys: String { case document_json_data case document_signup_response_data - case device_private_key + case document_signup_response_obj + case device_private_key_data + case device_private_key_obj case trusted_certificates } diff --git a/Sources/MdocDataTransfer18013/MdocHelpers.swift b/Sources/MdocDataTransfer18013/MdocHelpers.swift index 22fd745..03691b7 100644 --- a/Sources/MdocDataTransfer18013/MdocHelpers.swift +++ b/Sources/MdocDataTransfer18013/MdocHelpers.swift @@ -39,9 +39,12 @@ public class MdocHelpers { let sampleData = d.compactMap { $0.decodeJSON(type: SignUpResponse.self) } docs = sampleData.compactMap { $0.deviceResponse } devicePrivateKey = sampleData.compactMap { $0.devicePrivateKey }.first - } else if let drs = parameters[InitializeKeys.document_signup_response_data.rawValue] as? [DeviceResponse], let dpk = parameters[InitializeKeys.device_private_key.rawValue] as? CoseKeyPrivate { + } else if let drs = parameters[InitializeKeys.document_signup_response_obj.rawValue] as? [DeviceResponse], let dpk = parameters[InitializeKeys.device_private_key_obj.rawValue] as? CoseKeyPrivate { docs = drs devicePrivateKey = dpk + } else if let drsData = parameters[InitializeKeys.document_signup_response_data.rawValue] as? [Data], let dpk = parameters[InitializeKeys.device_private_key_data.rawValue] as? Data { + docs = drsData.compactMap({ DeviceResponse(data: [UInt8]($0))}) + devicePrivateKey = CoseKeyPrivate(privateKeyx963Data: dpk, crv: .p256) } if let i = parameters[InitializeKeys.trusted_certificates.rawValue] as? [Data] { iaca = i.compactMap { SecCertificateCreateWithData(nil, $0 as CFData) } From 6e361939d810dfd954ea6db900d2505897be4412 Mon Sep 17 00:00:00 2001 From: Filippos Sakellaropoulos Date: Thu, 30 Nov 2023 01:18:03 +0200 Subject: [PATCH 2/2] If no documents found return error "documents" : [+Document], + NameSpace => [ + IssuerSignedItemBytes ] ensure documents and namespaces have count>0. --- .../BLETransfer/MdocGATTServer.swift | 20 +++++++----- .../MdocDataTransfer18013/MdocHelpers.swift | 31 +++++++++++-------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/Sources/MdocDataTransfer18013/BLETransfer/MdocGATTServer.swift b/Sources/MdocDataTransfer18013/BLETransfer/MdocGATTServer.swift index a05799c..bcf28b6 100644 --- a/Sources/MdocDataTransfer18013/BLETransfer/MdocGATTServer.swift +++ b/Sources/MdocDataTransfer18013/BLETransfer/MdocGATTServer.swift @@ -49,13 +49,14 @@ public class MdocGattServer: ObservableObject { var sendBuffer = [Data]() var numBlocks: Int = 0 var subscribeCount: Int = 0 + var initSuccess:Bool = false public init(parameters: [String: Any]) throws { guard let (docs, devicePrivateKey, iaca) = MdocHelpers.initializeData(parameters: parameters) else { throw Self.makeError(code: .documents_not_provided) } self.docs = docs; self.devicePrivateKey = devicePrivateKey; self.iaca = iaca - status = .initialized; handleStatusChange(status) + status = .initialized; initSuccess = true; handleStatusChange(status) } @objc(CBPeripheralManagerDelegate) @@ -185,7 +186,7 @@ public class MdocGattServer: ObservableObject { qrCodeImageData = nil advertising = false subscribeCount = 0 - if status == .error { status = .initializing } + if status == .error && initSuccess { status = .initializing } } func handleStatusChange(_ newValue: TransferStatus) { @@ -195,7 +196,7 @@ public class MdocGattServer: ObservableObject { if newValue == .requestReceived { peripheralManager.stopAdvertising() deviceRequest = decodeRequestAndInformUser(requestData: readBuffer, devicePrivateKey: devicePrivateKey, readerKeyRawData: nil, handOver: BleTransferMode.QRHandover, handler: userSelected) - if deviceRequest == nil { error = Self.makeError(code: .requestDecodeError) } + if deviceRequest == nil && error == nil { error = Self.makeError(code: .requestDecodeError) } } else if newValue == .initialized { bleDelegate = Delegate(server: self) @@ -212,14 +213,18 @@ public class MdocGattServer: ObservableObject { } var isInErrorState: Bool { status == .error } + static var errorNoDocumentsDescriptionKey: String { "doctype_not_found" } + static func getErrorNoDocuments(_ docType: String) -> Error { NSError(domain: "\(MdocGattServer.self)", code: 0, userInfo: ["key": Self.errorNoDocumentsDescriptionKey, "%s": docType]) } public func userSelected(_ b: Bool, _ items: RequestItems?) { status = .userSelected if !b { error = Self.makeError(code: .userRejected) } if let items { do { - guard let (docToSend, _, _) = try MdocHelpers.getDeviceResponseToSend(deviceRequest: deviceRequest!, deviceResponses: docs, selectedItems: items, sessionEncryption: sessionEncryption, eReaderKey: sessionEncryption!.sessionKeys.publicKey, devicePrivateKey: devicePrivateKey) else { error = Self.makeError(code: .noDocumentToReturn); return } - guard let bytes = getSessionDataToSend(docToSend: docToSend) else { error = Self.makeError(code: .noDocumentToReturn); return } + let docTypeReq = deviceRequest?.docRequests.first?.itemsRequest.docType ?? "" + guard let (drToSend, _, _) = try MdocHelpers.getDeviceResponseToSend(deviceRequest: deviceRequest!, deviceResponses: docs, selectedItems: items, sessionEncryption: sessionEncryption, eReaderKey: sessionEncryption!.sessionKeys.publicKey, devicePrivateKey: devicePrivateKey) else { error = Self.getErrorNoDocuments(docTypeReq); return } + guard let dts = drToSend.documents, !dts.isEmpty else { error = Self.getErrorNoDocuments(docTypeReq); return} + guard let bytes = getSessionDataToSend(docToSend: drToSend) else { error = Self.getErrorNoDocuments(docTypeReq); return } prepareDataToSend(bytes) DispatchQueue.main.asyncAfter(deadline: .now()+0.2) { self.sendDataWithUpdates() } } catch { self.error = error } @@ -294,7 +299,8 @@ public class MdocGattServer: ObservableObject { guard var sessionEncryption else { logger.error("Session Encryption not initialized"); return nil } guard let requestData = try sessionEncryption.decrypt(requestCipherData) else { logger.error("Request data cannot be decrypted"); return nil } guard let deviceRequest = DeviceRequest(data: requestData) else { logger.error("Decrypted data cannot be decoded"); return nil } - guard let (_, validRequestItems, errorRequestItems) = try MdocHelpers.getDeviceResponseToSend(deviceRequest: deviceRequest, deviceResponses: docs, selectedItems: nil, sessionEncryption: sessionEncryption, eReaderKey: sessionEncryption.sessionKeys.publicKey, devicePrivateKey: devicePrivateKey) else { logger.error("Valid request items nil"); return nil } + guard let (drTest, validRequestItems, errorRequestItems) = try MdocHelpers.getDeviceResponseToSend(deviceRequest: deviceRequest, deviceResponses: docs, selectedItems: nil, sessionEncryption: sessionEncryption, eReaderKey: sessionEncryption.sessionKeys.publicKey, devicePrivateKey: devicePrivateKey) else { logger.error("Valid request items nil"); return nil } + if drTest.documents == nil { self.error = Self.getErrorNoDocuments(deviceRequest.docRequests.first?.itemsRequest.docType ?? ""); return nil } var params: [String: Any] = [UserRequestKeys.valid_items_requested.rawValue: validRequestItems, UserRequestKeys.error_items_requested.rawValue: errorRequestItems] if let docR = deviceRequest.docRequests.first { let mdocAuth = MdocReaderAuthentication(transcript: sessionEncryption.transcript) @@ -314,7 +320,7 @@ public class MdocGattServer: ObservableObject { public static func makeError(code: ErrorCode, str: String? = nil) -> NSError { let errorMessage = str ?? NSLocalizedString(code.description, comment: code.description) logger.error(Logger.Message(unicodeScalarLiteral: errorMessage)) - return NSError(domain: "\(MdocGattServer.self)", code: code.rawValue, userInfo: [NSLocalizedDescriptionKey: errorMessage]) + return NSError(domain: "\(MdocGattServer.self)", code: code.rawValue, userInfo: [NSLocalizedDescriptionKey: errorMessage, "key": code.description]) } } diff --git a/Sources/MdocDataTransfer18013/MdocHelpers.swift b/Sources/MdocDataTransfer18013/MdocHelpers.swift index 03691b7..7660bb4 100644 --- a/Sources/MdocDataTransfer18013/MdocHelpers.swift +++ b/Sources/MdocDataTransfer18013/MdocHelpers.swift @@ -95,23 +95,28 @@ public class MdocHelpers { nsErrorsToAdd[reqNamespace] = Dictionary(grouping: errorItemsSet, by: { $0 }).mapValues { _ in 0 } } } // end ns for - let issuerAuthToAdd = doc.issuerSigned.issuerAuth - let issToAdd = IssuerSigned(issuerNameSpaces: IssuerNameSpaces(nameSpaces: nsItemsToAdd), issuerAuth: issuerAuthToAdd) - var devSignedToAdd: DeviceSigned? = nil - if let eReaderKey, let sessionEncryption, let devicePrivateKey { - let authKeys = CoseKeyExchange(publicKey: eReaderKey, privateKey: devicePrivateKey) - let mdocAuth = MdocAuthentication(transcript: sessionEncryption.transcript, authKeys: authKeys) - guard let devAuth = try mdocAuth.getDeviceAuthForTransfer(docType: reqDocType) else {logger.error("Cannot create device auth"); return nil } - devSignedToAdd = DeviceSigned(deviceAuth: devAuth) - } let errors: Errors? = nsErrorsToAdd.count == 0 ? nil : Errors(errors: nsErrorsToAdd) - let docToAdd = Document(docType: reqDocType, issuerSigned: issToAdd, deviceSigned: devSignedToAdd, errors: errors) - docFiltered.append(docToAdd) - validReqItemsDocDict[reqDocType] = validReqItemsNsDict + if nsItemsToAdd.count > 0 { + let issuerAuthToAdd = doc.issuerSigned.issuerAuth + let issToAdd = IssuerSigned(issuerNameSpaces: IssuerNameSpaces(nameSpaces: nsItemsToAdd), issuerAuth: issuerAuthToAdd) + var devSignedToAdd: DeviceSigned? = nil + if let eReaderKey, let sessionEncryption, let devicePrivateKey { + let authKeys = CoseKeyExchange(publicKey: eReaderKey, privateKey: devicePrivateKey) + let mdocAuth = MdocAuthentication(transcript: sessionEncryption.transcript, authKeys: authKeys) + guard let devAuth = try mdocAuth.getDeviceAuthForTransfer(docType: reqDocType) else {logger.error("Cannot create device auth"); return nil } + devSignedToAdd = DeviceSigned(deviceAuth: devAuth) + } + let docToAdd = Document(docType: reqDocType, issuerSigned: issToAdd, deviceSigned: devSignedToAdd, errors: errors) + docFiltered.append(docToAdd) + validReqItemsDocDict[reqDocType] = validReqItemsNsDict + } else { + docErrors.append([reqDocType: UInt64(0)]) + } errorReqItemsDocDict[reqDocType] = nsErrorsToAdd.mapValues { Array($0.keys) } } // end doc for let documentErrors: [DocumentError]? = docErrors.count == 0 ? nil : docErrors.map(DocumentError.init(docErrors:)) - let deviceResponseToSend = DeviceResponse(version: deviceResponses.first!.version, documents: docFiltered, documentErrors: documentErrors, status: 0) + let documentsToAdd = docFiltered.count == 0 ? nil : docFiltered + let deviceResponseToSend = DeviceResponse(version: deviceResponses.first!.version, documents: documentsToAdd, documentErrors: documentErrors, status: 0) return (deviceResponseToSend, validReqItemsDocDict, errorReqItemsDocDict) }