Skip to content

Instantly share code, notes, and snippets.

/*
* Copyright (c) 2016 AirWatch, LLC. All rights reserved.
* This product is protected by copyright and intellectual property laws in
* the United States and other countries as well as by international treaties.
* AirWatch products may be covered by one or more patents listed at
* http://www.vmware.com/go/patents.
*/
package com.boxer.sdk;
import android.content.Context;
@shafty023
shafty023 / RawKeyGenerator.kt
Created June 9, 2020 22:31
Generates a random 32 byte key
/**
* Generates a random 32 byte key.
*
* @return a byte array containing random values
*/
fun generateRandomKey(): ByteArray =
ByteArray(32).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
SecureRandom.getInstanceStrong().nextBytes(this)
} else {
@shafty023
shafty023 / CreateNewKey.kt
Created June 9, 2020 22:40
Wrapper function to create a new key
private lateinit var rawByteKey: ByteArray
private lateinit var dbCharKey: CharArray
/**
* Generates a new database key.
*/
fun createNewKey() {
// This is the raw key that we'll be encrypting + storing
rawByteKey = generateRandomKey()
// This is the key that will be used by Room
@shafty023
shafty023 / Storable.kt
Created June 9, 2020 22:41
Container for everything needed to decrypt db
/**
* Container for everything needed for decrypting the database.
*
* @param iv initialization vector
* @param key encrypted database key
* @param salt cryptographic salt
*/
data class Storable(val iv: String, val key: String, val salt: String)
@shafty023
shafty023 / save_to_prefs.kt
Created June 9, 2020 22:52
Persist the db key to storage
/**
* Save the storable instance to preferences.
*
* @param storable a storable instance
*/
fun saveToPrefs(context: Context, storable: Storable) {
val serialized = Gson().toJson(storable)
val prefs = context.getSharedPreferences("database",
Context.MODE_PRIVATE)
prefs.edit().putString("key", serialized).apply()
@shafty023
shafty023 / get_raw_byte_key.kt
Created June 9, 2020 23:02
Decrypts the Storable instance using a passcode
/**
* 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)
@shafty023
shafty023 / get_char_key.kt
Created June 9, 2020 23:10
Returns the db key
/**
* 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)
}
/**
* 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
*/
@shafty023
shafty023 / datastore.kt
Last active June 10, 2020 00:37
DataStore
/**
* 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) {
@shafty023
shafty023 / state_machine.kt
Created June 10, 2020 00:46
StateMachine
/**
* Keeps track of current and prior application state.
*
* @param dataStore storage
*/
@Singleton
class StateMachine(private val dataStore: DataStore) {
/**
* Storage implementation for our state machine.
*