Skip to content

Commit

Permalink
Merge branch 'main' into gh-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
phisakel committed Oct 14, 2024
2 parents 99f94f4 + 2f00a68 commit a658cbe
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 55 deletions.
11 changes: 5 additions & 6 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ jobs:
runs-on: macos-latest

steps:
- uses: swift-actions/setup-swift@v1
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.0'
- name: Get swift version
run: swift --version # Swift 5.8
- uses: actions/checkout@v3
- name: Fix Up Private GitHub URLs
# Add personal access token to all private repo URLs
run: find . -type f \( -name 'Package.swift' -o -name 'Package.resolved' \) -exec sed -i '' "s/https:\/\/github.com\/eu-digital-identity-wallet/https:\/\/${{ secrets.USER_NAME }}:${{ secrets.USER_GITHUB_TOKEN }}@github.com\/eu-digital-identity-wallet/g" {} \;
run: swift --version
- uses: actions/checkout@v4
- name: Build
run: swift build
- name: Run tests
Expand Down
27 changes: 14 additions & 13 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
{
"originHash" : "fc60708f41996a66ff09913bb5bc0934ff3d47779d3ec833bf24adbed0bb2ad4",
"pins" : [
{
"identity" : "eudi-lib-ios-iso18013-data-model",
"kind" : "remoteSourceControl",
"location" : "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-data-model.git",
"state" : {
"revision" : "12314daf45d637a1afeda321d605f328d422fe8d",
"version" : "0.2.6"
"revision" : "c1b4383d6fc3387a8ed4c79177548624c4e34e3a",
"version" : "0.3.3"
}
},
{
"identity" : "eudi-lib-ios-iso18013-security",
"kind" : "remoteSourceControl",
"location" : "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-security.git",
"state" : {
"revision" : "41a7ba66ff5d614c9ef7d50607ef0a9af7ebb577",
"version" : "0.2.1"
"revision" : "13d65a1010ee9e6219f8bccbab6eb32f67405d86",
"version" : "0.2.6"
}
},
{
Expand All @@ -41,17 +42,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "ee97538f5b81ae89698fd95938896dec5217b148",
"version" : "1.1.1"
"revision" : "671108c96644956dddcd89dd59c203dcdb36cec7",
"version" : "1.1.4"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "bc1c29221f6dfeb0ebbfbc98eb95cd3d4967868e",
"version" : "3.4.0"
"revision" : "46072478ca365fe48370993833cb22de9b41567f",
"version" : "3.5.2"
}
},
{
Expand All @@ -77,19 +78,19 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5",
"version" : "1.5.4"
"revision" : "9cb486020ebf03bfa5b5df985387a14a98744537",
"version" : "1.6.1"
}
},
{
"identity" : "swiftcbor",
"kind" : "remoteSourceControl",
"location" : "https://github.com/niscy-eudiw/SwiftCBOR.git",
"state" : {
"revision" : "310dbc3975a5653237fed304d88a6dd59d04dd30",
"version" : "0.5.7"
"revision" : "2c8c55273d4c4aae21bb46c2afbae79ee072eff4",
"version" : "0.6.2"
}
}
],
"version" : 2
"version" : 3
}
7 changes: 3 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.8
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand All @@ -13,9 +13,8 @@ let package = Package(
targets: ["MdocDataTransfer18013"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
.package(url: "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-data-model.git", .upToNextMajor(from: "0.2.6")),
.package(url: "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-security.git", .upToNextMajor(from: "0.2.1")),
.package(url: "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-data-model.git", exact: "0.3.3"),
.package(url: "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-security.git", exact: "0.2.6"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
10 changes: 1 addition & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ The delegate object must be an instance of a class conforming to the ``MdocOffli
public protocol MdocOfflineDelegate: AnyObject {
func didChangeStatus(_ newStatus: TransferStatus)
func didFinishedWithError(_ error: Error)
func didReceiveRequest(_ request: [String: Any], handleAccept: @escaping (Bool) -> Void)
func didReceiveRequest(_ request: UserRequestInfo, handleAccept: @escaping (Bool) -> Void)
}
```

Expand All @@ -59,14 +59,6 @@ bleServerTransfer.performDeviceEngagement()
```
The QR code payload can be obtained from the property ``qrCodePayload`` when the ``status`` has the value ``TransferStatus.qrEngagementReady``.
When user (holder) acceptance is required, the app should show the request items and the reader certificate details (if reader auth is used).
The request dictionary in ``didReceiveRequest`` delegate method has the following parameters:

|Key | Value|
|--- | ---|
|items_requested|A dictionary of mdoc-types to array of requested items|
|reader_certificate_issuer|Reader certificate issuer|
|reader_auth_validated|Reader auth signature validated|
|reader_certificate_validation_message|Validation message for the reader certificate|

The BLE server will send the requested if the user accepts. In the case the client app must call the `handleAccept` callback with `true`.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import MdocDataModel18013
import MdocSecurity18013

/// BLE Gatt server implementation of mdoc transfer manager
public class MdocGattServer: ObservableObject {
public class MdocGattServer: @unchecked Sendable, ObservableObject {
var peripheralManager: CBPeripheralManager!
var bleDelegate: Delegate!
var remoteCentral: CBCentral!
Expand Down Expand Up @@ -65,7 +65,7 @@ public class MdocGattServer: ObservableObject {
}

@objc(CBPeripheralManagerDelegate)
class Delegate: NSObject, CBPeripheralManagerDelegate {
class Delegate: NSObject, @preconcurrency CBPeripheralManagerDelegate {
unowned var server: MdocGattServer

init(server: MdocGattServer) {
Expand Down Expand Up @@ -214,7 +214,7 @@ public class MdocGattServer: ObservableObject {
self.deviceRequest = decoded.deviceRequest
sessionEncryption = decoded.sessionEncryption
if decoded.isValidRequest {
delegate?.didReceiveRequest(decoded.params, handleSelected: userSelected)
delegate?.didReceiveRequest(decoded.userRequestInfo, handleSelected: userSelected)
} else {
userSelected(false, nil)
}
Expand Down Expand Up @@ -249,7 +249,6 @@ public class MdocGattServer: ObservableObject {
prepareDataToSend(bytesToSend)
DispatchQueue.main.asyncAfter(deadline: .now()+0.2) {
self.sendDataWithUpdates()
self.error = errorToSend
}
}
if !b { errorToSend = MdocHelpers.makeError(code: .userRejected) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import MdocSecurity18013
public protocol MdocOfflineDelegate: AnyObject {
func didChangeStatus(_ newStatus: TransferStatus)
func didFinishedWithError(_ error: Error)
func didReceiveRequest(_ request: [String: Any], handleSelected: @escaping (Bool, RequestItems?) -> Void)
func didReceiveRequest(_ request: UserRequestInfo, handleSelected: @escaping (Bool, RequestItems?) -> Void)
}


Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ import SwiftCBOR
/// The enum BleTransferMode defines the two roles in the communication, which can be a server or a client.
///
/// The four static variables are used to signal the start and the end of the communication. This is done by sending the bytes 0x01 and 0x02 for the start and end of the communication, respectively. For the start and end of the data transmission, the bytes 0x01 and 0x00 are used.
public enum BleTransferMode {
public enum BleTransferMode: Sendable {
case server
case client
// signals for coordination
static var START_REQUEST: [UInt8] = [0x01]
static var END_REQUEST: [UInt8] = [0x02]
static var START_DATA: [UInt8] = [0x01]
static var END_DATA: [UInt8] = [0x00]
static let START_REQUEST: [UInt8] = [0x01]
static let END_REQUEST: [UInt8] = [0x02]
static let START_DATA: [UInt8] = [0x01]
static let END_DATA: [UInt8] = [0x00]
public static let BASE_UUID_SUFFIX_SERVICE = "-0000-1000-8000-00805F9B34FB"
public static let QRHandover = CBOR.null
}

/// mdoc service characteristic definitions (mdoc is the GATT server)
public enum MdocServiceCharacteristic: String {
public enum MdocServiceCharacteristic: String, Sendable {
case state = "00000001-A123-48CE-896B-4C76973373E6"
case client2Server = "00000002-A123-48CE-896B-4C76973373E6"
case server2Client = "00000003-A123-48CE-896B-4C76973373E6"
Expand Down
8 changes: 4 additions & 4 deletions Sources/MdocDataTransfer18013/Enumerations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ limitations under the License.
import Foundation

/// Transfer status enumeration
public enum TransferStatus: String {
public enum TransferStatus: String, Sendable {
case initializing
case initialized
case qrEngagementReady
Expand All @@ -33,7 +33,7 @@ public enum TransferStatus: String {
}

/// Possible error codes
public enum ErrorCode: Int, CustomStringConvertible {
public enum ErrorCode: Int, CustomStringConvertible, Sendable {
case documents_not_provided
case invalidInputDocument
case invalidUrl
Expand Down Expand Up @@ -68,7 +68,7 @@ public enum ErrorCode: Int, CustomStringConvertible {
}

/// String keys for the initialization dictionary
public enum InitializeKeys: String {
public enum InitializeKeys: String, Sendable {
case document_json_data
case document_signup_issuer_signed_data
case document_signup_issuer_signed_obj
Expand All @@ -79,7 +79,7 @@ public enum InitializeKeys: String {
}

/// String keys for the user request dictionary
public enum UserRequestKeys: String {
public enum UserRequestKeys: String, Sendable {
case valid_items_requested
case error_items_requested
case reader_certificate_issuer
Expand Down
20 changes: 12 additions & 8 deletions Sources/MdocDataTransfer18013/MdocHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public class MdocHelpers {
/// - handOver: handOver structure
/// - Returns: A ``DeviceRequest`` object

public static func decodeRequestAndInformUser(deviceEngagement: DeviceEngagement?, docs: [String: IssuerSigned], iaca: [SecCertificate], requestData: Data, devicePrivateKeys: [String: CoseKeyPrivate], dauthMethod: DeviceAuthMethod, readerKeyRawData: [UInt8]?, handOver: CBOR) -> Result<(sessionEncryption: SessionEncryption, deviceRequest: DeviceRequest, params: [String: Any], isValidRequest: Bool), Error> {
public static func decodeRequestAndInformUser(deviceEngagement: DeviceEngagement?, docs: [String: IssuerSigned], iaca: [SecCertificate], requestData: Data, devicePrivateKeys: [String: CoseKeyPrivate], dauthMethod: DeviceAuthMethod, readerKeyRawData: [UInt8]?, handOver: CBOR) -> Result<(sessionEncryption: SessionEncryption, deviceRequest: DeviceRequest, userRequestInfo: UserRequestInfo, isValidRequest: Bool), Error> {
do {
guard let seCbor = try CBOR.decode([UInt8](requestData)) else { logger.error("Request Data is not Cbor"); return .failure(Self.makeError(code: .requestDecodeError)) }
guard var se = SessionEstablishment(cbor: seCbor) else { logger.error("Request Data cannot be decoded to session establisment"); return .failure(Self.makeError(code: .requestDecodeError)) }
Expand All @@ -107,16 +107,16 @@ public class MdocHelpers {
guard let deviceRequest = DeviceRequest(data: requestData) else { logger.error("Decrypted data cannot be decoded"); return .failure(Self.makeError(code: .requestDecodeError)) }
guard let (drTest, validRequestItems, errorRequestItems) = try Self.getDeviceResponseToSend(deviceRequest: deviceRequest, issuerSigned: docs, selectedItems: nil, sessionEncryption: sessionEncryption, eReaderKey: sessionEncryption.sessionKeys.publicKey, devicePrivateKeys: devicePrivateKeys, dauthMethod: dauthMethod) else { logger.error("Valid request items nil"); return .failure(Self.makeError(code: .requestDecodeError)) }
let bInvalidReq = (drTest.documents == nil)
var params: [String: Any] = [UserRequestKeys.valid_items_requested.rawValue: validRequestItems, UserRequestKeys.error_items_requested.rawValue: errorRequestItems]
var userRequestInfo = UserRequestInfo(validItemsRequested: validRequestItems, errorItemsRequested: errorRequestItems)
if let docR = deviceRequest.docRequests.first {
let mdocAuth = MdocReaderAuthentication(transcript: sessionEncryption.transcript)
if let readerAuthRawCBOR = docR.readerAuthRawCBOR, let certData = docR.readerCertificate, let x509 = try? X509.Certificate(derEncoded: [UInt8](certData)), let (b,reasonFailure) = try? mdocAuth.validateReaderAuth(readerAuthCBOR: readerAuthRawCBOR, readerAuthCertificate: certData, itemsRequestRawData: docR.itemsRequestRawData!, rootCerts: iaca) {
params[UserRequestKeys.reader_certificate_issuer.rawValue] = MdocHelpers.getCN(from: x509.subject.description)
params[UserRequestKeys.reader_auth_validated.rawValue] = b
if let reasonFailure { params[UserRequestKeys.reader_certificate_validation_message.rawValue] = reasonFailure }
if let readerAuthRawCBOR = docR.readerAuthRawCBOR, case let certData = docR.readerCertificates, certData.count > 0, let x509 = try? X509.Certificate(derEncoded: [UInt8](certData.first!)), let (b,reasonFailure) = try? mdocAuth.validateReaderAuth(readerAuthCBOR: readerAuthRawCBOR, readerAuthX5c: certData, itemsRequestRawData: docR.itemsRequestRawData!, rootCerts: iaca) {
userRequestInfo.readerCertificateIssuer = MdocHelpers.getCN(from: x509.subject.description)
userRequestInfo.readerAuthValidated = b
if let reasonFailure { userRequestInfo.readerCertificateValidationMessage = reasonFailure }
}
}
return .success((sessionEncryption: sessionEncryption, deviceRequest: deviceRequest, params: params, isValidRequest: !bInvalidReq))
return .success((sessionEncryption: sessionEncryption, deviceRequest: deviceRequest, userRequestInfo: userRequestInfo, isValidRequest: !bInvalidReq))
} catch { return .failure(error) }
}

Expand All @@ -142,7 +142,7 @@ public class MdocHelpers {
var docReq: DocRequest? // if selected items is null
if haveSelectedItems == false {
docReq = deviceRequest?.docRequests.findDoc(name: reqDocIdOrDocType)
guard let (doc, _) = Array(issuerSigned.values).findDoc(name: reqDocIdOrDocType) else {
guard let (_, _) = Array(issuerSigned.values).findDoc(name: reqDocIdOrDocType) else {
docErrors.append([reqDocIdOrDocType: UInt64(0)])
errorReqItemsDocDict[reqDocIdOrDocType] = [:]
continue
Expand Down Expand Up @@ -254,6 +254,7 @@ public class MdocHelpers {
/// - Parameters:
/// - vc: The view controller that will present the settings
/// - action: The action to perform
@MainActor
public static func checkBleAccess(_ vc: UIViewController, action: @escaping ()->Void) {
switch CBManager.authorization {
case .denied:
Expand All @@ -275,6 +276,7 @@ public class MdocHelpers {
/// - Parameters:
/// - vc: The view controller that will present the settings
/// - action: The action to perform
@MainActor
public static func checkCameraAccess(_ vc: UIViewController, action: @escaping ()->Void) {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .denied:
Expand Down Expand Up @@ -302,6 +304,7 @@ public class MdocHelpers {
/// - Parameters:
/// - vc: The view controller that will present the settings
/// - msg: The message to show
@MainActor
public static func presentSettings(_ vc: UIViewController, msg: String) {
let alertController = UIAlertController(title: NSLocalizedString("error", comment: ""), message: msg, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("cancel", comment: ""), style: .default))
Expand All @@ -316,6 +319,7 @@ public class MdocHelpers {
}

/// Finds the top view controller in the view hierarchy of the app. It is used to present a new view controller on top of any existing view controllers.
@MainActor
public static func getTopViewController(base: UIViewController? = UIApplication.shared.windows.first { $0.isKeyWindow }?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return getTopViewController(base: nav.visibleViewController)
Expand Down
33 changes: 33 additions & 0 deletions Sources/MdocDataTransfer18013/UserRequestInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
Copyright (c) 2023 European Commission

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

public struct UserRequestInfo : Sendable {
public init(validItemsRequested: RequestItems, errorItemsRequested: RequestItems? = nil, readerAuthValidated: Bool? = nil, readerCertificateIssuer: String? = nil, readerCertificateValidationMessage: String? = nil, readerLegalName: String? = nil) {
self.validItemsRequested = validItemsRequested
self.errorItemsRequested = errorItemsRequested
self.readerAuthValidated = readerAuthValidated
self.readerCertificateIssuer = readerCertificateIssuer
self.readerCertificateValidationMessage = readerCertificateValidationMessage
self.readerLegalName = readerLegalName
}

public var validItemsRequested: RequestItems
public var errorItemsRequested: RequestItems?
public var readerAuthValidated: Bool?
public var readerCertificateIssuer: String?
public var readerCertificateValidationMessage: String?
public var readerLegalName: String?
}
Loading

0 comments on commit a658cbe

Please sign in to comment.