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
/* | |
* 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; |
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
/** | |
* 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 { |
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
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 |
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
/** | |
* 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) |
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
/** | |
* 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() |
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
/** | |
* 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) |
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) | |
} |
OlderNewer