Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust Mdl presentation code to follow new APIs from mobile-sdk-rs #36

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
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/spruceid/mobile-sdk-rs.git",
"state" : {
"revision" : "b1e729b2adc97415019925c873b976648a2331cb",
"version" : "0.0.30"
"revision" : "06e0cfcbfe01bccf0e014688be4e0b6275638bd5",
"version" : "0.0.32"
}
},
{
Expand Down
3 changes: 1 addition & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ let package = Package(
targets: ["SpruceIDMobileSdk"])
],
dependencies: [
.package(url: "https://github.com/spruceid/mobile-sdk-rs.git", from: "0.0.31"),
// .package(path: "../mobile-sdk-rs"),
.package(url: "https://github.com/spruceid/mobile-sdk-rs.git", from: "0.0.32"),
.package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0")
],
targets: [
Expand Down
6 changes: 3 additions & 3 deletions Sources/MobileSdk/Credentials.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ public class CredentialStore {
callback: BLESessionStateDelegate,
useL2CAP: Bool = true
// , trustedReaders: TrustedReaders
) -> BLESessionManager? {
) async -> IsoMdlPresentation? {
if let firstMdoc = self.credentials.first(where: {$0 is MDoc}) {
return BLESessionManager(mdoc: firstMdoc as! MDoc,
return await IsoMdlPresentation(mdoc: firstMdoc as! MDoc,
engagement: DeviceEngagement.QRCode,
callback: callback,
useL2CAP: useL2CAP)
useL2CAP: useL2CAP)
} else {
return nil
}
Expand Down
165 changes: 165 additions & 0 deletions Sources/MobileSdk/IsoMdlPresentation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import CoreBluetooth
import Foundation
import SpruceIDMobileSdkRs

public enum DeviceEngagement {
case QRCode
}

/// To be implemented by the consumer to update the UI
public protocol BLESessionStateDelegate: AnyObject {
func update(state: BLESessionState)
}

public class IsoMdlPresentation {
var callback: BLESessionStateDelegate
var uuid: UUID
var session: MdlPresentationSession
var mdoc: MDoc
var bleManager: MDocHolderBLECentral!
var useL2CAP: Bool

init?(
mdoc: MDoc, engagement: DeviceEngagement,
callback: BLESessionStateDelegate, useL2CAP: Bool
) async {
self.callback = callback
self.uuid = UUID()
self.mdoc = mdoc
self.useL2CAP = useL2CAP
do {
self.session =
try await SpruceIDMobileSdkRs.initializeMdlPresentationFromBytes(
mdoc: mdoc.inner, uuid: self.uuid.uuidString)
bleManager = MDocHolderBLECentral(
callback: self,
serviceUuid: CBUUID(nsuuid: self.uuid),
useL2CAP: useL2CAP)
self.callback.update(
state: .engagingQRCode(
session.getQrCodeUri().data(using: .ascii)!))
} catch {
print("\(error)")
return nil
}
}

// Cancel the request mid-transaction and gracefully clean up the BLE stack.
public func cancel() {
bleManager.disconnectFromDevice(session: self.session)
}

public func submitNamespaces(items: [String: [String: [String]]]) {
do {
let payload = try session.generateResponse(permittedItems: items)
let query =
[
kSecClass: kSecClassKey,
kSecAttrApplicationLabel: self.mdoc.keyAlias,
kSecReturnRef: true
] as [String: Any]

// Find and cast the result as a SecKey instance.
var item: CFTypeRef?
var secKey: SecKey
switch SecItemCopyMatching(query as CFDictionary, &item) {
case errSecSuccess:
// swiftlint:disable force_cast
secKey = item as! SecKey
// swiftlint:enable force_cast
case errSecItemNotFound:
self.callback.update(state: .error(.generic("Key not found")))
self.cancel()
return
case let status:
self.callback.update(
state: .error(.generic("Keychain read failed: \(status)")))
self.cancel()
return
}
var error: Unmanaged<CFError>?
guard
let derSignature = SecKeyCreateSignature(
secKey,
.ecdsaSignatureMessageX962SHA256,
payload as CFData,
&error) as Data?
else {
self.callback.update(
state: .error(
.generic(
"Failed to sign message: \(error.debugDescription)")
))
self.cancel()
return
}
let response = try session.submitResponse(
derSignature: derSignature)
self.bleManager.writeOutgoingValue(data: response)
} catch {
self.callback.update(state: .error(.generic("\(error)")))
self.cancel()
}
}
}

extension IsoMdlPresentation: MDocBLEDelegate {
func callback(message: MDocBLECallback) {
switch message {
case .done:
self.callback.update(state: .success)
case .connected:
self.callback.update(state: .connected)
case .uploadProgress(let value, let total):
self.callback.update(state: .uploadProgress(value, total))
case .message(let data):
do {
let itemsRequests = try session.handleRequest(request: data)
self.callback.update(state: .selectNamespaces(itemsRequests))
} catch {
self.callback.update(state: .error(.generic("\(error)")))
self.cancel()
}
case .error(let error):
self.callback.update(
state: .error(BleSessionError(holderBleError: error)))
self.cancel()
}
}
}

public enum BleSessionError {
/// When discovery or communication with the peripheral fails
case peripheral(String)
/// When Bluetooth is unusable (e.g. unauthorized).
case bluetooth(CBCentralManager)
/// Generic unrecoverable error
case generic(String)

init(holderBleError: MdocHolderBleError) {
switch holderBleError {
case .peripheral(let string):
self = .peripheral(string)
case .bluetooth(let string):
self = .bluetooth(string)
}
}
}

public enum BLESessionState {
/// App should display the error message
case error(BleSessionError)
/// App should display the QR code
case engagingQRCode(Data)
/// App should indicate to the user that BLE connection has been made
case connected
/// App should display an interactive page for the user to chose which values to reveal
case selectNamespaces([ItemsRequest])
/// App should display the fact that a certain percentage of data has been sent
/// - Parameters:
/// - 0: The number of chunks sent to far
/// - 1: The total number of chunks to be sent
case uploadProgress(Int, Int)
/// App should display a success message and offer to close the page
case success
}
159 changes: 0 additions & 159 deletions Sources/MobileSdk/MDoc.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import CoreBluetooth
import CryptoKit
import Foundation
import SpruceIDMobileSdkRs
Expand Down Expand Up @@ -30,161 +29,3 @@ public class MDoc: Credential {
super.init(id: inner.id())
}
}

public enum DeviceEngagement {
case QRCode
}

/// To be implemented by the consumer to update the UI
public protocol BLESessionStateDelegate: AnyObject {
func update(state: BLESessionState)
}

public class BLESessionManager {
var callback: BLESessionStateDelegate
var uuid: UUID
var state: SessionManagerEngaged
var sessionManager: SessionManager?
var mdoc: MDoc
var bleManager: MDocHolderBLECentral!
var useL2CAP: Bool

init?(mdoc: MDoc, engagement: DeviceEngagement, callback: BLESessionStateDelegate, useL2CAP: Bool) {
self.callback = callback
self.uuid = UUID()
self.mdoc = mdoc
self.useL2CAP = useL2CAP
do {
let sessionData = try SpruceIDMobileSdkRs.initialiseSession(
document: mdoc.inner,
uuid: self.uuid.uuidString)
self.state = sessionData.state
bleManager = MDocHolderBLECentral(
callback: self,
serviceUuid: CBUUID(nsuuid: self.uuid),
useL2CAP: useL2CAP)
self.callback.update(state: .engagingQRCode(sessionData.qrCodeUri.data(using: .ascii)!))
} catch {
print("\(error)")
return nil
}
}

// Cancel the request mid-transaction and gracefully clean up the BLE stack.
public func cancel() {
bleManager.disconnectFromDevice()
}

public func submitNamespaces(items: [String: [String: [String]]]) {
do {
let payload = try SpruceIDMobileSdkRs.submitResponse(
sessionManager: sessionManager!,
permittedItems: items)
let query =
[
kSecClass: kSecClassKey,
kSecAttrApplicationLabel: self.mdoc.keyAlias,
kSecReturnRef: true
] as [String: Any]

// Find and cast the result as a SecKey instance.
var item: CFTypeRef?
var secKey: SecKey
switch SecItemCopyMatching(query as CFDictionary, &item) {
case errSecSuccess:
// swiftlint:disable force_cast
secKey = item as! SecKey
// swiftlint:enable force_cast
case errSecItemNotFound:
self.callback.update(state: .error(.generic("Key not found")))
self.cancel()
return
case let status:
self.callback.update(state: .error(.generic("Keychain read failed: \(status)")))
self.cancel()
return
}
var error: Unmanaged<CFError>?
guard
let derSignature = SecKeyCreateSignature(
secKey,
.ecdsaSignatureMessageX962SHA256,
payload as CFData,
&error) as Data?
else {
self.callback.update(
state: .error(.generic("Failed to sign message: \(error.debugDescription)")))
self.cancel()
return
}
let response = try SpruceIDMobileSdkRs.submitSignature(
sessionManager: sessionManager!,
derSignature: derSignature)
self.bleManager.writeOutgoingValue(data: response)
} catch {
self.callback.update(state: .error(.generic("\(error)")))
self.cancel()
}
}
}

extension BLESessionManager: MDocBLEDelegate {
func callback(message: MDocBLECallback) {
switch message {
case .done:
self.callback.update(state: .success)
case .connected:
self.callback.update(state: .connected)
case .uploadProgress(let value, let total):
self.callback.update(state: .uploadProgress(value, total))
case .message(let data):
do {
let requestData = try SpruceIDMobileSdkRs.handleRequest(state: self.state, request: data)
self.sessionManager = requestData.sessionManager
self.callback.update(state: .selectNamespaces(requestData.itemsRequests))
} catch {
self.callback.update(state: .error(.generic("\(error)")))
self.cancel()
}
case .error(let error):
self.callback.update(state: .error(BleSessionError(holderBleError: error)))
self.cancel()
}
}
}

public enum BleSessionError {
/// When discovery or communication with the peripheral fails
case peripheral(String)
/// When Bluetooth is unusable (e.g. unauthorized).
case bluetooth(CBCentralManager)
/// Generic unrecoverable error
case generic(String)

init(holderBleError: MdocHolderBleError) {
switch holderBleError {
case .peripheral(let string):
self = .peripheral(string)
case .bluetooth(let string):
self = .bluetooth(string)
}
}
}

public enum BLESessionState {
/// App should display the error message
case error(BleSessionError)
/// App should display the QR code
case engagingQRCode(Data)
/// App should indicate to the user that BLE connection has been made
case connected
/// App should display an interactive page for the user to chose which values to reveal
case selectNamespaces([ItemsRequest])
/// App should display the fact that a certain percentage of data has been sent
/// - Parameters:
/// - 0: The number of chunks sent to far
/// - 1: The total number of chunks to be sent
case uploadProgress(Int, Int)
/// App should display a success message and offer to close the page
case success
}
4 changes: 2 additions & 2 deletions Sources/MobileSdk/MDocHolderBLECentral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,10 @@ class MDocHolderBLECentral: NSObject {
}
}

func disconnectFromDevice() {
func disconnectFromDevice(session: MdlPresentationSession) {
let message: Data
do {
message = try terminateSession()
message = try session.terminateSession()
} catch {
print("\(error)")
message = Data([0x02])
Expand Down
Loading