This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
abstract class EncryptedDatabase : RoomDatabase() { | |
companion object { | |
fun getInstance(passcode: CharArray, context: Context): | |
EncryptedDatabase = buildDatabase(passcode, context) | |
private fun buildDatabase( | |
passcode: CharArray, | |
context: Context | |
): EncryptedDatabase { | |
// DatabaseKeyMgr is a singleton that all of the above code is wrapped into. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private val HEX_CHARS = "0123456789ABCDEF".toCharArray() | |
/** | |
* Extension function that converts a ByteArray to a hex encoded CharArray. | |
*/ | |
fun ByteArray.toHex(): CharArray { | |
val result = StringBuilder() | |
forEach { | |
val octet = it.toInt() | |
val firstIndex = (octet and 0xF0).ushr(4) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun persistRawKey(userPasscode: CharArray) { | |
val storable = toStorable(rawByteKey, userPasscode) | |
// Implementation explained in next step | |
saveToPrefs(storable) | |
} | |
/** | |
* Returns a [Storable] instance with the db key encrypted using PBE. | |
* | |
* @param rawDbKey the raw database key |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Retrieves the [Storable] instance from prefs. | |
* | |
* @param context the caller's context | |
* @return the storable instance | |
*/ | |
fun getStorable(context: Context): Storable? { | |
val prefs = context.getSharedPreferences("database", | |
Context.MODE_PRIVATE) | |
val serialized = prefs.getString("key", null) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Singleton | |
class Preferences(context: Context) { | |
private val prefs: SharedPreferences = context.getSharedPreferences("Prefs", Context.MODE_PRIVATE) | |
companion object { | |
const val PREF_CURRENT_STATE = "CurrentState" | |
const val PREF_LAST_KNOWN_ERROR = "LastKnownError" | |
const val PREF_LAST_ACTION_PERFORMED = "LastActionPerformed" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Keeps track of current and prior application state. | |
* | |
* @param dataStore storage | |
*/ | |
@Singleton | |
class StateMachine(private val dataStore: DataStore) { | |
/** | |
* Storage implementation for our state machine. | |
* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A storage implementation that enforces whole class serialization/deserialization. | |
* | |
* This enforces organization of data that needs to be persisted rather than having generic classes | |
* with lots of unrelated data. | |
* | |
* @param context the caller's context | |
*/ | |
@Singleton | |
class DataStore(context: Context) { |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A factory that can serialize/deserialize json. | |
*/ | |
object GsonUtils { | |
/** | |
* Handles deserializing the json string into a [T] instance. | |
* | |
* @param json the json to deserialize into an instance of [T] | |
* @throws JsonSyntaxException if there is a failure deserializing then this exception is thrown | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Returns the database key suitable for using with Room. | |
* | |
* @param passcode the user's passcode | |
* @param context the caller's context | |
*/ | |
fun getCharKey(passcode: CharArray, context: Context): CharArray { | |
if (dbCharKey == null) { | |
initKey(passcode, context) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Decrypts the [Storable] instance using the [passcode]. | |
* | |
* @pararm passcode the user's passcode | |
* @param storable the storable instance previously saved with [saveToPrefs] | |
* @return the raw byte key previously generated with [generateRandomKey] | |
*/ | |
fun getRawByteKey(passcode: CharArray, storable: Storable): ByteArray { | |
val aesWrappedKey = Base64.decode(storable.key, Base64.DEFAULT) | |
val iv = Base64.decode(storable.iv, Base64.DEFAULT) |
NewerOlder