Skip to content

Commit

Permalink
DROID-1740: Remove use of device credentials
Browse files Browse the repository at this point in the history
  • Loading branch information
Drew Carlson committed Jun 9, 2020
1 parent 254d94f commit 4c0149e
Showing 1 changed file with 44 additions and 34 deletions.
78 changes: 44 additions & 34 deletions app/src/main/java/com/breadwallet/tools/security/BrdUserManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ private const val PUT_PHRASE_RC = 400
private const val GET_PHRASE_RC = 401

private const val KEY_ACCOUNT = "account"
private const val KEY_PHRASE = "phrase"
private const val KEY_AUTH_KEY = "authKey"
private const val KEY_CREATION_TIME = "creationTimeSeconds"
private const val KEY_TOKEN = "token"
Expand Down Expand Up @@ -316,7 +317,9 @@ class CryptoUserManager(
.flowOn(Default)

override suspend fun getPhrase(): ByteArray? =
executeWithAuth { BRKeyStore.getPhrase(it, GET_PHRASE_RC) }
store?.getBytes(KEY_PHRASE, null)
?: executeWithAuth { BRKeyStore.getPhrase(it, GET_PHRASE_RC) }
?.also { bytes -> store?.edit { putBytes(KEY_PHRASE, bytes) } }

override fun getAccount(): Account? =
store?.getBytes(KEY_ACCOUNT, null)?.run {
Expand Down Expand Up @@ -447,9 +450,9 @@ class CryptoUserManager(
check(getPhrase() == null) { "Phrase already exists." }

try {
val storedPhrase = executeWithAuth {
BRKeyStore.putPhrase(phrase, it, PUT_PHRASE_RC)
BRKeyStore.getPhrase(it, GET_PHRASE_RC)
val storedPhrase = checkNotNull(store).run {
edit { putBytes(KEY_PHRASE, phrase) }
getBytes(KEY_PHRASE, null)
} ?: return@withLock SetupResult.FailedToPersistPhrase

val account = Account.createFromPhrase(
Expand Down Expand Up @@ -638,38 +641,45 @@ class CryptoUserManager(
return context
}

private fun isPhraseKeyValid(): Boolean = try {
// Attempt to retrieve the key that protects the paper key and initialize an encryption cipher.
val key = KeyStore.getInstance(ANDROID_KEY_STORE)
.apply { load(null) }
.getKey(BRKeyStore.PHRASE_ALIAS, null)
private fun isPhraseKeyValid(): Boolean {
if (store?.getBytes(KEY_PHRASE, null) != null) {
// BRKeyStore is not required anymore, phrase key is ignored.
return true
}

// If there is no key, then it has not been initialized yet. The key store is still considered valid.
if (key != null) {
val cipher = Cipher.getInstance(NEW_CIPHER_ALGORITHM)
cipher.init(Cipher.ENCRYPT_MODE, key)
return try {
// Attempt to retrieve the key that protects the paper key and initialize an encryption cipher.
val key = KeyStore.getInstance(ANDROID_KEY_STORE)
.apply { load(null) }
.getKey(BRKeyStore.PHRASE_ALIAS, null)

// If there is no key, then it has not been initialized yet. The key store is still considered valid.
if (key != null) {
val cipher = Cipher.getInstance(NEW_CIPHER_ALGORITHM)
cipher.init(Cipher.ENCRYPT_MODE, key)
}
true
} catch (e: KeyPermanentlyInvalidatedException) {
// If KeyPermanentlyInvalidatedException
// -> with no cause happens, then the password was disabled. See DROID-1019.
// If UnrecoverableKeyException
// -> with cause "Key blob corrupted" happens then the password was disabled & re-enabled. See DROID-1207.
// -> with cause "Key blob corrupted" happens then after DROID-1019 the app was opened again while password is on.
// -> with cause "Key not found" happens then after DROID-1019 the app was opened again while password is off.
// -> with cause "System error" (KeyStoreException) after app wipe on devices that need uninstall to recover.
// Note: These exceptions would happen before a UserNotAuthenticatedException, so we don't need to handle that.
logError("Phrase key permanently invalidated", e)
false
} catch (e: UnrecoverableKeyException) {
logError("Phrase key is unrecoverable", e)
false
} catch (e: GeneralSecurityException) {
logWarning("Phrase key may still be valid.", e)
true
} catch (e: IOException) {
logWarning("Phrase key may still be valid.", e)
true
}
true
} catch (e: KeyPermanentlyInvalidatedException) {
// If KeyPermanentlyInvalidatedException
// -> with no cause happens, then the password was disabled. See DROID-1019.
// If UnrecoverableKeyException
// -> with cause "Key blob corrupted" happens then the password was disabled & re-enabled. See DROID-1207.
// -> with cause "Key blob corrupted" happens then after DROID-1019 the app was opened again while password is on.
// -> with cause "Key not found" happens then after DROID-1019 the app was opened again while password is off.
// -> with cause "System error" (KeyStoreException) after app wipe on devices that need uninstall to recover.
// Note: These exceptions would happen before a UserNotAuthenticatedException, so we don't need to handle that.
logError("Phrase key permanently invalidated", e)
false
} catch (e: UnrecoverableKeyException) {
logError("Phrase key is unrecoverable", e)
false
} catch (e: GeneralSecurityException) {
logWarning("Phrase key may still be valid.", e)
true
} catch (e: IOException) {
logWarning("Phrase key may still be valid.", e)
true
}

private fun requiresUninstall(): Boolean {
Expand Down

0 comments on commit 4c0149e

Please sign in to comment.