Skip to content

Commit

Permalink
VIT-7220: Migrate away from androidx-security-crypto (#160)
Browse files Browse the repository at this point in the history
* VIT-7220: Migrate away from androidx-security-crypto

* Add logpoints
  • Loading branch information
andersio authored Aug 20, 2024
1 parent 38a223a commit 958c2b2
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 25 deletions.
57 changes: 46 additions & 11 deletions VitalClient/src/main/java/io/tryvital/client/VitalClient.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.tryvital.client

import android.annotation.SuppressLint
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
Expand All @@ -22,26 +24,19 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart

const val VITAL_PERFS_FILE_NAME: String = "vital_health_connect_prefs"
const val VITAL_CLIENT_LOCAL_STORAGE: String = "vital_client_local_storage"
const val VITAL_ENCRYPTED_PERFS_FILE_NAME: String = "safe_vital_health_connect_prefs"

@Suppress("unused")
class VitalClient internal constructor(context: Context) {
val sharedPreferences: SharedPreferences by lazy {
context.getSharedPreferences(
VITAL_PERFS_FILE_NAME, Context.MODE_PRIVATE
VITAL_PERFS_FILE_NAME, MODE_PRIVATE
)
}

val encryptedSharedPreferences: SharedPreferences by lazy {
try {
createEncryptedSharedPreferences(context)
} catch (e: Exception) {
VitalLogger.getOrCreate().logE(
"Failed to decrypt shared preferences, creating new encrypted shared preferences", e
)
context.deleteSharedPreferences(VITAL_ENCRYPTED_PERFS_FILE_NAME)
return@lazy createEncryptedSharedPreferences(context)
}
createLocalStorage(context.applicationContext)
}

private val configurationReader by lazy {
Expand Down Expand Up @@ -332,7 +327,47 @@ class VitalClient internal constructor(context: Context) {
}
}

fun createEncryptedSharedPreferences(context: Context) = EncryptedSharedPreferences.create(
@SuppressLint("ApplySharedPref")
internal fun createLocalStorage(context: Context): SharedPreferences = synchronized(VitalClient) {
val preferences = context.getSharedPreferences(
VITAL_CLIENT_LOCAL_STORAGE, MODE_PRIVATE
)

// If an EncryptedSharedPreferences exists (created by an earlier SDK version),
// migrate it to the new SharedPreferences.
val oldPreferences = context.getSharedPreferences(VITAL_ENCRYPTED_PERFS_FILE_NAME, MODE_PRIVATE)
if (!oldPreferences.getString("__androidx_security_crypto_encrypted_prefs_key_keyset__", "").isNullOrBlank()) {
val logger = VitalLogger.getOrCreate()
logger.logI("EncryptedSharedPrefs migration: detected")
try {
val oldDecryptedPreferences = getDeprecatedEncryptedSharedPreferences(context)
preferences.edit().apply {
for ((key, value) in oldDecryptedPreferences.all) {
@Suppress("UNCHECKED_CAST")
when (value) {
is String -> putString(key, value)
is Boolean -> putBoolean(key, value)
is Long -> putLong(key, value)
is Int -> putInt(key, value)
is Float -> putFloat(key, value)
is Set<*> -> putStringSet(key, value as Set<String>)
}
}
commit()
}
logger.logI("EncryptedSharedPrefs migration: completed")

} catch (e: Throwable) {
VitalLogger.getOrCreate().logE("EncryptedSharedPrefs migration: failed", e)
}
context.deleteSharedPreferences(VITAL_ENCRYPTED_PERFS_FILE_NAME)
logger.logI("EncryptedSharedPrefs migration: deleted old file")
}

return preferences
}

internal fun getDeprecatedEncryptedSharedPreferences(context: Context) = EncryptedSharedPreferences.create(
VITAL_ENCRYPTED_PERFS_FILE_NAME,
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
Expand Down
16 changes: 2 additions & 14 deletions VitalClient/src/main/java/io/tryvital/client/jwt/VitalJWTAuth.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ import android.content.SharedPreferences
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
import io.tryvital.client.Environment
import io.tryvital.client.Region
import io.tryvital.client.VITAL_ENCRYPTED_PERFS_FILE_NAME
import io.tryvital.client.createEncryptedSharedPreferences
import io.tryvital.client.createLocalStorage
import io.tryvital.client.utils.InstantJsonAdapter
import io.tryvital.client.utils.VitalLogger
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableSharedFlow
Expand All @@ -32,7 +29,6 @@ import okio.ByteString.Companion.decodeBase64
import java.io.IOException
import java.lang.IllegalArgumentException
import java.time.Instant
import java.util.Date
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

Expand Down Expand Up @@ -98,15 +94,7 @@ internal class VitalJWTAuth(
return shared
}

val sharedPreferences = try {
createEncryptedSharedPreferences(appContext)
} catch (e: Exception) {
VitalLogger.getOrCreate().logE(
"Failed to decrypt VitalJWTAuth preferences, re-creating it", e
)
appContext.deleteSharedPreferences(VITAL_ENCRYPTED_PERFS_FILE_NAME)
createEncryptedSharedPreferences(appContext)
}
val sharedPreferences = createLocalStorage(appContext)

this.shared = VitalJWTAuth(
preferences = sharedPreferences
Expand Down
2 changes: 2 additions & 0 deletions VitalClient/src/main/java/io/tryvital/client/utils/logger.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.tryvital.client.utils

import android.util.Log
import io.tryvital.client.BuildConfig

const val VITAL_LOGGER = "vital-logger"

Expand Down Expand Up @@ -38,6 +39,7 @@ class VitalLogger private constructor() {
fun getOrCreate(): VitalLogger = synchronized(VitalLogger) {
if (instance == null) {
instance = VitalLogger()
instance!!.enabled = BuildConfig.DEBUG
}
return instance!!
}
Expand Down

0 comments on commit 958c2b2

Please sign in to comment.