Skip to content

Commit

Permalink
Refactor security classes
Browse files Browse the repository at this point in the history
  • Loading branch information
AlvaroBrey committed Sep 4, 2018
1 parent ebd0561 commit c682027
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import es.usc.citius.servando.calendula.util.PreferenceUtils;
import es.usc.citius.servando.calendula.util.PresentationsTypeface;
import es.usc.citius.servando.calendula.util.security.SecuredVault;
import es.usc.citius.servando.calendula.util.security.SecurityProvider;


public class BaseModule extends CalendulaModule {
Expand Down Expand Up @@ -99,9 +100,7 @@ protected void onApplicationStartup(Context ctx) {

// initialize secured vault
if (!Build.FINGERPRINT.equals("robolectric")) {
SecuredVault.init(ctx);
} else {
SecuredVault.initForTesting();
SecurityProvider.init(ctx);
}

// initialize SQLite engine
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import java.nio.charset.Charset;
import java.sql.SQLException;

import es.usc.citius.servando.calendula.util.security.SecuredVault;
import es.usc.citius.servando.calendula.util.security.SecurityProvider;

public class SecureStringPersister extends BaseDataType {

Expand All @@ -55,13 +55,19 @@ public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int c

@Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) throws SQLException {
if (sqlArg == null) {
return null;
}
String v = new String(Base64.decode((String) sqlArg, Base64.DEFAULT), UTF8);
return SecuredVault.instance().decrypt(v);
return SecurityProvider.getEncryptionProvider().decrypt(v);
}

@Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) throws SQLException {
String encrypted = SecuredVault.instance().encrypt((String) javaObject);
if (javaObject == null) {
return null;
}
String encrypted = SecurityProvider.getEncryptionProvider().encrypt((String) javaObject);
return Base64.encodeToString(encrypted.getBytes(UTF8), Base64.DEFAULT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package es.usc.citius.servando.calendula.util.security

interface EncryptionProvider {
fun encrypt(value: String): String
fun decrypt(value: String): String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package es.usc.citius.servando.calendula.util.security

import devliving.online.securedpreferencestore.EncryptionManager
import es.usc.citius.servando.calendula.util.GsonUtil
import es.usc.citius.servando.calendula.util.LogUtil

class LibraryEncryptionProvider(private val encryptionManager: EncryptionManager) :
EncryptionProvider {

companion object {
private const val TAG = "LibraryEncryptionProv"
}

override fun encrypt(value: String): String {
val data = value.toByteArray(charset("UTF-8"))
val secret = encryptionManager.encrypt(data)
return GsonUtil.get().toJson(secret)
}

override fun decrypt(value: String): String {
try {
val secret =
GsonUtil.get().fromJson(value, EncryptionManager.EncryptedData::class.java)
val data = encryptionManager.decrypt(secret)
return data.toString(charset("UTF-8"))
} catch (e: Exception) {
LogUtil.d(TAG, "Error decrypting property", e)
throw e
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package es.usc.citius.servando.calendula.util.security

/**
* Noop encrypt/decrypt provider to be used when encryption is not available (for testing)
*/
class NoopEncryptionProvider : EncryptionProvider {
override fun encrypt(value: String): String = value
override fun decrypt(value: String): String = value
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,103 +18,10 @@

package es.usc.citius.servando.calendula.util.security

import android.content.Context
import android.content.SharedPreferences
import android.support.annotation.VisibleForTesting
import devliving.online.securedpreferencestore.DefaultRecoveryHandler
import devliving.online.securedpreferencestore.EncryptionManager
import devliving.online.securedpreferencestore.SecuredPreferenceStore
import es.usc.citius.servando.calendula.util.GsonUtil
import es.usc.citius.servando.calendula.util.LogUtil
import es.usc.citius.servando.calendula.util.PreferenceUtils

/**
* Allows storage of shared preferences encrypted using a key
* generated and stored using the android keystore
*/
class SecuredVault private constructor(val impl: SharedPreferences) : SharedPreferences by impl {

/**
* This needs to be implemented as a companion object:
* - We want to compose this class with SecuredPreferenceStore, in order expose the same interface
* - We can't use an object instead of a class because SecuredPreferenceStore has constructor with params
* - We want to use this class like a singleton anyway
*/
companion object {

private const val TAG = "SecuredVault"
private const val STORE_NAME = "secure_vault"
private const val STORE_PREFIX = "vault_pref"
private const val SEED_KEY = "CalendulaVault"

private var instance: SecuredVault? = null

@JvmStatic
fun instance(): SecuredVault {
if (instance == null) {
throw IllegalStateException("Not initialized")
} else {
return instance!!
}
}

@JvmStatic
fun init(ctx: Context) {
SecuredPreferenceStore.init(
ctx.applicationContext,
STORE_NAME,
STORE_PREFIX,
SEED_KEY.toByteArray(),
DefaultRecoveryHandler()
)
instance = SecuredVault(SecuredPreferenceStore.getSharedInstance())
}

@JvmStatic
@VisibleForTesting
fun initForTesting() {
instance = SecuredVault(PreferenceUtils.instance().preferences())
}

@JvmStatic
fun encrypt(value: String?): String? {
if (instance().impl is SecuredPreferenceStore) {
value?.let {
val data = value.toByteArray(charset("UTF-8"))
val secret =
(instance().impl as SecuredPreferenceStore).encryptionManager.encrypt(data)
return GsonUtil.get().toJson(secret)
}
return null
} else {
return value
}

}

@JvmStatic
fun decrypt(value: String?): String? {
when {
//testing only!
instance().impl !is SecuredPreferenceStore -> return value
value == null -> return null
value.isEmpty() -> return ""
else ->{
try {
val secret =
GsonUtil.get().fromJson(value, EncryptionManager.EncryptedData::class.java)
val data =
(instance().impl as SecuredPreferenceStore).encryptionManager.decrypt(secret)
return data.toString(charset("UTF-8"))
} catch (e: Exception) {
LogUtil.d(TAG, "Error decrypting property", e)
throw e
}
}
}

}
}


}
object SecuredVault : SharedPreferences by SecurityProvider.getPreferences()
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package es.usc.citius.servando.calendula.util.security

import android.content.Context
import android.content.SharedPreferences
import devliving.online.securedpreferencestore.DefaultRecoveryHandler
import devliving.online.securedpreferencestore.SecuredPreferenceStore
import es.usc.citius.servando.calendula.util.LogUtil
import es.usc.citius.servando.calendula.util.PreferenceUtils
import java.lang.ref.WeakReference

/**
* Abstracts access to encryption features: makes SecuredVault behave as normal prefs, and encryption noop, if it's not initialized (such as for testing)
*/
class SecurityProvider {

companion object {

private const val TAG = "SecurityProvider"

private const val STORE_NAME = "secure_vault"
private const val STORE_PREFIX = "vault_pref"
private const val SEED_KEY = "CalendulaVault"

private var securedPrefStore: SecuredPreferenceStore? = null
private var encryptionProvider: WeakReference<EncryptionProvider>? = null

@JvmStatic
fun init(ctx: Context) {
SecuredPreferenceStore.init(
ctx.applicationContext,
STORE_NAME,
STORE_PREFIX,
SEED_KEY.toByteArray(),
DefaultRecoveryHandler()
)
securedPrefStore = SecuredPreferenceStore.getSharedInstance()
encryptionProvider = null
}

fun isAvailable(): Boolean = securedPrefStore != null

@JvmStatic
fun getEncryptionProvider(): EncryptionProvider {

if (encryptionProvider == null) {
encryptionProvider = if (isAvailable()) {
WeakReference(LibraryEncryptionProvider(securedPrefStore!!.encryptionManager))
} else {
LogUtil.w(
TAG,
"getEncryptionProvider: SecurityProvider not initialized! Not using encryption. Is this what you want?"
)
WeakReference(NoopEncryptionProvider())
}
}

return encryptionProvider!!.get()!!
}

fun getPreferences(): SharedPreferences {
if (isAvailable()) {
return securedPrefStore as SharedPreferences
} else {
LogUtil.w(
TAG,
"getPreferences: SecurityProvider not initialized! Not using encryption. Is this what you want?"
)
return PreferenceUtils.instance().preferences()
}
}

}


}

0 comments on commit c682027

Please sign in to comment.