diff --git a/MobileSdk/build.gradle.kts b/MobileSdk/build.gradle.kts index ad8874a..b49fa9c 100644 --- a/MobileSdk/build.gradle.kts +++ b/MobileSdk/build.gradle.kts @@ -125,7 +125,7 @@ android { } dependencies { - api("com.spruceid.mobile.sdk.rs:mobilesdkrs:0.4.3") + api("com.spruceid.mobile.sdk.rs:mobilesdkrs:0.5.1") //noinspection GradleCompatible implementation("com.android.support:appcompat-v7:28.0.0") /* Begin UI dependencies */ diff --git a/MobileSdk/src/main/java/com/spruceid/mobile/sdk/CredentialPack.kt b/MobileSdk/src/main/java/com/spruceid/mobile/sdk/CredentialPack.kt index d605c78..e8c02bc 100644 --- a/MobileSdk/src/main/java/com/spruceid/mobile/sdk/CredentialPack.kt +++ b/MobileSdk/src/main/java/com/spruceid/mobile/sdk/CredentialPack.kt @@ -213,7 +213,7 @@ class CredentialPack { * it will be skipped without updating. */ @Throws(SavingException::class) - fun save(storage: StorageManagerInterface) { + suspend fun save(storage: StorageManagerInterface) { val vdcCollection = VdcCollection(storage) try { list().forEach { @@ -243,7 +243,7 @@ class CredentialPack { * Credentials that are in this pack __are__ removed from the VdcCollection. */ @Throws(SavingException::class) - fun remove(storage: StorageManagerInterface) { + suspend fun remove(storage: StorageManagerInterface) { intoContents().remove(storage) } @@ -254,7 +254,7 @@ class CredentialPack { /** * Clears all stored CredentialPacks. */ - fun clearPacks(storage: StorageManagerInterface) { + suspend fun clearPacks(storage: StorageManagerInterface) { try { storage.list() .filter { it.startsWith(CredentialPackContents.STORAGE_PREFIX) } @@ -270,7 +270,7 @@ class CredentialPack { * These can then be individually loaded. For eager loading of all packs, see `loadPacks`. */ @Throws(LoadingException::class) - fun listPacks(storage: StorageManagerInterface): List { + suspend fun listPacks(storage: StorageManagerInterface): List { val contents: Iterable try { contents = @@ -287,7 +287,7 @@ class CredentialPack { /** * Loads all CredentialPacks. */ - fun loadPacks(storage: StorageManagerInterface): List { + suspend fun loadPacks(storage: StorageManagerInterface): List { val vdcCollection = VdcCollection(storage) return listPacks(storage) .map { it.load(vdcCollection) } @@ -339,7 +339,7 @@ class CredentialPackContents { * Loads all of the credentials from the VdcCollection into a CredentialPack. */ @Throws(LoadingException::class) - fun load(vdcCollection: VdcCollection): CredentialPack { + suspend fun load(vdcCollection: VdcCollection): CredentialPack { val credentials = credentials .mapNotNull { @@ -378,7 +378,7 @@ class CredentialPackContents { } @Throws(SavingException::class) - internal fun save(storage: StorageManagerInterface) { + internal suspend fun save(storage: StorageManagerInterface) { try { storage.add(storageKey(), toBytes()) } catch (e: Exception) { @@ -387,7 +387,7 @@ class CredentialPackContents { } @Throws(SavingException::class) - internal fun remove(storage: StorageManagerInterface) { + internal suspend fun remove(storage: StorageManagerInterface) { val vdcCollection = VdcCollection(storage) credentials.forEach { try { diff --git a/MobileSdk/src/main/java/com/spruceid/mobile/sdk/StorageManager.kt b/MobileSdk/src/main/java/com/spruceid/mobile/sdk/StorageManager.kt index 89c3963..ee741ed 100644 --- a/MobileSdk/src/main/java/com/spruceid/mobile/sdk/StorageManager.kt +++ b/MobileSdk/src/main/java/com/spruceid/mobile/sdk/StorageManager.kt @@ -4,6 +4,8 @@ import com.spruceid.mobile.sdk.KeyManager import com.spruceid.mobile.sdk.rs.StorageManagerInterface import java.io.File import java.io.FileNotFoundException +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine class StorageManager(val context: Context) : StorageManagerInterface { /// Function: add @@ -14,7 +16,7 @@ class StorageManager(val context: Context) : StorageManagerInterface { /// Arguments: /// key - The key to add /// value - The value to add under the key - override fun add(key: String, value: ByteArray) = + override suspend fun add(key: String, value: ByteArray) = context.openFileOutput(filename(key), 0).use { it.write(encrypt(value)) it.close() @@ -27,7 +29,7 @@ class StorageManager(val context: Context) : StorageManagerInterface { /// /// Arguments: /// key - The key to retrieve - override fun get(key: String): ByteArray? { + override suspend fun get(key: String): ByteArray? { var bytes: ByteArray try { context.openFileInput(filename(key)).use { @@ -46,7 +48,7 @@ class StorageManager(val context: Context) : StorageManagerInterface { /// /// Arguments: /// key - The key to remove - override fun remove(key: String) { + override suspend fun remove(key: String) { File(context.filesDir, filename(key)).delete() } @@ -54,7 +56,7 @@ class StorageManager(val context: Context) : StorageManagerInterface { /// Function: list /// /// Lists all key-value pair in storage - override fun list(): List { + override suspend fun list(): List { val list = context.filesDir.list() ?: throw Exception("cannot list stored objects") return list.mapNotNull { @@ -77,16 +79,18 @@ class StorageManager(val context: Context) : StorageManagerInterface { /// /// Arguments: /// value - The string value to be encrypted - private fun encrypt(value: ByteArray): ByteArray { - val keyManager = KeyManager() - if (!keyManager.keyExists(KEY_NAME)) { - keyManager.generateEncryptionKey(KEY_NAME) + private suspend fun encrypt(value: ByteArray): ByteArray { + return suspendCoroutine { continuation -> + val keyManager = KeyManager() + if (!keyManager.keyExists(KEY_NAME)) { + keyManager.generateEncryptionKey(KEY_NAME) + } + val encrypted = keyManager.encryptPayload(KEY_NAME, value) + val iv = Base64.encodeToString(encrypted.first, B64_FLAGS) + val bytes = Base64.encodeToString(encrypted.second, B64_FLAGS) + val res = "$iv;$bytes".toByteArray() + continuation.resume(res) } - val encrypted = keyManager.encryptPayload(KEY_NAME, value) - val iv = Base64.encodeToString(encrypted.first, B64_FLAGS) - val bytes = Base64.encodeToString(encrypted.second, B64_FLAGS) - val res = "$iv;$bytes".toByteArray() - return res } /// Function: decrypt @@ -95,16 +99,19 @@ class StorageManager(val context: Context) : StorageManagerInterface { /// /// Arguments: /// value - The byte array to be decrypted - private fun decrypt(value: ByteArray): ByteArray { - val keyManager = KeyManager() - if (!keyManager.keyExists(KEY_NAME)) { - throw Exception("Cannot retrieve values before creating encryption keys") + private suspend fun decrypt(value: ByteArray): ByteArray { + return suspendCoroutine { continuation -> + val keyManager = KeyManager() + if (!keyManager.keyExists(KEY_NAME)) { + throw Exception("Cannot retrieve values before creating encryption keys") + } + val decoded = value.decodeToString().split(";") + assert(decoded.size == 2) + val iv = Base64.decode(decoded.first(), B64_FLAGS) + val encrypted = Base64.decode(decoded.last(), B64_FLAGS) + val decrypted = keyManager.decryptPayload(KEY_NAME, iv, encrypted) + continuation.resume(decrypted) } - val decoded = value.decodeToString().split(";") - assert(decoded.size == 2) - val iv = Base64.decode(decoded.first(), B64_FLAGS) - val encrypted = Base64.decode(decoded.last(), B64_FLAGS) - return keyManager.decryptPayload(KEY_NAME, iv, encrypted) } private const val FILENAME_PREFIX = "sprucekit:datastore" diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/CredentialPacksViewModel.kt b/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/CredentialPacksViewModel.kt index fd49c6a..ffba0bb 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/CredentialPacksViewModel.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/CredentialPacksViewModel.kt @@ -32,14 +32,14 @@ class CredentialPacksViewModel(application: Application) : AndroidViewModel(appl } } - fun saveCredentialPack(credentialPack: CredentialPack) { + suspend fun saveCredentialPack(credentialPack: CredentialPack) { credentialPack.save(storageManager) val tmpCredentialPacksList = _credentialPacks.value.toMutableStateList() tmpCredentialPacksList.add(credentialPack) _credentialPacks.value = tmpCredentialPacksList } - fun deleteAllCredentialPacks(onDeleteCredentialPack: ((CredentialPack) -> Unit)? = null) { + suspend fun deleteAllCredentialPacks(onDeleteCredentialPack: (suspend (CredentialPack) -> Unit)? = null) { _credentialPacks.value.forEach { credentialPack -> credentialPack.remove(storageManager) onDeleteCredentialPack?.invoke(credentialPack) @@ -47,7 +47,7 @@ class CredentialPacksViewModel(application: Application) : AndroidViewModel(appl _credentialPacks.value = emptyList() } - fun deleteCredentialPack(credentialPack: CredentialPack) { + suspend fun deleteCredentialPack(credentialPack: CredentialPack) { credentialPack.remove(storageManager) val tmpCredentialPacksList = _credentialPacks.value.toMutableStateList() tmpCredentialPacksList.remove(credentialPack) diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt index f64074a..a9a44f3 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt @@ -178,8 +178,8 @@ fun WalletHomeBody( credentialPack = credentialPack, statusListViewModel = statusListViewModel, onDelete = { - credentialPacksViewModel.deleteCredentialPack(credentialPack) scope.launch { + credentialPacksViewModel.deleteCredentialPack(credentialPack) credentialPack.list().forEach { credential -> val credentialInfo = getCredentialIdTitleAndIssuer( diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/walletsettings/WalletSettingsHomeView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/walletsettings/WalletSettingsHomeView.kt index af70ba8..fa90736 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/walletsettings/WalletSettingsHomeView.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/walletsettings/WalletSettingsHomeView.kt @@ -169,8 +169,8 @@ fun WalletSettingsHomeBody( Spacer(Modifier.weight(1f)) Button( onClick = { - credentialPacksViewModel.deleteAllCredentialPacks(onDeleteCredentialPack = { credentialPack -> - GlobalScope.launch { + GlobalScope.launch { + credentialPacksViewModel.deleteAllCredentialPacks(onDeleteCredentialPack = { credentialPack -> credentialPack.list().forEach { credential -> val credentialInfo = getCredentialIdTitleAndIssuer( @@ -189,8 +189,8 @@ fun WalletSettingsHomeBody( ) ) } - } - }) + }) + } }, shape = RoundedCornerShape(5.dp), colors = ButtonDefaults.buttonColors(