Skip to content

Commit

Permalink
Merge branch 'main' into skit-544-update-kotlin-18013-5-holder
Browse files Browse the repository at this point in the history
  • Loading branch information
todd-spruceid authored Oct 1, 2024
2 parents 133a9b9 + 08a4be8 commit 1618d6b
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 166 deletions.
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
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

0 comments on commit 1618d6b

Please sign in to comment.