diff --git a/build.gradle b/build.gradle index 0d1059f..5b0e1ef 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.7.10' repositories { google() jcenter() diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 11faf3c..22a3fd9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed Feb 09 11:51:17 JST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME +distributionUrl=https://services.gradle.org/distributions/gradle-7.1-all.zip diff --git a/securepreferences/build.gradle b/securepreferences/build.gradle index fa1666e..bd817e1 100644 --- a/securepreferences/build.gradle +++ b/securepreferences/build.gradle @@ -2,16 +2,16 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' group = 'com.github.Bitcoin-com' -version = '1.2.3' +version = '1.2.4' android { - compileSdkVersion 31 + compileSdkVersion 32 defaultConfig { - targetSdkVersion 31 + targetSdkVersion 32 minSdkVersion 23 - versionCode 10203 - versionName "1.2.3" + versionCode 10204 + versionName "1.2.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -34,7 +34,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // For @RequiresApi - implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.appcompat:appcompat:1.5.1' testImplementation 'junit:junit:4.13.2' // Robolectric environment testImplementation 'androidx.test:core:1.4.0' diff --git a/securepreferences/src/main/java/com/bitcoin/securepreferences/SecureStringEncrypter.kt b/securepreferences/src/main/java/com/bitcoin/securepreferences/SecureStringEncrypter.kt index 3e0b541..a193c3b 100644 --- a/securepreferences/src/main/java/com/bitcoin/securepreferences/SecureStringEncrypter.kt +++ b/securepreferences/src/main/java/com/bitcoin/securepreferences/SecureStringEncrypter.kt @@ -3,9 +3,7 @@ package com.bitcoin.securepreferences import android.app.KeyguardManager import android.content.Context import android.content.SharedPreferences -import android.os.Build import android.util.Log -import androidx.annotation.RequiresApi import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKeys import org.json.JSONObject @@ -31,16 +29,11 @@ class SecureStringEncrypter(context: Context, private val namespace: String) { val keyguardManager: KeyguardManager? = context.getSystemService(Context.KEYGUARD_SERVICE) as? KeyguardManager if (keyguardManager != null) { - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - deviceIsSecure = keyguardManager.isDeviceSecure - } else { - deviceIsSecure = keyguardManager.isKeyguardSecure - } + deviceIsSecure = keyguardManager.isDeviceSecure } mDeviceIsSecure = deviceIsSecure - Log.d(TAG, "Device is secure: ${mDeviceIsSecure}") + Log.d(TAG, "Device is secure: $mDeviceIsSecure") } @Synchronized @@ -53,11 +46,7 @@ class SecureStringEncrypter(context: Context, private val namespace: String) { when (versionOverride) { VERSION_UNENCRYPTED -> encryptionPassthroughOfString(value) VERSION_KEY_STORE_AES -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - encryptStringUsingKeystoreAes(value) - } else { - throw Exception("SDK version ${Build.VERSION.SDK_INT} is not supported ") - } + encryptStringUsingKeystoreAes(value) } VERSION_AES_KEY_ENCRYPTED_PREFERENCE -> encryptStringUsingAesThenEncryptedPreference( value @@ -77,18 +66,7 @@ class SecureStringEncrypter(context: Context, private val namespace: String) { @Synchronized fun encryptString(value: String): String { - return when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> { - encryptStringUsingKeystoreAes(value) - } - mDeviceIsSecure -> { - encryptStringUsingAesThenKeystoreRsa(value) - } - else -> { - // TODO: What is the right thing to do here? - encryptStringUsingAesThenKeystoreRsa(value) - } - } + return encryptStringUsingKeystoreAes(value) } private fun getEncryptedSharedPreference(): SharedPreferences { @@ -109,37 +87,33 @@ class SecureStringEncrypter(context: Context, private val namespace: String) { val sharedPreferences = getEncryptedSharedPreference() val keyRef = UUID.randomUUID().toString() - sharedPreferences.edit().putString(keyRef, Base64.toBase64String(aesEncrypted.key)).apply() + sharedPreferences.edit().putString(keyRef, Base64.toBase64String(aesEncrypted.key)).commit() encrypted.put(JSON_KEY, keyRef) encrypted.put(JSON_VALUE, aesEncrypted.encrypted) val container = JSONObject() container.put(JSON_VERSION, VERSION_AES_KEY_ENCRYPTED_PREFERENCE) container.put(JSON_ENCRYPTED, encrypted) - val jsonToSave = container.toString() - return jsonToSave + return container.toString() } private fun encryptStringUsingAesThenKeystoreRsa(value: String): String { val aesEncrypted: AesEncryptionResult = encryptUsingAesWithoutKeystore(value) - //Log.d(TAG, "aesEncrypted: ${aesEncrypted}") val rsaEncrypted: JSONObject = encryptWithVersionUsingRsa(mApplicationContext, aesEncrypted.key, namespace) - val encrypted: JSONObject = JSONObject() + val encrypted = JSONObject() encrypted.put(JSON_KEY, rsaEncrypted) encrypted.put(JSON_VALUE, aesEncrypted.encrypted) - val container: JSONObject = JSONObject() + val container = JSONObject() container.put(JSON_VERSION, VERSION_AES_KEY_STORE_RSA) container.put(JSON_ENCRYPTED, encrypted) - val jsonToSave: String = container.toString() - return jsonToSave + return container.toString() } - @RequiresApi(Build.VERSION_CODES.M) private fun encryptStringUsingKeystoreAes(value: String): String { val encrypted: JSONObject = encryptUsingAesWithKeystore(value, namespace) //Log.d(TAG, "aesEncrypted: ${encrypted}") @@ -147,54 +121,39 @@ class SecureStringEncrypter(context: Context, private val namespace: String) { val container: JSONObject = JSONObject() container.put(JSON_VERSION, VERSION_KEY_STORE_AES) container.put(JSON_ENCRYPTED, encrypted) - val jsonToSave: String = container.toString() - return jsonToSave + return container.toString() } - fun encryptionPassthroughOfString(value: String): String { + private fun encryptionPassthroughOfString(value: String): String { val container: JSONObject = JSONObject() container.put(JSON_VERSION, VERSION_UNENCRYPTED) container.put(JSON_VALUE, value) - val jsonToSave: String = container.toString() - return jsonToSave + return container.toString() } @Synchronized fun decryptString(json: String): String { - val parsed: JSONObject = JSONObject(json) - val version: Int? = parsed.optInt(JSON_VERSION) - if (version == null) { - throw Exception("Format of encrypted data not recognised.") - } - when (version) { + val parsed = JSONObject(json) + when (val version: Int = parsed.optInt(JSON_VERSION)) { VERSION_UNENCRYPTED -> { - val value: String? = parsed.optString(JSON_VALUE) - if (value == null) { - throw Exception("Value for encrypted data version $version not found.") - } - return value + return parsed.optString(JSON_VALUE) + ?: throw Exception("Value for encrypted data version $version not found.") } VERSION_AES_KEY_STORE_RSA -> { - val encrypted: JSONObject? = parsed.optJSONObject(JSON_ENCRYPTED) - if (encrypted == null) { - throw Exception("Encrypted value for encrypted data version $version not found.") - } + val encrypted: JSONObject = parsed.optJSONObject(JSON_ENCRYPTED) + ?: throw Exception("Encrypted value for encrypted data version $version not found.") return decryptStringEncryptedUsingAesThenKeyStoreRsa(encrypted) } VERSION_KEY_STORE_AES -> { - val encrypted: JSONObject? = parsed.optJSONObject(JSON_ENCRYPTED) - if (encrypted == null) { - throw Exception("Encrypted value for encrypted data version $version not found.") - } + val encrypted: JSONObject = parsed.optJSONObject(JSON_ENCRYPTED) + ?: throw Exception("Encrypted value for encrypted data version $version not found.") return getStringEncryptedUsingKeyStoreAes(encrypted) } VERSION_AES_KEY_ENCRYPTED_PREFERENCE -> { val encrypted = parsed.optJSONObject(JSON_ENCRYPTED) - if (encrypted == null) { - throw Exception("Encrypted value for encrypted data version $version not found.") - } + ?: throw Exception("Encrypted value for encrypted data version $version not found.") return decryptStringEncryptedUsingAesEncryptedSharedPreference(encrypted) } @@ -205,7 +164,7 @@ class SecureStringEncrypter(context: Context, private val namespace: String) { private fun decryptStringEncryptedUsingAesEncryptedSharedPreference(jsonObj: JSONObject): String { val keyRef = jsonObj.optString(JSON_KEY) val aesEncrypted = jsonObj.optJSONObject(JSON_VALUE) - if (keyRef == null || aesEncrypted == null) { + if (aesEncrypted == null) { throw Exception("Fetching JSON failed: keyRef: $keyRef EncryptedValue: $aesEncrypted") } val sharedPreferences = getEncryptedSharedPreference() @@ -217,9 +176,7 @@ class SecureStringEncrypter(context: Context, private val namespace: String) { val aesKey = Base64.decode(base64Key) - val plainText = decryptUsingAesWithoutKeyStore(aesKey, aesEncrypted) - - return plainText + return decryptUsingAesWithoutKeyStore(aesKey, aesEncrypted) } private fun decryptStringEncryptedUsingAesThenKeyStoreRsa(jsonObj: JSONObject): String { @@ -230,14 +187,12 @@ class SecureStringEncrypter(context: Context, private val namespace: String) { } val aesKey: ByteArray = decryptWithVersionUsingRsa(rsaEncryptedKey, namespace) - val plaintext: String = decryptUsingAesWithoutKeyStore(aesKey, aesEncrypted) - return plaintext + return decryptUsingAesWithoutKeyStore(aesKey, aesEncrypted) } private fun getStringEncryptedUsingKeyStoreAes(json: JSONObject): String { - val plaintext: String = decryptUsingAesWithKeyStore(json, namespace) - return plaintext + return decryptUsingAesWithKeyStore(json, namespace) } companion object {