From c6a06b22fda5cf28c9477cca5fc4715614984036 Mon Sep 17 00:00:00 2001 From: Ross Schulman Date: Fri, 27 Sep 2024 14:58:18 -0400 Subject: [PATCH 1/2] Modify MDL presentation implementation and related example app code to reflect changes to rust library APIs --- .../mobile/sdk/CredentialsViewModel.kt | 34 ++++------ ...essionManager.kt => IsoMdlPresentation.kt} | 68 ++++++++----------- .../wallet/SelectiveDisclosureView.kt | 4 +- .../mobilesdkexample/wallet/ShareView.kt | 4 +- 4 files changed, 46 insertions(+), 64 deletions(-) rename MobileSdk/src/main/java/com/spruceid/mobile/sdk/{BleSessionManager.kt => IsoMdlPresentation.kt} (53%) diff --git a/MobileSdk/src/main/java/com/spruceid/mobile/sdk/CredentialsViewModel.kt b/MobileSdk/src/main/java/com/spruceid/mobile/sdk/CredentialsViewModel.kt index ce1f713..8bc1840 100644 --- a/MobileSdk/src/main/java/com/spruceid/mobile/sdk/CredentialsViewModel.kt +++ b/MobileSdk/src/main/java/com/spruceid/mobile/sdk/CredentialsViewModel.kt @@ -3,12 +3,9 @@ package com.spruceid.mobile.sdk import android.bluetooth.BluetoothManager import android.util.Log import androidx.lifecycle.ViewModel -import com.spruceid.mobile.sdk.rs.RequestData -import com.spruceid.mobile.sdk.rs.SessionData -import com.spruceid.mobile.sdk.rs.handleRequest -import com.spruceid.mobile.sdk.rs.initialiseSession -import com.spruceid.mobile.sdk.rs.submitResponse -import com.spruceid.mobile.sdk.rs.submitSignature +import com.spruceid.mobile.sdk.rs.ItemsRequest +import com.spruceid.mobile.sdk.rs.MdlPresentationSession +import com.spruceid.mobile.sdk.rs.initializeMdlPresentationFromBytes import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import java.security.KeyStore @@ -23,15 +20,15 @@ class CredentialsViewModel : ViewModel() { private val _currState = MutableStateFlow(PresentmentState.UNINITIALIZED) val currState = _currState.asStateFlow() - private val _requestData = MutableStateFlow(null) - val requestData = _requestData.asStateFlow() - - private val _session = MutableStateFlow(null) + private val _session = MutableStateFlow(null) val session = _session.asStateFlow() private val _error = MutableStateFlow(null) val error = _error.asStateFlow() + private val _itemsRequests = MutableStateFlow>(listOf()) + val itemsRequest = _itemsRequests.asStateFlow() + private val _allowedNamespaces = MutableStateFlow>>>(mapOf()) val allowedNamespaces = _allowedNamespaces.asStateFlow() @@ -68,21 +65,21 @@ class CredentialsViewModel : ViewModel() { } private fun updateRequestData(data: ByteArray) { - _requestData.value = handleRequest(_session.value!!.state, data) + _itemsRequests.value = _session.value!!.handleRequest(data) val namespaces = - requestData.value!!.itemsRequests.map { itemsRequest -> itemsRequest.namespaces } + _itemsRequests.value.map { itemsRequest -> itemsRequest.namespaces } Log.d( "CredentialsViewModel.updateRequestData", - "Updating requestData: \nitemRequests ${requestData.value!!.itemsRequests.map { itemsRequest -> itemsRequest.docType }} namespaces: $namespaces" + "Updating requestData: \nitemRequests ${_itemsRequests.value.map { itemsRequest -> itemsRequest.docType }} namespaces: $namespaces" ) _currState.value = PresentmentState.SELECT_NAMESPACES } - fun present(bluetoothManager: BluetoothManager) { + suspend fun present(bluetoothManager: BluetoothManager) { Log.d("CredentialsViewModel.present", "Credentials: ${_credentials.value}") _uuid.value = UUID.randomUUID() val first: MDoc = _credentials.value.first() as MDoc - _session.value = initialiseSession(first.inner, _uuid.value.toString()) + _session.value = initializeMdlPresentationFromBytes(first.inner, _uuid.value.toString()) _currState.value = PresentmentState.ENGAGING_QR_CODE _transport.value = Transport(bluetoothManager) _transport.value!! @@ -91,7 +88,7 @@ class CredentialsViewModel : ViewModel() { _uuid.value, "BLE", "Central", - _session.value!!.bleIdent, + _session.value!!.getBleIdent(), ::updateRequestData, null ) @@ -113,8 +110,7 @@ class CredentialsViewModel : ViewModel() { _error.value = e throw e } - val payload = submitResponse( - _requestData.value!!.sessionManager, + val payload = _session.value!!.generateResponse( allowedNamespaces ) @@ -136,7 +132,7 @@ class CredentialsViewModel : ViewModel() { signer.initSign(entry.privateKey) signer.update(payload) val signature = signer.sign() - val response = submitSignature(_requestData.value!!.sessionManager, signature) + val response = _session.value!!.submitResponse(signature) _transport.value!!.send(response) _currState.value = PresentmentState.SUCCESS } catch (e: Error) { diff --git a/MobileSdk/src/main/java/com/spruceid/mobile/sdk/BleSessionManager.kt b/MobileSdk/src/main/java/com/spruceid/mobile/sdk/IsoMdlPresentation.kt similarity index 53% rename from MobileSdk/src/main/java/com/spruceid/mobile/sdk/BleSessionManager.kt rename to MobileSdk/src/main/java/com/spruceid/mobile/sdk/IsoMdlPresentation.kt index 3e4c9fd..c92705c 100644 --- a/MobileSdk/src/main/java/com/spruceid/mobile/sdk/BleSessionManager.kt +++ b/MobileSdk/src/main/java/com/spruceid/mobile/sdk/IsoMdlPresentation.kt @@ -3,67 +3,51 @@ package com.spruceid.mobile.sdk import android.bluetooth.BluetoothManager import android.util.Log import com.spruceid.mobile.sdk.rs.ItemsRequest -import com.spruceid.mobile.sdk.rs.SessionManager -import com.spruceid.mobile.sdk.rs.SessionManagerEngaged -import com.spruceid.mobile.sdk.rs.initialiseSession -import com.spruceid.mobile.sdk.rs.handleRequest -import com.spruceid.mobile.sdk.rs.submitResponse -import com.spruceid.mobile.sdk.rs.submitSignature +import com.spruceid.mobile.sdk.rs.MdlPresentationSession +import com.spruceid.mobile.sdk.rs.RequestException +import com.spruceid.mobile.sdk.rs.initializeMdlPresentationFromBytes import java.security.KeyStore import java.security.Signature import java.util.UUID abstract class BLESessionStateDelegate { abstract fun update(state: Map) + abstract fun error(error: Exception) } -public class BLESessionManager { - +class IsoMdlPresentation( + val mdoc: MDoc, + val keyAlias: String, + val bluetoothManager: BluetoothManager, val callback: BLESessionStateDelegate - val uuid: UUID - var state: SessionManagerEngaged? = null - var sessionManager: SessionManager? = null +) { + val uuid: UUID = UUID.randomUUID() + var session: MdlPresentationSession? = null var itemsRequests: List = listOf() - val mdoc: MDoc var bleManager: Transport? = null - constructor( - mdoc: MDoc, - bluetoothManager: BluetoothManager, - callback: BLESessionStateDelegate, - ) { - this.callback = callback - this.uuid = UUID.randomUUID() - this.mdoc = mdoc + suspend fun initialize() { try { - val sessionData = initialiseSession(mdoc.inner, uuid.toString()) - this.state = sessionData.state - this.bleManager = Transport(bluetoothManager) + session = initializeMdlPresentationFromBytes(this.mdoc.inner, uuid.toString()) + this.bleManager = Transport(this.bluetoothManager) this.bleManager!! .initialize( "Holder", this.uuid, "BLE", "Central", - sessionData.bleIdent, + session!!.getBleIdent(), ::updateRequestData, callback ) - this.callback.update(mapOf(Pair("engagingQRCode", sessionData.qrCodeUri))) + this.callback.update(mapOf(Pair("engagingQRCode", session!!.getQrCodeUri()))) } catch (e: Error) { Log.e("BleSessionManager.constructor", e.toString()) } } - fun cancel() { - this.bleManager?.terminate() - } - fun submitNamespaces(items: Map>>) { - val payload = submitResponse( - this.sessionManager!!, - items - ) + val payload = session!!.generateResponse(items) val ks: KeyStore = KeyStore.getInstance( "AndroidKeyStore" @@ -73,9 +57,9 @@ public class BLESessionManager { null ) - val entry = ks.getEntry(this.mdoc.keyAlias, null) + val entry = ks.getEntry(this.keyAlias, null) if (entry !is KeyStore.PrivateKeyEntry) { - throw IllegalStateException("No such private key under the alias <${this.mdoc.keyAlias}>") + throw IllegalStateException("No such private key under the alias <${this.keyAlias}>") } try { @@ -85,7 +69,7 @@ public class BLESessionManager { signer.update(payload) val signature = signer.sign() - val response = submitSignature(this.sessionManager!!, signature) + val response = session!!.submitResponse(signature) this.bleManager!!.send(response) } catch (e: Error) { Log.e("CredentialsViewModel.submitNamespaces", e.toString()) @@ -95,9 +79,11 @@ public class BLESessionManager { } fun updateRequestData(data: ByteArray) { - val requestData = handleRequest(this.state!!, data) - this.sessionManager = requestData.sessionManager - this.itemsRequests = requestData.itemsRequests - this.callback.update(mapOf(Pair("selectNamespaces", requestData.itemsRequests))) + try { + this.itemsRequests = session!!.handleRequest(data) + this.callback.update(mapOf(Pair("selectNamespaces", this.itemsRequests))) + } catch (e: RequestException) { + this.callback.error(e) + } } -} +} \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/SelectiveDisclosureView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/SelectiveDisclosureView.kt index 3997f05..e01dc70 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/SelectiveDisclosureView.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/SelectiveDisclosureView.kt @@ -35,7 +35,7 @@ fun SelectiveDisclosureView( onCancel: () -> Unit ) { - val requestData by credentialViewModel.requestData.collectAsState() + val itemsRequests by credentialViewModel.itemsRequest.collectAsState() val allowedNamespaces by credentialViewModel.allowedNamespaces.collectAsState() val selectNamespacesSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) @@ -56,7 +56,7 @@ fun SelectiveDisclosureView( .padding(all = 12.dp) .verticalScroll(rememberScrollState()) ) { - requestData!!.itemsRequests.map { itemsRequest -> + itemsRequests.map { itemsRequest -> Column { Text( text = "Document being requested:\n\t\t${itemsRequest.docType}\n", diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/ShareView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/ShareView.kt index f87dfe7..c1fae93 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/ShareView.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/ShareView.kt @@ -104,10 +104,10 @@ fun ShareView( } } PresentmentState.ENGAGING_QR_CODE -> { - if (session!!.qrCodeUri.isNotEmpty()) { + if (session!!.getQrCodeUri().isNotEmpty()) { Image( painter = rememberQrBitmapPainter( - session!!.qrCodeUri, + session!!.getQrCodeUri(), 300.dp, ), contentDescription = "Share QRCode", From a598888b02c8825f5444d45ee21e6e6ae960b934 Mon Sep 17 00:00:00 2001 From: Ross Schulman Date: Mon, 30 Sep 2024 10:31:33 -0400 Subject: [PATCH 2/2] Bump mobile-sdk-rs dependency version --- MobileSdk/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MobileSdk/build.gradle.kts b/MobileSdk/build.gradle.kts index 0507d87..0459155 100644 --- a/MobileSdk/build.gradle.kts +++ b/MobileSdk/build.gradle.kts @@ -118,7 +118,7 @@ android { } dependencies { - api("com.spruceid.mobile.sdk.rs:mobilesdkrs:0.0.31") + api("com.spruceid.mobile.sdk.rs:mobilesdkrs:0.0.32") //noinspection GradleCompatible implementation("com.android.support:appcompat-v7:28.0.0") /* Begin UI dependencies */